fix(console): text color shades, ui fixes, state handle restore (#3698)

* common project grant dialog, info box, label policy

* text styles, statehandler fix

* dialog, btn alignment, i18n

* search-user theme colors

* filter formfield sizing

* redirect uris

* shortcut layout

* settings grid type rest, shortcuts linking

* login policy, reset button consistency, metadata

* permission checks
This commit is contained in:
Max Peintner
2022-05-25 09:33:18 +02:00
committed by GitHub
parent 09b021b257
commit b6deed3e34
90 changed files with 1055 additions and 883 deletions
@@ -1,7 +1,7 @@
<cnsl-top-view
title="{{ app?.name }}"
[hasActions]="isZitadel === false && (['project.app.write:' + projectId, 'project.app.write'] | hasRole | async)"
docLink="https://docs.zitadel.ch/docs/guides/basics/projects"
docLink="https://docs.zitadel.com/docs/guides/basics/projects"
[sub]="app?.oidcConfig ? ('APP.OIDC.APPTYPE.' + app?.oidcConfig?.appType | translate) : 'API'"
[isActive]="app?.state === AppState.APP_STATE_ACTIVE"
[isInactive]="app?.state === AppState.APP_STATE_INACTIVE"
@@ -30,6 +30,11 @@
}
}
.redirect-section {
display: block;
margin-bottom: 1rem;
}
.app-desc {
font-size: 14px;
margin: 0;
@@ -131,6 +136,7 @@
display: flex;
align-items: center;
border: 1px solid if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2));
color: white;
border-radius: 0.5rem;
padding: 0.5rem 1rem;
margin-bottom: 1rem;
@@ -14,18 +14,11 @@
</cnsl-auth-method-radio>
</div>
<div mat-dialog-actions class="action">
<button color="primary" mat-stroked-button class="ok-button" (click)="closeDialog()">
<button color="primary" mat-stroked-button (click)="closeDialog()">
{{ 'ACTIONS.CLOSE' | translate }}
</button>
<button
[disabled]="!authmethod"
cdkFocusInitial
color="primary"
mat-raised-button
class="ok-button"
(click)="closeDialogWithMethod()"
>
<button [disabled]="!authmethod" cdkFocusInitial color="primary" mat-raised-button (click)="closeDialogWithMethod()">
{{ 'ACTIONS.SELECT' | translate }}
</button>
</div>
@@ -13,11 +13,7 @@ h1 {
.action {
display: flex;
justify-content: flex-end;
.ok-button {
margin-left: 0.5rem;
}
justify-content: space-between;
button {
border-radius: 0.5rem;
@@ -1,27 +1,40 @@
<form class="form" (ngSubmit)="add(redInput)" [attr.data-e2e]="'redirect-uris'">
<cnsl-form-field class="formfield">
<cnsl-label>{{ title }}</cnsl-label>
<form class="redirect-uris-form" (ngSubmit)="add(redInput)" [attr.data-e2e]="'redirect-uris'">
<cnsl-form-field class="formfield">
<cnsl-label>{{ title }}</cnsl-label>
<input #redInput cnslInput placeholder="ex. https://" [formControl]="redirectControl">
</cnsl-form-field>
<button matTooltip="{{'ACTIONS.ADD' | translate}}" type="submit" mat-icon-button
[disabled]="redirectControl.invalid || !canWrite">
<mat-icon>add</mat-icon>
</button>
<input #redInput cnslInput placeholder="ex. https://" [formControl]="redirectControl" />
</cnsl-form-field>
<button
matTooltip="{{ 'ACTIONS.ADD' | translate }}"
type="submit"
mat-icon-button
[disabled]="redirectControl.invalid || !canWrite"
>
<mat-icon>add</mat-icon>
</button>
</form>
<div class="uri-list">
<div *ngFor="let uri of urisList" class="uri-line">
<span class="uri"
[ngClass]="{'green': !devMode && uri?.startsWith('https://'), 'red': !devMode && !uri?.startsWith('https://')}">{{uri}}</span>
<span class="fill-space"></span>
<i *ngIf="!devMode && !(uri | redirect : isNative)" class="las la-exclamation red" [matTooltip]="isNative ? ('APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate) : ('APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate)"></i>
<div class="redirect-uris-list">
<div *ngFor="let uri of urisList" class="uri-line" [ngClass]="{ alert: !devMode && !(uri | redirect: isNative) }">
<span
class="uri"
[ngClass]="{ green: !devMode && uri?.startsWith('https://'), red: !devMode && !uri?.startsWith('https://') }"
>{{ uri }}</span
>
<span class="fill-space"></span>
<i
class="alerticon las la-exclamation red"
[matTooltip]="
isNative ? ('APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate) : ('APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate)
"
></i>
<button matTooltip="{{'ACTIONS.DELETE' | translate}}" mat-icon-button (click)="remove(uri)" class="icon-button">
<mat-icon class="icon">cancel</mat-icon>
</button>
</div>
<button matTooltip="{{ 'ACTIONS.DELETE' | translate }}" mat-icon-button (click)="remove(uri)" class="icon-button">
<mat-icon class="icon">cancel</mat-icon>
</button>
</div>
</div>
<p *ngIf="redirectControl.value && redirectControl.invalid" class="error">
{{'APP.OIDC.REDIRECTNOTVALID' | translate}}</p>
<p *ngIf="redirectControl.value && redirectControl.invalid" class="redirect-uris-error">
{{ 'APP.OIDC.REDIRECTNOTVALID' | translate }}
</p>
@@ -1,4 +1,80 @@
.form {
@use '@angular/material' as mat;
@mixin redirect-uris-theme($theme) {
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);
$is-dark-theme: map-get($theme, is-dark);
$warn: map-get($theme, warn);
$warn-color: map-get($warn, 500);
$button-text-color: map-get($foreground, text);
$button-disabled-text-color: map-get($foreground, disabled-button);
$divider-color: map-get($foreground, dividers);
$secondary-text: map-get($foreground, secondary-text);
.redirect-uris-list {
width: 100%;
.uri-line {
display: flex;
align-items: center;
margin: 0.5rem 0;
padding: 0 0 0 0.75rem;
border-radius: 4px;
background: map-get($background, infosection);
.uri {
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 14px;
overflow-x: auto;
}
.fill-space {
flex: 1;
}
i.green {
font-size: 1rem;
line-height: 35px;
height: 30px;
}
.icon-button {
height: 30px;
line-height: 30px;
.icon {
font-size: 1rem;
margin-bottom: 3px;
}
&:not(:hover) {
color: $secondary-text;
}
}
.alerticon {
display: none;
}
&.alert {
background-color: map-get($background, alertinfosection);
color: map-get($foreground, alertinfosection);
.alerticon {
display: inline;
}
}
}
}
.redirect-uris-error {
font-size: 13px;
color: $warn-color;
margin: 0 0.5rem 1.5rem 0.5rem;
}
}
.redirect-uris-form {
display: flex;
align-items: flex-end;
min-width: 320px;
@@ -12,52 +88,3 @@
margin-right: -0.5rem;
}
}
.uri-list {
margin: 0 0.5rem;
width: 100%;
.uri-line {
display: flex;
align-items: center;
.uri {
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 14px;
overflow-x: auto;
}
.fill-space {
flex: 1;
}
i.green {
font-size: 1rem;
line-height: 35px;
height: 30px;
}
i.red {
font-size: 1.2rem;
}
.icon-button {
height: 30px;
line-height: 30px;
.icon {
font-size: 1rem !important;
}
&:not(:hover) {
color: var(--grey);
}
}
}
}
.error {
font-size: 13px;
color: var(--warn);
margin: 0 0.5rem 1.5rem 0.5rem;
}
@@ -11,7 +11,7 @@ import { ManagementService } from 'src/app/services/mgmt.service';
})
export class ApplicationGridComponent implements OnInit {
@Input() public projectId: string = '';
@Input() public disabled: boolean = false;
@Input() public disabled: boolean = true;
@Output() public changeView: EventEmitter<void> = new EventEmitter();
public appsSubject: BehaviorSubject<App.AsObject[]> = new BehaviorSubject<App.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
@@ -6,7 +6,7 @@
[isInactive]="project?.state === ProjectState.PROJECT_STATE_INACTIVE"
[hasContributors]="true"
stateTooltip="{{ 'PROJECT.STATE.' + project?.state | translate }}"
[hasActions]="true"
[hasActions]="['project.app.write:' + project?.id, 'project.app.write$'] | hasRole | async"
>
<ng-template topActions cnslHasRole [hasRole]="['project.write:' + projectId, 'project.write']">
<button mat-menu-item (click)="openNameDialog()" aria-label="Edit project name" *ngIf="isZitadel === false">
@@ -68,8 +68,13 @@
<cnsl-meta-layout>
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList" [queryParam]="'id'">
<ng-container *ngIf="currentSetting === 'general' && project">
<ng-template cnslHasRole [hasRole]="['project.app.read:' + project.id, 'project.app.read']">
<cnsl-application-grid *ngIf="grid" [disabled]="isZitadel" (changeView)="grid = false" [projectId]="projectId">
<ng-template cnslHasRole [hasRole]="['project.app.read:' + project.id, 'project.app.read$']">
<cnsl-application-grid
*ngIf="grid"
[disabled]="isZitadel || (['project.app.write:' + project.id, 'project.app.write$'] | hasRole | async) === false"
(changeView)="grid = false"
[projectId]="projectId"
>
</cnsl-application-grid>
<cnsl-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}">
<div class="card-actions" card-actions>
@@ -77,7 +82,12 @@
<i matTooltip="show grid view" class="las la-th-large"></i>
</button>
</div>
<cnsl-applications [disabled]="isZitadel" [projectId]="projectId"></cnsl-applications>
<cnsl-applications
[disabled]="
isZitadel || (['project.app.write:' + project.id, 'project.app.write$'] | hasRole | async) === false
"
[projectId]="projectId"
></cnsl-applications>
</cnsl-card>
</ng-template>
@@ -111,7 +121,9 @@
>
{{ 'PROJECT.ROLE.ASSERTION' | translate }}</mat-checkbox
>
<p class="desc cnsl-secondary-text">{{ 'PROJECT.ROLE.ASSERTION_DESCRIPTION' | translate }}</p>
<cnsl-info-section class="desc cnsl-secondary-text">{{
'PROJECT.ROLE.ASSERTION_DESCRIPTION' | translate
}}</cnsl-info-section>
<mat-checkbox
[(ngModel)]="project.projectRoleCheck"
[disabled]="(['project.write$', 'project.write:' + project.id] | hasRole | async) === false"
@@ -119,7 +131,9 @@
>
{{ 'PROJECT.ROLE.CHECK' | translate }}</mat-checkbox
>
<p class="desc cnsl-secondary-text">{{ 'PROJECT.ROLE.CHECK_DESCRIPTION' | translate }}</p>
<cnsl-info-section class="desc cnsl-secondary-text">{{
'PROJECT.ROLE.CHECK_DESCRIPTION' | translate
}}</cnsl-info-section>
<mat-checkbox
[(ngModel)]="project.hasProjectCheck"
[disabled]="(['project.write$', 'project.write:' + project.id] | hasRole | async) === false"
@@ -127,10 +141,19 @@
>
{{ 'PROJECT.HAS_PROJECT' | translate }}</mat-checkbox
>
<p class="desc cnsl-secondary-text">{{ 'PROJECT.HAS_PROJECT_DESCRIPTION' | translate }}</p>
<cnsl-info-section class="desc cnsl-secondary-text">{{
'PROJECT.HAS_PROJECT_DESCRIPTION' | translate
}}</cnsl-info-section>
<div class="project-detail-btn-container">
<button mat-raised-button color="primary" (click)="saveProject()">{{ 'ACTIONS.SAVE' | translate }}</button>
<button
mat-raised-button
[disabled]="(['project.app.write:' + project.id, 'project.app.write$'] | hasRole | async) === false"
color="primary"
(click)="saveProject()"
>
{{ 'ACTIONS.SAVE' | translate }}
</button>
</div>
</cnsl-card>
</ng-template>
@@ -55,5 +55,5 @@
.desc {
font-size: 14px;
margin-bottom: 2rem;
margin-bottom: 1rem;
}
@@ -19,6 +19,7 @@ import { CardModule } from 'src/app/modules/card/card.module';
import { ChangesModule } from 'src/app/modules/changes/changes.module';
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
import { InfoRowModule } from 'src/app/modules/info-row/info-row.module';
import { InfoSectionModule } from 'src/app/modules/info-section/info-section.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
import { PaginatorModule } from 'src/app/modules/paginator/paginator.module';
@@ -71,6 +72,7 @@ import { OwnedProjectDetailComponent } from './owned-project-detail.component';
TopViewModule,
MatCheckboxModule,
MatSelectModule,
InfoSectionModule,
MatMenuModule,
MatProgressSpinnerModule,
ChangesModule,
@@ -1,65 +1,104 @@
<cnsl-detail-layout [hasBackButton]="true" title="{{ 'PROJECT.GRANT.DETAIL.TITLE' | translate }}">
<p class="subinfo" sub>
<span class="cnsl-secondary-text">{{ 'PROJECT.GRANT.DETAIL.DESC' | translate }}</span>
<a mat-icon-button href="https://docs.zitadel.com/docs/concepts/structure/projects#granted-organizations"
target="_blank">
<a
mat-icon-button
href="https://docs.zitadel.com/docs/concepts/structure/projects#granted-organizations"
target="_blank"
>
<i class="las la-info-circle"></i>
</a>
</p>
<div actions>
<button class="actions-trigger-desk" mat-raised-button color="primary" [matMenuTriggerFor]="actions">
<span>{{'ACTIONS.ACTIONS' | translate}}</span>
<span>{{ 'ACTIONS.ACTIONS' | translate }}</span>
<mat-icon class="icon">keyboard_arrow_down</mat-icon>
</button>
<button class="actions-trigger-mob" matTooltip="{{'ACTIONS.ACTIONS' | translate}}" mat-icon-button
[matMenuTriggerFor]="actions">
<button
class="actions-trigger-mob"
matTooltip="{{ 'ACTIONS.ACTIONS' | translate }}"
mat-icon-button
[matMenuTriggerFor]="actions"
>
<i class="las la-ellipsis-v"></i>
</button>
</div>
<mat-menu #actions="matMenu" xPosition="before">
<button mat-menu-item *ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
(click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' |
translate}}</button>
<button mat-menu-item *ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
(click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' |
translate}}</button>
<button
mat-menu-item
*ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
(click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE)"
>
{{ 'USER.PAGES.DEACTIVATE' | translate }}
</button>
<button
mat-menu-item
*ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
(click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE)"
>
{{ 'USER.PAGES.REACTIVATE' | translate }}
</button>
</mat-menu>
<div class="master-row">
<div>
</div>
<div></div>
</div>
<cnsl-project-grant-illustration *ngIf="grant" [grantedProject]="grant" [projectRoleOptions]="projectRoleOptions"
(roleRemoved)="removeRole($event)" (editRoleClicked)="editRoles()">
<cnsl-project-grant-illustration
*ngIf="grant && projectRoleOptions"
[grantedProject]="grant"
[projectRoleOptions]="projectRoleOptions"
(roleRemoved)="removeRole($event)"
(editRoleClicked)="editRoles()"
>
</cnsl-project-grant-illustration>
<h2 class="project-grant-h2">{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}</h2>
<p class="desc max-width-description">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p>
<p class="desc cnsl-secondary-text max-width-description">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p>
<cnsl-members-table *ngIf="grant" [dataSource]="dataSource"
[canWrite]="['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async"
[canDelete]="['project.grant.member.delete','project.grant.member.delete:' + grant.grantId] | hasRole | async"
[memberRoleOptions]="memberRoleOptions" (updateRoles)="updateMemberRoles($event.member, $event.change)"
(deleteMember)="removeProjectMember($event)" [factoryLoadFunc]="changePageFactory"
(changedSelection)="selection = $event" [refreshTrigger]="changePage">
<button class="cnsl-action-button" selectactions (click)="removeProjectMemberSelection()"
[disabled]="(['project.grant.member.delete','project.grant.member.delete:' + grant.grantId] | hasRole | async) === false"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" color="warn" mat-raised-button>
<cnsl-members-table
*ngIf="grant"
[dataSource]="dataSource"
[canWrite]="['project.grant.member.write', 'project.grant.member.write:' + grant.grantId] | hasRole | async"
[canDelete]="['project.grant.member.delete', 'project.grant.member.delete:' + grant.grantId] | hasRole | async"
[memberRoleOptions]="memberRoleOptions"
(updateRoles)="updateMemberRoles($event.member, $event.change)"
(deleteMember)="removeProjectMember($event)"
[factoryLoadFunc]="changePageFactory"
(changedSelection)="selection = $event"
[refreshTrigger]="changePage"
>
<button
class="cnsl-action-button"
selectactions
(click)="removeProjectMemberSelection()"
[disabled]="
(['project.grant.member.delete', 'project.grant.member.delete:' + grant.grantId] | hasRole | async) === false
"
matTooltip="{{ 'ORG_DETAIL.TABLE.DELETE' | translate }}"
color="warn"
mat-raised-button
>
<i class="las la-trash"></i>
<span>{{'ACTIONS.SELECTIONDELETE' | translate}}</span>
<span>{{ 'ACTIONS.SELECTIONDELETE' | translate }}</span>
<cnsl-action-keys [type]="ActionKeysType.DELETE" (actionTriggered)="removeProjectMemberSelection()">
</cnsl-action-keys>
</button>
<button class="cnsl-action-button" writeactions color="primary"
[disabled]="(['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async) === false"
(click)="openAddMember()" color="primary" mat-raised-button>
<button
class="cnsl-action-button"
writeactions
color="primary"
[disabled]="
(['project.grant.member.write', 'project.grant.member.write:' + grant.grantId] | hasRole | async) === false
"
(click)="openAddMember()"
color="primary"
mat-raised-button
>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
<cnsl-action-keys (actionTriggered)="openAddMember()">
</cnsl-action-keys>
<cnsl-action-keys (actionTriggered)="openAddMember()"> </cnsl-action-keys>
</button>
</cnsl-members-table>
</cnsl-detail-layout>
</cnsl-detail-layout>
@@ -3,17 +3,15 @@ import { MatDialog } from '@angular/material/dialog';
import { PageEvent } from '@angular/material/paginator';
import { ActivatedRoute } from '@angular/router';
import { ActionKeysType } from 'src/app/modules/action-keys/action-keys.component';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { UserGrantRoleDialogComponent } from 'src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component';
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { GrantedProject, ProjectGrantState, Role } from 'src/app/proto/generated/zitadel/project_pb';
import { User } from 'src/app/proto/generated/zitadel/user_pb';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
import {
ProjectGrantMembersCreateDialogComponent,
ProjectGrantMembersCreateDialogExportType,
} from './project-grant-members-create-dialog/project-grant-members-create-dialog.component';
import { ProjectGrantMembersDataSource } from './project-grant-members-datasource';
@Component({
@@ -69,25 +67,29 @@ export class ProjectGrantDetailComponent {
);
};
this.mgmtService.getProjectGrantByID(this.grantid, this.projectid).then((resp) => {
if (resp.projectGrant) {
this.grant = resp.projectGrant;
this.mgmtService
.getProjectGrantByID(this.grantid, this.projectid)
.then((resp) => {
console.log(resp);
if (resp.projectGrant) {
this.grant = resp.projectGrant;
const breadcrumbs = [
new Breadcrumb({
type: BreadcrumbType.ORG,
routerLink: ['/org'],
}),
new Breadcrumb({
type: BreadcrumbType.PROJECT,
name: '',
param: { key: 'projectid', value: resp.projectGrant.projectId },
routerLink: ['/projects', resp.projectGrant.projectId],
}),
];
this.breadcrumbService.setBreadcrumb(breadcrumbs);
}
});
const breadcrumbs = [
new Breadcrumb({
type: BreadcrumbType.ORG,
routerLink: ['/org'],
}),
new Breadcrumb({
type: BreadcrumbType.PROJECT,
name: '',
param: { key: 'projectid', value: resp.projectGrant.projectId },
routerLink: ['/projects', resp.projectGrant.projectId],
}),
];
this.breadcrumbService.setBreadcrumb(breadcrumbs);
}
})
.catch(this.toast.showError);
});
}
@@ -176,36 +178,35 @@ export class ProjectGrantDetailComponent {
}
public async openAddMember(): Promise<any> {
const keysList = await this.mgmtService.listProjectGrantMemberRoles();
const dialogRef = this.dialog.open(ProjectGrantMembersCreateDialogComponent, {
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
data: {
roleKeysList: keysList.resultList,
creationType: CreationType.PROJECT_GRANTED,
},
width: '400px',
});
dialogRef.afterClosed().subscribe((dataToAdd: ProjectGrantMembersCreateDialogExportType) => {
if (dataToAdd) {
Promise.all(
dataToAdd.userIds.map((userid: string) => {
return this.mgmtService.addProjectGrantMember(
this.grant.projectId,
this.grant.grantId,
userid,
dataToAdd.rolesKeyList,
);
}),
)
.then(() => {
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERADDED', true);
setTimeout(() => {
this.changePage.emit();
}, 3000);
})
.catch((error) => {
this.toast.showError(error);
});
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
const users: User.AsObject[] = resp.users;
const roles: string[] = resp.roles;
if (users && users.length && roles && roles.length) {
const userIds = users.map((user) => user.id);
Promise.all(
userIds.map((userid: string) => {
return this.mgmtService.addProjectGrantMember(this.grant.projectId, this.grant.grantId, userid, resp.roles);
}),
)
.then(() => {
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERADDED', true);
setTimeout(() => {
this.changePage.emit();
}, 3000);
})
.catch((error) => {
this.toast.showError(error);
});
}
}
});
}
@@ -15,6 +15,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { ActionKeysModule } from 'src/app/modules/action-keys/action-keys.module';
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { MembersTableModule } from 'src/app/modules/members-table/members-table.module';
@@ -25,16 +26,12 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
import { ProjectGrantDetailRoutingModule } from './project-grant-detail-routing.module';
import { ProjectGrantDetailComponent } from './project-grant-detail.component';
import { ProjectGrantIllustrationComponent } from './project-grant-illustration/project-grant-illustration.component';
import {
ProjectGrantMembersCreateDialogModule,
} from './project-grant-members-create-dialog/project-grant-members-create-dialog.module';
@NgModule({
declarations: [ProjectGrantDetailComponent, ProjectGrantIllustrationComponent],
imports: [
CommonModule,
ProjectGrantDetailRoutingModule,
ProjectGrantMembersCreateDialogModule,
MatAutocompleteModule,
HasRoleModule,
MatChipsModule,
@@ -54,6 +51,7 @@ import {
TranslateModule,
MatSelectModule,
DetailLayoutModule,
MemberCreateDialogModule,
HasRolePipeModule,
MembersTableModule,
MatDialogModule,
@@ -13,7 +13,6 @@ export class ProjectGrantIllustrationComponent {
@Output() public editRoleClicked: EventEmitter<void> = new EventEmitter();
ProjectGrantState: any = ProjectGrantState;
constructor() {}
public removeRole(roleKey: string): void {
this.roleRemoved.emit(roleKey);
@@ -1,27 +0,0 @@
<h1 mat-dialog-title>
<span class="title">{{'MEMBER.ADD' | translate}}</span>
</h1>
<p class="desc"> {{'ORG_DETAIL.MEMBER.ADDDESCRIPTION' | translate}}</p>
<div mat-dialog-content>
<cnsl-search-user-autocomplete (selectionChanged)="selectUsers($event)">
</cnsl-search-user-autocomplete>
<cnsl-form-field class="full-width" appearance="outline">
<cnsl-label>{{ 'PROJECT.MEMBER.ROLES' | translate }}</cnsl-label>
<mat-select [(ngModel)]="roleKeyList" multiple>
<mat-option *ngFor="let key of data.roleKeysList" [value]="key">
{{ key }}
</mat-option>
</mat-select>
</cnsl-form-field>
</div>
<div mat-dialog-actions class="action">
<button mat-stroked-button (click)="closeDialog()">
{{'ACTIONS.CANCEL' | translate}}
</button>
<button [disabled]="userIds.length === 0 || roleKeyList.length === 0" color="primary" mat-raised-button
class="ok-button" (click)="closeDialogWithSuccess()">
{{'ACTIONS.ADD' | translate}}
</button>
</div>
@@ -1,12 +0,0 @@
.full-width {
width: 100%;
}
.action {
display: flex;
justify-content: flex-end;
.ok-button {
margin-left: .5rem;
}
}
@@ -1,25 +0,0 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ProjectGrantMembersCreateDialogComponent } from './project-grant-members-create-dialog.component';
describe('ProjectGrantMembersCreateDialogComponent', () => {
let component: ProjectGrantMembersCreateDialogComponent;
let fixture: ComponentFixture<ProjectGrantMembersCreateDialogComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ProjectGrantMembersCreateDialogComponent],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProjectGrantMembersCreateDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,42 +0,0 @@
import { Component, Inject } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { User } from 'src/app/proto/generated/zitadel/user_pb';
export interface ProjectGrantMembersCreateDialogExportType {
userIds: string[];
rolesKeyList: string[];
}
@Component({
selector: 'cnsl-project-grant-members-create-dialog',
templateUrl: './project-grant-members-create-dialog.component.html',
styleUrls: ['./project-grant-members-create-dialog.component.scss'],
})
export class ProjectGrantMembersCreateDialogComponent {
public form!: FormGroup;
public userIds: string[] = [];
public roleKeyList: string[] = [];
constructor(
public dialogRef: MatDialogRef<ProjectGrantMembersCreateDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) { }
public selectUsers(users: User.AsObject | User.AsObject[]): void {
if (Array.isArray(users)) {
this.userIds = users.map(user => user.id);
}
}
public closeDialog(): void {
this.dialogRef.close(false);
}
public closeDialogWithSuccess(): void {
const exportData: ProjectGrantMembersCreateDialogExportType = {
userIds: this.userIds,
rolesKeyList: this.roleKeyList,
};
this.dialogRef.close(exportData);
}
}
@@ -1,27 +0,0 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { SearchUserAutocompleteModule } from 'src/app/modules/search-user-autocomplete/search-user-autocomplete.module';
import { ProjectGrantMembersCreateDialogComponent } from './project-grant-members-create-dialog.component';
@NgModule({
declarations: [ProjectGrantMembersCreateDialogComponent],
imports: [
CommonModule,
FormsModule,
MatDialogModule,
MatButtonModule,
TranslateModule,
MatSelectModule,
InputModule,
SearchUserAutocompleteModule,
],
})
export class ProjectGrantMembersCreateDialogModule { }
@@ -1,4 +1,9 @@
<cnsl-create-layout title="{{ 'PROJECT.PAGES.CREATE' | translate }}" [createSteps]="1" [currentCreateStep]="1">
<cnsl-create-layout
title="{{ 'PROJECT.PAGES.CREATE' | translate }}"
[createSteps]="1"
[currentCreateStep]="1"
(closed)="close()"
>
<h1>{{ 'PROJECT.PAGES.CREATE_DESC' | translate }}</h1>
<form cdkFocusRegionStart (ngSubmit)="saveProject()">
<div class="column">
@@ -14,7 +14,7 @@
}
.sub {
margin-bottom: 2rem;
margin-bottom: 1.5rem;
font-size: 14px;
}
@@ -38,7 +38,7 @@
opacity: 0.6;
font-size: 15px;
cursor: pointer;
color: map-get($foreground, base);
color: map-get($foreground, text);
&:first-child {
margin-right: 1rem;