@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,167 @@
1
+ import { z } from 'zod'
2
+ import { ToolDefinition } from '@/plugins/langchain/lib/tools-builder'
3
+ import { TasksService } from '@/themes/default/entities/tasks/tasks.service'
4
+ import { TaskStatus, TaskPriority } from '@/themes/default/entities/tasks/tasks.types'
5
+
6
+ /**
7
+ * Tool context for task operations
8
+ */
9
+ export interface TaskToolContext {
10
+ userId: string
11
+ teamId: string
12
+ }
13
+
14
+ /**
15
+ * Create task management tools for the AI agent
16
+ *
17
+ * @param context - User and team context for RLS
18
+ * @returns Array of task-related tool definitions
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const tools = createTaskTools({ userId: 'user-123', teamId: 'team-456' })
23
+ * const agent = await createAgent({
24
+ * systemPrompt: loadSystemPrompt('task-assistant'),
25
+ * tools,
26
+ * })
27
+ * ```
28
+ */
29
+ export function createTaskTools(context: TaskToolContext): ToolDefinition<any>[] {
30
+ const { userId, teamId } = context
31
+
32
+ return [
33
+ {
34
+ name: 'list_tasks',
35
+ description: 'List all tasks assigned to the user. Optionally filter by status and/or priority.',
36
+ schema: z.object({
37
+ status: z.enum(['todo', 'in-progress', 'review', 'done', 'blocked'])
38
+ .optional()
39
+ .describe('Filter tasks by status'),
40
+ priority: z.enum(['low', 'medium', 'high', 'urgent'])
41
+ .optional()
42
+ .describe('Filter tasks by priority level'),
43
+ }),
44
+ func: async ({ status, priority }) => {
45
+ try {
46
+ const result = await TasksService.list(userId, {
47
+ status: status as TaskStatus,
48
+ limit: 50,
49
+ teamId,
50
+ })
51
+ // Filter by priority if specified
52
+ let tasks = result.tasks
53
+ if (priority) {
54
+ tasks = tasks.filter(t => t.priority === priority)
55
+ }
56
+ return JSON.stringify(tasks.map(t => ({
57
+ id: t.id,
58
+ title: t.title,
59
+ status: t.status,
60
+ priority: t.priority,
61
+ dueDate: t.dueDate,
62
+ })), null, 2)
63
+ } catch (error) {
64
+ return `Error listing tasks: ${error instanceof Error ? error.message : 'Unknown error'}`
65
+ }
66
+ },
67
+ },
68
+ {
69
+ name: 'search_tasks',
70
+ description: 'Search tasks by keyword in title or description.',
71
+ schema: z.object({
72
+ query: z.string().describe('Search keyword'),
73
+ }),
74
+ func: async ({ query }) => {
75
+ try {
76
+ const result = await TasksService.list(userId, { limit: 100, teamId })
77
+ const filtered = result.tasks.filter(t =>
78
+ t.title.toLowerCase().includes(query.toLowerCase()) ||
79
+ t.description?.toLowerCase().includes(query.toLowerCase())
80
+ )
81
+ return JSON.stringify(filtered.slice(0, 10).map(t => ({
82
+ id: t.id,
83
+ title: t.title,
84
+ status: t.status,
85
+ priority: t.priority,
86
+ })), null, 2)
87
+ } catch (error) {
88
+ return `Error searching tasks: ${error instanceof Error ? error.message : 'Unknown error'}`
89
+ }
90
+ },
91
+ },
92
+ {
93
+ name: 'get_task_details',
94
+ description: 'Get full details of a specific task by ID.',
95
+ schema: z.object({
96
+ taskId: z.string().describe('The task ID'),
97
+ }),
98
+ func: async ({ taskId }) => {
99
+ try {
100
+ const task = await TasksService.getById(taskId, userId)
101
+ if (!task) {
102
+ return JSON.stringify({ error: 'Task not found' })
103
+ }
104
+ return JSON.stringify(task, null, 2)
105
+ } catch (error) {
106
+ return `Error getting task: ${error instanceof Error ? error.message : 'Unknown error'}`
107
+ }
108
+ },
109
+ },
110
+ {
111
+ name: 'create_task',
112
+ description: 'Create a new task.',
113
+ schema: z.object({
114
+ title: z.string().describe('Task title'),
115
+ description: z.string().optional().describe('Task description'),
116
+ priority: z.enum(['low', 'medium', 'high'])
117
+ .optional()
118
+ .default('medium')
119
+ .describe('Task priority'),
120
+ dueDate: z.string().optional().describe('Due date in ISO format (YYYY-MM-DD)'),
121
+ }),
122
+ func: async (data) => {
123
+ try {
124
+ const task = await TasksService.create(userId, {
125
+ title: data.title,
126
+ description: data.description,
127
+ priority: data.priority as TaskPriority,
128
+ dueDate: data.dueDate,
129
+ status: 'todo' as TaskStatus,
130
+ teamId,
131
+ })
132
+ return JSON.stringify(task, null, 2)
133
+ } catch (error) {
134
+ return `Error creating task: ${error instanceof Error ? error.message : 'Unknown error'}`
135
+ }
136
+ },
137
+ },
138
+ {
139
+ name: 'update_task',
140
+ description: 'Update an existing task.',
141
+ schema: z.object({
142
+ taskId: z.string().describe('The task ID to update'),
143
+ title: z.string().optional().describe('New title'),
144
+ description: z.string().optional().describe('New description'),
145
+ status: z.enum(['todo', 'in-progress', 'review', 'done', 'blocked'])
146
+ .optional()
147
+ .describe('New status'),
148
+ priority: z.enum(['low', 'medium', 'high'])
149
+ .optional()
150
+ .describe('New priority'),
151
+ dueDate: z.string().optional().describe('New due date in ISO format'),
152
+ }),
153
+ func: async ({ taskId, ...updates }) => {
154
+ try {
155
+ const task = await TasksService.update(userId, taskId, {
156
+ ...updates,
157
+ status: updates.status as TaskStatus,
158
+ priority: updates.priority as TaskPriority,
159
+ })
160
+ return JSON.stringify(task, null, 2)
161
+ } catch (error) {
162
+ return `Error updating task: ${error instanceof Error ? error.message : 'Unknown error'}`
163
+ }
164
+ },
165
+ },
166
+ ]
167
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Billing Scheduled Action Handler
3
+ *
4
+ * This handler performs daily checks for subscription renewals and updates
5
+ * subscription statuses for expired subscriptions.
6
+ *
7
+ * Uses core billing services for actual subscription lifecycle management.
8
+ *
9
+ * @module contents/themes/default/lib/scheduled-actions/billing
10
+ */
11
+
12
+ import { registerScheduledAction, scheduleRecurringAction, scheduleAction } from '@nextsparkjs/core/lib/scheduled-actions'
13
+ import type { ScheduledAction } from '@nextsparkjs/core/lib/scheduled-actions'
14
+ import { queryWithRLS } from '@nextsparkjs/core/lib/db'
15
+ import { expireTrials, handlePastDueGracePeriod } from '@nextsparkjs/core/lib/billing/jobs'
16
+ import { SubscriptionService } from '@nextsparkjs/core/lib/services/subscription.service'
17
+
18
+ /**
19
+ * Register the billing check action handler
20
+ *
21
+ * This function registers the 'billing:check-renewals' action handler that
22
+ * processes subscription renewals and updates expired subscriptions.
23
+ *
24
+ * Uses core billing services:
25
+ * - expireTrials(): Expire trials that have passed their end date
26
+ * - handlePastDueGracePeriod(): Expire past_due subscriptions after grace period
27
+ * - SubscriptionService.listExpiringSoon(): Get subscriptions expiring soon
28
+ */
29
+ export function registerBillingAction() {
30
+ registerScheduledAction(
31
+ 'billing:check-renewals',
32
+ async (_payload: unknown, _action: ScheduledAction) => {
33
+ console.log('[billing:check-renewals] Starting subscription lifecycle check...')
34
+
35
+ try {
36
+ // 1. Expire trials that have passed their end date
37
+ const trialsResult = await expireTrials()
38
+ console.log(`[billing:check-renewals] Trials expired: ${trialsResult.processed}`)
39
+ if (trialsResult.errors.length > 0) {
40
+ console.warn('[billing:check-renewals] Trial expiration errors:', trialsResult.errors)
41
+ }
42
+
43
+ // 2. Handle past_due subscriptions after grace period
44
+ const pastDueResult = await handlePastDueGracePeriod()
45
+ console.log(`[billing:check-renewals] Past due expired: ${pastDueResult.processed}`)
46
+ if (pastDueResult.errors.length > 0) {
47
+ console.warn('[billing:check-renewals] Past due handling errors:', pastDueResult.errors)
48
+ }
49
+
50
+ // 3. Send reminder webhooks for subscriptions expiring in 7 days
51
+ const expiringSoon = await SubscriptionService.listExpiringSoon(7)
52
+ console.log(`[billing:check-renewals] Subscriptions expiring in 7 days: ${expiringSoon.length}`)
53
+
54
+ // Schedule webhook for each expiring subscription
55
+ let webhooksScheduled = 0
56
+ for (const sub of expiringSoon) {
57
+ const daysUntilExpiry = Math.ceil(
58
+ (new Date(sub.currentPeriodEnd).getTime() - Date.now()) / (1000 * 60 * 60 * 24)
59
+ )
60
+
61
+ await scheduleAction('webhook:send', {
62
+ eventType: 'expiring_soon',
63
+ entityType: 'subscription',
64
+ entityId: sub.id,
65
+ data: {
66
+ subscriptionId: sub.id,
67
+ teamId: sub.teamId,
68
+ planSlug: sub.plan.slug,
69
+ status: sub.status,
70
+ periodEnd: sub.currentPeriodEnd,
71
+ daysUntilExpiry,
72
+ },
73
+ }, { teamId: sub.teamId })
74
+
75
+ webhooksScheduled++
76
+ }
77
+
78
+ if (webhooksScheduled > 0) {
79
+ console.log(`[billing:check-renewals] Scheduled ${webhooksScheduled} expiring_soon webhooks`)
80
+ }
81
+
82
+ // Log summary
83
+ console.log('[billing:check-renewals] Check completed successfully:', {
84
+ trialsExpired: trialsResult.processed,
85
+ pastDueExpired: pastDueResult.processed,
86
+ expiringSoon: expiringSoon.length,
87
+ webhooksScheduled,
88
+ })
89
+ } catch (error) {
90
+ console.error('[billing:check-renewals] Error during check:', error)
91
+ throw error
92
+ }
93
+ },
94
+ {
95
+ description: 'Daily subscription lifecycle check',
96
+ timeout: 60000, // 60 seconds for billing checks
97
+ }
98
+ )
99
+ }
100
+
101
+ /**
102
+ * Ensure billing action is scheduled as recurring
103
+ *
104
+ * This function should be called at app startup to ensure the billing check
105
+ * is scheduled to run daily. It checks if a recurring action already exists
106
+ * to avoid duplicates.
107
+ *
108
+ * @example
109
+ * // Call at app startup
110
+ * await ensureBillingActionScheduled()
111
+ */
112
+ export async function ensureBillingActionScheduled() {
113
+ try {
114
+ // Check if a pending recurring billing action already exists
115
+ // Only check for pending status - if no pending exists, create a new one
116
+ const existingQuery = `
117
+ SELECT id
118
+ FROM "scheduled_actions"
119
+ WHERE "actionType" = 'billing:check-renewals'
120
+ AND "recurringInterval" = 'daily'
121
+ AND status = 'pending'
122
+ LIMIT 1
123
+ `
124
+
125
+ const existing = await queryWithRLS<{ id: string }>(
126
+ existingQuery,
127
+ [],
128
+ null // System operation, no user context
129
+ )
130
+
131
+ if (existing.length > 0) {
132
+ console.log('[billing:check-renewals] Daily recurring action already scheduled')
133
+ return
134
+ }
135
+
136
+ // Schedule new recurring daily action
137
+ console.log('[billing:check-renewals] Scheduling new daily recurring action...')
138
+ const actionId = await scheduleRecurringAction(
139
+ 'billing:check-renewals',
140
+ {}, // No payload needed
141
+ 'daily'
142
+ )
143
+
144
+ console.log(`[billing:check-renewals] Daily recurring action scheduled with ID: ${actionId}`)
145
+ } catch (error) {
146
+ console.error('[billing:check-renewals] Error ensuring action is scheduled:', error)
147
+ // Don't throw - this is called at startup and shouldn't crash the app
148
+ }
149
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Theme Scheduled Actions - Handler Registration
3
+ *
4
+ * This module registers all scheduled action handlers for the default theme.
5
+ * The core initializer calls these functions at startup.
6
+ *
7
+ * @module contents/themes/default/lib/scheduled-actions
8
+ *
9
+ * @example
10
+ * // Called by core/lib/scheduled-actions/initializer.ts
11
+ * // Do NOT call directly from app code
12
+ * registerAllHandlers()
13
+ * await registerRecurringActions()
14
+ */
15
+
16
+ import { registerWebhookAction } from './webhook'
17
+ import { registerBillingAction, ensureBillingActionScheduled } from './billing'
18
+ import { addAction } from '@nextsparkjs/core/lib/plugins/hook-system'
19
+ import { scheduleAction } from '@nextsparkjs/core/lib/scheduled-actions'
20
+
21
+ /**
22
+ * Register all scheduled action handlers for this theme
23
+ *
24
+ * This function registers all action handlers (webhook, billing, etc.).
25
+ * Called by the core initializer - the guard is handled there.
26
+ */
27
+ export function registerAllHandlers(): void {
28
+ console.log('[theme:scheduled-actions] Registering action handlers...')
29
+
30
+ registerWebhookAction()
31
+ registerBillingAction()
32
+ registerEntityWebhookHooks()
33
+
34
+ console.log('[theme:scheduled-actions] All action handlers registered')
35
+ }
36
+
37
+ /**
38
+ * Register entity hooks for webhook triggers
39
+ *
40
+ * Listens to entity.tasks.created and entity.tasks.updated hooks
41
+ * and schedules webhook:send actions.
42
+ */
43
+ function registerEntityWebhookHooks() {
44
+ console.log('[theme:scheduled-actions] Registering entity webhook hooks...')
45
+
46
+ // AC-15: Task create triggers webhook:send
47
+ addAction('entity.tasks.created', async (hookData: Record<string, unknown>) => {
48
+ try {
49
+ console.log('[webhook:trigger] Task created, scheduling webhook...')
50
+ const entityData = hookData.data as Record<string, unknown> | undefined
51
+ await scheduleAction('webhook:send', {
52
+ eventType: 'created',
53
+ entityType: 'task',
54
+ entityId: (hookData.id as string) || (entityData?.id as string),
55
+ data: entityData,
56
+ teamId: entityData?.teamId as string | undefined,
57
+ })
58
+ } catch (error) {
59
+ console.error('[webhook:trigger] Error scheduling task create webhook:', error)
60
+ }
61
+ })
62
+
63
+ // AC-16: Task update triggers webhook:send
64
+ addAction('entity.tasks.updated', async (hookData: Record<string, unknown>) => {
65
+ try {
66
+ console.log('[webhook:trigger] Task updated, scheduling webhook...')
67
+ const entityData = hookData.data as Record<string, unknown> | undefined
68
+ await scheduleAction('webhook:send', {
69
+ eventType: 'updated',
70
+ entityType: 'task',
71
+ entityId: hookData.id as string,
72
+ data: entityData,
73
+ teamId: entityData?.teamId as string | undefined,
74
+ })
75
+ } catch (error) {
76
+ console.error('[webhook:trigger] Error scheduling task update webhook:', error)
77
+ }
78
+ })
79
+
80
+ // =========================================================================
81
+ // SUBSCRIPTION WEBHOOK HOOKS
82
+ // =========================================================================
83
+ // These hooks listen to subscription lifecycle events from SubscriptionService
84
+ // and schedule webhook:send actions to notify external systems.
85
+
86
+ // Subscription created
87
+ addAction('subscription.created', async (hookData: Record<string, unknown>) => {
88
+ try {
89
+ console.log('[webhook:trigger] Subscription created, scheduling webhook...')
90
+ const entityData = hookData.data as Record<string, unknown> | undefined
91
+ await scheduleAction('webhook:send', {
92
+ eventType: 'created',
93
+ entityType: 'subscription',
94
+ entityId: hookData.id as string,
95
+ data: entityData,
96
+ teamId: (hookData.teamId as string) || (entityData?.teamId as string),
97
+ webhookKey: 'subscriptions',
98
+ })
99
+ } catch (error) {
100
+ console.error('[webhook:trigger] Error scheduling subscription created webhook:', error)
101
+ }
102
+ })
103
+
104
+ // Subscription updated (status change, plan change)
105
+ addAction('subscription.updated', async (hookData: Record<string, unknown>) => {
106
+ try {
107
+ console.log('[webhook:trigger] Subscription updated, scheduling webhook...')
108
+ const entityData = hookData.data as Record<string, unknown> | undefined
109
+ await scheduleAction('webhook:send', {
110
+ eventType: 'updated',
111
+ entityType: 'subscription',
112
+ entityId: hookData.id as string,
113
+ data: {
114
+ ...entityData,
115
+ previousStatus: hookData.previousStatus,
116
+ newStatus: hookData.newStatus,
117
+ previousPlan: hookData.previousPlan,
118
+ newPlan: hookData.newPlan,
119
+ isUpgrade: hookData.isUpgrade,
120
+ },
121
+ teamId: (hookData.teamId as string) || (entityData?.teamId as string),
122
+ webhookKey: 'subscriptions',
123
+ })
124
+ } catch (error) {
125
+ console.error('[webhook:trigger] Error scheduling subscription updated webhook:', error)
126
+ }
127
+ })
128
+
129
+ // Subscription cancelled
130
+ addAction('subscription.cancelled', async (hookData: Record<string, unknown>) => {
131
+ try {
132
+ console.log('[webhook:trigger] Subscription cancelled, scheduling webhook...')
133
+ const entityData = hookData.data as Record<string, unknown> | undefined
134
+ await scheduleAction('webhook:send', {
135
+ eventType: 'cancelled',
136
+ entityType: 'subscription',
137
+ entityId: hookData.id as string,
138
+ data: {
139
+ ...entityData,
140
+ immediate: hookData.immediate,
141
+ cancelAtPeriodEnd: hookData.cancelAtPeriodEnd,
142
+ },
143
+ teamId: entityData?.teamId as string | undefined,
144
+ webhookKey: 'subscriptions',
145
+ })
146
+ } catch (error) {
147
+ console.error('[webhook:trigger] Error scheduling subscription cancelled webhook:', error)
148
+ }
149
+ })
150
+
151
+ console.log('[theme:scheduled-actions] Entity webhook hooks registered')
152
+ }
153
+
154
+ /**
155
+ * Register recurring scheduled actions for this theme
156
+ *
157
+ * This async function ensures all recurring actions are scheduled.
158
+ * Called by the core initializer after handlers are registered.
159
+ */
160
+ export async function registerRecurringActions(): Promise<void> {
161
+ console.log('[theme:scheduled-actions] Registering recurring actions...')
162
+
163
+ await ensureBillingActionScheduled()
164
+
165
+ console.log('[theme:scheduled-actions] All recurring actions registered')
166
+ }
167
+
168
+ // Re-export individual functions for direct use
169
+ export { registerWebhookAction } from './webhook'
170
+ export { registerBillingAction, ensureBillingActionScheduled } from './billing'