@memberjunction/ng-conversations 5.36.0 → 5.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +2 -2
  2. package/dist/lib/components/conversation/conversation-agent-picker.component.d.ts +47 -0
  3. package/dist/lib/components/conversation/conversation-agent-picker.component.d.ts.map +1 -0
  4. package/dist/lib/components/conversation/conversation-agent-picker.component.js +253 -0
  5. package/dist/lib/components/conversation/conversation-agent-picker.component.js.map +1 -0
  6. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +201 -4
  7. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  8. package/dist/lib/components/conversation/conversation-chat-area.component.js +776 -263
  9. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  10. package/dist/lib/components/conversation/conversation-empty-state.component.d.ts +2 -1
  11. package/dist/lib/components/conversation/conversation-empty-state.component.d.ts.map +1 -1
  12. package/dist/lib/components/conversation/conversation-empty-state.component.js +6 -3
  13. package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
  14. package/dist/lib/components/conversation/conversation-mode-picker.component.d.ts +57 -0
  15. package/dist/lib/components/conversation/conversation-mode-picker.component.d.ts.map +1 -0
  16. package/dist/lib/components/conversation/conversation-mode-picker.component.js +252 -0
  17. package/dist/lib/components/conversation/conversation-mode-picker.component.js.map +1 -0
  18. package/dist/lib/components/dialogs/rating-dialog.component.d.ts +31 -0
  19. package/dist/lib/components/dialogs/rating-dialog.component.d.ts.map +1 -0
  20. package/dist/lib/components/dialogs/rating-dialog.component.js +290 -0
  21. package/dist/lib/components/dialogs/rating-dialog.component.js.map +1 -0
  22. package/dist/lib/components/mention/mention-editor.component.d.ts +1 -1
  23. package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
  24. package/dist/lib/components/mention/mention-editor.component.js +1 -0
  25. package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
  26. package/dist/lib/components/message/conversation-message-rating.component.d.ts +43 -18
  27. package/dist/lib/components/message/conversation-message-rating.component.d.ts.map +1 -1
  28. package/dist/lib/components/message/conversation-message-rating.component.js +235 -193
  29. package/dist/lib/components/message/conversation-message-rating.component.js.map +1 -1
  30. package/dist/lib/components/message/message-input-box.component.d.ts +1 -1
  31. package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
  32. package/dist/lib/components/message/message-input-box.component.js +1 -1
  33. package/dist/lib/components/message/message-input-box.component.js.map +1 -1
  34. package/dist/lib/components/message/message-input.component.d.ts +58 -4
  35. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  36. package/dist/lib/components/message/message-input.component.js +135 -21
  37. package/dist/lib/components/message/message-input.component.js.map +1 -1
  38. package/dist/lib/components/message/message-item.component.d.ts +0 -13
  39. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  40. package/dist/lib/components/message/message-item.component.js +126 -152
  41. package/dist/lib/components/message/message-item.component.js.map +1 -1
  42. package/dist/lib/components/overlay/chat-overlay.component.d.ts +88 -5
  43. package/dist/lib/components/overlay/chat-overlay.component.d.ts.map +1 -1
  44. package/dist/lib/components/overlay/chat-overlay.component.js +296 -54
  45. package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
  46. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +11 -0
  47. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  48. package/dist/lib/components/workspace/conversation-workspace.component.js +13 -3
  49. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  50. package/dist/lib/conversations.module.d.ts +59 -57
  51. package/dist/lib/conversations.module.d.ts.map +1 -1
  52. package/dist/lib/conversations.module.js +12 -4
  53. package/dist/lib/conversations.module.js.map +1 -1
  54. package/dist/lib/models/conversation-state.model.d.ts +0 -27
  55. package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
  56. package/dist/lib/models/conversation-state.model.js.map +1 -1
  57. package/dist/lib/services/conversation-agent.service.d.ts +1 -9
  58. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  59. package/dist/lib/services/conversation-agent.service.js +22 -121
  60. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  61. package/dist/lib/services/conversation-attachment.service.d.ts.map +1 -1
  62. package/dist/lib/services/conversation-attachment.service.js +5 -26
  63. package/dist/lib/services/conversation-attachment.service.js.map +1 -1
  64. package/dist/lib/services/conversation-bridge.service.d.ts +13 -0
  65. package/dist/lib/services/conversation-bridge.service.d.ts.map +1 -1
  66. package/dist/lib/services/conversation-bridge.service.js +15 -0
  67. package/dist/lib/services/conversation-bridge.service.js.map +1 -1
  68. package/dist/lib/services/data-cache.service.d.ts.map +1 -1
  69. package/dist/lib/services/data-cache.service.js +0 -1
  70. package/dist/lib/services/data-cache.service.js.map +1 -1
  71. package/dist/lib/services/dialog.service.d.ts +24 -0
  72. package/dist/lib/services/dialog.service.d.ts.map +1 -1
  73. package/dist/lib/services/dialog.service.js +45 -0
  74. package/dist/lib/services/dialog.service.js.map +1 -1
  75. package/package.json +23 -22
  76. package/dist/lib/components/message/suggested-responses.component.d.ts +0 -55
  77. package/dist/lib/components/message/suggested-responses.component.d.ts.map +0 -1
  78. package/dist/lib/components/message/suggested-responses.component.js +0 -207
  79. package/dist/lib/components/message/suggested-responses.component.js.map +0 -1
@@ -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, 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
+ {"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 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"]}
@@ -27,6 +27,25 @@ export interface ConfirmDialogOptions {
27
27
  cancelText?: string;
28
28
  dangerous?: boolean;
29
29
  }
