Pull request #392: RED-5085: Skeleton loading

Merge in RED/ui from RED-5085 to master

* commit '2dc62c9fb1902485593a5babc7582b4425bc0690':
  RED-5085: Update common-ui
  RED-5085: Dossier right container skeleton & dark mode
  RED-5085: Dossier skeleton WIP
  RED-5085: Done skeleton loading on dashboard
  RED-5085: Skeleton loader WIP (Dashboard)
This commit is contained in:
Adina Teudan 2022-12-21 12:40:56 +01:00
commit 899a906298
21 changed files with 432 additions and 6 deletions

View File

@ -48,6 +48,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
allow: [ROLES.dossierAttributes.read, ROLES.dossierAttributes.readConfig],
redirectTo: '/auth-error',
},
skeleton: 'dossier',
},
loadChildren: () => import('./modules/dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule),
},
@ -119,9 +120,9 @@ const routes: IqserRoutes = [
{
path: 'dashboard',
loadChildren: () => import('./modules/dashboard/dashboard.module').then(m => m.DashboardModule),
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard, DossierTemplatesGuard, DashboardGuard],
routeGuards: [IqserAuthGuard, RedRoleGuard, IqserPermissionsGuard, DossierTemplatesGuard, DashboardGuard],
permissions: {
allow: [
ROLES.any,
@ -140,6 +141,7 @@ const routes: IqserRoutes = [
[DEFAULT_REDIRECT_KEY]: '/auth-error',
},
},
skeleton: 'dashboard',
},
},
{

View File

@ -5,3 +5,13 @@
<iqser-full-page-loading-indicator></iqser-full-page-loading-indicator>
<iqser-connection-status></iqser-connection-status>
<iqser-full-page-error></iqser-full-page-error>
<iqser-skeleton [templates]="{ dashboard: dashboardSkeleton, dossier: dossierSkeleton }"></iqser-skeleton>
<ng-template #dashboardSkeleton>
<redaction-dashboard-skeleton></redaction-dashboard-skeleton>
</ng-template>
<ng-template #dossierSkeleton>
<redaction-dossier-skeleton></redaction-dossier-skeleton>
</ng-template>

View File

@ -14,6 +14,7 @@ import {
IqserLoadingModule,
IqserPermissionsModule,
IqserPermissionsService,
IqserSharedModule,
IqserTranslateModule,
IqserUsersModule,
LanguageService,
@ -27,6 +28,7 @@ import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '@environments/environment';
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
import { NotificationsComponent } from '@components/notifications/notifications.component';
import { DashboardSkeletonComponent } from '@components/skeleton/dashboard-skeleton/dashboard-skeleton.component';
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
import { AppRoutingModule } from './app-routing.module';
import { SharedModule } from '@shared/shared.module';
@ -59,10 +61,25 @@ import { LicenseService } from '@services/license.service';
import { TenantIdInterceptor } from '@utils/tenant-id-interceptor';
import { UI_CACHES } from '@utils/constants';
import { RedRoleGuard } from '@users/red-role.guard';
import { SkeletonTopBarComponent } from '@components/skeleton/skeleton-top-bar/skeleton-top-bar.component';
import { DossierSkeletonComponent } from '@components/skeleton/dossier-skeleton/dossier-skeleton.component';
import { SkeletonStatsComponent } from '@components/skeleton/skeleton-stats/skeleton-stats.component';
const screens = [BaseScreenComponent, DownloadsListScreenComponent];
const components = [AppComponent, AuthErrorComponent, NotificationsComponent, SpotlightSearchComponent, BreadcrumbsComponent, ...screens];
const components = [
AppComponent,
AuthErrorComponent,
NotificationsComponent,
SpotlightSearchComponent,
BreadcrumbsComponent,
DashboardSkeletonComponent,
DossierSkeletonComponent,
SkeletonTopBarComponent,
SkeletonStatsComponent,
...screens,
];
export const appModuleFactory = (config: AppConfig) => {
@NgModule({
@ -83,6 +100,7 @@ export const appModuleFactory = (config: AppConfig) => {
existingUserService: UserService,
existingRoleGuard: RedRoleGuard,
}),
IqserSharedModule,
CachingModule.forRoot(UI_CACHES),
IqserHelpModeModule.forRoot(links),
PdfViewerModule,

View File

@ -0,0 +1,16 @@
<redaction-skeleton-top-bar></redaction-skeleton-top-bar>
<div class="overlay-shadow"></div>
<div class="container animate-flicker">
<div class="sk-line sk-h-24 sk-w-250 mb-20"></div>
<div class="sk-line sk-h-12 sk-w-340 mb-32"></div>
<div *ngFor="let template of [].constructor(3)" class="dialog">
<div class="flex-2 p-24">
<div class="sk-line sk-h-20 sk-w-200 mb-16"></div>
<div *ngFor="let line of [].constructor(4)" class="sk-line sk-h-12 sk-w-100 mb-16"></div>
</div>
<redaction-skeleton-stats *ngFor="let column of [].constructor(2)" class="flex-3 p-24"></redaction-skeleton-stats>
</div>
</div>

View File

@ -0,0 +1,33 @@
@use 'common-mixins';
:host {
min-height: 100vh;
background-color: var(--iqser-alt-background);
display: flex;
flex-direction: column;
align-items: center;
.container {
padding: 32px;
width: 900px;
max-width: 100%;
box-sizing: border-box;
}
}
.dialog {
flex-direction: row;
max-width: unset;
min-height: unset;
margin: 0 0 16px 0;
> div {
display: flex;
flex-direction: column;
overflow: hidden;
}
redaction-skeleton-stats {
border-left: 1px solid var(--iqser-separator);
}
}

View File

@ -0,0 +1,9 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'redaction-dashboard-skeleton',
templateUrl: './dashboard-skeleton.component.html',
styleUrls: ['./dashboard-skeleton.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DashboardSkeletonComponent {}

View File

@ -0,0 +1,134 @@
<redaction-skeleton-top-bar></redaction-skeleton-top-bar>
<section class="animate-flicker">
<div class="page-header">
<div class="flex-align-items-center w-100p">
<div class="sk-line sk-h-8 sk-w-56 mr-16"></div>
<ng-container *ngFor="let filter of [].constructor(3)">
<div class="sk-square mr-4"></div>
<div class="sk-line sk-h-8 sk-w-100 mr-25"></div>
</ng-container>
</div>
<div class="flex-align-items-center">
<div *ngFor="let button of [].constructor(5)" class="sk-square mr-20"></div>
<div class="sk-square ml-14"></div>
<div class="sk-square ml-26"></div>
</div>
</div>
<div class="overlay-shadow"></div>
<div class="content-inner">
<div class="content-container">
<div class="header-item">
<div class="header-title w-100p">
<iqser-round-checkbox></iqser-round-checkbox>
<div class="sk-line sk-h-8 sk-w-100"></div>
</div>
</div>
<div class="sk-table">
<div class="sk-table-column">
<div class="sk-table-header">
<div class="sk-line sk-h-8 sk-w-100"></div>
</div>
<div *ngFor="let column of [].constructor(12)" class="sk-table-cell">
<div class="sk-line sk-h-8 sk-w-100"></div>
</div>
</div>
<div class="sk-table-column">
<div class="sk-table-header">
<div class="sk-line sk-h-8 sk-w-70"></div>
</div>
<div *ngFor="let column of [].constructor(12)" class="sk-table-cell">
<div class="sk-line sk-h-8 sk-w-70"></div>
</div>
</div>
<div class="sk-table-column">
<div class="sk-table-header">
<div class="sk-line sk-h-8 sk-w-60"></div>
</div>
<div *ngFor="let template of workloadTemplates" class="sk-table-cell">
<ng-container *ngTemplateOutlet="template"></ng-container>
</div>
</div>
<div class="sk-table-column">
<div class="sk-table-header">
<div class="sk-line sk-h-8 sk-w-40"></div>
</div>
<div *ngFor="let column of [].constructor(12)" class="sk-table-cell">
<div class="sk-line sk-h-8 sk-w-40"></div>
</div>
</div>
<div class="sk-table-column">
<div class="sk-table-header">
<div class="sk-line sk-h-8 sk-w-70"></div>
</div>
<div *ngFor="let column of [].constructor(12)" class="sk-table-cell">
<div class="sk-circle w-24"></div>
</div>
</div>
<div class="sk-table-column">
<div class="sk-table-header">
<div class="sk-line sk-h-8 sk-w-70"></div>
</div>
<div *ngFor="let column of [].constructor(12)" class="sk-table-cell">
<div class="sk-line sk-h-8 sk-w-12"></div>
</div>
</div>
</div>
</div>
<div class="right-container">
<div class="header mb-24">
<div class="sk-line sk-h-24 sk-w-200"></div>
<div class="sk-square"></div>
</div>
<div class="sk-line sk-h-8 sk-w-70 mb-12"></div>
<div class="sk-line sk-h-12 sk-w-100 mb-40"></div>
<div class="sk-line sk-h-8 sk-w-70 mb-8"></div>
<div class="flex-align-items-center mb-20">
<div class="sk-circle w-32 mr-6"></div>
<div class="sk-line sk-h-8 sk-w-70"></div>
</div>
<div class="sk-line sk-h-8 sk-w-70 mb-10"></div>
<div class="d-flex mb-24">
<div *ngFor="let circle of [].constructor(9)" class="sk-circle w-32 mr-2"></div>
</div>
<redaction-skeleton-stats class="mb-32"></redaction-skeleton-stats>
<div *ngFor="let line of [].constructor(8)" class="sk-line sk-h-8 sk-w-250 mb-16"></div>
<div *ngFor="let line of [].constructor(4)" class="sk-line sk-h-8 sk-w-200 mt-16"></div>
</div>
</div>
</section>
<ng-template #workload1>
<div class="sk-square mr-4"></div>
<div class="sk-square mr-4"></div>
<div class="sk-rhombus"></div>
</ng-template>
<ng-template #workload2>
<div class="sk-square"></div>
</ng-template>
<ng-template #workload3></ng-template>
<ng-template #workload4>
<div class="sk-circle w-16 mr-4"></div>
<div class="sk-rhombus"></div>
</ng-template>
<ng-template #workload5>
<div class="sk-circle w-16"></div>
</ng-template>
<ng-template #workload6>
<div class="sk-square mr-4"></div>
<div class="sk-square"></div>
</ng-template>

View File

@ -0,0 +1,87 @@
@use 'common-mixins';
:host {
background-color: var(--iqser-background);
display: block;
}
.w-100p {
width: 100%;
}
.right-container {
display: flex;
flex-direction: column;
width: 375px;
min-width: 375px;
padding: 16px 24px;
overflow: hidden;
.header {
display: flex;
align-items: center;
justify-content: space-between;
}
}
.sk-table {
display: flex;
.sk-table-header,
.sk-table-cell {
padding: 0 10px;
border-bottom: 1px solid var(--iqser-separator);
box-sizing: border-box;
}
.sk-table-column {
display: flex;
flex-direction: column;
flex: 1;
&:first-child {
flex: 3;
.sk-table-header,
.sk-table-cell {
padding-left: 56px;
}
}
.sk-line {
margin: auto 0;
}
&:last-child {
flex: 2;
.sk-table-header,
.sk-table-cell {
padding-right: 21px;
}
.sk-line {
margin-left: auto;
}
}
&:nth-child(4),
&:nth-child(5) {
.sk-line,
.sk-circle {
margin: auto;
}
}
}
.sk-table-header {
height: 30px;
padding-top: 11px;
}
.sk-table-cell {
height: 80px;
display: flex;
align-items: center;
}
}

View File

@ -0,0 +1,35 @@
import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
@Component({
selector: 'redaction-dossier-skeleton',
templateUrl: './dossier-skeleton.component.html',
styleUrls: ['./dossier-skeleton.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DossierSkeletonComponent implements OnInit {
@ViewChild('workload1', { static: true }) workload1: TemplateRef<unknown>;
@ViewChild('workload2', { static: true }) workload2: TemplateRef<unknown>;
@ViewChild('workload3', { static: true }) workload3: TemplateRef<unknown>;
@ViewChild('workload4', { static: true }) workload4: TemplateRef<unknown>;
@ViewChild('workload5', { static: true }) workload5: TemplateRef<unknown>;
@ViewChild('workload6', { static: true }) workload6: TemplateRef<unknown>;
workloadTemplates: TemplateRef<unknown>[];
ngOnInit(): void {
this.workloadTemplates = [
this.workload1,
this.workload3,
this.workload2,
this.workload3,
this.workload4,
this.workload5,
this.workload1,
this.workload5,
this.workload6,
this.workload6,
this.workload2,
this.workload5,
];
}
}

View File

@ -0,0 +1,10 @@
<div class="flex-1 mr-16">
<mat-icon svgIcon="red:skeleton-stats"></mat-icon>
</div>
<div class="flex-1">
<div *ngFor="let line of [].constructor(4)" class="mb-16 d-flex align-center">
<div class="flex-1 sk-line sk-h-6 sk-w-12 mr-10"></div>
<div class="flex-1 sk-line sk-h-8 sk-w-100"></div>
</div>
</div>

View File

@ -0,0 +1,10 @@
:host {
display: flex;
align-items: center;
}
mat-icon {
width: unset;
height: unset;
color: var(--iqser-skeleton-color);
}

View File

@ -0,0 +1,9 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'redaction-skeleton-stats',
templateUrl: './skeleton-stats.component.html',
styleUrls: ['./skeleton-stats.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SkeletonStatsComponent {}

View File

@ -0,0 +1,15 @@
<div class="top-bar">
<div class="flex-2 visible-lg breadcrumbs-container">
<a [translate]="'top-bar.navigation-items.dashboard'" class="breadcrumb active"></a>
</div>
<a class="logo">
<div class="actions">
<iqser-logo icon="red:logo"></iqser-logo>
<div class="app-name">{{ titleService.getTitle() }}</div>
</div>
</a>
<div class="actions flex-2">
<iqser-user-button id="userMenu"></iqser-user-button>
</div>
</div>

View File

@ -0,0 +1,9 @@
:host {
display: contents;
}
.top-bar .actions {
display: flex;
align-items: center;
justify-content: flex-end;
}

View File

@ -0,0 +1,12 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Title } from '@angular/platform-browser';
@Component({
selector: 'redaction-skeleton-top-bar',
templateUrl: './skeleton-top-bar.component.html',
styleUrls: ['./skeleton-top-bar.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SkeletonTopBarComponent {
constructor(readonly titleService: Title) {}
}

View File

@ -20,6 +20,7 @@ const routes: Routes = [
routeGuards: [DossierFilesGuard],
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier],
dossiersService: ARCHIVED_DOSSIERS_SERVICE,
skeleton: 'dossier',
},
loadChildren: () => import('../dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule),
},

View File

@ -68,6 +68,7 @@ export class IconsModule {
'rss',
'rule',
'secret',
'skeleton-stats',
'status',
'status-info',
'template',

View File

@ -1,7 +1,7 @@
{
"ADMIN_CONTACT_NAME": null,
"ADMIN_CONTACT_URL": null,
"API_URL": "https://dev-08.iqser.cloud/redaction-gateway-v1",
"API_URL": "https://dev-04.iqser.cloud/redaction-gateway-v1",
"APP_NAME": "RedactManager",
"AUTO_READ_TIME": 3,
"BACKEND_APP_VERSION": "4.4.40",
@ -11,7 +11,7 @@
"MAX_RETRIES_ON_SERVER_ERROR": 3,
"OAUTH_CLIENT_ID": "redaction",
"OAUTH_IDP_HINT": null,
"OAUTH_URL": "https://dev-08.iqser.cloud/auth/realms/redaction",
"OAUTH_URL": "https://dev-04.iqser.cloud/auth/realms/redaction",
"RECENT_PERIOD_IN_HOURS": 24,
"SELECTION_MODE": "structural",
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",

View File

@ -0,0 +1,13 @@
<svg fill="none" height="160" viewBox="0 0 160 160" width="160" xmlns="http://www.w3.org/2000/svg">
<path clip-rule="evenodd"
d="M79.9993 0C124.182 0 160 35.817 160 80C160 124.183 124.182 160 79.9993 160C35.817 160 0 124.183 0 80C0 35.817 35.817 0 79.9993 0ZM80.1281 15.4951C44.7254 15.4951 16.0257 44.1948 16.0257 79.5981C16.0257 115.001 44.7254 143.701 80.1281 143.701C115.531 143.701 144.232 115.001 144.232 79.5981C144.232 44.1948 115.531 15.4951 80.1281 15.4951Z"
fill="currentColor"
fill-rule="evenodd" />
<path
d="M122 92.5H34C30.6863 92.5 28 95.1863 28 98.5C28 101.814 30.6863 104.5 34 104.5H122C125.314 104.5 128 101.814 128 98.5C128 95.1863 125.314 92.5 122 92.5Z"
fill="currentColor" />
<path
d="M88.5 60.5H71.5C64.8726 60.5 59.5 65.8726 59.5 72.5C59.5 79.1274 64.8726 84.5 71.5 84.5H88.5C95.1274 84.5 100.5 79.1274 100.5 72.5C100.5 65.8726 95.1274 60.5 88.5 60.5Z"
fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 982 B

View File

@ -80,6 +80,7 @@
$iqser-tab-hover: vars.$grey-6,
$iqser-loading-progress: vars.$grey-7,
$iqser-highlight-color: #fffcc4,
$iqser-skeleton-color: vars.$grey-4,
$iqser-font-family: 'Inter, sans-serif',
$iqser-app-name-font-family: 'OpenSans Extrabold, sans-serif'
);
@ -132,6 +133,7 @@ $dark-accent-10: darken(vars.$accent, 10%);
$iqser-tab-hover: vars.$accent,
$iqser-loading-progress: $light-accent-10,
$iqser-highlight-color: #905854,
$iqser-skeleton-color: vars.$grey-10,
$iqser-font-family: 'Inter, sans-serif',
$iqser-app-name-font-family: 'OpenSans Extrabold, sans-serif'
);

@ -1 +1 @@
Subproject commit cb3f1e3eea6353711d2e5bdfe8664b1b4b8e70e2
Subproject commit 9340cda21abe78b653c876b7d0fd045ecda28a57