@stacksjs/defaults 0.70.207 → 0.70.208

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 (408) hide show
  1. package/TODO.md +21 -0
  2. package/ai/AGENTS.md +79 -0
  3. package/ai/README.md +45 -0
  4. package/ai/claude/launch.json +12 -0
  5. package/ai/skills/stacks-actions/SKILL.md +128 -0
  6. package/ai/skills/stacks-ai/SKILL.md +276 -0
  7. package/ai/skills/stacks-alias/SKILL.md +94 -0
  8. package/ai/skills/stacks-analytics/SKILL.md +91 -0
  9. package/ai/skills/stacks-api/SKILL.md +738 -0
  10. package/ai/skills/stacks-arrays/SKILL.md +277 -0
  11. package/ai/skills/stacks-auth/SKILL.md +422 -0
  12. package/ai/skills/stacks-auto-imports/SKILL.md +138 -0
  13. package/ai/skills/stacks-brandkit/SKILL.md +811 -0
  14. package/ai/skills/stacks-browse/SKILL.md +127 -0
  15. package/ai/skills/stacks-browse/scripts/browse.ts +492 -0
  16. package/ai/skills/stacks-browser/SKILL.md +145 -0
  17. package/ai/skills/stacks-buddy/SKILL.md +676 -0
  18. package/ai/skills/stacks-build/SKILL.md +163 -0
  19. package/ai/skills/stacks-cache/SKILL.md +125 -0
  20. package/ai/skills/stacks-calendar/SKILL.md +113 -0
  21. package/ai/skills/stacks-chat/SKILL.md +371 -0
  22. package/ai/skills/stacks-cli/SKILL.md +112 -0
  23. package/ai/skills/stacks-cloud/SKILL.md +192 -0
  24. package/ai/skills/stacks-cms/SKILL.md +217 -0
  25. package/ai/skills/stacks-collections/SKILL.md +82 -0
  26. package/ai/skills/stacks-commerce/SKILL.md +114 -0
  27. package/ai/skills/stacks-composables/SKILL.md +161 -0
  28. package/ai/skills/stacks-config/SKILL.md +144 -0
  29. package/ai/skills/stacks-configuration/SKILL.md +154 -0
  30. package/ai/skills/stacks-cron/SKILL.md +123 -0
  31. package/ai/skills/stacks-crosswind/SKILL.md +190 -0
  32. package/ai/skills/stacks-dashboard/SKILL.md +173 -0
  33. package/ai/skills/stacks-database/SKILL.md +286 -0
  34. package/ai/skills/stacks-datetime/SKILL.md +269 -0
  35. package/ai/skills/stacks-dependencies/SKILL.md +111 -0
  36. package/ai/skills/stacks-deploy/SKILL.md +102 -0
  37. package/ai/skills/stacks-design-brutalist/SKILL.md +110 -0
  38. package/ai/skills/stacks-design-minimalist/SKILL.md +115 -0
  39. package/ai/skills/stacks-design-output/SKILL.md +55 -0
  40. package/ai/skills/stacks-design-soft/SKILL.md +156 -0
  41. package/ai/skills/stacks-design-taste/SKILL.md +1012 -0
  42. package/ai/skills/stacks-design-taste-codex/SKILL.md +294 -0
  43. package/ai/skills/stacks-desktop/SKILL.md +78 -0
  44. package/ai/skills/stacks-development/SKILL.md +298 -0
  45. package/ai/skills/stacks-dns/SKILL.md +149 -0
  46. package/ai/skills/stacks-docs/SKILL.md +109 -0
  47. package/ai/skills/stacks-email/SKILL.md +236 -0
  48. package/ai/skills/stacks-enums/SKILL.md +100 -0
  49. package/ai/skills/stacks-env/SKILL.md +181 -0
  50. package/ai/skills/stacks-error-handling/SKILL.md +137 -0
  51. package/ai/skills/stacks-events/SKILL.md +228 -0
  52. package/ai/skills/stacks-faker/SKILL.md +158 -0
  53. package/ai/skills/stacks-git/SKILL.md +158 -0
  54. package/ai/skills/stacks-guard/SKILL.md +98 -0
  55. package/ai/skills/stacks-health/SKILL.md +75 -0
  56. package/ai/skills/stacks-http/SKILL.md +316 -0
  57. package/ai/skills/stacks-i18n/SKILL.md +198 -0
  58. package/ai/skills/stacks-image-to-code/SKILL.md +629 -0
  59. package/ai/skills/stacks-imagegen-mobile/SKILL.md +1474 -0
  60. package/ai/skills/stacks-imagegen-web/SKILL.md +998 -0
  61. package/ai/skills/stacks-investigate/SKILL.md +119 -0
  62. package/ai/skills/stacks-jobs/SKILL.md +136 -0
  63. package/ai/skills/stacks-lint/SKILL.md +184 -0
  64. package/ai/skills/stacks-listeners/SKILL.md +99 -0
  65. package/ai/skills/stacks-logging/SKILL.md +78 -0
  66. package/ai/skills/stacks-mail/SKILL.md +120 -0
  67. package/ai/skills/stacks-middleware/SKILL.md +249 -0
  68. package/ai/skills/stacks-migrations/SKILL.md +105 -0
  69. package/ai/skills/stacks-models/SKILL.md +267 -0
  70. package/ai/skills/stacks-new-feature/SKILL.md +206 -0
  71. package/ai/skills/stacks-notifications/SKILL.md +210 -0
  72. package/ai/skills/stacks-objects/SKILL.md +205 -0
  73. package/ai/skills/stacks-office-hours/SKILL.md +134 -0
  74. package/ai/skills/stacks-orm/SKILL.md +342 -0
  75. package/ai/skills/stacks-path/SKILL.md +118 -0
  76. package/ai/skills/stacks-payments/SKILL.md +373 -0
  77. package/ai/skills/stacks-plan-review/SKILL.md +152 -0
  78. package/ai/skills/stacks-plugins/SKILL.md +149 -0
  79. package/ai/skills/stacks-push/SKILL.md +376 -0
  80. package/ai/skills/stacks-query-builder/SKILL.md +128 -0
  81. package/ai/skills/stacks-queue/SKILL.md +559 -0
  82. package/ai/skills/stacks-realtime/SKILL.md +442 -0
  83. package/ai/skills/stacks-redesign/SKILL.md +182 -0
  84. package/ai/skills/stacks-registry/SKILL.md +58 -0
  85. package/ai/skills/stacks-repl/SKILL.md +80 -0
  86. package/ai/skills/stacks-retro/SKILL.md +122 -0
  87. package/ai/skills/stacks-review/SKILL.md +135 -0
  88. package/ai/skills/stacks-router/SKILL.md +231 -0
  89. package/ai/skills/stacks-routes/SKILL.md +105 -0
  90. package/ai/skills/stacks-scaffolding/SKILL.md +162 -0
  91. package/ai/skills/stacks-scheduler/SKILL.md +363 -0
  92. package/ai/skills/stacks-search-engine/SKILL.md +112 -0
  93. package/ai/skills/stacks-security/SKILL.md +129 -0
  94. package/ai/skills/stacks-security-audit/SKILL.md +128 -0
  95. package/ai/skills/stacks-server/SKILL.md +458 -0
  96. package/ai/skills/stacks-shell/SKILL.md +62 -0
  97. package/ai/skills/stacks-slug/SKILL.md +87 -0
  98. package/ai/skills/stacks-sms/SKILL.md +385 -0
  99. package/ai/skills/stacks-socials/SKILL.md +303 -0
  100. package/ai/skills/stacks-storage/SKILL.md +576 -0
  101. package/ai/skills/stacks-strings/SKILL.md +416 -0
  102. package/ai/skills/stacks-stx/SKILL.md +176 -0
  103. package/ai/skills/stacks-technical-diagrams/LICENSE +22 -0
  104. package/ai/skills/stacks-technical-diagrams/SKILL.md +290 -0
  105. package/ai/skills/stacks-technical-diagrams/agents/openai.yaml +4 -0
  106. package/ai/skills/stacks-technical-diagrams/assets/template.html +1252 -0
  107. package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +289 -0
  108. package/ai/skills/stacks-technical-diagrams/bunfig.toml +1 -0
  109. package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +64 -0
  110. package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +88 -0
  111. package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +75 -0
  112. package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +70 -0
  113. package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +41 -0
  114. package/ai/skills/stacks-technical-diagrams/renderers/architecture/grid.mjs +62 -0
  115. package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +371 -0
  116. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +82 -0
  117. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +295 -0
  118. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +91 -0
  119. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +384 -0
  120. package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +84 -0
  121. package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +257 -0
  122. package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +42 -0
  123. package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +13 -0
  124. package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +230 -0
  125. package/ai/skills/stacks-technical-diagrams/renderers/shared/layout-report.mjs +40 -0
  126. package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +88 -0
  127. package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +40 -0
  128. package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +103 -0
  129. package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +481 -0
  130. package/ai/skills/stacks-technical-diagrams/schemas/README.md +68 -0
  131. package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +121 -0
  132. package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +43 -0
  133. package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +221 -0
  134. package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +241 -0
  135. package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +186 -0
  136. package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +332 -0
  137. package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +297 -0
  138. package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +28 -0
  139. package/ai/skills/stacks-testing/SKILL.md +129 -0
  140. package/ai/skills/stacks-tunnel/SKILL.md +125 -0
  141. package/ai/skills/stacks-types/SKILL.md +178 -0
  142. package/ai/skills/stacks-ui/SKILL.md +246 -0
  143. package/ai/skills/stacks-utils/SKILL.md +444 -0
  144. package/ai/skills/stacks-validation/SKILL.md +371 -0
  145. package/ai/skills/stacks-whois/SKILL.md +315 -0
  146. package/assets/world-map-data.ts +121 -0
  147. package/bootstrap.ts +114 -0
  148. package/docs/api-examples.md +46 -0
  149. package/docs/index.md +24 -0
  150. package/docs/markdown-examples.md +87 -0
  151. package/functions/analytics.ts +97 -0
  152. package/functions/api.ts +143 -0
  153. package/functions/auth.ts +208 -0
  154. package/functions/billing/payments.ts +149 -0
  155. package/functions/blog.ts +120 -0
  156. package/functions/cms/categorizables.ts +107 -0
  157. package/functions/cms/pages.ts +108 -0
  158. package/functions/cms/posts.ts +100 -0
  159. package/functions/cms/taggables.ts +102 -0
  160. package/functions/commerce/coupons.ts +128 -0
  161. package/functions/commerce/customers.ts +128 -0
  162. package/functions/commerce/gift-cards.ts +119 -0
  163. package/functions/commerce/orders.ts +112 -0
  164. package/functions/commerce/payments.ts +119 -0
  165. package/functions/commerce/products/categories.ts +123 -0
  166. package/functions/commerce/products/manufacturers.ts +123 -0
  167. package/functions/commerce/products/products.ts +123 -0
  168. package/functions/commerce/products/reviews.ts +123 -0
  169. package/functions/commerce/products/units.ts +123 -0
  170. package/functions/commerce/products/variants.ts +123 -0
  171. package/functions/commerce/shippings/delivery-routes.ts +123 -0
  172. package/functions/commerce/shippings/digital-deliveries.ts +119 -0
  173. package/functions/commerce/shippings/drivers.ts +123 -0
  174. package/functions/commerce/shippings/license-keys.ts +123 -0
  175. package/functions/commerce/shippings/shipping-methods.ts +123 -0
  176. package/functions/commerce/shippings/shipping-rates.ts +119 -0
  177. package/functions/commerce/shippings/shipping-zones.ts +119 -0
  178. package/functions/commerce/taxes.ts +119 -0
  179. package/functions/commerce/waitlist/products.ts +123 -0
  180. package/functions/commerce/waitlist/restaurants.ts +123 -0
  181. package/functions/craft.ts +107 -0
  182. package/functions/dashboard.ts +153 -0
  183. package/functions/inbox.ts +95 -0
  184. package/functions/jobs.ts +86 -0
  185. package/functions/monitoring/errors.ts +295 -0
  186. package/functions/queue.ts +111 -0
  187. package/functions/realtime/websockets.ts +96 -0
  188. package/functions/realtime.ts +52 -0
  189. package/functions/releases.ts +49 -0
  190. package/functions/search.ts +126 -0
  191. package/functions/toasts.ts +95 -0
  192. package/functions/uploads.ts +216 -0
  193. package/ide/.gitignore +4 -0
  194. package/ide/cursor/rules/auto-imports.mdc +574 -0
  195. package/ide/cursor/rules/code-style.mdc +12 -0
  196. package/ide/cursor/rules/documentation.mdc +9 -0
  197. package/ide/cursor/rules/error-handling-validation.mdc +11 -0
  198. package/ide/cursor/rules/key-conventions.mdc +11 -0
  199. package/ide/cursor/rules/project-structure.mdc +169 -0
  200. package/ide/cursor/rules/readme.mdc +318 -0
  201. package/ide/cursor/rules/syntax-formatting.mdc +9 -0
  202. package/ide/cursor/rules/testing.mdc +10 -0
  203. package/ide/cursor/rules/typescript.mdc +9 -0
  204. package/ide/cursor/rules/ui-styling.mdc +0 -0
  205. package/ide/dictionary.txt +397 -0
  206. package/ide/jetbrains/.fleet/settings.json +4 -0
  207. package/ide/jetbrains/.idea/.gitignore +5 -0
  208. package/ide/jetbrains/.idea/.name +1 -0
  209. package/ide/jetbrains/.idea/inspectionProfiles/Project_Default.xml +6 -0
  210. package/ide/jetbrains/.idea/jsLinters/eslint.xml +7 -0
  211. package/ide/jetbrains/.idea/modules.xml +8 -0
  212. package/ide/jetbrains/.idea/stacks.iml +22 -0
  213. package/ide/jetbrains/.idea/vcs.xml +6 -0
  214. package/ide/jetbrains/.idea/webResources.xml +14 -0
  215. package/ide/jetbrains/IntelliJ IDEA Global Settings +0 -0
  216. package/ide/jetbrains/installed.txt +4 -0
  217. package/ide/jetbrains/options/github-copilot.xml +5 -0
  218. package/ide/jetbrains/options/nodejs.xml +7 -0
  219. package/ide/jetbrains/options/templates.xml +124 -0
  220. package/ide/jetbrains/templates/JavaScript.xml +6 -0
  221. package/ide/jetbrains/templates/Stacks.xml +8 -0
  222. package/ide/vscode/.vscode/README.md +23 -0
  223. package/ide/vscode/.vscode/extensions.json +5 -0
  224. package/ide/vscode/.vscode/settings.json +176 -0
  225. package/ide/vscode/.vscode/stacks.code-snippets +181 -0
  226. package/ide/vscode/LICENSE.md +21 -0
  227. package/ide/vscode/README.md +36 -0
  228. package/ide/vscode/logo.png +0 -0
  229. package/ide/vscode/package.json +79 -0
  230. package/ide/zed/.zed/settings.json +139 -0
  231. package/lang/README.md +28 -0
  232. package/lang/de.yml +14 -0
  233. package/lang/en.yml +15 -0
  234. package/package.json +19 -3
  235. package/project/bootstrap +161 -0
  236. package/project/buddy +175 -0
  237. package/project/storage/framework/tsconfig.app.json +50 -0
  238. package/project/storage/framework/tsconfig.base.json +58 -0
  239. package/routes/core.ts +78 -0
  240. package/routes/dashboard-api.ts +233 -0
  241. package/routes/dashboard.ts +739 -0
  242. package/routes/email.ts +129 -0
  243. package/stores/[name].stx +16 -0
  244. package/stores/git.ts +71 -0
  245. package/stores/payment.ts +474 -0
  246. package/stores/queue.ts +43 -0
  247. package/stores/user.ts +29 -0
  248. package/stx-components-plugin.ts +25 -0
  249. package/styles/design-tokens.css +597 -0
  250. package/types/dashboard.ts +70 -0
  251. package/types/defaults.ts +438 -0
  252. package/vcs/github/CODE_OF_CONDUCT.md +83 -0
  253. package/vcs/github/CONTRIBUTING.md +187 -0
  254. package/vcs/github/EXPLANATIONS.md +41 -0
  255. package/vcs/github/FUNDING.yml +1 -0
  256. package/vcs/github/ISSUE_TEMPLATE/bug_report.yml +49 -0
  257. package/vcs/github/ISSUE_TEMPLATE/config.yml +10 -0
  258. package/vcs/github/ISSUE_TEMPLATE/feature_request.yml +44 -0
  259. package/vcs/github/SECURITY.md +70 -0
  260. package/vcs/github/renovate.json +5 -0
  261. package/vcs/github/stale.yml +14 -0
  262. package/vcs/github/workflows/README.md +16 -0
  263. package/vcs/github/workflows/ci.yml +114 -0
  264. package/vcs/github/workflows/export-size.yml +25 -0
  265. package/vcs/github/workflows/labeler.yml +13 -0
  266. package/vcs/github/workflows/release.yml +219 -0
  267. package/views/[...all].stx +8 -0
  268. package/views/about.md +3 -0
  269. package/views/auth/login.stx +1 -0
  270. package/views/auth/signup.stx +85 -0
  271. package/views/dashboard/.discovered-models.json +542 -0
  272. package/views/dashboard/AUDIT.md +904 -0
  273. package/views/dashboard/[...all].stx +89 -0
  274. package/views/dashboard/access-tokens/index.stx +177 -0
  275. package/views/dashboard/actions/index.stx +129 -0
  276. package/views/dashboard/analytics/blog/index.stx +356 -0
  277. package/views/dashboard/analytics/browsers/index.stx +263 -0
  278. package/views/dashboard/analytics/commerce/sales/index.stx +329 -0
  279. package/views/dashboard/analytics/commerce/web/index.stx +379 -0
  280. package/views/dashboard/analytics/countries/index.stx +289 -0
  281. package/views/dashboard/analytics/devices/index.stx +305 -0
  282. package/views/dashboard/analytics/events/index.stx +536 -0
  283. package/views/dashboard/analytics/index.stx +88 -0
  284. package/views/dashboard/analytics/marketing/index.stx +315 -0
  285. package/views/dashboard/analytics/pages/index.stx +271 -0
  286. package/views/dashboard/analytics/referrers/index.stx +319 -0
  287. package/views/dashboard/analytics/web/index.stx +356 -0
  288. package/views/dashboard/buddy/index.stx +198 -0
  289. package/views/dashboard/ci/index.stx +462 -0
  290. package/views/dashboard/cloud/index.stx +452 -0
  291. package/views/dashboard/commands/index.stx +295 -0
  292. package/views/dashboard/commerce/categories/index.stx +406 -0
  293. package/views/dashboard/commerce/coupons/index.stx +485 -0
  294. package/views/dashboard/commerce/customers/index.stx +410 -0
  295. package/views/dashboard/commerce/dashboard/index.stx +287 -0
  296. package/views/dashboard/commerce/delivery/delivery-routes.stx +305 -0
  297. package/views/dashboard/commerce/delivery/digital-delivery.stx +332 -0
  298. package/views/dashboard/commerce/delivery/drivers.stx +397 -0
  299. package/views/dashboard/commerce/delivery/index.stx +18 -0
  300. package/views/dashboard/commerce/delivery/license-keys.stx +380 -0
  301. package/views/dashboard/commerce/delivery/shipping-methods.stx +346 -0
  302. package/views/dashboard/commerce/delivery/shipping-rates.stx +338 -0
  303. package/views/dashboard/commerce/delivery/shipping-zones.stx +302 -0
  304. package/views/dashboard/commerce/delivery/tabs.ts +25 -0
  305. package/views/dashboard/commerce/gift-cards/index.stx +561 -0
  306. package/views/dashboard/commerce/manufacturers/index.stx +308 -0
  307. package/views/dashboard/commerce/orders/index.stx +247 -0
  308. package/views/dashboard/commerce/payments/index.stx +535 -0
  309. package/views/dashboard/commerce/pos/index.stx +653 -0
  310. package/views/dashboard/commerce/printers/devices/index.stx +273 -0
  311. package/views/dashboard/commerce/printers/receipts/index.stx +308 -0
  312. package/views/dashboard/commerce/products/detail.stx +563 -0
  313. package/views/dashboard/commerce/products/index.stx +370 -0
  314. package/views/dashboard/commerce/reviews/index.stx +656 -0
  315. package/views/dashboard/commerce/reviews/index.ts +0 -0
  316. package/views/dashboard/commerce/taxes/index.stx +377 -0
  317. package/views/dashboard/commerce/units/index.stx +317 -0
  318. package/views/dashboard/commerce/variants/index.stx +319 -0
  319. package/views/dashboard/commerce/waitlist/products.stx +942 -0
  320. package/views/dashboard/commerce/waitlist/restaurant.stx +1024 -0
  321. package/views/dashboard/components/index.stx +330 -0
  322. package/views/dashboard/composables/index.ts +19 -0
  323. package/views/dashboard/composables/useChart.ts +77 -0
  324. package/views/dashboard/composables/useNavigation.ts +99 -0
  325. package/views/dashboard/composables/useRole.ts +58 -0
  326. package/views/dashboard/composables/useTheme.ts +74 -0
  327. package/views/dashboard/content/authors/index.stx +171 -0
  328. package/views/dashboard/content/blog/index.stx +493 -0
  329. package/views/dashboard/content/categories/index.stx +169 -0
  330. package/views/dashboard/content/comments/index.stx +189 -0
  331. package/views/dashboard/content/dashboard.stx +383 -0
  332. package/views/dashboard/content/files/index.stx +706 -0
  333. package/views/dashboard/content/pages/index.stx +174 -0
  334. package/views/dashboard/content/posts/index.stx +269 -0
  335. package/views/dashboard/content/seo/index.stx +240 -0
  336. package/views/dashboard/content/tags/index.stx +167 -0
  337. package/views/dashboard/data/activity/index.stx +261 -0
  338. package/views/dashboard/data/dashboard/index.stx +116 -0
  339. package/views/dashboard/data/subscribers.stx +270 -0
  340. package/views/dashboard/data/teams.stx +275 -0
  341. package/views/dashboard/data/users.stx +279 -0
  342. package/views/dashboard/dependencies/index.stx +178 -0
  343. package/views/dashboard/deployments/[id].stx +234 -0
  344. package/views/dashboard/deployments/index.stx +9 -0
  345. package/views/dashboard/dns/index.stx +163 -0
  346. package/views/dashboard/environment/index.stx +283 -0
  347. package/views/dashboard/errors/index.stx +274 -0
  348. package/views/dashboard/forgot-password.stx +57 -0
  349. package/views/dashboard/functions/index.stx +159 -0
  350. package/views/dashboard/health/index.stx +248 -0
  351. package/views/dashboard/inbox/activity.stx +350 -0
  352. package/views/dashboard/inbox/index.stx +452 -0
  353. package/views/dashboard/inbox/settings.stx +290 -0
  354. package/views/dashboard/index.stx +252 -0
  355. package/views/dashboard/insights/index.stx +529 -0
  356. package/views/dashboard/jobs/[id].stx +430 -0
  357. package/views/dashboard/jobs/history.stx +222 -0
  358. package/views/dashboard/jobs/index.stx +214 -0
  359. package/views/dashboard/kanban/[id].stx +899 -0
  360. package/views/dashboard/kanban/index.stx +260 -0
  361. package/views/dashboard/layouts/default.stx +565 -0
  362. package/views/dashboard/layouts/guest.stx +10 -0
  363. package/views/dashboard/login.stx +48 -0
  364. package/views/dashboard/logs/index.stx +540 -0
  365. package/views/dashboard/mailboxes/index.stx +149 -0
  366. package/views/dashboard/management/permissions/index.stx +490 -0
  367. package/views/dashboard/marketing/campaigns/index.stx +604 -0
  368. package/views/dashboard/marketing/lists/index.stx +514 -0
  369. package/views/dashboard/marketing/reviews/index.stx +475 -0
  370. package/views/dashboard/marketing/social-posts/index.stx +560 -0
  371. package/views/dashboard/models/[model].stx +195 -0
  372. package/views/dashboard/models/index.stx +159 -0
  373. package/views/dashboard/monitoring/errors/index.stx +415 -0
  374. package/views/dashboard/notifications/dashboard.stx +383 -0
  375. package/views/dashboard/notifications/email.stx +336 -0
  376. package/views/dashboard/notifications/history.stx +373 -0
  377. package/views/dashboard/notifications/sms.stx +332 -0
  378. package/views/dashboard/packages/index.stx +195 -0
  379. package/views/dashboard/queries/[id].stx +381 -0
  380. package/views/dashboard/queries/history.stx +227 -0
  381. package/views/dashboard/queries/index.stx +335 -0
  382. package/views/dashboard/queries/slow.stx +318 -0
  383. package/views/dashboard/queue/index.stx +427 -0
  384. package/views/dashboard/realtime/index.stx +429 -0
  385. package/views/dashboard/register.stx +46 -0
  386. package/views/dashboard/releases/index.stx +389 -0
  387. package/views/dashboard/requests/index.stx +462 -0
  388. package/views/dashboard/serverless/index.stx +475 -0
  389. package/views/dashboard/servers/[id].stx +299 -0
  390. package/views/dashboard/servers/index.stx +483 -0
  391. package/views/dashboard/settings/billing.stx +50 -0
  392. package/views/dashboard/settings/index.stx +256 -0
  393. package/views/dashboard/settings/mail.stx +252 -0
  394. package/views/dashboard/stores/auth.ts +123 -0
  395. package/views/dashboard/stores/ci.ts +342 -0
  396. package/views/dashboard/stores/index.ts +129 -0
  397. package/views/dashboard/stores/kanban.ts +723 -0
  398. package/views/dashboard/stores/rbac.ts +350 -0
  399. package/views/dashboard/stx.config.ts +34 -0
  400. package/views/dashboard/teams/[id].stx +170 -0
  401. package/views/forum/category.stx +92 -0
  402. package/views/forum/index.stx +209 -0
  403. package/views/forum/new-topic.stx +113 -0
  404. package/views/forum/profile.stx +1 -0
  405. package/views/forum/topic.stx +143 -0
  406. package/views/hello-world/[name].stx +47 -0
  407. package/views/index.stx +21 -0
  408. package/views/system-tray/index.stx +147 -0