30
+ export interface RatingDialogOptions {
31
+ title?: string;
32
+ message?: string;
33
+ initialRating?: number | null;
34
+ initialComments?: string;
35
+ okText?: string;
36
+ cancelText?: string;
37
+ /** When true, the rating dialog shows a consent checkbox the user must
38
+ * accept before submitting (first-time-only authorization). */
39
+ requireConsent?: boolean;
40
+ }
41
+ export interface RatingDialogResult {
42
+ rating: number;
43
+ comments: string;
44
+ /** True only when the user *just* acknowledged consent in this dialog —
45
+ * i.e. `requireConsent` was true and they checked the box. The caller
46
+ * should persist the acknowledgement so consent isn't requested again. */
47
+ consentNewlyAcknowledged: boolean;
48
+ }
30
49
  /**
31
50
  * Dialog service for displaying MJ dialogs.
32
51
  * Replaces browser alert() and confirm() with proper UI components.
@@ -54,6 +73,11 @@ export declare class DialogService {
54
73
  value: string;
55
74
  secondValue?: string;
56
75
  } | null>;
76
+ /**
77
+ * Show a rating dialog (1-10 + free-form comments).
78
+ * @returns Promise<RatingDialogResult | null> — null if cancelled or no rating selected.
79
+ */
80
+ rating(options?: RatingDialogOptions): Promise<RatingDialogResult | null>;
57
81
  /**
58
82
  * Show a custom dialog with custom content and actions
59
83
  */
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/dialog.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAkB,MAAM,kCAAkC,CAAC;;AAIhG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,qBAGa,aAAa;IACZ,OAAO,CAAC,eAAe;gBAAf,eAAe,EAAE,eAAe;IAEpD;;;OAGG;IACH,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IA+BxD;;OAEG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB3E;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,GAAG;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAC;IAkElG;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,GAAE,MAAY,GAAG,WAAW;yCA1ItF,aAAa;6CAAb,aAAa;CAoKzB"}
1
+ {"version":3,"file":"dialog.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/dialog.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAkB,MAAM,kCAAkC,CAAC;;AAKhG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;oEACgE;IAChE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB;;+EAE2E;IAC3E,wBAAwB,EAAE,OAAO,CAAC;CACnC;AAED;;;GAGG;AACH,qBAGa,aAAa;IACZ,OAAO,CAAC,eAAe;gBAAf,eAAe,EAAE,eAAe;IAEpD;;;OAGG;IACH,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IA+BxD;;OAEG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB3E;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,GAAG;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAC;IAkElG;;;OAGG;IACH,MAAM,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IA0C7E;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,GAAE,MAAY,GAAG,WAAW;yCAxLtF,aAAa;6CAAb,aAAa;CAkNzB"}
@@ -1,5 +1,6 @@
1
1
  import { Injectable } from '@angular/core';
2
2
  import { InputDialogComponent } from '../components/dialogs/input-dialog.component';
3
+ import { RatingDialogComponent } from '../components/dialogs/rating-dialog.component';
3
4
  import * as i0 from "@angular/core";
4
5
  import * as i1 from "@memberjunction/ng-ui-components";
