mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:57:31 +00:00
fix(console): hide granted project navigation if none, cache zitadel permissions, emit refresh on org change, cleanup contributors, styling (#511)
* fix iam member model * fix org member model * fix auth user loading * copytoclipboard directive * directive logs, load bar on init, create user * typo * welcome section, contributor spinner * fix home link * fix stepper flow * show dialog on invalid token * fix app table refresh, pin icons light theme * cleanup contributor * inherit parent color, animations * use localized date pipe everywhere * cmp styles refactor, dont show granted p if none * fix navitem desc, fixed header * change permissions, caching * roles on org emit, use prom instead of hot obs * dont calc 100vh
This commit is contained in:
153
console/src/app/animations.ts
Normal file
153
console/src/app/animations.ts
Normal file
@@ -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<AnimationTriggerMetadata> = [
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]);
|
@@ -1,6 +1,6 @@
|
|||||||
<ng-container *ngIf="(authService.user | async) || {} as user">
|
<ng-container *ngIf="(authService.user | async) || {} as user">
|
||||||
<ng-container *ngIf="((['iam.read','iam.write'] | hasRole)) as iamuser$">
|
<ng-container *ngIf="((['iam.read','iam.write'] | hasRole)) as iamuser$">
|
||||||
<mat-toolbar class="root-header">
|
<mat-toolbar @toolbar class="root-header">
|
||||||
<button aria-label="Toggle sidenav" mat-icon-button (click)="drawer.toggle()">
|
<button aria-label="Toggle sidenav" mat-icon-button (click)="drawer.toggle()">
|
||||||
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
|
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<div class="side-column">
|
<div class="side-column">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<ng-container *ngIf="authService.authenticationChanged | async">
|
<ng-container *ngIf="authService.authenticationChanged | async">
|
||||||
<a class="nav-item" [routerLinkActive]="['active']"
|
<a @navitem class="nav-item" [routerLinkActive]="['active']"
|
||||||
[routerLinkActiveOptions]="{ exact: true }" [routerLink]="['/users/me']">
|
[routerLinkActiveOptions]="{ exact: true }" [routerLink]="['/users/me']">
|
||||||
<i class="icon las la-user-circle"></i>
|
<i class="icon las la-user-circle"></i>
|
||||||
<span class="label">{{ 'MENU.PERSONAL_INFO' | translate }}</span>
|
<span class="label">{{ 'MENU.PERSONAL_INFO' | translate }}</span>
|
||||||
@@ -64,52 +64,65 @@
|
|||||||
<div class="divider">
|
<div class="divider">
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/iam']">
|
<a @navitem class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/iam']">
|
||||||
<i class="icon las la-gem"></i>
|
<i class="icon las la-gem"></i>
|
||||||
<span class="label">{{'MENU.IAM' | translate}}</span>
|
<span class="label">{{'MENU.IAM' | translate}}</span>
|
||||||
</a>
|
</a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['org.read']">
|
<div *ngIf="org" [@navAnimation]="org">
|
||||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/org']">
|
<ng-template appHasRole [appHasRole]="['org.read']">
|
||||||
<i class="icon las la-archway"></i>
|
<a @navitem class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/org']">
|
||||||
<span class="label">{{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}}</span>
|
<i class="icon las la-archway"></i>
|
||||||
</a>
|
<span
|
||||||
</ng-template>
|
class="label">{{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}}</span>
|
||||||
|
</a>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['project.read']">
|
<ng-template appHasRole [appHasRole]="['project.read']">
|
||||||
<div class="divider">
|
<div @navitem class="divider">
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
<span>{{'MENU.PROJECTSSECTION' | translate}}</span>
|
<span>{{'MENU.PROJECTSSECTION' | translate}}</span>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/projects']">
|
<a @navitem class="nav-item" [routerLinkActive]="['active']"
|
||||||
<i class="icon las la-layer-group"></i>
|
[routerLink]="[ '/projects']">
|
||||||
<span class="label">{{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}}
|
<i class="icon las la-layer-group"></i>
|
||||||
{{ 'MENU.PROJECT' | translate }}</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/granted-projects']">
|
<div class="c_label">
|
||||||
<i class="icon las la-layer-group"></i>
|
<span>{{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}}
|
||||||
<span class="label">{{ 'MENU.GRANTEDPROJECT' | translate }}</span>
|
{{'MENU.PROJECT' | translate}} </span>
|
||||||
</a>
|
<span *ngIf="ownedProjectsCount as ownedPCount"
|
||||||
</ng-template>
|
class="count">{{ownedPCount}}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['user.read']">
|
<a @navitem *ngIf="grantedProjectsCount as grantPCount" class="nav-item"
|
||||||
<div class="divider">
|
[routerLinkActive]="['active']" [routerLink]="[ '/granted-projects']">
|
||||||
<div class="line"></div>
|
<i class="icon las la-layer-group"></i>
|
||||||
<span class="label">
|
<div class="c_label">
|
||||||
{{ 'MENU.USERSECTION' | translate }}</span>
|
<span>{{ 'MENU.GRANTEDPROJECT' | translate }}</span>
|
||||||
<div class="line"></div>
|
<span class="count">{{grantPCount}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
</a>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/users/all']"
|
<ng-template appHasRole [appHasRole]="['user.read']">
|
||||||
[routerLinkActiveOptions]="{ exact: true }">
|
<div @navitem class="divider">
|
||||||
<i class="icon las la-users"></i>
|
<div class="line"></div>
|
||||||
<span class="label">{{ 'MENU.USER' | translate }}</span>
|
<span class="label">
|
||||||
</a>
|
{{ 'MENU.USERSECTION' | translate }}</span>
|
||||||
</ng-template>
|
<div class="line"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a @navitem class="nav-item" [routerLinkActive]="['active']"
|
||||||
|
[routerLink]="[ '/users/all']" [routerLinkActiveOptions]="{ exact: true }">
|
||||||
|
<i class="icon las la-users"></i>
|
||||||
|
<span class="label">{{ 'MENU.USER' | translate }}</span>
|
||||||
|
</a>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,7 +130,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</mat-drawer>
|
</mat-drawer>
|
||||||
<mat-drawer-content class="content">
|
<mat-drawer-content class="content">
|
||||||
<div *ngIf="iamuser$ | async" class="admin-line" matTooltip="IAM Administrator">
|
<div @toolbar *ngIf="iamuser$ | async" class="admin-line" matTooltip="IAM Administrator">
|
||||||
<span>{{'MENU.IAMADMIN' | translate}}</span>
|
<span>{{'MENU.IAMADMIN' | translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="router" [@routeAnimations]="prepareRoute(outlet)">
|
<div class="router" [@routeAnimations]="prepareRoute(outlet)">
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
|
|
||||||
.root-header {
|
.root-header {
|
||||||
position: relative;
|
position: fixed;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -74,15 +77,16 @@
|
|||||||
.main-container {
|
.main-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: calc(100vh - 60px);
|
height: 100vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding-top: 60px;
|
||||||
|
|
||||||
.sidenav {
|
.sidenav {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
|
|
||||||
.side-column {
|
.side-column {
|
||||||
height: calc(100vh - 70px);
|
padding-top: 60px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
@@ -120,6 +124,17 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: .9rem;
|
font-size: .9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c_label {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.count {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #00000010;
|
background-color: #00000010;
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { animate, group, query, style, transition, trigger } from '@angular/animations';
|
|
||||||
import { BreakpointObserver } from '@angular/cdk/layout';
|
import { BreakpointObserver } from '@angular/cdk/layout';
|
||||||
import { OverlayContainer } from '@angular/cdk/overlay';
|
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||||
import { ViewportScroller } from '@angular/common';
|
import { ViewportScroller } from '@angular/common';
|
||||||
@@ -11,9 +10,11 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { Observable, of, Subscription } from 'rxjs';
|
import { Observable, of, Subscription } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { accountCard, navAnimations, routeAnimations, toolbarAnimation } from './animations';
|
||||||
import { Org, UserProfileView } from './proto/generated/auth_pb';
|
import { Org, UserProfileView } from './proto/generated/auth_pb';
|
||||||
import { AuthUserService } from './services/auth-user.service';
|
import { AuthUserService } from './services/auth-user.service';
|
||||||
import { AuthService } from './services/auth.service';
|
import { AuthService } from './services/auth.service';
|
||||||
|
import { ProjectService } from './services/project.service';
|
||||||
import { ThemeService } from './services/theme.service';
|
import { ThemeService } from './services/theme.service';
|
||||||
import { ToastService } from './services/toast.service';
|
import { ToastService } from './services/toast.service';
|
||||||
import { UpdateService } from './services/update.service';
|
import { UpdateService } from './services/update.service';
|
||||||
@@ -23,97 +24,10 @@ import { UpdateService } from './services/update.service';
|
|||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.scss'],
|
styleUrls: ['./app.component.scss'],
|
||||||
animations: [
|
animations: [
|
||||||
trigger('accounts', [
|
toolbarAnimation,
|
||||||
transition(':enter', [
|
...navAnimations,
|
||||||
style({
|
accountCard,
|
||||||
transform: 'scale(.9) translateY(-10%)',
|
routeAnimations,
|
||||||
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,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnDestroy {
|
export class AppComponent implements OnDestroy {
|
||||||
@@ -135,6 +49,10 @@ export class AppComponent implements OnDestroy {
|
|||||||
public orgLoading: boolean = false;
|
public orgLoading: boolean = false;
|
||||||
|
|
||||||
public showProjectSection: boolean = false;
|
public showProjectSection: boolean = false;
|
||||||
|
|
||||||
|
public grantedProjectsCount: number = 0;
|
||||||
|
public ownedProjectsCount: number = 0;
|
||||||
|
|
||||||
private authSub: Subscription = new Subscription();
|
private authSub: Subscription = new Subscription();
|
||||||
private orgSub: Subscription = new Subscription();
|
private orgSub: Subscription = new Subscription();
|
||||||
|
|
||||||
@@ -147,6 +65,7 @@ export class AppComponent implements OnDestroy {
|
|||||||
public overlayContainer: OverlayContainer,
|
public overlayContainer: OverlayContainer,
|
||||||
private themeService: ThemeService,
|
private themeService: ThemeService,
|
||||||
public userService: AuthUserService,
|
public userService: AuthUserService,
|
||||||
|
private projectService: ProjectService,
|
||||||
public matIconRegistry: MatIconRegistry,
|
public matIconRegistry: MatIconRegistry,
|
||||||
public domSanitizer: DomSanitizer,
|
public domSanitizer: DomSanitizer,
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
@@ -218,9 +137,12 @@ export class AppComponent implements OnDestroy {
|
|||||||
'mdi_pin',
|
'mdi_pin',
|
||||||
this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/pin.svg'),
|
this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/pin.svg'),
|
||||||
);
|
);
|
||||||
|
this.getProjectCount();
|
||||||
|
|
||||||
this.orgSub = this.authService.activeOrgChanged.subscribe(org => {
|
this.orgSub = this.authService.activeOrgChanged.subscribe(org => {
|
||||||
this.org = org;
|
this.org = org;
|
||||||
|
|
||||||
|
this.getProjectCount();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.authSub = this.authService.authenticationChanged.subscribe((authenticated) => {
|
this.authSub = this.authService.authenticationChanged.subscribe((authenticated) => {
|
||||||
@@ -289,5 +211,15 @@ export class AppComponent implements OnDestroy {
|
|||||||
this.authService.setActiveOrg(org);
|
this.authService.setActiveOrg(org);
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getProjectCount(): Promise<any> {
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
|
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({
|
@Directive({
|
||||||
@@ -10,7 +10,7 @@ export class HasRoleDirective {
|
|||||||
private hasView: boolean = false;
|
private hasView: boolean = false;
|
||||||
@Input() public set appHasRole(roles: string[]) {
|
@Input() public set appHasRole(roles: string[]) {
|
||||||
if (roles && roles.length > 0) {
|
if (roles && roles.length > 0) {
|
||||||
this.userService.isAllowed(roles).subscribe(isAllowed => {
|
this.authService.isAllowed(roles).subscribe(isAllowed => {
|
||||||
if (isAllowed && !this.hasView) {
|
if (isAllowed && !this.hasView) {
|
||||||
this.viewContainerRef.clear();
|
this.viewContainerRef.clear();
|
||||||
this.viewContainerRef.createEmbeddedView(this.templateRef);
|
this.viewContainerRef.createEmbeddedView(this.templateRef);
|
||||||
@@ -23,7 +23,7 @@ export class HasRoleDirective {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private userService: AuthUserService,
|
private authService: AuthService,
|
||||||
protected templateRef: TemplateRef<any>,
|
protected templateRef: TemplateRef<any>,
|
||||||
protected viewContainerRef: ViewContainerRef,
|
protected viewContainerRef: ViewContainerRef,
|
||||||
) { }
|
) { }
|
||||||
|
@@ -2,19 +2,19 @@ import { Injectable } from '@angular/core';
|
|||||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { AuthUserService } from '../services/auth-user.service';
|
import { AuthService } from '../services/auth.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class RoleGuard implements CanActivate {
|
export class RoleGuard implements CanActivate {
|
||||||
|
|
||||||
constructor(private userService: AuthUserService) { }
|
constructor(private authService: AuthService) { }
|
||||||
|
|
||||||
public canActivate(
|
public canActivate(
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot,
|
state: RouterStateSnapshot,
|
||||||
): Observable<boolean> {
|
): Observable<boolean> {
|
||||||
return this.userService.isAllowed(route.data['roles'], true);
|
return this.authService.isAllowed(route.data['roles'], true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,9 +15,5 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
outline: none;
|
outline: none;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
// &.active:hover {
|
|
||||||
// border: 2px solid #8795a1;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,9 +1,6 @@
|
|||||||
@import '~@angular/material/theming';
|
@import '~@angular/material/theming';
|
||||||
|
|
||||||
@mixin card-theme($theme) {
|
@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: map-get($theme, primary);
|
||||||
$primary-color: mat-color($primary, 500);
|
$primary-color: mat-color($primary, 500);
|
||||||
$primary-dark: mat-color($primary, A800);
|
$primary-dark: mat-color($primary, A800);
|
||||||
@@ -17,7 +14,6 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
outline: none;
|
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 {
|
.selection-icon {
|
||||||
opacity: 0;
|
opacity: 0;
|
@@ -1,7 +1,8 @@
|
|||||||
<span class="header">{{ 'CHANGES.LISTTITLE' | translate }}</span>
|
<span class="header">{{ 'CHANGES.LISTTITLE' | translate }}</span>
|
||||||
|
|
||||||
<div class="scroll-container" appScrollable (scrollPosition)="scrollHandler($event)">
|
<div class="scroll-container" appScrollable (scrollPosition)="scrollHandler($event)"
|
||||||
<li class="item change-item-back" *ngFor="let event of data | async">
|
[@cardAnimation]="data && (data | async)?.length">
|
||||||
|
<li class="item change-item-back" *ngFor="let event of data | async" @animate>
|
||||||
<span class="seq">
|
<span class="seq">
|
||||||
{{event.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
|
{{event.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}
|
||||||
</span>
|
</span>
|
||||||
|
@@ -1,3 +1,6 @@
|
|||||||
|
@import '~@angular/material/theming';
|
||||||
|
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
@@ -5,42 +8,53 @@
|
|||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-container {
|
@mixin changes-theme($theme) {
|
||||||
max-height: 60vh;
|
|
||||||
overflow-y: scroll;
|
|
||||||
|
|
||||||
.item {
|
.scroll-container {
|
||||||
box-sizing: border-box;
|
max-height: 60vh;
|
||||||
padding: .5rem;
|
overflow-y: scroll;
|
||||||
margin: .25rem 0;
|
|
||||||
border-radius: .5rem;
|
.item {
|
||||||
display: flex;
|
box-sizing: border-box;
|
||||||
flex-direction: column;
|
padding: .5rem;
|
||||||
.editor {
|
margin: .25rem 0;
|
||||||
color: #8795a1;
|
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;
|
font-size: 12px;
|
||||||
align-self: flex-end;
|
|
||||||
}
|
|
||||||
.seq {
|
|
||||||
color: #8795a1;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { animate, animateChild, keyframes, query, stagger, style, transition, trigger } from '@angular/animations';
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||||
import { catchError, scan, take, tap } from 'rxjs/operators';
|
import { catchError, scan, take, tap } from 'rxjs/operators';
|
||||||
@@ -16,6 +17,22 @@ export enum ChangeType {
|
|||||||
selector: 'app-changes',
|
selector: 'app-changes',
|
||||||
templateUrl: './changes.component.html',
|
templateUrl: './changes.component.html',
|
||||||
styleUrls: ['./changes.component.scss'],
|
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 {
|
export class ChangesComponent implements OnInit {
|
||||||
@Input() public changeType: ChangeType = ChangeType.USER;
|
@Input() public changeType: ChangeType = ChangeType.USER;
|
||||||
|
@@ -2,12 +2,12 @@
|
|||||||
<span class="header">{{ title }}</span>
|
<span class="header">{{ title }}</span>
|
||||||
<span class="sub-header">{{ description }}</span>
|
<span class="sub-header">{{ description }}</span>
|
||||||
<div class="people">
|
<div class="people">
|
||||||
<div class="img-list">
|
<div class="img-list" [@cardAnimation]="totalResult">
|
||||||
<mat-spinner diameter="20" *ngIf="loading"></mat-spinner>
|
<mat-spinner diameter="20" *ngIf="loading"></mat-spinner>
|
||||||
|
|
||||||
<ng-container *ngIf="totalResult < 10; else compact">
|
<ng-container *ngIf="totalResult < 10; else compact">
|
||||||
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
||||||
<div (click)="emitShowDetail()" class="avatar-circle"
|
<div @animate (click)="emitShowDetail()" class="avatar-circle"
|
||||||
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}"
|
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}"
|
||||||
[ngStyle]="{'z-index': 100 - i}">
|
[ngStyle]="{'z-index': 100 - i}">
|
||||||
<app-avatar *ngIf="member && (member.displayName || (member.firstName && member.lastName))"
|
<app-avatar *ngIf="member && (member.displayName || (member.firstName && member.lastName))"
|
||||||
|
@@ -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 { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
@@ -7,17 +7,18 @@ import { BehaviorSubject } from 'rxjs';
|
|||||||
templateUrl: './contributors.component.html',
|
templateUrl: './contributors.component.html',
|
||||||
styleUrls: ['./contributors.component.scss'],
|
styleUrls: ['./contributors.component.scss'],
|
||||||
animations: [
|
animations: [
|
||||||
trigger('list', [
|
trigger('cardAnimation', [
|
||||||
transition(':enter', [
|
transition('* => *', [
|
||||||
query('@animate',
|
query('@animate', stagger('40ms', animateChild()), { optional: true }),
|
||||||
stagger(80, animateChild()),
|
|
||||||
),
|
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
trigger('animate', [
|
trigger('animate', [
|
||||||
transition(':enter', [
|
transition(':enter', [
|
||||||
style({ opacity: 0, transform: 'translateX(100%)' }),
|
animate('.2s ease-in', keyframes([
|
||||||
animate('100ms', style({ opacity: 1, transform: 'translateX(0)' })),
|
style({ opacity: 0, offset: 0 }),
|
||||||
|
style({ opacity: .5, transform: 'scale(1.05)', offset: 0.3 }),
|
||||||
|
style({ opacity: 1, transform: 'scale(1)', offset: 1 }),
|
||||||
|
])),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
|
@@ -51,7 +51,7 @@
|
|||||||
<ng-container matColumnDef="creationDate">
|
<ng-container matColumnDef="creationDate">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.CREATIONDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.CREATIONDATE' | translate }} </th>
|
||||||
<td (click)="openDetailDialog(role)" mat-cell *matCellDef="let role">
|
<td (click)="openDetailDialog(role)" mat-cell *matCellDef="let role">
|
||||||
<span>{{role.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }}</span>
|
<span>{{role.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.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 { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||||
|
|
||||||
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
|
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
|
||||||
@@ -46,6 +47,7 @@ import { ProjectRolesComponent } from './project-roles.component';
|
|||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
TimestampToDatePipeModule,
|
TimestampToDatePipeModule,
|
||||||
RefreshTableModule,
|
RefreshTableModule,
|
||||||
|
LocalizedDatePipeModule,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
ProjectRolesComponent,
|
ProjectRolesComponent,
|
||||||
|
@@ -51,13 +51,13 @@
|
|||||||
<ng-container matColumnDef="creationDate">
|
<ng-container matColumnDef="creationDate">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th>
|
||||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||||
{{grant.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
|
{{grant.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="changeDate">
|
<ng-container matColumnDef="changeDate">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
|
||||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||||
{{grant.changeDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
|
{{grant.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="roleNamesList">
|
<ng-container matColumnDef="roleNamesList">
|
||||||
|
@@ -14,6 +14,7 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.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 { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||||
|
|
||||||
import { AvatarModule } from '../avatar/avatar.module';
|
import { AvatarModule } from '../avatar/avatar.module';
|
||||||
@@ -43,6 +44,7 @@ import { UserGrantsComponent } from './user-grants.component';
|
|||||||
HasRolePipeModule,
|
HasRolePipeModule,
|
||||||
TimestampToDatePipeModule,
|
TimestampToDatePipeModule,
|
||||||
RefreshTableModule,
|
RefreshTableModule,
|
||||||
|
LocalizedDatePipeModule,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
UserGrantsComponent,
|
UserGrantsComponent,
|
||||||
|
@@ -14,12 +14,12 @@
|
|||||||
<div class="text-part">
|
<div class="text-part">
|
||||||
<span *ngIf="item.changeDate" class="top">{{'PROJECT.PAGES.LASTMODIFIED' | translate}}
|
<span *ngIf="item.changeDate" class="top">{{'PROJECT.PAGES.LASTMODIFIED' | translate}}
|
||||||
{{
|
{{
|
||||||
item.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'
|
item.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="name" *ngIf="item.projectName">{{ item.projectName }}</span>
|
<span class="name" *ngIf="item.projectName">{{ item.projectName }}</span>
|
||||||
<span class="description" *ngIf="item.resourceOwnerName">{{item.resourceOwnerName}}</span>
|
<span class="description" *ngIf="item.resourceOwnerName">{{item.resourceOwnerName}}</span>
|
||||||
<span *ngIf="item.changeDate" class="created">{{'PROJECT.PAGES.CREATEDON' | translate}}
|
<span *ngIf="item.changeDate" class="created">{{'PROJECT.PAGES.CREATEDON' | translate}}
|
||||||
{{ item.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm' }}</span>
|
{{ item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
</div>
|
</div>
|
||||||
@@ -41,12 +41,12 @@
|
|||||||
<div class="text-part">
|
<div class="text-part">
|
||||||
<span *ngIf="item.changeDate" class="top">{{'PROJECT.PAGES.LASTMODIFIED' | translate}}
|
<span *ngIf="item.changeDate" class="top">{{'PROJECT.PAGES.LASTMODIFIED' | translate}}
|
||||||
{{
|
{{
|
||||||
item.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'
|
item.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="name" *ngIf="item.projectName">{{ item.projectName }}</span>
|
<span class="name" *ngIf="item.projectName">{{ item.projectName }}</span>
|
||||||
<span class="description" *ngIf="item.resourceOwnerName">{{item.resourceOwnerName}}</span>
|
<span class="description" *ngIf="item.resourceOwnerName">{{item.resourceOwnerName}}</span>
|
||||||
<span *ngIf="item.changeDate" class="created">{{'PROJECT.PAGES.CREATEDON' | translate}}
|
<span *ngIf="item.changeDate" class="created">{{'PROJECT.PAGES.CREATEDON' | translate}}
|
||||||
{{ item.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm' }}</span>
|
{{ item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
</div>
|
</div>
|
||||||
|
@@ -72,7 +72,7 @@
|
|||||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let project">
|
<td mat-cell *matCellDef="let project">
|
||||||
<span
|
<span
|
||||||
*ngIf="project.creationDate">{{project.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}}</span>
|
*ngIf="project.creationDate">{{project.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}</span>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CHANGEDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CHANGEDATE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let project">
|
<td mat-cell *matCellDef="let project">
|
||||||
<span
|
<span
|
||||||
*ngIf="project.changeDate">{{project.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}}</span>
|
*ngIf="project.changeDate">{{project.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}</span>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@@ -25,6 +25,7 @@ import { ProjectRolesModule } from 'src/app/modules/project-roles/project-roles.
|
|||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
||||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.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 { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||||
|
|
||||||
import { GrantedProjectDetailComponent } from './granted-project-detail/granted-project-detail.component';
|
import { GrantedProjectDetailComponent } from './granted-project-detail/granted-project-detail.component';
|
||||||
@@ -70,6 +71,7 @@ import { GrantedProjectsComponent } from './granted-projects.component';
|
|||||||
TranslateModule,
|
TranslateModule,
|
||||||
TimestampToDatePipeModule,
|
TimestampToDatePipeModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
LocalizedDatePipeModule,
|
||||||
MemberCreateDialogModule,
|
MemberCreateDialogModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
@@ -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 { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
||||||
import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module';
|
import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module';
|
||||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.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 { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||||
|
|
||||||
import { ApplicationGridComponent } from './application-grid/application-grid.component';
|
import { ApplicationGridComponent } from './application-grid/application-grid.component';
|
||||||
@@ -65,6 +66,7 @@ import { ProjectGrantsComponent } from './project-grants/project-grants.componen
|
|||||||
MetaLayoutModule,
|
MetaLayoutModule,
|
||||||
RefreshTableModule,
|
RefreshTableModule,
|
||||||
MemberCreateDialogModule,
|
MemberCreateDialogModule,
|
||||||
|
LocalizedDatePipeModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class OwnedProjectDetailModule { }
|
export class OwnedProjectDetailModule { }
|
||||||
|
@@ -43,14 +43,14 @@
|
|||||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th>
|
||||||
<td [routerLink]="['/projects',grant.projectId,'grant', grant.id]" class="pointer" mat-cell
|
<td [routerLink]="['/projects',grant.projectId,'grant', grant.id]" class="pointer" mat-cell
|
||||||
*matCellDef="let grant">
|
*matCellDef="let grant">
|
||||||
{{grant.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
|
{{grant.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} </td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="changeDate">
|
<ng-container matColumnDef="changeDate">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
|
||||||
<td [routerLink]="['/projects',grant.projectId,'grant', grant.id]" class="pointer" mat-cell
|
<td [routerLink]="['/projects',grant.projectId,'grant', grant.id]" class="pointer" mat-cell
|
||||||
*matCellDef="let grant">
|
*matCellDef="let grant">
|
||||||
{{grant.changeDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
|
{{grant.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} </td>
|
||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@@ -15,13 +15,13 @@
|
|||||||
<div class="text-part">
|
<div class="text-part">
|
||||||
<span *ngIf="item.changeDate" class="top">{{'PROJECT.PAGES.LASTMODIFIED' | translate}}
|
<span *ngIf="item.changeDate" class="top">{{'PROJECT.PAGES.LASTMODIFIED' | translate}}
|
||||||
{{
|
{{
|
||||||
item.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'
|
item.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="name" *ngIf="item.name">{{ item.name }}</span>
|
<span class="name" *ngIf="item.name">{{ item.name }}</span>
|
||||||
|
|
||||||
<span *ngIf="item.changeDate" class="created">{{'PROJECT.PAGES.CREATEDON' | translate}}
|
<span *ngIf="item.changeDate" class="created">{{'PROJECT.PAGES.CREATEDON' | translate}}
|
||||||
{{
|
{{
|
||||||
item.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'
|
item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
@@ -44,13 +44,13 @@
|
|||||||
<div class="text-part">
|
<div class="text-part">
|
||||||
<span *ngIf="item.changeDate" class="top">{{'PROJECT.PAGES.LASTMODIFIED' | translate}}
|
<span *ngIf="item.changeDate" class="top">{{'PROJECT.PAGES.LASTMODIFIED' | translate}}
|
||||||
{{
|
{{
|
||||||
item.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'
|
item.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="name" *ngIf="item.name">{{ item.name }}</span>
|
<span class="name" *ngIf="item.name">{{ item.name }}</span>
|
||||||
|
|
||||||
<span *ngIf="item.changeDate" class="created">{{'PROJECT.PAGES.CREATEDON' | translate}}
|
<span *ngIf="item.changeDate" class="created">{{'PROJECT.PAGES.CREATEDON' | translate}}
|
||||||
{{
|
{{
|
||||||
item.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'
|
item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
|
@@ -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 { SelectionModel } from '@angular/cdk/collections';
|
||||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
@@ -10,21 +10,25 @@ import { AuthService } from 'src/app/services/auth.service';
|
|||||||
templateUrl: './owned-project-grid.component.html',
|
templateUrl: './owned-project-grid.component.html',
|
||||||
styleUrls: ['./owned-project-grid.component.scss'],
|
styleUrls: ['./owned-project-grid.component.scss'],
|
||||||
animations: [
|
animations: [
|
||||||
trigger('list', [
|
trigger('cardAnimation', [
|
||||||
transition(':enter', [
|
transition('* => *', [
|
||||||
query('@animate',
|
query('@animate', stagger('100ms', animateChild()), { optional: true }),
|
||||||
stagger(100, animateChild()),
|
|
||||||
),
|
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
trigger('animate', [
|
trigger('animate', [
|
||||||
transition(':enter', [
|
transition(':enter', [
|
||||||
style({ opacity: 0, transform: 'translateY(-100%)' }),
|
animate('.2s ease-in', keyframes([
|
||||||
animate('100ms', style({ opacity: 1, transform: 'translateY(0)' })),
|
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', [
|
transition(':leave', [
|
||||||
style({ opacity: 1, transform: 'translateY(0)' }),
|
animate('.2s ease-out', keyframes([
|
||||||
animate('100ms', style({ opacity: 0, transform: 'translateY(100%)' })),
|
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 }),
|
||||||
|
])),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
|
@@ -61,7 +61,7 @@
|
|||||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let project">
|
<td mat-cell *matCellDef="let project">
|
||||||
<span
|
<span
|
||||||
*ngIf="project.creationDate">{{project.creationDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}}</span>
|
*ngIf="project.creationDate">{{project.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}</span>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CHANGEDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CHANGEDATE' | translate }} </th>
|
||||||
<td mat-cell *matCellDef="let project">
|
<td mat-cell *matCellDef="let project">
|
||||||
<span
|
<span
|
||||||
*ngIf="project.changeDate">{{project.changeDate | timestampToDate | date: 'EEE dd. MMM, HH:mm'}}</span>
|
*ngIf="project.changeDate">{{project.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}</span>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@@ -88,7 +88,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
|||||||
this.projectService.SearchProjects(limit, offset).then(res => {
|
this.projectService.SearchProjects(limit, offset).then(res => {
|
||||||
this.ownedProjectList = res.toObject().resultList;
|
this.ownedProjectList = res.toObject().resultList;
|
||||||
this.totalResult = res.toObject().totalResult;
|
this.totalResult = res.toObject().totalResult;
|
||||||
if (this.totalResult > 5) {
|
if (this.totalResult > 10) {
|
||||||
this.grid = false;
|
this.grid = false;
|
||||||
}
|
}
|
||||||
this.dataSource.data = this.ownedProjectList;
|
this.dataSource.data = this.ownedProjectList;
|
||||||
|
@@ -20,6 +20,7 @@ import { CardModule } from 'src/app/modules/card/card.module';
|
|||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
||||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.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 { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||||
|
|
||||||
import { OwnedProjectGridComponent } from './owned-project-list/owned-project-grid/owned-project-grid.component';
|
import { OwnedProjectGridComponent } from './owned-project-list/owned-project-grid/owned-project-grid.component';
|
||||||
@@ -58,6 +59,7 @@ import { OwnedProjectsComponent } from './owned-projects.component';
|
|||||||
MatSortModule,
|
MatSortModule,
|
||||||
HasRolePipeModule,
|
HasRolePipeModule,
|
||||||
TimestampToDatePipeModule,
|
TimestampToDatePipeModule,
|
||||||
|
LocalizedDatePipeModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
$primary: map-get($theme, primary);
|
$primary: map-get($theme, primary);
|
||||||
$primary-dark: mat-color($primary, A800);
|
$primary-dark: mat-color($primary, A800);
|
||||||
|
|
||||||
.theme-conent, .theme-app , .crescent {
|
.theme-conent, .crescent {
|
||||||
background-color: $primary-dark;
|
background-color: $primary-dark;
|
||||||
transition: background-color .4s cubic-bezier(0.645, 0.045, 0.355, 1);
|
transition: background-color .4s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||||
}
|
}
|
||||||
|
@@ -102,8 +102,8 @@ label {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[type="checkbox"]:checked + .theme-app{
|
[type="checkbox"]:checked + .theme-app{
|
||||||
background-color: $dark-background;
|
background-color: inherit;
|
||||||
color: $light-background;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
[type="checkbox"]:checked + .theme-app .crescent{
|
[type="checkbox"]:checked + .theme-app .crescent{
|
||||||
|
@@ -38,10 +38,8 @@
|
|||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['user.read', 'user.read:'+user?.id]">
|
<ng-template appHasRole [appHasRole]="['user.read', 'user.read:'+user?.id]">
|
||||||
<app-card title="{{ 'USER.PROFILE.TITLE' | translate }}">
|
<app-card title="{{ 'USER.PROFILE.TITLE' | translate }}">
|
||||||
<app-detail-form
|
<app-detail-form [disabled]="(['user.write:' + user?.id, 'user.write'] | hasRole | async) == false"
|
||||||
*ngIf="((authUserService.isAllowed(['user.write:' + user?.id, 'user.write']) | async) || false) as canwrite"
|
[genders]="genders" [languages]="languages" [profile]="user" (submitData)="saveProfile($event)">
|
||||||
[disabled]="canwrite" [genders]="genders" [languages]="languages" [profile]="user"
|
|
||||||
(submitData)="saveProfile($event)">
|
|
||||||
</app-detail-form>
|
</app-detail-form>
|
||||||
</app-card>
|
</app-card>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@@ -44,7 +44,6 @@ export class UserMfaComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
public getOTP(): void {
|
public getOTP(): void {
|
||||||
this.mgmtUserService.getUserMfas(this.user.id).then(mfas => {
|
this.mgmtUserService.getUserMfas(this.user.id).then(mfas => {
|
||||||
console.log(mfas.toObject().mfasList);
|
|
||||||
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
||||||
this.dataSource.sort = this.sort;
|
this.dataSource.sort = this.sort;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { AuthUserService } from '../services/auth-user.service';
|
import { AuthService } from '../services/auth.service';
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'hasRole',
|
name: 'hasRole',
|
||||||
})
|
})
|
||||||
export class HasRolePipe implements PipeTransform {
|
export class HasRolePipe implements PipeTransform {
|
||||||
|
|
||||||
constructor(private authUserService: AuthUserService) { }
|
constructor(private authService: AuthService) { }
|
||||||
|
|
||||||
public transform(values: string[], each: boolean = false): Observable<boolean> {
|
public transform(values: string[], each: boolean = false): Observable<boolean> {
|
||||||
return this.authUserService.isAllowed(values, each);
|
return this.authService.isAllowed(values, each);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
|
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
|
||||||
import { Metadata } from 'grpc-web';
|
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 { AuthServicePromiseClient } from '../proto/generated/auth_grpc_web_pb';
|
||||||
import {
|
import {
|
||||||
@@ -11,6 +9,7 @@ import {
|
|||||||
Gender,
|
Gender,
|
||||||
MfaOtpResponse,
|
MfaOtpResponse,
|
||||||
MultiFactors,
|
MultiFactors,
|
||||||
|
MyPermissions,
|
||||||
MyProjectOrgSearchQuery,
|
MyProjectOrgSearchQuery,
|
||||||
MyProjectOrgSearchRequest,
|
MyProjectOrgSearchRequest,
|
||||||
MyProjectOrgSearchResponse,
|
MyProjectOrgSearchResponse,
|
||||||
@@ -38,8 +37,6 @@ import { GrpcService, RequestFactory, ResponseMapper } from './grpc.service';
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class AuthUserService {
|
export class AuthUserService {
|
||||||
private _roleCache: string[] = [];
|
|
||||||
|
|
||||||
constructor(private readonly grpcClient: GrpcService,
|
constructor(private readonly grpcClient: GrpcService,
|
||||||
private grpcBackendService: GrpcBackendService,
|
private grpcBackendService: GrpcBackendService,
|
||||||
) { }
|
) { }
|
||||||
@@ -75,7 +72,6 @@ export class AuthUserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async GetMyUser(): Promise<UserView> {
|
public async GetMyUser(): Promise<UserView> {
|
||||||
return await this.request(
|
return await this.request(
|
||||||
c => c.getMyUser,
|
c => c.getMyUser,
|
||||||
@@ -175,7 +171,7 @@ export class AuthUserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getMyzitadelPermissions(): Promise<any> {
|
public async GetMyzitadelPermissions(): Promise<MyPermissions> {
|
||||||
return await this.request(
|
return await this.request(
|
||||||
c => c.getMyZitadelPermissions,
|
c => c.getMyZitadelPermissions,
|
||||||
new Empty(),
|
new Empty(),
|
||||||
@@ -183,19 +179,6 @@ export class AuthUserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetMyzitadelPermissions(): Observable<any> {
|
|
||||||
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<UserPhone> {
|
public async GetMyUserPhone(): Promise<UserPhone> {
|
||||||
// return this.grpcClient.auth.getMyUserPhone(new Empty());
|
// return this.grpcClient.auth.getMyUserPhone(new Empty());
|
||||||
return await this.request(
|
return await this.request(
|
||||||
@@ -312,31 +295,4 @@ export class AuthUserService {
|
|||||||
f => f,
|
f => f,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public isAllowed(roles: string[], each: boolean = false): Observable<boolean> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
|
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
|
||||||
import { BehaviorSubject, from, merge, Observable, of, Subject } from 'rxjs';
|
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 { Org, UserProfileView } from '../proto/generated/auth_pb';
|
||||||
import { AuthUserService } from './auth-user.service';
|
import { AuthUserService } from './auth-user.service';
|
||||||
@@ -22,6 +22,8 @@ export class AuthService {
|
|||||||
boolean
|
boolean
|
||||||
> = new BehaviorSubject(this.authenticated);
|
> = new BehaviorSubject(this.authenticated);
|
||||||
|
|
||||||
|
private zitadelPermissions: BehaviorSubject<string[]> = new BehaviorSubject(['user.resourceowner']);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private grpcService: GrpcService,
|
private grpcService: GrpcService,
|
||||||
private config: AuthConfig,
|
private config: AuthConfig,
|
||||||
@@ -44,9 +46,50 @@ export class AuthService {
|
|||||||
).pipe(
|
).pipe(
|
||||||
take(1),
|
take(1),
|
||||||
mergeMap(token => {
|
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<boolean> {
|
||||||
|
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 {
|
public get authenticated(): boolean {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
@import './styles/card';
|
@import 'src/app/modules/card/card';
|
||||||
@import './styles/table';
|
@import './styles/table';
|
||||||
@import './styles/sidenav-list';
|
@import './styles/sidenav-list';
|
||||||
@import 'src/app/modules/avatar/avatar.component';
|
@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 'src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component';
|
||||||
@import './styles/meta';
|
@import './styles/meta';
|
||||||
@import 'src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-card';
|
@import 'src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-card';
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,9 +6,9 @@
|
|||||||
$primary-color: mat-color($primary, 500);
|
$primary-color: mat-color($primary, 500);
|
||||||
$accent-color: mat-color($accent, 500);
|
$accent-color: mat-color($accent, 500);
|
||||||
$primary-dark: mat-color($primary, A900);
|
$primary-dark: mat-color($primary, A900);
|
||||||
|
$inverse-color: mat-color($primary, A600);
|
||||||
$sec-dark: mat-color($primary, A800);
|
$sec-dark: mat-color($primary, A800);
|
||||||
|
|
||||||
|
|
||||||
.mat-menu-item {
|
.mat-menu-item {
|
||||||
&.show-all {
|
&.show-all {
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
@@ -30,6 +30,15 @@
|
|||||||
color: $primary-color !important;
|
color: $primary-color !important;
|
||||||
background-color: rgba($color: $primary-color, $alpha: 0.1) !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 {
|
.mat-menu-content, .mat-menu-panel {
|
||||||
|
Reference in New Issue
Block a user