@stacksjs/defaults 0.70.207 → 0.70.208
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,536 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
@title('Dashboard - Events & Goals')
|
|
5
|
+
<PageLayout>
|
|
6
|
+
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between mb-6">
|
|
7
|
+
<div class="flex items-center space-x-2">
|
|
8
|
+
<div class="h-5 w-5 text-gray-500 dark:text-neutral-400 i-hugeicons-calendar-03"></div>
|
|
9
|
+
<select x-model="selectedDateRange" class="block pl-3 pr-10 py-1.5 text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600">
|
|
10
|
+
<template :for="range in dateRanges">
|
|
11
|
+
<option :value="range.id">{{ range.name }}</option>
|
|
12
|
+
</template>
|
|
13
|
+
</select>
|
|
14
|
+
<span class="text-gray-500 text-sm dark:text-neutral-400">{{ dateRangeDisplay() }}</span>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div class="flex flex-wrap gap-2 items-center">
|
|
18
|
+
<span class="text-gray-500 text-sm dark:text-neutral-400">compared to</span>
|
|
19
|
+
<select x-model="comparisonType" class="block pl-3 pr-10 py-1.5 text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600">
|
|
20
|
+
<option value="no-comparison">No comparison</option>
|
|
21
|
+
<option value="previous-period">Previous period</option>
|
|
22
|
+
<option value="previous-year">Previous year</option>
|
|
23
|
+
</select>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="mb-8">
|
|
28
|
+
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-4">
|
|
29
|
+
<h2 class="font-semibold text-gray-900 text-xl dark:text-white">Events & Goals</h2>
|
|
30
|
+
<div class="flex items-center mt-2 space-x-2 sm:mt-0">
|
|
31
|
+
<button @click="openCreateEvent()" class="inline-flex items-center px-3 py-2 font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md shadow-sm">
|
|
32
|
+
<div class="mr-1 h-4 w-4 i-hugeicons-plus-sign"></div>
|
|
33
|
+
Create Event
|
|
34
|
+
</button>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="mb-6 p-4 bg-gray-50 dark:bg-neutral-800 rounded-lg">
|
|
39
|
+
<div class="flex flex-col gap-4 sm:flex-row sm:items-center">
|
|
40
|
+
<span class="font-medium text-gray-700 text-sm dark:text-neutral-300">Calculate conversion rates using:</span>
|
|
41
|
+
<div class="flex items-center space-x-4">
|
|
42
|
+
<label class="inline-flex items-center">
|
|
43
|
+
<input type="radio" x-model="conversionCalculationType" value="pageviews" class="h-4 w-4 text-blue-600 border-gray-300 dark:border-neutral-600 focus:ring-blue-500" />
|
|
44
|
+
<span class="ml-2 text-gray-700 text-sm dark:text-neutral-300">Page views</span>
|
|
45
|
+
</label>
|
|
46
|
+
<label class="inline-flex items-center">
|
|
47
|
+
<input type="radio" x-model="conversionCalculationType" value="people" class="h-4 w-4 text-blue-600 border-gray-300 dark:border-neutral-600 focus:ring-blue-500" />
|
|
48
|
+
<span class="ml-2 text-gray-700 text-sm dark:text-neutral-300">People</span>
|
|
49
|
+
</label>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="text-gray-500 text-xs dark:text-neutral-400">{{ conversionFormula() }}</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
56
|
+
<table class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
|
|
57
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
58
|
+
<tr>
|
|
59
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Event Name</th>
|
|
60
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Completions</th>
|
|
61
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Value</th>
|
|
62
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Conversion Rate</th>
|
|
63
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Last Triggered</th>
|
|
64
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Actions</th>
|
|
65
|
+
</tr>
|
|
66
|
+
</thead>
|
|
67
|
+
<tbody class="divide-gray-200 divide-y dark:divide-neutral-700">
|
|
68
|
+
<template :for="event in activeEvents">
|
|
69
|
+
<tr @click="viewEventDetails(event.id)" class="hover:bg-gray-50 dark:hover:bg-neutral-700/50 cursor-pointer">
|
|
70
|
+
<td class="px-4 py-3 font-medium text-gray-900 text-sm whitespace-nowrap dark:text-white">{{ event.name }}</td>
|
|
71
|
+
<td class="px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">{{ formatNumber(event.completions) }}</td>
|
|
72
|
+
<td class="px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">{{ formatCurrency(event.value) }}</td>
|
|
73
|
+
<td class="px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">{{ event.conversionRate }}%</td>
|
|
74
|
+
<td class="px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">{{ formatDateOrDash(event.lastTriggered) }}</td>
|
|
75
|
+
<td class="px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">
|
|
76
|
+
<button @click.stop="confirmDelete(event.id)" class="text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300">
|
|
77
|
+
<div class="h-4 w-4 i-hugeicons-waste"></div>
|
|
78
|
+
</button>
|
|
79
|
+
</td>
|
|
80
|
+
</tr>
|
|
81
|
+
</template>
|
|
82
|
+
<tr :show="activeEvents().length === 0">
|
|
83
|
+
<td colspan="6" class="px-4 py-8 text-center text-gray-500 text-sm dark:text-neutral-400">
|
|
84
|
+
<div class="flex flex-col items-center justify-center">
|
|
85
|
+
<div class="mb-2 h-12 w-12 text-gray-300 dark:text-neutral-600 i-hugeicons-chart-01"></div>
|
|
86
|
+
<p class="mb-1">No events found</p>
|
|
87
|
+
<p class="text-xs">Create your first event to start tracking conversions</p>
|
|
88
|
+
</div>
|
|
89
|
+
</td>
|
|
90
|
+
</tr>
|
|
91
|
+
</tbody>
|
|
92
|
+
</table>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="mb-8 p-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
97
|
+
<div class="mb-4">
|
|
98
|
+
<h3 class="font-medium text-gray-900 text-lg dark:text-white">Event Implementation Guide</h3>
|
|
99
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Learn how to track events and conversions on your website</p>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="space-y-6">
|
|
102
|
+
<div>
|
|
103
|
+
<h4 class="mb-2 font-medium text-gray-900 text-md dark:text-white">Basic Event Tracking</h4>
|
|
104
|
+
<div class="overflow-x-auto p-4 text-white bg-gray-800 rounded-lg">
|
|
105
|
+
<pre class="text-sm"><code>// Track a simple event
|
|
106
|
+
stacks.trackEvent('newsletter signup');</code></pre>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
<div>
|
|
110
|
+
<h4 class="mb-2 font-medium text-gray-900 text-md dark:text-white">Track Events with Value</h4>
|
|
111
|
+
<div class="overflow-x-auto p-4 text-white bg-gray-800 rounded-lg">
|
|
112
|
+
<pre class="text-sm"><code>// Track an event with monetary value (in cents)
|
|
113
|
+
stacks.trackEvent('purchase completed', {
|
|
114
|
+
_value: 1000, // $10.00
|
|
115
|
+
});</code></pre>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div :show="blockedEvents().length > 0" class="mb-8">
|
|
122
|
+
<div class="flex items-center justify-between mb-4">
|
|
123
|
+
<h3 class="font-medium text-gray-900 text-lg dark:text-white">Blocked Events</h3>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
126
|
+
<table class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
|
|
127
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
128
|
+
<tr>
|
|
129
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Event Name</th>
|
|
130
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Completions</th>
|
|
131
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Created</th>
|
|
132
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Actions</th>
|
|
133
|
+
</tr>
|
|
134
|
+
</thead>
|
|
135
|
+
<tbody class="divide-gray-200 divide-y dark:divide-neutral-700">
|
|
136
|
+
<template :for="event in blockedEvents">
|
|
137
|
+
<tr class="hover:bg-gray-50 dark:hover:bg-neutral-700/50">
|
|
138
|
+
<td class="px-4 py-3 font-medium text-gray-500 text-sm whitespace-nowrap dark:text-neutral-400">
|
|
139
|
+
<div class="flex items-center">
|
|
140
|
+
<div class="mr-2 h-4 w-4 text-red-500 i-hugeicons-blocked"></div>
|
|
141
|
+
{{ event.name }}
|
|
142
|
+
</div>
|
|
143
|
+
</td>
|
|
144
|
+
<td class="px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-400">{{ formatNumber(event.completions) }}</td>
|
|
145
|
+
<td class="px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-400">{{ formatDateOrDash(event.createdAt) }}</td>
|
|
146
|
+
<td class="px-4 py-3 text-right text-sm whitespace-nowrap">
|
|
147
|
+
<div class="flex justify-end space-x-2">
|
|
148
|
+
<button @click="unblockEvent(event.id)" class="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">Unblock</button>
|
|
149
|
+
<button @click="confirmDelete(event.id)" class="text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300">Delete</button>
|
|
150
|
+
</div>
|
|
151
|
+
</td>
|
|
152
|
+
</tr>
|
|
153
|
+
</template>
|
|
154
|
+
</tbody>
|
|
155
|
+
</table>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<div :show="showEventDetails()" @click.self="closeEventDetails()" class="flex fixed inset-0 z-50 items-center justify-center p-4 bg-black bg-opacity-50">
|
|
160
|
+
<div class="overflow-y-auto p-6 max-h-[90vh] w-full sm:max-w-3xl bg-white dark:bg-neutral-800 rounded-lg shadow-xl">
|
|
161
|
+
<div class="flex items-start justify-between">
|
|
162
|
+
<div>
|
|
163
|
+
<h3 class="font-semibold text-gray-900 text-xl dark:text-white">{{ selectedEvent()?.name }}</h3>
|
|
164
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Created on {{ formatDateOrDash(selectedEvent()?.createdAt) }}</p>
|
|
165
|
+
</div>
|
|
166
|
+
<button @click="closeEventDetails()" class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
|
|
167
|
+
<i class="h-6 w-6 i-hugeicons-cancel-01"></i>
|
|
168
|
+
</button>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<div class="grid gap-4 grid-cols-1 sm:grid-cols-3 mt-6">
|
|
172
|
+
<div class="p-4 bg-gray-50 dark:bg-neutral-700 rounded-lg">
|
|
173
|
+
<div class="text-gray-500 text-sm dark:text-neutral-400">Completions</div>
|
|
174
|
+
<div class="font-bold text-2xl text-gray-900 dark:text-white">{{ formatNumber(selectedEvent()?.completions ?? 0) }}</div>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="p-4 bg-gray-50 dark:bg-neutral-700 rounded-lg">
|
|
177
|
+
<div class="text-gray-500 text-sm dark:text-neutral-400">Conversion Rate</div>
|
|
178
|
+
<div class="font-bold text-2xl text-gray-900 dark:text-white">{{ selectedEvent()?.conversionRate ?? 0 }}%</div>
|
|
179
|
+
</div>
|
|
180
|
+
<div class="p-4 bg-gray-50 dark:bg-neutral-700 rounded-lg">
|
|
181
|
+
<div class="text-gray-500 text-sm dark:text-neutral-400">Value</div>
|
|
182
|
+
<div class="font-bold text-2xl text-gray-900 dark:text-white">{{ formatCurrency(selectedEvent()?.value ?? 0) }}</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<div class="mt-6 p-4 bg-white dark:bg-neutral-700 rounded-lg shadow">
|
|
187
|
+
<h4 class="font-medium text-gray-900 text-md dark:text-white">Recent Activity</h4>
|
|
188
|
+
<div class="mt-2 h-64 w-full"><canvas id="event_details_chart"></canvas></div>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div class="mt-6">
|
|
192
|
+
<h4 class="mb-2 font-medium text-gray-900 text-md dark:text-white">Implementation Code</h4>
|
|
193
|
+
<div class="overflow-x-auto p-4 text-white bg-gray-800 rounded-lg">
|
|
194
|
+
<pre class="text-sm"><code>// Basic event tracking
|
|
195
|
+
stacks.trackEvent('{{ selectedEventName() }}');
|
|
196
|
+
|
|
197
|
+
// With value (for e-commerce)
|
|
198
|
+
stacks.trackEvent('{{ selectedEventName() }}', {
|
|
199
|
+
_value: 1000, // Value in cents
|
|
200
|
+
});</code></pre>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<div class="flex justify-end mt-6">
|
|
205
|
+
<button @click="closeEventDetails()" class="inline-flex justify-center mr-2 px-3 py-2 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">Close</button>
|
|
206
|
+
<button @click="confirmDeleteSelected()" class="inline-flex justify-center px-3 py-2 font-semibold text-sm text-white bg-red-600 hover:bg-red-500 rounded-md shadow-sm">Delete Event</button>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<div :show="showCreateEventModal()" @click.self="closeCreateEvent()" class="flex fixed inset-0 z-50 items-center justify-center p-4 bg-black bg-opacity-50">
|
|
212
|
+
<div class="overflow-y-auto p-6 max-h-[90vh] w-full sm:max-w-lg bg-white dark:bg-neutral-800 rounded-lg shadow-xl">
|
|
213
|
+
<div class="flex items-start justify-between">
|
|
214
|
+
<div>
|
|
215
|
+
<h3 class="font-semibold text-gray-900 text-xl dark:text-white">Create New Event</h3>
|
|
216
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Set up a new event to track conversions on your site</p>
|
|
217
|
+
</div>
|
|
218
|
+
<button @click="closeCreateEvent()" class="text-gray-400 hover:text-gray-500 dark:hover:text-gray-300">
|
|
219
|
+
<i class="h-6 w-6 i-hugeicons-cancel-01"></i>
|
|
220
|
+
</button>
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
<div class="mt-6 space-y-4">
|
|
224
|
+
<div>
|
|
225
|
+
<label for="eventName" class="block mb-1 font-medium text-gray-700 text-sm dark:text-neutral-300">Event Name</label>
|
|
226
|
+
<input id="eventName" x-model="newEventName" type="text" placeholder="e.g., Newsletter Signup" 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 shadow-sm" />
|
|
227
|
+
<p class="mt-1 text-gray-500 text-xs dark:text-neutral-400">Use a descriptive name without special characters or emojis</p>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<div class="flex items-center">
|
|
231
|
+
<input id="hasValue" :checked="newEventHasValue" @change="toggleNewEventHasValue()" type="checkbox" class="h-4 w-4 text-blue-600 border-gray-300 rounded dark:border-neutral-600 focus:ring-blue-500" />
|
|
232
|
+
<label for="hasValue" class="block ml-2 text-gray-700 text-sm dark:text-neutral-300">This event has a monetary value (e.g., purchase)</label>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<div :show="newEventHasValue()">
|
|
236
|
+
<label for="currency" class="block mb-1 font-medium text-gray-700 text-sm dark:text-neutral-300">Currency</label>
|
|
237
|
+
<select id="currency" x-model="newEventCurrency" class="block py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white 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 shadow-sm">
|
|
238
|
+
<option value="USD">USD - US Dollar</option>
|
|
239
|
+
<option value="EUR">EUR - Euro</option>
|
|
240
|
+
<option value="GBP">GBP - British Pound</option>
|
|
241
|
+
<option value="CAD">CAD - Canadian Dollar</option>
|
|
242
|
+
<option value="AUD">AUD - Australian Dollar</option>
|
|
243
|
+
</select>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<div class="flex justify-end mt-6">
|
|
248
|
+
<button @click="closeCreateEvent()" class="inline-flex justify-center mr-2 px-3 py-2 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>
|
|
249
|
+
<button @click="createEvent()" :disabled="!newEventName()" class="inline-flex justify-center px-3 py-2 font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md shadow-sm disabled:opacity-50">Create Event</button>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
<div :show="showDeleteConfirmModal()" @click.self="cancelDelete()" class="flex fixed inset-0 z-50 items-center justify-center p-4 bg-black bg-opacity-50">
|
|
255
|
+
<div class="overflow-y-auto p-6 max-h-[90vh] w-full sm:max-w-lg bg-white dark:bg-neutral-800 rounded-lg shadow-xl">
|
|
256
|
+
<div class="flex items-center justify-center mx-auto h-12 w-12 bg-red-100 dark:bg-red-900 rounded-full">
|
|
257
|
+
<div class="h-6 w-6 text-red-600 dark:text-red-400 i-hugeicons-alert-02"></div>
|
|
258
|
+
</div>
|
|
259
|
+
<div class="mt-3 text-center">
|
|
260
|
+
<h3 class="font-semibold text-gray-900 text-xl dark:text-white">{{ deleteConfirmTitle() }}</h3>
|
|
261
|
+
<p class="mt-2 text-gray-500 text-sm dark:text-neutral-400">{{ deleteConfirmMessage() }}</p>
|
|
262
|
+
</div>
|
|
263
|
+
<div class="flex justify-end mt-6">
|
|
264
|
+
<button @click="cancelDelete()" class="inline-flex justify-center mr-2 px-3 py-2 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>
|
|
265
|
+
<button @click="deleteEvent()" class="inline-flex justify-center px-3 py-2 font-semibold text-sm text-white bg-red-600 hover:bg-red-500 rounded-md shadow-sm">{{ deleteConfirmButtonLabel() }}</button>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
</PageLayout>
|
|
270
|
+
|
|
271
|
+
<script client>
|
|
272
|
+
// Date range options — moved out of the former `<script server>` block
|
|
273
|
+
// (stacksjs/stacks#1838).
|
|
274
|
+
const dateRanges = [
|
|
275
|
+
{ id: 'last7days', name: 'Last 7 Days' },
|
|
276
|
+
{ id: 'last30days', name: 'Last 30 Days' },
|
|
277
|
+
{ id: 'thisMonth', name: 'This Month' },
|
|
278
|
+
{ id: 'lastMonth', name: 'Last Month' },
|
|
279
|
+
{ id: 'thisYear', name: 'This Year' },
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
interface Event {
|
|
283
|
+
id: string
|
|
284
|
+
name: string
|
|
285
|
+
completions: number
|
|
286
|
+
value: number
|
|
287
|
+
conversionRate: number
|
|
288
|
+
status: 'active' | 'blocked'
|
|
289
|
+
createdAt: string
|
|
290
|
+
lastTriggered: string
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
interface EventChartData { date: string, completions: number, value: number }
|
|
294
|
+
|
|
295
|
+
interface ChartLike { destroy: () => void, update: () => void, data: { labels: string[], datasets: Array<{ label?: string, data: number[] }> } }
|
|
296
|
+
type ChartCtor = new (canvas: HTMLElement, config: { type: string, data: unknown, options?: unknown }) => ChartLike
|
|
297
|
+
|
|
298
|
+
const initialEvents: Event[] = [
|
|
299
|
+
{ id: '1', name: 'Newsletter Signup', completions: 342, value: 0, conversionRate: 15.5, status: 'active', createdAt: '2023-11-15', lastTriggered: '2024-03-05' },
|
|
300
|
+
{ id: '2', name: 'Free Trial Started', completions: 128, value: 0, conversionRate: 5.8, status: 'active', createdAt: '2023-11-20', lastTriggered: '2024-03-06' },
|
|
301
|
+
{ id: '3', name: 'Purchase Completed', completions: 87, value: 8700, conversionRate: 3.9, status: 'active', createdAt: '2023-12-01', lastTriggered: '2024-03-06' },
|
|
302
|
+
{ id: '4', name: 'Contact Form Submitted', completions: 215, value: 0, conversionRate: 9.8, status: 'active', createdAt: '2023-12-05', lastTriggered: '2024-03-05' },
|
|
303
|
+
{ id: '5', name: 'Documentation Downloaded', completions: 156, value: 0, conversionRate: 7.1, status: 'active', createdAt: '2024-01-10', lastTriggered: '2024-03-04' },
|
|
304
|
+
{ id: '6', name: 'Checkout Started', completions: 203, value: 0, conversionRate: 9.2, status: 'active', createdAt: '2024-01-15', lastTriggered: '2024-03-06' },
|
|
305
|
+
{ id: '7', name: '404: /old-page', completions: 42, value: 0, conversionRate: 1.9, status: 'blocked', createdAt: '2024-02-01', lastTriggered: '2024-02-28' },
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
const eventChartSeries: Record<string, EventChartData[]> = {
|
|
309
|
+
1: [{ date: '2024-03-01', completions: 12, value: 0 }, { date: '2024-03-02', completions: 15, value: 0 }, { date: '2024-03-03', completions: 10, value: 0 }, { date: '2024-03-04', completions: 18, value: 0 }, { date: '2024-03-05', completions: 22, value: 0 }, { date: '2024-03-06', completions: 14, value: 0 }],
|
|
310
|
+
2: [{ date: '2024-03-01', completions: 5, value: 0 }, { date: '2024-03-02', completions: 7, value: 0 }, { date: '2024-03-03', completions: 4, value: 0 }, { date: '2024-03-04', completions: 6, value: 0 }, { date: '2024-03-05', completions: 8, value: 0 }, { date: '2024-03-06', completions: 5, value: 0 }],
|
|
311
|
+
3: [{ date: '2024-03-01', completions: 3, value: 300 }, { date: '2024-03-02', completions: 5, value: 500 }, { date: '2024-03-03', completions: 2, value: 200 }, { date: '2024-03-04', completions: 4, value: 400 }, { date: '2024-03-05', completions: 6, value: 600 }, { date: '2024-03-06', completions: 4, value: 400 }],
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const events = state<Event[]>(initialEvents)
|
|
315
|
+
const selectedDateRange = state('last30days')
|
|
316
|
+
const comparisonType = state('no-comparison')
|
|
317
|
+
const conversionCalculationType = state<'pageviews' | 'people'>('pageviews')
|
|
318
|
+
|
|
319
|
+
const selectedEventId = state<string | null>(null)
|
|
320
|
+
const showCreateModal = state(false)
|
|
321
|
+
const newEventName = state('')
|
|
322
|
+
const newEventHasValue = state(false)
|
|
323
|
+
const newEventCurrency = state('USD')
|
|
324
|
+
|
|
325
|
+
const eventToDeleteId = state<string | null>(null)
|
|
326
|
+
|
|
327
|
+
const activeEvents = derived(() => events().filter(e => e.status === 'active'))
|
|
328
|
+
const blockedEvents = derived(() => events().filter(e => e.status === 'blocked'))
|
|
329
|
+
|
|
330
|
+
const selectedEvent = derived(() => events().find(e => e.id === selectedEventId()) ?? null)
|
|
331
|
+
const selectedEventName = derived(() => selectedEvent()?.name ?? '')
|
|
332
|
+
|
|
333
|
+
const showEventDetails = derived(() => selectedEvent() !== null)
|
|
334
|
+
const showCreateEventModal = derived(() => showCreateModal())
|
|
335
|
+
const showDeleteConfirmModal = derived(() => eventToDeleteId() !== null)
|
|
336
|
+
|
|
337
|
+
const eventToDelete = derived(() => events().find(e => e.id === eventToDeleteId()) ?? null)
|
|
338
|
+
|
|
339
|
+
const dateRangeDisplay = derived(() => {
|
|
340
|
+
const fmt = (d: Date) => d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
|
341
|
+
const now = new Date()
|
|
342
|
+
const range = selectedDateRange()
|
|
343
|
+
if (range === 'last7days') {
|
|
344
|
+
const start = new Date()
|
|
345
|
+
start.setDate(now.getDate() - 7)
|
|
346
|
+
return `${fmt(start)} → ${fmt(now)}`
|
|
347
|
+
}
|
|
348
|
+
if (range === 'last30days') {
|
|
349
|
+
const start = new Date()
|
|
350
|
+
start.setDate(now.getDate() - 30)
|
|
351
|
+
return `${fmt(start)} → ${fmt(now)}`
|
|
352
|
+
}
|
|
353
|
+
if (range === 'thisMonth')
|
|
354
|
+
return `${fmt(new Date(now.getFullYear(), now.getMonth(), 1))} → ${fmt(now)}`
|
|
355
|
+
if (range === 'lastMonth') {
|
|
356
|
+
const start = new Date(now.getFullYear(), now.getMonth() - 1, 1)
|
|
357
|
+
const end = new Date(now.getFullYear(), now.getMonth(), 0)
|
|
358
|
+
return `${fmt(start)} → ${fmt(end)}`
|
|
359
|
+
}
|
|
360
|
+
if (range === 'thisYear')
|
|
361
|
+
return `${fmt(new Date(now.getFullYear(), 0, 1))} → ${fmt(now)}`
|
|
362
|
+
return ''
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
const conversionFormula = derived(() =>
|
|
366
|
+
conversionCalculationType() === 'pageviews'
|
|
367
|
+
? 'Formula: Event completions / total Page views'
|
|
368
|
+
: 'Formula: Event completions / total People',
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
const deleteConfirmTitle = derived(() => eventToDelete()?.status === 'active' ? 'Block Event' : 'Delete Event')
|
|
372
|
+
const deleteConfirmButtonLabel = derived(() => eventToDelete()?.status === 'active' ? 'Block Event' : 'Delete Event')
|
|
373
|
+
const deleteConfirmMessage = derived(() => {
|
|
374
|
+
const ev = eventToDelete()
|
|
375
|
+
if (!ev)
|
|
376
|
+
return ''
|
|
377
|
+
return ev.status === 'active'
|
|
378
|
+
? `Are you sure you want to block "${ev.name}"? This will prevent data collection for this event in the future.`
|
|
379
|
+
: `Are you sure you want to permanently delete "${ev.name}"? This action cannot be undone.`
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
function formatNumber(value: number): string {
|
|
383
|
+
return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function formatCurrency(value: number, currency = 'USD'): string {
|
|
387
|
+
if (!value)
|
|
388
|
+
return '-'
|
|
389
|
+
return new Intl.NumberFormat('en-US', { style: 'currency', currency, minimumFractionDigits: 2 }).format(value / 100)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function formatDateOrDash(value: string | undefined | null): string {
|
|
393
|
+
if (!value || value === '-')
|
|
394
|
+
return '-'
|
|
395
|
+
return new Date(value).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function viewEventDetails(id: string) {
|
|
399
|
+
selectedEventId.set(id)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function closeEventDetails() {
|
|
403
|
+
selectedEventId.set(null)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function openCreateEvent() {
|
|
407
|
+
newEventName.set('')
|
|
408
|
+
newEventHasValue.set(false)
|
|
409
|
+
newEventCurrency.set('USD')
|
|
410
|
+
showCreateModal.set(true)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function closeCreateEvent() {
|
|
414
|
+
showCreateModal.set(false)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function toggleNewEventHasValue() {
|
|
418
|
+
newEventHasValue.set(!newEventHasValue())
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function createEvent() {
|
|
422
|
+
const name = newEventName().trim()
|
|
423
|
+
if (!name)
|
|
424
|
+
return
|
|
425
|
+
const nextId = (Math.max(0, ...events().map(e => Number.parseInt(e.id, 10))) + 1).toString()
|
|
426
|
+
events.set([...events(), {
|
|
427
|
+
id: nextId,
|
|
428
|
+
name,
|
|
429
|
+
completions: 0,
|
|
430
|
+
value: 0,
|
|
431
|
+
conversionRate: 0,
|
|
432
|
+
status: 'active',
|
|
433
|
+
createdAt: new Date().toISOString().split('T')[0]!,
|
|
434
|
+
lastTriggered: '-',
|
|
435
|
+
}])
|
|
436
|
+
closeCreateEvent()
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function confirmDelete(id: string) {
|
|
440
|
+
eventToDeleteId.set(id)
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function confirmDeleteSelected() {
|
|
444
|
+
const id = selectedEventId()
|
|
445
|
+
if (id)
|
|
446
|
+
eventToDeleteId.set(id)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function cancelDelete() {
|
|
450
|
+
eventToDeleteId.set(null)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function deleteEvent() {
|
|
454
|
+
const target = eventToDelete()
|
|
455
|
+
if (!target)
|
|
456
|
+
return
|
|
457
|
+
if (target.status === 'active') {
|
|
458
|
+
events.set(events().map(e => e.id === target.id ? { ...e, status: 'blocked' } : e))
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
events.set(events().filter(e => e.id !== target.id))
|
|
462
|
+
}
|
|
463
|
+
if (selectedEventId() === target.id)
|
|
464
|
+
selectedEventId.set(null)
|
|
465
|
+
eventToDeleteId.set(null)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function unblockEvent(id: string) {
|
|
469
|
+
events.set(events().map(e => e.id === id ? { ...e, status: 'active' } : e))
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
let chartHandle: ChartLike | null = null
|
|
473
|
+
|
|
474
|
+
async function ensureChartLib() {
|
|
475
|
+
const { Chart, registerables } = await import('@stacksjs/charts')
|
|
476
|
+
Chart.register(...registerables)
|
|
477
|
+
return Chart as unknown as ChartCtor
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async function renderEventChart() {
|
|
481
|
+
const event = selectedEvent()
|
|
482
|
+
if (!event)
|
|
483
|
+
return
|
|
484
|
+
chartHandle?.destroy()
|
|
485
|
+
chartHandle = null
|
|
486
|
+
const Ctor = await ensureChartLib()
|
|
487
|
+
const series = eventChartSeries[event.id] ?? []
|
|
488
|
+
const labels = series.map(item => {
|
|
489
|
+
const d = new Date(item.date)
|
|
490
|
+
return `${d.getMonth() + 1}/${d.getDate()}`
|
|
491
|
+
})
|
|
492
|
+
const handle = useChart({
|
|
493
|
+
Chart: Ctor,
|
|
494
|
+
id: 'event_details_chart',
|
|
495
|
+
type: 'line',
|
|
496
|
+
data: {
|
|
497
|
+
labels,
|
|
498
|
+
datasets: [{
|
|
499
|
+
label: 'Completions',
|
|
500
|
+
data: series.map(s => s.completions),
|
|
501
|
+
backgroundColor: 'rgba(59, 130, 246, 0.3)',
|
|
502
|
+
borderColor: 'rgba(59, 130, 246, 0.8)',
|
|
503
|
+
borderWidth: 2,
|
|
504
|
+
tension: 0.4,
|
|
505
|
+
fill: true,
|
|
506
|
+
}],
|
|
507
|
+
},
|
|
508
|
+
options: {
|
|
509
|
+
responsive: true,
|
|
510
|
+
maintainAspectRatio: false,
|
|
511
|
+
scales: {
|
|
512
|
+
x: { grid: { display: false } },
|
|
513
|
+
y: { beginAtZero: true, grid: { borderDash: [2, 4], color: 'rgba(160, 174, 192, 0.2)' }, ticks: { precision: 0 } },
|
|
514
|
+
},
|
|
515
|
+
plugins: { legend: { display: false }, tooltip: { mode: 'index', intersect: false } },
|
|
516
|
+
},
|
|
517
|
+
})
|
|
518
|
+
chartHandle = handle.instance as ChartLike | null
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
effect(() => {
|
|
522
|
+
if (selectedEvent())
|
|
523
|
+
queueMicrotask(() => renderEventChart())
|
|
524
|
+
else {
|
|
525
|
+
chartHandle?.destroy()
|
|
526
|
+
chartHandle = null
|
|
527
|
+
}
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
onDestroy(() => {
|
|
531
|
+
chartHandle?.destroy()
|
|
532
|
+
chartHandle = null
|
|
533
|
+
})
|
|
534
|
+
</script>
|
|
535
|
+
|
|
536
|
+
@endsection
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
<script client>
|
|
5
|
+
// Analytics Dashboard - macOS Style.
|
|
6
|
+
// Hub list — moved out of the former `<script server>` block
|
|
7
|
+
// (stacksjs/stacks#1838).
|
|
8
|
+
const analyticsCards = [
|
|
9
|
+
{ name: 'Web Analytics', description: 'Page views, sessions, and user behavior', icon: 'i-hugeicons-chart-line-data-02', to: '/analytics/web' },
|
|
10
|
+
{ name: 'Blog Analytics', description: 'Post performance and engagement', icon: 'i-hugeicons-edit-02', to: '/analytics/blog' },
|
|
11
|
+
{ name: 'Commerce Analytics', description: 'Sales and revenue insights', icon: 'i-hugeicons-shopping-cart-01', to: '/analytics/commerce/sales' },
|
|
12
|
+
{ name: 'Marketing Analytics', description: 'Campaign performance', icon: 'i-hugeicons-megaphone-02', to: '/analytics/marketing' },
|
|
13
|
+
{ name: 'Events', description: 'Custom event tracking', icon: 'i-hugeicons-calendar-03', to: '/analytics/events' },
|
|
14
|
+
{ name: 'Referrers', description: 'Traffic sources', icon: 'i-hugeicons-link-01', to: '/analytics/referrers' },
|
|
15
|
+
{ name: 'Pages', description: 'Top performing pages', icon: 'i-hugeicons-file-02', to: '/analytics/pages' },
|
|
16
|
+
{ name: 'Countries', description: 'Geographic distribution', icon: 'i-hugeicons-globe-02', to: '/analytics/countries' },
|
|
17
|
+
{ name: 'Devices', description: 'Device breakdown', icon: 'i-hugeicons-smart-phone-01', to: '/analytics/devices' },
|
|
18
|
+
{ name: 'Browsers', description: 'Browser usage', icon: 'i-hugeicons-browser', to: '/analytics/browsers' },
|
|
19
|
+
]
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
@title('Dashboard - Analytics')
|
|
23
|
+
<div>
|
|
24
|
+
<!-- Overview Stats -->
|
|
25
|
+
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 mb-6">
|
|
26
|
+
<StatsCard
|
|
27
|
+
title="Page Views"
|
|
28
|
+
value="124,892"
|
|
29
|
+
:trend="12"
|
|
30
|
+
trendLabel="vs last month"
|
|
31
|
+
icon="i-hugeicons-chart-line-data-02"
|
|
32
|
+
iconBg="primary"
|
|
33
|
+
/>
|
|
34
|
+
<StatsCard
|
|
35
|
+
title="Unique Visitors"
|
|
36
|
+
value="45,231"
|
|
37
|
+
:trend="8"
|
|
38
|
+
trendLabel="vs last month"
|
|
39
|
+
icon="i-hugeicons-user-multiple-02"
|
|
40
|
+
iconBg="success"
|
|
41
|
+
/>
|
|
42
|
+
<StatsCard
|
|
43
|
+
title="Avg. Session"
|
|
44
|
+
value="4m 32s"
|
|
45
|
+
:trend="-5"
|
|
46
|
+
trendLabel="vs last month"
|
|
47
|
+
icon="i-hugeicons-clock-01"
|
|
48
|
+
iconBg="info"
|
|
49
|
+
/>
|
|
50
|
+
<StatsCard
|
|
51
|
+
title="Bounce Rate"
|
|
52
|
+
value="32.4%"
|
|
53
|
+
:trend="-3"
|
|
54
|
+
trendLabel="Lower is better"
|
|
55
|
+
icon="i-hugeicons-logout-02"
|
|
56
|
+
iconBg="neutral"
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<!-- Analytics Sections -->
|
|
61
|
+
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
|
62
|
+
<template :for="card in analyticsCards">
|
|
63
|
+
<StxLink
|
|
64
|
+
:to="card.to"
|
|
65
|
+
class="block"
|
|
66
|
+
>
|
|
67
|
+
<Card variant="default" padding="md" hoverable clickable>
|
|
68
|
+
<div class="flex gap-4 items-start">
|
|
69
|
+
<div class="flex-shrink-0 p-2.5 text-blue-600 dark:text-blue-400 bg-blue-500/15 dark:bg-blue-400/20 rounded-lg">
|
|
70
|
+
<div :class="card.icon" class="h-5 w-5" />
|
|
71
|
+
</div>
|
|
72
|
+
<div class="flex-1 min-w-0">
|
|
73
|
+
<h3 class="font-medium text-[14px] text-neutral-900 dark:text-white">
|
|
74
|
+
{{ card.name }}
|
|
75
|
+
</h3>
|
|
76
|
+
<p class="mt-1 text-[12px] text-neutral-500 dark:text-neutral-400">
|
|
77
|
+
{{ card.description }}
|
|
78
|
+
</p>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="flex-shrink-0 h-4 w-4 text-neutral-400 i-hugeicons-arrow-right-01" />
|
|
81
|
+
</div>
|
|
82
|
+
</Card>
|
|
83
|
+
</StxLink>
|
|
84
|
+
</template>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
@endsection
|