Merge remote-tracking branch 'origin/master' into new-eventstore

This commit is contained in:
adlerhurst 2020-11-26 13:14:12 +01:00
commit 3bd4d3a8e3
204 changed files with 3743 additions and 2134 deletions

View File

@ -12,7 +12,7 @@
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"prefix": "cnsl",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",

1793
console/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -30,8 +30,8 @@
"angular-oauth2-oidc": "^10.0.3",
"angularx-qrcode": "^10.0.11",
"cors": "^2.8.5",
"file-saver": "^2.0.2",
"google-proto-files": "^2.2.0",
"file-saver": "^2.0.5",
"google-proto-files": "^2.3.0",
"google-protobuf": "^3.13.0",
"grpc": "^1.24.3",
"grpc-web": "^1.2.1",
@ -44,24 +44,24 @@
"zone.js": "~0.11.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1100.1",
"@angular/cli": "~11.0.1",
"@angular-devkit/build-angular": "~0.1100.2",
"@angular/cli": "~11.0.2",
"@angular/compiler-cli": "~11.0.0",
"@angular/language-service": "~11.0.0",
"@types/jasmine": "~3.6.0",
"@types/jasmine": "~3.6.2",
"@angular/language-service": "~11.0.2",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^14.14.6",
"@types/node": "^14.14.9",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"jasmine-spec-reporter": "~6.0.0",
"karma": "~5.2.3",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"prettier": "^2.1.2",
"prettier": "^2.2.0",
"protractor": "~7.0.0",
"stylelint": "^13.7.2",
"stylelint": "^13.8.0",
"stylelint-config-standard": "^20.0.0",
"stylelint-scss": "^3.18.0",
"ts-node": "~9.0.0",

View File

