@memberjunction/ng-artifacts 5.28.0 → 5.29.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/__tests__/artifact-snapshots.test.d.ts +2 -0
- package/dist/lib/__tests__/artifact-snapshots.test.d.ts.map +1 -0
- package/dist/lib/__tests__/artifact-snapshots.test.js +155 -0
- package/dist/lib/__tests__/artifact-snapshots.test.js.map +1 -0
- package/dist/lib/components/artifact-viewer-panel.component.d.ts +16 -2
- package/dist/lib/components/artifact-viewer-panel.component.d.ts.map +1 -1
- package/dist/lib/components/artifact-viewer-panel.component.js +128 -98
- package/dist/lib/components/artifact-viewer-panel.component.js.map +1 -1
- package/dist/lib/components/base-artifact-viewer.component.d.ts +24 -0
- package/dist/lib/components/base-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/base-artifact-viewer.component.js +15 -1
- package/dist/lib/components/base-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/code-artifact-viewer.component.d.ts +2 -0
- package/dist/lib/components/plugins/code-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/code-artifact-viewer.component.js +5 -1
- package/dist/lib/components/plugins/code-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/component-artifact-viewer.component.d.ts +2 -1
- package/dist/lib/components/plugins/component-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/component-artifact-viewer.component.js +29 -0
- package/dist/lib/components/plugins/component-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/data-artifact-viewer.component.d.ts +50 -43
- package/dist/lib/components/plugins/data-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/data-artifact-viewer.component.js +336 -137
- package/dist/lib/components/plugins/data-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/docx-artifact-viewer.component.d.ts +2 -0
- package/dist/lib/components/plugins/docx-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/docx-artifact-viewer.component.js +21 -6
- package/dist/lib/components/plugins/docx-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/html-artifact-viewer.component.d.ts +2 -0
- package/dist/lib/components/plugins/html-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/html-artifact-viewer.component.js +11 -1
- package/dist/lib/components/plugins/html-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/json-artifact-viewer.component.d.ts +2 -0
- package/dist/lib/components/plugins/json-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/json-artifact-viewer.component.js +5 -1
- package/dist/lib/components/plugins/json-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/markdown-artifact-viewer.component.d.ts +2 -0
- package/dist/lib/components/plugins/markdown-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/markdown-artifact-viewer.component.js +5 -1
- package/dist/lib/components/plugins/markdown-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.d.ts +2 -0
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.js +29 -10
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/svg-artifact-viewer.component.d.ts +2 -0
- package/dist/lib/components/plugins/svg-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/svg-artifact-viewer.component.js +11 -1
- package/dist/lib/components/plugins/svg-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.d.ts +2 -0
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.js +39 -13
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.js.map +1 -1
- package/dist/lib/services/analyze-artifact.service.d.ts +77 -0
- package/dist/lib/services/analyze-artifact.service.d.ts.map +1 -0
- package/dist/lib/services/analyze-artifact.service.js +120 -0
- package/dist/lib/services/analyze-artifact.service.js.map +1 -0
- package/dist/lib/snapshot-helpers.d.ts +48 -0
- package/dist/lib/snapshot-helpers.d.ts.map +1 -0
- package/dist/lib/snapshot-helpers.js +79 -0
- package/dist/lib/snapshot-helpers.js.map +1 -0
- package/dist/public-api.d.ts +2 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +16 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"svg-artifact-viewer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/plugins/svg-artifact-viewer.component.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,iCAAiC,EAAE,MAAM,mCAAmC,CAAC;;;;;;;IAyB5E,8BAAyB;IACvB,0BAA4D;IAC9D,iBAAM;;;IADqB,cAA4B;IAA5B,oEAA4B;;;;IAGvD,0CAIqC;IAHnC,gUAAwB;IAI1B,iBAAiB;;;IAJf,iDAAwB;IAExB,AADA,gCAAkB,6BACG;;AA9BjC;;GAEG;AAuGI,IAAM,0BAA0B,GAAhC,MAAM,0BAA2B,SAAQ,iCAAiC;IAY3D;IAXb,UAAU,GAAG,EAAE,CAAC;IAChB,cAAc,GAAa,EAAE,CAAC;IAC9B,QAAQ,GAAyB,SAAS,CAAC;IAElD;;OAEG;IACH,IAAoB,iBAAiB;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAoB,SAAuB;QACzC,KAAK,EAAE,CAAC;QADU,cAAS,GAAT,SAAS,CAAc;IAE3C,CAAC;IAED,QAAQ;QACN,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QACpC,iFAAiF;QACjF,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAC1E,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;gBACvD,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;YAC3C,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;gBACb,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;YACrD,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;
|
|
1
|
+
{"version":3,"file":"svg-artifact-viewer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/plugins/svg-artifact-viewer.component.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,iCAAiC,EAAE,MAAM,mCAAmC,CAAC;;;;;;;IAyB5E,8BAAyB;IACvB,0BAA4D;IAC9D,iBAAM;;;IADqB,cAA4B;IAA5B,oEAA4B;;;;IAGvD,0CAIqC;IAHnC,gUAAwB;IAI1B,iBAAiB;;;IAJf,iDAAwB;IAExB,AADA,gCAAkB,6BACG;;AA9BjC;;GAEG;AAuGI,IAAM,0BAA0B,GAAhC,MAAM,0BAA2B,SAAQ,iCAAiC;IAY3D;IAXb,UAAU,GAAG,EAAE,CAAC;IAChB,cAAc,GAAa,EAAE,CAAC;IAC9B,QAAQ,GAAyB,SAAS,CAAC;IAElD;;OAEG;IACH,IAAoB,iBAAiB;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAoB,SAAuB;QACzC,KAAK,EAAE,CAAC;QADU,cAAS,GAAT,SAAS,CAAc;IAE3C,CAAC;IAED,QAAQ;QACN,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QACpC,iFAAiF;QACjF,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAC1E,CAAC;IAEe,uBAAuB;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACrC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC1B,MAAM,IAAI,GAAG,IAAI,YAAY,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,eAAe,EAAE,IAAI,SAAS,CAAC;QACjD,IAAI,CAAC,MAAM,GAAG,EAAE,OAAO,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;gBACvD,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;YAC3C,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;gBACb,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;YACrD,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;oHAvCU,0BAA0B;6DAA1B,0BAA0B;YAhG/B,AADF,AADF,8BAAsD,aAC3B,gBAEgC;YAA/B,sHAAoB,SAAS,IAAC;YACpD,uBAA0B;YAAC,yBAC7B;YAAA,iBAAS;YACT,iCACqD;YAA9B,sHAAoB,QAAQ,IAAC;YAClD,uBAA2B;YAAC,wBAC9B;YAAA,iBAAS;YACT,iCAA6D;YAAnB,uGAAS,YAAQ,IAAC;YAC1D,uBAA2B;YAAC,uBAC9B;YACF,AADE,iBAAS,EACL;YACN,+BAAmC;YAK/B,AAJF,8FAA8B,oFAIrB;YASb,AADE,iBAAM,EACF;;YA5B2B,sCAAoB;YAExB,eAAuC;YAAvC,oDAAuC;YAIvC,eAAsC;YAAtC,mDAAsC;YAS/D,eAWC;YAXD,sDAWC;;;AAwEI,0BAA0B;IADtC,aAAa,CAAC,iCAAiC,EAAE,yBAAyB,CAAC;GAC/D,0BAA0B,CAwCtC;;iFAxCY,0BAA0B;cAtGtC,SAAS;6BACI,KAAK,YACP,wBAAwB,YACxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BT;;kFAqEU,0BAA0B","sourcesContent":["import { Component } from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { DataSnapshot } from '@memberjunction/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';\n\n/**\n * Viewer component for SVG artifacts\n */\n@Component({\n standalone: false,\n selector: 'mj-svg-artifact-viewer',\n template: `\n <div class=\"svg-artifact-viewer\" [ngClass]=\"cssClass\">\n <div class=\"svg-toolbar\">\n <button class=\"btn-icon\" [class.active]=\"viewMode === 'preview'\"\n title=\"Preview\" (click)=\"viewMode = 'preview'\">\n <i class=\"fas fa-eye\"></i> Preview\n </button>\n <button class=\"btn-icon\" [class.active]=\"viewMode === 'source'\"\n title=\"Source\" (click)=\"viewMode = 'source'\">\n <i class=\"fas fa-code\"></i> Source\n </button>\n <button class=\"btn-icon\" title=\"Copy SVG\" (click)=\"onCopy()\">\n <i class=\"fas fa-copy\"></i> Copy\n </button>\n </div>\n <div class=\"svg-content-container\">\n @if (viewMode === 'preview') {\n <div class=\"svg-preview\">\n <div class=\"svg-wrapper\" [innerHTML]=\"safeSvgContent\"></div>\n </div>\n } @else {\n <mj-code-editor\n [(ngModel)]=\"svgContent\"\n [language]=\"'xml'\"\n [readonly]=\"readonly\"\n style=\"width: 100%; height: 100%;\">\n </mj-code-editor>\n }\n </div>\n </div>\n `,\n styles: [`\n .svg-artifact-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .svg-toolbar {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: 8px;\n padding: 8px;\n background: #f5f5f5;\n border-bottom: 1px solid #ddd;\n }\n\n .btn-icon {\n padding: 6px 12px;\n background: white;\n border: 1px solid #ccc;\n border-radius: 4px;\n cursor: pointer;\n font-size: 12px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .btn-icon:hover {\n background: #e9ecef;\n border-color: #999;\n }\n\n .btn-icon.active {\n background: #007acc;\n color: white;\n border-color: #007acc;\n }\n\n .svg-content-container {\n flex: 1;\n overflow: auto;\n }\n\n .svg-preview {\n padding: 20px;\n background: white;\n height: 100%;\n overflow: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .svg-wrapper {\n max-width: 100%;\n max-height: 100%;\n }\n\n .svg-wrapper ::ng-deep svg {\n max-width: 100%;\n max-height: 100%;\n height: auto;\n }\n `]\n})\n@RegisterClass(BaseArtifactViewerPluginComponent, 'SvgArtifactViewerPlugin')\nexport class SvgArtifactViewerComponent extends BaseArtifactViewerPluginComponent {\n public svgContent = '';\n public safeSvgContent: SafeHtml = '';\n public viewMode: 'preview' | 'source' = 'preview';\n\n /**\n * SVG artifacts always have content to display\n */\n public override get hasDisplayContent(): boolean {\n return true;\n }\n\n constructor(private sanitizer: DomSanitizer) {\n super();\n }\n\n ngOnInit(): void {\n this.svgContent = this.getContent();\n // For SVG, we use sanitize with SecurityContext.HTML (1) to allow safe rendering\n this.safeSvgContent = this.sanitizer.sanitize(1, this.svgContent) || '';\n }\n\n public override GetCurrentStateSnapshot(): DataSnapshot | null {\n const content = this.getRawContent();\n if (!content) return null;\n const snap = new DataSnapshot();\n snap.title = this.getDisplayTitle() ?? undefined;\n snap.custom = { content };\n return snap;\n }\n\n onCopy(): void {\n if (this.svgContent) {\n navigator.clipboard.writeText(this.svgContent).then(() => {\n console.log('✅ Copied SVG to clipboard');\n }).catch(err => {\n console.error('Failed to copy to clipboard:', err);\n });\n }\n }\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { OnInit, ChangeDetectorRef } from '@angular/core';
|
|
2
|
+
import { DataSnapshot } from '@memberjunction/core';
|
|
2
3
|
import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
|
|
3
4
|
import { ArtifactFileService } from '../../services/artifact-file.service';
|
|
4
5
|
import { ColDef, GridReadyEvent } from 'ag-grid-community';
|
|
@@ -37,6 +38,7 @@ export declare class XlsxArtifactViewerComponent extends BaseArtifactViewerPlugi
|
|
|
37
38
|
private parseWorkbook;
|
|
38
39
|
private fetchAsArrayBuffer;
|
|
39
40
|
private showError;
|
|
41
|
+
GetCurrentStateSnapshot(): DataSnapshot | null;
|
|
40
42
|
static ɵfac: i0.ɵɵFactoryDeclaration<XlsxArtifactViewerComponent, never>;
|
|
41
43
|
static ɵcmp: i0.ɵɵComponentDeclaration<XlsxArtifactViewerComponent, "mj-xlsx-artifact-viewer", never, {}, {}, never, never, false, never>;
|
|
42
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xlsx-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/plugins/xlsx-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,iCAAiC,EAAE,MAAM,mCAAmC,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,cAAc,EAAW,MAAM,mBAAmB,CAAC;;AAEpE,mDAAmD;AACnD,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;IAC5D,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;;GAKG;AACH,
|
|
1
|
+
{"version":3,"file":"xlsx-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/plugins/xlsx-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,YAAY,EAAiC,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,iCAAiC,EAAE,MAAM,mCAAmC,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,cAAc,EAAW,MAAM,mBAAmB,CAAC;;AAEpE,mDAAmD;AACnD,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;IAC5D,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBA6Ia,2BAA4B,SAAQ,iCAAkC,YAAW,MAAM;IAYhG,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,GAAG;IAZN,SAAS,UAAQ;IACjB,aAAa,UAAS;IACtB,YAAY,SAAM;IAClB,MAAM,EAAE,SAAS,EAAE,CAAM;IACzB,gBAAgB,SAAK;IACrB,aAAa,EAAE,MAAM,CAAmE;IAE/F,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,WAAW,CAAM;gBAGf,WAAW,EAAE,mBAAmB,EAChC,GAAG,EAAE,iBAAiB;IAKhC,IAAoB,iBAAiB,IAAI,OAAO,CAE/C;IAED,IAAW,WAAW,IAAI,SAAS,GAAG,IAAI,CAEzC;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAKxC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAO1B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAgB1B,YAAY;IAsC1B,OAAO,CAAC,aAAa;YAmBP,kBAAkB;IAQhC,OAAO,CAAC,SAAS;IAMD,uBAAuB,IAAI,YAAY,GAAG,IAAI;yCAjInD,2BAA2B;2CAA3B,2BAA2B;CA0JvC"}
|
|
@@ -6,6 +6,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
};
|
|
7
7
|
import { Component } from '@angular/core';
|
|
8
8
|
import { RegisterClass } from '@memberjunction/global';
|
|
9
|
+
import { DataSnapshot, DataTable, MJColumnDescriptor } from '@memberjunction/core';
|
|
9
10
|
import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
|
|
10
11
|
import * as i0 from "@angular/core";
|
|
11
12
|
import * as i1 from "../../services/artifact-file.service";
|
|
@@ -88,7 +89,9 @@ let XlsxArtifactViewerComponent = class XlsxArtifactViewerComponent extends Base
|
|
|
88
89
|
this.fileService = fileService;
|
|
89
90
|
this.cdr = cdr;
|
|
90
91
|
}
|
|
91
|
-
get hasDisplayContent() {
|
|
92
|
+
get hasDisplayContent() {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
92
95
|
get activeSheet() {
|
|
93
96
|
return this.sheets[this.activeSheetIndex] ?? null;
|
|
94
97
|
}
|
|
@@ -143,7 +146,7 @@ let XlsxArtifactViewerComponent = class XlsxArtifactViewerComponent extends Base
|
|
|
143
146
|
// Create an object URL for download support
|
|
144
147
|
this.downloadUrl = this.fileService.dataUrlToObjectUrl(content, this.artifactVersion.MimeType || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
145
148
|
}
|
|
146
|
-
const XLSX = await import('xlsx');
|
|
149
|
+
const XLSX = (await import('xlsx'));
|
|
147
150
|
const workbook = XLSX.read(arrayBuffer, { type: 'array' });
|
|
148
151
|
this.sheets = this.parseWorkbook(workbook, XLSX);
|
|
149
152
|
this.isLoading = false;
|
|
@@ -154,14 +157,14 @@ let XlsxArtifactViewerComponent = class XlsxArtifactViewerComponent extends Base
|
|
|
154
157
|
}
|
|
155
158
|
}
|
|
156
159
|
parseWorkbook(workbook, XLSX) {
|
|
157
|
-
return workbook.SheetNames.map(name => {
|
|
160
|
+
return workbook.SheetNames.map((name) => {
|
|
158
161
|
const sheet = workbook.Sheets[name];
|
|
159
162
|
const rows = XLSX.utils.sheet_to_json(sheet, {
|
|
160
163
|
defval: null,
|
|
161
164
|
raw: false, // Format dates/numbers as strings so AG Grid can display them
|
|
162
165
|
});
|
|
163
166
|
const headers = rows.length > 0 ? Object.keys(rows[0]) : [];
|
|
164
|
-
const columnDefs = headers.map(h => ({
|
|
167
|
+
const columnDefs = headers.map((h) => ({
|
|
165
168
|
field: h,
|
|
166
169
|
headerName: h,
|
|
167
170
|
tooltipField: h,
|
|
@@ -181,6 +184,30 @@ let XlsxArtifactViewerComponent = class XlsxArtifactViewerComponent extends Base
|
|
|
181
184
|
this.errorMessage = message;
|
|
182
185
|
this.cdr.markForCheck();
|
|
183
186
|
}
|
|
187
|
+
GetCurrentStateSnapshot() {
|
|
188
|
+
if (this.sheets.length === 0)
|
|
189
|
+
return null;
|
|
190
|
+
// Convert parsed sheets into DataTables for structured snapshot
|
|
191
|
+
const tables = this.sheets.map((sheet) => {
|
|
192
|
+
const table = new DataTable();
|
|
193
|
+
table.name = sheet.name;
|
|
194
|
+
table.source = 'static';
|
|
195
|
+
table.columns = sheet.columnDefs
|
|
196
|
+
.filter((col) => col.field)
|
|
197
|
+
.map((col) => {
|
|
198
|
+
const desc = new MJColumnDescriptor(col.field);
|
|
199
|
+
desc.displayName = col.headerName ?? col.field;
|
|
200
|
+
desc.sqlBaseType = 'nvarchar';
|
|
201
|
+
return desc;
|
|
202
|
+
});
|
|
203
|
+
table.rows = sheet.rowData;
|
|
204
|
+
table.metadata = { rowCount: sheet.rowData.length };
|
|
205
|
+
return table;
|
|
206
|
+
});
|
|
207
|
+
const snap = DataSnapshot.FromTables(tables, this.getDisplayTitle() ?? undefined);
|
|
208
|
+
snap.activeTab = this.sheets[this.activeSheetIndex]?.name;
|
|
209
|
+
return snap;
|
|
210
|
+
}
|
|
184
211
|
static ɵfac = function XlsxArtifactViewerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || XlsxArtifactViewerComponent)(i0.ɵɵdirectiveInject(i1.ArtifactFileService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
185
212
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: XlsxArtifactViewerComponent, selectors: [["mj-xlsx-artifact-viewer"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 4, consts: [[1, "xlsx-viewer"], [3, "download", "fileName", "isDownloading", "showPrint"], [1, "xlsx-viewer__state"], [1, "xlsx-viewer__state", "xlsx-viewer__state--error"], [1, "fas", "fa-spinner", "fa-spin"], [1, "fas", "fa-exclamation-triangle"], [1, "xlsx-viewer__tabs"], [1, "xlsx-viewer__grid"], [1, "ag-theme-quartz", 3, "gridReady", "rowData", "columnDefs", "defaultColDef", "animateRows", "suppressMovableColumns", "enableCellTextSelection"], [1, "xlsx-viewer__tab", 3, "xlsx-viewer__tab--active"], [1, "xlsx-viewer__tab", 3, "click"], [1, "fas", "fa-table"]], template: function XlsxArtifactViewerComponent_Template(rf, ctx) { if (rf & 1) {
|
|
186
213
|
i0.ɵɵelementStart(0, "div", 0)(1, "mj-file-artifact-toolbar", 1);
|
|
@@ -193,7 +220,7 @@ let XlsxArtifactViewerComponent = class XlsxArtifactViewerComponent extends Base
|
|
|
193
220
|
i0.ɵɵproperty("fileName", (ctx.artifactVersion == null ? null : ctx.artifactVersion.FileName) || "workbook.xlsx")("isDownloading", ctx.isDownloading)("showPrint", false);
|
|
194
221
|
i0.ɵɵadvance();
|
|
195
222
|
i0.ɵɵconditional(ctx.isLoading ? 2 : ctx.errorMessage ? 3 : 4);
|
|
196
|
-
} }, dependencies: [i2.AgGridAngular, i3.FileArtifactToolbarComponent], styles: [".xlsx-viewer[_ngcontent-%COMP%] {\n
|
|
223
|
+
} }, dependencies: [i2.AgGridAngular, i3.FileArtifactToolbarComponent], styles: [".xlsx-viewer[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n }\n\n .xlsx-viewer__state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n flex: 1;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .xlsx-viewer__state--error[_ngcontent-%COMP%] {\n color: var(--mj-status-error-text);\n }\n\n .xlsx-viewer__tabs[_ngcontent-%COMP%] {\n display: flex;\n gap: 2px;\n padding: 4px 8px 0;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n overflow-x: auto;\n flex-shrink: 0;\n }\n\n .xlsx-viewer__tab[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 6px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-bottom: none;\n border-radius: 4px 4px 0 0;\n color: var(--mj-text-secondary);\n font-size: 12px;\n cursor: pointer;\n white-space: nowrap;\n transition:\n background 0.1s,\n color 0.1s;\n }\n\n .xlsx-viewer__tab[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n .xlsx-viewer__tab--active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n border-color: var(--mj-border-default);\n font-weight: 600;\n position: relative;\n }\n\n .xlsx-viewer__tab--active[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n bottom: -1px;\n left: 0;\n right: 0;\n height: 1px;\n background: var(--mj-bg-surface);\n }\n\n .xlsx-viewer__grid[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n }\n\n .xlsx-viewer__grid[_ngcontent-%COMP%] ag-grid-angular[_ngcontent-%COMP%] {\n height: 100%;\n width: 100%;\n display: block;\n }"] });
|
|
197
224
|
};
|
|
198
225
|
XlsxArtifactViewerComponent = __decorate([
|
|
199
226
|
RegisterClass(BaseArtifactViewerPluginComponent, 'XlsxArtifactViewerPlugin')
|
|
@@ -207,7 +234,8 @@ export { XlsxArtifactViewerComponent };
|
|
|
207
234
|
[fileName]="artifactVersion?.FileName || 'workbook.xlsx'"
|
|
208
235
|
[isDownloading]="isDownloading"
|
|
209
236
|
[showPrint]="false"
|
|
210
|
-
(download)="onDownload()"
|
|
237
|
+
(download)="onDownload()"
|
|
238
|
+
>
|
|
211
239
|
</mj-file-artifact-toolbar>
|
|
212
240
|
|
|
213
241
|
@if (isLoading) {
|
|
@@ -224,10 +252,7 @@ export { XlsxArtifactViewerComponent };
|
|
|
224
252
|
@if (sheets.length > 1) {
|
|
225
253
|
<div class="xlsx-viewer__tabs">
|
|
226
254
|
@for (sheet of sheets; track sheet.name; let i = $index) {
|
|
227
|
-
<button
|
|
228
|
-
class="xlsx-viewer__tab"
|
|
229
|
-
[class.xlsx-viewer__tab--active]="i === activeSheetIndex"
|
|
230
|
-
(click)="selectSheet(i)">
|
|
255
|
+
<button class="xlsx-viewer__tab" [class.xlsx-viewer__tab--active]="i === activeSheetIndex" (click)="selectSheet(i)">
|
|
231
256
|
<i class="fas fa-table"></i>
|
|
232
257
|
{{ sheet.name }}
|
|
233
258
|
</button>
|
|
@@ -244,12 +269,13 @@ export { XlsxArtifactViewerComponent };
|
|
|
244
269
|
[animateRows]="false"
|
|
245
270
|
[suppressMovableColumns]="false"
|
|
246
271
|
[enableCellTextSelection]="true"
|
|
247
|
-
(gridReady)="onGridReady($event)"
|
|
272
|
+
(gridReady)="onGridReady($event)"
|
|
273
|
+
>
|
|
248
274
|
</ag-grid-angular>
|
|
249
275
|
</div>
|
|
250
276
|
}
|
|
251
277
|
</div>
|
|
252
|
-
`, styles: ["\n
|
|
278
|
+
`, styles: ["\n .xlsx-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n }\n\n .xlsx-viewer__state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n flex: 1;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .xlsx-viewer__state--error {\n color: var(--mj-status-error-text);\n }\n\n .xlsx-viewer__tabs {\n display: flex;\n gap: 2px;\n padding: 4px 8px 0;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n overflow-x: auto;\n flex-shrink: 0;\n }\n\n .xlsx-viewer__tab {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 6px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-bottom: none;\n border-radius: 4px 4px 0 0;\n color: var(--mj-text-secondary);\n font-size: 12px;\n cursor: pointer;\n white-space: nowrap;\n transition:\n background 0.1s,\n color 0.1s;\n }\n\n .xlsx-viewer__tab:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n .xlsx-viewer__tab--active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n border-color: var(--mj-border-default);\n font-weight: 600;\n position: relative;\n }\n\n .xlsx-viewer__tab--active::after {\n content: '';\n position: absolute;\n bottom: -1px;\n left: 0;\n right: 0;\n height: 1px;\n background: var(--mj-bg-surface);\n }\n\n .xlsx-viewer__grid {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n }\n\n .xlsx-viewer__grid ag-grid-angular {\n height: 100%;\n width: 100%;\n display: block;\n }\n "] }]
|
|
253
279
|
}], () => [{ type: i1.ArtifactFileService }, { type: i0.ChangeDetectorRef }], null); })();
|
|
254
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(XlsxArtifactViewerComponent, { className: "XlsxArtifactViewerComponent", filePath: "src/lib/components/plugins/xlsx-artifact-viewer.component.ts", lineNumber:
|
|
280
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(XlsxArtifactViewerComponent, { className: "XlsxArtifactViewerComponent", filePath: "src/lib/components/plugins/xlsx-artifact-viewer.component.ts", lineNumber: 162 }); })();
|
|
255
281
|
//# sourceMappingURL=xlsx-artifact-viewer.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xlsx-artifact-viewer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/plugins/xlsx-artifact-viewer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, OnInit, ChangeDetectorRef } from '@angular/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';\nimport { ArtifactFileService } from '../../services/artifact-file.service';\nimport { ColDef, GridReadyEvent, GridApi } from 'ag-grid-community';\n\n/** A parsed sheet ready for display in AG Grid. */\ninterface SheetData {\n name: string;\n rowData: Record<string, string | number | boolean | null>[];\n columnDefs: ColDef[];\n}\n\n/**\n * Viewer plugin for Excel (XLSX/XLS) artifact versions stored in MJStorage.\n *\n * Downloads the file, parses all sheets with SheetJS, then renders the active\n * sheet in an AG Grid. Sheet tabs let the user switch between sheets.\n */\n@Component({\n standalone: false,\n selector: 'mj-xlsx-artifact-viewer',\n template: `\n <div class=\"xlsx-viewer\">\n <mj-file-artifact-toolbar\n [fileName]=\"artifactVersion?.FileName || 'workbook.xlsx'\"\n [isDownloading]=\"isDownloading\"\n [showPrint]=\"false\"\n (download)=\"onDownload()\">\n </mj-file-artifact-toolbar>\n\n @if (isLoading) {\n <div class=\"xlsx-viewer__state\">\n <i class=\"fas fa-spinner fa-spin\"></i>\n <span>Loading workbook…</span>\n </div>\n } @else if (errorMessage) {\n <div class=\"xlsx-viewer__state xlsx-viewer__state--error\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <span>{{ errorMessage }}</span>\n </div>\n } @else {\n @if (sheets.length > 1) {\n <div class=\"xlsx-viewer__tabs\">\n @for (sheet of sheets; track sheet.name; let i = $index) {\n <button\n class=\"xlsx-viewer__tab\"\n [class.xlsx-viewer__tab--active]=\"i === activeSheetIndex\"\n (click)=\"selectSheet(i)\">\n <i class=\"fas fa-table\"></i>\n {{ sheet.name }}\n </button>\n }\n </div>\n }\n\n <div class=\"xlsx-viewer__grid\">\n <ag-grid-angular\n class=\"ag-theme-quartz\"\n [rowData]=\"activeSheet?.rowData\"\n [columnDefs]=\"activeSheet?.columnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [animateRows]=\"false\"\n [suppressMovableColumns]=\"false\"\n [enableCellTextSelection]=\"true\"\n (gridReady)=\"onGridReady($event)\">\n </ag-grid-angular>\n </div>\n }\n </div>\n `,\n styles: [`\n .xlsx-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n }\n\n .xlsx-viewer__state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n flex: 1;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .xlsx-viewer__state--error {\n color: var(--mj-status-error-text);\n }\n\n .xlsx-viewer__tabs {\n display: flex;\n gap: 2px;\n padding: 4px 8px 0;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n overflow-x: auto;\n flex-shrink: 0;\n }\n\n .xlsx-viewer__tab {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 6px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-bottom: none;\n border-radius: 4px 4px 0 0;\n color: var(--mj-text-secondary);\n font-size: 12px;\n cursor: pointer;\n white-space: nowrap;\n transition: background 0.1s, color 0.1s;\n }\n\n .xlsx-viewer__tab:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n .xlsx-viewer__tab--active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n border-color: var(--mj-border-default);\n font-weight: 600;\n position: relative;\n }\n\n .xlsx-viewer__tab--active::after {\n content: '';\n position: absolute;\n bottom: -1px;\n left: 0;\n right: 0;\n height: 1px;\n background: var(--mj-bg-surface);\n }\n\n .xlsx-viewer__grid {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n }\n\n .xlsx-viewer__grid ag-grid-angular {\n height: 100%;\n width: 100%;\n display: block;\n }\n `]\n})\n@RegisterClass(BaseArtifactViewerPluginComponent, 'XlsxArtifactViewerPlugin')\nexport class XlsxArtifactViewerComponent extends BaseArtifactViewerPluginComponent implements OnInit {\n\n public isLoading = true;\n public isDownloading = false;\n public errorMessage = '';\n public sheets: SheetData[] = [];\n public activeSheetIndex = 0;\n public defaultColDef: ColDef = { resizable: true, sortable: true, filter: true, minWidth: 80 };\n\n private gridApi: GridApi | null = null;\n private downloadUrl = '';\n\n constructor(\n private fileService: ArtifactFileService,\n private cdr: ChangeDetectorRef,\n ) {\n super();\n }\n\n public override get hasDisplayContent(): boolean { return true; }\n\n public get activeSheet(): SheetData | null {\n return this.sheets[this.activeSheetIndex] ?? null;\n }\n\n async ngOnInit(): Promise<void> {\n await this.loadWorkbook();\n }\n\n public onGridReady(event: GridReadyEvent): void {\n this.gridApi = event.api;\n this.gridApi.sizeColumnsToFit();\n }\n\n public selectSheet(index: number): void {\n this.activeSheetIndex = index;\n this.cdr.markForCheck();\n // Let Angular render the new rowData/columnDefs before re-sizing\n setTimeout(() => this.gridApi?.sizeColumnsToFit(), 0);\n }\n\n public async onDownload(): Promise<void> {\n if (!this.downloadUrl || this.isDownloading) {\n return;\n }\n this.isDownloading = true;\n this.cdr.markForCheck();\n try {\n await this.triggerBrowserDownload(\n this.downloadUrl,\n this.artifactVersion?.FileName || 'workbook.xlsx',\n );\n } finally {\n this.isDownloading = false;\n this.cdr.markForCheck();\n }\n }\n\n // ─── Private helpers ────────────────────────────────────────────────────────\n\n private async loadWorkbook(): Promise<void> {\n if (!this.artifactVersion?.ID) {\n this.showError('No artifact version provided.');\n return;\n }\n\n try {\n let arrayBuffer: ArrayBuffer;\n\n if (this.artifactVersion.ContentMode === 'File') {\n // File-backed: download from storage via pre-auth URL\n this.downloadUrl = await this.fileService.getDownloadUrl(this.artifactVersion.ID);\n arrayBuffer = await this.fetchAsArrayBuffer(this.downloadUrl);\n } else {\n // Inline: content is a base64 data URL stored in the artifact version\n const content = this.artifactVersion.Content;\n if (!content) {\n this.showError('Artifact has no content.');\n return;\n }\n arrayBuffer = this.fileService.dataUrlToArrayBuffer(content);\n // Create an object URL for download support\n this.downloadUrl = this.fileService.dataUrlToObjectUrl(\n content,\n this.artifactVersion.MimeType || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'\n );\n }\n\n const XLSX = await import('xlsx') as unknown as XlsxModuleShim;\n const workbook = XLSX.read(arrayBuffer, { type: 'array' });\n this.sheets = this.parseWorkbook(workbook, XLSX);\n this.isLoading = false;\n this.cdr.markForCheck();\n } catch (err) {\n this.showError(`Could not load workbook: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n private parseWorkbook(workbook: WorkbookType, XLSX: XlsxModuleShim): SheetData[] {\n return workbook.SheetNames.map(name => {\n const sheet = workbook.Sheets[name];\n const rows = XLSX.utils.sheet_to_json<Record<string, string | number | boolean | null>>(sheet, {\n defval: null,\n raw: false, // Format dates/numbers as strings so AG Grid can display them\n });\n\n const headers = rows.length > 0 ? Object.keys(rows[0]) : [];\n const columnDefs: ColDef[] = headers.map(h => ({\n field: h,\n headerName: h,\n tooltipField: h,\n }));\n\n return { name, rowData: rows, columnDefs };\n });\n }\n\n private async fetchAsArrayBuffer(url: string): Promise<ArrayBuffer> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} fetching file`);\n }\n return response.arrayBuffer();\n }\n\n private showError(message: string): void {\n this.isLoading = false;\n this.errorMessage = message;\n this.cdr.markForCheck();\n }\n\n}\n\n\n// ─── Minimal type shims for xlsx dynamic import ────────────────────────────────\n\ninterface WorkbookType {\n SheetNames: string[];\n Sheets: Record<string, unknown>;\n}\n\ninterface XlsxModuleShim {\n read(data: ArrayBuffer, opts: { type: 'array' | 'buffer' | 'binary' | 'base64' | 'string' }): WorkbookType;\n utils: {\n sheet_to_json<T>(sheet: unknown, opts: { defval: null; raw: boolean }): T[];\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"xlsx-artifact-viewer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/plugins/xlsx-artifact-viewer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, OnInit, ChangeDetectorRef } from '@angular/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { DataSnapshot, DataTable, MJColumnDescriptor } from '@memberjunction/core';\nimport { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';\nimport { ArtifactFileService } from '../../services/artifact-file.service';\nimport { ColDef, GridReadyEvent, GridApi } from 'ag-grid-community';\n\n/** A parsed sheet ready for display in AG Grid. */\ninterface SheetData {\n name: string;\n rowData: Record<string, string | number | boolean | null>[];\n columnDefs: ColDef[];\n}\n\n/**\n * Viewer plugin for Excel (XLSX/XLS) artifact versions stored in MJStorage.\n *\n * Downloads the file, parses all sheets with SheetJS, then renders the active\n * sheet in an AG Grid. Sheet tabs let the user switch between sheets.\n */\n@Component({\n standalone: false,\n selector: 'mj-xlsx-artifact-viewer',\n template: `\n <div class=\"xlsx-viewer\">\n <mj-file-artifact-toolbar\n [fileName]=\"artifactVersion?.FileName || 'workbook.xlsx'\"\n [isDownloading]=\"isDownloading\"\n [showPrint]=\"false\"\n (download)=\"onDownload()\"\n >\n </mj-file-artifact-toolbar>\n\n @if (isLoading) {\n <div class=\"xlsx-viewer__state\">\n <i class=\"fas fa-spinner fa-spin\"></i>\n <span>Loading workbook…</span>\n </div>\n } @else if (errorMessage) {\n <div class=\"xlsx-viewer__state xlsx-viewer__state--error\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <span>{{ errorMessage }}</span>\n </div>\n } @else {\n @if (sheets.length > 1) {\n <div class=\"xlsx-viewer__tabs\">\n @for (sheet of sheets; track sheet.name; let i = $index) {\n <button class=\"xlsx-viewer__tab\" [class.xlsx-viewer__tab--active]=\"i === activeSheetIndex\" (click)=\"selectSheet(i)\">\n <i class=\"fas fa-table\"></i>\n {{ sheet.name }}\n </button>\n }\n </div>\n }\n\n <div class=\"xlsx-viewer__grid\">\n <ag-grid-angular\n class=\"ag-theme-quartz\"\n [rowData]=\"activeSheet?.rowData\"\n [columnDefs]=\"activeSheet?.columnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [animateRows]=\"false\"\n [suppressMovableColumns]=\"false\"\n [enableCellTextSelection]=\"true\"\n (gridReady)=\"onGridReady($event)\"\n >\n </ag-grid-angular>\n </div>\n }\n </div>\n `,\n styles: [\n `\n .xlsx-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n }\n\n .xlsx-viewer__state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n flex: 1;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .xlsx-viewer__state--error {\n color: var(--mj-status-error-text);\n }\n\n .xlsx-viewer__tabs {\n display: flex;\n gap: 2px;\n padding: 4px 8px 0;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n overflow-x: auto;\n flex-shrink: 0;\n }\n\n .xlsx-viewer__tab {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 6px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-bottom: none;\n border-radius: 4px 4px 0 0;\n color: var(--mj-text-secondary);\n font-size: 12px;\n cursor: pointer;\n white-space: nowrap;\n transition:\n background 0.1s,\n color 0.1s;\n }\n\n .xlsx-viewer__tab:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n .xlsx-viewer__tab--active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n border-color: var(--mj-border-default);\n font-weight: 600;\n position: relative;\n }\n\n .xlsx-viewer__tab--active::after {\n content: '';\n position: absolute;\n bottom: -1px;\n left: 0;\n right: 0;\n height: 1px;\n background: var(--mj-bg-surface);\n }\n\n .xlsx-viewer__grid {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n }\n\n .xlsx-viewer__grid ag-grid-angular {\n height: 100%;\n width: 100%;\n display: block;\n }\n `,\n ],\n})\n@RegisterClass(BaseArtifactViewerPluginComponent, 'XlsxArtifactViewerPlugin')\nexport class XlsxArtifactViewerComponent extends BaseArtifactViewerPluginComponent implements OnInit {\n public isLoading = true;\n public isDownloading = false;\n public errorMessage = '';\n public sheets: SheetData[] = [];\n public activeSheetIndex = 0;\n public defaultColDef: ColDef = { resizable: true, sortable: true, filter: true, minWidth: 80 };\n\n private gridApi: GridApi | null = null;\n private downloadUrl = '';\n\n constructor(\n private fileService: ArtifactFileService,\n private cdr: ChangeDetectorRef,\n ) {\n super();\n }\n\n public override get hasDisplayContent(): boolean {\n return true;\n }\n\n public get activeSheet(): SheetData | null {\n return this.sheets[this.activeSheetIndex] ?? null;\n }\n\n async ngOnInit(): Promise<void> {\n await this.loadWorkbook();\n }\n\n public onGridReady(event: GridReadyEvent): void {\n this.gridApi = event.api;\n this.gridApi.sizeColumnsToFit();\n }\n\n public selectSheet(index: number): void {\n this.activeSheetIndex = index;\n this.cdr.markForCheck();\n // Let Angular render the new rowData/columnDefs before re-sizing\n setTimeout(() => this.gridApi?.sizeColumnsToFit(), 0);\n }\n\n public async onDownload(): Promise<void> {\n if (!this.downloadUrl || this.isDownloading) {\n return;\n }\n this.isDownloading = true;\n this.cdr.markForCheck();\n try {\n await this.triggerBrowserDownload(this.downloadUrl, this.artifactVersion?.FileName || 'workbook.xlsx');\n } finally {\n this.isDownloading = false;\n this.cdr.markForCheck();\n }\n }\n\n // ─── Private helpers ────────────────────────────────────────────────────────\n\n private async loadWorkbook(): Promise<void> {\n if (!this.artifactVersion?.ID) {\n this.showError('No artifact version provided.');\n return;\n }\n\n try {\n let arrayBuffer: ArrayBuffer;\n\n if (this.artifactVersion.ContentMode === 'File') {\n // File-backed: download from storage via pre-auth URL\n this.downloadUrl = await this.fileService.getDownloadUrl(this.artifactVersion.ID);\n arrayBuffer = await this.fetchAsArrayBuffer(this.downloadUrl);\n } else {\n // Inline: content is a base64 data URL stored in the artifact version\n const content = this.artifactVersion.Content;\n if (!content) {\n this.showError('Artifact has no content.');\n return;\n }\n arrayBuffer = this.fileService.dataUrlToArrayBuffer(content);\n // Create an object URL for download support\n this.downloadUrl = this.fileService.dataUrlToObjectUrl(\n content,\n this.artifactVersion.MimeType || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n );\n }\n\n const XLSX = (await import('xlsx')) as unknown as XlsxModuleShim;\n const workbook = XLSX.read(arrayBuffer, { type: 'array' });\n this.sheets = this.parseWorkbook(workbook, XLSX);\n this.isLoading = false;\n this.cdr.markForCheck();\n } catch (err) {\n this.showError(`Could not load workbook: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n private parseWorkbook(workbook: WorkbookType, XLSX: XlsxModuleShim): SheetData[] {\n return workbook.SheetNames.map((name) => {\n const sheet = workbook.Sheets[name];\n const rows = XLSX.utils.sheet_to_json<Record<string, string | number | boolean | null>>(sheet, {\n defval: null,\n raw: false, // Format dates/numbers as strings so AG Grid can display them\n });\n\n const headers = rows.length > 0 ? Object.keys(rows[0]) : [];\n const columnDefs: ColDef[] = headers.map((h) => ({\n field: h,\n headerName: h,\n tooltipField: h,\n }));\n\n return { name, rowData: rows, columnDefs };\n });\n }\n\n private async fetchAsArrayBuffer(url: string): Promise<ArrayBuffer> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} fetching file`);\n }\n return response.arrayBuffer();\n }\n\n private showError(message: string): void {\n this.isLoading = false;\n this.errorMessage = message;\n this.cdr.markForCheck();\n }\n\n public override GetCurrentStateSnapshot(): DataSnapshot | null {\n if (this.sheets.length === 0) return null;\n\n // Convert parsed sheets into DataTables for structured snapshot\n const tables: DataTable[] = this.sheets.map((sheet) => {\n const table = new DataTable();\n table.name = sheet.name;\n table.source = 'static';\n table.columns = sheet.columnDefs\n .filter((col) => col.field)\n .map((col) => {\n const desc = new MJColumnDescriptor(col.field as string);\n desc.displayName = (col.headerName as string | undefined) ?? (col.field as string);\n desc.sqlBaseType = 'nvarchar';\n return desc;\n });\n table.rows = sheet.rowData;\n table.metadata = { rowCount: sheet.rowData.length };\n return table;\n });\n\n const snap = DataSnapshot.FromTables(tables, this.getDisplayTitle() ?? undefined);\n snap.activeTab = this.sheets[this.activeSheetIndex]?.name;\n return snap;\n }\n}\n\n// ─── Minimal type shims for xlsx dynamic import ────────────────────────────────\n\ninterface WorkbookType {\n SheetNames: string[];\n Sheets: Record<string, unknown>;\n}\n\ninterface XlsxModuleShim {\n read(data: ArrayBuffer, opts: { type: 'array' | 'buffer' | 'binary' | 'base64' | 'string' }): WorkbookType;\n utils: {\n sheet_to_json<T>(sheet: unknown, opts: { defval: null; raw: boolean }): T[];\n };\n}\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { DataSnapshot, UserInfo } from '@memberjunction/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
/**
|
|
4
|
+
* Arguments for {@link AnalyzeArtifactService.StartAnalysisConversation}.
|
|
5
|
+
*/
|
|
6
|
+
export interface StartAnalysisConversationArgs {
|
|
7
|
+
/** The DataSnapshot captured from the source artifact's GetCurrentStateSnapshot(). */
|
|
8
|
+
snapshot: DataSnapshot;
|
|
9
|
+
/** Context user for all entity saves. */
|
|
10
|
+
currentUser: UserInfo;
|
|
11
|
+
/** Environment the new conversation and artifact belong to. */
|
|
12
|
+
environmentId: string;
|
|
13
|
+
/** Optional title override. Defaults to snapshot.title or 'Untitled Snapshot'. */
|
|
14
|
+
title?: string;
|
|
15
|
+
/** Optional initial user message text. Defaults to 'Analyze "<title>"'. */
|
|
16
|
+
initialMessage?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Arguments for {@link AnalyzeArtifactService.CreateSnapshotArtifact}.
|
|
20
|
+
* Creates just the Artifact + ArtifactVersion — no conversation, no junction.
|
|
21
|
+
* Used by callers who already have a conversation and just want to persist
|
|
22
|
+
* the snapshot and link it to a specific user message themselves.
|
|
23
|
+
*/
|
|
24
|
+
export interface CreateSnapshotArtifactArgs {
|
|
25
|
+
snapshot: DataSnapshot;
|
|
26
|
+
currentUser: UserInfo;
|
|
27
|
+
environmentId: string;
|
|
28
|
+
title?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Result of {@link AnalyzeArtifactService.CreateSnapshotArtifact}.
|
|
32
|
+
*/
|
|
33
|
+
export interface CreateSnapshotArtifactResult {
|
|
34
|
+
artifactId: string;
|
|
35
|
+
artifactVersionId: string;
|
|
36
|
+
title: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Result of {@link AnalyzeArtifactService.StartAnalysisConversation}.
|
|
40
|
+
*/
|
|
41
|
+
export interface AnalyzeArtifactResult {
|
|
42
|
+
conversationId: string;
|
|
43
|
+
conversationDetailId: string;
|
|
44
|
+
artifactId: string;
|
|
45
|
+
artifactVersionId: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Plumbing for the standalone "Analyze" flow (unified data layer plan §8.8).
|
|
49
|
+
*
|
|
50
|
+
* Given a captured {@link DataSnapshot} from any artifact viewer, creates a
|
|
51
|
+
* fresh "Data Snapshot" artifact containing the snapshot JSON, a new
|
|
52
|
+
* conversation, an initial user message, and a junction linking the artifact
|
|
53
|
+
* version as `Direction='Input'` on that message. Agent kickoff and navigation
|
|
54
|
+
* are the caller's responsibility.
|
|
55
|
+
*
|
|
56
|
+
* Consumers subscribe to `(analyzeRequested)` on `<mj-artifact-viewer-panel>`,
|
|
57
|
+
* call `StartAnalysisConversation`, then route the user to the returned
|
|
58
|
+
* `conversationId`.
|
|
59
|
+
*/
|
|
60
|
+
export declare class AnalyzeArtifactService {
|
|
61
|
+
/**
|
|
62
|
+
* Creates ONLY the Artifact + ArtifactVersion for a captured snapshot.
|
|
63
|
+
* Does not create a conversation or any junctions. Used by callers who
|
|
64
|
+
* already have a conversation and want to attach the snapshot to a
|
|
65
|
+
* message they're about to send (e.g., in-conversation Analyze flow).
|
|
66
|
+
*/
|
|
67
|
+
CreateSnapshotArtifact(args: CreateSnapshotArtifactArgs): Promise<CreateSnapshotArtifactResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Create a new analysis conversation seeded with the captured snapshot.
|
|
70
|
+
* Performs six sequential saves; throws with the underlying entity error
|
|
71
|
+
* message on any failure.
|
|
72
|
+
*/
|
|
73
|
+
StartAnalysisConversation(args: StartAnalysisConversationArgs): Promise<AnalyzeArtifactResult>;
|
|
74
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AnalyzeArtifactService, never>;
|
|
75
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<AnalyzeArtifactService>;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=analyze-artifact.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-artifact.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/analyze-artifact.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAuB,QAAQ,EAAE,MAAM,sBAAsB,CAAC;;AAUnF;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,sFAAsF;IACtF,QAAQ,EAAE,YAAY,CAAC;IACvB,yCAAyC;IACzC,WAAW,EAAE,QAAQ,CAAC;IACtB,+DAA+D;IAC/D,aAAa,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,EAAE,QAAQ,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,qBACa,sBAAsB;IACjC;;;;;OAKG;IACG,sBAAsB,CAAC,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,4BAA4B,CAAC;IA+CrG;;;;OAIG;IACG,yBAAyB,CAAC,IAAI,EAAE,6BAA6B,GAAG,OAAO,CAAC,qBAAqB,CAAC;yCA3DzF,sBAAsB;6CAAtB,sBAAsB;CAgHlC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { LogStatus, Metadata } from '@memberjunction/core';
|
|
3
|
+
import { ArtifactMetadataEngine, } from '@memberjunction/core-entities';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Plumbing for the standalone "Analyze" flow (unified data layer plan §8.8).
|
|
7
|
+
*
|
|
8
|
+
* Given a captured {@link DataSnapshot} from any artifact viewer, creates a
|
|
9
|
+
* fresh "Data Snapshot" artifact containing the snapshot JSON, a new
|
|
10
|
+
* conversation, an initial user message, and a junction linking the artifact
|
|
11
|
+
* version as `Direction='Input'` on that message. Agent kickoff and navigation
|
|
12
|
+
* are the caller's responsibility.
|
|
13
|
+
*
|
|
14
|
+
* Consumers subscribe to `(analyzeRequested)` on `<mj-artifact-viewer-panel>`,
|
|
15
|
+
* call `StartAnalysisConversation`, then route the user to the returned
|
|
16
|
+
* `conversationId`.
|
|
17
|
+
*/
|
|
18
|
+
export class AnalyzeArtifactService {
|
|
19
|
+
/**
|
|
20
|
+
* Creates ONLY the Artifact + ArtifactVersion for a captured snapshot.
|
|
21
|
+
* Does not create a conversation or any junctions. Used by callers who
|
|
22
|
+
* already have a conversation and want to attach the snapshot to a
|
|
23
|
+
* message they're about to send (e.g., in-conversation Analyze flow).
|
|
24
|
+
*/
|
|
25
|
+
async CreateSnapshotArtifact(args) {
|
|
26
|
+
const { snapshot, currentUser, environmentId } = args;
|
|
27
|
+
const title = args.title || snapshot.title || 'Untitled Snapshot';
|
|
28
|
+
// Resolve the "Data Snapshot" artifact type.
|
|
29
|
+
const engine = ArtifactMetadataEngine.Instance;
|
|
30
|
+
await engine.Config(false, currentUser);
|
|
31
|
+
let snapshotType = engine.FindArtifactType('Data Snapshot');
|
|
32
|
+
if (!snapshotType) {
|
|
33
|
+
await engine.Config(true, currentUser);
|
|
34
|
+
snapshotType = engine.FindArtifactType('Data Snapshot');
|
|
35
|
+
}
|
|
36
|
+
if (!snapshotType) {
|
|
37
|
+
throw new Error('AnalyzeArtifactService: "Data Snapshot" artifact type not found. Ensure metadata has been pushed.');
|
|
38
|
+
}
|
|
39
|
+
const md = new Metadata();
|
|
40
|
+
const artifact = await md.GetEntityObject('MJ: Artifacts', currentUser);
|
|
41
|
+
artifact.Name = title;
|
|
42
|
+
artifact.Description = `Snapshot captured for analysis at ${new Date().toISOString()}`;
|
|
43
|
+
artifact.TypeID = snapshotType.ID;
|
|
44
|
+
artifact.UserID = currentUser.ID;
|
|
45
|
+
artifact.EnvironmentID = environmentId;
|
|
46
|
+
if (!(await artifact.Save())) {
|
|
47
|
+
throw new Error(`Failed to save artifact: ${artifact.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
48
|
+
}
|
|
49
|
+
const version = await md.GetEntityObject('MJ: Artifact Versions', currentUser);
|
|
50
|
+
version.ArtifactID = artifact.ID;
|
|
51
|
+
version.VersionNumber = 1;
|
|
52
|
+
version.Content = JSON.stringify(snapshot);
|
|
53
|
+
version.UserID = currentUser.ID;
|
|
54
|
+
version.Name = title;
|
|
55
|
+
if (!(await version.Save())) {
|
|
56
|
+
throw new Error(`Failed to save artifact version: ${version.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
artifactId: artifact.ID,
|
|
60
|
+
artifactVersionId: version.ID,
|
|
61
|
+
title,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create a new analysis conversation seeded with the captured snapshot.
|
|
66
|
+
* Performs six sequential saves; throws with the underlying entity error
|
|
67
|
+
* message on any failure.
|
|
68
|
+
*/
|
|
69
|
+
async StartAnalysisConversation(args) {
|
|
70
|
+
const { snapshot, currentUser, environmentId } = args;
|
|
71
|
+
const title = args.title || snapshot.title || 'Untitled Snapshot';
|
|
72
|
+
const initialMessage = args.initialMessage || `Analyze "${title}"`;
|
|
73
|
+
// Steps 1-3: Create the Data Snapshot artifact + version (shared with CreateSnapshotArtifact)
|
|
74
|
+
const { artifactId, artifactVersionId } = await this.CreateSnapshotArtifact({
|
|
75
|
+
snapshot,
|
|
76
|
+
currentUser,
|
|
77
|
+
environmentId,
|
|
78
|
+
title,
|
|
79
|
+
});
|
|
80
|
+
const md = new Metadata();
|
|
81
|
+
// 4. Create the conversation
|
|
82
|
+
const conversation = await md.GetEntityObject('MJ: Conversations', currentUser);
|
|
83
|
+
conversation.Name = `Analyze: ${title}`;
|
|
84
|
+
conversation.UserID = currentUser.ID;
|
|
85
|
+
conversation.EnvironmentID = environmentId;
|
|
86
|
+
if (!(await conversation.Save())) {
|
|
87
|
+
throw new Error(`Failed to save conversation: ${conversation.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
88
|
+
}
|
|
89
|
+
// 5. Seed the initial user message
|
|
90
|
+
const detail = await md.GetEntityObject('MJ: Conversation Details', currentUser);
|
|
91
|
+
detail.ConversationID = conversation.ID;
|
|
92
|
+
detail.Role = 'User';
|
|
93
|
+
detail.Message = initialMessage;
|
|
94
|
+
if (!(await detail.Save())) {
|
|
95
|
+
throw new Error(`Failed to save conversation detail: ${detail.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
96
|
+
}
|
|
97
|
+
// 6. Link the snapshot as an input artifact on that message
|
|
98
|
+
const junction = await md.GetEntityObject('MJ: Conversation Detail Artifacts', currentUser);
|
|
99
|
+
junction.ConversationDetailID = detail.ID;
|
|
100
|
+
junction.ArtifactVersionID = artifactVersionId;
|
|
101
|
+
junction.Direction = 'Input';
|
|
102
|
+
if (!(await junction.Save())) {
|
|
103
|
+
throw new Error(`Failed to link artifact to conversation: ${junction.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
104
|
+
}
|
|
105
|
+
LogStatus(`[AnalyzeArtifactService] Created conversation ${conversation.ID} with input artifact ${artifactId} v1`);
|
|
106
|
+
return {
|
|
107
|
+
conversationId: conversation.ID,
|
|
108
|
+
conversationDetailId: detail.ID,
|
|
109
|
+
artifactId,
|
|
110
|
+
artifactVersionId,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
static ɵfac = function AnalyzeArtifactService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AnalyzeArtifactService)(); };
|
|
114
|
+
static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: AnalyzeArtifactService, factory: AnalyzeArtifactService.ɵfac, providedIn: 'root' });
|
|
115
|
+
}
|
|
116
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AnalyzeArtifactService, [{
|
|
117
|
+
type: Injectable,
|
|
118
|
+
args: [{ providedIn: 'root' }]
|
|
119
|
+
}], null, null); })();
|
|
120
|
+
//# sourceMappingURL=analyze-artifact.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-artifact.service.js","sourceRoot":"","sources":["../../../src/lib/services/analyze-artifact.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { DataSnapshot, LogStatus, Metadata, UserInfo } from '@memberjunction/core';\nimport {\n ArtifactMetadataEngine,\n MJArtifactEntity,\n MJArtifactVersionEntity,\n MJConversationEntity,\n MJConversationDetailEntity,\n MJConversationDetailArtifactEntity,\n} from '@memberjunction/core-entities';\n\n/**\n * Arguments for {@link AnalyzeArtifactService.StartAnalysisConversation}.\n */\nexport interface StartAnalysisConversationArgs {\n /** The DataSnapshot captured from the source artifact's GetCurrentStateSnapshot(). */\n snapshot: DataSnapshot;\n /** Context user for all entity saves. */\n currentUser: UserInfo;\n /** Environment the new conversation and artifact belong to. */\n environmentId: string;\n /** Optional title override. Defaults to snapshot.title or 'Untitled Snapshot'. */\n title?: string;\n /** Optional initial user message text. Defaults to 'Analyze \"<title>\"'. */\n initialMessage?: string;\n}\n\n/**\n * Arguments for {@link AnalyzeArtifactService.CreateSnapshotArtifact}.\n * Creates just the Artifact + ArtifactVersion — no conversation, no junction.\n * Used by callers who already have a conversation and just want to persist\n * the snapshot and link it to a specific user message themselves.\n */\nexport interface CreateSnapshotArtifactArgs {\n snapshot: DataSnapshot;\n currentUser: UserInfo;\n environmentId: string;\n title?: string;\n}\n\n/**\n * Result of {@link AnalyzeArtifactService.CreateSnapshotArtifact}.\n */\nexport interface CreateSnapshotArtifactResult {\n artifactId: string;\n artifactVersionId: string;\n title: string;\n}\n\n/**\n * Result of {@link AnalyzeArtifactService.StartAnalysisConversation}.\n */\nexport interface AnalyzeArtifactResult {\n conversationId: string;\n conversationDetailId: string;\n artifactId: string;\n artifactVersionId: string;\n}\n\n/**\n * Plumbing for the standalone \"Analyze\" flow (unified data layer plan §8.8).\n *\n * Given a captured {@link DataSnapshot} from any artifact viewer, creates a\n * fresh \"Data Snapshot\" artifact containing the snapshot JSON, a new\n * conversation, an initial user message, and a junction linking the artifact\n * version as `Direction='Input'` on that message. Agent kickoff and navigation\n * are the caller's responsibility.\n *\n * Consumers subscribe to `(analyzeRequested)` on `<mj-artifact-viewer-panel>`,\n * call `StartAnalysisConversation`, then route the user to the returned\n * `conversationId`.\n */\n@Injectable({ providedIn: 'root' })\nexport class AnalyzeArtifactService {\n /**\n * Creates ONLY the Artifact + ArtifactVersion for a captured snapshot.\n * Does not create a conversation or any junctions. Used by callers who\n * already have a conversation and want to attach the snapshot to a\n * message they're about to send (e.g., in-conversation Analyze flow).\n */\n async CreateSnapshotArtifact(args: CreateSnapshotArtifactArgs): Promise<CreateSnapshotArtifactResult> {\n const { snapshot, currentUser, environmentId } = args;\n const title = args.title || snapshot.title || 'Untitled Snapshot';\n\n // Resolve the \"Data Snapshot\" artifact type.\n const engine = ArtifactMetadataEngine.Instance;\n await engine.Config(false, currentUser);\n let snapshotType = engine.FindArtifactType('Data Snapshot');\n if (!snapshotType) {\n await engine.Config(true, currentUser);\n snapshotType = engine.FindArtifactType('Data Snapshot');\n }\n if (!snapshotType) {\n throw new Error(\n 'AnalyzeArtifactService: \"Data Snapshot\" artifact type not found. Ensure metadata has been pushed.',\n );\n }\n\n const md = new Metadata();\n\n const artifact = await md.GetEntityObject<MJArtifactEntity>('MJ: Artifacts', currentUser);\n artifact.Name = title;\n artifact.Description = `Snapshot captured for analysis at ${new Date().toISOString()}`;\n artifact.TypeID = snapshotType.ID;\n artifact.UserID = currentUser.ID;\n artifact.EnvironmentID = environmentId;\n if (!(await artifact.Save())) {\n throw new Error(`Failed to save artifact: ${artifact.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n\n const version = await md.GetEntityObject<MJArtifactVersionEntity>('MJ: Artifact Versions', currentUser);\n version.ArtifactID = artifact.ID;\n version.VersionNumber = 1;\n version.Content = JSON.stringify(snapshot);\n version.UserID = currentUser.ID;\n version.Name = title;\n if (!(await version.Save())) {\n throw new Error(`Failed to save artifact version: ${version.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n\n return {\n artifactId: artifact.ID,\n artifactVersionId: version.ID,\n title,\n };\n }\n\n /**\n * Create a new analysis conversation seeded with the captured snapshot.\n * Performs six sequential saves; throws with the underlying entity error\n * message on any failure.\n */\n async StartAnalysisConversation(args: StartAnalysisConversationArgs): Promise<AnalyzeArtifactResult> {\n const { snapshot, currentUser, environmentId } = args;\n const title = args.title || snapshot.title || 'Untitled Snapshot';\n const initialMessage = args.initialMessage || `Analyze \"${title}\"`;\n\n // Steps 1-3: Create the Data Snapshot artifact + version (shared with CreateSnapshotArtifact)\n const { artifactId, artifactVersionId } = await this.CreateSnapshotArtifact({\n snapshot,\n currentUser,\n environmentId,\n title,\n });\n\n const md = new Metadata();\n\n // 4. Create the conversation\n const conversation = await md.GetEntityObject<MJConversationEntity>('MJ: Conversations', currentUser);\n conversation.Name = `Analyze: ${title}`;\n conversation.UserID = currentUser.ID;\n conversation.EnvironmentID = environmentId;\n if (!(await conversation.Save())) {\n throw new Error(`Failed to save conversation: ${conversation.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n\n // 5. Seed the initial user message\n const detail = await md.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', currentUser);\n detail.ConversationID = conversation.ID;\n detail.Role = 'User';\n detail.Message = initialMessage;\n if (!(await detail.Save())) {\n throw new Error(`Failed to save conversation detail: ${detail.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n\n // 6. Link the snapshot as an input artifact on that message\n const junction = await md.GetEntityObject<MJConversationDetailArtifactEntity>('MJ: Conversation Detail Artifacts', currentUser);\n junction.ConversationDetailID = detail.ID;\n junction.ArtifactVersionID = artifactVersionId;\n junction.Direction = 'Input';\n if (!(await junction.Save())) {\n throw new Error(`Failed to link artifact to conversation: ${junction.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n\n LogStatus(\n `[AnalyzeArtifactService] Created conversation ${conversation.ID} with input artifact ${artifactId} v1`,\n );\n\n return {\n conversationId: conversation.ID,\n conversationDetailId: detail.ID,\n artifactId,\n artifactVersionId,\n };\n }\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helper functions for creating DataSnapshots from artifact content.
|
|
3
|
+
*
|
|
4
|
+
* These are extracted from Angular viewer components so the logic can be
|
|
5
|
+
* unit-tested without Angular TestBed. Each viewer plugin delegates to
|
|
6
|
+
* the corresponding function here.
|
|
7
|
+
*/
|
|
8
|
+
import { DataSnapshot, DataTable } from '@memberjunction/core';
|
|
9
|
+
/**
|
|
10
|
+
* Shape of a DataArtifactSpec that the data viewer works with.
|
|
11
|
+
* Only the fields relevant to snapshot creation are required.
|
|
12
|
+
*/
|
|
13
|
+
interface DataArtifactSpecLike {
|
|
14
|
+
title?: string;
|
|
15
|
+
columns?: {
|
|
16
|
+
field: string;
|
|
17
|
+
headerName?: string;
|
|
18
|
+
sqlBaseType?: string;
|
|
19
|
+
}[];
|
|
20
|
+
rows?: Record<string, unknown>[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a DataSnapshot from JSON artifact content.
|
|
24
|
+
* Parses the JSON, counts top-level keys, and stores the parsed value in custom.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createJsonSnapshot(content: string | null, title: string | null): DataSnapshot | null;
|
|
27
|
+
/**
|
|
28
|
+
* Create a DataSnapshot from code artifact content.
|
|
29
|
+
* Counts lines and stores content plus lineCount in custom.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createCodeSnapshot(content: string | null, title: string | null): DataSnapshot | null;
|
|
32
|
+
/**
|
|
33
|
+
* Create a DataSnapshot from markdown artifact content.
|
|
34
|
+
* Stores the raw content in custom.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createMarkdownSnapshot(content: string | null, title: string | null): DataSnapshot | null;
|
|
37
|
+
/**
|
|
38
|
+
* Create a DataSnapshot from a DataArtifactSpec (data viewer).
|
|
39
|
+
* Uses NormalizeToTables to convert the spec into a tabular snapshot.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Build a combined SQL string from multiple tables, each with a header comment.
|
|
43
|
+
* Returns null if no tables have SQL.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildMultiTableSql(tables: DataTable[]): string | null;
|
|
46
|
+
export declare function createDataSnapshot(spec: DataArtifactSpecLike | null): DataSnapshot | null;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=snapshot-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/snapshot-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,SAAS,EAAqB,MAAM,sBAAsB,CAAC;AAElF;;;GAGG;AACH,UAAU,oBAAoB;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,GAAG,IAAI,CAcpG;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,GAAG,IAAI,CAQpG;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,GAAG,IAAI,CAMxG;AAED;;;GAGG;AACH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,GAAG,IAAI,CAMrE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI,GAAG,YAAY,GAAG,IAAI,CAKzF"}
|