@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.
- package/TODO.md +21 -0
- package/ai/AGENTS.md +79 -0
- package/ai/README.md +45 -0
- package/ai/claude/launch.json +12 -0
- package/ai/skills/stacks-actions/SKILL.md +128 -0
- package/ai/skills/stacks-ai/SKILL.md +276 -0
- package/ai/skills/stacks-alias/SKILL.md +94 -0
- package/ai/skills/stacks-analytics/SKILL.md +91 -0
- package/ai/skills/stacks-api/SKILL.md +738 -0
- package/ai/skills/stacks-arrays/SKILL.md +277 -0
- package/ai/skills/stacks-auth/SKILL.md +422 -0
- package/ai/skills/stacks-auto-imports/SKILL.md +138 -0
- package/ai/skills/stacks-brandkit/SKILL.md +811 -0
- package/ai/skills/stacks-browse/SKILL.md +127 -0
- package/ai/skills/stacks-browse/scripts/browse.ts +492 -0
- package/ai/skills/stacks-browser/SKILL.md +145 -0
- package/ai/skills/stacks-buddy/SKILL.md +676 -0
- package/ai/skills/stacks-build/SKILL.md +163 -0
- package/ai/skills/stacks-cache/SKILL.md +125 -0
- package/ai/skills/stacks-calendar/SKILL.md +113 -0
- package/ai/skills/stacks-chat/SKILL.md +371 -0
- package/ai/skills/stacks-cli/SKILL.md +112 -0
- package/ai/skills/stacks-cloud/SKILL.md +192 -0
- package/ai/skills/stacks-cms/SKILL.md +217 -0
- package/ai/skills/stacks-collections/SKILL.md +82 -0
- package/ai/skills/stacks-commerce/SKILL.md +114 -0
- package/ai/skills/stacks-composables/SKILL.md +161 -0
- package/ai/skills/stacks-config/SKILL.md +144 -0
- package/ai/skills/stacks-configuration/SKILL.md +154 -0
- package/ai/skills/stacks-cron/SKILL.md +123 -0
- package/ai/skills/stacks-crosswind/SKILL.md +190 -0
- package/ai/skills/stacks-dashboard/SKILL.md +173 -0
- package/ai/skills/stacks-database/SKILL.md +286 -0
- package/ai/skills/stacks-datetime/SKILL.md +269 -0
- package/ai/skills/stacks-dependencies/SKILL.md +111 -0
- package/ai/skills/stacks-deploy/SKILL.md +102 -0
- package/ai/skills/stacks-design-brutalist/SKILL.md +110 -0
- package/ai/skills/stacks-design-minimalist/SKILL.md +115 -0
- package/ai/skills/stacks-design-output/SKILL.md +55 -0
- package/ai/skills/stacks-design-soft/SKILL.md +156 -0
- package/ai/skills/stacks-design-taste/SKILL.md +1012 -0
- package/ai/skills/stacks-design-taste-codex/SKILL.md +294 -0
- package/ai/skills/stacks-desktop/SKILL.md +78 -0
- package/ai/skills/stacks-development/SKILL.md +298 -0
- package/ai/skills/stacks-dns/SKILL.md +149 -0
- package/ai/skills/stacks-docs/SKILL.md +109 -0
- package/ai/skills/stacks-email/SKILL.md +236 -0
- package/ai/skills/stacks-enums/SKILL.md +100 -0
- package/ai/skills/stacks-env/SKILL.md +181 -0
- package/ai/skills/stacks-error-handling/SKILL.md +137 -0
- package/ai/skills/stacks-events/SKILL.md +228 -0
- package/ai/skills/stacks-faker/SKILL.md +158 -0
- package/ai/skills/stacks-git/SKILL.md +158 -0
- package/ai/skills/stacks-guard/SKILL.md +98 -0
- package/ai/skills/stacks-health/SKILL.md +75 -0
- package/ai/skills/stacks-http/SKILL.md +316 -0
- package/ai/skills/stacks-i18n/SKILL.md +198 -0
- package/ai/skills/stacks-image-to-code/SKILL.md +629 -0
- package/ai/skills/stacks-imagegen-mobile/SKILL.md +1474 -0
- package/ai/skills/stacks-imagegen-web/SKILL.md +998 -0
- package/ai/skills/stacks-investigate/SKILL.md +119 -0
- package/ai/skills/stacks-jobs/SKILL.md +136 -0
- package/ai/skills/stacks-lint/SKILL.md +184 -0
- package/ai/skills/stacks-listeners/SKILL.md +99 -0
- package/ai/skills/stacks-logging/SKILL.md +78 -0
- package/ai/skills/stacks-mail/SKILL.md +120 -0
- package/ai/skills/stacks-middleware/SKILL.md +249 -0
- package/ai/skills/stacks-migrations/SKILL.md +105 -0
- package/ai/skills/stacks-models/SKILL.md +267 -0
- package/ai/skills/stacks-new-feature/SKILL.md +206 -0
- package/ai/skills/stacks-notifications/SKILL.md +210 -0
- package/ai/skills/stacks-objects/SKILL.md +205 -0
- package/ai/skills/stacks-office-hours/SKILL.md +134 -0
- package/ai/skills/stacks-orm/SKILL.md +342 -0
- package/ai/skills/stacks-path/SKILL.md +118 -0
- package/ai/skills/stacks-payments/SKILL.md +373 -0
- package/ai/skills/stacks-plan-review/SKILL.md +152 -0
- package/ai/skills/stacks-plugins/SKILL.md +149 -0
- package/ai/skills/stacks-push/SKILL.md +376 -0
- package/ai/skills/stacks-query-builder/SKILL.md +128 -0
- package/ai/skills/stacks-queue/SKILL.md +559 -0
- package/ai/skills/stacks-realtime/SKILL.md +442 -0
- package/ai/skills/stacks-redesign/SKILL.md +182 -0
- package/ai/skills/stacks-registry/SKILL.md +58 -0
- package/ai/skills/stacks-repl/SKILL.md +80 -0
- package/ai/skills/stacks-retro/SKILL.md +122 -0
- package/ai/skills/stacks-review/SKILL.md +135 -0
- package/ai/skills/stacks-router/SKILL.md +231 -0
- package/ai/skills/stacks-routes/SKILL.md +105 -0
- package/ai/skills/stacks-scaffolding/SKILL.md +162 -0
- package/ai/skills/stacks-scheduler/SKILL.md +363 -0
- package/ai/skills/stacks-search-engine/SKILL.md +112 -0
- package/ai/skills/stacks-security/SKILL.md +129 -0
- package/ai/skills/stacks-security-audit/SKILL.md +128 -0
- package/ai/skills/stacks-server/SKILL.md +458 -0
- package/ai/skills/stacks-shell/SKILL.md +62 -0
- package/ai/skills/stacks-slug/SKILL.md +87 -0
- package/ai/skills/stacks-sms/SKILL.md +385 -0
- package/ai/skills/stacks-socials/SKILL.md +303 -0
- package/ai/skills/stacks-storage/SKILL.md +576 -0
- package/ai/skills/stacks-strings/SKILL.md +416 -0
- package/ai/skills/stacks-stx/SKILL.md +176 -0
- package/ai/skills/stacks-technical-diagrams/LICENSE +22 -0
- package/ai/skills/stacks-technical-diagrams/SKILL.md +290 -0
- package/ai/skills/stacks-technical-diagrams/agents/openai.yaml +4 -0
- package/ai/skills/stacks-technical-diagrams/assets/template.html +1252 -0
- package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +289 -0
- package/ai/skills/stacks-technical-diagrams/bunfig.toml +1 -0
- package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +64 -0
- package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +88 -0
- package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +75 -0
- package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +70 -0
- package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +41 -0
- package/ai/skills/stacks-technical-diagrams/renderers/architecture/grid.mjs +62 -0
- package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +371 -0
- package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +82 -0
- package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +295 -0
- package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +91 -0
- package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +384 -0
- package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +84 -0
- package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +257 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +42 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +13 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +230 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/layout-report.mjs +40 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +88 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +40 -0
- package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +103 -0
- package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +481 -0
- package/ai/skills/stacks-technical-diagrams/schemas/README.md +68 -0
- package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +121 -0
- package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +43 -0
- package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +221 -0
- package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +241 -0
- package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +186 -0
- package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +332 -0
- package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +297 -0
- package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +28 -0
- package/ai/skills/stacks-testing/SKILL.md +129 -0
- package/ai/skills/stacks-tunnel/SKILL.md +125 -0
- package/ai/skills/stacks-types/SKILL.md +178 -0
- package/ai/skills/stacks-ui/SKILL.md +246 -0
- package/ai/skills/stacks-utils/SKILL.md +444 -0
- package/ai/skills/stacks-validation/SKILL.md +371 -0
- package/ai/skills/stacks-whois/SKILL.md +315 -0
- package/assets/world-map-data.ts +121 -0
- package/bootstrap.ts +114 -0
- package/docs/api-examples.md +46 -0
- package/docs/index.md +24 -0
- package/docs/markdown-examples.md +87 -0
- package/functions/analytics.ts +97 -0
- package/functions/api.ts +143 -0
- package/functions/auth.ts +208 -0
- package/functions/billing/payments.ts +149 -0
- package/functions/blog.ts +120 -0
- package/functions/cms/categorizables.ts +107 -0
- package/functions/cms/pages.ts +108 -0
- package/functions/cms/posts.ts +100 -0
- package/functions/cms/taggables.ts +102 -0
- package/functions/commerce/coupons.ts +128 -0
- package/functions/commerce/customers.ts +128 -0
- package/functions/commerce/gift-cards.ts +119 -0
- package/functions/commerce/orders.ts +112 -0
- package/functions/commerce/payments.ts +119 -0
- package/functions/commerce/products/categories.ts +123 -0
- package/functions/commerce/products/manufacturers.ts +123 -0
- package/functions/commerce/products/products.ts +123 -0
- package/functions/commerce/products/reviews.ts +123 -0
- package/functions/commerce/products/units.ts +123 -0
- package/functions/commerce/products/variants.ts +123 -0
- package/functions/commerce/shippings/delivery-routes.ts +123 -0
- package/functions/commerce/shippings/digital-deliveries.ts +119 -0
- package/functions/commerce/shippings/drivers.ts +123 -0
- package/functions/commerce/shippings/license-keys.ts +123 -0
- package/functions/commerce/shippings/shipping-methods.ts +123 -0
- package/functions/commerce/shippings/shipping-rates.ts +119 -0
- package/functions/commerce/shippings/shipping-zones.ts +119 -0
- package/functions/commerce/taxes.ts +119 -0
- package/functions/commerce/waitlist/products.ts +123 -0
- package/functions/commerce/waitlist/restaurants.ts +123 -0
- package/functions/craft.ts +107 -0
- package/functions/dashboard.ts +153 -0
- package/functions/inbox.ts +95 -0
- package/functions/jobs.ts +86 -0
- package/functions/monitoring/errors.ts +295 -0
- package/functions/queue.ts +111 -0
- package/functions/realtime/websockets.ts +96 -0
- package/functions/realtime.ts +52 -0
- package/functions/releases.ts +49 -0
- package/functions/search.ts +126 -0
- package/functions/toasts.ts +95 -0
- package/functions/uploads.ts +216 -0
- package/ide/.gitignore +4 -0
- package/ide/cursor/rules/auto-imports.mdc +574 -0
- package/ide/cursor/rules/code-style.mdc +12 -0
- package/ide/cursor/rules/documentation.mdc +9 -0
- package/ide/cursor/rules/error-handling-validation.mdc +11 -0
- package/ide/cursor/rules/key-conventions.mdc +11 -0
- package/ide/cursor/rules/project-structure.mdc +169 -0
- package/ide/cursor/rules/readme.mdc +318 -0
- package/ide/cursor/rules/syntax-formatting.mdc +9 -0
- package/ide/cursor/rules/testing.mdc +10 -0
- package/ide/cursor/rules/typescript.mdc +9 -0
- package/ide/cursor/rules/ui-styling.mdc +0 -0
- package/ide/dictionary.txt +397 -0
- package/ide/jetbrains/.fleet/settings.json +4 -0
- package/ide/jetbrains/.idea/.gitignore +5 -0
- package/ide/jetbrains/.idea/.name +1 -0
- package/ide/jetbrains/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/ide/jetbrains/.idea/jsLinters/eslint.xml +7 -0
- package/ide/jetbrains/.idea/modules.xml +8 -0
- package/ide/jetbrains/.idea/stacks.iml +22 -0
- package/ide/jetbrains/.idea/vcs.xml +6 -0
- package/ide/jetbrains/.idea/webResources.xml +14 -0
- package/ide/jetbrains/IntelliJ IDEA Global Settings +0 -0
- package/ide/jetbrains/installed.txt +4 -0
- package/ide/jetbrains/options/github-copilot.xml +5 -0
- package/ide/jetbrains/options/nodejs.xml +7 -0
- package/ide/jetbrains/options/templates.xml +124 -0
- package/ide/jetbrains/templates/JavaScript.xml +6 -0
- package/ide/jetbrains/templates/Stacks.xml +8 -0
- package/ide/vscode/.vscode/README.md +23 -0
- package/ide/vscode/.vscode/extensions.json +5 -0
- package/ide/vscode/.vscode/settings.json +176 -0
- package/ide/vscode/.vscode/stacks.code-snippets +181 -0
- package/ide/vscode/LICENSE.md +21 -0
- package/ide/vscode/README.md +36 -0
- package/ide/vscode/logo.png +0 -0
- package/ide/vscode/package.json +79 -0
- package/ide/zed/.zed/settings.json +139 -0
- package/lang/README.md +28 -0
- package/lang/de.yml +14 -0
- package/lang/en.yml +15 -0
- package/package.json +19 -3
- package/project/bootstrap +161 -0
- package/project/buddy +175 -0
- package/project/storage/framework/tsconfig.app.json +50 -0
- package/project/storage/framework/tsconfig.base.json +58 -0
- package/routes/core.ts +78 -0
- package/routes/dashboard-api.ts +233 -0
- package/routes/dashboard.ts +739 -0
- package/routes/email.ts +129 -0
- package/stores/[name].stx +16 -0
- package/stores/git.ts +71 -0
- package/stores/payment.ts +474 -0
- package/stores/queue.ts +43 -0
- package/stores/user.ts +29 -0
- package/stx-components-plugin.ts +25 -0
- package/styles/design-tokens.css +597 -0
- package/types/dashboard.ts +70 -0
- package/types/defaults.ts +438 -0
- package/vcs/github/CODE_OF_CONDUCT.md +83 -0
- package/vcs/github/CONTRIBUTING.md +187 -0
- package/vcs/github/EXPLANATIONS.md +41 -0
- package/vcs/github/FUNDING.yml +1 -0
- package/vcs/github/ISSUE_TEMPLATE/bug_report.yml +49 -0
- package/vcs/github/ISSUE_TEMPLATE/config.yml +10 -0
- package/vcs/github/ISSUE_TEMPLATE/feature_request.yml +44 -0
- package/vcs/github/SECURITY.md +70 -0
- package/vcs/github/renovate.json +5 -0
- package/vcs/github/stale.yml +14 -0
- package/vcs/github/workflows/README.md +16 -0
- package/vcs/github/workflows/ci.yml +114 -0
- package/vcs/github/workflows/export-size.yml +25 -0
- package/vcs/github/workflows/labeler.yml +13 -0
- package/vcs/github/workflows/release.yml +219 -0
- package/views/[...all].stx +8 -0
- package/views/about.md +3 -0
- package/views/auth/login.stx +1 -0
- package/views/auth/signup.stx +85 -0
- package/views/dashboard/.discovered-models.json +542 -0
- package/views/dashboard/AUDIT.md +904 -0
- package/views/dashboard/[...all].stx +89 -0
- package/views/dashboard/access-tokens/index.stx +177 -0
- package/views/dashboard/actions/index.stx +129 -0
- package/views/dashboard/analytics/blog/index.stx +356 -0
- package/views/dashboard/analytics/browsers/index.stx +263 -0
- package/views/dashboard/analytics/commerce/sales/index.stx +329 -0
- package/views/dashboard/analytics/commerce/web/index.stx +379 -0
- package/views/dashboard/analytics/countries/index.stx +289 -0
- package/views/dashboard/analytics/devices/index.stx +305 -0
- package/views/dashboard/analytics/events/index.stx +536 -0
- package/views/dashboard/analytics/index.stx +88 -0
- package/views/dashboard/analytics/marketing/index.stx +315 -0
- package/views/dashboard/analytics/pages/index.stx +271 -0
- package/views/dashboard/analytics/referrers/index.stx +319 -0
- package/views/dashboard/analytics/web/index.stx +356 -0
- package/views/dashboard/buddy/index.stx +198 -0
- package/views/dashboard/ci/index.stx +462 -0
- package/views/dashboard/cloud/index.stx +452 -0
- package/views/dashboard/commands/index.stx +295 -0
- package/views/dashboard/commerce/categories/index.stx +406 -0
- package/views/dashboard/commerce/coupons/index.stx +485 -0
- package/views/dashboard/commerce/customers/index.stx +410 -0
- package/views/dashboard/commerce/dashboard/index.stx +287 -0
- package/views/dashboard/commerce/delivery/delivery-routes.stx +305 -0
- package/views/dashboard/commerce/delivery/digital-delivery.stx +332 -0
- package/views/dashboard/commerce/delivery/drivers.stx +397 -0
- package/views/dashboard/commerce/delivery/index.stx +18 -0
- package/views/dashboard/commerce/delivery/license-keys.stx +380 -0
- package/views/dashboard/commerce/delivery/shipping-methods.stx +346 -0
- package/views/dashboard/commerce/delivery/shipping-rates.stx +338 -0
- package/views/dashboard/commerce/delivery/shipping-zones.stx +302 -0
- package/views/dashboard/commerce/delivery/tabs.ts +25 -0
- package/views/dashboard/commerce/gift-cards/index.stx +561 -0
- package/views/dashboard/commerce/manufacturers/index.stx +308 -0
- package/views/dashboard/commerce/orders/index.stx +247 -0
- package/views/dashboard/commerce/payments/index.stx +535 -0
- package/views/dashboard/commerce/pos/index.stx +653 -0
- package/views/dashboard/commerce/printers/devices/index.stx +273 -0
- package/views/dashboard/commerce/printers/receipts/index.stx +308 -0
- package/views/dashboard/commerce/products/detail.stx +563 -0
- package/views/dashboard/commerce/products/index.stx +370 -0
- package/views/dashboard/commerce/reviews/index.stx +656 -0
- package/views/dashboard/commerce/reviews/index.ts +0 -0
- package/views/dashboard/commerce/taxes/index.stx +377 -0
- package/views/dashboard/commerce/units/index.stx +317 -0
- package/views/dashboard/commerce/variants/index.stx +319 -0
- package/views/dashboard/commerce/waitlist/products.stx +942 -0
- package/views/dashboard/commerce/waitlist/restaurant.stx +1024 -0
- package/views/dashboard/components/index.stx +330 -0
- package/views/dashboard/composables/index.ts +19 -0
- package/views/dashboard/composables/useChart.ts +77 -0
- package/views/dashboard/composables/useNavigation.ts +99 -0
- package/views/dashboard/composables/useRole.ts +58 -0
- package/views/dashboard/composables/useTheme.ts +74 -0
- package/views/dashboard/content/authors/index.stx +171 -0
- package/views/dashboard/content/blog/index.stx +493 -0
- package/views/dashboard/content/categories/index.stx +169 -0
- package/views/dashboard/content/comments/index.stx +189 -0
- package/views/dashboard/content/dashboard.stx +383 -0
- package/views/dashboard/content/files/index.stx +706 -0
- package/views/dashboard/content/pages/index.stx +174 -0
- package/views/dashboard/content/posts/index.stx +269 -0
- package/views/dashboard/content/seo/index.stx +240 -0
- package/views/dashboard/content/tags/index.stx +167 -0
- package/views/dashboard/data/activity/index.stx +261 -0
- package/views/dashboard/data/dashboard/index.stx +116 -0
- package/views/dashboard/data/subscribers.stx +270 -0
- package/views/dashboard/data/teams.stx +275 -0
- package/views/dashboard/data/users.stx +279 -0
- package/views/dashboard/dependencies/index.stx +178 -0
- package/views/dashboard/deployments/[id].stx +234 -0
- package/views/dashboard/deployments/index.stx +9 -0
- package/views/dashboard/dns/index.stx +163 -0
- package/views/dashboard/environment/index.stx +283 -0
- package/views/dashboard/errors/index.stx +274 -0
- package/views/dashboard/forgot-password.stx +57 -0
- package/views/dashboard/functions/index.stx +159 -0
- package/views/dashboard/health/index.stx +248 -0
- package/views/dashboard/inbox/activity.stx +350 -0
- package/views/dashboard/inbox/index.stx +452 -0
- package/views/dashboard/inbox/settings.stx +290 -0
- package/views/dashboard/index.stx +252 -0
- package/views/dashboard/insights/index.stx +529 -0
- package/views/dashboard/jobs/[id].stx +430 -0
- package/views/dashboard/jobs/history.stx +222 -0
- package/views/dashboard/jobs/index.stx +214 -0
- package/views/dashboard/kanban/[id].stx +899 -0
- package/views/dashboard/kanban/index.stx +260 -0
- package/views/dashboard/layouts/default.stx +565 -0
- package/views/dashboard/layouts/guest.stx +10 -0
- package/views/dashboard/login.stx +48 -0
- package/views/dashboard/logs/index.stx +540 -0
- package/views/dashboard/mailboxes/index.stx +149 -0
- package/views/dashboard/management/permissions/index.stx +490 -0
- package/views/dashboard/marketing/campaigns/index.stx +604 -0
- package/views/dashboard/marketing/lists/index.stx +514 -0
- package/views/dashboard/marketing/reviews/index.stx +475 -0
- package/views/dashboard/marketing/social-posts/index.stx +560 -0
- package/views/dashboard/models/[model].stx +195 -0
- package/views/dashboard/models/index.stx +159 -0
- package/views/dashboard/monitoring/errors/index.stx +415 -0
- package/views/dashboard/notifications/dashboard.stx +383 -0
- package/views/dashboard/notifications/email.stx +336 -0
- package/views/dashboard/notifications/history.stx +373 -0
- package/views/dashboard/notifications/sms.stx +332 -0
- package/views/dashboard/packages/index.stx +195 -0
- package/views/dashboard/queries/[id].stx +381 -0
- package/views/dashboard/queries/history.stx +227 -0
- package/views/dashboard/queries/index.stx +335 -0
- package/views/dashboard/queries/slow.stx +318 -0
- package/views/dashboard/queue/index.stx +427 -0
- package/views/dashboard/realtime/index.stx +429 -0
- package/views/dashboard/register.stx +46 -0
- package/views/dashboard/releases/index.stx +389 -0
- package/views/dashboard/requests/index.stx +462 -0
- package/views/dashboard/serverless/index.stx +475 -0
- package/views/dashboard/servers/[id].stx +299 -0
- package/views/dashboard/servers/index.stx +483 -0
- package/views/dashboard/settings/billing.stx +50 -0
- package/views/dashboard/settings/index.stx +256 -0
- package/views/dashboard/settings/mail.stx +252 -0
- package/views/dashboard/stores/auth.ts +123 -0
- package/views/dashboard/stores/ci.ts +342 -0
- package/views/dashboard/stores/index.ts +129 -0
- package/views/dashboard/stores/kanban.ts +723 -0
- package/views/dashboard/stores/rbac.ts +350 -0
- package/views/dashboard/stx.config.ts +34 -0
- package/views/dashboard/teams/[id].stx +170 -0
- package/views/forum/category.stx +92 -0
- package/views/forum/index.stx +209 -0
- package/views/forum/new-topic.stx +113 -0
- package/views/forum/profile.stx +1 -0
- package/views/forum/topic.stx +143 -0
- package/views/hello-world/[name].stx +47 -0
- package/views/index.stx +21 -0
- package/views/system-tray/index.stx +147 -0
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
@title('Dashboard - Commerce Coupons')
|
|
5
|
+
<div class="p-6 space-y-6">
|
|
6
|
+
|
|
7
|
+
<PageHeader title="Coupons" description="Manage discount coupons for your store">
|
|
8
|
+
<template #actions>
|
|
9
|
+
<Button variant="primary" @click="openAddModal()">
|
|
10
|
+
<div class="h-4 w-4 i-hugeicons-plus-sign"></div>
|
|
11
|
+
Add Coupon
|
|
12
|
+
</Button>
|
|
13
|
+
</template>
|
|
14
|
+
</PageHeader>
|
|
15
|
+
|
|
16
|
+
<!-- Time range selector -->
|
|
17
|
+
<div class="flex items-center justify-between mt-4">
|
|
18
|
+
<p class="text-gray-500 text-sm dark:text-neutral-400">
|
|
19
|
+
Overview of your coupon performance
|
|
20
|
+
</p>
|
|
21
|
+
<div class="relative">
|
|
22
|
+
<select x-model="timeRange" class="block pl-3 pr-10 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600">
|
|
23
|
+
<template :for="range in timeRanges">
|
|
24
|
+
<option :value="range">{{ range }}</option>
|
|
25
|
+
</template>
|
|
26
|
+
</select>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<!-- Stats -->
|
|
31
|
+
<dl class="grid gap-5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 mt-5">
|
|
32
|
+
<div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
33
|
+
<dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Active Coupons</dt>
|
|
34
|
+
<dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">{{ couponStats().activeCoupons }}</dd>
|
|
35
|
+
<dd class="flex items-center mt-2 text-green-600 text-sm dark:text-green-400">
|
|
36
|
+
<div class="mr-1 h-4 w-4 i-hugeicons-analytics-up"></div>
|
|
37
|
+
<span>1 more than last month</span>
|
|
38
|
+
</dd>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
42
|
+
<dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Total Redemptions</dt>
|
|
43
|
+
<dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">{{ couponStats().totalRedemptions.toLocaleString() }}</dd>
|
|
44
|
+
<dd class="flex items-center mt-2 text-green-600 text-sm dark:text-green-400">
|
|
45
|
+
<div class="mr-1 h-4 w-4 i-hugeicons-analytics-up"></div>
|
|
46
|
+
<span>12.5% increase</span>
|
|
47
|
+
</dd>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
51
|
+
<dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Total Discount Amount</dt>
|
|
52
|
+
<dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">${{ couponStats().totalDiscountAmount.toLocaleString() }}</dd>
|
|
53
|
+
<dd class="flex items-center mt-2 text-green-600 text-sm dark:text-green-400">
|
|
54
|
+
<div class="mr-1 h-4 w-4 i-hugeicons-analytics-up"></div>
|
|
55
|
+
<span>8.2% increase</span>
|
|
56
|
+
</dd>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div class="overflow-hidden px-4 py-5 sm:p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
60
|
+
<dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-300">Avg. Discount per Order</dt>
|
|
61
|
+
<dd class="mt-1 font-semibold text-3xl text-gray-900 tracking-tight dark:text-white">${{ couponStats().avgDiscountPerOrder }}</dd>
|
|
62
|
+
<dd class="flex items-center mt-2 text-red-600 text-sm dark:text-red-400">
|
|
63
|
+
<div class="mr-1 h-4 w-4 i-hugeicons-analytics-down"></div>
|
|
64
|
+
<span>3.2% decrease</span>
|
|
65
|
+
</dd>
|
|
66
|
+
</div>
|
|
67
|
+
</dl>
|
|
68
|
+
|
|
69
|
+
<!-- Charts -->
|
|
70
|
+
<div class="grid gap-5 grid-cols-1 lg:grid-cols-2 mt-8">
|
|
71
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
72
|
+
<div class="p-6">
|
|
73
|
+
<h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Coupon Redemptions</h3>
|
|
74
|
+
<div class="mt-2 h-80">
|
|
75
|
+
<canvas id="chart_1"></canvas>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
81
|
+
<div class="p-6">
|
|
82
|
+
<h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Discount Amount</h3>
|
|
83
|
+
<div class="mt-2 h-80">
|
|
84
|
+
<canvas id="chart_2"></canvas>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<!-- Filters -->
|
|
91
|
+
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between mt-8">
|
|
92
|
+
<SearchFilter
|
|
93
|
+
placeholder="Search coupons..."
|
|
94
|
+
@search="handleSearch"
|
|
95
|
+
class="w-full md:w-96"
|
|
96
|
+
/>
|
|
97
|
+
<div class="flex flex-col gap-4 sm:flex-row">
|
|
98
|
+
<select
|
|
99
|
+
x-model="statusFilter"
|
|
100
|
+
class="block pl-3 pr-10 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600 focus:ring-inset"
|
|
101
|
+
>
|
|
102
|
+
<template :for="status in statuses">
|
|
103
|
+
<option :value="status">{{ status }}</option>
|
|
104
|
+
</template>
|
|
105
|
+
</select>
|
|
106
|
+
|
|
107
|
+
<select
|
|
108
|
+
x-model.number="itemsPerPage"
|
|
109
|
+
class="block pl-3 pr-10 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600 focus:ring-inset"
|
|
110
|
+
>
|
|
111
|
+
<option value="5">5 per page</option>
|
|
112
|
+
<option value="10">10 per page</option>
|
|
113
|
+
<option value="25">25 per page</option>
|
|
114
|
+
<option value="50">50 per page</option>
|
|
115
|
+
</select>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<!-- Coupons table -->
|
|
120
|
+
<div class="flow-root mt-6">
|
|
121
|
+
<div class="sm:flex sm:items-center sm:justify-between mb-4">
|
|
122
|
+
<h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">All Coupons</h3>
|
|
123
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">
|
|
124
|
+
A list of all the coupons in your store including their code, value, and status.
|
|
125
|
+
</p>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="overflow-x-auto -mx-4 -my-2 sm:-mx-6 lg:-mx-8">
|
|
128
|
+
<div class="inline-block py-2 sm:px-6 lg:px-8 min-w-full align-middle">
|
|
129
|
+
<div class="overflow-hidden ring-1 ring-black ring-opacity-5 sm:rounded-lg shadow">
|
|
130
|
+
<CouponsTable
|
|
131
|
+
:coupons="paginatedCoupons"
|
|
132
|
+
:searchQuery="searchQuery"
|
|
133
|
+
:statusFilter="statusFilter"
|
|
134
|
+
:sortBy="sortBy"
|
|
135
|
+
:sortOrder="sortOrder"
|
|
136
|
+
:currentPage="currentPage"
|
|
137
|
+
:itemsPerPage="itemsPerPage"
|
|
138
|
+
:statuses="statuses"
|
|
139
|
+
@toggle-sort="toggleSort"
|
|
140
|
+
@change-page="handlePageChange"
|
|
141
|
+
@previous-page="handlePrevPage"
|
|
142
|
+
@next-page="handleNextPage"
|
|
143
|
+
@view-coupon="viewCoupon"
|
|
144
|
+
@edit-coupon="editCoupon"
|
|
145
|
+
@delete-coupon="removeCoupon"
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<!-- Pagination -->
|
|
153
|
+
<div class="px-4 py-3 bg-gray-50 dark:bg-neutral-900 border-gray-200 border-t dark:border-neutral-700">
|
|
154
|
+
<Pagination
|
|
155
|
+
:currentPage="currentPage"
|
|
156
|
+
:totalItems="totalItems"
|
|
157
|
+
:itemsPerPage="itemsPerPage"
|
|
158
|
+
@prev="handlePrevPage"
|
|
159
|
+
@next="handleNextPage"
|
|
160
|
+
@page="handlePageChange"
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<!-- Coupons Form Modal -->
|
|
165
|
+
<CouponsForm
|
|
166
|
+
:show="showFormModal"
|
|
167
|
+
:mode="formMode"
|
|
168
|
+
:coupon="couponToEdit"
|
|
169
|
+
@close="closeFormModal"
|
|
170
|
+
@submit="handleFormSubmit"
|
|
171
|
+
/>
|
|
172
|
+
|
|
173
|
+
<!-- Delete Coupon Confirmation Modal -->
|
|
174
|
+
<div :show="showDeleteModal" class="fixed inset-0 overflow-y-auto z-10">
|
|
175
|
+
<div class="flex items-end justify-center sm:items-center p-4 sm:p-0 min-h-full text-center">
|
|
176
|
+
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="closeDeleteModal()"></div>
|
|
177
|
+
|
|
178
|
+
<div class="overflow-hidden relative pb-4 pt-5 px-4 sm:my-8 sm:p-6 sm:max-w-lg sm:w-full text-left bg-white dark:bg-neutral-800 rounded-lg shadow-xl transition-all transform">
|
|
179
|
+
<div>
|
|
180
|
+
<div class="flex items-center justify-center mx-auto h-12 w-12 bg-red-100 dark:bg-red-900 rounded-full">
|
|
181
|
+
<div class="h-6 w-6 text-red-600 dark:text-red-400 i-hugeicons-alert-triangle"></div>
|
|
182
|
+
</div>
|
|
183
|
+
<div class="mt-3 sm:mt-5 text-center">
|
|
184
|
+
<h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Delete Coupon</h3>
|
|
185
|
+
<div class="mt-2">
|
|
186
|
+
<p class="text-gray-500 text-sm dark:text-neutral-400">
|
|
187
|
+
Are you sure you want to delete <strong>{{ couponToDelete()?.code }}</strong>? This action cannot be undone.
|
|
188
|
+
</p>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="sm:grid sm:gap-3 sm:grid-cols-2 sm:grid-flow-row-dense mt-5 sm:mt-6">
|
|
193
|
+
<button
|
|
194
|
+
type="button"
|
|
195
|
+
@click="confirmDelete()"
|
|
196
|
+
class="inline-flex justify-center sm:col-start-2 px-3 py-2 w-full font-semibold text-sm text-white bg-red-600 hover:bg-red-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600 shadow-sm"
|
|
197
|
+
>
|
|
198
|
+
Delete Coupon
|
|
199
|
+
</button>
|
|
200
|
+
<button
|
|
201
|
+
type="button"
|
|
202
|
+
@click="closeDeleteModal()"
|
|
203
|
+
class="inline-flex justify-center sm:col-start-1 mt-3 px-3 py-2 sm:mt-0 w-full font-semibold text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-600 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 shadow-sm"
|
|
204
|
+
>
|
|
205
|
+
Cancel
|
|
206
|
+
</button>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<script client>
|
|
214
|
+
import { useCoupons } from '../../../../functions/commerce/coupons'
|
|
215
|
+
import type { Coupons, NewCoupon } from '../../../../types/defaults'
|
|
216
|
+
// Filter options — moved out of the former `<script server>` block
|
|
217
|
+
// (stacksjs/stacks#1838).
|
|
218
|
+
const statuses = ['all', 'Active', 'Expired', 'Scheduled'] as const
|
|
219
|
+
const timeRanges = ['Today', 'Last 7 days', 'Last 30 days', 'Last 90 days', 'Last year', 'All time'] as const
|
|
220
|
+
|
|
221
|
+
const { coupons, createCoupon, fetchCoupons, deleteCoupon, updateCoupon } = useCoupons()
|
|
222
|
+
|
|
223
|
+
const timeRange = state<string>('Last 30 days')
|
|
224
|
+
const searchQuery = state('')
|
|
225
|
+
const sortBy = state<'code' | 'discount_value' | 'usage_count' | 'end_date'>('code')
|
|
226
|
+
const sortOrder = state<'asc' | 'desc'>('asc')
|
|
227
|
+
const statusFilter = state<string>('all')
|
|
228
|
+
const currentPage = state(1)
|
|
229
|
+
const itemsPerPage = state(10)
|
|
230
|
+
|
|
231
|
+
const showFormModal = state(false)
|
|
232
|
+
const formMode = state<'add' | 'edit'>('add')
|
|
233
|
+
const couponToEdit = state<Coupons | null>(null)
|
|
234
|
+
const showDeleteModal = state(false)
|
|
235
|
+
const couponToDelete = state<Coupons | null>(null)
|
|
236
|
+
|
|
237
|
+
// Static chart options + monthly sample data — keep them in client
|
|
238
|
+
// scope so derived chart data can pick up signal-driven inputs later
|
|
239
|
+
// (timeRange, etc. when wired through to a real source).
|
|
240
|
+
const chartOptions = {
|
|
241
|
+
responsive: true,
|
|
242
|
+
maintainAspectRatio: false,
|
|
243
|
+
plugins: { legend: { display: false } },
|
|
244
|
+
scales: {
|
|
245
|
+
y: { beginAtZero: true, grid: { display: true, color: 'rgba(0, 0, 0, 0.05)' } },
|
|
246
|
+
x: { grid: { display: false } },
|
|
247
|
+
},
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function monthlyChartData(): { couponUsageData: unknown, discountAmountData: unknown } {
|
|
251
|
+
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
252
|
+
const redemptionsData = [120, 145, 132, 160, 185, 170, 210, 230, 245, 220, 260, 280]
|
|
253
|
+
const discountData = [2500, 3200, 2800, 3600, 4100, 3800, 4500, 5200, 5800, 5400, 6200, 6800]
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
couponUsageData: {
|
|
257
|
+
labels: months,
|
|
258
|
+
datasets: [{
|
|
259
|
+
label: 'Coupon Redemptions',
|
|
260
|
+
backgroundColor: 'rgba(59, 130, 246, 0.2)',
|
|
261
|
+
borderColor: 'rgba(59, 130, 246, 1)',
|
|
262
|
+
borderWidth: 2,
|
|
263
|
+
pointBackgroundColor: 'rgba(59, 130, 246, 1)',
|
|
264
|
+
pointBorderColor: '#fff',
|
|
265
|
+
pointHoverBackgroundColor: '#fff',
|
|
266
|
+
pointHoverBorderColor: 'rgba(59, 130, 246, 1)',
|
|
267
|
+
fill: true,
|
|
268
|
+
tension: 0.4,
|
|
269
|
+
data: redemptionsData,
|
|
270
|
+
}],
|
|
271
|
+
},
|
|
272
|
+
discountAmountData: {
|
|
273
|
+
labels: months,
|
|
274
|
+
datasets: [{
|
|
275
|
+
label: 'Discount Amount',
|
|
276
|
+
backgroundColor: 'rgba(16, 185, 129, 0.8)',
|
|
277
|
+
borderColor: 'rgba(16, 185, 129, 1)',
|
|
278
|
+
borderWidth: 1,
|
|
279
|
+
borderRadius: 4,
|
|
280
|
+
data: discountData,
|
|
281
|
+
}],
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function couponStats(): { activeCoupons: number, totalRedemptions: number, totalDiscountAmount: string, avgDiscountPerOrder: string } {
|
|
287
|
+
const list = coupons.value as Coupons[]
|
|
288
|
+
const activeCoupons = list.filter(c => c.status === 'Active').length
|
|
289
|
+
const totalRedemptions = list.reduce((sum, c) => sum + (c.usage_count || 0), 0)
|
|
290
|
+
|
|
291
|
+
const totalDiscountAmount = list.reduce((sum, c) => {
|
|
292
|
+
const avgOrderValue = 100
|
|
293
|
+
const discountPerUse = c.discount_type === 'Percentage'
|
|
294
|
+
? avgOrderValue * (c.discount_value / 100)
|
|
295
|
+
: c.discount_value
|
|
296
|
+
return sum + (discountPerUse * (c.usage_count || 0))
|
|
297
|
+
}, 0)
|
|
298
|
+
|
|
299
|
+
const avgDiscountPerOrder = totalRedemptions > 0
|
|
300
|
+
? (totalDiscountAmount / totalRedemptions).toFixed(2)
|
|
301
|
+
: '0.00'
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
activeCoupons,
|
|
305
|
+
totalRedemptions,
|
|
306
|
+
totalDiscountAmount: totalDiscountAmount.toFixed(0),
|
|
307
|
+
avgDiscountPerOrder,
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const filteredCoupons = derived(() => {
|
|
312
|
+
const q = searchQuery().toLowerCase()
|
|
313
|
+
const filter = statusFilter()
|
|
314
|
+
const by = sortBy()
|
|
315
|
+
const dir = sortOrder()
|
|
316
|
+
|
|
317
|
+
return (coupons.value as Coupons[])
|
|
318
|
+
.filter((c) => {
|
|
319
|
+
const matchesSearch = c.code?.toLowerCase().includes(q) || false
|
|
320
|
+
const matchesStatus = filter === 'all' || c.status === filter
|
|
321
|
+
return matchesSearch && matchesStatus
|
|
322
|
+
})
|
|
323
|
+
.sort((a, b) => {
|
|
324
|
+
let cmp = 0
|
|
325
|
+
if (by === 'code')
|
|
326
|
+
cmp = a.code.localeCompare(b.code)
|
|
327
|
+
else if (by === 'discount_value')
|
|
328
|
+
cmp = a.discount_value - b.discount_value
|
|
329
|
+
else if (by === 'usage_count')
|
|
330
|
+
cmp = (a.usage_count || 0) - (b.usage_count || 0)
|
|
331
|
+
else if (by === 'end_date')
|
|
332
|
+
cmp = new Date(a.end_date).getTime() - new Date(b.end_date).getTime()
|
|
333
|
+
return dir === 'asc' ? cmp : -cmp
|
|
334
|
+
})
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
const paginatedCoupons = derived(() => {
|
|
338
|
+
const start = (currentPage() - 1) * itemsPerPage()
|
|
339
|
+
return filteredCoupons().slice(start, start + itemsPerPage())
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
const totalItems = derived(() => filteredCoupons().length)
|
|
343
|
+
|
|
344
|
+
function handleSearch(query: string): void {
|
|
345
|
+
searchQuery.set(query)
|
|
346
|
+
currentPage.set(1)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function handlePrevPage(): void {
|
|
350
|
+
if (currentPage() > 1)
|
|
351
|
+
currentPage.update(p => p - 1)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function handleNextPage(): void {
|
|
355
|
+
const totalPages = Math.ceil(totalItems() / itemsPerPage())
|
|
356
|
+
if (currentPage() < totalPages)
|
|
357
|
+
currentPage.update(p => p + 1)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function handlePageChange(page: number): void {
|
|
361
|
+
currentPage.set(page)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function toggleSort(column: 'code' | 'discount_value' | 'usage_count' | 'end_date'): void {
|
|
365
|
+
if (sortBy() === column) {
|
|
366
|
+
sortOrder.update(d => d === 'asc' ? 'desc' : 'asc')
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
sortBy.set(column)
|
|
370
|
+
sortOrder.set('asc')
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function openAddModal(): void {
|
|
375
|
+
formMode.set('add')
|
|
376
|
+
couponToEdit.set(null)
|
|
377
|
+
showFormModal.set(true)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function closeFormModal(): void {
|
|
381
|
+
showFormModal.set(false)
|
|
382
|
+
couponToEdit.set(null)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function editCoupon(coupon: Coupons): void {
|
|
386
|
+
formMode.set('edit')
|
|
387
|
+
couponToEdit.set({ ...coupon })
|
|
388
|
+
showFormModal.set(true)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function viewCoupon(coupon: Coupons): void {
|
|
392
|
+
navigate(`/dashboard/commerce/coupons/${coupon.id}`)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function removeCoupon(coupon: Coupons): void {
|
|
396
|
+
couponToDelete.set(coupon)
|
|
397
|
+
showDeleteModal.set(true)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function closeDeleteModal(): void {
|
|
401
|
+
showDeleteModal.set(false)
|
|
402
|
+
couponToDelete.set(null)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async function handleFormSubmit(couponData: NewCoupon): Promise<void> {
|
|
406
|
+
if (formMode() === 'add')
|
|
407
|
+
await addCoupon(couponData)
|
|
408
|
+
else
|
|
409
|
+
await doUpdateCoupon(couponData)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async function addCoupon(couponData: NewCoupon): Promise<void> {
|
|
413
|
+
const list = coupons.value as Coupons[]
|
|
414
|
+
const id = list.length > 0 ? Math.max(...list.map(c => c.id || 0)) + 1 : 1
|
|
415
|
+
const optimistic = { id, ...couponData, usage_count: 0 } as unknown as Coupons
|
|
416
|
+
coupons.value = [...list, optimistic]
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
await createCoupon(couponData as unknown as Coupons)
|
|
420
|
+
closeFormModal()
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
coupons.value = (coupons.value as Coupons[]).filter(c => c.id !== id)
|
|
424
|
+
// eslint-disable-next-line no-console
|
|
425
|
+
console.error('Failed to create coupon:', error)
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async function doUpdateCoupon(couponData: NewCoupon): Promise<void> {
|
|
430
|
+
const target = couponToEdit()
|
|
431
|
+
if (!target)
|
|
432
|
+
return
|
|
433
|
+
const couponId = target.id
|
|
434
|
+
const list = coupons.value as Coupons[]
|
|
435
|
+
coupons.value = list.map(c => c.id === couponId ? { ...c, ...couponData } : c)
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
await updateCoupon(couponId as number, couponData)
|
|
439
|
+
closeFormModal()
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
// eslint-disable-next-line no-console
|
|
443
|
+
console.error('Failed to update coupon:', error)
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async function confirmDelete(): Promise<void> {
|
|
448
|
+
const target = couponToDelete()
|
|
449
|
+
if (!target)
|
|
450
|
+
return
|
|
451
|
+
const couponId = target.id
|
|
452
|
+
const list = coupons.value as Coupons[]
|
|
453
|
+
coupons.value = list.filter(c => c.id !== couponId)
|
|
454
|
+
|
|
455
|
+
try {
|
|
456
|
+
await deleteCoupon(couponId as number)
|
|
457
|
+
closeDeleteModal()
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
coupons.value = [...coupons.value, target]
|
|
461
|
+
// eslint-disable-next-line no-console
|
|
462
|
+
console.error('Failed to delete coupon:', error)
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const { Chart, registerables } = await import('@stacksjs/charts')
|
|
467
|
+
Chart.register(...registerables)
|
|
468
|
+
|
|
469
|
+
const chartData = monthlyChartData()
|
|
470
|
+
const { destroyAll } = useCharts([
|
|
471
|
+
{ Chart, id: 'chart_1', type: 'line', data: chartData.couponUsageData, options: chartOptions },
|
|
472
|
+
{ Chart, id: 'chart_2', type: 'bar', data: chartData.discountAmountData, options: chartOptions },
|
|
473
|
+
])
|
|
474
|
+
|
|
475
|
+
onMount(async () => {
|
|
476
|
+
await fetchCoupons()
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
// Tear charts down on SPA navigation so chart.js doesn't leak canvases.
|
|
480
|
+
onDestroy(() => {
|
|
481
|
+
destroyAll()
|
|
482
|
+
})
|
|
483
|
+
</script>
|
|
484
|
+
|
|
485
|
+
@endsection
|