@nextsparkjs/theme-default 0.1.0-beta.1

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 (333) hide show
  1. package/about/business.md +49 -0
  2. package/about/features.json +302 -0
  3. package/about/team.md +79 -0
  4. package/api/ai/chat/stream/route.ts +212 -0
  5. package/api/ai/orchestrator/route.ts +226 -0
  6. package/api/ai/single-agent/route.ts +291 -0
  7. package/api/ai/usage/route.ts +122 -0
  8. package/blocks/benefits/component.tsx +100 -0
  9. package/blocks/benefits/config.ts +11 -0
  10. package/blocks/benefits/examples.ts +85 -0
  11. package/blocks/benefits/fields.ts +156 -0
  12. package/blocks/benefits/schema.ts +33 -0
  13. package/blocks/cta-section/component.tsx +100 -0
  14. package/blocks/cta-section/config.ts +11 -0
  15. package/blocks/cta-section/examples.ts +41 -0
  16. package/blocks/cta-section/fields.ts +89 -0
  17. package/blocks/cta-section/index.ts +6 -0
  18. package/blocks/cta-section/schema.ts +32 -0
  19. package/blocks/cta-section/thumbnail.png +1 -0
  20. package/blocks/faq-accordion/component.tsx +156 -0
  21. package/blocks/faq-accordion/config.ts +11 -0
  22. package/blocks/faq-accordion/examples.ts +77 -0
  23. package/blocks/faq-accordion/fields.ts +119 -0
  24. package/blocks/faq-accordion/index.ts +6 -0
  25. package/blocks/faq-accordion/schema.ts +45 -0
  26. package/blocks/features-grid/component.tsx +112 -0
  27. package/blocks/features-grid/config.ts +11 -0
  28. package/blocks/features-grid/examples.ts +63 -0
  29. package/blocks/features-grid/fields.ts +97 -0
  30. package/blocks/features-grid/index.ts +6 -0
  31. package/blocks/features-grid/schema.ts +40 -0
  32. package/blocks/features-grid/thumbnail.png +1 -0
  33. package/blocks/hero/component.tsx +100 -0
  34. package/blocks/hero/config.ts +11 -0
  35. package/blocks/hero/examples.ts +35 -0
  36. package/blocks/hero/fields.ts +60 -0
  37. package/blocks/hero/index.ts +6 -0
  38. package/blocks/hero/schema.ts +32 -0
  39. package/blocks/hero/thumbnail.png +1 -0
  40. package/blocks/hero/thumbnail.png.txt +6 -0
  41. package/blocks/hero-with-form/component.tsx +232 -0
  42. package/blocks/hero-with-form/config.ts +11 -0
  43. package/blocks/hero-with-form/examples.ts +16 -0
  44. package/blocks/hero-with-form/fields.ts +207 -0
  45. package/blocks/hero-with-form/index.ts +6 -0
  46. package/blocks/hero-with-form/schema.ts +54 -0
  47. package/blocks/jumbotron/component.tsx +136 -0
  48. package/blocks/jumbotron/config.ts +11 -0
  49. package/blocks/jumbotron/examples.ts +36 -0
  50. package/blocks/jumbotron/fields.ts +202 -0
  51. package/blocks/jumbotron/index.ts +6 -0
  52. package/blocks/jumbotron/schema.ts +55 -0
  53. package/blocks/logo-cloud/component.tsx +154 -0
  54. package/blocks/logo-cloud/config.ts +11 -0
  55. package/blocks/logo-cloud/examples.ts +34 -0
  56. package/blocks/logo-cloud/fields.ts +133 -0
  57. package/blocks/logo-cloud/index.ts +6 -0
  58. package/blocks/logo-cloud/schema.ts +46 -0
  59. package/blocks/post-content/component.tsx +197 -0
  60. package/blocks/post-content/config.ts +11 -0
  61. package/blocks/post-content/examples.ts +33 -0
  62. package/blocks/post-content/fields.ts +165 -0
  63. package/blocks/post-content/index.ts +4 -0
  64. package/blocks/post-content/schema.ts +46 -0
  65. package/blocks/pricing-table/component.tsx +154 -0
  66. package/blocks/pricing-table/config.ts +11 -0
  67. package/blocks/pricing-table/examples.ts +96 -0
  68. package/blocks/pricing-table/fields.ts +161 -0
  69. package/blocks/pricing-table/index.ts +4 -0
  70. package/blocks/pricing-table/schema.ts +50 -0
  71. package/blocks/split-content/component.tsx +135 -0
  72. package/blocks/split-content/config.ts +11 -0
  73. package/blocks/split-content/examples.ts +38 -0
  74. package/blocks/split-content/fields.ts +198 -0
  75. package/blocks/split-content/index.ts +6 -0
  76. package/blocks/split-content/schema.ts +67 -0
  77. package/blocks/stats-counter/component.tsx +124 -0
  78. package/blocks/stats-counter/config.ts +11 -0
  79. package/blocks/stats-counter/examples.ts +61 -0
  80. package/blocks/stats-counter/fields.ts +134 -0
  81. package/blocks/stats-counter/index.ts +6 -0
  82. package/blocks/stats-counter/schema.ts +47 -0
  83. package/blocks/testimonials/component.tsx +114 -0
  84. package/blocks/testimonials/config.ts +11 -0
  85. package/blocks/testimonials/examples.ts +65 -0
  86. package/blocks/testimonials/fields.ts +105 -0
  87. package/blocks/testimonials/index.ts +6 -0
  88. package/blocks/testimonials/schema.ts +41 -0
  89. package/blocks/testimonials/thumbnail.png +1 -0
  90. package/blocks/text-content/component.tsx +97 -0
  91. package/blocks/text-content/config.ts +11 -0
  92. package/blocks/text-content/examples.ts +30 -0
  93. package/blocks/text-content/fields.ts +88 -0
  94. package/blocks/text-content/index.ts +6 -0
  95. package/blocks/text-content/schema.ts +30 -0
  96. package/blocks/text-content/thumbnail.png +1 -0
  97. package/blocks/timeline/component.tsx +267 -0
  98. package/blocks/timeline/config.ts +11 -0
  99. package/blocks/timeline/examples.ts +68 -0
  100. package/blocks/timeline/fields.ts +147 -0
  101. package/blocks/timeline/index.ts +6 -0
  102. package/blocks/timeline/schema.ts +49 -0
  103. package/blocks/video-hero/component.tsx +270 -0
  104. package/blocks/video-hero/config.ts +11 -0
  105. package/blocks/video-hero/examples.ts +24 -0
  106. package/blocks/video-hero/fields.ts +98 -0
  107. package/blocks/video-hero/index.ts +6 -0
  108. package/blocks/video-hero/schema.ts +39 -0
  109. package/components/ai-chat/ChatPanel.tsx +575 -0
  110. package/components/ai-chat/ConversationItem.tsx +266 -0
  111. package/components/ai-chat/ConversationSidebar.tsx +99 -0
  112. package/components/ai-chat/MarkdownRenderer.tsx +15 -0
  113. package/components/ai-chat/Message.tsx +42 -0
  114. package/components/ai-chat/MessageInput.tsx +49 -0
  115. package/components/ai-chat/MessageList.tsx +46 -0
  116. package/components/ai-chat/TypingIndicator.tsx +11 -0
  117. package/config/app.config.ts +367 -0
  118. package/config/billing.config.ts +349 -0
  119. package/config/dashboard.config.ts +506 -0
  120. package/config/dev.config.ts +104 -0
  121. package/config/features.config.ts +203 -0
  122. package/config/flows.config.ts +129 -0
  123. package/config/permissions.config.ts +245 -0
  124. package/config/theme.config.ts +74 -0
  125. package/docs/01-overview/01-introduction.md +335 -0
  126. package/docs/01-overview/02-customization.md +671 -0
  127. package/docs/02-features/01-components.md +155 -0
  128. package/docs/02-features/02-styling.md +139 -0
  129. package/docs/02-features/03-tasks-entity.md +407 -0
  130. package/docs/03-ai/01-overview.md +211 -0
  131. package/docs/03-ai/02-customization.md +436 -0
  132. package/entities/customers/customers.config.ts +75 -0
  133. package/entities/customers/customers.fields.ts +165 -0
  134. package/entities/customers/customers.service.ts +516 -0
  135. package/entities/customers/customers.types.ts +83 -0
  136. package/entities/customers/messages/en.json +66 -0
  137. package/entities/customers/messages/es.json +66 -0
  138. package/entities/customers/migrations/001_customers_table.sql +102 -0
  139. package/entities/customers/migrations/002_customers_metas.sql +92 -0
  140. package/entities/pages/messages/en.json +41 -0
  141. package/entities/pages/messages/es.json +41 -0
  142. package/entities/pages/migrations/001_pages_table.sql +112 -0
  143. package/entities/pages/migrations/002_pages_metas.sql +56 -0
  144. package/entities/pages/migrations/003_add_status.sql +50 -0
  145. package/entities/pages/pages-management.service.ts +610 -0
  146. package/entities/pages/pages.config.ts +94 -0
  147. package/entities/pages/pages.fields.ts +101 -0
  148. package/entities/pages/pages.service.ts +290 -0
  149. package/entities/pages/pages.types.ts +124 -0
  150. package/entities/posts/components/post-header.tsx +97 -0
  151. package/entities/posts/messages/en.json +55 -0
  152. package/entities/posts/messages/es.json +55 -0
  153. package/entities/posts/migrations/001_posts_table.sql +115 -0
  154. package/entities/posts/migrations/003_add_status.sql +44 -0
  155. package/entities/posts/migrations/004_entity_taxonomy_relations.sql +129 -0
  156. package/entities/posts/migrations/006_posts_metas.sql +56 -0
  157. package/entities/posts/posts.config.ts +101 -0
  158. package/entities/posts/posts.fields.ts +116 -0
  159. package/entities/posts/posts.service.ts +376 -0
  160. package/entities/posts/posts.types.ts +74 -0
  161. package/entities/tasks/messages/en.json +204 -0
  162. package/entities/tasks/messages/es.json +204 -0
  163. package/entities/tasks/migrations/001_tasks_table.sql +105 -0
  164. package/entities/tasks/migrations/002_task_metas.sql +85 -0
  165. package/entities/tasks/migrations/sample_data.json +77 -0
  166. package/entities/tasks/tasks.config.ts +79 -0
  167. package/entities/tasks/tasks.fields.ts +196 -0
  168. package/entities/tasks/tasks.service.ts +541 -0
  169. package/entities/tasks/tasks.types.ts +56 -0
  170. package/lib/hooks/useAiChat.ts +114 -0
  171. package/lib/hooks/useConversations.ts +376 -0
  172. package/lib/hooks/useOrchestratorChat.ts +122 -0
  173. package/lib/hooks/usePersistentChat.ts +315 -0
  174. package/lib/hooks/useStreamingChat.ts +127 -0
  175. package/lib/hooks/useTokenUsage.ts +63 -0
  176. package/lib/langchain/agents/customer-assistant.md +69 -0
  177. package/lib/langchain/agents/index.ts +61 -0
  178. package/lib/langchain/agents/orchestrator.md +59 -0
  179. package/lib/langchain/agents/page-assistant.md +85 -0
  180. package/lib/langchain/agents/single-agent.md +46 -0
  181. package/lib/langchain/agents/task-assistant.md +55 -0
  182. package/lib/langchain/config.ts +45 -0
  183. package/lib/langchain/handlers/customer-handler.ts +338 -0
  184. package/lib/langchain/handlers/page-handler.ts +232 -0
  185. package/lib/langchain/handlers/task-handler.ts +323 -0
  186. package/lib/langchain/langchain.config.ts +223 -0
  187. package/lib/langchain/observability.config.ts +30 -0
  188. package/lib/langchain/orchestrator.ts +562 -0
  189. package/lib/langchain/tools/customers.ts +176 -0
  190. package/lib/langchain/tools/index.ts +10 -0
  191. package/lib/langchain/tools/orchestrator.ts +92 -0
  192. package/lib/langchain/tools/pages.ts +289 -0
  193. package/lib/langchain/tools/tasks.ts +167 -0
  194. package/lib/scheduled-actions/billing.ts +149 -0
  195. package/lib/scheduled-actions/index.ts +170 -0
  196. package/lib/scheduled-actions/webhook.ts +231 -0
  197. package/lib/selectors.ts +197 -0
  198. package/messages/de/admin.json +219 -0
  199. package/messages/de/aiUsage.json +36 -0
  200. package/messages/de/buttons.json +19 -0
  201. package/messages/de/categories.json +35 -0
  202. package/messages/de/common.json +16 -0
  203. package/messages/de/dev.json +101 -0
  204. package/messages/de/docs.json +27 -0
  205. package/messages/de/entities.json +7 -0
  206. package/messages/de/features.json +119 -0
  207. package/messages/de/footer.json +22 -0
  208. package/messages/de/home.json +57 -0
  209. package/messages/de/index.ts +39 -0
  210. package/messages/de/mobileNav.json +13 -0
  211. package/messages/de/navigation.json +8 -0
  212. package/messages/de/observability.json +74 -0
  213. package/messages/de/posts.json +54 -0
  214. package/messages/de/pricing.json +102 -0
  215. package/messages/de/support.json +9 -0
  216. package/messages/de/teams.json +8 -0
  217. package/messages/en/admin.json +219 -0
  218. package/messages/en/aiUsage.json +36 -0
  219. package/messages/en/buttons.json +19 -0
  220. package/messages/en/categories.json +35 -0
  221. package/messages/en/common.json +16 -0
  222. package/messages/en/dev.json +106 -0
  223. package/messages/en/docs.json +27 -0
  224. package/messages/en/entities.json +7 -0
  225. package/messages/en/features.json +119 -0
  226. package/messages/en/footer.json +22 -0
  227. package/messages/en/home.json +57 -0
  228. package/messages/en/index.ts +39 -0
  229. package/messages/en/mobileNav.json +13 -0
  230. package/messages/en/navigation.json +8 -0
  231. package/messages/en/observability.json +74 -0
  232. package/messages/en/posts.json +54 -0
  233. package/messages/en/pricing.json +102 -0
  234. package/messages/en/support.json +9 -0
  235. package/messages/en/teams.json +8 -0
  236. package/messages/es/admin.json +219 -0
  237. package/messages/es/aiUsage.json +36 -0
  238. package/messages/es/buttons.json +19 -0
  239. package/messages/es/categories.json +35 -0
  240. package/messages/es/common.json +16 -0
  241. package/messages/es/dev.json +101 -0
  242. package/messages/es/docs.json +27 -0
  243. package/messages/es/entities.json +7 -0
  244. package/messages/es/features.json +119 -0
  245. package/messages/es/footer.json +22 -0
  246. package/messages/es/home.json +57 -0
  247. package/messages/es/index.ts +39 -0
  248. package/messages/es/mobileNav.json +13 -0
  249. package/messages/es/navigation.json +8 -0
  250. package/messages/es/observability.json +74 -0
  251. package/messages/es/posts.json +54 -0
  252. package/messages/es/pricing.json +102 -0
  253. package/messages/es/support.json +9 -0
  254. package/messages/es/teams.json +8 -0
  255. package/messages/fr/admin.json +219 -0
  256. package/messages/fr/aiUsage.json +36 -0
  257. package/messages/fr/buttons.json +19 -0
  258. package/messages/fr/categories.json +35 -0
  259. package/messages/fr/common.json +16 -0
  260. package/messages/fr/dev.json +101 -0
  261. package/messages/fr/docs.json +27 -0
  262. package/messages/fr/entities.json +7 -0
  263. package/messages/fr/features.json +119 -0
  264. package/messages/fr/footer.json +22 -0
  265. package/messages/fr/home.json +57 -0
  266. package/messages/fr/index.ts +39 -0
  267. package/messages/fr/mobileNav.json +13 -0
  268. package/messages/fr/navigation.json +8 -0
  269. package/messages/fr/observability.json +74 -0
  270. package/messages/fr/posts.json +54 -0
  271. package/messages/fr/pricing.json +102 -0
  272. package/messages/fr/support.json +9 -0
  273. package/messages/fr/teams.json +8 -0
  274. package/messages/it/admin.json +219 -0
  275. package/messages/it/aiUsage.json +36 -0
  276. package/messages/it/buttons.json +19 -0
  277. package/messages/it/categories.json +35 -0
  278. package/messages/it/common.json +16 -0
  279. package/messages/it/dev.json +101 -0
  280. package/messages/it/docs.json +27 -0
  281. package/messages/it/entities.json +7 -0
  282. package/messages/it/features.json +119 -0
  283. package/messages/it/footer.json +22 -0
  284. package/messages/it/home.json +57 -0
  285. package/messages/it/index.ts +39 -0
  286. package/messages/it/mobileNav.json +13 -0
  287. package/messages/it/navigation.json +8 -0
  288. package/messages/it/observability.json +74 -0
  289. package/messages/it/posts.json +54 -0
  290. package/messages/it/pricing.json +102 -0
  291. package/messages/it/support.json +9 -0
  292. package/messages/it/teams.json +8 -0
  293. package/messages/pt/admin.json +219 -0
  294. package/messages/pt/aiUsage.json +36 -0
  295. package/messages/pt/buttons.json +19 -0
  296. package/messages/pt/categories.json +35 -0
  297. package/messages/pt/common.json +16 -0
  298. package/messages/pt/dev.json +101 -0
  299. package/messages/pt/docs.json +27 -0
  300. package/messages/pt/entities.json +7 -0
  301. package/messages/pt/features.json +119 -0
  302. package/messages/pt/footer.json +22 -0
  303. package/messages/pt/home.json +57 -0
  304. package/messages/pt/index.ts +39 -0
  305. package/messages/pt/mobileNav.json +13 -0
  306. package/messages/pt/navigation.json +8 -0
  307. package/messages/pt/observability.json +74 -0
  308. package/messages/pt/posts.json +54 -0
  309. package/messages/pt/pricing.json +102 -0
  310. package/messages/pt/support.json +9 -0
  311. package/messages/pt/teams.json +8 -0
  312. package/migrations/089_add_editor_team_role.sql +39 -0
  313. package/migrations/090_demo_users_teams.sql +540 -0
  314. package/migrations/091_greek_teams_billing.sql +523 -0
  315. package/migrations/092_billing_sample_data.sql +774 -0
  316. package/migrations/093_pages_sample_data.sql +1158 -0
  317. package/migrations/094_posts_sample_data.sql +278 -0
  318. package/migrations/095_tasks_sample_data.sql +440 -0
  319. package/migrations/096_customers_sample_data.sql +358 -0
  320. package/migrations/097_scheduled_actions_sample_data.sql +111 -0
  321. package/package.json +22 -0
  322. package/public/docs/desktop-layout-example.png +0 -0
  323. package/styles/components.css +11 -0
  324. package/styles/globals.css +179 -0
  325. package/templates/(public)/blog/[slug]/page.tsx +65 -0
  326. package/templates/(public)/layout.tsx +25 -0
  327. package/templates/(public)/page.tsx +200 -0
  328. package/templates/(public)/support/page.tsx +321 -0
  329. package/templates/dashboard/(main)/agent-multi/page.tsx +63 -0
  330. package/templates/dashboard/(main)/agent-single/page.tsx +142 -0
  331. package/templates/dashboard/(main)/settings/ai-usage/page.tsx +157 -0
  332. package/templates/superadmin/ai-observability/[traceId]/page.tsx +27 -0
  333. package/templates/superadmin/ai-observability/page.tsx +17 -0
