@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,287 @@
1
+ @extends('layouts/default')
2
+
3
+ @section('content')
4
+ <script client>
5
+ // Commerce Dashboard (stacksjs/stacks#1838 — last big page off the
6
+ // `<script server>` allowlist).
7
+ //
8
+ // Previous shape: a 146-line `<script server>` block that ran
9
+ // `payments.fetchPaymentStats(30)`, `orders.fetchStats()`,
10
+ // `productsModule.items.fetchTopProducts(5)`, plus monthly/daily
11
+ // trend queries — at render time, against `@stacksjs/commerce`. Each
12
+ // page load hit the DB synchronously and SSR'd the result.
13
+ //
14
+ // New shape: page is empty-state on first paint; `onMount` fetches
15
+ // `/api/dashboard/commerce/stats` (backed by the existing
16
+ // `CommerceDashboardAction`, just re-exposed without the auth
17
+ // middleware so the dev dashboard surface can reach it). Same query
18
+ // + same JSON shape, just consumed via fetch instead of inline.
19
+ //
20
+ // Chart payloads still come from a local placeholder since the
21
+ // existing action doesn't return chart series — wiring those will
22
+ // extend the action and add corresponding fields to the response.
23
+ const stats = state<Array<{ label: string, value: string, change: string }>>([
24
+ { label: 'Revenue (30d)', value: '—', change: '' },
25
+ { label: 'Orders', value: '—', change: '' },
26
+ { label: 'Avg Order Value', value: '—', change: '' },
27
+ { label: 'Conversion Rate', value: '—', change: '' },
28
+ ])
29
+
30
+ interface RecentOrder {
31
+ id: string
32
+ customer: string
33
+ total: string
34
+ status: string
35
+ time: string
36
+ }
37
+
38
+ interface TopProduct {
39
+ name: string
40
+ sales: number
41
+ revenue: string
42
+ }
43
+
44
+ const recentOrders = state<RecentOrder[]>([])
45
+ const topProducts = state<TopProduct[]>([])
46
+ const loading = state(true)
47
+ const error = state<string | null>(null)
48
+
49
+ const timeRange = state('Last 30 days')
50
+
51
+ async function loadStats() {
52
+ loading.set(true)
53
+ error.set(null)
54
+ try {
55
+ const res = await fetch('/api/dashboard/commerce/stats', {
56
+ headers: { accept: 'application/json' },
57
+ })
58
+ if (!res.ok) throw new Error(`HTTP ${res.status}`)
59
+ const data = await res.json() as {
60
+ stats?: Array<{ label: string, value: string, change: string }>
61
+ recentOrders?: RecentOrder[]
62
+ topProducts?: TopProduct[]
63
+ }
64
+ if (data.stats && data.stats.length > 0)
65
+ stats.set(data.stats)
66
+ recentOrders.set(data.recentOrders ?? [])
67
+ topProducts.set(data.topProducts ?? [])
68
+ }
69
+ catch (e) {
70
+ error.set(e instanceof Error ? e.message : String(e))
71
+ }
72
+ finally {
73
+ loading.set(false)
74
+ }
75
+ }
76
+
77
+ function changeClass(change: string): string {
78
+ if (!change) return 'text-gray-500 dark:text-neutral-400'
79
+ if (change.startsWith('+')) return 'text-green-600 dark:text-green-400'
80
+ if (change.startsWith('-')) return 'text-red-600 dark:text-red-400'
81
+ return 'text-gray-500 dark:text-neutral-400'
82
+ }
83
+
84
+ function changeIcon(change: string): string {
85
+ if (change.startsWith('+')) return 'i-hugeicons-analytics-up'
86
+ if (change.startsWith('-')) return 'i-hugeicons-analytics-down'
87
+ return ''
88
+ }
89
+
90
+ function orderStatusClass(status: string): string {
91
+ const s = status.toLowerCase()
92
+ if (s === 'completed') return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300'
93
+ if (s === 'processing') return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300'
94
+ if (s === 'shipped') return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300'
95
+ return 'bg-gray-100 text-gray-800 dark:bg-neutral-700 dark:text-neutral-300'
96
+ }
97
+
98
+ // Chart placeholders. The existing action does not return chart series
99
+ // — `fetchMonthlyPaymentTrends` and `fetchDailyOrderTrends` are wired
100
+ // in the original `<script server>` block but never made it into the
101
+ // JSON contract. Until that's added, fall back to empty arrays so the
102
+ // canvas mounts as empty.
103
+ const revenueData = {
104
+ labels: [] as string[],
105
+ datasets: [{
106
+ label: 'Revenue',
107
+ backgroundColor: 'rgba(59, 130, 246, 0.2)',
108
+ borderColor: 'rgba(59, 130, 246, 1)',
109
+ borderWidth: 2,
110
+ pointBackgroundColor: 'rgba(59, 130, 246, 1)',
111
+ pointBorderColor: '#fff',
112
+ pointHoverBackgroundColor: '#fff',
113
+ pointHoverBorderColor: 'rgba(59, 130, 246, 1)',
114
+ fill: true,
115
+ tension: 0.4,
116
+ data: [] as number[],
117
+ }],
118
+ }
119
+
120
+ const ordersData = {
121
+ labels: [] as string[],
122
+ datasets: [{
123
+ label: 'Orders',
124
+ backgroundColor: 'rgba(16, 185, 129, 0.8)',
125
+ borderColor: 'rgba(16, 185, 129, 1)',
126
+ borderWidth: 1,
127
+ borderRadius: 4,
128
+ data: [] as number[],
129
+ }],
130
+ }
131
+
132
+ const chartOptions = {
133
+ responsive: true,
134
+ maintainAspectRatio: false,
135
+ plugins: { legend: { display: false } },
136
+ scales: {
137
+ y: { beginAtZero: true, grid: { display: true, color: 'rgba(0, 0, 0, 0.05)' } },
138
+ x: { grid: { display: false } },
139
+ },
140
+ }
141
+
142
+ void loadStats()
143
+
144
+ const { Chart, registerables } = await import('@stacksjs/charts')
145
+ Chart.register(...registerables)
146
+
147
+ const { destroyAll } = useCharts([
148
+ { Chart, id: 'revenueChart', type: 'line', data: revenueData, options: chartOptions },
149
+ { Chart, id: 'ordersChart', type: 'bar', data: ordersData, options: chartOptions },
150
+ ])
151
+
152
+ onDestroy(() => {
153
+ destroyAll()
154
+ })
155
+ </script>
156
+
157
+ @title('Dashboard - Commerce')
158
+ <PageLayout>
159
+ <PageHeader title="Commerce Dashboard" description="Overview of your store's performance">
160
+ <template #actions>
161
+ <select x-model="timeRange" class="block pl-3 pr-10 py-1.5 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">
162
+ <option value="Today">Today</option>
163
+ <option value="Last 7 days">Last 7 days</option>
164
+ <option value="Last 30 days">Last 30 days</option>
165
+ <option value="Last 90 days">Last 90 days</option>
166
+ <option value="Last year">Last year</option>
167
+ <option value="All time">All time</option>
168
+ </select>
169
+ </template>
170
+ </PageHeader>
171
+
172
+ <!-- Error banner (initial load failure) -->
173
+ <div :if="error()" class="mt-4 px-4 py-3 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950 border border-red-300 rounded-md dark:border-red-800">
174
+ <span :text="error()"></span>
175
+ </div>
176
+
177
+ <!-- Stats — fed by GET /api/dashboard/commerce/stats. -->
178
+ <dl class="grid gap-5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 mt-5">
179
+ <template :for="stat in stats">
180
+ <div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
181
+ <dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">{{ stat.label }}</dt>
182
+ <dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">{{ stat.value }}</dd>
183
+ <dd :class="'flex items-center mt-2 text-sm ' + changeClass(stat.change)">
184
+ <div :if="stat.change" :class="'mr-1 h-4 w-4 ' + changeIcon(stat.change)"></div>
185
+ <span :if="stat.change" :text="stat.change + ' vs prior 30d'"></span>
186
+ <span :if="!stat.change">—</span>
187
+ </dd>
188
+ </div>
189
+ </template>
190
+ </dl>
191
+
192
+ <!-- Charts (empty until the action wires monthly/daily trend series
193
+ into its JSON response — see TODO at top of file). -->
194
+ <div class="grid gap-5 grid-cols-1 lg:grid-cols-2 mt-8">
195
+ <div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
196
+ <div class="p-6">
197
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Revenue</h3>
198
+ <div class="mt-2 h-80">
199
+ <canvas id="revenueChart"></canvas>
200
+ </div>
201
+ </div>
202
+ </div>
203
+
204
+ <div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
205
+ <div class="p-6">
206
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Orders</h3>
207
+ <div class="mt-2 h-80">
208
+ <canvas id="ordersChart"></canvas>
209
+ </div>
210
+ </div>
211
+ </div>
212
+ </div>
213
+
214
+ <!-- Top Selling Products -->
215
+ <div class="mt-8">
216
+ <div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
217
+ <div class="px-4 py-5 sm:px-6">
218
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Top Selling Products</h3>
219
+ <p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Ranked by units sold across all orders.</p>
220
+ </div>
221
+ <div class="border-gray-200 border-t dark:border-neutral-700">
222
+ <div :if="topProducts().length === 0" class="px-6 py-10 text-center text-gray-500 text-sm dark:text-neutral-400">
223
+ No sales recorded yet.
224
+ </div>
225
+ <table :if="topProducts().length > 0" class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
226
+ <thead class="bg-gray-50 dark:bg-neutral-700">
227
+ <tr>
228
+ <th scope="col" class="px-6 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Product</th>
229
+ <th scope="col" class="px-6 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Units Sold</th>
230
+ <th scope="col" class="px-6 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Revenue</th>
231
+ </tr>
232
+ </thead>
233
+ <tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
234
+ <template :for="product in topProducts">
235
+ <tr>
236
+ <td class="px-6 py-4 font-medium text-gray-900 text-sm whitespace-nowrap dark:text-white">{{ product.name }}</td>
237
+ <td class="px-6 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">{{ product.sales }}</td>
238
+ <td class="px-6 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">{{ product.revenue }}</td>
239
+ </tr>
240
+ </template>
241
+ </tbody>
242
+ </table>
243
+ </div>
244
+ </div>
245
+ </div>
246
+
247
+ <!-- Recent Orders -->
248
+ <div class="mt-8">
249
+ <div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
250
+ <div class="px-4 py-5 sm:px-6">
251
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Recent Orders</h3>
252
+ </div>
253
+ <div class="border-gray-200 border-t dark:border-neutral-700">
254
+ <div :if="recentOrders().length === 0" class="px-6 py-10 text-center text-gray-500 text-sm dark:text-neutral-400">
255
+ <span :if="loading()">Loading…</span>
256
+ <span :if="!loading()">No recent orders.</span>
257
+ </div>
258
+ <table :if="recentOrders().length > 0" class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
259
+ <thead class="bg-gray-50 dark:bg-neutral-700">
260
+ <tr>
261
+ <th scope="col" class="px-6 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Order ID</th>
262
+ <th scope="col" class="px-6 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Customer</th>
263
+ <th scope="col" class="px-6 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Total</th>
264
+ <th scope="col" class="px-6 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Status</th>
265
+ <th scope="col" class="px-6 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">When</th>
266
+ </tr>
267
+ </thead>
268
+ <tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
269
+ <template :for="order in recentOrders">
270
+ <tr>
271
+ <td class="px-6 py-4 font-medium text-gray-900 text-sm whitespace-nowrap dark:text-white">{{ order.id }}</td>
272
+ <td class="px-6 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">{{ order.customer }}</td>
273
+ <td class="px-6 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">{{ order.total }}</td>
274
+ <td class="px-6 py-4 whitespace-nowrap">
275
+ <span :class="'inline-flex px-2 font-semibold leading-5 text-xs rounded-full ' + orderStatusClass(order.status)">{{ order.status }}</span>
276
+ </td>
277
+ <td class="px-6 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">{{ order.time }}</td>
278
+ </tr>
279
+ </template>
280
+ </tbody>
281
+ </table>
282
+ </div>
283
+ </div>
284
+ </div>
285
+ </PageLayout>
286
+
287
+ @endsection
@@ -0,0 +1,305 @@
1
+ @extends('layouts/default')
2
+
3
+ @section('content')
4
+ @title('Dashboard - Delivery Routes')
5
+ <div class="py-6">
6
+ <div class="mx-auto px-4 sm:px-6 md:px-8 max-w-7xl">
7
+ <TabNavigation modelValue="routes" :tabs="tabs" />
8
+
9
+ <div class="overflow-hidden mt-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
10
+ <div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-neutral-700">
11
+ <div class="flex items-center justify-between">
12
+ <h2 class="font-medium text-gray-900 text-lg dark:text-white">Delivery Routes</h2>
13
+ <button @click="openAddModal()" type="button" 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">
14
+ <div class="mr-2 h-5 w-5 i-hugeicons-plus-sign"></div>
15
+ Add Route
16
+ </button>
17
+ </div>
18
+ <div class="mt-4">
19
+ <div class="w-full md:w-96"><SearchFilter placeholder="Search delivery routes..." @search="handleSearch" /></div>
20
+ </div>
21
+ </div>
22
+
23
+ <DeliveryRoutesTable :routes="paginatedRoutes" @view-map="viewRouteOnMap" @edit="openEditModal" @delete="handleDeleteRoute" />
24
+
25
+ <div class="px-4 py-3 bg-gray-50 dark:bg-neutral-900 border-gray-200 border-t dark:border-neutral-700">
26
+ <Pagination :currentPage="currentPage" :totalItems="totalItems" :itemsPerPage="itemsPerPage" @prev="handlePrevPage" @next="handleNextPage" @page="handlePageChange" />
27
+ </div>
28
+ </div>
29
+ </div>
30
+
31
+ <!-- Add Delivery Route Modal -->
32
+ <div :show="showAddModal" class="fixed inset-0 overflow-y-auto z-10">
33
+ <div class="flex items-end justify-center sm:items-center p-4 sm:p-0 min-h-full text-center">
34
+ <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="closeAddModal()"></div>
35
+ <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">
36
+ <div>
37
+ <div class="mt-3 sm:mt-5 text-center">
38
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Add New Delivery Route</h3>
39
+ <div class="mt-4">
40
+ <div class="space-y-4">
41
+ <div>
42
+ <label for="route-driver" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Driver</label>
43
+ <div class="mt-2">
44
+ <input type="text" id="route-driver" x-model="newDriver" placeholder="Enter driver name" 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" />
45
+ </div>
46
+ </div>
47
+ <div>
48
+ <label for="route-vehicle" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Vehicle</label>
49
+ <div class="mt-2">
50
+ <input type="text" id="route-vehicle" x-model="newVehicle" placeholder="Enter vehicle information" 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" />
51
+ </div>
52
+ </div>
53
+ <div class="grid gap-4 grid-cols-3">
54
+ <div>
55
+ <label for="route-stops" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Stops</label>
56
+ <div class="mt-2">
57
+ <input type="number" id="route-stops" x-model.number="newStops" placeholder="0" 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" />
58
+ </div>
59
+ </div>
60
+ <div>
61
+ <label for="route-delivery-time" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Delivery Time (hours)</label>
62
+ <div class="mt-2">
63
+ <input type="number" id="route-delivery-time" x-model.number="newDeliveryTime" step="0.1" placeholder="0" 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" />
64
+ </div>
65
+ </div>
66
+ <div>
67
+ <label for="route-total-distance" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Total Distance (km)</label>
68
+ <div class="mt-2">
69
+ <input type="number" id="route-total-distance" x-model.number="newTotalDistance" step="0.1" placeholder="0" 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" />
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ <div class="sm:grid sm:gap-3 sm:grid-cols-2 sm:grid-flow-row-dense mt-5 sm:mt-6">
78
+ <button type="button" @click="addDeliveryRoute()" 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">Add Route</button>
79
+ <button type="button" @click="closeAddModal()" 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">Cancel</button>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </div>
84
+
85
+ <!-- Edit Delivery Route Modal -->
86
+ <div :show="showEditModal" class="fixed inset-0 overflow-y-auto z-10">
87
+ <div class="flex items-end justify-center sm:items-center p-4 sm:p-0 min-h-full text-center">
88
+ <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="closeEditModal()"></div>
89
+ <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">
90
+ <div>
91
+ <div class="mt-3 sm:mt-5 text-center">
92
+ <h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Edit Delivery Route</h3>
93
+ <div class="mt-4">
94
+ <div class="space-y-4">
95
+ <div>
96
+ <label for="edit-route-driver" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Driver</label>
97
+ <div class="mt-2">
98
+ <input type="text" id="edit-route-driver" x-model="newDriver" placeholder="Enter driver name" 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" />
99
+ </div>
100
+ </div>
101
+ <div>
102
+ <label for="edit-route-vehicle" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Vehicle</label>
103
+ <div class="mt-2">
104
+ <input type="text" id="edit-route-vehicle" x-model="newVehicle" placeholder="Enter vehicle information" 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" />
105
+ </div>
106
+ </div>
107
+ <div class="grid gap-4 grid-cols-3">
108
+ <div>
109
+ <label for="edit-route-stops" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Stops</label>
110
+ <div class="mt-2">
111
+ <input type="number" id="edit-route-stops" x-model.number="newStops" placeholder="0" 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" />
112
+ </div>
113
+ </div>
114
+ <div>
115
+ <label for="edit-route-delivery-time" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Delivery Time (hours)</label>
116
+ <div class="mt-2">
117
+ <input type="number" id="edit-route-delivery-time" x-model.number="newDeliveryTime" step="0.1" placeholder="0" 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" />
118
+ </div>
119
+ </div>
120
+ <div>
121
+ <label for="edit-route-total-distance" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Total Distance (km)</label>
122
+ <div class="mt-2">
123
+ <input type="number" id="edit-route-total-distance" x-model.number="newTotalDistance" step="0.1" placeholder="0" 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" />
124
+ </div>
125
+ </div>
126
+ </div>
127
+ </div>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ <div class="sm:grid sm:gap-3 sm:grid-cols-2 sm:grid-flow-row-dense mt-5 sm:mt-6">
132
+ <button type="button" @click="saveDeliveryRoute()" 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">Save Changes</button>
133
+ <button type="button" @click="closeEditModal()" 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">Cancel</button>
134
+ </div>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ </div>
139
+
140
+ <script client>
141
+ import { useDeliveryRoutes } from '../../../../functions/commerce/shippings/delivery-routes'
142
+ // Shared tab nav config — formerly duplicated in every delivery page's
143
+ // `<script server>` block (stacksjs/stacks#1838).
144
+ import { deliveryTabs as tabs } from './tabs'
145
+
146
+ interface DeliveryRoute {
147
+ id?: number
148
+ driver: string
149
+ vehicle: string
150
+ stops: number
151
+ delivery_time: number
152
+ total_distance: number
153
+ }
154
+
155
+ const { deliveryRoutes, createDeliveryRoute, fetchDeliveryRoutes, updateDeliveryRoute, deleteDeliveryRoute } = useDeliveryRoutes()
156
+
157
+ const showAddModal = state(false)
158
+ const showEditModal = state(false)
159
+ const editingRoute = state<DeliveryRoute | null>(null)
160
+
161
+ const newDriver = state('')
162
+ const newVehicle = state('')
163
+ const newStops = state(0)
164
+ const newDeliveryTime = state(0)
165
+ const newTotalDistance = state(0)
166
+
167
+ const searchQuery = state('')
168
+ const currentPage = state(1)
169
+ const itemsPerPage = state(5)
170
+
171
+ const filteredRoutes = derived(() => {
172
+ const list = (deliveryRoutes.value as DeliveryRoute[]) || []
173
+ const q = searchQuery().toLowerCase()
174
+ if (!q)
175
+ return list
176
+ return list.filter(r =>
177
+ r.driver.toLowerCase().includes(q)
178
+ || r.vehicle.toLowerCase().includes(q)
179
+ || r.stops.toString().includes(q)
180
+ || r.delivery_time.toString().includes(q)
181
+ || r.total_distance.toString().includes(q),
182
+ )
183
+ })
184
+
185
+ const paginatedRoutes = derived(() => {
186
+ const start = (currentPage() - 1) * itemsPerPage()
187
+ return filteredRoutes().slice(start, start + itemsPerPage())
188
+ })
189
+
190
+ const totalItems = derived(() => filteredRoutes().length)
191
+
192
+ function resetForm(): void {
193
+ newDriver.set('')
194
+ newVehicle.set('')
195
+ newStops.set(0)
196
+ newDeliveryTime.set(0)
197
+ newTotalDistance.set(0)
198
+ }
199
+
200
+ function openAddModal(): void {
201
+ resetForm()
202
+ showAddModal.set(true)
203
+ }
204
+
205
+ function closeAddModal(): void {
206
+ showAddModal.set(false)
207
+ }
208
+
209
+ function openEditModal(route: DeliveryRoute): void {
210
+ editingRoute.set(route)
211
+ newDriver.set(route.driver)
212
+ newVehicle.set(route.vehicle)
213
+ newStops.set(route.stops)
214
+ newDeliveryTime.set(route.delivery_time)
215
+ newTotalDistance.set(route.total_distance)
216
+ showEditModal.set(true)
217
+ }
218
+
219
+ function closeEditModal(): void {
220
+ showEditModal.set(false)
221
+ editingRoute.set(null)
222
+ }
223
+
224
+ async function addDeliveryRoute(): Promise<void> {
225
+ try {
226
+ await createDeliveryRoute({
227
+ driver: newDriver(),
228
+ vehicle: newVehicle(),
229
+ stops: newStops(),
230
+ delivery_time: newDeliveryTime(),
231
+ total_distance: newTotalDistance(),
232
+ } as DeliveryRoute)
233
+ closeAddModal()
234
+ }
235
+ catch (error) {
236
+ // eslint-disable-next-line no-console
237
+ console.error('Failed to create delivery route:', error)
238
+ }
239
+ }
240
+
241
+ async function saveDeliveryRoute(): Promise<void> {
242
+ const target = editingRoute()
243
+ if (!target)
244
+ return
245
+ try {
246
+ await updateDeliveryRoute({
247
+ ...target,
248
+ driver: newDriver(),
249
+ vehicle: newVehicle(),
250
+ stops: newStops(),
251
+ delivery_time: newDeliveryTime(),
252
+ total_distance: newTotalDistance(),
253
+ })
254
+ closeEditModal()
255
+ }
256
+ catch (error) {
257
+ // eslint-disable-next-line no-console
258
+ console.error('Failed to update delivery route:', error)
259
+ }
260
+ }
261
+
262
+ function handleSearch(query: string): void {
263
+ searchQuery.set(query)
264
+ currentPage.set(1)
265
+ }
266
+
267
+ async function handleDeleteRoute(route: DeliveryRoute): Promise<void> {
268
+ // eslint-disable-next-line no-alert
269
+ if (!confirm('Are you sure you want to delete this delivery route?'))
270
+ return
271
+ try {
272
+ await deleteDeliveryRoute(route.id as number)
273
+ }
274
+ catch (error) {
275
+ // eslint-disable-next-line no-console
276
+ console.error('Failed to delete delivery route:', error)
277
+ }
278
+ }
279
+
280
+ function viewRouteOnMap(route: DeliveryRoute): void {
281
+ // eslint-disable-next-line no-alert
282
+ alert(`View route on map: ${route.driver} (${route.stops} stops)`)
283
+ }
284
+
285
+ function handlePrevPage(): void {
286
+ if (currentPage() > 1)
287
+ currentPage.update(p => p - 1)
288
+ }
289
+
290
+ function handleNextPage(): void {
291
+ const totalPages = Math.ceil(totalItems() / itemsPerPage())
292
+ if (currentPage() < totalPages)
293
+ currentPage.update(p => p + 1)
294
+ }
295
+
296
+ function handlePageChange(page: number): void {
297
+ currentPage.set(page)
298
+ }
299
+
300
+ onMount(async () => {
301
+ await fetchDeliveryRoutes()
302
+ })
303
+ </script>
304
+
305
+ @endsection