@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,317 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
<div class="sm:flex sm:items-center sm:justify-between">
|
|
5
|
+
<PageHeader title="Units" description="Manage measurement units for your products" />
|
|
6
|
+
<div class="mt-4 sm:mt-0">
|
|
7
|
+
<button
|
|
8
|
+
type="button"
|
|
9
|
+
@click="openAddModal()"
|
|
10
|
+
class="inline-flex items-center px-3 py-2 font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 shadow-sm"
|
|
11
|
+
>
|
|
12
|
+
<div class="mr-1 h-5 w-5 i-hugeicons-plus-sign"></div>
|
|
13
|
+
Add unit
|
|
14
|
+
</button>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Filters -->
|
|
19
|
+
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between mt-6">
|
|
20
|
+
<div class="relative max-w-sm">
|
|
21
|
+
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
|
22
|
+
<div class="h-5 w-5 text-gray-400 i-hugeicons-search-01"></div>
|
|
23
|
+
</div>
|
|
24
|
+
<input
|
|
25
|
+
x-model="searchQuery"
|
|
26
|
+
type="text"
|
|
27
|
+
class="block pl-10 pr-3 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:placeholder:text-neutral-500 dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600 focus:ring-inset"
|
|
28
|
+
placeholder="Search units..."
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="flex flex-col gap-4 sm:flex-row">
|
|
33
|
+
<select
|
|
34
|
+
x-model="typeFilter"
|
|
35
|
+
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"
|
|
36
|
+
>
|
|
37
|
+
<option value="all">All Types</option>
|
|
38
|
+
<template :for="type in unitTypes">
|
|
39
|
+
<option value="{{ type }}">{{ type }}</option>
|
|
40
|
+
</template>
|
|
41
|
+
</select>
|
|
42
|
+
|
|
43
|
+
<select
|
|
44
|
+
x-model="defaultFilter"
|
|
45
|
+
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"
|
|
46
|
+
>
|
|
47
|
+
<option value="all">All Units</option>
|
|
48
|
+
<option value="default">Default Units</option>
|
|
49
|
+
<option value="not-default">Non-Default Units</option>
|
|
50
|
+
</select>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<!-- Units table -->
|
|
55
|
+
<div class="flow-root mt-6">
|
|
56
|
+
<div class="overflow-x-auto -mx-4 -my-2 sm:-mx-6 lg:-mx-8">
|
|
57
|
+
<div class="inline-block py-2 sm:px-6 lg:px-8 min-w-full align-middle">
|
|
58
|
+
<div class="overflow-hidden ring-1 ring-black ring-opacity-5 sm:rounded-lg shadow">
|
|
59
|
+
<table class="min-w-full divide-gray-300 divide-y dark:divide-neutral-700">
|
|
60
|
+
<thead class="bg-gray-50 dark:bg-neutral-700">
|
|
61
|
+
<tr>
|
|
62
|
+
<th scope="col" class="pl-4 pr-3 py-3.5 sm:pl-6 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">
|
|
63
|
+
<button @click="toggleSort('name')" class="inline-flex items-center group">
|
|
64
|
+
Name
|
|
65
|
+
<span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
|
|
66
|
+
<div class="h-4 w-4 {{ sortBy() === 'name' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
|
|
67
|
+
</span>
|
|
68
|
+
</button>
|
|
69
|
+
</th>
|
|
70
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">
|
|
71
|
+
<button @click="toggleSort('abbreviation')" class="inline-flex items-center group">
|
|
72
|
+
Abbreviation
|
|
73
|
+
<span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
|
|
74
|
+
<div class="h-4 w-4 {{ sortBy() === 'abbreviation' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
|
|
75
|
+
</span>
|
|
76
|
+
</button>
|
|
77
|
+
</th>
|
|
78
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">
|
|
79
|
+
<button @click="toggleSort('type')" class="inline-flex items-center group">
|
|
80
|
+
Type
|
|
81
|
+
<span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
|
|
82
|
+
<div class="h-4 w-4 {{ sortBy() === 'type' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
|
|
83
|
+
</span>
|
|
84
|
+
</button>
|
|
85
|
+
</th>
|
|
86
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">Description</th>
|
|
87
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">Default</th>
|
|
88
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-right text-sm dark:text-neutral-200">
|
|
89
|
+
<button @click="toggleSort('createdAt')" class="inline-flex items-center text-right group">
|
|
90
|
+
Created
|
|
91
|
+
<span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
|
|
92
|
+
<div class="h-4 w-4 {{ sortBy() === 'createdAt' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
|
|
93
|
+
</span>
|
|
94
|
+
</button>
|
|
95
|
+
</th>
|
|
96
|
+
<th scope="col" class="relative pl-3 pr-4 py-3.5 sm:pr-6">
|
|
97
|
+
<span class="sr-only">Actions</span>
|
|
98
|
+
</th>
|
|
99
|
+
</tr>
|
|
100
|
+
</thead>
|
|
101
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
102
|
+
<template :for="unit in filteredUnits">
|
|
103
|
+
<tr>
|
|
104
|
+
<td class="pl-4 pr-3 py-4 sm:pl-6 font-medium text-gray-900 text-sm whitespace-nowrap dark:text-white">{{ unit.name }}</td>
|
|
105
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">{{ unit.abbreviation }}</td>
|
|
106
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">{{ unit.type }}</td>
|
|
107
|
+
<td class="px-3 py-4 text-gray-500 text-sm dark:text-neutral-300">{{ unit.description }}</td>
|
|
108
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-300">
|
|
109
|
+
@if(unit.isDefault)
|
|
110
|
+
<div class="h-5 w-5 text-green-500 i-hugeicons-checkmark-circle-02"></div>
|
|
111
|
+
@else
|
|
112
|
+
<div class="h-5 w-5 text-gray-400 i-hugeicons-cancel-circle"></div>
|
|
113
|
+
@endif
|
|
114
|
+
</td>
|
|
115
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">{{ unit.createdAt }}</td>
|
|
116
|
+
<td class="relative pl-3 pr-4 py-4 sm:pr-6 font-medium text-right text-sm whitespace-nowrap">
|
|
117
|
+
<div class="flex items-center justify-end space-x-2">
|
|
118
|
+
<button type="button" class="text-gray-400 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300 duration-150 transition-colors">
|
|
119
|
+
<div class="h-5 w-5 i-hugeicons-edit-01"></div>
|
|
120
|
+
</button>
|
|
121
|
+
<button type="button" class="text-gray-400 dark:text-red-400 hover:text-red-900 dark:hover:text-red-300 duration-150 transition-colors">
|
|
122
|
+
<div class="h-5 w-5 i-hugeicons-waste"></div>
|
|
123
|
+
</button>
|
|
124
|
+
</div>
|
|
125
|
+
</td>
|
|
126
|
+
</tr>
|
|
127
|
+
</template>
|
|
128
|
+
@if(filteredUnits().length === 0)
|
|
129
|
+
<tr>
|
|
130
|
+
<td colspan="7" class="px-6 py-4 text-center text-gray-500 text-sm dark:text-neutral-400">
|
|
131
|
+
No units found matching your criteria
|
|
132
|
+
</td>
|
|
133
|
+
</tr>
|
|
134
|
+
@endif
|
|
135
|
+
</tbody>
|
|
136
|
+
</table>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<!-- Add Unit Modal -->
|
|
145
|
+
<div :show="showAddModal" class="fixed inset-0 overflow-y-auto z-10">
|
|
146
|
+
<div class="flex items-end justify-center sm:items-center p-4 sm:p-0 min-h-full text-center">
|
|
147
|
+
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="closeAddModal()"></div>
|
|
148
|
+
|
|
149
|
+
<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">
|
|
150
|
+
<div>
|
|
151
|
+
<div class="mt-3 sm:mt-5 text-center">
|
|
152
|
+
<h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Add New Unit</h3>
|
|
153
|
+
<div class="mt-4">
|
|
154
|
+
<div class="space-y-4">
|
|
155
|
+
<div class="grid gap-4 grid-cols-2">
|
|
156
|
+
<div>
|
|
157
|
+
<label for="unit-name" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Name</label>
|
|
158
|
+
<div class="mt-2">
|
|
159
|
+
<input type="text" id="unit-name" x-model="newName" class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm" />
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
<div>
|
|
163
|
+
<label for="unit-abbreviation" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Abbreviation</label>
|
|
164
|
+
<div class="mt-2">
|
|
165
|
+
<input type="text" id="unit-abbreviation" x-model="newAbbreviation" class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm" />
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
<div>
|
|
170
|
+
<label for="unit-type" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Type</label>
|
|
171
|
+
<div class="mt-2">
|
|
172
|
+
<select id="unit-type" x-model="newType" class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm">
|
|
173
|
+
<option value="">Select a type</option>
|
|
174
|
+
<template :for="type in unitTypes">
|
|
175
|
+
<option value="{{ type }}">{{ type }}</option>
|
|
176
|
+
</template>
|
|
177
|
+
<option value="Other">Other</option>
|
|
178
|
+
</select>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
<div>
|
|
182
|
+
<label for="unit-description" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Description</label>
|
|
183
|
+
<div class="mt-2">
|
|
184
|
+
<textarea id="unit-description" x-model="newDescription" rows="2" class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm"></textarea>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
<div class="flex items-center">
|
|
188
|
+
<input id="unit-default" type="checkbox" x-model="newIsDefault" class="h-4 w-4 text-blue-600 dark:bg-neutral-700 border-gray-300 rounded dark:border-neutral-600 focus:ring-blue-600" />
|
|
189
|
+
<label for="unit-default" class="block ml-2 text-gray-900 text-sm dark:text-neutral-200">Set as default for this type</label>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
<div class="sm:grid sm:gap-3 sm:grid-cols-2 sm:grid-flow-row-dense mt-5 sm:mt-6">
|
|
196
|
+
<button type="button" @click="addUnit()" class="inline-flex justify-center sm:col-start-2 px-3 py-2 w-full font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 shadow-sm">Add</button>
|
|
197
|
+
<button type="button" @click="closeAddModal()" class="inline-flex justify-center sm:col-start-1 mt-3 px-3 py-2 sm:mt-0 w-full font-semibold text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-600 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 shadow-sm">Cancel</button>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
</PageLayout>
|
|
201
|
+
<script client>
|
|
202
|
+
interface Unit {
|
|
203
|
+
id: number
|
|
204
|
+
name: string
|
|
205
|
+
abbreviation: string
|
|
206
|
+
type: string
|
|
207
|
+
description: string
|
|
208
|
+
isDefault: boolean
|
|
209
|
+
createdAt: string
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const initialUnits: Unit[] = [
|
|
213
|
+
{ id: 1, name: 'Slice', abbreviation: 'slice', type: 'Food', description: 'Individual slice of pizza or cake', isDefault: true, createdAt: '2023-05-15' },
|
|
214
|
+
{ id: 2, name: 'Whole', abbreviation: 'whole', type: 'Food', description: 'Complete item (e.g., whole pizza, whole cake)', isDefault: false, createdAt: '2023-05-15' },
|
|
215
|
+
{ id: 3, name: 'Milliliter', abbreviation: 'ml', type: 'Beverage', description: 'Volume measurement for beverages', isDefault: true, createdAt: '2023-06-02' },
|
|
216
|
+
{ id: 4, name: 'Bottle', abbreviation: 'btl', type: 'Beverage', description: 'Standard bottle size (varies by product)', isDefault: false, createdAt: '2023-06-02' },
|
|
217
|
+
{ id: 5, name: 'Piece', abbreviation: 'pc', type: 'General', description: 'Individual item count', isDefault: true, createdAt: '2023-06-10' },
|
|
218
|
+
{ id: 6, name: 'Box', abbreviation: 'box', type: 'Package', description: 'Box containing multiple items', isDefault: true, createdAt: '2023-07-05' },
|
|
219
|
+
{ id: 7, name: 'Serving', abbreviation: 'srv', type: 'Food', description: 'Individual portion size', isDefault: false, createdAt: '2023-07-12' },
|
|
220
|
+
{ id: 8, name: 'Gram', abbreviation: 'g', type: 'Weight', description: 'Weight measurement for food items', isDefault: true, createdAt: '2023-08-03' },
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
const units = state(initialUnits)
|
|
224
|
+
const searchQuery = state('')
|
|
225
|
+
const sortBy = state<'name' | 'abbreviation' | 'type' | 'createdAt'>('name')
|
|
226
|
+
const sortOrder = state<'asc' | 'desc'>('asc')
|
|
227
|
+
const typeFilter = state<string>('all')
|
|
228
|
+
const defaultFilter = state<'all' | 'default' | 'not-default'>('all')
|
|
229
|
+
|
|
230
|
+
const showAddModal = state(false)
|
|
231
|
+
const newName = state('')
|
|
232
|
+
const newAbbreviation = state('')
|
|
233
|
+
const newType = state<string>('')
|
|
234
|
+
const newDescription = state('')
|
|
235
|
+
const newIsDefault = state(false)
|
|
236
|
+
|
|
237
|
+
function unitTypes(): string[] {
|
|
238
|
+
return Array.from(new Set(units().map(u => u.type))).sort()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const filteredUnits = derived(() => {
|
|
242
|
+
const q = searchQuery().toLowerCase()
|
|
243
|
+
const tFilter = typeFilter()
|
|
244
|
+
const dFilter = defaultFilter()
|
|
245
|
+
const by = sortBy()
|
|
246
|
+
const dir = sortOrder()
|
|
247
|
+
|
|
248
|
+
return units()
|
|
249
|
+
.filter((u) => {
|
|
250
|
+
const matchesSearch
|
|
251
|
+
= u.name.toLowerCase().includes(q)
|
|
252
|
+
|| u.abbreviation.toLowerCase().includes(q)
|
|
253
|
+
|| u.description.toLowerCase().includes(q)
|
|
254
|
+
const matchesType = tFilter === 'all' || u.type === tFilter
|
|
255
|
+
const matchesDefault
|
|
256
|
+
= dFilter === 'all'
|
|
257
|
+
|| (dFilter === 'default' && u.isDefault)
|
|
258
|
+
|| (dFilter === 'not-default' && !u.isDefault)
|
|
259
|
+
return matchesSearch && matchesType && matchesDefault
|
|
260
|
+
})
|
|
261
|
+
.sort((a, b) => {
|
|
262
|
+
let cmp = 0
|
|
263
|
+
if (by === 'name') cmp = a.name.localeCompare(b.name)
|
|
264
|
+
else if (by === 'type') cmp = a.type.localeCompare(b.type)
|
|
265
|
+
else if (by === 'abbreviation') cmp = a.abbreviation.localeCompare(b.abbreviation)
|
|
266
|
+
else if (by === 'createdAt') cmp = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
|
267
|
+
return dir === 'asc' ? cmp : -cmp
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
function toggleSort(column: 'name' | 'abbreviation' | 'type' | 'createdAt'): void {
|
|
272
|
+
if (sortBy() === column) {
|
|
273
|
+
sortOrder.update(d => d === 'asc' ? 'desc' : 'asc')
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
sortBy.set(column)
|
|
277
|
+
sortOrder.set('asc')
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function openAddModal(): void {
|
|
282
|
+
newName.set('')
|
|
283
|
+
newAbbreviation.set('')
|
|
284
|
+
newType.set('')
|
|
285
|
+
newDescription.set('')
|
|
286
|
+
newIsDefault.set(false)
|
|
287
|
+
showAddModal.set(true)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function closeAddModal(): void {
|
|
291
|
+
showAddModal.set(false)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function addUnit(): void {
|
|
295
|
+
const list = units()
|
|
296
|
+
const id = list.length > 0 ? Math.max(...list.map(u => u.id)) + 1 : 1
|
|
297
|
+
const isDefault = newIsDefault()
|
|
298
|
+
const type = newType()
|
|
299
|
+
// If marking as default, clear default on other units of the same type.
|
|
300
|
+
const next = isDefault
|
|
301
|
+
? list.map(u => u.type === type ? { ...u, isDefault: false } : u)
|
|
302
|
+
: list
|
|
303
|
+
|
|
304
|
+
units.set([...next, {
|
|
305
|
+
id,
|
|
306
|
+
name: newName(),
|
|
307
|
+
abbreviation: newAbbreviation(),
|
|
308
|
+
type,
|
|
309
|
+
description: newDescription(),
|
|
310
|
+
isDefault,
|
|
311
|
+
createdAt: new Date().toISOString().split('T')[0] || '',
|
|
312
|
+
}])
|
|
313
|
+
closeAddModal()
|
|
314
|
+
}
|
|
315
|
+
</script>
|
|
316
|
+
|
|
317
|
+
@endsection
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
<div class="sm:flex sm:items-center sm:justify-between">
|
|
5
|
+
<PageHeader title="Variants" description="Manage product variants and options" />
|
|
6
|
+
<div class="mt-4 sm:mt-0">
|
|
7
|
+
<button
|
|
8
|
+
type="button"
|
|
9
|
+
@click="openAddModal()"
|
|
10
|
+
class="inline-flex items-center px-3 py-2 font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 shadow-sm"
|
|
11
|
+
>
|
|
12
|
+
<div class="mr-1 h-5 w-5 i-hugeicons-plus-sign"></div>
|
|
13
|
+
Add variant type
|
|
14
|
+
</button>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Filters -->
|
|
19
|
+
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between mt-6">
|
|
20
|
+
<div class="relative max-w-sm">
|
|
21
|
+
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
|
22
|
+
<div class="h-5 w-5 text-gray-400 i-hugeicons-search-01"></div>
|
|
23
|
+
</div>
|
|
24
|
+
<input
|
|
25
|
+
x-model="searchQuery"
|
|
26
|
+
type="text"
|
|
27
|
+
class="block pl-10 pr-3 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:placeholder:text-neutral-500 dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600 focus:ring-inset"
|
|
28
|
+
placeholder="Search variants..."
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="flex flex-col gap-4 sm:flex-row">
|
|
33
|
+
<select
|
|
34
|
+
x-model="activeFilter"
|
|
35
|
+
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"
|
|
36
|
+
>
|
|
37
|
+
<option value="all">All Variants</option>
|
|
38
|
+
<option value="active">Active Only</option>
|
|
39
|
+
<option value="inactive">Inactive Only</option>
|
|
40
|
+
</select>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<!-- Variants table -->
|
|
45
|
+
<div class="flow-root mt-6">
|
|
46
|
+
<div class="overflow-x-auto -mx-4 -my-2 sm:-mx-6 lg:-mx-8">
|
|
47
|
+
<div class="inline-block py-2 sm:px-6 lg:px-8 min-w-full align-middle">
|
|
48
|
+
<div class="overflow-hidden ring-1 ring-black ring-opacity-5 sm:rounded-lg shadow">
|
|
49
|
+
<table class="min-w-full divide-gray-300 divide-y dark:divide-neutral-700">
|
|
50
|
+
<thead class="bg-gray-50 dark:bg-neutral-700">
|
|
51
|
+
<tr>
|
|
52
|
+
<th scope="col" class="pl-4 pr-3 py-3.5 sm:pl-6 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">
|
|
53
|
+
<button @click="toggleSort('name')" class="inline-flex items-center group">
|
|
54
|
+
Variant Type
|
|
55
|
+
<span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
|
|
56
|
+
<div class="h-4 w-4 {{ sortBy() === 'name' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
|
|
57
|
+
</span>
|
|
58
|
+
</button>
|
|
59
|
+
</th>
|
|
60
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">Description</th>
|
|
61
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">
|
|
62
|
+
<button @click="toggleSort('optionCount')" class="inline-flex items-center group">
|
|
63
|
+
Options
|
|
64
|
+
<span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
|
|
65
|
+
<div class="h-4 w-4 {{ sortBy() === 'optionCount' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
|
|
66
|
+
</span>
|
|
67
|
+
</button>
|
|
68
|
+
</th>
|
|
69
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-neutral-200">Status</th>
|
|
70
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-right text-sm dark:text-neutral-200">
|
|
71
|
+
<button @click="toggleSort('createdAt')" class="inline-flex items-center text-right group">
|
|
72
|
+
Created
|
|
73
|
+
<span class="group-focus:visible group-hover:visible flex-none ml-2 text-gray-400 rounded">
|
|
74
|
+
<div class="h-4 w-4 {{ sortBy() === 'createdAt' ? (sortOrder() === 'asc' ? 'i-hugeicons-arrow-up-02' : 'i-hugeicons-arrow-down-02') : 'i-hugeicons-arrows-up-down' }}"></div>
|
|
75
|
+
</span>
|
|
76
|
+
</button>
|
|
77
|
+
</th>
|
|
78
|
+
<th scope="col" class="relative pl-3 pr-4 py-3.5 sm:pr-6">
|
|
79
|
+
<span class="sr-only">Actions</span>
|
|
80
|
+
</th>
|
|
81
|
+
</tr>
|
|
82
|
+
</thead>
|
|
83
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
84
|
+
<template :for="variantType in filteredVariantTypes">
|
|
85
|
+
<tr>
|
|
86
|
+
<td class="pl-4 pr-3 py-4 font-medium text-gray-900 text-sm whitespace-nowrap dark:text-white">{{ variantType.name }}</td>
|
|
87
|
+
<td class="px-3 py-4 text-gray-500 text-sm dark:text-neutral-300">{{ variantType.description }}</td>
|
|
88
|
+
<td class="px-3 py-4 text-gray-500 text-sm dark:text-neutral-300">
|
|
89
|
+
<div class="flex flex-wrap gap-1">
|
|
90
|
+
<template :for="option in variantType.options.slice(0, 5)">
|
|
91
|
+
<span class="inline-flex items-center px-2 py-1 font-medium text-blue-700 text-xs dark:text-blue-300 bg-blue-50 dark:bg-blue-900 rounded-full">
|
|
92
|
+
{{ option }}
|
|
93
|
+
</span>
|
|
94
|
+
</template>
|
|
95
|
+
@if(variantType.options.length > 5)
|
|
96
|
+
<span class="inline-flex items-center px-2 py-1 font-medium text-gray-600 text-xs dark:text-neutral-300 bg-gray-50 dark:bg-neutral-700 rounded-full">
|
|
97
|
+
+{{ variantType.options.length - 5 }} more
|
|
98
|
+
</span>
|
|
99
|
+
@endif
|
|
100
|
+
</div>
|
|
101
|
+
</td>
|
|
102
|
+
<td class="px-3 py-4 text-sm whitespace-nowrap">
|
|
103
|
+
<span class="inline-flex items-center px-2.5 py-0.5 font-medium text-xs rounded-full {{ variantType.active ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300' : 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300' }}">
|
|
104
|
+
{{ variantType.active ? 'Active' : 'Inactive' }}
|
|
105
|
+
</span>
|
|
106
|
+
</td>
|
|
107
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">{{ variantType.createdAt }}</td>
|
|
108
|
+
<td class="relative pl-3 pr-4 py-4 sm:pr-6 font-medium text-right text-sm whitespace-nowrap">
|
|
109
|
+
<div class="flex items-center justify-end space-x-2">
|
|
110
|
+
<button type="button" class="text-gray-400 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300 duration-150 transition-colors">
|
|
111
|
+
<div class="h-5 w-5 i-hugeicons-edit-01"></div>
|
|
112
|
+
</button>
|
|
113
|
+
<button type="button" class="text-gray-400 dark:text-red-400 hover:text-red-900 dark:hover:text-red-300 duration-150 transition-colors">
|
|
114
|
+
<div class="h-5 w-5 i-hugeicons-waste"></div>
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
</td>
|
|
118
|
+
</tr>
|
|
119
|
+
</template>
|
|
120
|
+
@if(filteredVariantTypes().length === 0)
|
|
121
|
+
<tr>
|
|
122
|
+
<td colspan="6" class="px-6 py-4 text-center text-gray-500 text-sm dark:text-neutral-400">
|
|
123
|
+
No variant types found matching your criteria
|
|
124
|
+
</td>
|
|
125
|
+
</tr>
|
|
126
|
+
@endif
|
|
127
|
+
</tbody>
|
|
128
|
+
</table>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<!-- Add Variant Type Modal -->
|
|
137
|
+
<div :show="showAddModal" class="fixed inset-0 overflow-y-auto z-10">
|
|
138
|
+
<div class="flex items-end justify-center sm:items-center p-4 sm:p-0 min-h-full text-center">
|
|
139
|
+
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" @click="closeAddModal()"></div>
|
|
140
|
+
|
|
141
|
+
<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">
|
|
142
|
+
<div>
|
|
143
|
+
<div class="mt-3 sm:mt-5 text-center">
|
|
144
|
+
<h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Add New Variant Type</h3>
|
|
145
|
+
<div class="mt-4">
|
|
146
|
+
<div class="space-y-4">
|
|
147
|
+
<div>
|
|
148
|
+
<label for="variant-name" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Name</label>
|
|
149
|
+
<div class="mt-2">
|
|
150
|
+
<input type="text" id="variant-name" x-model="newName" class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm" />
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
<div>
|
|
154
|
+
<label for="variant-description" class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Description</label>
|
|
155
|
+
<div class="mt-2">
|
|
156
|
+
<textarea id="variant-description" x-model="newDescription" rows="2" class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm"></textarea>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
<div>
|
|
160
|
+
<label class="block font-medium leading-6 text-gray-900 text-left text-sm dark:text-neutral-200">Options</label>
|
|
161
|
+
<div class="mt-2">
|
|
162
|
+
<div class="flex flex-wrap gap-2 mb-2">
|
|
163
|
+
<template :for="(option, index) in newOptions">
|
|
164
|
+
<div class="inline-flex items-center px-2 py-1 font-medium text-blue-700 text-xs dark:text-blue-300 bg-blue-50 dark:bg-blue-900 rounded-full">
|
|
165
|
+
{{ option }}
|
|
166
|
+
<button
|
|
167
|
+
type="button"
|
|
168
|
+
class="ml-1 text-blue-400 dark:text-blue-300 hover:text-blue-600 dark:hover:text-blue-100"
|
|
169
|
+
@click="removeOption(index)"
|
|
170
|
+
>
|
|
171
|
+
<div class="h-4 w-4 i-hugeicons-cancel-circle"></div>
|
|
172
|
+
</button>
|
|
173
|
+
</div>
|
|
174
|
+
</template>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="flex">
|
|
177
|
+
<input
|
|
178
|
+
type="text"
|
|
179
|
+
x-model="newOption"
|
|
180
|
+
placeholder="Add option"
|
|
181
|
+
class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-l-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 focus:ring-inset shadow-sm"
|
|
182
|
+
@keyup.enter="addOption()"
|
|
183
|
+
/>
|
|
184
|
+
<button
|
|
185
|
+
type="button"
|
|
186
|
+
@click="addOption()"
|
|
187
|
+
class="inline-flex items-center px-3 py-2 font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-r-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 shadow-sm"
|
|
188
|
+
>
|
|
189
|
+
Add
|
|
190
|
+
</button>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
<div class="flex items-center">
|
|
195
|
+
<input id="variant-active" type="checkbox" x-model="newActive" class="h-4 w-4 text-blue-600 dark:bg-neutral-700 border-gray-300 rounded dark:border-neutral-600 focus:ring-blue-600" />
|
|
196
|
+
<label for="variant-active" class="block ml-2 text-gray-900 text-sm dark:text-neutral-200">Active</label>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
<div class="sm:grid sm:gap-3 sm:grid-cols-2 sm:grid-flow-row-dense mt-5 sm:mt-6">
|
|
203
|
+
<button type="button" @click="addVariantType()" class="inline-flex justify-center sm:col-start-2 px-3 py-2 w-full font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 shadow-sm">Add</button>
|
|
204
|
+
<button type="button" @click="closeAddModal()" class="inline-flex justify-center sm:col-start-1 mt-3 px-3 py-2 sm:mt-0 w-full font-semibold text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-600 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 shadow-sm">Cancel</button>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</PageLayout>
|
|
208
|
+
<script client>
|
|
209
|
+
interface VariantType {
|
|
210
|
+
id: number
|
|
211
|
+
name: string
|
|
212
|
+
description: string
|
|
213
|
+
options: string[]
|
|
214
|
+
active: boolean
|
|
215
|
+
createdAt: string
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const initialVariantTypes: VariantType[] = [
|
|
219
|
+
{ id: 1, name: 'Size', description: 'Food portion size options', options: ['Small', 'Medium', 'Large', 'Family Size'], active: true, createdAt: '2023-05-10' },
|
|
220
|
+
{ id: 2, name: 'Crust Type', description: 'Pizza crust options', options: ['Thin', 'Regular', 'Thick', 'Stuffed', 'Gluten-Free'], active: true, createdAt: '2023-05-15' },
|
|
221
|
+
{ id: 3, name: 'Spice Level', description: 'Heat intensity options', options: ['Mild', 'Medium', 'Hot', 'Extra Hot'], active: true, createdAt: '2023-06-01' },
|
|
222
|
+
{ id: 4, name: 'Protein', description: 'Protein options for dishes', options: ['Chicken', 'Beef', 'Pork', 'Tofu', 'Shrimp', 'None'], active: true, createdAt: '2023-06-10' },
|
|
223
|
+
{ id: 5, name: 'Toppings', description: 'Additional toppings for dishes', options: ['Cheese', 'Mushrooms', 'Peppers', 'Onions', 'Olives', 'Bacon'], active: true, createdAt: '2023-06-20' },
|
|
224
|
+
{ id: 6, name: 'Dietary', description: 'Dietary preference options', options: ['Vegetarian', 'Vegan', 'Gluten-Free', 'Dairy-Free', 'Keto', 'Low-Carb'], active: true, createdAt: '2023-07-05' },
|
|
225
|
+
{ id: 7, name: 'Sauce', description: 'Sauce options for dishes', options: ['Marinara', 'Alfredo', 'BBQ', 'Buffalo', 'Sweet & Sour', 'None'], active: true, createdAt: '2023-07-15' },
|
|
226
|
+
{ id: 8, name: 'Sweetness', description: 'Sweetness level options', options: ['Not Sweet', 'Slightly Sweet', 'Medium Sweet', 'Very Sweet'], active: true, createdAt: '2023-08-01' },
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
const variantTypes = state(initialVariantTypes)
|
|
230
|
+
const searchQuery = state('')
|
|
231
|
+
const sortBy = state<'name' | 'optionCount' | 'createdAt'>('name')
|
|
232
|
+
const sortOrder = state<'asc' | 'desc'>('asc')
|
|
233
|
+
const activeFilter = state<'all' | 'active' | 'inactive'>('all')
|
|
234
|
+
|
|
235
|
+
const showAddModal = state(false)
|
|
236
|
+
const newName = state('')
|
|
237
|
+
const newDescription = state('')
|
|
238
|
+
const newOptions = state<string[]>([])
|
|
239
|
+
const newActive = state(true)
|
|
240
|
+
const newOption = state('')
|
|
241
|
+
|
|
242
|
+
const filteredVariantTypes = derived(() => {
|
|
243
|
+
const q = searchQuery().toLowerCase()
|
|
244
|
+
const aFilter = activeFilter()
|
|
245
|
+
const by = sortBy()
|
|
246
|
+
const dir = sortOrder()
|
|
247
|
+
|
|
248
|
+
return variantTypes()
|
|
249
|
+
.filter((v) => {
|
|
250
|
+
const matchesSearch
|
|
251
|
+
= v.name.toLowerCase().includes(q)
|
|
252
|
+
|| v.description.toLowerCase().includes(q)
|
|
253
|
+
|| v.options.some(o => o.toLowerCase().includes(q))
|
|
254
|
+
const matchesActive
|
|
255
|
+
= aFilter === 'all'
|
|
256
|
+
|| (aFilter === 'active' && v.active)
|
|
257
|
+
|| (aFilter === 'inactive' && !v.active)
|
|
258
|
+
return matchesSearch && matchesActive
|
|
259
|
+
})
|
|
260
|
+
.sort((a, b) => {
|
|
261
|
+
let cmp = 0
|
|
262
|
+
if (by === 'name') cmp = a.name.localeCompare(b.name)
|
|
263
|
+
else if (by === 'optionCount') cmp = a.options.length - b.options.length
|
|
264
|
+
else if (by === 'createdAt') cmp = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
|
265
|
+
return dir === 'asc' ? cmp : -cmp
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
function toggleSort(column: 'name' | 'optionCount' | 'createdAt'): void {
|
|
270
|
+
if (sortBy() === column) {
|
|
271
|
+
sortOrder.update(d => d === 'asc' ? 'desc' : 'asc')
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
sortBy.set(column)
|
|
275
|
+
sortOrder.set('asc')
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function addOption(): void {
|
|
280
|
+
const o = newOption().trim()
|
|
281
|
+
if (!o)
|
|
282
|
+
return
|
|
283
|
+
newOptions.update(list => [...list, o])
|
|
284
|
+
newOption.set('')
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function removeOption(index: number): void {
|
|
288
|
+
newOptions.update(list => list.filter((_, i) => i !== index))
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function openAddModal(): void {
|
|
292
|
+
newName.set('')
|
|
293
|
+
newDescription.set('')
|
|
294
|
+
newOptions.set([])
|
|
295
|
+
newActive.set(true)
|
|
296
|
+
newOption.set('')
|
|
297
|
+
showAddModal.set(true)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function closeAddModal(): void {
|
|
301
|
+
showAddModal.set(false)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function addVariantType(): void {
|
|
305
|
+
const list = variantTypes()
|
|
306
|
+
const id = list.length > 0 ? Math.max(...list.map(v => v.id)) + 1 : 1
|
|
307
|
+
variantTypes.set([...list, {
|
|
308
|
+
id,
|
|
309
|
+
name: newName(),
|
|
310
|
+
description: newDescription(),
|
|
311
|
+
options: [...newOptions()],
|
|
312
|
+
active: newActive(),
|
|
313
|
+
createdAt: new Date().toISOString().split('T')[0] || '',
|
|
314
|
+
}])
|
|
315
|
+
closeAddModal()
|
|
316
|
+
}
|
|
317
|
+
</script>
|
|
318
|
+
|
|
319
|
+
@endsection
|