diff --git a/console/src/app/animations.ts b/console/src/app/animations.ts new file mode 100644 index 0000000000..6f739dd3b0 --- /dev/null +++ b/console/src/app/animations.ts @@ -0,0 +1,153 @@ +import { + animate, + animateChild, + AnimationTriggerMetadata, + group, + query, + stagger, + style, + transition, + trigger, +} from '@angular/animations'; + +export const toolbarAnimation: AnimationTriggerMetadata = + trigger('toolbar', [ + transition(':enter', [ + style({ + transform: 'translateY(-100%)', + opacity: 0, + }), + animate( + '.2s ease-out', + style({ + transform: 'translateY(0%)', + opacity: 1, + }), + ), + ]), + ]); + +export const accountCard: AnimationTriggerMetadata = trigger('accounts', [ + transition(':enter', [ + style({ + transform: 'scale(.9) translateY(-10%)', + height: '200px', + opacity: 0, + }), + animate( + '.1s ease-out', + style({ + transform: 'scale(1) translateY(0%)', + height: '*', + opacity: 1, + }), + ), + ]), +]); + +export const navAnimations: Array = [ + trigger('navAnimation', [ + transition('* => *', [ + query('@navitem', stagger('50ms', animateChild()), { optional: true }), + ]), + ]), + trigger('navitem', [ + transition(':enter', [ + style({ + opacity: 0, + }), + animate( + '.0s', + style({ + opacity: 1, + }), + ), + ]), + transition(':leave', [ + style({ + opacity: 1, + }), + animate( + '.0s', + style({ + opacity: 0, + }), + ), + ]), + ]), +]; + +export const routeAnimations: AnimationTriggerMetadata = trigger('routeAnimations', [ + transition('HomePage => AddPage', [ + style({ transform: 'translateX(100%)' }), + animate('250ms ease-in-out', style({ transform: 'translateX(0%)' })), + ]), + transition('AddPage => HomePage', [animate('250ms', style({ transform: 'translateX(100%)' }))]), + transition('HomePage => DetailPage', [ + query(':enter, :leave', style({ position: 'absolute', left: 0, right: 0 }), { + optional: true, + }), + group([ + query( + ':enter', + [ + style({ + transform: 'translateX(20%)', + opacity: 0.5, + }), + animate( + '.35s ease-in', + style({ + transform: 'translateX(0%)', + opacity: 1, + }), + ), + ], + { + optional: true, + }, + ), + query( + ':leave', + [style({ opacity: 1, width: '100%' }), animate('.35s ease-out', style({ opacity: 0 }))], + { + optional: true, + }, + ), + ]), + ]), + transition('DetailPage => HomePage', [ + query(':enter, :leave', style({ position: 'absolute', left: 0, right: 0 }), { + optional: true, + }), + group([ + query( + ':enter', + [ + style({ + opacity: 0, + }), + animate( + '.35s ease-out', + style({ + opacity: 1, + }), + ), + ], + { + optional: true, + }, + ), + query( + ':leave', + [ + style({ width: '100%', transform: 'translateX(0%)' }), + animate('.35s ease-in', style({ transform: 'translateX(30%)', opacity: 0 })), + ], + { + optional: true, + }, + ), + ]), + ]), +]); diff --git a/console/src/app/app.component.html b/console/src/app/app.component.html index 552cea5150..1168150519 100644 --- a/console/src/app/app.component.html +++ b/console/src/app/app.component.html @@ -1,6 +1,6 @@ - + @@ -53,7 +53,7 @@
- {{ 'MENU.PERSONAL_INFO' | translate }} @@ -64,52 +64,65 @@
-
+ {{'MENU.IAM' | translate}}
- - - - {{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}} - - +
+ + + + {{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}} + + - -
-
- {{'MENU.PROJECTSSECTION' | translate}} -
-
+ +
+
+ {{'MENU.PROJECTSSECTION' | translate}} +
+
- - - {{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}} - {{ 'MENU.PROJECT' | translate }} - + + - - - {{ 'MENU.GRANTEDPROJECT' | translate }} - -
+
+ {{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}} + {{'MENU.PROJECT' | translate}} + {{ownedPCount}} +
+ - -
-
- - {{ 'MENU.USERSECTION' | translate }} -
-
+ + +
+ {{ 'MENU.GRANTEDPROJECT' | translate }} + {{grantPCount}} +
+
+
- - - {{ 'MENU.USER' | translate }} - -
+ +
+
+ + {{ 'MENU.USERSECTION' | translate }} +
+
+ + + + {{ 'MENU.USER' | translate }} + +
+
@@ -117,7 +130,7 @@
-
+
{{'MENU.IAMADMIN' | translate}}
diff --git a/console/src/app/app.component.scss b/console/src/app/app.component.scss index d09e013c72..bb9f03bab6 100644 --- a/console/src/app/app.component.scss +++ b/console/src/app/app.component.scss @@ -1,11 +1,14 @@ .root-header { - position: relative; + position: fixed; z-index: 100; display: flex; height: 60px; align-items: center; padding: 0 1rem; + top: 0; + left: 0; + right: 0; .logo { height: 40px; @@ -74,15 +77,16 @@ .main-container { display: flex; flex-direction: column; - height: calc(100vh - 60px); - width: 100%; + height: 100vh; + width: 100%; + padding-top: 60px; .sidenav { width: 300px; border-right: none; .side-column { - height: calc(100vh - 70px); + padding-top: 60px; display: flex; flex-direction: column; align-items: stretch; @@ -120,6 +124,17 @@ font-weight: 500; font-size: .9rem; } + + .c_label { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + + .count { + font-size: 12px; + } + } &:hover { background-color: #00000010; diff --git a/console/src/app/app.component.ts b/console/src/app/app.component.ts index bd12730be2..66089f7cea 100644 --- a/console/src/app/app.component.ts +++ b/console/src/app/app.component.ts @@ -1,4 +1,3 @@ -import { animate, group, query, style, transition, trigger } from '@angular/animations'; import { BreakpointObserver } from '@angular/cdk/layout'; import { OverlayContainer } from '@angular/cdk/overlay'; import { ViewportScroller } from '@angular/common'; @@ -11,9 +10,11 @@ import { TranslateService } from '@ngx-translate/core'; import { Observable, of, Subscription } from 'rxjs'; import { map } from 'rxjs/operators'; +import { accountCard, navAnimations, routeAnimations, toolbarAnimation } from './animations'; import { Org, UserProfileView } from './proto/generated/auth_pb'; import { AuthUserService } from './services/auth-user.service'; import { AuthService } from './services/auth.service'; +import { ProjectService } from './services/project.service'; import { ThemeService } from './services/theme.service'; import { ToastService } from './services/toast.service'; import { UpdateService } from './services/update.service'; @@ -23,97 +24,10 @@ import { UpdateService } from './services/update.service'; templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], animations: [ - trigger('accounts', [ - transition(':enter', [ - style({ - transform: 'scale(.9) translateY(-10%)', - height: '200px', - opacity: 0, - }), - animate( - '.1s ease-out', - style({ - transform: 'scale(1) translateY(0%)', - height: '*', - opacity: 1, - }), - ), - ]), - ]), - trigger('routeAnimations', [ - transition('HomePage => AddPage', [ - style({ transform: 'translateX(100%)' }), - animate('250ms ease-in-out', style({ transform: 'translateX(0%)' })), - ]), - transition('AddPage => HomePage', [animate('250ms', style({ transform: 'translateX(100%)' }))]), - transition('HomePage => DetailPage', [ - query(':enter, :leave', style({ position: 'absolute', left: 0, right: 0 }), { - optional: true, - }), - group([ - query( - ':enter', - [ - style({ - transform: 'translateX(20%)', - opacity: 0.5, - }), - animate( - '.35s ease-in', - style({ - transform: 'translateX(0%)', - opacity: 1, - }), - ), - ], - { - optional: true, - }, - ), - query( - ':leave', - [style({ opacity: 1, width: '100%' }), animate('.35s ease-out', style({ opacity: 0 }))], - { - optional: true, - }, - ), - ]), - ]), - transition('DetailPage => HomePage', [ - query(':enter, :leave', style({ position: 'absolute', left: 0, right: 0 }), { - optional: true, - }), - group([ - query( - ':enter', - [ - style({ - opacity: 0, - }), - animate( - '.35s ease-out', - style({ - opacity: 1, - }), - ), - ], - { - optional: true, - }, - ), - query( - ':leave', - [ - style({ width: '100%', transform: 'translateX(0%)' }), - animate('.35s ease-in', style({ transform: 'translateX(30%)', opacity: 0 })), - ], - { - optional: true, - }, - ), - ]), - ]), - ]), + toolbarAnimation, + ...navAnimations, + accountCard, + routeAnimations, ], }) export class AppComponent implements OnDestroy { @@ -135,6 +49,10 @@ export class AppComponent implements OnDestroy { public orgLoading: boolean = false; public showProjectSection: boolean = false; + + public grantedProjectsCount: number = 0; + public ownedProjectsCount: number = 0; + private authSub: Subscription = new Subscription(); private orgSub: Subscription = new Subscription(); @@ -147,6 +65,7 @@ export class AppComponent implements OnDestroy { public overlayContainer: OverlayContainer, private themeService: ThemeService, public userService: AuthUserService, + private projectService: ProjectService, public matIconRegistry: MatIconRegistry, public domSanitizer: DomSanitizer, private toast: ToastService, @@ -218,9 +137,12 @@ export class AppComponent implements OnDestroy { 'mdi_pin', this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/pin.svg'), ); + this.getProjectCount(); this.orgSub = this.authService.activeOrgChanged.subscribe(org => { this.org = org; + + this.getProjectCount(); }); this.authSub = this.authService.authenticationChanged.subscribe((authenticated) => { @@ -289,5 +211,15 @@ export class AppComponent implements OnDestroy { this.authService.setActiveOrg(org); this.router.navigate(['/']); } + + private async getProjectCount(): Promise { + this.ownedProjectsCount = await this.projectService.SearchProjects(0, 0).then(res => { + return res.toObject().totalResult; + }); + + this.grantedProjectsCount = await this.projectService.SearchGrantedProjects(0, 0).then(res => { + return res.toObject().totalResult; + }); + } } diff --git a/console/src/app/directives/has-role/has-role.directive.ts b/console/src/app/directives/has-role/has-role.directive.ts index 077e14466a..807fa87f3c 100644 --- a/console/src/app/directives/has-role/has-role.directive.ts +++ b/console/src/app/directives/has-role/has-role.directive.ts @@ -1,5 +1,5 @@ import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; -import { AuthUserService } from 'src/app/services/auth-user.service'; +import { AuthService } from 'src/app/services/auth.service'; @Directive({ @@ -10,7 +10,7 @@ export class HasRoleDirective { private hasView: boolean = false; @Input() public set appHasRole(roles: string[]) { if (roles && roles.length > 0) { - this.userService.isAllowed(roles).subscribe(isAllowed => { + this.authService.isAllowed(roles).subscribe(isAllowed => { if (isAllowed && !this.hasView) { this.viewContainerRef.clear(); this.viewContainerRef.createEmbeddedView(this.templateRef); @@ -23,7 +23,7 @@ export class HasRoleDirective { } constructor( - private userService: AuthUserService, + private authService: AuthService, protected templateRef: TemplateRef, protected viewContainerRef: ViewContainerRef, ) { } diff --git a/console/src/app/guards/role.guard.ts b/console/src/app/guards/role.guard.ts index 0c5d52029f..738f61cb4f 100644 --- a/console/src/app/guards/role.guard.ts +++ b/console/src/app/guards/role.guard.ts @@ -2,19 +2,19 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; import { Observable } from 'rxjs'; -import { AuthUserService } from '../services/auth-user.service'; +import { AuthService } from '../services/auth.service'; @Injectable({ providedIn: 'root', }) export class RoleGuard implements CanActivate { - constructor(private userService: AuthUserService) { } + constructor(private authService: AuthService) { } public canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot, ): Observable { - return this.userService.isAllowed(route.data['roles'], true); + return this.authService.isAllowed(route.data['roles'], true); } } diff --git a/console/src/app/modules/avatar/avatar.component.scss b/console/src/app/modules/avatar/avatar.component.scss index 046b6d68a3..dcfa3d4fb1 100644 --- a/console/src/app/modules/avatar/avatar.component.scss +++ b/console/src/app/modules/avatar/avatar.component.scss @@ -15,9 +15,5 @@ box-sizing: border-box; outline: none; color: white; - - // &.active:hover { - // border: 2px solid #8795a1; - // } } } \ No newline at end of file diff --git a/console/src/styles/card.scss b/console/src/app/modules/card/card.scss similarity index 82% rename from console/src/styles/card.scss rename to console/src/app/modules/card/card.scss index b698753b22..12dbc6ceba 100644 --- a/console/src/styles/card.scss +++ b/console/src/app/modules/card/card.scss @@ -1,9 +1,6 @@ @import '~@angular/material/theming'; @mixin card-theme($theme) { - $accent: map-get($theme, accent); - $background: map-get($theme, background); - $background-color: mat-color($background, card); $primary: map-get($theme, primary); $primary-color: mat-color($primary, 500); $primary-dark: mat-color($primary, A800); @@ -17,7 +14,6 @@ box-sizing: border-box; border-radius: .5rem; outline: none; - // box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.1), 0px 1px 1px 0px rgba(0, 0, 0, 0.1), 0px 1px 3px 0px rgba(0, 0, 0, 0.1); .selection-icon { opacity: 0; diff --git a/console/src/app/modules/changes/changes.component.html b/console/src/app/modules/changes/changes.component.html index 4908ad05d4..e9f028d76c 100644 --- a/console/src/app/modules/changes/changes.component.html +++ b/console/src/app/modules/changes/changes.component.html @@ -1,7 +1,8 @@ {{ 'CHANGES.LISTTITLE' | translate }} -
-
  • +
    +
  • {{event.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} diff --git a/console/src/app/modules/changes/changes.component.scss b/console/src/app/modules/changes/changes.component.scss index b8614b1afe..5b9352208c 100644 --- a/console/src/app/modules/changes/changes.component.scss +++ b/console/src/app/modules/changes/changes.component.scss @@ -1,3 +1,6 @@ +@import '~@angular/material/theming'; + + .header { display: block; margin-bottom: 1rem; @@ -5,42 +8,53 @@ margin-top: 1rem; } -.scroll-container { - max-height: 60vh; - overflow-y: scroll; +@mixin changes-theme($theme) { - .item { - box-sizing: border-box; - padding: .5rem; - margin: .25rem 0; - border-radius: .5rem; - display: flex; - flex-direction: column; - .editor { - color: #8795a1; + .scroll-container { + max-height: 60vh; + overflow-y: scroll; + + .item { + box-sizing: border-box; + padding: .5rem; + margin: .25rem 0; + border-radius: .5rem; + display: flex; + flex-direction: column; + .editor { + color: #8795a1; + font-size: 12px; + align-self: flex-end; + } + .seq { + color: #8795a1; + font-size: 12px; + align-self: flex-end; + } + + .desc { + overflow-x: auto; + font-size: 14px; + } + + $primary: map-get($theme, primary); + $primary-dark: mat-color($primary, A800); + + &.change-item-back { + background-color: rgba($primary-dark, 0.93); + transition: background-color .4s ease-in-out; + } + } + + .sp-wrapper { + padding: .5rem; + display: flex; + justify-content: center; + } + + .end-container { font-size: 12px; - align-self: flex-end; - } - .seq { color: #8795a1; - font-size: 12px; - align-self: flex-end; - } - - .desc { - overflow-x: auto; - font-size: 14px; } } - - .sp-wrapper { - padding: .5rem; - display: flex; - justify-content: center; - } - - .end-container { - font-size: 12px; - color: #8795a1; - } -} +} \ No newline at end of file diff --git a/console/src/app/modules/changes/changes.component.ts b/console/src/app/modules/changes/changes.component.ts index d47950e812..3c2deec0d6 100644 --- a/console/src/app/modules/changes/changes.component.ts +++ b/console/src/app/modules/changes/changes.component.ts @@ -1,3 +1,4 @@ +import { animate, animateChild, keyframes, query, stagger, style, transition, trigger } from '@angular/animations'; import { Component, Input, OnInit } from '@angular/core'; import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { catchError, scan, take, tap } from 'rxjs/operators'; @@ -16,6 +17,22 @@ export enum ChangeType { selector: 'app-changes', templateUrl: './changes.component.html', styleUrls: ['./changes.component.scss'], + animations: [ + trigger('cardAnimation', [ + transition('* => *', [ + query('@animate', stagger('50ms', animateChild()), { optional: true }), + ]), + ]), + trigger('animate', [ + transition(':enter', [ + animate('.2s ease-in', keyframes([ + style({ opacity: 0 }), + style({ opacity: .5, transform: 'scale(1.02)' }), + style({ opacity: 1, transform: 'scale(1)' }), + ])), + ]), + ]), + ], }) export class ChangesComponent implements OnInit { @Input() public changeType: ChangeType = ChangeType.USER; diff --git a/console/src/app/modules/contributors/contributors.component.html b/console/src/app/modules/contributors/contributors.component.html index 8d6548226c..f39fe44ad2 100644 --- a/console/src/app/modules/contributors/contributors.component.html +++ b/console/src/app/modules/contributors/contributors.component.html @@ -2,12 +2,12 @@ {{ title }} {{ description }}
    -
    +
    -
    *', [ + query('@animate', stagger('40ms', animateChild()), { optional: true }), ]), ]), trigger('animate', [ transition(':enter', [ - style({ opacity: 0, transform: 'translateX(100%)' }), - animate('100ms', style({ opacity: 1, transform: 'translateX(0)' })), + animate('.2s ease-in', keyframes([ + style({ opacity: 0, offset: 0 }), + style({ opacity: .5, transform: 'scale(1.05)', offset: 0.3 }), + style({ opacity: 1, transform: 'scale(1)', offset: 1 }), + ])), ]), ]), ], diff --git a/console/src/app/modules/project-roles/project-roles.component.html b/console/src/app/modules/project-roles/project-roles.component.html index 87de281374..82985b260d 100644 --- a/console/src/app/modules/project-roles/project-roles.component.html +++ b/console/src/app/modules/project-roles/project-roles.component.html @@ -51,7 +51,7 @@ {{ 'PROJECT.ROLE.CREATIONDATE' | translate }} - {{role.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }} + {{role.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} diff --git a/console/src/app/modules/project-roles/project-roles.module.ts b/console/src/app/modules/project-roles/project-roles.module.ts index 92dee97d99..d84177d736 100644 --- a/console/src/app/modules/project-roles/project-roles.module.ts +++ b/console/src/app/modules/project-roles/project-roles.module.ts @@ -16,6 +16,7 @@ import { RouterModule } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module'; +import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe.module'; import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module'; import { RefreshTableModule } from '../refresh-table/refresh-table.module'; @@ -46,6 +47,7 @@ import { ProjectRolesComponent } from './project-roles.component'; MatMenuModule, TimestampToDatePipeModule, RefreshTableModule, + LocalizedDatePipeModule, ], exports: [ ProjectRolesComponent, diff --git a/console/src/app/modules/user-grants/user-grants.component.html b/console/src/app/modules/user-grants/user-grants.component.html index 78c9c11f8e..ae4ce02392 100644 --- a/console/src/app/modules/user-grants/user-grants.component.html +++ b/console/src/app/modules/user-grants/user-grants.component.html @@ -51,13 +51,13 @@ {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} - {{grant.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }} + {{grant.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} - {{grant.changeDate | timestampToDate | date: 'dd. MMM, HH:mm' }} + {{grant.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} diff --git a/console/src/app/modules/user-grants/user-grants.module.ts b/console/src/app/modules/user-grants/user-grants.module.ts index cd123fee7a..f1b7c656a6 100644 --- a/console/src/app/modules/user-grants/user-grants.module.ts +++ b/console/src/app/modules/user-grants/user-grants.module.ts @@ -14,6 +14,7 @@ import { RouterModule } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module'; +import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe.module'; import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module'; import { AvatarModule } from '../avatar/avatar.module'; @@ -43,6 +44,7 @@ import { UserGrantsComponent } from './user-grants.component'; HasRolePipeModule, TimestampToDatePipeModule, RefreshTableModule, + LocalizedDatePipeModule, ], exports: [ UserGrantsComponent, diff --git a/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-grid/granted-project-grid.component.html b/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-grid/granted-project-grid.component.html index b757fd925d..75046f8e8d 100644 --- a/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-grid/granted-project-grid.component.html +++ b/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-grid/granted-project-grid.component.html @@ -14,12 +14,12 @@
    {{'PROJECT.PAGES.LASTMODIFIED' | translate}} {{ - item.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm' + item.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }} {{ item.projectName }} {{item.resourceOwnerName}} {{'PROJECT.PAGES.CREATEDON' | translate}} - {{ item.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm' }} + {{ item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}
    @@ -41,12 +41,12 @@
    {{'PROJECT.PAGES.LASTMODIFIED' | translate}} {{ - item.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm' + item.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }} {{ item.projectName }} {{item.resourceOwnerName}} {{'PROJECT.PAGES.CREATEDON' | translate}} - {{ item.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm' }} + {{ item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}
    diff --git a/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-list.component.html b/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-list.component.html index ddc4c6a986..4e7fe0be28 100644 --- a/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-list.component.html +++ b/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-list.component.html @@ -72,7 +72,7 @@ {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} {{project.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}} + *ngIf="project.creationDate">{{project.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} @@ -81,7 +81,7 @@ {{ 'PROJECT.TABLE.CHANGEDATE' | translate }} {{project.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}} + *ngIf="project.changeDate">{{project.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} diff --git a/console/src/app/pages/projects/granted-projects/granted-projects.module.ts b/console/src/app/pages/projects/granted-projects/granted-projects.module.ts index 6a4480d5dd..856687d0df 100644 --- a/console/src/app/pages/projects/granted-projects/granted-projects.module.ts +++ b/console/src/app/pages/projects/granted-projects/granted-projects.module.ts @@ -25,6 +25,7 @@ import { ProjectRolesModule } from 'src/app/modules/project-roles/project-roles. import { SharedModule } from 'src/app/modules/shared/shared.module'; import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module'; +import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe.module'; import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module'; import { GrantedProjectDetailComponent } from './granted-project-detail/granted-project-detail.component'; @@ -70,6 +71,7 @@ import { GrantedProjectsComponent } from './granted-projects.component'; TranslateModule, TimestampToDatePipeModule, SharedModule, + LocalizedDatePipeModule, MemberCreateDialogModule, ], }) diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.module.ts b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.module.ts index 448418fb88..600ee17eec 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.module.ts +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.module.ts @@ -23,6 +23,7 @@ import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table. import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module'; import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module'; +import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe.module'; import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module'; import { ApplicationGridComponent } from './application-grid/application-grid.component'; @@ -65,6 +66,7 @@ import { ProjectGrantsComponent } from './project-grants/project-grants.componen MetaLayoutModule, RefreshTableModule, MemberCreateDialogModule, + LocalizedDatePipeModule, ], }) export class OwnedProjectDetailModule { } diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/project-grants/project-grants.component.html b/console/src/app/pages/projects/owned-projects/owned-project-detail/project-grants/project-grants.component.html index 0d5c38d83e..4c98ce5ebd 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/project-grants/project-grants.component.html +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/project-grants/project-grants.component.html @@ -43,14 +43,14 @@ {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} - {{grant.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }} + {{grant.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} - {{grant.changeDate | timestampToDate | date: 'dd. MMM, HH:mm' }} + {{grant.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} diff --git a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.html b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.html index bdffcb1242..2293a23564 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.html +++ b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.html @@ -15,13 +15,13 @@
    {{'PROJECT.PAGES.LASTMODIFIED' | translate}} {{ - item.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm' + item.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }} {{ item.name }} {{'PROJECT.PAGES.CREATEDON' | translate}} {{ - item.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm' + item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}
    @@ -44,13 +44,13 @@
    {{'PROJECT.PAGES.LASTMODIFIED' | translate}} {{ - item.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm' + item.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }} {{ item.name }} {{'PROJECT.PAGES.CREATEDON' | translate}} {{ - item.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm' + item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}
    diff --git a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.ts b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.ts index 78416a4c31..094720c220 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.ts +++ b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.ts @@ -1,4 +1,4 @@ -import { animate, animateChild, query, stagger, style, transition, trigger } from '@angular/animations'; +import { animate, animateChild, keyframes, query, stagger, style, transition, trigger } from '@angular/animations'; import { SelectionModel } from '@angular/cdk/collections'; import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { Router } from '@angular/router'; @@ -10,21 +10,25 @@ import { AuthService } from 'src/app/services/auth.service'; templateUrl: './owned-project-grid.component.html', styleUrls: ['./owned-project-grid.component.scss'], animations: [ - trigger('list', [ - transition(':enter', [ - query('@animate', - stagger(100, animateChild()), - ), + trigger('cardAnimation', [ + transition('* => *', [ + query('@animate', stagger('100ms', animateChild()), { optional: true }), ]), ]), trigger('animate', [ transition(':enter', [ - style({ opacity: 0, transform: 'translateY(-100%)' }), - animate('100ms', style({ opacity: 1, transform: 'translateY(0)' })), + animate('.2s ease-in', keyframes([ + style({ opacity: 0, transform: 'translateY(-50%)', offset: 0 }), + style({ opacity: .5, transform: 'translateY(-10px) scale(1.1)', offset: 0.3 }), + style({ opacity: 1, transform: 'translateY(0)', offset: 1 }), + ])), ]), transition(':leave', [ - style({ opacity: 1, transform: 'translateY(0)' }), - animate('100ms', style({ opacity: 0, transform: 'translateY(100%)' })), + animate('.2s ease-out', keyframes([ + style({ opacity: 1, transform: 'scale(1.1)', offset: 0 }), + style({ opacity: .5, transform: 'scale(.5)', offset: 0.3 }), + style({ opacity: 0, transform: 'scale(0)', offset: 1 }), + ])), ]), ]), ], diff --git a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.html b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.html index 47247d8ae7..7b0bd7fc42 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.html +++ b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.html @@ -61,7 +61,7 @@ {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} {{project.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}} + *ngIf="project.creationDate">{{project.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} @@ -70,7 +70,7 @@ {{ 'PROJECT.TABLE.CHANGEDATE' | translate }} {{project.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}} + *ngIf="project.changeDate">{{project.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} diff --git a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.ts b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.ts index 2123e30856..6ef18ba66f 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.ts +++ b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.ts @@ -88,7 +88,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy { this.projectService.SearchProjects(limit, offset).then(res => { this.ownedProjectList = res.toObject().resultList; this.totalResult = res.toObject().totalResult; - if (this.totalResult > 5) { + if (this.totalResult > 10) { this.grid = false; } this.dataSource.data = this.ownedProjectList; diff --git a/console/src/app/pages/projects/owned-projects/owned-projects.module.ts b/console/src/app/pages/projects/owned-projects/owned-projects.module.ts index 448eff2475..565730b59c 100644 --- a/console/src/app/pages/projects/owned-projects/owned-projects.module.ts +++ b/console/src/app/pages/projects/owned-projects/owned-projects.module.ts @@ -20,6 +20,7 @@ import { CardModule } from 'src/app/modules/card/card.module'; import { SharedModule } from 'src/app/modules/shared/shared.module'; import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module'; +import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe.module'; import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module'; import { OwnedProjectGridComponent } from './owned-project-list/owned-project-grid/owned-project-grid.component'; @@ -58,6 +59,7 @@ import { OwnedProjectsComponent } from './owned-projects.component'; MatSortModule, HasRolePipeModule, TimestampToDatePipeModule, + LocalizedDatePipeModule, SharedModule, ], }) diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-card.scss b/console/src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-card.scss index 111a8caf9a..8e25b4bd02 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-card.scss +++ b/console/src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-card.scss @@ -4,7 +4,7 @@ $primary: map-get($theme, primary); $primary-dark: mat-color($primary, A800); - .theme-conent, .theme-app , .crescent { + .theme-conent, .crescent { background-color: $primary-dark; transition: background-color .4s cubic-bezier(0.645, 0.045, 0.355, 1); } diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-setting.component.scss b/console/src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-setting.component.scss index e5f16820f7..5344063aed 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-setting.component.scss +++ b/console/src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-setting.component.scss @@ -102,8 +102,8 @@ label { } [type="checkbox"]:checked + .theme-app{ - background-color: $dark-background; - color: $light-background; + background-color: inherit; + color: inherit; } [type="checkbox"]:checked + .theme-app .crescent{ diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html index d00162401c..59f2886448 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html @@ -38,10 +38,8 @@ - + diff --git a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts index b3dd9bb372..f4e695e6a6 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts @@ -44,7 +44,6 @@ export class UserMfaComponent implements OnInit, OnDestroy { public getOTP(): void { this.mgmtUserService.getUserMfas(this.user.id).then(mfas => { - console.log(mfas.toObject().mfasList); this.dataSource = new MatTableDataSource(mfas.toObject().mfasList); this.dataSource.sort = this.sort; }).catch(error => { diff --git a/console/src/app/pipes/has-role.pipe.ts b/console/src/app/pipes/has-role.pipe.ts index 502e1f1b06..d0c15e840c 100644 --- a/console/src/app/pipes/has-role.pipe.ts +++ b/console/src/app/pipes/has-role.pipe.ts @@ -1,16 +1,16 @@ import { Pipe, PipeTransform } from '@angular/core'; import { Observable } from 'rxjs'; -import { AuthUserService } from '../services/auth-user.service'; +import { AuthService } from '../services/auth.service'; @Pipe({ name: 'hasRole', }) export class HasRolePipe implements PipeTransform { - constructor(private authUserService: AuthUserService) { } + constructor(private authService: AuthService) { } public transform(values: string[], each: boolean = false): Observable { - return this.authUserService.isAllowed(values, each); + return this.authService.isAllowed(values, each); } } diff --git a/console/src/app/services/auth-user.service.ts b/console/src/app/services/auth-user.service.ts index cc762d5869..a836502425 100644 --- a/console/src/app/services/auth-user.service.ts +++ b/console/src/app/services/auth-user.service.ts @@ -1,8 +1,6 @@ import { Injectable } from '@angular/core'; import { Empty } from 'google-protobuf/google/protobuf/empty_pb'; import { Metadata } from 'grpc-web'; -import { from, Observable, of } from 'rxjs'; -import { catchError, switchMap } from 'rxjs/operators'; import { AuthServicePromiseClient } from '../proto/generated/auth_grpc_web_pb'; import { @@ -11,6 +9,7 @@ import { Gender, MfaOtpResponse, MultiFactors, + MyPermissions, MyProjectOrgSearchQuery, MyProjectOrgSearchRequest, MyProjectOrgSearchResponse, @@ -38,8 +37,6 @@ import { GrpcService, RequestFactory, ResponseMapper } from './grpc.service'; providedIn: 'root', }) export class AuthUserService { - private _roleCache: string[] = []; - constructor(private readonly grpcClient: GrpcService, private grpcBackendService: GrpcBackendService, ) { } @@ -75,7 +72,6 @@ export class AuthUserService { ); } - public async GetMyUser(): Promise { return await this.request( c => c.getMyUser, @@ -175,7 +171,7 @@ export class AuthUserService { ); } - private async getMyzitadelPermissions(): Promise { + public async GetMyzitadelPermissions(): Promise { return await this.request( c => c.getMyZitadelPermissions, new Empty(), @@ -183,19 +179,6 @@ export class AuthUserService { ); } - public GetMyzitadelPermissions(): Observable { - return from(this.getMyzitadelPermissions()); - } - - public hasRoles(userRoles: string[], requestedRoles: string[], each: boolean = false): boolean { - return each ? - requestedRoles.every(role => userRoles.includes(role)) : - requestedRoles.findIndex(role => { - return userRoles.findIndex(i => i.includes(role)) > -1; - // return userRoles.includes(role); - }) > -1; - } - public async GetMyUserPhone(): Promise { // return this.grpcClient.auth.getMyUserPhone(new Empty()); return await this.request( @@ -312,31 +295,4 @@ export class AuthUserService { f => f, ); } - - public isAllowed(roles: string[], each: boolean = false): Observable { - if (roles && roles.length > 0) { - if (this._roleCache.length > 0) { - return of(this.hasRoles(this._roleCache, roles)); - } - - return this.GetMyzitadelPermissions().pipe( - switchMap(response => { - let userRoles = []; - if (response.toObject().permissionsList) { - userRoles = response.toObject().permissionsList; - } else { - userRoles = ['user.resourceowner']; - } - this._roleCache = userRoles; - return of(this.hasRoles(userRoles, roles, each)); - }), - catchError((err) => { - return of(false); - }), - ); - } else { - return of(false); - } - } - } diff --git a/console/src/app/services/auth.service.ts b/console/src/app/services/auth.service.ts index f0a9fbbf78..b071482d0a 100644 --- a/console/src/app/services/auth.service.ts +++ b/console/src/app/services/auth.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { AuthConfig, OAuthService } from 'angular-oauth2-oidc'; import { BehaviorSubject, from, merge, Observable, of, Subject } from 'rxjs'; -import { catchError, filter, map, mergeMap, take, timeout } from 'rxjs/operators'; +import { catchError, filter, finalize, first, map, mergeMap, switchMap, take, timeout } from 'rxjs/operators'; import { Org, UserProfileView } from '../proto/generated/auth_pb'; import { AuthUserService } from './auth-user.service'; @@ -22,6 +22,8 @@ export class AuthService { boolean > = new BehaviorSubject(this.authenticated); + private zitadelPermissions: BehaviorSubject = new BehaviorSubject(['user.resourceowner']); + constructor( private grpcService: GrpcService, private config: AuthConfig, @@ -44,9 +46,50 @@ export class AuthService { ).pipe( take(1), mergeMap(token => { - return from(this.userService.GetMyUserProfile()).pipe(map(userprofile => userprofile.toObject())); + console.log(token); + return from(this.userService.GetMyUserProfile().then(userprofile => userprofile.toObject())); + }), + finalize(() => { + this.loadPermissions(); }), ); + + this.activeOrgChanged.subscribe(() => { + console.log('org change'); + this.loadPermissions(); + }); + } + + private loadPermissions(): void { + console.log('load permissions'); + merge([ + // this.authenticationChanged, + this.activeOrgChanged.pipe(map(org => !!org)), + ]).pipe( + first(), + switchMap(() => from(this.userService.GetMyzitadelPermissions())), + map(rolesResp => rolesResp.toObject().permissionsList), + ).subscribe(roles => this.zitadelPermissions.next(roles)); + } + + public isAllowed(roles: string[], each: boolean = false): Observable { + if (roles && roles.length > 0) { + return this.zitadelPermissions.pipe(switchMap(zroles => { + return of(this.hasRoles(zroles, roles, each)); + })); + } else { + return of(false); + } + } + + public hasRoles(userRoles: string[], requestedRoles: string[], each: boolean = false): boolean { + // console.log('has', userRoles); + // console.log('needs', requestedRoles); + return each ? + requestedRoles.every(role => userRoles.includes(role)) : + requestedRoles.findIndex(role => { + return userRoles.findIndex(i => i.includes(role)) > -1; + }) > -1; } public get authenticated(): boolean { diff --git a/console/src/component-themes.scss b/console/src/component-themes.scss index 4c4b203cde..9e4bc23601 100644 --- a/console/src/component-themes.scss +++ b/console/src/component-themes.scss @@ -1,8 +1,8 @@ -@import './styles/card'; +@import 'src/app/modules/card/card'; @import './styles/table'; @import './styles/sidenav-list'; @import 'src/app/modules/avatar/avatar.component'; -@import './styles/changes'; +@import 'src/app/modules/changes/changes.component'; @import 'src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component'; @import './styles/meta'; @import 'src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-card'; diff --git a/console/src/styles/changes.scss b/console/src/styles/changes.scss deleted file mode 100644 index 9c520417d9..0000000000 --- a/console/src/styles/changes.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import '~@angular/material/theming'; - -@mixin changes-theme($theme) { - $primary: map-get($theme, primary); - $primary-dark: mat-color($primary, A800); - - .change-item-back { - background-color: rgba($primary-dark, 0.93); - transition: background-color .4s ease-in-out; - } -} diff --git a/console/src/styles/sidenav-list.scss b/console/src/styles/sidenav-list.scss index 2c09c690cf..1681a6ffdb 100644 --- a/console/src/styles/sidenav-list.scss +++ b/console/src/styles/sidenav-list.scss @@ -6,9 +6,9 @@ $primary-color: mat-color($primary, 500); $accent-color: mat-color($accent, 500); $primary-dark: mat-color($primary, A900); + $inverse-color: mat-color($primary, A600); $sec-dark: mat-color($primary, A800); - .mat-menu-item { &.show-all { height: 2rem; @@ -30,6 +30,15 @@ color: $primary-color !important; background-color: rgba($color: $primary-color, $alpha: 0.1) !important; } + + .c_label { + .count { + background-color: $primary-color; + padding: 3px 6px; + border-radius: 50vw; + color: white; + } + } } .mat-menu-content, .mat-menu-panel {