@@ -0,0 +1,208 @@
1
+ import type { Ref } from '@stacksjs/stx'
2
+ import type { AuthUser, ErrorResponse, LoginError, LoginResponse, MeResponse, RegisterError, RegisterResponse, UserData } from '../types/dashboard'
3
+ import { useStorage } from '@stacksjs/browser'
4
+ import { ref } from '@stacksjs/stx'
5
+
6
+ const token = useStorage('token', '')
7
+ const user = useStorage<UserData | null>('user', null, undefined, {
8
+
9
+ serializer: {
10
+ read: (v: string): UserData | null => {
11
+ try {
12
+ return v ? (JSON.parse(v) as UserData | null) : null
13
+ }
14
+ catch {
15
+ return null
16
+ }
17
+ },
18
+ write: (v: UserData | null): string => JSON.stringify(v),
19
+ },
20
+ })
21
+
22
+ const baseUrl = process.env.VITE_API_URL || `http://localhost:${process.env.PORT_API || '3008'}`
23
+
24
+ // Create singleton state
25
+ const isAuthenticated = ref(false)
26
+
27
+ export interface AuthComposable {
28
+ isAuthenticated: Ref<boolean>
29
+ user: { value: UserData | null }
30
+ login: (user: AuthUser) => Promise<LoginResponse | LoginError>
31
+ register: (user: AuthUser) => Promise<RegisterResponse | RegisterError>
32
+ fetchAuthUser: () => Promise<UserData | null>
33
+ checkAuthentication: () => Promise<boolean>
34
+ logout: () => void
35
+ getToken: () => string | null
36
+ token: { value: string | null }
37
+ }
38
+
39
+ export function useAuth(): AuthComposable {
40
+ async function fetchAuthUser(): Promise<UserData | null> {
41
+ try {
42
+ if (!token.value) {
43
+ isAuthenticated.value = false
44
+ user.value = null
45
+ return null
46
+ }
47
+
48
+ const response = await fetch(`${baseUrl}/me`, {
49
+ headers: {
50
+ Authorization: `Bearer ${token.value}`,
51
+ Accept: 'application/json',
52
+ },
53
+ })
54
+
55
+ if (!response.ok) {
56
+ isAuthenticated.value = false
57
+ user.value = null
58
+ return null
59
+ }
60
+
61
+ const data = await response.json() as MeResponse
62
+
63
+ user.value = data
64
+ isAuthenticated.value = true
65
+
66
+ return data
67
+ }
68
+ catch (error) {
69
+ console.error('Error fetching user:', error)
70
+ isAuthenticated.value = false
71
+ user.value = null
72
+ return null
73
+ }
74
+ }
75
+
76
+ async function checkAuthentication(): Promise<boolean> {
77
+ try {
78
+ const userData = await fetchAuthUser()
79
+ return userData !== null
80
+ }
81
+ catch (error) {
82
+ console.error('Error checking authentication:', error)
83
+ return false
84
+ }
85
+ }
86
+
87
+ async function register(user: AuthUser): Promise<RegisterResponse | RegisterError> {
88
+ const url = `${baseUrl}/register`
89
+ const response = await fetch(url, {
90
+ method: 'POST',
91
+ headers: {
92
+ 'Content-Type': 'application/json',
93
+ },
94
+ body: JSON.stringify(user),
95
+ })
96
+
97
+ if (!response.ok) {
98
+ const errorData = await response.json() as RegisterError
99
+ return errorData
100
+ }
101
+
102
+ const data = await response.json() as RegisterResponse | RegisterError
103
+
104
+ if (isRegisterError(data)) {
105
+ return data
106
+ }
107
+
108
+ if (isRegisterResponse(data)) {
109
+ token.value = data.token
110
+ return data
111
+ }
112
+
113
+ return data
114
+ }
115
+
116
+ function isRegisterError(data: RegisterResponse | RegisterError): data is RegisterError {
117
+ return 'errors' in data
118
+ }
119
+
120
+ function isRegisterResponse(data: RegisterResponse | RegisterError): data is RegisterResponse {
121
+ return 'token' in data && 'user' in data
122
+ }
123
+
124
+ async function login(user: AuthUser): Promise<LoginResponse | LoginError> {
125
+ try {
126
+ const url = `${baseUrl}/login`
127
+ const response = await fetch(url, {
128
+ method: 'POST',
129
+ headers: {
130
+ 'Content-Type': 'application/json',
131
+ },
132
+ body: JSON.stringify(user),
133
+ })
134
+
135
+ if (!response.ok) {
136
+ const errorData = await response.json() as ErrorResponse
137
+ throw new Error(errorData.message || `HTTP error! status: ${response.status}`)
138
+ }
139
+
140
+ const data = await response.json() as LoginResponse
141
+
142
+ token.value = data.token
143
+
144
+ return data
145
+ }
146
+ catch (error) {
147
+ return error as LoginError
148
+ }
149
+ }
150
+
151
+ async function logout() {
152
+ try {
153
+ if (token.value) {
154
+ await fetch(`${baseUrl}/logout`, {
155
+ method: 'POST',
156
+ headers: {
157
+ Authorization: `Bearer ${token.value}`,
158
+ Accept: 'application/json',
159
+ },
160
+ })
161
+ }
162
+ }
163
+ catch (error) {
164
+ console.error('Error during logout:', error)
165
+ }
166
+ finally {
167
+ token.value = ''
168
+ user.value = null
169
+ isAuthenticated.value = false
170
+ }
171
+ }
172
+
173
+ return {
174
+ user,
175
+ isAuthenticated,
176
+ token,
177
+ getToken: () => token.value,
178
+ register,
179
+ login,
180
+ logout,
181
+ fetchAuthUser,
182
+ checkAuthentication,
183
+ }
184
+ }
185
+
186
+ // Strict auth guard middleware
187
+ // Usage: call in setup() of page/component, or in router beforeEach
188
+ // Pass { guest: true } for guest-only pages
189
+ export function authGuard(options: { guest?: boolean } = {}): void {
190
+ const guest = options.guest ?? false
191
+ const { isAuthenticated } = useAuth()
192
+
193
+ // Guard against SSR — window is only available in the browser
194
+ if (typeof window === 'undefined') return
195
+
196
+ if (guest) {
197
+ // Guest-only page: block if authenticated
198
+ if (isAuthenticated.value) {
199
+ window.location.replace('/')
200
+ }
201
+ return
202
+ }
203
+
204
+ // Auth-only page: block if not authenticated
205
+ if (!isAuthenticated.value) {
206
+ window.location.replace('/login')
207
+ }
208
+ }
@@ -0,0 +1,149 @@
1
+ import { confirmCardSetup, confirmPayment, loadCardElement, loadPaymentElement } from '@stacksjs/browser'
2
+
3
+ export function useBillable() {
4
+ // Resolved per call, not at module scope: `usePaymentStore` is a browser
5
+ // auto-import, so calling it while the module is being evaluated throws
6
+ // "usePaymentStore is not defined" the moment anything imports this file
7
+ // server-side, which the generated auto-import manifest does.
8
+ const paymentStore = usePaymentStore()
9
+
10
+ function convertUnixTimestampToDate(timestamp: number): string {
11
+ // Create a Date object from the Unix timestamp
12
+ const date = new Date(timestamp * 1000) // Multiply by 1000 to convert to milliseconds
13
+
14
+ // Define arrays for month names
15
+ const monthNames = [
16
+ 'January',
17
+ 'February',
18
+ 'March',
19
+ 'April',
20
+ 'May',
21
+ 'June',
22
+ 'July',
23
+ 'August',
24
+ 'September',
25
+ 'October',
26
+ 'November',
27
+ 'December',
28
+ ]
29
+
30
+ // Extract day, month, and year
31
+ const day = date.getDate()
32
+ const month = monthNames[date.getMonth()]
33
+ const year = date.getFullYear()
34
+
35
+ // Return the formatted string in "Month Day, Year" format
36
+ return `${month} ${day}, ${year}`
37
+ }
38
+
39
+ function formatTimestampDate(timestamp: string): string {
40
+ const date = new Date(timestamp)
41
+
42
+ const monthNames = [
43
+ 'January',
44
+ 'February',
45
+ 'March',
46
+ 'April',
47
+ 'May',
48
+ 'June',
49
+ 'July',
50
+ 'August',
51
+ 'September',
52
+ 'October',
53
+ 'November',
54
+ 'December',
55
+ ]
56
+
57
+ // Extract day, month, and year
58
+ const day = date.getDate()
59
+ const month = monthNames[date.getMonth()]
60
+ const year = date.getFullYear()
61
+
62
+ // Return the formatted string in "Month Day, Year" format
63
+ return `${month} ${day}, ${year}`
64
+ }
65
+
66
+ async function loadCardForm(clientSecret: string): Promise<boolean> {
67
+ const isCreated = await loadCardElement(clientSecret)
68
+
69
+ return isCreated
70
+ }
71
+
72
+ async function loadPaymentForm(clientSecret: string): Promise<boolean> {
73
+ const isCreated = await loadPaymentElement(clientSecret)
74
+
75
+ return isCreated
76
+ }
77
+
78
+ async function handleAddPaymentMethod(clientSecret: string, elements: any) {
79
+ try {
80
+ const { error, setupIntent } = await confirmCardSetup(clientSecret, elements)
81
+
82
+ if (error) {
83
+ console.error(error.message)
84
+ }
85
+ else {
86
+ await paymentStore.storePaymentMethod(setupIntent.payment_method)
87
+
88
+ if (!paymentStore.hasPaymentMethods) {
89
+ await paymentStore.setUserDefaultPaymentMethod(setupIntent.payment_method)
90
+ }
91
+ }
92
+ }
93
+ catch (err) {
94
+ console.error('Error processing payment:', err)
95
+ // Handle any unexpected errors
96
+ }
97
+ }
98
+
99
+ async function handlePayment(elements: any) {
100
+ try {
101
+ await confirmPayment(elements)
102
+ }
103
+ catch (err) {
104
+ console.error('Error processing payment:', err)
105
+ // Handle any unexpected errors
106
+ }
107
+ }
108
+
109
+ function isEmpty(object: any) {
110
+ return !object // Checks for null or undefined
111
+ || (typeof object === 'object'
112
+ && Object.keys(object).length === 0)
113
+ }
114
+
115
+ const updatingPlanState = computed(() => {
116
+ return !isEmpty(paymentStore.getCurrentPlan) && paymentStore.getPlanState
117
+ })
118
+
119
+ const showCurrentPlan = computed(() => {
120
+ return !isEmpty(paymentStore.getCurrentPlan) && !paymentStore.getPlanState
121
+ })
122
+
123
+ const showPlans = computed(() => {
124
+ return isEmpty(paymentStore.getCurrentPlan)
125
+ })
126
+
127
+ function editPlan() {
128
+ paymentStore.openPlans()
129
+ }
130
+
131
+ function cancelEditPlan() {
132
+ paymentStore.closePlans()
133
+ }
134
+
135
+ return {
136
+ loadCardForm,
137
+ loadPaymentForm,
138
+ handleAddPaymentMethod,
139
+ handlePayment,
140
+ isEmpty,
141
+ convertUnixTimestampToDate,
142
+ formatTimestampDate,
143
+ editPlan,
144
+ updatingPlanState,
145
+ showCurrentPlan,
146
+ cancelEditPlan,
147
+ showPlans,
148
+ }
149
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Markdown blog admin client.
3
+ *
4
+ * Talks to `/api/dashboard/blog`, which reads and writes `content/blog/*.md` —
5
+ * the same files BunPress renders at `/blog`. Saving here changes a file in the
6
+ * repo, so the dashboard is an editor for git-tracked content, not a database UI.
7
+ *
8
+ * Deliberately dependency-free: no `@stacksjs/stx`, no `@stacksjs/browser`, no
9
+ * signals. The stx client bundler marks those packages external and strips the
10
+ * import when it inlines this module into a page's setup function, which leaves
11
+ * the identifiers undefined at runtime. Reactive state therefore lives in the
12
+ * PAGE's `<script client>` block, where the runtime injects `state`/`derived`,
13
+ * and this module stays a plain async transport that returns data or throws.
14
+ *
15
+ * Server side: storage/framework/core/actions/src/blog-admin.ts
16
+ */
17
+
18
+ export interface BlogPost {
19
+ slug: string
20
+ title: string
21
+ description: string
22
+ date: string
23
+ author: string
24
+ authorBio: string
25
+ poster: string
26
+ featured: boolean
27
+ draft: boolean
28
+ body: string
29
+ wordCount: number
30
+ readingTime: number
31
+ updatedAt: string
32
+ }
33
+
34
+ export type BlogPostDraft = Omit<BlogPost, 'wordCount' | 'readingTime' | 'updatedAt'>
35
+
36
+ const api = '/api/dashboard/blog'
37
+
38
+ /**
39
+ * Headers for an unsafe (write) request.
40
+ *
41
+ * CSRF protection is default-on for POST/PATCH/DELETE and uses a stateless
42
+ * double-submit cookie: the server seeds a non-HttpOnly `X-CSRF-Token` cookie on
43
+ * safe responses, and the client has to echo it back in the matching header. The
44
+ * list request on page load is what seeds the cookie, so by the time an author
45
+ * can save, the token is there. See defaults/app/Middleware/Csrf.ts.
46
+ */
47
+ function writeHeaders(): Record<string, string> {
48
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' }
49
+ const match = document.cookie.match(/(?:^|;\s*)X-CSRF-Token=([^;]*)/)
50
+
51
+ if (match)
52
+ headers['X-CSRF-Token'] = decodeURIComponent(match[1])
53
+
54
+ return headers
55
+ }
56
+
57
+ /**
58
+ * Throws with the API's own message when there is one.
59
+ *
60
+ * The blog endpoints answer a rejected write with `{ message }` (slug taken,
61
+ * bad date, ...). Surfacing that beats a bare status code, since it is the text
62
+ * the author needs in order to fix the post.
63
+ */
64
+ async function unwrap<T>(response: Response): Promise<T> {
65
+ if (response.ok)
66
+ return await response.json() as T
67
+
68
+ let message = `Request failed with status ${response.status}`
69
+
70
+ try {
71
+ const body = await response.json() as { message?: string }
72
+ if (body?.message)
73
+ message = body.message
74
+ }
75
+ catch {
76
+ // Non-JSON error body; the status-code message stands.
77
+ }
78
+
79
+ throw new Error(message)
80
+ }
81
+
82
+ export interface BlogListResponse {
83
+ posts: BlogPost[]
84
+ publishedCount: number
85
+ draftCount: number
86
+ featuredCount: number
87
+ }
88
+
89
+ /** Every post on disk, drafts included. Bodies are omitted from the list. */
90
+ export async function fetchBlogPosts(): Promise<BlogListResponse> {
91
+ const body = await unwrap<BlogListResponse>(await fetch(api))
92
+
93
+ if (!Array.isArray(body?.posts))
94
+ throw new TypeError('The blog API returned an unexpected shape.')
95
+
96
+ return body
97
+ }
98
+
99
+ /** One post including its markdown body, which the list omits. */
100
+ export async function fetchBlogPost(slug: string): Promise<BlogPost> {
101
+ return await unwrap<BlogPost>(await fetch(`${api}/${encodeURIComponent(slug)}`))
102
+ }
103
+
104
+ /** Creates a post, or updates the one at `originalSlug`. */
105
+ export async function saveBlogPost(post: BlogPostDraft, originalSlug = ''): Promise<BlogPost> {
106
+ const isUpdate = !!originalSlug
107
+
108
+ return await unwrap<BlogPost>(await fetch(isUpdate ? `${api}/${encodeURIComponent(originalSlug)}` : api, {
109
+ method: isUpdate ? 'PATCH' : 'POST',
110
+ headers: writeHeaders(),
111
+ body: JSON.stringify(post),
112
+ }))
113
+ }
114
+
115
+ export async function deleteBlogPost(slug: string): Promise<void> {
116
+ await unwrap<{ message: string }>(await fetch(`${api}/${encodeURIComponent(slug)}`, {
117
+ method: 'DELETE',
118
+ headers: writeHeaders(),
119
+ }))
120
+ }
@@ -0,0 +1,107 @@
1
+ import type { Categorizables } from '../../types/defaults'
2
+ import { useFetch, useStorage } from '@stacksjs/browser'
3
+
4
+ // Create a persistent categories array using STX useStorage
5
+ const categorizables = useStorage<Categorizables[]>('categorizables', [])
6
+
7
+ const baseURL = process.env.VITE_API_URL || `http://localhost:${process.env.PORT_API || '3008'}`
8
+
9
+ // Basic fetch function to get all categories
10
+ async function fetchCategorizables(): Promise<Categorizables[]> {
11
+ const { error, data } = await useFetch(`${baseURL}/cms/categories`).get().json()
12
+
13
+ const categorizableJson = data.value as Categorizables[]
14
+ if (error.value) {
15
+ console.error('Error fetching categorizables:', error.value)
16
+ return []
17
+ }
18
+
19
+ // Ensure data is an array before assigning
20
+ if (Array.isArray(categorizableJson)) {
21
+ categorizables.value = categorizableJson
22
+ return categorizableJson
23
+ }
24
+ else {
25
+ console.error('Expected array of categorizables but received:', typeof categorizableJson)
26
+ return []
27
+ }
28
+ }
29
+
30
+ async function createCategorizable(categorizable: Partial<Categorizables>) {
31
+ const { error, data } = await useFetch(`${baseURL}/cms/categories`)
32
+ .post(JSON.stringify({
33
+ ...categorizable,
34
+ name: categorizable.name,
35
+ description: categorizable.description,
36
+ is_active: categorizable.is_active,
37
+ categorizable_type: categorizable.categorizable_type,
38
+ }))
39
+ .json()
40
+
41
+ if (error.value) {
42
+ console.error('Error creating categorizable:', error.value)
43
+ return null
44
+ }
45
+
46
+ const newCategorizable = data.value as Categorizables
47
+ if (newCategorizable) {
48
+ categorizables.value.push(newCategorizable)
49
+ return newCategorizable
50
+ }
51
+ return null
52
+ }
53
+
54
+ async function updateCategorizable(id: number, categorizable: Partial<Categorizables>) {
55
+ const { error, data } = await useFetch(`${baseURL}/cms/categories/${id}`)
56
+ .patch(JSON.stringify({
57
+ ...categorizable,
58
+ name: categorizable.name,
59
+ description: categorizable.description,
60
+ is_active: categorizable.is_active,
61
+ }))
62
+ .json()
63
+
64
+ if (error.value) {
65
+ console.error('Error updating categorizable:', error.value)
66
+ return null
67
+ }
68
+
69
+ const updatedCategorizable = data.value as Categorizables
70
+ if (updatedCategorizable) {
71
+ const index = categorizables.value.findIndex(c => c.id === id)
72
+ if (index !== -1) {
73
+ categorizables.value[index] = updatedCategorizable
74
+ }
75
+ return updatedCategorizable
76
+ }
77
+ return null
78
+ }
79
+
80
+ async function deleteCategorizable(id: number) {
81
+ const { error } = await useFetch(`${baseURL}/cms/categories/${id}`)
82
+ .delete()
83
+ .json()
84
+
85
+ if (error.value) {
86
+ console.error('Error deleting categorizable:', error.value)
87
+ return false
88
+ }
89
+
90
+ const index = categorizables.value.findIndex(c => c.id === id)
91
+ if (index !== -1) {
92
+ categorizables.value.splice(index, 1)
93
+ }
94
+
95
+ return true
96
+ }
97
+
98
+ // Export the composable
99
+ export function useCategorizables() {
100
+ return {
101
+ categorizables,
102
+ fetchCategorizables,
103
+ createCategorizable,
104
+ updateCategorizable,
105
+ deleteCategorizable,
106
+ }
107
+ }
@@ -0,0 +1,108 @@
1
+ import type { Pages } from '../../types/defaults'
2
+ import { useFetch, useStorage } from '@stacksjs/browser'
3
+
4
+ // Create a persistent pages array using STX useStorage
5
+ const pages = useStorage<Pages[]>('pages', [])
6
+
7
+ const baseURL = process.env.VITE_API_URL || `http://localhost:${process.env.PORT_API || '3008'}`
8
+
9
+ // Basic fetch function to get all pages
10
+ async function fetchPages() {
11
+ const { error, data } = await useFetch(`${baseURL}/cms/pages`).get().json()
12
+
13
+ const pagesJson = data.value as Pages[]
14
+ if (error.value) {
15
+ console.error('Error fetching pages:', error.value)
16
+ return []
17
+ }
18
+
19
+ // Ensure data is an array before assigning
20
+ if (Array.isArray(data.value)) {
21
+ pages.value = pagesJson
22
+ return data.value
23
+ }
24
+ else {
25
+ console.error('Expected array of pages but received:', typeof data.value)
26
+ return []
27
+ }
28
+ }
29
+
30
+ async function createPage(page: Partial<Pages>) {
31
+ const { error, data } = await useFetch(`${baseURL}/cms/pages`)
32
+ .post(JSON.stringify({
33
+ ...page,
34
+ title: page.title,
35
+ template: page.template,
36
+ views: page.views || 0,
37
+ conversions: page.conversions || 0,
38
+ }))
39
+ .json()
40
+
41
+ if (error.value) {
42
+ console.error('Error creating page:', error.value)
43
+ return null
44
+ }
45
+
46
+ const newPage = data.value as Pages
47
+ if (newPage) {
48
+ pages.value.push(newPage)
49
+ return newPage
50
+ }
51
+ return null
52
+ }
53
+
54
+ async function updatePage(id: number, page: Partial<Pages>) {
55
+ const { error, data } = await useFetch(`${baseURL}/cms/pages/${id}`)
56
+ .patch(JSON.stringify({
57
+ ...page,
58
+ title: page.title,
59
+ template: page.template,
60
+ views: page.views,
61
+ conversions: page.conversions,
62
+ }))
63
+ .json()
64
+
65
+ if (error.value) {
66
+ console.error('Error updating page:', error.value)
67
+ return null
68
+ }
69
+
70
+ const updatedPage = data.value as Pages
71
+ if (updatedPage) {
72
+ const index = pages.value.findIndex(p => p.id === id)
73
+ if (index !== -1) {
74
+ pages.value[index] = updatedPage
75
+ }
76
+ return updatedPage
77
+ }
78
+ return null
79
+ }
80
+
81
+ async function deletePage(id: number) {
82
+ const { error } = await useFetch(`${baseURL}/cms/pages/${id}`)
83
+ .delete()
84
+ .json()
85
+
86
+ if (error.value) {
87
+ console.error('Error deleting page:', error.value)
88
+ return false
89
+ }
90
+
91
+ const index = pages.value.findIndex(p => p.id === id)
92
+ if (index !== -1) {
93
+ pages.value.splice(index, 1)
94
+ }
95
+
96
+ return true
97
+ }
98
+
99
+ // Export the composable
100
+ export function usePages() {
101
+ return {
102
+ pages,
103
+ fetchPages,
104
+ createPage,
105
+ updatePage,
106
+ deletePage,
107
+ }
108
+ }