diff --git a/src/assets/icons/dossier-info.svg b/src/assets/icons/dossier-info.svg new file mode 100644 index 0000000..6c0057d --- /dev/null +++ b/src/assets/icons/dossier-info.svg @@ -0,0 +1,7 @@ + + + + + Svg Vector Icons : http://www.onlinewebfonts.com/icon + + \ No newline at end of file diff --git a/src/assets/icons/settings.svg b/src/assets/icons/settings.svg new file mode 100644 index 0000000..c00977c --- /dev/null +++ b/src/assets/icons/settings.svg @@ -0,0 +1,13 @@ + + + F1A86E9A-B510-4D33-B7AD-69EC87209666 + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/styles/common-layout.scss b/src/assets/styles/common-layout.scss index f68cf13..38605e6 100644 --- a/src/assets/styles/common-layout.scss +++ b/src/assets/styles/common-layout.scss @@ -127,16 +127,16 @@ section.settings { } } } +} - @media only screen and (max-width: 1600px) { - .user-column { - justify-content: center; - align-items: center; +@media only screen and (max-width: 1600px) { + .user-column { + justify-content: center; + align-items: center; - // TODO - redaction-initials-avatar .username { - display: none; - } + // TODO: Shouldn't use `redaction-` here + redaction-initials-avatar .username { + display: none; } } } @@ -214,78 +214,6 @@ section.settings { flex: 2; } -.mt-0 { - margin-top: 0 !important; -} - -.mt-5 { - margin-top: 5px; -} - -.mt-8 { - margin-top: 8px; -} - -.mt-12 { - margin-top: 12px; -} - -.mt-16 { - margin-top: 16px !important; -} - -.mt-20 { - margin-top: 20px; -} - -.mt-24 { - margin-top: 24px; -} - -.mt-32 { - margin-top: 32px; -} - -.mb-0 { - margin-bottom: 0 !important; -} - -.mb-6 { - margin-bottom: 6px; -} - -.mb-8 { - margin-bottom: 8px !important; -} - -.mb-12 { - margin-bottom: 12px !important; -} - -.ml-8 { - margin-left: 8px; -} - -.ml-14 { - margin-left: 14px; -} - -.ml-16 { - margin-left: 16px; -} - -.mr-24 { - margin-right: 24px; -} - -.pb-24 { - padding-bottom: 24px; -} - -.pb-32 { - padding-bottom: 32px; -} - .w-100 { min-width: 100px !important; width: 100px !important; @@ -309,22 +237,6 @@ section.settings { cursor: pointer; } -.mr-4 { - margin-right: 4px !important; -} - -.mr-8 { - margin-right: 8px !important; -} - -.mr-16 { - margin-right: 16px; -} - -.mr-34 { - margin-right: 34px; -} - .fit-content { width: fit-content; } diff --git a/src/assets/styles/common-styles.scss b/src/assets/styles/common-styles.scss index 722c27e..ed6c081 100644 --- a/src/assets/styles/common-styles.scss +++ b/src/assets/styles/common-styles.scss @@ -1,3 +1,4 @@ +@use 'common-utilities'; @use 'common-fonts'; @use 'common-inputs'; @use 'common-buttons'; diff --git a/src/assets/styles/common-utilities.scss b/src/assets/styles/common-utilities.scss new file mode 100644 index 0000000..46ab464 --- /dev/null +++ b/src/assets/styles/common-utilities.scss @@ -0,0 +1,26 @@ +/* Margins, paddings */ + +$start: 0; +$end: 100; + +$values: ""; +$sides: (top, bottom, left, right); + +@for $i from $start + 1 through $end { + $values: append($values, $i, comma); + $values: set-nth($values, 1, $start); +} + +// TODO: Check if !important can be avoided + +@each $space in $values { + @each $side in $sides { + .m#{str-slice($side, 0, 1)}-#{$space} { + margin-#{$side}: #{$space}px !important; + } + + .p#{str-slice($side, 0, 1)}-#{$space} { + padding-#{$side}: #{$space}px !important; + } + } +} diff --git a/src/lib/dialog/base-dialog.component.ts b/src/lib/dialog/base-dialog.component.ts index 017173d..13d1e7b 100644 --- a/src/lib/dialog/base-dialog.component.ts +++ b/src/lib/dialog/base-dialog.component.ts @@ -9,7 +9,7 @@ import { IqserEventTarget } from '../utils'; * However, some components (e.g. redaction-select, color picker) don't set focus on the input after choosing a value. * Also, other components (e.g. dropdown select) trigger a different action on enter, instead of submit. * - * Make sure to remove property type="submit" from the save button and the (submit)="save()" property from the form + * Make sure to remove the (submit)="save()" property from the form and to set type="button" on the save button * (otherwise the save request will be triggered twice). * */ export abstract class BaseDialogComponent { diff --git a/src/lib/icons/icons.module.ts b/src/lib/icons/icons.module.ts index 6869045..67f60a2 100644 --- a/src/lib/icons/icons.module.ts +++ b/src/lib/icons/icons.module.ts @@ -19,6 +19,7 @@ export class IqserIconsModule { 'collapse', 'csv', 'document', + 'dossier-info', 'download', 'edit', 'expand', @@ -37,6 +38,7 @@ export class IqserIconsModule { 'radio-selected', 'refresh', 'search', + 'settings', 'sort-asc', 'sort-desc', 'status-collapse', diff --git a/src/lib/listing/page-header/page-header.component.scss b/src/lib/listing/page-header/page-header.component.scss index acbdfdf..e69de29 100644 --- a/src/lib/listing/page-header/page-header.component.scss +++ b/src/lib/listing/page-header/page-header.component.scss @@ -1,3 +0,0 @@ -.ml-6 { - margin-left: 6px; -} diff --git a/src/lib/listing/workflow/column-header/column-header.component.scss b/src/lib/listing/workflow/column-header/column-header.component.scss index 3ce2e65..cd944a3 100644 --- a/src/lib/listing/workflow/column-header/column-header.component.scss +++ b/src/lib/listing/workflow/column-header/column-header.component.scss @@ -4,7 +4,3 @@ align-items: center; justify-content: space-between; } - -.mr-10 { - margin-right: 10px; -} diff --git a/src/lib/services/index.ts b/src/lib/services/index.ts index 214c413..026ff9d 100644 --- a/src/lib/services/index.ts +++ b/src/lib/services/index.ts @@ -3,3 +3,4 @@ export * from './toaster.service'; export * from './error-message.service'; export * from './generic.service'; export * from './composite-route.guard'; +export * from './stats.service'; diff --git a/src/lib/services/stats.service.ts b/src/lib/services/stats.service.ts new file mode 100644 index 0000000..3be767c --- /dev/null +++ b/src/lib/services/stats.service.ts @@ -0,0 +1,62 @@ +import { Inject, Injectable, Injector } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { HttpClient } from '@angular/common/http'; +import { tap } from 'rxjs/operators'; +import { HeadersConfiguration, mapEach, RequiredParam, Validate } from '../utils'; + +@Injectable() +export abstract class StatsService { + private readonly _http = this._injector.get(HttpClient); + private readonly _map = new Map>(); + + protected constructor( + protected readonly _injector: Injector, + @Inject('ENTITY_PRIMARY_KEY') protected readonly _primaryKey: string, + @Inject('ENTITY_CLASS') private readonly _entityClass: new (entityInterface: I, ...args: unknown[]) => E, + @Inject('ENTITY_PATH') protected readonly _defaultModelPath: string, + ) {} + + @Validate() + getFor(@RequiredParam() ids: string[]): Observable { + const request = this._http.post(`/${encodeURI(this._defaultModelPath)}`, ids, { + headers: HeadersConfiguration.getHeaders(), + observe: 'body', + }); + + return request.pipe( + mapEach(entity => new this._entityClass(entity)), + tap(entities => entities.forEach(entity => this.set(entity))), + ); + } + + get(key: string): E { + return this._getBehaviourSubject(key).value; + } + + set(stats: E): void { + if (!this._map.has(this._pluckPrimaryKey(stats))) { + this._map.set(this._pluckPrimaryKey(stats), new BehaviorSubject(stats)); + return; + } + + const old = this.get(this._pluckPrimaryKey(stats)); + if (JSON.stringify(old) !== JSON.stringify(stats)) { + this._getBehaviourSubject(this._pluckPrimaryKey(stats)).next(stats); + } + } + + watch$(key: string): Observable { + return this._getBehaviourSubject(key).asObservable(); + } + + private _pluckPrimaryKey(stats: E): string { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return stats[this._primaryKey] as string; + } + + private _getBehaviourSubject(key: string): BehaviorSubject { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this._map.get(key)!; + } +}