mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-11 16:45:13 +00:00
feat(console): user metadata, rehaul detail pages (#2209)
* service, sidenav, i18n, dialog * detail layout, user detail * metadata dialog from * dialog * features * formarray * metadata component * comp * user metadata refresh * use formarray, control, bulk save * metadata revert, has feature directive * lint * lint * typo * info row user, warn color optim * card cleanup, actions for user detail * project, org, user, app rehaul * lint * scss * digit fix * features and project grid rehaul * info-section layout, org domain info * readd palette scss * add svg email warn * missing translation * rm unused ts * lockoutpolicy * check for lockout feature
This commit is contained in:
+6
@@ -1,3 +1,9 @@
|
||||
h3 {
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .05em;
|
||||
}
|
||||
|
||||
.app-grid-header {
|
||||
display: flex;
|
||||
|
||||
+33
-37
@@ -5,48 +5,44 @@
|
||||
<mat-icon class="icon">arrow_back</mat-icon>
|
||||
</a>
|
||||
<h1>{{ 'PROJECT.PAGES.TITLE' | translate }} {{project?.name}}</h1>
|
||||
<ng-template appHasRole [appHasRole]="['project.write:'+projectId, 'project.write']">
|
||||
<button matTooltip="{{'ACTIONS.EDIT' | translate}}" mat-icon-button (click)="editstate = !editstate"
|
||||
aria-label="Edit project name" *ngIf="isZitadel === false">
|
||||
<i *ngIf="!editstate" class="las la-edit"></i>
|
||||
<mat-icon *ngIf="editstate">close</mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template appHasRole [appHasRole]="['project.delete:'+projectId, 'project.delete']">
|
||||
<button matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn" mat-icon-button
|
||||
(click)="deleteProject()" aria-label="Edit project name" *ngIf="isZitadel === false">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<button mat-stroked-button color="warn"
|
||||
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
|
||||
*ngIf="project?.state === ProjectState.PROJECT_STATE_ACTIVE"
|
||||
(click)="changeState(ProjectState.PROJECT_STATE_INACTIVE)">{{'PROJECT.TABLE.DEACTIVATE' |
|
||||
translate}}</button>
|
||||
<button mat-stroked-button color="warn"
|
||||
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
|
||||
*ngIf="project?.state === ProjectState.PROJECT_STATE_INACTIVE"
|
||||
(click)="changeState(ProjectState.PROJECT_STATE_ACTIVE)">{{'PROJECT.TABLE.ACTIVATE' |
|
||||
translate}}</button>
|
||||
<ng-template appHasRole [appHasRole]="['project.write:'+projectId, 'project.write']">
|
||||
<button class="actions-trigger" mat-raised-button color="primary" [matMenuTriggerFor]="actions">
|
||||
<span>{{'ACTIONS.ACTIONS' | translate}}</span>
|
||||
<mat-icon class="icon">keyboard_arrow_down</mat-icon>
|
||||
</button>
|
||||
<mat-menu #actions="matMenu" xPosition="before">
|
||||
<button mat-menu-item (click)="openNameDialog()"
|
||||
aria-label="Edit project name" *ngIf="isZitadel === false">
|
||||
{{'ACTIONS.RENAME' | translate}}
|
||||
</button>
|
||||
|
||||
<button mat-menu-item
|
||||
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
|
||||
*ngIf="project?.state === ProjectState.PROJECT_STATE_ACTIVE"
|
||||
(click)="changeState(ProjectState.PROJECT_STATE_INACTIVE)">
|
||||
{{'PROJECT.TABLE.DEACTIVATE' | translate}}
|
||||
</button>
|
||||
|
||||
<button mat-menu-item
|
||||
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
|
||||
*ngIf="project?.state === ProjectState.PROJECT_STATE_INACTIVE"
|
||||
(click)="changeState(ProjectState.PROJECT_STATE_ACTIVE)">
|
||||
{{'PROJECT.TABLE.ACTIVATE' | translate}}
|
||||
</button>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['project.delete$', 'project.delete:'+projectId]">
|
||||
<button mat-menu-item matTooltip="{{'ACTIONS.DELETE' | translate}}"
|
||||
(click)="deleteProject()" aria-label="Edit project name" *ngIf="isZitadel === false">
|
||||
<span [style.color]="'var(--warn)'">{{'PROJECT.PAGES.DELETE' | translate}}</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
</ng-template>
|
||||
|
||||
<div class="full-width">
|
||||
<div class="line">
|
||||
<ng-container *ngIf="editstate">
|
||||
<cnsl-form-field *ngIf="editstate && project?.name" class="formfield"
|
||||
hintLabel="The name is required!">
|
||||
<cnsl-label>{{'PROJECT.NAME' | translate}}</cnsl-label>
|
||||
<input cnslInput [(ngModel)]="project.name" name="name" />
|
||||
</cnsl-form-field>
|
||||
<button matTooltip="{{'ACTIONS.SAVE' | translate}}" class="icon-button" *ngIf="editstate"
|
||||
mat-icon-button (click)="updateName()">
|
||||
<mat-icon>check</mat-icon>
|
||||
</button>
|
||||
</ng-container>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
<p class="desc">{{ 'PROJECT.PAGES.DESCRIPTION' | translate }}</p>
|
||||
<p *ngIf="isZitadel" class="zitadel-warning">{{'PROJECT.PAGES.ZITADELPROJECT' | translate}}</p>
|
||||
</div>
|
||||
|
||||
+11
-1
@@ -14,12 +14,22 @@
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.2rem;
|
||||
margin: 0 1rem;
|
||||
margin-left: 2rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.actions-trigger {
|
||||
margin-top: .25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
margin-left: .5rem;
|
||||
margin-right: -.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.full-width {
|
||||
padding-top: 1rem;
|
||||
width: 100%;
|
||||
|
||||
+21
-2
@@ -21,6 +21,8 @@ import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { NameDialogComponent } from '../../../../modules/name-dialog/name-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-owned-project-detail',
|
||||
templateUrl: './owned-project-detail.component.html',
|
||||
@@ -41,7 +43,6 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
public grid: boolean = true;
|
||||
private subscription?: Subscription;
|
||||
public editstate: boolean = false;
|
||||
|
||||
public isZitadel: boolean = false;
|
||||
|
||||
@@ -73,6 +74,25 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
this.subscription?.unsubscribe();
|
||||
}
|
||||
|
||||
public openNameDialog(): void {
|
||||
const dialogRef = this.dialog.open(NameDialogComponent, {
|
||||
data: {
|
||||
name: this.project.name,
|
||||
titleKey: 'PROJECT.NAMEDIALOG.TITLE',
|
||||
descKey: 'PROJECT.NAMEDIALOG.DESCRIPTION',
|
||||
labelKey: 'PROJECT.NAMEDIALOG.NAME',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(name => {
|
||||
if (name) {
|
||||
this.project.name = name;
|
||||
this.updateName();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public openPrivateLabelingDialog(): void {
|
||||
const dialogRef = this.dialog.open(ProjectPrivateLabelingDialogComponent, {
|
||||
data: {
|
||||
@@ -221,7 +241,6 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
public updateName(): void {
|
||||
this.saveProject();
|
||||
this.editstate = false;
|
||||
}
|
||||
|
||||
public openAddMember(): void {
|
||||
|
||||
+40
-38
@@ -5,6 +5,7 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
@@ -35,43 +36,44 @@ import { OwnedProjectDetailComponent } from './owned-project-detail.component';
|
||||
import { ProjectGrantsComponent } from './project-grants/project-grants.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
OwnedProjectDetailComponent,
|
||||
ApplicationGridComponent,
|
||||
ApplicationsComponent,
|
||||
ProjectGrantsComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
AppCardModule,
|
||||
OwnedProjectDetailRoutingModule,
|
||||
TranslateModule,
|
||||
ReactiveFormsModule,
|
||||
HasRoleModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
ContributorsModule,
|
||||
MatTabsModule,
|
||||
WarnDialogModule,
|
||||
MatTooltipModule,
|
||||
ProjectRolesModule,
|
||||
HasRolePipeModule,
|
||||
UserGrantsModule,
|
||||
TimestampToDatePipeModule,
|
||||
MatTableModule,
|
||||
InputModule,
|
||||
CardModule,
|
||||
PaginatorModule,
|
||||
MatRippleModule,
|
||||
MatCheckboxModule,
|
||||
MatSelectModule,
|
||||
MatProgressSpinnerModule,
|
||||
ChangesModule,
|
||||
MetaLayoutModule,
|
||||
RefreshTableModule,
|
||||
MemberCreateDialogModule,
|
||||
LocalizedDatePipeModule,
|
||||
],
|
||||
declarations: [
|
||||
OwnedProjectDetailComponent,
|
||||
ApplicationGridComponent,
|
||||
ApplicationsComponent,
|
||||
ProjectGrantsComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
AppCardModule,
|
||||
OwnedProjectDetailRoutingModule,
|
||||
TranslateModule,
|
||||
ReactiveFormsModule,
|
||||
HasRoleModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
ContributorsModule,
|
||||
MatTabsModule,
|
||||
WarnDialogModule,
|
||||
MatTooltipModule,
|
||||
ProjectRolesModule,
|
||||
HasRolePipeModule,
|
||||
UserGrantsModule,
|
||||
TimestampToDatePipeModule,
|
||||
MatTableModule,
|
||||
InputModule,
|
||||
CardModule,
|
||||
PaginatorModule,
|
||||
MatRippleModule,
|
||||
MatCheckboxModule,
|
||||
MatSelectModule,
|
||||
MatMenuModule,
|
||||
MatProgressSpinnerModule,
|
||||
ChangesModule,
|
||||
MetaLayoutModule,
|
||||
RefreshTableModule,
|
||||
MemberCreateDialogModule,
|
||||
LocalizedDatePipeModule,
|
||||
],
|
||||
})
|
||||
export class OwnedProjectDetailModule { }
|
||||
|
||||
+8
-2
@@ -17,7 +17,10 @@
|
||||
{{
|
||||
item.details.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'
|
||||
}}</span>
|
||||
<span class="name" *ngIf="item.name">{{ item.name }}</span>
|
||||
<div class="name-row">
|
||||
<span class="name" *ngIf="item.name">{{ item.name }}</span>
|
||||
<div class="state-dot" [ngClass]="{'active': item.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': item.state === ProjectState.PROJECT_STATE_INACTIVE}"></div>
|
||||
</div>
|
||||
|
||||
<span *ngIf="item.details.changeDate" class="created">{{'PROJECT.PAGES.CREATEDON' | translate}}
|
||||
{{
|
||||
@@ -41,7 +44,10 @@
|
||||
{{
|
||||
item.details.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'
|
||||
}}</span>
|
||||
<span class="name" *ngIf="item.name">{{ item.name }}</span>
|
||||
<div class="name-row">
|
||||
<span class="name" *ngIf="item.name">{{ item.name }}</span>
|
||||
<div class="state-dot" [ngClass]="{'active': item.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': item.state === ProjectState.PROJECT_STATE_INACTIVE}"></div>
|
||||
</div>
|
||||
|
||||
<span *ngIf="item.details.creationDate" class="created">{{'PROJECT.PAGES.CREATEDON' | translate}}
|
||||
{{
|
||||
|
||||
+24
-9
@@ -44,7 +44,7 @@
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
margin: 1rem;
|
||||
flex-basis: 260px;
|
||||
flex-basis: 270px;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
@@ -57,10 +57,6 @@
|
||||
min-height: 166px;
|
||||
transition: box-shadow .1s ease-in;
|
||||
|
||||
&.inactive {
|
||||
color: var(--grey);
|
||||
}
|
||||
|
||||
img {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
@@ -81,10 +77,29 @@
|
||||
color: var(--grey);
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-top: 1rem;
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: .5rem;
|
||||
.name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 1rem 0 .5rem 0;
|
||||
|
||||
.name {
|
||||
font-size: 1.2rem;
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.state-dot {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 50%;
|
||||
|
||||
&.active {
|
||||
background-color: var(--success);
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
background-color: var(--warn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
|
||||
+1
@@ -121,6 +121,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
if (resp.details?.viewTimestamp) {
|
||||
this.viewTimestamp = resp.details?.viewTimestamp;
|
||||
}
|
||||
console.log(resp.resultList);
|
||||
this.dataSource.data = this.ownedProjectList;
|
||||
this.loadingSubject.next(false);
|
||||
}).catch(error => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sub {
|
||||
|
||||
@@ -24,6 +24,7 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
|
||||
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
|
||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
|
||||
|
||||
import { NameDialogModule } from '../../../modules/name-dialog/name-dialog.module';
|
||||
import {
|
||||
ProjectPrivateLabelingDialogModule,
|
||||
} from '../../../modules/project-private-labeling-dialog/project-private-labeling-dialog.module';
|
||||
@@ -43,6 +44,7 @@ import { OwnedProjectsComponent } from './owned-projects.component';
|
||||
OwnedProjectsRoutingModule,
|
||||
UserGrantsModule,
|
||||
FormsModule,
|
||||
NameDialogModule,
|
||||
ReactiveFormsModule,
|
||||
TranslateModule,
|
||||
AvatarModule,
|
||||
|
||||
Reference in New Issue
Block a user