spine-framework 0.1.0

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 (385) hide show
  1. package/.framework/README.md +129 -0
  2. package/.framework/cli/bin.cjs +14 -0
  3. package/.framework/cli/commands/agents.ts +153 -0
  4. package/.framework/cli/commands/auth.ts +94 -0
  5. package/.framework/cli/commands/create-app.ts +185 -0
  6. package/.framework/cli/commands/dev.ts +295 -0
  7. package/.framework/cli/commands/doctor.ts +442 -0
  8. package/.framework/cli/commands/generate.ts +332 -0
  9. package/.framework/cli/commands/init.ts +272 -0
  10. package/.framework/cli/commands/install-app.ts +391 -0
  11. package/.framework/cli/commands/items.ts +253 -0
  12. package/.framework/cli/commands/migrations.ts +141 -0
  13. package/.framework/cli/commands/pipelines.ts +166 -0
  14. package/.framework/cli/commands/status.ts +197 -0
  15. package/.framework/cli/commands/system.ts +184 -0
  16. package/.framework/cli/commands/test.ts +227 -0
  17. package/.framework/cli/commands/uninstall-app.ts +166 -0
  18. package/.framework/cli/context.ts +268 -0
  19. package/.framework/cli/env-loader.ts +36 -0
  20. package/.framework/cli/index.ts +106 -0
  21. package/.framework/cli/welcome.cjs +45 -0
  22. package/.framework/docs/API.md +384 -0
  23. package/.framework/docs/STABILITY.md +52 -0
  24. package/.framework/docs/admin-routes.md +76 -0
  25. package/.framework/docs/api-docs-progress.md +38 -0
  26. package/.framework/docs/api-governance.md +146 -0
  27. package/.framework/docs/api-testing-results.md +212 -0
  28. package/.framework/docs/apis/admin-configs.md +567 -0
  29. package/.framework/docs/apis/admin-data.md +272 -0
  30. package/.framework/docs/apis/index.md +231 -0
  31. package/.framework/docs/apis/internal.md +295 -0
  32. package/.framework/docs/apis/runtime.md +537 -0
  33. package/.framework/docs/assembly-launch-guide.md +138 -0
  34. package/.framework/docs/audit-results.md +590 -0
  35. package/.framework/docs/authorization-model.md +170 -0
  36. package/.framework/docs/db-api-inventory.md +95 -0
  37. package/.framework/docs/examples/custom-app/README.md +77 -0
  38. package/.framework/docs/examples/custom-function/README.md +27 -0
  39. package/.framework/docs/examples/custom-function/handler.ts +48 -0
  40. package/.framework/docs/examples/custom-webhook/README.md +68 -0
  41. package/.framework/docs/gap-remediation-backlog.md +103 -0
  42. package/.framework/docs/guides/cli-guide.md +224 -0
  43. package/.framework/docs/guides/getting-started.md +103 -0
  44. package/.framework/docs/guides/import-guide.md +193 -0
  45. package/.framework/docs/guides/testing-guide.md +229 -0
  46. package/.framework/docs/permission-examples.md +326 -0
  47. package/.framework/docs/ui-adoption-verification.md +111 -0
  48. package/.framework/docs/ui-api-coverage.md +84 -0
  49. package/.framework/docs/v2-compatibility-audit.md +228 -0
  50. package/.framework/functions/.gitkeep +1 -0
  51. package/.framework/functions/_shared/agent-runner.ts +1097 -0
  52. package/.framework/functions/_shared/app-manifest.ts +184 -0
  53. package/.framework/functions/_shared/audit.ts +150 -0
  54. package/.framework/functions/_shared/db.ts +174 -0
  55. package/.framework/functions/_shared/index.ts +382 -0
  56. package/.framework/functions/_shared/middleware.ts +490 -0
  57. package/.framework/functions/_shared/permissions.ts +1325 -0
  58. package/.framework/functions/_shared/pipeline-runner.ts +731 -0
  59. package/.framework/functions/_shared/principal.ts +760 -0
  60. package/.framework/functions/_shared/schema-utils.ts +967 -0
  61. package/.framework/functions/_shared/testing.ts +258 -0
  62. package/.framework/functions/_shared/trigger-engine.ts +425 -0
  63. package/.framework/functions/_shared/webhook-registration.ts +168 -0
  64. package/.framework/functions/_shared/webhook-registry.ts +129 -0
  65. package/.framework/functions/account-nodes.ts +111 -0
  66. package/.framework/functions/admin-data.ts +606 -0
  67. package/.framework/functions/ai-agents.ts +323 -0
  68. package/.framework/functions/api-keys.ts +376 -0
  69. package/.framework/functions/apps.ts +483 -0
  70. package/.framework/functions/auth.ts +196 -0
  71. package/.framework/functions/debug-auth.ts +107 -0
  72. package/.framework/functions/embeddings.ts +556 -0
  73. package/.framework/functions/integration-routes.ts +523 -0
  74. package/.framework/functions/integrations.ts +319 -0
  75. package/.framework/functions/item-progress.ts +272 -0
  76. package/.framework/functions/logs.ts +438 -0
  77. package/.framework/functions/observability.ts +275 -0
  78. package/.framework/functions/pipeline-executions.ts +494 -0
  79. package/.framework/functions/pipelines.ts +485 -0
  80. package/.framework/functions/prompt-configs.ts +339 -0
  81. package/.framework/functions/roles.ts +387 -0
  82. package/.framework/functions/system-cron.ts +742 -0
  83. package/.framework/functions/system.ts +323 -0
  84. package/.framework/functions/tests.ts +119 -0
  85. package/.framework/functions/timers.ts +357 -0
  86. package/.framework/functions/triggers.ts +563 -0
  87. package/.framework/functions/types.ts +604 -0
  88. package/.framework/migrations/000_foundation.sql +1256 -0
  89. package/.framework/migrations/001_seed.sql +92 -0
  90. package/.framework/migrations/002_seed_constraints.sql +13 -0
  91. package/.framework/migrations/003_auth_user_trigger.sql +59 -0
  92. package/.framework/src/App.tsx +126 -0
  93. package/.framework/src/apps/admin/index.tsx +173 -0
  94. package/.framework/src/components/AppWrapper.tsx +56 -0
  95. package/.framework/src/components/CustomAppLoader.tsx +116 -0
  96. package/.framework/src/components/admin/AdminListPage.tsx +151 -0
  97. package/.framework/src/components/admin/AdminSidebar.tsx +166 -0
  98. package/.framework/src/components/admin/AdminStatsCard.tsx +62 -0
  99. package/.framework/src/components/admin/SortableTableHeader.tsx +42 -0
  100. package/.framework/src/components/app-shell/GenericAppShell.tsx +181 -0
  101. package/.framework/src/components/app-shell/GenericDetailPage.tsx +200 -0
  102. package/.framework/src/components/app-shell/GenericListPage.tsx +116 -0
  103. package/.framework/src/components/app-sidebar.tsx +228 -0
  104. package/.framework/src/components/auth/ProtectedRoute.tsx +88 -0
  105. package/.framework/src/components/layout/AppShell.tsx +91 -0
  106. package/.framework/src/components/layout/Header.tsx +88 -0
  107. package/.framework/src/components/layout/Layout.tsx +95 -0
  108. package/.framework/src/components/layout/Sidebar.tsx +329 -0
  109. package/.framework/src/components/runtime/DataDetailHeader.tsx +77 -0
  110. package/.framework/src/components/runtime/DataDetailPage.tsx +171 -0
  111. package/.framework/src/components/runtime/DataFilters.tsx +91 -0
  112. package/.framework/src/components/runtime/DataHeader.tsx +68 -0
  113. package/.framework/src/components/runtime/DataListPage.tsx +124 -0
  114. package/.framework/src/components/runtime/DataStats.tsx +70 -0
  115. package/.framework/src/components/runtime/DataTable.tsx +174 -0
  116. package/.framework/src/components/runtime/SchemaDetailForm.tsx +134 -0
  117. package/.framework/src/components/runtime/index.ts +18 -0
  118. package/.framework/src/components/search-form.tsx +29 -0
  119. package/.framework/src/components/shared/AgentView.tsx +213 -0
  120. package/.framework/src/components/shared/FieldRenderer.tsx +478 -0
  121. package/.framework/src/components/shared/SchemaFields.tsx +226 -0
  122. package/.framework/src/components/ui/DataTable.tsx +343 -0
  123. package/.framework/src/components/ui/Form.tsx +281 -0
  124. package/.framework/src/components/ui/ItemCard.tsx +296 -0
  125. package/.framework/src/components/ui/ItemListView.tsx +308 -0
  126. package/.framework/src/components/ui/LoadingSpinner.tsx +52 -0
  127. package/.framework/src/components/ui/Modal.tsx +61 -0
  128. package/.framework/src/components/ui/RichTextEditor.tsx +210 -0
  129. package/.framework/src/components/ui/accordion.tsx +82 -0
  130. package/.framework/src/components/ui/alert-dialog.tsx +197 -0
  131. package/.framework/src/components/ui/alert.tsx +76 -0
  132. package/.framework/src/components/ui/aspect-ratio.tsx +11 -0
  133. package/.framework/src/components/ui/avatar.tsx +110 -0
  134. package/.framework/src/components/ui/badge.tsx +49 -0
  135. package/.framework/src/components/ui/breadcrumb.tsx +122 -0
  136. package/.framework/src/components/ui/button-group.tsx +83 -0
  137. package/.framework/src/components/ui/button.tsx +65 -0
  138. package/.framework/src/components/ui/calendar.tsx +222 -0
  139. package/.framework/src/components/ui/card.tsx +100 -0
  140. package/.framework/src/components/ui/carousel.tsx +240 -0
  141. package/.framework/src/components/ui/chart.tsx +373 -0
  142. package/.framework/src/components/ui/checkbox.tsx +31 -0
  143. package/.framework/src/components/ui/collapsible.tsx +33 -0
  144. package/.framework/src/components/ui/combobox.tsx +299 -0
  145. package/.framework/src/components/ui/command.tsx +193 -0
  146. package/.framework/src/components/ui/context-menu.tsx +261 -0
  147. package/.framework/src/components/ui/dialog.tsx +165 -0
  148. package/.framework/src/components/ui/direction.tsx +22 -0
  149. package/.framework/src/components/ui/drawer.tsx +132 -0
  150. package/.framework/src/components/ui/dropdown-menu.tsx +269 -0
  151. package/.framework/src/components/ui/empty.tsx +104 -0
  152. package/.framework/src/components/ui/field.tsx +238 -0
  153. package/.framework/src/components/ui/hover-card.tsx +42 -0
  154. package/.framework/src/components/ui/input-group.tsx +153 -0
  155. package/.framework/src/components/ui/input-otp.tsx +87 -0
  156. package/.framework/src/components/ui/input.tsx +19 -0
  157. package/.framework/src/components/ui/item.tsx +196 -0
  158. package/.framework/src/components/ui/kbd.tsx +26 -0
  159. package/.framework/src/components/ui/label.tsx +22 -0
  160. package/.framework/src/components/ui/menubar.tsx +277 -0
  161. package/.framework/src/components/ui/native-select.tsx +61 -0
  162. package/.framework/src/components/ui/navigation-menu.tsx +164 -0
  163. package/.framework/src/components/ui/pagination.tsx +129 -0
  164. package/.framework/src/components/ui/popover.tsx +87 -0
  165. package/.framework/src/components/ui/progress.tsx +31 -0
  166. package/.framework/src/components/ui/radio-group.tsx +42 -0
  167. package/.framework/src/components/ui/resizable.tsx +50 -0
  168. package/.framework/src/components/ui/scroll-area.tsx +53 -0
  169. package/.framework/src/components/ui/select.tsx +195 -0
  170. package/.framework/src/components/ui/separator.tsx +26 -0
  171. package/.framework/src/components/ui/sheet.tsx +145 -0
  172. package/.framework/src/components/ui/sidebar.tsx +706 -0
  173. package/.framework/src/components/ui/skeleton.tsx +13 -0
  174. package/.framework/src/components/ui/slider.tsx +59 -0
  175. package/.framework/src/components/ui/sonner.tsx +47 -0
  176. package/.framework/src/components/ui/spinner.tsx +10 -0
  177. package/.framework/src/components/ui/switch.tsx +33 -0
  178. package/.framework/src/components/ui/table-primitives.tsx +141 -0
  179. package/.framework/src/components/ui/table.tsx +114 -0
  180. package/.framework/src/components/ui/tabs.tsx +90 -0
  181. package/.framework/src/components/ui/textarea.tsx +18 -0
  182. package/.framework/src/components/ui/toggle-group.tsx +89 -0
  183. package/.framework/src/components/ui/toggle.tsx +45 -0
  184. package/.framework/src/components/ui/tooltip.tsx +57 -0
  185. package/.framework/src/contexts/AppContext.tsx +133 -0
  186. package/.framework/src/contexts/AuthContext.tsx +371 -0
  187. package/.framework/src/hooks/use-mobile.ts +19 -0
  188. package/.framework/src/hooks/useApi.ts +526 -0
  189. package/.framework/src/hooks/useApps.ts +114 -0
  190. package/.framework/src/hooks/useEntityList.ts +190 -0
  191. package/.framework/src/hooks/useEntityRecord.ts +308 -0
  192. package/.framework/src/hooks/useForm.ts +307 -0
  193. package/.framework/src/hooks/useListSchema.ts +264 -0
  194. package/.framework/src/hooks/useSchemaRecord.ts +223 -0
  195. package/.framework/src/index.css +128 -0
  196. package/.framework/src/lib/api.ts +156 -0
  197. package/.framework/src/lib/supabase.ts +94 -0
  198. package/.framework/src/lib/utils.ts +317 -0
  199. package/.framework/src/main.tsx +27 -0
  200. package/.framework/src/pages/DashboardPage.tsx +181 -0
  201. package/.framework/src/pages/NotFoundPage.tsx +39 -0
  202. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +161 -0
  203. package/.framework/src/pages/admin/AIAgentsPage.tsx +318 -0
  204. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +199 -0
  205. package/.framework/src/pages/admin/APIKeysPage.tsx +303 -0
  206. package/.framework/src/pages/admin/AlertsConfigPage.tsx +523 -0
  207. package/.framework/src/pages/admin/AppDetailPage.tsx +493 -0
  208. package/.framework/src/pages/admin/AppsPage.tsx +355 -0
  209. package/.framework/src/pages/admin/DesignedPage.tsx +491 -0
  210. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +534 -0
  211. package/.framework/src/pages/admin/EmbeddingsPage.tsx +424 -0
  212. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +176 -0
  213. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +109 -0
  214. package/.framework/src/pages/admin/IntegratedDashboard.tsx +402 -0
  215. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +187 -0
  216. package/.framework/src/pages/admin/IntegrationsPage.tsx +301 -0
  217. package/.framework/src/pages/admin/LogsPage.tsx +283 -0
  218. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +85 -0
  219. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +470 -0
  220. package/.framework/src/pages/admin/PipelineDetailPage.tsx +183 -0
  221. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +279 -0
  222. package/.framework/src/pages/admin/PipelinesPage.tsx +390 -0
  223. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +299 -0
  224. package/.framework/src/pages/admin/PromptConfigsPage.tsx +292 -0
  225. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +434 -0
  226. package/.framework/src/pages/admin/RoleDetailPage.tsx +273 -0
  227. package/.framework/src/pages/admin/RolesPage.tsx +292 -0
  228. package/.framework/src/pages/admin/SelectTestPage.tsx +61 -0
  229. package/.framework/src/pages/admin/ShadcnTestPage.tsx +588 -0
  230. package/.framework/src/pages/admin/SimpleDashboard.tsx +387 -0
  231. package/.framework/src/pages/admin/TestRunDetailPage.tsx +172 -0
  232. package/.framework/src/pages/admin/TestingDashboard.tsx +257 -0
  233. package/.framework/src/pages/admin/TimerDetailPage.tsx +151 -0
  234. package/.framework/src/pages/admin/TimersPage.tsx +376 -0
  235. package/.framework/src/pages/admin/TriggerDetailPage.tsx +149 -0
  236. package/.framework/src/pages/admin/TriggersPage.tsx +381 -0
  237. package/.framework/src/pages/admin/TypeDetailPage.tsx +694 -0
  238. package/.framework/src/pages/admin/TypesPage.tsx +295 -0
  239. package/.framework/src/pages/auth/LoginPage.tsx +188 -0
  240. package/.framework/src/pages/auth/RegisterPage.tsx +163 -0
  241. package/.framework/src/pages/spine-framework/APIPage.tsx +17 -0
  242. package/.framework/src/pages/spine-framework/CLIPage.tsx +25 -0
  243. package/.framework/src/types/auth.ts +125 -0
  244. package/.framework/src/types/types.ts +407 -0
  245. package/STRUCTURE.md +150 -0
  246. package/config/components.json +25 -0
  247. package/config/deno.lock +108 -0
  248. package/config/package-lock.json +17183 -0
  249. package/config/postcss.config.cjs +10 -0
  250. package/config/tailwind.config.cjs +78 -0
  251. package/config/tsconfig.build.json +32 -0
  252. package/config/tsconfig.cli.json +18 -0
  253. package/config/tsconfig.json +41 -0
  254. package/config/tsconfig.node.json +17 -0
  255. package/config/tsconfig.node.tsbuildinfo +1 -0
  256. package/config/tsconfig.tsbuildinfo +1 -0
  257. package/config/typedoc.json +16 -0
  258. package/config/vite.config.d.ts +2 -0
  259. package/config/vite.config.ts +72 -0
  260. package/dist/cli/commands/agents.d.ts +39 -0
  261. package/dist/cli/commands/agents.d.ts.map +1 -0
  262. package/dist/cli/commands/auth.d.ts +36 -0
  263. package/dist/cli/commands/auth.d.ts.map +1 -0
  264. package/dist/cli/commands/create-app.d.ts +23 -0
  265. package/dist/cli/commands/create-app.d.ts.map +1 -0
  266. package/dist/cli/commands/dev.d.ts +39 -0
  267. package/dist/cli/commands/dev.d.ts.map +1 -0
  268. package/dist/cli/commands/doctor.d.ts +42 -0
  269. package/dist/cli/commands/doctor.d.ts.map +1 -0
  270. package/dist/cli/commands/generate.d.ts +36 -0
  271. package/dist/cli/commands/generate.d.ts.map +1 -0
  272. package/dist/cli/commands/init.d.ts +30 -0
  273. package/dist/cli/commands/init.d.ts.map +1 -0
  274. package/dist/cli/commands/install-app.d.ts +30 -0
  275. package/dist/cli/commands/install-app.d.ts.map +1 -0
  276. package/dist/cli/commands/items.d.ts +45 -0
  277. package/dist/cli/commands/items.d.ts.map +1 -0
  278. package/dist/cli/commands/migrations.d.ts +41 -0
  279. package/dist/cli/commands/migrations.d.ts.map +1 -0
  280. package/dist/cli/commands/pipelines.d.ts +40 -0
  281. package/dist/cli/commands/pipelines.d.ts.map +1 -0
  282. package/dist/cli/commands/status.d.ts +23 -0
  283. package/dist/cli/commands/status.d.ts.map +1 -0
  284. package/dist/cli/commands/system.d.ts +29 -0
  285. package/dist/cli/commands/system.d.ts.map +1 -0
  286. package/dist/cli/commands/test.d.ts +46 -0
  287. package/dist/cli/commands/test.d.ts.map +1 -0
  288. package/dist/cli/commands/uninstall-app.d.ts +23 -0
  289. package/dist/cli/commands/uninstall-app.d.ts.map +1 -0
  290. package/dist/cli/context.d.ts +88 -0
  291. package/dist/cli/context.d.ts.map +1 -0
  292. package/dist/cli/env-loader.d.ts +14 -0
  293. package/dist/cli/env-loader.d.ts.map +1 -0
  294. package/dist/cli/index.d.ts +41 -0
  295. package/dist/cli/index.d.ts.map +1 -0
  296. package/dist/functions/_shared/agent-runner.d.ts +156 -0
  297. package/dist/functions/_shared/agent-runner.d.ts.map +1 -0
  298. package/dist/functions/_shared/app-manifest.d.ts +68 -0
  299. package/dist/functions/_shared/app-manifest.d.ts.map +1 -0
  300. package/dist/functions/_shared/audit.d.ts +91 -0
  301. package/dist/functions/_shared/audit.d.ts.map +1 -0
  302. package/dist/functions/_shared/db.d.ts +125 -0
  303. package/dist/functions/_shared/db.d.ts.map +1 -0
  304. package/dist/functions/_shared/index.d.ts +298 -0
  305. package/dist/functions/_shared/index.d.ts.map +1 -0
  306. package/dist/functions/_shared/middleware.d.ts +315 -0
  307. package/dist/functions/_shared/middleware.d.ts.map +1 -0
  308. package/dist/functions/_shared/permissions.d.ts +626 -0
  309. package/dist/functions/_shared/permissions.d.ts.map +1 -0
  310. package/dist/functions/_shared/pipeline-runner.d.ts +124 -0
  311. package/dist/functions/_shared/pipeline-runner.d.ts.map +1 -0
  312. package/dist/functions/_shared/principal.d.ts +284 -0
  313. package/dist/functions/_shared/principal.d.ts.map +1 -0
  314. package/dist/functions/_shared/schema-utils.d.ts +181 -0
  315. package/dist/functions/_shared/schema-utils.d.ts.map +1 -0
  316. package/dist/functions/_shared/testing.d.ts +172 -0
  317. package/dist/functions/_shared/testing.d.ts.map +1 -0
  318. package/dist/functions/_shared/trigger-engine.d.ts +140 -0
  319. package/dist/functions/_shared/trigger-engine.d.ts.map +1 -0
  320. package/dist/functions/_shared/webhook-registration.d.ts +81 -0
  321. package/dist/functions/_shared/webhook-registration.d.ts.map +1 -0
  322. package/dist/functions/_shared/webhook-registry.d.ts +57 -0
  323. package/dist/functions/_shared/webhook-registry.d.ts.map +1 -0
  324. package/dist/functions/account-nodes.d.ts +48 -0
  325. package/dist/functions/account-nodes.d.ts.map +1 -0
  326. package/dist/functions/admin-data.d.ts +178 -0
  327. package/dist/functions/admin-data.d.ts.map +1 -0
  328. package/dist/functions/ai-agents.d.ts +125 -0
  329. package/dist/functions/ai-agents.d.ts.map +1 -0
  330. package/dist/functions/api-keys.d.ts +140 -0
  331. package/dist/functions/api-keys.d.ts.map +1 -0
  332. package/dist/functions/apps.d.ts +163 -0
  333. package/dist/functions/apps.d.ts.map +1 -0
  334. package/dist/functions/auth.d.ts +74 -0
  335. package/dist/functions/auth.d.ts.map +1 -0
  336. package/dist/functions/debug-auth.d.ts +33 -0
  337. package/dist/functions/debug-auth.d.ts.map +1 -0
  338. package/dist/functions/embeddings.d.ts +205 -0
  339. package/dist/functions/embeddings.d.ts.map +1 -0
  340. package/dist/functions/integration-routes.d.ts +45 -0
  341. package/dist/functions/integration-routes.d.ts.map +1 -0
  342. package/dist/functions/integrations.d.ts +124 -0
  343. package/dist/functions/integrations.d.ts.map +1 -0
  344. package/dist/functions/item-progress.d.ts +41 -0
  345. package/dist/functions/item-progress.d.ts.map +1 -0
  346. package/dist/functions/logs.d.ts +162 -0
  347. package/dist/functions/logs.d.ts.map +1 -0
  348. package/dist/functions/observability.d.ts +123 -0
  349. package/dist/functions/observability.d.ts.map +1 -0
  350. package/dist/functions/pipeline-executions.d.ts +190 -0
  351. package/dist/functions/pipeline-executions.d.ts.map +1 -0
  352. package/dist/functions/pipelines.d.ts +171 -0
  353. package/dist/functions/pipelines.d.ts.map +1 -0
  354. package/dist/functions/prompt-configs.d.ts +125 -0
  355. package/dist/functions/prompt-configs.d.ts.map +1 -0
  356. package/dist/functions/roles.d.ts +118 -0
  357. package/dist/functions/roles.d.ts.map +1 -0
  358. package/dist/functions/system-cron.d.ts +65 -0
  359. package/dist/functions/system-cron.d.ts.map +1 -0
  360. package/dist/functions/system.d.ts +29 -0
  361. package/dist/functions/system.d.ts.map +1 -0
  362. package/dist/functions/tests.d.ts +28 -0
  363. package/dist/functions/tests.d.ts.map +1 -0
  364. package/dist/functions/timers.d.ts +139 -0
  365. package/dist/functions/timers.d.ts.map +1 -0
  366. package/dist/functions/triggers.d.ts +203 -0
  367. package/dist/functions/triggers.d.ts.map +1 -0
  368. package/dist/functions/types.d.ts +151 -0
  369. package/dist/functions/types.d.ts.map +1 -0
  370. package/dist/src/types/types.d.ts +364 -0
  371. package/dist/src/types/types.d.ts.map +1 -0
  372. package/package.json +192 -0
  373. package/scripts/app-install-cli.ts +286 -0
  374. package/scripts/assemble-frontend.sh +79 -0
  375. package/scripts/assemble-functions.sh +62 -0
  376. package/scripts/assemble.sh +35 -0
  377. package/scripts/boundary-check.sh +106 -0
  378. package/scripts/build-manifest.sh +80 -0
  379. package/scripts/check-core-integrity.sh +82 -0
  380. package/scripts/ingest-chunks.cjs +202 -0
  381. package/scripts/kb-chunk-parser.cjs +312 -0
  382. package/scripts/kb-chunk-parser.ts +330 -0
  383. package/scripts/load-test-app-install.ts +484 -0
  384. package/scripts/netlify-dev-wrapper.sh +22 -0
  385. package/scripts/verify-integrity.sh +69 -0
