@stacksjs/defaults 0.70.207 → 0.70.208

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (408) hide show
  1. package/TODO.md +21 -0
  2. package/ai/AGENTS.md +79 -0
  3. package/ai/README.md +45 -0
  4. package/ai/claude/launch.json +12 -0
  5. package/ai/skills/stacks-actions/SKILL.md +128 -0
  6. package/ai/skills/stacks-ai/SKILL.md +276 -0
  7. package/ai/skills/stacks-alias/SKILL.md +94 -0
  8. package/ai/skills/stacks-analytics/SKILL.md +91 -0
  9. package/ai/skills/stacks-api/SKILL.md +738 -0
  10. package/ai/skills/stacks-arrays/SKILL.md +277 -0
  11. package/ai/skills/stacks-auth/SKILL.md +422 -0
  12. package/ai/skills/stacks-auto-imports/SKILL.md +138 -0
  13. package/ai/skills/stacks-brandkit/SKILL.md +811 -0
  14. package/ai/skills/stacks-browse/SKILL.md +127 -0
  15. package/ai/skills/stacks-browse/scripts/browse.ts +492 -0
  16. package/ai/skills/stacks-browser/SKILL.md +145 -0
  17. package/ai/skills/stacks-buddy/SKILL.md +676 -0
  18. package/ai/skills/stacks-build/SKILL.md +163 -0
  19. package/ai/skills/stacks-cache/SKILL.md +125 -0
  20. package/ai/skills/stacks-calendar/SKILL.md +113 -0
  21. package/ai/skills/stacks-chat/SKILL.md +371 -0
  22. package/ai/skills/stacks-cli/SKILL.md +112 -0
  23. package/ai/skills/stacks-cloud/SKILL.md +192 -0
  24. package/ai/skills/stacks-cms/SKILL.md +217 -0
  25. package/ai/skills/stacks-collections/SKILL.md +82 -0
  26. package/ai/skills/stacks-commerce/SKILL.md +114 -0
  27. package/ai/skills/stacks-composables/SKILL.md +161 -0
  28. package/ai/skills/stacks-config/SKILL.md +144 -0
  29. package/ai/skills/stacks-configuration/SKILL.md +154 -0
  30. package/ai/skills/stacks-cron/SKILL.md +123 -0
  31. package/ai/skills/stacks-crosswind/SKILL.md +190 -0
  32. package/ai/skills/stacks-dashboard/SKILL.md +173 -0
  33. package/ai/skills/stacks-database/SKILL.md +286 -0
  34. package/ai/skills/stacks-datetime/SKILL.md +269 -0
  35. package/ai/skills/stacks-dependencies/SKILL.md +111 -0
  36. package/ai/skills/stacks-deploy/SKILL.md +102 -0
  37. package/ai/skills/stacks-design-brutalist/SKILL.md +110 -0
  38. package/ai/skills/stacks-design-minimalist/SKILL.md +115 -0
  39. package/ai/skills/stacks-design-output/SKILL.md +55 -0
  40. package/ai/skills/stacks-design-soft/SKILL.md +156 -0
  41. package/ai/skills/stacks-design-taste/SKILL.md +1012 -0
  42. package/ai/skills/stacks-design-taste-codex/SKILL.md +294 -0
  43. package/ai/skills/stacks-desktop/SKILL.md +78 -0
  44. package/ai/skills/stacks-development/SKILL.md +298 -0
  45. package/ai/skills/stacks-dns/SKILL.md +149 -0
  46. package/ai/skills/stacks-docs/SKILL.md +109 -0
  47. package/ai/skills/stacks-email/SKILL.md +236 -0
  48. package/ai/skills/stacks-enums/SKILL.md +100 -0
  49. package/ai/skills/stacks-env/SKILL.md +181 -0
  50. package/ai/skills/stacks-error-handling/SKILL.md +137 -0
  51. package/ai/skills/stacks-events/SKILL.md +228 -0
  52. package/ai/skills/stacks-faker/SKILL.md +158 -0
  53. package/ai/skills/stacks-git/SKILL.md +158 -0
  54. package/ai/skills/stacks-guard/SKILL.md +98 -0
  55. package/ai/skills/stacks-health/SKILL.md +75 -0
  56. package/ai/skills/stacks-http/SKILL.md +316 -0
  57. package/ai/skills/stacks-i18n/SKILL.md +198 -0
  58. package/ai/skills/stacks-image-to-code/SKILL.md +629 -0
  59. package/ai/skills/stacks-imagegen-mobile/SKILL.md +1474 -0
  60. package/ai/skills/stacks-imagegen-web/SKILL.md +998 -0
  61. package/ai/skills/stacks-investigate/SKILL.md +119 -0
  62. package/ai/skills/stacks-jobs/SKILL.md +136 -0
  63. package/ai/skills/stacks-lint/SKILL.md +184 -0
  64. package/ai/skills/stacks-listeners/SKILL.md +99 -0
  65. package/ai/skills/stacks-logging/SKILL.md +78 -0
  66. package/ai/skills/stacks-mail/SKILL.md +120 -0
  67. package/ai/skills/stacks-middleware/SKILL.md +249 -0
  68. package/ai/skills/stacks-migrations/SKILL.md +105 -0
  69. package/ai/skills/stacks-models/SKILL.md +267 -0
  70. package/ai/skills/stacks-new-feature/SKILL.md +206 -0
  71. package/ai/skills/stacks-notifications/SKILL.md +210 -0
  72. package/ai/skills/stacks-objects/SKILL.md +205 -0
  73. package/ai/skills/stacks-office-hours/SKILL.md +134 -0
  74. package/ai/skills/stacks-orm/SKILL.md +342 -0
  75. package/ai/skills/stacks-path/SKILL.md +118 -0
  76. package/ai/skills/stacks-payments/SKILL.md +373 -0
  77. package/ai/skills/stacks-plan-review/SKILL.md +152 -0
  78. package/ai/skills/stacks-plugins/SKILL.md +149 -0
  79. package/ai/skills/stacks-push/SKILL.md +376 -0
  80. package/ai/skills/stacks-query-builder/SKILL.md +128 -0
  81. package/ai/skills/stacks-queue/SKILL.md +559 -0
  82. package/ai/skills/stacks-realtime/SKILL.md +442 -0
  83. package/ai/skills/stacks-redesign/SKILL.md +182 -0
  84. package/ai/skills/stacks-registry/SKILL.md +58 -0
  85. package/ai/skills/stacks-repl/SKILL.md +80 -0
  86. package/ai/skills/stacks-retro/SKILL.md +122 -0
  87. package/ai/skills/stacks-review/SKILL.md +135 -0
  88. package/ai/skills/stacks-router/SKILL.md +231 -0
  89. package/ai/skills/stacks-routes/SKILL.md +105 -0
  90. package/ai/skills/stacks-scaffolding/SKILL.md +162 -0
  91. package/ai/skills/stacks-scheduler/SKILL.md +363 -0
  92. package/ai/skills/stacks-search-engine/SKILL.md +112 -0
  93. package/ai/skills/stacks-security/SKILL.md +129 -0
  94. package/ai/skills/stacks-security-audit/SKILL.md +128 -0
  95. package/ai/skills/stacks-server/SKILL.md +458 -0
  96. package/ai/skills/stacks-shell/SKILL.md +62 -0
  97. package/ai/skills/stacks-slug/SKILL.md +87 -0
  98. package/ai/skills/stacks-sms/SKILL.md +385 -0
  99. package/ai/skills/stacks-socials/SKILL.md +303 -0
  100. package/ai/skills/stacks-storage/SKILL.md +576 -0
  101. package/ai/skills/stacks-strings/SKILL.md +416 -0
  102. package/ai/skills/stacks-stx/SKILL.md +176 -0
  103. package/ai/skills/stacks-technical-diagrams/LICENSE +22 -0
  104. package/ai/skills/stacks-technical-diagrams/SKILL.md +290 -0
  105. package/ai/skills/stacks-technical-diagrams/agents/openai.yaml +4 -0
  106. package/ai/skills/stacks-technical-diagrams/assets/template.html +1252 -0
  107. package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +289 -0
  108. package/ai/skills/stacks-technical-diagrams/bunfig.toml +1 -0
  109. package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +64 -0
  110. package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +88 -0
  111. package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +75 -0
  112. package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +70 -0
  113. package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +41 -0
  114. package/ai/skills/stacks-technical-diagrams/renderers/architecture/grid.mjs +62 -0
  115. package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +371 -0
  116. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +82 -0
  117. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +295 -0
  118. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +91 -0
  119. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +384 -0
  120. package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +84 -0
  121. package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +257 -0
  122. package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +42 -0
  123. package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +13 -0
  124. package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +230 -0
  125. package/ai/skills/stacks-technical-diagrams/renderers/shared/layout-report.mjs +40 -0
  126. package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +88 -0
  127. package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +40 -0
  128. package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +103 -0
  129. package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +481 -0
  130. package/ai/skills/stacks-technical-diagrams/schemas/README.md +68 -0
  131. package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +121 -0
  132. package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +43 -0
  133. package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +221 -0
  134. package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +241 -0
  135. package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +186 -0
  136. package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +332 -0
  137. package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +297 -0
  138. package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +28 -0
  139. package/ai/skills/stacks-testing/SKILL.md +129 -0
  140. package/ai/skills/stacks-tunnel/SKILL.md +125 -0
  141. package/ai/skills/stacks-types/SKILL.md +178 -0
  142. package/ai/skills/stacks-ui/SKILL.md +246 -0
  143. package/ai/skills/stacks-utils/SKILL.md +444 -0
  144. package/ai/skills/stacks-validation/SKILL.md +371 -0
  145. package/ai/skills/stacks-whois/SKILL.md +315 -0
  146. package/assets/world-map-data.ts +121 -0
  147. package/bootstrap.ts +114 -0
  148. package/docs/api-examples.md +46 -0
  149. package/docs/index.md +24 -0
  150. package/docs/markdown-examples.md +87 -0
  151. package/functions/analytics.ts +97 -0
  152. package/functions/api.ts +143 -0
  153. package/functions/auth.ts +208 -0
  154. package/functions/billing/payments.ts +149 -0
  155. package/functions/blog.ts +120 -0
  156. package/functions/cms/categorizables.ts +107 -0
  157. package/functions/cms/pages.ts +108 -0
  158. package/functions/cms/posts.ts +100 -0
  159. package/functions/cms/taggables.ts +102 -0
  160. package/functions/commerce/coupons.ts +128 -0
  161. package/functions/commerce/customers.ts +128 -0
  162. package/functions/commerce/gift-cards.ts +119 -0
  163. package/functions/commerce/orders.ts +112 -0
  164. package/functions/commerce/payments.ts +119 -0
  165. package/functions/commerce/products/categories.ts +123 -0
  166. package/functions/commerce/products/manufacturers.ts +123 -0
  167. package/functions/commerce/products/products.ts +123 -0
  168. package/functions/commerce/products/reviews.ts +123 -0
  169. package/functions/commerce/products/units.ts +123 -0
  170. package/functions/commerce/products/variants.ts +123 -0
  171. package/functions/commerce/shippings/delivery-routes.ts +123 -0
  172. package/functions/commerce/shippings/digital-deliveries.ts +119 -0
  173. package/functions/commerce/shippings/drivers.ts +123 -0
  174. package/functions/commerce/shippings/license-keys.ts +123 -0
  175. package/functions/commerce/shippings/shipping-methods.ts +123 -0
  176. package/functions/commerce/shippings/shipping-rates.ts +119 -0
  177. package/functions/commerce/shippings/shipping-zones.ts +119 -0
  178. package/functions/commerce/taxes.ts +119 -0
  179. package/functions/commerce/waitlist/products.ts +123 -0
  180. package/functions/commerce/waitlist/restaurants.ts +123 -0
  181. package/functions/craft.ts +107 -0
  182. package/functions/dashboard.ts +153 -0
  183. package/functions/inbox.ts +95 -0
  184. package/functions/jobs.ts +86 -0
  185. package/functions/monitoring/errors.ts +295 -0
  186. package/functions/queue.ts +111 -0
  187. package/functions/realtime/websockets.ts +96 -0
  188. package/functions/realtime.ts +52 -0
  189. package/functions/releases.ts +49 -0
  190. package/functions/search.ts +126 -0
  191. package/functions/toasts.ts +95 -0
  192. package/functions/uploads.ts +216 -0
  193. package/ide/.gitignore +4 -0
  194. package/ide/cursor/rules/auto-imports.mdc +574 -0
  195. package/ide/cursor/rules/code-style.mdc +12 -0
  196. package/ide/cursor/rules/documentation.mdc +9 -0
  197. package/ide/cursor/rules/error-handling-validation.mdc +11 -0
  198. package/ide/cursor/rules/key-conventions.mdc +11 -0
  199. package/ide/cursor/rules/project-structure.mdc +169 -0
  200. package/ide/cursor/rules/readme.mdc +318 -0
  201. package/ide/cursor/rules/syntax-formatting.mdc +9 -0
  202. package/ide/cursor/rules/testing.mdc +10 -0
  203. package/ide/cursor/rules/typescript.mdc +9 -0
  204. package/ide/cursor/rules/ui-styling.mdc +0 -0
  205. package/ide/dictionary.txt +397 -0
  206. package/ide/jetbrains/.fleet/settings.json +4 -0
  207. package/ide/jetbrains/.idea/.gitignore +5 -0
  208. package/ide/jetbrains/.idea/.name +1 -0
  209. package/ide/jetbrains/.idea/inspectionProfiles/Project_Default.xml +6 -0
  210. package/ide/jetbrains/.idea/jsLinters/eslint.xml +7 -0
  211. package/ide/jetbrains/.idea/modules.xml +8 -0
  212. package/ide/jetbrains/.idea/stacks.iml +22 -0
  213. package/ide/jetbrains/.idea/vcs.xml +6 -0
  214. package/ide/jetbrains/.idea/webResources.xml +14 -0
  215. package/ide/jetbrains/IntelliJ IDEA Global Settings +0 -0
  216. package/ide/jetbrains/installed.txt +4 -0
  217. package/ide/jetbrains/options/github-copilot.xml +5 -0
  218. package/ide/jetbrains/options/nodejs.xml +7 -0
  219. package/ide/jetbrains/options/templates.xml +124 -0
  220. package/ide/jetbrains/templates/JavaScript.xml +6 -0
  221. package/ide/jetbrains/templates/Stacks.xml +8 -0
  222. package/ide/vscode/.vscode/README.md +23 -0
  223. package/ide/vscode/.vscode/extensions.json +5 -0
  224. package/ide/vscode/.vscode/settings.json +176 -0
  225. package/ide/vscode/.vscode/stacks.code-snippets +181 -0
  226. package/ide/vscode/LICENSE.md +21 -0
  227. package/ide/vscode/README.md +36 -0
  228. package/ide/vscode/logo.png +0 -0
  229. package/ide/vscode/package.json +79 -0
  230. package/ide/zed/.zed/settings.json +139 -0
  231. package/lang/README.md +28 -0
  232. package/lang/de.yml +14 -0
  233. package/lang/en.yml +15 -0
  234. package/package.json +19 -3
  235. package/project/bootstrap +161 -0
  236. package/project/buddy +175 -0
  237. package/project/storage/framework/tsconfig.app.json +50 -0
  238. package/project/storage/framework/tsconfig.base.json +58 -0
  239. package/routes/core.ts +78 -0
  240. package/routes/dashboard-api.ts +233 -0
  241. package/routes/dashboard.ts +739 -0
  242. package/routes/email.ts +129 -0
  243. package/stores/[name].stx +16 -0
  244. package/stores/git.ts +71 -0
  245. package/stores/payment.ts +474 -0
  246. package/stores/queue.ts +43 -0
  247. package/stores/user.ts +29 -0
  248. package/stx-components-plugin.ts +25 -0
  249. package/styles/design-tokens.css +597 -0
  250. package/types/dashboard.ts +70 -0
  251. package/types/defaults.ts +438 -0
  252. package/vcs/github/CODE_OF_CONDUCT.md +83 -0
  253. package/vcs/github/CONTRIBUTING.md +187 -0
  254. package/vcs/github/EXPLANATIONS.md +41 -0
  255. package/vcs/github/FUNDING.yml +1 -0
  256. package/vcs/github/ISSUE_TEMPLATE/bug_report.yml +49 -0
  257. package/vcs/github/ISSUE_TEMPLATE/config.yml +10 -0
  258. package/vcs/github/ISSUE_TEMPLATE/feature_request.yml +44 -0
  259. package/vcs/github/SECURITY.md +70 -0
  260. package/vcs/github/renovate.json +5 -0
  261. package/vcs/github/stale.yml +14 -0
  262. package/vcs/github/workflows/README.md +16 -0
  263. package/vcs/github/workflows/ci.yml +114 -0
  264. package/vcs/github/workflows/export-size.yml +25 -0
  265. package/vcs/github/workflows/labeler.yml +13 -0
  266. package/vcs/github/workflows/release.yml +219 -0
  267. package/views/[...all].stx +8 -0
  268. package/views/about.md +3 -0
  269. package/views/auth/login.stx +1 -0
  270. package/views/auth/signup.stx +85 -0
  271. package/views/dashboard/.discovered-models.json +542 -0
  272. package/views/dashboard/AUDIT.md +904 -0
  273. package/views/dashboard/[...all].stx +89 -0
  274. package/views/dashboard/access-tokens/index.stx +177 -0
  275. package/views/dashboard/actions/index.stx +129 -0
  276. package/views/dashboard/analytics/blog/index.stx +356 -0
  277. package/views/dashboard/analytics/browsers/index.stx +263 -0
  278. package/views/dashboard/analytics/commerce/sales/index.stx +329 -0
  279. package/views/dashboard/analytics/commerce/web/index.stx +379 -0
  280. package/views/dashboard/analytics/countries/index.stx +289 -0
  281. package/views/dashboard/analytics/devices/index.stx +305 -0
  282. package/views/dashboard/analytics/events/index.stx +536 -0
  283. package/views/dashboard/analytics/index.stx +88 -0
  284. package/views/dashboard/analytics/marketing/index.stx +315 -0
  285. package/views/dashboard/analytics/pages/index.stx +271 -0
  286. package/views/dashboard/analytics/referrers/index.stx +319 -0
  287. package/views/dashboard/analytics/web/index.stx +356 -0
  288. package/views/dashboard/buddy/index.stx +198 -0
  289. package/views/dashboard/ci/index.stx +462 -0
  290. package/views/dashboard/cloud/index.stx +452 -0
  291. package/views/dashboard/commands/index.stx +295 -0
  292. package/views/dashboard/commerce/categories/index.stx +406 -0
  293. package/views/dashboard/commerce/coupons/index.stx +485 -0
  294. package/views/dashboard/commerce/customers/index.stx +410 -0
  295. package/views/dashboard/commerce/dashboard/index.stx +287 -0
  296. package/views/dashboard/commerce/delivery/delivery-routes.stx +305 -0
  297. package/views/dashboard/commerce/delivery/digital-delivery.stx +332 -0
  298. package/views/dashboard/commerce/delivery/drivers.stx +397 -0
  299. package/views/dashboard/commerce/delivery/index.stx +18 -0
  300. package/views/dashboard/commerce/delivery/license-keys.stx +380 -0
  301. package/views/dashboard/commerce/delivery/shipping-methods.stx +346 -0
  302. package/views/dashboard/commerce/delivery/shipping-rates.stx +338 -0
  303. package/views/dashboard/commerce/delivery/shipping-zones.stx +302 -0
  304. package/views/dashboard/commerce/delivery/tabs.ts +25 -0
  305. package/views/dashboard/commerce/gift-cards/index.stx +561 -0
  306. package/views/dashboard/commerce/manufacturers/index.stx +308 -0
  307. package/views/dashboard/commerce/orders/index.stx +247 -0
  308. package/views/dashboard/commerce/payments/index.stx +535 -0
  309. package/views/dashboard/commerce/pos/index.stx +653 -0
  310. package/views/dashboard/commerce/printers/devices/index.stx +273 -0
  311. package/views/dashboard/commerce/printers/receipts/index.stx +308 -0
  312. package/views/dashboard/commerce/products/detail.stx +563 -0
  313. package/views/dashboard/commerce/products/index.stx +370 -0
  314. package/views/dashboard/commerce/reviews/index.stx +656 -0
  315. package/views/dashboard/commerce/reviews/index.ts +0 -0
  316. package/views/dashboard/commerce/taxes/index.stx +377 -0
  317. package/views/dashboard/commerce/units/index.stx +317 -0
  318. package/views/dashboard/commerce/variants/index.stx +319 -0
  319. package/views/dashboard/commerce/waitlist/products.stx +942 -0
  320. package/views/dashboard/commerce/waitlist/restaurant.stx +1024 -0
  321. package/views/dashboard/components/index.stx +330 -0
  322. package/views/dashboard/composables/index.ts +19 -0
  323. package/views/dashboard/composables/useChart.ts +77 -0
  324. package/views/dashboard/composables/useNavigation.ts +99 -0
  325. package/views/dashboard/composables/useRole.ts +58 -0
  326. package/views/dashboard/composables/useTheme.ts +74 -0
  327. package/views/dashboard/content/authors/index.stx +171 -0
  328. package/views/dashboard/content/blog/index.stx +493 -0
  329. package/views/dashboard/content/categories/index.stx +169 -0
  330. package/views/dashboard/content/comments/index.stx +189 -0
  331. package/views/dashboard/content/dashboard.stx +383 -0
  332. package/views/dashboard/content/files/index.stx +706 -0
  333. package/views/dashboard/content/pages/index.stx +174 -0
  334. package/views/dashboard/content/posts/index.stx +269 -0
  335. package/views/dashboard/content/seo/index.stx +240 -0
  336. package/views/dashboard/content/tags/index.stx +167 -0
  337. package/views/dashboard/data/activity/index.stx +261 -0
  338. package/views/dashboard/data/dashboard/index.stx +116 -0
  339. package/views/dashboard/data/subscribers.stx +270 -0
  340. package/views/dashboard/data/teams.stx +275 -0
  341. package/views/dashboard/data/users.stx +279 -0
  342. package/views/dashboard/dependencies/index.stx +178 -0
  343. package/views/dashboard/deployments/[id].stx +234 -0
  344. package/views/dashboard/deployments/index.stx +9 -0
  345. package/views/dashboard/dns/index.stx +163 -0
  346. package/views/dashboard/environment/index.stx +283 -0
  347. package/views/dashboard/errors/index.stx +274 -0
  348. package/views/dashboard/forgot-password.stx +57 -0
  349. package/views/dashboard/functions/index.stx +159 -0
  350. package/views/dashboard/health/index.stx +248 -0
  351. package/views/dashboard/inbox/activity.stx +350 -0
  352. package/views/dashboard/inbox/index.stx +452 -0
  353. package/views/dashboard/inbox/settings.stx +290 -0
  354. package/views/dashboard/index.stx +252 -0
  355. package/views/dashboard/insights/index.stx +529 -0
  356. package/views/dashboard/jobs/[id].stx +430 -0
  357. package/views/dashboard/jobs/history.stx +222 -0
  358. package/views/dashboard/jobs/index.stx +214 -0
  359. package/views/dashboard/kanban/[id].stx +899 -0
  360. package/views/dashboard/kanban/index.stx +260 -0
  361. package/views/dashboard/layouts/default.stx +565 -0
  362. package/views/dashboard/layouts/guest.stx +10 -0
  363. package/views/dashboard/login.stx +48 -0
  364. package/views/dashboard/logs/index.stx +540 -0
  365. package/views/dashboard/mailboxes/index.stx +149 -0
  366. package/views/dashboard/management/permissions/index.stx +490 -0
  367. package/views/dashboard/marketing/campaigns/index.stx +604 -0
  368. package/views/dashboard/marketing/lists/index.stx +514 -0
  369. package/views/dashboard/marketing/reviews/index.stx +475 -0
  370. package/views/dashboard/marketing/social-posts/index.stx +560 -0
  371. package/views/dashboard/models/[model].stx +195 -0
  372. package/views/dashboard/models/index.stx +159 -0
  373. package/views/dashboard/monitoring/errors/index.stx +415 -0
  374. package/views/dashboard/notifications/dashboard.stx +383 -0
  375. package/views/dashboard/notifications/email.stx +336 -0
  376. package/views/dashboard/notifications/history.stx +373 -0
  377. package/views/dashboard/notifications/sms.stx +332 -0
  378. package/views/dashboard/packages/index.stx +195 -0
  379. package/views/dashboard/queries/[id].stx +381 -0
  380. package/views/dashboard/queries/history.stx +227 -0
  381. package/views/dashboard/queries/index.stx +335 -0
  382. package/views/dashboard/queries/slow.stx +318 -0
  383. package/views/dashboard/queue/index.stx +427 -0
  384. package/views/dashboard/realtime/index.stx +429 -0
  385. package/views/dashboard/register.stx +46 -0
  386. package/views/dashboard/releases/index.stx +389 -0
  387. package/views/dashboard/requests/index.stx +462 -0
  388. package/views/dashboard/serverless/index.stx +475 -0
  389. package/views/dashboard/servers/[id].stx +299 -0
  390. package/views/dashboard/servers/index.stx +483 -0
  391. package/views/dashboard/settings/billing.stx +50 -0
  392. package/views/dashboard/settings/index.stx +256 -0
  393. package/views/dashboard/settings/mail.stx +252 -0
  394. package/views/dashboard/stores/auth.ts +123 -0
  395. package/views/dashboard/stores/ci.ts +342 -0
  396. package/views/dashboard/stores/index.ts +129 -0
  397. package/views/dashboard/stores/kanban.ts +723 -0
  398. package/views/dashboard/stores/rbac.ts +350 -0
  399. package/views/dashboard/stx.config.ts +34 -0
  400. package/views/dashboard/teams/[id].stx +170 -0
  401. package/views/forum/category.stx +92 -0
  402. package/views/forum/index.stx +209 -0
  403. package/views/forum/new-topic.stx +113 -0
  404. package/views/forum/profile.stx +1 -0
  405. package/views/forum/topic.stx +143 -0
  406. package/views/hello-world/[name].stx +47 -0
  407. package/views/index.stx +21 -0
  408. package/views/system-tray/index.stx +147 -0
