@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,50 @@
1
+ import { z } from 'zod'
2
+ import { baseBlockSchema } from '@nextsparkjs/core/types/blocks'
3
+
4
+ /**
5
+ * Pricing Plan Item Schema
6
+ * Individual pricing plan in the table
7
+ */
8
+ const planItemSchema = z.object({
9
+ name: z.string().min(1, 'Plan name is required').max(50),
10
+ price: z.string().min(1, 'Price is required').max(50),
11
+ period: z.string().max(50).optional(),
12
+ description: z.string().max(200).optional(),
13
+ features: z.string().optional(), // Newline-separated features
14
+ ctaText: z.string().max(50).optional(),
15
+ ctaUrl: z.string().optional(),
16
+ isPopular: z.boolean().default(false),
17
+ isDisabled: z.boolean().default(false),
18
+ })
19
+
20
+ export type PlanItem = z.infer<typeof planItemSchema>
21
+
22
+ /**
23
+ * Pricing Table Block Schema
24
+ *
25
+ * Extends base schema with:
26
+ * - title: Section title (from base)
27
+ * - subtitle: Section description (from base content field)
28
+ * - plans: Array of pricing plans
29
+ * - columns: Grid layout option (2, 3, 4 columns)
30
+ * - highlightPopular: Whether to highlight popular plan
31
+ *
32
+ * Note: Uses base schema title, content, backgroundColor, className, id
33
+ */
34
+ export const pricingTableSpecificSchema = z.object({
35
+ // Content: array of pricing plans
36
+ plans: z.array(planItemSchema)
37
+ .min(1, 'At least one plan is required')
38
+ .max(4, 'Maximum 4 plans allowed'),
39
+
40
+ // Design: column layout and highlighting
41
+ columns: z.enum(['2', '3', '4']).default('3'),
42
+ highlightPopular: z.boolean().default(true),
43
+ })
44
+
45
+ /**
46
+ * Complete Pricing Table Block Schema
47
+ */
48
+ export const schema = baseBlockSchema.merge(pricingTableSpecificSchema)
49
+
50
+ export type PricingTableBlockProps = z.infer<typeof schema>
@@ -0,0 +1,135 @@
1
+ import React from 'react'
2
+ import Image from 'next/image'
3
+ import { Button } from '@nextsparkjs/core/components/ui/button'
4
+ import { Check } from 'lucide-react'
5
+ import { cn } from '@nextsparkjs/core/lib/utils'
6
+ import { buildSectionClasses } from '@nextsparkjs/core/types/blocks'
7
+ import { sel } from '../../lib/selectors'
8
+ import type { SplitContentBlockProps, BulletPoint } from './schema'
9
+
10
+ /**
11
+ * Split Content Block Component
12
+ *
13
+ * Props from 3-tab structure:
14
+ * - Content: subtitle, title, content, image, imageAlt, bulletPoints, cta
15
+ * - Design: backgroundColor, imagePosition, imageStyle, verticalAlign
16
+ * - Advanced: className, id
17
+ */
18
+ export function SplitContentBlock({
19
+ // Content props
20
+ subtitle,
21
+ title,
22
+ content,
23
+ image,
24
+ imageAlt,
25
+ bulletPoints,
26
+ cta,
27
+ // Design props
28
+ backgroundColor,
29
+ imagePosition = 'left',
30
+ imageStyle = 'rounded',
31
+ verticalAlign = 'center',
32
+ // Advanced props
33
+ className,
34
+ id,
35
+ }: SplitContentBlockProps) {
36
+ // Safe fallback for bulletPoints array
37
+ const safeBulletPoints = bulletPoints ?? []
38
+
39
+ // Build section classes with background and custom className
40
+ const sectionClasses = buildSectionClasses(
41
+ 'py-16 px-4 md:py-24',
42
+ { backgroundColor, className }
43
+ )
44
+
45
+ // Image style classes
46
+ const imageStyleClasses = {
47
+ square: '',
48
+ rounded: 'rounded-lg',
49
+ circle: 'rounded-full',
50
+ }
51
+
52
+ // Vertical alignment classes
53
+ const alignmentClasses = {
54
+ top: 'items-start',
55
+ center: 'items-center',
56
+ bottom: 'items-end',
57
+ }
58
+
59
+ // Grid order classes based on image position
60
+ const imageOrderClass = imagePosition === 'right' ? 'lg:order-2' : 'lg:order-1'
61
+ const contentOrderClass = imagePosition === 'right' ? 'lg:order-1' : 'lg:order-2'
62
+
63
+ return (
64
+ <section id={id} className={sectionClasses} data-cy={sel('blocks.splitContent.container')}>
65
+ <div className="container mx-auto max-w-7xl">
66
+ <div className={cn('grid gap-8 lg:grid-cols-2 lg:gap-12', alignmentClasses[verticalAlign as 'top' | 'center' | 'bottom'])}>
67
+ {/* Image Column */}
68
+ <div className={cn('relative', imageOrderClass)}>
69
+ <div className="relative aspect-[4/3] w-full overflow-hidden">
70
+ <Image
71
+ src={image}
72
+ alt={imageAlt || title || 'Split content image'}
73
+ fill
74
+ className={cn('object-cover', imageStyleClasses[imageStyle as 'square' | 'rounded' | 'circle'])}
75
+ sizes="(max-width: 768px) 100vw, 50vw"
76
+ />
77
+ </div>
78
+ </div>
79
+
80
+ {/* Content Column */}
81
+ <div className={cn('flex flex-col justify-center', contentOrderClass)}>
82
+ {subtitle && (
83
+ <p className="mb-2 text-sm font-medium uppercase tracking-wide text-primary">
84
+ {subtitle}
85
+ </p>
86
+ )}
87
+
88
+ {title && (
89
+ <h2 className="mb-4 text-3xl font-bold md:text-4xl lg:text-5xl">
90
+ {title}
91
+ </h2>
92
+ )}
93
+
94
+ {content && (
95
+ <p className="mb-6 text-lg text-muted-foreground">
96
+ {content}
97
+ </p>
98
+ )}
99
+
100
+ {/* Bullet Points */}
101
+ {safeBulletPoints.length > 0 && (
102
+ <ul className="mb-6 space-y-3">
103
+ {safeBulletPoints.map((point: BulletPoint, index: number) => (
104
+ <li key={index} className="flex items-start gap-3">
105
+ <Check className="mt-1 h-5 w-5 flex-shrink-0 text-primary" />
106
+ <span className="text-base">{point.text}</span>
107
+ </li>
108
+ ))}
109
+ </ul>
110
+ )}
111
+
112
+ {/* CTA Button */}
113
+ {cta && cta.text && cta.link && (
114
+ <div className="mt-2">
115
+ <Button
116
+ asChild
117
+ size="lg"
118
+ variant={cta.variant || 'default'}
119
+ >
120
+ <a
121
+ href={cta.link}
122
+ target={cta.target}
123
+ rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
124
+ >
125
+ {cta.text}
126
+ </a>
127
+ </Button>
128
+ </div>
129
+ )}
130
+ </div>
131
+ </div>
132
+ </div>
133
+ </section>
134
+ )
135
+ }
@@ -0,0 +1,11 @@
1
+ import type { BlockConfig } from '@nextsparkjs/core/types/blocks'
2
+
3
+ export const config: Omit<BlockConfig, 'schema' | 'fieldDefinitions' | 'Component' | 'examples'> = {
4
+ slug: 'split-content',
5
+ name: 'Split Content',
6
+ description: 'A two-column section with image on one side and text content on the other, with option to reverse the order',
7
+ category: 'content',
8
+ icon: 'LayoutGrid',
9
+ thumbnail: '/theme/blocks/split-content/thumbnail.png',
10
+ scope: ['pages', 'posts']
11
+ }
@@ -0,0 +1,38 @@
1
+ import type { BlockExample } from '@nextsparkjs/core/types/blocks'
2
+
3
+ export const examples: BlockExample[] = [
4
+ {
5
+ name: 'Image Left',
6
+ description: 'Split layout with image on left',
7
+ props: {
8
+ title: 'Built for Teams',
9
+ content: 'Collaborate seamlessly with powerful tools designed for modern workplaces. Share ideas, track progress, and achieve more together.',
10
+ imageUrl: 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=800',
11
+ imageAlt: 'Team collaboration',
12
+ imagePosition: 'left',
13
+ cta: {
14
+ text: 'Learn More',
15
+ link: '/features',
16
+ target: '_self',
17
+ },
18
+ backgroundColor: 'white',
19
+ },
20
+ },
21
+ {
22
+ name: 'Image Right',
23
+ description: 'Split layout with image on right',
24
+ props: {
25
+ title: 'Analytics That Matter',
26
+ content: 'Make data-driven decisions with comprehensive analytics and reporting. Gain insights into user behavior, track key metrics, and optimize your strategy.',
27
+ imageUrl: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800',
28
+ imageAlt: 'Analytics dashboard',
29
+ imagePosition: 'right',
30
+ cta: {
31
+ text: 'View Dashboard',
32
+ link: '/analytics',
33
+ target: '_self',
34
+ },
35
+ backgroundColor: 'gray-50',
36
+ },
37
+ },
38
+ ]
@@ -0,0 +1,198 @@
1
+ import type { FieldDefinition } from '@nextsparkjs/core/types/blocks'
2
+ import {
3
+ baseDesignFields,
4
+ baseAdvancedFields,
5
+ } from '@nextsparkjs/core/types/blocks'
6
+
7
+ /**
8
+ * Split Content Block Field Definitions
9
+ *
10
+ * Organized into 3 tabs:
11
+ * - Content: subtitle, title, content, image, imageAlt, bulletPoints, cta
12
+ * - Design: backgroundColor (from base) + imagePosition, imageStyle, verticalAlign
13
+ * - Advanced: className, id (from base)
14
+ */
15
+
16
+ // Split Content specific content fields
17
+ const splitContentFields: FieldDefinition[] = [
18
+ {
19
+ name: 'subtitle',
20
+ label: 'Subtitle',
21
+ type: 'text',
22
+ tab: 'content',
23
+ required: false,
24
+ placeholder: 'Eyebrow text',
25
+ helpText: 'Optional label or eyebrow text above the title',
26
+ },
27
+ {
28
+ name: 'title',
29
+ label: 'Title',
30
+ type: 'text',
31
+ tab: 'content',
32
+ required: false,
33
+ placeholder: 'Main headline',
34
+ helpText: 'Main section headline',
35
+ },
36
+ {
37
+ name: 'content',
38
+ label: 'Content',
39
+ type: 'textarea',
40
+ tab: 'content',
41
+ required: false,
42
+ placeholder: 'Main description text...',
43
+ helpText: 'Main text content/description',
44
+ rows: 4,
45
+ },
46
+ {
47
+ name: 'image',
48
+ label: 'Image',
49
+ type: 'image',
50
+ tab: 'content',
51
+ required: true,
52
+ helpText: 'Featured image (recommended: 800x600px minimum)',
53
+ },
54
+ {
55
+ name: 'imageAlt',
56
+ label: 'Image Alt Text',
57
+ type: 'text',
58
+ tab: 'content',
59
+ required: false,
60
+ placeholder: 'Describe the image',
61
+ helpText: 'Alternative text for accessibility',
62
+ },
63
+ {
64
+ name: 'bulletPoints',
65
+ label: 'Bullet Points',
66
+ type: 'array',
67
+ tab: 'content',
68
+ required: false,
69
+ description: 'Optional list of bullet points',
70
+ helpText: 'Add up to 10 bullet points',
71
+ maxItems: 10,
72
+ itemFields: [
73
+ {
74
+ name: 'text',
75
+ label: 'Bullet Point',
76
+ type: 'text',
77
+ tab: 'content',
78
+ required: true,
79
+ placeholder: 'Bullet point text',
80
+ maxLength: 200,
81
+ },
82
+ ],
83
+ },
84
+ {
85
+ name: 'cta',
86
+ label: 'Call to Action',
87
+ type: 'text',
88
+ tab: 'content',
89
+ required: false,
90
+ description: 'Optional CTA button',
91
+ helpText: 'Configure button text, link, target, and variant',
92
+ },
93
+ {
94
+ name: 'cta.text',
95
+ label: 'CTA Text',
96
+ type: 'text',
97
+ tab: 'content',
98
+ required: false,
99
+ placeholder: 'Get Started',
100
+ },
101
+ {
102
+ name: 'cta.link',
103
+ label: 'CTA Link',
104
+ type: 'url',
105
+ tab: 'content',
106
+ required: false,
107
+ placeholder: 'https://example.com or /path',
108
+ },
109
+ {
110
+ name: 'cta.target',
111
+ label: 'CTA Target',
112
+ type: 'select',
113
+ tab: 'content',
114
+ required: false,
115
+ default: '_self',
116
+ options: [
117
+ { label: 'Same Window', value: '_self' },
118
+ { label: 'New Window', value: '_blank' },
119
+ ],
120
+ },
121
+ {
122
+ name: 'cta.variant',
123
+ label: 'CTA Variant',
124
+ type: 'select',
125
+ tab: 'content',
126
+ required: false,
127
+ default: 'default',
128
+ helpText: 'Button style variant',
129
+ options: [
130
+ { label: 'Default', value: 'default' },
131
+ { label: 'Outline', value: 'outline' },
132
+ { label: 'Secondary', value: 'secondary' },
133
+ ],
134
+ },
135
+ ]
136
+
137
+ // Split Content specific design fields
138
+ const splitContentDesignFields: FieldDefinition[] = [
139
+ {
140
+ name: 'imagePosition',
141
+ label: 'Image Position',
142
+ type: 'select',
143
+ tab: 'design',
144
+ required: false,
145
+ default: 'left',
146
+ description: 'Position of the image in the layout',
147
+ options: [
148
+ { label: 'Left', value: 'left' },
149
+ { label: 'Right', value: 'right' },
150
+ ],
151
+ },
152
+ {
153
+ name: 'imageStyle',
154
+ label: 'Image Style',
155
+ type: 'select',
156
+ tab: 'design',
157
+ required: false,
158
+ default: 'rounded',
159
+ description: 'Image corner style',
160
+ options: [
161
+ { label: 'Square', value: 'square' },
162
+ { label: 'Rounded', value: 'rounded' },
163
+ { label: 'Circle', value: 'circle' },
164
+ ],
165
+ },
166
+ {
167
+ name: 'verticalAlign',
168
+ label: 'Vertical Alignment',
169
+ type: 'select',
170
+ tab: 'design',
171
+ required: false,
172
+ default: 'center',
173
+ description: 'Vertical alignment of content',
174
+ options: [
175
+ { label: 'Top', value: 'top' },
176
+ { label: 'Center', value: 'center' },
177
+ { label: 'Bottom', value: 'bottom' },
178
+ ],
179
+ },
180
+ ]
181
+
182
+ /**
183
+ * Complete field definitions organized by tab
184
+ */
185
+ export const fieldDefinitions: FieldDefinition[] = [
186
+ // Content tab: split-content specific fields
187
+ ...splitContentFields,
188
+
189
+ // Design tab: base fields + split-content specific
190
+ ...baseDesignFields,
191
+ ...splitContentDesignFields,
192
+
193
+ // Advanced tab: base fields only
194
+ ...baseAdvancedFields,
195
+ ]
196
+
197
+ // Alias for compatibility
198
+ export const fields = fieldDefinitions
@@ -0,0 +1,6 @@
1
+ export { config } from './config'
2
+ export { schema } from './schema'
3
+ export { fields } from './fields'
4
+ export { SplitContentBlock as Component } from './component'
5
+
6
+ export type { SplitContentBlockProps } from './schema'
@@ -0,0 +1,67 @@
1
+ import { z } from 'zod'
2
+ import {
3
+ baseBlockSchema,
4
+ ctaSchema,
5
+ type BaseBlockProps,
6
+ } from '@nextsparkjs/core/types/blocks'
7
+
8
+ /**
9
+ * Bullet Point Schema
10
+ * Individual bullet point item
11
+ */
12
+ const bulletPointSchema = z.object({
13
+ text: z.string().min(1, 'Bullet point text is required').max(200),
14
+ })
15
+
16
+ export type BulletPoint = z.infer<typeof bulletPointSchema>
17
+
18
+ /**
19
+ * CTA with Variant Schema
20
+ * Extends base CTA with variant option
21
+ */
22
+ const ctaWithVariantSchema = ctaSchema.extend({
23
+ variant: z.enum(['default', 'outline', 'secondary']).default('default'),
24
+ })
25
+
26
+ export type CTAWithVariant = z.infer<typeof ctaWithVariantSchema>
27
+
28
+ /**
29
+ * Split Content Block Schema
30
+ *
31
+ * Extends base schema with:
32
+ * - subtitle: Optional eyebrow/label above title
33
+ * - image: Featured image URL (required)
34
+ * - imageAlt: Image alt text for accessibility
35
+ * - cta: CTA button with variant option
36
+ * - bulletPoints: Optional list of bullet points
37
+ * - imagePosition: Image position (left/right)
38
+ * - imageStyle: Image corner style (square/rounded/circle)
39
+ * - verticalAlign: Vertical content alignment
40
+ *
41
+ * Note: Uses base schema title, content, backgroundColor, className, id
42
+ */
43
+ export const splitContentSpecificSchema = z.object({
44
+ // Content fields
45
+ subtitle: z.string().optional(),
46
+ image: z.string().url('Must be a valid image URL').min(1, 'Image is required'),
47
+ imageAlt: z.string().optional(),
48
+ cta: ctaWithVariantSchema.optional(),
49
+ bulletPoints: z.array(bulletPointSchema).max(10, 'Maximum 10 bullet points allowed').optional(),
50
+
51
+ // Design fields
52
+ imagePosition: z.enum(['left', 'right']).default('left'),
53
+ imageStyle: z.enum(['square', 'rounded', 'circle']).default('rounded'),
54
+ verticalAlign: z.enum(['top', 'center', 'bottom']).default('center'),
55
+ })
56
+
57
+ /**
58
+ * Complete Split Content Block Schema
59
+ */
60
+ export const schema = baseBlockSchema
61
+ .omit({ cta: true }) // Remove base cta
62
+ .merge(splitContentSpecificSchema)
63
+
64
+ export type SplitContentBlockProps = z.infer<typeof schema>
65
+
66
+ // Also export for type-only imports
67
+ export type { BaseBlockProps }
@@ -0,0 +1,124 @@
1
+ import React from 'react'
2
+ import { cn } from '@nextsparkjs/core/lib/utils'
3
+ import { buildSectionClasses } from '@nextsparkjs/core/types/blocks'
4
+ import { sel } from '../../lib/selectors'
5
+ import type { StatsCounterBlockProps, StatItem } from './schema'
6
+
7
+ /**
8
+ * Stats Counter Block Component
9
+ *
10
+ * Props from 3-tab structure:
11
+ * - Content: title, content, cta, stats
12
+ * - Design: backgroundColor, columns, variant, size
13
+ * - Advanced: className, id
14
+ */
15
+ export function StatsCounterBlock({
16
+ // Base content props
17
+ title,
18
+ content,
19
+ cta,
20
+ // Stats-specific content
21
+ stats,
22
+ // Base design props
23
+ backgroundColor,
24
+ // Stats-specific design
25
+ columns = '4',
26
+ variant = 'default',
27
+ size = 'md',
28
+ // Base advanced props
29
+ className,
30
+ id,
31
+ }: StatsCounterBlockProps) {
32
+ // Safely handle stats array
33
+ const safeStats = Array.isArray(stats) ? stats : []
34
+
35
+ // Build column classes based on columns prop
36
+ const columnClasses: Record<string, string> = {
37
+ '2': 'sm:grid-cols-2',
38
+ '3': 'sm:grid-cols-2 lg:grid-cols-3',
39
+ '4': 'sm:grid-cols-2 lg:grid-cols-4',
40
+ }
41
+
42
+ // Size classes for numbers
43
+ const sizeClasses: Record<string, string> = {
44
+ sm: 'text-3xl md:text-4xl',
45
+ md: 'text-4xl md:text-5xl',
46
+ lg: 'text-5xl md:text-6xl lg:text-7xl',
47
+ }
48
+
49
+ // Variant-specific stat item classes
50
+ const getStatItemClasses = () => {
51
+ const baseClasses = 'flex flex-col items-center text-center'
52
+
53
+ switch (variant) {
54
+ case 'cards':
55
+ return cn(baseClasses, 'p-6 rounded-lg border bg-card shadow-sm')
56
+ case 'minimal':
57
+ return cn(baseClasses, 'p-4')
58
+ default:
59
+ return cn(baseClasses, 'p-6')
60
+ }
61
+ }
62
+
63
+ // Build section classes with background and custom className
64
+ const sectionClasses = buildSectionClasses(
65
+ 'py-16 px-4 md:py-24',
66
+ { backgroundColor, className }
67
+ )
68
+
69
+ return (
70
+ <section id={id} className={sectionClasses} data-cy={sel('blocks.statsCounter.container')}>
71
+ <div className="container mx-auto max-w-6xl">
72
+ {/* Section Header */}
73
+ {(title || content) && (
74
+ <div className="mb-12 text-center">
75
+ {title && (
76
+ <h2 className="mb-4 text-4xl font-bold md:text-5xl">
77
+ {title}
78
+ </h2>
79
+ )}
80
+ {content && (
81
+ <p className="mx-auto max-w-2xl text-lg text-muted-foreground">
82
+ {content}
83
+ </p>
84
+ )}
85
+ </div>
86
+ )}
87
+
88
+ {/* Stats Grid */}
89
+ <div className={cn('grid gap-8', columnClasses[columns] || columnClasses['4'])}>
90
+ {safeStats.map((stat: StatItem, index: number) => (
91
+ <div key={index} className={getStatItemClasses()}>
92
+ <div className={cn('font-bold mb-2', sizeClasses[size] || sizeClasses['md'])}>
93
+ {stat.prefix && (
94
+ <span className="text-primary">{stat.prefix}</span>
95
+ )}
96
+ {stat.value}
97
+ {stat.suffix && (
98
+ <span className="text-primary">{stat.suffix}</span>
99
+ )}
100
+ </div>
101
+ <div className="text-sm md:text-base text-muted-foreground font-medium">
102
+ {stat.label}
103
+ </div>
104
+ </div>
105
+ ))}
106
+ </div>
107
+
108
+ {/* Optional CTA */}
109
+ {cta && (
110
+ <div className="mt-12 text-center">
111
+ <a
112
+ href={cta.link}
113
+ target={cta.target}
114
+ rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
115
+ className="inline-flex items-center justify-center rounded-md bg-primary px-8 py-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
116
+ >
117
+ {cta.text}
118
+ </a>
119
+ </div>
120
+ )}
121
+ </div>
122
+ </section>
123
+ )
124
+ }
@@ -0,0 +1,11 @@
1
+ import type { BlockConfig } from '@nextsparkjs/core/types/blocks'
2
+
3
+ export const config: Omit<BlockConfig, 'schema' | 'fieldDefinitions' | 'Component' | 'examples'> = {
4
+ slug: 'stats-counter',
5
+ name: 'Stats Counter',
6
+ description: 'A section displaying key metrics/statistics with large numbers and labels (e.g., "10,000+ Customers", "99% Uptime")',
7
+ category: 'stats',
8
+ icon: 'TrendingUp',
9
+ thumbnail: '/theme/blocks/stats-counter/thumbnail.png',
10
+ scope: ['pages', 'posts']
11
+ }