mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:27:32 +00:00
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:
@@ -75,7 +75,7 @@ const routes: Routes = [
|
||||
loadChildren: () => import('./pages/actions/actions.module').then((m) => m.ActionsModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['org.read'],
|
||||
roles: ['org.action.read', 'org.flow.read'],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@@ -11,8 +11,6 @@
|
||||
$warn-color: mat.get-color-from-palette($warn, 500);
|
||||
$accent-color: mat.get-color-from-palette($accent, 500);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$back: map-get($background, background);
|
||||
$base: map-get($foreground, base);
|
||||
|
||||
.main-container {
|
||||
display: flex;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { BreakpointObserver } from '@angular/cdk/layout';
|
||||
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||
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 { MatDrawer } from '@angular/material/sidenav';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
@@ -19,8 +19,7 @@ import { KeyboardShortcutsService } from './services/keyboard-shortcuts/keyboard
|
||||
import { ManagementService } from './services/mgmt.service';
|
||||
import { NavigationService } from './services/navigation.service';
|
||||
import { OverlayWorkflowService } from './services/overlay/overlay-workflow.service';
|
||||
import { IntroWorkflowOverlays } from './services/overlay/workflows';
|
||||
import { StorageLocation, StorageService } from './services/storage.service';
|
||||
import { StorageService } from './services/storage.service';
|
||||
import { ThemeService } from './services/theme.service';
|
||||
import { UpdateService } from './services/update.service';
|
||||
|
||||
@@ -30,7 +29,7 @@ import { UpdateService } from './services/update.service';
|
||||
styleUrls: ['./app.component.scss'],
|
||||
animations: [toolbarAnimation, ...navAnimations, accountCard, routeAnimations, adminLineAnimation],
|
||||
})
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
export class AppComponent implements OnDestroy {
|
||||
@ViewChild('drawer') public drawer!: MatDrawer;
|
||||
public isHandset$: Observable<boolean> = this.breakpointObserver.observe('(max-width: 599px)').pipe(
|
||||
map((result) => {
|
||||
@@ -218,7 +217,10 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
.then((org) => {
|
||||
this.org = org;
|
||||
|
||||
this.startIntroWorkflow();
|
||||
this.loadPrivateLabelling();
|
||||
|
||||
// TODO add when console storage is implemented
|
||||
// this.startIntroWorkflow();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.router.navigate(['/users/me']);
|
||||
@@ -235,21 +237,19 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
private startIntroWorkflow(): void {
|
||||
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);
|
||||
}
|
||||
// TODO implement Console storage
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.loadPrivateLabelling();
|
||||
}
|
||||
// private startIntroWorkflow(): void {
|
||||
// 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 {
|
||||
this.destroy$.next();
|
||||
|
@@ -63,6 +63,11 @@
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.filter-select-method .mat-select {
|
||||
height: 36px;
|
||||
padding: 7px 10px;
|
||||
}
|
||||
|
||||
.subquery {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -82,6 +87,8 @@
|
||||
}
|
||||
|
||||
.filter-input-value {
|
||||
flex: 1;
|
||||
|
||||
input {
|
||||
height: 36px;
|
||||
font-size: 15px;
|
||||
|
@@ -96,7 +96,7 @@
|
||||
margin: 0.5rem 0;
|
||||
|
||||
a {
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@@ -57,7 +57,7 @@
|
||||
|
||||
.org-link {
|
||||
font-size: 14px;
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 0.75rem 0.5rem 0.75rem;
|
||||
height: 2.5rem;
|
||||
@@ -90,7 +90,7 @@
|
||||
.org-switch-button {
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
border-radius: 6px;
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
@@ -118,7 +118,7 @@
|
||||
|
||||
svg {
|
||||
opacity: 0.7;
|
||||
fill: map-get($foreground, base);
|
||||
fill: map-get($foreground, text);
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
}
|
||||
@@ -135,7 +135,7 @@
|
||||
|
||||
.breadcrumb-link {
|
||||
font-size: 14px;
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 0.75rem 0.5rem 0.75rem;
|
||||
height: 2.5rem;
|
||||
|
@@ -44,7 +44,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
|
||||
&.top-left {
|
||||
&::before {
|
||||
|
@@ -14,6 +14,7 @@
|
||||
padding-right: 1rem;
|
||||
font-size: 14px;
|
||||
margin: 0.5rem 0;
|
||||
box-shadow: 0 2px 5px rgb(0 0 0 / 10%);
|
||||
|
||||
&.fit {
|
||||
width: fit-content;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<h1 mat-dialog-title>
|
||||
<span>{{data.titleKey | translate}} {{data?.number}}</span>
|
||||
<span>{{ data.titleKey | translate }} {{ data?.number }}</span>
|
||||
</h1>
|
||||
<p class="desc cnsl-secondary-text">{{data.descKey | translate}}</p>
|
||||
<p class="desc cnsl-secondary-text">{{ data.descKey | translate }}</p>
|
||||
<div mat-dialog-content>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ data.labelKey | translate }}</cnsl-label>
|
||||
@@ -9,12 +9,11 @@
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button color="primary" mat-stroked-button class="ok-button" (click)="closeDialog()">
|
||||
{{'ACTIONS.CLOSE' | translate}}
|
||||
<button color="primary" mat-stroked-button (click)="closeDialog()">
|
||||
{{ 'ACTIONS.CLOSE' | translate }}
|
||||
</button>
|
||||
|
||||
<button [disabled]="!name" cdkFocusInitial color="primary" mat-raised-button class="ok-button"
|
||||
(click)="closeDialog(name)">
|
||||
{{'ACTIONS.RENAME' | translate}}
|
||||
<button [disabled]="!name" cdkFocusInitial color="primary" mat-raised-button (click)="closeDialog(name)">
|
||||
{{ 'ACTIONS.RENAME' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -15,10 +15,6 @@ h1 {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.ok-button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
@@ -1,25 +1,29 @@
|
||||
<div class="paginator-wrapper">
|
||||
<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">
|
||||
{{(timestamp | timestampToDate)| localizedDate: 'EEEE dd. MMM YYYY, HH:mm'}}
|
||||
{{ timestamp | timestampToDate | localizedDate: 'EEEE dd. MMM YYYY, HH:mm' }}
|
||||
</p>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<span class="pos cnsl-secondary-text" *ngIf="!hidePagination">{{pageIndex * pageSize}} - {{pageIndex * pageSize +
|
||||
pageSize}} </span>
|
||||
<span class="pos cnsl-secondary-text" *ngIf="!hidePagination"
|
||||
>{{ pageIndex * pageSize }} - {{ pageIndex * pageSize + pageSize }}
|
||||
</span>
|
||||
<div class="row" *ngIf="!hidePagination">
|
||||
<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">
|
||||
{{sizeOption}}
|
||||
{{ sizeOption }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
<button *ngIf="previousPossible" (click)="previous()" [disabled]="!previousPossible"
|
||||
mat-stroked-button>{{'PAGINATOR.PREVIOUS' |
|
||||
translate}}</button>
|
||||
<button *ngIf="nextPossible" (click)="next()" [disabled]="!nextPossible" mat-stroked-button>{{'PAGINATOR.NEXT' |
|
||||
translate}}</button>
|
||||
<button *ngIf="previousPossible" (click)="previous()" [disabled]="!previousPossible" mat-stroked-button>
|
||||
{{ 'PAGINATOR.PREVIOUS' | translate }}
|
||||
</button>
|
||||
<button *ngIf="nextPossible" (click)="next()" [disabled]="!nextPossible" mat-stroked-button>
|
||||
{{ 'PAGINATOR.NEXT' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -50,7 +50,7 @@
|
||||
}
|
||||
|
||||
/* stylelint-disable */
|
||||
::ng-deep .mat-select {
|
||||
::ng-deep .paginator-select.mat-select {
|
||||
min-width: 60px;
|
||||
height: 36px !important;
|
||||
padding-top: 8px !important;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<h1 mat-dialog-title class="title">
|
||||
<h1 class="title">
|
||||
<span>{{ data.title | translate }}</span>
|
||||
</h1>
|
||||
<div mat-dialog-content>
|
||||
|
@@ -1,9 +1,10 @@
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
|
@@ -19,6 +19,7 @@ export class DialogAddTypeComponent {
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<DialogAddTypeComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {
|
||||
this.availableMfaTypes = data.types;
|
||||
this.newMfaType = data.types && data.types[0] ? data.types[0] : undefined;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<button
|
||||
mat-stroked-button
|
||||
class="new-mfa cnsl-action-button"
|
||||
[disabled]="disabled"
|
||||
[disabled]="disabled || availableSelection.length === 0"
|
||||
(click)="!disabled ? addMfa() : null"
|
||||
>
|
||||
<mat-icon class="icon">add</mat-icon>
|
||||
|
@@ -35,7 +35,7 @@
|
||||
display: block;
|
||||
|
||||
&:not(:hover) {
|
||||
color: var(--grey);
|
||||
color: map-get($foreground, secondary-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -105,27 +105,12 @@ export class FactorTableComponent implements OnInit {
|
||||
}
|
||||
|
||||
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, {
|
||||
data: {
|
||||
title: 'MFA.CREATE.TITLE',
|
||||
desc: 'MFA.CREATE.DESCRIPTION',
|
||||
componentType: this.componentType,
|
||||
types: selection,
|
||||
types: this.availableSelection,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
@@ -244,4 +229,17 @@ export class FactorTableComponent implements OnInit {
|
||||
this.getData();
|
||||
}, 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;
|
||||
}
|
||||
}
|
||||
|
@@ -122,7 +122,6 @@ export class LoginPolicyComponent implements OnInit {
|
||||
)
|
||||
.pipe(take(1))
|
||||
.subscribe((allowed) => {
|
||||
console.log(allowed);
|
||||
if (allowed) {
|
||||
this.lifetimeForm.enable();
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="message-text-content">
|
||||
<cnsl-edit-text
|
||||
[chips]="chips[currentType]"
|
||||
[disabled]="(canWrite$ | async) === false"
|
||||
@@ -43,7 +43,7 @@
|
||||
></cnsl-edit-text>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<div class="message-text-actions">
|
||||
<button
|
||||
class="reset-button"
|
||||
*ngIf="(getCustomInitMessageTextMap$ | async) && (getCustomInitMessageTextMap$ | async)?.isDefault === false"
|
||||
|
@@ -40,11 +40,11 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.content {
|
||||
.message-text-content {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
.message-text-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 1rem;
|
||||
|
@@ -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>
|
||||
<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">
|
||||
<div class="lockout-content">
|
||||
@@ -38,17 +46,4 @@
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</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>
|
||||
<!-- </cnsl-card> -->
|
||||
|
@@ -1,7 +1,21 @@
|
||||
<h2>{{ 'POLICY.PRIVACY_POLICY.TITLE' | translate }}</h2>
|
||||
<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>
|
||||
<form *ngIf="form" [formGroup]="form" class="policy-content">
|
||||
@@ -26,17 +40,6 @@
|
||||
</div>
|
||||
|
||||
<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
|
||||
class="save-button"
|
||||
[disabled]="(canWrite$ | async) === false"
|
||||
|
@@ -61,18 +61,8 @@
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
.save-button,
|
||||
.reset-button {
|
||||
.save-button {
|
||||
display: block;
|
||||
margin: 0 1rem 0 0;
|
||||
}
|
||||
|
||||
.reset-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
i {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@
|
||||
pointer-events: none;
|
||||
border-radius: 0.5rem;
|
||||
transform: scale(0.9);
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
|
@@ -54,7 +54,7 @@
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button
|
||||
class="pl-action-button"
|
||||
*ngIf="view === View.CURRENT && serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{ 'POLICY.RESET' | translate }}"
|
||||
color="warn"
|
||||
(click)="removePolicy()"
|
||||
|
@@ -93,6 +93,11 @@
|
||||
.pl-action-button {
|
||||
align-self: flex-start;
|
||||
margin-top: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,20 +1,25 @@
|
||||
<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>
|
||||
<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>
|
||||
<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">
|
||||
<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-group>
|
||||
<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 mat-dialog-actions class="action">
|
||||
<button cdkFocusInitial mat-stroked-button class="ok-button" (click)="closeDialog()">
|
||||
{{'ACTIONS.CLOSE' | translate}}
|
||||
<button cdkFocusInitial mat-stroked-button (click)="closeDialog()">
|
||||
{{ 'ACTIONS.CLOSE' | translate }}
|
||||
</button>
|
||||
|
||||
<button [disabled]="setting === undefined" cdkFocusInitial color="primary" mat-raised-button class="ok-button"
|
||||
(click)="closeDialog(setting)">
|
||||
{{'ACTIONS.OK' | translate}}
|
||||
<button
|
||||
[disabled]="setting === undefined"
|
||||
cdkFocusInitial
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
(click)="closeDialog(setting)"
|
||||
>
|
||||
{{ 'ACTIONS.OK' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -7,11 +7,16 @@
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.radio-button {
|
||||
margin: 0.5rem 0;
|
||||
.project-radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.label {
|
||||
white-space: normal;
|
||||
.radio-button {
|
||||
margin: 0.5rem 0;
|
||||
|
||||
.label {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +29,6 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.ok-button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
<div class="found" *ngIf="users.length > 0">
|
||||
<!-- <span class="found-label cnsl-secondary-text">{{'USER.SEARCH.FOUND' | translate}}:</span> -->
|
||||
<div class="user-autocomplete-found" *ngIf="users.length > 0">
|
||||
<div class="found-user-row" *ngFor="let user of users; index as i">
|
||||
<div class="circle">
|
||||
<cnsl-avatar
|
||||
@@ -109,9 +108,11 @@
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
|
||||
<span class="target-desc">
|
||||
<span class="user-autocomplete-target-desc">
|
||||
{{ 'USER.TARGET.SELF' | translate }}
|
||||
<a (click)="changeTarget()">{{ 'USER.TARGET.CLICKHERE' | translate }}</a>
|
||||
<a (click)="changeTarget()"
|
||||
><strong>{{ 'USER.TARGET.CLICKHERE' | translate }}</strong></a
|
||||
>
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
@@ -122,9 +123,11 @@
|
||||
<cnsl-form-field class="user-create-form-field more-space">
|
||||
<cnsl-label>{{ 'USER.SEARCH.ADDITIONAL-EXTERNAL' | translate }}</cnsl-label>
|
||||
<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 }}
|
||||
<a (click)="changeTarget()">{{ 'USER.TARGET.CLICKHERE' | translate }}</a>
|
||||
<a (click)="changeTarget()"
|
||||
><strong>{{ 'USER.TARGET.CLICKHERE' | translate }}</strong></a
|
||||
>
|
||||
</span>
|
||||
</cnsl-form-field>
|
||||
|
||||
|
@@ -1,15 +1,79 @@
|
||||
.target-desc {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
margin-top: 0.5rem;
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
a {
|
||||
color: #4072b4;
|
||||
@mixin search-user-autocomplete-theme($theme) {
|
||||
$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 {
|
||||
cursor: pointer;
|
||||
color: #6992c9;
|
||||
text-decoration: underline;
|
||||
.user-autocomplete-found {
|
||||
margin: 0.5rem 0;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem 0;
|
||||
max-width: 500px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.add-user-row {
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
.found-user-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0;
|
||||
|
||||
button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
button {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.found-label {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.user-name-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.smaller {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.search-user-dl-btn {
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:not(:hover) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,60 +100,6 @@
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.found {
|
||||
margin: 0.5rem 0;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem 0;
|
||||
max-width: 500px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.add-user-row {
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
.found-user-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0;
|
||||
|
||||
button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
button {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.found-label {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.user-name-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.smaller {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.search-user-dl-btn {
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:not(:hover) {
|
||||
color: var(--grey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.circle {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
@@ -2,10 +2,10 @@ export interface SettingLinks {
|
||||
i18nTitle: string;
|
||||
i18nDesc: string;
|
||||
iamRouterLink: any;
|
||||
orgRouterLink: any;
|
||||
orgRouterLink?: any;
|
||||
queryParams: any;
|
||||
iamWithRole: string[];
|
||||
orgWithRole: string[];
|
||||
iamWithRole?: string[];
|
||||
orgWithRole?: string[];
|
||||
icon?: string;
|
||||
svgIcon?: string;
|
||||
color: string;
|
||||
@@ -49,12 +49,10 @@ export const PRIVACY_POLICY: SettingLinks = {
|
||||
|
||||
export const NOTIFICATION_GROUP: SettingLinks = {
|
||||
i18nTitle: 'SETTINGS.GROUPS.NOTIFICATIONS',
|
||||
i18nDesc: '',
|
||||
i18nDesc: 'SETTINGS.LIST.NOTIFICATIONS_DESC',
|
||||
iamRouterLink: ['/settings'],
|
||||
orgRouterLink: ['/org-settings'],
|
||||
queryParams: { id: 'notifications' },
|
||||
iamWithRole: ['iam.policy.read'],
|
||||
orgWithRole: ['policy.read'],
|
||||
icon: 'las la-bell',
|
||||
color: 'red',
|
||||
};
|
||||
|
@@ -1,5 +1,5 @@
|
||||
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 { 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 tag: string = '';
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
public SETTINGS: SettingLinks[] = SETTINGLINKS;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.SETTINGS = this.SETTINGS.filter((setting) =>
|
||||
this.type === PolicyComponentServiceType.MGMT ? !!setting.orgRouterLink : !!setting.iamRouterLink,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,99 +1,132 @@
|
||||
<div cdkDropListGroup>
|
||||
<div class="shortcut-container">
|
||||
<div class="shortcut-title-container">
|
||||
<h2>{{'HOME.SHORTCUTS.SHORTCUTS' | translate}}</h2>
|
||||
<button [matTooltip]="(editState ? 'ACTIONS.CLOSE' : 'ACTIONS.EDIT') | translate" class="shortcut-btn"
|
||||
(click)="editState = !editState" mat-icon-button>
|
||||
<i *ngIf="!editState" class=" las la-pen"></i>
|
||||
<mat-icon *ngIf="editState">close</mat-icon>
|
||||
<h2>{{ 'HOME.SHORTCUTS.SHORTCUTS' | translate }}</h2>
|
||||
<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-check"></i>
|
||||
</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>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="shortcut-list-row">
|
||||
<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-template cnslHasRole [hasRole]="shortcut.withRole">
|
||||
<a [routerLink]="!editState ? shortcut.routerLink : 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}}">
|
||||
<a
|
||||
[routerLink]="!editState ? shortcut.routerLink : null"
|
||||
[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 }}">
|
||||
<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>
|
||||
<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 *ngIf="shortcut.i18nTitle">{{ shortcut.i18nTitle | translate }}</span>
|
||||
<span *ngIf="shortcut.title">{{ shortcut.title }}</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
|
||||
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>
|
||||
</a>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div cdkDropList [cdkDropListData]="secondary" class="shortcut-list"
|
||||
(cdkDropListDropped)="drop($event, 'secondary')">
|
||||
<p *ngIf="editState" class="shortcut-desc">{{'HOME.SHORTCUTS.REORDER' | translate}}</p>
|
||||
<div cdkDropList [cdkDropListData]="secondary" class="shortcut-list" (cdkDropListDropped)="drop($event, 'secondary')">
|
||||
<p *ngIf="editState" class="shortcut-desc cnsl-secondary-text">{{ 'HOME.SHORTCUTS.REORDER' | translate }}</p>
|
||||
<ng-container *ngFor="let shortcut of secondary">
|
||||
<ng-template cnslHasRole [hasRole]="shortcut.withRole">
|
||||
<a [routerLink]="!editState ? shortcut.routerLink : null" class="shortcut-box"
|
||||
[ngClass]="{'edit-state': editState && !shortcut.disabled}" cdkDrag
|
||||
[cdkDragDisabled]="shortcut.disabled || !editState">
|
||||
<div class="shortcuts-avatar {{shortcut.color}}">
|
||||
<a
|
||||
[routerLink]="!editState ? shortcut.routerLink : null"
|
||||
[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 }}">
|
||||
<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>
|
||||
<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 *ngIf="shortcut.i18nTitle">{{ shortcut.i18nTitle | translate }}</span>
|
||||
<span *ngIf="shortcut.title">{{ shortcut.title }}</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
|
||||
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>
|
||||
</a>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<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-template cnslHasRole [hasRole]="shortcut.withRole">
|
||||
<a [routerLink]="!editState ? shortcut.routerLink : null" class="shortcut-box"
|
||||
[ngClass]="{'edit-state': editState && !shortcut.disabled}" cdkDrag
|
||||
[cdkDragDisabled]="shortcut.disabled || !editState">
|
||||
<div class="shortcuts-avatar {{shortcut.color}}">
|
||||
<a
|
||||
[routerLink]="!editState ? shortcut.routerLink : null"
|
||||
[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 }}">
|
||||
<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>
|
||||
<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 *ngIf="shortcut.i18nTitle">{{ shortcut.i18nTitle | translate }}</span>
|
||||
<span *ngIf="shortcut.title">{{ shortcut.title }}</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
|
||||
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>
|
||||
</a>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
@@ -102,92 +135,48 @@
|
||||
</div>
|
||||
|
||||
<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')">
|
||||
<p *ngIf="editState" class="shortcut-desc">{{'HOME.SHORTCUTS.REORDER' | translate}}</p>
|
||||
<ng-container *ngFor="let shortcut of allRoutes">
|
||||
<div
|
||||
cdkDropList
|
||||
[cdkDropListData]="allAvailableShortcuts"
|
||||
class="available-shortcut-list"
|
||||
(cdkDropListDropped)="drop($event, 'main')"
|
||||
>
|
||||
<ng-container *ngFor="let shortcut of allAvailableShortcuts">
|
||||
<ng-template cnslHasRole [hasRole]="shortcut.withRole">
|
||||
<div class="shortcut-box"
|
||||
[ngClass]="{'edit-state': editState && !shortcut.disabled, 'disabled': editState && shortcut.disabled}"
|
||||
cdkDrag [cdkDragDisabled]="shortcut.disabled">
|
||||
<div class="shortcuts-avatar {{shortcut.color}}">
|
||||
<div
|
||||
class="shortcut-box"
|
||||
[ngClass]="{ 'edit-state': editState && !shortcut.disabled, disabled: 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>
|
||||
<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>
|
||||
<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]="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
|
||||
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>
|
||||
@@ -207,4 +196,4 @@
|
||||
<span *ngIf="shortcut.title">{{shortcut.title}}</span>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-template> -->
|
||||
</ng-template> -->
|
||||
|
@@ -62,7 +62,6 @@
|
||||
.shortcut-desc {
|
||||
align-self: center;
|
||||
font-size: 14px;
|
||||
color: var(--grey);
|
||||
margin-top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -89,7 +88,6 @@
|
||||
.shortcut-desc {
|
||||
align-self: center;
|
||||
font-size: 14px;
|
||||
color: var(--grey);
|
||||
margin-top: 0;
|
||||
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 {
|
||||
box-sizing: border-box;
|
||||
border-radius: 1rem;
|
||||
|
@@ -17,7 +17,8 @@ export interface ShortcutItem {
|
||||
i18nTitle?: string;
|
||||
i18nDesc?: string;
|
||||
routerLink: any;
|
||||
withRole: string[];
|
||||
queryParams?: any;
|
||||
withRole?: string[];
|
||||
icon?: string;
|
||||
label?: string;
|
||||
svgIcon?: string;
|
||||
@@ -138,8 +139,9 @@ export class ShortcutsComponent implements OnDestroy {
|
||||
type: ShortcutType.POLICY,
|
||||
i18nTitle: p.i18nTitle,
|
||||
i18nDesc: p.i18nDesc,
|
||||
routerLink: p.orgRouterLink,
|
||||
withRole: p.orgWithRole,
|
||||
routerLink: p.orgRouterLink ?? p.iamRouterLink,
|
||||
queryParams: p.queryParams,
|
||||
withRole: p.orgWithRole ?? p.iamWithRole,
|
||||
icon: p.icon ?? '',
|
||||
svgIcon: p.svgIcon ?? '',
|
||||
color: p.color ?? '',
|
||||
@@ -176,10 +178,10 @@ export class ShortcutsComponent implements OnDestroy {
|
||||
} else {
|
||||
switch (listName) {
|
||||
case 'main':
|
||||
this.main = [PROFILE_SHORTCUT, CREATE_ORG, CREATE_PROJECT, CREATE_USER];
|
||||
this.main = [PROFILE_SHORTCUT /* CREATE_ORG, CREATE_PROJECT, CREATE_USER */];
|
||||
break;
|
||||
case 'secondary':
|
||||
this.secondary = [];
|
||||
this.secondary = [CREATE_ORG, CREATE_PROJECT, CREATE_USER];
|
||||
// [LOGIN_POLICY, PRIVATELABEL_POLICY].map((p) => {
|
||||
// const policy: string = {
|
||||
// i18nTitle: p.i18nTitle,
|
||||
@@ -194,7 +196,7 @@ export class ShortcutsComponent implements OnDestroy {
|
||||
// });
|
||||
break;
|
||||
case 'third':
|
||||
this.third = [];
|
||||
this.third = this.ALL_SHORTCUTS.filter((item) => item.i18nTitle === 'SETTINGS.GROUPS.APPEARANCE');
|
||||
// [LOGIN_TEXTS_POLICY, MESSAGE_TEXTS_POLICY].map((p) => {
|
||||
// const policy: ShortcutItem = {
|
||||
// i18nTitle: p.i18nTitle,
|
||||
@@ -276,4 +278,8 @@ export class ShortcutsComponent implements OnDestroy {
|
||||
public get allProjects(): ShortcutItem[] {
|
||||
return this.all.filter((s) => s.type === ShortcutType.PROJECT);
|
||||
}
|
||||
|
||||
public get allAvailableShortcuts(): ShortcutItem[] {
|
||||
return [...this.allRoutes, ...this.allPolicies, ...this.allProjects];
|
||||
}
|
||||
}
|
||||
|
@@ -1,30 +1,37 @@
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<ng-container *ngIf="tokenResponse">
|
||||
<div class="row">
|
||||
<p class="left">{{'USER.PERSONALACCESSTOKEN.ID' | translate}}</p>
|
||||
<p class="right">{{tokenResponse.tokenId}}</p>
|
||||
<p class="left cnsl-secondary-text">{{ 'USER.PERSONALACCESSTOKEN.ID' | translate }}</p>
|
||||
<p class="right">{{ tokenResponse.tokenId }}</p>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<button class="ctc" [disabled]="copied === tokenResponse.token"
|
||||
[matTooltip]="(copied !== tokenResponse.token ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate"
|
||||
cnslCopyToClipboard [valueToCopy]="tokenResponse.token" (copiedValue)="copied = $event" mat-icon-button>
|
||||
<button
|
||||
class="ctc"
|
||||
[disabled]="copied === tokenResponse.token"
|
||||
[matTooltip]="(copied !== tokenResponse.token ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"
|
||||
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-check"></i>
|
||||
</button>
|
||||
<span>{{tokenResponse.token}}</span>
|
||||
<span>{{ tokenResponse.token }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button color="primary" mat-raised-button class="ok-button" (click)="closeDialog()">
|
||||
{{'ACTIONS.CLOSE' | translate}}
|
||||
{{ 'ACTIONS.CLOSE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -28,7 +28,6 @@
|
||||
}
|
||||
|
||||
.left {
|
||||
color: var(--grey);
|
||||
margin-right: 1rem;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
|
@@ -69,7 +69,7 @@
|
||||
padding: 0.75rem 0;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
opacity: 0.6;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 1rem;
|
||||
|
@@ -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>
|
||||
<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-label>Domain</cnsl-label>
|
||||
@@ -9,11 +9,10 @@
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button mat-stroked-button (click)="closeDialog()">
|
||||
{{'ACTIONS.CANCEL' | translate}}
|
||||
{{ 'ACTIONS.CANCEL' | translate }}
|
||||
</button>
|
||||
|
||||
<button color="primary" mat-raised-button class="ok-button" [disabled]="!newdomain"
|
||||
(click)="closeDialogWithSuccess()">
|
||||
{{'ACTIONS.ADD' | translate}}
|
||||
<button color="primary" mat-raised-button class="ok-button" [disabled]="!newdomain" (click)="closeDialogWithSuccess()">
|
||||
{{ 'ACTIONS.ADD' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -4,7 +4,6 @@
|
||||
}
|
||||
|
||||
.desc {
|
||||
color: var(--grey);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
|
@@ -1,18 +1,34 @@
|
||||
<div class="max-width-container">
|
||||
<div class="enlarged-container">
|
||||
<h1>{{ 'GRANTS.TITLE' | translate }}</h1>
|
||||
<p class="desc cnsl-secondary-text">{{'GRANTS.DESC' | translate }}</p>
|
||||
<cnsl-user-grants *ngIf="grantContext === UserGrantContext.NONE"
|
||||
[displayedColumns]="['select', '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']">
|
||||
<p class="grants-desc cnsl-secondary-text">{{ 'GRANTS.DESC' | translate }}</p>
|
||||
<cnsl-user-grants
|
||||
*ngIf="grantContext === UserGrantContext.NONE"
|
||||
[displayedColumns]="[
|
||||
'select',
|
||||
'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 *ngIf="grantContext === UserGrantContext.OWNED_PROJECT" [context]="UserGrantContext.OWNED_PROJECT"
|
||||
[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
|
||||
*ngIf="grantContext === UserGrantContext.OWNED_PROJECT"
|
||||
[context]="UserGrantContext.OWNED_PROJECT"
|
||||
[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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,7 +2,7 @@ h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-bottom: 2rem;
|
||||
.grants-desc {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<cnsl-top-view
|
||||
title="{{ app?.name }}"
|
||||
[hasActions]="isZitadel === false && (['project.app.write:' + projectId, 'project.app.write'] | hasRole | async)"
|
||||
docLink="https://docs.zitadel.ch/docs/guides/basics/projects"
|
||||
docLink="https://docs.zitadel.com/docs/guides/basics/projects"
|
||||
[sub]="app?.oidcConfig ? ('APP.OIDC.APPTYPE.' + app?.oidcConfig?.appType | translate) : 'API'"
|
||||
[isActive]="app?.state === AppState.APP_STATE_ACTIVE"
|
||||
[isInactive]="app?.state === AppState.APP_STATE_INACTIVE"
|
||||
|
@@ -30,6 +30,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.redirect-section {
|
||||
display: block;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.app-desc {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
@@ -131,6 +136,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2));
|
||||
color: white;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
margin-bottom: 1rem;
|
||||
|
@@ -14,18 +14,11 @@
|
||||
</cnsl-auth-method-radio>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button color="primary" mat-stroked-button class="ok-button" (click)="closeDialog()">
|
||||
<button color="primary" mat-stroked-button (click)="closeDialog()">
|
||||
{{ 'ACTIONS.CLOSE' | translate }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
[disabled]="!authmethod"
|
||||
cdkFocusInitial
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="ok-button"
|
||||
(click)="closeDialogWithMethod()"
|
||||
>
|
||||
<button [disabled]="!authmethod" cdkFocusInitial color="primary" mat-raised-button (click)="closeDialogWithMethod()">
|
||||
{{ 'ACTIONS.SELECT' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
@@ -13,11 +13,7 @@ h1 {
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.ok-button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
justify-content: space-between;
|
||||
|
||||
button {
|
||||
border-radius: 0.5rem;
|
||||
|
@@ -1,27 +1,40 @@
|
||||
<form class="form" (ngSubmit)="add(redInput)" [attr.data-e2e]="'redirect-uris'">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ title }}</cnsl-label>
|
||||
<form class="redirect-uris-form" (ngSubmit)="add(redInput)" [attr.data-e2e]="'redirect-uris'">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ title }}</cnsl-label>
|
||||
|
||||
<input #redInput cnslInput placeholder="ex. https://" [formControl]="redirectControl">
|
||||
</cnsl-form-field>
|
||||
<button matTooltip="{{'ACTIONS.ADD' | translate}}" type="submit" mat-icon-button
|
||||
[disabled]="redirectControl.invalid || !canWrite">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
<input #redInput cnslInput placeholder="ex. https://" [formControl]="redirectControl" />
|
||||
</cnsl-form-field>
|
||||
<button
|
||||
matTooltip="{{ 'ACTIONS.ADD' | translate }}"
|
||||
type="submit"
|
||||
mat-icon-button
|
||||
[disabled]="redirectControl.invalid || !canWrite"
|
||||
>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="uri-list">
|
||||
<div *ngFor="let uri of urisList" class="uri-line">
|
||||
<span class="uri"
|
||||
[ngClass]="{'green': !devMode && uri?.startsWith('https://'), 'red': !devMode && !uri?.startsWith('https://')}">{{uri}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<i *ngIf="!devMode && !(uri | redirect : isNative)" class="las la-exclamation red" [matTooltip]="isNative ? ('APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate) : ('APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate)"></i>
|
||||
<div class="redirect-uris-list">
|
||||
<div *ngFor="let uri of urisList" class="uri-line" [ngClass]="{ alert: !devMode && !(uri | redirect: isNative) }">
|
||||
<span
|
||||
class="uri"
|
||||
[ngClass]="{ green: !devMode && uri?.startsWith('https://'), red: !devMode && !uri?.startsWith('https://') }"
|
||||
>{{ uri }}</span
|
||||
>
|
||||
<span class="fill-space"></span>
|
||||
<i
|
||||
class="alerticon las la-exclamation red"
|
||||
[matTooltip]="
|
||||
isNative ? ('APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate) : ('APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate)
|
||||
"
|
||||
></i>
|
||||
|
||||
<button matTooltip="{{'ACTIONS.DELETE' | translate}}" mat-icon-button (click)="remove(uri)" class="icon-button">
|
||||
<mat-icon class="icon">cancel</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button matTooltip="{{ 'ACTIONS.DELETE' | translate }}" mat-icon-button (click)="remove(uri)" class="icon-button">
|
||||
<mat-icon class="icon">cancel</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p *ngIf="redirectControl.value && redirectControl.invalid" class="error">
|
||||
{{'APP.OIDC.REDIRECTNOTVALID' | translate}}</p>
|
||||
<p *ngIf="redirectControl.value && redirectControl.invalid" class="redirect-uris-error">
|
||||
{{ 'APP.OIDC.REDIRECTNOTVALID' | translate }}
|
||||
</p>
|
||||
|
@@ -1,4 +1,80 @@
|
||||
.form {
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
@mixin redirect-uris-theme($theme) {
|
||||
$foreground: map-get($theme, foreground);
|
||||
$background: map-get($theme, background);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$warn: map-get($theme, warn);
|
||||
$warn-color: map-get($warn, 500);
|
||||
$button-text-color: map-get($foreground, text);
|
||||
$button-disabled-text-color: map-get($foreground, disabled-button);
|
||||
$divider-color: map-get($foreground, dividers);
|
||||
$secondary-text: map-get($foreground, secondary-text);
|
||||
|
||||
.redirect-uris-list {
|
||||
width: 100%;
|
||||
|
||||
.uri-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0.5rem 0;
|
||||
padding: 0 0 0 0.75rem;
|
||||
border-radius: 4px;
|
||||
background: map-get($background, infosection);
|
||||
|
||||
.uri {
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
font-size: 14px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
i.green {
|
||||
font-size: 1rem;
|
||||
line-height: 35px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
|
||||
.icon {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
&:not(:hover) {
|
||||
color: $secondary-text;
|
||||
}
|
||||
}
|
||||
|
||||
.alerticon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.alert {
|
||||
background-color: map-get($background, alertinfosection);
|
||||
color: map-get($foreground, alertinfosection);
|
||||
|
||||
.alerticon {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.redirect-uris-error {
|
||||
font-size: 13px;
|
||||
color: $warn-color;
|
||||
margin: 0 0.5rem 1.5rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.redirect-uris-form {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
min-width: 320px;
|
||||
@@ -12,52 +88,3 @@
|
||||
margin-right: -0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.uri-list {
|
||||
margin: 0 0.5rem;
|
||||
width: 100%;
|
||||
|
||||
.uri-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.uri {
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
font-size: 14px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
i.green {
|
||||
font-size: 1rem;
|
||||
line-height: 35px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
i.red {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
|
||||
.icon {
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
&:not(:hover) {
|
||||
color: var(--grey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
font-size: 13px;
|
||||
color: var(--warn);
|
||||
margin: 0 0.5rem 1.5rem 0.5rem;
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
})
|
||||
export class ApplicationGridComponent implements OnInit {
|
||||
@Input() public projectId: string = '';
|
||||
@Input() public disabled: boolean = false;
|
||||
@Input() public disabled: boolean = true;
|
||||
@Output() public changeView: EventEmitter<void> = new EventEmitter();
|
||||
public appsSubject: BehaviorSubject<App.AsObject[]> = new BehaviorSubject<App.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
|
@@ -6,7 +6,7 @@
|
||||
[isInactive]="project?.state === ProjectState.PROJECT_STATE_INACTIVE"
|
||||
[hasContributors]="true"
|
||||
stateTooltip="{{ 'PROJECT.STATE.' + project?.state | translate }}"
|
||||
[hasActions]="true"
|
||||
[hasActions]="['project.app.write:' + project?.id, 'project.app.write$'] | hasRole | async"
|
||||
>
|
||||
<ng-template topActions cnslHasRole [hasRole]="['project.write:' + projectId, 'project.write']">
|
||||
<button mat-menu-item (click)="openNameDialog()" aria-label="Edit project name" *ngIf="isZitadel === false">
|
||||
@@ -68,8 +68,13 @@
|
||||
<cnsl-meta-layout>
|
||||
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList" [queryParam]="'id'">
|
||||
<ng-container *ngIf="currentSetting === 'general' && project">
|
||||
<ng-template cnslHasRole [hasRole]="['project.app.read:' + project.id, 'project.app.read']">
|
||||
<cnsl-application-grid *ngIf="grid" [disabled]="isZitadel" (changeView)="grid = false" [projectId]="projectId">
|
||||
<ng-template cnslHasRole [hasRole]="['project.app.read:' + project.id, 'project.app.read$']">
|
||||
<cnsl-application-grid
|
||||
*ngIf="grid"
|
||||
[disabled]="isZitadel || (['project.app.write:' + project.id, 'project.app.write$'] | hasRole | async) === false"
|
||||
(changeView)="grid = false"
|
||||
[projectId]="projectId"
|
||||
>
|
||||
</cnsl-application-grid>
|
||||
<cnsl-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}">
|
||||
<div class="card-actions" card-actions>
|
||||
@@ -77,7 +82,12 @@
|
||||
<i matTooltip="show grid view" class="las la-th-large"></i>
|
||||
</button>
|
||||
</div>
|
||||
<cnsl-applications [disabled]="isZitadel" [projectId]="projectId"></cnsl-applications>
|
||||
<cnsl-applications
|
||||
[disabled]="
|
||||
isZitadel || (['project.app.write:' + project.id, 'project.app.write$'] | hasRole | async) === false
|
||||
"
|
||||
[projectId]="projectId"
|
||||
></cnsl-applications>
|
||||
</cnsl-card>
|
||||
</ng-template>
|
||||
|
||||
@@ -111,7 +121,9 @@
|
||||
>
|
||||
{{ 'PROJECT.ROLE.ASSERTION' | translate }}</mat-checkbox
|
||||
>
|
||||
<p class="desc cnsl-secondary-text">{{ 'PROJECT.ROLE.ASSERTION_DESCRIPTION' | translate }}</p>
|
||||
<cnsl-info-section class="desc cnsl-secondary-text">{{
|
||||
'PROJECT.ROLE.ASSERTION_DESCRIPTION' | translate
|
||||
}}</cnsl-info-section>
|
||||
<mat-checkbox
|
||||
[(ngModel)]="project.projectRoleCheck"
|
||||
[disabled]="(['project.write$', 'project.write:' + project.id] | hasRole | async) === false"
|
||||
@@ -119,7 +131,9 @@
|
||||
>
|
||||
{{ 'PROJECT.ROLE.CHECK' | translate }}</mat-checkbox
|
||||
>
|
||||
<p class="desc cnsl-secondary-text">{{ 'PROJECT.ROLE.CHECK_DESCRIPTION' | translate }}</p>
|
||||
<cnsl-info-section class="desc cnsl-secondary-text">{{
|
||||
'PROJECT.ROLE.CHECK_DESCRIPTION' | translate
|
||||
}}</cnsl-info-section>
|
||||
<mat-checkbox
|
||||
[(ngModel)]="project.hasProjectCheck"
|
||||
[disabled]="(['project.write$', 'project.write:' + project.id] | hasRole | async) === false"
|
||||
@@ -127,10 +141,19 @@
|
||||
>
|
||||
{{ 'PROJECT.HAS_PROJECT' | translate }}</mat-checkbox
|
||||
>
|
||||
<p class="desc cnsl-secondary-text">{{ 'PROJECT.HAS_PROJECT_DESCRIPTION' | translate }}</p>
|
||||
<cnsl-info-section class="desc cnsl-secondary-text">{{
|
||||
'PROJECT.HAS_PROJECT_DESCRIPTION' | translate
|
||||
}}</cnsl-info-section>
|
||||
|
||||
<div class="project-detail-btn-container">
|
||||
<button mat-raised-button color="primary" (click)="saveProject()">{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||
<button
|
||||
mat-raised-button
|
||||
[disabled]="(['project.app.write:' + project.id, 'project.app.write$'] | hasRole | async) === false"
|
||||
color="primary"
|
||||
(click)="saveProject()"
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
</ng-template>
|
||||
|
@@ -55,5 +55,5 @@
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ import { CardModule } from 'src/app/modules/card/card.module';
|
||||
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
||||
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
|
||||
import { InfoRowModule } from 'src/app/modules/info-row/info-row.module';
|
||||
import { InfoSectionModule } from 'src/app/modules/info-section/info-section.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||
import { PaginatorModule } from 'src/app/modules/paginator/paginator.module';
|
||||
@@ -71,6 +72,7 @@ import { OwnedProjectDetailComponent } from './owned-project-detail.component';
|
||||
TopViewModule,
|
||||
MatCheckboxModule,
|
||||
MatSelectModule,
|
||||
InfoSectionModule,
|
||||
MatMenuModule,
|
||||
MatProgressSpinnerModule,
|
||||
ChangesModule,
|
||||
|
@@ -1,65 +1,104 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" title="{{ 'PROJECT.GRANT.DETAIL.TITLE' | translate }}">
|
||||
<p class="subinfo" sub>
|
||||
<span class="cnsl-secondary-text">{{ 'PROJECT.GRANT.DETAIL.DESC' | translate }}</span>
|
||||
<a mat-icon-button href="https://docs.zitadel.com/docs/concepts/structure/projects#granted-organizations"
|
||||
target="_blank">
|
||||
<a
|
||||
mat-icon-button
|
||||
href="https://docs.zitadel.com/docs/concepts/structure/projects#granted-organizations"
|
||||
target="_blank"
|
||||
>
|
||||
<i class="las la-info-circle"></i>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div actions>
|
||||
<button class="actions-trigger-desk" mat-raised-button color="primary" [matMenuTriggerFor]="actions">
|
||||
<span>{{'ACTIONS.ACTIONS' | translate}}</span>
|
||||
<span>{{ 'ACTIONS.ACTIONS' | translate }}</span>
|
||||
<mat-icon class="icon">keyboard_arrow_down</mat-icon>
|
||||
</button>
|
||||
<button class="actions-trigger-mob" matTooltip="{{'ACTIONS.ACTIONS' | translate}}" mat-icon-button
|
||||
[matMenuTriggerFor]="actions">
|
||||
<button
|
||||
class="actions-trigger-mob"
|
||||
matTooltip="{{ 'ACTIONS.ACTIONS' | translate }}"
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="actions"
|
||||
>
|
||||
<i class="las la-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<mat-menu #actions="matMenu" xPosition="before">
|
||||
<button mat-menu-item *ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
|
||||
(click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' |
|
||||
translate}}</button>
|
||||
<button mat-menu-item *ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
|
||||
(click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' |
|
||||
translate}}</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE"
|
||||
(click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE)"
|
||||
>
|
||||
{{ 'USER.PAGES.DEACTIVATE' | translate }}
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE"
|
||||
(click)="changeState(ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE)"
|
||||
>
|
||||
{{ 'USER.PAGES.REACTIVATE' | translate }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<div class="master-row">
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
|
||||
<cnsl-project-grant-illustration *ngIf="grant" [grantedProject]="grant" [projectRoleOptions]="projectRoleOptions"
|
||||
(roleRemoved)="removeRole($event)" (editRoleClicked)="editRoles()">
|
||||
<cnsl-project-grant-illustration
|
||||
*ngIf="grant && projectRoleOptions"
|
||||
[grantedProject]="grant"
|
||||
[projectRoleOptions]="projectRoleOptions"
|
||||
(roleRemoved)="removeRole($event)"
|
||||
(editRoleClicked)="editRoles()"
|
||||
>
|
||||
</cnsl-project-grant-illustration>
|
||||
|
||||
<h2 class="project-grant-h2">{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}</h2>
|
||||
<p class="desc max-width-description">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p>
|
||||
<p class="desc cnsl-secondary-text max-width-description">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p>
|
||||
|
||||
<cnsl-members-table *ngIf="grant" [dataSource]="dataSource"
|
||||
[canWrite]="['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async"
|
||||
[canDelete]="['project.grant.member.delete','project.grant.member.delete:' + grant.grantId] | hasRole | async"
|
||||
[memberRoleOptions]="memberRoleOptions" (updateRoles)="updateMemberRoles($event.member, $event.change)"
|
||||
(deleteMember)="removeProjectMember($event)" [factoryLoadFunc]="changePageFactory"
|
||||
(changedSelection)="selection = $event" [refreshTrigger]="changePage">
|
||||
<button class="cnsl-action-button" selectactions (click)="removeProjectMemberSelection()"
|
||||
[disabled]="(['project.grant.member.delete','project.grant.member.delete:' + grant.grantId] | hasRole | async) === false"
|
||||
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" color="warn" mat-raised-button>
|
||||
<cnsl-members-table
|
||||
*ngIf="grant"
|
||||
[dataSource]="dataSource"
|
||||
[canWrite]="['project.grant.member.write', 'project.grant.member.write:' + grant.grantId] | hasRole | async"
|
||||
[canDelete]="['project.grant.member.delete', 'project.grant.member.delete:' + grant.grantId] | hasRole | async"
|
||||
[memberRoleOptions]="memberRoleOptions"
|
||||
(updateRoles)="updateMemberRoles($event.member, $event.change)"
|
||||
(deleteMember)="removeProjectMember($event)"
|
||||
[factoryLoadFunc]="changePageFactory"
|
||||
(changedSelection)="selection = $event"
|
||||
[refreshTrigger]="changePage"
|
||||
>
|
||||
<button
|
||||
class="cnsl-action-button"
|
||||
selectactions
|
||||
(click)="removeProjectMemberSelection()"
|
||||
[disabled]="
|
||||
(['project.grant.member.delete', 'project.grant.member.delete:' + grant.grantId] | hasRole | async) === false
|
||||
"
|
||||
matTooltip="{{ 'ORG_DETAIL.TABLE.DELETE' | translate }}"
|
||||
color="warn"
|
||||
mat-raised-button
|
||||
>
|
||||
<i class="las la-trash"></i>
|
||||
<span>{{'ACTIONS.SELECTIONDELETE' | translate}}</span>
|
||||
<span>{{ 'ACTIONS.SELECTIONDELETE' | translate }}</span>
|
||||
<cnsl-action-keys [type]="ActionKeysType.DELETE" (actionTriggered)="removeProjectMemberSelection()">
|
||||
</cnsl-action-keys>
|
||||
</button>
|
||||
<button class="cnsl-action-button" writeactions color="primary"
|
||||
[disabled]="(['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async) === false"
|
||||
(click)="openAddMember()" color="primary" mat-raised-button>
|
||||
<button
|
||||
class="cnsl-action-button"
|
||||
writeactions
|
||||
color="primary"
|
||||
[disabled]="
|
||||
(['project.grant.member.write', 'project.grant.member.write:' + grant.grantId] | hasRole | async) === false
|
||||
"
|
||||
(click)="openAddMember()"
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
>
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
<cnsl-action-keys (actionTriggered)="openAddMember()">
|
||||
</cnsl-action-keys>
|
||||
<cnsl-action-keys (actionTriggered)="openAddMember()"> </cnsl-action-keys>
|
||||
</button>
|
||||
</cnsl-members-table>
|
||||
</cnsl-detail-layout>
|
||||
</cnsl-detail-layout>
|
||||
|
@@ -3,17 +3,15 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { PageEvent } from '@angular/material/paginator';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ActionKeysType } from 'src/app/modules/action-keys/action-keys.component';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { UserGrantRoleDialogComponent } from 'src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { GrantedProject, ProjectGrantState, Role } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import {
|
||||
ProjectGrantMembersCreateDialogComponent,
|
||||
ProjectGrantMembersCreateDialogExportType,
|
||||
} from './project-grant-members-create-dialog/project-grant-members-create-dialog.component';
|
||||
import { ProjectGrantMembersDataSource } from './project-grant-members-datasource';
|
||||
|
||||
@Component({
|
||||
@@ -69,25 +67,29 @@ export class ProjectGrantDetailComponent {
|
||||
);
|
||||
};
|
||||
|
||||
this.mgmtService.getProjectGrantByID(this.grantid, this.projectid).then((resp) => {
|
||||
if (resp.projectGrant) {
|
||||
this.grant = resp.projectGrant;
|
||||
this.mgmtService
|
||||
.getProjectGrantByID(this.grantid, this.projectid)
|
||||
.then((resp) => {
|
||||
console.log(resp);
|
||||
if (resp.projectGrant) {
|
||||
this.grant = resp.projectGrant;
|
||||
|
||||
const breadcrumbs = [
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
}),
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.PROJECT,
|
||||
name: '',
|
||||
param: { key: 'projectid', value: resp.projectGrant.projectId },
|
||||
routerLink: ['/projects', resp.projectGrant.projectId],
|
||||
}),
|
||||
];
|
||||
this.breadcrumbService.setBreadcrumb(breadcrumbs);
|
||||
}
|
||||
});
|
||||
const breadcrumbs = [
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
}),
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.PROJECT,
|
||||
name: '',
|
||||
param: { key: 'projectid', value: resp.projectGrant.projectId },
|
||||
routerLink: ['/projects', resp.projectGrant.projectId],
|
||||
}),
|
||||
];
|
||||
this.breadcrumbService.setBreadcrumb(breadcrumbs);
|
||||
}
|
||||
})
|
||||
.catch(this.toast.showError);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -176,36 +178,35 @@ export class ProjectGrantDetailComponent {
|
||||
}
|
||||
|
||||
public async openAddMember(): Promise<any> {
|
||||
const keysList = await this.mgmtService.listProjectGrantMemberRoles();
|
||||
|
||||
const dialogRef = this.dialog.open(ProjectGrantMembersCreateDialogComponent, {
|
||||
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
||||
data: {
|
||||
roleKeysList: keysList.resultList,
|
||||
creationType: CreationType.PROJECT_GRANTED,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((dataToAdd: ProjectGrantMembersCreateDialogExportType) => {
|
||||
if (dataToAdd) {
|
||||
Promise.all(
|
||||
dataToAdd.userIds.map((userid: string) => {
|
||||
return this.mgmtService.addProjectGrantMember(
|
||||
this.grant.projectId,
|
||||
this.grant.grantId,
|
||||
userid,
|
||||
dataToAdd.rolesKeyList,
|
||||
);
|
||||
}),
|
||||
)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 3000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
const users: User.AsObject[] = resp.users;
|
||||
const roles: string[] = resp.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
const userIds = users.map((user) => user.id);
|
||||
Promise.all(
|
||||
userIds.map((userid: string) => {
|
||||
return this.mgmtService.addProjectGrantMember(this.grant.projectId, this.grant.grantId, userid, resp.roles);
|
||||
}),
|
||||
)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 3000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { ActionKeysModule } from 'src/app/modules/action-keys/action-keys.module';
|
||||
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { MembersTableModule } from 'src/app/modules/members-table/members-table.module';
|
||||
@@ -25,16 +26,12 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
|
||||
import { ProjectGrantDetailRoutingModule } from './project-grant-detail-routing.module';
|
||||
import { ProjectGrantDetailComponent } from './project-grant-detail.component';
|
||||
import { ProjectGrantIllustrationComponent } from './project-grant-illustration/project-grant-illustration.component';
|
||||
import {
|
||||
ProjectGrantMembersCreateDialogModule,
|
||||
} from './project-grant-members-create-dialog/project-grant-members-create-dialog.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ProjectGrantDetailComponent, ProjectGrantIllustrationComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ProjectGrantDetailRoutingModule,
|
||||
ProjectGrantMembersCreateDialogModule,
|
||||
MatAutocompleteModule,
|
||||
HasRoleModule,
|
||||
MatChipsModule,
|
||||
@@ -54,6 +51,7 @@ import {
|
||||
TranslateModule,
|
||||
MatSelectModule,
|
||||
DetailLayoutModule,
|
||||
MemberCreateDialogModule,
|
||||
HasRolePipeModule,
|
||||
MembersTableModule,
|
||||
MatDialogModule,
|
||||
|
@@ -13,7 +13,6 @@ export class ProjectGrantIllustrationComponent {
|
||||
@Output() public editRoleClicked: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
ProjectGrantState: any = ProjectGrantState;
|
||||
constructor() {}
|
||||
|
||||
public removeRole(roleKey: string): void {
|
||||
this.roleRemoved.emit(roleKey);
|
||||
|
@@ -1,27 +0,0 @@
|
||||
<h1 mat-dialog-title>
|
||||
<span class="title">{{'MEMBER.ADD' | translate}}</span>
|
||||
</h1>
|
||||
<p class="desc"> {{'ORG_DETAIL.MEMBER.ADDDESCRIPTION' | translate}}</p>
|
||||
<div mat-dialog-content>
|
||||
<cnsl-search-user-autocomplete (selectionChanged)="selectUsers($event)">
|
||||
</cnsl-search-user-autocomplete>
|
||||
|
||||
<cnsl-form-field class="full-width" appearance="outline">
|
||||
<cnsl-label>{{ 'PROJECT.MEMBER.ROLES' | translate }}</cnsl-label>
|
||||
<mat-select [(ngModel)]="roleKeyList" multiple>
|
||||
<mat-option *ngFor="let key of data.roleKeysList" [value]="key">
|
||||
{{ key }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button mat-stroked-button (click)="closeDialog()">
|
||||
{{'ACTIONS.CANCEL' | translate}}
|
||||
</button>
|
||||
|
||||
<button [disabled]="userIds.length === 0 || roleKeyList.length === 0" color="primary" mat-raised-button
|
||||
class="ok-button" (click)="closeDialogWithSuccess()">
|
||||
{{'ACTIONS.ADD' | translate}}
|
||||
</button>
|
||||
</div>
|
@@ -1,12 +0,0 @@
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.ok-button {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { ProjectGrantMembersCreateDialogComponent } from './project-grant-members-create-dialog.component';
|
||||
|
||||
describe('ProjectGrantMembersCreateDialogComponent', () => {
|
||||
let component: ProjectGrantMembersCreateDialogComponent;
|
||||
let fixture: ComponentFixture<ProjectGrantMembersCreateDialogComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ProjectGrantMembersCreateDialogComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProjectGrantMembersCreateDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -1,42 +0,0 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
|
||||
export interface ProjectGrantMembersCreateDialogExportType {
|
||||
userIds: string[];
|
||||
rolesKeyList: string[];
|
||||
}
|
||||
@Component({
|
||||
selector: 'cnsl-project-grant-members-create-dialog',
|
||||
templateUrl: './project-grant-members-create-dialog.component.html',
|
||||
styleUrls: ['./project-grant-members-create-dialog.component.scss'],
|
||||
})
|
||||
export class ProjectGrantMembersCreateDialogComponent {
|
||||
public form!: FormGroup;
|
||||
public userIds: string[] = [];
|
||||
public roleKeyList: string[] = [];
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<ProjectGrantMembersCreateDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) { }
|
||||
|
||||
public selectUsers(users: User.AsObject | User.AsObject[]): void {
|
||||
if (Array.isArray(users)) {
|
||||
this.userIds = users.map(user => user.id);
|
||||
}
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
public closeDialogWithSuccess(): void {
|
||||
const exportData: ProjectGrantMembersCreateDialogExportType = {
|
||||
userIds: this.userIds,
|
||||
rolesKeyList: this.roleKeyList,
|
||||
};
|
||||
this.dialogRef.close(exportData);
|
||||
}
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { SearchUserAutocompleteModule } from 'src/app/modules/search-user-autocomplete/search-user-autocomplete.module';
|
||||
|
||||
import { ProjectGrantMembersCreateDialogComponent } from './project-grant-members-create-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ProjectGrantMembersCreateDialogComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
MatDialogModule,
|
||||
MatButtonModule,
|
||||
TranslateModule,
|
||||
MatSelectModule,
|
||||
InputModule,
|
||||
SearchUserAutocompleteModule,
|
||||
],
|
||||
})
|
||||
export class ProjectGrantMembersCreateDialogModule { }
|
||||
|
@@ -1,4 +1,9 @@
|
||||
<cnsl-create-layout title="{{ 'PROJECT.PAGES.CREATE' | translate }}" [createSteps]="1" [currentCreateStep]="1">
|
||||
<cnsl-create-layout
|
||||
title="{{ 'PROJECT.PAGES.CREATE' | translate }}"
|
||||
[createSteps]="1"
|
||||
[currentCreateStep]="1"
|
||||
(closed)="close()"
|
||||
>
|
||||
<h1>{{ 'PROJECT.PAGES.CREATE_DESC' | translate }}</h1>
|
||||
<form cdkFocusRegionStart (ngSubmit)="saveProject()">
|
||||
<div class="column">
|
||||
|
@@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
.sub {
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
opacity: 0.6;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
|
||||
&:first-child {
|
||||
margin-right: 1rem;
|
||||
|
@@ -17,7 +17,7 @@
|
||||
|
||||
<div class="max-width-container">
|
||||
<cnsl-meta-layout>
|
||||
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList">
|
||||
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList" queryParam="id">
|
||||
<ng-container *ngIf="currentSetting === 'general'">
|
||||
<cnsl-card
|
||||
*ngIf="user && user.human && user.human.profile"
|
||||
|
@@ -28,7 +28,7 @@
|
||||
opacity: 0.6;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
|
@@ -2,8 +2,9 @@ import { MediaMatcher } from '@angular/cdk/layout';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, EventEmitter, OnDestroy } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
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 { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
|
||||
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
||||
@@ -57,7 +58,15 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
private mediaMatcher: MediaMatcher,
|
||||
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 small = this.mediaMatcher.matchMedia(mediaq).matches;
|
||||
if (small) {
|
||||
|
@@ -1,42 +1,55 @@
|
||||
<div mat-dialog-title class="title-row">
|
||||
<h1 class="title">{{'USER.METADATA.TITLE' | translate}}</h1>
|
||||
<div class="title-row">
|
||||
<h1 class="metadata-title">{{ 'USER.METADATA.TITLE' | translate }}</h1>
|
||||
<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>
|
||||
<button class="icon-button" mat-icon-button (click)="load()">
|
||||
<mat-icon class="icon">refresh</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<p class="desc">{{'USER.METADATA.DESCRIPTION' | translate}}</p>
|
||||
<div mat-dialog-content>
|
||||
<p class="desc">{{ 'USER.METADATA.DESCRIPTION' | translate }}</p>
|
||||
<div mat-dialog-content class="metadata-dialog-content">
|
||||
<form *ngFor="let md of metadata; index as i" (ngSubmit)="saveElement(i)">
|
||||
<div class="content">
|
||||
<cnsl-form-field #key id="key{{i}}" class="formfield">
|
||||
<cnsl-form-field #key id="key{{ i }}" class="formfield">
|
||||
<cnsl-label>{{ 'USER.METADATA.KEY' | translate }}</cnsl-label>
|
||||
<input cnslInput [(ngModel)]="md.key" [ngModelOptions]="{standalone: true}" />
|
||||
<input cnslInput [(ngModel)]="md.key" [ngModelOptions]="{ standalone: true }" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field #value id="value{{i}}" class="formfield">
|
||||
<cnsl-form-field #value id="value{{ i }}" class="formfield">
|
||||
<cnsl-label>{{ 'USER.METADATA.VALUE' | translate }}</cnsl-label>
|
||||
<input cnslInput [(ngModel)]="md.value" [ngModelOptions]="{standalone: true}" />
|
||||
<input cnslInput [(ngModel)]="md.value" [ngModelOptions]="{ standalone: true }" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<button mat-icon-button [disabled]="!(md.key && md.value)" class="set-button" type="submit" color="primary"
|
||||
matTooltip="{{ 'ACTIONS.SAVE' | translate }}">
|
||||
<button
|
||||
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>
|
||||
</button>
|
||||
<button 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 }}">
|
||||
<button
|
||||
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>
|
||||
</button>
|
||||
</div>
|
||||
</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>
|
||||
{{ 'ACTIONS.ADD' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button cdkFocusInitial color="primary" mat-stroked-button class="ok-button" (click)="closeDialog()">
|
||||
{{'ACTIONS.CLOSE' | translate}}
|
||||
<button cdkFocusInitial mat-stroked-button class="ok-button" (click)="closeDialog()">
|
||||
{{ 'ACTIONS.CLOSE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,8 +2,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
.metadata-title {
|
||||
font-size: 1.3rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -23,23 +23,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 -0.5rem;
|
||||
.metadata-dialog-content {
|
||||
min-width: 400px;
|
||||
|
||||
.formfield {
|
||||
flex: 1;
|
||||
margin: 0 0.5rem;
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 -0.5rem;
|
||||
|
||||
@media only screen and (max-width: 450px) {
|
||||
flex-basis: 100%;
|
||||
.formfield {
|
||||
flex: 1;
|
||||
margin: 0 0.5rem;
|
||||
|
||||
@media only screen and (max-width: 450px) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rm-button,
|
||||
.set-button {
|
||||
margin-top: 14px;
|
||||
.rm-button,
|
||||
.set-button {
|
||||
margin-top: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,22 +1,17 @@
|
||||
<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>
|
||||
<button mat-raised-button color="primary" class="edit" (click)="editMetadata()">{{'ACTIONS.EDIT' |
|
||||
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>
|
||||
<button mat-raised-button color="primary" class="edit" (click)="editMetadata()">{{ 'ACTIONS.EDIT' | translate }}</button>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="metadata?.length; else emptyList">
|
||||
<div class="metadata-set" *ngFor="let md of metadata">
|
||||
<span class="first cnsl-secondary-text">{{md.key}}</span>
|
||||
<span class="first cnsl-secondary-text">{{ md.key }}</span>
|
||||
<span class="second">{{ md.value }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #emptyList>
|
||||
<p class="empty-desc cnsl-secondary-text">{{'USER.METADATA.EMPTY' | translate}}</p>
|
||||
<p class="empty-desc cnsl-secondary-text">{{ 'USER.METADATA.EMPTY' | translate }}</p>
|
||||
</ng-template>
|
||||
</cnsl-card>
|
||||
</cnsl-card>
|
||||
|
@@ -1,9 +1,10 @@
|
||||
.metadata-details {
|
||||
padding-bottom: 1rem;
|
||||
|
||||
.actions {
|
||||
.metadata-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
.edit {
|
||||
font-size: 14px;
|
||||
@@ -53,6 +54,7 @@
|
||||
.empty-desc {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
|
@@ -16,8 +16,7 @@ export class MetadataComponent implements OnInit {
|
||||
public metadata: Metadata.AsObject[] = [];
|
||||
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 {
|
||||
this.loadMetadata();
|
||||
@@ -37,17 +36,20 @@ export class MetadataComponent implements OnInit {
|
||||
|
||||
public loadMetadata(): Promise<any> {
|
||||
this.loading = true;
|
||||
return (this.service as ManagementService).listUserMetadata(this.userId).then(resp => {
|
||||
this.loading = false;
|
||||
this.metadata = resp.resultList.map(md => {
|
||||
return {
|
||||
key: md.key,
|
||||
value: atob(md.value as string),
|
||||
};
|
||||
return (this.service as ManagementService)
|
||||
.listUserMetadata(this.userId)
|
||||
.then((resp) => {
|
||||
this.loading = false;
|
||||
this.metadata = resp.resultList.map((md) => {
|
||||
return {
|
||||
key: md.key,
|
||||
value: atob(md.value as string),
|
||||
};
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false;
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.loading = false;
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -37,7 +37,9 @@
|
||||
</cnsl-top-view>
|
||||
|
||||
<div *ngIf="loading" class="max-width-container">
|
||||
<mat-progress-spinner diameter="25" color="primary" mode="indeterminate"></mat-progress-spinner>
|
||||
<div class="sp-wrapper">
|
||||
<mat-progress-spinner diameter="25" color="primary" mode="indeterminate"></mat-progress-spinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!loading && !user" class="max-width-container">
|
||||
@@ -46,7 +48,7 @@
|
||||
|
||||
<div class="max-width-container" *ngIf="user && (['user.write$', 'user.write:' + user.id] | hasRole) as canWrite$">
|
||||
<cnsl-meta-layout>
|
||||
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList">
|
||||
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList" queryParam="id">
|
||||
<div *ngIf="error" class="max-width-container">
|
||||
<p>{{ error }}</p>
|
||||
</div>
|
||||
|
@@ -42,3 +42,7 @@
|
||||
.resendemail {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.sp-wrapper {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
@@ -65,9 +65,17 @@ export class UserDetailComponent implements OnInit {
|
||||
private _location: Location,
|
||||
private dialog: MatDialog,
|
||||
private router: Router,
|
||||
activatedRoute: ActivatedRoute,
|
||||
private mediaMatcher: MediaMatcher,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
) {
|
||||
activatedRoute.queryParams.pipe(take(1)).subscribe((params: Params) => {
|
||||
const { id } = params;
|
||||
if (id) {
|
||||
this.currentSetting = id;
|
||||
}
|
||||
});
|
||||
|
||||
breadcrumbService.setBreadcrumb([
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
|
@@ -3,14 +3,17 @@
|
||||
<ng-container *ngSwitchCase="Type.TYPE_HUMAN">
|
||||
<div class="users-title-row">
|
||||
<h1>{{ 'USER.PAGES.LIST' | translate }}</h1>
|
||||
<a mat-icon-button href="https://docs.zitadel.com/docs/concepts/structure/users" rel="noreferrer"
|
||||
target="_blank">
|
||||
<a mat-icon-button href="https://docs.zitadel.com/docs/concepts/structure/users" rel="noreferrer" target="_blank">
|
||||
<i class="las la-info-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<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>
|
||||
</ng-container>
|
||||
|
||||
@@ -18,8 +21,12 @@
|
||||
<h1>{{ 'USER.PAGES.LISTMACHINE' | translate }}</h1>
|
||||
<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>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -14,6 +14,6 @@
|
||||
}
|
||||
|
||||
.user-list-sub {
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
@@ -1,35 +1,64 @@
|
||||
<cnsl-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="totalResult"
|
||||
[hideRefresh]="true" [timestamp]="viewTimestamp" [selection]="selection"
|
||||
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [showBorder]="true">
|
||||
|
||||
<cnsl-refresh-table
|
||||
[loading]="loading$ | async"
|
||||
(refreshed)="refreshPage()"
|
||||
[dataSize]="totalResult"
|
||||
[hideRefresh]="true"
|
||||
[timestamp]="viewTimestamp"
|
||||
[selection]="selection"
|
||||
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes"
|
||||
[showBorder]="true"
|
||||
>
|
||||
<div leftActions class="user-table-left-actions">
|
||||
<button class="type-button" [ngClass]="{'active': type === Type.TYPE_HUMAN}"
|
||||
(click)="setType(Type.TYPE_HUMAN)">{{'USER.TABLE.TYPES.HUMAN' | translate}}</button>
|
||||
<button class="type-button" [ngClass]="{'active': type === Type.TYPE_MACHINE}"
|
||||
(click)="setType(Type.TYPE_MACHINE)">{{'USER.TABLE.TYPES.MACHINE' | translate}}</button>
|
||||
<button class="type-button" [ngClass]="{ active: type === Type.TYPE_HUMAN }" (click)="setType(Type.TYPE_HUMAN)">
|
||||
{{ 'USER.TABLE.TYPES.HUMAN' | translate }}
|
||||
</button>
|
||||
<button class="type-button" [ngClass]="{ active: type === Type.TYPE_MACHINE }" (click)="setType(Type.TYPE_MACHINE)">
|
||||
{{ 'USER.TABLE.TYPES.MACHINE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['user.write']" actions>
|
||||
<button (click)="deactivateSelectedUsers()" class="cnsl-action-button bg-state inactive" mat-raised-button
|
||||
*ngIf="selection.hasValue() && multipleDeactivatePossible" [disabled]="disabled" color="primary">
|
||||
<span class="">{{'USER.TABLE.DEACTIVATE' | translate}}</span>
|
||||
<cnsl-action-keys (actionTriggered)="deactivateSelectedUsers()" [type]="ActionKeysType.DEACTIVATE">
|
||||
</cnsl-action-keys>
|
||||
<button
|
||||
(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>
|
||||
<cnsl-action-keys (actionTriggered)="deactivateSelectedUsers()" [type]="ActionKeysType.DEACTIVATE"> </cnsl-action-keys>
|
||||
</button>
|
||||
<button (click)="reactivateSelectedUsers()" class="cnsl-action-button bg-state active margin-left" mat-raised-button
|
||||
*ngIf="selection.hasValue() && multipleActivatePossible" [disabled]="disabled" color="primary">
|
||||
<span class="">{{'USER.TABLE.ACTIVATE' | translate}}</span>
|
||||
<cnsl-action-keys (actionTriggered)="reactivateSelectedUsers()" [type]="ActionKeysType.REACTIVATE">
|
||||
</cnsl-action-keys>
|
||||
<button
|
||||
(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>
|
||||
<cnsl-action-keys (actionTriggered)="reactivateSelectedUsers()" [type]="ActionKeysType.REACTIVATE"> </cnsl-action-keys>
|
||||
</button>
|
||||
<cnsl-filter-user *ngIf="!selection.hasValue()" (filterChanged)="applySearchQuery($any($event))"
|
||||
(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]="disabled" class="cnsl-action-button">
|
||||
<cnsl-filter-user
|
||||
*ngIf="!selection.hasValue()"
|
||||
(filterChanged)="applySearchQuery($any($event))"
|
||||
(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>
|
||||
<span>{{ 'ACTIONS.NEW' | translate }}</span>
|
||||
<cnsl-action-keys *ngIf="!filterOpen"
|
||||
(actionTriggered)="gotoRouterLink([ '/users',type === Type.TYPE_HUMAN ? 'create' : 'create-machine'])">
|
||||
<cnsl-action-keys
|
||||
*ngIf="!filterOpen"
|
||||
(actionTriggered)="gotoRouterLink(['/users', type === Type.TYPE_HUMAN ? 'create' : 'create-machine'])"
|
||||
>
|
||||
</cnsl-action-keys>
|
||||
</a>
|
||||
</ng-template>
|
||||
@@ -38,19 +67,37 @@
|
||||
<table class="table" mat-table [dataSource]="dataSource" matSort (matSortChange)="sortChange($event)">
|
||||
<ng-container matColumnDef="select">
|
||||
<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()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()"
|
||||
>
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user" class="selection">
|
||||
<mat-checkbox [disabled]="disabled" color="primary" (click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(user) : null" [checked]="selection.isSelected(user)">
|
||||
|
||||
<mat-checkbox
|
||||
[disabled]="!canWrite"
|
||||
color="primary"
|
||||
(click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(user) : null"
|
||||
[checked]="selection.isSelected(user)"
|
||||
>
|
||||
<cnsl-avatar
|
||||
*ngIf="user.human && 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">
|
||||
*ngIf="
|
||||
user.human &&
|
||||
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>
|
||||
<ng-template #cog>
|
||||
<cnsl-avatar [forColor]="user?.preferredLoginName" [isMachine]="true">
|
||||
@@ -62,58 +109,74 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="displayName">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header
|
||||
[ngClass]="{'search-active': this.userSearchKey === UserListSearchKey.DISPLAY_NAME}">
|
||||
<th
|
||||
mat-header-cell
|
||||
*matHeaderCellDef
|
||||
mat-sort-header
|
||||
[ngClass]="{ 'search-active': this.userSearchKey === UserListSearchKey.DISPLAY_NAME }"
|
||||
>
|
||||
{{ 'USER.PROFILE.DISPLAYNAME' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
<span *ngIf="user.human">{{user.human.profile?.displayName}}</span>
|
||||
<span *ngIf="user.machine">{{user.machine.name}}</span>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
|
||||
<span *ngIf="user.human">{{ user.human.profile?.displayName }}</span>
|
||||
<span *ngIf="user.machine">{{ user.machine.name }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="username">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header
|
||||
[ngClass]="{'search-active': this.userSearchKey === UserListSearchKey.USER_NAME}">
|
||||
<th
|
||||
mat-header-cell
|
||||
*matHeaderCellDef
|
||||
mat-sort-header
|
||||
[ngClass]="{ 'search-active': this.userSearchKey === UserListSearchKey.USER_NAME }"
|
||||
>
|
||||
{{ 'USER.PROFILE.USERNAME' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user.userName}} </td>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
|
||||
{{ user.userName }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="email">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header
|
||||
[ngClass]="{'search-active': this.UserListSearchKey === UserListSearchKey.EMAIL}">
|
||||
<th
|
||||
mat-header-cell
|
||||
*matHeaderCellDef
|
||||
mat-sort-header
|
||||
[ngClass]="{ 'search-active': this.UserListSearchKey === UserListSearchKey.EMAIL }"
|
||||
>
|
||||
{{ 'USER.EMAIL' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
<span *ngIf="user.human?.email?.email">{{user.human?.email.email}}</span>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
|
||||
<span *ngIf="user.human?.email?.email">{{ user.human?.email.email }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="state">
|
||||
<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">
|
||||
<span class="state"
|
||||
[ngClass]="{'active': user.state === UserState.USER_STATE_ACTIVE, 'inactive': user.state === UserState.USER_STATE_INACTIVE}">
|
||||
{{ 'USER.DATA.STATE'+user.state | translate }}
|
||||
<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">
|
||||
<span
|
||||
class="state"
|
||||
[ngClass]="{
|
||||
active: user.state === UserState.USER_STATE_ACTIVE,
|
||||
inactive: user.state === UserState.USER_STATE_INACTIVE
|
||||
}"
|
||||
>
|
||||
{{ 'USER.DATA.STATE' + user.state | translate }}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="creationDate">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'USER.TABLE.CREATIONDATE' | translate}} </th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
<span class="no-break">{{user.details.creationDate | timestampToDate | localizedDate: 'fromNow'
|
||||
}}</span>
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'USER.TABLE.CREATIONDATE' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
|
||||
<span class="no-break">{{ user.details.creationDate | timestampToDate | localizedDate: 'fromNow' }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="changeDate">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.TABLE.CHANGEDATE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
<span class="no-break">{{user.details.changeDate | timestampToDate | localizedDate: 'fromNow'
|
||||
}}</span>
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'USER.TABLE.CHANGEDATE' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
|
||||
<span class="no-break">{{ user.details.changeDate | timestampToDate | localizedDate: 'fromNow' }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
@@ -121,28 +184,41 @@
|
||||
<th mat-header-cell *matHeaderCellDef class="user-tr-actions"></th>
|
||||
<td mat-cell *matCellDef="let user" class="user-tr-actions">
|
||||
<cnsl-table-actions>
|
||||
<button actions matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" (click)="deleteUser(user)"
|
||||
mat-icon-button>
|
||||
<button
|
||||
actions
|
||||
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
|
||||
color="warn"
|
||||
(click)="deleteUser(user)"
|
||||
[disabled]="!canWrite || !canDelete"
|
||||
mat-icon-button
|
||||
>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</cnsl-table-actions>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="type === Type.TYPE_HUMAN ? displayedColumnsHuman : displayedColumnsMachine">
|
||||
</tr>
|
||||
<tr class="highlight pointer" mat-row
|
||||
*matRowDef="let user; columns: (type === Type.TYPE_HUMAN ? displayedColumnsHuman : displayedColumnsMachine);">
|
||||
</tr>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="type === Type.TYPE_HUMAN ? displayedColumnsHuman : displayedColumnsMachine"></tr>
|
||||
<tr
|
||||
class="highlight pointer"
|
||||
mat-row
|
||||
*matRowDef="let user; columns: type === Type.TYPE_HUMAN ? displayedColumnsHuman : displayedColumnsMachine"
|
||||
></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div *ngIf="(loading$ | async) === false && !dataSource?.data?.length" class="no-content-row">
|
||||
<i class="las la-exclamation"></i>
|
||||
<span>{{'USER.TABLE.EMPTY' | translate}}</span>
|
||||
<span>{{ 'USER.TABLE.EMPTY' | translate }}</span>
|
||||
</div>
|
||||
<cnsl-paginator #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-paginator
|
||||
#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>
|
||||
|
@@ -16,7 +16,7 @@
|
||||
opacity: 0.6;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
color: map-get($foreground, base);
|
||||
color: map-get($foreground, text);
|
||||
|
||||
&:first-child {
|
||||
margin-right: 1rem;
|
||||
|
@@ -37,7 +37,7 @@ export class UserTableComponent implements OnInit {
|
||||
public Type: any = Type;
|
||||
@Input() public type: Type = Type.TYPE_HUMAN;
|
||||
@Input() refreshOnPreviousRoutes: string[] = [];
|
||||
@Input() disabled: boolean = false;
|
||||
@Input() canWrite: boolean = false;
|
||||
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
|
||||
@ViewChild(MatSort) public sort!: MatSort;
|
||||
public INITIAL_PAGE_SIZE: number = 20;
|
||||
@@ -77,6 +77,7 @@ export class UserTableComponent implements OnInit {
|
||||
public filterOpen: boolean = false;
|
||||
|
||||
private searchQueries: SearchQuery[] = [];
|
||||
@Input() public canDelete: boolean = false;
|
||||
constructor(
|
||||
private router: Router,
|
||||
public translate: TranslateService,
|
||||
|
@@ -1,37 +1,36 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { Router } from '@angular/router';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export abstract class StatehandlerProcessorService {
|
||||
public abstract createState(url: string): Observable<string | undefined>;
|
||||
public abstract createState(url: string): string;
|
||||
public abstract restoreState(state?: string): void;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
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 urlId = uuidv4();
|
||||
|
||||
sessionStorage.setItem(urlId, externalUrl);
|
||||
|
||||
return of(urlId);
|
||||
return urlId;
|
||||
}
|
||||
|
||||
public restoreState(state?: string): void {
|
||||
if (state === undefined) {
|
||||
return;
|
||||
} else {
|
||||
const url = sessionStorage.getItem(state);
|
||||
if (url === null) {
|
||||
return;
|
||||
} else {
|
||||
sessionStorage.removeItem(state);
|
||||
// window.location.replace(window.location.origin + url);
|
||||
this.router.navigate([url]);
|
||||
}
|
||||
}
|
||||
|
||||
const url = sessionStorage.getItem(state);
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
sessionStorage.removeItem(state);
|
||||
window.location.href = window.location.origin + url;
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,9 @@ export class StatehandlerServiceImpl implements StatehandlerService, OnDestroy {
|
||||
map(() => oauthService.state),
|
||||
takeUntil(this.unsubscribe$),
|
||||
)
|
||||
.subscribe((state) => processor.restoreState(state));
|
||||
.subscribe((state) => {
|
||||
processor.restoreState(state);
|
||||
});
|
||||
}
|
||||
|
||||
public initStateHandler(): void {
|
||||
@@ -47,9 +49,11 @@ export class StatehandlerServiceImpl implements StatehandlerService, OnDestroy {
|
||||
switchMap((url: string) => {
|
||||
if (url.includes('?login_hint=')) {
|
||||
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 {
|
||||
return this.processor.createState(url);
|
||||
return of(undefined);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
@@ -57,9 +57,10 @@ export class ThemeService {
|
||||
}
|
||||
|
||||
public saveTextColor(colorHex: string, isDark: boolean): void {
|
||||
this.primaryColorPalette = this.computeColors(colorHex);
|
||||
const theme = isDark ? 'dark' : 'light';
|
||||
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[] {
|
||||
|
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"SHORTCUTS": {
|
||||
"SHORTCUTS": "Shortcuts",
|
||||
"SETTINGS": "Organisation Einstellungen",
|
||||
"SETTINGS": "Verfügbare Shortcuts",
|
||||
"PROJECTS": "Projekte",
|
||||
"REORDER": "Zum Verschieben Kachel halten un 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."
|
||||
},
|
||||
"NAV": {
|
||||
"TEXT": "Diese Navigation ändert sich basierend auf den Breadcrumbs darüber."
|
||||
"TEXT": "Diese Navigation ändert sich basierend auf Ihrer Organisation oder Instanz"
|
||||
},
|
||||
"CONTEXTCHANGED": {
|
||||
"TEXT": "Achtung! Soeben wurde die Organisation gewechselt."
|
||||
@@ -818,6 +818,7 @@
|
||||
"LOCKOUT": "Sperrmechanismen",
|
||||
"COMPLEXITY": "Passwordkomplexität",
|
||||
"NOTIFICATIONS": "Benachrichtigungen",
|
||||
"NOTIFICATIONS_DESC": "SMTP und SMS Einstellungen",
|
||||
"MESSAGETEXTS": "Benachrichtigungstexte",
|
||||
"IDP": "Identity Provider",
|
||||
"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."
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"DIALOG": {
|
||||
@@ -1549,6 +1550,9 @@
|
||||
}
|
||||
},
|
||||
"DIALOG": {
|
||||
"CONFIG": {
|
||||
"TITLE": "OIDC configuration ändern"
|
||||
},
|
||||
"DELETE": {
|
||||
"TITLE": "App löschen",
|
||||
"DESCRIPTION": "Wollen Sie diese App wirklich löschen?"
|
||||
|
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"SHORTCUTS": {
|
||||
"SHORTCUTS": "Shortcuts",
|
||||
"SETTINGS": "Organization Settings",
|
||||
"SETTINGS": "Available shortcuts",
|
||||
"PROJECTS": "Projects",
|
||||
"REORDER": "Hold and drag the tile to move it",
|
||||
"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."
|
||||
},
|
||||
"NAV": {
|
||||
"TEXT": "This navigation changes based on the breadcrumbs above."
|
||||
"TEXT": "This navigation changes based on your selected oranization above or your instance"
|
||||
},
|
||||
"CONTEXTCHANGED": {
|
||||
"TEXT": "Attention! The organization context has changed."
|
||||
@@ -818,6 +818,7 @@
|
||||
"LOCKOUT": "Lockout",
|
||||
"COMPLEXITY": "Password complexity",
|
||||
"NOTIFICATIONS": "Notification providers and SMTP",
|
||||
"NOTIFICATIONS_DESC": "SMTP and SMS Settings",
|
||||
"MESSAGETEXTS": "Message Texts",
|
||||
"IDP": "Identity Providers",
|
||||
"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."
|
||||
},
|
||||
"1": {
|
||||
"TITLE": "Enforce project resource owner Policy",
|
||||
"TITLE": "Use project setting",
|
||||
"DESC": "The branding of the organization which owns the project will be shown"
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"DIALOG": {
|
||||
@@ -1549,6 +1550,9 @@
|
||||
}
|
||||
},
|
||||
"DIALOG": {
|
||||
"CONFIG": {
|
||||
"TITLE": "Change OIDC Configuration"
|
||||
},
|
||||
"DELETE": {
|
||||
"TITLE": "Delete App",
|
||||
"DESCRIPTION": "Do you really want to delete this application?"
|
||||
|
@@ -34,8 +34,8 @@
|
||||
"DESCRIPTION": "Iniziare rapidamente con ZITADEL."
|
||||
},
|
||||
"SHORTCUTS": {
|
||||
"SHORTCUTS": "Shortcuts",
|
||||
"SETTINGS": "Impostazioni dell' organizazzione",
|
||||
"SHORTCUTS": "Scorciatoie",
|
||||
"SETTINGS": "Scorciatoie disponibili",
|
||||
"PROJECTS": "Progetti",
|
||||
"REORDER": "Per spostare, 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."
|
||||
},
|
||||
"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": {
|
||||
"TEXT": "Attenzione! L'organizzazione è appena stata cambiata."
|
||||
@@ -818,6 +818,7 @@
|
||||
"LOCKOUT": "Meccanismi di bloccaggio",
|
||||
"COMPLEXITY": "complessità della password",
|
||||
"NOTIFICATIONS": "Notifiche",
|
||||
"NOTIFICATIONS_DESC": "Impostazioni SMTP e SMS",
|
||||
"MESSAGETEXTS": "Testi di notifica",
|
||||
"IDP": "Identity Providers",
|
||||
"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."
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"DIALOG": {
|
||||
@@ -1549,6 +1550,9 @@
|
||||
}
|
||||
},
|
||||
"DIALOG": {
|
||||
"CONFIG": {
|
||||
"TITLE": "Configurazione OIDC"
|
||||
},
|
||||
"DELETE": {
|
||||
"TITLE": "Rimuovere App",
|
||||
"DESCRIPTION": "Vuoi davvero rimuovere questa applicazione?"
|
||||
|
@@ -23,6 +23,7 @@
|
||||
@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/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/pages/projects/projects.component';
|
||||
@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/modules/project-role-chip/project-role-chip.component';
|
||||
@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/info-overlay/info-overlay.component.scss';
|
||||
@import './styles/codemirror.scss';
|
||||
@@ -67,6 +69,7 @@
|
||||
@include top-view-theme($theme);
|
||||
@include info-overlay-theme($theme);
|
||||
@include app-auth-method-radio-theme($theme);
|
||||
@include search-user-autocomplete-theme($theme);
|
||||
@include project-role-chips-theme($theme);
|
||||
@include card-theme($theme);
|
||||
@include footer-theme($theme);
|
||||
@@ -109,6 +112,7 @@
|
||||
@include user-grants-theme($theme);
|
||||
@include info-row-theme($theme);
|
||||
@include action-dialog-theme($theme);
|
||||
@include redirect-uris-theme($theme);
|
||||
@include action-keys-theme($theme);
|
||||
@include codemirror-theme($theme);
|
||||
@include contact-theme($theme);
|
||||
|
@@ -229,6 +229,9 @@ $caos-light-warn: (
|
||||
$caos-dark-theme-text: var(--theme-dark-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: (
|
||||
status-bar: map_get($caos-dark-background, 300),
|
||||
app-bar: map_get($caos-dark-background, 500),
|
||||
@@ -287,7 +290,7 @@ $caos-light-theme-background: (
|
||||
unselected-chip: map_get($caos-light-background, 300),
|
||||
disabled-list-option: map_get($caos-light-background, 200),
|
||||
tooltip: map_get($mat-gray, 700),
|
||||
infosection: map_get($caos-light-primary, 100),
|
||||
infosection: map_get($caos-light-background, 600),
|
||||
warninfosection: #ffc1c1,
|
||||
alertinfosection: rgb(251, 191, 36),
|
||||
successinfosection: #cbf4c9,
|
||||
@@ -307,7 +310,7 @@ $caos-dark-theme-foreground: (
|
||||
disabled-text: $light-disabled-text,
|
||||
elevation: black,
|
||||
hint-text: $light-disabled-text,
|
||||
secondary-text: $light-secondary-text,
|
||||
secondary-text: $caos-dark-theme-secondary-text,
|
||||
placeholder-text: rgba(white, 0.4),
|
||||
icon: rgba(white, 0.54),
|
||||
icons: rgba(white, 0.54),
|
||||
@@ -332,7 +335,7 @@ $caos-light-theme-foreground: (
|
||||
disabled-text: $dark-disabled-text,
|
||||
elevation: black,
|
||||
hint-text: $dark-disabled-text,
|
||||
secondary-text: $dark-secondary-text,
|
||||
secondary-text: $caos-light-theme-secondary-text,
|
||||
placeholder-text: rgba(black, 0.3),
|
||||
icon: rgba(black, 0.54),
|
||||
icons: rgba(black, 0.54),
|
||||
@@ -340,7 +343,7 @@ $caos-light-theme-foreground: (
|
||||
slider-min: rgba(black, 0.87),
|
||||
slider-off: rgba(black, 0.26),
|
||||
slider-off-active: rgba(black, 0.38),
|
||||
infosection: #4a4a4a,
|
||||
infosection: map-get(map-get($caos-light-background, contrast), 600),
|
||||
warninfosection: #620e0e,
|
||||
alertinfosection: rgb(146, 64, 14),
|
||||
successinfosection: #0e6245,
|
||||
@@ -514,7 +517,7 @@ $custom-typography: mat.define-typography-config(
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
-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);
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
|
||||
|
@@ -21,7 +21,7 @@
|
||||
font-size: 1rem;
|
||||
border: none;
|
||||
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;
|
||||
height: 40px;
|
||||
padding: 10px;
|
||||
|
Reference in New Issue
Block a user