@stacksjs/defaults 0.70.206 → 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,485 @@
1
+ @extends('layouts/default')
2
+
3
+ @section('content')
4
+ @title('Dashboard - Commerce Coupons')
5
+ <div class="p-6 space-y-6">
6
+
7
+ <PageHeader title="Coupons" description="Manage discount coupons for your store">
8
+ <template #actions>
9
+ <Button variant="primary" @click="openAddModal()">
10
+ <div class="h-4 w-4 i-hugeicons-plus-sign"></div>
11
+ Add Coupon
12
+ </Button>
13
+ </template>
14
+ </PageHeader>
15
+
16
+ <!-- Time range selector -->
17
+ <div class="flex items-center justify-between mt-4">
18
+ <p class="text-gray-500 text-sm dark:text-neutral-400">
19
+ Overview of your coupon performance
20
+ </p>
21
+ <div class="relative">
22
+ <select x-model="timeRange" class="block pl-3 pr-10 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600">
23
+ <template :for="range in timeRanges">
24
+ <option :value="range">{{ range }}</option>
25
+ </template>
26
+ </select>
27
+ </div>
28
+ </div>
29
+
30
+ <!-- Stats -->
31
+ <dl class="grid gap-5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 mt-5">
32
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
33
+ <dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Active Coupons</dt>
34
+ <dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">{{ couponStats().activeCoupons }}</dd>
35
+ <dd class="flex items-center mt-2 text-green-600 text-sm dark:text-green-400">
36
+ <div class="mr-1 h-4 w-4 i-hugeicons-analytics-up"></div>
37
+ <span>1 more than last month</span>
38
+ </dd>
39
+ </div>
40
+
41
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
42
+ <dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Total Redemptions</dt>
43
+ <dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">{{ couponStats().totalRedemptions.toLocaleString() }}</dd>
44
+ <dd class="flex items-center mt-2 text-green-600 text-sm dark:text-green-400">
45
+ <div class="mr-1 h-4 w-4 i-hugeicons-analytics-up"></div>
46
+ <span>12.5% increase</span>
47
+ </dd>
48
+ </div>
49
+
50
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
51
+ <dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Total Discount Amount</dt>
52
+ <dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">${{ couponStats().totalDiscountAmount.toLocaleString() }}</dd>
53
+ <dd class="flex items-center mt-2 text-green-600 text-sm dark:text-green-400">
54
+ <div class="mr-1 h-4 w-4 i-hugeicons-analytics-up"></div>
55
+ <span>8.2% increase</span>
56
+ </dd>
57
+ </div>
58
+
59
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
60
+ <dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Avg. Discount per Order</dt>
61
+ <dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">${{ couponStats().avgDiscountPerOrder }}</dd>
62
+ <dd class="flex items-center mt-2 text-red-600 text-sm dark:text-red-400">
63
+ <div class="mr-1 h-4 w-4 i-hugeicons-analytics-down"></div>
64
+ <span>3.2% decrease</span>
65
+ </dd>
66
+ </div>
67
+ </dl>
68
+
69
+ <!-- Charts -->
70
+ <div class="grid gap-5 grid-cols-1 lg:grid-cols-2 mt-8">
71
+ <div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
72
+ <div class="p-6">
73
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Coupon Redemptions</h3>
74
+ <div class="mt-2 h-80">
75
+ <canvas id="chart_1"></canvas>
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ <div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
81
+ <div class="p-6">
82
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Discount Amount</h3>
83
+ <div class="mt-2 h-80">
84
+ <canvas id="chart_2"></canvas>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ </div>
89
+
90
+ <!-- Filters -->
91
+ <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between mt-8">
92
+ <SearchFilter
93
+ placeholder="Search coupons..."
94
+ @search="handleSearch"
95
+ class="w-full md:w-96"
96
+ />
97
+ <div class="flex flex-col gap-4 sm:flex-row">
98
+ <select
99
+ x-model="statusFilter"
100
+ class="block pl-3 pr-10 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600 focus:ring-inset"
101
+ >
102
+ <template :for="status in statuses">
103
+ <option :value="status">{{ status }}</option>
104
+ </template>
105
+ </select>
106
+
107
+ <select
108
+ x-model.number="itemsPerPage"
109
+ class="block pl-3 pr-10 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600 focus:ring-inset"
110
+ >
111
+ <option value="5">5 per page</option>
112
+ <option value="10">10 per page</option>
113
+ <option value="25">25 per page</option>
114
+ <option value="50">50 per page</option>
115
+ </select>
116
+ </div>
117
+ </div>
118
+
119
+ <!-- Coupons table -->
120
+ <div class="flow-root mt-6">
121
+ <div class="sm:flex sm:items-center sm:justify-between mb-4">
122
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">All Coupons</h3>
123
+ <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">
124
+ A list of all the coupons in your store including their code, value, and status.
125
+ </p>
126
+ </div>
127
+ <div class="overflow-x-auto -mx-4 -my-2 sm:-mx-6 lg:-mx-8">
128
+ <div class="inline-block py-2 sm:px-6 lg:px-8 min-w-full align-middle">
129
+ <div class="overflow-hidden ring-1 ring-black ring-opacity-5 sm:rounded-lg shadow">
130
+ <CouponsTable
131
+ :coupons="paginatedCoupons"
132
+ :searchQuery="searchQuery"
133
+ :statusFilter="statusFilter"
134
+ :sortBy="sortBy"
135
+ :sortOrder="sortOrder"
136
+ :currentPage="currentPage"
137
+ :itemsPerPage="itemsPerPage"
138
+ :statuses="statuses"
139
+ @toggle-sort="toggleSort"
140
+ @change-page="handlePageChange"
141
+ @previous-page="handlePrevPage"
142
+ @next-page="handleNextPage"
143
+ @view-coupon="viewCoupon"
144
+ @edit-coupon="editCoupon"
145
+ @delete-coupon="removeCoupon"
146
+ />
147
+ </div>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+ <!-- Pagination -->
153
+ <div class="px-4 py-3 bg-gray-50 dark:bg-neutral-900 border-gray-200 border-t dark:border-neutral-700">
154
+ <Pagination
155
+ :currentPage="currentPage"
156
+ :totalItems="totalItems"
157
+ :itemsPerPage="itemsPerPage"
158
+ @prev="handlePrevPage"
159
+ @next="handleNextPage"
160
+ @page="handlePageChange"
161
+ />
162
+ </div>
163
+
164
+ <!-- Coupons Form Modal -->
165
+ <CouponsForm
166
+ :show="showFormModal"
167
+ :mode="formMode"
168
+ :coupon="couponToEdit"
169
+ @close="closeFormModal"
170
+ @submit="handleFormSubmit"
171
+ />
172
+
173
+ <!-- Delete Coupon Confirmation Modal -->
174
+ <div :show="showDeleteModal" class="fixed inset-0 overflow-y-auto z-10">
175
+ <div class="flex items-end justify-center sm:items-center p-4 sm:p-0 min-h-full text-center">
176
+ <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="closeDeleteModal()"></div>
177
+
178
+ <div class="overflow-hidden relative pb-4 pt-5 px-4 sm:my-8 sm:p-6 sm:max-w-lg sm:w-full text-left bg-white dark:bg-neutral-800 rounded-lg shadow-xl transition-all transform">
179
+ <div>
180
+ <div class="flex items-center justify-center mx-auto h-12 w-12 bg-red-100 dark:bg-red-900 rounded-full">
181
+ <div class="h-6 w-6 text-red-600 dark:text-red-400 i-hugeicons-alert-triangle"></div>
182
+ </div>
183
+ <div class="mt-3 sm:mt-5 text-center">
184
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Delete Coupon</h3>
185
+ <div class="mt-2">
186
+ <p class="text-gray-500 text-sm dark:text-neutral-400">
187
+ Are you sure you want to delete <strong>{{ couponToDelete()?.code }}</strong>? This action cannot be undone.
188
+ </p>
189
+ </div>
190
+ </div>
191
+ </div>
192
+ <div class="sm:grid sm:gap-3 sm:grid-cols-2 sm:grid-flow-row-dense mt-5 sm:mt-6">
193
+ <button
194
+ type="button"
195
+ @click="confirmDelete()"
196
+ class="inline-flex justify-center sm:col-start-2 px-3 py-2 w-full font-semibold text-sm text-white bg-red-600 hover:bg-red-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600 shadow-sm"
197
+ >
198
+ Delete Coupon
199
+ </button>
200
+ <button
201
+ type="button"
202
+ @click="closeDeleteModal()"
203
+ class="inline-flex justify-center sm:col-start-1 mt-3 px-3 py-2 sm:mt-0 w-full font-semibold text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-600 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 shadow-sm"
204
+ >
205
+ Cancel
206
+ </button>
207
+ </div>
208
+ </div>
209
+ </div>
210
+ </div>
211
+ </div>
212
+
213
+ <script client>
214
+ import { useCoupons } from '../../../../functions/commerce/coupons'
215
+ import type { Coupons, NewCoupon } from '../../../../types/defaults'
216
+ // Filter options — moved out of the former `<script server>` block
217
+ // (stacksjs/stacks#1838).
218
+ const statuses = ['all', 'Active', 'Expired', 'Scheduled'] as const
219
+ const timeRanges = ['Today', 'Last 7 days', 'Last 30 days', 'Last 90 days', 'Last year', 'All time'] as const
220
+
221
+ const { coupons, createCoupon, fetchCoupons, deleteCoupon, updateCoupon } = useCoupons()
222
+
223
+ const timeRange = state<string>('Last 30 days')
224
+ const searchQuery = state('')
225
+ const sortBy = state<'code' | 'discount_value' | 'usage_count' | 'end_date'>('code')
226
+ const sortOrder = state<'asc' | 'desc'>('asc')
227
+ const statusFilter = state<string>('all')
228
+ const currentPage = state(1)
229
+ const itemsPerPage = state(10)
230
+
231
+ const showFormModal = state(false)
232
+ const formMode = state<'add' | 'edit'>('add')
233
+ const couponToEdit = state<Coupons | null>(null)
234
+ const showDeleteModal = state(false)
235
+ const couponToDelete = state<Coupons | null>(null)
236
+
237
+ // Static chart options + monthly sample data — keep them in client
238
+ // scope so derived chart data can pick up signal-driven inputs later
239
+ // (timeRange, etc. when wired through to a real source).
240
+ const chartOptions = {
241
+ responsive: true,
242
+ maintainAspectRatio: false,
243
+ plugins: { legend: { display: false } },
244
+ scales: {
245
+ y: { beginAtZero: true, grid: { display: true, color: 'rgba(0, 0, 0, 0.05)' } },
246
+ x: { grid: { display: false } },
247
+ },
248
+ }
249
+
250
+ function monthlyChartData(): { couponUsageData: unknown, discountAmountData: unknown } {
251
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
252
+ const redemptionsData = [120, 145, 132, 160, 185, 170, 210, 230, 245, 220, 260, 280]
253
+ const discountData = [2500, 3200, 2800, 3600, 4100, 3800, 4500, 5200, 5800, 5400, 6200, 6800]
254
+
255
+ return {
256
+ couponUsageData: {
257
+ labels: months,
258
+ datasets: [{
259
+ label: 'Coupon Redemptions',
260
+ backgroundColor: 'rgba(59, 130, 246, 0.2)',
261
+ borderColor: 'rgba(59, 130, 246, 1)',
262
+ borderWidth: 2,
263
+ pointBackgroundColor: 'rgba(59, 130, 246, 1)',
264
+ pointBorderColor: '#fff',
265
+ pointHoverBackgroundColor: '#fff',
266
+ pointHoverBorderColor: 'rgba(59, 130, 246, 1)',
267
+ fill: true,
268
+ tension: 0.4,
269
+ data: redemptionsData,
270
+ }],
271
+ },
272
+ discountAmountData: {
273
+ labels: months,
274
+ datasets: [{
275
+ label: 'Discount Amount',
276
+ backgroundColor: 'rgba(16, 185, 129, 0.8)',
277
+ borderColor: 'rgba(16, 185, 129, 1)',
278
+ borderWidth: 1,
279
+ borderRadius: 4,
280
+ data: discountData,
281
+ }],
282
+ },
283
+ }
284
+ }
285
+
286
+ function couponStats(): { activeCoupons: number, totalRedemptions: number, totalDiscountAmount: string, avgDiscountPerOrder: string } {
287
+ const list = coupons.value as Coupons[]
288
+ const activeCoupons = list.filter(c => c.status === 'Active').length
289
+ const totalRedemptions = list.reduce((sum, c) => sum + (c.usage_count || 0), 0)
290
+
291
+ const totalDiscountAmount = list.reduce((sum, c) => {
292
+ const avgOrderValue = 100
293
+ const discountPerUse = c.discount_type === 'Percentage'
294
+ ? avgOrderValue * (c.discount_value / 100)
295
+ : c.discount_value
296
+ return sum + (discountPerUse * (c.usage_count || 0))
297
+ }, 0)
298
+
299
+ const avgDiscountPerOrder = totalRedemptions > 0
300
+ ? (totalDiscountAmount / totalRedemptions).toFixed(2)
301
+ : '0.00'
302
+
303
+ return {
304
+ activeCoupons,
305
+ totalRedemptions,
306
+ totalDiscountAmount: totalDiscountAmount.toFixed(0),
307
+ avgDiscountPerOrder,
308
+ }
309
+ }
310
+
311
+ const filteredCoupons = derived(() => {
312
+ const q = searchQuery().toLowerCase()
313
+ const filter = statusFilter()
314
+ const by = sortBy()
315
+ const dir = sortOrder()
316
+
317
+ return (coupons.value as Coupons[])
318
+ .filter((c) => {
319
+ const matchesSearch = c.code?.toLowerCase().includes(q) || false
320
+ const matchesStatus = filter === 'all' || c.status === filter
321
+ return matchesSearch && matchesStatus
322
+ })
323
+ .sort((a, b) => {
324
+ let cmp = 0
325
+ if (by === 'code')
326
+ cmp = a.code.localeCompare(b.code)
327
+ else if (by === 'discount_value')
328
+ cmp = a.discount_value - b.discount_value
329
+ else if (by === 'usage_count')
330
+ cmp = (a.usage_count || 0) - (b.usage_count || 0)
331
+ else if (by === 'end_date')
332
+ cmp = new Date(a.end_date).getTime() - new Date(b.end_date).getTime()
333
+ return dir === 'asc' ? cmp : -cmp
334
+ })
335
+ })
336
+
337
+ const paginatedCoupons = derived(() => {
338
+ const start = (currentPage() - 1) * itemsPerPage()
339
+ return filteredCoupons().slice(start, start + itemsPerPage())
340
+ })
341
+
342
+ const totalItems = derived(() => filteredCoupons().length)
343
+
344
+ function handleSearch(query: string): void {
345
+ searchQuery.set(query)
346
+ currentPage.set(1)
347
+ }
348
+
349
+ function handlePrevPage(): void {
350
+ if (currentPage() > 1)
351
+ currentPage.update(p => p - 1)
352
+ }
353
+
354
+ function handleNextPage(): void {
355
+ const totalPages = Math.ceil(totalItems() / itemsPerPage())
356
+ if (currentPage() < totalPages)
357
+ currentPage.update(p => p + 1)
358
+ }
359
+
360
+ function handlePageChange(page: number): void {
361
+ currentPage.set(page)
362
+ }
363
+
364
+ function toggleSort(column: 'code' | 'discount_value' | 'usage_count' | 'end_date'): void {
365
+ if (sortBy() === column) {
366
+ sortOrder.update(d => d === 'asc' ? 'desc' : 'asc')
367
+ }
368
+ else {
369
+ sortBy.set(column)
370
+ sortOrder.set('asc')
371
+ }
372
+ }
373
+
374
+ function openAddModal(): void {
375
+ formMode.set('add')
376
+ couponToEdit.set(null)
377
+ showFormModal.set(true)
378
+ }
379
+
380
+ function closeFormModal(): void {
381
+ showFormModal.set(false)
382
+ couponToEdit.set(null)
383
+ }
384
+
385
+ function editCoupon(coupon: Coupons): void {
386
+ formMode.set('edit')
387
+ couponToEdit.set({ ...coupon })
388
+ showFormModal.set(true)
389
+ }
390
+
391
+ function viewCoupon(coupon: Coupons): void {
392
+ navigate(`/dashboard/commerce/coupons/${coupon.id}`)
393
+ }
394
+
395
+ function removeCoupon(coupon: Coupons): void {
396
+ couponToDelete.set(coupon)
397
+ showDeleteModal.set(true)
398
+ }
399
+
400
+ function closeDeleteModal(): void {
401
+ showDeleteModal.set(false)
402
+ couponToDelete.set(null)
403
+ }
404
+
405
+ async function handleFormSubmit(couponData: NewCoupon): Promise<void> {
406
+ if (formMode() === 'add')
407
+ await addCoupon(couponData)
408
+ else
409
+ await doUpdateCoupon(couponData)
410
+ }
411
+
412
+ async function addCoupon(couponData: NewCoupon): Promise<void> {
413
+ const list = coupons.value as Coupons[]
414
+ const id = list.length > 0 ? Math.max(...list.map(c => c.id || 0)) + 1 : 1
415
+ const optimistic = { id, ...couponData, usage_count: 0 } as unknown as Coupons
416
+ coupons.value = [...list, optimistic]
417
+
418
+ try {
419
+ await createCoupon(couponData as unknown as Coupons)
420
+ closeFormModal()
421
+ }
422
+ catch (error) {
423
+ coupons.value = (coupons.value as Coupons[]).filter(c => c.id !== id)
424
+ // eslint-disable-next-line no-console
425
+ console.error('Failed to create coupon:', error)
426
+ }
427
+ }
428
+
429
+ async function doUpdateCoupon(couponData: NewCoupon): Promise<void> {
430
+ const target = couponToEdit()
431
+ if (!target)
432
+ return
433
+ const couponId = target.id
434
+ const list = coupons.value as Coupons[]
435
+ coupons.value = list.map(c => c.id === couponId ? { ...c, ...couponData } : c)
436
+
437
+ try {
438
+ await updateCoupon(couponId as number, couponData)
439
+ closeFormModal()
440
+ }
441
+ catch (error) {
442
+ // eslint-disable-next-line no-console
443
+ console.error('Failed to update coupon:', error)
444
+ }
445
+ }
446
+
447
+ async function confirmDelete(): Promise<void> {
448
+ const target = couponToDelete()
449
+ if (!target)
450
+ return
451
+ const couponId = target.id
452
+ const list = coupons.value as Coupons[]
453
+ coupons.value = list.filter(c => c.id !== couponId)
454
+
455
+ try {
456
+ await deleteCoupon(couponId as number)
457
+ closeDeleteModal()
458
+ }
459
+ catch (error) {
460
+ coupons.value = [...coupons.value, target]
461
+ // eslint-disable-next-line no-console
462
+ console.error('Failed to delete coupon:', error)
463
+ }
464
+ }
465
+
466
+ const { Chart, registerables } = await import('@stacksjs/charts')
467
+ Chart.register(...registerables)
468
+
469
+ const chartData = monthlyChartData()
470
+ const { destroyAll } = useCharts([
471
+ { Chart, id: 'chart_1', type: 'line', data: chartData.couponUsageData, options: chartOptions },
472
+ { Chart, id: 'chart_2', type: 'bar', data: chartData.discountAmountData, options: chartOptions },
473
+ ])
474
+
475
+ onMount(async () => {
476
+ await fetchCoupons()
477
+ })
478
+
479
+ // Tear charts down on SPA navigation so chart.js doesn't leak canvases.
480
+ onDestroy(() => {
481
+ destroyAll()
482
+ })
483
+ </script>
484
+
485
+ @endsection