@memberjunction/ng-explorer-core 5.36.0 → 5.38.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/generated/lazy-feature-config.d.ts +1 -1
- package/dist/generated/lazy-feature-config.d.ts.map +1 -1
- package/dist/generated/lazy-feature-config.js +4 -2
- package/dist/generated/lazy-feature-config.js.map +1 -1
- package/dist/lib/__tests__/form-resolver.service.test.d.ts +2 -0
- package/dist/lib/__tests__/form-resolver.service.test.d.ts.map +1 -0
- package/dist/lib/__tests__/form-resolver.service.test.js +258 -0
- package/dist/lib/__tests__/form-resolver.service.test.js.map +1 -0
- package/dist/lib/conversation-feedback/conversation-feedback.d.ts +108 -0
- package/dist/lib/conversation-feedback/conversation-feedback.d.ts.map +1 -0
- package/dist/lib/conversation-feedback/conversation-feedback.js +809 -0
- package/dist/lib/conversation-feedback/conversation-feedback.js.map +1 -0
- package/dist/lib/conversation-feedback/index.d.ts +2 -0
- package/dist/lib/conversation-feedback/index.d.ts.map +1 -0
- package/dist/lib/conversation-feedback/index.js +2 -0
- package/dist/lib/conversation-feedback/index.js.map +1 -0
- package/dist/lib/resource-wrappers/artifact-resource.component.d.ts +10 -0
- package/dist/lib/resource-wrappers/artifact-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/artifact-resource.component.js +15 -5
- package/dist/lib/resource-wrappers/artifact-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/record-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/record-resource.component.js +22 -6
- package/dist/lib/resource-wrappers/record-resource.component.js.map +1 -1
- package/dist/lib/services/form-resolver.service.d.ts +139 -0
- package/dist/lib/services/form-resolver.service.d.ts.map +1 -0
- package/dist/lib/services/form-resolver.service.js +235 -0
- package/dist/lib/services/form-resolver.service.js.map +1 -0
- package/dist/lib/shell/components/header/app-nav.component.d.ts.map +1 -1
- package/dist/lib/shell/components/header/app-nav.component.js +12 -0
- package/dist/lib/shell/components/header/app-nav.component.js.map +1 -1
- package/dist/lib/shell/components/tabs/component-cache-manager.d.ts +24 -5
- package/dist/lib/shell/components/tabs/component-cache-manager.d.ts.map +1 -1
- package/dist/lib/shell/components/tabs/component-cache-manager.js +58 -14
- package/dist/lib/shell/components/tabs/component-cache-manager.js.map +1 -1
- package/dist/lib/shell/components/tabs/tab-container.component.d.ts +42 -0
- package/dist/lib/shell/components/tabs/tab-container.component.d.ts.map +1 -1
- package/dist/lib/shell/components/tabs/tab-container.component.js +186 -12
- package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
- package/dist/lib/single-record/single-record.component.d.ts +41 -3
- package/dist/lib/single-record/single-record.component.d.ts.map +1 -1
- package/dist/lib/single-record/single-record.component.js +192 -23
- package/dist/lib/single-record/single-record.component.js.map +1 -1
- package/dist/module.d.ts +32 -30
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +15 -6
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +46 -44
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-feedback.js","sourceRoot":"","sources":["../../../src/lib/conversation-feedback/conversation-feedback.ts","../../../src/lib/conversation-feedback/conversation-feedback.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, OnInit, ChangeDetectorRef, HostListener, inject } from '@angular/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseResourceComponent } from '@memberjunction/ng-shared';\nimport { RunQuery } from '@memberjunction/core';\nimport { ApplicationManager } from '@memberjunction/ng-base-application';\n\nexport interface FeedbackRow {\n ratingID: string;\n rating: number;\n comments: string | null;\n createdAt: string;\n raterName: string | null;\n raterEmail: string | null;\n conversationDetailID: string;\n conversationID: string | null;\n conversationName: string | null;\n messageText: string | null;\n messageSnippet: string | null;\n messageRole: string | null;\n agentID: string | null;\n agentName: string | null;\n}\n\nexport type RatingBand = 'all' | 'high' | 'mid' | 'low';\nexport type DateRange = 'all' | '1d' | '7d' | '30d' | '90d';\n\n@Component({\n standalone: false,\n selector: 'mj-conversation-feedback',\n templateUrl: './conversation-feedback.html',\n styleUrls: ['./conversation-feedback.css']\n})\n@RegisterClass(BaseResourceComponent, 'ConversationFeedbackResource')\nexport class ConversationFeedbackResource extends BaseResourceComponent implements OnInit {\n isInitializing = true;\n loading = false;\n statsLoading = false;\n error: string | null = null;\n\n ratingBand: RatingBand = 'all';\n dateRange: DateRange = '7d';\n searchTerm = '';\n\n rows: FeedbackRow[] = [];\n totalRowCount = 0;\n\n pageSize: number = 10;\n currentPage: number = 1;\n readonly pageSizeOptions: number[] = [10, 25, 50, 100];\n\n stats = { total: 0, avgRating: 0, percentPositive: 0, percentNegative: 0 };\n\n selectedRow: FeedbackRow | null = null;\n\n private searchDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n private cdr = inject(ChangeDetectorRef);\n private appManager = inject(ApplicationManager);\n\n async GetResourceDisplayName(): Promise<string> {\n return 'Agent Feedback';\n }\n\n async GetResourceIconClass(): Promise<string> {\n return 'fa-solid fa-comment-dots';\n }\n\n override async ngOnInit(): Promise<void> {\n this.isInitializing = false;\n this.cdr.detectChanges();\n await this.applyFilters();\n this.NotifyLoadComplete();\n }\n\n /** Manual refresh from the page header — same path as a filter change. */\n async loadFeedback(): Promise<void> {\n await this.applyFilters();\n }\n\n setBand(band: RatingBand): void {\n if (this.ratingBand === band) return;\n this.ratingBand = band;\n this.currentPage = 1;\n void this.applyFilters();\n }\n\n setDateRange(range: DateRange): void {\n if (this.dateRange === range) return;\n this.dateRange = range;\n this.currentPage = 1;\n void this.applyFilters();\n }\n\n /**\n * Search input fires on every keystroke; debounce so we don't slam the\n * server while the user is typing. 250ms is enough to feel responsive\n * without firing on intermediate states.\n */\n onSearchChange(): void {\n if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer);\n this.searchDebounceTimer = setTimeout(() => {\n this.currentPage = 1;\n void this.applyFilters();\n }, 250);\n }\n\n onPageChange(event: { PageNumber: number; PageSize: number; StartRow: number }): void {\n if (event.PageNumber === this.currentPage) return;\n this.currentPage = event.PageNumber;\n void this.loadRows();\n }\n\n onPageSizeChange(size: number): void {\n const next = Number(size);\n if (!next || next === this.pageSize) return;\n this.pageSize = next;\n this.currentPage = 1;\n void this.loadRows();\n }\n\n openDrawer(row: FeedbackRow): void {\n this.selectedRow = row;\n this.cdr.detectChanges();\n }\n\n closeDrawer(): void {\n this.selectedRow = null;\n this.cdr.detectChanges();\n }\n\n async openConversation(): Promise<void> {\n if (!this.selectedRow?.conversationID) return;\n const conversationID = this.selectedRow.conversationID;\n\n // The Chat app owns the Conversations nav item; the active app (Agent\n // Feedback) does not, so we must target Chat explicitly. Fall back to\n // scanning all apps for a nav item whose DriverClass is the chat resource.\n let appId: string | undefined = this.appManager.GetAppByName('Chat')?.ID;\n let navItemLabel = 'Conversations';\n\n if (!appId) {\n for (const app of this.appManager.GetAllApps()) {\n const navItems = await app.GetNavItems();\n const match = navItems.find(n => (n as any).DriverClass === 'ChatConversationsResource');\n if (match) {\n appId = app.ID;\n navItemLabel = match.Label;\n break;\n }\n }\n }\n\n if (!appId) {\n this.error = 'Could not find a Chat application to open this conversation in.';\n this.cdr.detectChanges();\n return;\n }\n\n this.closeDrawer();\n await this.navigationService.OpenNavItemByName(navItemLabel, { conversationId: conversationID }, appId);\n }\n\n @HostListener('document:keydown.escape')\n onEscape(): void {\n if (this.selectedRow) this.closeDrawer();\n }\n\n bandClassFor(rating: number): string {\n if (rating >= 8) return 'rating-high';\n if (rating >= 4) return 'rating-mid';\n return 'rating-low';\n }\n\n trackByRatingID = (_: number, row: FeedbackRow): string => row.ratingID;\n\n /** 1-based index of the first row on the current page (0 when empty). */\n get rangeStart(): number {\n if (this.totalRowCount === 0) return 0;\n return (this.currentPage - 1) * this.pageSize + 1;\n }\n\n /** 1-based index of the last row on the current page (clamped to total). */\n get rangeEnd(): number {\n return Math.min(this.currentPage * this.pageSize, this.totalRowCount);\n }\n\n /**\n * True when the filtered set fits on a single page — in this case\n * mj-pagination hides itself, so the footer renders a fallback summary\n * line. When false we let mj-pagination own the bar (it has its own\n * `1-N of M` summary on the left + nav controls on the right).\n */\n get isSinglePage(): boolean {\n if (this.totalRowCount <= 0) return false;\n return Math.ceil(this.totalRowCount / this.pageSize) <= 1;\n }\n\n /**\n * Run both the rows-query (paged) and the stats-query (aggregate) against\n * the current filter state. Called when filters change or on initial load.\n */\n private async applyFilters(): Promise<void> {\n await Promise.all([this.loadRows(), this.loadStats()]);\n }\n\n /**\n * Fetch the current page of rows from the server. The mj-pagination\n * footer reads `totalRowCount` to derive the page count.\n */\n private async loadRows(): Promise<void> {\n this.loading = true;\n this.error = null;\n this.cdr.detectChanges();\n\n try {\n const rq = new RunQuery();\n const result = await rq.RunQuery({\n QueryName: 'ListConversationDetailFeedback',\n CategoryPath: '/MJ/Conversations',\n Parameters: this.buildQueryParameters(),\n StartRow: (this.currentPage - 1) * this.pageSize,\n MaxRows: this.pageSize\n });\n\n if (!result.Success) {\n this.error = result.ErrorMessage || 'Failed to load feedback';\n this.rows = [];\n this.totalRowCount = 0;\n return;\n }\n\n this.rows = (result.Results ?? []).map((r: any) => this.mapRow(r));\n this.totalRowCount = result.TotalRowCount ?? this.rows.length;\n } catch (e: any) {\n this.error = e?.message || 'Failed to load feedback';\n this.rows = [];\n this.totalRowCount = 0;\n } finally {\n this.loading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Aggregate counts over the same filter universe as `loadRows()`. Runs in\n * parallel with the rows query so the stat cards and the table stay in\n * sync after every filter change.\n */\n private async loadStats(): Promise<void> {\n this.statsLoading = true;\n this.cdr.detectChanges();\n\n try {\n const rq = new RunQuery();\n const result = await rq.RunQuery({\n QueryName: 'ListConversationDetailFeedbackStats',\n CategoryPath: '/MJ/Conversations',\n Parameters: this.buildQueryParameters()\n });\n\n const row = (result.Results?.[0] as any) || {};\n const total = Number(row.Total ?? 0);\n const avg = Number(row.AvgRating ?? 0);\n const pos = Number(row.PositiveCount ?? 0);\n const neg = Number(row.NegativeCount ?? 0);\n\n this.stats = {\n total,\n avgRating: total ? Math.round(avg * 10) / 10 : 0,\n percentPositive: total ? Math.round((pos / total) * 100) : 0,\n percentNegative: total ? Math.round((neg / total) * 100) : 0\n };\n } catch (e) {\n // Stats are non-critical — the table is the primary surface. Log and\n // leave the previous stats values in place.\n console.warn('[Feedback] stats query failed', e);\n } finally {\n this.statsLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Build the Nunjucks-bound parameter set passed to both queries. Server-side\n * filtering: emit only the keys that actually have a value so the SQL's\n * `{% if X %}` blocks stay inert when filters are at their default.\n */\n private buildQueryParameters(): Record<string, unknown> {\n const params: {\n MinRating?: number;\n MaxRating?: number;\n StartDate?: string;\n SearchText?: string;\n } = {};\n\n if (this.ratingBand === 'high') {\n params.MinRating = 8;\n } else if (this.ratingBand === 'mid') {\n params.MinRating = 4;\n params.MaxRating = 7;\n } else if (this.ratingBand === 'low') {\n params.MaxRating = 3;\n }\n\n const cutoffIso = this.dateRangeCutoffIso();\n if (cutoffIso) {\n params.StartDate = cutoffIso;\n }\n\n const term = this.searchTerm.trim();\n if (term) {\n params.SearchText = term;\n }\n\n return params;\n }\n\n private dateRangeCutoffIso(): string | null {\n if (this.dateRange === 'all') return null;\n const days = this.dateRange === '1d' ? 1\n : this.dateRange === '7d' ? 7\n : this.dateRange === '30d' ? 30\n : 90;\n const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);\n return cutoff.toISOString();\n }\n\n private mapRow(r: any): FeedbackRow {\n const message: string | null = r.MessageText ?? null;\n const snippet = message\n ? (message.length > 160 ? message.slice(0, 160) + '…' : message)\n : null;\n return {\n ratingID: r.RatingID,\n rating: r.Rating,\n comments: r.Comments ?? null,\n createdAt: r.RatedAt ?? '',\n raterName: r.RaterName ?? null,\n raterEmail: r.RaterEmail ?? null,\n conversationDetailID: r.ConversationDetailID,\n conversationID: r.ConversationID ?? null,\n conversationName: r.ConversationName ?? null,\n messageText: message,\n messageSnippet: snippet,\n messageRole: r.MessageRole ?? null,\n agentID: r.AgentID ?? null,\n agentName: r.AgentName ?? null\n };\n }\n}\n","<div class=\"feedback-container\">\n\n @if (isInitializing) {\n <div class=\"loading-overlay\"><div class=\"spinner\"></div> Loading...</div>\n }\n\n @if (!isInitializing) {\n\n <div class=\"sticky-header\">\n <div class=\"page-header\">\n <div class=\"page-title-area\">\n <div class=\"page-icon\"><i class=\"fa-solid fa-comment-dots\"></i></div>\n <div>\n <h1 class=\"page-title\">Agent Feedback</h1>\n <p class=\"page-subtitle\">User ratings and comments on AI messages</p>\n </div>\n </div>\n <div class=\"page-actions\">\n <button class=\"btn-action\" (click)=\"loadFeedback()\" [disabled]=\"loading\">\n <i class=\"fa-solid fa-arrows-rotate\" [class.fa-spin]=\"loading\"></i> Refresh\n </button>\n </div>\n </div>\n\n @if (error) {\n <div class=\"error-banner\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n {{ error }}\n <button (click)=\"error = null\" aria-label=\"Dismiss error\">×</button>\n </div>\n }\n\n <div class=\"stats-grid\">\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-total\"><i class=\"fa-solid fa-comment-dots\"></i></div>\n <div class=\"stat-content\">\n <div class=\"stat-value\">{{ stats.total }}</div>\n <div class=\"stat-label\">Total Ratings</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-avg\"><i class=\"fa-solid fa-star\"></i></div>\n <div class=\"stat-content\">\n <div class=\"stat-value\">{{ stats.avgRating }}<span class=\"stat-unit\">/10</span></div>\n <div class=\"stat-label\">Avg Rating</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-positive\"><i class=\"fa-solid fa-thumbs-up\"></i></div>\n <div class=\"stat-content\">\n <div class=\"stat-value rating-high-text\">{{ stats.percentPositive }}%</div>\n <div class=\"stat-label\">Positive (8-10)</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-negative\"><i class=\"fa-solid fa-thumbs-down\"></i></div>\n <div class=\"stat-content\">\n <div class=\"stat-value rating-low-text\">{{ stats.percentNegative }}%</div>\n <div class=\"stat-label\">Negative (1-3)</div>\n </div>\n </div>\n </div>\n\n <div class=\"filter-bar\">\n <div class=\"filter-row\">\n <div class=\"filter-group\">\n <span class=\"filter-label\">Date Range</span>\n <div class=\"btn-group\">\n <button class=\"btn-filter\" [class.active]=\"dateRange === '1d'\" (click)=\"setDateRange('1d')\">1d</button>\n <button class=\"btn-filter\" [class.active]=\"dateRange === '7d'\" (click)=\"setDateRange('7d')\">7d</button>\n <button class=\"btn-filter\" [class.active]=\"dateRange === '30d'\" (click)=\"setDateRange('30d')\">30d</button>\n <button class=\"btn-filter\" [class.active]=\"dateRange === '90d'\" (click)=\"setDateRange('90d')\">90d</button>\n <button class=\"btn-filter\" [class.active]=\"dateRange === 'all'\" (click)=\"setDateRange('all')\">All</button>\n </div>\n </div>\n <div class=\"filter-group\">\n <span class=\"filter-label\">Rating Band</span>\n <div class=\"btn-group\">\n <button class=\"btn-filter\" [class.active]=\"ratingBand === 'all'\" (click)=\"setBand('all')\">All</button>\n <button class=\"btn-filter band-high\" [class.active]=\"ratingBand === 'high'\" (click)=\"setBand('high')\">High 8-10</button>\n <button class=\"btn-filter band-mid\" [class.active]=\"ratingBand === 'mid'\" (click)=\"setBand('mid')\">Mid 4-7</button>\n <button class=\"btn-filter band-low\" [class.active]=\"ratingBand === 'low'\" (click)=\"setBand('low')\">Low 1-3</button>\n </div>\n </div>\n <div class=\"filter-group filter-search\">\n <span class=\"filter-label\">Search</span>\n <input\n type=\"text\"\n class=\"input-search\"\n [(ngModel)]=\"searchTerm\"\n (input)=\"onSearchChange()\"\n placeholder=\"Search comments, message, agent, rater...\" />\n </div>\n </div>\n <div class=\"diagnostic-line\">\n {{ totalRowCount }} {{ totalRowCount === 1 ? 'rating' : 'ratings' }} match the current filters\n </div>\n </div>\n </div>\n\n <div class=\"scrollable-content\">\n @if (loading) {\n <div class=\"loading-overlay\"><div class=\"spinner\"></div> Loading feedback...</div>\n }\n\n @if (!loading) {\n @if (rows.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-comment-slash\"></i>\n <p>No feedback matches the current filters.</p>\n </div>\n } @else {\n <div class=\"feedback-table-wrapper\">\n <table class=\"feedback-table\">\n <thead>\n <tr>\n <th class=\"col-rating\">Rating</th>\n <th class=\"col-comment\">Comments</th>\n <th class=\"col-agent\">Agent</th>\n <th class=\"col-message\">Message</th>\n <th class=\"col-rater\">Rater</th>\n <th class=\"col-when\">When</th>\n </tr>\n </thead>\n <tbody>\n @for (row of rows; track trackByRatingID($index, row)) {\n <tr\n class=\"feedback-row\"\n [class.expanded]=\"selectedRow?.ratingID === row.ratingID\"\n (click)=\"openDrawer(row)\">\n <td class=\"col-rating\">\n <span class=\"rating-badge\" [ngClass]=\"bandClassFor(row.rating)\">\n {{ row.rating }}<small>/10</small>\n </span>\n </td>\n <td class=\"col-comment\">\n @if (row.comments) {\n <div class=\"comment-text clamped\">{{ row.comments }}</div>\n } @else {\n <span class=\"muted\">(no comment)</span>\n }\n </td>\n <td class=\"col-agent\">\n <span class=\"agent-name\">{{ row.agentName || '—' }}</span>\n </td>\n <td class=\"col-message\">\n @if (row.messageSnippet) {\n <span class=\"message-snippet\">{{ row.messageSnippet }}</span>\n } @else {\n <span class=\"muted\">—</span>\n }\n </td>\n <td class=\"col-rater\">\n <div class=\"rater-name\">{{ row.raterName || row.raterEmail || '—' }}</div>\n @if (row.raterEmail && row.raterName) {\n <div class=\"rater-email\">{{ row.raterEmail }}</div>\n }\n </td>\n <td class=\"col-when\">\n <span class=\"when\">{{ row.createdAt | date:'short' }}</span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <!--\n Bottom bar. When the filtered set spans multiple pages mj-pagination\n renders its own full-width `1-N of M | controls` row. When it fits\n on a single page mj-pagination self-hides, so the footer renders\n a single fallback summary line in its place.\n -->\n <!--\n Bottom bar: mj-pagination on the left (it has its own internal\n `1-N of M | nav controls` layout) + page-size selector hugging the\n right edge. The selector lives outside mj-pagination because the\n shared component doesn't expose a size-change output; the parent\n owns PageSize.\n -->\n <div class=\"pagination-bar\">\n @if (isSinglePage) {\n <span class=\"pagination-summary\">\n {{ rangeStart | number }}-{{ rangeEnd | number }} of {{ totalRowCount | number }}\n {{ totalRowCount === 1 ? 'rating' : 'ratings' }}\n </span>\n } @else {\n <mj-pagination\n class=\"feedback-pagination\"\n [TotalRowCount]=\"totalRowCount\"\n [PageNumber]=\"currentPage\"\n [PageSize]=\"pageSize\"\n [IsLoading]=\"loading\"\n (PageChange)=\"onPageChange($event)\">\n </mj-pagination>\n }\n\n <label class=\"page-size-control\">\n <span class=\"page-size-label\">Rows</span>\n <select\n class=\"page-size-select\"\n [ngModel]=\"pageSize\"\n (ngModelChange)=\"onPageSizeChange($event)\">\n @for (size of pageSizeOptions; track size) {\n <option [ngValue]=\"size\">{{ size }}</option>\n }\n </select>\n </label>\n </div>\n }\n }\n </div>\n }\n\n <!-- ===== Detail slide-out panel ===== -->\n @if (selectedRow) {\n <div class=\"slideout-backdrop\" (click)=\"closeDrawer()\"></div>\n }\n\n <aside class=\"slideout-panel\"\n [class.open]=\"!!selectedRow\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"slideout-title\">\n @if (selectedRow; as r) {\n <div class=\"slideout-header\">\n <div class=\"slideout-title\">\n <i class=\"fa-solid fa-comment-dots\"></i>\n <div class=\"slideout-title-text\">\n <h2 id=\"slideout-title\" class=\"slideout-title-line\">\n Feedback on {{ r.agentName || 'AI message' }}\n </h2>\n <div class=\"slideout-subtitle\">\n <span class=\"rating-badge\" [ngClass]=\"bandClassFor(r.rating)\">\n {{ r.rating }}<small>/10</small>\n </span>\n <span>\n Rated by <strong>{{ r.raterName || r.raterEmail || 'Unknown user' }}</strong>\n · {{ r.createdAt | date:'medium' }}\n </span>\n </div>\n </div>\n </div>\n <button class=\"slideout-close\" (click)=\"closeDrawer()\" title=\"Close (Esc)\" aria-label=\"Close\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <div class=\"slideout-content\">\n <div class=\"slideout-section\">\n <div class=\"section-label\">Reviewer Comment</div>\n @if (r.comments) {\n <div class=\"comment-body\">{{ r.comments }}</div>\n } @else {\n <div class=\"comment-body muted\">(no comment was provided)</div>\n }\n </div>\n\n <div class=\"slideout-section\">\n <div class=\"meta-grid\">\n <div class=\"meta-card\">\n <div class=\"meta-label\">Agent</div>\n <div class=\"meta-value\">{{ r.agentName || '—' }}</div>\n </div>\n <div class=\"meta-card\">\n <div class=\"meta-label\">Conversation</div>\n <div class=\"meta-value\">{{ r.conversationName || '—' }}</div>\n </div>\n <div class=\"meta-card\">\n <div class=\"meta-label\">Message Role</div>\n <div class=\"meta-value\">{{ r.messageRole || '—' }}</div>\n </div>\n </div>\n </div>\n\n <div class=\"slideout-section\">\n <div class=\"message-card\">\n <div class=\"message-card-header\">\n <i class=\"fa-solid fa-message\"></i> Rated Message\n </div>\n <div class=\"message-card-body\">\n @if (r.messageText) {\n {{ r.messageText }}\n } @else {\n <span class=\"muted\">(message text unavailable)</span>\n }\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"slideout-footer\">\n <button mjButton variant=\"flat\" size=\"lg\" (click)=\"closeDrawer()\">Close</button>\n <button mjButton variant=\"primary\" size=\"lg\"\n (click)=\"openConversation()\"\n [disabled]=\"!r.conversationID\"\n [title]=\"r.conversationID ? 'Open the source conversation' : 'No conversation linked'\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n Open Conversation\n </button>\n </div>\n }\n </aside>\n</div>\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-feedback/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/conversation-feedback/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC","sourcesContent":["export * from './conversation-feedback';\n"]}
|
|
@@ -9,6 +9,16 @@ import * as i0 from "@angular/core";
|
|
|
9
9
|
*/
|
|
10
10
|
export declare class ArtifactResource extends BaseResourceComponent {
|
|
11
11
|
private readonly analyzeService;
|
|
12
|
+
private readonly applyService;
|
|
13
|
+
/**
|
|
14
|
+
* Apply-to-my-form handler — see InteractiveFormApplyService.
|
|
15
|
+
* Confirms with the user, then routes to Create or Modify depending on
|
|
16
|
+
* whether an Active override already exists for this entity+user.
|
|
17
|
+
*/
|
|
18
|
+
onApplyFormRequested(event: {
|
|
19
|
+
spec: unknown;
|
|
20
|
+
entityName: string;
|
|
21
|
+
}): Promise<void>;
|
|
12
22
|
currentUser: UserInfo | null;
|
|
13
23
|
artifactId: string;
|
|
14
24
|
environmentId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-resource.component.d.ts","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/artifact-resource.component.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAA+B,MAAM,+BAA+B,CAAC;;
|
|
1
|
+
{"version":3,"file":"artifact-resource.component.d.ts","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/artifact-resource.component.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAA+B,MAAM,+BAA+B,CAAC;;AAK1F;;;;GAIG;AACH,qBA8Ba,gBAAiB,SAAQ,qBAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAkC;IACjE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuC;IAEpE;;;;OAIG;IACG,oBAAoB,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQhF,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAQ;IACpC,UAAU,EAAE,MAAM,CAAM;IACxB,aAAa,EAAE,MAAM,CAAM;IAElC,QAAQ;IAoBR;;;;OAIG;IACG,kBAAkB,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,YAAY,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBxF,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAe3D,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;yCAjFpD,gBAAgB;2CAAhB,gBAAgB;CAoF5B"}
|
|
@@ -8,14 +8,14 @@ import { Component, ViewEncapsulation, inject } from '@angular/core';
|
|
|
8
8
|
import { RegisterClass } from '@memberjunction/global';
|
|
9
9
|
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
10
10
|
import { MJEnvironmentEntityExtended } from '@memberjunction/core-entities';
|
|
11
|
-
import { AnalyzeArtifactService } from '@memberjunction/ng-artifacts';
|
|
11
|
+
import { AnalyzeArtifactService, InteractiveFormApplyService } from '@memberjunction/ng-artifacts';
|
|
12
12
|
import { MJNotificationService } from '@memberjunction/ng-notifications';
|
|
13
13
|
import * as i0 from "@angular/core";
|
|
14
14
|
import * as i1 from "@memberjunction/ng-artifacts";
|
|
15
15
|
function ArtifactResource_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
16
16
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
17
17
|
i0.ɵɵelementStart(0, "mj-artifact-viewer-panel", 2);
|
|
18
|
-
i0.ɵɵlistener("analyzeRequested", function ArtifactResource_Conditional_1_Template_mj_artifact_viewer_panel_analyzeRequested_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onAnalyzeRequested($event)); });
|
|
18
|
+
i0.ɵɵlistener("analyzeRequested", function ArtifactResource_Conditional_1_Template_mj_artifact_viewer_panel_analyzeRequested_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onAnalyzeRequested($event)); })("applyFormRequested", function ArtifactResource_Conditional_1_Template_mj_artifact_viewer_panel_applyFormRequested_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onApplyFormRequested($event)); });
|
|
19
19
|
i0.ɵɵelementEnd();
|
|
20
20
|
} if (rf & 2) {
|
|
21
21
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
@@ -28,6 +28,15 @@ function ArtifactResource_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
|
28
28
|
*/
|
|
29
29
|
let ArtifactResource = class ArtifactResource extends BaseResourceComponent {
|
|
30
30
|
analyzeService = inject(AnalyzeArtifactService);
|
|
31
|
+
applyService = inject(InteractiveFormApplyService);
|
|
32
|
+
/**
|
|
33
|
+
* Apply-to-my-form handler — see InteractiveFormApplyService.
|
|
34
|
+
* Confirms with the user, then routes to Create or Modify depending on
|
|
35
|
+
* whether an Active override already exists for this entity+user.
|
|
36
|
+
*/
|
|
37
|
+
async onApplyFormRequested(event) {
|
|
38
|
+
await this.applyService.ConfirmAndApply(event.spec, event.entityName, this.ProviderToUse);
|
|
39
|
+
}
|
|
31
40
|
currentUser = null;
|
|
32
41
|
artifactId = '';
|
|
33
42
|
environmentId = '';
|
|
@@ -90,7 +99,7 @@ let ArtifactResource = class ArtifactResource extends BaseResourceComponent {
|
|
|
90
99
|
return 'fa-solid fa-file-code';
|
|
91
100
|
}
|
|
92
101
|
static ɵfac = /*@__PURE__*/ (() => { let ɵArtifactResource_BaseFactory; return function ArtifactResource_Factory(__ngFactoryType__) { return (ɵArtifactResource_BaseFactory || (ɵArtifactResource_BaseFactory = i0.ɵɵgetInheritedFactory(ArtifactResource)))(__ngFactoryType__ || ArtifactResource); }; })();
|
|
93
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ArtifactResource, selectors: [["mj-artifact-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 1, consts: [[1, "artifact-container"], [3, "artifactId", "currentUser", "environmentId", "showSaveToCollection", "canShare", "canEdit"], [3, "analyzeRequested", "artifactId", "currentUser", "environmentId", "showSaveToCollection", "canShare", "canEdit"]], template: function ArtifactResource_Template(rf, ctx) { if (rf & 1) {
|
|
102
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ArtifactResource, selectors: [["mj-artifact-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 1, consts: [[1, "artifact-container"], [3, "artifactId", "currentUser", "environmentId", "showSaveToCollection", "canShare", "canEdit"], [3, "analyzeRequested", "applyFormRequested", "artifactId", "currentUser", "environmentId", "showSaveToCollection", "canShare", "canEdit"]], template: function ArtifactResource_Template(rf, ctx) { if (rf & 1) {
|
|
94
103
|
i0.ɵɵelementStart(0, "div", 0);
|
|
95
104
|
i0.ɵɵconditionalCreate(1, ArtifactResource_Conditional_1_Template, 1, 6, "mj-artifact-viewer-panel", 1);
|
|
96
105
|
i0.ɵɵelementEnd();
|
|
@@ -115,11 +124,12 @@ export { ArtifactResource };
|
|
|
115
124
|
[showSaveToCollection]="true"
|
|
116
125
|
[canShare]="true"
|
|
117
126
|
[canEdit]="true"
|
|
118
|
-
(analyzeRequested)="onAnalyzeRequested($event)"
|
|
127
|
+
(analyzeRequested)="onAnalyzeRequested($event)"
|
|
128
|
+
(applyFormRequested)="onApplyFormRequested($event)">
|
|
119
129
|
</mj-artifact-viewer-panel>
|
|
120
130
|
}
|
|
121
131
|
</div>
|
|
122
132
|
`, encapsulation: ViewEncapsulation.None, styles: ["\n .artifact-container {\n display: flex;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n "] }]
|
|
123
133
|
}], null, null); })();
|
|
124
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ArtifactResource, { className: "ArtifactResource", filePath: "src/lib/resource-wrappers/artifact-resource.component.ts", lineNumber:
|
|
134
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ArtifactResource, { className: "ArtifactResource", filePath: "src/lib/resource-wrappers/artifact-resource.component.ts", lineNumber: 45 }); })();
|
|
125
135
|
//# sourceMappingURL=artifact-resource.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/artifact-resource.component.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAgB,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"artifact-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/artifact-resource.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ViewEncapsulation, inject } from '@angular/core';\nimport { DataSnapshot, Metadata, UserInfo } from '@memberjunction/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseResourceComponent } from '@memberjunction/ng-shared';\nimport { ResourceData, MJEnvironmentEntityExtended } from '@memberjunction/core-entities';\nimport { AnalyzeArtifactService, InteractiveFormApplyService } from '@memberjunction/ng-artifacts';\nimport type { ComponentSpec } from '@memberjunction/interactive-component-types';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\n\n/**\n * Artifact Resource - displays versioned content artifacts\n * Wraps the artifact-viewer-panel component for tab-based display\n * Supports reports, dashboards, forms, and other artifact types\n */\n@RegisterClass(BaseResourceComponent, 'ArtifactResource')\n@Component({\n standalone: false,\n selector: 'mj-artifact-resource',\n template: `\n <div class=\"artifact-container\">\n @if (currentUser && artifactId) {\n <mj-artifact-viewer-panel\n [artifactId]=\"artifactId\"\n [currentUser]=\"currentUser\"\n [environmentId]=\"environmentId\"\n [showSaveToCollection]=\"true\"\n [canShare]=\"true\"\n [canEdit]=\"true\"\n (analyzeRequested)=\"onAnalyzeRequested($event)\"\n (applyFormRequested)=\"onApplyFormRequested($event)\">\n </mj-artifact-viewer-panel>\n }\n </div>\n `,\n styles: [`\n .artifact-container {\n display: flex;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n `],\n encapsulation: ViewEncapsulation.None\n})\nexport class ArtifactResource extends BaseResourceComponent {\n private readonly analyzeService = inject(AnalyzeArtifactService);\n private readonly applyService = inject(InteractiveFormApplyService);\n\n /**\n * Apply-to-my-form handler — see InteractiveFormApplyService.\n * Confirms with the user, then routes to Create or Modify depending on\n * whether an Active override already exists for this entity+user.\n */\n async onApplyFormRequested(event: { spec: unknown; entityName: string }): Promise<void> {\n await this.applyService.ConfirmAndApply(\n event.spec as ComponentSpec,\n event.entityName,\n this.ProviderToUse,\n );\n }\n\n public currentUser: UserInfo | null = null;\n public artifactId: string = '';\n public environmentId: string = '';\n\n ngOnInit() {\n super.ngOnInit();\n const md = this.ProviderToUse;\n this.currentUser = md.CurrentUser;\n\n // Get artifact ID from resource data\n if (this.Data && this.Data.ResourceRecordID) {\n this.artifactId = this.Data.ResourceRecordID;\n }\n\n // Environment ID — use the configured environment if present, otherwise\n // fall back to the default environment. Must be a valid UUID since\n // downstream saves (e.g. AnalyzeArtifactService creating artifacts)\n // require a non-empty EnvironmentID.\n this.environmentId = (this.Data?.Configuration?.['environmentId'] as string | undefined)\n || MJEnvironmentEntityExtended.DefaultEnvironmentID;\n\n setTimeout(() => this.NotifyLoadComplete(), 100);\n }\n\n /**\n * Handler for the Analyze button on the artifact viewer panel.\n * Creates a Data Snapshot artifact from the live viewer state, opens a\n * new conversation with it attached as input, and routes the user there.\n */\n async onAnalyzeRequested(event: { artifactId: string; snapshot: DataSnapshot }): Promise<void> {\n if (!this.currentUser) return;\n\n try {\n const result = await this.analyzeService.StartAnalysisConversation({\n snapshot: event.snapshot,\n currentUser: this.currentUser,\n environmentId: this.environmentId,\n });\n\n // Route to the new conversation in the Conversations nav item\n await this.navigationService.OpenNavItemByName('Conversations', {\n conversationId: result.conversationId,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to start analysis conversation';\n MJNotificationService.Instance.CreateSimpleNotification(message, 'error', 5000);\n }\n }\n\n async GetResourceDisplayName(data: ResourceData): Promise<string> {\n // Try to load artifact name from database\n if (data.ResourceRecordID) {\n try {\n const md = this.ProviderToUse;\n const artifact = await md.GetEntityObject<any>('MJ: Conversation Artifacts');\n await artifact.Load(data.ResourceRecordID);\n return artifact.Name || `Artifact - ${data.ResourceRecordID}`;\n } catch (error) {\n console.error('Error loading artifact name:', error);\n }\n }\n return 'Artifact';\n }\n\n async GetResourceIconClass(data: ResourceData): Promise<string> {\n return 'fa-solid fa-file-code';\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-resource.component.d.ts","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/record-resource.component.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAAY,YAAY,EAAc,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;;AAC7F,qBAOa,oBAAqB,SAAQ,qBAAqB;IAC3D,IAAW,UAAU,IAAI,YAAY,CAEpC;WAEa,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,YAAY;
|
|
1
|
+
{"version":3,"file":"record-resource.component.d.ts","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/record-resource.component.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAAY,YAAY,EAAc,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;;AAC7F,qBAOa,oBAAqB,SAAQ,qBAAqB;IAC3D,IAAW,UAAU,IAAI,YAAY,CAEpC;WAEa,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,YAAY;IAwBrF,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA2B3D,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;yCAxDtD,oBAAoB;2CAApB,oBAAoB;CAqEhC"}
|
|
@@ -19,9 +19,19 @@ let EntityRecordResource = class EntityRecordResource extends BaseResourceCompon
|
|
|
19
19
|
static GetPrimaryKey(data, provider) {
|
|
20
20
|
// global-provider-ok: static helper has no component instance scope; falls back to default provider
|
|
21
21
|
const md = (provider ?? Metadata.Provider);
|
|
22
|
-
const
|
|
22
|
+
const requested = (data.Configuration.Entity ?? '').trim();
|
|
23
|
+
// Prefer EntityByName (project rule: never use Entities.find for
|
|
24
|
+
// single-entity lookups — case/whitespace tolerant + O(1) map). Fall
|
|
25
|
+
// back to DisplayName match for callers (AI agents, hand-typed URLs,
|
|
26
|
+
// external CTAs) that pass the human-readable display name instead
|
|
27
|
+
// of the canonical Name (e.g. "Users" for "MJ: Users").
|
|
28
|
+
let e = md.EntityByName(requested);
|
|
23
29
|
if (!e) {
|
|
24
|
-
|
|
30
|
+
const lowered = requested.toLowerCase();
|
|
31
|
+
e = md.Entities.find((x) => (x.DisplayName ?? '').trim().toLowerCase() === lowered);
|
|
32
|
+
}
|
|
33
|
+
if (!e) {
|
|
34
|
+
throw new Error(`Entity ${data.Configuration.Entity} not found in metadata (tried Name and DisplayName)`);
|
|
25
35
|
}
|
|
26
36
|
let compositeKey = new CompositeKey();
|
|
27
37
|
compositeKey.LoadFromURLSegment(e, data.ResourceRecordID);
|
|
@@ -32,7 +42,13 @@ let EntityRecordResource = class EntityRecordResource extends BaseResourceCompon
|
|
|
32
42
|
return '';
|
|
33
43
|
}
|
|
34
44
|
const md = this.ProviderToUse;
|
|
35
|
-
|
|
45
|
+
let e = md.EntityByName(data.Configuration.Entity);
|
|
46
|
+
if (!e) {
|
|
47
|
+
// Same DisplayName fallback as GetPrimaryKey — keeps the
|
|
48
|
+
// breadcrumb/title in sync when a CTA used the display name.
|
|
49
|
+
const lowered = (data.Configuration.Entity ?? '').trim().toLowerCase();
|
|
50
|
+
e = md.Entities.find((x) => (x.DisplayName ?? '').trim().toLowerCase() === lowered);
|
|
51
|
+
}
|
|
36
52
|
if (!e) {
|
|
37
53
|
return '';
|
|
38
54
|
}
|
|
@@ -59,9 +75,9 @@ let EntityRecordResource = class EntityRecordResource extends BaseResourceCompon
|
|
|
59
75
|
}
|
|
60
76
|
}
|
|
61
77
|
static ɵfac = /*@__PURE__*/ (() => { let ɵEntityRecordResource_BaseFactory; return function EntityRecordResource_Factory(__ngFactoryType__) { return (ɵEntityRecordResource_BaseFactory || (ɵEntityRecordResource_BaseFactory = i0.ɵɵgetInheritedFactory(EntityRecordResource)))(__ngFactoryType__ || EntityRecordResource); }; })();
|
|
62
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EntityRecordResource, selectors: [["mj-record-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 3, consts: [[3, "loadComplete", "recordSaved", "PrimaryKey", "entityName", "newRecordValues"]], template: function EntityRecordResource_Template(rf, ctx) { if (rf & 1) {
|
|
78
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EntityRecordResource, selectors: [["mj-record-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 3, consts: [[3, "loadComplete", "recordSaved", "recordDismissed", "PrimaryKey", "entityName", "newRecordValues"]], template: function EntityRecordResource_Template(rf, ctx) { if (rf & 1) {
|
|
63
79
|
i0.ɵɵelementStart(0, "mj-single-record", 0);
|
|
64
|
-
i0.ɵɵlistener("loadComplete", function EntityRecordResource_Template_mj_single_record_loadComplete_0_listener() { return ctx.NotifyLoadComplete(); })("recordSaved", function EntityRecordResource_Template_mj_single_record_recordSaved_0_listener($event) { return ctx.ResourceRecordSaved($event); });
|
|
80
|
+
i0.ɵɵlistener("loadComplete", function EntityRecordResource_Template_mj_single_record_loadComplete_0_listener() { return ctx.NotifyLoadComplete(); })("recordSaved", function EntityRecordResource_Template_mj_single_record_recordSaved_0_listener($event) { return ctx.ResourceRecordSaved($event); })("recordDismissed", function EntityRecordResource_Template_mj_single_record_recordDismissed_0_listener() { return ctx.NotifyCloseRequested(); });
|
|
65
81
|
i0.ɵɵelementEnd();
|
|
66
82
|
} if (rf & 2) {
|
|
67
83
|
i0.ɵɵproperty("PrimaryKey", ctx.PrimaryKey)("entityName", ctx.Data.Configuration.Entity)("newRecordValues", ctx.Data.Configuration.NewRecordValues);
|
|
@@ -73,7 +89,7 @@ EntityRecordResource = EntityRecordResource_1 = __decorate([
|
|
|
73
89
|
export { EntityRecordResource };
|
|
74
90
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(EntityRecordResource, [{
|
|
75
91
|
type: Component,
|
|
76
|
-
args: [{ standalone: false, selector: 'mj-record-resource', template: `<mj-single-record [PrimaryKey]="this.PrimaryKey" [entityName]="Data.Configuration.Entity" [newRecordValues]="Data.Configuration.NewRecordValues" (loadComplete)="NotifyLoadComplete()" (recordSaved)="ResourceRecordSaved($event)" ></mj-single-record>`, styles: [":host { display: block; height: 100%; width: 100%; }"] }]
|
|
92
|
+
args: [{ standalone: false, selector: 'mj-record-resource', template: `<mj-single-record [PrimaryKey]="this.PrimaryKey" [entityName]="Data.Configuration.Entity" [newRecordValues]="Data.Configuration.NewRecordValues" (loadComplete)="NotifyLoadComplete()" (recordSaved)="ResourceRecordSaved($event)" (recordDismissed)="NotifyCloseRequested()"></mj-single-record>`, styles: [":host { display: block; height: 100%; width: 100%; }"] }]
|
|
77
93
|
}], null, null); })();
|
|
78
94
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityRecordResource, { className: "EntityRecordResource", filePath: "src/lib/resource-wrappers/record-resource.component.ts", lineNumber: 13 }); })();
|
|
79
95
|
//# sourceMappingURL=record-resource.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/record-resource.component.ts"],"names":[],"mappings":";;;;;;;AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAiC,MAAM,sBAAsB,CAAC;;;AAQtF,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,qBAAqB;;IAC3D,IAAW,UAAU;QACjB,OAAO,sBAAoB,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC7E,CAAC;IAEM,MAAM,CAAC,aAAa,CAAC,IAAkB,EAAE,QAA4B;QACxE,oGAAoG;QACpG,MAAM,EAAE,GAAG,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAsB,CAAC;QAChE,MAAM,
|
|
1
|
+
{"version":3,"file":"record-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/record-resource.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component } from '@angular/core';\nimport { BaseResourceComponent } from '@memberjunction/ng-shared';\nimport { ResourceData } from '@memberjunction/core-entities';\nimport { RegisterClass } from '@memberjunction/global';\nimport { Metadata, CompositeKey, EntityInfo, IMetadataProvider } from '@memberjunction/core';\n@RegisterClass(BaseResourceComponent, 'RecordResource')\n@Component({\n standalone: false,\n selector: 'mj-record-resource',\n styles: [`:host { display: block; height: 100%; width: 100%; }`],\n template: `<mj-single-record [PrimaryKey]=\"this.PrimaryKey\" [entityName]=\"Data.Configuration.Entity\" [newRecordValues]=\"Data.Configuration.NewRecordValues\" (loadComplete)=\"NotifyLoadComplete()\" (recordSaved)=\"ResourceRecordSaved($event)\" (recordDismissed)=\"NotifyCloseRequested()\"></mj-single-record>`\n})\nexport class EntityRecordResource extends BaseResourceComponent {\n public get PrimaryKey(): CompositeKey {\n return EntityRecordResource.GetPrimaryKey(this.Data, this.ProviderToUse);\n }\n\n public static GetPrimaryKey(data: ResourceData, provider?: IMetadataProvider): CompositeKey {\n // global-provider-ok: static helper has no component instance scope; falls back to default provider\n const md = (provider ?? Metadata.Provider) as IMetadataProvider;\n const requested = (data.Configuration.Entity ?? '').trim();\n // Prefer EntityByName (project rule: never use Entities.find for\n // single-entity lookups — case/whitespace tolerant + O(1) map). Fall\n // back to DisplayName match for callers (AI agents, hand-typed URLs,\n // external CTAs) that pass the human-readable display name instead\n // of the canonical Name (e.g. \"Users\" for \"MJ: Users\").\n let e: EntityInfo | undefined = md.EntityByName(requested);\n if (!e) {\n const lowered = requested.toLowerCase();\n e = md.Entities.find((x: EntityInfo) =>\n (x.DisplayName ?? '').trim().toLowerCase() === lowered);\n }\n if (!e){\n throw new Error(`Entity ${data.Configuration.Entity} not found in metadata (tried Name and DisplayName)`);\n }\n\n let compositeKey: CompositeKey = new CompositeKey();\n compositeKey.LoadFromURLSegment(e, data.ResourceRecordID);\n return compositeKey;\n }\n\n async GetResourceDisplayName(data: ResourceData): Promise<string> {\n if (!data.Configuration.Entity) {\n return '';\n }\n\n const md = this.ProviderToUse;\n let e = md.EntityByName(data.Configuration.Entity);\n if (!e) {\n // Same DisplayName fallback as GetPrimaryKey — keeps the\n // breadcrumb/title in sync when a CTA used the display name.\n const lowered = (data.Configuration.Entity ?? '').trim().toLowerCase();\n e = md.Entities.find((x: EntityInfo) =>\n (x.DisplayName ?? '').trim().toLowerCase() === lowered);\n }\n if (!e) {\n return '';\n }\n\n const pk: CompositeKey = EntityRecordResource.GetPrimaryKey(data, this.ProviderToUse);\n if (pk.HasValue) {\n const name = await md.GetEntityRecordName(data.Configuration.Entity, pk);\n return name ? name : e.DisplayNameOrName;\n } else {\n return `New ${e.DisplayNameOrName} Record`;\n }\n }\n\n async GetResourceIconClass(data: ResourceData): Promise<string> {\n if (!data.Configuration.Entity){\n return ''\n }\n else {\n const md = this.ProviderToUse;\n const e = md.Entities.find(e => e.Name.trim().toLowerCase() === data.Configuration.Entity.trim().toLowerCase());\n if (e)\n return e?.Icon;\n else\n return '';\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Type } from '@angular/core';
|
|
2
|
+
import { IMetadataProvider, UserInfo, EntityInfo } from '@memberjunction/core';
|
|
3
|
+
import { BaseFormComponent } from '@memberjunction/ng-base-forms';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Slim row shape for an `EntityFormOverride` lookup. Resolution doesn't need
|
|
7
|
+
* the full BaseEntity object — `simple` ResultType is faster and avoids a
|
|
8
|
+
* compile-time dependency on the generated entity class. The row is only ever
|
|
9
|
+
* read by the resolver; mutation goes through the generated entity in other
|
|
10
|
+
* code paths (Studio, AI authoring).
|
|
11
|
+
*/
|
|
12
|
+
export interface EntityFormOverrideRow {
|
|
13
|
+
ID: string;
|
|
14
|
+
EntityID: string;
|
|
15
|
+
ComponentID: string;
|
|
16
|
+
Scope: 'User' | 'Role' | 'Global';
|
|
17
|
+
UserID: string | null;
|
|
18
|
+
RoleID: string | null;
|
|
19
|
+
Priority: number;
|
|
20
|
+
Status: 'Active' | 'Inactive' | 'Pending';
|
|
21
|
+
Name?: string;
|
|
22
|
+
Description?: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** Resolved form kind for a given (entity, user, roles) tuple. */
|
|
25
|
+
export type FormResolution = {
|
|
26
|
+
kind: 'interactive';
|
|
27
|
+
override: EntityFormOverrideRow;
|
|
28
|
+
variants: EntityFormOverrideRow[];
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'class';
|
|
31
|
+
subClass: Type<BaseFormComponent>;
|
|
32
|
+
variants: EntityFormOverrideRow[];
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'none';
|
|
35
|
+
variants: EntityFormOverrideRow[];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Picks the form to render for an entity record and exposes the full list of
|
|
39
|
+
* applicable variants so the toolbar's variant switcher can offer alternates.
|
|
40
|
+
*
|
|
41
|
+
* Tier order for the **default** pick:
|
|
42
|
+
* 1. `EntityFormOverride` row matching the caller's User/Role/Global scope,
|
|
43
|
+
* Status='Active', ordered by scope tier (User > Role > Global), then
|
|
44
|
+
* Priority DESC, then `__mj_CreatedAt DESC`. First row wins.
|
|
45
|
+
* 2. Existing `ClassFactory.GetRegistration(BaseFormComponent, entityName)` —
|
|
46
|
+
* the @RegisterClass + CodeGen-generated path used today.
|
|
47
|
+
* 3. None — caller surfaces the "no form registered" error.
|
|
48
|
+
*
|
|
49
|
+
* **Session selection.** If the user previously chose a non-default variant
|
|
50
|
+
* via the variant switcher and that choice is still applicable + Active, that
|
|
51
|
+
* choice wins over the default. Choice is keyed by entity name in
|
|
52
|
+
* `localStorage`. Honoring this is part of the variant-switcher contract;
|
|
53
|
+
* without it, switching variants would only last for the lifetime of the
|
|
54
|
+
* record-form view.
|
|
55
|
+
*
|
|
56
|
+
* **Performance.** Backed by `InteractiveFormsEngine` (in MJCoreEntities)
|
|
57
|
+
* — overrides are cached in memory with BaseEntity event-driven
|
|
58
|
+
* invalidation, so resolution is an O(N) JS filter against a small
|
|
59
|
+
* set instead of a per-LoadForm RunView round-trip. Cold latency
|
|
60
|
+
* dropped from ~50ms to <1ms after the first load. The engine is
|
|
61
|
+
* lazy-Config'd here on first resolution; users who never open a
|
|
62
|
+
* record pay nothing.
|
|
63
|
+
*/
|
|
64
|
+
export declare class FormResolverService {
|
|
65
|
+
ResolveFormForEntity(entity: EntityInfo, user: UserInfo, provider: IMetadataProvider): Promise<FormResolution>;
|
|
66
|
+
/**
|
|
67
|
+
* Public list-API for the variant switcher UI. Returns all variants
|
|
68
|
+
* applicable to (entity, user). Includes Active, Pending, and Inactive
|
|
69
|
+
* rows so the picker can offer "switch back to v1.0.0" alongside the
|
|
70
|
+
* current active variant.
|
|
71
|
+
*/
|
|
72
|
+
ListVariantsForEntity(entity: EntityInfo, user: UserInfo, provider: IMetadataProvider): Promise<EntityFormOverrideRow[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Sentinel stored in localStorage when the user **explicitly picks the
|
|
75
|
+
* "Default form"** option from the variant picker. Distinct from a
|
|
76
|
+
* missing localStorage key (= "no preference, use auto-pick rules")
|
|
77
|
+
* and from an override UUID. Without this sentinel, picking "Default"
|
|
78
|
+
* cleared the preference and `pickActive` re-applied the auto-pick —
|
|
79
|
+
* which always selects the first Active override, making the
|
|
80
|
+
* CodeGen/Angular fallback unreachable from the UI.
|
|
81
|
+
*
|
|
82
|
+
* Format: a leading `__` makes it visually distinct from a UUID and
|
|
83
|
+
* impossible to collide with one (UUIDs don't contain underscores).
|
|
84
|
+
*/
|
|
85
|
+
static readonly EXPLICIT_DEFAULT_SENTINEL = "__codegen-default__";
|
|
86
|
+
/**
|
|
87
|
+
* Build the per-entity setting key. Lowercased so case variants of
|
|
88
|
+
* the entity name collapse onto a single record in `MJ: User Settings`.
|
|
89
|
+
*/
|
|
90
|
+
private settingKey;
|
|
91
|
+
/**
|
|
92
|
+
* Persist a specific override choice. Pass the override UUID. Writes
|
|
93
|
+
* via `UserInfoEngine.SetSettingDebounced` so rapid successive picks
|
|
94
|
+
* don't hammer the DB. Use {@link SetExplicitDefault} to record "user
|
|
95
|
+
* wants the CodeGen Angular fallback" and {@link ClearSelectedVariant}
|
|
96
|
+
* to wipe the preference entirely (revert to auto-pick).
|
|
97
|
+
*/
|
|
98
|
+
SetSelectedVariant(entityName: string, overrideID: string): void;
|
|
99
|
+
/**
|
|
100
|
+
* Record that the user explicitly picked the "Default form" row in
|
|
101
|
+
* the picker. `pickActive` reads this sentinel and returns null even
|
|
102
|
+
* when Active overrides exist for the entity, so the form-loading
|
|
103
|
+
* path falls back to CodeGen's `@RegisterClass` lookup.
|
|
104
|
+
*/
|
|
105
|
+
SetExplicitDefault(entityName: string): void;
|
|
106
|
+
/**
|
|
107
|
+
* Wipe the user's stored preference for this entity. Next load
|
|
108
|
+
* applies the auto-pick rules (first Active override in tier order).
|
|
109
|
+
* Called internally when a saved override ID is no longer valid.
|
|
110
|
+
* Fire-and-forget — the resolver doesn't need to await the delete.
|
|
111
|
+
*/
|
|
112
|
+
ClearSelectedVariant(entityName: string): void;
|
|
113
|
+
/**
|
|
114
|
+
* Read the user's previously-saved variant choice for the entity.
|
|
115
|
+
* Synchronous because `UserInfoEngine` keeps the user-settings table
|
|
116
|
+
* in memory after bootstrap. Returns the stored UUID, the explicit-
|
|
117
|
+
* default sentinel, or null when no preference exists.
|
|
118
|
+
*/
|
|
119
|
+
GetSelectedVariant(entityName: string): string | null;
|
|
120
|
+
private listApplicableVariants;
|
|
121
|
+
/**
|
|
122
|
+
* Pick the override that should actually render given the variant list
|
|
123
|
+
* and the user's stored selection (if any).
|
|
124
|
+
*
|
|
125
|
+
* Selection rules:
|
|
126
|
+
* - If the user explicitly picked the "Default form" row in the
|
|
127
|
+
* variant picker (sentinel in localStorage) → return null so the
|
|
128
|
+
* form-loading path falls back to CodeGen's `@RegisterClass` lookup.
|
|
129
|
+
* This is what makes the Angular fallback reachable from the UI.
|
|
130
|
+
* - Else if the user has a saved variant ID AND that variant is in
|
|
131
|
+
* the applicable list AND it's Active → use it.
|
|
132
|
+
* - Else → first Active row in tier+priority order (auto-pick).
|
|
133
|
+
* - Else → null (fall back to CodeGen/@RegisterClass path).
|
|
134
|
+
*/
|
|
135
|
+
private pickActive;
|
|
136
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormResolverService, never>;
|
|
137
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<FormResolverService>;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=form-resolver.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-resolver.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/form-resolver.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,IAAI,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,UAAU,EAAY,MAAM,sBAAsB,CAAC;AAGzF,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;;AAGlE;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,kEAAkE;AAClE,MAAM,MAAM,cAAc,GACpB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,qBAAqB,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,GAC3F;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,GACvF;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,CAAC;AAiB1D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,mBAAmB;IAEtB,oBAAoB,CACtB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,iBAAiB,GAC5B,OAAO,CAAC,cAAc,CAAC;IAc1B;;;;;OAKG;IACG,qBAAqB,CACvB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,iBAAiB,GAC5B,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAInC;;;;;;;;;;;OAWG;IACH,gBAAuB,yBAAyB,yBAAyB;IAEzE;;;OAGG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;;;OAMG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAIhE;;;;;OAKG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAO5C;;;;;OAKG;IACH,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAK9C;;;;;OAKG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;YAMvC,sBAAsB;IAwEpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,UAAU;yCA7LT,mBAAmB;6CAAnB,mBAAmB;CA6M/B"}
|