5
6
  /**
@@ -139,6 +140,50 @@ export class DialogService {
139
140
  });
140
141
  });
141
142
  }
143
+ /**
144
+ * Show a rating dialog (1-10 + free-form comments).
145
+ * @returns Promise<RatingDialogResult | null> — null if cancelled or no rating selected.
146
+ */
147
+ rating(options = {}) {
148
+ return new Promise((resolve) => {
149
+ const okText = options.okText || 'Submit';
150
+ const cancelText = options.cancelText || 'Cancel';
151
+ const dialogRef = this.mjDialogService.open({
152
+ title: options.title || 'Rate this response',
153
+ content: RatingDialogComponent,
154
+ actions: [
155
+ { text: okText, primary: true },
156
+ { text: cancelText, primary: false }
157
+ ],
158
+ width: 560,
159
+ minWidth: 320
160
+ });
161
+ const componentInstance = dialogRef.Content.instance;
162
+ componentInstance.message = options.message ?? '';
163
+ componentInstance.initialRating = options.initialRating ?? null;
164
+ componentInstance.initialComments = options.initialComments ?? '';
165
+ componentInstance.requireConsent = options.requireConsent ?? false;
166
+ dialogRef.Result.subscribe((result) => {
167
+ const action = result;
168
+ if (action && 'text' in action && action.text === okText) {
169
+ const rating = componentInstance.getRating();
170
+ if (rating == null || !componentInstance.isConsentValid()) {
171
+ resolve(null);
172
+ }
173
+ else {
174
+ resolve({
175
+ rating,
176
+ comments: componentInstance.getComments(),
177
+ consentNewlyAcknowledged: componentInstance.wasConsentNewlyAcknowledged()
178
+ });
179
+ }
180
+ }
181
+ else {
182
+ resolve(null);
183
+ }
184
+ });
185
+ });
186
+ }
142
187
  /**
143
188
  * Show a custom dialog with custom content and actions
144
189
  */
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.service.js","sourceRoot":"","sources":["../../../src/lib/services/dialog.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { MJDialogService, MJDialogRef, MJDialogAction } from '@memberjunction/ng-ui-components';\nimport { Observable } from 'rxjs';\nimport { InputDialogComponent } from '../components/dialogs/input-dialog.component';\n\nexport interface DialogButton {\n text: string;\n primary?: boolean;\n action: () => void;\n}\n\nexport interface InputDialogOptions {\n title: string;\n message: string;\n inputLabel: string;\n inputValue?: string;\n inputType?: 'text' | 'textarea' | 'number' | 'email';\n placeholder?: string;\n required?: boolean;\n secondInputLabel?: string;\n secondInputValue?: string;\n secondInputPlaceholder?: string;\n secondInputRequired?: boolean;\n okText?: string;\n cancelText?: string;\n}\n\nexport interface ConfirmDialogOptions {\n title: string;\n message: string;\n okText?: string;\n cancelText?: string;\n dangerous?: boolean;\n}\n\n/**\n * Dialog service for displaying MJ dialogs.\n * Replaces browser alert() and confirm() with proper UI components.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class DialogService {\n constructor(private mjDialogService: MJDialogService) {}\n\n /**\n * Show a confirmation dialog\n * @returns Promise<boolean> - true if user clicked OK, false if cancelled\n */\n confirm(options: ConfirmDialogOptions): Promise<boolean> {\n return new Promise((resolve) => {\n const dialogRef = this.mjDialogService.open({\n title: options.title,\n content: options.message,\n actions: [\n {\n text: options.okText || 'OK',\n primary: true,\n themeColor: options.dangerous ? 'error' : 'primary'\n },\n {\n text: options.cancelText || 'Cancel',\n primary: false\n }\n ],\n width: 450,\n minWidth: 250\n });\n\n dialogRef.Result.subscribe((result) => {\n const action = result as MJDialogAction | undefined;\n if (action && 'text' in action) {\n resolve(action.text === (options.okText || 'OK'));\n } else {\n resolve(false);\n }\n });\n });\n }\n\n /**\n * Show an alert dialog\n */\n alert(title: string, message: string, okText: string = 'OK'): Promise<void> {\n return new Promise((resolve) => {\n const dialogRef = this.mjDialogService.open({\n title: title,\n content: message,\n actions: [\n {\n text: okText,\n primary: true\n }\n ],\n width: 450,\n minWidth: 250\n });\n\n dialogRef.Result.subscribe(() => {\n resolve();\n });\n });\n }\n\n /**\n * Show an input dialog\n * @returns Promise<string | {value: string; secondValue?: string} | null> -\n * If single input: returns string\n * If dual input: returns object with both values\n * Returns null if cancelled\n */\n input(options: InputDialogOptions): Promise<string | {value: string; secondValue?: string} | null> {\n return new Promise((resolve) => {\n const dialogRef = this.mjDialogService.open({\n title: options.title,\n content: InputDialogComponent,\n actions: [\n {\n text: options.okText || 'OK',\n primary: true\n },\n {\n text: options.cancelText || 'Cancel',\n primary: false\n }\n ],\n width: 500,\n minWidth: 300\n });\n\n // Pass data to the component\n const componentInstance = dialogRef.Content!.instance as unknown as InputDialogComponent;\n componentInstance.message = options.message;\n componentInstance.inputLabel = options.inputLabel;\n componentInstance.inputType = options.inputType || 'text';\n componentInstance.placeholder = options.placeholder || '';\n componentInstance.required = options.required || false;\n componentInstance.value = options.inputValue || '';\n componentInstance.secondInputLabel = options.secondInputLabel || '';\n componentInstance.secondInputPlaceholder = options.secondInputPlaceholder || '';\n componentInstance.secondInputRequired = options.secondInputRequired || false;\n componentInstance.secondValue = options.secondInputValue || '';\n\n // Focus and select input after dialog opens\n setTimeout(() => {\n const inputElement = document.querySelector('.mj-dialog-body input, .mj-dialog-body textarea') as HTMLInputElement | HTMLTextAreaElement;\n if (inputElement) {\n inputElement.focus();\n inputElement.select();\n }\n }, 100);\n\n dialogRef.Result.subscribe((result) => {\n const action = result as MJDialogAction | undefined;\n if (action && 'text' in action && action.text === (options.okText || 'OK')) {\n const value = componentInstance.getValue();\n if (options.required && !value) {\n resolve(null);\n } else {\n // If second input is present, return object with both values\n if (options.secondInputLabel) {\n resolve({\n value: value,\n secondValue: componentInstance.getSecondValue()\n });\n } else {\n // Single input - return string for backward compatibility\n resolve(value);\n }\n }\n } else {\n resolve(null);\n }\n });\n });\n }\n\n /**\n * Show a custom dialog with custom content and actions\n */\n custom(title: string, content: string, buttons: DialogButton[], width: number = 500): MJDialogRef {\n const actions = buttons.map(btn => ({\n text: btn.text,\n primary: btn.primary || false\n }));\n\n const dialogRef = this.mjDialogService.open({\n title: title,\n content: content,\n actions: actions,\n width: width,\n minWidth: 300\n });\n\n dialogRef.Result.subscribe((result) => {\n const action = result as MJDialogAction | undefined;\n if (action && 'text' in action) {\n const button = buttons.find(b => b.text === action.text);\n if (button && button.action) {\n button.action();\n }\n }\n });\n\n return dialogRef;\n }\n}\n"]}
1
+ {"version":3,"file":"dialog.service.js","sourceRoot":"","sources":["../../../src/lib/services/dialog.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { MJDialogService, MJDialogRef, MJDialogAction } from '@memberjunction/ng-ui-components';\nimport { Observable } from 'rxjs';\nimport { InputDialogComponent } from '../components/dialogs/input-dialog.component';\nimport { RatingDialogComponent } from '../components/dialogs/rating-dialog.component';\n\nexport interface DialogButton {\n text: string;\n primary?: boolean;\n action: () => void;\n}\n\nexport interface InputDialogOptions {\n title: string;\n message: string;\n inputLabel: string;\n inputValue?: string;\n inputType?: 'text' | 'textarea' | 'number' | 'email';\n placeholder?: string;\n required?: boolean;\n secondInputLabel?: string;\n secondInputValue?: string;\n secondInputPlaceholder?: string;\n secondInputRequired?: boolean;\n okText?: string;\n cancelText?: string;\n}\n\nexport interface ConfirmDialogOptions {\n title: string;\n message: string;\n okText?: string;\n cancelText?: string;\n dangerous?: boolean;\n}\n\nexport interface RatingDialogOptions {\n title?: string;\n message?: string;\n initialRating?: number | null;\n initialComments?: string;\n okText?: string;\n cancelText?: string;\n /** When true, the rating dialog shows a consent checkbox the user must\n * accept before submitting (first-time-only authorization). */\n requireConsent?: boolean;\n}\n\nexport interface RatingDialogResult {\n rating: number;\n comments: string;\n /** True only when the user *just* acknowledged consent in this dialog —\n * i.e. `requireConsent` was true and they checked the box. The caller\n * should persist the acknowledgement so consent isn't requested again. */\n consentNewlyAcknowledged: boolean;\n}\n\n/**\n * Dialog service for displaying MJ dialogs.\n * Replaces browser alert() and confirm() with proper UI components.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class DialogService {\n constructor(private mjDialogService: MJDialogService) {}\n\n /**\n * Show a confirmation dialog\n * @returns Promise<boolean> - true if user clicked OK, false if cancelled\n */\n confirm(options: ConfirmDialogOptions): Promise<boolean> {\n return new Promise((resolve) => {\n const dialogRef = this.mjDialogService.open({\n title: options.title,\n content: options.message,\n actions: [\n {\n text: options.okText || 'OK',\n primary: true,\n themeColor: options.dangerous ? 'error' : 'primary'\n },\n {\n text: options.cancelText || 'Cancel',\n primary: false\n }\n ],\n width: 450,\n minWidth: 250\n });\n\n dialogRef.Result.subscribe((result) => {\n const action = result as MJDialogAction | undefined;\n if (action && 'text' in action) {\n resolve(action.text === (options.okText || 'OK'));\n } else {\n resolve(false);\n }\n });\n });\n }\n\n /**\n * Show an alert dialog\n */\n alert(title: string, message: string, okText: string = 'OK'): Promise<void> {\n return new Promise((resolve) => {\n const dialogRef = this.mjDialogService.open({\n title: title,\n content: message,\n actions: [\n {\n text: okText,\n primary: true\n }\n ],\n width: 450,\n minWidth: 250\n });\n\n dialogRef.Result.subscribe(() => {\n resolve();\n });\n });\n }\n\n /**\n * Show an input dialog\n * @returns Promise<string | {value: string; secondValue?: string} | null> -\n * If single input: returns string\n * If dual input: returns object with both values\n * Returns null if cancelled\n */\n input(options: InputDialogOptions): Promise<string | {value: string; secondValue?: string} | null> {\n return new Promise((resolve) => {\n const dialogRef = this.mjDialogService.open({\n title: options.title,\n content: InputDialogComponent,\n actions: [\n {\n text: options.okText || 'OK',\n primary: true\n },\n {\n text: options.cancelText || 'Cancel',\n primary: false\n }\n ],\n width: 500,\n minWidth: 300\n });\n\n // Pass data to the component\n const componentInstance = dialogRef.Content!.instance as unknown as InputDialogComponent;\n componentInstance.message = options.message;\n componentInstance.inputLabel = options.inputLabel;\n componentInstance.inputType = options.inputType || 'text';\n componentInstance.placeholder = options.placeholder || '';\n componentInstance.required = options.required || false;\n componentInstance.value = options.inputValue || '';\n componentInstance.secondInputLabel = options.secondInputLabel || '';\n componentInstance.secondInputPlaceholder = options.secondInputPlaceholder || '';\n componentInstance.secondInputRequired = options.secondInputRequired || false;\n componentInstance.secondValue = options.secondInputValue || '';\n\n // Focus and select input after dialog opens\n setTimeout(() => {\n const inputElement = document.querySelector('.mj-dialog-body input, .mj-dialog-body textarea') as HTMLInputElement | HTMLTextAreaElement;\n if (inputElement) {\n inputElement.focus();\n inputElement.select();\n }\n }, 100);\n\n dialogRef.Result.subscribe((result) => {\n const action = result as MJDialogAction | undefined;\n if (action && 'text' in action && action.text === (options.okText || 'OK')) {\n const value = componentInstance.getValue();\n if (options.required && !value) {\n resolve(null);\n } else {\n // If second input is present, return object with both values\n if (options.secondInputLabel) {\n resolve({\n value: value,\n secondValue: componentInstance.getSecondValue()\n });\n } else {\n // Single input - return string for backward compatibility\n resolve(value);\n }\n }\n } else {\n resolve(null);\n }\n });\n });\n }\n\n /**\n * Show a rating dialog (1-10 + free-form comments).\n * @returns Promise<RatingDialogResult | null> — null if cancelled or no rating selected.\n */\n rating(options: RatingDialogOptions = {}): Promise<RatingDialogResult | null> {\n return new Promise((resolve) => {\n const okText = options.okText || 'Submit';\n const cancelText = options.cancelText || 'Cancel';\n\n const dialogRef = this.mjDialogService.open({\n title: options.title || 'Rate this response',\n content: RatingDialogComponent,\n actions: [\n { text: okText, primary: true },\n { text: cancelText, primary: false }\n ],\n width: 560,\n minWidth: 320\n });\n\n const componentInstance = dialogRef.Content!.instance as unknown as RatingDialogComponent;\n componentInstance.message = options.message ?? '';\n componentInstance.initialRating = options.initialRating ?? null;\n componentInstance.initialComments = options.initialComments ?? '';\n componentInstance.requireConsent = options.requireConsent ?? false;\n\n dialogRef.Result.subscribe((result) => {\n const action = result as MJDialogAction | undefined;\n if (action && 'text' in action && action.text === okText) {\n const rating = componentInstance.getRating();\n if (rating == null || !componentInstance.isConsentValid()) {\n resolve(null);\n } else {\n resolve({\n rating,\n comments: componentInstance.getComments(),\n consentNewlyAcknowledged: componentInstance.wasConsentNewlyAcknowledged()\n });\n }\n } else {\n resolve(null);\n }\n });\n });\n }\n\n /**\n * Show a custom dialog with custom content and actions\n */\n custom(title: string, content: string, buttons: DialogButton[], width: number = 500): MJDialogRef {\n const actions = buttons.map(btn => ({\n text: btn.text,\n primary: btn.primary || false\n }));\n\n const dialogRef = this.mjDialogService.open({\n title: title,\n content: content,\n actions: actions,\n width: width,\n minWidth: 300\n });\n\n dialogRef.Result.subscribe((result) => {\n const action = result as MJDialogAction | undefined;\n if (action && 'text' in action) {\n const button = buttons.find(b => b.text === action.text);\n if (button && button.action) {\n button.action();\n }\n }\n });\n\n return dialogRef;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-conversations",
3
- "version": "5.36.0",
3
+ "version": "5.38.0",
4
4
  "description": "MemberJunction: Conversation, Collection, and Artifact management components for any Angular application",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -29,29 +29,30 @@
29
29
  "@angular/router": "21.1.3"
30
30
  },
31
31
  "dependencies": {
32
- "@memberjunction/ng-ui-components": "5.36.0",
33
32
  "@angular/animations": "21.1.3",
34
33
  "@angular/cdk": "21.1.3",
35
- "@memberjunction/ai": "5.36.0",
36
- "@memberjunction/ai-agent-client": "5.36.0",
37
- "@memberjunction/ai-core-plus": "5.36.0",
38
- "@memberjunction/ai-engine-base": "5.36.0",
39
- "@memberjunction/ng-agent-client": "5.36.0",
40
- "@memberjunction/core": "5.36.0",
41
- "@memberjunction/core-entities": "5.36.0",
42
- "@memberjunction/global": "5.36.0",
43
- "@memberjunction/graphql-dataprovider": "5.36.0",
44
- "@memberjunction/ng-artifacts": "5.36.0",
45
- "@memberjunction/ng-base-types": "5.36.0",
46
- "@memberjunction/ng-code-editor": "5.36.0",
47
- "@memberjunction/ng-container-directives": "5.36.0",
48
- "@memberjunction/ng-forms": "5.36.0",
49
- "@memberjunction/ng-markdown": "5.36.0",
50
- "@memberjunction/ng-notifications": "5.36.0",
51
- "@memberjunction/ng-resource-permissions": "5.36.0",
52
- "@memberjunction/ng-shared-generic": "5.36.0",
53
- "@memberjunction/ng-tasks": "5.36.0",
54
- "@memberjunction/ng-testing": "5.36.0",
34
+ "@memberjunction/ai": "5.38.0",
35
+ "@memberjunction/ai-agent-client": "5.38.0",
36
+ "@memberjunction/ai-core-plus": "5.38.0",
37
+ "@memberjunction/ai-engine-base": "5.38.0",
38
+ "@memberjunction/core": "5.38.0",
39
+ "@memberjunction/core-entities": "5.38.0",
40
+ "@memberjunction/global": "5.38.0",
41
+ "@memberjunction/graphql-dataprovider": "5.38.0",
42
+ "@memberjunction/interactive-component-types": "5.38.0",
43
+ "@memberjunction/ng-agent-client": "5.38.0",
44
+ "@memberjunction/ng-artifacts": "5.38.0",
45
+ "@memberjunction/ng-base-types": "5.38.0",
46
+ "@memberjunction/ng-code-editor": "5.38.0",
47
+ "@memberjunction/ng-container-directives": "5.38.0",
48
+ "@memberjunction/ng-forms": "5.38.0",
49
+ "@memberjunction/ng-markdown": "5.38.0",
50
+ "@memberjunction/ng-notifications": "5.38.0",
51
+ "@memberjunction/ng-resource-permissions": "5.38.0",
52
+ "@memberjunction/ng-shared-generic": "5.38.0",
53
+ "@memberjunction/ng-tasks": "5.38.0",
54
+ "@memberjunction/ng-testing": "5.38.0",
55
+ "@memberjunction/ng-ui-components": "5.38.0",
55
56
  "rxjs": "^7.8.2",
56
57
  "tslib": "^2.8.1"
57
58
  },
@@ -1,55 +0,0 @@
1
- import { EventEmitter, ElementRef, ChangeDetectorRef, OnInit, AfterViewInit } from '@angular/core';
2
- import { SuggestedResponse } from '../../models/conversation-state.model';
3
- import * as i0 from "@angular/core";
4
- /**
5
- * Component for displaying suggested response options below AI messages
6
- * Provides quick reply buttons and optional text input for streamlined conversation flow
7
- */
8
- export declare class SuggestedResponsesComponent implements OnInit, AfterViewInit {
9
- private cdr;
10
- suggestedResponses: SuggestedResponse[];
11
- disabled: boolean;
12
- isLastMessage: boolean;
13
- isConversationOwner: boolean;
14
- responseSelected: EventEmitter<{
15
- text: string;
16
- customInput?: string;
17
- }>;
18
- inputField?: ElementRef<HTMLInputElement>;
19
- customInputValue: string;
20
- constructor(cdr: ChangeDetectorRef);
21
- ngOnInit(): void;
22
- ngAfterViewInit(): void;
23
- /**
24
- * Get all regular button responses (not input fields)
25
- */
26
- get regularResponses(): SuggestedResponse[];
27
- /**
28
- * Get the input response option (if any)
29
- * Only supports one input option per message
30
- */
31
- get inputResponse(): SuggestedResponse | null;
32
- /**
33
- * Check if component should be visible
34
- */
35
- get isVisible(): boolean;
36
- /**
37
- * Handle regular button click
38
- */
39
- onResponseClick(response: SuggestedResponse): void;
40
- /**
41
- * Handle input submission (Enter key or submit button click)
42
- */
43
- onInputSubmit(): void;
44
- /**
45
- * Handle Enter key in input field
46
- */
47
- onInputKeydown(event: KeyboardEvent): void;
48
- /**
49
- * Track by function for ngFor to help Angular track items
50
- */
51
- trackByIndex(index: number): number;
52
- static ɵfac: i0.ɵɵFactoryDeclaration<SuggestedResponsesComponent, never>;
53
- static ɵcmp: i0.ɵɵComponentDeclaration<SuggestedResponsesComponent, "mj-suggested-responses", never, { "suggestedResponses": { "alias": "suggestedResponses"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "isLastMessage": { "alias": "isLastMessage"; "required": false; }; "isConversationOwner": { "alias": "isConversationOwner"; "required": false; }; }, { "responseSelected": "responseSelected"; }, never, never, false, never>;
54
- }
55
- //# sourceMappingURL=suggested-responses.component.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"suggested-responses.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/message/suggested-responses.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAa,UAAU,EAAE,iBAAiB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACxI,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;;AAE1E;;;GAGG;AACH,qBAMa,2BAA4B,YAAW,MAAM,EAAE,aAAa;IAY3D,OAAO,CAAC,GAAG;IAXd,kBAAkB,EAAE,iBAAiB,EAAE,CAAM;IAC7C,QAAQ,EAAE,OAAO,CAAS;IAC1B,aAAa,EAAE,OAAO,CAAS;IAC/B,mBAAmB,EAAE,OAAO,CAAS;IAEpC,gBAAgB;cAA2B,MAAM;sBAAgB,MAAM;OAAK;IAE7D,UAAU,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAE5D,gBAAgB,EAAE,MAAM,CAAM;gBAEjB,GAAG,EAAE,iBAAiB;IAI1C,QAAQ;IAYR,eAAe;IAIf;;OAEG;IACH,IAAW,gBAAgB,IAAI,iBAAiB,EAAE,CAEjD;IAED;;;OAGG;IACH,IAAW,aAAa,IAAI,iBAAiB,GAAG,IAAI,CAEnD;IAED;;OAEG;IACH,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED;;OAEG;IACI,eAAe,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAgBzD;;OAEG;IACI,aAAa,IAAI,IAAI;IAmB5B;;OAEG;IACI,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAOjD;;OAEG;IACI,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;yCA5G/B,2BAA2B;2CAA3B,2BAA2B;CA+GvC"}
@@ -1,207 +0,0 @@
1
- import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
2
- import * as i0 from "@angular/core";
3
- import * as i1 from "@angular/forms";
4
- const _c0 = ["inputField"];
5
- function SuggestedResponsesComponent_Conditional_0_For_7_Conditional_1_Template(rf, ctx) { if (rf & 1) {
6
- i0.ɵɵelement(0, "i");
7
- } if (rf & 2) {
8
- const response_r2 = i0.ɵɵnextContext().$implicit;
9
- i0.ɵɵclassMap("fas " + response_r2.iconClass);
10
- } }
11
- function SuggestedResponsesComponent_Conditional_0_For_7_Template(rf, ctx) { if (rf & 1) {
12
- const _r1 = i0.ɵɵgetCurrentView();
13
- i0.ɵɵelementStart(0, "button", 7);
14
- i0.ɵɵlistener("click", function SuggestedResponsesComponent_Conditional_0_For_7_Template_button_click_0_listener() { const response_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onResponseClick(response_r2)); });
15
- i0.ɵɵconditionalCreate(1, SuggestedResponsesComponent_Conditional_0_For_7_Conditional_1_Template, 1, 2, "i", 8);
16
- i0.ɵɵelementStart(2, "span");
17
- i0.ɵɵtext(3);
18
- i0.ɵɵelementEnd()();
19
- } if (rf & 2) {
20
- const response_r2 = ctx.$implicit;
21
- const ctx_r2 = i0.ɵɵnextContext(2);
22
- i0.ɵɵproperty("disabled", ctx_r2.disabled)("title", response_r2.text);
23
- i0.ɵɵadvance();
24
- i0.ɵɵconditional(response_r2.iconClass ? 1 : -1);
25
- i0.ɵɵadvance(2);
26
- i0.ɵɵtextInterpolate(response_r2.text);
27
- } }
28
- function SuggestedResponsesComponent_Conditional_0_Conditional_8_Conditional_1_Template(rf, ctx) { if (rf & 1) {
29
- i0.ɵɵelement(0, "i", 13);
30
- } if (rf & 2) {
31
- const ctx_r2 = i0.ɵɵnextContext(3);
32
- i0.ɵɵclassMap("fas " + ctx_r2.inputResponse.iconClass);
33
- } }
34
- function SuggestedResponsesComponent_Conditional_0_Conditional_8_Template(rf, ctx) { if (rf & 1) {
35
- const _r4 = i0.ɵɵgetCurrentView();
36
- i0.ɵɵelementStart(0, "div", 6);
37
- i0.ɵɵconditionalCreate(1, SuggestedResponsesComponent_Conditional_0_Conditional_8_Conditional_1_Template, 1, 2, "i", 9);
38
- i0.ɵɵelementStart(2, "input", 10, 0);
39
- i0.ɵɵtwoWayListener("ngModelChange", function SuggestedResponsesComponent_Conditional_0_Conditional_8_Template_input_ngModelChange_2_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(2); i0.ɵɵtwoWayBindingSet(ctx_r2.customInputValue, $event) || (ctx_r2.customInputValue = $event); return i0.ɵɵresetView($event); });
40
- i0.ɵɵlistener("keydown", function SuggestedResponsesComponent_Conditional_0_Conditional_8_Template_input_keydown_2_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onInputKeydown($event)); });
41
- i0.ɵɵelementEnd();
42
- i0.ɵɵelementStart(4, "button", 11);
43
- i0.ɵɵlistener("click", function SuggestedResponsesComponent_Conditional_0_Conditional_8_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onInputSubmit()); });
44
- i0.ɵɵelement(5, "i", 12);
45
- i0.ɵɵelementEnd()();
46
- } if (rf & 2) {
47
- const ctx_r2 = i0.ɵɵnextContext(2);
48
- i0.ɵɵadvance();
49
- i0.ɵɵconditional(ctx_r2.inputResponse.iconClass ? 1 : -1);
50
- i0.ɵɵadvance();
51
- i0.ɵɵproperty("placeholder", ctx_r2.inputResponse.text);
52
- i0.ɵɵtwoWayProperty("ngModel", ctx_r2.customInputValue);
53
- i0.ɵɵproperty("disabled", ctx_r2.disabled);
54
- i0.ɵɵadvance(2);
55
- i0.ɵɵproperty("disabled", ctx_r2.disabled || !ctx_r2.customInputValue || !ctx_r2.customInputValue.trim());
56
- } }
57
- function SuggestedResponsesComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
58
- i0.ɵɵelementStart(0, "div", 1)(1, "div", 2);
59
- i0.ɵɵelement(2, "i", 3);
60
- i0.ɵɵelementStart(3, "span");
61
- i0.ɵɵtext(4, "Quick replies");
62
- i0.ɵɵelementEnd()();
63
- i0.ɵɵelementStart(5, "div", 4);
64
- i0.ɵɵrepeaterCreate(6, SuggestedResponsesComponent_Conditional_0_For_7_Template, 4, 4, "button", 5, i0.ɵɵrepeaterTrackByIndex);
65
- i0.ɵɵconditionalCreate(8, SuggestedResponsesComponent_Conditional_0_Conditional_8_Template, 6, 5, "div", 6);
66
- i0.ɵɵelementEnd()();
67
- } if (rf & 2) {
68
- const ctx_r2 = i0.ɵɵnextContext();
69
- i0.ɵɵadvance(6);
70
- i0.ɵɵrepeater(ctx_r2.regularResponses);
71
- i0.ɵɵadvance(2);
72
- i0.ɵɵconditional(ctx_r2.inputResponse ? 8 : -1);
73
- } }
74
- /**
75
- * Component for displaying suggested response options below AI messages
76
- * Provides quick reply buttons and optional text input for streamlined conversation flow
77
- */
78
- export class SuggestedResponsesComponent {
79
- cdr;
80
- suggestedResponses = [];
81
- disabled = false;
82
- isLastMessage = false;
83
- isConversationOwner = false;
84
- responseSelected = new EventEmitter();
85
- inputField;
86
- customInputValue = '';
87
- constructor(cdr) {
88
- this.cdr = cdr;
89
- console.log('🏗️ SuggestedResponsesComponent constructed');
90
- }
91
- ngOnInit() {
92
- console.log('🎬 SuggestedResponsesComponent.ngOnInit', {
93
- suggestedResponses: this.suggestedResponses,
94
- disabled: this.disabled,
95
- isLastMessage: this.isLastMessage,
96
- isConversationOwner: this.isConversationOwner,
97
- isVisible: this.isVisible,
98
- regularResponses: this.regularResponses,
99
- regularResponsesLength: this.regularResponses.length
100
- });
101
- }
102
- ngAfterViewInit() {
103
- console.log('👁️ SuggestedResponsesComponent.ngAfterViewInit - component rendered');
104
- }
105
- /**
106
- * Get all regular button responses (not input fields)
107
- */
108
- get regularResponses() {
109
- return this.suggestedResponses.filter(r => !r.allowInput);
110
- }
111
- /**
112
- * Get the input response option (if any)
113
- * Only supports one input option per message
114
- */
115
- get inputResponse() {
116
- return this.suggestedResponses.find(r => r.allowInput) || null;
117
- }
118
- /**
119
- * Check if component should be visible
120
- */
121
- get isVisible() {
122
- return this.isLastMessage && this.isConversationOwner && this.suggestedResponses.length > 0;
123
- }
124
- /**
125
- * Handle regular button click
126
- */
127
- onResponseClick(response) {
128
- console.log('🖱️ BUTTON CLICKED!', {
129
- response,
130
- disabled: this.disabled,
131
- willEmit: !this.disabled
132
- });
133
- if (!this.disabled) {
134
- console.log('📤 Emitting responseSelected event');
135
- this.responseSelected.emit({ text: response.text });
136
- console.log('✅ Event emitted');
137
- }
138
- else {
139
- console.warn('⚠️ Button is disabled, not emitting');
140
- }
141
- }
142
- /**
143
- * Handle input submission (Enter key or submit button click)
144
- */
145
- onInputSubmit() {
146
- const trimmedValue = this.customInputValue?.trim();
147
- if (!this.disabled && trimmedValue && this.inputResponse) {
148
- this.responseSelected.emit({
149
- text: this.inputResponse.text,
150
- customInput: trimmedValue
151
- });
152
- this.customInputValue = '';
153
- // Clear input field and trigger change detection
154
- Promise.resolve().then(() => {
155
- if (this.inputField) {
156
- this.inputField.nativeElement.value = '';
157
- }
158
- this.cdr.detectChanges();
159
- });
160
- }
161
- }
162
- /**
163
- * Handle Enter key in input field
164
- */
165
- onInputKeydown(event) {
166
- if (event.key === 'Enter' && !event.shiftKey) {
167
- event.preventDefault();
168
- this.onInputSubmit();
169
- }
170
- }
171
- /**
172
- * Track by function for ngFor to help Angular track items
173
- */
174
- trackByIndex(index) {
175
- return index;
176
- }
177
- static ɵfac = function SuggestedResponsesComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SuggestedResponsesComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
178
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SuggestedResponsesComponent, selectors: [["mj-suggested-responses"]], viewQuery: function SuggestedResponsesComponent_Query(rf, ctx) { if (rf & 1) {
179
- i0.ɵɵviewQuery(_c0, 5);
180
- } if (rf & 2) {
181
- let _t;
182
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.inputField = _t.first);
183
- } }, inputs: { suggestedResponses: "suggestedResponses", disabled: "disabled", isLastMessage: "isLastMessage", isConversationOwner: "isConversationOwner" }, outputs: { responseSelected: "responseSelected" }, standalone: false, decls: 1, vars: 1, consts: [["inputField", ""], [1, "suggested-responses"], [1, "suggested-responses-header"], [1, "fa", "fa-lightbulb"], [1, "suggested-responses-buttons"], ["type", "button", 1, "suggested-response-btn", 3, "disabled", "title"], [1, "suggested-response-input-container"], ["type", "button", 1, "suggested-response-btn", 3, "click", "disabled", "title"], [3, "class"], [1, "input-icon", 3, "class"], ["type", "text", "autocomplete", "off", 1, "suggested-response-input", 3, "ngModelChange", "keydown", "placeholder", "ngModel", "disabled"], ["title", "Submit", 1, "suggested-response-submit", 3, "click", "disabled"], [1, "fa", "fa-paper-plane"], [1, "input-icon"]], template: function SuggestedResponsesComponent_Template(rf, ctx) { if (rf & 1) {
184
- i0.ɵɵconditionalCreate(0, SuggestedResponsesComponent_Conditional_0_Template, 9, 1, "div", 1);
185
- } if (rf & 2) {
186
- i0.ɵɵconditional(ctx.isVisible ? 0 : -1);
187
- } }, dependencies: [i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgModel], styles: [".suggested-responses[_ngcontent-%COMP%] {\n margin-top: 16px;\n padding: 14px 16px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n border: 1px solid var(--mj-border-default);\n box-shadow: 0 2px 4px color-mix(in srgb, var(--mj-text-primary) 4%, transparent);\n}\n\n.suggested-responses-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 10px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.suggested-responses-header[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-status-warning);\n}\n\n.suggested-responses-buttons[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n}\n\n.suggested-response-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n color: var(--mj-text-primary);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n white-space: nowrap;\n box-shadow: 0 1px 2px color-mix(in srgb, var(--mj-text-primary) 5%, transparent);\n}\n\n.suggested-response-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.suggested-response-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n\n.suggested-response-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n background: var(--mj-bg-surface-sunken);\n}\n\n.suggested-response-input-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n flex: 1;\n min-width: 250px;\n max-width: 500px;\n background: var(--mj-bg-surface-card);\n border: 2px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 6px 8px 6px 12px;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px color-mix(in srgb, var(--mj-text-primary) 5%, transparent);\n}\n\n.suggested-response-input-container[_ngcontent-%COMP%]:focus-within {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n}\n\n.suggested-response-input-container[_ngcontent-%COMP%] .input-icon[_ngcontent-%COMP%] {\n font-size: 16px;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n.suggested-response-input[_ngcontent-%COMP%] {\n flex: 1;\n border: none;\n outline: none;\n font-size: 14px;\n padding: 6px 8px;\n background: transparent;\n color: var(--mj-text-primary);\n}\n\n.suggested-response-input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-muted);\n font-style: italic;\n opacity: 1;\n}\n\n.suggested-response-input[_ngcontent-%COMP%]:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.suggested-response-submit[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 8px 14px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n box-shadow: 0 1px 3px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.suggested-response-submit[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.suggested-response-submit[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n box-shadow: 0 2px 6px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.suggested-response-submit[_ngcontent-%COMP%]:active:not(:disabled) {\n box-shadow: 0 1px 3px color-mix(in srgb, var(--mj-brand-primary) 50%, transparent);\n}\n\n.suggested-response-submit[_ngcontent-%COMP%]:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n background: var(--mj-text-muted);\n box-shadow: none;\n}\n\n\n\n@media (max-width: 768px) {\n .suggested-responses-buttons[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n }\n\n .suggested-response-btn[_ngcontent-%COMP%] {\n justify-content: center;\n }\n\n .suggested-response-input-container[_ngcontent-%COMP%] {\n max-width: 100%;\n }\n}"] });
188
- }
189
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SuggestedResponsesComponent, [{
190
- type: Component,
191
- args: [{ standalone: false, selector: 'mj-suggested-responses', template: "@if (isVisible) {\n <div class=\"suggested-responses\">\n <div class=\"suggested-responses-header\">\n <i class=\"fa fa-lightbulb\"></i>\n <span>Quick replies</span>\n </div>\n <div class=\"suggested-responses-buttons\">\n <!-- TEST: Native onclick\n <button type=\"button\" onclick=\"alert('Native onclick works!');\" style=\"background: red; color: white;\">TEST</button>\n -->\n <!-- Regular button responses (allowInput: false) -->\n @for (response of regularResponses; track $index) {\n <button\n type=\"button\"\n class=\"suggested-response-btn\"\n [disabled]=\"disabled\"\n (click)=\"onResponseClick(response)\"\n [title]=\"response.text\">\n @if (response.iconClass) {\n <i [class]=\"'fas ' + response.iconClass\"></i>\n }\n <span>{{ response.text }}</span>\n </button>\n }\n <!-- Input option (allowInput: true) -->\n @if (inputResponse) {\n <div class=\"suggested-response-input-container\">\n @if (inputResponse.iconClass) {\n <i [class]=\"'fas ' + inputResponse.iconClass\" class=\"input-icon\"></i>\n }\n <input\n type=\"text\"\n class=\"suggested-response-input\"\n [placeholder]=\"inputResponse.text\"\n [(ngModel)]=\"customInputValue\"\n [disabled]=\"disabled\"\n (keydown)=\"onInputKeydown($event)\"\n #inputField\n autocomplete=\"off\">\n <button\n class=\"suggested-response-submit\"\n [disabled]=\"disabled || !customInputValue || !customInputValue.trim()\"\n (click)=\"onInputSubmit()\"\n title=\"Submit\">\n <i class=\"fa fa-paper-plane\"></i>\n </button>\n </div>\n }\n </div>\n </div>\n}\n", styles: [".suggested-responses {\n margin-top: 16px;\n padding: 14px 16px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n border: 1px solid var(--mj-border-default);\n box-shadow: 0 2px 4px color-mix(in srgb, var(--mj-text-primary) 4%, transparent);\n}\n\n.suggested-responses-header {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 10px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.suggested-responses-header i {\n font-size: 13px;\n color: var(--mj-status-warning);\n}\n\n.suggested-responses-buttons {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n}\n\n.suggested-response-btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 10px 16px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n color: var(--mj-text-primary);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n white-space: nowrap;\n box-shadow: 0 1px 2px color-mix(in srgb, var(--mj-text-primary) 5%, transparent);\n}\n\n.suggested-response-btn i {\n font-size: 14px;\n}\n\n.suggested-response-btn:hover:not(:disabled) {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n\n.suggested-response-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n background: var(--mj-bg-surface-sunken);\n}\n\n.suggested-response-input-container {\n display: flex;\n align-items: center;\n gap: 10px;\n flex: 1;\n min-width: 250px;\n max-width: 500px;\n background: var(--mj-bg-surface-card);\n border: 2px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 6px 8px 6px 12px;\n transition: all 0.2s ease;\n box-shadow: 0 1px 2px color-mix(in srgb, var(--mj-text-primary) 5%, transparent);\n}\n\n.suggested-response-input-container:focus-within {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n}\n\n.suggested-response-input-container .input-icon {\n font-size: 16px;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n.suggested-response-input {\n flex: 1;\n border: none;\n outline: none;\n font-size: 14px;\n padding: 6px 8px;\n background: transparent;\n color: var(--mj-text-primary);\n}\n\n.suggested-response-input::placeholder {\n color: var(--mj-text-muted);\n font-style: italic;\n opacity: 1;\n}\n\n.suggested-response-input:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.suggested-response-submit {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 8px 14px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n box-shadow: 0 1px 3px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.suggested-response-submit i {\n font-size: 14px;\n}\n\n.suggested-response-submit:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n box-shadow: 0 2px 6px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.suggested-response-submit:active:not(:disabled) {\n box-shadow: 0 1px 3px color-mix(in srgb, var(--mj-brand-primary) 50%, transparent);\n}\n\n.suggested-response-submit:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n background: var(--mj-text-muted);\n box-shadow: none;\n}\n\n/* Responsive adjustments */\n@media (max-width: 768px) {\n .suggested-responses-buttons {\n flex-direction: column;\n align-items: stretch;\n }\n\n .suggested-response-btn {\n justify-content: center;\n }\n\n .suggested-response-input-container {\n max-width: 100%;\n }\n}\n"] }]
192
- }], () => [{ type: i0.ChangeDetectorRef }], { suggestedResponses: [{
193
- type: Input
194
- }], disabled: [{
195
- type: Input
196
- }], isLastMessage: [{
197
- type: Input
198
- }], isConversationOwner: [{
199
- type: Input
200
- }], responseSelected: [{
201
- type: Output
202
- }], inputField: [{
203
- type: ViewChild,
204
- args: ['inputField']
205
- }] }); })();
206
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SuggestedResponsesComponent, { className: "SuggestedResponsesComponent", filePath: "src/lib/components/message/suggested-responses.component.ts", lineNumber: 14 }); })();
207
- //# sourceMappingURL=suggested-responses.component.js.map