@open-mercato/ai-assistant 0.6.3-develop.3901.1.ddad60693a → 0.6.3

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 (80) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-06-deep-link.spec.js +87 -0
  3. package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-06-deep-link.spec.js.map +7 -0
  4. package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-07-owner-label.spec.js +119 -0
  5. package/dist/modules/ai_assistant/__integration__/TC-AI-sharing-07-owner-label.spec.js.map +7 -0
  6. package/dist/modules/ai_assistant/acl.js +1 -0
  7. package/dist/modules/ai_assistant/acl.js.map +2 -2
  8. package/dist/modules/ai_assistant/api/ai/chat/route.js +3 -0
  9. package/dist/modules/ai_assistant/api/ai/chat/route.js.map +2 -2
  10. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/[userId]/route.js +128 -0
  11. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/[userId]/route.js.map +7 -0
  12. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/route.js +271 -0
  13. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/route.js.map +7 -0
  14. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/route.js +9 -1
  15. package/dist/modules/ai_assistant/api/ai/conversations/[conversationId]/route.js.map +2 -2
  16. package/dist/modules/ai_assistant/api/ai/conversations/route.js +4 -1
  17. package/dist/modules/ai_assistant/api/ai/conversations/route.js.map +2 -2
  18. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +5 -1
  19. package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +2 -2
  20. package/dist/modules/ai_assistant/components/ConversationShareButton.js +5 -0
  21. package/dist/modules/ai_assistant/components/ConversationShareButton.js.map +7 -0
  22. package/dist/modules/ai_assistant/components/ConversationShareDialog.js +5 -0
  23. package/dist/modules/ai_assistant/components/ConversationShareDialog.js.map +7 -0
  24. package/dist/modules/ai_assistant/data/entities.js +3 -0
  25. package/dist/modules/ai_assistant/data/entities.js.map +2 -2
  26. package/dist/modules/ai_assistant/data/repositories/AiChatConversationRepository.js +235 -5
  27. package/dist/modules/ai_assistant/data/repositories/AiChatConversationRepository.js.map +2 -2
  28. package/dist/modules/ai_assistant/events.js +14 -0
  29. package/dist/modules/ai_assistant/events.js.map +2 -2
  30. package/dist/modules/ai_assistant/i18n/de.json +17 -0
  31. package/dist/modules/ai_assistant/i18n/en.json +17 -0
  32. package/dist/modules/ai_assistant/i18n/es.json +17 -0
  33. package/dist/modules/ai_assistant/i18n/pl.json +17 -0
  34. package/dist/modules/ai_assistant/lib/conversation-storage.js +12 -3
  35. package/dist/modules/ai_assistant/lib/conversation-storage.js.map +2 -2
  36. package/dist/modules/ai_assistant/migrations/Migration20260522120000_ai_assistant.js +15 -0
  37. package/dist/modules/ai_assistant/migrations/Migration20260522120000_ai_assistant.js.map +7 -0
  38. package/dist/modules/ai_assistant/notifications.client.js +30 -0
  39. package/dist/modules/ai_assistant/notifications.client.js.map +7 -0
  40. package/dist/modules/ai_assistant/notifications.js +27 -0
  41. package/dist/modules/ai_assistant/notifications.js.map +7 -0
  42. package/dist/modules/ai_assistant/setup.js +2 -1
  43. package/dist/modules/ai_assistant/setup.js.map +2 -2
  44. package/dist/modules/ai_assistant/subscribers/conversation-shared-notify.js +59 -0
  45. package/dist/modules/ai_assistant/subscribers/conversation-shared-notify.js.map +7 -0
  46. package/dist/modules/ai_assistant/widgets/notifications/ConversationSharedRenderer.js +123 -0
  47. package/dist/modules/ai_assistant/widgets/notifications/ConversationSharedRenderer.js.map +7 -0
  48. package/generated/entities/ai_chat_conversation_participant/index.ts +1 -0
  49. package/generated/entity-fields-registry.ts +1 -0
  50. package/package.json +7 -8
  51. package/src/modules/ai_assistant/__integration__/TC-AI-sharing-06-deep-link.spec.ts +117 -0
  52. package/src/modules/ai_assistant/__integration__/TC-AI-sharing-07-owner-label.spec.ts +159 -0
  53. package/src/modules/ai_assistant/__tests__/integration/ai-chat-sharing.test.ts +406 -0
  54. package/src/modules/ai_assistant/acl.ts +1 -0
  55. package/src/modules/ai_assistant/api/ai/chat/route.ts +3 -0
  56. package/src/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/[userId]/route.ts +149 -0
  57. package/src/modules/ai_assistant/api/ai/conversations/[conversationId]/participants/route.ts +314 -0
  58. package/src/modules/ai_assistant/api/ai/conversations/[conversationId]/route.ts +9 -1
  59. package/src/modules/ai_assistant/api/ai/conversations/route.ts +4 -1
  60. package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +4 -0
  61. package/src/modules/ai_assistant/components/ConversationShareButton.tsx +1 -0
  62. package/src/modules/ai_assistant/components/ConversationShareDialog.tsx +1 -0
  63. package/src/modules/ai_assistant/data/entities.ts +4 -0
  64. package/src/modules/ai_assistant/data/repositories/AiChatConversationRepository.ts +270 -7
  65. package/src/modules/ai_assistant/data/repositories/__tests__/AiChatConversationRepository.test.ts +297 -3
  66. package/src/modules/ai_assistant/events.ts +31 -0
  67. package/src/modules/ai_assistant/i18n/__tests__/conversation-share-translations.test.ts +59 -0
  68. package/src/modules/ai_assistant/i18n/de.json +17 -0
  69. package/src/modules/ai_assistant/i18n/en.json +17 -0
  70. package/src/modules/ai_assistant/i18n/es.json +17 -0
  71. package/src/modules/ai_assistant/i18n/pl.json +17 -0
  72. package/src/modules/ai_assistant/lib/conversation-storage.ts +22 -1
  73. package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +25 -0
  74. package/src/modules/ai_assistant/migrations/Migration20260522120000_ai_assistant.ts +15 -0
  75. package/src/modules/ai_assistant/notifications.client.ts +29 -0
  76. package/src/modules/ai_assistant/notifications.ts +25 -0
  77. package/src/modules/ai_assistant/setup.ts +2 -1
  78. package/src/modules/ai_assistant/subscribers/__tests__/conversation-shared-notify.test.ts +116 -0
  79. package/src/modules/ai_assistant/subscribers/conversation-shared-notify.ts +78 -0
  80. package/src/modules/ai_assistant/widgets/notifications/ConversationSharedRenderer.tsx +121 -0
