@memberjunction/ng-archive-manager 5.43.0 → 5.44.0
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.
- package/dist/lib/archive-config/archive-config-admin.component.js +65 -74
- package/dist/lib/archive-config/archive-config-admin.component.js.map +1 -1
- package/dist/lib/archive-manager.module.d.ts +2 -1
- package/dist/lib/archive-manager.module.d.ts.map +1 -1
- package/dist/lib/archive-manager.module.js +6 -2
- package/dist/lib/archive-manager.module.js.map +1 -1
- package/dist/lib/archive-restore/archive-restore-dialog.component.js +20 -27
- package/dist/lib/archive-restore/archive-restore-dialog.component.js.map +1 -1
- package/dist/lib/archive-run-viewer/archive-run-viewer.component.js +51 -56
- package/dist/lib/archive-run-viewer/archive-run-viewer.component.js.map +1 -1
- package/package.json +9 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-restore-dialog.component.js","sourceRoot":"","sources":["../../../src/lib/archive-restore/archive-restore-dialog.component.ts","../../../src/lib/archive-restore/archive-restore-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Input,\n Output,\n EventEmitter,\n OnDestroy,\n inject,\n} from '@angular/core';\nimport { RunView } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/** Represents a single archived version of a record */\nexport interface ArchiveVersion {\n /** Archive run detail ID */\n ID: string;\n /** Date the archive was created */\n ArchivedAt: Date;\n /** Number of fields in this archive */\n FieldCount: number;\n /** Size in bytes */\n Bytes: number;\n /** Storage path where archive is stored */\n StoragePath: string;\n /** The archived field data as a JSON string */\n ArchivedData: string;\n}\n\n/** Result status after a restore attempt */\nexport type RestoreStatus = 'idle' | 'loading' | 'restoring' | 'success' | 'error';\n\n@Component({\n standalone: false,\n selector: 'mj-archive-restore-dialog',\n templateUrl: './archive-restore-dialog.component.html',\n styleUrls: ['./archive-restore-dialog.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ArchiveRestoreDialogComponent implements OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n /** All available versions for this record */\n Versions: ArchiveVersion[] = [];\n\n /** Currently selected version for preview */\n SelectedVersion: ArchiveVersion | null = null;\n\n /** Parsed JSON preview of the selected version */\n PreviewJson = '';\n\n /** Current operation status */\n Status: RestoreStatus = 'idle';\n\n /** Error message if restore fails */\n ErrorMessage = '';\n\n /** Success message after restore */\n SuccessMessage = '';\n\n private destroyed = false;\n\n // --- EntityName input ---\n private _entityName = '';\n\n @Input()\n set EntityName(value: string) {\n this._entityName = value;\n }\n get EntityName(): string {\n return this._entityName;\n }\n\n // --- RecordID input ---\n private _recordID = '';\n\n @Input()\n set RecordID(value: string) {\n this._recordID = value;\n }\n get RecordID(): string {\n return this._recordID;\n }\n\n // --- Visible input with reactive loading ---\n private _visible = false;\n\n @Input()\n set Visible(value: boolean) {\n const previous = this._visible;\n this._visible = value;\n if (value && !previous) {\n this.onDialogOpened();\n }\n if (!value && previous) {\n this.onDialogClosed();\n }\n }\n get Visible(): boolean {\n return this._visible;\n }\n\n /** Two-way binding support for Visible */\n @Output() VisibleChange = new EventEmitter<boolean>();\n\n /** Emitted after a successful restore */\n @Output() RestoreCompleted = new EventEmitter<void>();\n\n ngOnDestroy(): void {\n this.destroyed = true;\n }\n\n /** Select a version from the timeline */\n SelectVersion(version: ArchiveVersion): void {\n this.SelectedVersion = version;\n this.PreviewJson = this.formatJson(version.ArchivedData);\n this.cdr.markForCheck();\n }\n\n /** Check if a version is the currently selected one */\n IsSelected(version: ArchiveVersion): boolean {\n return UUIDsEqual(this.SelectedVersion?.ID, version.ID);\n }\n\n /** Initiate restore of the selected version */\n async RestoreSelectedVersion(): Promise<void> {\n if (!this.SelectedVersion) {\n return;\n }\n\n this.Status = 'restoring';\n this.ErrorMessage = '';\n this.SuccessMessage = '';\n this.cdr.markForCheck();\n\n try {\n await this.performRestore(this.SelectedVersion);\n this.Status = 'success';\n this.SuccessMessage = 'Record restored successfully.';\n this.RestoreCompleted.emit();\n } catch (err: unknown) {\n this.Status = 'error';\n this.ErrorMessage = err instanceof Error ? err.message : 'Restore failed. Please try again.';\n } finally {\n if (!this.destroyed) {\n this.cdr.markForCheck();\n }\n }\n }\n\n /** Close the dialog */\n Close(): void {\n this._visible = false;\n this.VisibleChange.emit(false);\n }\n\n /** Format bytes into a human-readable string */\n FormatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB'];\n const k = 1024;\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n const value = bytes / Math.pow(k, i);\n return `${value.toFixed(i > 0 ? 1 : 0)} ${units[i]}`;\n }\n\n /** Format a date for display */\n FormatDate(date: Date): string {\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n }\n\n /** Called when the dialog becomes visible */\n private async onDialogOpened(): Promise<void> {\n this.resetState();\n await this.loadVersions();\n }\n\n /** Called when the dialog is hidden */\n private onDialogClosed(): void {\n this.resetState();\n }\n\n /** Load all archived versions for this record */\n private async loadVersions(): Promise<void> {\n if (!this._entityName || !this._recordID) {\n return;\n }\n\n this.Status = 'loading';\n this.cdr.markForCheck();\n\n try {\n const result = await this.fetchVersionRecords();\n this.Versions = this.mapVersionRecords(result);\n this.autoSelectFirstVersion();\n this.Status = 'idle';\n } catch {\n this.Status = 'error';\n this.ErrorMessage = 'Failed to load archive versions.';\n } finally {\n if (!this.destroyed) {\n this.cdr.markForCheck();\n }\n }\n }\n\n /** Fetch version records from the server */\n private async fetchVersionRecords(): Promise<{ Success: boolean; Results: VersionDetailRecord[] }> {\n const rv = new RunView();\n const escapedEntity = this._entityName.replace(/'/g, \"''\");\n const escapedRecord = this._recordID.replace(/'/g, \"''\");\n\n return rv.RunView<VersionDetailRecord>({\n EntityName: 'MJ: Archive Run Details',\n ExtraFilter: `Entity='${escapedEntity}' AND RecordID='${escapedRecord}' AND Status='Success'`,\n OrderBy: '__mj_CreatedAt DESC',\n ResultType: 'simple',\n Fields: ['ID', 'BytesArchived', 'StoragePath', '__mj_CreatedAt'],\n });\n }\n\n /** Map raw records to ArchiveVersion objects */\n private mapVersionRecords(result: { Success: boolean; Results: VersionDetailRecord[] }): ArchiveVersion[] {\n if (!result.Success) {\n return [];\n }\n return result.Results.map((r) => ({\n ID: r.ID,\n ArchivedAt: new Date(r.__mj_CreatedAt),\n FieldCount: 0,\n Bytes: r.BytesArchived ?? 0,\n StoragePath: r.StoragePath ?? '',\n ArchivedData: '{}',\n }));\n }\n\n /** Auto-select the first (newest) version if available */\n private autoSelectFirstVersion(): void {\n if (this.Versions.length > 0) {\n this.SelectVersion(this.Versions[0]);\n }\n }\n\n /** Perform the actual restore operation */\n private async performRestore(_version: ArchiveVersion): Promise<void> {\n // TODO: Integrate with ArchivingEngine.RestoreRecord() when available\n // For now, simulate a brief delay to represent the async operation\n await new Promise<void>((resolve) => setTimeout(resolve, 500));\n }\n\n /** Format a JSON string for pretty display */\n private formatJson(jsonString: string): string {\n try {\n const parsed = JSON.parse(jsonString);\n return JSON.stringify(parsed, null, 2);\n } catch {\n return jsonString;\n }\n }\n\n /** Reset all dialog state */\n private resetState(): void {\n this.Versions = [];\n this.SelectedVersion = null;\n this.PreviewJson = '';\n this.Status = 'idle';\n this.ErrorMessage = '';\n this.SuccessMessage = '';\n }\n}\n\n/** Shape of a version detail record from the simple RunView query */\ninterface VersionDetailRecord {\n ID: string;\n BytesArchived: number;\n StoragePath: string;\n __mj_CreatedAt: string;\n}\n","@if (Visible) {\n <!-- Backdrop overlay -->\n <div class=\"restore-overlay\" (click)=\"Close()\"></div>\n\n <!-- Modal dialog -->\n <div class=\"restore-dialog\" role=\"dialog\" aria-labelledby=\"restore-dialog-title\">\n <!-- Header -->\n <div class=\"restore-header\">\n <h2 id=\"restore-dialog-title\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n Archive History\n </h2>\n <button class=\"restore-close-btn\" (click)=\"Close()\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <!-- Loading state -->\n @if (Status === 'loading') {\n <div class=\"restore-loading\">\n <mj-loading text=\"Loading archived versions...\"></mj-loading>\n </div>\n }\n\n <!-- Content area -->\n @if (Status !== 'loading') {\n <div class=\"restore-content\">\n <!-- Left: Timeline -->\n <div class=\"restore-timeline-panel\">\n @if (Versions.length === 0) {\n <div class=\"restore-empty\">\n <i class=\"fa-solid fa-inbox\"></i>\n <p>No archived versions found.</p>\n </div>\n }\n\n @if (Versions.length > 0) {\n <div class=\"timeline\">\n @for (version of Versions; track version.ID) {\n <div\n class=\"timeline-node\"\n [class.active]=\"IsSelected(version)\"\n (click)=\"SelectVersion(version)\"\n (keydown.enter)=\"SelectVersion(version)\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'Version from ' + FormatDate(version.ArchivedAt)\">\n <div class=\"timeline-dot\"></div>\n <div class=\"timeline-content\">\n <span class=\"timeline-date\">{{ FormatDate(version.ArchivedAt) }}</span>\n <span class=\"timeline-meta\">\n {{ version.FieldCount }} field{{ version.FieldCount !== 1 ? 's' : '' }}\n ·\n {{ FormatBytes(version.Bytes) }}\n </span>\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Right: Preview -->\n <div class=\"restore-preview-panel\">\n @if (SelectedVersion) {\n <div class=\"preview-header\">\n <h3>Archived Fields Preview</h3>\n <span class=\"preview-date\">{{ FormatDate(SelectedVersion.ArchivedAt) }}</span>\n </div>\n <pre class=\"preview-json\">{{ PreviewJson }}</pre>\n }\n\n @if (!SelectedVersion && Versions.length > 0) {\n <div class=\"preview-placeholder\">\n <i class=\"fa-solid fa-hand-pointer\"></i>\n <p>Select a version to preview</p>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Status messages -->\n @if (Status === 'success') {\n <div class=\"restore-message restore-message-success\">\n <i class=\"fa-solid fa-circle-check\"></i>\n {{ SuccessMessage }}\n </div>\n }\n @if (Status === 'error' && ErrorMessage) {\n <div class=\"restore-message restore-message-error\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n {{ ErrorMessage }}\n </div>\n }\n\n <!-- Action bar -->\n <div class=\"restore-actions\">\n <button\n class=\"btn btn-primary\"\n [disabled]=\"!SelectedVersion || Status === 'restoring'\"\n (click)=\"RestoreSelectedVersion()\">\n @if (Status === 'restoring') {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Restoring...\n } @else {\n <i class=\"fa-solid fa-rotate-left\"></i>\n Restore to this Version\n }\n </button>\n <button class=\"btn btn-secondary\" (click)=\"Close()\">\n Close\n </button>\n </div>\n </div>\n}\n"]}
|
|
1
|
+
{"version":3,"file":"archive-restore-dialog.component.js","sourceRoot":"","sources":["../../../src/lib/archive-restore/archive-restore-dialog.component.ts","../../../src/lib/archive-restore/archive-restore-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Input,\n Output,\n EventEmitter,\n OnDestroy,\n inject,\n} from '@angular/core';\nimport { RunView } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/** Represents a single archived version of a record */\nexport interface ArchiveVersion {\n /** Archive run detail ID */\n ID: string;\n /** Date the archive was created */\n ArchivedAt: Date;\n /** Number of fields in this archive */\n FieldCount: number;\n /** Size in bytes */\n Bytes: number;\n /** Storage path where archive is stored */\n StoragePath: string;\n /** The archived field data as a JSON string */\n ArchivedData: string;\n}\n\n/** Result status after a restore attempt */\nexport type RestoreStatus = 'idle' | 'loading' | 'restoring' | 'success' | 'error';\n\n@Component({\n standalone: false,\n selector: 'mj-archive-restore-dialog',\n templateUrl: './archive-restore-dialog.component.html',\n styleUrls: ['./archive-restore-dialog.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ArchiveRestoreDialogComponent implements OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n /** All available versions for this record */\n Versions: ArchiveVersion[] = [];\n\n /** Currently selected version for preview */\n SelectedVersion: ArchiveVersion | null = null;\n\n /** Parsed JSON preview of the selected version */\n PreviewJson = '';\n\n /** Current operation status */\n Status: RestoreStatus = 'idle';\n\n /** Error message if restore fails */\n ErrorMessage = '';\n\n /** Success message after restore */\n SuccessMessage = '';\n\n private destroyed = false;\n\n // --- EntityName input ---\n private _entityName = '';\n\n @Input()\n set EntityName(value: string) {\n this._entityName = value;\n }\n get EntityName(): string {\n return this._entityName;\n }\n\n // --- RecordID input ---\n private _recordID = '';\n\n @Input()\n set RecordID(value: string) {\n this._recordID = value;\n }\n get RecordID(): string {\n return this._recordID;\n }\n\n // --- Visible input with reactive loading ---\n private _visible = false;\n\n @Input()\n set Visible(value: boolean) {\n const previous = this._visible;\n this._visible = value;\n if (value && !previous) {\n this.onDialogOpened();\n }\n if (!value && previous) {\n this.onDialogClosed();\n }\n }\n get Visible(): boolean {\n return this._visible;\n }\n\n /** Two-way binding support for Visible */\n @Output() VisibleChange = new EventEmitter<boolean>();\n\n /** Emitted after a successful restore */\n @Output() RestoreCompleted = new EventEmitter<void>();\n\n ngOnDestroy(): void {\n this.destroyed = true;\n }\n\n /** Select a version from the timeline */\n SelectVersion(version: ArchiveVersion): void {\n this.SelectedVersion = version;\n this.PreviewJson = this.formatJson(version.ArchivedData);\n this.cdr.markForCheck();\n }\n\n /** Check if a version is the currently selected one */\n IsSelected(version: ArchiveVersion): boolean {\n return UUIDsEqual(this.SelectedVersion?.ID, version.ID);\n }\n\n /** Initiate restore of the selected version */\n async RestoreSelectedVersion(): Promise<void> {\n if (!this.SelectedVersion) {\n return;\n }\n\n this.Status = 'restoring';\n this.ErrorMessage = '';\n this.SuccessMessage = '';\n this.cdr.markForCheck();\n\n try {\n await this.performRestore(this.SelectedVersion);\n this.Status = 'success';\n this.SuccessMessage = 'Record restored successfully.';\n this.RestoreCompleted.emit();\n } catch (err: unknown) {\n this.Status = 'error';\n this.ErrorMessage = err instanceof Error ? err.message : 'Restore failed. Please try again.';\n } finally {\n if (!this.destroyed) {\n this.cdr.markForCheck();\n }\n }\n }\n\n /** Close the dialog */\n Close(): void {\n this._visible = false;\n this.VisibleChange.emit(false);\n }\n\n /** Format bytes into a human-readable string */\n FormatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB'];\n const k = 1024;\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n const value = bytes / Math.pow(k, i);\n return `${value.toFixed(i > 0 ? 1 : 0)} ${units[i]}`;\n }\n\n /** Format a date for display */\n FormatDate(date: Date): string {\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n }\n\n /** Called when the dialog becomes visible */\n private async onDialogOpened(): Promise<void> {\n this.resetState();\n await this.loadVersions();\n }\n\n /** Called when the dialog is hidden */\n private onDialogClosed(): void {\n this.resetState();\n }\n\n /** Load all archived versions for this record */\n private async loadVersions(): Promise<void> {\n if (!this._entityName || !this._recordID) {\n return;\n }\n\n this.Status = 'loading';\n this.cdr.markForCheck();\n\n try {\n const result = await this.fetchVersionRecords();\n this.Versions = this.mapVersionRecords(result);\n this.autoSelectFirstVersion();\n this.Status = 'idle';\n } catch {\n this.Status = 'error';\n this.ErrorMessage = 'Failed to load archive versions.';\n } finally {\n if (!this.destroyed) {\n this.cdr.markForCheck();\n }\n }\n }\n\n /** Fetch version records from the server */\n private async fetchVersionRecords(): Promise<{ Success: boolean; Results: VersionDetailRecord[] }> {\n const rv = new RunView();\n const escapedEntity = this._entityName.replace(/'/g, \"''\");\n const escapedRecord = this._recordID.replace(/'/g, \"''\");\n\n return rv.RunView<VersionDetailRecord>({\n EntityName: 'MJ: Archive Run Details',\n ExtraFilter: `Entity='${escapedEntity}' AND RecordID='${escapedRecord}' AND Status='Success'`,\n OrderBy: '__mj_CreatedAt DESC',\n ResultType: 'simple',\n Fields: ['ID', 'BytesArchived', 'StoragePath', '__mj_CreatedAt'],\n });\n }\n\n /** Map raw records to ArchiveVersion objects */\n private mapVersionRecords(result: { Success: boolean; Results: VersionDetailRecord[] }): ArchiveVersion[] {\n if (!result.Success) {\n return [];\n }\n return result.Results.map((r) => ({\n ID: r.ID,\n ArchivedAt: new Date(r.__mj_CreatedAt),\n FieldCount: 0,\n Bytes: r.BytesArchived ?? 0,\n StoragePath: r.StoragePath ?? '',\n ArchivedData: '{}',\n }));\n }\n\n /** Auto-select the first (newest) version if available */\n private autoSelectFirstVersion(): void {\n if (this.Versions.length > 0) {\n this.SelectVersion(this.Versions[0]);\n }\n }\n\n /** Perform the actual restore operation */\n private async performRestore(_version: ArchiveVersion): Promise<void> {\n // TODO: Integrate with ArchivingEngine.RestoreRecord() when available\n // For now, simulate a brief delay to represent the async operation\n await new Promise<void>((resolve) => setTimeout(resolve, 500));\n }\n\n /** Format a JSON string for pretty display */\n private formatJson(jsonString: string): string {\n try {\n const parsed = JSON.parse(jsonString);\n return JSON.stringify(parsed, null, 2);\n } catch {\n return jsonString;\n }\n }\n\n /** Reset all dialog state */\n private resetState(): void {\n this.Versions = [];\n this.SelectedVersion = null;\n this.PreviewJson = '';\n this.Status = 'idle';\n this.ErrorMessage = '';\n this.SuccessMessage = '';\n }\n}\n\n/** Shape of a version detail record from the simple RunView query */\ninterface VersionDetailRecord {\n ID: string;\n BytesArchived: number;\n StoragePath: string;\n __mj_CreatedAt: string;\n}\n","@if (Visible) {\n <!-- Backdrop overlay -->\n <div class=\"restore-overlay\" (click)=\"Close()\"></div>\n\n <!-- Modal dialog -->\n <div class=\"restore-dialog\" role=\"dialog\" aria-labelledby=\"restore-dialog-title\">\n <!-- Header -->\n <div class=\"restore-header\">\n <h2 id=\"restore-dialog-title\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n Archive History\n </h2>\n <button class=\"restore-close-btn\" (click)=\"Close()\" aria-label=\"Close dialog\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <!-- Loading state -->\n @if (Status === 'loading') {\n <div class=\"restore-loading\">\n <mj-loading text=\"Loading archived versions...\"></mj-loading>\n </div>\n }\n\n <!-- Content area -->\n @if (Status !== 'loading') {\n <div class=\"restore-content\">\n <!-- Left: Timeline -->\n <div class=\"restore-timeline-panel\">\n @if (Versions.length === 0) {\n <mj-empty-state class=\"restore-empty\" Size=\"compact\"\n Icon=\"fa-solid fa-inbox\"\n Title=\"No archived versions found.\" />\n }\n\n @if (Versions.length > 0) {\n <div class=\"timeline\">\n @for (version of Versions; track version.ID) {\n <div\n class=\"timeline-node\"\n [class.active]=\"IsSelected(version)\"\n (click)=\"SelectVersion(version)\"\n (keydown.enter)=\"SelectVersion(version)\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'Version from ' + FormatDate(version.ArchivedAt)\">\n <div class=\"timeline-dot\"></div>\n <div class=\"timeline-content\">\n <span class=\"timeline-date\">{{ FormatDate(version.ArchivedAt) }}</span>\n <span class=\"timeline-meta\">\n {{ version.FieldCount }} field{{ version.FieldCount !== 1 ? 's' : '' }}\n ·\n {{ FormatBytes(version.Bytes) }}\n </span>\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Right: Preview -->\n <div class=\"restore-preview-panel\">\n @if (SelectedVersion) {\n <div class=\"preview-header\">\n <h3>Archived Fields Preview</h3>\n <span class=\"preview-date\">{{ FormatDate(SelectedVersion.ArchivedAt) }}</span>\n </div>\n <pre class=\"preview-json\">{{ PreviewJson }}</pre>\n }\n\n @if (!SelectedVersion && Versions.length > 0) {\n <mj-empty-state class=\"preview-placeholder\"\n Icon=\"fa-solid fa-hand-pointer\"\n Title=\"No version selected\"\n Message=\"Select a version to preview\" />\n }\n </div>\n </div>\n }\n\n <!-- Status messages -->\n @if (Status === 'success') {\n <div class=\"restore-message restore-message-success\">\n <i class=\"fa-solid fa-circle-check\"></i>\n {{ SuccessMessage }}\n </div>\n }\n @if (Status === 'error' && ErrorMessage) {\n <div class=\"restore-message restore-message-error\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n {{ ErrorMessage }}\n </div>\n }\n\n <!-- Action bar -->\n <div class=\"restore-actions\">\n <button\n class=\"btn btn-primary\"\n [disabled]=\"!SelectedVersion || Status === 'restoring'\"\n (click)=\"RestoreSelectedVersion()\">\n @if (Status === 'restoring') {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Restoring...\n } @else {\n <i class=\"fa-solid fa-rotate-left\"></i>\n Restore to this Version\n }\n </button>\n <button class=\"btn btn-secondary\" (click)=\"Close()\">\n Close\n </button>\n </div>\n </div>\n}\n"]}
|
|
@@ -3,6 +3,7 @@ import { RunView } from '@memberjunction/core';
|
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import * as i1 from "@angular/common";
|
|
5
5
|
import * as i2 from "@memberjunction/ng-shared-generic";
|
|
6
|
+
import * as i3 from "@memberjunction/ng-ui-components";
|
|
6
7
|
const _forTrack0 = ($index, $item) => $item.ID;
|
|
7
8
|
function ArchiveRunViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
8
9
|
i0.ɵɵelementStart(0, "div", 1);
|
|
@@ -10,15 +11,11 @@ function ArchiveRunViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1)
|
|
|
10
11
|
i0.ɵɵelementEnd();
|
|
11
12
|
} }
|
|
12
13
|
function ArchiveRunViewerComponent_Conditional_2_Conditional_36_Template(rf, ctx) { if (rf & 1) {
|
|
13
|
-
i0.ɵɵ
|
|
14
|
-
i0.ɵɵelement(1, "i", 22);
|
|
15
|
-
i0.ɵɵelementStart(2, "p");
|
|
16
|
-
i0.ɵɵtext(3, "No archive runs found.");
|
|
17
|
-
i0.ɵɵelementEnd()();
|
|
14
|
+
i0.ɵɵelement(0, "mj-empty-state", 20);
|
|
18
15
|
} }
|
|
19
16
|
function ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
20
|
-
i0.ɵɵelementStart(0, "span",
|
|
21
|
-
i0.ɵɵelement(1, "i",
|
|
17
|
+
i0.ɵɵelementStart(0, "span", 30);
|
|
18
|
+
i0.ɵɵelement(1, "i", 34);
|
|
22
19
|
i0.ɵɵtext(2);
|
|
23
20
|
i0.ɵɵelementEnd();
|
|
24
21
|
} if (rf & 2) {
|
|
@@ -27,8 +24,8 @@ function ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_13_Template(
|
|
|
27
24
|
i0.ɵɵtextInterpolate1(" ", run_r2.RecordsFailed, " failed ");
|
|
28
25
|
} }
|
|
29
26
|
function ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
30
|
-
i0.ɵɵelementStart(0, "span",
|
|
31
|
-
i0.ɵɵelement(1, "i",
|
|
27
|
+
i0.ɵɵelementStart(0, "span", 31);
|
|
28
|
+
i0.ɵɵelement(1, "i", 35);
|
|
32
29
|
i0.ɵɵtext(2);
|
|
33
30
|
i0.ɵɵelementEnd();
|
|
34
31
|
} if (rf & 2) {
|
|
@@ -38,30 +35,30 @@ function ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_14_Template(
|
|
|
38
35
|
} }
|
|
39
36
|
function ArchiveRunViewerComponent_Conditional_2_For_38_Template(rf, ctx) { if (rf & 1) {
|
|
40
37
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
41
|
-
i0.ɵɵelementStart(0, "div",
|
|
38
|
+
i0.ɵɵelementStart(0, "div", 22);
|
|
42
39
|
i0.ɵɵlistener("click", function ArchiveRunViewerComponent_Conditional_2_For_38_Template_div_click_0_listener() { const run_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.OpenRunDrawer(run_r2)); })("keydown.enter", function ArchiveRunViewerComponent_Conditional_2_For_38_Template_div_keydown_enter_0_listener() { const run_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.OpenRunDrawer(run_r2)); });
|
|
43
|
-
i0.ɵɵelementStart(1, "div",
|
|
40
|
+
i0.ɵɵelementStart(1, "div", 23)(2, "span", 24);
|
|
44
41
|
i0.ɵɵtext(3);
|
|
45
42
|
i0.ɵɵelementEnd();
|
|
46
|
-
i0.ɵɵelementStart(4, "span",
|
|
43
|
+
i0.ɵɵelementStart(4, "span", 25);
|
|
47
44
|
i0.ɵɵtext(5);
|
|
48
45
|
i0.ɵɵelementEnd()();
|
|
49
|
-
i0.ɵɵelementStart(6, "div",
|
|
50
|
-
i0.ɵɵelement(7, "i",
|
|
46
|
+
i0.ɵɵelementStart(6, "div", 26);
|
|
47
|
+
i0.ɵɵelement(7, "i", 27);
|
|
51
48
|
i0.ɵɵtext(8);
|
|
52
49
|
i0.ɵɵelementEnd();
|
|
53
|
-
i0.ɵɵelementStart(9, "div",
|
|
50
|
+
i0.ɵɵelementStart(9, "div", 28)(10, "span", 29);
|
|
54
51
|
i0.ɵɵelement(11, "i", 13);
|
|
55
52
|
i0.ɵɵtext(12);
|
|
56
53
|
i0.ɵɵelementEnd();
|
|
57
|
-
i0.ɵɵconditionalCreate(13, ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_13_Template, 3, 1, "span",
|
|
58
|
-
i0.ɵɵconditionalCreate(14, ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_14_Template, 3, 1, "span",
|
|
59
|
-
i0.ɵɵelementStart(15, "span",
|
|
60
|
-
i0.ɵɵelement(16, "i",
|
|
54
|
+
i0.ɵɵconditionalCreate(13, ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_13_Template, 3, 1, "span", 30);
|
|
55
|
+
i0.ɵɵconditionalCreate(14, ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_14_Template, 3, 1, "span", 31);
|
|
56
|
+
i0.ɵɵelementStart(15, "span", 29);
|
|
57
|
+
i0.ɵɵelement(16, "i", 32);
|
|
61
58
|
i0.ɵɵtext(17);
|
|
62
59
|
i0.ɵɵelementEnd();
|
|
63
|
-
i0.ɵɵelementStart(18, "span",
|
|
64
|
-
i0.ɵɵelement(19, "i",
|
|
60
|
+
i0.ɵɵelementStart(18, "span", 29);
|
|
61
|
+
i0.ɵɵelement(19, "i", 33);
|
|
65
62
|
i0.ɵɵtext(20);
|
|
66
63
|
i0.ɵɵelementEnd()()();
|
|
67
64
|
} if (rf & 2) {
|
|
@@ -127,7 +124,7 @@ function ArchiveRunViewerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1)
|
|
|
127
124
|
i0.ɵɵelementStart(33, "div", 18)(34, "h3", 19);
|
|
128
125
|
i0.ɵɵtext(35, "Run History");
|
|
129
126
|
i0.ɵɵelementEnd();
|
|
130
|
-
i0.ɵɵconditionalCreate(36, ArchiveRunViewerComponent_Conditional_2_Conditional_36_Template,
|
|
127
|
+
i0.ɵɵconditionalCreate(36, ArchiveRunViewerComponent_Conditional_2_Conditional_36_Template, 1, 0, "mj-empty-state", 20);
|
|
131
128
|
i0.ɵɵrepeaterCreate(37, ArchiveRunViewerComponent_Conditional_2_For_38_Template, 21, 10, "div", 21, _forTrack0);
|
|
132
129
|
i0.ɵɵelementEnd();
|
|
133
130
|
} if (rf & 2) {
|
|
@@ -147,29 +144,27 @@ function ArchiveRunViewerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1)
|
|
|
147
144
|
} }
|
|
148
145
|
function ArchiveRunViewerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
149
146
|
const _r4 = i0.ɵɵgetCurrentView();
|
|
150
|
-
i0.ɵɵelementStart(0, "div",
|
|
147
|
+
i0.ɵɵelementStart(0, "div", 36);
|
|
151
148
|
i0.ɵɵlistener("click", function ArchiveRunViewerComponent_Conditional_3_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseDrawer()); });
|
|
152
149
|
i0.ɵɵelementEnd();
|
|
153
150
|
} }
|
|
154
151
|
function ArchiveRunViewerComponent_Conditional_5_Conditional_45_Template(rf, ctx) { if (rf & 1) {
|
|
155
|
-
i0.ɵɵelement(0, "mj-loading",
|
|
152
|
+
i0.ɵɵelement(0, "mj-loading", 51);
|
|
156
153
|
} }
|
|
157
154
|
function ArchiveRunViewerComponent_Conditional_5_Conditional_46_Template(rf, ctx) { if (rf & 1) {
|
|
158
|
-
i0.ɵɵ
|
|
159
|
-
i0.ɵɵtext(1, "No detail records found.");
|
|
160
|
-
i0.ɵɵelementEnd();
|
|
155
|
+
i0.ɵɵelement(0, "mj-empty-state", 52);
|
|
161
156
|
} }
|
|
162
157
|
function ArchiveRunViewerComponent_Conditional_5_Conditional_47_For_14_Template(rf, ctx) { if (rf & 1) {
|
|
163
|
-
i0.ɵɵelementStart(0, "tr")(1, "td",
|
|
158
|
+
i0.ɵɵelementStart(0, "tr")(1, "td", 55);
|
|
164
159
|
i0.ɵɵtext(2);
|
|
165
160
|
i0.ɵɵelementEnd();
|
|
166
|
-
i0.ɵɵelementStart(3, "td",
|
|
161
|
+
i0.ɵɵelementStart(3, "td", 56);
|
|
167
162
|
i0.ɵɵtext(4);
|
|
168
163
|
i0.ɵɵelementEnd();
|
|
169
|
-
i0.ɵɵelementStart(5, "td")(6, "span",
|
|
164
|
+
i0.ɵɵelementStart(5, "td")(6, "span", 57);
|
|
170
165
|
i0.ɵɵtext(7);
|
|
171
166
|
i0.ɵɵelementEnd()();
|
|
172
|
-
i0.ɵɵelementStart(8, "td",
|
|
167
|
+
i0.ɵɵelementStart(8, "td", 58);
|
|
173
168
|
i0.ɵɵtext(9);
|
|
174
169
|
i0.ɵɵelementEnd()();
|
|
175
170
|
} if (rf & 2) {
|
|
@@ -189,7 +184,7 @@ function ArchiveRunViewerComponent_Conditional_5_Conditional_47_For_14_Template(
|
|
|
189
184
|
i0.ɵɵtextInterpolate(ctx_r2.FormatBytes(detail_r6.Bytes));
|
|
190
185
|
} }
|
|
191
186
|
function ArchiveRunViewerComponent_Conditional_5_Conditional_47_Template(rf, ctx) { if (rf & 1) {
|
|
192
|
-
i0.ɵɵelementStart(0, "div",
|
|
187
|
+
i0.ɵɵelementStart(0, "div", 53)(1, "table", 54)(2, "thead")(3, "tr")(4, "th");
|
|
193
188
|
i0.ɵɵtext(5, "Entity");
|
|
194
189
|
i0.ɵɵelementEnd();
|
|
195
190
|
i0.ɵɵelementStart(6, "th");
|
|
@@ -211,61 +206,61 @@ function ArchiveRunViewerComponent_Conditional_5_Conditional_47_Template(rf, ctx
|
|
|
211
206
|
} }
|
|
212
207
|
function ArchiveRunViewerComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
213
208
|
const _r5 = i0.ɵɵgetCurrentView();
|
|
214
|
-
i0.ɵɵelementStart(0, "div",
|
|
209
|
+
i0.ɵɵelementStart(0, "div", 37)(1, "h3");
|
|
215
210
|
i0.ɵɵtext(2, "Run Details");
|
|
216
211
|
i0.ɵɵelementEnd();
|
|
217
|
-
i0.ɵɵelementStart(3, "button",
|
|
212
|
+
i0.ɵɵelementStart(3, "button", 38);
|
|
218
213
|
i0.ɵɵlistener("click", function ArchiveRunViewerComponent_Conditional_5_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r5); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseDrawer()); });
|
|
219
|
-
i0.ɵɵelement(4, "i",
|
|
214
|
+
i0.ɵɵelement(4, "i", 39);
|
|
220
215
|
i0.ɵɵelementEnd()();
|
|
221
|
-
i0.ɵɵelementStart(5, "div",
|
|
216
|
+
i0.ɵɵelementStart(5, "div", 40)(6, "div", 41)(7, "span", 42);
|
|
222
217
|
i0.ɵɵtext(8, "Configuration");
|
|
223
218
|
i0.ɵɵelementEnd();
|
|
224
|
-
i0.ɵɵelementStart(9, "span",
|
|
219
|
+
i0.ɵɵelementStart(9, "span", 43);
|
|
225
220
|
i0.ɵɵtext(10);
|
|
226
221
|
i0.ɵɵelementEnd()();
|
|
227
|
-
i0.ɵɵelementStart(11, "div",
|
|
222
|
+
i0.ɵɵelementStart(11, "div", 41)(12, "span", 42);
|
|
228
223
|
i0.ɵɵtext(13, "Status");
|
|
229
224
|
i0.ɵɵelementEnd();
|
|
230
|
-
i0.ɵɵelementStart(14, "span",
|
|
225
|
+
i0.ɵɵelementStart(14, "span", 25);
|
|
231
226
|
i0.ɵɵtext(15);
|
|
232
227
|
i0.ɵɵelementEnd()();
|
|
233
|
-
i0.ɵɵelementStart(16, "div",
|
|
228
|
+
i0.ɵɵelementStart(16, "div", 41)(17, "span", 42);
|
|
234
229
|
i0.ɵɵtext(18, "Started");
|
|
235
230
|
i0.ɵɵelementEnd();
|
|
236
|
-
i0.ɵɵelementStart(19, "span",
|
|
231
|
+
i0.ɵɵelementStart(19, "span", 43);
|
|
237
232
|
i0.ɵɵtext(20);
|
|
238
233
|
i0.ɵɵelementEnd()();
|
|
239
|
-
i0.ɵɵelementStart(21, "div",
|
|
234
|
+
i0.ɵɵelementStart(21, "div", 41)(22, "span", 42);
|
|
240
235
|
i0.ɵɵtext(23, "Duration");
|
|
241
236
|
i0.ɵɵelementEnd();
|
|
242
|
-
i0.ɵɵelementStart(24, "span",
|
|
237
|
+
i0.ɵɵelementStart(24, "span", 43);
|
|
243
238
|
i0.ɵɵtext(25);
|
|
244
239
|
i0.ɵɵelementEnd()()();
|
|
245
|
-
i0.ɵɵelementStart(26, "div",
|
|
240
|
+
i0.ɵɵelementStart(26, "div", 44)(27, "div", 45)(28, "span", 46);
|
|
246
241
|
i0.ɵɵtext(29);
|
|
247
242
|
i0.ɵɵelementEnd();
|
|
248
|
-
i0.ɵɵelementStart(30, "span",
|
|
243
|
+
i0.ɵɵelementStart(30, "span", 47);
|
|
249
244
|
i0.ɵɵtext(31, "Archived");
|
|
250
245
|
i0.ɵɵelementEnd()();
|
|
251
|
-
i0.ɵɵelementStart(32, "div",
|
|
246
|
+
i0.ɵɵelementStart(32, "div", 48)(33, "span", 46);
|
|
252
247
|
i0.ɵɵtext(34);
|
|
253
248
|
i0.ɵɵelementEnd();
|
|
254
|
-
i0.ɵɵelementStart(35, "span",
|
|
249
|
+
i0.ɵɵelementStart(35, "span", 47);
|
|
255
250
|
i0.ɵɵtext(36, "Failed");
|
|
256
251
|
i0.ɵɵelementEnd()();
|
|
257
|
-
i0.ɵɵelementStart(37, "div",
|
|
252
|
+
i0.ɵɵelementStart(37, "div", 49)(38, "span", 46);
|
|
258
253
|
i0.ɵɵtext(39);
|
|
259
254
|
i0.ɵɵelementEnd();
|
|
260
|
-
i0.ɵɵelementStart(40, "span",
|
|
255
|
+
i0.ɵɵelementStart(40, "span", 47);
|
|
261
256
|
i0.ɵɵtext(41, "Skipped");
|
|
262
257
|
i0.ɵɵelementEnd()()();
|
|
263
|
-
i0.ɵɵelementStart(42, "div",
|
|
258
|
+
i0.ɵɵelementStart(42, "div", 50)(43, "h4");
|
|
264
259
|
i0.ɵɵtext(44, "Per-Record Results");
|
|
265
260
|
i0.ɵɵelementEnd();
|
|
266
|
-
i0.ɵɵconditionalCreate(45, ArchiveRunViewerComponent_Conditional_5_Conditional_45_Template, 1, 0, "mj-loading",
|
|
267
|
-
i0.ɵɵconditionalCreate(46, ArchiveRunViewerComponent_Conditional_5_Conditional_46_Template,
|
|
268
|
-
i0.ɵɵconditionalCreate(47, ArchiveRunViewerComponent_Conditional_5_Conditional_47_Template, 15, 0, "div",
|
|
261
|
+
i0.ɵɵconditionalCreate(45, ArchiveRunViewerComponent_Conditional_5_Conditional_45_Template, 1, 0, "mj-loading", 51);
|
|
262
|
+
i0.ɵɵconditionalCreate(46, ArchiveRunViewerComponent_Conditional_5_Conditional_46_Template, 1, 0, "mj-empty-state", 52);
|
|
263
|
+
i0.ɵɵconditionalCreate(47, ArchiveRunViewerComponent_Conditional_5_Conditional_47_Template, 15, 0, "div", 53);
|
|
269
264
|
i0.ɵɵelementEnd();
|
|
270
265
|
} if (rf & 2) {
|
|
271
266
|
const ctx_r2 = i0.ɵɵnextContext();
|
|
@@ -486,7 +481,7 @@ export class ArchiveRunViewerComponent {
|
|
|
486
481
|
}
|
|
487
482
|
}
|
|
488
483
|
static ɵfac = function ArchiveRunViewerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ArchiveRunViewerComponent)(); };
|
|
489
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ArchiveRunViewerComponent, selectors: [["mj-archive-run-viewer"]], standalone: false, decls: 6, vars: 6, consts: [[1, "run-viewer"], [1, "run-viewer-loading"], [1, "archive-drawer-backdrop"], [1, "archive-drawer"], ["text", "Loading archive runs..."], [1, "metrics-row"], [1, "metric-card"], [1, "metric-icon"], [1, "fa-solid", "fa-play-circle"], [1, "metric-content"], [1, "metric-value"], [1, "metric-label"], [1, "metric-icon", "metric-icon-archive"], [1, "fa-solid", "fa-box-archive"], [1, "metric-icon", "metric-icon-storage"], [1, "fa-solid", "fa-database"], [1, "metric-icon", "metric-icon-time"], [1, "fa-solid", "fa-clock"], [1, "run-list"], [1, "run-list-title"], [
|
|
484
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ArchiveRunViewerComponent, selectors: [["mj-archive-run-viewer"]], standalone: false, decls: 6, vars: 6, consts: [[1, "run-viewer"], [1, "run-viewer-loading"], [1, "archive-drawer-backdrop"], [1, "archive-drawer"], ["text", "Loading archive runs..."], [1, "metrics-row"], [1, "metric-card"], [1, "metric-icon"], [1, "fa-solid", "fa-play-circle"], [1, "metric-content"], [1, "metric-value"], [1, "metric-label"], [1, "metric-icon", "metric-icon-archive"], [1, "fa-solid", "fa-box-archive"], [1, "metric-icon", "metric-icon-storage"], [1, "fa-solid", "fa-database"], [1, "metric-icon", "metric-icon-time"], [1, "fa-solid", "fa-clock"], [1, "run-list"], [1, "run-list-title"], ["Size", "compact", "Icon", "fa-solid fa-inbox", "Title", "No archive runs found.", 1, "run-empty"], ["tabindex", "0", "role", "button", 1, "run-card"], ["tabindex", "0", "role", "button", 1, "run-card", 3, "click", "keydown.enter"], [1, "run-card-header"], [1, "run-card-name"], [1, "status-badge", 3, "ngClass"], [1, "run-card-date"], [1, "fa-solid", "fa-calendar"], [1, "run-card-stats"], [1, "run-stat"], [1, "run-stat", "run-stat-error"], [1, "run-stat", "run-stat-muted"], [1, "fa-solid", "fa-stopwatch"], [1, "fa-solid", "fa-hard-drive"], [1, "fa-solid", "fa-circle-xmark"], [1, "fa-solid", "fa-forward"], [1, "archive-drawer-backdrop", 3, "click"], [1, "drawer-header"], ["aria-label", "Close drawer", 1, "drawer-close-btn", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "drawer-summary"], [1, "drawer-summary-row"], [1, "drawer-label"], [1, "drawer-value"], [1, "drawer-stats"], [1, "drawer-stat"], [1, "drawer-stat-value"], [1, "drawer-stat-label"], [1, "drawer-stat", "drawer-stat-error"], [1, "drawer-stat", "drawer-stat-muted"], [1, "drawer-details"], ["text", "Loading details...", "size", "small"], ["Size", "compact", "Icon", "", "Title", "No detail records found.", 1, "drawer-details-empty"], [1, "drawer-table-wrapper"], [1, "drawer-table"], [1, "cell-entity"], [1, "cell-id", 3, "title"], [1, "status-badge", "status-badge-sm", 3, "ngClass"], [1, "cell-bytes"]], template: function ArchiveRunViewerComponent_Template(rf, ctx) { if (rf & 1) {
|
|
490
485
|
i0.ɵɵelementStart(0, "div", 0);
|
|
491
486
|
i0.ɵɵconditionalCreate(1, ArchiveRunViewerComponent_Conditional_1_Template, 2, 0, "div", 1);
|
|
492
487
|
i0.ɵɵconditionalCreate(2, ArchiveRunViewerComponent_Conditional_2_Template, 39, 5);
|
|
@@ -505,11 +500,11 @@ export class ArchiveRunViewerComponent {
|
|
|
505
500
|
i0.ɵɵclassProp("open", ctx.DrawerOpen);
|
|
506
501
|
i0.ɵɵadvance();
|
|
507
502
|
i0.ɵɵconditional(ctx.SelectedRun ? 5 : -1);
|
|
508
|
-
} }, dependencies: [i1.NgClass, i2.LoadingComponent], styles: [".run-viewer[_ngcontent-%COMP%] {\n padding: 20px;\n position: relative;\n}\n\n\n\n.run-viewer-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 60px;\n}\n\n\n\n.metrics-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.metric-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px 20px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 6%, transparent);\n }\n}\n\n.metric-icon[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 42px;\n height: 42px;\n border-radius: 10px;\n font-size: 1.1rem;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.metric-icon-archive[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.metric-icon-storage[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.metric-icon-time[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-info) 10%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n.metric-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n}\n\n.metric-value[_ngcontent-%COMP%] {\n font-size: 1.25rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.metric-label[_ngcontent-%COMP%] {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n\n\n.run-list-title[_ngcontent-%COMP%] {\n margin: 0 0 14px;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.run-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 40px;\n color: var(--mj-text-muted);\n text-align: center;\n\n i {\n font-size: 2rem;\n margin-bottom: 12px;\n }\n\n p {\n margin: 0;\n font-size: 0.9rem;\n }\n}\n\n\n\n.run-card[_ngcontent-%COMP%] {\n padding: 14px 18px;\n margin-bottom: 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n cursor: pointer;\n transition: border-color 0.15s ease, background 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n background: var(--mj-bg-surface-hover);\n }\n\n &:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n }\n}\n\n.run-card-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n\n.run-card-name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n}\n\n.run-card-date[_ngcontent-%COMP%] {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n\n i {\n margin-right: 4px;\n }\n}\n\n.run-card-stats[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 14px;\n}\n\n.run-stat[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n\n i {\n font-size: 0.72rem;\n }\n}\n\n.run-stat-error[_ngcontent-%COMP%] {\n color: var(--mj-status-error-text);\n}\n\n.run-stat-muted[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n\n\n.status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n font-size: 0.72rem;\n font-weight: 600;\n border-radius: 10px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.status-badge-sm[_ngcontent-%COMP%] {\n padding: 1px 7px;\n font-size: 0.68rem;\n}\n\n.status-badge-success[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.status-badge-error[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.status-badge-info[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.status-badge-default[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n\n\n.archive-drawer-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 999;\n}\n\n\n\n.archive-drawer[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n right: 0;\n width: 400px;\n height: 100vh;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-text-primary) 10%, transparent);\n z-index: 1000;\n transform: translateX(100%);\n transition: transform 0.3s ease;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n &.open {\n transform: translateX(0);\n }\n}\n\n\n\n.drawer-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n\n h3 {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-close-btn[_ngcontent-%COMP%] {\n background: none;\n border: none;\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n color: var(--mj-text-muted);\n font-size: 1rem;\n transition: color 0.15s ease, background 0.15s ease;\n\n &:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n }\n}\n\n\n\n.drawer-summary[_ngcontent-%COMP%] {\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-summary-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 4px 0;\n}\n\n.drawer-label[_ngcontent-%COMP%] {\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.drawer-value[_ngcontent-%COMP%] {\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n}\n\n\n\n.drawer-stats[_ngcontent-%COMP%] {\n display: flex;\n gap: 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-stat[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 14px 10px;\n border-right: 1px solid var(--mj-border-subtle);\n\n &:last-child {\n border-right: none;\n }\n}\n\n.drawer-stat-value[_ngcontent-%COMP%] {\n font-size: 1.2rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.drawer-stat-error[_ngcontent-%COMP%] .drawer-stat-value[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.drawer-stat-muted[_ngcontent-%COMP%] .drawer-stat-value[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.drawer-stat-label[_ngcontent-%COMP%] {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n\n\n.drawer-details[_ngcontent-%COMP%] {\n flex: 1;\n padding: 14px 18px;\n overflow-y: auto;\n\n h4 {\n margin: 0 0 12px;\n font-size: 0.88rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-details-empty[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n text-align: center;\n padding: 20px;\n}\n\n.drawer-table-wrapper[_ngcontent-%COMP%] {\n overflow-x: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n}\n\n.drawer-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.78rem;\n\n th {\n padding: 7px 10px;\n text-align: left;\n font-weight: 600;\n font-size: 0.72rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n td {\n padding: 6px 10px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n }\n\n tr:last-child td {\n border-bottom: none;\n }\n}\n\n.cell-entity[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.cell-id[_ngcontent-%COMP%] {\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: monospace;\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n}\n\n.cell-bytes[_ngcontent-%COMP%] {\n white-space: nowrap;\n}\n\n\n\n@media (max-width: 900px) {\n .metrics-row[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n@media (max-width: 600px) {\n .metrics-row[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n}"], changeDetection: 0 });
|
|
503
|
+
} }, dependencies: [i1.NgClass, i2.LoadingComponent, i3.MJEmptyStateComponent], styles: [".run-viewer[_ngcontent-%COMP%] {\n padding: 20px;\n position: relative;\n}\n\n\n\n.run-viewer-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 60px;\n}\n\n\n\n.metrics-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.metric-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px 20px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 6%, transparent);\n }\n}\n\n.metric-icon[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 42px;\n height: 42px;\n border-radius: 10px;\n font-size: 1.1rem;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.metric-icon-archive[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.metric-icon-storage[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.metric-icon-time[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-info) 10%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n.metric-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n}\n\n.metric-value[_ngcontent-%COMP%] {\n font-size: 1.25rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.metric-label[_ngcontent-%COMP%] {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n\n\n.run-list-title[_ngcontent-%COMP%] {\n margin: 0 0 14px;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.run-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 40px;\n color: var(--mj-text-muted);\n text-align: center;\n\n i {\n font-size: 2rem;\n margin-bottom: 12px;\n }\n\n p {\n margin: 0;\n font-size: 0.9rem;\n }\n}\n\n\n\n.run-card[_ngcontent-%COMP%] {\n padding: 14px 18px;\n margin-bottom: 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n cursor: pointer;\n transition: border-color 0.15s ease, background 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n background: var(--mj-bg-surface-hover);\n }\n\n &:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n }\n}\n\n.run-card-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n\n.run-card-name[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n}\n\n.run-card-date[_ngcontent-%COMP%] {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n\n i {\n margin-right: 4px;\n }\n}\n\n.run-card-stats[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 14px;\n}\n\n.run-stat[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n\n i {\n font-size: 0.72rem;\n }\n}\n\n.run-stat-error[_ngcontent-%COMP%] {\n color: var(--mj-status-error-text);\n}\n\n.run-stat-muted[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n\n\n.status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n font-size: 0.72rem;\n font-weight: 600;\n border-radius: 10px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.status-badge-sm[_ngcontent-%COMP%] {\n padding: 1px 7px;\n font-size: 0.68rem;\n}\n\n.status-badge-success[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.status-badge-error[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.status-badge-info[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.status-badge-default[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n\n\n.archive-drawer-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 999;\n}\n\n\n\n.archive-drawer[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n right: 0;\n width: 400px;\n height: 100vh;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-text-primary) 10%, transparent);\n z-index: 1000;\n transform: translateX(100%);\n transition: transform 0.3s ease;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n &.open {\n transform: translateX(0);\n }\n}\n\n\n\n.drawer-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n\n h3 {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-close-btn[_ngcontent-%COMP%] {\n background: none;\n border: none;\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n color: var(--mj-text-muted);\n font-size: 1rem;\n transition: color 0.15s ease, background 0.15s ease;\n\n &:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n }\n}\n\n\n\n.drawer-summary[_ngcontent-%COMP%] {\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-summary-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 4px 0;\n}\n\n.drawer-label[_ngcontent-%COMP%] {\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.drawer-value[_ngcontent-%COMP%] {\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n}\n\n\n\n.drawer-stats[_ngcontent-%COMP%] {\n display: flex;\n gap: 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-stat[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 14px 10px;\n border-right: 1px solid var(--mj-border-subtle);\n\n &:last-child {\n border-right: none;\n }\n}\n\n.drawer-stat-value[_ngcontent-%COMP%] {\n font-size: 1.2rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.drawer-stat-error[_ngcontent-%COMP%] .drawer-stat-value[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n}\n\n.drawer-stat-muted[_ngcontent-%COMP%] .drawer-stat-value[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.drawer-stat-label[_ngcontent-%COMP%] {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n\n\n.drawer-details[_ngcontent-%COMP%] {\n flex: 1;\n padding: 14px 18px;\n overflow-y: auto;\n\n h4 {\n margin: 0 0 12px;\n font-size: 0.88rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-details-empty[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n text-align: center;\n padding: 20px;\n}\n\n.drawer-table-wrapper[_ngcontent-%COMP%] {\n overflow-x: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n}\n\n.drawer-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.78rem;\n\n th {\n padding: 7px 10px;\n text-align: left;\n font-weight: 600;\n font-size: 0.72rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n td {\n padding: 6px 10px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n }\n\n tr:last-child td {\n border-bottom: none;\n }\n}\n\n.cell-entity[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.cell-id[_ngcontent-%COMP%] {\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: monospace;\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n}\n\n.cell-bytes[_ngcontent-%COMP%] {\n white-space: nowrap;\n}\n\n\n\n@media (max-width: 900px) {\n .metrics-row[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n@media (max-width: 600px) {\n .metrics-row[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n}"], changeDetection: 0 });
|
|
509
504
|
}
|
|
510
505
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ArchiveRunViewerComponent, [{
|
|
511
506
|
type: Component,
|
|
512
|
-
args: [{ standalone: false, selector: 'mj-archive-run-viewer', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"run-viewer\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"run-viewer-loading\">\n <mj-loading text=\"Loading archive runs...\"></mj-loading>\n </div>\n }\n\n @if (!IsLoading) {\n <!-- Summary metric cards -->\n <div class=\"metrics-row\">\n <div class=\"metric-card\">\n <div class=\"metric-icon\">\n <i class=\"fa-solid fa-play-circle\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ Metrics.TotalRuns }}</span>\n <span class=\"metric-label\">Total Runs</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-archive\">\n <i class=\"fa-solid fa-box-archive\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ Metrics.TotalRecordsArchived }}</span>\n <span class=\"metric-label\">Records Archived</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-storage\">\n <i class=\"fa-solid fa-database\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ FormatBytes(Metrics.TotalStorageBytes) }}</span>\n <span class=\"metric-label\">Storage Used</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-time\">\n <i class=\"fa-solid fa-clock\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ FormatDate(Metrics.LastRunDate) }}</span>\n <span class=\"metric-label\">Last Run</span>\n </div>\n </div>\n </div>\n\n <!-- Run list -->\n <div class=\"run-list\">\n <h3 class=\"run-list-title\">Run History</h3>\n\n @if (Runs.length === 0) {\n <div class=\"run-empty\">\n <i class=\"fa-solid fa-inbox\"></i>\n <p>No archive runs found.</p>\n </div>\n }\n\n @for (run of Runs; track run.ID) {\n <div\n class=\"run-card\"\n (click)=\"OpenRunDrawer(run)\"\n (keydown.enter)=\"OpenRunDrawer(run)\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'View details for run on ' + FormatDate(run.StartedAt)\">\n <div class=\"run-card-header\">\n <span class=\"run-card-name\">{{ run.ConfigurationName }}</span>\n <span class=\"status-badge\" [ngClass]=\"GetStatusClass(run.Status)\">{{ run.Status }}</span>\n </div>\n <div class=\"run-card-date\">\n <i class=\"fa-solid fa-calendar\"></i>\n {{ FormatDate(run.StartedAt) }}\n </div>\n <div class=\"run-card-stats\">\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-box-archive\"></i>\n {{ run.RecordsArchived }} archived\n </span>\n @if (run.RecordsFailed > 0) {\n <span class=\"run-stat run-stat-error\">\n <i class=\"fa-solid fa-circle-xmark\"></i>\n {{ run.RecordsFailed }} failed\n </span>\n }\n @if (run.RecordsSkipped > 0) {\n <span class=\"run-stat run-stat-muted\">\n <i class=\"fa-solid fa-forward\"></i>\n {{ run.RecordsSkipped }} skipped\n </span>\n }\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-stopwatch\"></i>\n {{ FormatDuration(run.DurationSeconds) }}\n </span>\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-hard-drive\"></i>\n {{ FormatBytes(run.TotalBytes) }}\n </span>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Drawer backdrop -->\n @if (DrawerOpen) {\n <div class=\"archive-drawer-backdrop\" (click)=\"CloseDrawer()\"></div>\n }\n\n <!-- Slide-in drawer -->\n <div class=\"archive-drawer\" [class.open]=\"DrawerOpen\">\n @if (SelectedRun) {\n <!-- Drawer header -->\n <div class=\"drawer-header\">\n <h3>Run Details</h3>\n <button class=\"drawer-close-btn\" (click)=\"CloseDrawer()\" aria-label=\"Close drawer\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <!-- Run summary -->\n <div class=\"drawer-summary\">\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Configuration</span>\n <span class=\"drawer-value\">{{ SelectedRun.ConfigurationName }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Status</span>\n <span class=\"status-badge\" [ngClass]=\"GetStatusClass(SelectedRun.Status)\">{{ SelectedRun.Status }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Started</span>\n <span class=\"drawer-value\">{{ FormatDate(SelectedRun.StartedAt) }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Duration</span>\n <span class=\"drawer-value\">{{ FormatDuration(SelectedRun.DurationSeconds) }}</span>\n </div>\n </div>\n\n <!-- Stats row -->\n <div class=\"drawer-stats\">\n <div class=\"drawer-stat\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsArchived }}</span>\n <span class=\"drawer-stat-label\">Archived</span>\n </div>\n <div class=\"drawer-stat drawer-stat-error\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsFailed }}</span>\n <span class=\"drawer-stat-label\">Failed</span>\n </div>\n <div class=\"drawer-stat drawer-stat-muted\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsSkipped }}</span>\n <span class=\"drawer-stat-label\">Skipped</span>\n </div>\n </div>\n\n <!-- Detail table -->\n <div class=\"drawer-details\">\n <h4>Per-Record Results</h4>\n\n @if (IsDrawerLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n }\n\n @if (!IsDrawerLoading && RunDetails.length === 0) {\n <p class=\"drawer-details-empty\">No detail records found.</p>\n }\n\n @if (!IsDrawerLoading && RunDetails.length > 0) {\n <div class=\"drawer-table-wrapper\">\n <table class=\"drawer-table\">\n <thead>\n <tr>\n <th>Entity</th>\n <th>Record ID</th>\n <th>Status</th>\n <th>Bytes</th>\n </tr>\n </thead>\n <tbody>\n @for (detail of RunDetails; track detail.ID) {\n <tr>\n <td class=\"cell-entity\">{{ detail.EntityName }}</td>\n <td class=\"cell-id\" [title]=\"detail.RecordID\">{{ detail.RecordID }}</td>\n <td>\n <span class=\"status-badge status-badge-sm\" [ngClass]=\"GetStatusClass(detail.Status)\">\n {{ detail.Status }}\n </span>\n </td>\n <td class=\"cell-bytes\">{{ FormatBytes(detail.Bytes) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".run-viewer {\n padding: 20px;\n position: relative;\n}\n\n/* Loading */\n.run-viewer-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 60px;\n}\n\n/* Metric cards row */\n.metrics-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.metric-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px 20px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 6%, transparent);\n }\n}\n\n.metric-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 42px;\n height: 42px;\n border-radius: 10px;\n font-size: 1.1rem;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.metric-icon-archive {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.metric-icon-storage {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.metric-icon-time {\n background: color-mix(in srgb, var(--mj-status-info) 10%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n.metric-content {\n display: flex;\n flex-direction: column;\n}\n\n.metric-value {\n font-size: 1.25rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.metric-label {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* Run list */\n.run-list-title {\n margin: 0 0 14px;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.run-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 40px;\n color: var(--mj-text-muted);\n text-align: center;\n\n i {\n font-size: 2rem;\n margin-bottom: 12px;\n }\n\n p {\n margin: 0;\n font-size: 0.9rem;\n }\n}\n\n/* Run card */\n.run-card {\n padding: 14px 18px;\n margin-bottom: 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n cursor: pointer;\n transition: border-color 0.15s ease, background 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n background: var(--mj-bg-surface-hover);\n }\n\n &:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n }\n}\n\n.run-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n\n.run-card-name {\n font-weight: 600;\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n}\n\n.run-card-date {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n\n i {\n margin-right: 4px;\n }\n}\n\n.run-card-stats {\n display: flex;\n flex-wrap: wrap;\n gap: 14px;\n}\n\n.run-stat {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n\n i {\n font-size: 0.72rem;\n }\n}\n\n.run-stat-error {\n color: var(--mj-status-error-text);\n}\n\n.run-stat-muted {\n color: var(--mj-text-muted);\n}\n\n/* Status badges */\n.status-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n font-size: 0.72rem;\n font-weight: 600;\n border-radius: 10px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.status-badge-sm {\n padding: 1px 7px;\n font-size: 0.68rem;\n}\n\n.status-badge-success {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.status-badge-error {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.status-badge-info {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.status-badge-default {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n/* Drawer backdrop */\n.archive-drawer-backdrop {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 999;\n}\n\n/* Slide-in drawer */\n.archive-drawer {\n position: fixed;\n top: 0;\n right: 0;\n width: 400px;\n height: 100vh;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-text-primary) 10%, transparent);\n z-index: 1000;\n transform: translateX(100%);\n transition: transform 0.3s ease;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n &.open {\n transform: translateX(0);\n }\n}\n\n/* Drawer header */\n.drawer-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n\n h3 {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-close-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n color: var(--mj-text-muted);\n font-size: 1rem;\n transition: color 0.15s ease, background 0.15s ease;\n\n &:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n }\n}\n\n/* Drawer summary */\n.drawer-summary {\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-summary-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 4px 0;\n}\n\n.drawer-label {\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.drawer-value {\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n}\n\n/* Drawer stats */\n.drawer-stats {\n display: flex;\n gap: 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-stat {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 14px 10px;\n border-right: 1px solid var(--mj-border-subtle);\n\n &:last-child {\n border-right: none;\n }\n}\n\n.drawer-stat-value {\n font-size: 1.2rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.drawer-stat-error .drawer-stat-value {\n color: var(--mj-status-error);\n}\n\n.drawer-stat-muted .drawer-stat-value {\n color: var(--mj-text-muted);\n}\n\n.drawer-stat-label {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* Drawer detail table */\n.drawer-details {\n flex: 1;\n padding: 14px 18px;\n overflow-y: auto;\n\n h4 {\n margin: 0 0 12px;\n font-size: 0.88rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-details-empty {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n text-align: center;\n padding: 20px;\n}\n\n.drawer-table-wrapper {\n overflow-x: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n}\n\n.drawer-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.78rem;\n\n th {\n padding: 7px 10px;\n text-align: left;\n font-weight: 600;\n font-size: 0.72rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n td {\n padding: 6px 10px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n }\n\n tr:last-child td {\n border-bottom: none;\n }\n}\n\n.cell-entity {\n font-weight: 500;\n}\n\n.cell-id {\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: monospace;\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n}\n\n.cell-bytes {\n white-space: nowrap;\n}\n\n/* Responsive: stack metric cards on small screens */\n@media (max-width: 900px) {\n .metrics-row {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n@media (max-width: 600px) {\n .metrics-row {\n grid-template-columns: 1fr;\n }\n}\n"] }]
|
|
507
|
+
args: [{ standalone: false, selector: 'mj-archive-run-viewer', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"run-viewer\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"run-viewer-loading\">\n <mj-loading text=\"Loading archive runs...\"></mj-loading>\n </div>\n }\n\n @if (!IsLoading) {\n <!-- Summary metric cards -->\n <div class=\"metrics-row\">\n <div class=\"metric-card\">\n <div class=\"metric-icon\">\n <i class=\"fa-solid fa-play-circle\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ Metrics.TotalRuns }}</span>\n <span class=\"metric-label\">Total Runs</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-archive\">\n <i class=\"fa-solid fa-box-archive\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ Metrics.TotalRecordsArchived }}</span>\n <span class=\"metric-label\">Records Archived</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-storage\">\n <i class=\"fa-solid fa-database\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ FormatBytes(Metrics.TotalStorageBytes) }}</span>\n <span class=\"metric-label\">Storage Used</span>\n </div>\n </div>\n\n <div class=\"metric-card\">\n <div class=\"metric-icon metric-icon-time\">\n <i class=\"fa-solid fa-clock\"></i>\n </div>\n <div class=\"metric-content\">\n <span class=\"metric-value\">{{ FormatDate(Metrics.LastRunDate) }}</span>\n <span class=\"metric-label\">Last Run</span>\n </div>\n </div>\n </div>\n\n <!-- Run list -->\n <div class=\"run-list\">\n <h3 class=\"run-list-title\">Run History</h3>\n\n @if (Runs.length === 0) {\n <mj-empty-state class=\"run-empty\" Size=\"compact\"\n Icon=\"fa-solid fa-inbox\"\n Title=\"No archive runs found.\" />\n }\n\n @for (run of Runs; track run.ID) {\n <div\n class=\"run-card\"\n (click)=\"OpenRunDrawer(run)\"\n (keydown.enter)=\"OpenRunDrawer(run)\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'View details for run on ' + FormatDate(run.StartedAt)\">\n <div class=\"run-card-header\">\n <span class=\"run-card-name\">{{ run.ConfigurationName }}</span>\n <span class=\"status-badge\" [ngClass]=\"GetStatusClass(run.Status)\">{{ run.Status }}</span>\n </div>\n <div class=\"run-card-date\">\n <i class=\"fa-solid fa-calendar\"></i>\n {{ FormatDate(run.StartedAt) }}\n </div>\n <div class=\"run-card-stats\">\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-box-archive\"></i>\n {{ run.RecordsArchived }} archived\n </span>\n @if (run.RecordsFailed > 0) {\n <span class=\"run-stat run-stat-error\">\n <i class=\"fa-solid fa-circle-xmark\"></i>\n {{ run.RecordsFailed }} failed\n </span>\n }\n @if (run.RecordsSkipped > 0) {\n <span class=\"run-stat run-stat-muted\">\n <i class=\"fa-solid fa-forward\"></i>\n {{ run.RecordsSkipped }} skipped\n </span>\n }\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-stopwatch\"></i>\n {{ FormatDuration(run.DurationSeconds) }}\n </span>\n <span class=\"run-stat\">\n <i class=\"fa-solid fa-hard-drive\"></i>\n {{ FormatBytes(run.TotalBytes) }}\n </span>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Drawer backdrop -->\n @if (DrawerOpen) {\n <div class=\"archive-drawer-backdrop\" (click)=\"CloseDrawer()\"></div>\n }\n\n <!-- Slide-in drawer -->\n <div class=\"archive-drawer\" [class.open]=\"DrawerOpen\">\n @if (SelectedRun) {\n <!-- Drawer header -->\n <div class=\"drawer-header\">\n <h3>Run Details</h3>\n <button class=\"drawer-close-btn\" (click)=\"CloseDrawer()\" aria-label=\"Close drawer\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <!-- Run summary -->\n <div class=\"drawer-summary\">\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Configuration</span>\n <span class=\"drawer-value\">{{ SelectedRun.ConfigurationName }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Status</span>\n <span class=\"status-badge\" [ngClass]=\"GetStatusClass(SelectedRun.Status)\">{{ SelectedRun.Status }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Started</span>\n <span class=\"drawer-value\">{{ FormatDate(SelectedRun.StartedAt) }}</span>\n </div>\n <div class=\"drawer-summary-row\">\n <span class=\"drawer-label\">Duration</span>\n <span class=\"drawer-value\">{{ FormatDuration(SelectedRun.DurationSeconds) }}</span>\n </div>\n </div>\n\n <!-- Stats row -->\n <div class=\"drawer-stats\">\n <div class=\"drawer-stat\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsArchived }}</span>\n <span class=\"drawer-stat-label\">Archived</span>\n </div>\n <div class=\"drawer-stat drawer-stat-error\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsFailed }}</span>\n <span class=\"drawer-stat-label\">Failed</span>\n </div>\n <div class=\"drawer-stat drawer-stat-muted\">\n <span class=\"drawer-stat-value\">{{ SelectedRun.RecordsSkipped }}</span>\n <span class=\"drawer-stat-label\">Skipped</span>\n </div>\n </div>\n\n <!-- Detail table -->\n <div class=\"drawer-details\">\n <h4>Per-Record Results</h4>\n\n @if (IsDrawerLoading) {\n <mj-loading text=\"Loading details...\" size=\"small\"></mj-loading>\n }\n\n @if (!IsDrawerLoading && RunDetails.length === 0) {\n <mj-empty-state class=\"drawer-details-empty\" Size=\"compact\" Icon=\"\" Title=\"No detail records found.\" />\n }\n\n @if (!IsDrawerLoading && RunDetails.length > 0) {\n <div class=\"drawer-table-wrapper\">\n <table class=\"drawer-table\">\n <thead>\n <tr>\n <th>Entity</th>\n <th>Record ID</th>\n <th>Status</th>\n <th>Bytes</th>\n </tr>\n </thead>\n <tbody>\n @for (detail of RunDetails; track detail.ID) {\n <tr>\n <td class=\"cell-entity\">{{ detail.EntityName }}</td>\n <td class=\"cell-id\" [title]=\"detail.RecordID\">{{ detail.RecordID }}</td>\n <td>\n <span class=\"status-badge status-badge-sm\" [ngClass]=\"GetStatusClass(detail.Status)\">\n {{ detail.Status }}\n </span>\n </td>\n <td class=\"cell-bytes\">{{ FormatBytes(detail.Bytes) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".run-viewer {\n padding: 20px;\n position: relative;\n}\n\n/* Loading */\n.run-viewer-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 60px;\n}\n\n/* Metric cards row */\n.metrics-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.metric-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px 20px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-text-primary) 6%, transparent);\n }\n}\n\n.metric-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 42px;\n height: 42px;\n border-radius: 10px;\n font-size: 1.1rem;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.metric-icon-archive {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.metric-icon-storage {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.metric-icon-time {\n background: color-mix(in srgb, var(--mj-status-info) 10%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n.metric-content {\n display: flex;\n flex-direction: column;\n}\n\n.metric-value {\n font-size: 1.25rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.metric-label {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* Run list */\n.run-list-title {\n margin: 0 0 14px;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.run-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 40px;\n color: var(--mj-text-muted);\n text-align: center;\n\n i {\n font-size: 2rem;\n margin-bottom: 12px;\n }\n\n p {\n margin: 0;\n font-size: 0.9rem;\n }\n}\n\n/* Run card */\n.run-card {\n padding: 14px 18px;\n margin-bottom: 10px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n cursor: pointer;\n transition: border-color 0.15s ease, background 0.15s ease;\n\n &:hover {\n border-color: var(--mj-border-default);\n background: var(--mj-bg-surface-hover);\n }\n\n &:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n }\n}\n\n.run-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n\n.run-card-name {\n font-weight: 600;\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n}\n\n.run-card-date {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n\n i {\n margin-right: 4px;\n }\n}\n\n.run-card-stats {\n display: flex;\n flex-wrap: wrap;\n gap: 14px;\n}\n\n.run-stat {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n\n i {\n font-size: 0.72rem;\n }\n}\n\n.run-stat-error {\n color: var(--mj-status-error-text);\n}\n\n.run-stat-muted {\n color: var(--mj-text-muted);\n}\n\n/* Status badges */\n.status-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n font-size: 0.72rem;\n font-weight: 600;\n border-radius: 10px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.status-badge-sm {\n padding: 1px 7px;\n font-size: 0.68rem;\n}\n\n.status-badge-success {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.status-badge-error {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.status-badge-info {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.status-badge-default {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n/* Drawer backdrop */\n.archive-drawer-backdrop {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 999;\n}\n\n/* Slide-in drawer */\n.archive-drawer {\n position: fixed;\n top: 0;\n right: 0;\n width: 400px;\n height: 100vh;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-text-primary) 10%, transparent);\n z-index: 1000;\n transform: translateX(100%);\n transition: transform 0.3s ease;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n\n &.open {\n transform: translateX(0);\n }\n}\n\n/* Drawer header */\n.drawer-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n\n h3 {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-close-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n color: var(--mj-text-muted);\n font-size: 1rem;\n transition: color 0.15s ease, background 0.15s ease;\n\n &:hover {\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-hover);\n }\n}\n\n/* Drawer summary */\n.drawer-summary {\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-summary-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 4px 0;\n}\n\n.drawer-label {\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.drawer-value {\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n}\n\n/* Drawer stats */\n.drawer-stats {\n display: flex;\n gap: 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.drawer-stat {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 14px 10px;\n border-right: 1px solid var(--mj-border-subtle);\n\n &:last-child {\n border-right: none;\n }\n}\n\n.drawer-stat-value {\n font-size: 1.2rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.drawer-stat-error .drawer-stat-value {\n color: var(--mj-status-error);\n}\n\n.drawer-stat-muted .drawer-stat-value {\n color: var(--mj-text-muted);\n}\n\n.drawer-stat-label {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* Drawer detail table */\n.drawer-details {\n flex: 1;\n padding: 14px 18px;\n overflow-y: auto;\n\n h4 {\n margin: 0 0 12px;\n font-size: 0.88rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n}\n\n.drawer-details-empty {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n text-align: center;\n padding: 20px;\n}\n\n.drawer-table-wrapper {\n overflow-x: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n}\n\n.drawer-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.78rem;\n\n th {\n padding: 7px 10px;\n text-align: left;\n font-weight: 600;\n font-size: 0.72rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n td {\n padding: 6px 10px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n }\n\n tr:last-child td {\n border-bottom: none;\n }\n}\n\n.cell-entity {\n font-weight: 500;\n}\n\n.cell-id {\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: monospace;\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n}\n\n.cell-bytes {\n white-space: nowrap;\n}\n\n/* Responsive: stack metric cards on small screens */\n@media (max-width: 900px) {\n .metrics-row {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n@media (max-width: 600px) {\n .metrics-row {\n grid-template-columns: 1fr;\n }\n}\n"] }]
|
|
513
508
|
}], null, null); })();
|
|
514
509
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ArchiveRunViewerComponent, { className: "ArchiveRunViewerComponent", filePath: "src/lib/archive-run-viewer/archive-run-viewer.component.ts", lineNumber: 51 }); })();
|
|
515
510
|
//# sourceMappingURL=archive-run-viewer.component.js.map
|