@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,83 @@
1
+ /**
2
+ * Customers Service Types
3
+ *
4
+ * Type definitions for the CustomersService.
5
+ * Customers is a private entity (access.shared: true) - all authenticated
6
+ * users can access all records within their team context.
7
+ *
8
+ * @module CustomersTypes
9
+ */
10
+
11
+ /**
12
+ * Day of week values for visit/contact days
13
+ */
14
+ export type DayOfWeek = 'lun' | 'mar' | 'mie' | 'jue' | 'vie'
15
+
16
+ /**
17
+ * Customer entity
18
+ */
19
+ export interface Customer {
20
+ id: string
21
+ name: string
22
+ account: number
23
+ office: string
24
+ phone?: string
25
+ salesRep?: string
26
+ visitDays?: DayOfWeek[]
27
+ contactDays?: DayOfWeek[]
28
+ createdAt?: string
29
+ updatedAt?: string
30
+ }
31
+
32
+ /**
33
+ * Options for listing customers
34
+ */
35
+ export interface CustomerListOptions {
36
+ limit?: number
37
+ offset?: number
38
+ orderBy?: 'name' | 'account' | 'office' | 'salesRep' | 'createdAt'
39
+ orderDir?: 'asc' | 'desc'
40
+ }
41
+
42
+ /**
43
+ * Result of listing customers with pagination
44
+ */
45
+ export interface CustomerListResult {
46
+ customers: Customer[]
47
+ total: number
48
+ }
49
+
50
+ /**
51
+ * Search options
52
+ */
53
+ export interface CustomerSearchOptions {
54
+ query: string
55
+ limit?: number
56
+ }
57
+
58
+ /**
59
+ * Data required to create a new customer
60
+ */
61
+ export interface CustomerCreateData {
62
+ name: string
63
+ account: number
64
+ office: string
65
+ teamId: string
66
+ phone?: string
67
+ salesRep?: string
68
+ visitDays?: DayOfWeek[]
69
+ contactDays?: DayOfWeek[]
70
+ }
71
+
72
+ /**
73
+ * Data for updating an existing customer
74
+ */
75
+ export interface CustomerUpdateData {
76
+ name?: string
77
+ account?: number
78
+ office?: string
79
+ phone?: string
80
+ salesRep?: string
81
+ visitDays?: DayOfWeek[]
82
+ contactDays?: DayOfWeek[]
83
+ }
@@ -0,0 +1,66 @@
1
+ {
2
+ "entity": {
3
+ "name": "Customer",
4
+ "plural": "Customers",
5
+ "description": "Manage your customers and their information"
6
+ },
7
+ "fields": {
8
+ "office": {
9
+ "label": "Office",
10
+ "description": "Customer office or branch location",
11
+ "placeholder": "Enter office..."
12
+ },
13
+ "number": {
14
+ "label": "Customer Number",
15
+ "description": "Unique customer identifier",
16
+ "placeholder": "Enter customer number..."
17
+ },
18
+ "name": {
19
+ "label": "Name",
20
+ "description": "Customer name",
21
+ "placeholder": "Enter customer name..."
22
+ },
23
+ "phone": {
24
+ "label": "Phone",
25
+ "description": "Customer phone number",
26
+ "placeholder": "Enter phone number..."
27
+ },
28
+ "salesRep": {
29
+ "label": "Sales Representative",
30
+ "description": "Assigned sales representative",
31
+ "placeholder": "Enter sales rep..."
32
+ },
33
+ "visitDays": {
34
+ "label": "Visit Days",
35
+ "description": "Scheduled visit days",
36
+ "placeholder": "Enter visit days..."
37
+ },
38
+ "contactDays": {
39
+ "label": "Contact Days",
40
+ "description": "Preferred contact days",
41
+ "placeholder": "Enter contact days..."
42
+ },
43
+ "image": {
44
+ "label": "Image",
45
+ "description": "Customer logo or image URL",
46
+ "placeholder": "Enter image URL..."
47
+ }
48
+ },
49
+ "actions": {
50
+ "create": "Create Customer",
51
+ "edit": "Edit Customer",
52
+ "delete": "Delete Customer",
53
+ "view": "View Customer",
54
+ "list": "List Customers",
55
+ "search": "Search Customers",
56
+ "export": "Export Customers",
57
+ "import": "Import Customers"
58
+ },
59
+ "messages": {
60
+ "created": "Customer created successfully",
61
+ "updated": "Customer updated successfully",
62
+ "deleted": "Customer deleted successfully",
63
+ "notFound": "Customer not found",
64
+ "error": "An error occurred while processing the customer"
65
+ }
66
+ }
@@ -0,0 +1,66 @@
1
+ {
2
+ "entity": {
3
+ "name": "Cliente",
4
+ "plural": "Clientes",
5
+ "description": "Gestiona tus clientes y su información"
6
+ },
7
+ "fields": {
8
+ "office": {
9
+ "label": "Oficina",
10
+ "description": "Oficina o sucursal del cliente",
11
+ "placeholder": "Ingrese oficina..."
12
+ },
13
+ "number": {
14
+ "label": "Número de Cliente",
15
+ "description": "Identificador único del cliente",
16
+ "placeholder": "Ingrese número de cliente..."
17
+ },
18
+ "name": {
19
+ "label": "Nombre",
20
+ "description": "Nombre del cliente",
21
+ "placeholder": "Ingrese nombre del cliente..."
22
+ },
23
+ "phone": {
24
+ "label": "Teléfono",
25
+ "description": "Número de teléfono del cliente",
26
+ "placeholder": "Ingrese número de teléfono..."
27
+ },
28
+ "salesRep": {
29
+ "label": "Representante de Ventas",
30
+ "description": "Representante de ventas asignado",
31
+ "placeholder": "Ingrese representante de ventas..."
32
+ },
33
+ "visitDays": {
34
+ "label": "Días de Visita",
35
+ "description": "Días programados de visita",
36
+ "placeholder": "Ingrese días de visita..."
37
+ },
38
+ "contactDays": {
39
+ "label": "Días de Contacto",
40
+ "description": "Días preferidos de contacto",
41
+ "placeholder": "Ingrese días de contacto..."
42
+ },
43
+ "image": {
44
+ "label": "Imagen",
45
+ "description": "Logo o URL de imagen del cliente",
46
+ "placeholder": "Ingrese URL de imagen..."
47
+ }
48
+ },
49
+ "actions": {
50
+ "create": "Crear Cliente",
51
+ "edit": "Editar Cliente",
52
+ "delete": "Eliminar Cliente",
53
+ "view": "Ver Cliente",
54
+ "list": "Listar Clientes",
55
+ "search": "Buscar Clientes",
56
+ "export": "Exportar Clientes",
57
+ "import": "Importar Clientes"
58
+ },
59
+ "messages": {
60
+ "created": "Cliente creado exitosamente",
61
+ "updated": "Cliente actualizado exitosamente",
62
+ "deleted": "Cliente eliminado exitosamente",
63
+ "notFound": "Cliente no encontrado",
64
+ "error": "Ocurrió un error al procesar el cliente"
65
+ }
66
+ }
@@ -0,0 +1,102 @@
1
+ -- Migration: 001_customers_table.sql
2
+ -- Description: Customers (table, indexes, RLS)
3
+ -- Date: 2025-01-24
4
+ -- Updated: 2025-11-26 (Phase 2 - Team Isolation)
5
+
6
+ -- ============================================
7
+ -- TABLE
8
+ -- ============================================
9
+ DROP TABLE IF EXISTS public."customers" CASCADE;
10
+
11
+ CREATE TABLE IF NOT EXISTS public."customers" (
12
+ -- Primary Key
13
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
14
+
15
+ -- Relational Fields (al inicio)
16
+ "userId" TEXT NOT NULL REFERENCES public."users"(id) ON DELETE CASCADE,
17
+ "teamId" TEXT NOT NULL REFERENCES public."teams"(id) ON DELETE CASCADE,
18
+
19
+ -- Entity-specific fields
20
+ office TEXT NOT NULL,
21
+ account INTEGER NOT NULL UNIQUE,
22
+ name TEXT NOT NULL,
23
+ phone TEXT,
24
+ "salesRep" TEXT,
25
+ "visitDays" JSONB DEFAULT '[]'::jsonb,
26
+ "contactDays" JSONB DEFAULT '[]'::jsonb,
27
+
28
+ -- System fields
29
+ "createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
30
+ "updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now()
31
+ );
32
+
33
+ COMMENT ON TABLE public."customers" IS 'Customers table with team isolation via RLS';
34
+ COMMENT ON COLUMN public."customers"."userId" IS 'User who created this customer record';
35
+ COMMENT ON COLUMN public."customers"."teamId" IS 'Team context for isolation';
36
+ COMMENT ON COLUMN public."customers".office IS 'Customer office or branch';
37
+ COMMENT ON COLUMN public."customers".account IS 'Unique customer account number (used for identification and external integrations)';
38
+ COMMENT ON COLUMN public."customers".name IS 'Customer name or company name';
39
+ COMMENT ON COLUMN public."customers".phone IS 'Customer phone number';
40
+ COMMENT ON COLUMN public."customers"."salesRep" IS 'Sales representative assigned to customer';
41
+ COMMENT ON COLUMN public."customers"."visitDays" IS 'Days of the week for customer visits as JSONB array';
42
+ COMMENT ON COLUMN public."customers"."contactDays" IS 'Days of the week for customer contact as JSONB array';
43
+
44
+ -- ============================================
45
+ -- TRIGGER updatedAt (uses Better Auth function)
46
+ -- ============================================
47
+ DROP TRIGGER IF EXISTS customer_set_updated_at ON public."customers";
48
+ CREATE TRIGGER customer_set_updated_at
49
+ BEFORE UPDATE ON public."customers"
50
+ FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
51
+
52
+ -- ============================================
53
+ -- INDEXES
54
+ -- ============================================
55
+ CREATE INDEX IF NOT EXISTS idx_customer_user_id ON public."customers"("userId");
56
+ CREATE INDEX IF NOT EXISTS idx_customer_team_id ON public."customers"("teamId");
57
+ CREATE INDEX IF NOT EXISTS idx_customer_office ON public."customers"(office);
58
+ CREATE INDEX IF NOT EXISTS idx_customer_account ON public."customers"(account);
59
+ CREATE INDEX IF NOT EXISTS idx_customer_name ON public."customers"(name);
60
+ CREATE INDEX IF NOT EXISTS idx_customer_sales_rep ON public."customers"("salesRep");
61
+ CREATE INDEX IF NOT EXISTS idx_customer_created_at ON public."customers"("createdAt");
62
+ CREATE INDEX IF NOT EXISTS idx_customer_team_created ON public."customers"("teamId", "createdAt" DESC);
63
+
64
+ -- JSONB indexes
65
+ CREATE INDEX IF NOT EXISTS idx_customer_visit_days_gin ON public."customers" USING GIN ("visitDays");
66
+ CREATE INDEX IF NOT EXISTS idx_customer_contact_days_gin ON public."customers" USING GIN ("contactDays");
67
+
68
+ -- ============================================
69
+ -- RLS
70
+ -- ============================================
71
+ ALTER TABLE public."customers" ENABLE ROW LEVEL SECURITY;
72
+
73
+ -- Cleanup existing policies
74
+ DROP POLICY IF EXISTS "Users can view own customers" ON public."customers";
75
+ DROP POLICY IF EXISTS "Users can create customers" ON public."customers";
76
+ DROP POLICY IF EXISTS "Users can insert own customers" ON public."customers";
77
+ DROP POLICY IF EXISTS "Users can update own customers" ON public."customers";
78
+ DROP POLICY IF EXISTS "Users can delete own customers" ON public."customers";
79
+ DROP POLICY IF EXISTS "Customers auth can do all" ON public."customers";
80
+ DROP POLICY IF EXISTS "Customers team can do all" ON public."customers";
81
+
82
+ -- ============================
83
+ -- RLS: TEAM ISOLATION ONLY
84
+ -- ============================
85
+ -- IMPORTANTE: RLS solo verifica team membership
86
+ -- La lógica de access.shared (user isolation) se maneja a NIVEL APP
87
+ -- Esto permite cambiar el comportamiento desde el config sin modificar RLS
88
+ CREATE POLICY "Customers team can do all"
89
+ ON public."customers"
90
+ FOR ALL TO authenticated
91
+ USING (
92
+ -- Superadmin bypass
93
+ public.is_superadmin()
94
+ OR
95
+ -- Team isolation only: user must be member of the team
96
+ "teamId" = ANY(public.get_user_team_ids())
97
+ )
98
+ WITH CHECK (
99
+ public.is_superadmin()
100
+ OR
101
+ "teamId" = ANY(public.get_user_team_ids())
102
+ );
@@ -0,0 +1,92 @@
1
+ -- Migration: 002_customers_metas.sql
2
+ -- Description: Customers metas (table, indexes, RLS)
3
+ -- Date: 2025-01-24
4
+ -- Updated: 2025-11-26 (Phase 2 - Team Isolation via parent)
5
+
6
+ -- ============================================
7
+ -- TABLE
8
+ -- ============================================
9
+ -- No DROP needed - removed automatically by parent table CASCADE
10
+ CREATE TABLE IF NOT EXISTS public."customers_metas" (
11
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
12
+ "entityId" TEXT NOT NULL REFERENCES public."customers"(id) ON DELETE CASCADE,
13
+ "metaKey" TEXT NOT NULL,
14
+ "metaValue" JSONB NOT NULL DEFAULT '{}'::jsonb,
15
+ "dataType" TEXT DEFAULT 'json',
16
+ "isPublic" BOOLEAN NOT NULL DEFAULT false,
17
+ "isSearchable" BOOLEAN NOT NULL DEFAULT false,
18
+ "createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
19
+ "updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
20
+ CONSTRAINT customers_metas_unique_key UNIQUE ("entityId", "metaKey")
21
+ );
22
+
23
+ COMMENT ON TABLE public."customers_metas" IS 'Customers metadata table - stores additional key-value pairs for customers';
24
+ COMMENT ON COLUMN public."customers_metas"."entityId" IS 'Generic foreign key to parent customer entity';
25
+ COMMENT ON COLUMN public."customers_metas"."metaKey" IS 'Metadata key name';
26
+ COMMENT ON COLUMN public."customers_metas"."metaValue" IS 'Metadata value as JSONB';
27
+ COMMENT ON COLUMN public."customers_metas"."dataType" IS 'Type hint for the value: json, string, number, boolean';
28
+ COMMENT ON COLUMN public."customers_metas"."isPublic" IS 'Whether this metadata is publicly readable';
29
+ COMMENT ON COLUMN public."customers_metas"."isSearchable" IS 'Whether this metadata is searchable';
30
+
31
+ -- ============================================
32
+ -- TRIGGER updatedAt (uses Better Auth function)
33
+ -- ============================================
34
+ DROP TRIGGER IF EXISTS customers_metas_set_updated_at ON public."customers_metas";
35
+ CREATE TRIGGER customers_metas_set_updated_at
36
+ BEFORE UPDATE ON public."customers_metas"
37
+ FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
38
+
39
+ -- ============================================
40
+ -- INDEXES
41
+ -- ============================================
42
+ CREATE INDEX IF NOT EXISTS idx_customers_metas_customer_id ON public."customers_metas"("entityId");
43
+ CREATE INDEX IF NOT EXISTS idx_customers_metas_key ON public."customers_metas"("metaKey");
44
+ CREATE INDEX IF NOT EXISTS idx_customers_metas_composite ON public."customers_metas"("entityId", "metaKey", "isPublic");
45
+ CREATE INDEX IF NOT EXISTS idx_customers_metas_is_public ON public."customers_metas"("isPublic") WHERE "isPublic" = true;
46
+ CREATE INDEX IF NOT EXISTS idx_customers_metas_is_searchable ON public."customers_metas"("isSearchable") WHERE "isSearchable" = true;
47
+ CREATE INDEX IF NOT EXISTS idx_customers_metas_searchable_key ON public."customers_metas"("metaKey") WHERE "isSearchable" = true;
48
+ CREATE INDEX IF NOT EXISTS idx_customers_metas_value_gin ON public."customers_metas" USING GIN ("metaValue");
49
+ CREATE INDEX IF NOT EXISTS idx_customers_metas_value_ops ON public."customers_metas" USING GIN ("metaValue" jsonb_path_ops);
50
+
51
+ -- ============================================
52
+ -- RLS
53
+ -- ============================================
54
+ ALTER TABLE public."customers_metas" ENABLE ROW LEVEL SECURITY;
55
+
56
+ -- Cleanup existing policies
57
+ DROP POLICY IF EXISTS "Users can view own customer metas" ON public."customers_metas";
58
+ DROP POLICY IF EXISTS "Users can insert customer metas" ON public."customers_metas";
59
+ DROP POLICY IF EXISTS "Users can insert own customer metas" ON public."customers_metas";
60
+ DROP POLICY IF EXISTS "Users can update own customer metas" ON public."customers_metas";
61
+ DROP POLICY IF EXISTS "Users can delete own customer metas" ON public."customers_metas";
62
+ DROP POLICY IF EXISTS "Customer metas auth can do all" ON public."customers_metas";
63
+ DROP POLICY IF EXISTS "Customer metas team can do all" ON public."customers_metas";
64
+
65
+ -- ============================
66
+ -- RLS: TEAM ISOLATION VIA PARENT
67
+ -- ============================
68
+ -- Hereda el aislamiento del parent customer via teamId
69
+ -- La lógica de access.shared se maneja a NIVEL APP
70
+ CREATE POLICY "Customer metas team can do all"
71
+ ON public."customers_metas"
72
+ FOR ALL TO authenticated
73
+ USING (
74
+ -- Superadmin bypass
75
+ public.is_superadmin()
76
+ OR
77
+ -- Team isolation via parent customer
78
+ EXISTS (
79
+ SELECT 1 FROM public."customers" c
80
+ WHERE c.id = "entityId"
81
+ AND c."teamId" = ANY(public.get_user_team_ids())
82
+ )
83
+ )
84
+ WITH CHECK (
85
+ public.is_superadmin()
86
+ OR
87
+ EXISTS (
88
+ SELECT 1 FROM public."customers" c
89
+ WHERE c.id = "entityId"
90
+ AND c."teamId" = ANY(public.get_user_team_ids())
91
+ )
92
+ );
@@ -0,0 +1,41 @@
1
+ {
2
+ "entity": {
3
+ "singular": "Page",
4
+ "plural": "Pages"
5
+ },
6
+ "fields": {
7
+ "title": {
8
+ "label": "Title",
9
+ "placeholder": "Enter page title...",
10
+ "description": "Page title displayed in browser and navigation"
11
+ },
12
+ "slug": {
13
+ "label": "Slug",
14
+ "placeholder": "page-slug",
15
+ "description": "URL-friendly identifier (e.g., 'about' for /about)"
16
+ },
17
+ "status": {
18
+ "label": "Status",
19
+ "description": "Publication status",
20
+ "options": {
21
+ "draft": "Draft",
22
+ "published": "Published",
23
+ "scheduled": "Scheduled",
24
+ "archived": "Archived"
25
+ }
26
+ }
27
+ },
28
+ "actions": {
29
+ "create": "Create Page",
30
+ "edit": "Edit Page",
31
+ "delete": "Delete Page",
32
+ "publish": "Publish",
33
+ "unpublish": "Unpublish"
34
+ },
35
+ "messages": {
36
+ "created": "Page created successfully",
37
+ "updated": "Page updated successfully",
38
+ "deleted": "Page deleted successfully",
39
+ "published": "Page published successfully"
40
+ }
41
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "entity": {
3
+ "singular": "Pagina",
4
+ "plural": "Paginas"
5
+ },
6
+ "fields": {
7
+ "title": {
8
+ "label": "Titulo",
9
+ "placeholder": "Ingresa el titulo de la pagina...",
10
+ "description": "Titulo de la pagina mostrado en el navegador y navegacion"
11
+ },
12
+ "slug": {
13
+ "label": "Slug",
14
+ "placeholder": "slug-pagina",
15
+ "description": "Identificador amigable para URL (ej: 'acerca' para /acerca)"
16
+ },
17
+ "status": {
18
+ "label": "Estado",
19
+ "description": "Estado de publicacion",
20
+ "options": {
21
+ "draft": "Borrador",
22
+ "published": "Publicado",
23
+ "scheduled": "Programado",
24
+ "archived": "Archivado"
25
+ }
26
+ }
27
+ },
28
+ "actions": {
29
+ "create": "Crear Pagina",
30
+ "edit": "Editar Pagina",
31
+ "delete": "Eliminar Pagina",
32
+ "publish": "Publicar",
33
+ "unpublish": "Despublicar"
34
+ },
35
+ "messages": {
36
+ "created": "Pagina creada exitosamente",
37
+ "updated": "Pagina actualizada exitosamente",
38
+ "deleted": "Pagina eliminada exitosamente",
39
+ "published": "Pagina publicada exitosamente"
40
+ }
41
+ }
@@ -0,0 +1,112 @@
1
+ -- Migration: 008_pages_table.sql
2
+ -- Description: Create pages table for dynamic page builder system with blocks
3
+ -- Date: 2025-01-21
4
+ -- Updated: 2025-12-17 (Add userId, teamId system fields + team isolation RLS)
5
+
6
+ -- ============================================
7
+ -- TABLE: pages
8
+ -- ============================================
9
+ DROP TABLE IF EXISTS public.pages CASCADE;
10
+
11
+ CREATE TABLE IF NOT EXISTS public.pages (
12
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
13
+
14
+ -- Relational Fields (at the beginning)
15
+ "userId" TEXT NOT NULL REFERENCES public."users"(id) ON DELETE CASCADE,
16
+ "teamId" TEXT NOT NULL REFERENCES public."teams"(id) ON DELETE CASCADE,
17
+
18
+ slug VARCHAR(255) NOT NULL,
19
+ title VARCHAR(255) NOT NULL,
20
+ blocks JSONB NOT NULL DEFAULT '[]'::JSONB,
21
+ locale VARCHAR(10) NOT NULL DEFAULT 'en',
22
+
23
+ -- SEO fields
24
+ "seoTitle" VARCHAR(255),
25
+ "seoDescription" TEXT,
26
+ "seoKeywords" TEXT,
27
+ "ogImage" TEXT,
28
+ noindex BOOLEAN DEFAULT FALSE,
29
+ nofollow BOOLEAN DEFAULT FALSE,
30
+
31
+ -- Meta fields
32
+ published BOOLEAN DEFAULT FALSE,
33
+ "authorId" TEXT REFERENCES public."users"(id) ON DELETE SET NULL,
34
+ "createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
35
+ "updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
36
+
37
+ -- Constraints
38
+ CONSTRAINT unique_slug_locale UNIQUE(slug, locale),
39
+ CONSTRAINT valid_slug CHECK (slug ~ '^[a-z0-9\-]+$'),
40
+ CONSTRAINT valid_locale CHECK (locale ~ '^[a-z]{2}(-[A-Z]{2})?$'),
41
+ CONSTRAINT slug_length CHECK (LENGTH(slug) >= 2 AND LENGTH(slug) <= 100),
42
+ CONSTRAINT title_not_empty CHECK (LENGTH(TRIM(title)) > 0)
43
+ );
44
+
45
+ -- ============================================
46
+ -- COMMENTS
47
+ -- ============================================
48
+ COMMENT ON TABLE public.pages IS 'Dynamic pages created via block editor system';
49
+ COMMENT ON COLUMN public.pages."userId" IS 'User who created this page';
50
+ COMMENT ON COLUMN public.pages."teamId" IS 'Team context for isolation';
51
+ COMMENT ON COLUMN public.pages.blocks IS 'Array of block instances with props (JSONB)';
52
+ COMMENT ON COLUMN public.pages.slug IS 'URL-friendly identifier (lowercase, hyphens only)';
53
+ COMMENT ON COLUMN public.pages.locale IS 'Language/locale code (e.g., en, es, en-US)';
54
+
55
+ -- ============================================
56
+ -- TRIGGER: updatedAt
57
+ -- ============================================
58
+ DROP TRIGGER IF EXISTS pages_set_updated_at ON public.pages;
59
+ CREATE TRIGGER pages_set_updated_at
60
+ BEFORE UPDATE ON public.pages
61
+ FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
62
+
63
+ -- ============================================
64
+ -- INDEXES
65
+ -- ============================================
66
+ CREATE INDEX IF NOT EXISTS idx_pages_user_id ON public.pages("userId");
67
+ CREATE INDEX IF NOT EXISTS idx_pages_team_id ON public.pages("teamId");
68
+ CREATE INDEX IF NOT EXISTS idx_pages_slug ON public.pages(slug);
69
+ CREATE INDEX IF NOT EXISTS idx_pages_locale ON public.pages(locale);
70
+ CREATE INDEX IF NOT EXISTS idx_pages_published ON public.pages(published);
71
+ CREATE INDEX IF NOT EXISTS idx_pages_author ON public.pages("authorId");
72
+ CREATE INDEX IF NOT EXISTS idx_pages_created ON public.pages("createdAt" DESC);
73
+ CREATE INDEX IF NOT EXISTS idx_pages_slug_locale ON public.pages(slug, locale);
74
+ CREATE INDEX IF NOT EXISTS idx_pages_published_locale ON public.pages(published, locale) WHERE published = TRUE;
75
+ CREATE INDEX IF NOT EXISTS idx_pages_blocks_gin ON public.pages USING GIN (blocks);
76
+ CREATE INDEX IF NOT EXISTS idx_pages_team_created ON public.pages("teamId", "createdAt" DESC);
77
+
78
+ -- ============================================
79
+ -- RLS
80
+ -- ============================================
81
+ ALTER TABLE public.pages ENABLE ROW LEVEL SECURITY;
82
+
83
+ DROP POLICY IF EXISTS "pages public can select" ON public.pages;
84
+ DROP POLICY IF EXISTS "pages auth can do all" ON public.pages;
85
+ DROP POLICY IF EXISTS "Pages team can do all" ON public.pages;
86
+
87
+ -- Public can read published pages
88
+ CREATE POLICY "pages public can select"
89
+ ON public.pages
90
+ FOR SELECT TO anon
91
+ USING (published = TRUE);
92
+
93
+ -- ============================
94
+ -- RLS: TEAM ISOLATION ONLY
95
+ -- ============================
96
+ -- IMPORTANT: RLS only verifies team membership
97
+ -- access.shared logic (user isolation) is handled at APP LEVEL
98
+ CREATE POLICY "Pages team can do all"
99
+ ON public.pages
100
+ FOR ALL TO authenticated
101
+ USING (
102
+ -- Superadmin bypass
103
+ public.is_superadmin()
104
+ OR
105
+ -- Team isolation only: user must be member of the team
106
+ "teamId" = ANY(public.get_user_team_ids())
107
+ )
108
+ WITH CHECK (
109
+ public.is_superadmin()
110
+ OR
111
+ "teamId" = ANY(public.get_user_team_ids())
112
+ );
@@ -0,0 +1,56 @@
1
+ -- Migration: 002_pages_metas.sql
2
+ -- Description: Create pages_metas table for flexible key-value metadata storage
3
+ -- Date: 2025-01-25
4
+
5
+ -- Create pages_metas table
6
+ CREATE TABLE IF NOT EXISTS "pages_metas" (
7
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
8
+ "pageId" UUID NOT NULL REFERENCES public."pages"(id) ON DELETE CASCADE,
9
+ "metaKey" TEXT NOT NULL,
10
+ "metaValue" JSONB NOT NULL DEFAULT '{}'::jsonb,
11
+ "dataType" TEXT,
12
+ "isPublic" BOOLEAN NOT NULL DEFAULT FALSE,
13
+ "isSearchable" BOOLEAN NOT NULL DEFAULT FALSE,
14
+ "createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
15
+ "updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
16
+ CONSTRAINT pages_metas_unique_key UNIQUE ("pageId", "metaKey")
17
+ );
18
+
19
+ -- Indices
20
+ CREATE INDEX IF NOT EXISTS idx_pages_metas_page_id ON "pages_metas"("pageId");
21
+ CREATE INDEX IF NOT EXISTS idx_pages_metas_key ON "pages_metas"("metaKey");
22
+ CREATE INDEX IF NOT EXISTS idx_pages_metas_composite ON "pages_metas"("pageId", "metaKey", "isPublic");
23
+ CREATE INDEX IF NOT EXISTS idx_pages_metas_searchable ON "pages_metas"("isSearchable") WHERE "isSearchable" = true;
24
+ CREATE INDEX IF NOT EXISTS idx_pages_metas_public ON "pages_metas"("isPublic") WHERE "isPublic" = true;
25
+ CREATE INDEX IF NOT EXISTS idx_pages_metas_value_gin ON "pages_metas" USING GIN ("metaValue");
26
+
27
+ -- Trigger for updatedAt
28
+ DROP TRIGGER IF EXISTS pages_metas_set_updated_at ON "pages_metas";
29
+ CREATE TRIGGER pages_metas_set_updated_at
30
+ BEFORE UPDATE ON "pages_metas"
31
+ FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
32
+
33
+ -- ============================================
34
+ -- RLS POLICIES
35
+ -- ============================================
36
+ ALTER TABLE "pages_metas" ENABLE ROW LEVEL SECURITY;
37
+
38
+ -- Public can read public metadata
39
+ DROP POLICY IF EXISTS "pages_metas_public_select" ON "pages_metas";
40
+ CREATE POLICY "pages_metas_public_select"
41
+ ON "pages_metas" FOR SELECT TO anon
42
+ USING ("isPublic" = TRUE);
43
+
44
+ -- Authenticated users can manage all metadata
45
+ DROP POLICY IF EXISTS "pages_metas_auth_all" ON "pages_metas";
46
+ CREATE POLICY "pages_metas_auth_all"
47
+ ON "pages_metas" FOR ALL TO authenticated
48
+ USING (true)
49
+ WITH CHECK (true);
50
+
51
+ -- Comments
52
+ COMMENT ON TABLE "pages_metas" IS 'Flexible key-value metadata storage for pages';
53
+ COMMENT ON COLUMN "pages_metas"."pageId" IS 'Reference to the parent page';
54
+ COMMENT ON COLUMN "pages_metas"."metaKey" IS 'Unique key identifier for the metadata';
55
+ COMMENT ON COLUMN "pages_metas"."metaValue" IS 'JSONB value for flexible data storage';
56
+ COMMENT ON COLUMN "pages_metas"."isPublic" IS 'If true, this metadata can be read by anyone';