@@ -0,0 +1,376 @@
1
+ 'use client'
2
+
3
+ import { useState, useCallback, useEffect } from 'react'
4
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
5
+ import { useTeamContext } from '@nextsparkjs/core/contexts/TeamContext'
6
+ import { authClient } from '@nextsparkjs/core/lib/auth-client'
7
+
8
+ /**
9
+ * Conversation info from the API
10
+ */
11
+ export interface ConversationInfo {
12
+ sessionId: string
13
+ name: string | null
14
+ messageCount: number
15
+ firstMessage: string | null
16
+ isPinned: boolean
17
+ createdAt: string
18
+ updatedAt: string
19
+ }
20
+
21
+ interface ListConversationsResponse {
22
+ success: boolean
23
+ data?: {
24
+ sessions: ConversationInfo[]
25
+ count: number
26
+ maxAllowed: number
27
+ }
28
+ error?: string
29
+ }
30
+
31
+ interface CreateConversationResponse {
32
+ success: boolean
33
+ data?: {
34
+ sessionId: string
35
+ name: string | null
36
+ createdAt: string
37
+ }
38
+ error?: string
39
+ message?: string
40
+ }
41
+
42
+ interface UpdateConversationResponse {
43
+ success: boolean
44
+ data?: ConversationInfo
45
+ error?: string
46
+ }
47
+
48
+ interface DeleteConversationResponse {
49
+ success: boolean
50
+ message?: string
51
+ error?: string
52
+ }
53
+
54
+ const ACTIVE_SESSION_KEY = 'active-conversation-session'
55
+ const CONVERSATIONS_QUERY_KEY = 'conversations'
56
+
57
+ /**
58
+ * Conversations Management Hook
59
+ *
60
+ * Manages the list of conversations and CRUD operations.
61
+ * Works with the LangChain plugin sessions endpoint.
62
+ */
63
+ export function useConversations() {
64
+ const [activeSessionId, setActiveSessionIdState] = useState<string | null>(null)
65
+ const [isInitialized, setIsInitialized] = useState(false)
66
+ const { currentTeam } = useTeamContext()
67
+ const queryClient = useQueryClient()
68
+
69
+ // Get current user session
70
+ const { data: session } = useQuery({
71
+ queryKey: ['session'],
72
+ queryFn: async () => {
73
+ const { data } = await authClient.getSession()
74
+ return data
75
+ },
76
+ staleTime: 1000 * 60 * 5, // 5 minutes
77
+ })
78
+
79
+ const userId = session?.user?.id
80
+ const teamId = currentTeam?.id
81
+
82
+ // Load active session from localStorage
83
+ useEffect(() => {
84
+ if (!userId || isInitialized) return
85
+
86
+ try {
87
+ const stored = localStorage.getItem(`${ACTIVE_SESSION_KEY}-${userId}`)
88
+ if (stored) {
89
+ setActiveSessionIdState(stored)
90
+ }
91
+ } catch {
92
+ // Ignore parse errors
93
+ }
94
+ setIsInitialized(true)
95
+ }, [userId, isInitialized])
96
+
97
+ // Save active session to localStorage
98
+ const setActiveSession = useCallback((sessionId: string | null) => {
99
+ setActiveSessionIdState(sessionId)
100
+ if (userId) {
101
+ try {
102
+ if (sessionId) {
103
+ localStorage.setItem(`${ACTIVE_SESSION_KEY}-${userId}`, sessionId)
104
+ } else {
105
+ localStorage.removeItem(`${ACTIVE_SESSION_KEY}-${userId}`)
106
+ }
107
+ } catch {
108
+ // Ignore storage errors
109
+ }
110
+ }
111
+ }, [userId])
112
+
113
+ // Fetch conversations list
114
+ const {
115
+ data: conversationsData,
116
+ isLoading,
117
+ error: queryError,
118
+ refetch,
119
+ } = useQuery({
120
+ queryKey: [CONVERSATIONS_QUERY_KEY, userId, teamId],
121
+ queryFn: async (): Promise<ListConversationsResponse> => {
122
+ if (!teamId) {
123
+ return { success: true, data: { sessions: [], count: 0, maxAllowed: 50 } }
124
+ }
125
+
126
+ const headers: Record<string, string> = {
127
+ 'Content-Type': 'application/json',
128
+ 'x-team-id': teamId,
129
+ }
130
+
131
+ const response = await fetch('/api/v1/plugin/langchain/sessions', {
132
+ method: 'GET',
133
+ headers,
134
+ })
135
+
136
+ if (!response.ok) {
137
+ const errorData = await response.json().catch(() => ({}))
138
+ throw new Error(errorData.error || 'Failed to fetch conversations')
139
+ }
140
+
141
+ return response.json()
142
+ },
143
+ enabled: !!userId && !!teamId && isInitialized,
144
+ staleTime: 1000 * 30, // 30 seconds
145
+ })
146
+
147
+ const conversations = conversationsData?.data?.sessions || []
148
+ const conversationCount = conversationsData?.data?.count || 0
149
+ const maxConversations = conversationsData?.data?.maxAllowed || 50
150
+
151
+ // Auto-select first conversation if none selected
152
+ useEffect(() => {
153
+ if (!isInitialized) return
154
+
155
+ // If we have conversations but no active one, select the first
156
+ if (conversations.length > 0 && !activeSessionId) {
157
+ setActiveSession(conversations[0].sessionId)
158
+ }
159
+
160
+ // If the active session was deleted, clear it
161
+ if (activeSessionId && conversations.length > 0) {
162
+ const exists = conversations.some((c) => c.sessionId === activeSessionId)
163
+ if (!exists) {
164
+ setActiveSession(conversations[0].sessionId)
165
+ }
166
+ }
167
+
168
+ // If no conversations at all, clear active session
169
+ if (conversations.length === 0 && activeSessionId) {
170
+ setActiveSession(null)
171
+ }
172
+ }, [conversations, activeSessionId, isInitialized, setActiveSession])
173
+
174
+ // Create conversation mutation
175
+ const createMutation = useMutation({
176
+ mutationFn: async (name?: string): Promise<CreateConversationResponse> => {
177
+ if (!teamId) {
178
+ throw new Error('No team context')
179
+ }
180
+
181
+ const headers: Record<string, string> = {
182
+ 'Content-Type': 'application/json',
183
+ 'x-team-id': teamId,
184
+ }
185
+
186
+ const response = await fetch('/api/v1/plugin/langchain/sessions', {
187
+ method: 'POST',
188
+ headers,
189
+ body: JSON.stringify({ name }),
190
+ })
191
+
192
+ const data = await response.json()
193
+
194
+ if (!response.ok) {
195
+ throw new Error(data.message || data.error || 'Failed to create conversation')
196
+ }
197
+
198
+ return data
199
+ },
200
+ onSuccess: (data) => {
201
+ if (data.success && data.data) {
202
+ // Set the new conversation as active
203
+ setActiveSession(data.data.sessionId)
204
+ // Refetch conversations list
205
+ queryClient.invalidateQueries({ queryKey: [CONVERSATIONS_QUERY_KEY] })
206
+ }
207
+ },
208
+ })
209
+
210
+ // Delete conversation mutation
211
+ const deleteMutation = useMutation({
212
+ mutationFn: async (sessionId: string): Promise<DeleteConversationResponse> => {
213
+ if (!teamId) {
214
+ throw new Error('No team context')
215
+ }
216
+
217
+ const headers: Record<string, string> = {
218
+ 'Content-Type': 'application/json',
219
+ 'x-team-id': teamId,
220
+ }
221
+
222
+ const response = await fetch('/api/v1/plugin/langchain/sessions', {
223
+ method: 'DELETE',
224
+ headers,
225
+ body: JSON.stringify({ sessionId }),
226
+ })
227
+
228
+ if (!response.ok) {
229
+ const errorData = await response.json().catch(() => ({}))
230
+ throw new Error(errorData.error || 'Failed to delete conversation')
231
+ }
232
+
233
+ return response.json()
234
+ },
235
+ onSuccess: (_, deletedSessionId) => {
236
+ // If we deleted the active session, clear it (useEffect will select a new one)
237
+ if (activeSessionId === deletedSessionId) {
238
+ setActiveSession(null)
239
+ }
240
+ // Refetch conversations list
241
+ queryClient.invalidateQueries({ queryKey: [CONVERSATIONS_QUERY_KEY] })
242
+ },
243
+ })
244
+
245
+ // Rename conversation mutation
246
+ const renameMutation = useMutation({
247
+ mutationFn: async ({
248
+ sessionId,
249
+ name,
250
+ }: {
251
+ sessionId: string
252
+ name: string
253
+ }): Promise<UpdateConversationResponse> => {
254
+ if (!teamId) {
255
+ throw new Error('No team context')
256
+ }
257
+
258
+ const headers: Record<string, string> = {
259
+ 'Content-Type': 'application/json',
260
+ 'x-team-id': teamId,
261
+ }
262
+
263
+ const response = await fetch('/api/v1/plugin/langchain/sessions', {
264
+ method: 'PATCH',
265
+ headers,
266
+ body: JSON.stringify({ sessionId, name }),
267
+ })
268
+
269
+ if (!response.ok) {
270
+ const errorData = await response.json().catch(() => ({}))
271
+ throw new Error(errorData.error || 'Failed to rename conversation')
272
+ }
273
+
274
+ return response.json()
275
+ },
276
+ onSuccess: () => {
277
+ queryClient.invalidateQueries({ queryKey: [CONVERSATIONS_QUERY_KEY] })
278
+ },
279
+ })
280
+
281
+ // Toggle pin mutation
282
+ const togglePinMutation = useMutation({
283
+ mutationFn: async (sessionId: string): Promise<UpdateConversationResponse> => {
284
+ if (!teamId) {
285
+ throw new Error('No team context')
286
+ }
287
+
288
+ // Find current pin state
289
+ const conversation = conversations.find((c) => c.sessionId === sessionId)
290
+ if (!conversation) {
291
+ throw new Error('Conversation not found')
292
+ }
293
+
294
+ const headers: Record<string, string> = {
295
+ 'Content-Type': 'application/json',
296
+ 'x-team-id': teamId,
297
+ }
298
+
299
+ const response = await fetch('/api/v1/plugin/langchain/sessions', {
300
+ method: 'PATCH',
301
+ headers,
302
+ body: JSON.stringify({ sessionId, isPinned: !conversation.isPinned }),
303
+ })
304
+
305
+ if (!response.ok) {
306
+ const errorData = await response.json().catch(() => ({}))
307
+ throw new Error(errorData.error || 'Failed to toggle pin')
308
+ }
309
+
310
+ return response.json()
311
+ },
312
+ onSuccess: () => {
313
+ queryClient.invalidateQueries({ queryKey: [CONVERSATIONS_QUERY_KEY] })
314
+ },
315
+ })
316
+
317
+ // Action handlers
318
+ const createConversation = useCallback(
319
+ async (name?: string): Promise<string> => {
320
+ const result = await createMutation.mutateAsync(name)
321
+ return result.data?.sessionId || ''
322
+ },
323
+ [createMutation]
324
+ )
325
+
326
+ const deleteConversation = useCallback(
327
+ async (sessionId: string): Promise<void> => {
328
+ await deleteMutation.mutateAsync(sessionId)
329
+ },
330
+ [deleteMutation]
331
+ )
332
+
333
+ const renameConversation = useCallback(
334
+ async (sessionId: string, name: string): Promise<void> => {
335
+ await renameMutation.mutateAsync({ sessionId, name })
336
+ },
337
+ [renameMutation]
338
+ )
339
+
340
+ const togglePin = useCallback(
341
+ async (sessionId: string): Promise<void> => {
342
+ await togglePinMutation.mutateAsync(sessionId)
343
+ },
344
+ [togglePinMutation]
345
+ )
346
+
347
+ return {
348
+ // Data
349
+ conversations,
350
+ activeSessionId,
351
+ isLoading,
352
+ error: queryError?.message || null,
353
+
354
+ // Actions
355
+ createConversation,
356
+ deleteConversation,
357
+ renameConversation,
358
+ togglePin,
359
+ setActiveSession,
360
+ refetchConversations: refetch,
361
+
362
+ // Computed
363
+ canCreateNew: conversationCount < maxConversations,
364
+ conversationCount,
365
+ maxConversations,
366
+
367
+ // Mutation states
368
+ isCreating: createMutation.isPending,
369
+ isDeleting: deleteMutation.isPending,
370
+ isRenaming: renameMutation.isPending,
371
+ isTogglingPin: togglePinMutation.isPending,
372
+
373
+ // Ready state
374
+ isReady: isInitialized && !!userId && !!teamId,
375
+ }
376
+ }
@@ -0,0 +1,122 @@
1
+ 'use client'
2
+
3
+ import { useState, useCallback } from 'react'
4
+ import { useMutation } from '@tanstack/react-query'
5
+ import { useTeamContext } from '@nextsparkjs/core/contexts/TeamContext'
6
+
7
+ export interface OrchestratorMessage {
8
+ id: string
9
+ role: 'user' | 'assistant'
10
+ content: string
11
+ timestamp: number
12
+ agentUsed?: 'orchestrator' | 'task' | 'customer' | 'page'
13
+ }
14
+
15
+ interface ChatResponse {
16
+ success: boolean
17
+ data?: {
18
+ message: string
19
+ sessionId: string
20
+ agentUsed?: 'orchestrator' | 'task' | 'customer' | 'page'
21
+ }
22
+ error?: string
23
+ }
24
+
25
+ /**
26
+ * Hook for testing the multi-agent orchestrator
27
+ * Similar to useAiChat but includes agentUsed in the response
28
+ */
29
+ export function useOrchestratorChat() {
30
+ const [messages, setMessages] = useState<OrchestratorMessage[]>([])
31
+ const [input, setInput] = useState('')
32
+ const [sessionId, setSessionId] = useState<string | undefined>(undefined)
33
+ const [error, setError] = useState<string | null>(null)
34
+ const { currentTeam } = useTeamContext()
35
+
36
+ const sendMutation = useMutation({
37
+ mutationFn: async (message: string): Promise<ChatResponse> => {
38
+ const headers: Record<string, string> = {
39
+ 'Content-Type': 'application/json'
40
+ }
41
+
42
+ // Include team context if available
43
+ if (currentTeam?.id) {
44
+ headers['x-team-id'] = currentTeam.id
45
+ }
46
+
47
+ const response = await fetch('/api/v1/theme/default/ai/orchestrator', {
48
+ method: 'POST',
49
+ headers,
50
+ body: JSON.stringify({ message, sessionId })
51
+ })
52
+
53
+ if (!response.ok) {
54
+ const errorData = await response.json().catch(() => ({}))
55
+ throw new Error(errorData.error || 'Failed to send message')
56
+ }
57
+
58
+ return response.json()
59
+ },
60
+ onMutate: (message) => {
61
+ setError(null)
62
+ // Optimistic update - add user message immediately
63
+ const userMessage: OrchestratorMessage = {
64
+ id: crypto.randomUUID(),
65
+ role: 'user',
66
+ content: message,
67
+ timestamp: Date.now()
68
+ }
69
+ setMessages(prev => [...prev, userMessage])
70
+ setInput('')
71
+ },
72
+ onSuccess: (data) => {
73
+ if (data.success && data.data) {
74
+ setSessionId(data.data.sessionId)
75
+ const aiMessage: OrchestratorMessage = {
76
+ id: crypto.randomUUID(),
77
+ role: 'assistant',
78
+ content: data.data.message,
79
+ timestamp: Date.now(),
80
+ agentUsed: data.data.agentUsed
81
+ }
82
+ setMessages(prev => [...prev, aiMessage])
83
+ } else if (data.error) {
84
+ setError(data.error)
85
+ }
86
+ },
87
+ onError: (err: Error) => {
88
+ setError(err.message)
89
+ // Add error message to chat
90
+ const errorMessage: OrchestratorMessage = {
91
+ id: crypto.randomUUID(),
92
+ role: 'assistant',
93
+ content: `Error: ${err.message}`,
94
+ timestamp: Date.now()
95
+ }
96
+ setMessages(prev => [...prev, errorMessage])
97
+ }
98
+ })
99
+
100
+ const sendMessage = useCallback(() => {
101
+ if (!input.trim() || sendMutation.isPending) return
102
+ sendMutation.mutate(input)
103
+ }, [input, sendMutation])
104
+
105
+ const clearChat = useCallback(() => {
106
+ setMessages([])
107
+ setSessionId(undefined)
108
+ setError(null)
109
+ setInput('')
110
+ }, [])
111
+
112
+ return {
113
+ messages,
114
+ input,
115
+ setInput,
116
+ error,
117
+ isLoading: sendMutation.isPending,
118
+ sendMessage,
119
+ clearChat,
120
+ sessionId
121
+ }
122
+ }