@ -27,6 +27,23 @@ export const toolbarAnimation: AnimationTriggerMetadata =
]),
]);
export const adminLineAnimation: AnimationTriggerMetadata =
trigger('adminline', [
transition(':enter', [
style({
transform: 'translateY(100%)',
opacity: 0.5,
}),
animate(
'.2s ease-out',
style({
transform: 'translateY(0%)',
opacity: 1,
}),
),
]),
]);
export const accountCard: AnimationTriggerMetadata = trigger('accounts', [
transition(':enter', [
style({

View File

@ -24,10 +24,11 @@
</mat-spinner>
</div>
<mat-form-field class="filter-form" appearance="fill">
<input matInput [formControl]="filterControl" autocomplete="off" (click)="$event.stopPropagation()"
placeholder="{{'ORG.PAGES.FILTERPLACEHOLDER' | translate}}" #input>
</mat-form-field>
<div class="filter-wrapper">
<input cnslInput class="filter-input" [formControl]="filterControl" autocomplete="off"
(click)="$event.stopPropagation()" placeholder="{{'ORG.PAGES.FILTERPLACEHOLDER' | translate}}"
#input>
</div>
<div class="org-wrapper">
<button [ngClass]="{'active': temporg.id === org?.id}" [disabled]="!temporg.id"
@ -64,8 +65,9 @@
<div class="side-column">
<div class="list">
<ng-container *ngIf="authenticationService.authenticationChanged | async">
<a @navitem class="nav-item" [routerLinkActive]="['active']"
[routerLinkActiveOptions]="{ exact: true }" [routerLink]="['/users/me']">
<a @navitem matTooltip="{{'MENU.TOOLTIP.PERSONAL' | translate}}" class="nav-item"
[routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: true }"
[routerLink]="['/users/me']">
<i class="icon las la-user-circle"></i>
<span class="label">{{ 'MENU.PERSONAL_INFO' | translate }}</span>
</a>
@ -77,7 +79,8 @@
<span>{{'MENU.ADMINSECTION' | translate}}</span>
<div class="hiddenline"></div>
</div>
<a @navitem class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/iam']">
<a @navitem matTooltip="{{'MENU.TOOLTIP.IAM' | translate}}" class="nav-item"
[routerLinkActive]="['active']" [routerLink]="[ '/iam']">
<i class="icon las la-gem"></i>
<span class="label">{{'MENU.IAM' | translate}}</span>
</a>
@ -85,7 +88,14 @@
<div *ngIf="org" [@navAnimation]="org">
<ng-template appHasRole [appHasRole]="['org.read']">
<a @navitem class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/org']">
<div @navitem class="divider">
<div class="line"></div>
<span>{{'MENU.ORGSECTION' | translate}}</span>
<div class="hiddenline"></div>
</div>
<a @navitem matTooltip="{{'MENU.TOOLTIP.ORG' | translate}}" class="nav-item"
[routerLinkActive]="['active']" [routerLink]="[ '/org']">
<i class="icon las la-archway"></i>
<span
class="label">{{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}}</span>
@ -93,13 +103,8 @@
</ng-template>
<ng-template appHasRole [appHasRole]="['project.read(:[0-9]*)?']">
<div @navitem class="divider">
<div class="line"></div>
<span>{{'MENU.PROJECTSSECTION' | translate}}</span>
<div class="hiddenline"></div>
</div>
<a @navitem class="nav-item" [routerLinkActive]="['active']"
<a @navitem matTooltip="{{'MENU.TOOLTIP.SELFPROJECTS' | translate}}"
class="nav-item indented" [routerLinkActive]="['active']"
[routerLink]="[ '/projects']">
<i class="icon las la-layer-group"></i>
@ -111,9 +116,9 @@
</div>
</a>
<a @navitem
<a @navitem matTooltip="{{'MENU.TOOLTIP.GRANTEDPROJECTS' | translate}}"
*ngIf="mgmtService?.grantedProjectsCount && (mgmtService?.grantedProjectsCount | async)"
class="nav-item" [routerLinkActive]="['active']"
class="nav-item indented" [routerLinkActive]="['active']"
[routerLink]="[ '/granted-projects']">
<i class="icon las la-layer-group"></i>
<div class="c_label">
@ -124,20 +129,15 @@
</ng-template>
<ng-template appHasRole [appHasRole]="['user.read(:[0-9]*)?']">
<div @navitem class="divider">
<div class="line"></div>
<span class="label">
{{ 'MENU.USERSECTION' | translate }}</span>
<div class="hiddenline"></div>
</div>
<a @navitem class="nav-item" [routerLinkActive]="['active']"
<a @navitem matTooltip="{{'MENU.TOOLTIP.HUMANUSERS' | translate}}"
class="nav-item indented" [routerLinkActive]="['active']"
[routerLink]="[ '/users/list/humans']" [routerLinkActiveOptions]="{ exact: true }">
<i class="icon las la-user-friends"></i>
<span class="label">{{ 'MENU.HUMANUSERS' | translate }}</span>
</a>
<a @navitem class="nav-item" [routerLinkActive]="['active']"
<a @navitem matTooltip="{{'MENU.TOOLTIP.MACHINEUSERS' | translate}}"
class="nav-item indented" [routerLinkActive]="['active']"
[routerLink]="[ '/users/list/machines']"
[routerLinkActiveOptions]="{ exact: true }">
<i class="icon las la-users-cog"></i>
@ -146,15 +146,15 @@
</ng-template>
<ng-template appHasRole [appHasRole]="['user.grant.read(:[0-9]*)?']">
<div @navitem class="divider">
<!-- <div @navitem class="divider">
<div class="line"></div>
<span class="label">
{{ 'MENU.GRANTSECTION' | translate }}</span>
<div class="hiddenline"></div>
</div>
</div> -->
<a @navitem class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/grants']"
[routerLinkActiveOptions]="{ exact: true }">
<a @navitem class="nav-item indented" [routerLinkActive]="['active']"
[routerLink]="[ '/grants']" [routerLinkActiveOptions]="{ exact: true }">
<i class="icon las la-shield-alt"></i>
<span class="label">{{ 'MENU.GRANTS' | translate }}</span>
</a>
@ -172,7 +172,7 @@
</div>
</mat-drawer-content>
</mat-drawer-container>
<div @toolbar *ngIf="iamuser$ | async" class="admin-line" [ngClass]="{'expanded': !hideAdminWarn}"
<div @adminline *ngIf="iamuser$ | async" class="admin-line" [ngClass]="{'expanded': !hideAdminWarn}"
matTooltip="IAM Administrator">
<button [matTooltip]="!hideAdminWarn ? 'Unpin': 'Pin'" (click)="toggleAdminHide()" mat-icon-button>
<mat-icon *ngIf="!hideAdminWarn" svgIcon="mdi_pin"></mat-icon>

View File

@ -103,17 +103,21 @@
align-items: center;
text-decoration: none;
cursor: pointer;
padding: .2rem 1rem;
padding: 0 1rem;
margin-right: .5rem;
&.indented {
padding-left: 2rem;
}
.icon {
margin: .5rem 1rem;
}
.label {
margin-bottom: 0;
font-weight: 500;
font-size: .9rem;
font-size: 14px;
letter-spacing: .05em;
}
.c_label {
@ -121,7 +125,8 @@
display: flex;
justify-content: space-between;
align-items: center;
font-size: .9rem;
font-size: 14px;
letter-spacing: .03em;
.count {
font-size: 12px;
@ -198,7 +203,7 @@
display: flex;
align-items: center;
width: 100%;
margin: .5rem 0;
margin: 5px 0;
span {
border: 1px solid #81868a40;
@ -219,7 +224,6 @@
.hiddenline {
display: block;
visibility: hidden;
// flex: 1;
width: 4rem;
}
}
@ -228,7 +232,6 @@
.filter-form {
margin: 0 .5rem;
/* stylelint-disable */
$foreground: map-get($theme, foreground);
color: mat-color($foreground, text) !important;
}
@ -245,6 +248,10 @@
.menu {
position: relative;
.filter-wrapper {
padding: 4px;
}
.spinner-w {
top: 1rem;
left: 0;

View File

@ -11,7 +11,7 @@ import { LangChangeEvent, TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
import { catchError, debounceTime, finalize, map, take } from 'rxjs/operators';
import { accountCard, navAnimations, routeAnimations, toolbarAnimation } from './animations';
import { accountCard, adminLineAnimation, navAnimations, routeAnimations, toolbarAnimation } from './animations';
import {
MyProjectOrgSearchKey,
MyProjectOrgSearchQuery,
@ -34,6 +34,7 @@ import { UpdateService } from './services/update.service';
...navAnimations,
accountCard,
routeAnimations,
adminLineAnimation,
],
})
export class AppComponent implements OnDestroy {

View File

@ -8,9 +8,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatNativeDateModule } from '@angular/material/core';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@ -34,6 +32,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 { InputModule } from './modules/input/input.module';
import { WarnDialogModule } from './modules/warn-dialog/warn-dialog.module';
import { SignedoutComponent } from './pages/signedout/signedout.component';
import { HasRolePipeModule } from './pipes/has-role-pipe/has-role-pipe.module';
@ -110,8 +109,7 @@ const authConfig: AuthConfig = {
MatSidenavModule,
MatCardModule,
OutsideClickModule,
MatFormFieldModule,
MatInputModule,
InputModule,
HasRolePipeModule,
MatProgressBarModule,
MatProgressSpinnerModule,

View File

@ -6,15 +6,15 @@
<div mat-dialog-content>
<!-- if no context -->
<ng-container *ngIf="showCreationTypeSelector">
<mat-form-field class="full-width" appearance="outline">
<mat-label>{{ 'MEMBER.CREATIONTYPE' | translate }}</mat-label>
<cnsl-form-field class="full-width" appearance="outline">
<cnsl-label>{{ 'MEMBER.CREATIONTYPE' | translate }}</cnsl-label>
<mat-select [(ngModel)]="creationType" (selectionChange)="loadRoles()">
<mat-option *ngFor="let type of creationTypes" [value]="type.type"
[disabled]="(type.disabled$ | async) == false">
{{ 'MEMBER.CREATIONTYPES.'+type.type | translate}}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
<ng-container
*ngIf="creationType === CreationType.PROJECT_OWNED || creationType === CreationType.PROJECT_GRANTED">
@ -30,15 +30,15 @@
<app-search-user-autocomplete [users]="preselectedUsers" (selectionChanged)="users = $event">
</app-search-user-autocomplete>
<mat-form-field class="full-width" appearance="outline"
<cnsl-form-field class="full-width" appearance="outline"
*ngIf="creationType === CreationType.PROJECT_OWNED || creationType === CreationType.PROJECT_GRANTED || creationType === CreationType.IAM">
<mat-label>{{ 'ROLESLABEL' | translate }}</mat-label>
<cnsl-label>{{ 'ROLESLABEL' | translate }}</cnsl-label>
<mat-select [(ngModel)]="roles" multiple>
<mat-option *ngFor="let role of memberRoleOptions" [value]="role">
{{ role }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
<ng-container *ngIf="creationType === CreationType.ORG">
<app-org-member-roles-autocomplete (selectionChanged)="setOrgMemberRoles($event)">

View File

@ -4,10 +4,9 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
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 {
@ -24,9 +23,8 @@ import { MemberCreateDialogComponent } from './member-create-dialog.component';
MatDialogModule,
MatButtonModule,
MatChipsModule,
MatInputModule,
TranslateModule,
MatFormFieldModule,
InputModule,
MatSelectModule,
FormsModule,
ReactiveFormsModule,

View File

@ -1,6 +1,6 @@
<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}"
[ngStyle]="{'height': size+'px', 'width': size+'px', 'fontSize': (fontSize-1)+'px', 'background': color}"
[ngClass]="{'active': active}">
{{credentials}}
</div>

View File

@ -13,7 +13,10 @@
text-transform: uppercase;
background-color: $primary-color;
box-sizing: border-box;
letter-spacing: .05em;
font-size: 14px;
outline: none;
color: white;
font-weight: bold;
}
}

View File

@ -9,7 +9,7 @@ export class AvatarComponent implements OnInit {
@Input() name: string = '';
@Input() credentials: string = '';
@Input() size: number = 24;
@Input() fontSize: number = 16;
@Input() fontSize: number = 14;
@Input() active: boolean = false;
@Input() color: string = '';
constructor() { }
@ -30,22 +30,22 @@ export class AvatarComponent implements OnInit {
getColor(userName: string): string {
const colors = [
'#B44D51',
'#B75073',
'#84498E',
'#705998',
'#5C6598',
'#7F90D3',
'#3E93B9',
'#3494A0',
'#25716A',
'#427E41',
'#89A568',
'#90924D',
'#E2B032',
'#C97358',
'#6D5B54',
'#6B7980',
'linear-gradient(40deg, #B44D51 30%, rgb(241,138,138))',
'linear-gradient(40deg, #B75073 30%, rgb(234,96,143))',
'linear-gradient(40deg, #84498E 30%, rgb(214,116,230))',
'linear-gradient(40deg, #705998 30%, rgb(163,131,220))',
'linear-gradient(40deg, #5C6598 30%, rgb(135,148,222))',
'linear-gradient(40deg, #7F90D3 30%, rgb(181,196,247))',
'linear-gradient(40deg, #3E93B9 30%, rgb(150,215,245))',
'linear-gradient(40deg, #3494A0 30%, rgb(71,205,222))',
'linear-gradient(40deg, #25716A 30%, rgb(58,185,173))',
'linear-gradient(40deg, #427E41 30%, rgb(97,185,96))',
'linear-gradient(40deg, #89A568 30%, rgb(176,212,133))',
'linear-gradient(40deg, #90924D 30%, rgb(187,189,98))',
'linear-gradient(40deg, #E2B032 30%, rgb(245,203,99))',
'linear-gradient(40deg, #C97358 30%, rgb(245,148,118))',
'linear-gradient(40deg, #6D5B54 30%, rgb(152,121,108))',
'linear-gradient(40deg, #6B7980 30%, rgb(134,163,177))',
];
let hash = 0;

View File

@ -4,7 +4,7 @@
padding: 1.5rem;
border-radius: .5rem;
padding-top: 1rem;
min-width: 350px;
min-width: 300px;
.header {
margin-top: 0;
@ -21,7 +21,9 @@
.title {
margin: 0;
font-weight: 400;
font-size: 18px;
font-size: 16px;
text-transform: uppercase;
letter-spacing: .05em;
}
.fill-space {
@ -45,4 +47,9 @@
width: 100%;
height: 100%;
}
@media only screen and (max-width: 500px) {
margin: .5rem 0;
padding: .5rem;
}
}

View File

@ -1,4 +1,9 @@
<span class="header">{{ 'CHANGES.LISTTITLE' | translate }}</span>
<div class="change-header">
<span class="ch-header">{{ 'CHANGES.LISTTITLE' | translate }}</span>
<button matTooltip="{{'ACTIONS.REFRESH' | translate}}" (click)="init()" mat-icon-button>
<mat-icon class="icon">refresh</mat-icon>
</button>
</div>
<div class="scroll-container" appScrollable (scrollPosition)="scrollHandler($event)">
<li class="item change-item-back" *ngFor="let event of data | async">

View File

@ -1,11 +1,23 @@
@import '~@angular/material/theming';
.header {
display: block;
margin-bottom: 1rem;
font-weight: 400;
margin-top: 1rem;
font-size: 14px;
.change-header {
display: flex;
justify-content: space-between;
align-items: center;
.ch-header {
display: block;
margin-bottom: 1rem;
font-weight: 400;
margin-top: 1rem;
font-size: 14px;
letter-spacing: .05em;
text-transform: uppercase;
}
.icon {
font-size: 1.2rem;
}
}
@mixin changes-theme($theme) {

View File

@ -1,6 +1,6 @@
import { Component, Input, OnInit } from '@angular/core';
import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, scan, take, tap } from 'rxjs/operators';
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { BehaviorSubject, from, Observable, of, Subject } from 'rxjs';
import { catchError, debounceTime, scan, take, takeUntil, tap } from 'rxjs/operators';
import { Change, Changes } from 'src/app/proto/generated/management_pb';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service';
@ -10,6 +10,7 @@ export enum ChangeType {
USER = 'user',
ORG = 'org',
PROJECT = 'project',
APP = 'app',
}
@Component({
@ -17,10 +18,12 @@ export enum ChangeType {
templateUrl: './changes.component.html',
styleUrls: ['./changes.component.scss'],
})
export class ChangesComponent implements OnInit {
export class ChangesComponent implements OnInit, OnDestroy {
@Input() public changeType: ChangeType = ChangeType.USER;
@Input() public id: string = '';
@Input() public secId: string = '';
@Input() public sortDirectionAsc: boolean = true;
@Input() public refresh!: Observable<void>;
public bottom: boolean = false;
private _done: BehaviorSubject<any> = new BehaviorSubject(false);
@ -30,10 +33,23 @@ export class ChangesComponent implements OnInit {
loading: Observable<boolean> = this._loading.asObservable();
public data!: Observable<Change.AsObject[]>;
public changes!: Changes.AsObject;
constructor(private mgmtUserService: ManagementService, private authUserService: GrpcAuthService) { }
private destroyed$: Subject<void> = new Subject();
constructor(private mgmtUserService: ManagementService, private authUserService: GrpcAuthService) {
}
ngOnInit(): void {
this.init();
if (this.refresh) {
this.refresh.pipe(takeUntil(this.destroyed$), debounceTime(2000)).subscribe(() => {
console.log('asdf');
this.init();
});
}
}
ngOnDestroy(): void {
this.destroyed$.next();
}
public scrollHandler(e: any): void {
@ -42,7 +58,7 @@ export class ChangesComponent implements OnInit {
}
}
private init(): void {
public init(): void {
let first: Promise<Changes>;
switch (this.changeType) {
case ChangeType.MYUSER: first = this.authUserService.GetMyUserChanges(20, 0);
@ -53,6 +69,8 @@ export class ChangesComponent implements OnInit {
break;
case ChangeType.ORG: first = this.mgmtUserService.OrgChanges(this.id, 20, 0);
break;
case ChangeType.APP: first = this.mgmtUserService.ApplicationChanges(this.id, this.secId, 20, 0);
break;
}
this.mapAndUpdate(first);
@ -78,6 +96,8 @@ export class ChangesComponent implements OnInit {
break;
case ChangeType.ORG: more = this.mgmtUserService.OrgChanges(this.id, 20, cursor);
break;
case ChangeType.APP: more = this.mgmtUserService.ApplicationChanges(this.id, this.secId, 20, cursor);
break;
}
this.mapAndUpdate(more);

View File

@ -1,7 +1,10 @@
import { ScrollingModule } from '@angular/cdk/scrolling';
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 { TranslateModule } from '@ngx-translate/core';
import { ScrollableModule } from 'src/app/directives/scrollable/scrollable.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
@ -20,10 +23,13 @@ import { ChangesComponent } from './changes.component';
ScrollableModule,
MatProgressSpinnerModule,
TranslateModule,
MatIconModule,
MatButtonModule,
HasRolePipeModule,
ScrollingModule,
LocalizedDatePipeModule,
TimestampToDatePipeModule,
MatTooltipModule,
],
exports: [
ChangesComponent,

View File

@ -1,5 +1,5 @@
<div class="groups">
<span class="header">{{ title }}</span>
<span class="co-header">{{ title }}</span>
<span class="sub-header">{{ description }}</span>
<div class="people">
<div class="img-list" [@cardAnimation]="totalResult">
@ -34,9 +34,9 @@
<mat-icon>add</mat-icon>
</button>
<span class="fill-space"></span>
<button class="refresh-img" (click)="emitRefresh()" [disabled]="disabled" mat-icon-button
aria-label="refresh contributors">
<mat-icon>refresh</mat-icon>
<button matTooltip="{{'ACTIONS.REFRESH' | translate}}" class="refresh-img" (click)="emitRefresh()"
[disabled]="disabled" mat-icon-button aria-label="refresh contributors">
<mat-icon class="icon">refresh</mat-icon>
</button>
</div>
</div>

View File

@ -1,10 +1,13 @@
.groups {
padding-top: 1rem;
.header {
.co-header {
display: block;
margin-bottom: 1rem;
font-weight: 400;
font-size: 14px;
letter-spacing: .05em;
text-transform: uppercase;
}
.sub-header {
@ -45,7 +48,11 @@
.refresh-img {
float: left;
margin: 0 8px 0 -15px;
margin: 0 0 0 -15px;
.icon {
font-size: 1.2rem;
}
}
.avatar-circle {

View File

@ -4,6 +4,7 @@ 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 { TranslateModule } from '@ngx-translate/core';
import { AvatarModule } from '../avatar/avatar.module';
import { ContributorsComponent } from './contributors.component';
@ -19,6 +20,7 @@ import { ContributorsComponent } from './contributors.component';
MatTooltipModule,
MatButtonModule,
MatProgressSpinnerModule,
TranslateModule,
],
exports: [
ContributorsComponent,

View File

@ -15,6 +15,7 @@
padding-bottom: 3rem;
.detail-left {
align-self: flex-start;
width: 100px;
display: flex;
padding: 1rem;

View File

@ -0,0 +1,18 @@
import { animate, AnimationTriggerMetadata, state, style, transition, trigger } from '@angular/animations';
/**
* Animations used by the CnslFormFieldComponent.
*/
export const cnslFormFieldAnimations: {
readonly transitionMessages: AnimationTriggerMetadata;
} = {
/** Animation that transitions the form field's error and hint messages. */
transitionMessages: trigger('transitionMessages', [
// TODO(mmalerba): Use angular animations for label animation as well.
state('enter', style({ opacity: 1, transform: 'translateY(0%)' })),
transition('void => enter', [
style({ opacity: 0, transform: 'translateY(-100%)' }),
animate('3000ms cubic-bezier(0.55, 0, 0.55, 0.2)'),
]),
]),
};

View File

@ -0,0 +1,20 @@
import { Directive, InjectionToken, Input } from '@angular/core';
let nextUniqueId = 0;
export const CNSL_ERROR = new InjectionToken<CnslErrorDirective>('CnslError');
@Directive({
selector: '[cnsl-error]',
host: {
'class': 'cnsl-error',
'role': 'alert',
'[attr.id]': 'id',
},
providers: [{ provide: CNSL_ERROR, useExisting: CnslErrorDirective }],
})
export class CnslErrorDirective {
@Input() id: string = `cnsl-error-${nextUniqueId++}`;
constructor() { }
}

View File

@ -0,0 +1,17 @@
<div class="cnsl-form-field-wrapper" (click)="_control.onContainerClick && _control.onContainerClick($event)">
<ng-content select="cnsl-label"></ng-content>
<div class="cnsl-rel" #inputContainer>
<ng-content></ng-content>
<ng-content select="cnslSuffix"></ng-content>
</div>
<div class="cnsl-form-field-subscript-wrapper" [ngSwitch]="_getDisplayedMessages()">
<div *ngSwitchCase="'error'" class="cnsl-form-field-error-wrapper"
[@transitionMessages]="_subscriptAnimationState">
<ng-content select="cnsl-error"></ng-content>
</div>
<div *ngSwitchCase="'hint'" class="cnsl-form-field-hint-wrapper"
[@transitionMessages]="_subscriptAnimationState">
<ng-content select="cnsl-hint"></ng-content>
</div>
</div>
</div>

View File

@ -0,0 +1,45 @@
@import '~@angular/material/theming';
@mixin cnsl-form-field-theme($theme) {
$primary: map-get($theme, primary);
$primary-color: mat-color($primary, 500);
$is-dark-theme: map-get($theme, is-dark);
.ng-untouched {
.cnsl-error {
visibility: hidden;
transition: visibility .2 ease;
}
}
.cnsl-form-field-wrapper {
width: 100%;
margin: 10px 0;
}
.cnsl-rel {
position: relative;
}
[cnslSuffix] {
position: absolute;
right: .5rem;
top: 9px;
height: inherit;
vertical-align: middle;
}
// Wrapper for the hints and error messages.
.cnsl-form-field-subscript-wrapper {
position: absolute;
box-sizing: border-box;
width: 100%;
overflow: hidden; // prevents multi-line errors from overlapping the control
}
.cnsl-form-field-hint-wrapper,
.cnsl-form-field-error-wrapper {
display: flex;
}
}

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { CnslFormFieldComponent } from './form-field.component';
describe('CnslFormFieldComponent', () => {
let component: CnslFormFieldComponent;
let fixture: ComponentFixture<CnslFormFieldComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [CnslFormFieldComponent],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CnslFormFieldComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,164 @@
import {
AfterContentInit,
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ContentChild,
ContentChildren,
ElementRef,
HostListener,
Inject,
InjectionToken,
OnDestroy,
QueryList,
ViewChild,
ViewEncapsulation,
} from '@angular/core';
import { NgControl } from '@angular/forms';
import { MatFormFieldControl } from '@angular/material/form-field';
import { Subject } from 'rxjs';
import { startWith, takeUntil } from 'rxjs/operators';
import { cnslFormFieldAnimations } from './animations';
import { CNSL_ERROR, CnslErrorDirective } from './error.directive';
import { _CNSL_HINT, CnslHintDirective } from './hint.directive';
export const CNSL_FORM_FIELD = new InjectionToken<CnslFormFieldComponent>('CnslFormFieldComponent');
class CnslFormFieldBase {
constructor(public _elementRef: ElementRef) { }
}
@Component({
selector: 'cnsl-form-field',
templateUrl: './form-field.component.html',
styleUrls: ['./form-field.component.scss'],
providers: [
{ provide: CNSL_FORM_FIELD, useExisting: CnslFormFieldComponent },
],
host: {
'[class.ng-untouched]': '_shouldForward("untouched")',
'[class.ng-touched]': '_shouldForward("touched")',
'[class.ng-pristine]': '_shouldForward("pristine")',
'[class.ng-dirty]': '_shouldForward("dirty")',
'[class.ng-valid]': '_shouldForward("valid")',
'[class.ng-invalid]': '_shouldForward("invalid")',
'[class.ng-pending]': '_shouldForward("pending")',
// '[class.cnsl-form-field-invalid]': '_control.errorState',
},
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [cnslFormFieldAnimations.transitionMessages],
})
export class CnslFormFieldComponent extends CnslFormFieldBase implements OnDestroy, AfterContentInit, AfterViewInit {
focused: boolean = false;
private _destroyed: Subject<void> = new Subject<void>();
@ViewChild('connectionContainer', { static: true }) _connectionContainerRef!: ElementRef;
@ViewChild('inputContainer') _inputContainerRef!: ElementRef;
@ContentChild(MatFormFieldControl) _controlNonStatic!: MatFormFieldControl<any>;
@ContentChild(MatFormFieldControl, { static: true }) _controlStatic!: MatFormFieldControl<any>;
get _control(): MatFormFieldControl<any> {
// TODO(crisbeto): we need this workaround in order to support both Ivy and ViewEngine.
// We should clean this up once Ivy is the default renderer.
return this._explicitFormFieldControl || this._controlNonStatic || this._controlStatic;
}
set _control(value: MatFormFieldControl<any>) {
this._explicitFormFieldControl = value;
}
private _explicitFormFieldControl!: MatFormFieldControl<any>;
readonly stateChanges: Subject<void> = new Subject<void>();
_subscriptAnimationState: string = '';
@ContentChildren(CNSL_ERROR as any, { descendants: true }) _errorChildren!: QueryList<CnslErrorDirective>;
@ContentChildren(_CNSL_HINT, { descendants: true }) _hintChildren!: QueryList<CnslHintDirective>;
@HostListener('blur', ['false'])
_focusChanged(isFocused: boolean): void {
console.log('blur1');
if (isFocused !== this.focused && (!isFocused)) {
this.focused = isFocused;
this.stateChanges.next();
}
}
constructor(public _elementRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef,
@Inject(ElementRef)
// Use `ElementRef` here so Angular has something to inject.
_labelOptions: any) {
super(_elementRef);
}
public ngAfterViewInit(): void {
// Avoid animations on load.
this._subscriptAnimationState = 'enter';
this._changeDetectorRef.detectChanges();
}
public ngOnDestroy(): void {
this._destroyed.next();
this._destroyed.complete();
}
public ngAfterContentInit(): void {
this._validateControlChild();
const control = this._control;
control.stateChanges.pipe(startWith(null)).subscribe(() => {
this._syncDescribedByIds();
this._changeDetectorRef.markForCheck();
});
// Run change detection if the value changes.
if (control.ngControl && control.ngControl.valueChanges) {
control.ngControl.valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(() => this._changeDetectorRef.markForCheck());
}
// Update the aria-described by when the number of errors changes.
this._errorChildren.changes.pipe(startWith(null)).subscribe(() => {
this._syncDescribedByIds();
this._changeDetectorRef.markForCheck();
});
}
/** Throws an error if the form field's control is missing. */
protected _validateControlChild(): void {
if (!this._control) {
throw Error('cnsl-form-field must contain a MatFormFieldControl.');
}
}
private _syncDescribedByIds(): void {
if (this._control) {
const ids: string[] = [];
// TODO(wagnermaciel): Remove the type check when we find the root cause of this bug.
if (this._control.userAriaDescribedBy &&
typeof this._control.userAriaDescribedBy === 'string') {
ids.push(...this._control.userAriaDescribedBy.split(' '));
}
if (this._errorChildren) {
ids.push(...this._errorChildren.map(error => error.id));
}
this._control.setDescribedByIds(ids);
}
}
/** Determines whether a class from the NgControl should be forwarded to the host element. */
_shouldForward(prop: keyof NgControl): boolean {
const ngControl = this._control ? this._control.ngControl : null;
return ngControl && ngControl[prop];
}
/** Determines whether to display hints or errors. */
_getDisplayedMessages(): 'error' | 'hint' {
return (this._errorChildren && this._errorChildren.length > 0) ? 'error' : 'hint';
}
}

View File

@ -0,0 +1,30 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { MatRippleModule } from '@angular/material/core';
import { LabelModule } from 'src/app/modules/label/label.module';
import { LabelComponent } from '../label/label.component';
import { CnslErrorDirective } from './error.directive';
import { CnslFormFieldComponent } from './form-field.component';
import { CnslHintDirective } from './hint.directive';
@NgModule({
declarations: [
CnslFormFieldComponent,
CnslErrorDirective,
CnslHintDirective,
],
imports: [
CommonModule,
MatRippleModule,
LabelModule,
],
exports: [
CnslFormFieldComponent,
LabelComponent,
CnslErrorDirective,
CnslHintDirective,
],
})
export class FormFieldModule { }

View File

@ -0,0 +1,33 @@
import { Directive, InjectionToken, Input } from '@angular/core';
let nextUniqueId = 0;
/**
* Injection token that can be used to reference instances of `MatHint`. It serves as
* alternative token to the actual `MatHint` class which could cause unnecessary
* retention of the class and its directive metadata.
*
* *Note*: This is not part of the public API as the MDC-based form-field will not
* need a lightweight token for `MatHint` and we want to reduce breaking changes.
*/
export const _CNSL_HINT = new InjectionToken<CnslHintDirective>('CnslHintDirective');
/** Hint text to be shown underneath the form field control. */
@Directive({
selector: 'cnsl-hint',
host: {
'class': 'cnsl-hint',
'[class.cnsl-form-field-hint-end]': 'align === "end"',
'[attr.id]': 'id',
// Remove align attribute to prevent it from interfering with layout.
'[attr.align]': 'null',
},
providers: [{ provide: _CNSL_HINT, useExisting: CnslHintDirective }],
})
export class CnslHintDirective {
/** Whether to align the hint label at the start or end of the line. */
@Input() align: 'start' | 'end' = 'start';
/** Unique ID for the hint. Used for the aria-describedby on the form field control. */
@Input() id: string = `mat-hint-${nextUniqueId++}`;
}

View File

@ -16,56 +16,57 @@
<form (ngSubmit)="addIdp()">
<ng-container [formGroup]="formGroup">
<div class="content">
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'IDP.NAME' | translate }}</mat-label>
<input matInput formControlName="name" />
</mat-form-field>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'IDP.ISSUER' | translate }}</mat-label>
<input matInput formControlName="issuer" />
</mat-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
<input cnslInput formControlName="issuer" />
</cnsl-form-field>
</div>
<div class="content">
<p class="desc">Client Id / Client Secret</p>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'IDP.CLIENTID' | translate }}</mat-label>
<input matInput formControlName="clientId" />
</mat-form-field>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'IDP.CLIENTSECRET' | translate }}</mat-label>
<input matInput formControlName="clientSecret" />
</mat-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'IDP.CLIENTID' | translate }}</cnsl-label>
<input cnslInput formControlName="clientId" />
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'IDP.CLIENTSECRET' | translate }}</cnsl-label>
<input cnslInput formControlName="clientSecret" />
</cnsl-form-field>
</div>
<div class="content">
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'IDP.SCOPESLIST' | translate }}</mat-label>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'IDP.SCOPESLIST' | translate }}</cnsl-label>
<mat-chip-list #chipScopesList aria-label="scope selection" *ngIf="scopesList">
<mat-chip class="chip" *ngFor="let scope of scopesList.value" selectable="false" removable
(removed)="removeScope(scope)">
{{scope}} <mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input [matChipInputFor]="chipScopesList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addScope($event)">
<input cnslInput [matChipInputFor]="chipScopesList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addScope($event)">
</mat-chip-list>
</mat-form-field>
</cnsl-form-field>
</div>
<div class="content">
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</mat-label>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</cnsl-label>
<mat-select formControlName="idpDisplayNameMapping">
<mat-option *ngFor="let field of mappingFields" [value]="field">
{{ 'IDP.MAPPINTFIELD.'+field | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</mat-label>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</cnsl-label>
<mat-select formControlName="usernameMapping">
<mat-option *ngFor="let field of mappingFields" [value]="field">
{{ 'IDP.MAPPINTFIELD.'+field | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
</div>
</ng-container>

View File

@ -3,13 +3,12 @@ import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
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 { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { IdpCreateRoutingModule } from './idp-create-routing.module';
import { IdpCreateComponent } from './idp-create.component';
@ -21,8 +20,7 @@ import { IdpCreateComponent } from './idp-create.component';
CommonModule,
FormsModule,
ReactiveFormsModule,
MatInputModule,
MatFormFieldModule,
InputModule,
MatButtonModule,
MatSelectModule,
MatIconModule,

View File

@ -4,22 +4,22 @@
<form (ngSubmit)="updateIdp()">
<ng-container [formGroup]="idpForm">
<div class="content">
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'IDP.ID' | translate }}</mat-label>
<input matInput formControlName="id" />
</mat-form-field>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'IDP.NAME' | translate }}</mat-label>
<input matInput formControlName="name" />
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'IDP.STYLE' | translate }}</mat-label>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'IDP.ID' | translate }}</cnsl-label>
<input cnslInput formControlName="id" />
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'IDP.STYLE' | translate }}</cnsl-label>
<mat-select formControlName="stylingType">
<mat-option *ngFor="let field of styleFields" [value]="field">
{{ 'IDP.STYLEFIELD.'+field | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
</div>
</ng-container>
@ -38,51 +38,51 @@
<form (ngSubmit)="updateOidcConfig()">
<ng-container [formGroup]="oidcConfigForm">
<div class="content">
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'IDP.ISSUER' | translate }}</mat-label>
<input matInput formControlName="issuer" />
</mat-form-field>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'IDP.CLIENTID' | translate }}</mat-label>
<input matInput formControlName="clientId" />
</mat-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
<input cnslInput formControlName="issuer" />
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'IDP.CLIENTID' | translate }}</cnsl-label>
<input cnslInput formControlName="clientId" />
</cnsl-form-field>
<mat-checkbox class="desc" [(ngModel)]="showIdSecretSection"
[ngModelOptions]="{standalone: true}">
Update Client Secret
</mat-checkbox>
<mat-form-field appearance="outline" class="formfield" *ngIf="showIdSecretSection">
<mat-label>{{ 'IDP.CLIENTSECRET' | translate }}</mat-label>
<input matInput formControlName="clientSecret" />
</mat-form-field>
<mat-form-field appearance="outline" class="formfield fullwidth">
<mat-label>{{ 'IDP.SCOPESLIST' | translate }}</mat-label>
<cnsl-form-field appearance="outline" class="formfield" *ngIf="showIdSecretSection">
<cnsl-label>{{ 'IDP.CLIENTSECRET' | translate }}</cnsl-label>
<input cnslInput formControlName="clientSecret" />
</cnsl-form-field>
<cnsl-form-field appearance="outline" class="formfield fullwidth">
<cnsl-label>{{ 'IDP.SCOPESLIST' | translate }}</cnsl-label>
<mat-chip-list #chipScopesList aria-label="scope selection">
<mat-chip class="chip" *ngFor="let scope of scopesList?.value" selectable="false"
removable (removed)="removeScope(scope)">
{{scope}} <mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input [matChipInputFor]="chipScopesList"
<input cnslInput [matChipInputFor]="chipScopesList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addScope($event)">
</mat-chip-list>
</mat-form-field>
</cnsl-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</mat-label>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</cnsl-label>
<mat-select formControlName="idpDisplayNameMapping">
<mat-option *ngFor="let field of mappingFields" [value]="field">
{{ 'IDP.MAPPINGFIELD.'+field | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</mat-label>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</cnsl-label>
<mat-select formControlName="usernameMapping">
<mat-option *ngFor="let field of mappingFields" [value]="field">
{{ 'IDP.MAPPINGFIELD.'+field | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
</div>
</ng-container>

View File

@ -4,16 +4,15 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { IdpRoutingModule } from './idp-routing.module';
import { IdpComponent } from './idp.component';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { MatSelectModule } from '@angular/material/select';
@NgModule({
declarations: [IdpComponent],
@ -22,8 +21,7 @@ import { MatSelectModule } from '@angular/material/select';
IdpRoutingModule,
FormsModule,
ReactiveFormsModule,
MatInputModule,
MatFormFieldModule,
InputModule,
MatButtonModule,
MatIconModule,
MatTooltipModule,

View File

@ -0,0 +1,19 @@
import { Injectable } from '@angular/core';
import { FormControl, FormGroupDirective, NgForm } from '@angular/forms';
/** Provider that defines how form controls behave with regards to displaying error messages. */
@Injectable({ providedIn: 'root' })
export class ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return !!(control && control.invalid && (control.touched || (form && form.submitted)));
}
}
/** Error state matcher that matches when a control is invalid and dirty. */
@Injectable()
export class ShowOnDirtyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return !!(control && control.invalid && (control.dirty || (form && form.submitted)));
}
}

View File

@ -0,0 +1,8 @@
import { InputDirective } from './input.directive';
describe('InputDirective', () => {
it('should create an instance', () => {
const directive = new InputDirective();
expect(directive).toBeTruthy();
});
});

View File

@ -0,0 +1,459 @@
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { getSupportedInputTypes, Platform } from '@angular/cdk/platform';
import { AutofillMonitor } from '@angular/cdk/text-field';
import {
AfterViewInit,
Directive,
DoCheck,
ElementRef,
HostListener,
Inject,
Input,
NgZone,
OnChanges,
OnDestroy,
Optional,
Self,
} from '@angular/core';
import { FormGroupDirective, NgControl, NgForm } from '@angular/forms';
import { CanUpdateErrorState, CanUpdateErrorStateCtor, ErrorStateMatcher, mixinErrorState } from '@angular/material/core';
import { MAT_FORM_FIELD, MatFormField, MatFormFieldControl } from '@angular/material/form-field';
import { getMatInputUnsupportedTypeError, MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input';
import { Subject } from 'rxjs';
// Invalid input type. Using one of these will throw an MatInputUnsupportedTypeError.
const MAT_INPUT_INVALID_TYPES = [
'button',
'checkbox',
'file',
'hidden',
'image',
'radio',
'range',
'reset',
'submit',
];
let nextUniqueId = 0;
// Boilerplate for applying mixins to MatInput.
/** @docs-private */
class MatInputBase {
constructor(public _defaultErrorStateMatcher: ErrorStateMatcher,
public _parentForm: NgForm,
public _parentFormGroup: FormGroupDirective,
/** @docs-private */
public ngControl: NgControl) { }
}
const _MatInputMixinBase: CanUpdateErrorStateCtor & typeof MatInputBase =
mixinErrorState(MatInputBase);
/** Directive that allows a native input to work inside a `MatFormField`. */
@Directive({
selector: `input[cnslInput], textarea[cnslInput], select[cnslNativeControl]`,
exportAs: 'cnslInput',
host: {
/**
* @breaking-change 8.0.0 remove .mat-form-field-autofill-control in favor of AutofillMonitor.
*/
// 'class': 'cnsl-input-element cnsl-form-field-autofill-control',
// '[class.mat-input-server]': '_isServer',
// Native input properties that are overwritten by Angular inputs need to be synced with
// the native input element. Otherwise property bindings for those don't work.
'[attr.id]': 'id',
// At the time of writing, we have a lot of customer tests that look up the input based on its
// placeholder. Since we sometimes omit the placeholder attribute from the DOM to prevent screen
// readers from reading it twice, we have to keep it somewhere in the DOM for the lookup.
'[attr.data-placeholder]': 'placeholder',
'[disabled]': 'disabled',
'[required]': 'required',
'[attr.readonly]': 'readonly && !_isNativeSelect || null',
'[attr.aria-invalid]': 'errorState',
'[attr.aria-required]': 'required.toString()',
},
providers: [{ provide: MatFormFieldControl, useExisting: InputDirective }],
})
export class InputDirective extends _MatInputMixinBase implements MatFormFieldControl<any>, OnChanges,
OnDestroy, AfterViewInit, DoCheck, CanUpdateErrorState {
protected _uid: string = `cnsl-input-${nextUniqueId++}`;
protected _previousNativeValue: any;
private _inputValueAccessor: { value: any; };
private _previousPlaceholder!: string | null;
/** Whether the component is being rendered on the server. */
readonly _isServer: boolean;
/** Whether the component is a native html select. */
readonly _isNativeSelect: boolean;
/** Whether the component is a textarea. */
readonly _isTextarea: boolean;
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
focused: boolean = false;
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
readonly stateChanges: Subject<void> = new Subject<void>();
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
controlType: string = 'mat-input';
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
autofilled: boolean = false;
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
@Input()
get disabled(): boolean {
if (this.ngControl && this.ngControl.disabled !== null) {
return this.ngControl.disabled;
}
return this._disabled;
}
set disabled(value: boolean) {
this._disabled = coerceBooleanProperty(value);
// Browsers may not fire the blur event if the input is disabled too quickly.
// Reset from here to ensure that the element doesn't become stuck.
if (this.focused) {
this.focused = false;
this.stateChanges.next();
}
}
protected _disabled: boolean = false;
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
@Input()
get id(): string { return this._id; }
set id(value: string) { this._id = value || this._uid; }
protected _id!: string;
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
@Input() placeholder!: string;
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
@Input()
get required(): boolean { return this._required; }
set required(value: boolean) { this._required = coerceBooleanProperty(value); }
protected _required: boolean = false;
/** Input type of the element. */
@Input()
get type(): string { return this._type; }
set type(value: string) {
this._type = value || 'text';
this._validateType();
// When using Angular inputs, developers are no longer able to set the properties on the native
// input element. To ensure that bindings for `type` work, we need to sync the setter
// with the native property. Textarea elements don't support the type property or attribute.
if (!this._isTextarea && getSupportedInputTypes().has(this._type)) {
(this._elementRef.nativeElement as HTMLInputElement).type = this._type;
}
}
protected _type: string = 'text';
/** An object used to control when error messages are shown. */
@Input() errorStateMatcher!: ErrorStateMatcher;
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
// tslint:disable-next-line:no-input-rename
@Input('aria-describedby') userAriaDescribedBy!: string;
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
@Input()
get value(): string { return this._inputValueAccessor.value; }
set value(value: string) {
if (value !== this.value) {
this._inputValueAccessor.value = value;
this.stateChanges.next();
}
}
/** Whether the element is readonly. */
@Input()
get readonly(): boolean { return this._readonly; }
set readonly(value: boolean) { this._readonly = coerceBooleanProperty(value); }
private _readonly: boolean = false;
protected _neverEmptyInputTypes: string[] = [
'date',
'datetime',
'datetime-local',
'month',
'time',
'week',
].filter(t => getSupportedInputTypes().has(t));
constructor(
protected _elementRef: ElementRef<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
protected _platform: Platform,
/** @docs-private */
@Optional() @Self() public ngControl: NgControl,
@Optional() _parentForm: NgForm,
@Optional() _parentFormGroup: FormGroupDirective,
_defaultErrorStateMatcher: ErrorStateMatcher,
@Optional() @Self() @Inject(MAT_INPUT_VALUE_ACCESSOR) inputValueAccessor: any,
private _autofillMonitor: AutofillMonitor,
ngZone: NgZone,
// TODO: Remove this once the legacy appearance has been removed. We only need
// to inject the form-field for determining whether the placeholder has been promoted.
@Optional() @Inject(MAT_FORM_FIELD) private _formField?: MatFormField) {
super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
const element = this._elementRef.nativeElement;
const nodeName = element.nodeName.toLowerCase();
// If no input value accessor was explicitly specified, use the element as the input value
// accessor.
this._inputValueAccessor = inputValueAccessor || element;
this._previousNativeValue = this.value;
// Force setter to be called in case id was not specified.
this.id = this.id;
// On some versions of iOS the caret gets stuck in the wrong place when holding down the delete
// key. In order to get around this we need to "jiggle" the caret loose. Since this bug only
// exists on iOS, we only bother to install the listener on iOS.
if (_platform.IOS) {
ngZone.runOutsideAngular(() => {
_elementRef.nativeElement.addEventListener('keyup', (event: Event) => {
const el: HTMLInputElement = event.target as HTMLInputElement;
if (!el.value && !el.selectionStart && !el.selectionEnd) {
// Note: Just setting `0, 0` doesn't fix the issue. Setting
// `1, 1` fixes it for the first time that you type text and
// then hold delete. Toggling to `1, 1` and then back to
// `0, 0` seems to completely fix it.
el.setSelectionRange(1, 1);
el.setSelectionRange(0, 0);
}
});
});
}
this._isServer = !this._platform.isBrowser;
this._isNativeSelect = nodeName === 'select';
this._isTextarea = nodeName === 'textarea';
if (this._isNativeSelect) {
this.controlType = (element as HTMLSelectElement).multiple ? 'mat-native-select-multiple' :
'mat-native-select';
}
}
ngAfterViewInit(): void {
if (this._platform.isBrowser) {
this._autofillMonitor.monitor(this._elementRef.nativeElement).subscribe(event => {
this.autofilled = event.isAutofilled;
this.stateChanges.next();
});
}
}
ngOnChanges(): void {
this.stateChanges.next();
}
ngOnDestroy(): void {
this.stateChanges.complete();
if (this._platform.isBrowser) {
this._autofillMonitor.stopMonitoring(this._elementRef.nativeElement);
}
}
ngDoCheck(): void {
if (this.ngControl) {
// We need to re-evaluate this on every change detection cycle, because there are some
// error triggers that we can't subscribe to (e.g. parent form submissions). This means
// that whatever logic is in here has to be super lean or we risk destroying the performance.
this.updateErrorState();
}
// We need to dirty-check the native element's value, because there are some cases where
// we won't be notified when it changes (e.g. the consumer isn't using forms or they're
// updating the value using `emitEvent: false`).
this._dirtyCheckNativeValue();
// We need to dirty-check and set the placeholder attribute ourselves, because whether it's
// present or not depends on a query which is prone to "changed after checked" errors.
this._dirtyCheckPlaceholder();
}
/** Focuses the input. */
focus(options?: FocusOptions): void {
this._elementRef.nativeElement.focus(options);
}
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
// ViewEngine they're overwritten.
// TODO(crisbeto): we move this back into `host` once Ivy is turned on by default.
/** Callback for the cases where the focused state of the input changes. */
// tslint:disable:no-host-decorator-in-concrete
@HostListener('focus', ['true'])
@HostListener('blur', ['false'])
// tslint:enable:no-host-decorator-in-concrete
_focusChanged(isFocused: boolean): void {
if (isFocused !== this.focused && (!this.readonly || !isFocused)) {
this.focused = isFocused;
this.stateChanges.next();
}
}
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
// ViewEngine they're overwritten.
// TODO(crisbeto): we move this back into `host` once Ivy is turned on by default.
// tslint:disable-next-line:no-host-decorator-in-concrete
@HostListener('input')
_onInput(): void {
// This is a noop function and is used to let Angular know whenever the value changes.
// Angular will run a new change detection each time the `input` event has been dispatched.
// It's necessary that Angular recognizes the value change, because when floatingLabel
// is set to false and Angular forms aren't used, the placeholder won't recognize the
// value changes and will not disappear.
// Listening to the input event wouldn't be necessary when the input is using the
// FormsModule or ReactiveFormsModule, because Angular forms also listens to input events.
}
/** Does some manual dirty checking on the native input `placeholder` attribute. */
private _dirtyCheckPlaceholder(): void {
// If we're hiding the native placeholder, it should also be cleared from the DOM, otherwise
// screen readers will read it out twice: once from the label and once from the attribute.
// TODO: can be removed once we get rid of the `legacy` style for the form field, because it's
// the only one that supports promoting the placeholder to a label.
const placeholder = this._formField?._hideControlPlaceholder?.() ? null : this.placeholder;
if (placeholder !== this._previousPlaceholder) {
const element = this._elementRef.nativeElement;
this._previousPlaceholder = placeholder;
placeholder ?
element.setAttribute('placeholder', placeholder) : element.removeAttribute('placeholder');
}
}
/** Does some manual dirty checking on the native input `value` property. */
protected _dirtyCheckNativeValue(): void {
const newValue = this._elementRef.nativeElement.value;
if (this._previousNativeValue !== newValue) {
this._previousNativeValue = newValue;
this.stateChanges.next();
}
}
/** Make sure the input is a supported type. */
protected _validateType(): void {
if (MAT_INPUT_INVALID_TYPES.indexOf(this._type) > -1) {
throw getMatInputUnsupportedTypeError(this._type);
}
}
/** Checks whether the input type is one of the types that are never empty. */
protected _isNeverEmpty(): boolean {
return this._neverEmptyInputTypes.indexOf(this._type) > -1;
}
/** Checks whether the input is invalid based on the native validation. */
protected _isBadInput(): boolean {
// The `validity` property won't be present on platform-server.
const validity = (this._elementRef.nativeElement as HTMLInputElement).validity;
return validity && validity.badInput;
}
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
get empty(): boolean {
return !this._isNeverEmpty() && !this._elementRef.nativeElement.value && !this._isBadInput() &&
!this.autofilled;
}
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
get shouldLabelFloat(): boolean {
if (this._isNativeSelect) {
// For a single-selection `<select>`, the label should float when the selected option has
// a non-empty display value. For a `<select multiple>`, the label *always* floats to avoid
// overlapping the label with the options.
const selectElement = this._elementRef.nativeElement as HTMLSelectElement;
const firstOption: HTMLOptionElement | undefined = selectElement.options[0];
// On most browsers the `selectedIndex` will always be 0, however on IE and Edge it'll be
// -1 if the `value` is set to something, that isn't in the list of options, at a later point.
return this.focused || selectElement.multiple || !this.empty ||
!!(selectElement.selectedIndex > -1 && firstOption && firstOption.label);
} else {
return this.focused || !this.empty;
}
}
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
setDescribedByIds(ids: string[]): void {
if (ids.length) {
this._elementRef.nativeElement.setAttribute('aria-describedby', ids.join(' '));
} else {
this._elementRef.nativeElement.removeAttribute('aria-describedby');
}
}
/**
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
onContainerClick(): void {
// Do not re-focus the input element if the element is already focused. Otherwise it can happen
// that someone clicks on a time input and the cursor resets to the "hours" field while the
// "minutes" field was actually clicked. See: https://github.com/angular/components/issues/12849
if (!this.focused) {
this.focus();
}
}
// tslint:disable
static ngAcceptInputType_disabled: BooleanInput;
static ngAcceptInputType_readonly: BooleanInput;
static ngAcceptInputType_required: BooleanInput;
// Accept `any` to avoid conflicts with other directives on `<input>` that may
// accept different types.
static ngAcceptInputType_value: any;
// tslint:enable
}

View File

@ -0,0 +1,22 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { MatRippleModule } from '@angular/material/core';
import { FormFieldModule } from '../form-field/form-field.module';
import { LabelModule } from '../label/label.module';
import { ErrorStateMatcher } from './error-options';
import { InputDirective } from './input.directive';
@NgModule({
declarations: [InputDirective],
imports: [
LabelModule,
CommonModule,
FormFieldModule,
MatRippleModule,
],
exports: [InputDirective, FormFieldModule],
providers: [ErrorStateMatcher],
})
export class InputModule { }

View File

@ -0,0 +1,3 @@
<span class="cnsl-label">
<ng-content></ng-content>
</span>

View File

@ -0,0 +1,16 @@
@import '~@angular/material/theming';
@mixin cnsl-label-theme($theme) {
$primary: map-get($theme, primary);
$primary-color: mat-color($primary, 500);
$is-dark-theme: map-get($theme, is-dark);
.cnsl-label {
display: block;
font-size: 12px;
color: if($is-dark-theme, var(--grey), var(--grey));
margin-bottom: 4px;
font-weight: 400;
}
}

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { AvatarComponent } from './avatar.component';
describe('AvatarComponent', () => {
let component: AvatarComponent;
let fixture: ComponentFixture<AvatarComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [AvatarComponent],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AvatarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'cnsl-label',
templateUrl: './label.component.html',
styleUrls: ['./label.component.scss'],
})
export class LabelComponent {
constructor() { }
}

View File

@ -0,0 +1,20 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { MatRippleModule } from '@angular/material/core';
import { LabelComponent } from './label.component';
@NgModule({
declarations: [LabelComponent],
imports: [
CommonModule,
MatRippleModule,
],
exports: [
LabelComponent,
],
})
export class LabelModule { }

View File

@ -73,17 +73,16 @@
</ng-container>
<ng-container matColumnDef="roles">
<th mat-header-cell *matHeaderCellDef> {{ 'ROLESLABEL' | translate }} </th>
<td mat-cell *matCellDef="let member">
<mat-form-field class="form-field" appearance="outline">
<mat-label>{{ 'ROLESLABEL' | translate }}</mat-label>
<th mat-header-cell *matHeaderCellDef class="role-row"> {{ 'ROLESLABEL' | translate }} </th>
<td mat-cell *matCellDef="let member" class="role-row">
<cnsl-form-field class="form-field" appearance="outline">
<mat-select [(ngModel)]="member.rolesList" multiple [disabled]="!canWrite"
(selectionChange)="updateRoles.emit({member: member, change: $event})">
<mat-option *ngFor="let role of memberRoleOptions" [value]="role">
{{ role }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
</td>
</ng-container>

View File

@ -26,11 +26,6 @@
.action {
width: 40px;
}
.selection {
width: 50px;
max-width: 50px;
}
}
tr {
@ -60,3 +55,7 @@
outline: none;
cursor: pointer;
}
.role-row {
min-width: 200px;
}

View File

@ -3,7 +3,6 @@ import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSelectModule } from '@angular/material/select';
@ -12,6 +11,7 @@ import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { AvatarModule } from '../avatar/avatar.module';
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
@ -23,7 +23,7 @@ import { MembersTableComponent } from './members-table.component';
],
imports: [
CommonModule,
MatFormFieldModule,
InputModule,
MatSelectModule,
MatCheckboxModule,
MatIconModule,

View File

@ -15,7 +15,7 @@ export class MetaLayoutComponent {
}
public hidden: boolean = false;
public isSmallScreen$: Observable<boolean> = this.breakpointObserver
.observe('(max-width: 899px)')
.observe('(max-width: 1000px)')
.pipe(map(result => {
return result.matches;
}));

View File

@ -1,44 +0,0 @@
@import '~@angular/material/theming';
@mixin meta-theme($theme) {
/* stylelint-disable */
$primary: map-get($theme, primary);
$primary-color: mat-color($primary, 500);
$primary-dark: mat-color($primary, A800);
/* stylelint-enable */
.meta-wrapper {
.meta {
position: relative;
flex: 1 0 300px;
&::after {
border-left: 2px solid $primary-color;
-webkit-border-image:
-webkit-gradient(
linear,
left top,
left bottom,
from($primary-color),
to(rgb(0, 0, 0, .5)),
color-stop(
01,
$primary-dark
)
) 50 21;
border-image:
-webkit-gradient(
linear,
left top,
left bottom,
from($primary-color),
to(rgb(0, 0, 0, .5)),
color-stop(
01,
$primary-dark
)
) 50 21;
}
}
}
}

View File

@ -2,15 +2,15 @@
[description]="'POLICY.LABEL.DESCRIPTION' | translate">
<div class="content" *ngIf="labelData">
<mat-form-field class="form-field" appearance="outline">
<mat-label>{{'POLICY.LABEL.PRIMARYCOLOR' | translate}}</mat-label>
<input [(ngModel)]="labelData.primaryColor" matInput />
</mat-form-field>
<cnsl-form-field class="form-field" appearance="outline">
<cnsl-label>{{'POLICY.LABEL.PRIMARYCOLOR' | translate}}</cnsl-label>
<input cnslInput [(ngModel)]="labelData.primaryColor" />
</cnsl-form-field>
<mat-form-field class="form-field" appearance="outline">
<mat-label>{{'POLICY.LABEL.SECONDARYCOLOR' | translate}}</mat-label>
<input [(ngModel)]="labelData.secondaryColor" matInput />
</mat-form-field>
<cnsl-form-field class="form-field" appearance="outline">
<cnsl-label>{{'POLICY.LABEL.SECONDARYCOLOR' | translate}}</cnsl-label>
<input cnslInput [(ngModel)]="labelData.secondaryColor" />
</cnsl-form-field>
</div>
<div class="btn-container">

View File

@ -2,14 +2,13 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { LabelPolicyRoutingModule } from './label-policy-routing.module';
import { LabelPolicyComponent } from './label-policy.component';
@ -20,8 +19,7 @@ import { LabelPolicyComponent } from './label-policy.component';
LabelPolicyRoutingModule,
CommonModule,
FormsModule,
MatInputModule,
MatFormFieldModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
MatIconModule,

View File

@ -4,25 +4,25 @@
<p class="desc"> {{'LOGINPOLICY.ADDIDP.DESCRIPTION' | translate}}</p>
<div mat-dialog-content>
<mat-form-field *ngIf="serviceType == PolicyComponentServiceType.MGMT" class="full-width" appearance="outline">
<mat-label>{{ 'IDP.TYPE' | translate }}</mat-label>
<cnsl-form-field *ngIf="serviceType == PolicyComponentServiceType.MGMT" class="full-width" appearance="outline">
<cnsl-label>{{ 'IDP.TYPE' | translate }}</cnsl-label>
<mat-select [(ngModel)]="idpType" (selectionChange)="loadIdps()">
<mat-option *ngFor="let type of idpTypes" [value]="type">
{{ 'IDP.TYPES.'+type | translate}}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
<p>{{'LOGINPOLICY.ADDIDP.SELECTIDPS' | translate}}</p>
<mat-form-field class="full-width" appearance="outline">
<mat-label>{{ 'LOGINPOLICY.ADDIDP.SELECTIDPS' | translate }}</mat-label>
<cnsl-form-field class="full-width" appearance="outline">
<cnsl-label>{{ 'LOGINPOLICY.ADDIDP.SELECTIDPS' | translate }}</cnsl-label>
<mat-select [(ngModel)]="idp">
<mat-option *ngFor="let idp of availableIdps" [value]="idp">
{{ idp.name }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
</div>
<div mat-dialog-actions class="action">
<button mat-button (click)="closeDialog()">

View File

@ -61,8 +61,7 @@ export class AddIdpDialogComponent {
query.setKey(IdpSearchKey.IDPSEARCHKEY_PROVIDER_TYPE);
query.setMethod(SearchMethod.SEARCHMETHOD_EQUALS);
query.setValue(this.idpType.toString());
console.log(this.idpType);
console.log(query.toObject());
this.mgmtService.SearchIdps(undefined, undefined, [query]).then(idps => {
this.availableIdps = idps.toObject().resultList;
});

View File

@ -3,9 +3,9 @@ import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { AddIdpDialogComponent } from './add-idp-dialog.component';
@ -16,7 +16,7 @@ import { AddIdpDialogComponent } from './add-idp-dialog.component';
MatDialogModule,
MatButtonModule,
TranslateModule,
MatFormFieldModule,
InputModule,
MatSelectModule,
FormsModule,
],

View File

@ -2,9 +2,7 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
@ -13,6 +11,7 @@ import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { CardModule } from 'src/app/modules/card/card.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { IdpTableModule } from 'src/app/modules/idp-table/idp-table.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module';
@ -26,8 +25,7 @@ import { LoginPolicyComponent } from './login-policy.component';
CommonModule,
FormsModule,
CardModule,
MatInputModule,
MatFormFieldModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
MatIconModule,

View File

@ -2,14 +2,13 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { OrgIamPolicyRoutingModule } from './org-iam-policy-routing.module';
import { OrgIamPolicyComponent } from './org-iam-policy.component';
@ -20,8 +19,7 @@ import { OrgIamPolicyComponent } from './org-iam-policy.component';
OrgIamPolicyRoutingModule,
CommonModule,
FormsModule,
MatInputModule,
MatFormFieldModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
MatIconModule,

View File

@ -2,14 +2,13 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { PasswordAgePolicyRoutingModule } from './password-age-policy-routing.module';
import { PasswordAgePolicyComponent } from './password-age-policy.component';
@ -20,8 +19,7 @@ import { PasswordAgePolicyComponent } from './password-age-policy.component';
PasswordAgePolicyRoutingModule,
CommonModule,
FormsModule,
MatInputModule,
MatFormFieldModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
MatIconModule,

View File

@ -2,15 +2,14 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { PasswordComplexityPolicyRoutingModule } from './password-complexity-policy-routing.module';
import { PasswordComplexityPolicyComponent } from './password-complexity-policy.component';
@ -21,8 +20,7 @@ import { PasswordComplexityPolicyComponent } from './password-complexity-policy.
PasswordComplexityPolicyRoutingModule,
CommonModule,
FormsModule,
MatInputModule,
MatFormFieldModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
MatIconModule,

View File

@ -2,14 +2,13 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { PasswordLockoutPolicyRoutingModule } from './password-lockout-policy-routing.module';
import { PasswordLockoutPolicyComponent } from './password-lockout-policy.component';
@ -20,8 +19,7 @@ import { PasswordLockoutPolicyComponent } from './password-lockout-policy.compon
PasswordLockoutPolicyRoutingModule,
CommonModule,
FormsModule,
MatInputModule,
MatFormFieldModule,
InputModule,
MatButtonModule,
MatSlideToggleModule,
MatIconModule,

View File

@ -1,4 +1,4 @@
<h1>{{'POLICY.TITLE' | translate}}</h1>
<h2>{{'POLICY.TITLE' | translate}}</h2>
<p class="top-desc">{{'POLICY.DESCRIPTION' | translate}}</p>

View File

@ -1,5 +1,7 @@
h1 {
h2 {
font-size: 1.2rem;
letter-spacing: .05em;
text-transform: uppercase;
}
.top-desc {
@ -28,7 +30,7 @@ h1 {
height: 60px;
width: 60px;
border-radius: 50%;
background: linear-gradient(40deg, rgb(129, 85, 185) 30%, #8983f7);
background: linear-gradient(40deg, rgb(129, 85, 185) 30%, #5469d4);
display: flex;
align-items: center;
justify-content: center;

View File

@ -4,18 +4,18 @@
<p class="desc"> {{'PROJECT.ROLE.EDITDESCRIPTION' | translate}}</p>
<div mat-dialog-content>
<form *ngIf="formGroup" class="full-width" [formGroup]="formGroup" (ngSubmit)="submitForm()">
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'PROJECT.ROLE.KEY' | translate }}</mat-label>
<input matInput formControlName="key" />
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'PROJECT.ROLE.DISPLAY_NAME' | translate }}</mat-label>
<input matInput formControlName="displayName" />
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'PROJECT.ROLE.GROUP' | translate }}</mat-label>
<input matInput formControlName="group" />
</mat-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'PROJECT.ROLE.KEY' | translate }}</cnsl-label>
<input cnslInput formControlName="key" />
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'PROJECT.ROLE.DISPLAY_NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="displayName" />
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'PROJECT.ROLE.GROUP' | translate }}</cnsl-label>
<input cnslInput formControlName="group" />
</cnsl-form-field>
</form>
</div>

View File

@ -60,6 +60,11 @@
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<div *ngIf="(dataSource.loading$ | async) == false && !dataSource?.totalResult" class="no-content-row">
<i class="las la-exclamation"></i>
<span>{{'PROJECT.ROLE.EMPTY' | translate}}</span>
</div>
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50"
[pageSizeOptions]="[25, 50, 100, 250]">
</mat-paginator>

View File

@ -26,11 +26,6 @@
width: 40px;
}
.selection {
width: 50px;
max-width: 50px;
}
.margin-neg {
margin-left: -1rem;
}

