@memberjunction/ng-archive-manager 5.43.0 → 5.45.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.
@@ -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 &middot;\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 &middot;\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.ɵɵelementStart(0, "div", 20);
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", 31);
21
- i0.ɵɵelement(1, "i", 35);
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", 32);
31
- i0.ɵɵelement(1, "i", 36);
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", 23);
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", 24)(2, "span", 25);
40
+ i0.ɵɵelementStart(1, "div", 23)(2, "span", 24);
44
41
  i0.ɵɵtext(3);
45
42
  i0.ɵɵelementEnd();
46
- i0.ɵɵelementStart(4, "span", 26);
43
+ i0.ɵɵelementStart(4, "span", 25);
47
44
  i0.ɵɵtext(5);
48
45
  i0.ɵɵelementEnd()();
49
- i0.ɵɵelementStart(6, "div", 27);
50
- i0.ɵɵelement(7, "i", 28);
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", 29)(10, "span", 30);
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", 31);
58
- i0.ɵɵconditionalCreate(14, ArchiveRunViewerComponent_Conditional_2_For_38_Conditional_14_Template, 3, 1, "span", 32);
59
- i0.ɵɵelementStart(15, "span", 30);
60
- i0.ɵɵelement(16, "i", 33);
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", 30);
64
- i0.ɵɵelement(19, "i", 34);
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, 4, 0, "div", 20);
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", 37);
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", 52);
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.ɵɵelementStart(0, "p", 53);
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", 56);
158
+ i0.ɵɵelementStart(0, "tr")(1, "td", 55);
164
159
  i0.ɵɵtext(2);
165
160
  i0.ɵɵelementEnd();
166
- i0.ɵɵelementStart(3, "td", 57);
161
+ i0.ɵɵelementStart(3, "td", 56);
167
162
  i0.ɵɵtext(4);
168
163
  i0.ɵɵelementEnd();
169
- i0.ɵɵelementStart(5, "td")(6, "span", 58);
164
+ i0.ɵɵelementStart(5, "td")(6, "span", 57);
170
165
  i0.ɵɵtext(7);
171
166
  i0.ɵɵelementEnd()();
172
- i0.ɵɵelementStart(8, "td", 59);
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", 54)(1, "table", 55)(2, "thead")(3, "tr")(4, "th");
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", 38)(1, "h3");
209
+ i0.ɵɵelementStart(0, "div", 37)(1, "h3");
215
210
  i0.ɵɵtext(2, "Run Details");
216
211
  i0.ɵɵelementEnd();
217
- i0.ɵɵelementStart(3, "button", 39);
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", 40);
214
+ i0.ɵɵelement(4, "i", 39);
220
215
  i0.ɵɵelementEnd()();
221
- i0.ɵɵelementStart(5, "div", 41)(6, "div", 42)(7, "span", 43);
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", 44);
219
+ i0.ɵɵelementStart(9, "span", 43);
225
220
  i0.ɵɵtext(10);
226
221
  i0.ɵɵelementEnd()();
227
- i0.ɵɵelementStart(11, "div", 42)(12, "span", 43);
222
+ i0.ɵɵelementStart(11, "div", 41)(12, "span", 42);
228
223
  i0.ɵɵtext(13, "Status");
229
224
  i0.ɵɵelementEnd();
230
- i0.ɵɵelementStart(14, "span", 26);
225
+ i0.ɵɵelementStart(14, "span", 25);
231
226
  i0.ɵɵtext(15);
232
227
  i0.ɵɵelementEnd()();
233
- i0.ɵɵelementStart(16, "div", 42)(17, "span", 43);
228
+ i0.ɵɵelementStart(16, "div", 41)(17, "span", 42);
234
229
  i0.ɵɵtext(18, "Started");
235
230
  i0.ɵɵelementEnd();
236
- i0.ɵɵelementStart(19, "span", 44);
231
+ i0.ɵɵelementStart(19, "span", 43);
237
232
  i0.ɵɵtext(20);
238
233
  i0.ɵɵelementEnd()();
239
- i0.ɵɵelementStart(21, "div", 42)(22, "span", 43);
234
+ i0.ɵɵelementStart(21, "div", 41)(22, "span", 42);
240
235
  i0.ɵɵtext(23, "Duration");
241
236
  i0.ɵɵelementEnd();
242
- i0.ɵɵelementStart(24, "span", 44);
237
+ i0.ɵɵelementStart(24, "span", 43);
243
238
  i0.ɵɵtext(25);
244
239
  i0.ɵɵelementEnd()()();
245
- i0.ɵɵelementStart(26, "div", 45)(27, "div", 46)(28, "span", 47);
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", 48);
243
+ i0.ɵɵelementStart(30, "span", 47);
249
244
  i0.ɵɵtext(31, "Archived");
250
245
  i0.ɵɵelementEnd()();
251
- i0.ɵɵelementStart(32, "div", 49)(33, "span", 47);
246
+ i0.ɵɵelementStart(32, "div", 48)(33, "span", 46);
252
247
  i0.ɵɵtext(34);
253
248
  i0.ɵɵelementEnd();
254
- i0.ɵɵelementStart(35, "span", 48);
249
+ i0.ɵɵelementStart(35, "span", 47);
255
250
  i0.ɵɵtext(36, "Failed");
256
251
  i0.ɵɵelementEnd()();
257
- i0.ɵɵelementStart(37, "div", 50)(38, "span", 47);
252
+ i0.ɵɵelementStart(37, "div", 49)(38, "span", 46);
258
253
  i0.ɵɵtext(39);
259
254
  i0.ɵɵelementEnd();
260
- i0.ɵɵelementStart(40, "span", 48);
255
+ i0.ɵɵelementStart(40, "span", 47);
261
256
  i0.ɵɵtext(41, "Skipped");
262
257
  i0.ɵɵelementEnd()()();
263
- i0.ɵɵelementStart(42, "div", 51)(43, "h4");
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", 52);
267
- i0.ɵɵconditionalCreate(46, ArchiveRunViewerComponent_Conditional_5_Conditional_46_Template, 2, 0, "p", 53);
268
- i0.ɵɵconditionalCreate(47, ArchiveRunViewerComponent_Conditional_5_Conditional_47_Template, 15, 0, "div", 54);
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"], [1, "run-empty"], ["tabindex", "0", "role", "button", 1, "run-card"], [1, "fa-solid", "fa-inbox"], ["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"], [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) {
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