@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,231 @@
1
+ /**
2
+ * Webhook Scheduled Action Handler - Multi-Endpoint Support
3
+ *
4
+ * This handler supports multiple webhook endpoints configured in app.config.ts.
5
+ * Each endpoint can be:
6
+ * 1. Explicitly called by key (webhookKey in payload)
7
+ * 2. Auto-matched by event pattern (e.g., 'task:created' -> 'tasks' endpoint)
8
+ * 3. Fallback to default endpoint
9
+ * 4. Legacy single webhookUrl support for backward compatibility
10
+ *
11
+ * @module contents/themes/default/lib/scheduled-actions/webhook
12
+ */
13
+
14
+ import { registerScheduledAction } from '@nextsparkjs/core/lib/scheduled-actions'
15
+ import type { ScheduledAction } from '@nextsparkjs/core/lib/scheduled-actions'
16
+ import { APP_CONFIG_OVERRIDES } from '@/themes/default/config/app.config'
17
+ import type { WebhookEndpointConfig } from '@nextsparkjs/core/lib/config/types'
18
+
19
+ /**
20
+ * Webhook payload interface with optional explicit key
21
+ */
22
+ interface WebhookPayload {
23
+ /** Event type (e.g., 'created', 'updated', 'deleted', 'renewed') */
24
+ eventType: string
25
+ /** Entity type (e.g., 'task', 'subscription') */
26
+ entityType: string
27
+ /** Entity ID */
28
+ entityId: string
29
+ /** Full entity data */
30
+ data: unknown
31
+ /** Team ID context */
32
+ teamId?: string
33
+ /** Explicit webhook endpoint key (overrides pattern matching) */
34
+ webhookKey?: string
35
+ }
36
+
37
+ /**
38
+ * Resolved webhook endpoint
39
+ */
40
+ interface ResolvedWebhook {
41
+ url: string
42
+ key: string
43
+ }
44
+
45
+ /**
46
+ * Get typed scheduledActions config from APP_CONFIG_OVERRIDES
47
+ */
48
+ function getScheduledActionsConfig() {
49
+ return APP_CONFIG_OVERRIDES.scheduledActions as {
50
+ webhookUrl?: string
51
+ webhooks?: {
52
+ endpoints: Record<string, WebhookEndpointConfig>
53
+ defaultEndpoint?: string
54
+ }
55
+ } | undefined
56
+ }
57
+
58
+ /**
59
+ * Resolve webhook URL based on payload
60
+ *
61
+ * Resolution priority:
62
+ * 1. Explicit webhookKey in payload
63
+ * 2. Pattern matching based on entityType:eventType
64
+ * 3. Default endpoint
65
+ * 4. Legacy webhookUrl (backward compatibility)
66
+ *
67
+ * @param payload - Webhook payload with event info
68
+ * @returns Resolved webhook with URL and key, or null if no match
69
+ */
70
+ function resolveWebhookUrl(payload: WebhookPayload): ResolvedWebhook | null {
71
+ const config = getScheduledActionsConfig()
72
+ const webhooksConfig = config?.webhooks
73
+ const legacyUrl = config?.webhookUrl
74
+
75
+ // 1. Explicit webhook key provided
76
+ if (payload.webhookKey && webhooksConfig?.endpoints[payload.webhookKey]) {
77
+ const endpoint = webhooksConfig.endpoints[payload.webhookKey]
78
+ if (endpoint.enabled !== false) {
79
+ const url = process.env[endpoint.envVar]
80
+ if (url) {
81
+ return { url, key: payload.webhookKey }
82
+ }
83
+ console.warn(`[webhook:send] Endpoint '${payload.webhookKey}' envVar '${endpoint.envVar}' not set`)
84
+ }
85
+ }
86
+
87
+ // 2. Pattern matching
88
+ if (webhooksConfig?.endpoints) {
89
+ const eventPattern = `${payload.entityType}:${payload.eventType}`
90
+
91
+ for (const [key, endpoint] of Object.entries(webhooksConfig.endpoints)) {
92
+ if (endpoint.enabled === false) continue
93
+ if (!endpoint.patterns) continue
94
+
95
+ for (const pattern of endpoint.patterns) {
96
+ if (matchesPattern(eventPattern, pattern)) {
97
+ const url = process.env[endpoint.envVar]
98
+ if (url) {
99
+ return { url, key }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ // 3. Default endpoint
107
+ if (webhooksConfig?.defaultEndpoint && webhooksConfig.endpoints[webhooksConfig.defaultEndpoint]) {
108
+ const endpoint = webhooksConfig.endpoints[webhooksConfig.defaultEndpoint]
109
+ if (endpoint.enabled !== false) {
110
+ const url = process.env[endpoint.envVar]
111
+ if (url) {
112
+ return { url, key: webhooksConfig.defaultEndpoint }
113
+ }
114
+ }
115
+ }
116
+
117
+ // 4. Legacy webhookUrl (backward compatibility)
118
+ if (legacyUrl) {
119
+ return { url: legacyUrl, key: 'legacy' }
120
+ }
121
+
122
+ return null
123
+ }
124
+
125
+ /**
126
+ * Pattern matching for event routing
127
+ *
128
+ * Supports wildcard patterns:
129
+ * - 'task:*' matches 'task:created', 'task:updated', etc.
130
+ * - '*:created' matches 'task:created', 'subscription:created', etc.
131
+ * - '*:*' matches all events
132
+ *
133
+ * @param event - Actual event (e.g., 'task:created')
134
+ * @param pattern - Pattern to match (e.g., 'task:*')
135
+ * @returns True if pattern matches event
136
+ */
137
+ function matchesPattern(event: string, pattern: string): boolean {
138
+ const [eventEntity, eventAction] = event.split(':')
139
+ const [patternEntity, patternAction] = pattern.split(':')
140
+
141
+ const entityMatch = patternEntity === '*' || patternEntity === eventEntity
142
+ const actionMatch = patternAction === '*' || patternAction === eventAction
143
+
144
+ return entityMatch && actionMatch
145
+ }
146
+
147
+ /**
148
+ * Register the webhook action handler
149
+ *
150
+ * This function registers the 'webhook:send' action handler that sends POST
151
+ * requests to configured webhook endpoints.
152
+ *
153
+ * @example
154
+ * // Register at app startup
155
+ * registerWebhookAction()
156
+ *
157
+ * // Schedule a webhook with explicit key
158
+ * await scheduleAction('webhook:send', {
159
+ * eventType: 'created',
160
+ * entityType: 'task',
161
+ * entityId: task.id,
162
+ * data: task,
163
+ * webhookKey: 'tasks', // Explicit routing
164
+ * })
165
+ *
166
+ * // Schedule a webhook with auto-matching
167
+ * await scheduleAction('webhook:send', {
168
+ * eventType: 'created',
169
+ * entityType: 'subscription',
170
+ * entityId: sub.id,
171
+ * data: sub,
172
+ * // Will auto-match to 'subscriptions' endpoint via pattern
173
+ * })
174
+ */
175
+ export function registerWebhookAction() {
176
+ registerScheduledAction(
177
+ 'webhook:send',
178
+ async (payload: unknown, action: ScheduledAction) => {
179
+ const webhookPayload = payload as WebhookPayload
180
+
181
+ // Validate required fields
182
+ if (!webhookPayload.eventType || !webhookPayload.entityType || !webhookPayload.entityId) {
183
+ throw new Error('Invalid webhook payload: missing required fields (eventType, entityType, entityId)')
184
+ }
185
+
186
+ // Resolve webhook URL
187
+ const resolved = resolveWebhookUrl(webhookPayload)
188
+
189
+ if (!resolved) {
190
+ // No URL configured - skip silently (not an error, just not configured)
191
+ console.log(`[webhook:send] No webhook URL configured for ${webhookPayload.entityType}:${webhookPayload.eventType}, skipping`)
192
+ return
193
+ }
194
+
195
+ console.log(`[webhook:send] Sending to '${resolved.key}' for ${webhookPayload.entityType}:${webhookPayload.eventType}`)
196
+
197
+ // Send webhook POST request
198
+ const response = await fetch(resolved.url, {
199
+ method: 'POST',
200
+ headers: {
201
+ 'Content-Type': 'application/json',
202
+ 'User-Agent': 'NextSpark-Webhook/1.0',
203
+ 'X-Webhook-Key': resolved.key,
204
+ },
205
+ body: JSON.stringify({
206
+ event: webhookPayload.eventType,
207
+ entity: webhookPayload.entityType,
208
+ entityId: webhookPayload.entityId,
209
+ data: webhookPayload.data,
210
+ teamId: webhookPayload.teamId,
211
+ timestamp: new Date().toISOString(),
212
+ actionId: action.id,
213
+ }),
214
+ })
215
+
216
+ // Check if webhook was successful
217
+ if (!response.ok) {
218
+ const errorText = await response.text()
219
+ throw new Error(
220
+ `Webhook '${resolved.key}' failed with status ${response.status}: ${errorText.substring(0, 200)}`
221
+ )
222
+ }
223
+
224
+ console.log(`[webhook:send] Webhook '${resolved.key}' sent successfully (status: ${response.status})`)
225
+ },
226
+ {
227
+ description: 'Send webhook notification to configured endpoint',
228
+ timeout: 15000, // 15 seconds timeout for webhooks
229
+ }
230
+ )
231
+ }
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Theme Block Selectors
3
+ *
4
+ * This file defines selectors for block components in the theme.
5
+ * It's placed in lib/ instead of tests/ so TypeScript can resolve imports.
6
+ *
7
+ * Used by:
8
+ * - Block components (for data-cy attributes)
9
+ * - Cypress tests (via tests/cypress/src/selectors.ts)
10
+ */
11
+
12
+ import { createSelectorHelpers } from '@nextsparkjs/core/lib/test/selector-factory'
13
+ import { CORE_SELECTORS } from '@nextsparkjs/core/lib/test/core-selectors'
14
+
15
+ // =============================================================================
16
+ // BLOCK SELECTORS
17
+ // =============================================================================
18
+
19
+ /**
20
+ * Block-specific selectors for the default theme.
21
+ * Each block has at minimum a 'container' selector.
22
+ * Dynamic selectors use {index} placeholder.
23
+ */
24
+ export const BLOCK_SELECTORS = {
25
+ hero: {
26
+ container: 'block-hero',
27
+ },
28
+ faqAccordion: {
29
+ container: 'block-faq-accordion',
30
+ item: 'faq-item-{index}',
31
+ question: 'faq-question-{index}',
32
+ answer: 'faq-answer-{index}',
33
+ },
34
+ benefits: {
35
+ container: 'block-benefits',
36
+ },
37
+ ctaSection: {
38
+ container: 'block-cta-section',
39
+ },
40
+ featuresGrid: {
41
+ container: 'block-features-grid',
42
+ },
43
+ heroWithForm: {
44
+ container: 'block-hero-with-form',
45
+ form: {
46
+ firstname: 'hero-form-firstname',
47
+ lastname: 'hero-form-lastname',
48
+ email: 'hero-form-email',
49
+ phone: 'hero-form-phone',
50
+ area: 'hero-form-area',
51
+ consent: 'hero-form-consent',
52
+ submit: 'hero-form-submit',
53
+ },
54
+ },
55
+ jumbotron: {
56
+ container: 'block-jumbotron',
57
+ },
58
+ logoCloud: {
59
+ container: 'block-logo-cloud',
60
+ item: 'logo-item-{index}',
61
+ link: 'logo-link-{index}',
62
+ },
63
+ postContent: {
64
+ container: 'block-post-content',
65
+ divider: 'post-content-divider',
66
+ cta: 'post-content-cta',
67
+ },
68
+ pricingTable: {
69
+ container: 'block-pricing-table',
70
+ plan: 'pricing-plan-{index}',
71
+ features: 'plan-features',
72
+ cta: 'plan-cta-{index}',
73
+ },
74
+ splitContent: {
75
+ container: 'block-split-content',
76
+ },
77
+ statsCounter: {
78
+ container: 'block-stats-counter',
79
+ },
80
+ testimonials: {
81
+ container: 'block-testimonials',
82
+ },
83
+ textContent: {
84
+ container: 'block-text-content',
85
+ },
86
+ timeline: {
87
+ container: 'block-timeline',
88
+ },
89
+ videoHero: {
90
+ container: 'block-video-hero',
91
+ },
92
+ } as const
93
+
94
+ // =============================================================================
95
+ // THEME SELECTORS (CORE + BLOCKS)
96
+ // =============================================================================
97
+
98
+ // =============================================================================
99
+ // DEVTOOLS SELECTORS
100
+ // =============================================================================
101
+
102
+ /**
103
+ * DevTools-specific selectors for the default theme.
104
+ */
105
+ export const DEVTOOLS_SELECTORS = {
106
+ scheduledActions: {
107
+ page: 'devtools-scheduled-actions-page',
108
+ filterStatus: 'scheduled-actions-filter-status',
109
+ filterType: 'scheduled-actions-filter-type',
110
+ filterApply: 'scheduled-actions-filter-apply',
111
+ filterReset: 'scheduled-actions-filter-reset',
112
+ table: 'scheduled-actions-table',
113
+ row: 'scheduled-actions-row-{id}',
114
+ cellType: 'scheduled-actions-cell-type',
115
+ cellStatus: 'scheduled-actions-cell-status',
116
+ cellScheduledAt: 'scheduled-actions-cell-scheduled-at',
117
+ cellTeam: 'scheduled-actions-cell-team',
118
+ cellPayload: 'scheduled-actions-cell-payload',
119
+ cellError: 'scheduled-actions-cell-error',
120
+ statusPending: 'scheduled-actions-status-pending',
121
+ statusRunning: 'scheduled-actions-status-running',
122
+ statusCompleted: 'scheduled-actions-status-completed',
123
+ statusFailed: 'scheduled-actions-status-failed',
124
+ pagination: 'scheduled-actions-pagination',
125
+ paginationPrev: 'scheduled-actions-pagination-prev',
126
+ paginationNext: 'scheduled-actions-pagination-next',
127
+ emptyState: 'scheduled-actions-empty-state',
128
+ },
129
+ } as const
130
+
131
+ // =============================================================================
132
+ // THEME SELECTORS (CORE + BLOCKS + DEVTOOLS)
133
+ // =============================================================================
134
+
135
+ /**
136
+ * Complete theme selectors merging core and blocks.
137
+ */
138
+ export const THEME_SELECTORS = {
139
+ ...CORE_SELECTORS,
140
+ blocks: BLOCK_SELECTORS,
141
+ devtools: DEVTOOLS_SELECTORS,
142
+ } as const
143
+
144
+ // =============================================================================
145
+ // EXPORTS
146
+ // =============================================================================
147
+
148
+ /**
149
+ * Create helpers bound to theme selectors
150
+ */
151
+ const helpers = createSelectorHelpers(THEME_SELECTORS)
152
+
153
+ /**
154
+ * Full selectors object (core + theme extensions)
155
+ */
156
+ export const SELECTORS = helpers.SELECTORS
157
+
158
+ /**
159
+ * Get a selector value by path
160
+ *
161
+ * @example
162
+ * sel('auth.login.form') // 'login-form'
163
+ * sel('blocks.hero.container') // 'block-hero'
164
+ * sel('blocks.faqAccordion.item', { index: '0' }) // 'faq-item-0'
165
+ */
166
+ export const sel = helpers.sel
167
+
168
+ /**
169
+ * Alias for sel
170
+ */
171
+ export const s = helpers.s
172
+
173
+ /**
174
+ * Get selector only in dev/test environments
175
+ */
176
+ export const selDev = helpers.selDev
177
+
178
+ /**
179
+ * Get Cypress selector string [data-cy="..."]
180
+ *
181
+ * @example
182
+ * cySelector('blocks.hero.container') // '[data-cy="block-hero"]'
183
+ */
184
+ export const cySelector = helpers.cySelector
185
+
186
+ /**
187
+ * Create entity-specific selector helpers
188
+ */
189
+ export const entitySelectors = helpers.entitySelectors
190
+
191
+ /**
192
+ * Type exports
193
+ */
194
+ export type ThemeSelectorsType = typeof THEME_SELECTORS
195
+ export type BlockSelectorsType = typeof BLOCK_SELECTORS
196
+ export type { Replacements } from '@nextsparkjs/core/lib/test/selector-factory'
197
+ export { CORE_SELECTORS }
@@ -0,0 +1,219 @@
1
+ {
2
+ "pages": {
3
+ "title": "Seiten",
4
+ "description": "Verwalten Sie Ihre dynamischen Seiten",
5
+ "loading": "Seiten werden geladen...",
6
+ "empty": "Keine Seiten gefunden. Erstellen Sie Ihre erste Seite, um zu beginnen.",
7
+ "search": {
8
+ "placeholder": "Seiten suchen..."
9
+ },
10
+ "filters": {
11
+ "locale": "Sprache",
12
+ "allLocales": "Alle Sprachen",
13
+ "status": "Status",
14
+ "allStatuses": "Alle Status",
15
+ "published": "Veroeffentlicht",
16
+ "draft": "Entwurf"
17
+ },
18
+ "table": {
19
+ "title": "Titel",
20
+ "slug": "Slug",
21
+ "locale": "Sprache",
22
+ "status": "Status",
23
+ "updated": "Zuletzt aktualisiert"
24
+ },
25
+ "status": {
26
+ "published": "Veroeffentlicht",
27
+ "draft": "Entwurf",
28
+ "unsaved": "Ungespeicherte Aenderungen"
29
+ },
30
+ "actions": {
31
+ "create": "Seite erstellen",
32
+ "edit": "Bearbeiten",
33
+ "view": "Ansehen",
34
+ "delete": "Loeschen",
35
+ "save": "Speichern",
36
+ "saving": "Speichern...",
37
+ "publish": "Veroeffentlichen",
38
+ "preview": "Vorschau"
39
+ },
40
+ "new": {
41
+ "title": "Neue Seite erstellen",
42
+ "description": "Erstellen Sie eine neue dynamische Seite fuer Ihre Website",
43
+ "back": "Zurueck zu Seiten",
44
+ "success": "Seite erfolgreich erstellt",
45
+ "form": {
46
+ "title": "Seitendetails",
47
+ "description": "Geben Sie die grundlegenden Informationen Ihrer Seite ein",
48
+ "fields": {
49
+ "title": "Seitentitel",
50
+ "slug": "URL-Slug",
51
+ "locale": "Sprache"
52
+ },
53
+ "placeholders": {
54
+ "title": "Ueber uns",
55
+ "slug": "ueber-uns"
56
+ },
57
+ "hints": {
58
+ "slug": "URL-freundliche Version des Titels (Kleinbuchstaben, keine Leerzeichen)"
59
+ },
60
+ "actions": {
61
+ "cancel": "Abbrechen",
62
+ "create": "Seite erstellen",
63
+ "creating": "Erstellen..."
64
+ }
65
+ }
66
+ },
67
+ "edit": {
68
+ "title": "Seite bearbeiten",
69
+ "loading": "Seite wird geladen...",
70
+ "back": "Zurueck zu Seiten",
71
+ "fields": {
72
+ "title": "Titel",
73
+ "slug": "Slug"
74
+ },
75
+ "placeholders": {
76
+ "title": "Seitentitel",
77
+ "slug": "seiten-slug"
78
+ },
79
+ "status": {
80
+ "published": "Veroeffentlicht",
81
+ "unsaved": "Ungespeicherte Aenderungen"
82
+ },
83
+ "actions": {
84
+ "save": "Speichern",
85
+ "saving": "Speichern...",
86
+ "publish": "Veroeffentlichen",
87
+ "preview": "Vorschau",
88
+ "viewPage": "Ansehen"
89
+ },
90
+ "viewMode": {
91
+ "layout": "Layout",
92
+ "preview": "Vorschau"
93
+ },
94
+ "messages": {
95
+ "saved": "Seite erfolgreich gespeichert"
96
+ }
97
+ },
98
+ "settings": {
99
+ "title": "Seiteneinstellungen",
100
+ "seo": {
101
+ "title": "SEO-Einstellungen",
102
+ "metaTitle": "Meta-Titel",
103
+ "metaTitlePlaceholder": "Geben Sie den Titel fuer Suchmaschinen ein",
104
+ "metaTitleHint": "Empfohlen: 50-60 Zeichen",
105
+ "metaDescription": "Meta-Beschreibung",
106
+ "metaDescriptionPlaceholder": "Geben Sie eine kurze Beschreibung fuer Suchmaschinen ein",
107
+ "characters": "Zeichen",
108
+ "metaKeywords": "Schluesselwoerter",
109
+ "metaKeywordsPlaceholder": "wort1, wort2, wort3",
110
+ "metaKeywordsHint": "Durch Komma getrennte Schluesselwoerter",
111
+ "ogImage": "Social-Media-Bild (OG Image)",
112
+ "ogImagePlaceholder": "https://beispiel.de/bild.jpg",
113
+ "ogImageHint": "Bild, das beim Teilen in sozialen Netzwerken angezeigt wird"
114
+ },
115
+ "customFields": {
116
+ "title": "Benutzerdefinierte Felder",
117
+ "empty": "Keine benutzerdefinierten Felder hinzugefuegt",
118
+ "keyPlaceholder": "Feldschluessel",
119
+ "valuePlaceholder": "Feldwert",
120
+ "add": "Feld hinzufuegen"
121
+ }
122
+ }
123
+ },
124
+ "blockEditor": {
125
+ "picker": {
126
+ "title": "Block-Bibliothek",
127
+ "description": "Bloecke ziehen oder klicken zum Hinzufuegen",
128
+ "empty": "Keine Bloecke gefunden",
129
+ "search": {
130
+ "placeholder": "Bloecke suchen..."
131
+ },
132
+ "categories": {
133
+ "all": "Alle"
134
+ }
135
+ },
136
+ "canvas": {
137
+ "empty": {
138
+ "message": "Noch keine Bloecke hinzugefuegt",
139
+ "hint": "Ziehen Sie Bloecke aus der Bibliothek, um Ihre Seite zu erstellen"
140
+ }
141
+ },
142
+ "block": {
143
+ "error": {
144
+ "notFound": "Block '{slug}' nicht gefunden"
145
+ }
146
+ },
147
+ "settings": {
148
+ "empty": {
149
+ "message": "Kein Block ausgewaehlt",
150
+ "hint": "Waehlen Sie einen Block auf der Arbeitsflaeche aus, um seine Einstellungen zu bearbeiten"
151
+ },
152
+ "error": {
153
+ "notFound": "Block-Einstellungen nicht gefunden"
154
+ },
155
+ "noFields": "Dieser Block hat keine konfigurierbaren Felder",
156
+ "actions": {
157
+ "reset": "Zuruecksetzen",
158
+ "remove": "Entfernen"
159
+ },
160
+ "tabs": {
161
+ "content": "Inhalt",
162
+ "design": "Design",
163
+ "advanced": "Erweitert",
164
+ "noContentFields": "Keine Inhaltsfelder verfuegbar",
165
+ "noDesignFields": "Keine Designfelder verfuegbar",
166
+ "noAdvancedFields": "Keine erweiterten Felder verfuegbar"
167
+ }
168
+ },
169
+ "form": {
170
+ "selectPlaceholder": "Option auswaehlen",
171
+ "emptyArray": "Keine Elemente hinzugefuegt",
172
+ "itemNumber": "Element {number}",
173
+ "addItem": "Element hinzufuegen",
174
+ "itemCount": "{current} von {max} Elementen"
175
+ }
176
+ },
177
+ "builder": {
178
+ "back": "Zurueck",
179
+ "loading": "Laden...",
180
+ "fields": {
181
+ "title": "Titel",
182
+ "slug": "Slug"
183
+ },
184
+ "placeholders": {
185
+ "title": "Titel eingeben...",
186
+ "slug": "url-slug",
187
+ "excerpt": "Kurze Beschreibung...",
188
+ "imageUrl": "https://beispiel.de/bild.jpg"
189
+ },
190
+ "status": {
191
+ "published": "Veroeffentlicht",
192
+ "draft": "Entwurf",
193
+ "scheduled": "Geplant",
194
+ "archived": "Archiviert",
195
+ "unsaved": "Ungespeicherte Aenderungen"
196
+ },
197
+ "actions": {
198
+ "save": "Speichern",
199
+ "saving": "Speichern...",
200
+ "viewPage": "Ansehen"
201
+ },
202
+ "viewMode": {
203
+ "layout": "Layout",
204
+ "preview": "Vorschau"
205
+ },
206
+ "sidebar": {
207
+ "blocks": "Bloecke",
208
+ "fields": "Felder",
209
+ "fieldsTitle": "Entitaetsfelder",
210
+ "fieldsDescription": "Zusaetzliche Eigenschaften konfigurieren",
211
+ "noFields": "Keine zusaetzlichen Felder verfuegbar",
212
+ "noCategories": "Keine Kategorien verfuegbar"
213
+ },
214
+ "messages": {
215
+ "saved": "Erfolgreich gespeichert",
216
+ "created": "Erfolgreich erstellt"
217
+ }
218
+ }
219
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "title": "KI-Nutzung",
3
+ "description": "Ueberwachen Sie Ihre KI-Token-Nutzung und Kosten",
4
+ "loading": "Nutzungsdaten werden geladen...",
5
+ "error": "Fehler beim Laden der Nutzungsdaten",
6
+ "period": {
7
+ "today": "Heute",
8
+ "7d": "7 Tage",
9
+ "30d": "30 Tage",
10
+ "all": "Gesamte Zeit"
11
+ },
12
+ "stats": {
13
+ "totalTokens": {
14
+ "title": "Tokens Gesamt",
15
+ "input": "Eingabe",
16
+ "output": "Ausgabe"
17
+ },
18
+ "estimatedCost": {
19
+ "title": "Geschaetzte Kosten",
20
+ "currency": "EUR"
21
+ },
22
+ "requests": {
23
+ "title": "Anfragen",
24
+ "description": "Gesamte KI-Anfragen"
25
+ }
26
+ },
27
+ "usageByModel": {
28
+ "title": "Nutzung nach Modell",
29
+ "tokens": "Tokens"
30
+ },
31
+ "dailyUsage": {
32
+ "title": "Taegliche Nutzung",
33
+ "noData": "Keine Nutzungsdaten fuer diesen Zeitraum",
34
+ "tokens": "Tokens"
35
+ }
36
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "save": "Speichern",
3
+ "cancel": "Abbrechen",
4
+ "delete": "Loeschen",
5
+ "edit": "Bearbeiten",
6
+ "add": "Hinzufuegen",
7
+ "update": "Aktualisieren",
8
+ "submit": "Absenden",
9
+ "close": "Schliessen",
10
+ "back": "Zurueck",
11
+ "next": "Weiter",
12
+ "previous": "Zurueck",
13
+ "continue": "Fortfahren",
14
+ "finish": "Abschliessen",
15
+ "signIn": "Anmelden",
16
+ "signUp": "Registrieren",
17
+ "signOut": "Abmelden",
18
+ "signingOut": "Abmelden..."
19
+ }