View File

@ -4,9 +4,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@ -15,6 +13,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
@ -33,10 +32,9 @@ import { ProjectRolesComponent } from './project-roles.component';
MatTableModule,
MatPaginatorModule,
MatDialogModule,
MatFormFieldModule,
InputModule,
FormsModule,
ReactiveFormsModule,
MatInputModule,
MatIconModule,
MatProgressSpinnerModule,
MatCheckboxModule,

View File

@ -1,15 +1,12 @@
<div class="table-header-row">
<div class="col">
<span class="desc"
*ngIf="timestamp">{{timestamp | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}</span>
<ng-container *ngIf="!selection.hasValue()">
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
<span class="count">{{dataSize}}</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>
<span *ngIf="!selection.hasValue()" class="count">{{dataSize}}</span>
<span *ngIf="selection.hasValue()" class="count">{{selection?.selected?.length}}</span>
<div class="desc">
<span *ngIf="!selection.hasValue()">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
<span *ngIf="selection.hasValue()">{{'ORG_DETAIL.TABLE.SELECTION' | translate}}</span>
<span *ngIf="timestamp">{{timestamp | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}</span>
</div>
</div>
<span class="fill-space"></span>
<mat-spinner class="spinner" *ngIf="loading" diameter="20"></mat-spinner>

View File

@ -6,10 +6,16 @@
.col {
display: flex;
flex-direction: column;
align-items: flex-start;
.desc {
font-size: .8rem;
color: var(--grey);
margin-right: 1rem;
span {
display: block;
}
}
.count {
@ -31,8 +37,3 @@
margin-right: .5rem;
}
}
// .table-wrapper {
// width: 100%;
// overflow-x: auto;
// }

View File

@ -48,7 +48,6 @@ export class RefreshTableComponent implements OnInit {
if (this.emitRefreshOnPreviousRoutes.length && this.refreshService.previousUrls
.some(url => this.emitRefreshOnPreviousRoutes.includes(url))) {
setTimeout(() => {
console.log('refresh now');
this.emitRefresh();
}, 1000);
}

View File

@ -1,7 +1,7 @@
<form>
<mat-form-field appearance="outline" class="full-width">
<mat-label>Project Name</mat-label>
<input matInput *ngIf="singleOutput" type="text" placeholder="Search for the project name" #nameInput
<cnsl-form-field appearance="outline" class="full-width">
<cnsl-label>Project Name</cnsl-label>
<input cnslInput *ngIf="singleOutput" type="text" placeholder="Search for the project name" #nameInput
[formControl]="myControl" [matAutocomplete]="auto" />
<mat-chip-list *ngIf="!singleOutput" #chipList aria-label="name selection">
@ -10,7 +10,7 @@
{{selectedProject?.name ? selectedProject.name + ' (owned)' : selectedProject?.projectName ? selectedProject.projectName + ' (granted)': ''}}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
<input placeholder="{{'PROJECT.NAME' | translate}}" #nameInput [formControl]="myControl"
<input cnslInput placeholder="{{'PROJECT.NAME' | translate}}" #nameInput [formControl]="myControl"
[matAutocomplete]="auto" [matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="add($event)" />
@ -24,5 +24,5 @@
{{project?.name ? project.name + ' (owned)' : project?.projectName ? project.projectName + ' (granted)': ''}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</cnsl-form-field>
</form>

View File

@ -4,12 +4,11 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { SearchProjectAutocompleteComponent } from './search-project-autocomplete.component';
@ -22,9 +21,8 @@ import { SearchProjectAutocompleteComponent } from './search-project-autocomplet
MatAutocompleteModule,
MatChipsModule,
MatButtonModule,
MatFormFieldModule,
InputModule,
MatIconModule,
MatInputModule,
ReactiveFormsModule,
MatProgressSpinnerModule,
FormsModule,

View File

@ -1,7 +1,7 @@
<form>
<mat-form-field appearance="outline" class="full-width">
<mat-label>Role Name</mat-label>
<input matInput *ngIf="singleOutput" type="text" placeholder="Search for the role name" #nameInput
<cnsl-form-field appearance="outline" class="full-width">
<cnsl-label>Role Name</cnsl-label>
<input cnslInput *ngIf="singleOutput" type="text" placeholder="Search for the role name" #nameInput
[formControl]="myControl" [matAutocomplete]="auto" />
<mat-chip-list *ngIf="!singleOutput" #chipList aria-label="name selection">
@ -10,7 +10,7 @@
{{selectedRole.displayName}}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
<input placeholder="Role Name" #nameInput [formControl]="myControl" [matAutocomplete]="auto"
<input cnslInput placeholder="Role Name" #nameInput [formControl]="myControl" [matAutocomplete]="auto"
[matChipInputFor]="chipList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur" (matChipInputTokenEnd)="add($event)" />
</mat-chip-list>
@ -23,5 +23,5 @@
{{role.displayName}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</cnsl-form-field>
</form>

View File

@ -4,11 +4,10 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { SearchRolesAutocompleteComponent } from './search-roles-autocomplete.component';
@ -20,9 +19,8 @@ import { SearchRolesAutocompleteComponent } from './search-roles-autocomplete.co
MatAutocompleteModule,
MatChipsModule,
MatButtonModule,
MatFormFieldModule,
InputModule,
MatIconModule,
MatInputModule,
ReactiveFormsModule,
MatProgressSpinnerModule,
FormsModule,

View File

@ -1,8 +1,8 @@
<form>
<mat-form-field *ngIf="target && target == UserTarget.SELF" appearance="outline" class="full-width">
<mat-label>Organizations User Loginname</mat-label>
<cnsl-form-field *ngIf="target && target == UserTarget.SELF" appearance="outline" class="full-width more-space">
<cnsl-label>Organizations User Loginname</cnsl-label>
<input matInput *ngIf="singleOutput" type="text" placeholder="Search for the user loginname" #usernameInput
<input cnslInput *ngIf="singleOutput" type="text" placeholder="Search for the user loginname" #usernameInput
[formControl]="myControl" [matAutocomplete]="auto" />
<mat-chip-list *ngIf="!singleOutput" #chipList aria-label="loginname selection">
@ -13,8 +13,8 @@
{{selecteduser.preferredLoginName}}</small>
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
<input placeholder="{{'ORG_DETAIL.MEMBER.LOGINNAME' | translate}}" #usernameInput [formControl]="myControl"
[matAutocomplete]="auto" [matChipInputFor]="chipList"
<input cnslInput placeholder="{{'ORG_DETAIL.MEMBER.LOGINNAME' | translate}}" #usernameInput
[formControl]="myControl" [matAutocomplete]="auto" [matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="add($event)" />
</mat-chip-list>
@ -29,21 +29,21 @@
</mat-option>
</mat-autocomplete>
<mat-hint class="target-desc">
<span class="target-desc">
{{'USER.TARGET.SELF'| translate}}
<a (click)="changeTarget()">{{'USER.TARGET.CLICKHERE' | translate}}</a>
</mat-hint>
</mat-form-field>
</span>
</cnsl-form-field>
<div *ngIf="target && target == UserTarget.EXTERNAL" class="line">
<mat-form-field class="form-field" appearance="outline">
<mat-label>Global User Loginname</mat-label>
<input matInput type="text" [formControl]="globalLoginNameControl" />
<mat-hint class="target-desc">
<cnsl-form-field class="form-field" appearance="outline">
<cnsl-label>Global User Loginname</cnsl-label>
<input cnslInput type="text" [formControl]="globalLoginNameControl" />
<span class="target-desc">
{{(target == UserTarget.SELF ? 'USER.TARGET.SELF' : 'USER.TARGET.EXTERNAL') | translate}}
<a (click)="changeTarget()">{{'USER.TARGET.CLICKHERE' | translate}}</a>
</mat-hint>
</mat-form-field>
</span>
</cnsl-form-field>
<button color="primary" mat-icon-button (click)="getGlobalUser()">
<i class="las la-search"></i>

View File

@ -2,7 +2,13 @@
width: 100%;
}
.more-space {
margin-bottom: 1rem;
}
.target-desc {
font-size: 14px;
a {
color: #4072b4;
@ -23,7 +29,7 @@
}
button {
margin-top: 1rem;
margin-top: 30px;
}
}

View File

@ -4,14 +4,13 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { AvatarModule } from '../avatar/avatar.module';
import { InputModule } from '../input/input.module';
import { SearchUserAutocompleteComponent } from './search-user-autocomplete.component';
@ -22,8 +21,7 @@ import { SearchUserAutocompleteComponent } from './search-user-autocomplete.comp
MatAutocompleteModule,
MatChipsModule,
MatButtonModule,
MatFormFieldModule,
MatInputModule,
InputModule,
MatIconModule,
ReactiveFormsModule,
MatProgressSpinnerModule,

View File

@ -1,11 +1,10 @@
<app-refresh-table [loading]="dataSource?.loading$ | async" (refreshed)="changePage()"
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [timestamp]="dataSource?.viewTimestamp"
[dataSize]="dataSource?.totalResult" [selection]="selection">
<mat-form-field @appearfade *ngIf="userGrantSearchKey != undefined" actions class="filtername">
<mat-label>{{'USER.PAGES.FILTER' | translate}}</mat-label>
<input matInput (keyup)="applyFilter($event)"
<cnsl-form-field @appearfade *ngIf="userGrantSearchKey != undefined" actions class="filtername">
<input cnslInput (keyup)="applyFilter($event)"
[placeholder]="('USER.TABLE.FILTER.' + userGrantSearchKey.toString()) | translate" #input>
</mat-form-field>
</cnsl-form-field>
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions
(click)="deleteGrantSelection()" *ngIf="selection.hasValue() && disableDelete == false">
@ -65,25 +64,27 @@
{{grant.projectName}} </td>
</ng-container>
<ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th>
<ng-container matColumnDef="dates">
<th mat-header-cell *matHeaderCellDef> DATES </th>
<td mat-cell *matCellDef="let grant">
{{grant.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} </td>
</ng-container>
<ng-container matColumnDef="changeDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
<td mat-cell *matCellDef="let grant">
{{grant.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} </td>
<div class="date-block">
<span class="date-sub">{{ 'PROJECT.GRANT.CREATIONDATE' | translate }}:</span>
<span>{{grant.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
</div>
<div class="date-block">
<span class="date-sub">{{ 'PROJECT.GRANT.CHANGEDATE' | translate }}</span>
<span>{{grant.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
</div>
</td>
</ng-container>
<ng-container matColumnDef="roleNamesList">
<th mat-header-cell *matHeaderCellDef>
<th mat-header-cell *matHeaderCellDef class="role-data">
{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}
<template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: UserGrantSearchKey.USERGRANTSEARCHKEY_ROLE_KEY}"></template>
</th>
<td mat-cell *matCellDef="let grant; let i = index">
<td mat-cell *matCellDef="let grant; let i = index" class="role-data">
<ng-container
*ngIf="(context === UserGrantContext.USER || context === UserGrantContext.NONE) && (grant.grantId && grantToEdit !== grant.id) || (grantToEdit !== grant.id)">
<div class="flex-row">
@ -101,41 +102,45 @@
</div>
</ng-container>
<ng-container
*ngIf="(context === UserGrantContext.OWNED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && grantToEdit == grant.id && loadedProjectId && loadedProjectId === grant.projectId">
<mat-form-field class="form-field" appearance="outline">
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
<mat-select [(ngModel)]="grant.roleKeysList" multiple
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.grantId] : []) | hasRole | async))"
(selectionChange)="updateRoles(grant, $event)">
<mat-option *ngFor="let role of projectRoleOptions" [value]="role.key">
{{role.key}}
</mat-option>
</mat-select>
</mat-form-field>
<button *ngIf="context === UserGrantContext.USER || context === UserGrantContext.NONE"
mat-icon-button (click)="grantToEdit=''">
<mat-icon>close</mat-icon>
</button>
</ng-container>
<div class="row-form">
<ng-container
*ngIf="(context === UserGrantContext.OWNED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && grantToEdit == grant.id && loadedProjectId && loadedProjectId === grant.projectId">
<cnsl-form-field class="form-field" appearance="outline">
<!-- <cnsl-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</cnsl-label> -->
<mat-select [(ngModel)]="grant.roleKeysList" multiple
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.grantId] : []) | hasRole | async))"
(selectionChange)="updateRoles(grant, $event)">
<mat-option *ngFor="let role of projectRoleOptions" [value]="role.key">
{{role.key}}
</mat-option>
</mat-select>
</cnsl-form-field>
<button matTooltip="{{'ACTIONS.CLOSE' | translate}}"
*ngIf="context === UserGrantContext.USER || context === UserGrantContext.NONE"
mat-icon-button (click)="grantToEdit=''">
<mat-icon>close</mat-icon>
</button>
</ng-container>
<ng-container
*ngIf="(context === UserGrantContext.GRANTED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && loadedGrantId && loadedGrantId === grant.grantId && grantToEdit == grant.id">
<mat-form-field class="form-field" appearance="outline">
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
<mat-select [(ngModel)]="grant.roleKeysList" multiple
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.grantId] : []) | hasRole | async))"
(selectionChange)="updateRoles(grant, $event)">
<mat-option *ngFor="let role of grantRoleOptions" [value]="role">
{{role}}
</mat-option>
</mat-select>
</mat-form-field>
<button *ngIf="context === UserGrantContext.USER || context === UserGrantContext.NONE"
mat-icon-button (click)="grantToEdit=''">
<mat-icon>close</mat-icon>
</button>
</ng-container>
<ng-container
*ngIf="(context === UserGrantContext.GRANTED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && loadedGrantId && loadedGrantId === grant.grantId && grantToEdit == grant.id">
<cnsl-form-field class="form-field" appearance="outline">
<!-- <cnsl-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</cnsl-label> -->
<mat-select [(ngModel)]="grant.roleKeysList" multiple
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.grantId] : []) | hasRole | async))"
(selectionChange)="updateRoles(grant, $event)">
<mat-option *ngFor="let role of grantRoleOptions" [value]="role">
{{role}}
</mat-option>
</mat-select>
</cnsl-form-field>
<button matTooltip="{{'ACTIONS.CLOSE' | translate}}"
*ngIf="context === UserGrantContext.USER || context === UserGrantContext.NONE"
mat-icon-button (click)="grantToEdit=''">
<mat-icon>close</mat-icon>
</button>
</ng-container>
</div>
</td>
</ng-container>
@ -144,6 +149,10 @@
</tr>
</table>
<div *ngIf="(dataSource.loading$ | async) == false && !dataSource?.totalResult" class="no-content-row">
<i class="las la-exclamation"></i>
<span>{{'GRANTS.EMPTY' | translate}}</span>
</div>
<mat-paginator class="paginator" #paginator [length]="dataSource.totalResult" [pageSize]="INITIAL_PAGE_SIZE"
[length]="dataSource.totalResult" [pageSizeOptions]="[2, 3, 25, 50, 100, 250]" (page)="changePage($event)">
</mat-paginator>
@ -152,7 +161,7 @@
<ng-template #templateRef let-key="key">
<button class="search-button" mat-icon-button (click)="setFilter(key)">
<mat-icon *ngIf="this.userGrantSearchKey != key">search</mat-icon>
<mat-icon *ngIf="this.userGrantSearchKey == key">search_off</mat-icon>
<mat-icon class="icon" *ngIf="this.userGrantSearchKey != key">search</mat-icon>
<mat-icon class="icon" *ngIf="this.userGrantSearchKey == key">search_off</mat-icon>
</button>
</ng-template>

