@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,561 @@
1
+ @extends('layouts/default')
2
+
3
+ @section('content')
4
+ @title('Dashboard - Commerce Gift Cards')
5
+ <PageLayout>
6
+ <div class="sm:flex sm:items-center sm:justify-between">
7
+ <PageHeader title="Gift Cards" description="Manage gift cards for your store" />
8
+ <div class="mt-4 sm:mt-0">
9
+ <button
10
+ type="button"
11
+ @click="openAddModal()"
12
+ class="inline-flex items-center px-3 py-2 font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 shadow-sm"
13
+ >
14
+ <div class="mr-1 h-5 w-5 i-hugeicons-plus-sign"></div>
15
+ Create gift card
16
+ </button>
17
+ </div>
18
+ </div>
19
+
20
+ <!-- Time range selector -->
21
+ <div class="flex items-center justify-between mt-4">
22
+ <p class="text-gray-500 text-sm dark:text-neutral-400">
23
+ Overview of your gift card performance
24
+ </p>
25
+ <div class="relative">
26
+ <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">
27
+ <template :for="range in timeRanges">
28
+ <option :value="range">{{ range }}</option>
29
+ </template>
30
+ </select>
31
+ </div>
32
+ </div>
33
+
34
+ <!-- Stats -->
35
+ <dl class="grid gap-5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 mt-5">
36
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
37
+ <dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Active Gift Cards</dt>
38
+ <dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">{{ giftCardStats().activeGiftCards }}</dd>
39
+ <dd class="flex items-center mt-2 text-green-600 text-sm dark:text-green-400">
40
+ <div class="mr-1 h-4 w-4 i-hugeicons-analytics-up"></div>
41
+ <span>{{ giftCardStats().totalGiftCards > 0 ? Math.round(giftCardStats().activeGiftCards / giftCardStats().totalGiftCards * 100) : 0 }}% of total</span>
42
+ </dd>
43
+ </div>
44
+
45
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
46
+ <dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Total Value</dt>
47
+ <dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">${{ Number.parseFloat(giftCardStats().totalInitialValue).toLocaleString() }}</dd>
48
+ <dd class="flex items-center mt-2 text-green-600 text-sm dark:text-green-400">
49
+ <div class="mr-1 h-4 w-4 i-hugeicons-analytics-up"></div>
50
+ <span>10.5% increase</span>
51
+ </dd>
52
+ </div>
53
+
54
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
55
+ <dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Current Balance</dt>
56
+ <dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">${{ Number.parseFloat(giftCardStats().totalCurrentBalance).toLocaleString() }}</dd>
57
+ <dd class="flex items-center mt-2 text-blue-600 text-sm dark:text-blue-400">
58
+ <div class="mr-1 h-4 w-4 i-hugeicons-analytics-up"></div>
59
+ <span>${{ Number.parseFloat(giftCardStats().totalRedeemed).toLocaleString() }} redeemed</span>
60
+ </dd>
61
+ </div>
62
+
63
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
64
+ <dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Redemption Rate</dt>
65
+ <dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">{{ giftCardStats().redemptionRate }}%</dd>
66
+ <dd class="flex items-center mt-2 text-green-600 text-sm dark:text-green-400">
67
+ <div class="mr-1 h-4 w-4 i-hugeicons-analytics-up"></div>
68
+ <span>5.2% increase</span>
69
+ </dd>
70
+ </div>
71
+ </dl>
72
+
73
+ <!-- Charts -->
74
+ <div class="grid gap-5 grid-cols-1 lg:grid-cols-2 mt-8">
75
+ <div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
76
+ <div class="p-6">
77
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Gift Card Sales</h3>
78
+ <div class="mt-2 h-80">
79
+ <canvas id="chart_1"></canvas>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ <div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
85
+ <div class="p-6">
86
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Gift Card Revenue</h3>
87
+ <div class="mt-2 h-80">
88
+ <canvas id="chart_2"></canvas>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </div>
93
+
94
+ <!-- Filters -->
95
+ <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between mt-8">
96
+ <div class="relative max-w-sm">
97
+ <div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
98
+ <div class="h-5 w-5 text-gray-400 i-hugeicons-search-01"></div>
99
+ </div>
100
+ <input
101
+ x-model="searchQuery"
102
+ type="text"
103
+ class="block pl-10 pr-3 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:placeholder:text-neutral-500 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"
104
+ placeholder="Search gift cards..."
105
+ />
106
+ </div>
107
+
108
+ <div class="flex flex-col gap-4 sm:flex-row">
109
+ <select
110
+ x-model="statusFilter"
111
+ 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"
112
+ >
113
+ <template :for="status in statuses">
114
+ <option :value="status">{{ status === 'all' ? 'All Statuses' : status }}</option>
115
+ </template>
116
+ </select>
117
+ </div>
118
+ </div>
119
+
120
+ <!-- Gift Cards table -->
121
+ <div class="flow-root mt-6">
122
+ <div class="sm:flex sm:items-center sm:justify-between mb-4">
123
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">All Gift Cards</h3>
124
+ <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">
125
+ A list of all the gift cards in your store including their value, balance, and status.
126
+ </p>
127
+ </div>
128
+ <div class="overflow-x-auto -mx-4 -my-2 sm:-mx-6 lg:-mx-8">
129
+ <div class="inline-block py-2 sm:px-6 lg:px-8 min-w-full align-middle">
130
+ <div class="overflow-hidden ring-1 ring-black ring-opacity-5 sm:rounded-lg shadow">
131
+ <table class="min-w-full divide-gray-300 divide-y dark:divide-neutral-700">
132
+ <thead class="bg-gray-50 dark:bg-neutral-700">
133
+ <tr>
134
+ <th scope="col" class="pl-4 pr-3 py-3.5 sm:pl-6 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">
135
+ Code
136
+ </th>
137
+ <th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">Recipient</th>
138
+ <th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">
139
+ <button @click="toggleSort('initial_balance')" class="inline-flex items-center group">
140
+ Initial Value
141
+ <span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
142
+ <div class="h-4 w-4 {{ sortBy() === 'initial_balance' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
143
+ </span>
144
+ </button>
145
+ </th>
146
+ <th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">
147
+ <button @click="toggleSort('current_balance')" class="inline-flex items-center group">
148
+ Balance
149
+ <span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
150
+ <div class="h-4 w-4 {{ sortBy() === 'current_balance' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
151
+ </span>
152
+ </button>
153
+ </th>
154
+ <th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">
155
+ <button @click="toggleSort('purchaseDate')" class="inline-flex items-center group">
156
+ Purchase Date
157
+ <span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
158
+ <div class="h-4 w-4 {{ sortBy() === 'purchaseDate' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
159
+ </span>
160
+ </button>
161
+ </th>
162
+ <th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">
163
+ <button @click="toggleSort('expiry_date')" class="inline-flex items-center group">
164
+ Expiry Date
165
+ <span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
166
+ <div class="h-4 w-4 {{ sortBy() === 'expiry_date' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
167
+ </span>
168
+ </button>
169
+ </th>
170
+ <th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">Status</th>
171
+ <th scope="col" class="relative pl-3 pr-4 py-3.5 sm:pr-6">
172
+ <span class="sr-only">Actions</span>
173
+ </th>
174
+ </tr>
175
+ </thead>
176
+ <tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
177
+ <template :for="card in filteredGiftCards">
178
+ <tr>
179
+ <td class="pl-4 pr-3 py-4 sm:pl-6 font-medium text-gray-900 text-sm whitespace-nowrap dark:text-white">
180
+ {{ card.code }}
181
+ </td>
182
+ <td class="px-3 py-4 text-gray-500 text-sm dark:text-neutral-300">
183
+ <div>{{ card.recipient_name }}</div>
184
+ <div class="text-gray-400 text-xs">{{ card.recipient_email }}</div>
185
+ </td>
186
+ <td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">
187
+ ${{ card.initial_balance.toFixed(2) }}
188
+ </td>
189
+ <td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">
190
+ ${{ card.current_balance.toFixed(2) }}
191
+ </td>
192
+ <td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">
193
+ {{ card.created_at }}
194
+ </td>
195
+ <td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">
196
+ {{ card.expiry_date }}
197
+ </td>
198
+ <td class="px-3 py-4 text-sm whitespace-nowrap">
199
+ <span class="inline-flex items-center px-2 py-1 font-medium text-xs rounded-md {{ getStatusClass(card.status) }}">
200
+ {{ card.status }}
201
+ </span>
202
+ </td>
203
+ <td class="relative pl-3 pr-4 py-4 sm:pr-6 font-medium text-right text-sm whitespace-nowrap">
204
+ <div class="flex items-center justify-end space-x-2">
205
+ <button type="button" class="text-gray-400 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300 duration-150 transition-colors">
206
+ <div class="h-5 w-5 i-hugeicons-view"></div>
207
+ </button>
208
+ <button type="button" class="text-gray-400 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300 duration-150 transition-colors">
209
+ <div class="h-5 w-5 i-hugeicons-edit-01"></div>
210
+ </button>
211
+ </div>
212
+ </td>
213
+ </tr>
214
+ </template>
215
+ @if(filteredGiftCards().length === 0)
216
+ <tr>
217
+ <td colspan="8" class="px-6 py-4 text-center text-gray-500 text-sm dark:text-neutral-400">
218
+ No gift cards found matching your criteria
219
+ </td>
220
+ </tr>
221
+ @endif
222
+ </tbody>
223
+ </table>
224
+ </div>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ </div>
230
+
231
+ <!-- Add Gift Card Modal — kept mounted; toggle via :show. -->
232
+ <div :show="showAddModal" class="fixed inset-0 overflow-y-auto z-10">
233
+ <div class="flex items-end justify-center sm:items-center p-4 sm:p-0 min-h-full text-center">
234
+ <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="closeAddModal()"></div>
235
+
236
+ <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">
237
+ <div>
238
+ <div class="mt-3 sm:mt-5 text-center">
239
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Create New Gift Card</h3>
240
+ <div class="mt-4">
241
+ <div class="space-y-4">
242
+ <div>
243
+ <label for="gift-card-code" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Gift Card Code</label>
244
+ <div class="mt-2">
245
+ <input
246
+ type="text"
247
+ id="gift-card-code"
248
+ x-model="newGiftCardCode"
249
+ readonly
250
+ class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 bg-gray-50 dark:bg-neutral-700 dark:bg-neutral-900 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm"
251
+ />
252
+ <p class="mt-1 text-gray-500 text-left text-xs dark:text-neutral-400">Auto-generated code</p>
253
+ </div>
254
+ </div>
255
+
256
+ <div>
257
+ <label for="gift-card-value" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Value</label>
258
+ <div class="mt-2">
259
+ <div class="relative rounded-md shadow-sm">
260
+ <div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
261
+ <span class="text-gray-500 sm:text-sm">$</span>
262
+ </div>
263
+ <input
264
+ type="number"
265
+ id="gift-card-value"
266
+ x-model.number="newGiftCardInitialValue"
267
+ class="block pl-7 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset"
268
+ />
269
+ </div>
270
+ </div>
271
+ </div>
272
+
273
+ <div>
274
+ <label for="recipient-name" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Recipient Name</label>
275
+ <div class="mt-2">
276
+ <input
277
+ type="text"
278
+ id="recipient-name"
279
+ x-model="newGiftCardRecipient"
280
+ class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm"
281
+ placeholder="Enter recipient name"
282
+ />
283
+ </div>
284
+ </div>
285
+
286
+ <div>
287
+ <label for="recipient-email" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Recipient Email</label>
288
+ <div class="mt-2">
289
+ <input
290
+ type="email"
291
+ id="recipient-email"
292
+ x-model="newGiftCardEmail"
293
+ class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm"
294
+ placeholder="Enter recipient email"
295
+ />
296
+ </div>
297
+ </div>
298
+
299
+ <div>
300
+ <label for="expiry-date" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Expiry Date</label>
301
+ <div class="mt-2">
302
+ <input
303
+ type="date"
304
+ id="expiry-date"
305
+ x-model="newGiftCardExpiryDate"
306
+ class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm"
307
+ />
308
+ </div>
309
+ </div>
310
+ </div>
311
+ </div>
312
+ </div>
313
+ </div>
314
+ <div class="sm:grid sm:gap-3 sm:grid-cols-2 sm:grid-flow-row-dense mt-5 sm:mt-6">
315
+ <button
316
+ type="button"
317
+ @click="addGiftCard()"
318
+ class="inline-flex justify-center sm:col-start-2 px-3 py-2 w-full font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 shadow-sm"
319
+ >
320
+ Create
321
+ </button>
322
+ <button
323
+ type="button"
324
+ @click="closeAddModal()"
325
+ 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"
326
+ >
327
+ Cancel
328
+ </button>
329
+ </div>
330
+ </div>
331
+ </PageLayout>
332
+ <script client>
333
+ import { useGiftCards } from '../../../../functions/commerce/gift-cards'
334
+ import { useAuth } from '../../../../functions/auth'
335
+ // Filter options — moved out of the former `<script server>` block
336
+ // (stacksjs/stacks#1838).
337
+ const statuses = ['all', 'Active', 'Used', 'Expired'] as const
338
+ const timeRanges = ['Today', 'Last 7 days', 'Last 30 days', 'Last 90 days', 'Last year', 'All time'] as const
339
+
340
+ interface GiftCard {
341
+ id?: number
342
+ code: string
343
+ initial_balance: number
344
+ current_balance: number
345
+ customer_id?: number
346
+ status: string
347
+ recipient_email?: string
348
+ recipient_name?: string
349
+ expiry_date: string | number
350
+ created_at?: string
351
+ is_active?: boolean
352
+ is_digital?: boolean
353
+ }
354
+
355
+ const { giftCards, createGiftCard, fetchGiftCards } = useGiftCards()
356
+ const { user } = useAuth()
357
+
358
+ const timeRange = state<string>('Last 30 days')
359
+ const searchQuery = state('')
360
+ const sortBy = state<'initial_balance' | 'current_balance' | 'purchaseDate' | 'expiry_date'>('purchaseDate')
361
+ const sortOrder = state<'asc' | 'desc'>('desc')
362
+ const statusFilter = state<string>('all')
363
+
364
+ // Modal + form signals.
365
+ const oneYearFromNow = new Date()
366
+ oneYearFromNow.setFullYear(oneYearFromNow.getFullYear() + 1)
367
+ const futureDate = oneYearFromNow.toISOString().split('T')[0] || ''
368
+
369
+ const showAddModal = state(false)
370
+ const newGiftCardCode = state('')
371
+ const newGiftCardInitialValue = state(50)
372
+ const newGiftCardRecipient = state('')
373
+ const newGiftCardEmail = state('')
374
+ const newGiftCardExpiryDate = state(futureDate)
375
+
376
+ const chartOptions = {
377
+ responsive: true,
378
+ maintainAspectRatio: false,
379
+ plugins: { legend: { display: false } },
380
+ scales: {
381
+ y: { beginAtZero: true, grid: { display: true, color: 'rgba(0, 0, 0, 0.05)' } },
382
+ x: { grid: { display: false } },
383
+ },
384
+ }
385
+
386
+ function monthlyChartData(): { giftCardSalesData: unknown, giftCardRevenueData: unknown } {
387
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
388
+ const salesData = [2, 3, 5, 4, 6, 8, 7, 10, 12, 15, 18, 20]
389
+ const revenueData = [100, 150, 250, 200, 300, 400, 350, 500, 600, 750, 900, 1000]
390
+
391
+ return {
392
+ giftCardSalesData: {
393
+ labels: months,
394
+ datasets: [{
395
+ label: 'Gift Card Sales',
396
+ backgroundColor: 'rgba(59, 130, 246, 0.2)',
397
+ borderColor: 'rgba(59, 130, 246, 1)',
398
+ borderWidth: 2,
399
+ pointBackgroundColor: 'rgba(59, 130, 246, 1)',
400
+ pointBorderColor: '#fff',
401
+ pointHoverBackgroundColor: '#fff',
402
+ pointHoverBorderColor: 'rgba(59, 130, 246, 1)',
403
+ fill: true,
404
+ tension: 0.4,
405
+ data: salesData,
406
+ }],
407
+ },
408
+ giftCardRevenueData: {
409
+ labels: months,
410
+ datasets: [{
411
+ label: 'Gift Card Revenue',
412
+ backgroundColor: 'rgba(16, 185, 129, 0.8)',
413
+ borderColor: 'rgba(16, 185, 129, 1)',
414
+ borderWidth: 1,
415
+ borderRadius: 4,
416
+ data: revenueData,
417
+ }],
418
+ },
419
+ }
420
+ }
421
+
422
+ function giftCardStats(): { activeGiftCards: number, totalGiftCards: number, totalInitialValue: string, totalCurrentBalance: string, totalRedeemed: string, redemptionRate: string } {
423
+ const list = giftCards.value as GiftCard[]
424
+ const activeGiftCards = list.filter(c => c.status === 'Active').length
425
+ const totalGiftCards = list.length
426
+ const totalInitialValue = list.reduce((sum, c) => sum + c.initial_balance, 0)
427
+ const totalCurrentBalance = list.reduce((sum, c) => sum + c.current_balance, 0)
428
+ const totalRedeemed = totalInitialValue - totalCurrentBalance
429
+ const redemptionRate = totalInitialValue > 0
430
+ ? ((totalRedeemed / totalInitialValue) * 100).toFixed(1)
431
+ : '0.0'
432
+
433
+ return {
434
+ activeGiftCards,
435
+ totalGiftCards,
436
+ totalInitialValue: totalInitialValue.toFixed(2),
437
+ totalCurrentBalance: totalCurrentBalance.toFixed(2),
438
+ totalRedeemed: totalRedeemed.toFixed(2),
439
+ redemptionRate,
440
+ }
441
+ }
442
+
443
+ const filteredGiftCards = derived(() => {
444
+ const q = searchQuery().toLowerCase()
445
+ const filter = statusFilter()
446
+ const by = sortBy()
447
+ const dir = sortOrder()
448
+
449
+ return (giftCards.value as GiftCard[])
450
+ .filter((c) => {
451
+ const matchesSearch
452
+ = c.code.toLowerCase().includes(q)
453
+ || (c.recipient_name?.toLowerCase().includes(q) ?? false)
454
+ || (c.recipient_email?.toLowerCase().includes(q) ?? false)
455
+ const matchesStatus = filter === 'all' || c.status === filter
456
+ return matchesSearch && matchesStatus
457
+ })
458
+ .sort((a, b) => {
459
+ let cmp = 0
460
+ if (by === 'purchaseDate')
461
+ cmp = new Date(a.created_at ?? 0).getTime() - new Date(b.created_at ?? 0).getTime()
462
+ else if (by === 'expiry_date')
463
+ cmp = new Date(a.expiry_date).getTime() - new Date(b.expiry_date).getTime()
464
+ else if (by === 'initial_balance')
465
+ cmp = a.initial_balance - b.initial_balance
466
+ else if (by === 'current_balance')
467
+ cmp = a.current_balance - b.current_balance
468
+ return dir === 'asc' ? cmp : -cmp
469
+ })
470
+ })
471
+
472
+ function toggleSort(column: 'initial_balance' | 'current_balance' | 'purchaseDate' | 'expiry_date'): void {
473
+ if (sortBy() === column) {
474
+ sortOrder.update(d => d === 'asc' ? 'desc' : 'asc')
475
+ }
476
+ else {
477
+ sortBy.set(column)
478
+ sortOrder.set('desc')
479
+ }
480
+ }
481
+
482
+ function getStatusClass(status: string): string {
483
+ switch (status) {
484
+ case 'Active':
485
+ return 'bg-green-50 text-green-700 ring-1 ring-inset ring-green-600/20 dark:bg-green-900/30 dark:text-green-400'
486
+ case 'Used':
487
+ return 'bg-gray-50 text-gray-700 ring-1 ring-inset ring-gray-600/20 dark:bg-neutral-900/30 dark:text-neutral-400'
488
+ case 'Expired':
489
+ return 'bg-red-50 text-red-700 ring-1 ring-inset ring-red-600/20 dark:bg-red-900/30 dark:text-red-400'
490
+ default:
491
+ return 'bg-gray-50 text-gray-700 ring-1 ring-inset ring-gray-600/20 dark:bg-neutral-900/30 dark:text-neutral-400'
492
+ }
493
+ }
494
+
495
+ function generateGiftCardCode(): string {
496
+ const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
497
+ const segment = (): string => {
498
+ let s = ''
499
+ for (let i = 0; i < 4; i++)
500
+ s += chars.charAt(Math.floor(Math.random() * chars.length))
501
+ return s
502
+ }
503
+ return `GFT-${segment()}-${segment()}-${segment()}`
504
+ }
505
+
506
+ function openAddModal(): void {
507
+ newGiftCardCode.set(generateGiftCardCode())
508
+ newGiftCardInitialValue.set(50)
509
+ newGiftCardRecipient.set('')
510
+ newGiftCardEmail.set('')
511
+ newGiftCardExpiryDate.set(futureDate)
512
+ showAddModal.set(true)
513
+ }
514
+
515
+ function closeAddModal(): void {
516
+ showAddModal.set(false)
517
+ }
518
+
519
+ async function addGiftCard(): Promise<void> {
520
+ const payload = {
521
+ code: newGiftCardCode(),
522
+ initial_balance: newGiftCardInitialValue(),
523
+ current_balance: newGiftCardInitialValue(),
524
+ customer_id: (user.value as { id?: number } | null)?.id ?? 1,
525
+ status: 'Active',
526
+ recipient_email: newGiftCardEmail(),
527
+ recipient_name: newGiftCardRecipient(),
528
+ expiry_date: new Date(newGiftCardExpiryDate()).getTime(),
529
+ is_active: true,
530
+ is_digital: true,
531
+ }
532
+
533
+ try {
534
+ await createGiftCard(payload as unknown as GiftCard)
535
+ closeAddModal()
536
+ }
537
+ catch (error) {
538
+ // eslint-disable-next-line no-console
539
+ console.error('Failed to create gift card:', error)
540
+ }
541
+ }
542
+
543
+ const { Chart, registerables } = await import('@stacksjs/charts')
544
+ Chart.register(...registerables)
545
+
546
+ const chartData = monthlyChartData()
547
+ const { destroyAll } = useCharts([
548
+ { Chart, id: 'chart_1', type: 'line', data: chartData.giftCardSalesData, options: chartOptions },
549
+ { Chart, id: 'chart_2', type: 'bar', data: chartData.giftCardRevenueData, options: chartOptions },
550
+ ])
551
+
552
+ onMount(async () => {
553
+ await fetchGiftCards()
554
+ })
555
+
556
+ onDestroy(() => {
557
+ destroyAll()
558
+ })
559
+ </script>
560
+
561
+ @endsection