@@ -0,0 +1,78 @@
1
+ import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'
2
+ import { buildNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'
3
+ import { defaultLocale } from '@open-mercato/shared/lib/i18n/config'
4
+ import { loadDictionary } from '@open-mercato/shared/lib/i18n/server'
5
+ import { createTranslator } from '@open-mercato/shared/lib/i18n/translate'
6
+ import { notificationTypes } from '../notifications'
7
+ import type { AiConversationSharedPayload } from '../events'
8
+
9
+ export const metadata = {
10
+ event: 'ai_assistant.conversation.shared',
11
+ persistent: true,
12
+ id: 'ai_assistant:conversation-shared-notify',
13
+ }
14
+
15
+ type ResolverContext = {
16
+ resolve: <T = unknown>(name: string) => T
17
+ container?: { resolve<T = unknown>(name: string): T }
18
+ }
19
+
20
+ async function resolveDefaultLocaleStrings(
21
+ titleKey: string | undefined,
22
+ bodyKey: string | undefined,
23
+ ): Promise<{ title: string | undefined; body: string | undefined }> {
24
+ if (!titleKey && !bodyKey) return { title: titleKey, body: bodyKey }
25
+ try {
26
+ const dict = await loadDictionary(defaultLocale)
27
+ const t = createTranslator(dict)
28
+ return {
29
+ title: titleKey ? t(titleKey) : titleKey,
30
+ body: bodyKey ? t(bodyKey) : bodyKey,
31
+ }
32
+ } catch {
33
+ return { title: titleKey, body: bodyKey }
34
+ }
35
+ }
36
+
37
+ export default async function handleConversationShared(
38
+ payload: AiConversationSharedPayload,
39
+ ctx: ResolverContext,
40
+ ): Promise<void> {
41
+ if (!payload?.participantUserId || !payload.tenantId) return
42
+
43
+ const typeDef = notificationTypes.find((t) => t.type === 'ai_assistant.conversation_shared')
44
+ if (!typeDef) return
45
+
46
+ const container = ctx.container ?? { resolve: ctx.resolve }
47
+ let notificationService: ReturnType<typeof resolveNotificationService> | null
48
+ try {
49
+ notificationService = resolveNotificationService(container)
50
+ } catch {
51
+ return
52
+ }
53
+
54
+ const notificationInput = buildNotificationFromType(typeDef, {
55
+ recipientUserId: payload.participantUserId,
56
+ bodyVariables: {},
57
+ sourceEntityType: 'ai_assistant:ai_chat_conversation',
58
+ sourceEntityId: payload.conversationId,
59
+ linkHref: `/backend?openAiConversation=${payload.conversationId}`,
60
+ })
61
+
62
+ // Persist a human-readable fallback alongside the i18n keys so consumers
63
+ // that do not run the client renderer (email digests, exports) display a
64
+ // resolved string instead of the raw key. The client renderer continues
65
+ // to re-translate via titleKey/bodyKey for the viewer's locale.
66
+ const resolved = await resolveDefaultLocaleStrings(typeDef.titleKey, typeDef.bodyKey)
67
+ if (resolved.title !== undefined) notificationInput.title = resolved.title
68
+ if (resolved.body !== undefined) notificationInput.body = resolved.body
69
+
70
+ try {
71
+ await notificationService.create(notificationInput, {
72
+ tenantId: payload.tenantId,
73
+ organizationId: payload.organizationId ?? null,
74
+ })
75
+ } catch (err) {
76
+ console.warn('[ai_assistant.conversationSharedNotify] create failed', err)
77
+ }
78
+ }
@@ -0,0 +1,121 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Share2, ExternalLink, X } from 'lucide-react'
5
+ import { useRouter } from 'next/navigation'
6
+ import { Button } from '@open-mercato/ui/primitives/button'
7
+ import { IconButton } from '@open-mercato/ui/primitives/icon-button'
8
+ import { cn } from '@open-mercato/shared/lib/utils'
9
+ import { formatRelativeTime } from '@open-mercato/shared/lib/time'
10
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
11
+ import type { NotificationRendererProps } from '@open-mercato/shared/modules/notifications/types'
12
+
13
+ export function ConversationSharedRenderer({
14
+ notification,
15
+ onAction,
16
+ onDismiss,
17
+ actions = [],
18
+ }: NotificationRendererProps) {
19
+ const t = useT()
20
+ const router = useRouter()
21
+ const [executing, setExecuting] = React.useState(false)
22
+ const isUnread = notification.status === 'unread'
23
+ const viewAction = actions.find((a) => a.id === 'view') ?? actions[0] ?? null
24
+
25
+ const handleView = async () => {
26
+ setExecuting(true)
27
+ try {
28
+ if (viewAction) {
29
+ await onAction(viewAction.id)
30
+ }
31
+ if (notification.linkHref) router.push(notification.linkHref)
32
+ } finally {
33
+ setExecuting(false)
34
+ }
35
+ }
36
+
37
+ const timeAgo = formatRelativeTime(notification.createdAt, { translate: t }) ?? ''
38
+
39
+ return (
40
+ <div
41
+ className={cn(
42
+ 'group relative flex gap-4 items-start rounded-xl p-3 transition-colors hover:bg-muted/40 cursor-pointer',
43
+ isUnread && 'bg-muted/20',
44
+ )}
45
+ onClick={executing ? undefined : handleView}
46
+ onKeyDown={(e) => {
47
+ if (executing) return
48
+ if (e.key === 'Enter' || e.key === ' ') {
49
+ e.preventDefault()
50
+ handleView()
51
+ }
52
+ }}
53
+ role="button"
54
+ tabIndex={0}
55
+ >
56
+ <div className="relative shrink-0 flex size-10 items-center justify-center rounded-full bg-status-info-bg">
57
+ <Share2 className="size-5 text-status-info-icon" aria-hidden="true" />
58
+ {isUnread ? (
59
+ <span
60
+ className="absolute -right-1 -top-1 size-3 rounded-full bg-accent-indigo ring-2 ring-background"
61
+ aria-hidden="true"
62
+ />
63
+ ) : null}
64
+ </div>
65
+
66
+ <div className="flex min-w-0 flex-1 flex-col gap-1">
67
+ <p className="truncate text-sm font-medium leading-5 tracking-tight text-foreground">
68
+ {notification.title}
69
+ </p>
70
+
71
+ <div className="text-xs leading-4 text-muted-foreground">
72
+ {timeAgo ? <span className="whitespace-nowrap">{timeAgo}</span> : null}
73
+ </div>
74
+
75
+ <div className="mt-2 flex items-center gap-2">
76
+ <Button
77
+ type="button"
78
+ size="sm"
79
+ className="h-8 rounded-md px-2.5 bg-accent-indigo text-accent-indigo-foreground hover:bg-accent-indigo/90"
80
+ onClick={(e) => {
81
+ e.stopPropagation()
82
+ handleView()
83
+ }}
84
+ disabled={executing || (!viewAction && !notification.linkHref)}
85
+ >
86
+ <ExternalLink className="size-3.5" aria-hidden="true" />
87
+ {t('ai_assistant.notifications.conversation_shared.view_button', 'View Conversation')}
88
+ </Button>
89
+ <Button
90
+ type="button"
91
+ variant="outline"
92
+ size="sm"
93
+ className="h-8 rounded-md px-2.5"
94
+ onClick={(e) => {
95
+ e.stopPropagation()
96
+ onDismiss()
97
+ }}
98
+ >
99
+ {t('notifications.actions.dismiss', 'Dismiss')}
100
+ </Button>
101
+ </div>
102
+ </div>
103
+
104
+ <IconButton
105
+ type="button"
106
+ variant="ghost"
107
+ size="xs"
108
+ className="absolute right-2 top-2 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100"
109
+ onClick={(e) => {
110
+ e.stopPropagation()
111
+ onDismiss()
112
+ }}
113
+ aria-label={t('notifications.actions.dismiss', 'Dismiss')}
114
+ >
115
+ <X className="size-3.5" />
116
+ </IconButton>
117
+ </div>
118
+ )
119
+ }
120
+
121
+ export default ConversationSharedRenderer