@memberjunction/ng-dashboard-viewer 5.28.0 → 5.30.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/dialogs/add-panel-dialog/add-panel-dialog.component.js +2 -2
- package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.js.map +1 -1
- package/dist/lib/parts/artifact-part.component.d.ts +14 -3
- package/dist/lib/parts/artifact-part.component.d.ts.map +1 -1
- package/dist/lib/parts/artifact-part.component.js +38 -10
- package/dist/lib/parts/artifact-part.component.js.map +1 -1
- package/package.json +10 -10
|
@@ -431,7 +431,7 @@ export class AddPanelDialogComponent {
|
|
|
431
431
|
} if (rf & 2) {
|
|
432
432
|
let _t;
|
|
433
433
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.configPanelContainer = _t.first);
|
|
434
|
-
} }, inputs: { partTypes: "partTypes", visible: "visible" }, outputs: { panelAdded: "panelAdded", cancelled: "cancelled" }, standalone: false, decls: 1, vars: 1, consts: [["configPanelContainer", ""], [1, "add-part-dialog-overlay"], [1, "add-part-dialog-overlay", 3, "click"], [1, "add-part-dialog", 3, "click"], [1, "dialog-header"], [1, "fa-solid", "fa-plus-circle"], [1, "close-button", 3, "click"], [1, "fa-solid", "fa-times"], [1, "dialog-content"], [1, "instruction"], [1, "part-type-grid"], [1, "part-type-card"], [1, "no-types"], [1, "dialog-footer"], [1, "btn-secondary", 3, "click"], [1, "part-type-card", 3, "click"], [1, "card-icon"], [1, "card-content"], [1, "card-title"], [1, "card-description"], [1, "fa-solid", "fa-chevron-right", "card-arrow"], [1, "warning-icon"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "back-button", 3, "click"], [1, "fa-solid", "fa-arrow-left"], [1, "dialog-content", "config-content"], [1, "loading-state"], [1, "error-state"], [1, "config-placeholder"], [1, "validation-errors"], [1, "btn-primary", 3, "click", "disabled"], [1, "fa-solid", "fa-plus"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "error-hint"], [1, "fa-solid", "fa-cog"], [1, "error-item"], [1, "fa-solid", "fa-exclamation-circle"]], template: function AddPanelDialogComponent_Template(rf, ctx) { if (rf & 1) {
|
|
434
|
+
} }, inputs: { partTypes: "partTypes", visible: "visible" }, outputs: { panelAdded: "panelAdded", cancelled: "cancelled" }, standalone: false, decls: 1, vars: 1, consts: [["configPanelContainer", ""], [1, "add-part-dialog-overlay"], [1, "add-part-dialog-overlay", 3, "click"], [1, "add-part-dialog", 3, "click"], [1, "dialog-header"], [1, "fa-solid", "fa-plus-circle"], ["aria-label", "Close dialog", 1, "close-button", 3, "click"], [1, "fa-solid", "fa-times"], [1, "dialog-content"], [1, "instruction"], [1, "part-type-grid"], [1, "part-type-card"], [1, "no-types"], [1, "dialog-footer"], [1, "btn-secondary", 3, "click"], [1, "part-type-card", 3, "click"], [1, "card-icon"], [1, "card-content"], [1, "card-title"], [1, "card-description"], [1, "fa-solid", "fa-chevron-right", "card-arrow"], [1, "warning-icon"], [1, "fa-solid", "fa-exclamation-triangle"], ["aria-label", "Go back", 1, "back-button", 3, "click"], [1, "fa-solid", "fa-arrow-left"], [1, "dialog-content", "config-content"], [1, "loading-state"], [1, "error-state"], [1, "config-placeholder"], [1, "validation-errors"], [1, "btn-primary", 3, "click", "disabled"], [1, "fa-solid", "fa-plus"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "error-hint"], [1, "fa-solid", "fa-cog"], [1, "error-item"], [1, "fa-solid", "fa-exclamation-circle"]], template: function AddPanelDialogComponent_Template(rf, ctx) { if (rf & 1) {
|
|
435
435
|
i0.ɵɵconditionalCreate(0, AddPanelDialogComponent_Conditional_0_Template, 4, 2, "div", 1);
|
|
436
436
|
} if (rf & 2) {
|
|
437
437
|
i0.ɵɵconditional(ctx.visible ? 0 : -1);
|
|
@@ -439,7 +439,7 @@ export class AddPanelDialogComponent {
|
|
|
439
439
|
}
|
|
440
440
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AddPanelDialogComponent, [{
|
|
441
441
|
type: Component,
|
|
442
|
-
args: [{ standalone: false, selector: 'mj-add-panel-dialog', template: "<!-- Add Part Dialog -->\n@if (visible) {\n <div class=\"add-part-dialog-overlay\" (click)=\"onCancel()\">\n <div class=\"add-part-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Step 1: Select Part Type -->\n @if (step === 'select-type') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <h3>\n <i class=\"fa-solid fa-plus-circle\"></i>\n Add Part\n </h3>\n <button class=\"close-button\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content -->\n <div class=\"dialog-content\">\n <p class=\"instruction\">Select a part type to add to your dashboard:</p>\n <div class=\"part-type-grid\">\n @for (partType of partTypes; track partType) {\n <div\n class=\"part-type-card\"\n (click)=\"onPartTypeSelect(partType)\">\n <div class=\"card-icon\">\n <i [class]=\"partType.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n </div>\n <div class=\"card-content\">\n <span class=\"card-title\">{{ partType.Name }}</span>\n @if (partType.Description) {\n <span class=\"card-description\">\n {{ partType.Description }}\n </span>\n }\n </div>\n <i class=\"fa-solid fa-chevron-right card-arrow\"></i>\n </div>\n }\n </div>\n @if (partTypes.length === 0) {\n <div class=\"no-types\">\n <div class=\"warning-icon\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </div>\n <h4>No part types available</h4>\n <p>Dashboard part types have not been configured in the system.</p>\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n <!-- Step 2: Configure Part -->\n @if (step === 'configure') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <button class=\"back-button\" (click)=\"goBack()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n </button>\n <h3>\n <i [class]=\"selectedPartType?.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n Configure {{ getConfigTypeName() }} Part\n </h3>\n <button class=\"close-button\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content - Dynamic Config Panel -->\n <div class=\"dialog-content config-content\">\n <!-- Loading state -->\n @if (isLoadingPanel) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading configuration panel...</span>\n </div>\n }\n <!-- Error state -->\n @if (loadError && !isLoadingPanel) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ loadError }}</span>\n <p class=\"error-hint\">You can still add this part with default settings.</p>\n </div>\n }\n <!-- Dynamic config panel container -->\n <ng-container #configPanelContainer></ng-container>\n <!-- No config panel available (custom type) -->\n @if (!hasConfigPanel() && !isLoadingPanel && !loadError) {\n <div class=\"config-placeholder\">\n <i class=\"fa-solid fa-cog\"></i>\n <p>This part type uses default configuration.</p>\n </div>\n }\n <!-- Validation Errors -->\n @if (currentResult && currentResult.errors.length > 0) {\n <div class=\"validation-errors\">\n @for (error of currentResult.errors; track error) {\n <div class=\"error-item\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n {{ error }}\n </div>\n }\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-primary\" (click)=\"addPart()\" [disabled]=\"!canAddPart && !loadError\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Part\n </button>\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n </div>\n </div>\n}\n", styles: ["/**\n * Add Part Dialog Styles\n */\n\n/* ========================================\n Overlay\n ======================================== */\n\n.add-part-dialog-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n animation: fadeIn 0.15s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* ========================================\n Dialog Container\n ======================================== */\n\n.add-part-dialog {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n width: 560px;\n max-width: 90vw;\n max-height: 85vh;\n overflow: hidden;\n animation: slideUp 0.2s ease;\n}\n\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* ========================================\n Header\n ======================================== */\n\n.dialog-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.dialog-header h3 {\n flex: 1;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.dialog-header h3 i {\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.back-button,\n.close-button {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.back-button:hover,\n.close-button:hover {\n background: rgba(0, 0, 0, 0.08);\n color: var(--mj-text-primary);\n}\n\n.back-button i,\n.close-button i {\n font-size: 16px;\n}\n\n/* ========================================\n Content\n ======================================== */\n\n.dialog-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n min-height: 0;\n}\n\n.instruction {\n margin: 0 0 20px 0;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n/* ========================================\n Part Type Grid\n ======================================== */\n\n.part-type-grid {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.part-type-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 20px;\n border: 2px solid color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-bg-surface);\n}\n\n.part-type-card:hover {\n background: var(--mj-bg-page);\n border-color: var(--mj-brand-primary);\n transform: translateX(4px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.part-type-card:hover .card-arrow {\n opacity: 1;\n transform: translateX(0);\n}\n\n.card-icon {\n width: 56px;\n height: 56px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 12px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.card-icon i {\n font-size: 24px;\n}\n\n.card-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.card-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.card-description {\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n.card-arrow {\n flex-shrink: 0;\n color: var(--mj-brand-primary);\n opacity: 0;\n transform: translateX(-8px);\n transition: all 0.2s ease;\n font-size: 16px;\n}\n\n/* ========================================\n No Types\n ======================================== */\n\n.no-types {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 40px;\n text-align: center;\n}\n\n.no-types .warning-icon {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 16px;\n}\n\n.no-types .warning-icon i {\n font-size: 28px;\n color: var(--mj-status-warning);\n}\n\n.no-types h4 {\n margin: 0 0 8px 0;\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.no-types p {\n margin: 0;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n/* ========================================\n Configuration Form\n ======================================== */\n\n.config-content {\n padding: 24px;\n}\n\n.form-group {\n margin-bottom: 20px;\n}\n\n.form-group label {\n display: block;\n margin-bottom: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.form-group input,\n.form-group select {\n width: 100%;\n padding: 12px 14px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n}\n\n.form-group input:focus,\n.form-group select:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.form-group input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.form-hint {\n display: block;\n margin-top: 6px;\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n.config-placeholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-text-disabled);\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n margin-top: 20px;\n}\n\n.config-placeholder i {\n font-size: 32px;\n margin-bottom: 12px;\n color: var(--mj-border-strong);\n}\n\n.config-placeholder p {\n margin: 0;\n font-size: 14px;\n}\n\n/* ========================================\n Footer\n ======================================== */\n\n.dialog-footer {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 24px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.btn-secondary {\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n\n.btn-secondary:hover {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-strong);\n}\n\n.btn-primary {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-brand-primary);\n border: none;\n color: var(--mj-text-inverse);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.btn-primary:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.btn-primary i {\n font-size: 12px;\n}\n\n.btn-primary:disabled {\n background: var(--mj-border-strong);\n cursor: not-allowed;\n box-shadow: none;\n}\n\n.btn-primary:disabled:hover {\n transform: none;\n box-shadow: none;\n}\n\n/* ========================================\n Validation Errors\n ======================================== */\n\n.validation-errors {\n margin-top: 16px;\n padding: 12px 16px;\n background: color-mix(in srgb, var(--mj-status-error) 5%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 20%, var(--mj-bg-surface));\n border-radius: 8px;\n}\n\n.error-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: var(--mj-status-error);\n}\n\n.error-item i {\n font-size: 14px;\n}\n\n.error-item + .error-item {\n margin-top: 8px;\n}\n\n/* ========================================\n Loading State\n ======================================== */\n\n.loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-text-secondary);\n}\n\n.loading-state i {\n font-size: 24px;\n color: var(--mj-brand-primary);\n}\n\n.loading-state span {\n font-size: 14px;\n}\n\n/* ========================================\n Error State\n ======================================== */\n\n.error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-status-warning);\n text-align: center;\n}\n\n.error-state i {\n font-size: 32px;\n}\n\n.error-state span {\n font-size: 14px;\n max-width: 300px;\n}\n\n.error-state .error-hint {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n}\n"] }]
|
|
442
|
+
args: [{ standalone: false, selector: 'mj-add-panel-dialog', template: "<!-- Add Part Dialog -->\n@if (visible) {\n <div class=\"add-part-dialog-overlay\" (click)=\"onCancel()\">\n <div class=\"add-part-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Step 1: Select Part Type -->\n @if (step === 'select-type') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <h3>\n <i class=\"fa-solid fa-plus-circle\"></i>\n Add Part\n </h3>\n <button class=\"close-button\" aria-label=\"Close dialog\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content -->\n <div class=\"dialog-content\">\n <p class=\"instruction\">Select a part type to add to your dashboard:</p>\n <div class=\"part-type-grid\">\n @for (partType of partTypes; track partType) {\n <div\n class=\"part-type-card\"\n (click)=\"onPartTypeSelect(partType)\">\n <div class=\"card-icon\">\n <i [class]=\"partType.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n </div>\n <div class=\"card-content\">\n <span class=\"card-title\">{{ partType.Name }}</span>\n @if (partType.Description) {\n <span class=\"card-description\">\n {{ partType.Description }}\n </span>\n }\n </div>\n <i class=\"fa-solid fa-chevron-right card-arrow\"></i>\n </div>\n }\n </div>\n @if (partTypes.length === 0) {\n <div class=\"no-types\">\n <div class=\"warning-icon\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </div>\n <h4>No part types available</h4>\n <p>Dashboard part types have not been configured in the system.</p>\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n <!-- Step 2: Configure Part -->\n @if (step === 'configure') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <button class=\"back-button\" aria-label=\"Go back\" (click)=\"goBack()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n </button>\n <h3>\n <i [class]=\"selectedPartType?.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n Configure {{ getConfigTypeName() }} Part\n </h3>\n <button class=\"close-button\" aria-label=\"Close dialog\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content - Dynamic Config Panel -->\n <div class=\"dialog-content config-content\">\n <!-- Loading state -->\n @if (isLoadingPanel) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading configuration panel...</span>\n </div>\n }\n <!-- Error state -->\n @if (loadError && !isLoadingPanel) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ loadError }}</span>\n <p class=\"error-hint\">You can still add this part with default settings.</p>\n </div>\n }\n <!-- Dynamic config panel container -->\n <ng-container #configPanelContainer></ng-container>\n <!-- No config panel available (custom type) -->\n @if (!hasConfigPanel() && !isLoadingPanel && !loadError) {\n <div class=\"config-placeholder\">\n <i class=\"fa-solid fa-cog\"></i>\n <p>This part type uses default configuration.</p>\n </div>\n }\n <!-- Validation Errors -->\n @if (currentResult && currentResult.errors.length > 0) {\n <div class=\"validation-errors\">\n @for (error of currentResult.errors; track error) {\n <div class=\"error-item\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n {{ error }}\n </div>\n }\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-primary\" (click)=\"addPart()\" [disabled]=\"!canAddPart && !loadError\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Part\n </button>\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n </div>\n </div>\n}\n", styles: ["/**\n * Add Part Dialog Styles\n */\n\n/* ========================================\n Overlay\n ======================================== */\n\n.add-part-dialog-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n animation: fadeIn 0.15s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* ========================================\n Dialog Container\n ======================================== */\n\n.add-part-dialog {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n width: 560px;\n max-width: 90vw;\n max-height: 85vh;\n overflow: hidden;\n animation: slideUp 0.2s ease;\n}\n\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* ========================================\n Header\n ======================================== */\n\n.dialog-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.dialog-header h3 {\n flex: 1;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.dialog-header h3 i {\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.back-button,\n.close-button {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.back-button:hover,\n.close-button:hover {\n background: rgba(0, 0, 0, 0.08);\n color: var(--mj-text-primary);\n}\n\n.back-button i,\n.close-button i {\n font-size: 16px;\n}\n\n/* ========================================\n Content\n ======================================== */\n\n.dialog-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n min-height: 0;\n}\n\n.instruction {\n margin: 0 0 20px 0;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n/* ========================================\n Part Type Grid\n ======================================== */\n\n.part-type-grid {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.part-type-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 20px;\n border: 2px solid color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-bg-surface);\n}\n\n.part-type-card:hover {\n background: var(--mj-bg-page);\n border-color: var(--mj-brand-primary);\n transform: translateX(4px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.part-type-card:hover .card-arrow {\n opacity: 1;\n transform: translateX(0);\n}\n\n.card-icon {\n width: 56px;\n height: 56px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 12px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.card-icon i {\n font-size: 24px;\n}\n\n.card-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.card-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.card-description {\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n.card-arrow {\n flex-shrink: 0;\n color: var(--mj-brand-primary);\n opacity: 0;\n transform: translateX(-8px);\n transition: all 0.2s ease;\n font-size: 16px;\n}\n\n/* ========================================\n No Types\n ======================================== */\n\n.no-types {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 40px;\n text-align: center;\n}\n\n.no-types .warning-icon {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 16px;\n}\n\n.no-types .warning-icon i {\n font-size: 28px;\n color: var(--mj-status-warning);\n}\n\n.no-types h4 {\n margin: 0 0 8px 0;\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.no-types p {\n margin: 0;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n/* ========================================\n Configuration Form\n ======================================== */\n\n.config-content {\n padding: 24px;\n}\n\n.form-group {\n margin-bottom: 20px;\n}\n\n.form-group label {\n display: block;\n margin-bottom: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.form-group input,\n.form-group select {\n width: 100%;\n padding: 12px 14px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n}\n\n.form-group input:focus,\n.form-group select:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.form-group input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.form-hint {\n display: block;\n margin-top: 6px;\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n.config-placeholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-text-disabled);\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n margin-top: 20px;\n}\n\n.config-placeholder i {\n font-size: 32px;\n margin-bottom: 12px;\n color: var(--mj-border-strong);\n}\n\n.config-placeholder p {\n margin: 0;\n font-size: 14px;\n}\n\n/* ========================================\n Footer\n ======================================== */\n\n.dialog-footer {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 24px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.btn-secondary {\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n\n.btn-secondary:hover {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-strong);\n}\n\n.btn-primary {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-brand-primary);\n border: none;\n color: var(--mj-text-inverse);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.btn-primary:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.btn-primary i {\n font-size: 12px;\n}\n\n.btn-primary:disabled {\n background: var(--mj-border-strong);\n cursor: not-allowed;\n box-shadow: none;\n}\n\n.btn-primary:disabled:hover {\n transform: none;\n box-shadow: none;\n}\n\n/* ========================================\n Validation Errors\n ======================================== */\n\n.validation-errors {\n margin-top: 16px;\n padding: 12px 16px;\n background: color-mix(in srgb, var(--mj-status-error) 5%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 20%, var(--mj-bg-surface));\n border-radius: 8px;\n}\n\n.error-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: var(--mj-status-error);\n}\n\n.error-item i {\n font-size: 14px;\n}\n\n.error-item + .error-item {\n margin-top: 8px;\n}\n\n/* ========================================\n Loading State\n ======================================== */\n\n.loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-text-secondary);\n}\n\n.loading-state i {\n font-size: 24px;\n color: var(--mj-brand-primary);\n}\n\n.loading-state span {\n font-size: 14px;\n}\n\n/* ========================================\n Error State\n ======================================== */\n\n.error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-status-warning);\n text-align: center;\n}\n\n.error-state i {\n font-size: 32px;\n}\n\n.error-state span {\n font-size: 14px;\n max-width: 300px;\n}\n\n.error-state .error-hint {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n}\n"] }]
|
|
443
443
|
}], () => [{ type: i0.ChangeDetectorRef }], { partTypes: [{
|
|
444
444
|
type: Input
|
|
445
445
|
}], visible: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-panel-dialog.component.js","sourceRoot":"","sources":["../../../../src/lib/dialogs/add-panel-dialog/add-panel-dialog.component.ts","../../../../src/lib/dialogs/add-panel-dialog/add-panel-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n ChangeDetectorRef,\n ViewChild,\n ViewContainerRef,\n ComponentRef,\n AfterViewInit,\n OnDestroy\n} from '@angular/core';\nimport { MJGlobal } from '@memberjunction/global';\nimport { MJDashboardPartTypeEntity } from '@memberjunction/core-entities';\nimport { PanelConfig } from '../../models/dashboard-types';\nimport { BaseConfigPanel, ConfigPanelResult } from '../../config-panels/base-config-panel';\n\n/**\n * Result when a panel is added\n */\nexport interface AddPanelResult {\n PartType: MJDashboardPartTypeEntity;\n Config: PanelConfig;\n Title: string;\n Icon?: string;\n}\n\n/**\n * Dialog step type\n */\ntype DialogStep = 'select-type' | 'configure';\n\n/**\n * Dialog for adding a new part to the dashboard.\n * Two-step flow: first select the part type, then configure using dynamically loaded config panels.\n *\n * Config panels are loaded via ClassFactory using DashboardPartType.ConfigDialogClass,\n * allowing new part types to be added without modifying this component.\n */\n@Component({\n standalone: false,\n selector: 'mj-add-panel-dialog',\n templateUrl: './add-panel-dialog.component.html',\n styleUrls: ['./add-panel-dialog.component.css']\n})\nexport class AddPanelDialogComponent implements AfterViewInit, OnDestroy {\n // ========================================\n // Inputs\n // ========================================\n\n /** Available part types to choose from */\n @Input() partTypes: MJDashboardPartTypeEntity[] = [];\n\n /** Whether the dialog is visible */\n @Input()\n set visible(value: boolean) {\n const previous = this._visible;\n this._visible = value;\n if (!value && previous) {\n // Dialog closing - cleanup\n this.destroyConfigPanel();\n }\n }\n get visible(): boolean {\n return this._visible;\n }\n private _visible = false;\n\n // ========================================\n // Outputs\n // ========================================\n\n /** Emitted when a part is configured and ready to add */\n @Output() panelAdded = new EventEmitter<AddPanelResult>();\n\n /** Emitted when the dialog is cancelled */\n @Output() cancelled = new EventEmitter<void>();\n\n // ========================================\n // ViewChild for dynamic component loading\n // ========================================\n\n @ViewChild('configPanelContainer', { read: ViewContainerRef, static: false })\n configPanelContainer!: ViewContainerRef;\n\n // ========================================\n // State\n // ========================================\n\n public step: DialogStep = 'select-type';\n public selectedPartType: MJDashboardPartTypeEntity | null = null;\n\n /** Current config panel result (updated via configChanged event) */\n public currentResult: ConfigPanelResult | null = null;\n\n /** Whether the Add Part button should be enabled */\n public canAddPart = false;\n\n /** Reference to dynamically created config panel */\n private configPanelRef: ComponentRef<BaseConfigPanel> | null = null;\n\n /** Whether the view has been initialized */\n private viewInitialized = false;\n\n /** Error loading the config panel */\n public loadError: string | null = null;\n\n /** Whether config panel is loading */\n public isLoadingPanel = false;\n\n // ========================================\n // Constructor\n // ========================================\n\n constructor(private readonly cdr: ChangeDetectorRef) {}\n\n // ========================================\n // Lifecycle\n // ========================================\n\n ngAfterViewInit(): void {\n this.viewInitialized = true;\n }\n\n ngOnDestroy(): void {\n this.destroyConfigPanel();\n }\n\n // ========================================\n // Public Methods\n // ========================================\n\n /**\n * Reset the dialog to initial state\n */\n public reset(): void {\n this.step = 'select-type';\n this.selectedPartType = null;\n this.currentResult = null;\n this.canAddPart = false;\n this.loadError = null;\n this.isLoadingPanel = false;\n this.destroyConfigPanel();\n this.cdr.detectChanges();\n }\n\n /**\n * Select a part type and go to configuration step\n */\n public onPartTypeSelect(partType: MJDashboardPartTypeEntity): void {\n this.selectedPartType = partType;\n this.currentResult = null;\n this.canAddPart = false;\n this.loadError = null;\n this.step = 'configure';\n this.cdr.detectChanges();\n\n // Load the config panel after view updates\n setTimeout(() => this.loadConfigPanel(), 0);\n }\n\n /**\n * Go back to type selection\n */\n public goBack(): void {\n this.step = 'select-type';\n this.currentResult = null;\n this.canAddPart = false;\n this.loadError = null;\n this.destroyConfigPanel();\n this.cdr.detectChanges();\n }\n\n /**\n * Handle config changes from embedded config panel\n */\n public onConfigChanged(result: ConfigPanelResult): void {\n this.currentResult = result;\n this.canAddPart = result.isValid;\n this.cdr.detectChanges();\n }\n\n /**\n * Add the configured part\n */\n public addPart(): void {\n if (!this.selectedPartType) return;\n\n // Get result from the dynamic panel\n if (this.configPanelRef) {\n const panel = this.configPanelRef.instance;\n const result = panel.getResult();\n\n if (!result.isValid) {\n this.currentResult = result;\n this.canAddPart = false;\n this.cdr.detectChanges();\n return;\n }\n\n this.panelAdded.emit({\n PartType: this.selectedPartType,\n Config: result.config,\n Title: result.title,\n Icon: result.icon || this.selectedPartType.Icon || 'fa-solid fa-puzzle-piece'\n });\n\n this.reset();\n return;\n }\n\n // Handle types without a config panel - use a generic config with just the type\n this.panelAdded.emit({\n PartType: this.selectedPartType,\n Config: { type: this.selectedPartType.Name },\n Title: this.selectedPartType.Name,\n Icon: this.selectedPartType.Icon || 'fa-solid fa-puzzle-piece'\n });\n this.reset();\n }\n\n /**\n * Cancel the dialog\n */\n public onCancel(): void {\n this.reset();\n this.cancelled.emit();\n }\n\n /**\n * Get the configuration type name\n */\n public getConfigTypeName(): string {\n if (!this.selectedPartType) return '';\n const name = this.selectedPartType.Name;\n if (name === 'WebURL') return 'Web URL';\n return name;\n }\n\n /**\n * Check if the selected part type has a config panel class\n */\n public hasConfigPanel(): boolean {\n return !!this.selectedPartType?.ConfigDialogClass;\n }\n\n // ========================================\n // Private Methods\n // ========================================\n\n /**\n * Dynamically load the config panel component\n */\n private async loadConfigPanel(): Promise<void> {\n this.destroyConfigPanel();\n\n if (!this.selectedPartType?.ConfigDialogClass) {\n // No config panel for this type - that's okay, use defaults\n this.canAddPart = true;\n this.cdr.detectChanges();\n return;\n }\n\n if (!this.viewInitialized || !this.configPanelContainer) {\n this.loadError = 'View container not ready';\n this.cdr.detectChanges();\n return;\n }\n\n this.isLoadingPanel = true;\n this.cdr.detectChanges();\n\n try {\n // Use ClassFactory to create the config panel instance\n const panelInstance = await MJGlobal.Instance.ClassFactory.CreateInstanceAsync<BaseConfigPanel>(\n BaseConfigPanel,\n this.selectedPartType.ConfigDialogClass\n );\n\n if (!panelInstance) {\n this.loadError = `Could not create config panel: ${this.selectedPartType.ConfigDialogClass}`;\n this.isLoadingPanel = false;\n this.canAddPart = true; // Allow adding with default config\n this.cdr.detectChanges();\n return;\n }\n\n // Get the component class from the instance\n const componentClass = (panelInstance as object).constructor as typeof BaseConfigPanel;\n\n // Clear the container and create the component\n this.configPanelContainer.clear();\n this.configPanelRef = this.configPanelContainer.createComponent(componentClass as never);\n\n // Set inputs on the component\n const panel = this.configPanelRef.instance;\n panel.partType = this.selectedPartType;\n panel.panel = null; // New panel, not editing\n panel.config = null; // Start with defaults\n\n // Subscribe to config changes\n panel.configChanged.subscribe((result: ConfigPanelResult) => {\n this.onConfigChanged(result);\n });\n\n this.isLoadingPanel = false;\n this.cdr.detectChanges();\n\n } catch (error) {\n this.loadError = `Failed to load config panel: ${error instanceof Error ? error.message : String(error)}`;\n this.isLoadingPanel = false;\n this.canAddPart = true; // Allow adding with default config\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Destroy the dynamically created config panel\n */\n private destroyConfigPanel(): void {\n if (this.configPanelRef) {\n this.configPanelRef.destroy();\n this.configPanelRef = null;\n }\n }\n}\n","<!-- Add Part Dialog -->\n@if (visible) {\n <div class=\"add-part-dialog-overlay\" (click)=\"onCancel()\">\n <div class=\"add-part-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Step 1: Select Part Type -->\n @if (step === 'select-type') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <h3>\n <i class=\"fa-solid fa-plus-circle\"></i>\n Add Part\n </h3>\n <button class=\"close-button\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content -->\n <div class=\"dialog-content\">\n <p class=\"instruction\">Select a part type to add to your dashboard:</p>\n <div class=\"part-type-grid\">\n @for (partType of partTypes; track partType) {\n <div\n class=\"part-type-card\"\n (click)=\"onPartTypeSelect(partType)\">\n <div class=\"card-icon\">\n <i [class]=\"partType.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n </div>\n <div class=\"card-content\">\n <span class=\"card-title\">{{ partType.Name }}</span>\n @if (partType.Description) {\n <span class=\"card-description\">\n {{ partType.Description }}\n </span>\n }\n </div>\n <i class=\"fa-solid fa-chevron-right card-arrow\"></i>\n </div>\n }\n </div>\n @if (partTypes.length === 0) {\n <div class=\"no-types\">\n <div class=\"warning-icon\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </div>\n <h4>No part types available</h4>\n <p>Dashboard part types have not been configured in the system.</p>\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n <!-- Step 2: Configure Part -->\n @if (step === 'configure') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <button class=\"back-button\" (click)=\"goBack()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n </button>\n <h3>\n <i [class]=\"selectedPartType?.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n Configure {{ getConfigTypeName() }} Part\n </h3>\n <button class=\"close-button\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content - Dynamic Config Panel -->\n <div class=\"dialog-content config-content\">\n <!-- Loading state -->\n @if (isLoadingPanel) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading configuration panel...</span>\n </div>\n }\n <!-- Error state -->\n @if (loadError && !isLoadingPanel) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ loadError }}</span>\n <p class=\"error-hint\">You can still add this part with default settings.</p>\n </div>\n }\n <!-- Dynamic config panel container -->\n <ng-container #configPanelContainer></ng-container>\n <!-- No config panel available (custom type) -->\n @if (!hasConfigPanel() && !isLoadingPanel && !loadError) {\n <div class=\"config-placeholder\">\n <i class=\"fa-solid fa-cog\"></i>\n <p>This part type uses default configuration.</p>\n </div>\n }\n <!-- Validation Errors -->\n @if (currentResult && currentResult.errors.length > 0) {\n <div class=\"validation-errors\">\n @for (error of currentResult.errors; track error) {\n <div class=\"error-item\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n {{ error }}\n </div>\n }\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-primary\" (click)=\"addPart()\" [disabled]=\"!canAddPart && !loadError\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Part\n </button>\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n </div>\n </div>\n}\n"]}
|
|
1
|
+
{"version":3,"file":"add-panel-dialog.component.js","sourceRoot":"","sources":["../../../../src/lib/dialogs/add-panel-dialog/add-panel-dialog.component.ts","../../../../src/lib/dialogs/add-panel-dialog/add-panel-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n ChangeDetectorRef,\n ViewChild,\n ViewContainerRef,\n ComponentRef,\n AfterViewInit,\n OnDestroy\n} from '@angular/core';\nimport { MJGlobal } from '@memberjunction/global';\nimport { MJDashboardPartTypeEntity } from '@memberjunction/core-entities';\nimport { PanelConfig } from '../../models/dashboard-types';\nimport { BaseConfigPanel, ConfigPanelResult } from '../../config-panels/base-config-panel';\n\n/**\n * Result when a panel is added\n */\nexport interface AddPanelResult {\n PartType: MJDashboardPartTypeEntity;\n Config: PanelConfig;\n Title: string;\n Icon?: string;\n}\n\n/**\n * Dialog step type\n */\ntype DialogStep = 'select-type' | 'configure';\n\n/**\n * Dialog for adding a new part to the dashboard.\n * Two-step flow: first select the part type, then configure using dynamically loaded config panels.\n *\n * Config panels are loaded via ClassFactory using DashboardPartType.ConfigDialogClass,\n * allowing new part types to be added without modifying this component.\n */\n@Component({\n standalone: false,\n selector: 'mj-add-panel-dialog',\n templateUrl: './add-panel-dialog.component.html',\n styleUrls: ['./add-panel-dialog.component.css']\n})\nexport class AddPanelDialogComponent implements AfterViewInit, OnDestroy {\n // ========================================\n // Inputs\n // ========================================\n\n /** Available part types to choose from */\n @Input() partTypes: MJDashboardPartTypeEntity[] = [];\n\n /** Whether the dialog is visible */\n @Input()\n set visible(value: boolean) {\n const previous = this._visible;\n this._visible = value;\n if (!value && previous) {\n // Dialog closing - cleanup\n this.destroyConfigPanel();\n }\n }\n get visible(): boolean {\n return this._visible;\n }\n private _visible = false;\n\n // ========================================\n // Outputs\n // ========================================\n\n /** Emitted when a part is configured and ready to add */\n @Output() panelAdded = new EventEmitter<AddPanelResult>();\n\n /** Emitted when the dialog is cancelled */\n @Output() cancelled = new EventEmitter<void>();\n\n // ========================================\n // ViewChild for dynamic component loading\n // ========================================\n\n @ViewChild('configPanelContainer', { read: ViewContainerRef, static: false })\n configPanelContainer!: ViewContainerRef;\n\n // ========================================\n // State\n // ========================================\n\n public step: DialogStep = 'select-type';\n public selectedPartType: MJDashboardPartTypeEntity | null = null;\n\n /** Current config panel result (updated via configChanged event) */\n public currentResult: ConfigPanelResult | null = null;\n\n /** Whether the Add Part button should be enabled */\n public canAddPart = false;\n\n /** Reference to dynamically created config panel */\n private configPanelRef: ComponentRef<BaseConfigPanel> | null = null;\n\n /** Whether the view has been initialized */\n private viewInitialized = false;\n\n /** Error loading the config panel */\n public loadError: string | null = null;\n\n /** Whether config panel is loading */\n public isLoadingPanel = false;\n\n // ========================================\n // Constructor\n // ========================================\n\n constructor(private readonly cdr: ChangeDetectorRef) {}\n\n // ========================================\n // Lifecycle\n // ========================================\n\n ngAfterViewInit(): void {\n this.viewInitialized = true;\n }\n\n ngOnDestroy(): void {\n this.destroyConfigPanel();\n }\n\n // ========================================\n // Public Methods\n // ========================================\n\n /**\n * Reset the dialog to initial state\n */\n public reset(): void {\n this.step = 'select-type';\n this.selectedPartType = null;\n this.currentResult = null;\n this.canAddPart = false;\n this.loadError = null;\n this.isLoadingPanel = false;\n this.destroyConfigPanel();\n this.cdr.detectChanges();\n }\n\n /**\n * Select a part type and go to configuration step\n */\n public onPartTypeSelect(partType: MJDashboardPartTypeEntity): void {\n this.selectedPartType = partType;\n this.currentResult = null;\n this.canAddPart = false;\n this.loadError = null;\n this.step = 'configure';\n this.cdr.detectChanges();\n\n // Load the config panel after view updates\n setTimeout(() => this.loadConfigPanel(), 0);\n }\n\n /**\n * Go back to type selection\n */\n public goBack(): void {\n this.step = 'select-type';\n this.currentResult = null;\n this.canAddPart = false;\n this.loadError = null;\n this.destroyConfigPanel();\n this.cdr.detectChanges();\n }\n\n /**\n * Handle config changes from embedded config panel\n */\n public onConfigChanged(result: ConfigPanelResult): void {\n this.currentResult = result;\n this.canAddPart = result.isValid;\n this.cdr.detectChanges();\n }\n\n /**\n * Add the configured part\n */\n public addPart(): void {\n if (!this.selectedPartType) return;\n\n // Get result from the dynamic panel\n if (this.configPanelRef) {\n const panel = this.configPanelRef.instance;\n const result = panel.getResult();\n\n if (!result.isValid) {\n this.currentResult = result;\n this.canAddPart = false;\n this.cdr.detectChanges();\n return;\n }\n\n this.panelAdded.emit({\n PartType: this.selectedPartType,\n Config: result.config,\n Title: result.title,\n Icon: result.icon || this.selectedPartType.Icon || 'fa-solid fa-puzzle-piece'\n });\n\n this.reset();\n return;\n }\n\n // Handle types without a config panel - use a generic config with just the type\n this.panelAdded.emit({\n PartType: this.selectedPartType,\n Config: { type: this.selectedPartType.Name },\n Title: this.selectedPartType.Name,\n Icon: this.selectedPartType.Icon || 'fa-solid fa-puzzle-piece'\n });\n this.reset();\n }\n\n /**\n * Cancel the dialog\n */\n public onCancel(): void {\n this.reset();\n this.cancelled.emit();\n }\n\n /**\n * Get the configuration type name\n */\n public getConfigTypeName(): string {\n if (!this.selectedPartType) return '';\n const name = this.selectedPartType.Name;\n if (name === 'WebURL') return 'Web URL';\n return name;\n }\n\n /**\n * Check if the selected part type has a config panel class\n */\n public hasConfigPanel(): boolean {\n return !!this.selectedPartType?.ConfigDialogClass;\n }\n\n // ========================================\n // Private Methods\n // ========================================\n\n /**\n * Dynamically load the config panel component\n */\n private async loadConfigPanel(): Promise<void> {\n this.destroyConfigPanel();\n\n if (!this.selectedPartType?.ConfigDialogClass) {\n // No config panel for this type - that's okay, use defaults\n this.canAddPart = true;\n this.cdr.detectChanges();\n return;\n }\n\n if (!this.viewInitialized || !this.configPanelContainer) {\n this.loadError = 'View container not ready';\n this.cdr.detectChanges();\n return;\n }\n\n this.isLoadingPanel = true;\n this.cdr.detectChanges();\n\n try {\n // Use ClassFactory to create the config panel instance\n const panelInstance = await MJGlobal.Instance.ClassFactory.CreateInstanceAsync<BaseConfigPanel>(\n BaseConfigPanel,\n this.selectedPartType.ConfigDialogClass\n );\n\n if (!panelInstance) {\n this.loadError = `Could not create config panel: ${this.selectedPartType.ConfigDialogClass}`;\n this.isLoadingPanel = false;\n this.canAddPart = true; // Allow adding with default config\n this.cdr.detectChanges();\n return;\n }\n\n // Get the component class from the instance\n const componentClass = (panelInstance as object).constructor as typeof BaseConfigPanel;\n\n // Clear the container and create the component\n this.configPanelContainer.clear();\n this.configPanelRef = this.configPanelContainer.createComponent(componentClass as never);\n\n // Set inputs on the component\n const panel = this.configPanelRef.instance;\n panel.partType = this.selectedPartType;\n panel.panel = null; // New panel, not editing\n panel.config = null; // Start with defaults\n\n // Subscribe to config changes\n panel.configChanged.subscribe((result: ConfigPanelResult) => {\n this.onConfigChanged(result);\n });\n\n this.isLoadingPanel = false;\n this.cdr.detectChanges();\n\n } catch (error) {\n this.loadError = `Failed to load config panel: ${error instanceof Error ? error.message : String(error)}`;\n this.isLoadingPanel = false;\n this.canAddPart = true; // Allow adding with default config\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Destroy the dynamically created config panel\n */\n private destroyConfigPanel(): void {\n if (this.configPanelRef) {\n this.configPanelRef.destroy();\n this.configPanelRef = null;\n }\n }\n}\n","<!-- Add Part Dialog -->\n@if (visible) {\n <div class=\"add-part-dialog-overlay\" (click)=\"onCancel()\">\n <div class=\"add-part-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Step 1: Select Part Type -->\n @if (step === 'select-type') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <h3>\n <i class=\"fa-solid fa-plus-circle\"></i>\n Add Part\n </h3>\n <button class=\"close-button\" aria-label=\"Close dialog\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content -->\n <div class=\"dialog-content\">\n <p class=\"instruction\">Select a part type to add to your dashboard:</p>\n <div class=\"part-type-grid\">\n @for (partType of partTypes; track partType) {\n <div\n class=\"part-type-card\"\n (click)=\"onPartTypeSelect(partType)\">\n <div class=\"card-icon\">\n <i [class]=\"partType.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n </div>\n <div class=\"card-content\">\n <span class=\"card-title\">{{ partType.Name }}</span>\n @if (partType.Description) {\n <span class=\"card-description\">\n {{ partType.Description }}\n </span>\n }\n </div>\n <i class=\"fa-solid fa-chevron-right card-arrow\"></i>\n </div>\n }\n </div>\n @if (partTypes.length === 0) {\n <div class=\"no-types\">\n <div class=\"warning-icon\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </div>\n <h4>No part types available</h4>\n <p>Dashboard part types have not been configured in the system.</p>\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n <!-- Step 2: Configure Part -->\n @if (step === 'configure') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <button class=\"back-button\" aria-label=\"Go back\" (click)=\"goBack()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n </button>\n <h3>\n <i [class]=\"selectedPartType?.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n Configure {{ getConfigTypeName() }} Part\n </h3>\n <button class=\"close-button\" aria-label=\"Close dialog\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content - Dynamic Config Panel -->\n <div class=\"dialog-content config-content\">\n <!-- Loading state -->\n @if (isLoadingPanel) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading configuration panel...</span>\n </div>\n }\n <!-- Error state -->\n @if (loadError && !isLoadingPanel) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ loadError }}</span>\n <p class=\"error-hint\">You can still add this part with default settings.</p>\n </div>\n }\n <!-- Dynamic config panel container -->\n <ng-container #configPanelContainer></ng-container>\n <!-- No config panel available (custom type) -->\n @if (!hasConfigPanel() && !isLoadingPanel && !loadError) {\n <div class=\"config-placeholder\">\n <i class=\"fa-solid fa-cog\"></i>\n <p>This part type uses default configuration.</p>\n </div>\n }\n <!-- Validation Errors -->\n @if (currentResult && currentResult.errors.length > 0) {\n <div class=\"validation-errors\">\n @for (error of currentResult.errors; track error) {\n <div class=\"error-item\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n {{ error }}\n </div>\n }\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-primary\" (click)=\"addPart()\" [disabled]=\"!canAddPart && !loadError\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Part\n </button>\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n </div>\n </div>\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ChangeDetectorRef, AfterViewInit, OnDestroy } from '@angular/core';
|
|
2
2
|
import { BaseDashboardPart } from './base-dashboard-part';
|
|
3
|
-
import { NavigationRequest } from '@memberjunction/ng-artifacts';
|
|
4
|
-
import { UserInfo, CompositeKey } from '@memberjunction/core';
|
|
3
|
+
import { AnalyzeArtifactService, NavigationRequest } from '@memberjunction/ng-artifacts';
|
|
4
|
+
import { DataSnapshot, UserInfo, CompositeKey } from '@memberjunction/core';
|
|
5
5
|
import { Subject } from 'rxjs';
|
|
6
6
|
import * as i0 from "@angular/core";
|
|
7
7
|
/**
|
|
@@ -9,6 +9,7 @@ import * as i0 from "@angular/core";
|
|
|
9
9
|
* Displays artifacts using mj-artifact-viewer-panel including reports, charts, and AI-generated content.
|
|
10
10
|
*/
|
|
11
11
|
export declare class ArtifactPartComponent extends BaseDashboardPart implements AfterViewInit, OnDestroy {
|
|
12
|
+
private analyzeService;
|
|
12
13
|
/**
|
|
13
14
|
* Current user - required by the artifact viewer panel.
|
|
14
15
|
* Should be provided by the dashboard host or retrieved from a service.
|
|
@@ -32,7 +33,7 @@ export declare class ArtifactPartComponent extends BaseDashboardPart implements
|
|
|
32
33
|
}>;
|
|
33
34
|
get currentUser(): UserInfo;
|
|
34
35
|
get environmentId(): string;
|
|
35
|
-
constructor(cdr: ChangeDetectorRef);
|
|
36
|
+
constructor(cdr: ChangeDetectorRef, analyzeService: AnalyzeArtifactService);
|
|
36
37
|
ngAfterViewInit(): void;
|
|
37
38
|
loadContent(): Promise<void>;
|
|
38
39
|
/**
|
|
@@ -60,6 +61,16 @@ export declare class ArtifactPartComponent extends BaseDashboardPart implements
|
|
|
60
61
|
* Handle general navigation request events from artifact viewer plugins
|
|
61
62
|
*/
|
|
62
63
|
onNavigationRequest(event: NavigationRequest): void;
|
|
64
|
+
/**
|
|
65
|
+
* Handler for the Analyze button on the embedded artifact viewer.
|
|
66
|
+
* Captures the live DataSnapshot, creates an analysis conversation with
|
|
67
|
+
* the snapshot attached as input, and emits a navigation request to open
|
|
68
|
+
* the new conversation in the host application.
|
|
69
|
+
*/
|
|
70
|
+
onAnalyzeRequested(event: {
|
|
71
|
+
artifactId: string;
|
|
72
|
+
snapshot: DataSnapshot;
|
|
73
|
+
}): Promise<void>;
|
|
63
74
|
protected cleanup(): void;
|
|
64
75
|
static ɵfac: i0.ɵɵFactoryDeclaration<ArtifactPartComponent, never>;
|
|
65
76
|
static ɵcmp: i0.ɵɵComponentDeclaration<ArtifactPartComponent, "mj-artifact-part", never, { "CurrentUser": { "alias": "CurrentUser"; "required": false; }; "EnvironmentId": { "alias": "EnvironmentId"; "required": false; }; }, {}, never, never, false, never>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-part.component.d.ts","sourceRoot":"","sources":["../../../src/lib/parts/artifact-part.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,iBAAiB,EAAE,aAAa,EAAE,SAAS,EAAS,MAAM,eAAe,CAAC;AAE9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"artifact-part.component.d.ts","sourceRoot":"","sources":["../../../src/lib/parts/artifact-part.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,iBAAiB,EAAE,aAAa,EAAE,SAAS,EAAS,MAAM,eAAe,CAAC;AAE9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAY,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;;AAE/B;;;GAGG;AACH,qBA8Ga,qBAAsB,SAAQ,iBAAkB,YAAW,aAAa,EAAE,SAAS;IAqCxD,OAAO,CAAC,cAAc;IApC1D;;;OAGG;IACM,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAE7C;;;OAGG;IACM,aAAa,EAAE,MAAM,CAAM;IAE7B,WAAW,UAAS;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,UAAU,EAAE,OAAO,CAAS;IAC5B,QAAQ,EAAE,OAAO,CAAQ;IACzB,eAAe,EAAE,OAAO,CAAS;IACjC,kBAAkB,EAAE,OAAO,CAAS;IACpC,cAAc;oBAA6B,MAAM;uBAAiB,MAAM;OAAM;IAGrF,IAAW,WAAW,IAAI,QAAQ,CAQjC;IAED,IAAW,aAAa,IAAI,MAAM,CAEjC;gBAEW,GAAG,EAAE,iBAAiB,EAAU,cAAc,EAAE,sBAAsB;IAUlF,eAAe,IAAI,IAAI;IAMV,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BzC;;OAEG;IACI,OAAO,IAAI,IAAI;IAStB;;OAEG;IACI,gBAAgB,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,cAAc,GAAG,YAAY,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAe1J;;OAEG;IACI,kBAAkB,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,YAAY,CAAA;KAAE,GAAG,IAAI;IAmB1F;;OAEG;IACI,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAS1D;;;;;OAKG;IACU,kBAAkB,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,YAAY,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;cAmBlF,OAAO,IAAI,IAAI;yCAzKzB,qBAAqB;2CAArB,qBAAqB;CA8KjC"}
|
|
@@ -10,8 +10,8 @@ import { BaseDashboardPart } from './base-dashboard-part';
|
|
|
10
10
|
import { Metadata } from '@memberjunction/core';
|
|
11
11
|
import { Subject } from 'rxjs';
|
|
12
12
|
import * as i0 from "@angular/core";
|
|
13
|
-
import * as i1 from "@memberjunction/ng-
|
|
14
|
-
import * as i2 from "@memberjunction/ng-
|
|
13
|
+
import * as i1 from "@memberjunction/ng-artifacts";
|
|
14
|
+
import * as i2 from "@memberjunction/ng-shared-generic";
|
|
15
15
|
function ArtifactPartComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
16
16
|
i0.ɵɵelementStart(0, "div", 1);
|
|
17
17
|
i0.ɵɵelement(1, "mj-loading", 5);
|
|
@@ -41,7 +41,7 @@ function ArtifactPartComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
|
41
41
|
function ArtifactPartComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
42
42
|
const _r2 = i0.ɵɵgetCurrentView();
|
|
43
43
|
i0.ɵɵelementStart(0, "mj-artifact-viewer-panel", 8);
|
|
44
|
-
i0.ɵɵlistener("navigateToLink", function ArtifactPartComponent_Conditional_4_Template_mj_artifact_viewer_panel_navigateToLink_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onNavigateToLink($event)); })("openEntityRecord", function ArtifactPartComponent_Conditional_4_Template_mj_artifact_viewer_panel_openEntityRecord_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onOpenEntityRecord($event)); })("navigationRequest", function ArtifactPartComponent_Conditional_4_Template_mj_artifact_viewer_panel_navigationRequest_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onNavigationRequest($event)); });
|
|
44
|
+
i0.ɵɵlistener("navigateToLink", function ArtifactPartComponent_Conditional_4_Template_mj_artifact_viewer_panel_navigateToLink_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onNavigateToLink($event)); })("openEntityRecord", function ArtifactPartComponent_Conditional_4_Template_mj_artifact_viewer_panel_openEntityRecord_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onOpenEntityRecord($event)); })("navigationRequest", function ArtifactPartComponent_Conditional_4_Template_mj_artifact_viewer_panel_navigationRequest_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onNavigationRequest($event)); })("analyzeRequested", function ArtifactPartComponent_Conditional_4_Template_mj_artifact_viewer_panel_analyzeRequested_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onAnalyzeRequested($event)); });
|
|
45
45
|
i0.ɵɵelementEnd();
|
|
46
46
|
} if (rf & 2) {
|
|
47
47
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -52,6 +52,7 @@ function ArtifactPartComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
|
52
52
|
* Displays artifacts using mj-artifact-viewer-panel including reports, charts, and AI-generated content.
|
|
53
53
|
*/
|
|
54
54
|
let ArtifactPartComponent = class ArtifactPartComponent extends BaseDashboardPart {
|
|
55
|
+
analyzeService;
|
|
55
56
|
/**
|
|
56
57
|
* Current user - required by the artifact viewer panel.
|
|
57
58
|
* Should be provided by the dashboard host or retrieved from a service.
|
|
@@ -83,8 +84,15 @@ let ArtifactPartComponent = class ArtifactPartComponent extends BaseDashboardPar
|
|
|
83
84
|
get environmentId() {
|
|
84
85
|
return this.EnvironmentId || '';
|
|
85
86
|
}
|
|
86
|
-
constructor(cdr) {
|
|
87
|
+
constructor(cdr, analyzeService) {
|
|
88
|
+
// Note: this component is instantiated twice — once via bare `new` by
|
|
89
|
+
// ClassFactory.CreateInstanceAsync (just to extract the constructor
|
|
90
|
+
// reference), then properly via createComponent() with a full injector.
|
|
91
|
+
// Constructor parameters are undefined on the bare path but Angular DI
|
|
92
|
+
// populates them on the real path. Field initializers calling inject()
|
|
93
|
+
// would throw on the bare path, so we use constructor injection.
|
|
87
94
|
super(cdr);
|
|
95
|
+
this.analyzeService = analyzeService;
|
|
88
96
|
}
|
|
89
97
|
ngAfterViewInit() {
|
|
90
98
|
if (this.Panel) {
|
|
@@ -162,13 +170,32 @@ let ArtifactPartComponent = class ArtifactPartComponent extends BaseDashboardPar
|
|
|
162
170
|
onNavigationRequest(event) {
|
|
163
171
|
this.RequestOpenNavItem(event.navItemName, event.appName, event.queryParams, false);
|
|
164
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Handler for the Analyze button on the embedded artifact viewer.
|
|
175
|
+
* Captures the live DataSnapshot, creates an analysis conversation with
|
|
176
|
+
* the snapshot attached as input, and emits a navigation request to open
|
|
177
|
+
* the new conversation in the host application.
|
|
178
|
+
*/
|
|
179
|
+
async onAnalyzeRequested(event) {
|
|
180
|
+
try {
|
|
181
|
+
const result = await this.analyzeService.StartAnalysisConversation({
|
|
182
|
+
snapshot: event.snapshot,
|
|
183
|
+
currentUser: this.currentUser,
|
|
184
|
+
environmentId: this.environmentId,
|
|
185
|
+
});
|
|
186
|
+
this.RequestOpenNavItem('Conversations', undefined, { conversationId: result.conversationId }, false);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
this.setError(error instanceof Error ? error.message : 'Failed to start analysis conversation');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
165
192
|
cleanup() {
|
|
166
193
|
this.refreshTrigger.complete();
|
|
167
194
|
this.artifactId = null;
|
|
168
195
|
this.versionNumber = undefined;
|
|
169
196
|
}
|
|
170
|
-
static ɵfac = function ArtifactPartComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ArtifactPartComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
171
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ArtifactPartComponent, selectors: [["mj-artifact-part"]], inputs: { CurrentUser: "CurrentUser", EnvironmentId: "EnvironmentId" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 8, consts: [[1, "artifact-part"], [1, "loading-state"], [1, "error-state"], [1, "empty-state"], [3, "artifactId", "currentUser", "environmentId", "versionNumber", "showSaveToCollection", "showHeader", "showTabs", "showCloseButton", "showMaximizeButton", "viewContext", "canShare", "canEdit", "isMaximized", "refreshTrigger"], ["text", "Loading artifact..."], [1, "fa-solid", "fa-exclamation-triangle"], [1, "fa-solid", "fa-palette"], [3, "navigateToLink", "openEntityRecord", "navigationRequest", "artifactId", "currentUser", "environmentId", "versionNumber", "showSaveToCollection", "showHeader", "showTabs", "showCloseButton", "showMaximizeButton", "viewContext", "canShare", "canEdit", "isMaximized", "refreshTrigger"]], template: function ArtifactPartComponent_Template(rf, ctx) { if (rf & 1) {
|
|
197
|
+
static ɵfac = function ArtifactPartComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ArtifactPartComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.AnalyzeArtifactService)); };
|
|
198
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ArtifactPartComponent, selectors: [["mj-artifact-part"]], inputs: { CurrentUser: "CurrentUser", EnvironmentId: "EnvironmentId" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 8, consts: [[1, "artifact-part"], [1, "loading-state"], [1, "error-state"], [1, "empty-state"], [3, "artifactId", "currentUser", "environmentId", "versionNumber", "showSaveToCollection", "showHeader", "showTabs", "showCloseButton", "showMaximizeButton", "viewContext", "canShare", "canEdit", "isMaximized", "refreshTrigger"], ["text", "Loading artifact..."], [1, "fa-solid", "fa-exclamation-triangle"], [1, "fa-solid", "fa-palette"], [3, "navigateToLink", "openEntityRecord", "navigationRequest", "analyzeRequested", "artifactId", "currentUser", "environmentId", "versionNumber", "showSaveToCollection", "showHeader", "showTabs", "showCloseButton", "showMaximizeButton", "viewContext", "canShare", "canEdit", "isMaximized", "refreshTrigger"]], template: function ArtifactPartComponent_Template(rf, ctx) { if (rf & 1) {
|
|
172
199
|
i0.ɵɵelementStart(0, "div", 0);
|
|
173
200
|
i0.ɵɵconditionalCreate(1, ArtifactPartComponent_Conditional_1_Template, 2, 0, "div", 1);
|
|
174
201
|
i0.ɵɵconditionalCreate(2, ArtifactPartComponent_Conditional_2_Template, 4, 1, "div", 2);
|
|
@@ -185,7 +212,7 @@ let ArtifactPartComponent = class ArtifactPartComponent extends BaseDashboardPar
|
|
|
185
212
|
i0.ɵɵconditional(!ctx.IsLoading && !ctx.ErrorMessage && !ctx.hasArtifact ? 3 : -1);
|
|
186
213
|
i0.ɵɵadvance();
|
|
187
214
|
i0.ɵɵconditional(!ctx.IsLoading && !ctx.ErrorMessage && ctx.hasArtifact && ctx.artifactId ? 4 : -1);
|
|
188
|
-
} }, dependencies: [
|
|
215
|
+
} }, dependencies: [i2.LoadingComponent, i1.ArtifactViewerPanelComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .artifact-part[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n }\n\n .loading-state[_ngcontent-%COMP%], \n .error-state[_ngcontent-%COMP%], \n .empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%], \n .empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n }\n\n .empty-state[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n color: var(--mj-text-primary);\n }\n\n .empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n }\n\n mj-artifact-viewer-panel[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n }"] });
|
|
189
216
|
};
|
|
190
217
|
ArtifactPartComponent = __decorate([
|
|
191
218
|
RegisterClass(BaseDashboardPart, 'ArtifactPanelRenderer')
|
|
@@ -238,15 +265,16 @@ export { ArtifactPartComponent };
|
|
|
238
265
|
[refreshTrigger]="refreshTrigger"
|
|
239
266
|
(navigateToLink)="onNavigateToLink($event)"
|
|
240
267
|
(openEntityRecord)="onOpenEntityRecord($event)"
|
|
241
|
-
(navigationRequest)="onNavigationRequest($event)"
|
|
268
|
+
(navigationRequest)="onNavigationRequest($event)"
|
|
269
|
+
(analyzeRequested)="onAnalyzeRequested($event)">
|
|
242
270
|
</mj-artifact-viewer-panel>
|
|
243
271
|
}
|
|
244
272
|
</div>
|
|
245
273
|
`, styles: ["\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .artifact-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n }\n\n .loading-state,\n .error-state,\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .error-state i,\n .empty-state i {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .error-state i {\n color: var(--mj-status-error);\n }\n\n .empty-state h4 {\n margin: 0 0 8px 0;\n color: var(--mj-text-primary);\n }\n\n .empty-state p {\n margin: 0;\n font-size: 13px;\n }\n\n mj-artifact-viewer-panel {\n flex: 1;\n min-height: 0;\n }\n "] }]
|
|
246
|
-
}], () => [{ type: i0.ChangeDetectorRef }], { CurrentUser: [{
|
|
274
|
+
}], () => [{ type: i0.ChangeDetectorRef }, { type: i1.AnalyzeArtifactService }], { CurrentUser: [{
|
|
247
275
|
type: Input
|
|
248
276
|
}], EnvironmentId: [{
|
|
249
277
|
type: Input
|
|
250
278
|
}] }); })();
|
|
251
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ArtifactPartComponent, { className: "ArtifactPartComponent", filePath: "src/lib/parts/artifact-part.component.ts", lineNumber:
|
|
279
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ArtifactPartComponent, { className: "ArtifactPartComponent", filePath: "src/lib/parts/artifact-part.component.ts", lineNumber: 123 }); })();
|
|
252
280
|
//# sourceMappingURL=artifact-part.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-part.component.js","sourceRoot":"","sources":["../../../src/lib/parts/artifact-part.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ChangeDetectorRef, AfterViewInit, OnDestroy, Input } from '@angular/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseDashboardPart } from './base-dashboard-part';\nimport { PanelConfig } from '../models/dashboard-types';\nimport { NavigationRequest } from '@memberjunction/ng-artifacts';\nimport { UserInfo, Metadata, CompositeKey } from '@memberjunction/core';\nimport { Subject } from 'rxjs';\n\n/**\n * Runtime renderer for Artifact dashboard parts.\n * Displays artifacts using mj-artifact-viewer-panel including reports, charts, and AI-generated content.\n */\n@RegisterClass(BaseDashboardPart, 'ArtifactPanelRenderer')\n@Component({\n standalone: false,\n selector: 'mj-artifact-part',\n template: `\n <div class=\"artifact-part\" [class.loading]=\"IsLoading\" [class.error]=\"ErrorMessage\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading artifact...\"></mj-loading>\n </div>\n }\n \n <!-- Error state -->\n @if (ErrorMessage && !IsLoading) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ ErrorMessage }}</span>\n </div>\n }\n \n <!-- No artifact configured -->\n @if (!IsLoading && !ErrorMessage && !hasArtifact) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-palette\"></i>\n <h4>No Artifact Selected</h4>\n <p>Click the configure button to select an artifact for this part.</p>\n </div>\n }\n \n <!-- Artifact Viewer Panel -->\n @if (!IsLoading && !ErrorMessage && hasArtifact && artifactId) {\n <mj-artifact-viewer-panel\n [artifactId]=\"artifactId\"\n [currentUser]=\"currentUser\"\n [environmentId]=\"environmentId\"\n [versionNumber]=\"versionNumber\"\n [showSaveToCollection]=\"false\"\n [showHeader]=\"showHeader\"\n [showTabs]=\"showTabs\"\n [showCloseButton]=\"showCloseButton\"\n [showMaximizeButton]=\"showMaximizeButton\"\n [viewContext]=\"null\"\n [canShare]=\"false\"\n [canEdit]=\"false\"\n [isMaximized]=\"false\"\n [refreshTrigger]=\"refreshTrigger\"\n (navigateToLink)=\"onNavigateToLink($event)\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\"\n (navigationRequest)=\"onNavigationRequest($event)\">\n </mj-artifact-viewer-panel>\n }\n </div>\n `,\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .artifact-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n }\n\n .loading-state,\n .error-state,\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .error-state i,\n .empty-state i {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .error-state i {\n color: var(--mj-status-error);\n }\n\n .empty-state h4 {\n margin: 0 0 8px 0;\n color: var(--mj-text-primary);\n }\n\n .empty-state p {\n margin: 0;\n font-size: 13px;\n }\n\n mj-artifact-viewer-panel {\n flex: 1;\n min-height: 0;\n }\n `]\n})\nexport class ArtifactPartComponent extends BaseDashboardPart implements AfterViewInit, OnDestroy {\n /**\n * Current user - required by the artifact viewer panel.\n * Should be provided by the dashboard host or retrieved from a service.\n */\n @Input() CurrentUser: UserInfo | null = null;\n\n /**\n * Environment ID - required by the artifact viewer panel.\n * Should be provided by the dashboard host.\n */\n @Input() EnvironmentId: string = '';\n\n public hasArtifact = false;\n public artifactId: string | null = null;\n public versionNumber: number | undefined;\n public showHeader: boolean = false; // Default to false for dashboard embedding\n public showTabs: boolean = true;\n public showCloseButton: boolean = false; // Always false in dashboard context - close handled by dashboard\n public showMaximizeButton: boolean = false; // Always false in dashboard context - maximize handled by dashboard\n public refreshTrigger = new Subject<{ artifactId: string; versionNumber: number }>();\n\n // Expose for template\n public get currentUser(): UserInfo {\n // Use provided CurrentUser, or fall back to Metadata.CurrentUser\n // In client-side Angular context, Metadata.CurrentUser should always be available\n const user = this.CurrentUser || new Metadata().CurrentUser;\n if (!user) {\n throw new Error('No current user available - user must be logged in to view artifacts');\n }\n return user;\n }\n\n public get environmentId(): string {\n return this.EnvironmentId || '';\n }\n\n constructor(cdr: ChangeDetectorRef) {\n super(cdr);\n }\n\n ngAfterViewInit(): void {\n if (this.Panel) {\n this.loadContent();\n }\n }\n\n public async loadContent(): Promise<void> {\n const config = this.getConfig<PanelConfig>();\n const artifactId = config?.['artifactId'] as string | undefined;\n\n if (!artifactId) {\n this.hasArtifact = false;\n this.cdr.detectChanges();\n return;\n }\n\n this.setLoading(true);\n\n try {\n // Set artifact ID and version from config\n this.artifactId = artifactId;\n this.versionNumber = config?.['versionNumber'] as number | undefined;\n // Display options - showHeader defaults to false for clean dashboard embedding\n this.showHeader = (config?.['showHeader'] as boolean) ?? false;\n this.showTabs = (config?.['showTabs'] as boolean) ?? true;\n this.hasArtifact = true;\n\n this.setLoading(false);\n } catch (error) {\n this.setError(error instanceof Error ? error.message : 'Failed to load artifact');\n }\n }\n\n /**\n * Refresh the artifact display\n */\n public refresh(): void {\n if (this.artifactId && this.versionNumber) {\n this.refreshTrigger.next({\n artifactId: this.artifactId,\n versionNumber: this.versionNumber\n });\n }\n }\n\n /**\n * Handle navigation link events from artifact viewer (conversation/collection links)\n */\n public onNavigateToLink(event: { type: 'conversation' | 'collection'; id: string; artifactId?: string; versionNumber?: number; versionId?: string }): void {\n // Emit data change event for navigation link (for listeners)\n this.emitDataChanged({\n type: 'navigate-to-link',\n linkType: event.type,\n linkId: event.id,\n artifactId: event.artifactId,\n versionNumber: event.versionNumber,\n versionId: event.versionId\n });\n\n // TODO: Add navigation request methods for conversation/collection when needed\n // For now, these are emitted as data change events for parent components to handle\n }\n\n /**\n * Handle entity record navigation events from artifact viewer\n */\n public onOpenEntityRecord(event: { entityName: string; compositeKey: CompositeKey }): void {\n // Emit data change event for listeners\n this.emitDataChanged({\n type: 'open-entity-record',\n entityName: event.entityName,\n compositeKey: event.compositeKey\n });\n\n // Use proper navigation request to bubble up through the stack\n if (event.entityName && event.compositeKey) {\n this.RequestOpenEntityRecord(\n event.entityName,\n event.compositeKey.ToURLSegment(),\n 'view',\n false\n );\n }\n }\n\n /**\n * Handle general navigation request events from artifact viewer plugins\n */\n public onNavigationRequest(event: NavigationRequest): void {\n this.RequestOpenNavItem(\n event.navItemName,\n event.appName,\n event.queryParams,\n false\n );\n }\n\n protected override cleanup(): void {\n this.refreshTrigger.complete();\n this.artifactId = null;\n this.versionNumber = undefined;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"artifact-part.component.js","sourceRoot":"","sources":["../../../src/lib/parts/artifact-part.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ChangeDetectorRef, AfterViewInit, OnDestroy, Input } from '@angular/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseDashboardPart } from './base-dashboard-part';\nimport { PanelConfig } from '../models/dashboard-types';\nimport { AnalyzeArtifactService, NavigationRequest } from '@memberjunction/ng-artifacts';\nimport { DataSnapshot, UserInfo, Metadata, CompositeKey } from '@memberjunction/core';\nimport { Subject } from 'rxjs';\n\n/**\n * Runtime renderer for Artifact dashboard parts.\n * Displays artifacts using mj-artifact-viewer-panel including reports, charts, and AI-generated content.\n */\n@RegisterClass(BaseDashboardPart, 'ArtifactPanelRenderer')\n@Component({\n standalone: false,\n selector: 'mj-artifact-part',\n template: `\n <div class=\"artifact-part\" [class.loading]=\"IsLoading\" [class.error]=\"ErrorMessage\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading artifact...\"></mj-loading>\n </div>\n }\n \n <!-- Error state -->\n @if (ErrorMessage && !IsLoading) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ ErrorMessage }}</span>\n </div>\n }\n \n <!-- No artifact configured -->\n @if (!IsLoading && !ErrorMessage && !hasArtifact) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-palette\"></i>\n <h4>No Artifact Selected</h4>\n <p>Click the configure button to select an artifact for this part.</p>\n </div>\n }\n \n <!-- Artifact Viewer Panel -->\n @if (!IsLoading && !ErrorMessage && hasArtifact && artifactId) {\n <mj-artifact-viewer-panel\n [artifactId]=\"artifactId\"\n [currentUser]=\"currentUser\"\n [environmentId]=\"environmentId\"\n [versionNumber]=\"versionNumber\"\n [showSaveToCollection]=\"false\"\n [showHeader]=\"showHeader\"\n [showTabs]=\"showTabs\"\n [showCloseButton]=\"showCloseButton\"\n [showMaximizeButton]=\"showMaximizeButton\"\n [viewContext]=\"null\"\n [canShare]=\"false\"\n [canEdit]=\"false\"\n [isMaximized]=\"false\"\n [refreshTrigger]=\"refreshTrigger\"\n (navigateToLink)=\"onNavigateToLink($event)\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\"\n (navigationRequest)=\"onNavigationRequest($event)\"\n (analyzeRequested)=\"onAnalyzeRequested($event)\">\n </mj-artifact-viewer-panel>\n }\n </div>\n `,\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .artifact-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n }\n\n .loading-state,\n .error-state,\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .error-state i,\n .empty-state i {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .error-state i {\n color: var(--mj-status-error);\n }\n\n .empty-state h4 {\n margin: 0 0 8px 0;\n color: var(--mj-text-primary);\n }\n\n .empty-state p {\n margin: 0;\n font-size: 13px;\n }\n\n mj-artifact-viewer-panel {\n flex: 1;\n min-height: 0;\n }\n `]\n})\nexport class ArtifactPartComponent extends BaseDashboardPart implements AfterViewInit, OnDestroy {\n /**\n * Current user - required by the artifact viewer panel.\n * Should be provided by the dashboard host or retrieved from a service.\n */\n @Input() CurrentUser: UserInfo | null = null;\n\n /**\n * Environment ID - required by the artifact viewer panel.\n * Should be provided by the dashboard host.\n */\n @Input() EnvironmentId: string = '';\n\n public hasArtifact = false;\n public artifactId: string | null = null;\n public versionNumber: number | undefined;\n public showHeader: boolean = false; // Default to false for dashboard embedding\n public showTabs: boolean = true;\n public showCloseButton: boolean = false; // Always false in dashboard context - close handled by dashboard\n public showMaximizeButton: boolean = false; // Always false in dashboard context - maximize handled by dashboard\n public refreshTrigger = new Subject<{ artifactId: string; versionNumber: number }>();\n\n // Expose for template\n public get currentUser(): UserInfo {\n // Use provided CurrentUser, or fall back to Metadata.CurrentUser\n // In client-side Angular context, Metadata.CurrentUser should always be available\n const user = this.CurrentUser || new Metadata().CurrentUser;\n if (!user) {\n throw new Error('No current user available - user must be logged in to view artifacts');\n }\n return user;\n }\n\n public get environmentId(): string {\n return this.EnvironmentId || '';\n }\n\n constructor(cdr: ChangeDetectorRef, private analyzeService: AnalyzeArtifactService) {\n // Note: this component is instantiated twice — once via bare `new` by\n // ClassFactory.CreateInstanceAsync (just to extract the constructor\n // reference), then properly via createComponent() with a full injector.\n // Constructor parameters are undefined on the bare path but Angular DI\n // populates them on the real path. Field initializers calling inject()\n // would throw on the bare path, so we use constructor injection.\n super(cdr);\n }\n\n ngAfterViewInit(): void {\n if (this.Panel) {\n this.loadContent();\n }\n }\n\n public async loadContent(): Promise<void> {\n const config = this.getConfig<PanelConfig>();\n const artifactId = config?.['artifactId'] as string | undefined;\n\n if (!artifactId) {\n this.hasArtifact = false;\n this.cdr.detectChanges();\n return;\n }\n\n this.setLoading(true);\n\n try {\n // Set artifact ID and version from config\n this.artifactId = artifactId;\n this.versionNumber = config?.['versionNumber'] as number | undefined;\n // Display options - showHeader defaults to false for clean dashboard embedding\n this.showHeader = (config?.['showHeader'] as boolean) ?? false;\n this.showTabs = (config?.['showTabs'] as boolean) ?? true;\n this.hasArtifact = true;\n\n this.setLoading(false);\n } catch (error) {\n this.setError(error instanceof Error ? error.message : 'Failed to load artifact');\n }\n }\n\n /**\n * Refresh the artifact display\n */\n public refresh(): void {\n if (this.artifactId && this.versionNumber) {\n this.refreshTrigger.next({\n artifactId: this.artifactId,\n versionNumber: this.versionNumber\n });\n }\n }\n\n /**\n * Handle navigation link events from artifact viewer (conversation/collection links)\n */\n public onNavigateToLink(event: { type: 'conversation' | 'collection'; id: string; artifactId?: string; versionNumber?: number; versionId?: string }): void {\n // Emit data change event for navigation link (for listeners)\n this.emitDataChanged({\n type: 'navigate-to-link',\n linkType: event.type,\n linkId: event.id,\n artifactId: event.artifactId,\n versionNumber: event.versionNumber,\n versionId: event.versionId\n });\n\n // TODO: Add navigation request methods for conversation/collection when needed\n // For now, these are emitted as data change events for parent components to handle\n }\n\n /**\n * Handle entity record navigation events from artifact viewer\n */\n public onOpenEntityRecord(event: { entityName: string; compositeKey: CompositeKey }): void {\n // Emit data change event for listeners\n this.emitDataChanged({\n type: 'open-entity-record',\n entityName: event.entityName,\n compositeKey: event.compositeKey\n });\n\n // Use proper navigation request to bubble up through the stack\n if (event.entityName && event.compositeKey) {\n this.RequestOpenEntityRecord(\n event.entityName,\n event.compositeKey.ToURLSegment(),\n 'view',\n false\n );\n }\n }\n\n /**\n * Handle general navigation request events from artifact viewer plugins\n */\n public onNavigationRequest(event: NavigationRequest): void {\n this.RequestOpenNavItem(\n event.navItemName,\n event.appName,\n event.queryParams,\n false\n );\n }\n\n /**\n * Handler for the Analyze button on the embedded artifact viewer.\n * Captures the live DataSnapshot, creates an analysis conversation with\n * the snapshot attached as input, and emits a navigation request to open\n * the new conversation in the host application.\n */\n public async onAnalyzeRequested(event: { artifactId: string; snapshot: DataSnapshot }): Promise<void> {\n try {\n const result = await this.analyzeService.StartAnalysisConversation({\n snapshot: event.snapshot,\n currentUser: this.currentUser,\n environmentId: this.environmentId,\n });\n\n this.RequestOpenNavItem(\n 'Conversations',\n undefined,\n { conversationId: result.conversationId },\n false,\n );\n } catch (error) {\n this.setError(error instanceof Error ? error.message : 'Failed to start analysis conversation');\n }\n }\n\n protected override cleanup(): void {\n this.refreshTrigger.complete();\n this.artifactId = null;\n this.versionNumber = undefined;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-dashboard-viewer",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.30.0",
|
|
4
4
|
"description": "MemberJunction: Angular components for metadata-driven dashboards with Golden Layout panels, supporting views, queries, artifacts, and custom content",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"golden-layout": "^2.6.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@memberjunction/core": "5.
|
|
41
|
-
"@memberjunction/core-entities": "5.
|
|
42
|
-
"@memberjunction/global": "5.
|
|
43
|
-
"@memberjunction/ng-artifacts": "5.
|
|
44
|
-
"@memberjunction/ng-entity-viewer": "5.
|
|
45
|
-
"@memberjunction/ng-map-view": "5.
|
|
46
|
-
"@memberjunction/ng-query-viewer": "5.
|
|
47
|
-
"@memberjunction/ng-shared-generic": "5.
|
|
48
|
-
"@memberjunction/ng-trees": "5.
|
|
40
|
+
"@memberjunction/core": "5.30.0",
|
|
41
|
+
"@memberjunction/core-entities": "5.30.0",
|
|
42
|
+
"@memberjunction/global": "5.30.0",
|
|
43
|
+
"@memberjunction/ng-artifacts": "5.30.0",
|
|
44
|
+
"@memberjunction/ng-entity-viewer": "5.30.0",
|
|
45
|
+
"@memberjunction/ng-map-view": "5.30.0",
|
|
46
|
+
"@memberjunction/ng-query-viewer": "5.30.0",
|
|
47
|
+
"@memberjunction/ng-shared-generic": "5.30.0",
|
|
48
|
+
"@memberjunction/ng-trees": "5.30.0",
|
|
49
49
|
"rxjs": "^7.8.2",
|
|
50
50
|
"tslib": "^2.8.1"
|
|
51
51
|
},
|