mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-10 16:15:12 +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
-1
@@ -1,4 +1,4 @@
|
||||
<h1 mat-dialog-title class="title">
|
||||
<h1 class="title">
|
||||
<span>{{ data.title | translate }}</span>
|
||||
</h1>
|
||||
<div mat-dialog-content>
|
||||
|
||||
+2
-1
@@ -1,9 +1,10 @@
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
|
||||
+1
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
display: block;
|
||||
|
||||
&:not(:hover) {
|
||||
color: var(--grey);
|
||||
color: map-get($foreground, secondary-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-16
@@ -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;
|
||||
|
||||
+13
-18
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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()"
|
||||
|
||||
+5
@@ -93,6 +93,11 @@
|
||||
.pl-action-button {
|
||||
align-self: flex-start;
|
||||
margin-top: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-11
@@ -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>
|
||||
|
||||
+9
-8
@@ -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;
|
||||
}
|
||||
|
||||
+9
-6
@@ -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="[email protected]" />
|
||||
<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>
|
||||
|
||||
|
||||
+74
-64
@@ -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;
|
||||
|
||||
+2
-9
@@ -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>
|
||||
|
||||
+1
-5
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+31
-8
@@ -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>
|
||||
|
||||
+1
-1
@@ -55,5 +55,5 @@
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
+2
@@ -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,
|
||||
|
||||
+72
-33
@@ -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>
|
||||
|
||||
+48
-47
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+2
-4
@@ -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,
|
||||
|
||||
-1
@@ -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);
|
||||
|
||||
-27
@@ -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>
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.ok-button {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
}
|
||||
-25
@@ -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();
|
||||
});
|
||||
});
|
||||
-42
@@ -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);
|
||||
}
|
||||
}
|
||||
-27
@@ -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;
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
|
||||
+10
-1
@@ -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) {
|
||||
|
||||
+30
-17
@@ -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>
|
||||
|
||||
+19
-15
@@ -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[] {
|
||||
|
||||
Reference in New Issue
Block a user