View File

@ -23,21 +23,21 @@
th {
.search-button {
display: none;
visibility: hidden;
width: 30px;
.icon {
font-size: 1.2rem;
}
}
&:hover,
&.search-active {
.search-button {
display: inline-block;
visibility: visible;
}
}
}
.selection {
width: 50px;
max-width: 50px;
}
}
}
@ -68,7 +68,9 @@
button {
margin: .5rem 0;
display: block;
max-width: 120px;
box-sizing: border-box;
i {
font-size: 1.2rem;
@ -77,10 +79,35 @@
}
}
.row-form {
display: flex;
align-items: center;
.form-field {
flex: 1;
}
}
.fill-space {
flex: 1;
}
.role-data {
min-width: 180px;
}
.date-block {
margin: .5rem 0;
display: block;
min-width: 120px;
.date-sub {
font-size: 13px;
color: var(--grey);
display: block;
}
}
.filtername {
margin-right: 1rem;
}

View File

@ -67,7 +67,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
@Input() public displayedColumns: string[] = ['select',
'user',
'org',
'projectId', 'creationDate', 'changeDate', 'roleNamesList'];
'projectId', 'dates', 'roleNamesList'];
public ngOnInit(): void {
this.dataSource = new UserGrantsDataSource(this.userService);

View File

@ -3,9 +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 { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
@ -18,12 +16,12 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { InputModule } from '../../modules/input/input.module';
import { AvatarModule } from '../avatar/avatar.module';
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
import { UserGrantsComponent } from './user-grants.component';
@NgModule({
declarations: [UserGrantsComponent],
imports: [
@ -40,13 +38,12 @@ import { UserGrantsComponent } from './user-grants.component';
MatCheckboxModule,
MatTooltipModule,
MatSelectModule,
MatInputModule,
MatFormFieldModule,
TranslateModule,
HasRolePipeModule,
TimestampToDatePipeModule,
RefreshTableModule,
LocalizedDatePipeModule,
InputModule,
],
exports: [
UserGrantsComponent,

View File

@ -1,9 +1,9 @@
<div class="max-width-container">
<h2>{{ 'GRANTS.TITLE' | translate }}</h2>
<h1>{{ 'GRANTS.TITLE' | translate }}</h1>
<p class="desc">{{'GRANTS.DESC' | translate }}</p>
<app-user-grants
[displayedColumns]="['select', 'user', 'org', 'projectId', 'creationDate', 'changeDate', 'roleNamesList']"
<app-user-grants [displayedColumns]="['select', 'user', 'org', 'projectId', 'dates', 'roleNamesList']"
[disableWrite]="((['user.grant.write$'] | hasRole) | async) == false"
[disableDelete]="((['user.grant.delete$'] | hasRole) | async) == false">
[disableDelete]="((['user.grant.delete$'] | hasRole) | async) == false"
[refreshOnPreviousRoutes]="['/grant-create']">
</app-user-grants>
</div>

View File

@ -1,3 +1,7 @@
h1 {
margin-top: 0;
}
.desc {
color: var(--grey);
margin-bottom: 2rem;

View File

@ -19,11 +19,6 @@
padding-right: 0;
}
}
.selection {
width: 50px;
max-width: 50px;
}
}
}

View File

@ -59,7 +59,6 @@ export class IamMembersComponent {
}
public removeMemberSelection(): void {
console.log(this.selection);
Promise.all(this.selection.map(member => {
return this.adminService.RemoveIamMember(member.userId).then(() => {
this.toast.showInfo('IAM.TOAST.MEMBERREMOVED', true);

View File

@ -19,11 +19,6 @@
padding-right: 0;
}
}
.selection {
width: 50px;
max-width: 50px;
}
}
}

View File

@ -6,19 +6,18 @@ import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips';
import { MatDialogModule } from '@angular/material/dialog';
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 { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
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';
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
import { PolicyGridModule } from 'src/app/modules/policy-grid/policy-grid.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
@ -46,10 +45,9 @@ import { IamComponent } from './iam.component';
MatCheckboxModule,
MetaLayoutModule,
MatIconModule,
MatTabsModule,
MatTableModule,
MatPaginatorModule,
MatFormFieldModule,
InputModule,
MatSortModule,
MatTooltipModule,
ReactiveFormsModule,

View File

@ -12,17 +12,18 @@
<ng-container *ngIf="!forSelf">
<ng-container *ngIf="currentCreateStep == 1">
<h1>{{'ORG.PAGES.ORGDETAIL_TITLE' | translate}}</h1>
<h1>{{'ORG.PAGES.ORGDETAIL_TITLE' | translate}} </h1>
<form [formGroup]="orgForm" (ngSubmit)="next()">
<div class="content">
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'ORG_DETAIL.DETAIL.NAME' | translate }}</mat-label>
<input matInput formControlName="name" />
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'ORG_DETAIL.DETAIL.DOMAIN' | translate }}</mat-label>
<input matInput formControlName="domain" />
</mat-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'ORG_DETAIL.DETAIL.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'ORG_DETAIL.DETAIL.DOMAIN' | translate }}</cnsl-label>
<input cnslInput formControlName="domain" />
</cnsl-form-field>
</div>
<div class="btn-container">
@ -42,67 +43,67 @@
<form [formGroup]="userForm" class="form">
<div class="content">
<p class="section">{{ 'USER.CREATE.NAMEANDEMAILSECTION' | translate }}</p>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.USERNAME' | translate }}</mat-label>
<input matInput formControlName="userName" required />
<mat-error *ngIf="userName?.invalid && userName?.errors?.required">
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PROFILE.USERNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="userName" required />
<span cnsl-error *ngIf="userName?.invalid && userName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.EMAIL' | translate }}</mat-label>
<input matInput formControlName="email" required />
<mat-error *ngIf="email?.invalid && email?.errors?.required">
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PROFILE.EMAIL' | translate }}</cnsl-label>
<input cnslInput formControlName="email" required />
<span cnsl-error *ngIf="email?.invalid && email?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</mat-label>
<input matInput formControlName="firstName" required />
<mat-error *ngIf="firstName?.invalid && firstName?.errors?.required">
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="firstName" required />
<span cnsl-error *ngIf="firstName?.invalid && firstName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.LASTNAME' | translate }}</mat-label>
<input matInput formControlName="lastName" required />
<mat-error *ngIf="lastName?.invalid && lastName?.errors?.required">
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PROFILE.LASTNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="lastName" required />
<span cnsl-error *ngIf="lastName?.invalid && lastName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</mat-label>
<input matInput formControlName="nickName" />
<mat-error *ngIf="nickName?.invalid && nickName?.errors?.required">
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="nickName" />
<span cnsl-error *ngIf="nickName?.invalid && nickName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
</span>
</cnsl-form-field>
<p class="section">{{ 'USER.CREATE.GENDERLANGSECTION' | translate }}</p>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.GENDER' | translate }}</mat-label>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PROFILE.GENDER' | translate }}</cnsl-label>
<mat-select formControlName="gender">
<mat-option *ngFor="let gender of genders" [value]="gender">
{{ 'GENDERS.'+gender | translate }}
</mat-option>
</mat-select>
<mat-error *ngIf="gender?.invalid && gender?.errors?.required">
<span cnsl-error *ngIf="gender?.invalid && gender?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</mat-label>
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-label>
<mat-select formControlName="preferredLanguage">
<mat-option *ngFor="let language of languages" [value]="language">
{{ 'LANGUAGES.'+language | translate }}
</mat-option>
<mat-error
<span cnsl-error
*ngIf="preferredLanguage?.invalid && preferredLanguage?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</span>
</mat-select>
</mat-form-field>
</cnsl-form-field>
<mat-checkbox class="checkbox" [(ngModel)]="usePassword"
[ngModelOptions]="{standalone: true}" (change)="initPwdValidators()">
@ -116,28 +117,28 @@
</app-password-complexity-view>
<form [formGroup]="pwdForm" class="form">
<mat-form-field class="formfield" *ngIf="password" appearance="outline">
<mat-label>{{ 'USER.PASSWORD.NEW' | translate }}</mat-label>
<input autocomplete="off" name="firstpassword" matInput
<cnsl-form-field class="formfield" *ngIf="password" appearance="outline">
<cnsl-label>{{ 'USER.PASSWORD.NEW' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="firstpassword"
formControlName="password" type="password" />
<mat-error *ngIf="password?.errors?.required">
<span cnsl-error *ngIf="password?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</span>
</mat-form-field>
<mat-form-field class="formfield" *ngIf="confirmPassword" appearance="outline">
<mat-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</mat-label>
<input autocomplete="off" name="confirmPassword" matInput
</cnsl-form-field>
<cnsl-form-field class="formfield" *ngIf="confirmPassword" appearance="outline">
<cnsl-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="confirmPassword"
formControlName="confirmPassword" type="password" />
<mat-error *ngIf="confirmPassword?.errors?.required">
<span cnsl-error *ngIf="confirmPassword?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.notequal">
</span>
<span cnsl-error *ngIf="confirmPassword?.errors?.notequal">
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
</mat-error>
</mat-form-field>
</span>
</cnsl-form-field>
</form>
</ng-container>
</div>
@ -157,13 +158,14 @@
<ng-template appHasRole [appHasRole]="['org.create']">
<div *ngIf="forSelf">
<ng-container *ngIf="currentCreateStep == 1">
<h1>{{'ORG.PAGES.ORGDETAIL_TITLE' | translate}}</h1>
<h1>{{'ORG.PAGES.ORGDETAIL_TITLE_WITHOUT_DOMAIN' | translate}} </h1>
<form [formGroup]="orgForm" (ngSubmit)="createOrgForSelf()">
<div class="content">
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'ORG_DETAIL.DETAIL.NAME' | translate }}</mat-label>
<input matInput formControlName="name" />
</mat-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'ORG_DETAIL.DETAIL.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />
</cnsl-form-field>
</div>
<div class="btn-container">

