@memberjunction/ng-conversations 5.30.0 → 5.31.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/components/collection/artifact-collection-picker-modal.component.d.ts +2 -1
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.js +9 -6
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.js.map +1 -1
- package/dist/lib/components/collection/artifact-create-modal.component.d.ts +2 -1
- package/dist/lib/components/collection/artifact-create-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/artifact-create-modal.component.js +9 -6
- package/dist/lib/components/collection/artifact-create-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.d.ts +2 -1
- package/dist/lib/components/collection/collection-form-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.js +8 -5
- package/dist/lib/components/collection/collection-form-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-tree.component.d.ts +2 -1
- package/dist/lib/components/collection/collection-tree.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-tree.component.js +9 -6
- package/dist/lib/components/collection/collection-tree.component.js.map +1 -1
- package/dist/lib/components/collection/collection-view.component.d.ts +2 -1
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +12 -10
- package/dist/lib/components/collection/collection-view.component.js.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.d.ts +2 -1
- package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.js +18 -12
- package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +2 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +22 -11
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/library/library-full-view.component.d.ts +2 -1
- package/dist/lib/components/library/library-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/library/library-full-view.component.js +8 -5
- package/dist/lib/components/library/library-full-view.component.js.map +1 -1
- package/dist/lib/components/message/conversation-message-rating.component.d.ts +2 -1
- package/dist/lib/components/message/conversation-message-rating.component.d.ts.map +1 -1
- package/dist/lib/components/message/conversation-message-rating.component.js +9 -7
- package/dist/lib/components/message/conversation-message-rating.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +2 -1
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +12 -5
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +6 -1
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts +2 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.js +6 -6
- package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
- package/dist/lib/components/project/project-form-modal.component.d.ts +2 -1
- package/dist/lib/components/project/project-form-modal.component.d.ts.map +1 -1
- package/dist/lib/components/project/project-form-modal.component.js +6 -5
- package/dist/lib/components/project/project-form-modal.component.js.map +1 -1
- package/dist/lib/components/project/project-selector.component.d.ts +2 -1
- package/dist/lib/components/project/project-selector.component.d.ts.map +1 -1
- package/dist/lib/components/project/project-selector.component.js +8 -6
- package/dist/lib/components/project/project-selector.component.js.map +1 -1
- package/dist/lib/components/share/share-modal.component.d.ts +2 -1
- package/dist/lib/components/share/share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/share/share-modal.component.js +11 -9
- package/dist/lib/components/share/share-modal.component.js.map +1 -1
- package/dist/lib/components/shared/user-picker.component.d.ts +2 -1
- package/dist/lib/components/shared/user-picker.component.d.ts.map +1 -1
- package/dist/lib/components/shared/user-picker.component.js +6 -4
- package/dist/lib/components/shared/user-picker.component.js.map +1 -1
- package/dist/lib/components/task/tasks-full-view.component.d.ts +2 -1
- package/dist/lib/components/task/tasks-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/task/tasks-full-view.component.js +9 -8
- package/dist/lib/components/task/tasks-full-view.component.js.map +1 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.js +5 -0
- package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
- package/dist/lib/components/thread/thread-panel.component.d.ts +2 -1
- package/dist/lib/components/thread/thread-panel.component.d.ts.map +1 -1
- package/dist/lib/components/thread/thread-panel.component.js +7 -4
- package/dist/lib/components/thread/thread-panel.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +8 -4
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/models/lazy-artifact-info.js +1 -1
- package/dist/lib/models/lazy-artifact-info.js.map +1 -1
- package/dist/lib/services/active-tasks.service.d.ts +7 -1
- package/dist/lib/services/active-tasks.service.d.ts.map +1 -1
- package/dist/lib/services/active-tasks.service.js +12 -2
- package/dist/lib/services/active-tasks.service.js.map +1 -1
- package/dist/lib/services/agent-state.service.d.ts +7 -1
- package/dist/lib/services/agent-state.service.d.ts.map +1 -1
- package/dist/lib/services/agent-state.service.js +12 -2
- package/dist/lib/services/agent-state.service.js.map +1 -1
- package/dist/lib/services/artifact-permission.service.d.ts +8 -1
- package/dist/lib/services/artifact-permission.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-permission.service.js +21 -7
- package/dist/lib/services/artifact-permission.service.js.map +1 -1
- package/dist/lib/services/artifact-state.service.d.ts +8 -1
- package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-state.service.js +25 -11
- package/dist/lib/services/artifact-state.service.js.map +1 -1
- package/dist/lib/services/artifact-use-tracking.service.d.ts +7 -1
- package/dist/lib/services/artifact-use-tracking.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-use-tracking.service.js +12 -2
- package/dist/lib/services/artifact-use-tracking.service.js.map +1 -1
- package/dist/lib/services/collection-permission.service.d.ts +7 -1
- package/dist/lib/services/collection-permission.service.d.ts.map +1 -1
- package/dist/lib/services/collection-permission.service.js +19 -9
- package/dist/lib/services/collection-permission.service.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +8 -0
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +15 -3
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-attachment.service.d.ts +7 -1
- package/dist/lib/services/conversation-attachment.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-attachment.service.js +12 -2
- package/dist/lib/services/conversation-attachment.service.js.map +1 -1
- package/dist/lib/services/data-cache.service.d.ts +7 -1
- package/dist/lib/services/data-cache.service.d.ts.map +1 -1
- package/dist/lib/services/data-cache.service.js +16 -7
- package/dist/lib/services/data-cache.service.js.map +1 -1
- package/dist/lib/services/export.service.d.ts +7 -1
- package/dist/lib/services/export.service.d.ts.map +1 -1
- package/dist/lib/services/export.service.js +12 -2
- package/dist/lib/services/export.service.js.map +1 -1
- package/dist/lib/services/search.service.d.ts +7 -1
- package/dist/lib/services/search.service.d.ts.map +1 -1
- package/dist/lib/services/search.service.js +16 -6
- package/dist/lib/services/search.service.js.map +1 -1
- package/package.json +22 -22
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Injectable } from '@angular/core';
|
|
2
|
-
import { Metadata } from '@memberjunction/core';
|
|
2
|
+
import { Metadata, RunView } from '@memberjunction/core';
|
|
3
3
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
5
5
|
/**
|
|
@@ -16,7 +16,17 @@ export class DataCacheService {
|
|
|
16
16
|
// Separate arrays for each entity type
|
|
17
17
|
conversations = [];
|
|
18
18
|
conversationDetails = [];
|
|
19
|
+
_provider = null;
|
|
19
20
|
constructor() { }
|
|
21
|
+
/**
|
|
22
|
+
* Set the metadata provider this service should use. When unset, falls back to Metadata.Provider.
|
|
23
|
+
*/
|
|
24
|
+
set Provider(value) {
|
|
25
|
+
this._provider = value;
|
|
26
|
+
}
|
|
27
|
+
get Provider() {
|
|
28
|
+
return this._provider ?? Metadata.Provider;
|
|
29
|
+
}
|
|
20
30
|
// =============================================================================
|
|
21
31
|
// MJConversationEntity Methods
|
|
22
32
|
// =============================================================================
|
|
@@ -33,7 +43,7 @@ export class DataCacheService {
|
|
|
33
43
|
return cached;
|
|
34
44
|
}
|
|
35
45
|
// Not in cache - load from DB
|
|
36
|
-
const md =
|
|
46
|
+
const md = this.Provider;
|
|
37
47
|
const conversation = await md.GetEntityObject('MJ: Conversations', currentUser);
|
|
38
48
|
const loaded = await conversation.Load(id);
|
|
39
49
|
if (loaded) {
|
|
@@ -50,7 +60,7 @@ export class DataCacheService {
|
|
|
50
60
|
* @returns New MJConversationEntity instance (already cached)
|
|
51
61
|
*/
|
|
52
62
|
async createConversation(currentUser) {
|
|
53
|
-
const md =
|
|
63
|
+
const md = this.Provider;
|
|
54
64
|
const conversation = await md.GetEntityObject('MJ: Conversations', currentUser);
|
|
55
65
|
// Automatically add to cache - user code doesn't need to do anything
|
|
56
66
|
// Note: Conversation has no ID yet (not saved), so we can't deduplicate
|
|
@@ -88,7 +98,7 @@ export class DataCacheService {
|
|
|
88
98
|
return cached;
|
|
89
99
|
}
|
|
90
100
|
// Not in cache - load from DB
|
|
91
|
-
const md =
|
|
101
|
+
const md = this.Provider;
|
|
92
102
|
const detail = await md.GetEntityObject('MJ: Conversation Details', currentUser);
|
|
93
103
|
const loaded = await detail.Load(id);
|
|
94
104
|
if (loaded) {
|
|
@@ -105,7 +115,7 @@ export class DataCacheService {
|
|
|
105
115
|
* @returns New MJConversationDetailEntity instance (already cached)
|
|
106
116
|
*/
|
|
107
117
|
async createConversationDetail(currentUser) {
|
|
108
|
-
const md =
|
|
118
|
+
const md = this.Provider;
|
|
109
119
|
const detail = await md.GetEntityObject('MJ: Conversation Details', currentUser);
|
|
110
120
|
// Automatically add to cache - user code doesn't need to do anything
|
|
111
121
|
// Note: Detail has no ID yet (not saved), so we can't deduplicate
|
|
@@ -124,8 +134,7 @@ export class DataCacheService {
|
|
|
124
134
|
async loadConversationDetails(conversationId, currentUser) {
|
|
125
135
|
const timestamp = new Date().toISOString();
|
|
126
136
|
console.log(`[${timestamp}] 💾 DataCacheService.loadConversationDetails - Loading messages for conversation ${conversationId}`);
|
|
127
|
-
const
|
|
128
|
-
const rv = new (await import('@memberjunction/core')).RunView();
|
|
137
|
+
const rv = RunView.FromMetadataProvider(this.Provider);
|
|
129
138
|
console.log(`[${timestamp}] 💾 DataCacheService - Executing RunView for Conversation Details`);
|
|
130
139
|
const result = await rv.RunView({
|
|
131
140
|
EntityName: 'MJ: Conversation Details',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-cache.service.js","sourceRoot":"","sources":["../../../src/lib/services/data-cache.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { MJConversationEntity, MJConversationDetailEntity } from '@memberjunction/core-entities';\nimport { Metadata, UserInfo, BaseEntity } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Global singleton cache for conversation-related entities.\n * Maintains single instances of entities to prevent race conditions and ensure\n * all components see the same object state.\n *\n * Pattern: Instead of creating new entity instances with GetEntityObject() everywhere,\n * request entities from this cache. The cache returns the same instance each time,\n * so when one component updates entity.Status = 'Complete', all other components\n * see that change immediately without needing to reload from the database.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class DataCacheService {\n // Separate arrays for each entity type\n private conversations: MJConversationEntity[] = [];\n private conversationDetails: MJConversationDetailEntity[] = [];\n\n constructor() {}\n\n // =============================================================================\n // MJConversationEntity Methods\n // =============================================================================\n\n /**\n * Get a MJConversationEntity by ID from cache, or load it if not cached\n * @param id The conversation ID\n * @param currentUser User context for loading\n * @returns The cached or loaded MJConversationEntity, or null if not found\n */\n async getConversation(id: string, currentUser: UserInfo): Promise<MJConversationEntity | null> {\n // Check cache first\n const cached = this.conversations.find(c => UUIDsEqual(c.ID, id));\n if (cached) {\n return cached;\n }\n\n // Not in cache - load from DB\n const md = new Metadata();\n const conversation = await md.GetEntityObject<MJConversationEntity>('MJ: Conversations', currentUser);\n const loaded = await conversation.Load(id);\n\n if (loaded) {\n // Add to cache\n this.conversations.push(conversation);\n return conversation;\n }\n\n return null;\n }\n\n /**\n * Create a new MJConversationEntity and automatically cache it\n * The cache is the ONLY place GetEntityObject() should be called\n * @param currentUser User context\n * @returns New MJConversationEntity instance (already cached)\n */\n async createConversation(currentUser: UserInfo): Promise<MJConversationEntity> {\n const md = new Metadata();\n const conversation = await md.GetEntityObject<MJConversationEntity>('MJ: Conversations', currentUser);\n\n // Automatically add to cache - user code doesn't need to do anything\n // Note: Conversation has no ID yet (not saved), so we can't deduplicate\n // After Save(), the ID will be populated and future getConversation() calls will find it\n this.conversations.push(conversation);\n\n return conversation;\n }\n\n /**\n * Remove a MJConversationEntity from the cache\n * @param id The conversation ID to remove\n */\n removeConversation(id: string): void {\n this.conversations = this.conversations.filter(c => !UUIDsEqual(c.ID, id));\n }\n\n /**\n * Get all cached MJConversationEntity objects (no DB call)\n * @returns Array of cached conversations\n */\n getCachedConversations(): MJConversationEntity[] {\n return this.conversations;\n }\n\n // =============================================================================\n // MJConversationDetailEntity Methods\n // =============================================================================\n\n /**\n * Get a MJConversationDetailEntity by ID from cache, or load it if not cached\n * @param id The conversation detail ID\n * @param currentUser User context for loading\n * @returns The cached or loaded MJConversationDetailEntity, or null if not found\n */\n async getConversationDetail(id: string, currentUser: UserInfo): Promise<MJConversationDetailEntity | null> {\n // Check cache first\n const cached = this.conversationDetails.find(d => UUIDsEqual(d.ID, id));\n if (cached) {\n return cached;\n }\n\n // Not in cache - load from DB\n const md = new Metadata();\n const detail = await md.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', currentUser);\n const loaded = await detail.Load(id);\n\n if (loaded) {\n // Add to cache\n this.conversationDetails.push(detail);\n return detail;\n }\n\n return null;\n }\n\n /**\n * Create a new MJConversationDetailEntity and automatically cache it\n * The cache is the ONLY place GetEntityObject() should be called\n * @param currentUser User context\n * @returns New MJConversationDetailEntity instance (already cached)\n */\n async createConversationDetail(currentUser: UserInfo): Promise<MJConversationDetailEntity> {\n const md = new Metadata();\n const detail = await md.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', currentUser);\n\n // Automatically add to cache - user code doesn't need to do anything\n // Note: Detail has no ID yet (not saved), so we can't deduplicate\n // After Save(), the ID will be populated and future getConversationDetail() calls will find it\n\n detail.UserRating = 1 // TO-DO - temp fix for a BUG - remove this after 2.105.0 makes this field optional again, right now it requires a value even though column is nullable\n\n this.conversationDetails.push(detail);\n\n return detail;\n }\n\n /**\n * Load all ConversationDetail entities for a conversation and cache them\n * Used when loading a conversation's message history\n * @param conversationId The conversation ID\n * @param currentUser User context\n * @returns Array of MJConversationDetailEntity objects\n */\n async loadConversationDetails(conversationId: string, currentUser: UserInfo): Promise<MJConversationDetailEntity[]> {\n const timestamp = new Date().toISOString();\n console.log(`[${timestamp}] 💾 DataCacheService.loadConversationDetails - Loading messages for conversation ${conversationId}`);\n\n const md = new Metadata();\n const rv = new (await import('@memberjunction/core')).RunView();\n\n console.log(`[${timestamp}] 💾 DataCacheService - Executing RunView for Conversation Details`);\n const result = await rv.RunView<MJConversationDetailEntity>(\n {\n EntityName: 'MJ: Conversation Details',\n ExtraFilter: `ConversationID='${conversationId}'`,\n OrderBy: '__mj_CreatedAt ASC',\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (result.Success) {\n const details = result.Results || [];\n console.log(`[${timestamp}] 💾 DataCacheService - Loaded ${details.length} message(s) for conversation ${conversationId}`);\n\n // Add all to cache (avoid duplicates by checking ID)\n for (const detail of details) {\n const existingIndex = this.conversationDetails.findIndex(d => UUIDsEqual(d.ID, detail.ID));\n if (existingIndex >= 0) {\n // Replace existing (ensures we keep the same object reference)\n this.conversationDetails[existingIndex] = detail;\n } else {\n // Add new\n this.conversationDetails.push(detail);\n }\n }\n\n return details;\n }\n\n console.log(`[${timestamp}] 💾 DataCacheService - Failed to load messages for conversation ${conversationId}`);\n return [];\n }\n\n /**\n * Get all cached ConversationDetail entities for a conversation (no DB call)\n * @param conversationId The conversation ID\n * @returns Array of cached conversation details\n */\n getCachedConversationDetails(conversationId: string): MJConversationDetailEntity[] {\n return this.conversationDetails.filter(d => UUIDsEqual(d.ConversationID, conversationId));\n }\n\n /**\n * Remove a MJConversationDetailEntity from the cache\n * @param id The conversation detail ID to remove\n */\n removeConversationDetail(id: string): void {\n this.conversationDetails = this.conversationDetails.filter(d => !UUIDsEqual(d.ID, id));\n }\n\n // =============================================================================\n // Cache Management\n // =============================================================================\n\n /**\n * Refresh entity data by clearing cached entities of a specific type\n * This forces a reload from the database on next access\n * @param entityName The entity name to refresh (e.g., 'Conversations', 'MJ: Conversation Details')\n */\n async refreshEntity(entityName: string): Promise<void> {\n switch (entityName) {\n case 'Conversations':\n this.conversations = [];\n break;\n case 'MJ: Conversation Details':\n this.conversationDetails = [];\n break;\n default:\n console.warn('Unknown entity name for refresh:', entityName);\n }\n }\n\n /**\n * Refresh a specific cache by name\n * This method can be extended to handle different cache types\n * @param cacheName The cache name ('Core', 'AI', 'Actions')\n */\n async refreshCache(cacheName: 'Core' | 'AI' | 'Actions'): Promise<void> {\n switch (cacheName) {\n case 'Core':\n // Clear core entity caches\n this.clear();\n break;\n case 'AI':\n // AI-specific cache refresh would go here\n // For now, just log it\n console.log('AI cache refresh requested (not yet implemented)');\n break;\n case 'Actions':\n // Actions-specific cache refresh would go here\n console.log('Actions cache refresh requested (not yet implemented)');\n break;\n default:\n console.warn('Unknown cache name for refresh:', cacheName);\n }\n }\n\n /**\n * Clear all cached entities\n * Call when switching environments, users, or when needed\n */\n clear(): void {\n this.conversations = [];\n this.conversationDetails = [];\n }\n\n /**\n * Clear cached entities for a specific conversation\n * @param conversationId The conversation ID\n */\n clearConversation(conversationId: string): void {\n this.conversations = this.conversations.filter(c => !UUIDsEqual(c.ID, conversationId));\n this.conversationDetails = this.conversationDetails.filter(d => !UUIDsEqual(d.ConversationID, conversationId));\n }\n\n /**\n * Get cache statistics for debugging\n * @returns Object with cache sizes\n */\n getStats(): { conversations: number; conversationDetails: number } {\n return {\n conversations: this.conversations.length,\n conversationDetails: this.conversationDetails.length\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"data-cache.service.js","sourceRoot":"","sources":["../../../src/lib/services/data-cache.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { MJConversationEntity, MJConversationDetailEntity } from '@memberjunction/core-entities';\nimport { Metadata, UserInfo, BaseEntity, RunView, IMetadataProvider } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Global singleton cache for conversation-related entities.\n * Maintains single instances of entities to prevent race conditions and ensure\n * all components see the same object state.\n *\n * Pattern: Instead of creating new entity instances with GetEntityObject() everywhere,\n * request entities from this cache. The cache returns the same instance each time,\n * so when one component updates entity.Status = 'Complete', all other components\n * see that change immediately without needing to reload from the database.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class DataCacheService {\n // Separate arrays for each entity type\n private conversations: MJConversationEntity[] = [];\n private conversationDetails: MJConversationDetailEntity[] = [];\n\n private _provider: IMetadataProvider | null = null;\n\n constructor() {}\n\n /**\n * Set the metadata provider this service should use. When unset, falls back to Metadata.Provider.\n */\n public set Provider(value: IMetadataProvider | null) {\n this._provider = value;\n }\n\n public get Provider(): IMetadataProvider {\n return this._provider ?? Metadata.Provider;\n }\n\n // =============================================================================\n // MJConversationEntity Methods\n // =============================================================================\n\n /**\n * Get a MJConversationEntity by ID from cache, or load it if not cached\n * @param id The conversation ID\n * @param currentUser User context for loading\n * @returns The cached or loaded MJConversationEntity, or null if not found\n */\n async getConversation(id: string, currentUser: UserInfo): Promise<MJConversationEntity | null> {\n // Check cache first\n const cached = this.conversations.find(c => UUIDsEqual(c.ID, id));\n if (cached) {\n return cached;\n }\n\n // Not in cache - load from DB\n const md = this.Provider;\n const conversation = await md.GetEntityObject<MJConversationEntity>('MJ: Conversations', currentUser);\n const loaded = await conversation.Load(id);\n\n if (loaded) {\n // Add to cache\n this.conversations.push(conversation);\n return conversation;\n }\n\n return null;\n }\n\n /**\n * Create a new MJConversationEntity and automatically cache it\n * The cache is the ONLY place GetEntityObject() should be called\n * @param currentUser User context\n * @returns New MJConversationEntity instance (already cached)\n */\n async createConversation(currentUser: UserInfo): Promise<MJConversationEntity> {\n const md = this.Provider;\n const conversation = await md.GetEntityObject<MJConversationEntity>('MJ: Conversations', currentUser);\n\n // Automatically add to cache - user code doesn't need to do anything\n // Note: Conversation has no ID yet (not saved), so we can't deduplicate\n // After Save(), the ID will be populated and future getConversation() calls will find it\n this.conversations.push(conversation);\n\n return conversation;\n }\n\n /**\n * Remove a MJConversationEntity from the cache\n * @param id The conversation ID to remove\n */\n removeConversation(id: string): void {\n this.conversations = this.conversations.filter(c => !UUIDsEqual(c.ID, id));\n }\n\n /**\n * Get all cached MJConversationEntity objects (no DB call)\n * @returns Array of cached conversations\n */\n getCachedConversations(): MJConversationEntity[] {\n return this.conversations;\n }\n\n // =============================================================================\n // MJConversationDetailEntity Methods\n // =============================================================================\n\n /**\n * Get a MJConversationDetailEntity by ID from cache, or load it if not cached\n * @param id The conversation detail ID\n * @param currentUser User context for loading\n * @returns The cached or loaded MJConversationDetailEntity, or null if not found\n */\n async getConversationDetail(id: string, currentUser: UserInfo): Promise<MJConversationDetailEntity | null> {\n // Check cache first\n const cached = this.conversationDetails.find(d => UUIDsEqual(d.ID, id));\n if (cached) {\n return cached;\n }\n\n // Not in cache - load from DB\n const md = this.Provider;\n const detail = await md.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', currentUser);\n const loaded = await detail.Load(id);\n\n if (loaded) {\n // Add to cache\n this.conversationDetails.push(detail);\n return detail;\n }\n\n return null;\n }\n\n /**\n * Create a new MJConversationDetailEntity and automatically cache it\n * The cache is the ONLY place GetEntityObject() should be called\n * @param currentUser User context\n * @returns New MJConversationDetailEntity instance (already cached)\n */\n async createConversationDetail(currentUser: UserInfo): Promise<MJConversationDetailEntity> {\n const md = this.Provider;\n const detail = await md.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', currentUser);\n\n // Automatically add to cache - user code doesn't need to do anything\n // Note: Detail has no ID yet (not saved), so we can't deduplicate\n // After Save(), the ID will be populated and future getConversationDetail() calls will find it\n\n detail.UserRating = 1 // TO-DO - temp fix for a BUG - remove this after 2.105.0 makes this field optional again, right now it requires a value even though column is nullable\n\n this.conversationDetails.push(detail);\n\n return detail;\n }\n\n /**\n * Load all ConversationDetail entities for a conversation and cache them\n * Used when loading a conversation's message history\n * @param conversationId The conversation ID\n * @param currentUser User context\n * @returns Array of MJConversationDetailEntity objects\n */\n async loadConversationDetails(conversationId: string, currentUser: UserInfo): Promise<MJConversationDetailEntity[]> {\n const timestamp = new Date().toISOString();\n console.log(`[${timestamp}] 💾 DataCacheService.loadConversationDetails - Loading messages for conversation ${conversationId}`);\n\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n console.log(`[${timestamp}] 💾 DataCacheService - Executing RunView for Conversation Details`);\n const result = await rv.RunView<MJConversationDetailEntity>(\n {\n EntityName: 'MJ: Conversation Details',\n ExtraFilter: `ConversationID='${conversationId}'`,\n OrderBy: '__mj_CreatedAt ASC',\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (result.Success) {\n const details = result.Results || [];\n console.log(`[${timestamp}] 💾 DataCacheService - Loaded ${details.length} message(s) for conversation ${conversationId}`);\n\n // Add all to cache (avoid duplicates by checking ID)\n for (const detail of details) {\n const existingIndex = this.conversationDetails.findIndex(d => UUIDsEqual(d.ID, detail.ID));\n if (existingIndex >= 0) {\n // Replace existing (ensures we keep the same object reference)\n this.conversationDetails[existingIndex] = detail;\n } else {\n // Add new\n this.conversationDetails.push(detail);\n }\n }\n\n return details;\n }\n\n console.log(`[${timestamp}] 💾 DataCacheService - Failed to load messages for conversation ${conversationId}`);\n return [];\n }\n\n /**\n * Get all cached ConversationDetail entities for a conversation (no DB call)\n * @param conversationId The conversation ID\n * @returns Array of cached conversation details\n */\n getCachedConversationDetails(conversationId: string): MJConversationDetailEntity[] {\n return this.conversationDetails.filter(d => UUIDsEqual(d.ConversationID, conversationId));\n }\n\n /**\n * Remove a MJConversationDetailEntity from the cache\n * @param id The conversation detail ID to remove\n */\n removeConversationDetail(id: string): void {\n this.conversationDetails = this.conversationDetails.filter(d => !UUIDsEqual(d.ID, id));\n }\n\n // =============================================================================\n // Cache Management\n // =============================================================================\n\n /**\n * Refresh entity data by clearing cached entities of a specific type\n * This forces a reload from the database on next access\n * @param entityName The entity name to refresh (e.g., 'Conversations', 'MJ: Conversation Details')\n */\n async refreshEntity(entityName: string): Promise<void> {\n switch (entityName) {\n case 'Conversations':\n this.conversations = [];\n break;\n case 'MJ: Conversation Details':\n this.conversationDetails = [];\n break;\n default:\n console.warn('Unknown entity name for refresh:', entityName);\n }\n }\n\n /**\n * Refresh a specific cache by name\n * This method can be extended to handle different cache types\n * @param cacheName The cache name ('Core', 'AI', 'Actions')\n */\n async refreshCache(cacheName: 'Core' | 'AI' | 'Actions'): Promise<void> {\n switch (cacheName) {\n case 'Core':\n // Clear core entity caches\n this.clear();\n break;\n case 'AI':\n // AI-specific cache refresh would go here\n // For now, just log it\n console.log('AI cache refresh requested (not yet implemented)');\n break;\n case 'Actions':\n // Actions-specific cache refresh would go here\n console.log('Actions cache refresh requested (not yet implemented)');\n break;\n default:\n console.warn('Unknown cache name for refresh:', cacheName);\n }\n }\n\n /**\n * Clear all cached entities\n * Call when switching environments, users, or when needed\n */\n clear(): void {\n this.conversations = [];\n this.conversationDetails = [];\n }\n\n /**\n * Clear cached entities for a specific conversation\n * @param conversationId The conversation ID\n */\n clearConversation(conversationId: string): void {\n this.conversations = this.conversations.filter(c => !UUIDsEqual(c.ID, conversationId));\n this.conversationDetails = this.conversationDetails.filter(d => !UUIDsEqual(d.ConversationID, conversationId));\n }\n\n /**\n * Get cache statistics for debugging\n * @returns Object with cache sizes\n */\n getStats(): { conversations: number; conversationDetails: number } {\n return {\n conversations: this.conversations.length,\n conversationDetails: this.conversationDetails.length\n };\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UserInfo } from '@memberjunction/core';
|
|
1
|
+
import { UserInfo, IMetadataProvider } from '@memberjunction/core';
|
|
2
2
|
import * as i0 from "@angular/core";
|
|
3
3
|
export type ExportFormat = 'json' | 'markdown' | 'html' | 'text';
|
|
4
4
|
export interface ExportOptions {
|
|
@@ -8,6 +8,12 @@ export interface ExportOptions {
|
|
|
8
8
|
includeCSS?: boolean;
|
|
9
9
|
}
|
|
10
10
|
export declare class ExportService {
|
|
11
|
+
private _provider;
|
|
12
|
+
/**
|
|
13
|
+
* Set the metadata provider this service should use. When unset, falls back to Metadata.Provider.
|
|
14
|
+
*/
|
|
15
|
+
set Provider(value: IMetadataProvider | null);
|
|
16
|
+
get Provider(): IMetadataProvider;
|
|
11
17
|
exportConversation(conversationId: string, format: ExportFormat, currentUser: UserInfo, options?: ExportOptions): Promise<void>;
|
|
12
18
|
private loadConversationData;
|
|
13
19
|
private exportAsJSON;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/export.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,QAAQ,EAAE,MAAM,sBAAsB,CAAC;;
|
|
1
|
+
{"version":3,"file":"export.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/export.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,QAAQ,EAAY,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;;AAEtF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,qBAGa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAkC;IAEnD;;OAEG;IACH,IAAW,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,EAElD;IAED,IAAW,QAAQ,IAAI,iBAAiB,CAEvC;IAEK,kBAAkB,CACtB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,YAAY,EACpB,WAAW,EAAE,QAAQ,EACrB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC;YA2CF,oBAAoB;IA+BlC,OAAO,CAAC,YAAY;IA8CpB,OAAO,CAAC,gBAAgB;IAmCxB,OAAO,CAAC,YAAY;IA+DpB,OAAO,CAAC,YAAY;IAqCpB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,YAAY;yCAnTT,aAAa;6CAAb,aAAa;CAuTzB"}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { Injectable } from '@angular/core';
|
|
2
|
-
import { RunView } from '@memberjunction/core';
|
|
2
|
+
import { RunView, Metadata } from '@memberjunction/core';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
export class ExportService {
|
|
5
|
+
_provider = null;
|
|
6
|
+
/**
|
|
7
|
+
* Set the metadata provider this service should use. When unset, falls back to Metadata.Provider.
|
|
8
|
+
*/
|
|
9
|
+
set Provider(value) {
|
|
10
|
+
this._provider = value;
|
|
11
|
+
}
|
|
12
|
+
get Provider() {
|
|
13
|
+
return this._provider ?? Metadata.Provider;
|
|
14
|
+
}
|
|
5
15
|
async exportConversation(conversationId, format, currentUser, options = {}) {
|
|
6
16
|
const conversation = await this.loadConversationData(conversationId, currentUser);
|
|
7
17
|
// Apply default options
|
|
@@ -41,7 +51,7 @@ export class ExportService {
|
|
|
41
51
|
this.downloadFile(content, filename, mimeType);
|
|
42
52
|
}
|
|
43
53
|
async loadConversationData(conversationId, currentUser) {
|
|
44
|
-
const rv =
|
|
54
|
+
const rv = RunView.FromMetadataProvider(this.Provider);
|
|
45
55
|
// Load conversation and details in parallel
|
|
46
56
|
const [conversationResult, detailsResult] = await rv.RunViews([
|
|
47
57
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.service.js","sourceRoot":"","sources":["../../../src/lib/services/export.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,OAAO,EAAY,MAAM,sBAAsB,CAAC;;AAczD,MAAM,OAAO,aAAa;IACxB,KAAK,CAAC,kBAAkB,CACtB,cAAsB,EACtB,MAAoB,EACpB,WAAqB,EACrB,UAAyB,EAAE;QAE3B,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QAElF,wBAAwB;QACxB,MAAM,aAAa,GAA4B;YAC7C,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,IAAI;YAChD,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,IAAI;YAChD,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;YACxC,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI;SACvC,CAAC;QAEF,IAAI,OAAe,CAAC;QACpB,IAAI,QAAgB,CAAC;QACrB,IAAI,QAAgB,CAAC;QAErB,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,MAAM;gBACT,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;gBACzD,QAAQ,GAAG,gBAAgB,YAAY,CAAC,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC;gBACxF,QAAQ,GAAG,kBAAkB,CAAC;gBAC9B,MAAM;YACR,KAAK,UAAU;gBACb,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;gBAC7D,QAAQ,GAAG,gBAAgB,YAAY,CAAC,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC;gBACtF,QAAQ,GAAG,eAAe,CAAC;gBAC3B,MAAM;YACR,KAAK,MAAM;gBACT,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;gBACzD,QAAQ,GAAG,gBAAgB,YAAY,CAAC,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC;gBACxF,QAAQ,GAAG,WAAW,CAAC;gBACvB,MAAM;YACR,KAAK,MAAM;gBACT,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;gBACzD,QAAQ,GAAG,gBAAgB,YAAY,CAAC,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC;gBACvF,QAAQ,GAAG,YAAY,CAAC;gBACxB,MAAM;YACR;gBACE,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACjD,CAAC;IAEO,KAAK,CAAC,oBAAoB,CAChC,cAAsB,EACtB,WAAqB;QAErB,MAAM,EAAE,GAAG,IAAI,OAAO,EAAE,CAAC;QAEzB,4CAA4C;QAC5C,MAAM,CAAC,kBAAkB,EAAE,aAAa,CAAC,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC;YAC5D;gBACE,UAAU,EAAE,mBAAmB;gBAC/B,WAAW,EAAE,OAAO,cAAc,GAAG;gBACrC,UAAU,EAAE,eAAe;aAC5B;YACD;gBACE,UAAU,EAAE,0BAA0B;gBACtC,WAAW,EAAE,mBAAmB,cAAc,GAAG;gBACjD,OAAO,EAAE,cAAc;gBACvB,UAAU,EAAE,eAAe;aAC5B;SACF,EAAE,WAAW,CAAC,CAAC;QAEhB,IAAI,CAAC,kBAAkB,CAAC,OAAO,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;YACvE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO;YACL,YAAY,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAyB;YACnE,OAAO,EAAE,CAAC,aAAa,CAAC,OAAO,IAAI,EAAE,CAAiC;SACvE,CAAC;IACJ,CAAC;IAEO,YAAY,CAClB,IAGC,EACD,OAAgC;QAEhC,MAAM,UAAU,GAA4B,EAAE,CAAC;QAE/C,4BAA4B;QAC5B,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,UAAU,CAAC,YAAY,GAAG;gBACxB,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE;gBACxB,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI;gBAC5B,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,WAAW;gBAC1C,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc;gBAC3C,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc;aAC5C,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,YAAY,GAAG;gBACxB,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI;aAC7B,CAAC;QACJ,CAAC;QAED,4BAA4B;QAC5B,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;gBACvD,MAAM,OAAO,GAA4B;oBACvC,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,OAAO,EAAE,MAAM,CAAC,OAAO;iBACxB,CAAC;gBAEF,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;oBAC5B,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;oBACvB,OAAO,CAAC,QAAQ,GAAG,KAAK,GAAG,CAAC,CAAC;oBAC7B,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;gBAC5C,CAAC;gBAED,OAAO,OAAO,CAAC;YACjB,CAAC,CAAC,CAAC;QACL,CAAC;QAED,0BAA0B;QAC1B,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IAEO,gBAAgB,CACtB,IAGC,EACD,OAAgC;QAEhC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC,YAAY,CAAC,IAAI,MAAM,CAAC;QAE3C,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;YAClC,EAAE,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,MAAM,CAAC;QAC/C,CAAC;QAED,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,EAAE,IAAI,gBAAgB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,MAAM,CAAC;QAChF,CAAC;QAED,EAAE,IAAI,SAAS,CAAC;QAEhB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClC,EAAE,IAAI,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,IAAI,SAAS,CAAC,MAAM,CAAC;gBAChE,EAAE,IAAI,GAAG,MAAM,CAAC,OAAO,MAAM,CAAC;gBAE9B,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;oBAC5B,EAAE,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC;gBAC1D,CAAC;gBAED,EAAE,IAAI,SAAS,CAAC;YAClB,CAAC;QACH,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,YAAY,CAClB,IAGC,EACD,OAAgC;QAEhC,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;;;;;;;;;;;WAW7B,CAAC,CAAC,CAAC,EAAE,CAAC;QAEb,IAAI,IAAI,GAAG;;;;;WAKJ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,cAAc,CAAC,WAAW,MAAM;;;QAG7E,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,cAAc,CAAC,OAAO,CAAC;QAErE,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,IAAI,IAAI;;MAER,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;kBACnF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC;SAC1D,CAAC;QACN,CAAC;QAED,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,SAAS,CAAC;gBAC1D,IAAI,IAAI;wBACQ,SAAS;wBACT,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,IAAI,SAAS,CAAC;2BAC1C,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,QAAQ,CAAC;gBAEjE,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;oBAC5B,IAAI,IAAI;6BACW,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC;gBACpE,CAAC;gBAED,IAAI,IAAI;SACP,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,IAAI;;QAEJ,CAAC;QAEL,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,YAAY,CAClB,IAGC,EACD,OAAgC;QAEhC,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,cAAc,CAAC;QACtD,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC;QACvB,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;QAEzC,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;YAClC,IAAI,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,MAAM,CAAC;QACjD,CAAC;QAED,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,IAAI,IAAI,YAAY,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,MAAM,CAAC;QAC9E,CAAC;QAED,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;QAEhC,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,IAAI,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,IAAI,SAAS,CAAC,KAAK,CAAC;gBAC/D,IAAI,IAAI,GAAG,MAAM,CAAC,OAAO,IAAI,CAAC;gBAE9B,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;oBAC5B,IAAI,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC;gBAC1D,CAAC;gBAED,IAAI,IAAI,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;YACzC,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,YAAY,CAAC,OAAe,EAAE,QAAgB,EAAE,QAAgB;QACtE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;QAChB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAChC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAEO,UAAU,CAAC,IAAmB;QACpC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;QACzB,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE;YAC/B,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,MAAM;YACb,GAAG,EAAE,SAAS;YACd,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,SAAS;SAClB,CAAC,CAAC;IACL,CAAC;IAEO,cAAc,CAAC,IAAY;QACjC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,CAAC;IAEO,UAAU,CAAC,IAAY;QAC7B,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC1C,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC;QACvB,OAAO,GAAG,CAAC,SAAS,CAAC;IACvB,CAAC;IAEO,YAAY;QAClB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC;uGAzSU,aAAa;gEAAb,aAAa,WAAb,aAAa,mBAFZ,MAAM;;iFAEP,aAAa;cAHzB,UAAU;eAAC;gBACV,UAAU,EAAE,MAAM;aACnB","sourcesContent":["import { Injectable } from '@angular/core';\nimport { MJConversationEntity, MJConversationDetailEntity } from '@memberjunction/core-entities';\nimport { RunView, UserInfo } from '@memberjunction/core';\n\nexport type ExportFormat = 'json' | 'markdown' | 'html' | 'text';\n\nexport interface ExportOptions {\n includeMessages?: boolean;\n includeMetadata?: boolean;\n prettyPrint?: boolean;\n includeCSS?: boolean;\n}\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ExportService {\n async exportConversation(\n conversationId: string,\n format: ExportFormat,\n currentUser: UserInfo,\n options: ExportOptions = {}\n ): Promise<void> {\n const conversation = await this.loadConversationData(conversationId, currentUser);\n\n // Apply default options\n const exportOptions: Required<ExportOptions> = {\n includeMessages: options.includeMessages ?? true,\n includeMetadata: options.includeMetadata ?? true,\n prettyPrint: options.prettyPrint ?? true,\n includeCSS: options.includeCSS ?? true\n };\n\n let content: string;\n let filename: string;\n let mimeType: string;\n\n switch (format) {\n case 'json':\n content = this.exportAsJSON(conversation, exportOptions);\n filename = `conversation-${conversation.conversation.Name}-${this.getTimestamp()}.json`;\n mimeType = 'application/json';\n break;\n case 'markdown':\n content = this.exportAsMarkdown(conversation, exportOptions);\n filename = `conversation-${conversation.conversation.Name}-${this.getTimestamp()}.md`;\n mimeType = 'text/markdown';\n break;\n case 'html':\n content = this.exportAsHTML(conversation, exportOptions);\n filename = `conversation-${conversation.conversation.Name}-${this.getTimestamp()}.html`;\n mimeType = 'text/html';\n break;\n case 'text':\n content = this.exportAsText(conversation, exportOptions);\n filename = `conversation-${conversation.conversation.Name}-${this.getTimestamp()}.txt`;\n mimeType = 'text/plain';\n break;\n default:\n throw new Error(`Unsupported export format: ${format}`);\n }\n\n this.downloadFile(content, filename, mimeType);\n }\n\n private async loadConversationData(\n conversationId: string,\n currentUser: UserInfo\n ): Promise<{ conversation: MJConversationEntity; details: MJConversationDetailEntity[] }> {\n const rv = new RunView();\n\n // Load conversation and details in parallel\n const [conversationResult, detailsResult] = await rv.RunViews([\n {\n EntityName: 'MJ: Conversations',\n ExtraFilter: `ID='${conversationId}'`,\n ResultType: 'entity_object'\n },\n {\n EntityName: 'MJ: Conversation Details',\n ExtraFilter: `ConversationID='${conversationId}'`,\n OrderBy: 'Sequence ASC',\n ResultType: 'entity_object'\n }\n ], currentUser);\n\n if (!conversationResult.Success || !conversationResult.Results?.length) {\n throw new Error('Conversation not found');\n }\n\n return {\n conversation: conversationResult.Results[0] as MJConversationEntity,\n details: (detailsResult.Results || []) as MJConversationDetailEntity[]\n };\n }\n\n private exportAsJSON(\n data: {\n conversation: MJConversationEntity;\n details: MJConversationDetailEntity[];\n },\n options: Required<ExportOptions>\n ): string {\n const exportData: Record<string, unknown> = {};\n\n // Add metadata if requested\n if (options.includeMetadata) {\n exportData.conversation = {\n id: data.conversation.ID,\n name: data.conversation.Name,\n description: data.conversation.Description,\n createdAt: data.conversation.__mj_CreatedAt,\n updatedAt: data.conversation.__mj_UpdatedAt\n };\n } else {\n exportData.conversation = {\n name: data.conversation.Name\n };\n }\n\n // Add messages if requested\n if (options.includeMessages) {\n exportData.messages = data.details.map((detail, index) => {\n const message: Record<string, unknown> = {\n role: detail.Role,\n message: detail.Message\n };\n\n if (options.includeMetadata) {\n message.id = detail.ID;\n message.sequence = index + 1;\n message.timestamp = detail.__mj_CreatedAt;\n }\n\n return message;\n });\n }\n\n // Use pretty print option\n return JSON.stringify(exportData, null, options.prettyPrint ? 2 : 0);\n }\n\n private exportAsMarkdown(\n data: {\n conversation: MJConversationEntity;\n details: MJConversationDetailEntity[];\n },\n options: Required<ExportOptions>\n ): string {\n let md = `# ${data.conversation.Name}\\n\\n`;\n\n if (data.conversation.Description) {\n md += `${data.conversation.Description}\\n\\n`;\n }\n\n if (options.includeMetadata) {\n md += `**Created:** ${this.formatDate(data.conversation.__mj_CreatedAt)}\\n\\n`;\n }\n\n md += `---\\n\\n`;\n\n if (options.includeMessages) {\n for (const detail of data.details) {\n md += `## ${this.capitalizeRole(detail.Role || 'Unknown')}\\n\\n`;\n md += `${detail.Message}\\n\\n`;\n\n if (options.includeMetadata) {\n md += `*${this.formatDate(detail.__mj_CreatedAt)}*\\n\\n`;\n }\n\n md += `---\\n\\n`;\n }\n }\n\n return md;\n }\n\n private exportAsHTML(\n data: {\n conversation: MJConversationEntity;\n details: MJConversationDetailEntity[];\n },\n options: Required<ExportOptions>\n ): string {\n const styles = options.includeCSS ? `\n <style>\n body { font-family: system-ui, -apple-system, sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; line-height: 1.6; }\n h1 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; }\n .meta { color: #666; font-size: 14px; margin-bottom: 30px; }\n .message { margin-bottom: 30px; padding: 20px; border-radius: 8px; background: #f5f5f5; }\n .message.user { background: #e3f2fd; }\n .message.assistant { background: #f5f5f5; }\n .role { font-weight: 600; color: #007bff; margin-bottom: 10px; }\n .content { white-space: pre-wrap; }\n .timestamp { color: #999; font-size: 12px; margin-top: 10px; }\n </style>` : '';\n\n let html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${this.escapeHtml(data.conversation.Name || 'Conversation')}</title>${styles}\n</head>\n<body>\n <h1>${this.escapeHtml(data.conversation.Name || 'Conversation')}</h1>`;\n\n if (options.includeMetadata) {\n html += `\n <div class=\"meta\">\n ${data.conversation.Description ? `<p>${this.escapeHtml(data.conversation.Description)}</p>` : ''}\n <p>Created: ${this.formatDate(data.conversation.__mj_CreatedAt)}</p>\n </div>`;\n }\n\n if (options.includeMessages) {\n for (const detail of data.details) {\n const roleClass = detail.Role?.toLowerCase() || 'unknown';\n html += `\n <div class=\"message ${roleClass}\">\n <div class=\"role\">${this.capitalizeRole(detail.Role || 'Unknown')}</div>\n <div class=\"content\">${this.escapeHtml(detail.Message || '')}</div>`;\n\n if (options.includeMetadata) {\n html += `\n <div class=\"timestamp\">${this.formatDate(detail.__mj_CreatedAt)}</div>`;\n }\n\n html += `\n </div>`;\n }\n }\n\n html += `\n</body>\n</html>`;\n\n return html;\n }\n\n private exportAsText(\n data: {\n conversation: MJConversationEntity;\n details: MJConversationDetailEntity[];\n },\n options: Required<ExportOptions>\n ): string {\n const name = data.conversation.Name || 'Conversation';\n let text = `${name}\\n`;\n text += '='.repeat(name.length) + '\\n\\n';\n\n if (data.conversation.Description) {\n text += `${data.conversation.Description}\\n\\n`;\n }\n\n if (options.includeMetadata) {\n text += `Created: ${this.formatDate(data.conversation.__mj_CreatedAt)}\\n\\n`;\n }\n\n text += '-'.repeat(80) + '\\n\\n';\n\n if (options.includeMessages) {\n for (const detail of data.details) {\n text += `[${this.capitalizeRole(detail.Role || 'Unknown')}]\\n`;\n text += `${detail.Message}\\n`;\n\n if (options.includeMetadata) {\n text += `(${this.formatDate(detail.__mj_CreatedAt)})\\n`;\n }\n\n text += '\\n' + '-'.repeat(80) + '\\n\\n';\n }\n }\n\n return text;\n }\n\n private downloadFile(content: string, filename: string, mimeType: string): void {\n const blob = new Blob([content], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n }\n\n private formatDate(date: Date | string): string {\n const d = new Date(date);\n return d.toLocaleString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit'\n });\n }\n\n private capitalizeRole(role: string): string {\n return role.charAt(0).toUpperCase() + role.slice(1).toLowerCase();\n }\n\n private escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n\n private getTimestamp(): string {\n const now = new Date();\n return now.toISOString().replace(/[:.]/g, '-').slice(0, -5);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"export.service.js","sourceRoot":"","sources":["../../../src/lib/services/export.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { MJConversationEntity, MJConversationDetailEntity } from '@memberjunction/core-entities';\nimport { RunView, UserInfo, Metadata, IMetadataProvider } from '@memberjunction/core';\n\nexport type ExportFormat = 'json' | 'markdown' | 'html' | 'text';\n\nexport interface ExportOptions {\n includeMessages?: boolean;\n includeMetadata?: boolean;\n prettyPrint?: boolean;\n includeCSS?: boolean;\n}\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ExportService {\n private _provider: IMetadataProvider | null = null;\n\n /**\n * Set the metadata provider this service should use. When unset, falls back to Metadata.Provider.\n */\n public set Provider(value: IMetadataProvider | null) {\n this._provider = value;\n }\n\n public get Provider(): IMetadataProvider {\n return this._provider ?? Metadata.Provider;\n }\n\n async exportConversation(\n conversationId: string,\n format: ExportFormat,\n currentUser: UserInfo,\n options: ExportOptions = {}\n ): Promise<void> {\n const conversation = await this.loadConversationData(conversationId, currentUser);\n\n // Apply default options\n const exportOptions: Required<ExportOptions> = {\n includeMessages: options.includeMessages ?? true,\n includeMetadata: options.includeMetadata ?? true,\n prettyPrint: options.prettyPrint ?? true,\n includeCSS: options.includeCSS ?? true\n };\n\n let content: string;\n let filename: string;\n let mimeType: string;\n\n switch (format) {\n case 'json':\n content = this.exportAsJSON(conversation, exportOptions);\n filename = `conversation-${conversation.conversation.Name}-${this.getTimestamp()}.json`;\n mimeType = 'application/json';\n break;\n case 'markdown':\n content = this.exportAsMarkdown(conversation, exportOptions);\n filename = `conversation-${conversation.conversation.Name}-${this.getTimestamp()}.md`;\n mimeType = 'text/markdown';\n break;\n case 'html':\n content = this.exportAsHTML(conversation, exportOptions);\n filename = `conversation-${conversation.conversation.Name}-${this.getTimestamp()}.html`;\n mimeType = 'text/html';\n break;\n case 'text':\n content = this.exportAsText(conversation, exportOptions);\n filename = `conversation-${conversation.conversation.Name}-${this.getTimestamp()}.txt`;\n mimeType = 'text/plain';\n break;\n default:\n throw new Error(`Unsupported export format: ${format}`);\n }\n\n this.downloadFile(content, filename, mimeType);\n }\n\n private async loadConversationData(\n conversationId: string,\n currentUser: UserInfo\n ): Promise<{ conversation: MJConversationEntity; details: MJConversationDetailEntity[] }> {\n const rv = RunView.FromMetadataProvider(this.Provider);\n\n // Load conversation and details in parallel\n const [conversationResult, detailsResult] = await rv.RunViews([\n {\n EntityName: 'MJ: Conversations',\n ExtraFilter: `ID='${conversationId}'`,\n ResultType: 'entity_object'\n },\n {\n EntityName: 'MJ: Conversation Details',\n ExtraFilter: `ConversationID='${conversationId}'`,\n OrderBy: 'Sequence ASC',\n ResultType: 'entity_object'\n }\n ], currentUser);\n\n if (!conversationResult.Success || !conversationResult.Results?.length) {\n throw new Error('Conversation not found');\n }\n\n return {\n conversation: conversationResult.Results[0] as MJConversationEntity,\n details: (detailsResult.Results || []) as MJConversationDetailEntity[]\n };\n }\n\n private exportAsJSON(\n data: {\n conversation: MJConversationEntity;\n details: MJConversationDetailEntity[];\n },\n options: Required<ExportOptions>\n ): string {\n const exportData: Record<string, unknown> = {};\n\n // Add metadata if requested\n if (options.includeMetadata) {\n exportData.conversation = {\n id: data.conversation.ID,\n name: data.conversation.Name,\n description: data.conversation.Description,\n createdAt: data.conversation.__mj_CreatedAt,\n updatedAt: data.conversation.__mj_UpdatedAt\n };\n } else {\n exportData.conversation = {\n name: data.conversation.Name\n };\n }\n\n // Add messages if requested\n if (options.includeMessages) {\n exportData.messages = data.details.map((detail, index) => {\n const message: Record<string, unknown> = {\n role: detail.Role,\n message: detail.Message\n };\n\n if (options.includeMetadata) {\n message.id = detail.ID;\n message.sequence = index + 1;\n message.timestamp = detail.__mj_CreatedAt;\n }\n\n return message;\n });\n }\n\n // Use pretty print option\n return JSON.stringify(exportData, null, options.prettyPrint ? 2 : 0);\n }\n\n private exportAsMarkdown(\n data: {\n conversation: MJConversationEntity;\n details: MJConversationDetailEntity[];\n },\n options: Required<ExportOptions>\n ): string {\n let md = `# ${data.conversation.Name}\\n\\n`;\n\n if (data.conversation.Description) {\n md += `${data.conversation.Description}\\n\\n`;\n }\n\n if (options.includeMetadata) {\n md += `**Created:** ${this.formatDate(data.conversation.__mj_CreatedAt)}\\n\\n`;\n }\n\n md += `---\\n\\n`;\n\n if (options.includeMessages) {\n for (const detail of data.details) {\n md += `## ${this.capitalizeRole(detail.Role || 'Unknown')}\\n\\n`;\n md += `${detail.Message}\\n\\n`;\n\n if (options.includeMetadata) {\n md += `*${this.formatDate(detail.__mj_CreatedAt)}*\\n\\n`;\n }\n\n md += `---\\n\\n`;\n }\n }\n\n return md;\n }\n\n private exportAsHTML(\n data: {\n conversation: MJConversationEntity;\n details: MJConversationDetailEntity[];\n },\n options: Required<ExportOptions>\n ): string {\n const styles = options.includeCSS ? `\n <style>\n body { font-family: system-ui, -apple-system, sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; line-height: 1.6; }\n h1 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; }\n .meta { color: #666; font-size: 14px; margin-bottom: 30px; }\n .message { margin-bottom: 30px; padding: 20px; border-radius: 8px; background: #f5f5f5; }\n .message.user { background: #e3f2fd; }\n .message.assistant { background: #f5f5f5; }\n .role { font-weight: 600; color: #007bff; margin-bottom: 10px; }\n .content { white-space: pre-wrap; }\n .timestamp { color: #999; font-size: 12px; margin-top: 10px; }\n </style>` : '';\n\n let html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${this.escapeHtml(data.conversation.Name || 'Conversation')}</title>${styles}\n</head>\n<body>\n <h1>${this.escapeHtml(data.conversation.Name || 'Conversation')}</h1>`;\n\n if (options.includeMetadata) {\n html += `\n <div class=\"meta\">\n ${data.conversation.Description ? `<p>${this.escapeHtml(data.conversation.Description)}</p>` : ''}\n <p>Created: ${this.formatDate(data.conversation.__mj_CreatedAt)}</p>\n </div>`;\n }\n\n if (options.includeMessages) {\n for (const detail of data.details) {\n const roleClass = detail.Role?.toLowerCase() || 'unknown';\n html += `\n <div class=\"message ${roleClass}\">\n <div class=\"role\">${this.capitalizeRole(detail.Role || 'Unknown')}</div>\n <div class=\"content\">${this.escapeHtml(detail.Message || '')}</div>`;\n\n if (options.includeMetadata) {\n html += `\n <div class=\"timestamp\">${this.formatDate(detail.__mj_CreatedAt)}</div>`;\n }\n\n html += `\n </div>`;\n }\n }\n\n html += `\n</body>\n</html>`;\n\n return html;\n }\n\n private exportAsText(\n data: {\n conversation: MJConversationEntity;\n details: MJConversationDetailEntity[];\n },\n options: Required<ExportOptions>\n ): string {\n const name = data.conversation.Name || 'Conversation';\n let text = `${name}\\n`;\n text += '='.repeat(name.length) + '\\n\\n';\n\n if (data.conversation.Description) {\n text += `${data.conversation.Description}\\n\\n`;\n }\n\n if (options.includeMetadata) {\n text += `Created: ${this.formatDate(data.conversation.__mj_CreatedAt)}\\n\\n`;\n }\n\n text += '-'.repeat(80) + '\\n\\n';\n\n if (options.includeMessages) {\n for (const detail of data.details) {\n text += `[${this.capitalizeRole(detail.Role || 'Unknown')}]\\n`;\n text += `${detail.Message}\\n`;\n\n if (options.includeMetadata) {\n text += `(${this.formatDate(detail.__mj_CreatedAt)})\\n`;\n }\n\n text += '\\n' + '-'.repeat(80) + '\\n\\n';\n }\n }\n\n return text;\n }\n\n private downloadFile(content: string, filename: string, mimeType: string): void {\n const blob = new Blob([content], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n }\n\n private formatDate(date: Date | string): string {\n const d = new Date(date);\n return d.toLocaleString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit'\n });\n }\n\n private capitalizeRole(role: string): string {\n return role.charAt(0).toUpperCase() + role.slice(1).toLowerCase();\n }\n\n private escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n\n private getTimestamp(): string {\n const now = new Date();\n return now.toISOString().replace(/[:.]/g, '-').slice(0, -5);\n }\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
|
-
import { UserInfo } from '@memberjunction/core';
|
|
2
|
+
import { UserInfo, IMetadataProvider } from '@memberjunction/core';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
/**
|
|
5
5
|
* Types of searchable content
|
|
@@ -61,7 +61,13 @@ export declare class SearchService {
|
|
|
61
61
|
readonly dateRange$: Observable<DateRange>;
|
|
62
62
|
readonly isSearching$: Observable<boolean>;
|
|
63
63
|
readonly searchResults$: Observable<GroupedSearchResults>;
|
|
64
|
+
private _provider;
|
|
64
65
|
constructor();
|
|
66
|
+
/**
|
|
67
|
+
* Set the metadata provider this service should use. When unset, falls back to Metadata.Provider.
|
|
68
|
+
*/
|
|
69
|
+
set Provider(value: IMetadataProvider | null);
|
|
70
|
+
get Provider(): IMetadataProvider;
|
|
65
71
|
/**
|
|
66
72
|
* Initialize debounced search
|
|
67
73
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/search.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,UAAU,EAAe,MAAM,MAAM,CAAC;AAWhE,OAAO,EAAW,QAAQ,EAAY,MAAM,sBAAsB,CAAC;;
|
|
1
|
+
{"version":3,"file":"search.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/search.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,10 +29,20 @@ export class SearchService {
|
|
|
29
29
|
dateRange$ = this._dateRange$.asObservable();
|
|
30
30
|
isSearching$ = this._isSearching$.asObservable();
|
|
31
31
|
searchResults$ = this._searchResults$.asObservable();
|
|
32
|
+
_provider = null;
|
|
32
33
|
constructor() {
|
|
33
34
|
this.initializeSearch();
|
|
34
35
|
this.loadRecentSearches();
|
|
35
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Set the metadata provider this service should use. When unset, falls back to Metadata.Provider.
|
|
39
|
+
*/
|
|
40
|
+
set Provider(value) {
|
|
41
|
+
this._provider = value;
|
|
42
|
+
}
|
|
43
|
+
get Provider() {
|
|
44
|
+
return this._provider ?? Metadata.Provider;
|
|
45
|
+
}
|
|
36
46
|
/**
|
|
37
47
|
* Initialize debounced search
|
|
38
48
|
*/
|
|
@@ -101,7 +111,7 @@ export class SearchService {
|
|
|
101
111
|
* Search conversations by name and description
|
|
102
112
|
*/
|
|
103
113
|
async searchConversations(query, environmentId, currentUser, dateRange) {
|
|
104
|
-
const rv =
|
|
114
|
+
const rv = RunView.FromMetadataProvider(this.Provider);
|
|
105
115
|
const lowerQuery = query.toLowerCase();
|
|
106
116
|
let filter = `EnvironmentID='${environmentId}' AND (IsArchived IS NULL OR IsArchived=0)`;
|
|
107
117
|
filter += ` AND (LOWER(Name) LIKE '%${this.escapeSQL(lowerQuery)}%' OR LOWER(Description) LIKE '%${this.escapeSQL(lowerQuery)}%')`;
|
|
@@ -128,7 +138,7 @@ export class SearchService {
|
|
|
128
138
|
* Search message content
|
|
129
139
|
*/
|
|
130
140
|
async searchMessages(query, environmentId, currentUser, dateRange) {
|
|
131
|
-
const rv =
|
|
141
|
+
const rv = RunView.FromMetadataProvider(this.Provider);
|
|
132
142
|
const lowerQuery = query.toLowerCase();
|
|
133
143
|
// First get conversations in this environment
|
|
134
144
|
let filter = `ConversationID IN (SELECT ID FROM vwConversations WHERE EnvironmentID='${environmentId}' AND (IsArchived IS NULL OR IsArchived=0))`;
|
|
@@ -158,7 +168,7 @@ export class SearchService {
|
|
|
158
168
|
* Includes artifacts from both conversations and collections
|
|
159
169
|
*/
|
|
160
170
|
async searchArtifacts(query, environmentId, currentUser, dateRange) {
|
|
161
|
-
const rv =
|
|
171
|
+
const rv = RunView.FromMetadataProvider(this.Provider);
|
|
162
172
|
const lowerQuery = query.toLowerCase();
|
|
163
173
|
// Search artifacts directly by name and description
|
|
164
174
|
let filter = `EnvironmentID='${environmentId}'`;
|
|
@@ -208,7 +218,7 @@ export class SearchService {
|
|
|
208
218
|
* Includes both owned collections and shared collections user has access to
|
|
209
219
|
*/
|
|
210
220
|
async searchCollections(query, environmentId, currentUser, dateRange) {
|
|
211
|
-
const rv =
|
|
221
|
+
const rv = RunView.FromMetadataProvider(this.Provider);
|
|
212
222
|
const lowerQuery = query.toLowerCase();
|
|
213
223
|
const ownerFilter = `OwnerID='${currentUser.ID}'`;
|
|
214
224
|
const permissionSubquery = `ID IN (
|
|
@@ -243,10 +253,10 @@ export class SearchService {
|
|
|
243
253
|
* Only includes tasks in conversations user has access to
|
|
244
254
|
*/
|
|
245
255
|
async searchTasks(query, environmentId, currentUser, dateRange) {
|
|
246
|
-
const rv =
|
|
256
|
+
const rv = RunView.FromMetadataProvider(this.Provider);
|
|
247
257
|
const lowerQuery = query.toLowerCase();
|
|
248
258
|
// Build filter using same logic as TasksFullViewComponent
|
|
249
|
-
const md =
|
|
259
|
+
const md = this.Provider;
|
|
250
260
|
const cd = md.EntityByName('MJ: Conversation Details');
|
|
251
261
|
const c = md.EntityByName('MJ: Conversations');
|
|
252
262
|
if (!cd || !c) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.service.js","sourceRoot":"","sources":["../../../src/lib/services/search.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { BehaviorSubject, Observable, Subject, of } from 'rxjs';\nimport { debounceTime, distinctUntilChanged, switchMap, map } from 'rxjs/operators';\nimport {\n MJConversationEntity,\n MJConversationDetailEntity,\n MJConversationArtifactEntity,\n MJCollectionEntity,\n MJCollectionArtifactEntity,\n MJTaskEntity,\n MJArtifactEntity\n} from '@memberjunction/core-entities';\nimport { RunView, UserInfo, Metadata } from '@memberjunction/core';\n\n/**\n * Types of searchable content\n */\nexport type SearchResultType = 'conversation' | 'message' | 'artifact' | 'collection' | 'task';\n\n/**\n * Filter options for search\n */\nexport type SearchFilter = 'all' | 'conversations' | 'messages' | 'artifacts' | 'collections' | 'tasks';\n\n/**\n * Unified search result\n */\nexport interface SearchResult {\n id: string;\n type: SearchResultType;\n title: string;\n preview: string;\n matchedText?: string;\n conversationId?: string;\n conversationName?: string;\n artifactType?: string;\n collectionId?: string;\n collectionName?: string;\n createdAt: Date;\n relevanceScore: number;\n}\n\n/**\n * Search results grouped by type\n */\nexport interface GroupedSearchResults {\n conversations: SearchResult[];\n messages: SearchResult[];\n artifacts: SearchResult[];\n collections: SearchResult[];\n tasks: SearchResult[];\n total: number;\n}\n\n/**\n * Date range filter\n */\nexport interface DateRange {\n start: Date | null;\n end: Date | null;\n}\n\n/**\n * Service for searching across conversations, messages, and artifacts\n * Provides debounced search with result ranking and filtering\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class SearchService {\n private _searchQuery$ = new Subject<string>();\n private _searchFilter$ = new BehaviorSubject<SearchFilter>('all');\n private _dateRange$ = new BehaviorSubject<DateRange>({ start: null, end: null });\n private _isSearching$ = new BehaviorSubject<boolean>(false);\n private _searchResults$ = new BehaviorSubject<GroupedSearchResults>({\n conversations: [],\n messages: [],\n artifacts: [],\n collections: [],\n tasks: [],\n total: 0\n });\n\n // Recent searches stored in memory (could be persisted to localStorage)\n private recentSearches: string[] = [];\n private readonly MAX_RECENT_SEARCHES = 10;\n\n // Public observables\n public readonly searchQuery$ = this._searchQuery$.asObservable();\n public readonly searchFilter$ = this._searchFilter$.asObservable();\n public readonly dateRange$ = this._dateRange$.asObservable();\n public readonly isSearching$ = this._isSearching$.asObservable();\n public readonly searchResults$ = this._searchResults$.asObservable();\n\n constructor() {\n this.initializeSearch();\n this.loadRecentSearches();\n }\n\n /**\n * Initialize debounced search\n */\n private initializeSearch(): void {\n this._searchQuery$.pipe(\n debounceTime(300),\n distinctUntilChanged()\n ).subscribe(query => {\n if (query && query.trim().length > 0) {\n this.addToRecentSearches(query);\n }\n });\n }\n\n /**\n * Search across all content types\n */\n public async search(\n query: string,\n environmentId: string,\n currentUser: UserInfo\n ): Promise<GroupedSearchResults> {\n if (!query || query.trim().length === 0) {\n const emptyResults: GroupedSearchResults = {\n conversations: [],\n messages: [],\n artifacts: [],\n collections: [],\n tasks: [],\n total: 0\n };\n this._searchResults$.next(emptyResults);\n return emptyResults;\n }\n\n this._isSearching$.next(true);\n this._searchQuery$.next(query);\n\n try {\n const filter = this._searchFilter$.value;\n const dateRange = this._dateRange$.value;\n\n // Search based on active filter\n const [conversations, messages, artifacts, collections, tasks] = await Promise.all([\n filter === 'all' || filter === 'conversations'\n ? this.searchConversations(query, environmentId, currentUser, dateRange)\n : Promise.resolve([]),\n filter === 'all' || filter === 'messages'\n ? this.searchMessages(query, environmentId, currentUser, dateRange)\n : Promise.resolve([]),\n filter === 'all' || filter === 'artifacts'\n ? this.searchArtifacts(query, environmentId, currentUser, dateRange)\n : Promise.resolve([]),\n filter === 'all' || filter === 'collections'\n ? this.searchCollections(query, environmentId, currentUser, dateRange)\n : Promise.resolve([]),\n filter === 'all' || filter === 'tasks'\n ? this.searchTasks(query, environmentId, currentUser, dateRange)\n : Promise.resolve([])\n ]);\n\n const results: GroupedSearchResults = {\n conversations,\n messages,\n artifacts,\n collections,\n tasks,\n total: conversations.length + messages.length + artifacts.length + collections.length + tasks.length\n };\n\n this._searchResults$.next(results);\n return results;\n } finally {\n this._isSearching$.next(false);\n }\n }\n\n /**\n * Search conversations by name and description\n */\n private async searchConversations(\n query: string,\n environmentId: string,\n currentUser: UserInfo,\n dateRange: DateRange\n ): Promise<SearchResult[]> {\n const rv = new RunView();\n const lowerQuery = query.toLowerCase();\n\n let filter = `EnvironmentID='${environmentId}' AND (IsArchived IS NULL OR IsArchived=0)`;\n filter += ` AND (LOWER(Name) LIKE '%${this.escapeSQL(lowerQuery)}%' OR LOWER(Description) LIKE '%${this.escapeSQL(lowerQuery)}%')`;\n\n if (dateRange.start) {\n filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;\n }\n if (dateRange.end) {\n filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;\n }\n\n const result = await rv.RunView<MJConversationEntity>(\n {\n EntityName: 'MJ: Conversations',\n ExtraFilter: filter,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 100,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (!result.Success || !result.Results) {\n console.error('Failed to search conversations:', result.ErrorMessage);\n return [];\n }\n\n return result.Results.map(conv => this.mapConversationToSearchResult(conv, query));\n }\n\n /**\n * Search message content\n */\n private async searchMessages(\n query: string,\n environmentId: string,\n currentUser: UserInfo,\n dateRange: DateRange\n ): Promise<SearchResult[]> {\n const rv = new RunView();\n const lowerQuery = query.toLowerCase();\n\n // First get conversations in this environment\n let filter = `ConversationID IN (SELECT ID FROM vwConversations WHERE EnvironmentID='${environmentId}' AND (IsArchived IS NULL OR IsArchived=0))`;\n filter += ` AND LOWER(Message) LIKE '%${this.escapeSQL(lowerQuery)}%'`;\n filter += ` AND (HiddenToUser IS NULL OR HiddenToUser=0)`;\n\n if (dateRange.start) {\n filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;\n }\n if (dateRange.end) {\n filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;\n }\n\n const result = await rv.RunView<MJConversationDetailEntity>(\n {\n EntityName: 'MJ: Conversation Details',\n ExtraFilter: filter,\n OrderBy: '__mj_CreatedAt DESC',\n MaxRows: 100,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (!result.Success || !result.Results) {\n console.error('Failed to search messages:', result.ErrorMessage);\n return [];\n }\n\n return result.Results.map(msg => this.mapMessageToSearchResult(msg, query));\n }\n\n /**\n * Search artifacts by name and description\n * Includes artifacts from both conversations and collections\n */\n private async searchArtifacts(\n query: string,\n environmentId: string,\n currentUser: UserInfo,\n dateRange: DateRange\n ): Promise<SearchResult[]> {\n const rv = new RunView();\n const lowerQuery = query.toLowerCase();\n\n // Search artifacts directly by name and description\n let filter = `EnvironmentID='${environmentId}'`;\n filter += ` AND (LOWER(Name) LIKE '%${this.escapeSQL(lowerQuery)}%' OR LOWER(Description) LIKE '%${this.escapeSQL(lowerQuery)}%')`;\n\n if (dateRange.start) {\n filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;\n }\n if (dateRange.end) {\n filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;\n }\n\n const result = await rv.RunView<MJArtifactEntity>(\n {\n EntityName: 'MJ: Artifacts',\n ExtraFilter: `${filter} AND (Visibility IS NULL OR Visibility='Always')`,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 100,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (!result.Success || !result.Results) {\n console.error('Failed to search artifacts:', result.ErrorMessage);\n return [];\n }\n\n const searchResults: SearchResult[] = [];\n\n // For each artifact, check if it's in a collection for context\n for (const artifact of result.Results) {\n // Check for collection associations\n const collResult = await rv.RunView<MJCollectionArtifactEntity>(\n {\n EntityName: 'MJ: Collection Artifacts',\n ExtraFilter: `ArtifactVersionID IN (\n SELECT ID FROM [__mj].[vwArtifactVersions] WHERE ArtifactID='${artifact.ID}'\n )`,\n MaxRows: 1,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (collResult.Success && collResult.Results && collResult.Results.length > 0) {\n const collArtifact = collResult.Results[0];\n searchResults.push(\n this.mapArtifactToSearchResult(\n artifact,\n query,\n collArtifact.CollectionID,\n collArtifact.Collection\n )\n );\n } else {\n // No collection association\n searchResults.push(this.mapArtifactToSearchResult(artifact, query));\n }\n }\n\n return searchResults;\n }\n\n /**\n * Search collections by name and description\n * Includes both owned collections and shared collections user has access to\n */\n private async searchCollections(\n query: string,\n environmentId: string,\n currentUser: UserInfo,\n dateRange: DateRange\n ): Promise<SearchResult[]> {\n const rv = new RunView();\n const lowerQuery = query.toLowerCase();\n\n const ownerFilter = `OwnerID='${currentUser.ID}'`;\n const permissionSubquery = `ID IN (\n SELECT CollectionID\n FROM [__mj].[vwCollectionPermissions]\n WHERE UserID='${currentUser.ID}'\n )`;\n\n let filter = `EnvironmentID='${environmentId}'`;\n filter += ` AND (OwnerID IS NULL OR ${ownerFilter} OR ${permissionSubquery})`;\n filter += ` AND (LOWER(Name) LIKE '%${this.escapeSQL(lowerQuery)}%' OR LOWER(Description) LIKE '%${this.escapeSQL(lowerQuery)}%')`;\n\n if (dateRange.start) {\n filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;\n }\n if (dateRange.end) {\n filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;\n }\n\n const result = await rv.RunView<MJCollectionEntity>(\n {\n EntityName: 'MJ: Collections',\n ExtraFilter: filter,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 100,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (!result.Success || !result.Results) {\n console.error('Failed to search collections:', result.ErrorMessage);\n return [];\n }\n\n return result.Results.map(coll => this.mapCollectionToSearchResult(coll, query));\n }\n\n /**\n * Search tasks by name, description, and notes\n * Only includes tasks in conversations user has access to\n */\n private async searchTasks(\n query: string,\n environmentId: string,\n currentUser: UserInfo,\n dateRange: DateRange\n ): Promise<SearchResult[]> {\n const rv = new RunView();\n const lowerQuery = query.toLowerCase();\n\n // Build filter using same logic as TasksFullViewComponent\n const md = new Metadata();\n const cd = md.EntityByName('MJ: Conversation Details');\n const c = md.EntityByName('MJ: Conversations');\n\n if (!cd || !c) {\n console.warn('⚠️ Missing metadata for Conversations or Conversation Details');\n return [];\n }\n\n let filter = `ParentID IS NULL AND (UserID = '${currentUser.ID}' OR ConversationDetailID IN (\n SELECT ID FROM [${cd.SchemaName}].[${cd.BaseView}]\n WHERE\n UserID ='${currentUser.ID}' OR\n ConversationID IN (\n SELECT ID FROM [${c.SchemaName}].[${c.BaseView}] WHERE UserID='${currentUser.ID}'\n )\n ))`;\n\n filter += ` AND (LOWER(Name) LIKE '%${this.escapeSQL(lowerQuery)}%' OR LOWER(Description) LIKE '%${this.escapeSQL(lowerQuery)}%')`;\n\n if (dateRange.start) {\n filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;\n }\n if (dateRange.end) {\n filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;\n }\n\n const result = await rv.RunView<MJTaskEntity>(\n {\n EntityName: 'MJ: Tasks',\n ExtraFilter: filter,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 100,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (!result.Success || !result.Results) {\n console.error('Failed to search tasks:', result.ErrorMessage);\n return [];\n }\n\n return result.Results.map(task => this.mapTaskToSearchResult(task, query));\n }\n\n /**\n * Map conversation entity to search result\n */\n private mapConversationToSearchResult(conversation: MJConversationEntity, query: string): SearchResult {\n const lowerQuery = query.toLowerCase();\n const name = conversation.Name || 'Untitled Conversation';\n const description = conversation.Description || '';\n\n // Calculate relevance score\n let score = 0;\n if (name.toLowerCase().includes(lowerQuery)) score += 10;\n if (description.toLowerCase().includes(lowerQuery)) score += 5;\n\n // Find matched text\n const matchedText = this.extractMatchContext(\n name.toLowerCase().includes(lowerQuery) ? name : description,\n query\n );\n\n return {\n id: conversation.ID,\n type: 'conversation',\n title: name,\n preview: description || 'No description',\n matchedText,\n conversationId: conversation.ID,\n conversationName: name,\n createdAt: conversation.__mj_CreatedAt,\n relevanceScore: score\n };\n }\n\n /**\n * Map message entity to search result\n */\n private mapMessageToSearchResult(message: MJConversationDetailEntity, query: string): SearchResult {\n const messageText = message.Message || '';\n const matchedText = this.extractMatchContext(messageText, query);\n\n return {\n id: message.ID,\n type: 'message',\n title: `Message in ${message.Conversation || 'Unknown Conversation'}`,\n preview: this.truncateText(messageText, 150),\n matchedText,\n conversationId: message.ConversationID,\n conversationName: message.Conversation || undefined,\n createdAt: message.__mj_CreatedAt,\n relevanceScore: 5\n };\n }\n\n /**\n * Map artifact entity to search result\n */\n private mapArtifactToSearchResult(\n artifact: MJArtifactEntity,\n query: string,\n collectionId?: string,\n collectionName?: string\n ): SearchResult {\n const name = artifact.Name || 'Untitled Artifact';\n const description = artifact.Description || '';\n const lowerQuery = query.toLowerCase();\n\n let score = 0;\n if (name.toLowerCase().includes(lowerQuery)) score += 10;\n if (description.toLowerCase().includes(lowerQuery)) score += 5;\n\n const matchedText = this.extractMatchContext(\n name.toLowerCase().includes(lowerQuery) ? name : description,\n query\n );\n\n return {\n id: artifact.ID,\n type: 'artifact',\n title: name,\n preview: description || 'No description',\n matchedText,\n collectionId,\n collectionName,\n artifactType: artifact.Type || undefined,\n createdAt: artifact.__mj_CreatedAt,\n relevanceScore: score\n };\n }\n\n /**\n * Map collection entity to search result\n */\n private mapCollectionToSearchResult(collection: MJCollectionEntity, query: string): SearchResult {\n const lowerQuery = query.toLowerCase();\n const name = collection.Name || 'Untitled Collection';\n const description = collection.Description || '';\n\n let score = 0;\n if (name.toLowerCase().includes(lowerQuery)) score += 10;\n if (description.toLowerCase().includes(lowerQuery)) score += 5;\n\n const matchedText = this.extractMatchContext(\n name.toLowerCase().includes(lowerQuery) ? name : description,\n query\n );\n\n return {\n id: collection.ID,\n type: 'collection',\n title: name,\n preview: description || 'No description',\n matchedText,\n collectionId: collection.ID,\n collectionName: name,\n createdAt: collection.__mj_CreatedAt,\n relevanceScore: score\n };\n }\n\n /**\n * Map task entity to search result\n */\n private mapTaskToSearchResult(task: MJTaskEntity, query: string): SearchResult {\n const lowerQuery = query.toLowerCase();\n const name = task.Name || 'Untitled Task';\n const description = task.Description || '';\n\n let score = 0;\n if (name.toLowerCase().includes(lowerQuery)) score += 10;\n if (description.toLowerCase().includes(lowerQuery)) score += 5;\n\n const matchedText = this.extractMatchContext(\n name.toLowerCase().includes(lowerQuery) ? name : description,\n query\n );\n\n return {\n id: task.ID,\n type: 'task',\n title: name,\n preview: description || 'No description',\n matchedText,\n createdAt: task.__mj_CreatedAt,\n relevanceScore: score\n };\n }\n\n /**\n * Extract context around matched text\n */\n private extractMatchContext(text: string, query: string, contextLength: number = 100): string {\n const lowerText = text.toLowerCase();\n const lowerQuery = query.toLowerCase();\n const index = lowerText.indexOf(lowerQuery);\n\n if (index === -1) return this.truncateText(text, contextLength);\n\n const start = Math.max(0, index - contextLength / 2);\n const end = Math.min(text.length, index + query.length + contextLength / 2);\n\n let context = text.substring(start, end);\n if (start > 0) context = '...' + context;\n if (end < text.length) context = context + '...';\n\n return context;\n }\n\n /**\n * Truncate text to specified length\n */\n private truncateText(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return text.substring(0, maxLength) + '...';\n }\n\n /**\n * Escape SQL special characters\n */\n private escapeSQL(value: string): string {\n return value.replace(/'/g, \"''\");\n }\n\n /**\n * Set search filter\n */\n public setSearchFilter(filter: SearchFilter): void {\n this._searchFilter$.next(filter);\n }\n\n /**\n * Set date range filter\n */\n public setDateRange(range: DateRange): void {\n this._dateRange$.next(range);\n }\n\n /**\n * Clear all filters\n */\n public clearFilters(): void {\n this._searchFilter$.next('all');\n this._dateRange$.next({ start: null, end: null });\n }\n\n /**\n * Clear search results\n */\n public clearResults(): void {\n this._searchResults$.next({\n conversations: [],\n messages: [],\n artifacts: [],\n collections: [],\n tasks: [],\n total: 0\n });\n }\n\n /**\n * Get recent searches\n */\n public getRecentSearches(): string[] {\n return [...this.recentSearches];\n }\n\n /**\n * Add to recent searches\n */\n private addToRecentSearches(query: string): void {\n const trimmed = query.trim();\n if (!trimmed) return;\n\n // Remove if already exists\n this.recentSearches = this.recentSearches.filter(q => q !== trimmed);\n\n // Add to front\n this.recentSearches.unshift(trimmed);\n\n // Keep only MAX_RECENT_SEARCHES\n if (this.recentSearches.length > this.MAX_RECENT_SEARCHES) {\n this.recentSearches = this.recentSearches.slice(0, this.MAX_RECENT_SEARCHES);\n }\n\n this.saveRecentSearches();\n }\n\n /**\n * Clear recent searches\n */\n public clearRecentSearches(): void {\n this.recentSearches = [];\n this.saveRecentSearches();\n }\n\n /**\n * Load recent searches from localStorage\n */\n private loadRecentSearches(): void {\n try {\n const stored = localStorage.getItem('mj-recent-searches');\n if (stored) {\n this.recentSearches = JSON.parse(stored);\n }\n } catch (error) {\n console.error('Failed to load recent searches:', error);\n this.recentSearches = [];\n }\n }\n\n /**\n * Save recent searches to localStorage\n */\n private saveRecentSearches(): void {\n try {\n localStorage.setItem('mj-recent-searches', JSON.stringify(this.recentSearches));\n } catch (error) {\n console.error('Failed to save recent searches:', error);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"search.service.js","sourceRoot":"","sources":["../../../src/lib/services/search.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { BehaviorSubject, Observable, Subject, of } from 'rxjs';\nimport { debounceTime, distinctUntilChanged, switchMap, map } from 'rxjs/operators';\nimport {\n MJConversationEntity,\n MJConversationDetailEntity,\n MJConversationArtifactEntity,\n MJCollectionEntity,\n MJCollectionArtifactEntity,\n MJTaskEntity,\n MJArtifactEntity\n} from '@memberjunction/core-entities';\nimport { RunView, UserInfo, Metadata, IMetadataProvider } from '@memberjunction/core';\n\n/**\n * Types of searchable content\n */\nexport type SearchResultType = 'conversation' | 'message' | 'artifact' | 'collection' | 'task';\n\n/**\n * Filter options for search\n */\nexport type SearchFilter = 'all' | 'conversations' | 'messages' | 'artifacts' | 'collections' | 'tasks';\n\n/**\n * Unified search result\n */\nexport interface SearchResult {\n id: string;\n type: SearchResultType;\n title: string;\n preview: string;\n matchedText?: string;\n conversationId?: string;\n conversationName?: string;\n artifactType?: string;\n collectionId?: string;\n collectionName?: string;\n createdAt: Date;\n relevanceScore: number;\n}\n\n/**\n * Search results grouped by type\n */\nexport interface GroupedSearchResults {\n conversations: SearchResult[];\n messages: SearchResult[];\n artifacts: SearchResult[];\n collections: SearchResult[];\n tasks: SearchResult[];\n total: number;\n}\n\n/**\n * Date range filter\n */\nexport interface DateRange {\n start: Date | null;\n end: Date | null;\n}\n\n/**\n * Service for searching across conversations, messages, and artifacts\n * Provides debounced search with result ranking and filtering\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class SearchService {\n private _searchQuery$ = new Subject<string>();\n private _searchFilter$ = new BehaviorSubject<SearchFilter>('all');\n private _dateRange$ = new BehaviorSubject<DateRange>({ start: null, end: null });\n private _isSearching$ = new BehaviorSubject<boolean>(false);\n private _searchResults$ = new BehaviorSubject<GroupedSearchResults>({\n conversations: [],\n messages: [],\n artifacts: [],\n collections: [],\n tasks: [],\n total: 0\n });\n\n // Recent searches stored in memory (could be persisted to localStorage)\n private recentSearches: string[] = [];\n private readonly MAX_RECENT_SEARCHES = 10;\n\n // Public observables\n public readonly searchQuery$ = this._searchQuery$.asObservable();\n public readonly searchFilter$ = this._searchFilter$.asObservable();\n public readonly dateRange$ = this._dateRange$.asObservable();\n public readonly isSearching$ = this._isSearching$.asObservable();\n public readonly searchResults$ = this._searchResults$.asObservable();\n\n private _provider: IMetadataProvider | null = null;\n\n constructor() {\n this.initializeSearch();\n this.loadRecentSearches();\n }\n\n /**\n * Set the metadata provider this service should use. When unset, falls back to Metadata.Provider.\n */\n public set Provider(value: IMetadataProvider | null) {\n this._provider = value;\n }\n\n public get Provider(): IMetadataProvider {\n return this._provider ?? Metadata.Provider;\n }\n\n /**\n * Initialize debounced search\n */\n private initializeSearch(): void {\n this._searchQuery$.pipe(\n debounceTime(300),\n distinctUntilChanged()\n ).subscribe(query => {\n if (query && query.trim().length > 0) {\n this.addToRecentSearches(query);\n }\n });\n }\n\n /**\n * Search across all content types\n */\n public async search(\n query: string,\n environmentId: string,\n currentUser: UserInfo\n ): Promise<GroupedSearchResults> {\n if (!query || query.trim().length === 0) {\n const emptyResults: GroupedSearchResults = {\n conversations: [],\n messages: [],\n artifacts: [],\n collections: [],\n tasks: [],\n total: 0\n };\n this._searchResults$.next(emptyResults);\n return emptyResults;\n }\n\n this._isSearching$.next(true);\n this._searchQuery$.next(query);\n\n try {\n const filter = this._searchFilter$.value;\n const dateRange = this._dateRange$.value;\n\n // Search based on active filter\n const [conversations, messages, artifacts, collections, tasks] = await Promise.all([\n filter === 'all' || filter === 'conversations'\n ? this.searchConversations(query, environmentId, currentUser, dateRange)\n : Promise.resolve([]),\n filter === 'all' || filter === 'messages'\n ? this.searchMessages(query, environmentId, currentUser, dateRange)\n : Promise.resolve([]),\n filter === 'all' || filter === 'artifacts'\n ? this.searchArtifacts(query, environmentId, currentUser, dateRange)\n : Promise.resolve([]),\n filter === 'all' || filter === 'collections'\n ? this.searchCollections(query, environmentId, currentUser, dateRange)\n : Promise.resolve([]),\n filter === 'all' || filter === 'tasks'\n ? this.searchTasks(query, environmentId, currentUser, dateRange)\n : Promise.resolve([])\n ]);\n\n const results: GroupedSearchResults = {\n conversations,\n messages,\n artifacts,\n collections,\n tasks,\n total: conversations.length + messages.length + artifacts.length + collections.length + tasks.length\n };\n\n this._searchResults$.next(results);\n return results;\n } finally {\n this._isSearching$.next(false);\n }\n }\n\n /**\n * Search conversations by name and description\n */\n private async searchConversations(\n query: string,\n environmentId: string,\n currentUser: UserInfo,\n dateRange: DateRange\n ): Promise<SearchResult[]> {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const lowerQuery = query.toLowerCase();\n\n let filter = `EnvironmentID='${environmentId}' AND (IsArchived IS NULL OR IsArchived=0)`;\n filter += ` AND (LOWER(Name) LIKE '%${this.escapeSQL(lowerQuery)}%' OR LOWER(Description) LIKE '%${this.escapeSQL(lowerQuery)}%')`;\n\n if (dateRange.start) {\n filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;\n }\n if (dateRange.end) {\n filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;\n }\n\n const result = await rv.RunView<MJConversationEntity>(\n {\n EntityName: 'MJ: Conversations',\n ExtraFilter: filter,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 100,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (!result.Success || !result.Results) {\n console.error('Failed to search conversations:', result.ErrorMessage);\n return [];\n }\n\n return result.Results.map(conv => this.mapConversationToSearchResult(conv, query));\n }\n\n /**\n * Search message content\n */\n private async searchMessages(\n query: string,\n environmentId: string,\n currentUser: UserInfo,\n dateRange: DateRange\n ): Promise<SearchResult[]> {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const lowerQuery = query.toLowerCase();\n\n // First get conversations in this environment\n let filter = `ConversationID IN (SELECT ID FROM vwConversations WHERE EnvironmentID='${environmentId}' AND (IsArchived IS NULL OR IsArchived=0))`;\n filter += ` AND LOWER(Message) LIKE '%${this.escapeSQL(lowerQuery)}%'`;\n filter += ` AND (HiddenToUser IS NULL OR HiddenToUser=0)`;\n\n if (dateRange.start) {\n filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;\n }\n if (dateRange.end) {\n filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;\n }\n\n const result = await rv.RunView<MJConversationDetailEntity>(\n {\n EntityName: 'MJ: Conversation Details',\n ExtraFilter: filter,\n OrderBy: '__mj_CreatedAt DESC',\n MaxRows: 100,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (!result.Success || !result.Results) {\n console.error('Failed to search messages:', result.ErrorMessage);\n return [];\n }\n\n return result.Results.map(msg => this.mapMessageToSearchResult(msg, query));\n }\n\n /**\n * Search artifacts by name and description\n * Includes artifacts from both conversations and collections\n */\n private async searchArtifacts(\n query: string,\n environmentId: string,\n currentUser: UserInfo,\n dateRange: DateRange\n ): Promise<SearchResult[]> {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const lowerQuery = query.toLowerCase();\n\n // Search artifacts directly by name and description\n let filter = `EnvironmentID='${environmentId}'`;\n filter += ` AND (LOWER(Name) LIKE '%${this.escapeSQL(lowerQuery)}%' OR LOWER(Description) LIKE '%${this.escapeSQL(lowerQuery)}%')`;\n\n if (dateRange.start) {\n filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;\n }\n if (dateRange.end) {\n filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;\n }\n\n const result = await rv.RunView<MJArtifactEntity>(\n {\n EntityName: 'MJ: Artifacts',\n ExtraFilter: `${filter} AND (Visibility IS NULL OR Visibility='Always')`,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 100,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (!result.Success || !result.Results) {\n console.error('Failed to search artifacts:', result.ErrorMessage);\n return [];\n }\n\n const searchResults: SearchResult[] = [];\n\n // For each artifact, check if it's in a collection for context\n for (const artifact of result.Results) {\n // Check for collection associations\n const collResult = await rv.RunView<MJCollectionArtifactEntity>(\n {\n EntityName: 'MJ: Collection Artifacts',\n ExtraFilter: `ArtifactVersionID IN (\n SELECT ID FROM [__mj].[vwArtifactVersions] WHERE ArtifactID='${artifact.ID}'\n )`,\n MaxRows: 1,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (collResult.Success && collResult.Results && collResult.Results.length > 0) {\n const collArtifact = collResult.Results[0];\n searchResults.push(\n this.mapArtifactToSearchResult(\n artifact,\n query,\n collArtifact.CollectionID,\n collArtifact.Collection\n )\n );\n } else {\n // No collection association\n searchResults.push(this.mapArtifactToSearchResult(artifact, query));\n }\n }\n\n return searchResults;\n }\n\n /**\n * Search collections by name and description\n * Includes both owned collections and shared collections user has access to\n */\n private async searchCollections(\n query: string,\n environmentId: string,\n currentUser: UserInfo,\n dateRange: DateRange\n ): Promise<SearchResult[]> {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const lowerQuery = query.toLowerCase();\n\n const ownerFilter = `OwnerID='${currentUser.ID}'`;\n const permissionSubquery = `ID IN (\n SELECT CollectionID\n FROM [__mj].[vwCollectionPermissions]\n WHERE UserID='${currentUser.ID}'\n )`;\n\n let filter = `EnvironmentID='${environmentId}'`;\n filter += ` AND (OwnerID IS NULL OR ${ownerFilter} OR ${permissionSubquery})`;\n filter += ` AND (LOWER(Name) LIKE '%${this.escapeSQL(lowerQuery)}%' OR LOWER(Description) LIKE '%${this.escapeSQL(lowerQuery)}%')`;\n\n if (dateRange.start) {\n filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;\n }\n if (dateRange.end) {\n filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;\n }\n\n const result = await rv.RunView<MJCollectionEntity>(\n {\n EntityName: 'MJ: Collections',\n ExtraFilter: filter,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 100,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (!result.Success || !result.Results) {\n console.error('Failed to search collections:', result.ErrorMessage);\n return [];\n }\n\n return result.Results.map(coll => this.mapCollectionToSearchResult(coll, query));\n }\n\n /**\n * Search tasks by name, description, and notes\n * Only includes tasks in conversations user has access to\n */\n private async searchTasks(\n query: string,\n environmentId: string,\n currentUser: UserInfo,\n dateRange: DateRange\n ): Promise<SearchResult[]> {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const lowerQuery = query.toLowerCase();\n\n // Build filter using same logic as TasksFullViewComponent\n const md = this.Provider;\n const cd = md.EntityByName('MJ: Conversation Details');\n const c = md.EntityByName('MJ: Conversations');\n\n if (!cd || !c) {\n console.warn('⚠️ Missing metadata for Conversations or Conversation Details');\n return [];\n }\n\n let filter = `ParentID IS NULL AND (UserID = '${currentUser.ID}' OR ConversationDetailID IN (\n SELECT ID FROM [${cd.SchemaName}].[${cd.BaseView}]\n WHERE\n UserID ='${currentUser.ID}' OR\n ConversationID IN (\n SELECT ID FROM [${c.SchemaName}].[${c.BaseView}] WHERE UserID='${currentUser.ID}'\n )\n ))`;\n\n filter += ` AND (LOWER(Name) LIKE '%${this.escapeSQL(lowerQuery)}%' OR LOWER(Description) LIKE '%${this.escapeSQL(lowerQuery)}%')`;\n\n if (dateRange.start) {\n filter += ` AND __mj_CreatedAt >= '${dateRange.start.toISOString()}'`;\n }\n if (dateRange.end) {\n filter += ` AND __mj_CreatedAt <= '${dateRange.end.toISOString()}'`;\n }\n\n const result = await rv.RunView<MJTaskEntity>(\n {\n EntityName: 'MJ: Tasks',\n ExtraFilter: filter,\n OrderBy: '__mj_UpdatedAt DESC',\n MaxRows: 100,\n ResultType: 'entity_object'\n },\n currentUser\n );\n\n if (!result.Success || !result.Results) {\n console.error('Failed to search tasks:', result.ErrorMessage);\n return [];\n }\n\n return result.Results.map(task => this.mapTaskToSearchResult(task, query));\n }\n\n /**\n * Map conversation entity to search result\n */\n private mapConversationToSearchResult(conversation: MJConversationEntity, query: string): SearchResult {\n const lowerQuery = query.toLowerCase();\n const name = conversation.Name || 'Untitled Conversation';\n const description = conversation.Description || '';\n\n // Calculate relevance score\n let score = 0;\n if (name.toLowerCase().includes(lowerQuery)) score += 10;\n if (description.toLowerCase().includes(lowerQuery)) score += 5;\n\n // Find matched text\n const matchedText = this.extractMatchContext(\n name.toLowerCase().includes(lowerQuery) ? name : description,\n query\n );\n\n return {\n id: conversation.ID,\n type: 'conversation',\n title: name,\n preview: description || 'No description',\n matchedText,\n conversationId: conversation.ID,\n conversationName: name,\n createdAt: conversation.__mj_CreatedAt,\n relevanceScore: score\n };\n }\n\n /**\n * Map message entity to search result\n */\n private mapMessageToSearchResult(message: MJConversationDetailEntity, query: string): SearchResult {\n const messageText = message.Message || '';\n const matchedText = this.extractMatchContext(messageText, query);\n\n return {\n id: message.ID,\n type: 'message',\n title: `Message in ${message.Conversation || 'Unknown Conversation'}`,\n preview: this.truncateText(messageText, 150),\n matchedText,\n conversationId: message.ConversationID,\n conversationName: message.Conversation || undefined,\n createdAt: message.__mj_CreatedAt,\n relevanceScore: 5\n };\n }\n\n /**\n * Map artifact entity to search result\n */\n private mapArtifactToSearchResult(\n artifact: MJArtifactEntity,\n query: string,\n collectionId?: string,\n collectionName?: string\n ): SearchResult {\n const name = artifact.Name || 'Untitled Artifact';\n const description = artifact.Description || '';\n const lowerQuery = query.toLowerCase();\n\n let score = 0;\n if (name.toLowerCase().includes(lowerQuery)) score += 10;\n if (description.toLowerCase().includes(lowerQuery)) score += 5;\n\n const matchedText = this.extractMatchContext(\n name.toLowerCase().includes(lowerQuery) ? name : description,\n query\n );\n\n return {\n id: artifact.ID,\n type: 'artifact',\n title: name,\n preview: description || 'No description',\n matchedText,\n collectionId,\n collectionName,\n artifactType: artifact.Type || undefined,\n createdAt: artifact.__mj_CreatedAt,\n relevanceScore: score\n };\n }\n\n /**\n * Map collection entity to search result\n */\n private mapCollectionToSearchResult(collection: MJCollectionEntity, query: string): SearchResult {\n const lowerQuery = query.toLowerCase();\n const name = collection.Name || 'Untitled Collection';\n const description = collection.Description || '';\n\n let score = 0;\n if (name.toLowerCase().includes(lowerQuery)) score += 10;\n if (description.toLowerCase().includes(lowerQuery)) score += 5;\n\n const matchedText = this.extractMatchContext(\n name.toLowerCase().includes(lowerQuery) ? name : description,\n query\n );\n\n return {\n id: collection.ID,\n type: 'collection',\n title: name,\n preview: description || 'No description',\n matchedText,\n collectionId: collection.ID,\n collectionName: name,\n createdAt: collection.__mj_CreatedAt,\n relevanceScore: score\n };\n }\n\n /**\n * Map task entity to search result\n */\n private mapTaskToSearchResult(task: MJTaskEntity, query: string): SearchResult {\n const lowerQuery = query.toLowerCase();\n const name = task.Name || 'Untitled Task';\n const description = task.Description || '';\n\n let score = 0;\n if (name.toLowerCase().includes(lowerQuery)) score += 10;\n if (description.toLowerCase().includes(lowerQuery)) score += 5;\n\n const matchedText = this.extractMatchContext(\n name.toLowerCase().includes(lowerQuery) ? name : description,\n query\n );\n\n return {\n id: task.ID,\n type: 'task',\n title: name,\n preview: description || 'No description',\n matchedText,\n createdAt: task.__mj_CreatedAt,\n relevanceScore: score\n };\n }\n\n /**\n * Extract context around matched text\n */\n private extractMatchContext(text: string, query: string, contextLength: number = 100): string {\n const lowerText = text.toLowerCase();\n const lowerQuery = query.toLowerCase();\n const index = lowerText.indexOf(lowerQuery);\n\n if (index === -1) return this.truncateText(text, contextLength);\n\n const start = Math.max(0, index - contextLength / 2);\n const end = Math.min(text.length, index + query.length + contextLength / 2);\n\n let context = text.substring(start, end);\n if (start > 0) context = '...' + context;\n if (end < text.length) context = context + '...';\n\n return context;\n }\n\n /**\n * Truncate text to specified length\n */\n private truncateText(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return text.substring(0, maxLength) + '...';\n }\n\n /**\n * Escape SQL special characters\n */\n private escapeSQL(value: string): string {\n return value.replace(/'/g, \"''\");\n }\n\n /**\n * Set search filter\n */\n public setSearchFilter(filter: SearchFilter): void {\n this._searchFilter$.next(filter);\n }\n\n /**\n * Set date range filter\n */\n public setDateRange(range: DateRange): void {\n this._dateRange$.next(range);\n }\n\n /**\n * Clear all filters\n */\n public clearFilters(): void {\n this._searchFilter$.next('all');\n this._dateRange$.next({ start: null, end: null });\n }\n\n /**\n * Clear search results\n */\n public clearResults(): void {\n this._searchResults$.next({\n conversations: [],\n messages: [],\n artifacts: [],\n collections: [],\n tasks: [],\n total: 0\n });\n }\n\n /**\n * Get recent searches\n */\n public getRecentSearches(): string[] {\n return [...this.recentSearches];\n }\n\n /**\n * Add to recent searches\n */\n private addToRecentSearches(query: string): void {\n const trimmed = query.trim();\n if (!trimmed) return;\n\n // Remove if already exists\n this.recentSearches = this.recentSearches.filter(q => q !== trimmed);\n\n // Add to front\n this.recentSearches.unshift(trimmed);\n\n // Keep only MAX_RECENT_SEARCHES\n if (this.recentSearches.length > this.MAX_RECENT_SEARCHES) {\n this.recentSearches = this.recentSearches.slice(0, this.MAX_RECENT_SEARCHES);\n }\n\n this.saveRecentSearches();\n }\n\n /**\n * Clear recent searches\n */\n public clearRecentSearches(): void {\n this.recentSearches = [];\n this.saveRecentSearches();\n }\n\n /**\n * Load recent searches from localStorage\n */\n private loadRecentSearches(): void {\n try {\n const stored = localStorage.getItem('mj-recent-searches');\n if (stored) {\n this.recentSearches = JSON.parse(stored);\n }\n } catch (error) {\n console.error('Failed to load recent searches:', error);\n this.recentSearches = [];\n }\n }\n\n /**\n * Save recent searches to localStorage\n */\n private saveRecentSearches(): void {\n try {\n localStorage.setItem('mj-recent-searches', JSON.stringify(this.recentSearches));\n } catch (error) {\n console.error('Failed to save recent searches:', error);\n }\n }\n}\n"]}
|