@@ -0,0 +1,273 @@
1
+ /**
2
+ * @module src/pages/admin/RoleDetailPage
3
+ * @audience installer
4
+ * @layer frontend-page
5
+ * @stability stable
6
+ *
7
+ * Create / view / edit page for a single role.
8
+ */
9
+
10
+ import { useState, useEffect } from 'react'
11
+ import { useParams, useNavigate } from 'react-router-dom'
12
+ import { Button } from '../../components/ui/button'
13
+ import { Input } from '../../components/ui/input'
14
+ import { Textarea } from '../../components/ui/textarea'
15
+ import { Checkbox } from '../../components/ui/checkbox'
16
+ import { Label } from '../../components/ui/label'
17
+ import { Card, CardContent, CardHeader, CardTitle } from '../../components/ui/card'
18
+ import { Skeleton } from '../../components/ui/skeleton'
19
+ import { Alert, AlertDescription, AlertTitle } from '../../components/ui/alert'
20
+ import { ArrowLeft, ShieldCheck, AlertCircle } from 'lucide-react'
21
+ import { useApi } from '../../hooks/useApi'
22
+ import { apiFetch } from '../../lib/api'
23
+ import { formatDateTime } from '../../lib/utils'
24
+
25
+ interface Role {
26
+ id: string
27
+ name: string
28
+ slug: string
29
+ description?: string
30
+ permissions?: Record<string, any>
31
+ is_system: boolean
32
+ is_active: boolean
33
+ app_id?: string
34
+ created_at: string
35
+ updated_at: string
36
+ }
37
+
38
+ export function RoleDetailPage() {
39
+ const { id } = useParams<{ id: string }>()
40
+ const navigate = useNavigate()
41
+ const isCreateMode = !id || id === 'new'
42
+
43
+ const [formData, setFormData] = useState({
44
+ name: '',
45
+ slug: '',
46
+ description: '',
47
+ is_system: false,
48
+ is_active: true,
49
+ permissions: {}
50
+ })
51
+ const [permissionsJson, setPermissionsJson] = useState('{}')
52
+ const [isSubmitting, setIsSubmitting] = useState(false)
53
+ const [error, setError] = useState<string | null>(null)
54
+
55
+ const { data: role, loading, error: fetchError } = useApi<Role>(
56
+ async () => {
57
+ if (isCreateMode) return null
58
+ const response = await apiFetch(`/api/roles?action=get&id=${id}`)
59
+ if (!response.ok) throw new Error('Failed to fetch role')
60
+ const result = await response.json()
61
+ return result.data
62
+ },
63
+ { immediate: !isCreateMode }
64
+ )
65
+
66
+ useEffect(() => {
67
+ if (role) {
68
+ setFormData({
69
+ name: role.name || '',
70
+ slug: role.slug || '',
71
+ description: role.description || '',
72
+ is_system: role.is_system || false,
73
+ is_active: role.is_active !== false,
74
+ permissions: role.permissions || {}
75
+ })
76
+ setPermissionsJson(JSON.stringify(role.permissions || {}, null, 2))
77
+ }
78
+ }, [role])
79
+
80
+ const handleSubmit = async (e: React.FormEvent) => {
81
+ e.preventDefault()
82
+ setIsSubmitting(true)
83
+ setError(null)
84
+
85
+ try {
86
+ let parsedPermissions
87
+ try {
88
+ parsedPermissions = JSON.parse(permissionsJson)
89
+ } catch (e) {
90
+ throw new Error('Invalid JSON in permissions field')
91
+ }
92
+
93
+ const payload = {
94
+ ...formData,
95
+ permissions: parsedPermissions
96
+ }
97
+
98
+ const url = isCreateMode ? '/api/roles' : `/api/roles?id=${id}`
99
+ const method = isCreateMode ? 'POST' : 'PATCH'
100
+
101
+ const response = await apiFetch(url, {
102
+ method,
103
+ headers: { 'Content-Type': 'application/json' },
104
+ body: JSON.stringify(payload)
105
+ })
106
+
107
+ if (!response.ok) {
108
+ const errorData = await response.json().catch(() => ({}))
109
+ throw new Error(errorData.error || 'Failed to save role')
110
+ }
111
+
112
+ const result = await response.json()
113
+ navigate(`/spine-framework/admin/configs/roles/${result.id || id}`)
114
+ } catch (err: any) {
115
+ setError(err.message || 'Failed to save role')
116
+ } finally {
117
+ setIsSubmitting(false)
118
+ }
119
+ }
120
+
121
+ if (loading) {
122
+ return (
123
+ <div className="space-y-6">
124
+ <Skeleton className="h-8 w-48" />
125
+ <Card>
126
+ <CardHeader><Skeleton className="h-6 w-32" /></CardHeader>
127
+ <CardContent className="space-y-4">
128
+ <Skeleton className="h-4 w-full" />
129
+ <Skeleton className="h-4 w-full" />
130
+ <Skeleton className="h-4 w-2/3" />
131
+ </CardContent>
132
+ </Card>
133
+ </div>
134
+ )
135
+ }
136
+
137
+ if (fetchError) {
138
+ return (
139
+ <Alert variant="destructive">
140
+ <AlertCircle className="h-4 w-4" />
141
+ <AlertTitle>Failed to load role</AlertTitle>
142
+ <AlertDescription>{fetchError}</AlertDescription>
143
+ </Alert>
144
+ )
145
+ }
146
+
147
+ return (
148
+ <div className="space-y-6">
149
+ <div className="flex items-center gap-4">
150
+ <Button variant="ghost" onClick={() => navigate(-1)}>
151
+ <ArrowLeft className="w-5 h-5" />
152
+ </Button>
153
+ <h1 className="text-2xl font-bold">
154
+ {isCreateMode ? 'Create Role' : role?.name || 'Role Detail'}
155
+ </h1>
156
+ </div>
157
+
158
+ {error && (
159
+ <Alert variant="destructive">
160
+ <AlertCircle className="h-4 w-4" />
161
+ <AlertTitle>Error</AlertTitle>
162
+ <AlertDescription>{error}</AlertDescription>
163
+ </Alert>
164
+ )}
165
+
166
+ <form onSubmit={handleSubmit} className="space-y-6">
167
+ <Card>
168
+ <CardHeader>
169
+ <CardTitle className="flex items-center gap-2">
170
+ <ShieldCheck className="w-5 h-5 text-primary" />
171
+ Basic Information
172
+ </CardTitle>
173
+ </CardHeader>
174
+ <CardContent>
175
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
176
+ <div className="space-y-2">
177
+ <Label>Name *</Label>
178
+ <Input
179
+ value={formData.name}
180
+ onChange={(e) => setFormData({ ...formData, name: e.target.value })}
181
+ placeholder="e.g., Sales Manager"
182
+ required
183
+ />
184
+ </div>
185
+ <div className="space-y-2">
186
+ <Label>Slug *</Label>
187
+ <Input
188
+ value={formData.slug}
189
+ onChange={(e) => setFormData({ ...formData, slug: e.target.value })}
190
+ placeholder="e.g., sales_manager"
191
+ required
192
+ disabled={!isCreateMode}
193
+ />
194
+ {!isCreateMode && (
195
+ <p className="text-xs text-muted-foreground">Slug cannot be changed after creation</p>
196
+ )}
197
+ </div>
198
+ <div className="md:col-span-2 space-y-2">
199
+ <Label>Description</Label>
200
+ <Textarea
201
+ value={formData.description}
202
+ onChange={(e) => setFormData({ ...formData, description: e.target.value })}
203
+ placeholder="Describe what this role can do..."
204
+ rows={2}
205
+ />
206
+ </div>
207
+ </div>
208
+ </CardContent>
209
+ </Card>
210
+
211
+ <Card>
212
+ <CardHeader>
213
+ <CardTitle>Settings</CardTitle>
214
+ </CardHeader>
215
+ <CardContent>
216
+ <div className="flex flex-wrap gap-6">
217
+ <div className="flex items-center gap-2">
218
+ <Checkbox
219
+ id="is_system"
220
+ checked={formData.is_system}
221
+ onCheckedChange={(checked) => setFormData({ ...formData, is_system: checked === true })}
222
+ disabled={!isCreateMode}
223
+ />
224
+ <div className="space-y-0.5">
225
+ <Label htmlFor="is_system" className="text-sm">System Role</Label>
226
+ <p className="text-xs text-muted-foreground">Cannot be deleted, reserved for core functions</p>
227
+ </div>
228
+ </div>
229
+ <div className="flex items-center gap-2">
230
+ <Checkbox
231
+ id="is_active"
232
+ checked={formData.is_active}
233
+ onCheckedChange={(checked) => setFormData({ ...formData, is_active: checked === true })}
234
+ />
235
+ <Label htmlFor="is_active" className="text-sm">Active</Label>
236
+ </div>
237
+ </div>
238
+ </CardContent>
239
+ </Card>
240
+
241
+ <Card>
242
+ <CardHeader>
243
+ <CardTitle>Permissions (JSON)</CardTitle>
244
+ </CardHeader>
245
+ <CardContent>
246
+ <p className="text-sm text-muted-foreground mb-3">
247
+ Define permissions as a JSON object. Each key is a type slug, value is an array of allowed actions.
248
+ </p>
249
+ <Textarea
250
+ value={permissionsJson}
251
+ onChange={(e) => setPermissionsJson(e.target.value)}
252
+ className="font-mono text-sm"
253
+ placeholder={`{\n "item": ["read", "write"],\n "account": ["read"]\n}`}
254
+ rows={10}
255
+ />
256
+ <p className="text-xs text-muted-foreground mt-2">
257
+ Example: <code className="bg-muted px-1 py-0.5 rounded">{`{"support_ticket": ["read", "write", "admin"]}`}</code>
258
+ </p>
259
+ </CardContent>
260
+ </Card>
261
+
262
+ <div className="flex items-center justify-end gap-3">
263
+ <Button type="button" variant="secondary" onClick={() => navigate(-1)}>
264
+ Cancel
265
+ </Button>
266
+ <Button type="submit" disabled={isSubmitting}>
267
+ {isSubmitting ? 'Saving...' : isCreateMode ? 'Create Role' : 'Update Role'}
268
+ </Button>
269
+ </div>
270
+ </form>
271
+ </div>
272
+ )
273
+ }
@@ -0,0 +1,292 @@
1
+ /**
2
+ * @module src/pages/admin/RolesPage
3
+ * @audience installer
4
+ * @layer frontend-page
5
+ * @stability stable
6
+ *
7
+ * Admin list page for roles. Fetches all roles via `/api/roles?action=list`,
8
+ * applies client-side search and sort, and renders inside `AdminListPage`
9
+ * with stat cards and a sortable table. Row clicks navigate to
10
+ * `/spine-framework/admin/configs/roles/:id`.
11
+ *
12
+ * @seeAlso src/components/admin/AdminListPage.tsx
13
+ * @seeAlso src/pages/admin/RoleDetailPage.tsx
14
+ */
15
+
16
+ import React, { useState } from 'react'
17
+ import { Plus, ShieldCheck, CheckCircle, XCircle, Settings, FileText, Cog } from 'lucide-react';
18
+ import { LoadingSpinner } from '../../components/ui/LoadingSpinner'
19
+ import { Button } from '../../components/ui/button'
20
+ import { AdminListPage } from '../../components/admin/AdminListPage'
21
+ import { SortableTableHeader } from '../../components/admin/SortableTableHeader'
22
+ import { formatDateTime } from '../../lib/utils'
23
+ import { useApi } from '../../hooks/useApi'
24
+ import { apiFetch } from '../../lib/api'
25
+
26
+ interface Role {
27
+ id: string
28
+ name: string
29
+ slug: string
30
+ description?: string
31
+ permissions?: Record<string, any>
32
+ is_system: boolean
33
+ is_active: boolean
34
+ app_id?: string
35
+ app?: any
36
+ created_at: string
37
+ updated_at: string
38
+ }
39
+
40
+ export function RolesPage() {
41
+ const [searchTerm, setSearchTerm] = useState('')
42
+ const [selectedStatus, setSelectedStatus] = useState('all')
43
+ const [selectedType, setSelectedType] = useState('all')
44
+ const [sortKey, setSortKey] = useState('name')
45
+ const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc')
46
+
47
+ // Fetch roles from API
48
+ const { data: roles, loading, error, refetch } = useApi<Role[]>(
49
+ async () => {
50
+ const response = await apiFetch('/api/roles?action=list')
51
+ if (!response.ok) throw new Error('Failed to fetch roles')
52
+ const result = await response.json()
53
+ return (result.data || result) as Role[]
54
+ },
55
+ { immediate: true }
56
+ )
57
+
58
+ const filteredRoles = (roles || []).filter(role => {
59
+ const matchesSearch = role.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
60
+ role.slug.toLowerCase().includes(searchTerm.toLowerCase()) ||
61
+ (role.description?.toLowerCase().includes(searchTerm.toLowerCase()) || false)
62
+ const matchesStatus = selectedStatus === 'all' ||
63
+ (selectedStatus === 'active' && role.is_active) ||
64
+ (selectedStatus === 'inactive' && !role.is_active)
65
+ const matchesType = selectedType === 'all' ||
66
+ (selectedType === 'system' && role.is_system) ||
67
+ (selectedType === 'custom' && !role.is_system)
68
+ return matchesSearch && matchesStatus && matchesType
69
+ })
70
+
71
+ // Helper functions
72
+ const getStatusBadge = (role: Role) => {
73
+ if (!role.is_active) {
74
+ return <span className="inline-flex px-2 py-0.5 text-xs font-medium rounded-md bg-slate-100 text-slate-600">Inactive</span>
75
+ }
76
+ if (role.is_system) {
77
+ return <span className="inline-flex px-2 py-0.5 text-xs font-medium rounded-md bg-green-100 text-green-700">System</span>
78
+ }
79
+ return <span className="inline-flex px-2 py-0.5 text-xs font-medium rounded-md bg-blue-100 text-blue-700">Custom</span>
80
+ }
81
+
82
+ const handleSort = (key: string) => {
83
+ if (sortKey === key) {
84
+ setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc')
85
+ } else {
86
+ setSortKey(key)
87
+ setSortDirection('asc')
88
+ }
89
+ }
90
+
91
+ const handleRowClick = (role: Role) => {
92
+ window.location.href = `/spine-framework/admin/configs/roles/${role.id}`
93
+ }
94
+
95
+ // Sort roles
96
+ const sortedRoles = [...(filteredRoles || [])].sort((a, b) => {
97
+ let aValue: any = a[sortKey as keyof Role]
98
+ let bValue: any = b[sortKey as keyof Role]
99
+
100
+ if (typeof aValue === 'string') {
101
+ return sortDirection === 'asc'
102
+ ? aValue.localeCompare(bValue)
103
+ : bValue.localeCompare(aValue)
104
+ }
105
+
106
+ if (typeof aValue === 'boolean') {
107
+ return sortDirection === 'asc' ? (aValue ? 1 : 0) : (bValue ? 1 : 0)
108
+ }
109
+
110
+ return 0
111
+ })
112
+
113
+ const statsCards = [
114
+ {
115
+ title: 'Total Roles',
116
+ value: (roles || []).length,
117
+ icon: ShieldCheck,
118
+ iconColor: 'text-blue-500'
119
+ },
120
+ {
121
+ title: 'Active',
122
+ value: (roles || []).filter(r => r.is_active).length,
123
+ icon: CheckCircle,
124
+ iconColor: 'text-green-500'
125
+ },
126
+ {
127
+ title: 'System Roles',
128
+ value: (roles || []).filter(r => r.is_system).length,
129
+ icon: Cog,
130
+ iconColor: 'text-purple-500'
131
+ },
132
+ {
133
+ title: 'Custom Roles',
134
+ value: (roles || []).filter(r => !r.is_system).length,
135
+ icon: FileText,
136
+ iconColor: 'text-orange-500'
137
+ }
138
+ ]
139
+
140
+ const statusOptions = [
141
+ { value: 'all', label: 'All Status' },
142
+ { value: 'active', label: 'Active' },
143
+ { value: 'inactive', label: 'Inactive' }
144
+ ]
145
+
146
+ const typeOptions = [
147
+ { value: 'all', label: 'All Types' },
148
+ { value: 'system', label: 'System' },
149
+ { value: 'custom', label: 'Custom' }
150
+ ]
151
+
152
+ const filters = [
153
+ {
154
+ label: 'Status',
155
+ value: selectedStatus,
156
+ options: statusOptions,
157
+ onChange: setSelectedStatus
158
+ },
159
+ {
160
+ label: 'Type',
161
+ value: selectedType,
162
+ options: typeOptions,
163
+ onChange: setSelectedType
164
+ }
165
+ ]
166
+
167
+ return (
168
+ <AdminListPage
169
+ title="Roles"
170
+ description="Manage system and custom roles"
171
+ newButtonText="Add Role"
172
+ newButtonHref="/spine-framework/admin/configs/roles/new"
173
+ statsCards={statsCards}
174
+ searchPlaceholder="Search roles..."
175
+ searchValue={searchTerm}
176
+ onSearchChange={setSearchTerm}
177
+ filters={filters}
178
+ loading={loading}
179
+ error={error}
180
+ emptyMessage="No roles found"
181
+ emptyIcon={ShieldCheck}
182
+ >
183
+ {sortedRoles.length === 0 ? (
184
+ <div className="p-8 text-center">
185
+ <ShieldCheck className="mx-auto h-12 w-12 text-slate-400" />
186
+ <h3 className="mt-2 text-sm font-medium text-slate-900">No roles found</h3>
187
+ <p className="mt-1 text-sm text-slate-500">
188
+ Try adjusting your search or filters
189
+ </p>
190
+ </div>
191
+ ) : (
192
+ <table className="min-w-full divide-y divide-slate-200">
193
+ <thead className="bg-slate-50">
194
+ <tr>
195
+ <SortableTableHeader
196
+ title="Role"
197
+ sortKey="name"
198
+ currentSortKey={sortKey}
199
+ currentSortDirection={sortDirection}
200
+ onSort={handleSort}
201
+ />
202
+ <SortableTableHeader
203
+ title="Slug"
204
+ sortKey="slug"
205
+ currentSortKey={sortKey}
206
+ currentSortDirection={sortDirection}
207
+ onSort={handleSort}
208
+ />
209
+ <SortableTableHeader
210
+ title="Type"
211
+ sortKey="is_system"
212
+ currentSortKey={sortKey}
213
+ currentSortDirection={sortDirection}
214
+ onSort={handleSort}
215
+ />
216
+ <SortableTableHeader
217
+ title="Status"
218
+ sortKey="is_active"
219
+ currentSortKey={sortKey}
220
+ currentSortDirection={sortDirection}
221
+ onSort={handleSort}
222
+ />
223
+ <SortableTableHeader
224
+ title="Permissions"
225
+ sortKey="permissions"
226
+ currentSortKey={sortKey}
227
+ currentSortDirection={sortDirection}
228
+ onSort={handleSort}
229
+ />
230
+ <SortableTableHeader
231
+ title="Created"
232
+ sortKey="created_at"
233
+ currentSortKey={sortKey}
234
+ currentSortDirection={sortDirection}
235
+ onSort={handleSort}
236
+ />
237
+ <th className="relative px-6 py-3">
238
+ <span className="sr-only">Actions</span>
239
+ </th>
240
+ </tr>
241
+ </thead>
242
+ <tbody className="bg-white divide-y divide-slate-200">
243
+ {sortedRoles.map((role) => (
244
+ <tr
245
+ key={role.id}
246
+ className="hover:bg-slate-50 cursor-pointer transition-colors"
247
+ onClick={() => handleRowClick(role)}
248
+ >
249
+ <td className="px-6 py-4 whitespace-nowrap">
250
+ <div>
251
+ <div className="font-medium text-slate-900">
252
+ <span className="text-accent-blue hover:text-navy">
253
+ {role.name}
254
+ </span>
255
+ </div>
256
+ {role.description && (
257
+ <div className="text-sm text-slate-500">{role.description}</div>
258
+ )}
259
+ </div>
260
+ </td>
261
+ <td className="px-6 py-4 whitespace-nowrap">
262
+ <span className="text-sm text-slate-500 font-mono">{role.slug}</span>
263
+ </td>
264
+ <td className="px-6 py-4 whitespace-nowrap">
265
+ {getStatusBadge(role)}
266
+ </td>
267
+ <td className="px-6 py-4 whitespace-nowrap">
268
+ <span className={`inline-flex px-2 py-0.5 text-xs font-medium rounded-md ${
269
+ role.is_active
270
+ ? 'bg-green-100 text-green-700'
271
+ : 'bg-slate-100 text-slate-600'
272
+ }`}>
273
+ {role.is_active ? 'Active' : 'Inactive'}
274
+ </span>
275
+ </td>
276
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-slate-500">
277
+ {Object.keys(role.permissions || {}).length}
278
+ </td>
279
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-slate-500">
280
+ {formatDateTime(role.created_at)}
281
+ </td>
282
+ <td className="px-6 py-4 whitespace-nowrap text-right">
283
+ <span className="text-slate-400">→</span>
284
+ </td>
285
+ </tr>
286
+ ))}
287
+ </tbody>
288
+ </table>
289
+ )}
290
+ </AdminListPage>
291
+ )
292
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @module src/pages/admin/SelectTestPage
3
+ * @audience installer
4
+ * @layer frontend-page
5
+ * @stability testing
6
+ *
7
+ * Test page to isolate Select component issues.
8
+ */
9
+
10
+ import { useState } from 'react'
11
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../components/ui/card'
12
+ import {
13
+ Select,
14
+ SelectContent,
15
+ SelectItem,
16
+ SelectTrigger,
17
+ SelectValue,
18
+ } from '../../components/ui/select'
19
+ import { Label } from '../../components/ui/label'
20
+
21
+ export function SelectTestPage() {
22
+ const [selected, setSelected] = useState('')
23
+
24
+ return (
25
+ <div className="flex-1 space-y-6 p-8 pt-6">
26
+ <div className="space-y-2">
27
+ <h1 className="text-3xl font-bold tracking-tight">Select Component Test</h1>
28
+ <p className="text-muted-foreground">
29
+ Testing shadcn Select component only
30
+ </p>
31
+ </div>
32
+
33
+ <Card>
34
+ <CardHeader>
35
+ <CardTitle>Select Demo</CardTitle>
36
+ <CardDescription>Testing Select component functionality</CardDescription>
37
+ </CardHeader>
38
+ <CardContent className="space-y-4">
39
+ <div className="space-y-2">
40
+ <Label htmlFor="select-demo">Choose an option</Label>
41
+ <Select value={selected} onValueChange={setSelected}>
42
+ <SelectTrigger id="select-demo">
43
+ <SelectValue placeholder="Select an option" />
44
+ </SelectTrigger>
45
+ <SelectContent>
46
+ <SelectItem value="option1">Option 1</SelectItem>
47
+ <SelectItem value="option2">Option 2</SelectItem>
48
+ <SelectItem value="option3">Option 3</SelectItem>
49
+ <SelectItem value="option4">Option 4</SelectItem>
50
+ </SelectContent>
51
+ </Select>
52
+ </div>
53
+
54
+ <div className="text-sm text-muted-foreground">
55
+ Selected value: {selected || 'none'}
56
+ </div>
57
+ </CardContent>
58
+ </Card>
59
+ </div>
60
+ )
61
+ }