View File

@ -107,8 +107,15 @@ export class OrgCreateComponent {
this.adminService
.SetUpOrg(createOrgRequest, humanRequest)
.then((data: OrgSetUpResponse) => {
this.router.navigate(['orgs', data.toObject().org?.id]);
.then((org: OrgSetUpResponse) => {
this.router.navigate(['/org/overview']);
// const orgResp = org.getOrg();
// if (orgResp) {
// this.authService.setActiveOrg(orgResp.toObject());
// this.router.navigate(['/org']);
// } else {
// this.router.navigate(['/org', 'overview']);
// }
})
.catch(error => {
this.toast.showError(error);
@ -193,7 +200,12 @@ export class OrgCreateComponent {
public createOrgForSelf(): void {
if (this.name && this.name.value) {
this.mgmtService.CreateOrg(this.name.value).then((org) => {
this.router.navigate(['orgs', org.toObject().id]);
this.router.navigate(['/org/overview']);
// const newOrg = org.toObject();
// setTimeout(() => {
// this.authService.setActiveOrg(newOrg);
// this.router.navigate(['/org']);
// }, 1000);
}).catch(error => {
this.toast.showError(error);
});

View File

@ -3,13 +3,12 @@ import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
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 { MatSelectModule } from '@angular/material/select';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { PasswordComplexityViewModule } from 'src/app/modules/password-complexity-view/password-complexity-view.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
@ -23,8 +22,7 @@ import { OrgCreateComponent } from './org-create.component';
CommonModule,
FormsModule,
ReactiveFormsModule,
MatInputModule,
MatFormFieldModule,
InputModule,
MatButtonModule,
MatIconModule,
MatSelectModule,

View File

@ -2,10 +2,10 @@
<div mat-dialog-content>
<p class="desc"> {{'ORG.DOMAINS.ADD.DESCRIPTION' | translate}}</p>
<mat-form-field label="Domain" required="true" class="form-field" appearance="outline">
<mat-label>Domain</mat-label>
<input matInput [(ngModel)]="newdomain" />
</mat-form-field>
<cnsl-form-field label="Domain" required="true" class="form-field" appearance="outline">
<cnsl-label>Domain</cnsl-label>
<input cnslInput [(ngModel)]="newdomain" />
</cnsl-form-field>
</div>
<div mat-dialog-actions class="action">
<button mat-button (click)="closeDialog()">

View File

@ -2,9 +2,8 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { AddDomainDialogComponent } from './add-domain-dialog.component';
@ -14,8 +13,7 @@ import { AddDomainDialogComponent } from './add-domain-dialog.component';
CommonModule,
TranslateModule,
MatButtonModule,
MatFormFieldModule,
MatInputModule,
InputModule,
FormsModule,
],
})

View File

@ -38,7 +38,6 @@
color="primary">{{ 'ORG.PAGES.DOWNLOAD_FILE' | translate }}</button>
<button color="primary" class="verify-button" type="submit" mat-raised-button (click)="validate()">
<span>{{ 'ACTIONS.VERIFY' | translate }}</span>
<mat-spinner class="spinner" *ngIf="!validating" diameter="20" mode="indeterminate"></mat-spinner>
</button>
<mat-spinner class="spinner" *ngIf="validating" diameter="20" mode="indeterminate"></mat-spinner>
</div>

View File

@ -10,7 +10,6 @@
.domain {
display: flex;
align-items: center;
padding: .5rem 0;
flex-wrap: wrap;
.title {
@ -55,6 +54,7 @@
&:hover {
.rem-button {
visibility: visible;
transition: none;
}
}
}

View File

@ -5,11 +5,11 @@
<app-refresh-table *ngIf="dataSource" (refreshed)="refresh()" [dataSize]="dataSource.data.length"
[loading]="loading$ | async">
<mat-form-field @appearfade *ngIf="orgSearchKey != undefined" actions class="filter">
<mat-label>{{'ORG.PAGES.FILTER' | translate}}</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="{{'ORG.PAGES.FILTERPLACEHOLDER' | translate}}"
<cnsl-form-field @appearfade *ngIf="orgSearchKey != undefined" actions class="filter">
<cnsl-label>{{'ORG.PAGES.FILTER' | translate}}</cnsl-label>
<input cnslInput (keyup)="applyFilter($event)" placeholder="{{'ORG.PAGES.FILTERPLACEHOLDER' | translate}}"
#input>
</mat-form-field>
</cnsl-form-field>
<table [dataSource]="dataSource" mat-table class="table" matSort aria-label="Elements">

View File

@ -23,11 +23,6 @@ h1 {
padding-right: 0;
}
}
.selection {
width: 50px;
max-width: 50px;
}
}
.pointer {

View File

@ -1,5 +1,4 @@
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatInput } from '@angular/material/input';
import { AfterViewInit, Component, Input, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
@ -23,7 +22,7 @@ export class OrgListComponent implements AfterViewInit {
@ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
@ViewChild('input') public filter!: MatInput;
@ViewChild('input') public filter!: Input;
public dataSource!: MatTableDataSource<Org.AsObject>;
public displayedColumns: string[] = ['select', 'id', 'name'];

View File

@ -2,15 +2,14 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatRadioModule } from '@angular/material/radio';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
@ -34,8 +33,7 @@ import { OrgListComponent } from './org-list.component';
MatButtonModule,
MatTooltipModule,
MatRadioModule,
MatFormFieldModule,
MatInputModule,
InputModule,
FormsModule,
],
})

View File

@ -1,6 +1,6 @@
<form>
<mat-form-field appearance="outline" class="full-width">
<mat-label>Role Name</mat-label>
<cnsl-form-field appearance="outline" class="full-width">
<cnsl-label>Role Name</cnsl-label>
<mat-select [formControl]="myControl" multiple>
<mat-option *ngIf="isLoading" class="is-loading">
@ -10,5 +10,5 @@
{{ role }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
</form>

View File

@ -2,12 +2,11 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { OrgMemberRolesAutocompleteComponent } from './org-member-roles-autocomplete.component';
@ -17,9 +16,8 @@ import { OrgMemberRolesAutocompleteComponent } from './org-member-roles-autocomp
CommonModule,
MatButtonModule,
MatSelectModule,
MatFormFieldModule,
InputModule,
MatIconModule,
MatInputModule,
ReactiveFormsModule,
MatProgressSpinnerModule,
FormsModule,

View File

@ -4,9 +4,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTabsModule } from '@angular/material/tabs';
@ -17,6 +15,7 @@ import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
import { CardModule } from 'src/app/modules/card/card.module';
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
import { PolicyGridModule } from 'src/app/modules/policy-grid/policy-grid.module';
import { SharedModule } from 'src/app/modules/shared/shared.module';
@ -37,8 +36,7 @@ import { OrgsRoutingModule } from './orgs-routing.module';
OrgsRoutingModule,
FormsModule,
HasRoleModule,
MatFormFieldModule,
MatInputModule,
InputModule,
MatButtonModule,
MatDialogModule,
CardModule,

View File

@ -21,18 +21,18 @@
<ng-template matStepLabel>{{'APP.OIDC.NAMEANDTYPESECTION' | translate}}</ng-template>
<p class="step-title">{{'APP.OIDC.TITLEFIRST' | translate}}</p>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'APP.NAME' | translate }}</mat-label>
<input matInput formControlName="name" />
<mat-error *ngIf="name?.errors?.required">{{'PROJECT.APP.NAMEREQUIRED' | translate}}</mat-error>
</mat-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />
<span cnsl-error *ngIf="name?.errors?.required">{{'PROJECT.APP.NAMEREQUIRED' | translate}}</span>
</cnsl-form-field>
<p class="step-title">{{'APP.OIDC.TYPETITLE' | translate}}</p>
<mat-radio-group color="primary" aria-labelledby="radio-group-label" class="radio-group"
formControlName="applicationType">
<mat-radio-button class="radio-button" *ngFor="let type of oidcAppTypes | keyvalue"
[value]="type.value">
<div>{{'APP.OIDC.APPTYPE'+type.key | translate}}</div>
<div>{{('APP.OIDC.APPTYPE'+type.key.toString()) | translate}}</div>
</mat-radio-button>
</mat-radio-group>
<div class="actions">
@ -89,19 +89,24 @@
<p class="step-description" *ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB">
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
<mat-form-field appearance="outline" class="full-width">
<mat-label>{{ 'APP.OIDC.REDIRECT' | translate }}</mat-label>
<mat-chip-list #chipRedirectList aria-label="uri selection">
<mat-chip class="chip" *ngFor="let uri of oidcApp.redirectUrisList" selected removable
[matTooltip]="!uri.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
[color]="!uri.startsWith('https://') ? 'warn': 'white'" (removed)="removeUri(uri, 'REDIRECT')">
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input [matChipInputFor]="chipRedirectList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true" [formControl]="redirectControl"
(matChipInputTokenEnd)="addUri($event, 'REDIRECT')">
</mat-chip-list>
</mat-form-field>
<form class="chip-form" (ngSubmit)="addUri(redInput, 'REDIRECT')">
<cnsl-form-field appearance="outline" class="full-width">
<cnsl-label>{{ 'APP.OIDC.REDIRECT' | translate }}</cnsl-label>
<input #redInput cnslInput placeholder="{{'APP.OIDC.COMMAORENTERSEPERATION' | translate}}"
[formControl]="redirectControl">
</cnsl-form-field>
<button matTooltip="{{'ACTIONS.ADD' | translate}}" type="submit" mat-icon-button>
<mat-icon>add</mat-icon>
</button>
</form>
<mat-chip-list #chipRedirectList aria-label="uri selection">
<mat-chip #redInput class="chip" *ngFor="let uri of oidcApp.redirectUrisList" selected removable
[matTooltip]="!uri?.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
[color]="!uri?.startsWith('https://') ? 'warn': 'white'" (removed)="removeUri(uri, 'REDIRECT')">
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
</mat-chip-list>
<p *ngIf="redirectControl.invalid" class="error">{{'APP.OIDC.REDIRECTNOTVALID' | translate}}</p>
<p class="step-title">{{'APP.OIDC.POSTREDIRECTTITLE' | translate}}</p>
@ -112,20 +117,25 @@
*ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB || oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT">
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
<mat-form-field appearance="outline" class="full-width">
<mat-label>{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}</mat-label>
<mat-chip-list #chipPostRedirectList aria-label="uri selection">
<mat-chip class="chip" *ngFor="let uri of oidcApp.postLogoutRedirectUrisList"
[matTooltip]="!uri.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
removable (removed)="removeUri(uri, 'POSTREDIRECT')" selected
[color]="!uri.startsWith('https://') ? 'warn': 'white'">
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input [matChipInputFor]="chipPostRedirectList" [formControl]="postRedirectControl"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addUri($event, 'POSTREDIRECT')">
</mat-chip-list>
</mat-form-field>
<form class="chip-form" (ngSubmit)="addUri(postInput, 'POSTREDIRECT')">
<cnsl-form-field appearance="outline" class="full-width">
<cnsl-label>{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}</cnsl-label>
<input #postInput cnslInput placeholder="{{'APP.OIDC.COMMAORENTERSEPERATION' | translate}}"
[formControl]="postRedirectControl">
</cnsl-form-field>
<button matTooltip="{{'ACTIONS.ADD' | translate}}" type="submit" mat-icon-button>
<mat-icon>add</mat-icon>
</button>
</form>
<mat-chip-list #chipPostRedirectList aria-label="uri selection">
<mat-chip class="chip" *ngFor="let uri of oidcApp.postLogoutRedirectUrisList"
[matTooltip]="!uri?.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
removable (removed)="removeUri(uri, 'POSTREDIRECT')" selected
[color]="!uri?.startsWith('https://') ? 'warn': 'white'">
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
</mat-chip-list>
<p *ngIf="postRedirectControl.invalid" class="error">{{'APP.OIDC.REDIRECTNOTVALID' | translate}}</p>
<div class="actions">
@ -223,71 +233,72 @@
<div *ngIf="devmode" class="dev">
<form [formGroup]="form" (ngSubmit)="saveOIDCApp()">
<div class="content">
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'APP.NAME' | translate }}</mat-label>
<input matInput formControlName="name" />
</mat-form-field>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />
</cnsl-form-field>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'APP.OIDC.APPTYPE' | translate }}</mat-label>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.OIDC.APPTYPE' | translate }}</cnsl-label>
<mat-select formControlName="applicationType">
<mat-option *ngFor="let type of oidcAppTypes" [value]="type">
{{ 'APP.OIDC.APPTYPE'+type | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'APP.OIDC.GRANT' | translate }}</mat-label>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.OIDC.GRANT' | translate }}</cnsl-label>
<mat-select formControlName="grantTypesList" multiple>
<mat-option *ngFor="let grant of oidcGrantTypes" [value]="grant.type">
{{ ('APP.OIDC.GRANT' + grant.type) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'APP.OIDC.RESPONSE' | translate }}</mat-label>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.OIDC.RESPONSE' | translate }}</cnsl-label>
<mat-select formControlName="responseTypesList" multiple>
<mat-option *ngFor="let type of oidcResponseTypes" [value]="type.type">
{{ 'APP.OIDC.RESPONSE'+type.type | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'APP.OIDC.AUTHMETHOD' | translate }}</mat-label>
<cnsl-form-field appearance="outline" class="formfield">
<cnsl-label>{{ 'APP.OIDC.AUTHMETHOD' | translate }}</cnsl-label>
<mat-select formControlName="authMethodType">
<mat-option *ngFor="let type of oidcAuthMethodType" [value]="type.type">
{{ 'APP.OIDC.AUTHMETHOD'+type.type | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
<mat-form-field appearance="outline" class="formfield full-width">
<mat-label>{{ 'APP.OIDC.REDIRECT' | translate }}</mat-label>
<cnsl-form-field appearance="outline" class="formfield full-width">
<cnsl-label>{{ 'APP.OIDC.REDIRECT' | translate }}</cnsl-label>
<mat-chip-list #chipRedirectList aria-label="uri selection">
<mat-chip class="chip" *ngFor="let uri of oidcApp.redirectUrisList" removable
(removed)="removeUri(uri, 'REDIRECT')">
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input [matChipInputFor]="chipRedirectList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addUri($event, 'REDIRECT')">
<input cnslInput [matChipInputFor]="chipRedirectList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addUri($event, 'REDIRECT')">
</mat-chip-list>
</mat-form-field>
</cnsl-form-field>
<mat-form-field appearance="outline" class="formfield full-width">
<mat-label>{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}</mat-label>
<cnsl-form-field appearance="outline" class="formfield full-width">
<cnsl-label>{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}</cnsl-label>
<mat-chip-list #chipPostRedirectList aria-label="uri selection">
<mat-chip class="chip" *ngFor="let uri of oidcApp.postLogoutRedirectUrisList" removable
(removed)="removeUri(uri, 'POSTREDIRECT')">
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input [matChipInputFor]="chipPostRedirectList"
<input cnslInput [matChipInputFor]="chipPostRedirectList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addUri($event, 'POSTREDIRECT')">
</mat-chip-list>
</mat-form-field>
</cnsl-form-field>
</div>

View File

@ -56,8 +56,9 @@ p.desc {
margin: 0 -1.5rem;
.step-title {
font-size: 1.2rem;
color: var(--grey);
font-size: 1rem;
text-transform: uppercase;
letter-spacing: .05em;
}
.step-description {
@ -65,12 +66,31 @@ p.desc {
color: var(--grey);
}
.chip-form {
width: 100%;
display: flex;
align-items: center;
.formfield {
flex: 1;
}
button {
margin-top: 1rem;
}
}
.error {
font-size: 13px;
color: #f44336;
margin-top: 0;
}
.chip {
border-radius: 4px;
height: 40px;
}
.chip[color='white'] {
background-color: #fafafa;
}

Some files were not shown because too many files have changed in this diff Show More