mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 20:47:22 +00:00
fix(console): user create loading state and i18n, improved app create stepper, invalid token dialog, cleanup, new home (#509)
* fix iam member model * fix org member model * fix auth user loading * copytoclipboard directive * directive logs, load bar on init, create user * typo * welcome section, contributor spinner * fix home link * fix stepper flow * show dialog on invalid token * fix app table refresh, pin icons light theme * cleanup contributor * Update console/src/assets/i18n/en.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster <florian@caos.ch> Co-authored-by: Florian Forster <florian@caos.ch>
This commit is contained in:
parent
c105bf483b
commit
af60b88997
@ -204,6 +204,21 @@ export class AppComponent implements OnDestroy {
|
||||
this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/lock-reset.svg'),
|
||||
);
|
||||
|
||||
this.matIconRegistry.addSvgIcon(
|
||||
'mdi_broom',
|
||||
this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/broom.svg'),
|
||||
);
|
||||
|
||||
this.matIconRegistry.addSvgIcon(
|
||||
'mdi_pin_outline',
|
||||
this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/pin-outline.svg'),
|
||||
);
|
||||
|
||||
this.matIconRegistry.addSvgIcon(
|
||||
'mdi_pin',
|
||||
this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/pin.svg'),
|
||||
);
|
||||
|
||||
this.orgSub = this.authService.activeOrgChanged.subscribe(org => {
|
||||
this.org = org;
|
||||
});
|
||||
|
@ -5,6 +5,7 @@ import localeDe from '@angular/common/locales/de';
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
@ -27,6 +28,7 @@ import { HasRoleModule } from './directives/has-role/has-role.module';
|
||||
import { OutsideClickModule } from './directives/outside-click/outside-click.module';
|
||||
import { AccountsCardModule } from './modules/accounts-card/accounts-card.module';
|
||||
import { AvatarModule } from './modules/avatar/avatar.module';
|
||||
import { WarnDialogModule } from './modules/warn-dialog/warn-dialog.module';
|
||||
import { SignedoutComponent } from './pages/signedout/signedout.component';
|
||||
import { HasRolePipeModule } from './pipes/has-role-pipe.module';
|
||||
import { AuthUserService } from './services/auth-user.service';
|
||||
@ -105,6 +107,8 @@ const authConfig: AuthConfig = {
|
||||
MatMenuModule,
|
||||
MatSnackBarModule,
|
||||
AvatarModule,
|
||||
WarnDialogModule,
|
||||
MatDialogModule,
|
||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
||||
],
|
||||
providers: [
|
||||
|
@ -0,0 +1,31 @@
|
||||
import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appCopyToClipboard]',
|
||||
})
|
||||
export class CopyToClipboardDirective {
|
||||
@Input() valueToCopy: string = '';
|
||||
@Output() copiedValue: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
@HostListener('document:click', ['$event.target']) onMouseEnter(): void {
|
||||
this.copytoclipboard(this.valueToCopy);
|
||||
}
|
||||
|
||||
public copytoclipboard(value: string): void {
|
||||
const selBox = document.createElement('textarea');
|
||||
selBox.style.position = 'fixed';
|
||||
selBox.style.left = '0';
|
||||
selBox.style.top = '0';
|
||||
selBox.style.opacity = '0';
|
||||
selBox.value = value;
|
||||
document.body.appendChild(selBox);
|
||||
selBox.focus();
|
||||
selBox.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(selBox);
|
||||
this.copiedValue.emit(value);
|
||||
setTimeout(() => {
|
||||
this.copiedValue.emit('');
|
||||
}, 3000);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { CopyToClipboardDirective } from './copy-to-clipboard.directive';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CopyToClipboardDirective,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
],
|
||||
exports: [
|
||||
CopyToClipboardDirective,
|
||||
],
|
||||
})
|
||||
export class CopyToClipboardModule { }
|
@ -7,7 +7,7 @@
|
||||
<span class="u-email">{{profile?.preferredLoginName}}</span>
|
||||
<span class="iamuser" *ngIf="iamuser">IAM USER</span>
|
||||
|
||||
<button (click)="editUserProfile()" mat-stroked-button>{{'USER.EDITACCOUNT' | translate}}</button>
|
||||
<button color="primary" (click)="editUserProfile()" mat-stroked-button>{{'USER.EDITACCOUNT' | translate}}</button>
|
||||
<div class="l-accounts">
|
||||
<mat-progress-bar *ngIf="loadingUsers" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
<a class="row" *ngFor="let user of users" (click)="selectAccount(user.loginName)">
|
||||
@ -35,5 +35,5 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<button (click)="logout()" mat-stroked-button>{{'MENU.LOGOUT' | translate}}</button>
|
||||
<button (click)="logout()" color="primary" mat-stroked-button>{{'MENU.LOGOUT' | translate}}</button>
|
||||
</div>
|
@ -1,4 +1,5 @@
|
||||
<div class="avatar-circle dontcloseonclick"
|
||||
<div class="avatar-circle dontcloseonclick" matRipple [matRippleColor]="'#ffffff20'" matRippleUnbounded="true"
|
||||
matRippleCentered="true"
|
||||
[ngStyle]="{'height': size+'px', 'width': size+'px', 'fontSize': (fontSize-1)+'px', 'background-color': color}"
|
||||
[ngClass]="{'active': active}">
|
||||
{{credentials}}
|
||||
|
@ -16,8 +16,8 @@
|
||||
outline: none;
|
||||
color: white;
|
||||
|
||||
&.active:hover {
|
||||
border: 2px solid #8795a1;
|
||||
}
|
||||
// &.active:hover {
|
||||
// border: 2px solid #8795a1;
|
||||
// }
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
|
||||
import { AvatarComponent } from './avatar.component';
|
||||
|
||||
@ -9,6 +10,7 @@ import { AvatarComponent } from './avatar.component';
|
||||
declarations: [AvatarComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatRippleModule,
|
||||
],
|
||||
exports: [
|
||||
AvatarComponent,
|
||||
|
@ -3,6 +3,8 @@
|
||||
<span class="sub-header">{{ description }}</span>
|
||||
<div class="people">
|
||||
<div class="img-list">
|
||||
<mat-spinner diameter="20" *ngIf="loading"></mat-spinner>
|
||||
|
||||
<ng-container *ngIf="totalResult < 10; else compact">
|
||||
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
||||
<div (click)="emitShowDetail()" class="avatar-circle"
|
||||
|
@ -28,10 +28,13 @@
|
||||
margin-left: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all .15s ease-in-out;
|
||||
|
||||
mat-spinner {
|
||||
margin-left: -15px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.avatar-circle {
|
||||
transition: all .3s ease-in-out;
|
||||
float: left;
|
||||
margin: 0 8px 0 -15px;
|
||||
height: 32px;
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { animate, animateChild, query, stagger, style, transition, trigger } from '@angular/animations';
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@ -5,12 +6,28 @@ import { BehaviorSubject } from 'rxjs';
|
||||
selector: 'app-contributors',
|
||||
templateUrl: './contributors.component.html',
|
||||
styleUrls: ['./contributors.component.scss'],
|
||||
animations: [
|
||||
trigger('list', [
|
||||
transition(':enter', [
|
||||
query('@animate',
|
||||
stagger(80, animateChild()),
|
||||
),
|
||||
]),
|
||||
]),
|
||||
trigger('animate', [
|
||||
transition(':enter', [
|
||||
style({ opacity: 0, transform: 'translateX(100%)' }),
|
||||
animate('100ms', style({ opacity: 1, transform: 'translateX(0)' })),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class ContributorsComponent {
|
||||
@Input() title: string = '';
|
||||
@Input() description: string = '';
|
||||
@Input() disabled: boolean = false;
|
||||
@Input() totalResult: number = 0;
|
||||
@Input() loading: boolean = false;
|
||||
@Input() membersSubject!: BehaviorSubject<any[]>;
|
||||
@Output() addClicked: EventEmitter<void> = new EventEmitter();
|
||||
@Output() showDetailClicked: EventEmitter<void> = new EventEmitter();
|
||||
|
@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
|
||||
import { AvatarModule } from '../avatar/avatar.module';
|
||||
@ -17,6 +18,7 @@ import { ContributorsComponent } from './contributors.component';
|
||||
MatIconModule,
|
||||
MatTooltipModule,
|
||||
MatButtonModule,
|
||||
MatProgressSpinnerModule,
|
||||
],
|
||||
exports: [
|
||||
ContributorsComponent,
|
||||
|
@ -3,10 +3,10 @@
|
||||
<p class="desc"> {{data.descriptionKey | translate}}</p>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button mat-button (click)="closeDialog()">
|
||||
<button *ngIf="data.cancelKey" mat-button (click)="closeDialog()">
|
||||
{{data.cancelKey | translate}}
|
||||
</button>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<button color="warn" mat-raised-button class="ok-button" (click)="closeDialogWithSuccess()">
|
||||
{{data.confirmKey | translate}}
|
||||
</button>
|
||||
|
@ -10,12 +10,15 @@
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.ok-button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
@ -1,9 +1,17 @@
|
||||
<div class="wrapper">
|
||||
<div class="header">
|
||||
<img alt="zitadel logo" *ngIf="dark; else lighttheme"
|
||||
src="../assets/images/zitadel-logo-oneline-darkdesign.svg" />
|
||||
<ng-template #lighttheme>
|
||||
<img alt="zitadel logo" src="../assets/images/zitadel-logo-oneline-lightdesign.svg" />
|
||||
<div class="wrapper max-width-container">
|
||||
<div class="header" *ngIf="(authService.user | async) || {} as user">
|
||||
<app-avatar [routerLink]="['/users/me']" *ngIf="user && (user.displayName || (user.firstName && user.lastName))"
|
||||
class="avatar" [name]="user.displayName ? user.displayName : (user.firstName + ' '+ user.lastName)"
|
||||
[size]="100">
|
||||
</app-avatar>
|
||||
<h3 *ngIf="(user.displayName || user.firstName || user.lastName); else loader">
|
||||
{{'HOME.WELCOME' | translate}},
|
||||
{{user?.displayName ? user.displayName : (user.firstName + ' '+ user.lastName)}}</h3>
|
||||
<p>{{user?.userName}}</p>
|
||||
|
||||
<p class="wlc_stnce">{{'HOME.WELCOMESENTENCE' | translate}}</p>
|
||||
<ng-template #loader>
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
@ -77,9 +85,14 @@
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<div class="footer">
|
||||
<a color="primary" mat-button [routerLink]="['/users/me']">{{'HOME.USERS_BUTTON' | translate}}</a>
|
||||
<a color="primary" mat-button [routerLink]="['/users/all']">{{'HOME.USERS_BUTTON' | translate}}</a>
|
||||
</div>
|
||||
</app-card>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<p class="disclaimer">{{'HOME.DISCLAIMER' | translate}}
|
||||
<!-- TODO: Add doc link to security here -->
|
||||
<!-- <a href="https://caos.github.io/site#security"></a> -->
|
||||
</p>
|
||||
</div>
|
@ -1,39 +1,25 @@
|
||||
|
||||
.wrapper {
|
||||
width: 80%;
|
||||
max-width: 1350px;
|
||||
@media only screen and (max-width: 700px) {
|
||||
width: 90%;
|
||||
}
|
||||
margin: auto;
|
||||
|
||||
padding-bottom: 100px;
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 4rem 0;
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
|
||||
.avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
font-size: 100px;
|
||||
line-height: 100px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
}
|
||||
p {
|
||||
color: #8795a1;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
h3 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 60px;
|
||||
align-self: flex-end;
|
||||
.wlc_stnce {
|
||||
color: #8795a1;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,5 +70,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
color: #8795a1;
|
||||
font-size: 14px;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { AuthService } from 'src/app/services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
@ -7,9 +8,9 @@ import { Component } from '@angular/core';
|
||||
})
|
||||
export class HomeComponent {
|
||||
public dark: boolean = true;
|
||||
|
||||
constructor() {
|
||||
constructor(public authService: AuthService) {
|
||||
const theme = localStorage.getItem('theme');
|
||||
this.dark = theme === 'dark-theme' ? true : theme === 'light-theme' ? false : true;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { AvatarModule } from 'src/app/modules/avatar/avatar.module';
|
||||
import { CardModule } from 'src/app/modules/card/card.module';
|
||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||
|
||||
@ -21,7 +23,9 @@ import { HomeComponent } from './home.component';
|
||||
HomeRoutingModule,
|
||||
MatButtonModule,
|
||||
TranslateModule,
|
||||
AvatarModule,
|
||||
SharedModule,
|
||||
MatProgressSpinnerModule,
|
||||
CardModule,
|
||||
],
|
||||
})
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { DataSource } from '@angular/cdk/collections';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { IamMemberSearchResponse } from 'src/app/proto/generated/admin_pb';
|
||||
import { ProjectMember } from 'src/app/proto/generated/management_pb';
|
||||
import { IamMemberView } from 'src/app/proto/generated/admin_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
|
||||
/**
|
||||
@ -10,9 +9,9 @@ import { AdminService } from 'src/app/services/admin.service';
|
||||
* encapsulate all logic for fetching and manipulating the displayed data
|
||||
* (including sorting, pagination, and filtering).
|
||||
*/
|
||||
export class IamMembersDataSource extends DataSource<ProjectMember.AsObject> {
|
||||
export class IamMembersDataSource extends DataSource<IamMemberView.AsObject> {
|
||||
public totalResult: number = 0;
|
||||
public membersSubject: BehaviorSubject<ProjectMember.AsObject[]> = new BehaviorSubject<ProjectMember.AsObject[]>([]);
|
||||
public membersSubject: BehaviorSubject<IamMemberView.AsObject[]> = new BehaviorSubject<IamMemberView.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@ -21,25 +20,21 @@ export class IamMembersDataSource extends DataSource<ProjectMember.AsObject> {
|
||||
}
|
||||
|
||||
public loadMembers(
|
||||
pageIndex: number, pageSize: number, grantId?: string, sortDirection?: string): void {
|
||||
pageIndex: number, pageSize: number): void {
|
||||
const offset = pageIndex * pageSize;
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
// TODO
|
||||
const promise: Promise<IamMemberSearchResponse> =
|
||||
this.adminService.SearchIamMembers(pageSize, offset);
|
||||
if (promise) {
|
||||
from(promise).pipe(
|
||||
map(resp => {
|
||||
this.totalResult = resp.toObject().totalResult;
|
||||
return resp.toObject().resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
).subscribe(members => {
|
||||
this.membersSubject.next(members);
|
||||
});
|
||||
}
|
||||
|
||||
from(this.adminService.SearchIamMembers(pageSize, offset)).pipe(
|
||||
map(resp => {
|
||||
this.totalResult = resp.toObject().totalResult;
|
||||
return resp.toObject().resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
).subscribe(members => {
|
||||
this.membersSubject.next(members);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -48,7 +43,7 @@ export class IamMembersDataSource extends DataSource<ProjectMember.AsObject> {
|
||||
* the returned stream emits new items.
|
||||
* @returns A stream of the items to be rendered.
|
||||
*/
|
||||
public connect(): Observable<ProjectMember.AsObject[]> {
|
||||
public connect(): Observable<IamMemberView.AsObject[]> {
|
||||
return this.membersSubject.asObservable();
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatTable } from '@angular/material/table';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { IamMemberView } from 'src/app/proto/generated/admin_pb';
|
||||
import { ProjectMember, ProjectType, User } from 'src/app/proto/generated/management_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
@ -20,9 +21,9 @@ export class IamMembersComponent implements AfterViewInit {
|
||||
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
|
||||
public disabled: boolean = false;
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
@ViewChild(MatTable) public table!: MatTable<ProjectMember.AsObject>;
|
||||
@ViewChild(MatTable) public table!: MatTable<IamMemberView.AsObject>;
|
||||
public dataSource!: IamMembersDataSource;
|
||||
public selection: SelectionModel<ProjectMember.AsObject> = new SelectionModel<ProjectMember.AsObject>(true, []);
|
||||
public selection: SelectionModel<IamMemberView.AsObject> = new SelectionModel<IamMemberView.AsObject>(true, []);
|
||||
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
|
||||
@ -32,7 +33,7 @@ export class IamMembersComponent implements AfterViewInit {
|
||||
private toast: ToastService) {
|
||||
|
||||
this.dataSource = new IamMembersDataSource(this.adminService);
|
||||
this.dataSource.loadMembers(0, 25, 'asc');
|
||||
this.dataSource.loadMembers(0, 25);
|
||||
}
|
||||
|
||||
public ngAfterViewInit(): void {
|
||||
|
@ -41,7 +41,9 @@
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'IAM.VIEWS.ACTIONS' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let role">
|
||||
<button mat-icon-button matTooltip="{{'IAM.VIEWS.CLEAR' | translate}}"
|
||||
(click)="cancelView(role.viewName, role.database)"><i class="las la-redo-alt"></i></button>
|
||||
(click)="cancelView(role.viewName, role.database)">
|
||||
<mat-icon svgIcon="mdi_broom"></mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
@ -9,9 +9,10 @@
|
||||
</div>
|
||||
|
||||
<div class="side" metainfo>
|
||||
<app-contributors [totalResult]="totalMemberResult" [membersSubject]="membersSubject"
|
||||
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}" description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}"
|
||||
(addClicked)="openAddMember()" (showDetailClicked)="showDetail()" [disabled]="false">
|
||||
<app-contributors [totalResult]="totalMemberResult" [loading]="loading$ | async"
|
||||
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()" [disabled]="false">
|
||||
</app-contributors>
|
||||
</div>
|
||||
</app-meta-layout>
|
@ -39,8 +39,8 @@
|
||||
|
||||
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
|
||||
<mat-tab label="Details">
|
||||
<app-contributors [totalResult]="totalMemberResult" [membersSubject]="membersSubject"
|
||||
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
<app-contributors [totalResult]="totalMemberResult" [loading]="loading$ | async"
|
||||
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()" [disabled]="false">
|
||||
</app-contributors>
|
||||
|
@ -1,17 +1,12 @@
|
||||
import { DataSource } from '@angular/cdk/collections';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { ProjectMember, ProjectMemberSearchResponse } from 'src/app/proto/generated/management_pb';
|
||||
import { OrgMemberView } from 'src/app/proto/generated/management_pb';
|
||||
import { OrgService } from 'src/app/services/org.service';
|
||||
|
||||
/**
|
||||
* Data source for the ProjectMembers view. This class should
|
||||
* encapsulate all logic for fetching and manipulating the displayed data
|
||||
* (including sorting, pagination, and filtering).
|
||||
*/
|
||||
export class ProjectMembersDataSource extends DataSource<ProjectMember.AsObject> {
|
||||
export class OrgMembersDataSource extends DataSource<OrgMemberView.AsObject> {
|
||||
public totalResult: number = 0;
|
||||
public membersSubject: BehaviorSubject<ProjectMember.AsObject[]> = new BehaviorSubject<ProjectMember.AsObject[]>([]);
|
||||
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]> = new BehaviorSubject<OrgMemberView.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@ -19,26 +14,20 @@ export class ProjectMembersDataSource extends DataSource<ProjectMember.AsObject>
|
||||
super();
|
||||
}
|
||||
|
||||
public loadMembers(
|
||||
pageIndex: number, pageSize: number, grantId?: string, sortDirection?: string): void {
|
||||
public loadMembers(pageIndex: number, pageSize: number): void {
|
||||
const offset = pageIndex * pageSize;
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
// TODO
|
||||
const promise: Promise<ProjectMemberSearchResponse> =
|
||||
this.orgService.SearchMyOrgMembers(pageSize, offset);
|
||||
if (promise) {
|
||||
from(promise).pipe(
|
||||
map(resp => {
|
||||
this.totalResult = resp.toObject().totalResult;
|
||||
return resp.toObject().resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
).subscribe(members => {
|
||||
this.membersSubject.next(members);
|
||||
});
|
||||
}
|
||||
from(this.orgService.SearchMyOrgMembers(pageSize, offset)).pipe(
|
||||
map(resp => {
|
||||
this.totalResult = resp.toObject().totalResult;
|
||||
return resp.toObject().resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
).subscribe(members => {
|
||||
this.membersSubject.next(members);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +36,7 @@ export class ProjectMembersDataSource extends DataSource<ProjectMember.AsObject>
|
||||
* the returned stream emits new items.
|
||||
* @returns A stream of the items to be rendered.
|
||||
*/
|
||||
public connect(): Observable<ProjectMember.AsObject[]> {
|
||||
public connect(): Observable<OrgMemberView.AsObject[]> {
|
||||
return this.membersSubject.asObservable();
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,11 @@ import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatTable } from '@angular/material/table';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { Org, ProjectMember, ProjectType, User } from 'src/app/proto/generated/management_pb';
|
||||
import { Org, OrgMemberView, ProjectMember, ProjectType, User } from 'src/app/proto/generated/management_pb';
|
||||
import { OrgService } from 'src/app/services/org.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { ProjectMembersDataSource } from './org-members-datasource';
|
||||
import { OrgMembersDataSource } from './org-members-datasource';
|
||||
|
||||
@Component({
|
||||
selector: 'app-org-members',
|
||||
@ -21,9 +21,9 @@ export class OrgMembersComponent implements AfterViewInit {
|
||||
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
|
||||
public disabled: boolean = false;
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
@ViewChild(MatTable) public table!: MatTable<ProjectMember.AsObject>;
|
||||
public dataSource!: ProjectMembersDataSource;
|
||||
public selection: SelectionModel<ProjectMember.AsObject> = new SelectionModel<ProjectMember.AsObject>(true, []);
|
||||
@ViewChild(MatTable) public table!: MatTable<OrgMemberView.AsObject>;
|
||||
public dataSource!: OrgMembersDataSource;
|
||||
public selection: SelectionModel<OrgMemberView.AsObject> = new SelectionModel<OrgMemberView.AsObject>(true, []);
|
||||
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
|
||||
@ -33,8 +33,8 @@ export class OrgMembersComponent implements AfterViewInit {
|
||||
private toast: ToastService) {
|
||||
this.orgService.GetMyOrg().then(org => {
|
||||
this.org = org.toObject();
|
||||
this.dataSource = new ProjectMembersDataSource(this.orgService);
|
||||
this.dataSource.loadMembers(0, 25, 'asc');
|
||||
this.dataSource = new OrgMembersDataSource(this.orgService);
|
||||
this.dataSource.loadMembers(0, 25);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -41,29 +41,13 @@
|
||||
</form>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [editable]="true">
|
||||
<ng-template matStepLabel>{{'APP.OIDC.GRANTSECTION' | translate}}</ng-template>
|
||||
|
||||
<p class="step-title">{{'APP.OIDC.GRANTTITLE' | translate}}</p>
|
||||
|
||||
<div class="checkbox-container">
|
||||
<mat-checkbox (change)="changeGrant()" *ngFor="let granttype of oidcGrantTypes" color="primary"
|
||||
[(ngModel)]="granttype.checked" [disabled]="granttype.disabled">
|
||||
{{'APP.OIDC.GRANT'+granttype.type | translate}}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
|
||||
<button mat-raised-button color="primary" matStepperNext>{{'ACTIONS.CONTINUE' | translate}}</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [editable]="true" *ngIf="!grantTypeChecked(OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE)">
|
||||
<mat-step [editable]="true"
|
||||
*ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT">
|
||||
<ng-template matStepLabel>{{'APP.OIDC.RESPONSESECTION' | translate}}</ng-template>
|
||||
<div class="checkbox-container">
|
||||
<mat-checkbox *ngFor="let responsetype of oidcResponseTypes | keyvalue" (change)="changeResponseType()"
|
||||
color="primary" [(ngModel)]="responsetype.checked">
|
||||
{{'APP.OIDC.RESPONSE'+responsetype.key | translate}}
|
||||
<mat-checkbox *ngFor="let responsetype of oidcResponseTypes" (change)="changeResponseType()"
|
||||
color="primary" [(ngModel)]="responsetype.checked" [disabled]="responsetype.disabled">
|
||||
{{'APP.OIDC.RESPONSE'+responsetype.type | translate}}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="actions">
|
||||
@ -72,8 +56,8 @@
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [stepControl]="secondFormGroup" [editable]="true"
|
||||
*ngIf="applicationType?.value !== OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
|
||||
<mat-step *ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB"
|
||||
[stepControl]="secondFormGroup" [editable]="true">
|
||||
<form [formGroup]="secondFormGroup">
|
||||
<ng-template matStepLabel>{{'APP.OIDC.AUTHMETHODSECTION' | translate}}</ng-template>
|
||||
|
||||
@ -157,19 +141,32 @@
|
||||
<span class="left">
|
||||
{{ 'APP.GRANT' | translate }}
|
||||
</span>
|
||||
<span class="right">
|
||||
<span *ngFor="let element of oidcApp.grantTypesList">
|
||||
<span class="right" *ngIf="oidcApp.grantTypesList?.length > 0">
|
||||
[<span *ngFor="let element of oidcApp.grantTypesList; index as i">
|
||||
{{'APP.OIDC.GRANT'+element | translate}}
|
||||
</span>
|
||||
{{i < oidcApp.grantTypesList.length - 1 ? ', ': ''}}
|
||||
</span>]
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="left">
|
||||
{{ 'APP.OIDC.RESPONSE' | translate }}
|
||||
</span>
|
||||
<span class="right" *ngIf="oidcApp.responseTypesList?.length > 0">
|
||||
[<span *ngFor="let element of oidcApp.responseTypesList; index as i">
|
||||
{{('APP.OIDC.RESPONSE'+element | translate)}}
|
||||
{{i < oidcApp.responseTypesList.length - 1 ? ', ': ''}}
|
||||
</span>]
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span class="left">
|
||||
{{ 'APP.OIDC.AUTHMETHOD' | translate }}
|
||||
</span>
|
||||
<span class="right">
|
||||
<span *ngFor="let element of oidcApp.responseTypesList">
|
||||
{{'APP.OIDC.RESPONSE'+element | translate}}
|
||||
<span>
|
||||
{{'APP.OIDC.AUTHMETHOD'+oidcApp?.authMethodType | translate}}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -178,10 +175,11 @@
|
||||
<span class="left">
|
||||
{{ 'APP.OIDC.REDIRECT' | translate }}
|
||||
</span>
|
||||
<span class="right">
|
||||
<span *ngFor="let redirect of oidcApp.redirectUrisList">
|
||||
<span class="right" *ngIf="oidcApp.redirectUrisList?.length > 0">
|
||||
[<span *ngFor="let redirect of oidcApp.redirectUrisList; index as i">
|
||||
{{redirect}}
|
||||
</span>
|
||||
{{i < oidcApp.redirectUrisList.length - 1 ? ', ': ''}}
|
||||
</span>]
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -189,10 +187,11 @@
|
||||
<span class="left">
|
||||
{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}
|
||||
</span>
|
||||
<span class="right">
|
||||
<span *ngFor="let redirect of oidcApp.postLogoutRedirectUrisList">
|
||||
<span class="right" *ngIf="oidcApp.postLogoutRedirectUrisList?.length > 0">
|
||||
[<span *ngFor="let redirect of oidcApp.postLogoutRedirectUrisList; index as i">
|
||||
{{redirect}}
|
||||
</span>
|
||||
{{i < oidcApp.postLogoutRedirectUrisList.length - 1 ? ', ': ''}}
|
||||
</span>]
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -14,7 +14,11 @@ p.desc {
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 4rem 4rem 2rem 4rem;
|
||||
padding: 4rem 4rem 2rem 4rem;
|
||||
|
||||
mat-progress-bar {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.abort-container {
|
||||
display: flex;
|
||||
|
@ -32,11 +32,36 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
public loading: boolean = false;
|
||||
public oidcApp: OIDCApplicationCreate.AsObject = new OIDCApplicationCreate().toObject();
|
||||
|
||||
public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; }[] = [
|
||||
{ type: OIDCResponseType.OIDCRESPONSETYPE_CODE, checked: false },
|
||||
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN, checked: false },
|
||||
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN, checked: false },
|
||||
public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; disabled: boolean; }[] = [
|
||||
{ type: OIDCResponseType.OIDCRESPONSETYPE_CODE, checked: false, disabled: false },
|
||||
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN, checked: false, disabled: false },
|
||||
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN, checked: false, disabled: false },
|
||||
];
|
||||
|
||||
public oidcAppTypes: OIDCApplicationType[] = [
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB,
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT,
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE,
|
||||
];
|
||||
|
||||
public oidcAuthMethodType: { type: OIDCAuthMethodType, checked: boolean, disabled: boolean; }[] = [
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, checked: false, disabled: false },
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, checked: false, disabled: false },
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, checked: false, disabled: false },
|
||||
];
|
||||
|
||||
// stepper
|
||||
firstFormGroup!: FormGroup;
|
||||
secondFormGroup!: FormGroup;
|
||||
// thirdFormGroup!: FormGroup;
|
||||
|
||||
// devmode
|
||||
public form!: FormGroup;
|
||||
|
||||
public OIDCApplicationType: any = OIDCApplicationType;
|
||||
public OIDCGrantType: any = OIDCGrantType;
|
||||
public OIDCAuthMethodType: any = OIDCAuthMethodType;
|
||||
|
||||
public oidcGrantTypes: {
|
||||
type: OIDCGrantType,
|
||||
checked: boolean,
|
||||
@ -47,28 +72,6 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
// { type: OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, checked: false, disabled: true },
|
||||
// TODO show when implemented
|
||||
];
|
||||
public oidcAppTypes: OIDCApplicationType[] = [
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB,
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT,
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE,
|
||||
];
|
||||
public oidcAuthMethodType: { type: OIDCAuthMethodType, checked: boolean, disabled: boolean; }[] = [
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, checked: false, disabled: false },
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, checked: false, disabled: false },
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, checked: false, disabled: false },
|
||||
];
|
||||
|
||||
// stepper
|
||||
firstFormGroup!: FormGroup;
|
||||
secondFormGroup!: FormGroup;
|
||||
thirdFormGroup!: FormGroup;
|
||||
|
||||
// devmode
|
||||
public form!: FormGroup;
|
||||
|
||||
public OIDCApplicationType: any = OIDCApplicationType;
|
||||
public OIDCGrantType: any = OIDCGrantType;
|
||||
public OIDCAuthMethodType: any = OIDCAuthMethodType;
|
||||
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
|
||||
@ -92,8 +95,8 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
this.form.valueChanges.pipe(debounceTime(300)).subscribe((value) => {
|
||||
this.oidcApp.name = this.formname?.value;
|
||||
this.oidcApp.applicationType = this.formapplicationType?.value;
|
||||
this.oidcApp.grantTypesList = this.formgrantTypesList?.value;
|
||||
this.oidcApp.responseTypesList = this.formresponseTypesList?.value;
|
||||
this.oidcApp.grantTypesList = this.formgrantTypesList?.value;
|
||||
this.oidcApp.authMethodType = this.formauthMethodType?.value;
|
||||
|
||||
console.log(this.oidcApp);
|
||||
@ -103,48 +106,60 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
name: ['', [Validators.required]],
|
||||
applicationType: ['', [Validators.required]],
|
||||
});
|
||||
|
||||
this.firstFormGroup.valueChanges.subscribe(value => {
|
||||
if (this.firstFormGroup.valid) {
|
||||
console.log('change firstform', value);
|
||||
switch (value.applicationType) {
|
||||
case OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE:
|
||||
console.log('NATIVE');
|
||||
this.oidcResponseTypes[0].checked = true;
|
||||
this.oidcApp.responseTypesList = [OIDCResponseType.OIDCRESPONSETYPE_CODE];
|
||||
|
||||
this.oidcApp.grantTypesList =
|
||||
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE];
|
||||
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE;
|
||||
break;
|
||||
case OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB:
|
||||
console.log('WEB');
|
||||
|
||||
this.oidcAuthMethodType[0].disabled = false;
|
||||
this.oidcAuthMethodType[1].disabled = true; // NONE DISABLED
|
||||
this.oidcAuthMethodType[2].disabled = false; // POST POSSIBLE
|
||||
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC); // BASIC DEFAULT
|
||||
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC;
|
||||
|
||||
this.oidcResponseTypes[0].checked = true;
|
||||
this.oidcApp.responseTypesList = [OIDCResponseType.OIDCRESPONSETYPE_CODE];
|
||||
this.changeResponseType();
|
||||
|
||||
this.oidcApp.grantTypesList =
|
||||
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE];
|
||||
|
||||
break;
|
||||
case OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT:
|
||||
console.log('USERAGENT');
|
||||
|
||||
this.oidcResponseTypes[0].checked = true;
|
||||
this.oidcApp.responseTypesList = [OIDCResponseType.OIDCRESPONSETYPE_CODE];
|
||||
|
||||
this.oidcApp.grantTypesList =
|
||||
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDCGRANTTYPE_IMPLICIT];
|
||||
|
||||
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
this.oidcApp.name = this.name?.value;
|
||||
this.oidcApp.applicationType = this.applicationType?.value;
|
||||
}
|
||||
});
|
||||
|
||||
this.secondFormGroup = this.fb.group({
|
||||
authMethodType: [OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, [Validators.required]],
|
||||
});
|
||||
|
||||
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC;
|
||||
|
||||
this.firstFormGroup.valueChanges.subscribe(value => {
|
||||
if (this.applicationType?.value === OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE) {
|
||||
this.oidcResponseTypes[0].checked = true;
|
||||
// this.oidcApp.responseTypesList = [this.oidcResponseTypes[0].type];
|
||||
|
||||
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE);
|
||||
this.oidcAuthMethodType[1].disabled = true;
|
||||
}
|
||||
if (this.applicationType?.value === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB) {
|
||||
this.oidcResponseTypes[0].checked = true;
|
||||
this.oidcApp.responseTypesList = [this.oidcResponseTypes[0].type];
|
||||
|
||||
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE);
|
||||
this.oidcAuthMethodType[0].disabled = true;
|
||||
this.oidcAuthMethodType[2].disabled = true;
|
||||
}
|
||||
if (this.applicationType?.value === OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT) {
|
||||
this.oidcResponseTypes[0].checked = true;
|
||||
this.oidcApp.responseTypesList = [this.oidcResponseTypes[0].type];
|
||||
|
||||
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE);
|
||||
this.oidcAuthMethodType[0].disabled = true;
|
||||
this.oidcAuthMethodType[2].disabled = true;
|
||||
|
||||
this.oidcGrantTypes[1].disabled = false;
|
||||
}
|
||||
|
||||
this.changeResponseType();
|
||||
this.changeGrant();
|
||||
|
||||
this.oidcApp.name = this.name?.value;
|
||||
this.oidcApp.applicationType = this.applicationType?.value;
|
||||
});
|
||||
|
||||
this.secondFormGroup.valueChanges.subscribe(value => {
|
||||
this.oidcApp.authMethodType = this.authMethodType?.value;
|
||||
this.oidcApp.authMethodType = value.authMethodType;
|
||||
});
|
||||
}
|
||||
|
||||
@ -226,14 +241,14 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
changeGrant(): void {
|
||||
this.oidcApp.grantTypesList = this.oidcGrantTypes.filter(gt => gt.checked).map(gt => gt.type);
|
||||
}
|
||||
|
||||
changeResponseType(): void {
|
||||
this.oidcApp.responseTypesList = this.oidcResponseTypes.filter(gt => gt.checked).map(gt => gt.type);
|
||||
}
|
||||
|
||||
moreThanOneOption(options: Array<{ type: OIDCGrantType, checked: boolean, disabled: boolean; }>): boolean {
|
||||
return options.filter(option => option.disabled === false).length > 1;
|
||||
}
|
||||
|
||||
get name(): AbstractControl | null {
|
||||
return this.firstFormGroup.get('name');
|
||||
}
|
||||
|
@ -86,7 +86,9 @@
|
||||
<mat-form-field class="full-width" appearance="outline">
|
||||
<mat-label>{{ 'APP.OIDC.REDIRECT' | translate }}</mat-label>
|
||||
<mat-chip-list #chipRedirectList>
|
||||
<mat-chip *ngFor="let redirect of redirectUrisList" [selectable]="selectable"
|
||||
<mat-chip *ngFor="let redirect of redirectUrisList" selected
|
||||
[matTooltip]="!redirect.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
|
||||
[color]="!redirect.startsWith('https://') ? 'warn': 'green'"
|
||||
(removed)="remove(redirect, RedirectType.REDIRECT)">
|
||||
{{redirect}}
|
||||
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
|
||||
@ -100,8 +102,10 @@
|
||||
<mat-form-field class="full-width" appearance="outline">
|
||||
<mat-label>{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}</mat-label>
|
||||
<mat-chip-list #chipPostRedirectList>
|
||||
<mat-chip *ngFor="let redirect of postLogoutRedirectUrisList" [selectable]="selectable"
|
||||
(removed)="remove(redirect, RedirectType.POSTREDIRECT)">
|
||||
<mat-chip *ngFor="let redirect of postLogoutRedirectUrisList" selected
|
||||
(removed)="remove(redirect, RedirectType.POSTREDIRECT)"
|
||||
[matTooltip]="!redirect.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
|
||||
[color]="!redirect.startsWith('https://') ? 'warn': 'green'">
|
||||
{{redirect}}
|
||||
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
|
||||
</mat-chip>
|
||||
|
@ -84,4 +84,8 @@
|
||||
border-radius: .5rem;
|
||||
margin: 0 .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
mat-chip[color='green'] {
|
||||
background-color: #56a392 !important;
|
||||
}
|
@ -35,7 +35,6 @@ enum RedirectType {
|
||||
})
|
||||
export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
public errorMessage: string = '';
|
||||
public selectable: boolean = false;
|
||||
public removable: boolean = true;
|
||||
public addOnBlur: boolean = true;
|
||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||
@ -219,7 +218,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.projectService
|
||||
.UpdateOIDCAppConfig(this.projectId, this.app.id, this.app.oidcConfig)
|
||||
.then((data: OIDCConfig) => {
|
||||
.then(() => {
|
||||
this.toast.showInfo('APP.TOAST.OIDCUPDATED', true);
|
||||
})
|
||||
.catch(error => {
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
<div *ngIf="data.clientSecret" class="flex">
|
||||
<button color="primary" [disabled]="copied == data.clientSecret" matTooltip="copy to clipboard"
|
||||
(click)="copytoclipboard(data.clientSecret)" mat-icon-button>
|
||||
appCopyToClipboard [valueToCopy]="data.clientSecret" (copiedValue)="this.copied = $event" mat-icon-button>
|
||||
<i *ngIf="copied != data.clientSecret" class="las la-clipboard"></i>
|
||||
<i *ngIf="copied == data.clientSecret" class="las la-clipboard-check"></i>
|
||||
</button>
|
||||
|
@ -14,22 +14,4 @@ export class AppSecretDialogComponent {
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
public copytoclipboard(value: string): void {
|
||||
const selBox = document.createElement('textarea');
|
||||
selBox.style.position = 'fixed';
|
||||
selBox.style.left = '0';
|
||||
selBox.style.top = '0';
|
||||
selBox.style.opacity = '0';
|
||||
selBox.value = value;
|
||||
document.body.appendChild(selBox);
|
||||
selBox.focus();
|
||||
selBox.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(selBox);
|
||||
this.copied = value;
|
||||
setTimeout(() => {
|
||||
this.copied = '';
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatStepperModule } from '@angular/material/stepper';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy-to-clipboard.module';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { CardModule } from 'src/app/modules/card/card.module';
|
||||
|
||||
@ -55,6 +56,7 @@ import { AppsRoutingModule } from './apps-routing.module';
|
||||
TranslateModule,
|
||||
MatStepperModule,
|
||||
MatRadioModule,
|
||||
CopyToClipboardModule,
|
||||
],
|
||||
exports: [TranslateModule],
|
||||
})
|
||||
|
@ -34,8 +34,8 @@
|
||||
|
||||
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
|
||||
<mat-tab label="Details">
|
||||
<app-contributors [totalResult]="totalMemberResult" [membersSubject]="membersSubject"
|
||||
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
<app-contributors [totalResult]="totalMemberResult" [loading]="loading$ | async"
|
||||
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()" [disabled]="false">
|
||||
</app-contributors>
|
||||
|
@ -5,7 +5,7 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject, from, of, Subscription } from 'rxjs';
|
||||
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
@ -72,7 +72,8 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
public totalMemberResult: number = 0;
|
||||
public membersSubject: BehaviorSubject<ProjectMemberView.AsObject[]>
|
||||
= new BehaviorSubject<ProjectMemberView.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
constructor(
|
||||
public translate: TranslateService,
|
||||
|
@ -26,7 +26,8 @@
|
||||
</div>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||
mat-icon-button>
|
||||
<mat-icon>push_pin_outline</mat-icon>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -52,7 +53,8 @@
|
||||
</div>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||
mat-icon-button>
|
||||
<mat-icon>push_pin_outline</mat-icon>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<p class="n-items" *ngIf="!loading && items.length === 0 && selection.selected.length === 0">
|
||||
|
@ -6,16 +6,21 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="app-container">
|
||||
<div class="sp-container" *ngIf="(loading$ | async)">
|
||||
<mat-spinner color="accent" diameter="50"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<div [routerLink]="['/projects', projectId, 'apps', app.id ]" class="app-wrap"
|
||||
*ngFor="let app of appsSubject | async">
|
||||
<div class="morph-card">
|
||||
<div class="morph-card" matRipple>
|
||||
{{ app.name.charAt(0)}}
|
||||
</div>
|
||||
<span class="name">{{app.name}}</span>
|
||||
</div>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['project.app.write']">
|
||||
<div class="app-wrap" *ngIf="!disabled" [routerLink]="[ '/projects', projectId, 'apps', 'create']">
|
||||
<div class="morph-card add">
|
||||
<div class="morph-card add" matRipple>
|
||||
<mat-icon>add</mat-icon>
|
||||
</div>
|
||||
<span class="name">{{ 'ACTIONS.NEW' | translate }}</span>
|
||||
|
@ -21,6 +21,14 @@
|
||||
margin: 0 -1rem;
|
||||
padding-bottom: 2rem;
|
||||
|
||||
.sp-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: calc(82px + 2rem);
|
||||
height: calc(82px + 2rem);
|
||||
}
|
||||
|
||||
.app-wrap {
|
||||
outline: none;
|
||||
display: flex;
|
||||
@ -40,11 +48,24 @@
|
||||
margin: 1rem;
|
||||
border-radius: .5rem;
|
||||
border: 1px solid $accent-color;
|
||||
font-weight: 800;
|
||||
background-color: $primary-dark;
|
||||
transition: background-color .4s ease-in-out;
|
||||
transition: background-color .2s ease-in-out;
|
||||
background-image:
|
||||
linear-gradient(transparent 11px, rgba($accent-color,.5) 12px, transparent 12px),
|
||||
linear-gradient(90deg, transparent 11px, rgba($accent-color,.5) 12px, transparent 12px);
|
||||
background-size: 100% 12px, 12px 100%;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($accent-color,.2);
|
||||
}
|
||||
|
||||
&.add {
|
||||
background: $accent-color;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($accent-color,.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ export class ApplicationGridComponent implements OnInit {
|
||||
@Input() public disabled: boolean = false;
|
||||
@Output() public changeView: EventEmitter<void> = new EventEmitter();
|
||||
public appsSubject: BehaviorSubject<Application.AsObject[]> = new BehaviorSubject<Application.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
constructor(private projectService: ProjectService) { }
|
||||
|
@ -19,7 +19,7 @@ export class ProjectApplicationsDataSource extends DataSource<Application.AsObje
|
||||
super();
|
||||
}
|
||||
|
||||
public loadApps(projectId: string, pageIndex: number, pageSize: number, sortDirection?: string): void {
|
||||
public loadApps(projectId: string, pageIndex: number, pageSize: number): void {
|
||||
const offset = pageIndex * pageSize;
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
|
@ -1,54 +1,41 @@
|
||||
<div class="table-header-row">
|
||||
<div class="col">
|
||||
<ng-container *ngIf="!selection.hasValue()">
|
||||
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
|
||||
<span class="count">{{dataSource?.appsSubject.value.length}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selection.hasValue()">
|
||||
<span class="desc">{{'ORG_DETAIL.TABLE.SELECTION' | translate}}</span>
|
||||
<span class="count">{{selection?.selected?.length}}</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<ng-template appHasRole [appHasRole]="['project.app.write']">
|
||||
<app-refresh-table [loading]="dataSource.loading$ | async" [selection]="selection" (refreshed)="refreshPage()"
|
||||
[dataSize]="dataSource.totalResult">
|
||||
<ng-template appHasRole [appHasRole]="['project.app.write']" actions>
|
||||
<a [disabled]="disabled" class="add-button" [routerLink]="[ '/projects', projectId, 'apps', 'create']"
|
||||
color="primary" mat-raised-button>
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
</a>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper">
|
||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
<div class="table-wrapper">
|
||||
<table [dataSource]="dataSource" mat-table class="full-width-table" matSort aria-label="Elements">
|
||||
<ng-container matColumnDef="select">
|
||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||
[checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<td class="selection" mat-cell *matCellDef="let row">
|
||||
<mat-checkbox color="primary" (click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.NAME' | translate }} </th>
|
||||
<td class="pointer" [routerLink]="['/projects', projectId, 'apps', role.id ]" mat-cell
|
||||
*matCellDef="let role">
|
||||
{{role.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr class="data-row" mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="25"
|
||||
[pageSizeOptions]="[25, 50, 100, 250]">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
|
||||
<table [dataSource]="dataSource" mat-table class="full-width-table" matSort aria-label="Elements">
|
||||
<ng-container matColumnDef="select">
|
||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||
[checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<td class="selection" mat-cell *matCellDef="let row">
|
||||
<mat-checkbox color="primary" (click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.NAME' | translate }} </th>
|
||||
<td class="pointer" [routerLink]="['/projects', projectId, 'apps', role.id ]" mat-cell
|
||||
*matCellDef="let role">
|
||||
{{role.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr class="data-row" mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
</app-refresh-table>
|
@ -1,73 +1,39 @@
|
||||
|
||||
.table-header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.desc {
|
||||
font-size: .8rem;
|
||||
color: #8795a1;
|
||||
}
|
||||
.count {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.add-button {
|
||||
border-radius: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
overflow: auto;
|
||||
|
||||
.spinner-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
table, mat-paginator {
|
||||
width: 100%;
|
||||
background-color: #2d2e30;
|
||||
|
||||
td, th {
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.data-row {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #ffffff05;
|
||||
}
|
||||
}
|
||||
|
||||
.selection {
|
||||
width: 50px;
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
.margin-neg {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
}
|
||||
|
||||
table, mat-paginator {
|
||||
width: 100%;
|
||||
background-color: #2d2e30;
|
||||
|
||||
td, th {
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.data-row {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #ffffff05;
|
||||
}
|
||||
}
|
||||
|
||||
.selection {
|
||||
width: 50px;
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
.margin-neg {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pointer {
|
||||
|
@ -33,7 +33,7 @@ export class ApplicationsComponent implements AfterViewInit, OnInit {
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.dataSource = new ProjectApplicationsDataSource(this.projectService);
|
||||
this.dataSource.loadApps(this.projectId, 0, 25, 'asc');
|
||||
this.dataSource.loadApps(this.projectId, 0, 25);
|
||||
}
|
||||
|
||||
public ngAfterViewInit(): void {
|
||||
@ -51,7 +51,6 @@ export class ApplicationsComponent implements AfterViewInit, OnInit {
|
||||
this.projectId,
|
||||
this.paginator.pageIndex,
|
||||
this.paginator.pageSize,
|
||||
this.sort.direction,
|
||||
);
|
||||
}
|
||||
|
||||
@ -66,4 +65,8 @@ export class ApplicationsComponent implements AfterViewInit, OnInit {
|
||||
this.selection.clear() :
|
||||
this.dataSource.appsSubject.value.forEach((row: Application.AsObject) => this.selection.select(row));
|
||||
}
|
||||
|
||||
public refreshPage(): void {
|
||||
this.dataSource.loadApps(this.projectId, this.paginator.pageIndex, this.paginator.pageSize);
|
||||
}
|
||||
}
|
||||
|
@ -101,8 +101,8 @@
|
||||
|
||||
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
|
||||
<mat-tab label="Details">
|
||||
<app-contributors [totalResult]="totalMemberResult" [membersSubject]="membersSubject"
|
||||
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
<app-contributors [loading]="loading$ | async" [totalResult]="totalMemberResult"
|
||||
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()" [disabled]="false">
|
||||
</app-contributors>
|
||||
|
@ -5,7 +5,7 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject, from, of, Subscription } from 'rxjs';
|
||||
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
@ -72,7 +72,8 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
public totalMemberResult: number = 0;
|
||||
public membersSubject: BehaviorSubject<ProjectMemberView.AsObject[]>
|
||||
= new BehaviorSubject<ProjectMemberView.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
constructor(
|
||||
public translate: TranslateService,
|
||||
|
@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
@ -56,6 +57,7 @@ import { ProjectGrantsComponent } from './project-grants/project-grants.componen
|
||||
MatFormFieldModule,
|
||||
CardModule,
|
||||
MatPaginatorModule,
|
||||
MatRippleModule,
|
||||
MatCheckboxModule,
|
||||
MatSelectModule,
|
||||
MatProgressSpinnerModule,
|
||||
|
@ -29,7 +29,8 @@
|
||||
</div>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||
mat-icon-button>
|
||||
<mat-icon>push_pin</mat-icon>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -57,7 +58,8 @@
|
||||
</div>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||
mat-icon-button>
|
||||
<mat-icon>push_pin</mat-icon>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -133,10 +133,6 @@
|
||||
margin-bottom: 0.25rem;
|
||||
color: #8795a1;
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
opacity: 1;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
@ -39,6 +40,7 @@ import { ProjectGrantMembersModule } from './project-grant-members/project-grant
|
||||
MatProgressSpinnerModule,
|
||||
FormsModule,
|
||||
TranslateModule,
|
||||
MatSelectModule,
|
||||
],
|
||||
})
|
||||
export class ProjectGrantDetailModule { }
|
||||
|
@ -13,6 +13,8 @@
|
||||
<p class="desc">{{ 'USER.CREATE.DESCRIPTION' | translate }}</p>
|
||||
</div>
|
||||
|
||||
<mat-progress-bar *ngIf="loading" color="accent" mode="indeterminate"></mat-progress-bar>
|
||||
|
||||
<form *ngIf="userForm" [formGroup]="userForm" (ngSubmit)="createUser()" class="form">
|
||||
<!-- <h2>{{ 'USER.PAGES.CREATE' | translate}}</h2> -->
|
||||
<div class="content">
|
||||
|
@ -39,6 +39,7 @@ export class UserCreateComponent implements OnDestroy {
|
||||
private sub: Subscription = new Subscription();
|
||||
|
||||
public userLoginMustBeDomain: boolean = false;
|
||||
public loading: boolean = false;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
@ -47,12 +48,15 @@ export class UserCreateComponent implements OnDestroy {
|
||||
private fb: FormBuilder,
|
||||
private orgService: OrgService,
|
||||
) {
|
||||
this.loading = true;
|
||||
this.orgService.GetMyOrgIamPolicy().then((iampolicy) => {
|
||||
this.userLoginMustBeDomain = iampolicy.toObject().userLoginMustBeDomain;
|
||||
this.initForm();
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
this.initForm();
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@ -83,13 +87,16 @@ export class UserCreateComponent implements OnDestroy {
|
||||
public createUser(): void {
|
||||
this.user = this.userForm.value;
|
||||
|
||||
this.loading = true;
|
||||
this.userService
|
||||
.CreateUser(this.user)
|
||||
.then((data: User) => {
|
||||
this.loading = false;
|
||||
this.toast.showInfo('USER.TOAST.CREATED', true);
|
||||
this.router.navigate(['users', data.getId()]);
|
||||
})
|
||||
.catch(error => {
|
||||
this.loading = false;
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
@ -29,6 +30,7 @@ import { UserCreateComponent } from './user-create.component';
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatProgressBarModule,
|
||||
MatCheckboxModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
|
@ -13,7 +13,7 @@
|
||||
<span>{{login}}</span>
|
||||
<button color="primary" [disabled]="copied == login"
|
||||
[matTooltip]="(copied != login ? 'USER.PAGES.COPY' : 'USER.PAGES.COPIED' ) | translate"
|
||||
(click)="copytoclipboard(login)" mat-icon-button>
|
||||
appCopyToClipboard [valueToCopy]="login" (copiedValue)="copied = $event" mat-icon-button>
|
||||
<i *ngIf="copied != login" class="las la-clipboard"></i>
|
||||
<i *ngIf="copied == login" class="las la-clipboard-check"></i>
|
||||
</button>
|
||||
|
@ -39,9 +39,11 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
private dialog: MatDialog,
|
||||
) {
|
||||
this.loading = true;
|
||||
this.getData().then(() => {
|
||||
this.userService.GetMyUser().then(user => {
|
||||
this.user = user.toObject();
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
@ -149,30 +151,4 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
this.phoneEditState = false;
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(): Promise<void> {
|
||||
this.userService.GetMyUser().then(user => {
|
||||
this.user = user.toObject();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public copytoclipboard(value: string): void {
|
||||
const selBox = document.createElement('textarea');
|
||||
selBox.style.position = 'fixed';
|
||||
selBox.style.left = '0';
|
||||
selBox.style.top = '0';
|
||||
selBox.style.opacity = '0';
|
||||
selBox.value = value;
|
||||
document.body.appendChild(selBox);
|
||||
selBox.focus();
|
||||
selBox.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(selBox);
|
||||
this.copied = value;
|
||||
setTimeout(() => {
|
||||
this.copied = '';
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,6 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
|
||||
public getOTP(): void {
|
||||
this.service.GetMyMfas().then(mfas => {
|
||||
console.log(mfas.toObject().mfasList);
|
||||
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
||||
this.dataSource.sort = this.sort;
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
@import '~@angular/material/theming';
|
||||
|
||||
@mixin theme-card($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$primary-dark: mat-color($primary, A800);
|
||||
|
||||
.theme-conent, .theme-app , .crescent {
|
||||
background-color: $primary-dark;
|
||||
transition: background-color .4s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
$dark-background: #2d2e30;
|
||||
$light-background: #fafafa;
|
||||
|
||||
:root {
|
||||
transition: none;
|
||||
@ -21,6 +22,7 @@ $dark-background: #2d2e30;
|
||||
height: 6rem;
|
||||
background: linear-gradient(40deg, #FF0080,#FF8C00 70%);
|
||||
margin: auto;
|
||||
box-shadow: 0 30px 60px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
.crescent {
|
||||
@ -29,10 +31,10 @@ $dark-background: #2d2e30;
|
||||
right: 0;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
background: #fafafa;
|
||||
background: $light-background;
|
||||
transform: scale(0);
|
||||
transform-origin: top right;
|
||||
transition: transform .6s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
transition: transform .4s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
p {
|
||||
@ -66,7 +68,7 @@ label {
|
||||
|
||||
.toggle {
|
||||
position: absolute;
|
||||
background-color: #fafafa;
|
||||
background-color: $light-background;
|
||||
box-shadow: 0 2px 15px rgba(0,0,0,.15);
|
||||
}
|
||||
|
||||
@ -101,7 +103,7 @@ label {
|
||||
|
||||
[type="checkbox"]:checked + .theme-app{
|
||||
background-color: $dark-background;
|
||||
color: #fafafa;
|
||||
color: $light-background;
|
||||
}
|
||||
|
||||
[type="checkbox"]:checked + .theme-app .crescent{
|
||||
|
@ -13,6 +13,7 @@ import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { QRCodeModule } from 'angularx-qrcode';
|
||||
import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy-to-clipboard.module';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { CardModule } from 'src/app/modules/card/card.module';
|
||||
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
||||
@ -70,6 +71,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
||||
MatPaginatorModule,
|
||||
SharedModule,
|
||||
RefreshTableModule,
|
||||
CopyToClipboardModule,
|
||||
],
|
||||
})
|
||||
export class UserDetailModule { }
|
||||
|
@ -25,12 +25,13 @@
|
||||
<app-card title="{{ 'USER.PAGES.LOGINNAMES' | translate }}"
|
||||
description="{{ 'USER.PAGES.LOGINNAMESDESC' | translate }}" *ngIf="user">
|
||||
<div class="login-name-row" *ngFor="let login of user?.loginNamesList">
|
||||
<span>{{login}}</span>
|
||||
<span>{{login}} </span>
|
||||
<button color="primary" [disabled]="copied == login"
|
||||
[matTooltip]="(copied != login ? 'USER.PAGES.COPY' : 'USER.PAGES.COPIED' ) | translate"
|
||||
(click)="copytoclipboard(login)" mat-icon-button>
|
||||
appCopyToClipboard [valueToCopy]="login" (copiedValue)="copied = $event" mat-icon-button>
|
||||
<i *ngIf="copied != login" class="las la-clipboard"></i>
|
||||
<i *ngIf="copied == login" class="las la-clipboard-check"></i>
|
||||
|
||||
</button>
|
||||
</div>
|
||||
</app-card>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
@ -27,7 +27,6 @@ export class UserDetailComponent implements OnInit, OnDestroy {
|
||||
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
||||
public languages: string[] = ['de', 'en'];
|
||||
|
||||
public isMgmt: boolean = false;
|
||||
private subscription: Subscription = new Subscription();
|
||||
public emailEditState: boolean = false;
|
||||
public phoneEditState: boolean = false;
|
||||
@ -49,11 +48,11 @@ export class UserDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.subscription = this.route.params.subscribe(params => {
|
||||
this.loading = true;
|
||||
this.getData(params).then(() => {
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
this.loading = false;
|
||||
const { id } = params;
|
||||
this.mgmtUserService.GetUserByID(id).then(user => {
|
||||
this.user = user.toObject();
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -157,15 +156,6 @@ export class UserDetailComponent implements OnInit, OnDestroy {
|
||||
this._location.back();
|
||||
}
|
||||
|
||||
private async getData({ id }: Params): Promise<void> {
|
||||
this.isMgmt = true;
|
||||
this.mgmtUserService.GetUserByID(id).then(user => {
|
||||
this.user = user.toObject();
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
public sendSetPasswordNotification(): void {
|
||||
this.mgmtUserService.SendSetPasswordNotification(this.user.id, NotificationType.NOTIFICATIONTYPE_EMAIL)
|
||||
.then(() => {
|
||||
@ -174,22 +164,4 @@ export class UserDetailComponent implements OnInit, OnDestroy {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public copytoclipboard(value: string): void {
|
||||
const selBox = document.createElement('textarea');
|
||||
selBox.style.position = 'fixed';
|
||||
selBox.style.left = '0';
|
||||
selBox.style.top = '0';
|
||||
selBox.style.opacity = '0';
|
||||
selBox.value = value;
|
||||
document.body.appendChild(selBox);
|
||||
selBox.focus();
|
||||
selBox.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(selBox);
|
||||
this.copied = value;
|
||||
setTimeout(() => {
|
||||
this.copied = '';
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,22 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
import { WarnDialogComponent } from '../modules/warn-dialog/warn-dialog.component';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ToastService {
|
||||
constructor(private snackBar: MatSnackBar, private translate: TranslateService, private authService: AuthService) { }
|
||||
constructor(private dialog: MatDialog,
|
||||
private snackBar: MatSnackBar,
|
||||
private translate: TranslateService,
|
||||
private authService: AuthService,
|
||||
) { }
|
||||
|
||||
public showInfo(message: string, i18nkey: boolean = false): void {
|
||||
if (i18nkey) {
|
||||
@ -28,10 +34,19 @@ export class ToastService {
|
||||
const { message, code, metadata } = grpcError;
|
||||
// TODO: remove check for code === 7
|
||||
if (code === 16 || (code === 7 && message === 'invalid token')) {
|
||||
const actionObserver$ = this.showMessage(decodeURI(message), 'Login');
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.LOGIN',
|
||||
titleKey: 'ERRORS.TOKENINVALID.TITLE',
|
||||
descriptionKey: 'ERRORS.TOKENINVALID.DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
actionObserver$.pipe(take(1)).subscribe(() => {
|
||||
this.authService.authenticate(undefined, true, true);
|
||||
dialogRef.afterClosed().pipe(take(1)).subscribe(resp => {
|
||||
if (resp) {
|
||||
this.authService.authenticate(undefined, true, true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.showMessage(decodeURI(message), 'close', { duration: 3000 });
|
||||
|
@ -16,7 +16,11 @@
|
||||
"USERS_BUTTON": "Benutzer anzeigen",
|
||||
"IAM": "Identity and Access Management",
|
||||
"IAM_DESC": "Verwalte deine Organisationen und Administratoren.",
|
||||
"IAM_BUTTON": "ZITADEL verwalten"
|
||||
"IAM_BUTTON": "ZITADEL verwalten",
|
||||
"WELCOME":"Willkommen",
|
||||
"WELCOMESENTENCE":"Hier findest du die empfohlenen Aktionen basierend auf deinen zuletzt erworbenen Berechtigungen. Beachte bitte, dass du möglicherweise deine Organisation in der Kopfzeile wechseln musst.",
|
||||
"DISCLAIMER":"Du kannst nur die Einstellungen deiner aktuellen Organisation sehen, die in der Kopfzeile angegeben ist. ZITADEL behandelt deine Daten vertraulich und sicher.",
|
||||
"DISCLAIMERLINK":"Mehr Informationen zur Sicherheit"
|
||||
},
|
||||
"MENU": {
|
||||
"PERSONAL_INFO": "Persönliche Informationen",
|
||||
@ -51,10 +55,15 @@
|
||||
"CHANGE":"Ändern",
|
||||
"REACTIVATE":"Aktivieren",
|
||||
"DEACTIVATE":"Deaktivieren",
|
||||
"REFRESH":"Aktualisieren"
|
||||
"REFRESH":"Aktualisieren",
|
||||
"LOGIN":"Login"
|
||||
},
|
||||
"ERRORS": {
|
||||
"REQUIRED": "Bitte fülle alle benötigten Felder aus!"
|
||||
"REQUIRED": "Bitte fülle alle benötigten Felder aus!",
|
||||
"TOKENINVALID": {
|
||||
"TITLE":"Du bist abgemeldet",
|
||||
"DESCRIPTION":"Klicke auf Knopf Login um dich wieder einzuloggen!"
|
||||
}
|
||||
},
|
||||
"USER": {
|
||||
"TITLE": "Persönliche Informationen",
|
||||
@ -102,7 +111,8 @@
|
||||
"NAMEANDEMAILSECTION":"Name und Email",
|
||||
"GENDERLANGSECTION":"Geschlecht und Sprache",
|
||||
"PHONESECTION":"Telefonnummer",
|
||||
"PASSWORDSECTION":"Setze ein initiales Passwort."
|
||||
"PASSWORDSECTION":"Setze ein initiales Passwort.",
|
||||
"ADDRESSANDPHONESECTION":"Telefonnummer"
|
||||
},
|
||||
"CODEDIALOG": {
|
||||
"TITLE":"Telefonnummer verifizieren",
|
||||
|
@ -16,7 +16,11 @@
|
||||
"USERS_BUTTON": "Show users",
|
||||
"IAM": "Identity and Access Management",
|
||||
"IAM_DESC": "Manage your organisations and administrators.",
|
||||
"IAM_BUTTON": "Manage ZITADEL"
|
||||
"IAM_BUTTON": "Manage ZITADEL",
|
||||
"WELCOME":"Welcome",
|
||||
"WELCOMESENTENCE":"Here you can find recommended Actions based on your last acquired permissions. Note that you may have to select your organisation in the header above.",
|
||||
"DISCLAIMER":"You can only see settings of your current organisation specified in the header. ZITADEL treats your data confidentially and securely.",
|
||||
"DISCLAIMERLINK":"Further information"
|
||||
},
|
||||
"MENU": {
|
||||
"PERSONAL_INFO": "Personal Information",
|
||||
@ -51,10 +55,15 @@
|
||||
"CHANGE":"Change",
|
||||
"REACTIVATE":"Reactivate",
|
||||
"DEACTIVATE":"Deactivate",
|
||||
"REFRESH":"Refresh"
|
||||
"REFRESH":"Refresh",
|
||||
"LOGIN":"Login"
|
||||
},
|
||||
"ERRORS": {
|
||||
"REQUIRED": "Some required fields are missing!"
|
||||
"REQUIRED": "Some required fields are missing!",
|
||||
"TOKENINVALID": {
|
||||
"TITLE":"Your auth token has expired",
|
||||
"DESCRIPTION":"Click the button below to login again!"
|
||||
}
|
||||
},
|
||||
"USER": {
|
||||
"TITLE": "Personal Information",
|
||||
@ -102,7 +111,8 @@
|
||||
"NAMEANDEMAILSECTION":"Name and Email",
|
||||
"GENDERLANGSECTION":"Gender and Language",
|
||||
"PHONESECTION":"Phonenumbers",
|
||||
"PASSWORDSECTION":"Set the initial Password."
|
||||
"PASSWORDSECTION":"Set the initial Password.",
|
||||
"ADDRESSANDPHONESECTION":"Phonenumber"
|
||||
},
|
||||
"CODEDIALOG": {
|
||||
"TITLE":"Verify Phone Number",
|
||||
|
@ -61,29 +61,29 @@
|
||||
<path d="M1496.17,759.473L1555.54,720.014" style="fill:none;"/>
|
||||
</g>
|
||||
<g transform="matrix(1.32803,-0.355844,-0.311363,-1.16202,-1672.97,1561.28)">
|
||||
<path d="M1496.1,754.362C1496.1,754.362 1497.2,755.607 1501.13,753.598C1503.25,752.509 1505.74,751.023 1508.49,749.329C1513.52,746.234 1519.35,742.314 1525.19,738.438C1530.13,735.166 1534.94,731.832 1539.27,728.802C1542.87,726.279 1549.36,722.059 1549.81,721.75C1552.75,719.73 1552.18,718.196 1552.18,718.196L1555.28,724.152C1555.28,724.152 1553.77,722.905 1551.37,724.681C1550.93,725.006 1544.52,729.349 1540.82,731.68C1536.38,734.479 1531.45,737.766 1526.52,741.049C1520.68,744.94 1514.82,748.79 1509.98,752.255C1507.33,754.151 1504.89,755.771 1503.09,757.456C1499.47,760.841 1501.26,763.283 1501.26,763.283L1496.1,754.362Z" style="fill:rgb(35,35,35);"/>
|
||||
<path d="M1496.1,754.362C1496.1,754.362 1497.2,755.607 1501.13,753.598C1503.25,752.509 1505.74,751.023 1508.49,749.329C1513.52,746.234 1519.35,742.314 1525.19,738.438C1530.13,735.166 1534.94,731.832 1539.27,728.802C1542.87,726.279 1549.36,722.059 1549.81,721.75C1552.75,719.73 1552.18,718.196 1552.18,718.196L1555.28,724.152C1555.28,724.152 1553.77,722.905 1551.37,724.681C1550.93,725.006 1544.52,729.349 1540.82,731.68C1536.38,734.479 1531.45,737.766 1526.52,741.049C1520.68,744.94 1514.82,748.79 1509.98,752.255C1507.33,754.151 1504.89,755.771 1503.09,757.456C1499.47,760.841 1501.26,763.283 1501.26,763.283L1496.1,754.362Z" style="fill:#8795a1;"/>
|
||||
</g>
|
||||
<g transform="matrix(1.299,0,0,1.08306,-3394.18,-2084.88)">
|
||||
<g transform="matrix(94.2338,0,0,94.1776,2827.58,2063)">
|
||||
<path d="M0.449,-0.7L0.177,-0.7C0.185,-0.682 0.197,-0.654 0.2,-0.648C0.205,-0.639 0.216,-0.628 0.239,-0.628L0.32,-0.628C0.332,-0.628 0.336,-0.62 0.334,-0.611L0.128,0L0.389,0C0.412,0 0.422,-0.01 0.427,-0.02L0.45,-0.071L0.255,-0.071C0.245,-0.071 0.239,-0.078 0.242,-0.09L0.449,-0.7Z" style="fill:rgb(35,35,35);fill-rule:nonzero;"/>
|
||||
<path d="M0.449,-0.7L0.177,-0.7C0.185,-0.682 0.197,-0.654 0.2,-0.648C0.205,-0.639 0.216,-0.628 0.239,-0.628L0.32,-0.628C0.332,-0.628 0.336,-0.62 0.334,-0.611L0.128,0L0.389,0C0.412,0 0.422,-0.01 0.427,-0.02L0.45,-0.071L0.255,-0.071C0.245,-0.071 0.239,-0.078 0.242,-0.09L0.449,-0.7Z" style="fill:#8795a1;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(94.2338,0,0,94.1776,2912.39,2063)">
|
||||
<path d="M0.214,-0.7L0.214,-0.015C0.215,-0.01 0.218,0 0.235,0L0.286,0L0.286,-0.672C0.286,-0.684 0.278,-0.7 0.257,-0.7L0.214,-0.7Z" style="fill:rgb(35,35,35);fill-rule:nonzero;"/>
|
||||
<path d="M0.214,-0.7L0.214,-0.015C0.215,-0.01 0.218,0 0.235,0L0.286,0L0.286,-0.672C0.286,-0.684 0.278,-0.7 0.257,-0.7L0.214,-0.7Z" style="fill:#8795a1;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(94.2338,0,0,94.1776,2987.78,2063)">
|
||||
<path d="M0.441,-0.7L0.155,-0.7C0.143,-0.7 0.133,-0.69 0.133,-0.678L0.133,-0.629L0.234,-0.629L0.234,-0.015C0.234,-0.01 0.237,0 0.254,0L0.305,0L0.305,-0.612C0.306,-0.621 0.313,-0.629 0.323,-0.629L0.379,-0.629C0.402,-0.629 0.413,-0.639 0.417,-0.648L0.441,-0.7Z" style="fill:rgb(35,35,35);fill-rule:nonzero;"/>
|
||||
<path d="M0.441,-0.7L0.155,-0.7C0.143,-0.7 0.133,-0.69 0.133,-0.678L0.133,-0.629L0.234,-0.629L0.234,-0.015C0.234,-0.01 0.237,0 0.254,0L0.305,0L0.305,-0.612C0.306,-0.621 0.313,-0.629 0.323,-0.629L0.379,-0.629C0.402,-0.629 0.413,-0.639 0.417,-0.648L0.441,-0.7Z" style="fill:#8795a1;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(94.2338,0,0,94.1776,3067.88,2063)">
|
||||
<path d="M0.422,0L0.343,0L0.28,-0.482L0.217,0L0.138,0L0.244,-0.7L0.283,-0.7C0.313,-0.7 0.318,-0.681 0.321,-0.662L0.422,0Z" style="fill:rgb(35,35,35);fill-rule:nonzero;"/>
|
||||
<path d="M0.422,0L0.343,0L0.28,-0.482L0.217,0L0.138,0L0.244,-0.7L0.283,-0.7C0.313,-0.7 0.318,-0.681 0.321,-0.662L0.422,0Z" style="fill:#8795a1;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(94.2338,0,0,94.1776,3148.92,2063)">
|
||||
<path d="M0.186,-0.7L0.186,0L0.325,0C0.374,0 0.413,-0.039 0.414,-0.088L0.414,-0.612C0.413,-0.661 0.374,-0.7 0.325,-0.7L0.186,-0.7ZM0.343,-0.108C0.343,-0.081 0.325,-0.071 0.305,-0.071L0.258,-0.071L0.258,-0.628L0.305,-0.628C0.325,-0.628 0.343,-0.618 0.343,-0.592L0.343,-0.108Z" style="fill:rgb(35,35,35);fill-rule:nonzero;"/>
|
||||
<path d="M0.186,-0.7L0.186,0L0.325,0C0.374,0 0.413,-0.039 0.414,-0.088L0.414,-0.612C0.413,-0.661 0.374,-0.7 0.325,-0.7L0.186,-0.7ZM0.343,-0.108C0.343,-0.081 0.325,-0.071 0.305,-0.071L0.258,-0.071L0.258,-0.628L0.305,-0.628C0.325,-0.628 0.343,-0.618 0.343,-0.592L0.343,-0.108Z" style="fill:#8795a1;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(94.2338,0,0,94.1776,3233.73,2063)">
|
||||
<path d="M0.291,-0.071L0.291,-0.314C0.291,-0.323 0.299,-0.331 0.308,-0.331L0.338,-0.331C0.361,-0.331 0.371,-0.341 0.376,-0.35C0.379,-0.356 0.391,-0.385 0.399,-0.403L0.291,-0.403L0.291,-0.611C0.291,-0.621 0.298,-0.628 0.308,-0.628L0.366,-0.628C0.389,-0.628 0.4,-0.639 0.404,-0.648L0.428,-0.7L0.241,-0.7C0.229,-0.7 0.22,-0.691 0.219,-0.68L0.219,0L0.379,0C0.402,0 0.413,-0.01 0.418,-0.019C0.421,-0.025 0.433,-0.053 0.441,-0.071L0.291,-0.071Z" style="fill:rgb(35,35,35);fill-rule:nonzero;"/>
|
||||
<path d="M0.291,-0.071L0.291,-0.314C0.291,-0.323 0.299,-0.331 0.308,-0.331L0.338,-0.331C0.361,-0.331 0.371,-0.341 0.376,-0.35C0.379,-0.356 0.391,-0.385 0.399,-0.403L0.291,-0.403L0.291,-0.611C0.291,-0.621 0.298,-0.628 0.308,-0.628L0.366,-0.628C0.389,-0.628 0.4,-0.639 0.404,-0.648L0.428,-0.7L0.241,-0.7C0.229,-0.7 0.22,-0.691 0.219,-0.68L0.219,0L0.379,0C0.402,0 0.413,-0.01 0.418,-0.019C0.421,-0.025 0.433,-0.053 0.441,-0.071L0.291,-0.071Z" style="fill:#8795a1;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(94.2338,0,0,94.1776,3318.54,2063)">
|
||||
<path d="M0.283,-0.071L0.283,-0.678C0.283,-0.69 0.273,-0.699 0.261,-0.7L0.211,-0.7L0.211,0L0.383,0C0.406,0 0.417,-0.01 0.422,-0.019C0.425,-0.025 0.437,-0.053 0.445,-0.071L0.283,-0.071Z" style="fill:rgb(35,35,35);fill-rule:nonzero;"/>
|
||||
<path d="M0.283,-0.071L0.283,-0.678C0.283,-0.69 0.273,-0.699 0.261,-0.7L0.211,-0.7L0.211,0L0.383,0C0.406,0 0.417,-0.01 0.422,-0.019C0.425,-0.025 0.437,-0.053 0.445,-0.071L0.283,-0.071Z" style="fill:#8795a1;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
1
console/src/assets/mdi/broom.svg
Normal file
1
console/src/assets/mdi/broom.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19.36,2.72L20.78,4.14L15.06,9.85C16.13,11.39 16.28,13.24 15.38,14.44L9.06,8.12C10.26,7.22 12.11,7.37 13.65,8.44L19.36,2.72M5.93,17.57C3.92,15.56 2.69,13.16 2.35,10.92L7.23,8.83L14.67,16.27L12.58,21.15C10.34,20.81 7.94,19.58 5.93,17.57Z" /></svg>
|
After Width: | Height: | Size: 531 B |
1
console/src/assets/mdi/pin-outline.svg
Normal file
1
console/src/assets/mdi/pin-outline.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12M8.8,14L10,12.8V4H14V12.8L15.2,14H8.8Z" /></svg>
|
After Width: | Height: | Size: 391 B |
1
console/src/assets/mdi/pin.svg
Normal file
1
console/src/assets/mdi/pin.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z" /></svg>
|
After Width: | Height: | Size: 354 B |
@ -5,7 +5,7 @@
|
||||
@import './styles/changes';
|
||||
@import 'src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component';
|
||||
@import './styles/meta';
|
||||
@import './styles/theme-card';
|
||||
@import 'src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-card';
|
||||
|
||||
@mixin component-themes($theme) {
|
||||
@include avatar-theme($theme);
|
||||
|
@ -154,7 +154,7 @@ $custom-typography: mat-typography-config(
|
||||
@include angular-material-theme($light-theme);
|
||||
color: #202124;
|
||||
|
||||
.sidenav, .main-container {
|
||||
.sidenav, .main-container, .mat-dialog-container {
|
||||
background-color: #fafafa;
|
||||
transition: background-color .4s ease-in-out;
|
||||
}
|
||||
@ -183,7 +183,7 @@ $custom-typography: mat-typography-config(
|
||||
@include component-themes($dark-theme);
|
||||
@include angular-material-theme($dark-theme);
|
||||
|
||||
.sidenav, .main-container {
|
||||
.sidenav, .main-container, .mat-dialog-container {
|
||||
background-color: #212224;
|
||||
transition: background-color .4s ease-in-out;
|
||||
}
|
||||
|
@ -45,5 +45,11 @@
|
||||
.iamuser {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.edit-button {
|
||||
&:hover {
|
||||
color: $border-selected-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@
|
||||
}
|
||||
|
||||
.root-header {
|
||||
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.1), 0px 1px 1px 0px rgba(0, 0, 0, 0.1), 0px 1px 3px 0px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,0.12);
|
||||
background-color: $primary-dark !important;
|
||||
transition: background-color .4s ease-in-out;
|
||||
}
|
||||
@ -56,6 +56,9 @@
|
||||
.admin-line {
|
||||
background: $accent-color;
|
||||
color: white;
|
||||
margin-right: 1rem;
|
||||
border-top-right-radius: 50vw;
|
||||
border-bottom-right-radius: 50vw;
|
||||
|
||||
// &::after {
|
||||
// content: '';
|
||||
|
@ -1,21 +0,0 @@
|
||||
@import '~@angular/material/theming';
|
||||
|
||||
@mixin theme-card($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$primary-dark: mat-color($primary, A800);
|
||||
|
||||
.theme-conent {
|
||||
background-color: $primary-dark;
|
||||
transition: background-color .4s ease-in-out;
|
||||
}
|
||||
|
||||
.theme-app {
|
||||
background-color: $primary-dark;
|
||||
transition: background-color .4s ease-in-out;
|
||||
}
|
||||
|
||||
.crescent {
|
||||
background-color: $primary-dark;
|
||||
transition: background-color .4s ease-in-out;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user