@@ -0,0 +1,563 @@
1
+ @extends('layouts/default')
2
+
3
+ @section('content')
4
+ @title('Product Detail')
5
+ <PageLayout variant="bare">
6
+ <div class="container mx-auto px-4 py-8">
7
+ <!-- Loading state -->
8
+ <div :show="loading" class="flex items-center justify-center h-64">
9
+ <div class="h-12 w-12 border-b-2 border-primary border-t-2 rounded-full animate-spin"></div>
10
+ </div>
11
+
12
+ <div :show="hasProduct" class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow-md">
13
+ <!-- Header with back button and actions -->
14
+ <div class="flex items-center justify-between p-6 border-b border-gray-200 dark:border-neutral-700">
15
+ <div class="flex items-center">
16
+ <button @click="goBack()" class="mr-4 text-gray-500 dark:text-neutral-400 hover:text-gray-700 dark:hover:text-gray-200">
17
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
18
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
19
+ </svg>
20
+ </button>
21
+ <h1 class="font-bold text-2xl text-gray-900 dark:text-white">{{ productName() }}</h1>
22
+ </div>
23
+ <div class="flex space-x-2">
24
+ <button @click="editProduct()" class="px-4 py-2 text-white bg-primary hover:bg-primary-dark rounded-md transition-colors">Edit</button>
25
+ <button @click="deleteProduct()" class="px-4 py-2 text-white bg-red-500 hover:bg-red-600 rounded-md transition-colors">Delete</button>
26
+ </div>
27
+ </div>
28
+
29
+ <!-- Product content -->
30
+ <div class="grid gap-8 grid-cols-1 md:grid-cols-2 p-6">
31
+ <!-- Images and videos section -->
32
+ <div>
33
+ <div class="aspect-square overflow-hidden mb-4 bg-gray-100 dark:bg-neutral-700 rounded-lg">
34
+ <img src="{{ activeImage() }}" alt="{{ productName() }}" class="object-contain h-full w-full">
35
+ </div>
36
+
37
+ <div class="grid gap-2 grid-cols-5">
38
+ <template :for="(image, index) in productImages">
39
+ <div @click="selectImage({{ index }})" class="aspect-square overflow-hidden rounded-md cursor-pointer {{ activeImageIndex() === index ? 'ring-2 ring-primary' : '' }}">
40
+ <img src="{{ image }}" alt="{{ productName() }} thumbnail {{ index + 1 }}" class="object-cover h-full w-full">
41
+ </div>
42
+ </template>
43
+
44
+ <template :for="(video, index) in productVideos">
45
+ <div @click="openVideoModal('{{ video }}')" class="flex aspect-square overflow-hidden items-center justify-center bg-gray-200 dark:bg-neutral-600 rounded-md cursor-pointer">
46
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-gray-500 dark:text-neutral-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
47
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
48
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
49
+ </svg>
50
+ </div>
51
+ </template>
52
+ </div>
53
+ </div>
54
+
55
+ <!-- Product details -->
56
+ <div>
57
+ <div class="mb-6">
58
+ <div class="flex items-center justify-between mb-2">
59
+ <h2 class="font-bold text-3xl text-gray-900 dark:text-white">{{ productName() }}</h2>
60
+ <span class="px-3 py-1 text-sm rounded-full {{ statusClass() }}">{{ productStatus() }}</span>
61
+ </div>
62
+ <p class="font-semibold text-2xl text-gray-900 dark:text-white">${{ productPriceFormatted() }}</p>
63
+ </div>
64
+
65
+ <div class="mb-6">
66
+ <h3 class="mb-2 font-semibold text-gray-900 text-lg dark:text-white">Description</h3>
67
+ <p class="text-gray-700 dark:text-neutral-300">{{ productDescription() }}</p>
68
+ </div>
69
+
70
+ <div class="grid gap-4 grid-cols-2 mb-6">
71
+ <div>
72
+ <h3 class="mb-2 font-semibold text-gray-900 text-lg dark:text-white">Details</h3>
73
+ <ul class="space-y-2">
74
+ <li class="flex justify-between">
75
+ <span class="text-gray-600 dark:text-neutral-400">Category:</span>
76
+ <span class="text-gray-900 dark:text-white">{{ productCategory() }}</span>
77
+ </li>
78
+ <li class="flex justify-between">
79
+ <span class="text-gray-600 dark:text-neutral-400">Manufacturer:</span>
80
+ <span class="text-gray-900 dark:text-white">{{ productManufacturer() }}</span>
81
+ </li>
82
+ <li class="flex justify-between">
83
+ <span class="text-gray-600 dark:text-neutral-400">Stock:</span>
84
+ <span class="text-gray-900 dark:text-white">{{ productStock() }} {{ productUnit() }}s</span>
85
+ </li>
86
+ <li class="flex justify-between">
87
+ <span class="text-gray-600 dark:text-neutral-400">Tax Rate:</span>
88
+ <span class="text-gray-900 dark:text-white">{{ productTaxRate() }}%</span>
89
+ </li>
90
+ </ul>
91
+ </div>
92
+
93
+ <div>
94
+ <h3 class="mb-2 font-semibold text-gray-900 text-lg dark:text-white">Variants</h3>
95
+ <div :show="hasVariants">
96
+ <template :for="variant in productVariants">
97
+ <span class="inline-block mb-2 mr-2 px-3 py-1 text-gray-800 text-sm dark:text-neutral-200 bg-gray-100 dark:bg-neutral-700 rounded-full">{{ variant }}</span>
98
+ </template>
99
+ </div>
100
+ <p :show="noVariants" class="text-gray-600 dark:text-neutral-400">No variants available</p>
101
+ </div>
102
+ </div>
103
+
104
+ <div class="mb-6">
105
+ <h3 class="mb-2 font-semibold text-gray-900 text-lg dark:text-white">Specifications</h3>
106
+ <div class="p-4 bg-gray-50 dark:bg-neutral-700 rounded-lg">
107
+ <dl class="grid gap-x-4 gap-y-2 grid-cols-1 sm:grid-cols-2">
108
+ <template :for="entry in productSpecificationEntries">
109
+ <div class="pb-2 sm:pb-0 border-b border-gray-200 dark:border-neutral-600">
110
+ <dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">{{ entry.key }}</dt>
111
+ <dd class="mt-1 text-gray-900 text-sm dark:text-white">{{ entry.value }}</dd>
112
+ </div>
113
+ </template>
114
+ </dl>
115
+ </div>
116
+ </div>
117
+
118
+ <!-- Similar Products Section -->
119
+ <div class="mb-6">
120
+ <div class="flex items-center justify-between mb-2">
121
+ <h3 class="font-semibold text-gray-900 text-lg dark:text-white">Similar Products</h3>
122
+ <button @click="openSimilarProductsDialog()" class="text-blue-600 text-sm dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">Manage</button>
123
+ </div>
124
+
125
+ <div :show="hasSimilarProducts" class="grid gap-4 grid-cols-2">
126
+ <template :for="similar in similarProducts">
127
+ <div class="flex overflow-hidden flex-col border border-gray-200 rounded-lg dark:border-neutral-700">
128
+ <img src="{{ similar.images[0] }}" alt="{{ similar.name }}" class="object-cover h-32 w-full">
129
+ <div class="p-3">
130
+ <h4 class="font-medium text-gray-900 dark:text-white">{{ similar.name }}</h4>
131
+ <p class="text-gray-500 text-sm dark:text-neutral-400">{{ similar.category }}</p>
132
+ <p class="mt-1 font-semibold text-gray-900 text-sm dark:text-white">${{ similar.price.toFixed(2) }}</p>
133
+ </div>
134
+ </div>
135
+ </template>
136
+ </div>
137
+ <p :show="noSimilarProducts" class="text-gray-600 text-sm dark:text-neutral-400">No similar products available</p>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </div>
142
+
143
+ <div :show="notFound" class="p-8 text-center">
144
+ <p class="text-gray-600 text-xl dark:text-neutral-400">Product not found</p>
145
+ <button @click="goBack()" class="mt-4 px-4 py-2 text-white bg-primary hover:bg-primary-dark rounded-md transition-colors">Back to Products</button>
146
+ </div>
147
+
148
+ <!-- Video Modal -->
149
+ <div :show="showVideoModal" class="flex fixed inset-0 z-50 items-center justify-center bg-black bg-opacity-75">
150
+ <div class="relative max-w-4xl w-full bg-white dark:bg-neutral-800 rounded-lg shadow-xl">
151
+ <div class="flex items-center justify-between p-4 border-b border-gray-200 dark:border-neutral-700">
152
+ <h3 class="font-semibold text-gray-900 text-lg dark:text-white">Product Video</h3>
153
+ <button @click="closeVideoModal()" class="text-gray-500 dark:text-neutral-400 hover:text-gray-700 dark:hover:text-gray-200">
154
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
155
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
156
+ </svg>
157
+ </button>
158
+ </div>
159
+ <div class="p-6">
160
+ <div class="flex aspect-video items-center justify-center bg-black">
161
+ <p class="text-white">Video would play here: {{ activeVideoUrl }}</p>
162
+ </div>
163
+ </div>
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Edit Product Dialog -->
168
+ <div :show="showEditDialog" class="flex fixed inset-0 z-50 items-center justify-center bg-black/50">
169
+ <div class="fixed inset-0" aria-hidden="true" @click="closeEditDialog()"></div>
170
+ <div class="relative z-10 mx-auto p-6 max-w-2xl w-full bg-white dark:bg-neutral-800 rounded-xl shadow-xl">
171
+ <div class="flex items-center justify-between mb-4">
172
+ <h3 class="font-medium text-gray-900 text-lg dark:text-white">Edit Product</h3>
173
+ <button class="p-1 text-gray-400 dark:text-neutral-300 hover:text-gray-500 dark:hover:text-gray-100 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2" @click="closeEditDialog()">
174
+ <div class="h-5 w-5 i-hugeicons-cancel-01"></div>
175
+ </button>
176
+ </div>
177
+
178
+ <div class="space-y-4">
179
+ <div class="grid gap-4 grid-cols-1 md:grid-cols-2">
180
+ <div>
181
+ <label for="edit-name" class="block font-medium text-gray-700 text-sm dark:text-neutral-300">Name</label>
182
+ <input id="edit-name" x-model="editName" type="text" class="block mt-1 w-full sm:text-sm dark:text-white dark:bg-neutral-700 border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:ring-blue-500 shadow-sm" />
183
+ </div>
184
+ <div>
185
+ <label for="edit-price" class="block font-medium text-gray-700 text-sm dark:text-neutral-300">Price</label>
186
+ <input id="edit-price" x-model.number="editPrice" type="number" step="0.01" class="block mt-1 w-full sm:text-sm dark:text-white dark:bg-neutral-700 border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:ring-blue-500 shadow-sm" />
187
+ </div>
188
+ <div>
189
+ <label for="edit-category" class="block font-medium text-gray-700 text-sm dark:text-neutral-300">Category</label>
190
+ <input id="edit-category" x-model="editCategory" type="text" class="block mt-1 w-full sm:text-sm dark:text-white dark:bg-neutral-700 border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:ring-blue-500 shadow-sm" />
191
+ </div>
192
+ <div>
193
+ <label for="edit-stock" class="block font-medium text-gray-700 text-sm dark:text-neutral-300">Stock</label>
194
+ <input id="edit-stock" x-model.number="editStock" type="number" class="block mt-1 w-full sm:text-sm dark:text-white dark:bg-neutral-700 border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:ring-blue-500 shadow-sm" />
195
+ </div>
196
+ <div>
197
+ <label for="edit-status" class="block font-medium text-gray-700 text-sm dark:text-neutral-300">Status</label>
198
+ <select id="edit-status" x-model="editStatus" class="block mt-1 w-full sm:text-sm dark:text-white dark:bg-neutral-700 border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:ring-blue-500 shadow-sm">
199
+ <option value="Active">Active</option>
200
+ <option value="Low Stock">Low Stock</option>
201
+ <option value="Out of Stock">Out of Stock</option>
202
+ </select>
203
+ </div>
204
+ <div>
205
+ <label for="edit-manufacturer" class="block font-medium text-gray-700 text-sm dark:text-neutral-300">Manufacturer</label>
206
+ <input id="edit-manufacturer" x-model="editManufacturer" type="text" class="block mt-1 w-full sm:text-sm dark:text-white dark:bg-neutral-700 border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:ring-blue-500 shadow-sm" />
207
+ </div>
208
+ </div>
209
+
210
+ <div>
211
+ <label for="edit-description" class="block font-medium text-gray-700 text-sm dark:text-neutral-300">Description</label>
212
+ <textarea id="edit-description" x-model="editDescription" rows="4" class="block mt-1 w-full sm:text-sm dark:text-white dark:bg-neutral-700 border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:ring-blue-500 shadow-sm"></textarea>
213
+ </div>
214
+
215
+ <div class="flex justify-end mt-6 space-x-3">
216
+ <button @click="closeEditDialog()" class="px-4 py-2 font-medium text-gray-700 text-sm dark:text-white bg-white dark:bg-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-600 border border-gray-300 rounded-md dark:border-neutral-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 shadow-sm">Cancel</button>
217
+ <button @click="saveProduct()" class="px-4 py-2 font-medium text-sm text-white bg-blue-600 hover:bg-blue-700 border border-transparent rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 shadow-sm">Save Changes</button>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ </div>
222
+
223
+ <!-- Similar Products Dialog -->
224
+ <div :show="showSimilarProductsDialog" class="flex fixed inset-0 z-50 items-center justify-center bg-black/50">
225
+ <div class="fixed inset-0" aria-hidden="true" @click="closeSimilarProductsDialog()"></div>
226
+ <div class="relative z-10 mx-auto p-6 max-w-2xl w-full bg-white dark:bg-neutral-800 rounded-xl shadow-xl">
227
+ <div class="flex items-center justify-between mb-4">
228
+ <h3 class="font-medium text-gray-900 text-lg dark:text-white">Manage Similar Products</h3>
229
+ <button class="p-1 text-gray-400 dark:text-neutral-300 hover:text-gray-500 dark:hover:text-gray-100 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2" @click="closeSimilarProductsDialog()">
230
+ <div class="h-5 w-5 i-hugeicons-cancel-01"></div>
231
+ </button>
232
+ </div>
233
+
234
+ <div class="space-y-4">
235
+ <p class="text-gray-500 text-sm dark:text-neutral-400">
236
+ Select products that are similar to <strong>{{ productName() }}</strong>. These will be shown as recommendations.
237
+ </p>
238
+
239
+ <div class="overflow-y-auto max-h-96 border border-gray-200 rounded-md dark:border-neutral-700">
240
+ <ul class="divide-gray-200 divide-y dark:divide-neutral-700">
241
+ <template :for="similar in otherProducts">
242
+ <li class="flex items-center px-4 py-3 hover:bg-gray-50 dark:hover:bg-neutral-700">
243
+ <input id="similar-product-{{ similar.id }}" type="checkbox" value="{{ similar.id }}" @change="toggleSimilarProduct({{ similar.id }})" {{ isSimilarProductSelected(similar.id) ? 'checked' : '' }} class="h-4 w-4 text-blue-600 border-gray-300 rounded dark:border-neutral-600 focus:ring-blue-500" />
244
+ <label for="similar-product-{{ similar.id }}" class="flex items-center ml-3 cursor-pointer">
245
+ <img src="{{ similar.images[0] }}" alt="" class="object-cover flex-shrink-0 h-10 w-10 rounded-md">
246
+ <div class="ml-3">
247
+ <p class="font-medium text-gray-900 text-sm dark:text-white">{{ similar.name }}</p>
248
+ <p class="text-gray-500 text-sm dark:text-neutral-400">{{ similar.category }} - ${{ similar.price.toFixed(2) }}</p>
249
+ </div>
250
+ </label>
251
+ </li>
252
+ </template>
253
+ </ul>
254
+ </div>
255
+
256
+ <div class="flex justify-end mt-6 space-x-3">
257
+ <button @click="closeSimilarProductsDialog()" class="px-4 py-2 font-medium text-gray-700 text-sm dark:text-white bg-white dark:bg-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-600 border border-gray-300 rounded-md dark:border-neutral-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 shadow-sm">Cancel</button>
258
+ <button @click="saveSimilarProducts()" class="px-4 py-2 font-medium text-sm text-white bg-blue-600 hover:bg-blue-700 border border-transparent rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 shadow-sm">Save Changes</button>
259
+ </div>
260
+ </div>
261
+ </div>
262
+ </div>
263
+ </div>
264
+ </PageLayout>
265
+
266
+ <script client>
267
+ interface ProductDetail {
268
+ id: number
269
+ name: string
270
+ price: number
271
+ description: string
272
+ category: string
273
+ stock: number
274
+ status: string
275
+ images: string[]
276
+ videos: string[]
277
+ manufacturer: string
278
+ variants: string[]
279
+ unit: string
280
+ taxRate: number
281
+ specifications: Record<string, string>
282
+ similarProducts?: number[]
283
+ }
284
+
285
+ const route = useRoute()
286
+
287
+ const product = state<ProductDetail | null>(null)
288
+ const loading = state(true)
289
+ const activeImageIndex = state(0)
290
+ const showVideoModal = state(false)
291
+ const activeVideoUrl = state('')
292
+
293
+ const showEditDialog = state(false)
294
+ const showSimilarProductsDialog = state(false)
295
+
296
+ const editName = state('')
297
+ const editPrice = state(0)
298
+ const editCategory = state('')
299
+ const editStock = state(0)
300
+ const editStatus = state('Active')
301
+ const editManufacturer = state('')
302
+ const editDescription = state('')
303
+ const editSimilarProductIds = state<number[]>([])
304
+
305
+ const allProducts = state<ProductDetail[]>([])
306
+
307
+ const hasProduct = derived(() => !loading() && product() !== null)
308
+ const notFound = derived(() => !loading() && product() === null)
309
+
310
+ function productName(): string { return product()?.name ?? '' }
311
+ function productDescription(): string { return product()?.description ?? '' }
312
+ function productCategory(): string { return product()?.category ?? '' }
313
+ function productManufacturer(): string { return product()?.manufacturer ?? '' }
314
+ function productStock(): number { return product()?.stock ?? 0 }
315
+ function productUnit(): string { return product()?.unit ?? '' }
316
+ function productTaxRate(): number { return product()?.taxRate ?? 0 }
317
+ function productStatus(): string { return product()?.status ?? '' }
318
+ function productPriceFormatted(): string { return (product()?.price ?? 0).toFixed(2) }
319
+ function productImages(): string[] { return product()?.images ?? [] }
320
+ function productVideos(): string[] { return product()?.videos ?? [] }
321
+ function productVariants(): string[] { return product()?.variants ?? [] }
322
+
323
+ function activeImage(): string {
324
+ const images = productImages()
325
+ return images[activeImageIndex()] ?? ''
326
+ }
327
+
328
+ const hasVariants = derived(() => productVariants().length > 0)
329
+ const noVariants = derived(() => !hasVariants())
330
+
331
+ function productSpecificationEntries(): Array<{ key: string, value: string }> {
332
+ const specs = product()?.specifications ?? {}
333
+ return Object.entries(specs).map(([key, value]) => ({ key, value }))
334
+ }
335
+
336
+ const similarProducts = derived(() => {
337
+ const current = product()
338
+ const ids = current?.similarProducts
339
+ if (!current || !ids || !ids.length)
340
+ return []
341
+ return allProducts().filter(p => ids.includes(p.id))
342
+ })
343
+
344
+ const hasSimilarProducts = derived(() => similarProducts().length > 0)
345
+ const noSimilarProducts = derived(() => !hasSimilarProducts())
346
+
347
+ function otherProducts(): ProductDetail[] {
348
+ const currentId = product()?.id
349
+ return allProducts().filter(p => p.id !== currentId)
350
+ }
351
+
352
+ function isSimilarProductSelected(id: number): boolean {
353
+ return editSimilarProductIds().includes(id)
354
+ }
355
+
356
+ function statusClass(): string {
357
+ const s = productStatus()
358
+ if (s === 'Active')
359
+ return 'bg-green-100 text-green-800 dark:bg-green-800 dark:text-green-100'
360
+ if (s === 'Low Stock')
361
+ return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-100'
362
+ if (s === 'Out of Stock')
363
+ return 'bg-red-100 text-red-800 dark:bg-red-800 dark:text-red-100'
364
+ return ''
365
+ }
366
+
367
+ function selectImage(index: number): void {
368
+ activeImageIndex.set(index)
369
+ }
370
+
371
+ function openVideoModal(videoUrl: string): void {
372
+ activeVideoUrl.set(videoUrl)
373
+ showVideoModal.set(true)
374
+ }
375
+
376
+ function closeVideoModal(): void {
377
+ showVideoModal.set(false)
378
+ activeVideoUrl.set('')
379
+ }
380
+
381
+ function goBack(): void {
382
+ navigate('/commerce/products')
383
+ }
384
+
385
+ function editProduct(): void {
386
+ const current = product()
387
+ if (!current)
388
+ return
389
+ editName.set(current.name)
390
+ editPrice.set(current.price)
391
+ editCategory.set(current.category)
392
+ editStock.set(current.stock)
393
+ editStatus.set(current.status)
394
+ editManufacturer.set(current.manufacturer)
395
+ editDescription.set(current.description)
396
+ showEditDialog.set(true)
397
+ }
398
+
399
+ function closeEditDialog(): void {
400
+ showEditDialog.set(false)
401
+ }
402
+
403
+ function saveProduct(): void {
404
+ const current = product()
405
+ if (!current)
406
+ return
407
+ product.set({
408
+ ...current,
409
+ name: editName(),
410
+ price: editPrice(),
411
+ category: editCategory(),
412
+ stock: editStock(),
413
+ status: editStatus(),
414
+ manufacturer: editManufacturer(),
415
+ description: editDescription(),
416
+ })
417
+ // eslint-disable-next-line no-console
418
+ console.log('[success] Product updated successfully')
419
+ closeEditDialog()
420
+ }
421
+
422
+ function openSimilarProductsDialog(): void {
423
+ const current = product()
424
+ if (!current)
425
+ return
426
+ editSimilarProductIds.set([...(current.similarProducts ?? [])])
427
+ showSimilarProductsDialog.set(true)
428
+ }
429
+
430
+ function closeSimilarProductsDialog(): void {
431
+ showSimilarProductsDialog.set(false)
432
+ }
433
+
434
+ function toggleSimilarProduct(id: number): void {
435
+ const ids = editSimilarProductIds()
436
+ if (ids.includes(id))
437
+ editSimilarProductIds.set(ids.filter(x => x !== id))
438
+ else
439
+ editSimilarProductIds.set([...ids, id])
440
+ }
441
+
442
+ function saveSimilarProducts(): void {
443
+ const current = product()
444
+ if (!current)
445
+ return
446
+ product.set({ ...current, similarProducts: editSimilarProductIds() })
447
+ // eslint-disable-next-line no-console
448
+ console.log('[success] Similar products updated successfully')
449
+ closeSimilarProductsDialog()
450
+ }
451
+
452
+ function deleteProduct(): void {
453
+ // eslint-disable-next-line no-console
454
+ console.log('[error] Delete functionality would be implemented here')
455
+ }
456
+
457
+ onMount(() => {
458
+ setTimeout(() => {
459
+ const productId = Number.parseInt(route.params.id as string, 10) || 1
460
+
461
+ allProducts.set([
462
+ {
463
+ id: 1,
464
+ name: 'Margherita Pizza',
465
+ price: 12.99,
466
+ description: 'Classic Italian pizza with tomato sauce, fresh mozzarella, basil, and extra virgin olive oil.',
467
+ category: 'Pizza',
468
+ stock: 78,
469
+ status: 'Active',
470
+ images: ['https://images.unsplash.com/photo-1604382354936-07c5d9983bd3?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80'],
471
+ videos: [],
472
+ manufacturer: 'Pizza Palace',
473
+ variants: ['Size', 'Crust Type'],
474
+ unit: 'Whole',
475
+ taxRate: 7,
476
+ specifications: {},
477
+ similarProducts: [2, 5],
478
+ },
479
+ {
480
+ id: 2,
481
+ name: 'Classic Cheeseburger',
482
+ price: 9.99,
483
+ description: 'Juicy beef patty with melted cheese, lettuce, tomato, and special sauce.',
484
+ category: 'Burgers',
485
+ stock: 124,
486
+ status: 'Active',
487
+ images: ['https://images.unsplash.com/photo-1568901346375-23c9450c58cd?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80'],
488
+ videos: [],
489
+ manufacturer: 'Burger Bistro',
490
+ variants: ['Size', 'Protein', 'Toppings'],
491
+ unit: 'Piece',
492
+ taxRate: 7,
493
+ specifications: {},
494
+ similarProducts: [1],
495
+ },
496
+ {
497
+ id: 5,
498
+ name: 'Fettuccine Alfredo',
499
+ price: 13.99,
500
+ description: 'Creamy pasta with parmesan cheese sauce.',
501
+ category: 'Pasta',
502
+ stock: 210,
503
+ status: 'Active',
504
+ images: ['https://images.unsplash.com/photo-1645112411341-6c4fd023882c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80'],
505
+ videos: [],
506
+ manufacturer: 'Pasta Paradise',
507
+ variants: ['Size', 'Protein'],
508
+ unit: 'Serving',
509
+ taxRate: 7,
510
+ specifications: {},
511
+ similarProducts: [1],
512
+ },
513
+ ])
514
+
515
+ product.set({
516
+ id: productId,
517
+ name: 'Margherita Pizza',
518
+ price: 12.99,
519
+ description: 'Classic Italian pizza with tomato sauce, fresh mozzarella, basil, and extra virgin olive oil. Made with our signature hand-tossed dough and baked in a wood-fired oven for the perfect crispy yet chewy crust.',
520
+ category: 'Pizza',
521
+ stock: 78,
522
+ status: 'Active',
523
+ images: [
524
+ 'https://images.unsplash.com/photo-1604382354936-07c5d9983bd3?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
525
+ 'https://images.unsplash.com/photo-1593560708920-61dd98c46a4e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
526
+ 'https://images.unsplash.com/photo-1574071318508-1cdbab80d002?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
527
+ ],
528
+ videos: [
529
+ 'https://example.com/videos/margherita-pizza.mp4',
530
+ 'https://example.com/videos/pizza-preparation.mp4',
531
+ ],
532
+ manufacturer: 'Pizza Palace',
533
+ variants: ['Size', 'Crust Type'],
534
+ unit: 'Whole',
535
+ taxRate: 7,
536
+ specifications: {
537
+ Calories: '250 per slice',
538
+ 'Preparation Time': '15-20 minutes',
539
+ Ingredients: 'Dough, Tomato Sauce, Fresh Mozzarella, Basil, Olive Oil, Salt',
540
+ Allergens: 'Wheat, Dairy',
541
+ Dietary: 'Vegetarian',
542
+ 'Serving Size': '8 slices per pizza',
543
+ },
544
+ similarProducts: [2, 5],
545
+ })
546
+ loading.set(false)
547
+ }, 1000)
548
+ })
549
+ </script>
550
+
551
+ <style>
552
+ .fade-enter-active,
553
+ .fade-leave-active {
554
+ transition: opacity 0.3s ease;
555
+ }
556
+
557
+ .fade-enter-from,
558
+ .fade-leave-to {
559
+ opacity: 0;
560
+ }
561
+ </style>
562
+
563
+ @endsection