mirror of
https://github.com/valitydev/dashboard.git
synced 2024-11-06 10:35:21 +00:00
Add main nav (#308)
This commit is contained in:
parent
47ba5ceb8f
commit
a799eebf5e
@ -1,15 +1,7 @@
|
||||
<div fxLayout fxLayoutGap="16px">
|
||||
<!-- <dsh-action-item iconName="brightness_4" (click)="changeTheme()"></dsh-action-item> -->
|
||||
<dsh-action-item iconName="subject" [dshDropdownTriggerFor]="claimsDropdown"></dsh-action-item>
|
||||
<dsh-action-item iconName="person" [dshDropdownTriggerFor]="userDropdown"></dsh-action-item>
|
||||
</div>
|
||||
|
||||
<dsh-dropdown width="590px" #claimsDropdown="dshDropdown">
|
||||
<ng-template>
|
||||
<dsh-claims (menuItemSelected)="closeDropdown()" (navigatedToAllClaims)="closeDropdown()"></dsh-claims>
|
||||
</ng-template>
|
||||
</dsh-dropdown>
|
||||
|
||||
<dsh-dropdown width="180px" #userDropdown="dshDropdown">
|
||||
<ng-template><dsh-user></dsh-user></ng-template>
|
||||
</dsh-dropdown>
|
||||
|
@ -15,7 +15,6 @@ import { StateNavModule } from '@dsh/components/navigation';
|
||||
import { ClaimsService } from '../../api/claims';
|
||||
import { ActionItemComponent } from './action-item';
|
||||
import { ActionbarComponent } from './actionbar.component';
|
||||
import { ClaimsComponent, ClaimsListComponent, ClaimsListItemComponent } from './claims';
|
||||
import { UserComponent } from './user';
|
||||
|
||||
@NgModule({
|
||||
@ -32,14 +31,7 @@ import { UserComponent } from './user';
|
||||
CommonModule,
|
||||
TranslocoModule,
|
||||
],
|
||||
declarations: [
|
||||
ActionbarComponent,
|
||||
ActionItemComponent,
|
||||
ClaimsListComponent,
|
||||
ClaimsListItemComponent,
|
||||
UserComponent,
|
||||
ClaimsComponent,
|
||||
],
|
||||
declarations: [ActionbarComponent, ActionItemComponent, UserComponent],
|
||||
providers: [ClaimsService],
|
||||
exports: [ActionbarComponent],
|
||||
})
|
||||
|
@ -1,22 +0,0 @@
|
||||
<div fxLayout fxLayoutGap="10px" fxLayoutAlign="space-between center" class="item">
|
||||
<div *transloco="let claimType; read: 'claimType'" fxFlex="60">
|
||||
{{ claimType[type] }}
|
||||
</div>
|
||||
<dsh-status *transloco="let claimStatus; read: 'claimStatus'" fxFlex="40" [color]="statusColor">{{
|
||||
claimStatus[claim.status]
|
||||
}}</dsh-status>
|
||||
<button dsh-icon-button [matMenuTriggerFor]="menu">
|
||||
<mat-icon svgIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<mat-menu #menu="matMenu">
|
||||
<button
|
||||
*transloco="let t; scope: 'actionbar'; read: 'actionbar.claims'"
|
||||
mat-menu-item
|
||||
[routerLink]="['/claim', claim.id]"
|
||||
(click)="menuItemSelected.emit()"
|
||||
>
|
||||
{{ t.details }}
|
||||
</button>
|
||||
</mat-menu>
|
@ -1,5 +0,0 @@
|
||||
$height: 50px;
|
||||
|
||||
.item {
|
||||
min-height: $height;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
} from '@angular/core';
|
||||
|
||||
import { Claim } from '../../../../../api-codegen/claim-management/swagger-codegen';
|
||||
import { StatusColor } from '../../../../../theme-manager';
|
||||
import { claimStatusToColor, ClaimType, getClaimType } from '../../../../../view-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-claims-list-item',
|
||||
templateUrl: 'claims-list-item.component.html',
|
||||
styleUrls: ['claims-list-item.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ClaimsListItemComponent implements OnChanges {
|
||||
@Input() claim: Claim;
|
||||
|
||||
@Output() menuItemSelected = new EventEmitter();
|
||||
|
||||
type: ClaimType;
|
||||
statusColor: StatusColor;
|
||||
|
||||
ngOnChanges({ claim }: SimpleChanges) {
|
||||
if (claim.isFirstChange || claim.currentValue.id !== claim.previousValue.id) {
|
||||
const { changeset, status } = claim.currentValue;
|
||||
this.type = getClaimType(changeset);
|
||||
this.statusColor = claimStatusToColor(status);
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from './claims-list-item.component';
|
@ -1,3 +0,0 @@
|
||||
<div fxLayout="column" fxLayoutGap="10px">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
@ -1,8 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-claims-list',
|
||||
templateUrl: 'claims-list.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ClaimsListComponent {}
|
@ -1,2 +0,0 @@
|
||||
export * from './claims-list.component';
|
||||
export * from './claims-list-item';
|
@ -1,23 +0,0 @@
|
||||
<dsh-dropdown-content
|
||||
*transloco="let t; scope: 'actionbar'; read: 'actionbar.claims'"
|
||||
fxLayout="column"
|
||||
fxLayoutGap="10px"
|
||||
>
|
||||
<h1 class="mat-title">{{ t.title }}</h1>
|
||||
<div class="loading-container" *ngIf="isLoading$ | async" fxLayout fxLayoutAlign="center center">
|
||||
<dsh-spinner></dsh-spinner>
|
||||
</div>
|
||||
<dsh-claims-list>
|
||||
<dsh-claims-list-item
|
||||
*ngFor="let claim of claims$ | async"
|
||||
[claim]="claim"
|
||||
(menuItemSelected)="menuItemSelected.next()"
|
||||
>
|
||||
</dsh-claims-list-item>
|
||||
</dsh-claims-list>
|
||||
<div *ngIf="error$ | async" class="mat-body-1">{{ t.httpError }}</div>
|
||||
<div *ngIf="noClaims$ | async" class="mat-body-1">
|
||||
{{ t.noClaims }}
|
||||
</div>
|
||||
<button dsh-button class="action" (click)="navigateToClaims()">{{ t.goToAll }}</button>
|
||||
</dsh-dropdown-content>
|
@ -1,7 +0,0 @@
|
||||
.action {
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
height: 100px;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ClaimsService } from './claims.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-claims',
|
||||
templateUrl: 'claims.component.html',
|
||||
styleUrls: ['claims.component.scss'],
|
||||
providers: [ClaimsService],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ClaimsComponent {
|
||||
@Output() menuItemSelected = new EventEmitter();
|
||||
|
||||
@Output() navigatedToAllClaims = new EventEmitter();
|
||||
|
||||
claims$ = this.claimsService.claims$;
|
||||
isLoading$ = this.claimsService.isLoading$;
|
||||
error$ = this.claimsService.error$;
|
||||
noClaims$ = this.claimsService.noClaims$;
|
||||
|
||||
constructor(private router: Router, private claimsService: ClaimsService) {}
|
||||
|
||||
navigateToClaims() {
|
||||
this.navigatedToAllClaims.next();
|
||||
this.router.navigate(['claims']);
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { map, pluck, shareReplay } from 'rxjs/operators';
|
||||
|
||||
import { ClaimsService as ClaimsApiService } from '../../../api/claims/claims.service';
|
||||
import { booleanDelay, takeError } from '../../../custom-operators';
|
||||
import { filterViewClaims } from '../../../view-utils';
|
||||
|
||||
@Injectable()
|
||||
export class ClaimsService {
|
||||
claims$ = this.claimsService
|
||||
.searchClaims(5, ['pending', 'review', 'accepted'])
|
||||
.pipe(pluck('result'), map(filterViewClaims), shareReplay(1));
|
||||
noClaims$ = this.claims$.pipe(map((c) => c.length === 0));
|
||||
isLoading$ = this.claims$.pipe(booleanDelay());
|
||||
error$ = this.claims$.pipe(takeError);
|
||||
|
||||
constructor(private claimsService: ClaimsApiService) {}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
export * from './claims.component';
|
||||
export * from './claims-list';
|
59
src/app/container/toolbar/toolbar-links.service.ts
Normal file
59
src/app/container/toolbar/toolbar-links.service.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { distinctUntilChanged, map, shareReplay, startWith } from 'rxjs/operators';
|
||||
|
||||
import { WalletService } from '../../api';
|
||||
import { RouteEnv } from '../../sections/route-env';
|
||||
import { Link } from '../../shared';
|
||||
import { findActivePath } from '../../shared/utils';
|
||||
|
||||
export interface ToolbarLink extends Link {
|
||||
id: string;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
export enum LinkId {
|
||||
main = 'main',
|
||||
payments = 'payments',
|
||||
wallets = 'wallets',
|
||||
claims = 'claims',
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ToolbarLinksService {
|
||||
private url$ = this.router.events.pipe(
|
||||
startWith(null),
|
||||
map(() => this.router.url),
|
||||
distinctUntilChanged(),
|
||||
shareReplay(1)
|
||||
);
|
||||
links$ = this.walletsService.hasWallets$.pipe(
|
||||
map((hasWallets) => this.createLinks(hasWallets)),
|
||||
shareReplay(1)
|
||||
);
|
||||
active$ = combineLatest([this.url$, this.links$]).pipe(
|
||||
map(([url, links]) => findActivePath(url, links)),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(private walletsService: WalletService, private router: Router) {}
|
||||
|
||||
private createLinks(hasWallets: boolean): ToolbarLink[] {
|
||||
return [
|
||||
{ id: LinkId.main, path: '', activateStartPaths: [''] },
|
||||
{
|
||||
id: LinkId.payments,
|
||||
path: `payment-section/env/${RouteEnv.real}/operations/payments`,
|
||||
activateStartPaths: ['/payment-section', '/invoice'],
|
||||
},
|
||||
{
|
||||
id: LinkId.wallets,
|
||||
path: 'wallet-section/wallets',
|
||||
activateStartPaths: ['/wallet-section', '/wallet'],
|
||||
hidden: !hasWallets,
|
||||
},
|
||||
{ id: LinkId.claims, path: 'claims', activateStartPaths: ['/claims', '/claim', '/onboarding'] },
|
||||
];
|
||||
}
|
||||
}
|
@ -1,18 +1,16 @@
|
||||
<div class="toolbar" fxLayout fxLayoutGap="24px" fxLayoutAlign="start center">
|
||||
<dsh-brand fxFlex="168px" [type]="brandType" class="dsh-side-section"></dsh-brand>
|
||||
<!-- <nav *transloco="let t; scope: 'toolbar'; read: 'toolbar.nav'" dsh-tab-nav-bar type="main">
|
||||
<a dsh-tab-link>
|
||||
{{ t.main }}
|
||||
</a>
|
||||
<a dsh-tab-link>
|
||||
{{ t.payments }}
|
||||
</a>
|
||||
<a dsh-tab-link>
|
||||
{{ t.wallets }}
|
||||
</a>
|
||||
<a dsh-tab-link badge="1" [active]="true">
|
||||
{{ t.claims }}
|
||||
</a>
|
||||
</nav> -->
|
||||
<nav
|
||||
*transloco="let t; scope: 'toolbar'; read: 'toolbar.nav'"
|
||||
dsh-tab-nav-bar
|
||||
type="main"
|
||||
[inverted]="inverted$ | async"
|
||||
>
|
||||
<ng-container *ngFor="let link of links$ | async">
|
||||
<a dsh-tab-link *ngIf="!link.hidden" [routerLink]="link.path" [active]="(active$ | async) === link">
|
||||
{{ t[link.id] }}
|
||||
</a>
|
||||
</ng-container>
|
||||
</nav>
|
||||
<dsh-actionbar fxFlex="grow" fxLayoutAlign="end"></dsh-actionbar>
|
||||
</div>
|
||||
|
@ -1,13 +1,22 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { BrandType } from '../brand';
|
||||
import { LinkId, ToolbarLinksService } from './toolbar-links.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-toolbar',
|
||||
templateUrl: './toolbar.component.html',
|
||||
styleUrls: ['./toolbar.component.scss'],
|
||||
providers: [ToolbarLinksService],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ToolbarComponent {
|
||||
@Input() brandType: BrandType = BrandType.normal;
|
||||
|
||||
links$ = this.toolbarLinksService.links$;
|
||||
inverted$ = this.toolbarLinksService.active$.pipe(map((active) => active.id === LinkId.main));
|
||||
active$ = this.toolbarLinksService.active$;
|
||||
|
||||
constructor(private toolbarLinksService: ToolbarLinksService) {}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from './components';
|
||||
export * from './services';
|
||||
export * from './pipes';
|
||||
export * from './models';
|
||||
|
1
src/app/shared/models/index.ts
Normal file
1
src/app/shared/models/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './link';
|
4
src/app/shared/models/link.ts
Normal file
4
src/app/shared/models/link.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface Link {
|
||||
path: string;
|
||||
activateStartPaths?: string[];
|
||||
}
|
27
src/app/shared/utils/find-active-link.ts
Normal file
27
src/app/shared/utils/find-active-link.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Link } from '../models';
|
||||
|
||||
interface ActivePathsCount {
|
||||
count: number;
|
||||
length: number;
|
||||
}
|
||||
|
||||
function countActivePaths(url: string, paths: string[]): ActivePathsCount {
|
||||
return paths.reduce<ActivePathsCount>(
|
||||
({ count, length }, p) =>
|
||||
url.indexOf(p) === 0 ? { count: ++count, length: length + p.length } : { count, length },
|
||||
{ count: 0, length: 0 }
|
||||
);
|
||||
}
|
||||
|
||||
export function findActivePath<L extends Pick<Link, 'activateStartPaths'>>(url: string, links: L[]): L {
|
||||
return links.reduce<[L, ActivePathsCount]>(
|
||||
(max, link) => {
|
||||
const [, { count: maxCount, length: maxLength }] = max;
|
||||
const { count, length } = countActivePaths(url, link.activateStartPaths || []);
|
||||
return !count || maxCount > count || (maxCount === count && maxLength > length)
|
||||
? max
|
||||
: [link, { count, length }];
|
||||
},
|
||||
[null, { count: 0, length: 0 }]
|
||||
)[0];
|
||||
}
|
1
src/app/shared/utils/index.ts
Normal file
1
src/app/shared/utils/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './find-active-link';
|
@ -9,20 +9,25 @@
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.dsh {
|
||||
&-tab-label {
|
||||
color: map-get($foreground, text);
|
||||
}
|
||||
|
||||
&-tab-label-active {
|
||||
color: map-get($primary, 400);
|
||||
}
|
||||
|
||||
&-tab-labels,
|
||||
&-tab-links {
|
||||
border-color: map-get($foreground, divider);
|
||||
}
|
||||
|
||||
&-tab-label {
|
||||
color: map-get($foreground, text);
|
||||
|
||||
&-active {
|
||||
color: map-get($primary, 400);
|
||||
}
|
||||
|
||||
&-disabled {
|
||||
color: map-get($foreground, disabled-text);
|
||||
}
|
||||
}
|
||||
|
||||
&-tab-link-badge {
|
||||
color: map-get($foreground, contrast-text);
|
||||
background-color: mat-color($warn, 300);
|
||||
}
|
||||
|
||||
@ -30,8 +35,13 @@
|
||||
background-color: map-get($primary, 400);
|
||||
}
|
||||
|
||||
&-tab-label-disabled {
|
||||
color: map-get($foreground, disabled-text);
|
||||
&-tab-nav-bar-inverted {
|
||||
.dsh-tab-label {
|
||||
color: map-get($foreground, contrast-text);
|
||||
}
|
||||
.dsh-ink-bar {
|
||||
background-color: map-get($foreground, contrast-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,6 @@ $TAB_LINK_MAIN_BADGE_SIZE: 24px;
|
||||
}
|
||||
|
||||
&-badge {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
margin-left: $TAB_LINK_BADGE_MARGIN;
|
||||
border-radius: 9999px;
|
||||
|
@ -22,6 +22,7 @@ import { CanDisable, HasTabIndex } from '@angular/material/core';
|
||||
import { merge, of as observableOf, Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { coerceBoolean } from '../../../../utils';
|
||||
import { DshInkBarDirective } from '../ink-bar.directive';
|
||||
import { TabType } from '../tab-type';
|
||||
|
||||
@ -38,6 +39,7 @@ export class TabNavComponent implements AfterContentChecked, AfterContentInit, O
|
||||
@Input() type: TabType;
|
||||
|
||||
@HostBinding('class.dsh-tab-nav-bar') tabNavBar = true;
|
||||
@Input() @coerceBoolean @HostBinding('class.dsh-tab-nav-bar-inverted') inverted = false;
|
||||
|
||||
private readonly _onDestroy = new Subject<void>();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user