@maro-tech/card 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveTemplateValue, evaluateFormCondition, InputSelectComponent, InputComponent } from '@maro-tech/form';
|
|
1
2
|
import * as i1 from '@angular/common';
|
|
2
3
|
import { CommonModule } from '@angular/common';
|
|
3
4
|
import * as i0 from '@angular/core';
|
|
@@ -8,13 +9,12 @@ import { DomSanitizer } from '@angular/platform-browser';
|
|
|
8
9
|
import Panzoom from '@panzoom/panzoom';
|
|
9
10
|
import { LucideDownload, LucideDynamicIcon, LucideEye, LucideFileText, LucidePlus, LucideSparkles, LucideTrash } from '@lucide/angular';
|
|
10
11
|
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
|
|
11
|
-
import { map, shareReplay, forkJoin, switchMap, timer, of
|
|
12
|
+
import { map, shareReplay, forkJoin, switchMap, timer, of } from 'rxjs';
|
|
12
13
|
import { UiTableComponent, UiPaginatorComponent } from '@maro-tech/table';
|
|
13
14
|
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
|
14
15
|
import { map as map$1, exhaustMap, catchError, takeWhile, takeUntil, finalize } from 'rxjs/operators';
|
|
15
16
|
import * as i1$1 from '@angular/forms';
|
|
16
17
|
import { FormsModule } from '@angular/forms';
|
|
17
|
-
import { InputSelectComponent, InputComponent } from '@maro-tech/form';
|
|
18
18
|
|
|
19
19
|
function normalizeCardConfig(config) {
|
|
20
20
|
return {
|
|
@@ -46,34 +46,14 @@ function normalizeActions(actions) {
|
|
|
46
46
|
function resolveSections(sections, data) {
|
|
47
47
|
return (sections || []).map((section) => ({
|
|
48
48
|
...section,
|
|
49
|
-
props:
|
|
49
|
+
props: resolveTemplateValue(section.props, data),
|
|
50
50
|
actions: resolveActions(section.actions, data),
|
|
51
51
|
tabs: section.tabs?.length ? resolveSections(section.tabs, data) : section.tabs,
|
|
52
52
|
panels: section.panels?.length ? resolveSections(section.panels, data) : section.panels,
|
|
53
53
|
}));
|
|
54
54
|
}
|
|
55
55
|
function resolveActions(actions, data) {
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
function resolveValue(value, data) {
|
|
59
|
-
if (typeof value === 'string') {
|
|
60
|
-
const match = value.match(/^\{\{([a-zA-Z0-9_.-]+)\}\}$/);
|
|
61
|
-
return match ? getByPath(data, match[1]) : value;
|
|
62
|
-
}
|
|
63
|
-
if (Array.isArray(value))
|
|
64
|
-
return value.map((item) => resolveValue(item, data));
|
|
65
|
-
if (value && typeof value === 'object') {
|
|
66
|
-
return Object.fromEntries(Object.entries(value)
|
|
67
|
-
.map(([key, item]) => [key, resolveValue(item, data)]));
|
|
68
|
-
}
|
|
69
|
-
return value;
|
|
70
|
-
}
|
|
71
|
-
function getByPath(source, path) {
|
|
72
|
-
return path.split('.').reduce((current, segment) => {
|
|
73
|
-
if (current == null || typeof current !== 'object')
|
|
74
|
-
return undefined;
|
|
75
|
-
return current[segment];
|
|
76
|
-
}, source);
|
|
56
|
+
return resolveTemplateValue(actions, data);
|
|
77
57
|
}
|
|
78
58
|
|
|
79
59
|
const CARD_ACTION_REQUESTED = 'card.action.requested';
|
|
@@ -754,7 +734,7 @@ class TableComponent {
|
|
|
754
734
|
label: String(action.label || action.id || 'Дія'),
|
|
755
735
|
type: String(action.type || 'api'),
|
|
756
736
|
api: action.api ? this.resolve(action.api, row) : action.api,
|
|
757
|
-
payload: action.payload ?
|
|
737
|
+
payload: action.payload ? resolveTemplateValue(action.payload, row, { interpolate: true }) : action.payload,
|
|
758
738
|
context: {
|
|
759
739
|
entity: this.tableName(),
|
|
760
740
|
recordId: String(row['id'] || '').trim() || undefined,
|
|
@@ -873,7 +853,7 @@ class TableComponent {
|
|
|
873
853
|
if (!condition)
|
|
874
854
|
return true;
|
|
875
855
|
try {
|
|
876
|
-
return
|
|
856
|
+
return evaluateFormCondition(condition, { ...row, data: row });
|
|
877
857
|
}
|
|
878
858
|
catch {
|
|
879
859
|
return false;
|
|
@@ -913,24 +893,8 @@ class TableComponent {
|
|
|
913
893
|
const value = Number(this.config?.props?.[key]);
|
|
914
894
|
return Number.isFinite(value) && value > 0 ? Math.trunc(value) : fallback;
|
|
915
895
|
}
|
|
916
|
-
resolveObject(value, row) {
|
|
917
|
-
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, this.resolveValue(item, row)]));
|
|
918
|
-
}
|
|
919
896
|
resolve(value, row) {
|
|
920
|
-
return String(
|
|
921
|
-
}
|
|
922
|
-
resolveValue(value, row) {
|
|
923
|
-
if (typeof value === 'string') {
|
|
924
|
-
return value.replace(/\{\{([a-zA-Z0-9_.-]+)\}\}/g, (_match, path) => {
|
|
925
|
-
const result = String(path).split('.').reduce((current, key) => current && typeof current === 'object' ? current[key] : undefined, row);
|
|
926
|
-
return result == null ? '' : String(result);
|
|
927
|
-
});
|
|
928
|
-
}
|
|
929
|
-
if (Array.isArray(value))
|
|
930
|
-
return value.map((item) => this.resolveValue(item, row));
|
|
931
|
-
if (value && typeof value === 'object')
|
|
932
|
-
return this.resolveObject(value, row);
|
|
933
|
-
return value;
|
|
897
|
+
return String(resolveTemplateValue(value, row, { interpolate: true }));
|
|
934
898
|
}
|
|
935
899
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
936
900
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: TableComponent, isStandalone: true, selector: "app-card-table", inputs: { config: "config", data: "data" }, usesOnChanges: true, ngImport: i0, template: "<section class=\"space-y-4\">\n @if (headerActions().length) {\n <div class=\"flex justify-end gap-2\">\n @for (action of headerActions(); track action.id) {\n <button type=\"button\" class=\"rounded-lg border border-slate-200 px-3 py-1.5 text-sm\" (click)=\"onHeaderAction(action)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <app-ui-table\n class=\"flex min-h-0 max-h-[80dvh] flex-col overflow-hidden\"\n [data]=\"tableData()\"\n [columns]=\"columns()\"\n [actionsPosition]=\"actionsPositionSignal()\"\n [pageSizeOptions]=\"pageSizeOptionsSignal()\"\n [isLoading]=\"isLoading()\"\n [disablePaging]=\"disablePaging\"\n (pageChange)=\"onPageChange($event)\"\n (actionClick)=\"onAction($event)\"\n (cellChange)=\"onCellChange($event)\"\n ></app-ui-table>\n</section>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: UiTableComponent, selector: "app-ui-table", inputs: ["pageSizeOptions", "actionsPosition", "columns", "isLoading", "data", "disablePaging", "rowClickable", "imageResolver"], outputs: ["pageChange", "rowClick", "actionClick", "cellChange"] }] });
|
|
@@ -1268,7 +1232,7 @@ class DocumentsUploadModalComponent {
|
|
|
1268
1232
|
}
|
|
1269
1233
|
}
|
|
1270
1234
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: DocumentsUploadModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1271
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: DocumentsUploadModalComponent, isStandalone: true, selector: "app-documents-upload-modal", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", saved: "saved" }, viewQueries: [{ propertyName: "uploadFileInput", first: true, predicate: ["uploadFileInput"], descendants: true }], ngImport: i0, template: "<core-modal\n title=\"\u0414\u043E\u0434\u0430\u0442\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\"\n [isOpen]=\"isOpen()\"\n (closed)=\"close()\"\n>\n <div class=\"space-y-4\">\n <input-select\n [data]=\"'doc_type'\"\n name=\"uploadDocumentTypeId\"\n label=\"\u0422\u0438\u043F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\"\n hint=\"\u041D\u0435\u043E\u0431\u043E\u0432'\u044F\u0437\u043A\u043E\u0432\u043E\"\n [ngModel]=\"documentTypeId()\"\n (ngModelChange)=\"onDocumentTypeChange($event)\"\n placeholder=\"\u0422\u0438\u043F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\"\n [searchable]=\"true\"\n ></input-select>\n\n <div class=\"space-y-2\">\n <label class=\"block text-sm font-medium text-slate-700\" for=\"document-files\">\u0424\u0430\u0439\u043B\u0438</label>\n <input\n #uploadFileInput\n id=\"document-files\"\n type=\"file\"\n class=\"block w-full rounded-xl border border-slate-200 bg-white px-3 py-2.5 text-sm text-slate-900 outline-none transition file:mr-3 file:rounded-lg file:border-0 file:bg-slate-900 file:px-3 file:py-2 file:text-sm file:font-medium file:text-white focus:border-slate-400 focus:ring-2 focus:ring-slate-200\"\n multiple\n (change)=\"onFilesSelected($event)\"\n />\n @if (fileNames().length) {\n <div class=\"rounded-xl border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-600\">\n @for (fileName of fileNames(); track fileName) {\n <div class=\"truncate\">{{ fileName }}</div>\n }\n </div>\n } @else {\n <p class=\"text-xs text-slate-500\">\u041C\u043E\u0436\u043D\u0430 \u043E\u0431\u0440\u0430\u0442\u0438 \u043E\u0434\u0438\u043D \u0430\u0431\u043E \u0434\u0435\u043A\u0456\u043B\u044C\u043A\u0430 \u0444\u0430\u0439\u043B\u0456\u0432.</p>\n }\n </div>\n </div>\n\n <div slot=\"footer\" class=\"flex justify-end gap-2\">\n <ui-button variant=\"ghost\" [disabled]=\"isUploading()\" (click)=\"close()\">\u0421\u043A\u0430\u0441\u0443\u0432\u0430\u0442\u0438</ui-button>\n <ui-button variant=\"primary\" [disabled]=\"isUploading()\" (click)=\"submitUpload()\">\n {{ isUploading() ? '\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F...' : '\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438' }}\n </ui-button>\n </div>\n</core-modal>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["variant", "size", "disabled", "fullWidth", "type", "withShadow", "icon", "iconOnly", "ariaLabel"] }, { kind: "component", type: UiModalComponent, selector: "core-modal", inputs: ["isOpen", "title", "size", "position", "closable", "closeOnBackdrop", "closeOnEscape"], outputs: ["closed"] }, { kind: "component", type: InputSelectComponent, selector: "input-select", inputs: ["label", "hint", "error", "placeholder", "name", "data", "dataParams", "required", "disabled", "multiple", "emptyStateLabel", "searchable", "options", "triggerClass", "panelClass"], outputs: ["valueChange", "searchChange"] }] });
|
|
1235
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: DocumentsUploadModalComponent, isStandalone: true, selector: "app-documents-upload-modal", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", saved: "saved" }, viewQueries: [{ propertyName: "uploadFileInput", first: true, predicate: ["uploadFileInput"], descendants: true }], ngImport: i0, template: "<core-modal\n title=\"\u0414\u043E\u0434\u0430\u0442\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\"\n [isOpen]=\"isOpen()\"\n (closed)=\"close()\"\n>\n <div class=\"space-y-4\">\n <input-select\n [data]=\"'doc_type'\"\n name=\"uploadDocumentTypeId\"\n label=\"\u0422\u0438\u043F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\"\n hint=\"\u041D\u0435\u043E\u0431\u043E\u0432'\u044F\u0437\u043A\u043E\u0432\u043E\"\n [ngModel]=\"documentTypeId()\"\n (ngModelChange)=\"onDocumentTypeChange($event)\"\n placeholder=\"\u0422\u0438\u043F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\"\n [searchable]=\"true\"\n ></input-select>\n\n <div class=\"space-y-2\">\n <label class=\"block text-sm font-medium text-slate-700\" for=\"document-files\">\u0424\u0430\u0439\u043B\u0438</label>\n <input\n #uploadFileInput\n id=\"document-files\"\n type=\"file\"\n class=\"block w-full rounded-xl border border-slate-200 bg-white px-3 py-2.5 text-sm text-slate-900 outline-none transition file:mr-3 file:rounded-lg file:border-0 file:bg-slate-900 file:px-3 file:py-2 file:text-sm file:font-medium file:text-white focus:border-slate-400 focus:ring-2 focus:ring-slate-200\"\n multiple\n (change)=\"onFilesSelected($event)\"\n />\n @if (fileNames().length) {\n <div class=\"rounded-xl border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-600\">\n @for (fileName of fileNames(); track fileName) {\n <div class=\"truncate\">{{ fileName }}</div>\n }\n </div>\n } @else {\n <p class=\"text-xs text-slate-500\">\u041C\u043E\u0436\u043D\u0430 \u043E\u0431\u0440\u0430\u0442\u0438 \u043E\u0434\u0438\u043D \u0430\u0431\u043E \u0434\u0435\u043A\u0456\u043B\u044C\u043A\u0430 \u0444\u0430\u0439\u043B\u0456\u0432.</p>\n }\n </div>\n </div>\n\n <div slot=\"footer\" class=\"flex justify-end gap-2\">\n <ui-button variant=\"ghost\" [disabled]=\"isUploading()\" (click)=\"close()\">\u0421\u043A\u0430\u0441\u0443\u0432\u0430\u0442\u0438</ui-button>\n <ui-button variant=\"primary\" [disabled]=\"isUploading()\" (click)=\"submitUpload()\">\n {{ isUploading() ? '\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F...' : '\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438' }}\n </ui-button>\n </div>\n</core-modal>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["variant", "size", "disabled", "fullWidth", "type", "withShadow", "icon", "iconOnly", "ariaLabel"] }, { kind: "component", type: UiModalComponent, selector: "core-modal", inputs: ["isOpen", "title", "size", "position", "closable", "closeOnBackdrop", "closeOnEscape"], outputs: ["closed"] }, { kind: "component", type: InputSelectComponent, selector: "input-select", inputs: ["label", "hint", "error", "placeholder", "name", "data", "dataParams", "required", "disabled", "multiple", "emptyStateLabel", "searchable", "options", "triggerClass", "panelClass", "actions"], outputs: ["actionTriggered", "valueChange", "searchChange"] }] });
|
|
1272
1236
|
}
|
|
1273
1237
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: DocumentsUploadModalComponent, decorators: [{
|
|
1274
1238
|
type: Component,
|
|
@@ -1659,7 +1623,6 @@ class HistoryTabComponent {
|
|
|
1659
1623
|
destroyRef = inject(DestroyRef);
|
|
1660
1624
|
refreshSequence = signal(0, ...(ngDevMode ? [{ debugName: "refreshSequence" }] : /* istanbul ignore next */ []));
|
|
1661
1625
|
lastRequestKey = '';
|
|
1662
|
-
dictionaryCache = new Map();
|
|
1663
1626
|
constructor() {
|
|
1664
1627
|
this.eventBus.on(ENTITY_UPDATED)
|
|
1665
1628
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
@@ -1785,8 +1748,8 @@ class HistoryTabComponent {
|
|
|
1785
1748
|
label: config.label || field,
|
|
1786
1749
|
type: config.type || 'text',
|
|
1787
1750
|
summary: values.summary,
|
|
1788
|
-
from: await this.toDisplayValue(values.from, config),
|
|
1789
|
-
to: await this.toDisplayValue(values.to, config),
|
|
1751
|
+
from: await this.toDisplayValue(values.from, config, values.from_data),
|
|
1752
|
+
to: await this.toDisplayValue(values.to, config, values.to_data),
|
|
1790
1753
|
};
|
|
1791
1754
|
}
|
|
1792
1755
|
return { ...change, changedFields };
|
|
@@ -1800,6 +1763,8 @@ class HistoryTabComponent {
|
|
|
1800
1763
|
return [key, {
|
|
1801
1764
|
from: from && typeof from === 'object' && 'value' in from ? from.value : from,
|
|
1802
1765
|
to: to && typeof to === 'object' && 'value' in to ? to.value : to,
|
|
1766
|
+
from_data: field?.from_data,
|
|
1767
|
+
to_data: field?.to_data,
|
|
1803
1768
|
summary: typeof field?.changeSummary === 'string' ? field.changeSummary : undefined,
|
|
1804
1769
|
}];
|
|
1805
1770
|
}));
|
|
@@ -1814,10 +1779,10 @@ class HistoryTabComponent {
|
|
|
1814
1779
|
}
|
|
1815
1780
|
return undefined;
|
|
1816
1781
|
}
|
|
1817
|
-
async toDisplayValue(value, config) {
|
|
1782
|
+
async toDisplayValue(value, config, data) {
|
|
1818
1783
|
const display = config.type === 'boolean' && value !== null && value !== undefined && value !== ''
|
|
1819
1784
|
? (value === true || value === 'true' || value === 1 ? 'Так' : 'Ні')
|
|
1820
|
-
: await this.resolveDisplay(value, config);
|
|
1785
|
+
: await this.resolveDisplay(value, config, data);
|
|
1821
1786
|
const structured = config.displayMode === 'structured' ? await this.toStructuredItems(value, config.itemFields) : undefined;
|
|
1822
1787
|
const result = {
|
|
1823
1788
|
value: value ?? null,
|
|
@@ -1830,23 +1795,24 @@ class HistoryTabComponent {
|
|
|
1830
1795
|
result.url = `/api/files/${encodeURIComponent(String(value))}`;
|
|
1831
1796
|
return result;
|
|
1832
1797
|
}
|
|
1833
|
-
async resolveDisplay(value, config) {
|
|
1798
|
+
async resolveDisplay(value, config, data) {
|
|
1834
1799
|
if (value === null || value === undefined || value === '')
|
|
1835
1800
|
return 'Не задано';
|
|
1801
|
+
const dataLabel = this.getDataLabel(data);
|
|
1802
|
+
if (dataLabel !== null)
|
|
1803
|
+
return dataLabel;
|
|
1836
1804
|
const values = Array.isArray(value) ? value : [value];
|
|
1837
|
-
return
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
const
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
this.dictionaryCache.set(cacheKey, request);
|
|
1849
|
-
return request;
|
|
1805
|
+
return values
|
|
1806
|
+
.filter((item) => item !== null && item !== undefined && item !== '')
|
|
1807
|
+
.map((item) => typeof item === 'object' ? JSON.stringify(item) : String(item))
|
|
1808
|
+
.filter(Boolean).join(', ') || 'Не задано';
|
|
1809
|
+
}
|
|
1810
|
+
getDataLabel(data) {
|
|
1811
|
+
const values = Array.isArray(data) ? data : [data];
|
|
1812
|
+
const labels = values
|
|
1813
|
+
.map((item) => item && typeof item === 'object' && 'label' in item ? String(item.label ?? '') : '')
|
|
1814
|
+
.filter(Boolean);
|
|
1815
|
+
return labels.length ? labels.join(', ') : null;
|
|
1850
1816
|
}
|
|
1851
1817
|
async toStructuredItems(value, itemFields) {
|
|
1852
1818
|
const values = Array.isArray(value) ? value : value && typeof value === 'object' ? [value] : [];
|
|
@@ -1867,7 +1833,8 @@ class HistoryTabComponent {
|
|
|
1867
1833
|
if (value === null || value === undefined || value === '' || seenLabels.has(label))
|
|
1868
1834
|
return null;
|
|
1869
1835
|
seenLabels.add(label);
|
|
1870
|
-
const display =
|
|
1836
|
+
const display = this.getDataLabel(record[`${key}_data`])
|
|
1837
|
+
|| (typeof value === 'object' ? JSON.stringify(value) : String(value));
|
|
1871
1838
|
return { key, label, value, display };
|
|
1872
1839
|
}))).filter((field) => field !== null),
|
|
1873
1840
|
};
|
|
@@ -1956,7 +1923,7 @@ class HistoryTabComponent {
|
|
|
1956
1923
|
: operation.changes || [];
|
|
1957
1924
|
}
|
|
1958
1925
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: HistoryTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1959
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: HistoryTabComponent, isStandalone: true, selector: "app-history-tab", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<section class=\"flex h-[calc(100dvh-14rem)] min-h-[24rem] min-w-0 flex-col gap-4 overflow-hidden\">\n <div class=\"flex shrink-0 flex-wrap items-end gap-4 rounded-3xl border border-slate-200 bg-white p-5\">\n <div class=\"min-w-56 flex-1\">\n <input-base label=\"\u0412\u0456\u0434\" type=\"datetime-local\" [ngModel]=\"fromDate()\" (ngModelChange)=\"onDateFilterChange('from', $event)\" />\n </div>\n <div class=\"min-w-56 flex-1\">\n <input-base label=\"\u0414\u043E\" type=\"datetime-local\" [ngModel]=\"toDate()\" (ngModelChange)=\"onDateFilterChange('to', $event)\" />\n </div>\n <div class=\"min-w-56 flex-1\">\n <input-select label=\"\u0414\u0456\u044F\" placeholder=\"\u0423\u0441\u0456 \u0434\u0456\u0457\" [options]=\"availableActionOptions()\" [searchable]=\"false\" [ngModel]=\"actionFilter()\" (ngModelChange)=\"onActionFilterChange($event)\" />\n </div>\n <div class=\"min-w-56 flex-1\">\n <input-select label=\"\u0429\u043E \u0437\u043C\u0456\u043D\u044E\u0432\u0430\u043B\u043E\u0441\u044F\" placeholder=\"\u0423\u0441\u0456 \u0442\u0438\u043F\u0438 \u0437\u043C\u0456\u043D\" [options]=\"availableChangeTypeOptions()\" [searchable]=\"false\" [ngModel]=\"changeTypeFilter()\" (ngModelChange)=\"onChangeTypeFilterChange($event)\" />\n </div>\n <ui-button variant=\"ghost\" type=\"button\" (click)=\"clearFilters()\">\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u0438</ui-button>\n </div>\n <div class=\"min-h-0 flex-1 overflow-y-auto overscroll-contain pr-1\">\n @if (isLoading()) {\n <div class=\"rounded-2xl border border-slate-200 bg-white p-6 text-sm text-slate-500\">\n \u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u0456\u0441\u0442\u043E\u0440\u0456\u0457\u2026\n </div>\n } @else if (hasError()) {\n <div class=\"rounded-2xl border border-red-200 bg-red-50 p-6 text-sm text-red-700\">\n \u041D\u0435 \u0432\u0434\u0430\u043B\u043E\u0441\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0456\u0441\u0442\u043E\u0440\u0456\u044E \u0437\u043C\u0456\u043D.\n </div>\n } @else if (!operations().length) {\n <div class=\"rounded-2xl border border-slate-200 bg-white p-6 text-sm text-slate-500\">\n \u0406\u0441\u0442\u043E\u0440\u0456\u044F \u0437\u043C\u0456\u043D \u043F\u043E\u043A\u0438 \u043F\u043E\u0440\u043E\u0436\u043D\u044F.\n </div>\n } @else {\n <div class=\"space-y-3\">\n @for (operation of operations(); track operation.id) {\n <article class=\"rounded-2xl border border-slate-200 bg-white p-5 shadow-sm\">\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h3 class=\"text-sm font-semibold text-slate-900\">{{ operation.description }}</h3>\n @if (operation.actorName) {\n <p class=\"mt-1 text-sm text-slate-500\">{{ operation.actorName }}</p>\n }\n </div>\n <time class=\"text-xs text-slate-400\" [dateTime]=\"operation.createdAt\">\n {{ operation.createdAt | date: 'dd.MM.yyyy HH:mm' }}\n </time>\n </div>\n\n @if (getPrimaryChanges(operation).length) {\n <div class=\"mt-4 space-y-2 border-t border-slate-100 pt-4\">\n @for (change of getPrimaryChanges(operation); track change.entityId + change.entityType) {\n @for (field of (change.changedFields | keyvalue); track field.key) {\n <div class=\"flex flex-wrap items-center gap-2 text-sm\">\n <span class=\"font-medium text-slate-700\">{{ field.value.label }}</span>\n @if (field.value.summary) { <span class=\"text-xs text-slate-500\">{{ field.value.summary }}</span> }\n @if (isStructuredField(field.value)) {\n @if (getStructuredItems(field.value.from).length) {\n <div class=\"min-w-[16rem] flex-1 space-y-1\">\n <div class=\"text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-400\">\u0411\u0443\u043B\u043E</div>\n @for (item of getStructuredItems(field.value.from); track item.key) {\n <div class=\"rounded-lg border border-slate-200 bg-slate-50 px-2 py-1 text-xs text-slate-600\">\n @for (itemField of item.fields; track itemField.key) {<span class=\"mr-2\"><span class=\"font-medium\">{{ itemField.label }}:</span> {{ itemField.display }}</span>}\n </div>\n }\n </div>\n } @else { <span class=\"text-slate-400\">\u041F\u043E\u0437\u0438\u0446\u0456\u0439 \u0434\u043E \u0437\u043C\u0456\u043D\u0438 \u043D\u0435 \u0431\u0443\u043B\u043E</span> }\n } @else if (isImageField(field.value)) {\n @if (getImageUrls(field.value.from.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.from.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-slate-100\" (click)=\"openImageViewer(getImageUrls(field.value.from.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u0441\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u0421\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.from.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.from.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.from.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-500\" [innerHTML]=\"field.value.from.display\"></div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.from.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.from.url\">{{ field.value.from.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.from.display }}</span>\n }\n }\n <span class=\"mx-1 shrink-0 text-lg font-semibold text-slate-500\">\u2192</span>\n @if (isStructuredField(field.value)) {\n @if (getStructuredItems(field.value.to).length) {\n <div class=\"min-w-[16rem] flex-1 space-y-1\">\n <div class=\"text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-400\">\u0421\u0442\u0430\u043B\u043E</div>\n @for (item of getStructuredItems(field.value.to); track item.key) {\n <div class=\"rounded-lg border border-slate-200 bg-slate-50 px-2 py-1 text-xs text-slate-600\">\n @for (itemField of item.fields; track itemField.key) {<span class=\"mr-2\"><span class=\"font-medium\">{{ itemField.label }}:</span> {{ itemField.display }}</span>}\n </div>\n }\n </div>\n } @else { <span class=\"text-slate-400\">\u041F\u0456\u0441\u043B\u044F \u0437\u043C\u0456\u043D\u0438 \u043F\u043E\u0437\u0438\u0446\u0456\u0439 \u043D\u0435 \u0437\u0430\u043B\u0438\u0448\u0438\u043B\u043E\u0441\u044F</span> }\n } @else if (isImageField(field.value)) {\n @if (getImageUrls(field.value.to.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.to.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-slate-100\" (click)=\"openImageViewer(getImageUrls(field.value.to.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u043D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u041D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.to.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.to.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-900\" [innerHTML]=\"field.value.to.display\"></div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.to.url\">{{ field.value.to.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.to.display }}</span>\n }\n }\n </div>\n }\n }\n </div>\n }\n\n @if (getRelatedChanges(operation).length) {\n <div class=\"mt-4 border-t border-slate-100 pt-4\">\n <button\n type=\"button\"\n class=\"flex w-full items-center justify-between gap-3 text-left text-sm text-slate-600\"\n [attr.aria-expanded]=\"isExpanded(operation.id)\"\n (click)=\"toggleOperation(operation.id)\"\n >\n <span>\n \u0414\u0435\u0442\u0430\u043B\u0456 \u0437\u043C\u0456\u043D:\n <span class=\"font-medium text-slate-900\">{{ getAffectedSummary(operation) }}</span>\n </span>\n <span class=\"text-lg leading-none text-slate-400\">{{ isExpanded(operation.id) ? '\u2212' : '+' }}</span>\n </button>\n\n @if (isExpanded(operation.id)) {\n <div class=\"mt-4 space-y-4\">\n @for (change of getRelatedChanges(operation); track change.entityId + change.entityType) {\n <div class=\"rounded-xl bg-slate-50 p-3\">\n <p class=\"mb-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-500\">\n {{ getEntityLabel(change.entityType, change.entityLabel) }} \u00B7 {{ getActionLabel(change.action) }}\n </p>\n @if (getChangeContextLabel(change)) {\n <p class=\"mb-2 text-sm font-medium text-slate-700\">{{ getChangeContextLabel(change) }}</p>\n }\n @for (field of (change.changedFields | keyvalue); track field.key) {\n <div class=\"flex flex-wrap items-center gap-2 text-sm\">\n <span class=\"font-medium text-slate-700\">{{ field.value.label }}</span>\n @if (field.value.summary) { <span class=\"text-xs text-slate-500\">{{ field.value.summary }}</span> }\n @if (isStructuredField(field.value)) {\n @if (getStructuredItems(field.value.from).length) {\n <div class=\"min-w-[16rem] flex-1 space-y-1\">\n <div class=\"text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-400\">\u0411\u0443\u043B\u043E</div>\n @for (item of getStructuredItems(field.value.from); track item.key) {\n <div class=\"rounded-lg border border-slate-200 bg-white px-2 py-1 text-xs text-slate-600\">\n @for (itemField of item.fields; track itemField.key) {<span class=\"mr-2\"><span class=\"font-medium\">{{ itemField.label }}:</span> {{ itemField.display }}</span>}\n </div>\n }\n </div>\n } @else { <span class=\"text-slate-400\">\u041F\u043E\u0437\u0438\u0446\u0456\u0439 \u0434\u043E \u0437\u043C\u0456\u043D\u0438 \u043D\u0435 \u0431\u0443\u043B\u043E</span> }\n } @else if (isImageField(field.value)) {\n @if (getImageUrls(field.value.from.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.from.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-white\" (click)=\"openImageViewer(getImageUrls(field.value.from.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u0441\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u0421\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.from.url) {\n <a class=\"text-sky-700 underline\" [href]=\"field.value.from.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.from.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-500\" [innerHTML]=\"field.value.from.display\"></div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.from.url) {\n <a class=\"text-sky-700 underline\" [routerLink]=\"field.value.from.url\">{{ field.value.from.display }}</a>\n } @else {\n <span class=\"text-slate-400\">{{ field.value.from.display }}</span>\n }\n }\n <span class=\"mx-1 shrink-0 text-lg font-semibold text-slate-500\">\u2192</span>\n @if (isStructuredField(field.value)) {\n @if (getStructuredItems(field.value.to).length) {\n <div class=\"min-w-[16rem] flex-1 space-y-1\">\n <div class=\"text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-400\">\u0421\u0442\u0430\u043B\u043E</div>\n @for (item of getStructuredItems(field.value.to); track item.key) {\n <div class=\"rounded-lg border border-slate-200 bg-slate-50 px-2 py-1 text-xs text-slate-600\">\n @for (itemField of item.fields; track itemField.key) {<span class=\"mr-2\"><span class=\"font-medium\">{{ itemField.label }}:</span> {{ itemField.display }}</span>}\n </div>\n }\n </div>\n } @else { <span class=\"text-slate-400\">\u041F\u0456\u0441\u043B\u044F \u0437\u043C\u0456\u043D\u0438 \u043F\u043E\u0437\u0438\u0446\u0456\u0439 \u043D\u0435 \u0437\u0430\u043B\u0438\u0448\u0438\u043B\u043E\u0441\u044F</span> }\n } @else if (isImageField(field.value)) {\n @if (getImageUrls(field.value.to.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.to.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-white\" (click)=\"openImageViewer(getImageUrls(field.value.to.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u043D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u041D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.to.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.to.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-900\" [innerHTML]=\"field.value.to.display\"></div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.to.url\">{{ field.value.to.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.to.display }}</span>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </article>\n }\n </div>\n }\n </div>\n\n <app-ui-paginator\n class=\"block shrink-0\"\n [pageIndex]=\"page()\"\n [pageSize]=\"pageSize()\"\n [total]=\"total()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n (pageChange)=\"onPageChange($event)\"\n (pageSizeChange)=\"onPageSizeChange($event)\"\n ></app-ui-paginator>\n</section>\n\n@if (isViewerOpen()) {\n <app-images-viewer [urls]=\"viewerUrls()\" (closed)=\"closeImageViewer()\" />\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ImagesViewerComponent, selector: "app-images-viewer", inputs: ["urls", "initialIndex"], outputs: ["closed"] }, { kind: "component", type: InputComponent, selector: "input-base", inputs: ["label", "hint", "placeholder", "name", "id", "type", "element", "rows", "required", "autocomplete", "pattern", "error", "actions"], outputs: ["actionTriggered"] }, { kind: "component", type: InputSelectComponent, selector: "input-select", inputs: ["label", "hint", "error", "placeholder", "name", "data", "dataParams", "required", "disabled", "multiple", "emptyStateLabel", "searchable", "options", "triggerClass", "panelClass"], outputs: ["valueChange", "searchChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["variant", "size", "disabled", "fullWidth", "type", "withShadow", "icon", "iconOnly", "ariaLabel"] }, { kind: "component", type: UiPaginatorComponent, selector: "app-ui-paginator", inputs: ["pageIndex", "pageSize", "total", "pageSizeOptions"], outputs: ["pageChange", "pageSizeChange"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }] });
|
|
1926
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: HistoryTabComponent, isStandalone: true, selector: "app-history-tab", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<section class=\"flex h-[calc(100dvh-14rem)] min-h-[24rem] min-w-0 flex-col gap-4 overflow-hidden\">\n <div class=\"flex shrink-0 flex-wrap items-end gap-4 rounded-3xl border border-slate-200 bg-white p-5\">\n <div class=\"min-w-56 flex-1\">\n <input-base label=\"\u0412\u0456\u0434\" type=\"datetime-local\" [ngModel]=\"fromDate()\" (ngModelChange)=\"onDateFilterChange('from', $event)\" />\n </div>\n <div class=\"min-w-56 flex-1\">\n <input-base label=\"\u0414\u043E\" type=\"datetime-local\" [ngModel]=\"toDate()\" (ngModelChange)=\"onDateFilterChange('to', $event)\" />\n </div>\n <div class=\"min-w-56 flex-1\">\n <input-select label=\"\u0414\u0456\u044F\" placeholder=\"\u0423\u0441\u0456 \u0434\u0456\u0457\" [options]=\"availableActionOptions()\" [searchable]=\"false\" [ngModel]=\"actionFilter()\" (ngModelChange)=\"onActionFilterChange($event)\" />\n </div>\n <div class=\"min-w-56 flex-1\">\n <input-select label=\"\u0429\u043E \u0437\u043C\u0456\u043D\u044E\u0432\u0430\u043B\u043E\u0441\u044F\" placeholder=\"\u0423\u0441\u0456 \u0442\u0438\u043F\u0438 \u0437\u043C\u0456\u043D\" [options]=\"availableChangeTypeOptions()\" [searchable]=\"false\" [ngModel]=\"changeTypeFilter()\" (ngModelChange)=\"onChangeTypeFilterChange($event)\" />\n </div>\n <ui-button variant=\"ghost\" type=\"button\" (click)=\"clearFilters()\">\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u0438</ui-button>\n </div>\n <div class=\"min-h-0 flex-1 overflow-y-auto overscroll-contain pr-1\">\n @if (isLoading()) {\n <div class=\"rounded-2xl border border-slate-200 bg-white p-6 text-sm text-slate-500\">\n \u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u0456\u0441\u0442\u043E\u0440\u0456\u0457\u2026\n </div>\n } @else if (hasError()) {\n <div class=\"rounded-2xl border border-red-200 bg-red-50 p-6 text-sm text-red-700\">\n \u041D\u0435 \u0432\u0434\u0430\u043B\u043E\u0441\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0456\u0441\u0442\u043E\u0440\u0456\u044E \u0437\u043C\u0456\u043D.\n </div>\n } @else if (!operations().length) {\n <div class=\"rounded-2xl border border-slate-200 bg-white p-6 text-sm text-slate-500\">\n \u0406\u0441\u0442\u043E\u0440\u0456\u044F \u0437\u043C\u0456\u043D \u043F\u043E\u043A\u0438 \u043F\u043E\u0440\u043E\u0436\u043D\u044F.\n </div>\n } @else {\n <div class=\"space-y-3\">\n @for (operation of operations(); track operation.id) {\n <article class=\"rounded-2xl border border-slate-200 bg-white p-5 shadow-sm\">\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h3 class=\"text-sm font-semibold text-slate-900\">{{ operation.description }}</h3>\n @if (operation.actorName) {\n <p class=\"mt-1 text-sm text-slate-500\">{{ operation.actorName }}</p>\n }\n </div>\n <time class=\"text-xs text-slate-400\" [dateTime]=\"operation.createdAt\">\n {{ operation.createdAt | date: 'dd.MM.yyyy HH:mm' }}\n </time>\n </div>\n\n @if (getPrimaryChanges(operation).length) {\n <div class=\"mt-4 space-y-2 border-t border-slate-100 pt-4\">\n @for (change of getPrimaryChanges(operation); track change.entityId + change.entityType) {\n @for (field of (change.changedFields | keyvalue); track field.key) {\n <div class=\"flex flex-wrap items-center gap-2 text-sm\">\n <span class=\"font-medium text-slate-700\">{{ field.value.label }}</span>\n @if (field.value.summary) { <span class=\"text-xs text-slate-500\">{{ field.value.summary }}</span> }\n @if (isStructuredField(field.value)) {\n @if (getStructuredItems(field.value.from).length) {\n <div class=\"min-w-[16rem] flex-1 space-y-1\">\n <div class=\"text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-400\">\u0411\u0443\u043B\u043E</div>\n @for (item of getStructuredItems(field.value.from); track item.key) {\n <div class=\"rounded-lg border border-slate-200 bg-slate-50 px-2 py-1 text-xs text-slate-600\">\n @for (itemField of item.fields; track itemField.key) {<span class=\"mr-2\"><span class=\"font-medium\">{{ itemField.label }}:</span> {{ itemField.display }}</span>}\n </div>\n }\n </div>\n } @else { <span class=\"text-slate-400\">\u041F\u043E\u0437\u0438\u0446\u0456\u0439 \u0434\u043E \u0437\u043C\u0456\u043D\u0438 \u043D\u0435 \u0431\u0443\u043B\u043E</span> }\n } @else if (isImageField(field.value)) {\n @if (getImageUrls(field.value.from.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.from.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-slate-100\" (click)=\"openImageViewer(getImageUrls(field.value.from.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u0441\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u0421\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.from.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.from.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.from.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-500\" [innerHTML]=\"field.value.from.display\"></div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.from.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.from.url\">{{ field.value.from.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.from.display }}</span>\n }\n }\n <span class=\"mx-1 shrink-0 text-lg font-semibold text-slate-500\">\u2192</span>\n @if (isStructuredField(field.value)) {\n @if (getStructuredItems(field.value.to).length) {\n <div class=\"min-w-[16rem] flex-1 space-y-1\">\n <div class=\"text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-400\">\u0421\u0442\u0430\u043B\u043E</div>\n @for (item of getStructuredItems(field.value.to); track item.key) {\n <div class=\"rounded-lg border border-slate-200 bg-slate-50 px-2 py-1 text-xs text-slate-600\">\n @for (itemField of item.fields; track itemField.key) {<span class=\"mr-2\"><span class=\"font-medium\">{{ itemField.label }}:</span> {{ itemField.display }}</span>}\n </div>\n }\n </div>\n } @else { <span class=\"text-slate-400\">\u041F\u0456\u0441\u043B\u044F \u0437\u043C\u0456\u043D\u0438 \u043F\u043E\u0437\u0438\u0446\u0456\u0439 \u043D\u0435 \u0437\u0430\u043B\u0438\u0448\u0438\u043B\u043E\u0441\u044F</span> }\n } @else if (isImageField(field.value)) {\n @if (getImageUrls(field.value.to.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.to.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-slate-100\" (click)=\"openImageViewer(getImageUrls(field.value.to.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u043D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u041D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.to.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.to.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-900\" [innerHTML]=\"field.value.to.display\"></div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.to.url\">{{ field.value.to.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.to.display }}</span>\n }\n }\n </div>\n }\n }\n </div>\n }\n\n @if (getRelatedChanges(operation).length) {\n <div class=\"mt-4 border-t border-slate-100 pt-4\">\n <button\n type=\"button\"\n class=\"flex w-full items-center justify-between gap-3 text-left text-sm text-slate-600\"\n [attr.aria-expanded]=\"isExpanded(operation.id)\"\n (click)=\"toggleOperation(operation.id)\"\n >\n <span>\n \u0414\u0435\u0442\u0430\u043B\u0456 \u0437\u043C\u0456\u043D:\n <span class=\"font-medium text-slate-900\">{{ getAffectedSummary(operation) }}</span>\n </span>\n <span class=\"text-lg leading-none text-slate-400\">{{ isExpanded(operation.id) ? '\u2212' : '+' }}</span>\n </button>\n\n @if (isExpanded(operation.id)) {\n <div class=\"mt-4 space-y-4\">\n @for (change of getRelatedChanges(operation); track change.entityId + change.entityType) {\n <div class=\"rounded-xl bg-slate-50 p-3\">\n <p class=\"mb-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-500\">\n {{ getEntityLabel(change.entityType, change.entityLabel) }} \u00B7 {{ getActionLabel(change.action) }}\n </p>\n @if (getChangeContextLabel(change)) {\n <p class=\"mb-2 text-sm font-medium text-slate-700\">{{ getChangeContextLabel(change) }}</p>\n }\n @for (field of (change.changedFields | keyvalue); track field.key) {\n <div class=\"flex flex-wrap items-center gap-2 text-sm\">\n <span class=\"font-medium text-slate-700\">{{ field.value.label }}</span>\n @if (field.value.summary) { <span class=\"text-xs text-slate-500\">{{ field.value.summary }}</span> }\n @if (isStructuredField(field.value)) {\n @if (getStructuredItems(field.value.from).length) {\n <div class=\"min-w-[16rem] flex-1 space-y-1\">\n <div class=\"text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-400\">\u0411\u0443\u043B\u043E</div>\n @for (item of getStructuredItems(field.value.from); track item.key) {\n <div class=\"rounded-lg border border-slate-200 bg-white px-2 py-1 text-xs text-slate-600\">\n @for (itemField of item.fields; track itemField.key) {<span class=\"mr-2\"><span class=\"font-medium\">{{ itemField.label }}:</span> {{ itemField.display }}</span>}\n </div>\n }\n </div>\n } @else { <span class=\"text-slate-400\">\u041F\u043E\u0437\u0438\u0446\u0456\u0439 \u0434\u043E \u0437\u043C\u0456\u043D\u0438 \u043D\u0435 \u0431\u0443\u043B\u043E</span> }\n } @else if (isImageField(field.value)) {\n @if (getImageUrls(field.value.from.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.from.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-white\" (click)=\"openImageViewer(getImageUrls(field.value.from.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u0441\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u0421\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.from.url) {\n <a class=\"text-sky-700 underline\" [href]=\"field.value.from.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.from.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-500\" [innerHTML]=\"field.value.from.display\"></div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.from.url) {\n <a class=\"text-sky-700 underline\" [routerLink]=\"field.value.from.url\">{{ field.value.from.display }}</a>\n } @else {\n <span class=\"text-slate-400\">{{ field.value.from.display }}</span>\n }\n }\n <span class=\"mx-1 shrink-0 text-lg font-semibold text-slate-500\">\u2192</span>\n @if (isStructuredField(field.value)) {\n @if (getStructuredItems(field.value.to).length) {\n <div class=\"min-w-[16rem] flex-1 space-y-1\">\n <div class=\"text-[10px] font-semibold uppercase tracking-[0.16em] text-slate-400\">\u0421\u0442\u0430\u043B\u043E</div>\n @for (item of getStructuredItems(field.value.to); track item.key) {\n <div class=\"rounded-lg border border-slate-200 bg-slate-50 px-2 py-1 text-xs text-slate-600\">\n @for (itemField of item.fields; track itemField.key) {<span class=\"mr-2\"><span class=\"font-medium\">{{ itemField.label }}:</span> {{ itemField.display }}</span>}\n </div>\n }\n </div>\n } @else { <span class=\"text-slate-400\">\u041F\u0456\u0441\u043B\u044F \u0437\u043C\u0456\u043D\u0438 \u043F\u043E\u0437\u0438\u0446\u0456\u0439 \u043D\u0435 \u0437\u0430\u043B\u0438\u0448\u0438\u043B\u043E\u0441\u044F</span> }\n } @else if (isImageField(field.value)) {\n @if (getImageUrls(field.value.to.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.to.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-white\" (click)=\"openImageViewer(getImageUrls(field.value.to.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u043D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u041D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.to.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.to.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-900\" [innerHTML]=\"field.value.to.display\"></div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.to.url\">{{ field.value.to.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.to.display }}</span>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </article>\n }\n </div>\n }\n </div>\n\n <app-ui-paginator\n class=\"block shrink-0\"\n [pageIndex]=\"page()\"\n [pageSize]=\"pageSize()\"\n [total]=\"total()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n (pageChange)=\"onPageChange($event)\"\n (pageSizeChange)=\"onPageSizeChange($event)\"\n ></app-ui-paginator>\n</section>\n\n@if (isViewerOpen()) {\n <app-images-viewer [urls]=\"viewerUrls()\" (closed)=\"closeImageViewer()\" />\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ImagesViewerComponent, selector: "app-images-viewer", inputs: ["urls", "initialIndex"], outputs: ["closed"] }, { kind: "component", type: InputComponent, selector: "input-base", inputs: ["label", "hint", "placeholder", "name", "id", "type", "element", "rows", "required", "autocomplete", "pattern", "error", "actions"], outputs: ["actionTriggered"] }, { kind: "component", type: InputSelectComponent, selector: "input-select", inputs: ["label", "hint", "error", "placeholder", "name", "data", "dataParams", "required", "disabled", "multiple", "emptyStateLabel", "searchable", "options", "triggerClass", "panelClass", "actions"], outputs: ["actionTriggered", "valueChange", "searchChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["variant", "size", "disabled", "fullWidth", "type", "withShadow", "icon", "iconOnly", "ariaLabel"] }, { kind: "component", type: UiPaginatorComponent, selector: "app-ui-paginator", inputs: ["pageIndex", "pageSize", "total", "pageSizeOptions"], outputs: ["pageChange", "pageSizeChange"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }] });
|
|
1960
1927
|
}
|
|
1961
1928
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: HistoryTabComponent, decorators: [{
|
|
1962
1929
|
type: Component,
|