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

View File

@@ -75,7 +75,7 @@ const routes: Routes = [
loadChildren: () => import('./pages/actions/actions.module').then((m) => m.ActionsModule), loadChildren: () => import('./pages/actions/actions.module').then((m) => m.ActionsModule),
canActivate: [AuthGuard, RoleGuard], canActivate: [AuthGuard, RoleGuard],
data: { data: {
roles: ['org.read'], roles: ['org.action.read', 'org.flow.read'],
}, },
}, },
{ {

View File

@@ -11,8 +11,6 @@
$warn-color: mat.get-color-from-palette($warn, 500); $warn-color: mat.get-color-from-palette($warn, 500);
$accent-color: mat.get-color-from-palette($accent, 500); $accent-color: mat.get-color-from-palette($accent, 500);
$is-dark-theme: map-get($theme, is-dark); $is-dark-theme: map-get($theme, is-dark);
$back: map-get($background, background);
$base: map-get($foreground, base);
.main-container { .main-container {
display: flex; display: flex;

View File

@@ -1,7 +1,7 @@
import { BreakpointObserver } from '@angular/cdk/layout'; import { BreakpointObserver } from '@angular/cdk/layout';
import { OverlayContainer } from '@angular/cdk/overlay'; import { OverlayContainer } from '@angular/cdk/overlay';
import { DOCUMENT, ViewportScroller } from '@angular/common'; import { DOCUMENT, ViewportScroller } from '@angular/common';
import { Component, HostBinding, HostListener, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Component, HostBinding, HostListener, Inject, OnDestroy, ViewChild } from '@angular/core';
import { MatIconRegistry } from '@angular/material/icon'; import { MatIconRegistry } from '@angular/material/icon';
import { MatDrawer } from '@angular/material/sidenav'; import { MatDrawer } from '@angular/material/sidenav';
import { DomSanitizer } from '@angular/platform-browser'; import { DomSanitizer } from '@angular/platform-browser';
@@ -19,8 +19,7 @@ import { KeyboardShortcutsService } from './services/keyboard-shortcuts/keyboard
import { ManagementService } from './services/mgmt.service'; import { ManagementService } from './services/mgmt.service';
import { NavigationService } from './services/navigation.service'; import { NavigationService } from './services/navigation.service';
import { OverlayWorkflowService } from './services/overlay/overlay-workflow.service'; import { OverlayWorkflowService } from './services/overlay/overlay-workflow.service';
import { IntroWorkflowOverlays } from './services/overlay/workflows'; import { StorageService } from './services/storage.service';
import { StorageLocation, StorageService } from './services/storage.service';
import { ThemeService } from './services/theme.service'; import { ThemeService } from './services/theme.service';
import { UpdateService } from './services/update.service'; import { UpdateService } from './services/update.service';
@@ -30,7 +29,7 @@ import { UpdateService } from './services/update.service';
styleUrls: ['./app.component.scss'], styleUrls: ['./app.component.scss'],
animations: [toolbarAnimation, ...navAnimations, accountCard, routeAnimations, adminLineAnimation], animations: [toolbarAnimation, ...navAnimations, accountCard, routeAnimations, adminLineAnimation],
}) })
export class AppComponent implements OnInit, OnDestroy { export class AppComponent implements OnDestroy {
@ViewChild('drawer') public drawer!: MatDrawer; @ViewChild('drawer') public drawer!: MatDrawer;
public isHandset$: Observable<boolean> = this.breakpointObserver.observe('(max-width: 599px)').pipe( public isHandset$: Observable<boolean> = this.breakpointObserver.observe('(max-width: 599px)').pipe(
map((result) => { map((result) => {
@@ -218,7 +217,10 @@ export class AppComponent implements OnInit, OnDestroy {
.then((org) => { .then((org) => {
this.org = org; this.org = org;
this.startIntroWorkflow(); this.loadPrivateLabelling();
// TODO add when console storage is implemented
// this.startIntroWorkflow();
}) })
.catch((error) => { .catch((error) => {
this.router.navigate(['/users/me']); this.router.navigate(['/users/me']);
@@ -235,21 +237,19 @@ export class AppComponent implements OnInit, OnDestroy {
}); });
} }
private startIntroWorkflow(): void { // TODO implement Console storage
setTimeout(() => {
const cb = () => {
this.storageService.setItem('intro-dismissed', true, StorageLocation.local);
};
const dismissed = this.storageService.getItem('intro-dismissed', StorageLocation.local);
if (!dismissed) {
this.workflowService.startWorkflow(IntroWorkflowOverlays, cb);
}
}, 1000);
}
public ngOnInit(): void { // private startIntroWorkflow(): void {
this.loadPrivateLabelling(); // setTimeout(() => {
} // const cb = () => {
// this.storageService.setItem('intro-dismissed', true, StorageLocation.local);
// };
// const dismissed = this.storageService.getItem('intro-dismissed', StorageLocation.local);
// if (!dismissed) {
// this.workflowService.startWorkflow(IntroWorkflowOverlays, cb);
// }
// }, 1000);
// }
public ngOnDestroy(): void { public ngOnDestroy(): void {
this.destroy$.next(); this.destroy$.next();

View File

@@ -63,6 +63,11 @@
font-size: 15px; font-size: 15px;
} }
.filter-select-method .mat-select {
height: 36px;
padding: 7px 10px;
}
.subquery { .subquery {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@@ -82,6 +87,8 @@
} }
.filter-input-value { .filter-input-value {
flex: 1;
input { input {
height: 36px; height: 36px;
font-size: 15px; font-size: 15px;

View File

@@ -96,7 +96,7 @@
margin: 0.5rem 0; margin: 0.5rem 0;
a { a {
color: map-get($foreground, base); color: map-get($foreground, text);
text-decoration: none; text-decoration: none;
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -57,7 +57,7 @@
.org-link { .org-link {
font-size: 14px; font-size: 14px;
color: map-get($foreground, base); color: map-get($foreground, text);
text-decoration: none; text-decoration: none;
padding: 0.5rem 0.75rem 0.5rem 0.75rem; padding: 0.5rem 0.75rem 0.5rem 0.75rem;
height: 2.5rem; height: 2.5rem;
@@ -90,7 +90,7 @@
.org-switch-button { .org-switch-button {
font-weight: bold; font-weight: bold;
border: none; border: none;
color: map-get($foreground, base); color: map-get($foreground, text);
border-radius: 6px; border-radius: 6px;
position: relative; position: relative;
font-size: 14px; font-size: 14px;
@@ -118,7 +118,7 @@
svg { svg {
opacity: 0.7; opacity: 0.7;
fill: map-get($foreground, base); fill: map-get($foreground, text);
height: 1.25rem; height: 1.25rem;
width: 1.25rem; width: 1.25rem;
} }
@@ -135,7 +135,7 @@
.breadcrumb-link { .breadcrumb-link {
font-size: 14px; font-size: 14px;
color: map-get($foreground, base); color: map-get($foreground, text);
text-decoration: none; text-decoration: none;
padding: 0.5rem 0.75rem 0.5rem 0.75rem; padding: 0.5rem 0.75rem 0.5rem 0.75rem;
height: 2.5rem; height: 2.5rem;

View File

@@ -44,7 +44,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
color: map-get($foreground, base); color: map-get($foreground, text);
&.top-left { &.top-left {
&::before { &::before {

View File

@@ -14,6 +14,7 @@
padding-right: 1rem; padding-right: 1rem;
font-size: 14px; font-size: 14px;
margin: 0.5rem 0; margin: 0.5rem 0;
box-shadow: 0 2px 5px rgb(0 0 0 / 10%);
&.fit { &.fit {
width: fit-content; width: fit-content;

View File

@@ -9,12 +9,11 @@
</cnsl-form-field> </cnsl-form-field>
</div> </div>
<div mat-dialog-actions class="action"> <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 }} {{ 'ACTIONS.CLOSE' | translate }}
</button> </button>
<button [disabled]="!name" cdkFocusInitial color="primary" mat-raised-button class="ok-button" <button [disabled]="!name" cdkFocusInitial color="primary" mat-raised-button (click)="closeDialog(name)">
(click)="closeDialog(name)">
{{ 'ACTIONS.RENAME' | translate }} {{ 'ACTIONS.RENAME' | translate }}
</button> </button>
</div> </div>

View File

@@ -15,10 +15,6 @@ h1 {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
.ok-button {
margin-left: 0.5rem;
}
button { button {
border-radius: 0.5rem; border-radius: 0.5rem;
} }

View File

@@ -1,25 +1,29 @@
<div class="paginator-wrapper"> <div class="paginator-wrapper">
<div class="col"> <div class="col">
<p class="length"><span>{{length}} </span>{{'PAGINATOR.COUNT' | translate}}</p> <p class="length">
<span>{{ length }} </span>{{ 'PAGINATOR.COUNT' | translate }}
</p>
<p class="ts cnsl-secondary-text" *ngIf="timestamp"> <p class="ts cnsl-secondary-text" *ngIf="timestamp">
{{(timestamp | timestampToDate)| localizedDate: 'EEEE dd. MMM YYYY, HH:mm'}} {{ timestamp | timestampToDate | localizedDate: 'EEEE dd. MMM YYYY, HH:mm' }}
</p> </p>
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
<span class="pos cnsl-secondary-text" *ngIf="!hidePagination">{{pageIndex * pageSize}} - {{pageIndex * pageSize + <span class="pos cnsl-secondary-text" *ngIf="!hidePagination"
pageSize}} </span> >{{ pageIndex * pageSize }} - {{ pageIndex * pageSize + pageSize }}
</span>
<div class="row" *ngIf="!hidePagination"> <div class="row" *ngIf="!hidePagination">
<cnsl-form-field class="size" appearance="outline"> <cnsl-form-field class="size" appearance="outline">
<mat-select [(ngModel)]="pageSize" (selectionChange)="emitChange()"> <mat-select class="paginator-select" [(ngModel)]="pageSize" (selectionChange)="emitChange()">
<mat-option *ngFor="let sizeOption of pageSizeOptions" [value]="sizeOption"> <mat-option *ngFor="let sizeOption of pageSizeOptions" [value]="sizeOption">
{{ sizeOption }} {{ sizeOption }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</cnsl-form-field> </cnsl-form-field>
<button *ngIf="previousPossible" (click)="previous()" [disabled]="!previousPossible" <button *ngIf="previousPossible" (click)="previous()" [disabled]="!previousPossible" mat-stroked-button>
mat-stroked-button>{{'PAGINATOR.PREVIOUS' | {{ 'PAGINATOR.PREVIOUS' | translate }}
translate}}</button> </button>
<button *ngIf="nextPossible" (click)="next()" [disabled]="!nextPossible" mat-stroked-button>{{'PAGINATOR.NEXT' | <button *ngIf="nextPossible" (click)="next()" [disabled]="!nextPossible" mat-stroked-button>
translate}}</button> {{ 'PAGINATOR.NEXT' | translate }}
</button>
</div> </div>
</div> </div>

View File

@@ -50,7 +50,7 @@
} }
/* stylelint-disable */ /* stylelint-disable */
::ng-deep .mat-select { ::ng-deep .paginator-select.mat-select {
min-width: 60px; min-width: 60px;
height: 36px !important; height: 36px !important;
padding-top: 8px !important; padding-top: 8px !important;

View File

@@ -1,4 +1,4 @@
<h1 mat-dialog-title class="title"> <h1 class="title">
<span>{{ data.title | translate }}</span> <span>{{ data.title | translate }}</span>
</h1> </h1>
<div mat-dialog-content> <div mat-dialog-content>

View File

@@ -1,9 +1,10 @@
.title { .title {
font-size: 1.5rem; font-size: 1.3rem;
} }
.desc { .desc {
font-size: 14px; font-size: 14px;
margin-top: 0;
} }
.form-field { .form-field {

View File

@@ -19,6 +19,7 @@ export class DialogAddTypeComponent {
constructor(public dialogRef: MatDialogRef<DialogAddTypeComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(public dialogRef: MatDialogRef<DialogAddTypeComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {
this.availableMfaTypes = data.types; this.availableMfaTypes = data.types;
this.newMfaType = data.types && data.types[0] ? data.types[0] : undefined;
} }
public closeDialog(): void { public closeDialog(): void {

View File

@@ -22,7 +22,7 @@
<button <button
mat-stroked-button mat-stroked-button
class="new-mfa cnsl-action-button" class="new-mfa cnsl-action-button"
[disabled]="disabled" [disabled]="disabled || availableSelection.length === 0"
(click)="!disabled ? addMfa() : null" (click)="!disabled ? addMfa() : null"
> >
<mat-icon class="icon">add</mat-icon> <mat-icon class="icon">add</mat-icon>

View File

@@ -35,7 +35,7 @@
display: block; display: block;
&:not(:hover) { &:not(:hover) {
color: var(--grey); color: map-get($foreground, secondary-text);
} }
} }
} }

View File

@@ -105,27 +105,12 @@ export class FactorTableComponent implements OnInit {
} }
public addMfa(): void { public addMfa(): void {
let selection: any[] = [];
if (this.componentType === LoginMethodComponentType.MultiFactor) {
selection = [MultiFactorType.MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION];
} else if (this.componentType === LoginMethodComponentType.SecondFactor) {
selection = [SecondFactorType.SECOND_FACTOR_TYPE_U2F, SecondFactorType.SECOND_FACTOR_TYPE_OTP];
}
this.mfas.forEach((mfa) => {
const index = selection.findIndex((sel) => sel === mfa);
if (index > -1) {
selection.splice(index, 1);
}
});
const dialogRef = this.dialog.open(DialogAddTypeComponent, { const dialogRef = this.dialog.open(DialogAddTypeComponent, {
data: { data: {
title: 'MFA.CREATE.TITLE', title: 'MFA.CREATE.TITLE',
desc: 'MFA.CREATE.DESCRIPTION', desc: 'MFA.CREATE.DESCRIPTION',
componentType: this.componentType, componentType: this.componentType,
types: selection, types: this.availableSelection,
}, },
width: '400px', width: '400px',
}); });
@@ -244,4 +229,17 @@ export class FactorTableComponent implements OnInit {
this.getData(); this.getData();
}, to); }, to);
} }
public get availableSelection(): Array<MultiFactorType | SecondFactorType> {
const allTypes: MultiFactorType[] | SecondFactorType[] =
this.componentType === LoginMethodComponentType.MultiFactor
? [MultiFactorType.MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION]
: this.componentType === LoginMethodComponentType.SecondFactor
? [SecondFactorType.SECOND_FACTOR_TYPE_U2F, SecondFactorType.SECOND_FACTOR_TYPE_OTP]
: [];
const filtered = (allTypes as Array<MultiFactorType | SecondFactorType>).filter((type) => !this.mfas.includes(type));
return filtered;
}
} }

View File

@@ -122,7 +122,6 @@ export class LoginPolicyComponent implements OnInit {
) )
.pipe(take(1)) .pipe(take(1))
.subscribe((allowed) => { .subscribe((allowed) => {
console.log(allowed);
if (allowed) { if (allowed) {
this.lifetimeForm.enable(); this.lifetimeForm.enable();
} }

View File

@@ -32,7 +32,7 @@
</cnsl-form-field> </cnsl-form-field>
</div> </div>
<div class="content"> <div class="message-text-content">
<cnsl-edit-text <cnsl-edit-text
[chips]="chips[currentType]" [chips]="chips[currentType]"
[disabled]="(canWrite$ | async) === false" [disabled]="(canWrite$ | async) === false"
@@ -43,7 +43,7 @@
></cnsl-edit-text> ></cnsl-edit-text>
</div> </div>
<div class="actions"> <div class="message-text-actions">
<button <button
class="reset-button" class="reset-button"
*ngIf="(getCustomInitMessageTextMap$ | async) && (getCustomInitMessageTextMap$ | async)?.isDefault === false" *ngIf="(getCustomInitMessageTextMap$ | async) && (getCustomInitMessageTextMap$ | async)?.isDefault === false"

View File

@@ -40,11 +40,11 @@
display: block; display: block;
} }
.content { .message-text-content {
padding-top: 1rem; padding-top: 1rem;
} }
.actions { .message-text-actions {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
margin-top: 1rem; margin-top: 1rem;

View File

@@ -1,11 +1,19 @@
<!-- <cnsl-card
title="{{ 'POLICY.PWD_LOCKOUT.TITLE' | translate }}"
description="{{ 'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate }}"
> -->
<h2>{{ 'POLICY.PWD_LOCKOUT.TITLE' | translate }}</h2> <h2>{{ 'POLICY.PWD_LOCKOUT.TITLE' | translate }}</h2>
<p class="cnsl-secondary-text">{{ 'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate }}</p> <p class="cnsl-secondary-text">{{ 'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate }}</p>
<cnsl-info-section class="default" *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section> <ng-template cnslHasRole [hasRole]="['policy.delete']">
<button
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
matTooltip="{{ 'POLICY.RESET' | translate }}"
color="warn"
(click)="resetPolicy()"
mat-stroked-button
>
{{ 'POLICY.RESET' | translate }}
</button>
</ng-template>
<!-- <cnsl-info-section class="default" *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section> -->
<cnsl-card *ngIf="lockoutData"> <cnsl-card *ngIf="lockoutData">
<div class="lockout-content"> <div class="lockout-content">
@@ -38,17 +46,4 @@
> >
{{ 'ACTIONS.SAVE' | translate }} {{ 'ACTIONS.SAVE' | translate }}
</button> </button>
<ng-template cnslHasRole [hasRole]="['policy.delete']">
<button
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
matTooltip="{{ 'POLICY.RESET' | translate }}"
color="warn"
(click)="resetPolicy()"
mat-stroked-button
>
{{ 'POLICY.RESET' | translate }}
</button>
</ng-template>
</div> </div>
<!-- </cnsl-card> -->

View File

@@ -1,7 +1,21 @@
<h2>{{ 'POLICY.PRIVACY_POLICY.TITLE' | translate }}</h2> <h2>{{ 'POLICY.PRIVACY_POLICY.TITLE' | translate }}</h2>
<p class="cnsl-secondary-text">{{ 'POLICY.PRIVACY_POLICY.DESCRIPTION' | translate }}</p> <p class="cnsl-secondary-text">{{ 'POLICY.PRIVACY_POLICY.DESCRIPTION' | translate }}</p>
<cnsl-info-section *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section> <ng-container *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault">
<ng-template cnslHasRole [hasRole]="['policy.delete']">
<button
color="primary"
matTooltip="{{ 'POLICY.RESET' | translate }}"
color="warn"
(click)="resetDefault()"
mat-stroked-button
>
{{ 'POLICY.RESET' | translate }}
</button>
</ng-template>
</ng-container>
<!-- <cnsl-info-section *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section> -->
<div> <div>
<form *ngIf="form" [formGroup]="form" class="policy-content"> <form *ngIf="form" [formGroup]="form" class="policy-content">
@@ -26,17 +40,6 @@
</div> </div>
<div class="policy-actions"> <div class="policy-actions">
<button
*ngIf="privacyPolicy && privacyPolicy.isDefault === false"
class="reset-button"
[disabled]="(canWrite$ | async) === false"
(click)="resetDefault()"
color="warn"
type="submit"
mat-stroked-button
>
<i class="las la-history"></i> {{ 'ACTIONS.RESETDEFAULT' | translate }}
</button>
<button <button
class="save-button" class="save-button"
[disabled]="(canWrite$ | async) === false" [disabled]="(canWrite$ | async) === false"

View File

@@ -61,18 +61,8 @@
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
.save-button, .save-button {
.reset-button {
display: block; display: block;
margin: 0 1rem 0 0; margin: 0;
}
.reset-button {
display: flex;
align-items: center;
i {
margin-bottom: 3px;
}
} }
} }

View File

@@ -20,7 +20,7 @@
pointer-events: none; pointer-events: none;
border-radius: 0.5rem; border-radius: 0.5rem;
transform: scale(0.9); transform: scale(0.9);
color: map-get($foreground, base); color: map-get($foreground, text);
* { * {
pointer-events: none; pointer-events: none;

View File

@@ -54,7 +54,7 @@
<ng-template cnslHasRole [hasRole]="['policy.delete']"> <ng-template cnslHasRole [hasRole]="['policy.delete']">
<button <button
class="pl-action-button" class="pl-action-button"
*ngIf="view === View.CURRENT && serviceType === PolicyComponentServiceType.MGMT && !isDefault" *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
matTooltip="{{ 'POLICY.RESET' | translate }}" matTooltip="{{ 'POLICY.RESET' | translate }}"
color="warn" color="warn"
(click)="removePolicy()" (click)="removePolicy()"

View File

@@ -93,6 +93,11 @@
.pl-action-button { .pl-action-button {
align-self: flex-start; align-self: flex-start;
margin-top: 0.5rem; margin-top: 0.5rem;
margin-right: 0.5rem;
&:last-child {
margin-right: 0;
}
} }
} }

View File

@@ -1,7 +1,7 @@
<h1 class="title">{{ 'PROJECT.PAGES.PRIVATELABEL.DIALOG.TITLE' | translate }} {{ data?.number }}</h1> <h1 class="title">{{ 'PROJECT.PAGES.PRIVATELABEL.DIALOG.TITLE' | translate }} {{ data?.number }}</h1>
<p class="desc cnsl-secondary-text">{{ 'PROJECT.PAGES.PRIVATELABEL.DIALOG.DESCRIPTION' | translate }}</p> <p class="desc cnsl-secondary-text">{{ 'PROJECT.PAGES.PRIVATELABEL.DIALOG.DESCRIPTION' | translate }}</p>
<div mat-dialog-content> <div mat-dialog-content>
<mat-radio-group class="example-radio-group" [(ngModel)]="setting"> <mat-radio-group class="project-radio-group" [(ngModel)]="setting">
<mat-radio-button class="radio-button" *ngFor="let selection of settings" [value]="selection"> <mat-radio-button class="radio-button" *ngFor="let selection of settings" [value]="selection">
<span class="label">{{ 'PROJECT.PAGES.PRIVATELABEL.' + selection + '.TITLE' | translate }}</span> <span class="label">{{ 'PROJECT.PAGES.PRIVATELABEL.' + selection + '.TITLE' | translate }}</span>
</mat-radio-button> </mat-radio-button>
@@ -9,12 +9,17 @@
<cnsl-info-section class="info">{{ 'PROJECT.PAGES.PRIVATELABEL.' + setting + '.DESC' | translate }}</cnsl-info-section> <cnsl-info-section class="info">{{ 'PROJECT.PAGES.PRIVATELABEL.' + setting + '.DESC' | translate }}</cnsl-info-section>
</div> </div>
<div mat-dialog-actions class="action"> <div mat-dialog-actions class="action">
<button cdkFocusInitial mat-stroked-button class="ok-button" (click)="closeDialog()"> <button cdkFocusInitial mat-stroked-button (click)="closeDialog()">
{{ 'ACTIONS.CLOSE' | translate }} {{ 'ACTIONS.CLOSE' | translate }}
</button> </button>
<button [disabled]="setting === undefined" cdkFocusInitial color="primary" mat-raised-button class="ok-button" <button
(click)="closeDialog(setting)"> [disabled]="setting === undefined"
cdkFocusInitial
color="primary"
mat-raised-button
(click)="closeDialog(setting)"
>
{{ 'ACTIONS.OK' | translate }} {{ 'ACTIONS.OK' | translate }}
</button> </button>
</div> </div>

View File

@@ -7,6 +7,10 @@
font-size: 0.9rem; font-size: 0.9rem;
} }
.project-radio-group {
display: flex;
flex-direction: column;
.radio-button { .radio-button {
margin: 0.5rem 0; margin: 0.5rem 0;
@@ -14,6 +18,7 @@
white-space: normal; white-space: normal;
} }
} }
}
.info { .info {
margin: 1rem 0; margin: 1rem 0;
@@ -24,10 +29,6 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
.ok-button {
margin-left: 0.5rem;
}
button { button {
border-radius: 0.5rem; border-radius: 0.5rem;
} }

View File

@@ -1,5 +1,4 @@
<div class="found" *ngIf="users.length > 0"> <div class="user-autocomplete-found" *ngIf="users.length > 0">
<!-- <span class="found-label cnsl-secondary-text">{{'USER.SEARCH.FOUND' | translate}}:</span> -->
<div class="found-user-row" *ngFor="let user of users; index as i"> <div class="found-user-row" *ngFor="let user of users; index as i">
<div class="circle"> <div class="circle">
<cnsl-avatar <cnsl-avatar
@@ -109,9 +108,11 @@
</mat-option> </mat-option>
</mat-autocomplete> </mat-autocomplete>
<span class="target-desc"> <span class="user-autocomplete-target-desc">
{{ 'USER.TARGET.SELF' | translate }} {{ 'USER.TARGET.SELF' | translate }}
<a (click)="changeTarget()">{{ 'USER.TARGET.CLICKHERE' | translate }}</a> <a (click)="changeTarget()"
><strong>{{ 'USER.TARGET.CLICKHERE' | translate }}</strong></a
>
</span> </span>
</cnsl-form-field> </cnsl-form-field>
</div> </div>
@@ -122,9 +123,11 @@
<cnsl-form-field class="user-create-form-field more-space"> <cnsl-form-field class="user-create-form-field more-space">
<cnsl-label>{{ 'USER.SEARCH.ADDITIONAL-EXTERNAL' | translate }}</cnsl-label> <cnsl-label>{{ 'USER.SEARCH.ADDITIONAL-EXTERNAL' | translate }}</cnsl-label>
<input cnslInput type="text" [formControl]="globalLoginNameControl" placeholder="example@externaldomain.com" /> <input cnslInput type="text" [formControl]="globalLoginNameControl" placeholder="example@externaldomain.com" />
<span class="target-desc"> <span class="user-autocomplete-target-desc">
{{ (target === UserTarget.SELF ? 'USER.TARGET.SELF' : 'USER.TARGET.EXTERNAL') | translate }} {{ (target === UserTarget.SELF ? 'USER.TARGET.SELF' : 'USER.TARGET.EXTERNAL') | translate }}
<a (click)="changeTarget()">{{ 'USER.TARGET.CLICKHERE' | translate }}</a> <a (click)="changeTarget()"
><strong>{{ 'USER.TARGET.CLICKHERE' | translate }}</strong></a
>
</span> </span>
</cnsl-form-field> </cnsl-form-field>

View File

@@ -1,42 +1,13 @@
.target-desc { @use '@angular/material' as mat;
font-size: 14px;
display: block;
margin-top: 0.5rem;
a { @mixin search-user-autocomplete-theme($theme) {
color: #4072b4; $primary: map-get($theme, primary);
$primary-color: mat.get-color-from-palette($primary, 500);
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);
$secondary-text: map-get($foreground, secondary-text);
&:hover { .user-autocomplete-found {
cursor: pointer;
color: #6992c9;
text-decoration: underline;
}
}
}
.user-create-form-field {
flex: 1;
&.more-space {
margin-bottom: 1rem;
}
}
.line {
display: flex;
max-width: 500px;
button {
margin-top: 30px;
}
}
.sm-dlt {
cursor: pointer;
font-size: 0.8rem;
}
.found {
margin: 0.5rem 0; margin: 0.5rem 0;
border-radius: 0.5rem; border-radius: 0.5rem;
padding: 0.5rem 0; padding: 0.5rem 0;
@@ -85,11 +56,50 @@
transition: color 0.2s ease; transition: color 0.2s ease;
&:not(:hover) { &:not(:hover) {
color: var(--grey); color: $secondary-text;
} }
} }
} }
.user-autocomplete-target-desc {
font-size: 14px;
display: block;
margin-top: 0.5rem;
a {
color: $primary-color;
&:hover {
cursor: pointer;
color: mat.get-color-from-palette($primary, 400);
text-decoration: underline;
}
}
}
}
.user-create-form-field {
flex: 1;
&.more-space {
margin-bottom: 1rem;
}
}
.line {
display: flex;
max-width: 500px;
button {
margin-top: 30px;
}
}
.sm-dlt {
cursor: pointer;
font-size: 0.8rem;
}
.circle { .circle {
margin-right: 0.5rem; margin-right: 0.5rem;
} }

View File

@@ -2,10 +2,10 @@ export interface SettingLinks {
i18nTitle: string; i18nTitle: string;
i18nDesc: string; i18nDesc: string;
iamRouterLink: any; iamRouterLink: any;
orgRouterLink: any; orgRouterLink?: any;
queryParams: any; queryParams: any;
iamWithRole: string[]; iamWithRole?: string[];
orgWithRole: string[]; orgWithRole?: string[];
icon?: string; icon?: string;
svgIcon?: string; svgIcon?: string;
color: string; color: string;
@@ -49,12 +49,10 @@ export const PRIVACY_POLICY: SettingLinks = {
export const NOTIFICATION_GROUP: SettingLinks = { export const NOTIFICATION_GROUP: SettingLinks = {
i18nTitle: 'SETTINGS.GROUPS.NOTIFICATIONS', i18nTitle: 'SETTINGS.GROUPS.NOTIFICATIONS',
i18nDesc: '', i18nDesc: 'SETTINGS.LIST.NOTIFICATIONS_DESC',
iamRouterLink: ['/settings'], iamRouterLink: ['/settings'],
orgRouterLink: ['/org-settings'],
queryParams: { id: 'notifications' }, queryParams: { id: 'notifications' },
iamWithRole: ['iam.policy.read'], iamWithRole: ['iam.policy.read'],
orgWithRole: ['policy.read'],
icon: 'las la-bell', icon: 'las la-bell',
color: 'red', color: 'red',
}; };

View File

@@ -1,5 +1,5 @@
import { animate, style, transition, trigger } from '@angular/animations'; import { animate, style, transition, trigger } from '@angular/animations';
import { Component, Input } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { SETTINGLINKS, SettingLinks } from './settinglinks'; import { SETTINGLINKS, SettingLinks } from './settinglinks';
@@ -35,9 +35,15 @@ import { SETTINGLINKS, SettingLinks } from './settinglinks';
]), ]),
], ],
}) })
export class SettingsGridComponent { export class SettingsGridComponent implements OnInit {
@Input() public type!: PolicyComponentServiceType; @Input() public type!: PolicyComponentServiceType;
@Input() public tag: string = ''; @Input() public tag: string = '';
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public SETTINGS: SettingLinks[] = SETTINGLINKS; public SETTINGS: SettingLinks[] = SETTINGLINKS;
ngOnInit(): void {
this.SETTINGS = this.SETTINGS.filter((setting) =>
this.type === PolicyComponentServiceType.MGMT ? !!setting.orgRouterLink : !!setting.iamRouterLink,
);
}
} }

View File

@@ -2,10 +2,14 @@
<div class="shortcut-container"> <div class="shortcut-container">
<div class="shortcut-title-container"> <div class="shortcut-title-container">
<h2>{{ 'HOME.SHORTCUTS.SHORTCUTS' | translate }}</h2> <h2>{{ 'HOME.SHORTCUTS.SHORTCUTS' | translate }}</h2>
<button [matTooltip]="(editState ? 'ACTIONS.CLOSE' : 'ACTIONS.EDIT') | translate" class="shortcut-btn" <button
(click)="editState = !editState" mat-icon-button> [matTooltip]="(editState ? 'ACTIONS.SAVE' : 'ACTIONS.EDIT') | translate"
class="shortcut-btn"
(click)="editState = !editState"
mat-icon-button
>
<i *ngIf="!editState" class="las la-pen"></i> <i *ngIf="!editState" class="las la-pen"></i>
<mat-icon *ngIf="editState">close</mat-icon> <i *ngIf="editState" class="las la-check"></i>
</button> </button>
<button matTooltip="{{ 'ACTIONS.RESETDEFAULT' | translate }}" (click)="reset()" *ngIf="editState" mat-icon-button> <button matTooltip="{{ 'ACTIONS.RESETDEFAULT' | translate }}" (click)="reset()" *ngIf="editState" mat-icon-button>
<i *ngIf="editState" class="las la-undo-alt"></i> <i *ngIf="editState" class="las la-undo-alt"></i>
@@ -14,12 +18,17 @@
<div class="shortcut-list-row"> <div class="shortcut-list-row">
<div cdkDropList [cdkDropListData]="main" class="shortcut-list" (cdkDropListDropped)="drop($event, 'main')"> <div cdkDropList [cdkDropListData]="main" class="shortcut-list" (cdkDropListDropped)="drop($event, 'main')">
<p *ngIf="editState" class="shortcut-desc">{{'HOME.SHORTCUTS.REORDER' | translate}}</p> <p *ngIf="editState" class="shortcut-desc cnsl-secondary-text">{{ 'HOME.SHORTCUTS.REORDER' | translate }}</p>
<ng-container *ngFor="let shortcut of main"> <ng-container *ngFor="let shortcut of main">
<ng-template cnslHasRole [hasRole]="shortcut.withRole"> <ng-template cnslHasRole [hasRole]="shortcut.withRole">
<a [routerLink]="!editState ? shortcut.routerLink : null" class="shortcut-box" <a
[ngClass]="{'edit-state': editState && !shortcut.disabled, 'disabled': editState && shortcut.disabled}" [routerLink]="!editState ? shortcut.routerLink : null"
cdkDrag [cdkDragDisabled]="shortcut.disabled || !editState"> [queryParams]="shortcut.queryParams ? shortcut.queryParams : null"
class="shortcut-box"
[ngClass]="{ 'edit-state': editState && !shortcut.disabled, disabled: editState && shortcut.disabled }"
cdkDrag
[cdkDragDisabled]="shortcut.disabled || !editState"
>
<div class="shortcuts-avatar {{ shortcut.color }}"> <div class="shortcuts-avatar {{ shortcut.color }}">
<mat-icon *ngIf="shortcut.svgIcon" class="icon" [svgIcon]="shortcut.svgIcon"></mat-icon> <mat-icon *ngIf="shortcut.svgIcon" class="icon" [svgIcon]="shortcut.svgIcon"></mat-icon>
<i *ngIf="shortcut.icon" class="icon {{ shortcut.icon }}"></i> <i *ngIf="shortcut.icon" class="icon {{ shortcut.icon }}"></i>
@@ -32,23 +41,32 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="shortcut-state-dot" *ngIf="shortcut && shortcut.state !== undefined" <div
class="shortcut-state-dot"
*ngIf="shortcut && shortcut.state !== undefined"
matTooltip="{{ 'PROJECT.STATE.' + shortcut.state | translate }}" matTooltip="{{ 'PROJECT.STATE.' + shortcut.state | translate }}"
[ngClass]="{'active': shortcut.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': shortcut.state === ProjectState.PROJECT_STATE_INACTIVE }"> [ngClass]="{
</div> active: shortcut.state === ProjectState.PROJECT_STATE_ACTIVE,
inactive: shortcut.state === ProjectState.PROJECT_STATE_INACTIVE
}"
></div>
</a> </a>
</ng-template> </ng-template>
</ng-container> </ng-container>
</div> </div>
<div cdkDropList [cdkDropListData]="secondary" class="shortcut-list" <div cdkDropList [cdkDropListData]="secondary" class="shortcut-list" (cdkDropListDropped)="drop($event, 'secondary')">
(cdkDropListDropped)="drop($event, 'secondary')"> <p *ngIf="editState" class="shortcut-desc cnsl-secondary-text">{{ 'HOME.SHORTCUTS.REORDER' | translate }}</p>
<p *ngIf="editState" class="shortcut-desc">{{'HOME.SHORTCUTS.REORDER' | translate}}</p>
<ng-container *ngFor="let shortcut of secondary"> <ng-container *ngFor="let shortcut of secondary">
<ng-template cnslHasRole [hasRole]="shortcut.withRole"> <ng-template cnslHasRole [hasRole]="shortcut.withRole">
<a [routerLink]="!editState ? shortcut.routerLink : null" class="shortcut-box" <a
[ngClass]="{'edit-state': editState && !shortcut.disabled}" cdkDrag [routerLink]="!editState ? shortcut.routerLink : null"
[cdkDragDisabled]="shortcut.disabled || !editState"> [queryParams]="shortcut.queryParams ? shortcut.queryParams : null"
class="shortcut-box"
[ngClass]="{ 'edit-state': editState && !shortcut.disabled }"
cdkDrag
[cdkDragDisabled]="shortcut.disabled || !editState"
>
<div class="shortcuts-avatar {{ shortcut.color }}"> <div class="shortcuts-avatar {{ shortcut.color }}">
<mat-icon *ngIf="shortcut.svgIcon" class="icon" [svgIcon]="shortcut.svgIcon"></mat-icon> <mat-icon *ngIf="shortcut.svgIcon" class="icon" [svgIcon]="shortcut.svgIcon"></mat-icon>
<i *ngIf="shortcut.icon" class="icon {{ shortcut.icon }}"></i> <i *ngIf="shortcut.icon" class="icon {{ shortcut.icon }}"></i>
@@ -61,22 +79,32 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="shortcut-state-dot" *ngIf="shortcut && shortcut.state !== undefined" <div
class="shortcut-state-dot"
*ngIf="shortcut && shortcut.state !== undefined"
matTooltip="{{ 'PROJECT.STATE.' + shortcut.state | translate }}" matTooltip="{{ 'PROJECT.STATE.' + shortcut.state | translate }}"
[ngClass]="{'active': shortcut.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': shortcut.state === ProjectState.PROJECT_STATE_INACTIVE }"> [ngClass]="{
</div> active: shortcut.state === ProjectState.PROJECT_STATE_ACTIVE,
inactive: shortcut.state === ProjectState.PROJECT_STATE_INACTIVE
}"
></div>
</a> </a>
</ng-template> </ng-template>
</ng-container> </ng-container>
</div> </div>
<div cdkDropList [cdkDropListData]="third" class="shortcut-list" (cdkDropListDropped)="drop($event, 'third')"> <div cdkDropList [cdkDropListData]="third" class="shortcut-list" (cdkDropListDropped)="drop($event, 'third')">
<p *ngIf="editState" class="shortcut-desc">{{'HOME.SHORTCUTS.REORDER' | translate}}</p> <p *ngIf="editState" class="shortcut-desc cnsl-secondary-text">{{ 'HOME.SHORTCUTS.REORDER' | translate }}</p>
<ng-container *ngFor="let shortcut of third"> <ng-container *ngFor="let shortcut of third">
<ng-template cnslHasRole [hasRole]="shortcut.withRole"> <ng-template cnslHasRole [hasRole]="shortcut.withRole">
<a [routerLink]="!editState ? shortcut.routerLink : null" class="shortcut-box" <a
[ngClass]="{'edit-state': editState && !shortcut.disabled}" cdkDrag [routerLink]="!editState ? shortcut.routerLink : null"
[cdkDragDisabled]="shortcut.disabled || !editState"> [queryParams]="shortcut.queryParams ? shortcut.queryParams : null"
class="shortcut-box"
[ngClass]="{ 'edit-state': editState && !shortcut.disabled }"
cdkDrag
[cdkDragDisabled]="shortcut.disabled || !editState"
>
<div class="shortcuts-avatar {{ shortcut.color }}"> <div class="shortcuts-avatar {{ shortcut.color }}">
<mat-icon *ngIf="shortcut.svgIcon" class="icon" [svgIcon]="shortcut.svgIcon"></mat-icon> <mat-icon *ngIf="shortcut.svgIcon" class="icon" [svgIcon]="shortcut.svgIcon"></mat-icon>
<i *ngIf="shortcut.icon" class="icon {{ shortcut.icon }}"></i> <i *ngIf="shortcut.icon" class="icon {{ shortcut.icon }}"></i>
@@ -90,10 +118,15 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="shortcut-state-dot" *ngIf="shortcut && shortcut.state !== undefined" <div
class="shortcut-state-dot"
*ngIf="shortcut && shortcut.state !== undefined"
matTooltip="{{ 'PROJECT.STATE.' + shortcut.state | translate }}" matTooltip="{{ 'PROJECT.STATE.' + shortcut.state | translate }}"
[ngClass]="{'active': shortcut.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': shortcut.state === ProjectState.PROJECT_STATE_INACTIVE }"> [ngClass]="{
</div> active: shortcut.state === ProjectState.PROJECT_STATE_ACTIVE,
inactive: shortcut.state === ProjectState.PROJECT_STATE_INACTIVE
}"
></div>
</a> </a>
</ng-template> </ng-template>
</ng-container> </ng-container>
@@ -104,15 +137,23 @@
<div class="shortcut-container" *ngIf="editState"> <div class="shortcut-container" *ngIf="editState">
<h2>{{ 'HOME.SHORTCUTS.SETTINGS' | translate }}</h2> <h2>{{ 'HOME.SHORTCUTS.SETTINGS' | translate }}</h2>
<div class="shortcut-list-row"> <div class="available-shortcut-wrapper">
<p *ngIf="editState" class="shortcut-desc cnsl-secondary-text">{{ 'HOME.SHORTCUTS.REORDER' | translate }}</p>
<div cdkDropList [cdkDropListData]="allRoutes" class="shortcut-list" (cdkDropListDropped)="drop($event, 'main')"> <div
<p *ngIf="editState" class="shortcut-desc">{{'HOME.SHORTCUTS.REORDER' | translate}}</p> cdkDropList
<ng-container *ngFor="let shortcut of allRoutes"> [cdkDropListData]="allAvailableShortcuts"
class="available-shortcut-list"
(cdkDropListDropped)="drop($event, 'main')"
>
<ng-container *ngFor="let shortcut of allAvailableShortcuts">
<ng-template cnslHasRole [hasRole]="shortcut.withRole"> <ng-template cnslHasRole [hasRole]="shortcut.withRole">
<div class="shortcut-box" <div
[ngClass]="{'edit-state': editState && !shortcut.disabled, 'disabled': editState && shortcut.disabled}" class="shortcut-box"
cdkDrag [cdkDragDisabled]="shortcut.disabled"> [ngClass]="{ 'edit-state': editState && !shortcut.disabled, disabled: editState && shortcut.disabled }"
cdkDrag
[cdkDragDisabled]="shortcut.disabled"
>
<div class="shortcuts-avatar {{ shortcut.color }}"> <div class="shortcuts-avatar {{ shortcut.color }}">
<mat-icon *ngIf="shortcut.svgIcon" class="icon" [svgIcon]="shortcut.svgIcon"></mat-icon> <mat-icon *ngIf="shortcut.svgIcon" class="icon" [svgIcon]="shortcut.svgIcon"></mat-icon>
<i *ngIf="shortcut.icon" class="icon {{ shortcut.icon }}"></i> <i *ngIf="shortcut.icon" class="icon {{ shortcut.icon }}"></i>
@@ -121,73 +162,21 @@
<div class="shortcut-col"> <div class="shortcut-col">
<span *ngIf="shortcut.i18nTitle">{{ shortcut.i18nTitle | translate }}</span> <span *ngIf="shortcut.i18nTitle">{{ shortcut.i18nTitle | translate }}</span>
<span *ngIf="shortcut.title">{{ shortcut.title }}</span> <span *ngIf="shortcut.title">{{ shortcut.title }}</span>
<span class="shortcut-item-desc cnsl-secondary-text" *ngIf="shortcut.i18nDesc">{{shortcut.i18nDesc | <span class="shortcut-item-desc cnsl-secondary-text" *ngIf="shortcut.i18nDesc">{{
translate}}</span> shortcut.i18nDesc | translate
}}</span>
<span class="shortcut-item-desc cnsl-secondary-text" *ngIf="shortcut.desc">{{ shortcut.desc }}</span> <span class="shortcut-item-desc cnsl-secondary-text" *ngIf="shortcut.desc">{{ shortcut.desc }}</span>
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="shortcut-state-dot" *ngIf="shortcut && shortcut.state !== undefined" <div
class="shortcut-state-dot"
*ngIf="shortcut && shortcut.state !== undefined"
matTooltip="{{ 'PROJECT.STATE.' + shortcut.state | translate }}" matTooltip="{{ 'PROJECT.STATE.' + shortcut.state | translate }}"
[ngClass]="{'active': shortcut.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': shortcut.state === ProjectState.PROJECT_STATE_INACTIVE }"> [ngClass]="{
</div> active: shortcut.state === ProjectState.PROJECT_STATE_ACTIVE,
</div> inactive: shortcut.state === ProjectState.PROJECT_STATE_INACTIVE
</ng-template> }"
</ng-container> ></div>
</div>
<div cdkDropList [cdkDropListData]="allPolicies" class="shortcut-list"
(cdkDropListDropped)="drop($event, 'secondary')">
<p *ngIf="editState" class="shortcut-desc">{{'HOME.SHORTCUTS.REORDER' | translate}}</p>
<ng-container *ngFor="let shortcut of allPolicies">
<ng-template cnslHasRole [hasRole]="shortcut.withRole">
<div class="shortcut-box" [ngClass]="{'edit-state': editState && !shortcut.disabled}" cdkDrag
[cdkDragDisabled]="shortcut.disabled">
<div class="shortcuts-avatar {{shortcut.color}}">
<mat-icon *ngIf="shortcut.svgIcon" class="icon" [svgIcon]="shortcut.svgIcon"></mat-icon>
<i *ngIf="shortcut.icon" class="icon {{shortcut.icon}}"></i>
<span *ngIf="shortcut.label" class="shortcuts-avatar-label">{{shortcut.label}}</span>
</div>
<div class="shortcut-col">
<span *ngIf="shortcut.i18nTitle">{{shortcut.i18nTitle | translate}}</span>
<span *ngIf="shortcut.title">{{shortcut.title}}</span>
<span class="shortcut-item-desc cnsl-secondary-text" *ngIf="shortcut.i18nDesc">{{shortcut.i18nDesc |
translate}}</span>
<span class="shortcut-item-desc cnsl-secondary-text" *ngIf="shortcut.desc">{{shortcut.desc}}</span>
</div>
<span class="fill-space"></span>
<div class="shortcut-state-dot" *ngIf="shortcut && shortcut.state !== undefined"
matTooltip="{{'PROJECT.STATE.'+shortcut.state | translate}}"
[ngClass]="{'active': shortcut.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': shortcut.state === ProjectState.PROJECT_STATE_INACTIVE }">
</div>
</div>
</ng-template>
</ng-container>
</div>
<div cdkDropList [cdkDropListData]="allProjects" class="shortcut-list"
(cdkDropListDropped)="drop($event, 'third')">
<p *ngIf="editState" class="shortcut-desc">{{'HOME.SHORTCUTS.REORDER' | translate}}</p>
<ng-container *ngFor="let shortcut of allProjects">
<ng-template cnslHasRole [hasRole]="shortcut.withRole">
<div class="shortcut-box" [ngClass]="{'edit-state': editState && !shortcut.disabled}" cdkDrag
[cdkDragDisabled]="shortcut.disabled">
<div class="shortcuts-avatar {{shortcut.color}}">
<mat-icon *ngIf="shortcut.svgIcon" class="icon" [svgIcon]="shortcut.svgIcon"></mat-icon>
<i *ngIf="shortcut.icon" class="icon {{shortcut.icon}}"></i>
<span *ngIf="shortcut.label" class="shortcuts-avatar-label">{{shortcut.label}}</span>
</div>
<div class="shortcut-col">
<span *ngIf="shortcut.i18nTitle">{{shortcut.i18nTitle | translate}}</span>
<span *ngIf="shortcut.title">{{shortcut.title}}</span>
<span class="shortcut-item-desc cnsl-secondary-text" *ngIf="shortcut.i18nDesc">{{shortcut.i18nDesc |
translate}}</span>
<span class="shortcut-item-desc cnsl-secondary-text" *ngIf="shortcut.desc">{{shortcut.desc}}</span>
</div>
<span class="fill-space"></span>
<div class="shortcut-state-dot" *ngIf="shortcut && shortcut.state !== undefined"
matTooltip="{{'PROJECT.STATE.'+shortcut.state | translate}}"
[ngClass]="{'active': shortcut.state === ProjectState.PROJECT_STATE_ACTIVE, 'inactive': shortcut.state === ProjectState.PROJECT_STATE_INACTIVE }">
</div>
</div> </div>
</ng-template> </ng-template>
</ng-container> </ng-container>

View File

@@ -62,7 +62,6 @@
.shortcut-desc { .shortcut-desc {
align-self: center; align-self: center;
font-size: 14px; font-size: 14px;
color: var(--grey);
margin-top: 0; margin-top: 0;
text-align: center; text-align: center;
} }
@@ -89,7 +88,6 @@
.shortcut-desc { .shortcut-desc {
align-self: center; align-self: center;
font-size: 14px; font-size: 14px;
color: var(--grey);
margin-top: 0; margin-top: 0;
text-align: center; text-align: center;
} }
@@ -209,6 +207,30 @@
} }
} }
.available-shortcut-wrapper {
border: 1px solid $border-color;
border-radius: 1rem;
padding: 1rem;
.available-shortcut-list {
display: grid;
grid-template-columns: 1fr;
grid-gap: 1rem;
@media only screen and (min-width: 800px) {
grid-template-columns: 1fr 1fr;
}
@media only screen and (min-width: 1200px) {
grid-template-columns: 1fr 1fr 1fr;
}
.shortcut-box {
margin: 0;
}
}
}
.cdk-drag-preview { .cdk-drag-preview {
box-sizing: border-box; box-sizing: border-box;
border-radius: 1rem; border-radius: 1rem;

View File

@@ -17,7 +17,8 @@ export interface ShortcutItem {
i18nTitle?: string; i18nTitle?: string;
i18nDesc?: string; i18nDesc?: string;
routerLink: any; routerLink: any;
withRole: string[]; queryParams?: any;
withRole?: string[];
icon?: string; icon?: string;
label?: string; label?: string;
svgIcon?: string; svgIcon?: string;
@@ -138,8 +139,9 @@ export class ShortcutsComponent implements OnDestroy {
type: ShortcutType.POLICY, type: ShortcutType.POLICY,
i18nTitle: p.i18nTitle, i18nTitle: p.i18nTitle,
i18nDesc: p.i18nDesc, i18nDesc: p.i18nDesc,
routerLink: p.orgRouterLink, routerLink: p.orgRouterLink ?? p.iamRouterLink,
withRole: p.orgWithRole, queryParams: p.queryParams,
withRole: p.orgWithRole ?? p.iamWithRole,
icon: p.icon ?? '', icon: p.icon ?? '',
svgIcon: p.svgIcon ?? '', svgIcon: p.svgIcon ?? '',
color: p.color ?? '', color: p.color ?? '',
@@ -176,10 +178,10 @@ export class ShortcutsComponent implements OnDestroy {
} else { } else {
switch (listName) { switch (listName) {
case 'main': case 'main':
this.main = [PROFILE_SHORTCUT, CREATE_ORG, CREATE_PROJECT, CREATE_USER]; this.main = [PROFILE_SHORTCUT /* CREATE_ORG, CREATE_PROJECT, CREATE_USER */];
break; break;
case 'secondary': case 'secondary':
this.secondary = []; this.secondary = [CREATE_ORG, CREATE_PROJECT, CREATE_USER];
// [LOGIN_POLICY, PRIVATELABEL_POLICY].map((p) => { // [LOGIN_POLICY, PRIVATELABEL_POLICY].map((p) => {
// const policy: string = { // const policy: string = {
// i18nTitle: p.i18nTitle, // i18nTitle: p.i18nTitle,
@@ -194,7 +196,7 @@ export class ShortcutsComponent implements OnDestroy {
// }); // });
break; break;
case 'third': case 'third':
this.third = []; this.third = this.ALL_SHORTCUTS.filter((item) => item.i18nTitle === 'SETTINGS.GROUPS.APPEARANCE');
// [LOGIN_TEXTS_POLICY, MESSAGE_TEXTS_POLICY].map((p) => { // [LOGIN_TEXTS_POLICY, MESSAGE_TEXTS_POLICY].map((p) => {
// const policy: ShortcutItem = { // const policy: ShortcutItem = {
// i18nTitle: p.i18nTitle, // i18nTitle: p.i18nTitle,
@@ -276,4 +278,8 @@ export class ShortcutsComponent implements OnDestroy {
public get allProjects(): ShortcutItem[] { public get allProjects(): ShortcutItem[] {
return this.all.filter((s) => s.type === ShortcutType.PROJECT); return this.all.filter((s) => s.type === ShortcutType.PROJECT);
} }
public get allAvailableShortcuts(): ShortcutItem[] {
return [...this.allRoutes, ...this.allPolicies, ...this.allProjects];
}
} }

View File

@@ -1,20 +1,27 @@
<span class="title" mat-dialog-title>{{ 'USER.PERSONALACCESSTOKEN.ADDED.TITLE' | translate }}</span> <span class="title" mat-dialog-title>{{ 'USER.PERSONALACCESSTOKEN.ADDED.TITLE' | translate }}</span>
<div mat-dialog-content> <div mat-dialog-content>
<cnsl-info-section [type]="InfoSectionType.WARN"> {{'USER.PERSONALACCESSTOKEN.ADDED.DESCRIPTION' | translate}} <cnsl-info-section [type]="InfoSectionType.WARN">
{{ 'USER.PERSONALACCESSTOKEN.ADDED.DESCRIPTION' | translate }}
</cnsl-info-section> </cnsl-info-section>
<ng-container *ngIf="tokenResponse"> <ng-container *ngIf="tokenResponse">
<div class="row"> <div class="row">
<p class="left">{{'USER.PERSONALACCESSTOKEN.ID' | translate}}</p> <p class="left cnsl-secondary-text">{{ 'USER.PERSONALACCESSTOKEN.ID' | translate }}</p>
<p class="right">{{ tokenResponse.tokenId }}</p> <p class="right">{{ tokenResponse.tokenId }}</p>
</div> </div>
<div class="row" *ngIf="tokenResponse.token"> <div class="row" *ngIf="tokenResponse.token">
<p class="left">{{'USER.PERSONALACCESSTOKEN.TOKEN' | translate}}</p> <p class="left cnsl-secondary-text">{{ 'USER.PERSONALACCESSTOKEN.TOKEN' | translate }}</p>
<div class="right"> <div class="right">
<button class="ctc" [disabled]="copied === tokenResponse.token" <button
class="ctc"
[disabled]="copied === tokenResponse.token"
[matTooltip]="(copied !== tokenResponse.token ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate" [matTooltip]="(copied !== tokenResponse.token ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"
cnslCopyToClipboard [valueToCopy]="tokenResponse.token" (copiedValue)="copied = $event" mat-icon-button> cnslCopyToClipboard
[valueToCopy]="tokenResponse.token"
(copiedValue)="copied = $event"
mat-icon-button
>
<i *ngIf="copied !== tokenResponse.token" class="las la-clipboard"></i> <i *ngIf="copied !== tokenResponse.token" class="las la-clipboard"></i>
<i *ngIf="copied === tokenResponse.token" class="las la-clipboard-check"></i> <i *ngIf="copied === tokenResponse.token" class="las la-clipboard-check"></i>
</button> </button>

View File

@@ -28,7 +28,6 @@
} }
.left { .left {
color: var(--grey);
margin-right: 1rem; margin-right: 1rem;
margin-top: 0; margin-top: 0;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;

View File

@@ -69,7 +69,7 @@
padding: 0.75rem 0; padding: 0.75rem 0;
font-size: 15px; font-size: 15px;
cursor: pointer; cursor: pointer;
color: map-get($foreground, base); color: map-get($foreground, text);
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -16,7 +16,7 @@
opacity: 0.6; opacity: 0.6;
font-size: 15px; font-size: 15px;
cursor: pointer; cursor: pointer;
color: map-get($foreground, base); color: map-get($foreground, text);
&:not(:last-child) { &:not(:last-child) {
margin-right: 1rem; margin-right: 1rem;

View File

@@ -1,6 +1,6 @@
<span class="title" mat-dialog-title>{{ 'ORG.DOMAINS.ADD.TITLE' | translate }}</span> <span class="title" mat-dialog-title>{{ 'ORG.DOMAINS.ADD.TITLE' | translate }}</span>
<div mat-dialog-content> <div mat-dialog-content>
<p class="desc"> {{'ORG.DOMAINS.ADD.DESCRIPTION' | translate}}</p> <p class="desc cnsl-secondary-text">{{ 'ORG.DOMAINS.ADD.DESCRIPTION' | translate }}</p>
<cnsl-form-field label="Domain" required="true" class="form-field" appearance="outline"> <cnsl-form-field label="Domain" required="true" class="form-field" appearance="outline">
<cnsl-label>Domain</cnsl-label> <cnsl-label>Domain</cnsl-label>
@@ -12,8 +12,7 @@
{{ 'ACTIONS.CANCEL' | translate }} {{ 'ACTIONS.CANCEL' | translate }}
</button> </button>
<button color="primary" mat-raised-button class="ok-button" [disabled]="!newdomain" <button color="primary" mat-raised-button class="ok-button" [disabled]="!newdomain" (click)="closeDialogWithSuccess()">
(click)="closeDialogWithSuccess()">
{{ 'ACTIONS.ADD' | translate }} {{ 'ACTIONS.ADD' | translate }}
</button> </button>
</div> </div>

View File

@@ -4,7 +4,6 @@
} }
.desc { .desc {
color: var(--grey);
font-size: 0.9rem; font-size: 0.9rem;
} }

View File

@@ -1,18 +1,34 @@
<div class="max-width-container"> <div class="max-width-container">
<div class="enlarged-container"> <div class="enlarged-container">
<h1>{{ 'GRANTS.TITLE' | translate }}</h1> <h1>{{ 'GRANTS.TITLE' | translate }}</h1>
<p class="desc cnsl-secondary-text">{{'GRANTS.DESC' | translate }}</p> <p class="grants-desc cnsl-secondary-text">{{ 'GRANTS.DESC' | translate }}</p>
<cnsl-user-grants *ngIf="grantContext === UserGrantContext.NONE" <cnsl-user-grants
[displayedColumns]="['select', 'user', 'org', 'projectId','type', 'creationDate','changeDate', 'roleNamesList', 'actions']" *ngIf="grantContext === UserGrantContext.NONE"
[disableWrite]="((['user.grant.write$'] | hasRole) | async) === false" [displayedColumns]="[
[disableDelete]="((['user.grant.delete$'] | hasRole) | async) === false" 'select',
[refreshOnPreviousRoutes]="['/grant-create']"> 'user',
'org',
'projectId',
'type',
'creationDate',
'changeDate',
'roleNamesList',
'actions'
]"
[disableWrite]="(['user.grant.write$'] | hasRole | async) === false"
[disableDelete]="(['user.grant.delete$'] | hasRole | async) === false"
[refreshOnPreviousRoutes]="['/grant-create']"
>
</cnsl-user-grants> </cnsl-user-grants>
<cnsl-user-grants *ngIf="grantContext === UserGrantContext.OWNED_PROJECT" [context]="UserGrantContext.OWNED_PROJECT" <cnsl-user-grants
[projectId]="projectId" [refreshOnPreviousRoutes]="['/grant-create/project/'+projectId]" *ngIf="grantContext === UserGrantContext.OWNED_PROJECT"
[disableWrite]="((['user.grant.write$', 'user.grant.write:'+projectId] | hasRole) | async) === false" [context]="UserGrantContext.OWNED_PROJECT"
[disableDelete]="((['user.grant.delete$','user.grant.delete:'+projectId] | hasRole) | async) === false"> [projectId]="projectId"
[refreshOnPreviousRoutes]="['/grant-create/project/' + projectId]"
[disableWrite]="(['user.grant.write$', 'user.grant.write:' + projectId] | hasRole | async) === false"
[disableDelete]="(['user.grant.delete$', 'user.grant.delete:' + projectId] | hasRole | async) === false"
>
</cnsl-user-grants> </cnsl-user-grants>
</div> </div>
</div> </div>

View File

@@ -2,7 +2,7 @@ h1 {
margin: 0; margin: 0;
} }
.desc { .grants-desc {
margin-bottom: 2rem; margin-bottom: 1rem;
font-size: 14px; font-size: 14px;
} }

View File

@@ -1,7 +1,7 @@
<cnsl-top-view <cnsl-top-view
title="{{ app?.name }}" title="{{ app?.name }}"
[hasActions]="isZitadel === false && (['project.app.write:' + projectId, 'project.app.write'] | hasRole | async)" [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'" [sub]="app?.oidcConfig ? ('APP.OIDC.APPTYPE.' + app?.oidcConfig?.appType | translate) : 'API'"
[isActive]="app?.state === AppState.APP_STATE_ACTIVE" [isActive]="app?.state === AppState.APP_STATE_ACTIVE"
[isInactive]="app?.state === AppState.APP_STATE_INACTIVE" [isInactive]="app?.state === AppState.APP_STATE_INACTIVE"

View File

@@ -30,6 +30,11 @@
} }
} }
.redirect-section {
display: block;
margin-bottom: 1rem;
}
.app-desc { .app-desc {
font-size: 14px; font-size: 14px;
margin: 0; margin: 0;
@@ -131,6 +136,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
border: 1px solid if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); border: 1px solid if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2));
color: white;
border-radius: 0.5rem; border-radius: 0.5rem;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;

View File

@@ -14,18 +14,11 @@
</cnsl-auth-method-radio> </cnsl-auth-method-radio>
</div> </div>
<div mat-dialog-actions class="action"> <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 }} {{ 'ACTIONS.CLOSE' | translate }}
</button> </button>
<button <button [disabled]="!authmethod" cdkFocusInitial color="primary" mat-raised-button (click)="closeDialogWithMethod()">
[disabled]="!authmethod"
cdkFocusInitial
color="primary"
mat-raised-button
class="ok-button"
(click)="closeDialogWithMethod()"
>
{{ 'ACTIONS.SELECT' | translate }} {{ 'ACTIONS.SELECT' | translate }}
</button> </button>
</div> </div>

View File

@@ -13,11 +13,7 @@ h1 {
.action { .action {
display: flex; display: flex;
justify-content: flex-end; justify-content: space-between;
.ok-button {
margin-left: 0.5rem;
}
button { button {
border-radius: 0.5rem; border-radius: 0.5rem;

View File

@@ -1,21 +1,33 @@
<form class="form" (ngSubmit)="add(redInput)" [attr.data-e2e]="'redirect-uris'"> <form class="redirect-uris-form" (ngSubmit)="add(redInput)" [attr.data-e2e]="'redirect-uris'">
<cnsl-form-field class="formfield"> <cnsl-form-field class="formfield">
<cnsl-label>{{ title }}</cnsl-label> <cnsl-label>{{ title }}</cnsl-label>
<input #redInput cnslInput placeholder="ex. https://" [formControl]="redirectControl"> <input #redInput cnslInput placeholder="ex. https://" [formControl]="redirectControl" />
</cnsl-form-field> </cnsl-form-field>
<button matTooltip="{{'ACTIONS.ADD' | translate}}" type="submit" mat-icon-button <button
[disabled]="redirectControl.invalid || !canWrite"> matTooltip="{{ 'ACTIONS.ADD' | translate }}"
type="submit"
mat-icon-button
[disabled]="redirectControl.invalid || !canWrite"
>
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</button> </button>
</form> </form>
<div class="uri-list"> <div class="redirect-uris-list">
<div *ngFor="let uri of urisList" class="uri-line"> <div *ngFor="let uri of urisList" class="uri-line" [ngClass]="{ alert: !devMode && !(uri | redirect: isNative) }">
<span class="uri" <span
[ngClass]="{'green': !devMode && uri?.startsWith('https://'), 'red': !devMode && !uri?.startsWith('https://')}">{{uri}}</span> class="uri"
[ngClass]="{ green: !devMode && uri?.startsWith('https://'), red: !devMode && !uri?.startsWith('https://') }"
>{{ uri }}</span
>
<span class="fill-space"></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> <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"> <button matTooltip="{{ 'ACTIONS.DELETE' | translate }}" mat-icon-button (click)="remove(uri)" class="icon-button">
<mat-icon class="icon">cancel</mat-icon> <mat-icon class="icon">cancel</mat-icon>
@@ -23,5 +35,6 @@
</div> </div>
</div> </div>
<p *ngIf="redirectControl.value && redirectControl.invalid" class="error"> <p *ngIf="redirectControl.value && redirectControl.invalid" class="redirect-uris-error">
{{'APP.OIDC.REDIRECTNOTVALID' | translate}}</p> {{ 'APP.OIDC.REDIRECTNOTVALID' | translate }}
</p>

View File

@@ -1,25 +1,26 @@
.form { @use '@angular/material' as mat;
display: flex;
align-items: flex-end;
min-width: 320px;
.formfield { @mixin redirect-uris-theme($theme) {
flex: 1; $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);
button { .redirect-uris-list {
margin-bottom: 14px;
margin-right: -0.5rem;
}
}
.uri-list {
margin: 0 0.5rem;
width: 100%; width: 100%;
.uri-line { .uri-line {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 0.5rem 0;
padding: 0 0 0 0.75rem;
border-radius: 4px;
background: map-get($background, infosection);
.uri { .uri {
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
@@ -37,27 +38,53 @@
height: 30px; height: 30px;
} }
i.red {
font-size: 1.2rem;
}
.icon-button { .icon-button {
height: 30px; height: 30px;
line-height: 30px; line-height: 30px;
.icon { .icon {
font-size: 1rem !important; font-size: 1rem;
margin-bottom: 3px;
} }
&:not(:hover) { &:not(:hover) {
color: var(--grey); color: $secondary-text;
}
}
.alerticon {
display: none;
}
&.alert {
background-color: map-get($background, alertinfosection);
color: map-get($foreground, alertinfosection);
.alerticon {
display: inline;
} }
} }
} }
} }
.error { .redirect-uris-error {
font-size: 13px; font-size: 13px;
color: var(--warn); color: $warn-color;
margin: 0 0.5rem 1.5rem 0.5rem; margin: 0 0.5rem 1.5rem 0.5rem;
} }
}
.redirect-uris-form {
display: flex;
align-items: flex-end;
min-width: 320px;
.formfield {
flex: 1;
}
button {
margin-bottom: 14px;
margin-right: -0.5rem;
}
}

View File

@@ -11,7 +11,7 @@ import { ManagementService } from 'src/app/services/mgmt.service';
}) })
export class ApplicationGridComponent implements OnInit { export class ApplicationGridComponent implements OnInit {
@Input() public projectId: string = ''; @Input() public projectId: string = '';
@Input() public disabled: boolean = false; @Input() public disabled: boolean = true;
@Output() public changeView: EventEmitter<void> = new EventEmitter(); @Output() public changeView: EventEmitter<void> = new EventEmitter();
public appsSubject: BehaviorSubject<App.AsObject[]> = new BehaviorSubject<App.AsObject[]>([]); public appsSubject: BehaviorSubject<App.AsObject[]> = new BehaviorSubject<App.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);

View File

@@ -6,7 +6,7 @@
[isInactive]="project?.state === ProjectState.PROJECT_STATE_INACTIVE" [isInactive]="project?.state === ProjectState.PROJECT_STATE_INACTIVE"
[hasContributors]="true" [hasContributors]="true"
stateTooltip="{{ 'PROJECT.STATE.' + project?.state | translate }}" 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']"> <ng-template topActions cnslHasRole [hasRole]="['project.write:' + projectId, 'project.write']">
<button mat-menu-item (click)="openNameDialog()" aria-label="Edit project name" *ngIf="isZitadel === false"> <button mat-menu-item (click)="openNameDialog()" aria-label="Edit project name" *ngIf="isZitadel === false">
@@ -68,8 +68,13 @@
<cnsl-meta-layout> <cnsl-meta-layout>
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList" [queryParam]="'id'"> <cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList" [queryParam]="'id'">
<ng-container *ngIf="currentSetting === 'general' && project"> <ng-container *ngIf="currentSetting === 'general' && project">
<ng-template cnslHasRole [hasRole]="['project.app.read:' + project.id, 'project.app.read']"> <ng-template cnslHasRole [hasRole]="['project.app.read:' + project.id, 'project.app.read$']">
<cnsl-application-grid *ngIf="grid" [disabled]="isZitadel" (changeView)="grid = false" [projectId]="projectId"> <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-application-grid>
<cnsl-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}"> <cnsl-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}">
<div class="card-actions" card-actions> <div class="card-actions" card-actions>
@@ -77,7 +82,12 @@
<i matTooltip="show grid view" class="las la-th-large"></i> <i matTooltip="show grid view" class="las la-th-large"></i>
</button> </button>
</div> </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> </cnsl-card>
</ng-template> </ng-template>
@@ -111,7 +121,9 @@
> >
{{ 'PROJECT.ROLE.ASSERTION' | translate }}</mat-checkbox {{ '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 <mat-checkbox
[(ngModel)]="project.projectRoleCheck" [(ngModel)]="project.projectRoleCheck"
[disabled]="(['project.write$', 'project.write:' + project.id] | hasRole | async) === false" [disabled]="(['project.write$', 'project.write:' + project.id] | hasRole | async) === false"
@@ -119,7 +131,9 @@
> >
{{ 'PROJECT.ROLE.CHECK' | translate }}</mat-checkbox {{ '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 <mat-checkbox
[(ngModel)]="project.hasProjectCheck" [(ngModel)]="project.hasProjectCheck"
[disabled]="(['project.write$', 'project.write:' + project.id] | hasRole | async) === false" [disabled]="(['project.write$', 'project.write:' + project.id] | hasRole | async) === false"
@@ -127,10 +141,19 @@
> >
{{ 'PROJECT.HAS_PROJECT' | translate }}</mat-checkbox {{ '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"> <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> </div>
</cnsl-card> </cnsl-card>
</ng-template> </ng-template>

View File

@@ -55,5 +55,5 @@
.desc { .desc {
font-size: 14px; font-size: 14px;
margin-bottom: 2rem; margin-bottom: 1rem;
} }

View File

@@ -19,6 +19,7 @@ import { CardModule } from 'src/app/modules/card/card.module';
import { ChangesModule } from 'src/app/modules/changes/changes.module'; import { ChangesModule } from 'src/app/modules/changes/changes.module';
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module'; import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
import { InfoRowModule } from 'src/app/modules/info-row/info-row.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 { InputModule } from 'src/app/modules/input/input.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module'; import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
import { PaginatorModule } from 'src/app/modules/paginator/paginator.module'; import { PaginatorModule } from 'src/app/modules/paginator/paginator.module';
@@ -71,6 +72,7 @@ import { OwnedProjectDetailComponent } from './owned-project-detail.component';
TopViewModule, TopViewModule,
MatCheckboxModule, MatCheckboxModule,
MatSelectModule, MatSelectModule,
InfoSectionModule,
MatMenuModule, MatMenuModule,
MatProgressSpinnerModule, MatProgressSpinnerModule,
ChangesModule, ChangesModule,

View File

@@ -1,8 +1,11 @@
<cnsl-detail-layout [hasBackButton]="true" title="{{ 'PROJECT.GRANT.DETAIL.TITLE' | translate }}"> <cnsl-detail-layout [hasBackButton]="true" title="{{ 'PROJECT.GRANT.DETAIL.TITLE' | translate }}">
<p class="subinfo" sub> <p class="subinfo" sub>
<span class="cnsl-secondary-text">{{ 'PROJECT.GRANT.DETAIL.DESC' | translate }}</span> <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" <a
target="_blank"> mat-icon-button
href="https://docs.zitadel.com/docs/concepts/structure/projects#granted-organizations"
target="_blank"
>
<i class="las la-info-circle"></i> <i class="las la-info-circle"></i>
</a> </a>
</p> </p>
@@ -12,54 +15,90 @@
<span>{{ 'ACTIONS.ACTIONS' | translate }}</span> <span>{{ 'ACTIONS.ACTIONS' | translate }}</span>
<mat-icon class="icon">keyboard_arrow_down</mat-icon> <mat-icon class="icon">keyboard_arrow_down</mat-icon>
</button> </button>
<button class="actions-trigger-mob" matTooltip="{{'ACTIONS.ACTIONS' | translate}}" mat-icon-button <button
[matMenuTriggerFor]="actions"> class="actions-trigger-mob"
matTooltip="{{ 'ACTIONS.ACTIONS' | translate }}"
mat-icon-button
[matMenuTriggerFor]="actions"
>
<i class="las la-ellipsis-v"></i> <i class="las la-ellipsis-v"></i>
</button> </button>
</div> </div>
<mat-menu #actions="matMenu" xPosition="before"> <mat-menu #actions="matMenu" xPosition="before">
<button mat-menu-item *ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE" <button
(click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' | mat-menu-item
translate}}</button> *ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
<button mat-menu-item *ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE" (click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE)"
(click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' | >
translate}}</button> {{ '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> </mat-menu>
<div class="master-row"> <div class="master-row">
<div> <div></div>
</div>
</div> </div>
<cnsl-project-grant-illustration *ngIf="grant" [grantedProject]="grant" [projectRoleOptions]="projectRoleOptions" <cnsl-project-grant-illustration
(roleRemoved)="removeRole($event)" (editRoleClicked)="editRoles()"> *ngIf="grant && projectRoleOptions"
[grantedProject]="grant"
[projectRoleOptions]="projectRoleOptions"
(roleRemoved)="removeRole($event)"
(editRoleClicked)="editRoles()"
>
</cnsl-project-grant-illustration> </cnsl-project-grant-illustration>
<h2 class="project-grant-h2">{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}</h2> <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" <cnsl-members-table
*ngIf="grant"
[dataSource]="dataSource"
[canWrite]="['project.grant.member.write', 'project.grant.member.write:' + grant.grantId] | hasRole | async" [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" [canDelete]="['project.grant.member.delete', 'project.grant.member.delete:' + grant.grantId] | hasRole | async"
[memberRoleOptions]="memberRoleOptions" (updateRoles)="updateMemberRoles($event.member, $event.change)" [memberRoleOptions]="memberRoleOptions"
(deleteMember)="removeProjectMember($event)" [factoryLoadFunc]="changePageFactory" (updateRoles)="updateMemberRoles($event.member, $event.change)"
(changedSelection)="selection = $event" [refreshTrigger]="changePage"> (deleteMember)="removeProjectMember($event)"
<button class="cnsl-action-button" selectactions (click)="removeProjectMemberSelection()" [factoryLoadFunc]="changePageFactory"
[disabled]="(['project.grant.member.delete','project.grant.member.delete:' + grant.grantId] | hasRole | async) === false" (changedSelection)="selection = $event"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" color="warn" mat-raised-button> [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> <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 [type]="ActionKeysType.DELETE" (actionTriggered)="removeProjectMemberSelection()">
</cnsl-action-keys> </cnsl-action-keys>
</button> </button>
<button class="cnsl-action-button" writeactions color="primary" <button
[disabled]="(['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async) === false" class="cnsl-action-button"
(click)="openAddMember()" color="primary" mat-raised-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 }} <mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
<cnsl-action-keys (actionTriggered)="openAddMember()"> <cnsl-action-keys (actionTriggered)="openAddMember()"> </cnsl-action-keys>
</cnsl-action-keys>
</button> </button>
</cnsl-members-table> </cnsl-members-table>
</cnsl-detail-layout> </cnsl-detail-layout>

View File

@@ -3,17 +3,15 @@ import { MatDialog } from '@angular/material/dialog';
import { PageEvent } from '@angular/material/paginator'; import { PageEvent } from '@angular/material/paginator';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { ActionKeysType } from 'src/app/modules/action-keys/action-keys.component'; 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 { 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 { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { GrantedProject, ProjectGrantState, Role } from 'src/app/proto/generated/zitadel/project_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 { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.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'; import { ProjectGrantMembersDataSource } from './project-grant-members-datasource';
@Component({ @Component({
@@ -69,7 +67,10 @@ export class ProjectGrantDetailComponent {
); );
}; };
this.mgmtService.getProjectGrantByID(this.grantid, this.projectid).then((resp) => { this.mgmtService
.getProjectGrantByID(this.grantid, this.projectid)
.then((resp) => {
console.log(resp);
if (resp.projectGrant) { if (resp.projectGrant) {
this.grant = resp.projectGrant; this.grant = resp.projectGrant;
@@ -87,7 +88,8 @@ export class ProjectGrantDetailComponent {
]; ];
this.breadcrumbService.setBreadcrumb(breadcrumbs); this.breadcrumbService.setBreadcrumb(breadcrumbs);
} }
}); })
.catch(this.toast.showError);
}); });
} }
@@ -176,25 +178,23 @@ export class ProjectGrantDetailComponent {
} }
public async openAddMember(): Promise<any> { public async openAddMember(): Promise<any> {
const keysList = await this.mgmtService.listProjectGrantMemberRoles(); const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
const dialogRef = this.dialog.open(ProjectGrantMembersCreateDialogComponent, {
data: { data: {
roleKeysList: keysList.resultList, creationType: CreationType.PROJECT_GRANTED,
}, },
width: '400px', width: '400px',
}); });
dialogRef.afterClosed().subscribe((dataToAdd: ProjectGrantMembersCreateDialogExportType) => { dialogRef.afterClosed().subscribe((resp) => {
if (dataToAdd) { 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( Promise.all(
dataToAdd.userIds.map((userid: string) => { userIds.map((userid: string) => {
return this.mgmtService.addProjectGrantMember( return this.mgmtService.addProjectGrantMember(this.grant.projectId, this.grant.grantId, userid, resp.roles);
this.grant.projectId,
this.grant.grantId,
userid,
dataToAdd.rolesKeyList,
);
}), }),
) )
.then(() => { .then(() => {
@@ -207,6 +207,7 @@ export class ProjectGrantDetailComponent {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
}
}); });
} }

View File

@@ -15,6 +15,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { ActionKeysModule } from 'src/app/modules/action-keys/action-keys.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 { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module'; import { InputModule } from 'src/app/modules/input/input.module';
import { MembersTableModule } from 'src/app/modules/members-table/members-table.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 { ProjectGrantDetailRoutingModule } from './project-grant-detail-routing.module';
import { ProjectGrantDetailComponent } from './project-grant-detail.component'; import { ProjectGrantDetailComponent } from './project-grant-detail.component';
import { ProjectGrantIllustrationComponent } from './project-grant-illustration/project-grant-illustration.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({ @NgModule({
declarations: [ProjectGrantDetailComponent, ProjectGrantIllustrationComponent], declarations: [ProjectGrantDetailComponent, ProjectGrantIllustrationComponent],
imports: [ imports: [
CommonModule, CommonModule,
ProjectGrantDetailRoutingModule, ProjectGrantDetailRoutingModule,
ProjectGrantMembersCreateDialogModule,
MatAutocompleteModule, MatAutocompleteModule,
HasRoleModule, HasRoleModule,
MatChipsModule, MatChipsModule,
@@ -54,6 +51,7 @@ import {
TranslateModule, TranslateModule,
MatSelectModule, MatSelectModule,
DetailLayoutModule, DetailLayoutModule,
MemberCreateDialogModule,
HasRolePipeModule, HasRolePipeModule,
MembersTableModule, MembersTableModule,
MatDialogModule, MatDialogModule,

View File

@@ -13,7 +13,6 @@ export class ProjectGrantIllustrationComponent {
@Output() public editRoleClicked: EventEmitter<void> = new EventEmitter(); @Output() public editRoleClicked: EventEmitter<void> = new EventEmitter();
ProjectGrantState: any = ProjectGrantState; ProjectGrantState: any = ProjectGrantState;
constructor() {}
public removeRole(roleKey: string): void { public removeRole(roleKey: string): void {
this.roleRemoved.emit(roleKey); this.roleRemoved.emit(roleKey);

View File

@@ -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>

View File

@@ -1,12 +0,0 @@
.full-width {
width: 100%;
}
.action {
display: flex;
justify-content: flex-end;
.ok-button {
margin-left: .5rem;
}
}

View File

@@ -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();
});
});

View File

@@ -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);
}
}

View File

@@ -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 { }

View File

@@ -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> <h1>{{ 'PROJECT.PAGES.CREATE_DESC' | translate }}</h1>
<form cdkFocusRegionStart (ngSubmit)="saveProject()"> <form cdkFocusRegionStart (ngSubmit)="saveProject()">
<div class="column"> <div class="column">

View File

@@ -14,7 +14,7 @@
} }
.sub { .sub {
margin-bottom: 2rem; margin-bottom: 1.5rem;
font-size: 14px; font-size: 14px;
} }
@@ -38,7 +38,7 @@
opacity: 0.6; opacity: 0.6;
font-size: 15px; font-size: 15px;
cursor: pointer; cursor: pointer;
color: map-get($foreground, base); color: map-get($foreground, text);
&:first-child { &:first-child {
margin-right: 1rem; margin-right: 1rem;

View File

@@ -17,7 +17,7 @@
<div class="max-width-container"> <div class="max-width-container">
<cnsl-meta-layout> <cnsl-meta-layout>
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList"> <cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList" queryParam="id">
<ng-container *ngIf="currentSetting === 'general'"> <ng-container *ngIf="currentSetting === 'general'">
<cnsl-card <cnsl-card
*ngIf="user && user.human && user.human.profile" *ngIf="user && user.human && user.human.profile"

View File

@@ -28,7 +28,7 @@
opacity: 0.6; opacity: 0.6;
font-size: 15px; font-size: 15px;
cursor: pointer; cursor: pointer;
color: map-get($foreground, base); color: map-get($foreground, text);
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -2,8 +2,9 @@ import { MediaMatcher } from '@angular/cdk/layout';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Component, EventEmitter, OnDestroy } from '@angular/core'; import { Component, EventEmitter, OnDestroy } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs'; import { Subscription, take } from 'rxjs';
import { ChangeType } from 'src/app/modules/changes/changes.component'; import { ChangeType } from 'src/app/modules/changes/changes.component';
import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component'; import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource'; import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
@@ -57,7 +58,15 @@ export class AuthUserDetailComponent implements OnDestroy {
private breadcrumbService: BreadcrumbService, private breadcrumbService: BreadcrumbService,
private mediaMatcher: MediaMatcher, private mediaMatcher: MediaMatcher,
private _location: Location, private _location: Location,
activatedRoute: ActivatedRoute,
) { ) {
activatedRoute.queryParams.pipe(take(1)).subscribe((params: Params) => {
const { id } = params;
if (id) {
this.currentSetting = id;
}
});
const mediaq: string = '(max-width: 500px)'; const mediaq: string = '(max-width: 500px)';
const small = this.mediaMatcher.matchMedia(mediaq).matches; const small = this.mediaMatcher.matchMedia(mediaq).matches;
if (small) { if (small) {

View File

@@ -1,5 +1,5 @@
<div mat-dialog-title class="title-row"> <div class="title-row">
<h1 class="title">{{'USER.METADATA.TITLE' | translate}}</h1> <h1 class="metadata-title">{{ 'USER.METADATA.TITLE' | translate }}</h1>
<span class="fill-space"></span> <span class="fill-space"></span>
<p *ngIf="ts" class="ts cnsl-secondary-text">{{ ts | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</p> <p *ngIf="ts" class="ts cnsl-secondary-text">{{ ts | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</p>
<mat-spinner *ngIf="loading" diameter="20"></mat-spinner> <mat-spinner *ngIf="loading" diameter="20"></mat-spinner>
@@ -8,7 +8,7 @@
</button> </button>
</div> </div>
<p class="desc">{{ 'USER.METADATA.DESCRIPTION' | translate }}</p> <p class="desc">{{ 'USER.METADATA.DESCRIPTION' | translate }}</p>
<div mat-dialog-content> <div mat-dialog-content class="metadata-dialog-content">
<form *ngFor="let md of metadata; index as i" (ngSubmit)="saveElement(i)"> <form *ngFor="let md of metadata; index as i" (ngSubmit)="saveElement(i)">
<div class="content"> <div class="content">
<cnsl-form-field #key id="key{{ i }}" class="formfield"> <cnsl-form-field #key id="key{{ i }}" class="formfield">
@@ -20,23 +20,36 @@
<input cnslInput [(ngModel)]="md.value" [ngModelOptions]="{ standalone: true }" /> <input cnslInput [(ngModel)]="md.value" [ngModelOptions]="{ standalone: true }" />
</cnsl-form-field> </cnsl-form-field>
<button mat-icon-button [disabled]="!(md.key && md.value)" class="set-button" type="submit" color="primary" <button
matTooltip="{{ 'ACTIONS.SAVE' | translate }}"> mat-icon-button
[disabled]="!(md.key && md.value)"
class="set-button"
type="submit"
color="primary"
matTooltip="{{ 'ACTIONS.SAVE' | translate }}"
>
<i class="las la-save"></i> <i class="las la-save"></i>
</button> </button>
<button mat-icon-button (click)="removeEntry(i)" [disabled]="metadata.length < 2 && i === 0 && !md.key" <button
class="rm-button" type="button" color="warn" matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"> mat-icon-button
(click)="removeEntry(i)"
[disabled]="metadata.length < 2 && i === 0 && !md.key"
class="rm-button"
type="button"
color="warn"
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
>
<i class="las la-trash"></i> <i class="las la-trash"></i>
</button> </button>
</div> </div>
</form> </form>
<button color="primary" (click)="addEntry()" mat-stroked-button color="primary" class="continue-button" type="button"> <button (click)="addEntry()" mat-stroked-button class="continue-button" type="button">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
{{ 'ACTIONS.ADD' | translate }} {{ 'ACTIONS.ADD' | translate }}
</button> </button>
</div> </div>
<div mat-dialog-actions class="action"> <div mat-dialog-actions class="action">
<button cdkFocusInitial color="primary" mat-stroked-button class="ok-button" (click)="closeDialog()"> <button cdkFocusInitial mat-stroked-button class="ok-button" (click)="closeDialog()">
{{ 'ACTIONS.CLOSE' | translate }} {{ 'ACTIONS.CLOSE' | translate }}
</button> </button>
</div> </div>

View File

@@ -2,8 +2,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
.title { .metadata-title {
font-size: 1.5rem; font-size: 1.3rem;
margin: 0; margin: 0;
} }
@@ -23,6 +23,9 @@
} }
} }
.metadata-dialog-content {
min-width: 400px;
.content { .content {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -42,6 +45,7 @@
margin-top: 14px; margin-top: 14px;
} }
} }
}
.action { .action {
display: flex; display: flex;

View File

@@ -1,12 +1,7 @@
<cnsl-card class="metadata-details" title="{{ 'USER.METADATA.TITLE' | translate }}"> <cnsl-card class="metadata-details" title="{{ 'USER.METADATA.TITLE' | translate }}">
<div card-actions class="actions"> <div class="metadata-actions">
<mat-spinner class="spinner" diameter="20" *ngIf="loading"></mat-spinner> <mat-spinner class="spinner" diameter="20" *ngIf="loading"></mat-spinner>
<button mat-raised-button color="primary" class="edit" (click)="editMetadata()">{{'ACTIONS.EDIT' | <button mat-raised-button color="primary" class="edit" (click)="editMetadata()">{{ 'ACTIONS.EDIT' | translate }}</button>
translate}}</button>
<button matTooltip="{{'ACTIONS.REFRESH' | translate}}" class="refresh-btn" (click)="loadMetadata()" mat-icon-button
aria-label="refresh contributors">
<mat-icon class="icon">refresh</mat-icon>
</button>
</div> </div>
<ng-container *ngIf="metadata?.length; else emptyList"> <ng-container *ngIf="metadata?.length; else emptyList">

View File

@@ -1,9 +1,10 @@
.metadata-details { .metadata-details {
padding-bottom: 1rem; padding-bottom: 1rem;
.actions { .metadata-actions {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end;
.edit { .edit {
font-size: 14px; font-size: 14px;
@@ -53,6 +54,7 @@
.empty-desc { .empty-desc {
margin: 0; margin: 0;
font-size: 14px; font-size: 14px;
margin-bottom: 0.5rem;
} }
.refresh-btn { .refresh-btn {

View File

@@ -16,8 +16,7 @@ export class MetadataComponent implements OnInit {
public metadata: Metadata.AsObject[] = []; public metadata: Metadata.AsObject[] = [];
public loading: boolean = false; public loading: boolean = false;
constructor(private dialog: MatDialog, private service: ManagementService, private toast: ToastService, constructor(private dialog: MatDialog, private service: ManagementService, private toast: ToastService) {}
) { }
ngOnInit(): void { ngOnInit(): void {
this.loadMetadata(); this.loadMetadata();
@@ -37,15 +36,18 @@ export class MetadataComponent implements OnInit {
public loadMetadata(): Promise<any> { public loadMetadata(): Promise<any> {
this.loading = true; this.loading = true;
return (this.service as ManagementService).listUserMetadata(this.userId).then(resp => { return (this.service as ManagementService)
.listUserMetadata(this.userId)
.then((resp) => {
this.loading = false; this.loading = false;
this.metadata = resp.resultList.map(md => { this.metadata = resp.resultList.map((md) => {
return { return {
key: md.key, key: md.key,
value: atob(md.value as string), value: atob(md.value as string),
}; };
}); });
}).catch((error) => { })
.catch((error) => {
this.loading = false; this.loading = false;
this.toast.showError(error); this.toast.showError(error);
}); });

View File

@@ -37,8 +37,10 @@
</cnsl-top-view> </cnsl-top-view>
<div *ngIf="loading" class="max-width-container"> <div *ngIf="loading" class="max-width-container">
<div class="sp-wrapper">
<mat-progress-spinner diameter="25" color="primary" mode="indeterminate"></mat-progress-spinner> <mat-progress-spinner diameter="25" color="primary" mode="indeterminate"></mat-progress-spinner>
</div> </div>
</div>
<div *ngIf="!loading && !user" class="max-width-container"> <div *ngIf="!loading && !user" class="max-width-container">
<p class="no-user-error">{{ 'USER.PAGES.NOUSER' | translate }}</p> <p class="no-user-error">{{ 'USER.PAGES.NOUSER' | translate }}</p>
@@ -46,7 +48,7 @@
<div class="max-width-container" *ngIf="user && (['user.write$', 'user.write:' + user.id] | hasRole) as canWrite$"> <div class="max-width-container" *ngIf="user && (['user.write$', 'user.write:' + user.id] | hasRole) as canWrite$">
<cnsl-meta-layout> <cnsl-meta-layout>
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList"> <cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList" queryParam="id">
<div *ngIf="error" class="max-width-container"> <div *ngIf="error" class="max-width-container">
<p>{{ error }}</p> <p>{{ error }}</p>
</div> </div>

View File

@@ -42,3 +42,7 @@
.resendemail { .resendemail {
margin-top: 0.5rem; margin-top: 0.5rem;
} }
.sp-wrapper {
margin: 1rem 0;
}

View File

@@ -65,9 +65,17 @@ export class UserDetailComponent implements OnInit {
private _location: Location, private _location: Location,
private dialog: MatDialog, private dialog: MatDialog,
private router: Router, private router: Router,
activatedRoute: ActivatedRoute,
private mediaMatcher: MediaMatcher, private mediaMatcher: MediaMatcher,
breadcrumbService: BreadcrumbService, breadcrumbService: BreadcrumbService,
) { ) {
activatedRoute.queryParams.pipe(take(1)).subscribe((params: Params) => {
const { id } = params;
if (id) {
this.currentSetting = id;
}
});
breadcrumbService.setBreadcrumb([ breadcrumbService.setBreadcrumb([
new Breadcrumb({ new Breadcrumb({
type: BreadcrumbType.ORG, type: BreadcrumbType.ORG,

View File

@@ -3,14 +3,17 @@
<ng-container *ngSwitchCase="Type.TYPE_HUMAN"> <ng-container *ngSwitchCase="Type.TYPE_HUMAN">
<div class="users-title-row"> <div class="users-title-row">
<h1>{{ 'USER.PAGES.LIST' | translate }}</h1> <h1>{{ 'USER.PAGES.LIST' | translate }}</h1>
<a mat-icon-button href="https://docs.zitadel.com/docs/concepts/structure/users" rel="noreferrer" <a mat-icon-button href="https://docs.zitadel.com/docs/concepts/structure/users" rel="noreferrer" target="_blank">
target="_blank">
<i class="las la-info-circle"></i> <i class="las la-info-circle"></i>
</a> </a>
</div> </div>
<p class="user-list-sub cnsl-secondary-text">{{ 'USER.PAGES.DESCRIPTION' | translate }}</p> <p class="user-list-sub cnsl-secondary-text">{{ 'USER.PAGES.DESCRIPTION' | translate }}</p>
<cnsl-user-table [type]="Type.TYPE_HUMAN" [disabled]="(['user.write$'] | hasRole | async) === false"> <cnsl-user-table
[type]="Type.TYPE_HUMAN"
[canWrite]="!!(['user.write$'] | hasRole | async)"
[canDelete]="!!(['user.delete$'] | hasRole | async)"
>
</cnsl-user-table> </cnsl-user-table>
</ng-container> </ng-container>
@@ -18,7 +21,11 @@
<h1>{{ 'USER.PAGES.LISTMACHINE' | translate }}</h1> <h1>{{ 'USER.PAGES.LISTMACHINE' | translate }}</h1>
<p class="user-list-sub cnsl-secondary-text">{{ 'USER.PAGES.DESCRIPTIONMACHINE' | translate }}</p> <p class="user-list-sub cnsl-secondary-text">{{ 'USER.PAGES.DESCRIPTIONMACHINE' | translate }}</p>
<cnsl-user-table [type]="Type.TYPE_MACHINE" [disabled]="(['user.write$'] | hasRole | async) === false"> <cnsl-user-table
[type]="Type.TYPE_MACHINE"
[canWrite]="!!(['user.write$'] | hasRole | async)"
[canDelete]="!!(['user.delete$'] | hasRole | async)"
>
</cnsl-user-table> </cnsl-user-table>
</ng-container> </ng-container>
</div> </div>

View File

@@ -14,6 +14,6 @@
} }
.user-list-sub { .user-list-sub {
margin-bottom: 2rem; margin-bottom: 1.5rem;
font-size: 14px; font-size: 14px;
} }

View File

@@ -1,35 +1,64 @@
<cnsl-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="totalResult" <cnsl-refresh-table
[hideRefresh]="true" [timestamp]="viewTimestamp" [selection]="selection" [loading]="loading$ | async"
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [showBorder]="true"> (refreshed)="refreshPage()"
[dataSize]="totalResult"
[hideRefresh]="true"
[timestamp]="viewTimestamp"
[selection]="selection"
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes"
[showBorder]="true"
>
<div leftActions class="user-table-left-actions"> <div leftActions class="user-table-left-actions">
<button class="type-button" [ngClass]="{'active': type === Type.TYPE_HUMAN}" <button class="type-button" [ngClass]="{ active: type === Type.TYPE_HUMAN }" (click)="setType(Type.TYPE_HUMAN)">
(click)="setType(Type.TYPE_HUMAN)">{{'USER.TABLE.TYPES.HUMAN' | translate}}</button> {{ 'USER.TABLE.TYPES.HUMAN' | translate }}
<button class="type-button" [ngClass]="{'active': type === Type.TYPE_MACHINE}" </button>
(click)="setType(Type.TYPE_MACHINE)">{{'USER.TABLE.TYPES.MACHINE' | translate}}</button> <button class="type-button" [ngClass]="{ active: type === Type.TYPE_MACHINE }" (click)="setType(Type.TYPE_MACHINE)">
{{ 'USER.TABLE.TYPES.MACHINE' | translate }}
</button>
</div> </div>
<ng-template cnslHasRole [hasRole]="['user.write']" actions> <ng-template cnslHasRole [hasRole]="['user.write']" actions>
<button (click)="deactivateSelectedUsers()" class="cnsl-action-button bg-state inactive" mat-raised-button <button
*ngIf="selection.hasValue() && multipleDeactivatePossible" [disabled]="disabled" color="primary"> (click)="deactivateSelectedUsers()"
class="cnsl-action-button bg-state inactive"
mat-raised-button
*ngIf="selection.hasValue() && multipleDeactivatePossible"
[disabled]="!canWrite"
color="primary"
>
<span class="">{{ 'USER.TABLE.DEACTIVATE' | translate }}</span> <span class="">{{ 'USER.TABLE.DEACTIVATE' | translate }}</span>
<cnsl-action-keys (actionTriggered)="deactivateSelectedUsers()" [type]="ActionKeysType.DEACTIVATE"> <cnsl-action-keys (actionTriggered)="deactivateSelectedUsers()" [type]="ActionKeysType.DEACTIVATE"> </cnsl-action-keys>
</cnsl-action-keys>
</button> </button>
<button (click)="reactivateSelectedUsers()" class="cnsl-action-button bg-state active margin-left" mat-raised-button <button
*ngIf="selection.hasValue() && multipleActivatePossible" [disabled]="disabled" color="primary"> (click)="reactivateSelectedUsers()"
class="cnsl-action-button bg-state active margin-left"
mat-raised-button
*ngIf="selection.hasValue() && multipleActivatePossible"
[disabled]="!canWrite"
color="primary"
>
<span class="">{{ 'USER.TABLE.ACTIVATE' | translate }}</span> <span class="">{{ 'USER.TABLE.ACTIVATE' | translate }}</span>
<cnsl-action-keys (actionTriggered)="reactivateSelectedUsers()" [type]="ActionKeysType.REACTIVATE"> <cnsl-action-keys (actionTriggered)="reactivateSelectedUsers()" [type]="ActionKeysType.REACTIVATE"> </cnsl-action-keys>
</cnsl-action-keys>
</button> </button>
<cnsl-filter-user *ngIf="!selection.hasValue()" (filterChanged)="applySearchQuery($any($event))" <cnsl-filter-user
(filterOpen)="filterOpen = $event"></cnsl-filter-user> *ngIf="!selection.hasValue()"
<a *ngIf="!selection.hasValue()" [routerLink]="[ '/users',type === Type.TYPE_HUMAN ? 'create' : 'create-machine']" (filterChanged)="applySearchQuery($any($event))"
color="primary" mat-raised-button [disabled]="disabled" class="cnsl-action-button"> (filterOpen)="filterOpen = $event"
></cnsl-filter-user>
<a
*ngIf="!selection.hasValue()"
[routerLink]="['/users', type === Type.TYPE_HUMAN ? 'create' : 'create-machine']"
color="primary"
mat-raised-button
[disabled]="!canWrite"
class="cnsl-action-button"
>
<mat-icon class="icon">add</mat-icon> <mat-icon class="icon">add</mat-icon>
<span>{{ 'ACTIONS.NEW' | translate }}</span> <span>{{ 'ACTIONS.NEW' | translate }}</span>
<cnsl-action-keys *ngIf="!filterOpen" <cnsl-action-keys
(actionTriggered)="gotoRouterLink([ '/users',type === Type.TYPE_HUMAN ? 'create' : 'create-machine'])"> *ngIf="!filterOpen"
(actionTriggered)="gotoRouterLink(['/users', type === Type.TYPE_HUMAN ? 'create' : 'create-machine'])"
>
</cnsl-action-keys> </cnsl-action-keys>
</a> </a>
</ng-template> </ng-template>
@@ -38,19 +67,37 @@
<table class="table" mat-table [dataSource]="dataSource" matSort (matSortChange)="sortChange($event)"> <table class="table" mat-table [dataSource]="dataSource" matSort (matSortChange)="sortChange($event)">
<ng-container matColumnDef="select"> <ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef class="selection"> <th mat-header-cell *matHeaderCellDef class="selection">
<mat-checkbox [disabled]="disabled" color="primary" (change)="$event ? masterToggle() : null" <mat-checkbox
[disabled]="!canWrite"
color="primary"
(change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()" [checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()"> [indeterminate]="selection.hasValue() && !isAllSelected()"
>
</mat-checkbox> </mat-checkbox>
</th> </th>
<td mat-cell *matCellDef="let user" class="selection"> <td mat-cell *matCellDef="let user" class="selection">
<mat-checkbox [disabled]="disabled" color="primary" (click)="$event.stopPropagation()" <mat-checkbox
(change)="$event ? selection.toggle(user) : null" [checked]="selection.isSelected(user)"> [disabled]="!canWrite"
color="primary"
(click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(user) : null"
[checked]="selection.isSelected(user)"
>
<cnsl-avatar <cnsl-avatar
*ngIf="user.human && user.human.profile.displayName && user.human?.profile.firstName && user.human?.profile.lastName; else cog" *ngIf="
class="avatar" [name]="user.human.profile.displayName" [avatarUrl]="user.human?.profile?.avatarUrl || ''" user.human &&
[forColor]="user?.preferredLoginName" [size]="32"> user.human.profile.displayName &&
user.human?.profile.firstName &&
user.human?.profile.lastName;
else cog
"
class="avatar"
[name]="user.human.profile.displayName"
[avatarUrl]="user.human?.profile?.avatarUrl || ''"
[forColor]="user?.preferredLoginName"
[size]="32"
>
</cnsl-avatar> </cnsl-avatar>
<ng-template #cog> <ng-template #cog>
<cnsl-avatar [forColor]="user?.preferredLoginName" [isMachine]="true"> <cnsl-avatar [forColor]="user?.preferredLoginName" [isMachine]="true">
@@ -62,8 +109,12 @@
</ng-container> </ng-container>
<ng-container matColumnDef="displayName"> <ng-container matColumnDef="displayName">
<th mat-header-cell *matHeaderCellDef mat-sort-header <th
[ngClass]="{'search-active': this.userSearchKey === UserListSearchKey.DISPLAY_NAME}"> mat-header-cell
*matHeaderCellDef
mat-sort-header
[ngClass]="{ 'search-active': this.userSearchKey === UserListSearchKey.DISPLAY_NAME }"
>
{{ 'USER.PROFILE.DISPLAYNAME' | translate }} {{ 'USER.PROFILE.DISPLAYNAME' | translate }}
</th> </th>
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null"> <td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
@@ -73,17 +124,26 @@
</ng-container> </ng-container>
<ng-container matColumnDef="username"> <ng-container matColumnDef="username">
<th mat-header-cell *matHeaderCellDef mat-sort-header <th
[ngClass]="{'search-active': this.userSearchKey === UserListSearchKey.USER_NAME}"> mat-header-cell
*matHeaderCellDef
mat-sort-header
[ngClass]="{ 'search-active': this.userSearchKey === UserListSearchKey.USER_NAME }"
>
{{ 'USER.PROFILE.USERNAME' | translate }} {{ 'USER.PROFILE.USERNAME' | translate }}
</th> </th>
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null"> <td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
{{user.userName}} </td> {{ user.userName }}
</td>
</ng-container> </ng-container>
<ng-container matColumnDef="email"> <ng-container matColumnDef="email">
<th mat-header-cell *matHeaderCellDef mat-sort-header <th
[ngClass]="{'search-active': this.UserListSearchKey === UserListSearchKey.EMAIL}"> mat-header-cell
*matHeaderCellDef
mat-sort-header
[ngClass]="{ 'search-active': this.UserListSearchKey === UserListSearchKey.EMAIL }"
>
{{ 'USER.EMAIL' | translate }} {{ 'USER.EMAIL' | translate }}
</th> </th>
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null"> <td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
@@ -94,8 +154,13 @@
<ng-container matColumnDef="state"> <ng-container matColumnDef="state">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'USER.DATA.STATE' | translate }}</th> <th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'USER.DATA.STATE' | translate }}</th>
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null"> <td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
<span class="state" <span
[ngClass]="{'active': user.state === UserState.USER_STATE_ACTIVE, 'inactive': user.state === UserState.USER_STATE_INACTIVE}"> class="state"
[ngClass]="{
active: user.state === UserState.USER_STATE_ACTIVE,
inactive: user.state === UserState.USER_STATE_INACTIVE
}"
>
{{ 'USER.DATA.STATE' + user.state | translate }} {{ 'USER.DATA.STATE' + user.state | translate }}
</span> </span>
</td> </td>
@@ -104,16 +169,14 @@
<ng-container matColumnDef="creationDate"> <ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef>{{ 'USER.TABLE.CREATIONDATE' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'USER.TABLE.CREATIONDATE' | translate }}</th>
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null"> <td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
<span class="no-break">{{user.details.creationDate | timestampToDate | localizedDate: 'fromNow' <span class="no-break">{{ user.details.creationDate | timestampToDate | localizedDate: 'fromNow' }}</span>
}}</span>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="changeDate"> <ng-container matColumnDef="changeDate">
<th mat-header-cell *matHeaderCellDef>{{ 'USER.TABLE.CHANGEDATE' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'USER.TABLE.CHANGEDATE' | translate }}</th>
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null"> <td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
<span class="no-break">{{user.details.changeDate | timestampToDate | localizedDate: 'fromNow' <span class="no-break">{{ user.details.changeDate | timestampToDate | localizedDate: 'fromNow' }}</span>
}}</span>
</td> </td>
</ng-container> </ng-container>
@@ -121,20 +184,26 @@
<th mat-header-cell *matHeaderCellDef class="user-tr-actions"></th> <th mat-header-cell *matHeaderCellDef class="user-tr-actions"></th>
<td mat-cell *matCellDef="let user" class="user-tr-actions"> <td mat-cell *matCellDef="let user" class="user-tr-actions">
<cnsl-table-actions> <cnsl-table-actions>
<button actions matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" (click)="deleteUser(user)" <button
mat-icon-button> actions
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
color="warn"
(click)="deleteUser(user)"
[disabled]="!canWrite || !canDelete"
mat-icon-button
>
<i class="las la-trash"></i> <i class="las la-trash"></i>
</button> </button>
</cnsl-table-actions> </cnsl-table-actions>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="type === Type.TYPE_HUMAN ? displayedColumnsHuman : displayedColumnsMachine"> <tr mat-header-row *matHeaderRowDef="type === Type.TYPE_HUMAN ? displayedColumnsHuman : displayedColumnsMachine"></tr>
</tr> <tr
<tr class="highlight pointer" mat-row class="highlight pointer"
*matRowDef="let user; columns: (type === Type.TYPE_HUMAN ? displayedColumnsHuman : displayedColumnsMachine);"> mat-row
</tr> *matRowDef="let user; columns: type === Type.TYPE_HUMAN ? displayedColumnsHuman : displayedColumnsMachine"
></tr>
</table> </table>
</div> </div>
@@ -142,7 +211,14 @@
<i class="las la-exclamation"></i> <i class="las la-exclamation"></i>
<span>{{ 'USER.TABLE.EMPTY' | translate }}</span> <span>{{ 'USER.TABLE.EMPTY' | translate }}</span>
</div> </div>
<cnsl-paginator #paginator class="paginator" [timestamp]="viewTimestamp" [length]="totalResult || 0" <cnsl-paginator
[pageSize]="INITIAL_PAGE_SIZE" [timestamp]="viewTimestamp" [pageSizeOptions]="[10, 20, 50, 100]" #paginator
(page)="changePage($event)"></cnsl-paginator> class="paginator"
[timestamp]="viewTimestamp"
[length]="totalResult || 0"
[pageSize]="INITIAL_PAGE_SIZE"
[timestamp]="viewTimestamp"
[pageSizeOptions]="[10, 20, 50, 100]"
(page)="changePage($event)"
></cnsl-paginator>
</cnsl-refresh-table> </cnsl-refresh-table>

View File

@@ -16,7 +16,7 @@
opacity: 0.6; opacity: 0.6;
font-size: 15px; font-size: 15px;
cursor: pointer; cursor: pointer;
color: map-get($foreground, base); color: map-get($foreground, text);
&:first-child { &:first-child {
margin-right: 1rem; margin-right: 1rem;

View File

@@ -37,7 +37,7 @@ export class UserTableComponent implements OnInit {
public Type: any = Type; public Type: any = Type;
@Input() public type: Type = Type.TYPE_HUMAN; @Input() public type: Type = Type.TYPE_HUMAN;
@Input() refreshOnPreviousRoutes: string[] = []; @Input() refreshOnPreviousRoutes: string[] = [];
@Input() disabled: boolean = false; @Input() canWrite: boolean = false;
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
@ViewChild(MatSort) public sort!: MatSort; @ViewChild(MatSort) public sort!: MatSort;
public INITIAL_PAGE_SIZE: number = 20; public INITIAL_PAGE_SIZE: number = 20;
@@ -77,6 +77,7 @@ export class UserTableComponent implements OnInit {
public filterOpen: boolean = false; public filterOpen: boolean = false;
private searchQueries: SearchQuery[] = []; private searchQueries: SearchQuery[] = [];
@Input() public canDelete: boolean = false;
constructor( constructor(
private router: Router, private router: Router,
public translate: TranslateService, public translate: TranslateService,

View File

@@ -1,37 +1,36 @@
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs'; import { Router } from '@angular/router';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
export abstract class StatehandlerProcessorService { export abstract class StatehandlerProcessorService {
public abstract createState(url: string): Observable<string | undefined>; public abstract createState(url: string): string;
public abstract restoreState(state?: string): void; public abstract restoreState(state?: string): void;
} }
@Injectable() @Injectable()
export class StatehandlerProcessorServiceImpl implements StatehandlerProcessorService { export class StatehandlerProcessorServiceImpl implements StatehandlerProcessorService {
constructor(private location: Location) {} constructor(private location: Location, private router: Router) {}
public createState(url: string): Observable<string> { public createState(url: string): string {
const externalUrl = this.location.prepareExternalUrl(url); const externalUrl = this.location.prepareExternalUrl(url);
const urlId = uuidv4(); const urlId = uuidv4();
sessionStorage.setItem(urlId, externalUrl); sessionStorage.setItem(urlId, externalUrl);
return urlId;
return of(urlId);
} }
public restoreState(state?: string): void { public restoreState(state?: string): void {
if (state === undefined) { if (state === undefined) {
return; return;
} } else {
const url = sessionStorage.getItem(state); const url = sessionStorage.getItem(state);
if (url === null) { if (url === null) {
return; return;
} } else {
sessionStorage.removeItem(state); sessionStorage.removeItem(state);
window.location.href = window.location.origin + url; // window.location.replace(window.location.origin + url);
this.router.navigate([url]);
}
}
} }
} }

View File

@@ -23,7 +23,9 @@ export class StatehandlerServiceImpl implements StatehandlerService, OnDestroy {
map(() => oauthService.state), map(() => oauthService.state),
takeUntil(this.unsubscribe$), takeUntil(this.unsubscribe$),
) )
.subscribe((state) => processor.restoreState(state)); .subscribe((state) => {
processor.restoreState(state);
});
} }
public initStateHandler(): void { public initStateHandler(): void {
@@ -47,9 +49,11 @@ export class StatehandlerServiceImpl implements StatehandlerService, OnDestroy {
switchMap((url: string) => { switchMap((url: string) => {
if (url.includes('?login_hint=')) { if (url.includes('?login_hint=')) {
const newUrl = this.removeParam('login_hint', url); const newUrl = this.removeParam('login_hint', url);
return of(newUrl); return of(this.processor.createState(newUrl));
} else if (url) {
return of(this.processor.createState(url));
} else { } else {
return this.processor.createState(url); return of(undefined);
} }
}), }),
); );

View File

@@ -57,9 +57,10 @@ export class ThemeService {
} }
public saveTextColor(colorHex: string, isDark: boolean): void { public saveTextColor(colorHex: string, isDark: boolean): void {
this.primaryColorPalette = this.computeColors(colorHex);
const theme = isDark ? 'dark' : 'light'; const theme = isDark ? 'dark' : 'light';
document.documentElement.style.setProperty(`--theme-${theme}-${'text'}`, colorHex); document.documentElement.style.setProperty(`--theme-${theme}-${'text'}`, colorHex);
const secondaryTextHex = tinycolor(colorHex).setAlpha(0.78).toHex8String();
document.documentElement.style.setProperty(`--theme-${theme}-${'secondary-text'}`, secondaryTextHex);
} }
private computeColors(hex: string): Color[] { private computeColors(hex: string): Color[] {

View File

@@ -35,7 +35,7 @@
}, },
"SHORTCUTS": { "SHORTCUTS": {
"SHORTCUTS": "Shortcuts", "SHORTCUTS": "Shortcuts",
"SETTINGS": "Organisation Einstellungen", "SETTINGS": "Verfügbare Shortcuts",
"PROJECTS": "Projekte", "PROJECTS": "Projekte",
"REORDER": "Zum Verschieben Kachel halten un ziehen", "REORDER": "Zum Verschieben Kachel halten un ziehen",
"ADD": "Zum Hinzufügen Kachel halten und ziehen" "ADD": "Zum Hinzufügen Kachel halten und ziehen"
@@ -135,7 +135,7 @@
"TEXT": "Hier können Sie zwischen Ihren Benutzerkonten wechseln und Ihre Sessions und Ihr Profil verwalten." "TEXT": "Hier können Sie zwischen Ihren Benutzerkonten wechseln und Ihre Sessions und Ihr Profil verwalten."
}, },
"NAV": { "NAV": {
"TEXT": "Diese Navigation ändert sich basierend auf den Breadcrumbs darüber." "TEXT": "Diese Navigation ändert sich basierend auf Ihrer Organisation oder Instanz"
}, },
"CONTEXTCHANGED": { "CONTEXTCHANGED": {
"TEXT": "Achtung! Soeben wurde die Organisation gewechselt." "TEXT": "Achtung! Soeben wurde die Organisation gewechselt."
@@ -818,6 +818,7 @@
"LOCKOUT": "Sperrmechanismen", "LOCKOUT": "Sperrmechanismen",
"COMPLEXITY": "Passwordkomplexität", "COMPLEXITY": "Passwordkomplexität",
"NOTIFICATIONS": "Benachrichtigungen", "NOTIFICATIONS": "Benachrichtigungen",
"NOTIFICATIONS_DESC": "SMTP und SMS Einstellungen",
"MESSAGETEXTS": "Benachrichtigungstexte", "MESSAGETEXTS": "Benachrichtigungstexte",
"IDP": "Identity Provider", "IDP": "Identity Provider",
"DOMAIN": "Domain Einstellungen", "DOMAIN": "Domain Einstellungen",
@@ -1181,11 +1182,11 @@
"DESC": "Sobald der Nutzer identifiziert ist, wird das Branding der von ihm gewählten Organisation angezeigt, davor wird der Default des Systems angezeigt." "DESC": "Sobald der Nutzer identifiziert ist, wird das Branding der von ihm gewählten Organisation angezeigt, davor wird der Default des Systems angezeigt."
}, },
"1": { "1": {
"TITLE": "Durchsetzung der Richtlinie des Projekteigentümers", "TITLE": "Einstellung des Projekteigentümers",
"DESC": "Das Branding der Organisation, die Eigentümerin des Projekts ist, wird angezeigt" "DESC": "Das Branding der Organisation, die Eigentümerin des Projekts ist, wird angezeigt"
}, },
"2": { "2": {
"TITLE": "Durchsetzung der Richtlinie des Benutzereigentümers", "TITLE": "Einstellung des Benutzereigentümers",
"DESC": "Das Branding der Organisation des Projekts wird angezeigt, sobald der Benutzer identifiziert ist, wird jedoch auf die Einstellung der Organisation des identifizierten Benutzers, gewechselt." "DESC": "Das Branding der Organisation des Projekts wird angezeigt, sobald der Benutzer identifiziert ist, wird jedoch auf die Einstellung der Organisation des identifizierten Benutzers, gewechselt."
}, },
"DIALOG": { "DIALOG": {
@@ -1549,6 +1550,9 @@
} }
}, },
"DIALOG": { "DIALOG": {
"CONFIG": {
"TITLE": "OIDC configuration ändern"
},
"DELETE": { "DELETE": {
"TITLE": "App löschen", "TITLE": "App löschen",
"DESCRIPTION": "Wollen Sie diese App wirklich löschen?" "DESCRIPTION": "Wollen Sie diese App wirklich löschen?"

View File

@@ -35,7 +35,7 @@
}, },
"SHORTCUTS": { "SHORTCUTS": {
"SHORTCUTS": "Shortcuts", "SHORTCUTS": "Shortcuts",
"SETTINGS": "Organization Settings", "SETTINGS": "Available shortcuts",
"PROJECTS": "Projects", "PROJECTS": "Projects",
"REORDER": "Hold and drag the tile to move it", "REORDER": "Hold and drag the tile to move it",
"ADD": "Hold and drag a tile to add" "ADD": "Hold and drag a tile to add"
@@ -135,7 +135,7 @@
"TEXT": "Here you can switch between your user accounts and manage your sessions and profile." "TEXT": "Here you can switch between your user accounts and manage your sessions and profile."
}, },
"NAV": { "NAV": {
"TEXT": "This navigation changes based on the breadcrumbs above." "TEXT": "This navigation changes based on your selected oranization above or your instance"
}, },
"CONTEXTCHANGED": { "CONTEXTCHANGED": {
"TEXT": "Attention! The organization context has changed." "TEXT": "Attention! The organization context has changed."
@@ -818,6 +818,7 @@
"LOCKOUT": "Lockout", "LOCKOUT": "Lockout",
"COMPLEXITY": "Password complexity", "COMPLEXITY": "Password complexity",
"NOTIFICATIONS": "Notification providers and SMTP", "NOTIFICATIONS": "Notification providers and SMTP",
"NOTIFICATIONS_DESC": "SMTP and SMS Settings",
"MESSAGETEXTS": "Message Texts", "MESSAGETEXTS": "Message Texts",
"IDP": "Identity Providers", "IDP": "Identity Providers",
"DOMAIN": "Domain settings", "DOMAIN": "Domain settings",
@@ -1181,11 +1182,11 @@
"DESC": "As soon as the user is identified, the branding of the organization of the identified user will be shown, before the system default is shown." "DESC": "As soon as the user is identified, the branding of the organization of the identified user will be shown, before the system default is shown."
}, },
"1": { "1": {
"TITLE": "Enforce project resource owner Policy", "TITLE": "Use project setting",
"DESC": "The branding of the organization which owns the project will be shown" "DESC": "The branding of the organization which owns the project will be shown"
}, },
"2": { "2": {
"TITLE": "Allow Login User resource owner policy", "TITLE": "Use User Organization setting",
"DESC": "The branding of the organization of the project will be shown, but as soon as the user is identified, the setting of the organization of the identified user, will be shown." "DESC": "The branding of the organization of the project will be shown, but as soon as the user is identified, the setting of the organization of the identified user, will be shown."
}, },
"DIALOG": { "DIALOG": {
@@ -1549,6 +1550,9 @@
} }
}, },
"DIALOG": { "DIALOG": {
"CONFIG": {
"TITLE": "Change OIDC Configuration"
},
"DELETE": { "DELETE": {
"TITLE": "Delete App", "TITLE": "Delete App",
"DESCRIPTION": "Do you really want to delete this application?" "DESCRIPTION": "Do you really want to delete this application?"

View File

@@ -34,8 +34,8 @@
"DESCRIPTION": "Iniziare rapidamente con ZITADEL." "DESCRIPTION": "Iniziare rapidamente con ZITADEL."
}, },
"SHORTCUTS": { "SHORTCUTS": {
"SHORTCUTS": "Shortcuts", "SHORTCUTS": "Scorciatoie",
"SETTINGS": "Impostazioni dell' organizazzione", "SETTINGS": "Scorciatoie disponibili",
"PROJECTS": "Progetti", "PROJECTS": "Progetti",
"REORDER": "Per spostare, tieni premuto e trascina il riquadro", "REORDER": "Per spostare, tieni premuto e trascina il riquadro",
"ADD": "Per aggiungere, tieni premuto e trascina il riquadro" "ADD": "Per aggiungere, tieni premuto e trascina il riquadro"
@@ -135,7 +135,7 @@
"TEXT": "Qui puoi passare da un account utente all'altro e gestire le sessioni e il profilo." "TEXT": "Qui puoi passare da un account utente all'altro e gestire le sessioni e il profilo."
}, },
"NAV": { "NAV": {
"TEXT": "Questa navigazione cambia in base ai breadcrumb sopra." "TEXT": "Questa navigazione cambia in base all' organizzazione impostata sopra e la tua istanza."
}, },
"CONTEXTCHANGED": { "CONTEXTCHANGED": {
"TEXT": "Attenzione! L'organizzazione è appena stata cambiata." "TEXT": "Attenzione! L'organizzazione è appena stata cambiata."
@@ -818,6 +818,7 @@
"LOCKOUT": "Meccanismi di bloccaggio", "LOCKOUT": "Meccanismi di bloccaggio",
"COMPLEXITY": "complessità della password", "COMPLEXITY": "complessità della password",
"NOTIFICATIONS": "Notifiche", "NOTIFICATIONS": "Notifiche",
"NOTIFICATIONS_DESC": "Impostazioni SMTP e SMS",
"MESSAGETEXTS": "Testi di notifica", "MESSAGETEXTS": "Testi di notifica",
"IDP": "Identity Providers", "IDP": "Identity Providers",
"DOMAIN": "Impostazioni del dominio", "DOMAIN": "Impostazioni del dominio",
@@ -1181,11 +1182,11 @@
"DESC": "Non appena l'utente viene identificato, viene mostrata l'impostazione Branding dell'organizzazione, prima che venga mostrato il default del sistema." "DESC": "Non appena l'utente viene identificato, viene mostrata l'impostazione Branding dell'organizzazione, prima che venga mostrato il default del sistema."
}, },
"1": { "1": {
"TITLE": "Applica l'impostazione Branding delle risorse del progetto", "TITLE": "Applica l'impostazione del progetto",
"DESC": "Branding dell'organizzazione del progetto sar\u00e0 mostrata" "DESC": "Branding dell'organizzazione del progetto sar\u00e0 mostrata"
}, },
"2": { "2": {
"TITLE": "Consentire l'accesso all'impostazione Branding dell'utente", "TITLE": "Applica l'impostazione dell' organizzazione dell' utente",
"DESC": "Verr\u00e0 mostrata l'impostazione Branding dell'organizzazione del progetto, ma non appena l'utente viene identificato, verr\u00e0 mostrata l'impostazione dell'organizzazione dell'utente identificato." "DESC": "Verr\u00e0 mostrata l'impostazione Branding dell'organizzazione del progetto, ma non appena l'utente viene identificato, verr\u00e0 mostrata l'impostazione dell'organizzazione dell'utente identificato."
}, },
"DIALOG": { "DIALOG": {
@@ -1549,6 +1550,9 @@
} }
}, },
"DIALOG": { "DIALOG": {
"CONFIG": {
"TITLE": "Configurazione OIDC"
},
"DELETE": { "DELETE": {
"TITLE": "Rimuovere App", "TITLE": "Rimuovere App",
"DESCRIPTION": "Vuoi davvero rimuovere questa applicazione?" "DESCRIPTION": "Vuoi davvero rimuovere questa applicazione?"

View File

@@ -23,6 +23,7 @@
@import 'src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component'; @import 'src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component';
@import 'src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component'; @import 'src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component';
@import 'src/app/pages/projects/apps/app-detail/app-detail.component'; @import 'src/app/pages/projects/apps/app-detail/app-detail.component';
@import 'src/app/pages/projects/apps/redirect-uris/redirect-uris.component';
@import 'src/app/modules/top-view/top-view.component'; @import 'src/app/modules/top-view/top-view.component';
@import 'src/app/pages/projects/projects.component'; @import 'src/app/pages/projects/projects.component';
@import 'src/app/modules/edit-text/edit-text.component.scss'; @import 'src/app/modules/edit-text/edit-text.component.scss';
@@ -51,6 +52,7 @@
@import 'src/app/pages/actions/add-action-dialog/add-action-dialog.component'; @import 'src/app/pages/actions/add-action-dialog/add-action-dialog.component';
@import 'src/app/modules/project-role-chip/project-role-chip.component'; @import 'src/app/modules/project-role-chip/project-role-chip.component';
@import 'src/app/pages/home/home.component.scss'; @import 'src/app/pages/home/home.component.scss';
@import 'src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss';
@import 'src/app/modules/policies/login-policy/factor-table/factor-table.component.scss'; @import 'src/app/modules/policies/login-policy/factor-table/factor-table.component.scss';
@import 'src/app/modules/info-overlay/info-overlay.component.scss'; @import 'src/app/modules/info-overlay/info-overlay.component.scss';
@import './styles/codemirror.scss'; @import './styles/codemirror.scss';
@@ -67,6 +69,7 @@
@include top-view-theme($theme); @include top-view-theme($theme);
@include info-overlay-theme($theme); @include info-overlay-theme($theme);
@include app-auth-method-radio-theme($theme); @include app-auth-method-radio-theme($theme);
@include search-user-autocomplete-theme($theme);
@include project-role-chips-theme($theme); @include project-role-chips-theme($theme);
@include card-theme($theme); @include card-theme($theme);
@include footer-theme($theme); @include footer-theme($theme);
@@ -109,6 +112,7 @@
@include user-grants-theme($theme); @include user-grants-theme($theme);
@include info-row-theme($theme); @include info-row-theme($theme);
@include action-dialog-theme($theme); @include action-dialog-theme($theme);
@include redirect-uris-theme($theme);
@include action-keys-theme($theme); @include action-keys-theme($theme);
@include codemirror-theme($theme); @include codemirror-theme($theme);
@include contact-theme($theme); @include contact-theme($theme);

View File

@@ -229,6 +229,9 @@ $caos-light-warn: (
$caos-dark-theme-text: var(--theme-dark-text); $caos-dark-theme-text: var(--theme-dark-text);
$caos-light-theme-text: var(--theme-light-text); $caos-light-theme-text: var(--theme-light-text);
$caos-dark-theme-secondary-text: var(--theme-dark-secondary-text);
$caos-light-theme-secondary-text: var(--theme-light-secondary-text);
$caos-dark-theme-background: ( $caos-dark-theme-background: (
status-bar: map_get($caos-dark-background, 300), status-bar: map_get($caos-dark-background, 300),
app-bar: map_get($caos-dark-background, 500), app-bar: map_get($caos-dark-background, 500),
@@ -287,7 +290,7 @@ $caos-light-theme-background: (
unselected-chip: map_get($caos-light-background, 300), unselected-chip: map_get($caos-light-background, 300),
disabled-list-option: map_get($caos-light-background, 200), disabled-list-option: map_get($caos-light-background, 200),
tooltip: map_get($mat-gray, 700), tooltip: map_get($mat-gray, 700),
infosection: map_get($caos-light-primary, 100), infosection: map_get($caos-light-background, 600),
warninfosection: #ffc1c1, warninfosection: #ffc1c1,
alertinfosection: rgb(251, 191, 36), alertinfosection: rgb(251, 191, 36),
successinfosection: #cbf4c9, successinfosection: #cbf4c9,
@@ -307,7 +310,7 @@ $caos-dark-theme-foreground: (
disabled-text: $light-disabled-text, disabled-text: $light-disabled-text,
elevation: black, elevation: black,
hint-text: $light-disabled-text, hint-text: $light-disabled-text,
secondary-text: $light-secondary-text, secondary-text: $caos-dark-theme-secondary-text,
placeholder-text: rgba(white, 0.4), placeholder-text: rgba(white, 0.4),
icon: rgba(white, 0.54), icon: rgba(white, 0.54),
icons: rgba(white, 0.54), icons: rgba(white, 0.54),
@@ -332,7 +335,7 @@ $caos-light-theme-foreground: (
disabled-text: $dark-disabled-text, disabled-text: $dark-disabled-text,
elevation: black, elevation: black,
hint-text: $dark-disabled-text, hint-text: $dark-disabled-text,
secondary-text: $dark-secondary-text, secondary-text: $caos-light-theme-secondary-text,
placeholder-text: rgba(black, 0.3), placeholder-text: rgba(black, 0.3),
icon: rgba(black, 0.54), icon: rgba(black, 0.54),
icons: rgba(black, 0.54), icons: rgba(black, 0.54),
@@ -340,7 +343,7 @@ $caos-light-theme-foreground: (
slider-min: rgba(black, 0.87), slider-min: rgba(black, 0.87),
slider-off: rgba(black, 0.26), slider-off: rgba(black, 0.26),
slider-off-active: rgba(black, 0.38), slider-off-active: rgba(black, 0.38),
infosection: #4a4a4a, infosection: map-get(map-get($caos-light-background, contrast), 600),
warninfosection: #620e0e, warninfosection: #620e0e,
alertinfosection: rgb(146, 64, 14), alertinfosection: rgb(146, 64, 14),
successinfosection: #0e6245, successinfosection: #0e6245,
@@ -514,7 +517,7 @@ $custom-typography: mat.define-typography-config(
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
background-color: map-get($background, background); background-color: map-get($background, background);
border-radius: 8px; border-radius: 8px;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !important; transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !important;

View File

@@ -21,7 +21,7 @@
font-size: 1rem; font-size: 1rem;
border: none; border: none;
border: 1px solid if($is-dark-theme, #f9f7f725, #1a191938); border: 1px solid if($is-dark-theme, #f9f7f725, #1a191938);
background-color: if($is-dark-theme, #00000020, #00000005); background-color: if($is-dark-theme, #00000020, #00000004);
border-radius: 4px; border-radius: 4px;
height: 40px; height: 40px;
padding: 10px; padding: 10px;