@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,356 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
@title('Dashboard - Blog Analytics')
|
|
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
|
+
<span class="font-medium text-gray-700 text-sm dark:text-neutral-300">{{ dateRangeDisplay }}</span>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="flex flex-wrap gap-2 items-center">
|
|
12
|
+
<span class="text-gray-500 text-sm dark:text-neutral-400">compared to</span>
|
|
13
|
+
<div class="relative">
|
|
14
|
+
<select x-model="comparisonType" 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">
|
|
15
|
+
<option value="no-comparison">No comparison</option>
|
|
16
|
+
<option value="previous-period">Previous period</option>
|
|
17
|
+
<option value="previous-year">Previous year</option>
|
|
18
|
+
</select>
|
|
19
|
+
</div>
|
|
20
|
+
<button type="button" class="inline-flex items-center px-3 py-1.5 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">
|
|
21
|
+
<div class="mr-1 h-4 w-4 i-hugeicons-settings-01"></div>
|
|
22
|
+
Auto
|
|
23
|
+
</button>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-6 mb-8 p-4 bg-black dark:bg-neutral-900 rounded-lg">
|
|
28
|
+
<div class="flex flex-col items-center justify-center">
|
|
29
|
+
<div class="font-bold text-4xl text-white">{{ analyticsOverview.realtime }}</div>
|
|
30
|
+
<div class="text-gray-400 text-sm">Realtime</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="flex flex-col items-center justify-center">
|
|
33
|
+
<div class="font-bold text-4xl text-white">2.2k</div>
|
|
34
|
+
<div class="text-gray-400 text-sm">People</div>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="flex flex-col items-center justify-center">
|
|
37
|
+
<div class="font-bold text-4xl text-white">3k</div>
|
|
38
|
+
<div class="text-gray-400 text-sm">Views</div>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="flex flex-col items-center justify-center">
|
|
41
|
+
<div class="font-bold text-4xl text-white">{{ analyticsOverview.avgTimeOnSite }}</div>
|
|
42
|
+
<div class="text-gray-400 text-sm">Avg time on site</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="flex flex-col items-center justify-center">
|
|
45
|
+
<div class="font-bold text-4xl text-white">{{ analyticsOverview.bounceRate }}</div>
|
|
46
|
+
<div class="text-gray-400 text-sm">Bounce rate</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="flex flex-col items-center justify-center">
|
|
49
|
+
<div class="font-bold text-4xl text-white">{{ analyticsOverview.eventCompletions }}</div>
|
|
50
|
+
<div class="text-gray-400 text-sm">Event completions</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div class="mb-8 p-4 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
55
|
+
<div class="mb-4">
|
|
56
|
+
<h3 class="font-medium leading-6 text-gray-900 text-lg dark:text-white">Traffic Overview</h3>
|
|
57
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Daily page views and unique visitors</p>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="h-80 w-full"><canvas id="blog_traffic_chart"></canvas></div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div class="grid gap-8 grid-cols-1 lg:grid-cols-2">
|
|
63
|
+
<div>
|
|
64
|
+
<div class="mb-4"><h3 class="font-medium text-gray-900 text-lg dark:text-white">Pages</h3></div>
|
|
65
|
+
<div class="overflow-hidden border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
66
|
+
<table class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
|
|
67
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
68
|
+
<tr>
|
|
69
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Pages</th>
|
|
70
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Entries</th>
|
|
71
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Visitors</th>
|
|
72
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Views</th>
|
|
73
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">%</th>
|
|
74
|
+
</tr>
|
|
75
|
+
</thead>
|
|
76
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
77
|
+
<template :for="page in pagesData">
|
|
78
|
+
<tr class="relative hover:bg-gray-50 dark:hover:bg-neutral-700/50">
|
|
79
|
+
<td class="px-4 py-3 text-gray-900 text-sm whitespace-nowrap dark:text-white">
|
|
80
|
+
<div class="absolute inset-0 bg-blue-100/50 dark:bg-blue-900/50" :style="'width: ' + page.percentage + '%'"></div>
|
|
81
|
+
<span class="relative z-10">{{ page.path }}</span>
|
|
82
|
+
</td>
|
|
83
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ page.entries }}</span></td>
|
|
84
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ page.visitors }}</span></td>
|
|
85
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ page.views }}</span></td>
|
|
86
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ page.percentage }}%</span></td>
|
|
87
|
+
</tr>
|
|
88
|
+
</template>
|
|
89
|
+
</tbody>
|
|
90
|
+
</table>
|
|
91
|
+
</div>
|
|
92
|
+
<div class="flex justify-end mt-2"><StxLink to="/analytics/pages" class="text-blue-600 text-sm dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">View all pages</StxLink></div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div>
|
|
96
|
+
<div class="mb-4"><h3 class="font-medium text-gray-900 text-lg dark:text-white">Referrers</h3></div>
|
|
97
|
+
<div class="overflow-hidden border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
98
|
+
<table class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
|
|
99
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
100
|
+
<tr>
|
|
101
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Sources</th>
|
|
102
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Visitors</th>
|
|
103
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Views</th>
|
|
104
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">%</th>
|
|
105
|
+
</tr>
|
|
106
|
+
</thead>
|
|
107
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
108
|
+
<template :for="referrer in referrersData">
|
|
109
|
+
<tr class="relative hover:bg-gray-50 dark:hover:bg-neutral-700/50">
|
|
110
|
+
<td class="px-4 py-3 text-gray-900 text-sm whitespace-nowrap dark:text-white">
|
|
111
|
+
<div class="absolute inset-0 bg-green-100/50 dark:bg-green-900/50" :style="'width: ' + referrer.percentage + '%'"></div>
|
|
112
|
+
<span class="relative z-10">{{ referrer.name }}</span>
|
|
113
|
+
</td>
|
|
114
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ referrer.visitors }}</span></td>
|
|
115
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ referrer.views }}</span></td>
|
|
116
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ referrer.percentage }}%</span></td>
|
|
117
|
+
</tr>
|
|
118
|
+
</template>
|
|
119
|
+
</tbody>
|
|
120
|
+
</table>
|
|
121
|
+
</div>
|
|
122
|
+
<div class="flex justify-end mt-2"><StxLink to="/analytics/referrers" class="text-blue-600 text-sm dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">View all referrers</StxLink></div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div class="grid gap-8 grid-cols-1 lg:grid-cols-3 mt-8">
|
|
127
|
+
<div>
|
|
128
|
+
<div class="mb-4"><h3 class="font-medium text-gray-900 text-lg dark:text-white">Device Types</h3></div>
|
|
129
|
+
<div class="overflow-hidden border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
130
|
+
<table class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
|
|
131
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
132
|
+
<tr>
|
|
133
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Device Types</th>
|
|
134
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Visitors</th>
|
|
135
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">%</th>
|
|
136
|
+
</tr>
|
|
137
|
+
</thead>
|
|
138
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
139
|
+
<template :for="device in deviceData">
|
|
140
|
+
<tr class="relative hover:bg-gray-50 dark:hover:bg-neutral-700/50">
|
|
141
|
+
<td class="px-4 py-3 text-gray-900 text-sm whitespace-nowrap dark:text-white">
|
|
142
|
+
<div class="absolute inset-0 bg-purple-100/50 dark:bg-purple-900/50" :style="'width: ' + device.percentage + '%'"></div>
|
|
143
|
+
<span class="relative z-10">{{ device.name }}</span>
|
|
144
|
+
</td>
|
|
145
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ device.visitors }}</span></td>
|
|
146
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ device.percentage }}%</span></td>
|
|
147
|
+
</tr>
|
|
148
|
+
</template>
|
|
149
|
+
</tbody>
|
|
150
|
+
</table>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="flex justify-end mt-2"><StxLink to="/analytics/devices" class="text-blue-600 text-sm dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">View all devices</StxLink></div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<div>
|
|
156
|
+
<div class="mb-4"><h3 class="font-medium text-gray-900 text-lg dark:text-white">Browsers</h3></div>
|
|
157
|
+
<div class="overflow-hidden border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
158
|
+
<table class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
|
|
159
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
160
|
+
<tr>
|
|
161
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Browsers</th>
|
|
162
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Visitors</th>
|
|
163
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">%</th>
|
|
164
|
+
</tr>
|
|
165
|
+
</thead>
|
|
166
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
167
|
+
<template :for="browser in browserData">
|
|
168
|
+
<tr class="relative hover:bg-gray-50 dark:hover:bg-neutral-700/50">
|
|
169
|
+
<td class="px-4 py-3 text-gray-900 text-sm whitespace-nowrap dark:text-white">
|
|
170
|
+
<div class="absolute inset-0 bg-amber-100/50 dark:bg-amber-900/50" :style="'width: ' + browser.percentage + '%'"></div>
|
|
171
|
+
<span class="relative z-10">{{ browser.name }}</span>
|
|
172
|
+
</td>
|
|
173
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ browser.visitors }}</span></td>
|
|
174
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ browser.percentage }}%</span></td>
|
|
175
|
+
</tr>
|
|
176
|
+
</template>
|
|
177
|
+
</tbody>
|
|
178
|
+
</table>
|
|
179
|
+
</div>
|
|
180
|
+
<div class="flex justify-end mt-2"><StxLink to="/analytics/browsers" class="text-blue-600 text-sm dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">View all browsers</StxLink></div>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div>
|
|
184
|
+
<div class="mb-4"><h3 class="font-medium text-gray-900 text-lg dark:text-white">Countries</h3></div>
|
|
185
|
+
<div class="overflow-hidden border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
186
|
+
<table class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
|
|
187
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
188
|
+
<tr>
|
|
189
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Countries</th>
|
|
190
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Visitors</th>
|
|
191
|
+
<th class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">%</th>
|
|
192
|
+
</tr>
|
|
193
|
+
</thead>
|
|
194
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
195
|
+
<template :for="country in countriesData">
|
|
196
|
+
<tr class="relative hover:bg-gray-50 dark:hover:bg-neutral-700/50">
|
|
197
|
+
<td class="px-4 py-3 text-gray-900 text-sm whitespace-nowrap dark:text-white">
|
|
198
|
+
<div class="absolute inset-0 bg-rose-100/50 dark:bg-rose-900/50" :style="'width: ' + country.percentage + '%'"></div>
|
|
199
|
+
<span class="relative z-10"><span class="mr-2">{{ country.flag }}</span>{{ country.name }}</span>
|
|
200
|
+
</td>
|
|
201
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ country.visitors }}</span></td>
|
|
202
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300"><span class="relative z-10">{{ country.percentage }}%</span></td>
|
|
203
|
+
</tr>
|
|
204
|
+
</template>
|
|
205
|
+
</tbody>
|
|
206
|
+
</table>
|
|
207
|
+
</div>
|
|
208
|
+
<div class="flex justify-end mt-2"><StxLink to="/analytics/countries" class="text-blue-600 text-sm dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">View all countries</StxLink></div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</PageLayout>
|
|
212
|
+
<script client>
|
|
213
|
+
|
|
214
|
+
interface ChartLike { destroy: () => void }
|
|
215
|
+
type ChartCtor = new (canvas: HTMLElement, config: { type: string, data: unknown, options?: unknown }) => ChartLike
|
|
216
|
+
|
|
217
|
+
// Static analytics tables + overview — moved out of the former
|
|
218
|
+
// `<script server>` block (stacksjs/stacks#1838).
|
|
219
|
+
const pagesRaw = [
|
|
220
|
+
{ path: '/', entries: 1800, visitors: 1800, views: 2100 },
|
|
221
|
+
{ path: '/blog', entries: 113, visitors: 113, views: 139 },
|
|
222
|
+
{ path: '/about', entries: 51, visitors: 51, views: 77 },
|
|
223
|
+
{ path: '/contact', entries: 42, visitors: 42, views: 51 },
|
|
224
|
+
{ path: '/products', entries: 37, visitors: 37, views: 60 },
|
|
225
|
+
{ path: '/guide/get-started.html', entries: 21, visitors: 29, views: 45 },
|
|
226
|
+
{ path: '/docs', entries: 16, visitors: 17, views: 202 },
|
|
227
|
+
{ path: '/guide/intro.html', entries: 12, visitors: 17, views: 22 },
|
|
228
|
+
{ path: '/guide/get-started', entries: 9, visitors: 12, views: 13 },
|
|
229
|
+
{ path: '/team', entries: 9, visitors: 10, views: 15 },
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
const referrersRaw = [
|
|
233
|
+
{ name: 'Direct / Unknown', visitors: 1400, views: 2100 },
|
|
234
|
+
{ name: 'X/Twitter', visitors: 545, views: 580 },
|
|
235
|
+
{ name: 'Google', visitors: 46, views: 50 },
|
|
236
|
+
{ name: 'stacksjs.com', visitors: 45, views: 63 },
|
|
237
|
+
{ name: 'stacksjs.netlify.app', visitors: 38, views: 60 },
|
|
238
|
+
{ name: 'github.com', visitors: 34, views: 47 },
|
|
239
|
+
{ name: 'DuckDuckGo', visitors: 32, views: 35 },
|
|
240
|
+
{ name: 'Bing', visitors: 22, views: 24 },
|
|
241
|
+
{ name: 'Baidu', visitors: 8, views: 8 },
|
|
242
|
+
{ name: 'npmjs.com', visitors: 7, views: 7 },
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
const deviceData = [
|
|
246
|
+
{ name: 'Desktop', visitors: 1300, percentage: 60 },
|
|
247
|
+
{ name: 'Phone', visitors: 874, percentage: 38 },
|
|
248
|
+
{ name: 'Tablet', visitors: 5, percentage: 2 },
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
const browserData = [
|
|
252
|
+
{ name: 'Chrome', visitors: 1300, percentage: 58 },
|
|
253
|
+
{ name: 'Safari', visitors: 579, percentage: 26 },
|
|
254
|
+
{ name: 'Edge', visitors: 129, percentage: 6 },
|
|
255
|
+
{ name: 'Firefox', visitors: 92, percentage: 4 },
|
|
256
|
+
{ name: 'Mozilla', visitors: 24, percentage: 1 },
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
const countriesRaw = [
|
|
260
|
+
{ name: 'United States of America', visitors: 768, flag: '🇺🇸' },
|
|
261
|
+
{ name: 'Germany', visitors: 140, flag: '🇩🇪' },
|
|
262
|
+
{ name: 'United Kingdom', visitors: 87, flag: '🇬🇧' },
|
|
263
|
+
{ name: 'India', visitors: 83, flag: '🇮🇳' },
|
|
264
|
+
{ name: 'Indonesia', visitors: 80, flag: '🇮🇩' },
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
const totalPageViews = pagesRaw.reduce((s, p) => s + p.views, 0)
|
|
268
|
+
const totalReferrerViews = referrersRaw.reduce((s, r) => s + r.views, 0)
|
|
269
|
+
const totalCountryVisitors = countriesRaw.reduce((s, c) => s + c.visitors, 0)
|
|
270
|
+
|
|
271
|
+
const pagesData = pagesRaw.map(p => ({ ...p, percentage: Math.round((p.views / totalPageViews) * 100) }))
|
|
272
|
+
const referrersData = referrersRaw.map(r => ({ ...r, percentage: Math.round((r.views / totalReferrerViews) * 100) }))
|
|
273
|
+
const countriesData = countriesRaw.map(c => ({ ...c, percentage: Math.round((c.visitors / totalCountryVisitors) * 100) }))
|
|
274
|
+
|
|
275
|
+
const analyticsOverview = {
|
|
276
|
+
realtime: 0,
|
|
277
|
+
people: 2200,
|
|
278
|
+
views: 3000,
|
|
279
|
+
avgTimeOnSite: '03:36',
|
|
280
|
+
bounceRate: '89%',
|
|
281
|
+
eventCompletions: 0,
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const dateRangeDisplay = state('All Time: Sep 7, 2023 to Mar 6, 2025')
|
|
285
|
+
const comparisonType = state('no-comparison')
|
|
286
|
+
|
|
287
|
+
const trafficData = state<Array<{ date: string, pageViews: number, visitors: number }>>([
|
|
288
|
+
{ date: '2023-11-01', pageViews: 100, visitors: 80 },
|
|
289
|
+
{ date: '2023-11-15', pageViews: 250, visitors: 180 },
|
|
290
|
+
{ date: '2023-12-01', pageViews: 150, visitors: 120 },
|
|
291
|
+
{ date: '2023-12-15', pageViews: 220, visitors: 170 },
|
|
292
|
+
{ date: '2024-01-01', pageViews: 180, visitors: 140 },
|
|
293
|
+
{ date: '2024-01-15', pageViews: 320, visitors: 240 },
|
|
294
|
+
{ date: '2024-02-01', pageViews: 280, visitors: 210 },
|
|
295
|
+
{ date: '2024-02-15', pageViews: 400, visitors: 300 },
|
|
296
|
+
{ date: '2024-03-01', pageViews: 350, visitors: 270 },
|
|
297
|
+
{ date: '2024-03-15', pageViews: 300, visitors: 230 },
|
|
298
|
+
{ date: '2024-04-01', pageViews: 250, visitors: 190 },
|
|
299
|
+
{ date: '2024-04-15', pageViews: 200, visitors: 150 },
|
|
300
|
+
{ date: '2024-05-01', pageViews: 150, visitors: 120 },
|
|
301
|
+
{ date: '2024-05-15', pageViews: 120, visitors: 90 },
|
|
302
|
+
{ date: '2024-06-01', pageViews: 100, visitors: 80 },
|
|
303
|
+
{ date: '2024-06-15', pageViews: 130, visitors: 100 },
|
|
304
|
+
{ date: '2024-07-01', pageViews: 150, visitors: 120 },
|
|
305
|
+
{ date: '2024-07-15', pageViews: 120, visitors: 90 },
|
|
306
|
+
{ date: '2024-08-01', pageViews: 100, visitors: 80 },
|
|
307
|
+
{ date: '2024-08-15', pageViews: 80, visitors: 60 },
|
|
308
|
+
{ date: '2024-09-01', pageViews: 100, visitors: 80 },
|
|
309
|
+
{ date: '2024-09-15', pageViews: 120, visitors: 90 },
|
|
310
|
+
{ date: '2024-10-01', pageViews: 150, visitors: 120 },
|
|
311
|
+
{ date: '2024-10-15', pageViews: 100, visitors: 80 },
|
|
312
|
+
{ date: '2024-11-01', pageViews: 80, visitors: 60 },
|
|
313
|
+
{ date: '2024-11-15', pageViews: 120, visitors: 90 },
|
|
314
|
+
{ date: '2024-12-01', pageViews: 300, visitors: 230 },
|
|
315
|
+
{ date: '2024-12-15', pageViews: 150, visitors: 120 },
|
|
316
|
+
{ date: '2025-01-01', pageViews: 100, visitors: 80 },
|
|
317
|
+
{ date: '2025-01-15', pageViews: 80, visitors: 60 },
|
|
318
|
+
{ date: '2025-02-01', pageViews: 60, visitors: 40 },
|
|
319
|
+
])
|
|
320
|
+
|
|
321
|
+
let chartHandle: { destroy: () => void } | null = null
|
|
322
|
+
|
|
323
|
+
onMount(async () => {
|
|
324
|
+
const { Chart, registerables } = await import('@stacksjs/charts')
|
|
325
|
+
Chart.register(...registerables)
|
|
326
|
+
const Ctor = Chart as unknown as ChartCtor
|
|
327
|
+
chartHandle = useChart({
|
|
328
|
+
Chart: Ctor,
|
|
329
|
+
id: 'blog_traffic_chart',
|
|
330
|
+
type: 'line',
|
|
331
|
+
data: {
|
|
332
|
+
labels: trafficData().map(item => item.date),
|
|
333
|
+
datasets: [
|
|
334
|
+
{ label: 'Page Views', data: trafficData().map(item => item.pageViews), backgroundColor: 'rgba(147, 197, 253, 0.3)', borderColor: 'rgba(59, 130, 246, 0.8)', borderWidth: 2, tension: 0.4, fill: true },
|
|
335
|
+
{ label: 'Visitors', data: trafficData().map(item => item.visitors), backgroundColor: 'rgba(147, 197, 253, 0.1)', borderColor: 'rgba(96, 165, 250, 0.8)', borderWidth: 2, tension: 0.4, fill: false },
|
|
336
|
+
],
|
|
337
|
+
},
|
|
338
|
+
options: {
|
|
339
|
+
responsive: true,
|
|
340
|
+
maintainAspectRatio: false,
|
|
341
|
+
scales: {
|
|
342
|
+
x: { grid: { display: false }, ticks: { maxTicksLimit: 4 } },
|
|
343
|
+
y: { beginAtZero: true, grid: { borderDash: [2, 4], color: 'rgba(160, 174, 192, 0.2)' }, ticks: { maxTicksLimit: 6 } },
|
|
344
|
+
},
|
|
345
|
+
plugins: { legend: { display: true, position: 'top' }, tooltip: { mode: 'index', intersect: false } },
|
|
346
|
+
},
|
|
347
|
+
})
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
onDestroy(() => {
|
|
351
|
+
chartHandle?.destroy()
|
|
352
|
+
chartHandle = null
|
|
353
|
+
})
|
|
354
|
+
</script>
|
|
355
|
+
|
|
356
|
+
@endsection
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
@title('Dashboard - Browser Analytics')
|
|
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
|
+
<span class="font-medium text-gray-700 text-sm dark:text-neutral-300">{{ dateRangeDisplay }}</span>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="flex flex-wrap gap-2 items-center">
|
|
13
|
+
<span class="text-gray-500 text-sm dark:text-neutral-400">compared to</span>
|
|
14
|
+
<div class="relative">
|
|
15
|
+
<select x-model="comparisonType" 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">
|
|
16
|
+
<option value="no-comparison">No comparison</option>
|
|
17
|
+
<option value="previous-period">Previous period</option>
|
|
18
|
+
<option value="previous-year">Previous year</option>
|
|
19
|
+
</select>
|
|
20
|
+
</div>
|
|
21
|
+
<button type="button" class="inline-flex items-center px-3 py-1.5 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">
|
|
22
|
+
<div class="mr-1 h-4 w-4 i-hugeicons-settings-01"></div>
|
|
23
|
+
Auto
|
|
24
|
+
</button>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="mb-8">
|
|
29
|
+
<h1 class="font-bold text-2xl text-gray-900 dark:text-white">Browser Analytics</h1>
|
|
30
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Detailed breakdown of browser usage across your website</p>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="grid gap-8 grid-cols-1 lg:grid-cols-2 mb-8">
|
|
34
|
+
<div class="p-4 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
35
|
+
<div class="mb-4">
|
|
36
|
+
<h3 class="font-medium leading-6 text-gray-900 text-lg dark:text-white">Browser Distribution</h3>
|
|
37
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Percentage of visitors by browser</p>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="h-80 w-full">
|
|
40
|
+
<canvas id="browserDistributionChart"></canvas>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="p-4 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
45
|
+
<div class="mb-4">
|
|
46
|
+
<h3 class="font-medium leading-6 text-gray-900 text-lg dark:text-white">Browser Trends</h3>
|
|
47
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Browser usage trends over time</p>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="h-80 w-full">
|
|
50
|
+
<canvas id="browserTrendChart"></canvas>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="mb-8">
|
|
56
|
+
<div class="mb-4">
|
|
57
|
+
<h3 class="font-medium text-gray-900 text-lg dark:text-white">Browser Overview</h3>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="overflow-hidden border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
61
|
+
<table class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
|
|
62
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
63
|
+
<tr>
|
|
64
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Browser</th>
|
|
65
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Visitors</th>
|
|
66
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">%</th>
|
|
67
|
+
</tr>
|
|
68
|
+
</thead>
|
|
69
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
70
|
+
<template :for="(browser, index) in browserData">
|
|
71
|
+
<tr class="relative hover:bg-gray-50 dark:hover:bg-neutral-700/50">
|
|
72
|
+
<td class="px-4 py-3 text-gray-900 text-sm whitespace-nowrap dark:text-white">
|
|
73
|
+
<div class="absolute inset-0 bg-amber-100/50 dark:bg-amber-900/50" style="width: {{ browser.percentage }}%"></div>
|
|
74
|
+
<span class="relative z-10">{{ browser.name }}</span>
|
|
75
|
+
</td>
|
|
76
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">
|
|
77
|
+
<span class="relative z-10">{{ browser.visitors }}</span>
|
|
78
|
+
</td>
|
|
79
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">
|
|
80
|
+
<span class="relative z-10">{{ browser.percentage }}%</span>
|
|
81
|
+
</td>
|
|
82
|
+
</tr>
|
|
83
|
+
</template>
|
|
84
|
+
</tbody>
|
|
85
|
+
</table>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div>
|
|
90
|
+
<div class="mb-4">
|
|
91
|
+
<h3 class="font-medium text-gray-900 text-lg dark:text-white">Detailed Browser Data</h3>
|
|
92
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Browser usage by version and operating system</p>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div class="overflow-hidden border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
96
|
+
<table class="min-w-full divide-gray-200 divide-y dark:divide-neutral-700">
|
|
97
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
98
|
+
<tr>
|
|
99
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Browser</th>
|
|
100
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">Version</th>
|
|
101
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-left text-xs tracking-wider uppercase dark:text-neutral-300">OS</th>
|
|
102
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">Visitors</th>
|
|
103
|
+
<th scope="col" class="px-4 py-3 font-medium text-gray-500 text-right text-xs tracking-wider uppercase dark:text-neutral-300">%</th>
|
|
104
|
+
</tr>
|
|
105
|
+
</thead>
|
|
106
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
107
|
+
<template :for="(browser, index) in detailedBrowserData">
|
|
108
|
+
<tr class="relative hover:bg-gray-50 dark:hover:bg-neutral-700/50">
|
|
109
|
+
<td class="px-4 py-3 text-gray-900 text-sm whitespace-nowrap dark:text-white">
|
|
110
|
+
<div class="absolute inset-0 bg-amber-100/50 dark:bg-amber-900/50" style="width: {{ browser.percentage }}%"></div>
|
|
111
|
+
<span class="relative z-10">{{ browser.name }}</span>
|
|
112
|
+
</td>
|
|
113
|
+
<td class="relative px-4 py-3 text-gray-900 text-sm whitespace-nowrap dark:text-white">
|
|
114
|
+
<span class="relative z-10">{{ browser.version }}</span>
|
|
115
|
+
</td>
|
|
116
|
+
<td class="relative px-4 py-3 text-gray-900 text-sm whitespace-nowrap dark:text-white">
|
|
117
|
+
<span class="relative z-10">{{ browser.os }}</span>
|
|
118
|
+
</td>
|
|
119
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">
|
|
120
|
+
<span class="relative z-10">{{ browser.visitors }}</span>
|
|
121
|
+
</td>
|
|
122
|
+
<td class="relative px-4 py-3 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-300">
|
|
123
|
+
<span class="relative z-10">{{ browser.percentage }}%</span>
|
|
124
|
+
</td>
|
|
125
|
+
</tr>
|
|
126
|
+
</template>
|
|
127
|
+
</tbody>
|
|
128
|
+
</table>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</PageLayout>
|
|
132
|
+
<script client>
|
|
133
|
+
|
|
134
|
+
interface BrowserRow {
|
|
135
|
+
name: string
|
|
136
|
+
visitors: number
|
|
137
|
+
percentage: number
|
|
138
|
+
version?: string
|
|
139
|
+
os?: string
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
interface ChartLike { destroy: () => void }
|
|
143
|
+
type ChartCtor = new (canvas: HTMLElement, config: { type: string, data: unknown, options?: unknown }) => ChartLike
|
|
144
|
+
|
|
145
|
+
const dateRangeDisplay = state('All Time: Sep 7, 2023 to Mar 6, 2025')
|
|
146
|
+
const comparisonType = state('no-comparison')
|
|
147
|
+
|
|
148
|
+
const browserData = state<BrowserRow[]>([
|
|
149
|
+
{ name: 'Chrome', visitors: 1300, percentage: 58 },
|
|
150
|
+
{ name: 'Safari', visitors: 579, percentage: 26 },
|
|
151
|
+
{ name: 'Edge', visitors: 129, percentage: 6 },
|
|
152
|
+
{ name: 'Firefox', visitors: 92, percentage: 4 },
|
|
153
|
+
{ name: 'Mozilla', visitors: 24, percentage: 1 },
|
|
154
|
+
{ name: 'Opera', visitors: 18, percentage: 0.8 },
|
|
155
|
+
{ name: 'Samsung Internet', visitors: 15, percentage: 0.7 },
|
|
156
|
+
{ name: 'UC Browser', visitors: 12, percentage: 0.5 },
|
|
157
|
+
{ name: 'Internet Explorer', visitors: 8, percentage: 0.4 },
|
|
158
|
+
{ name: 'Brave', visitors: 7, percentage: 0.3 },
|
|
159
|
+
])
|
|
160
|
+
|
|
161
|
+
const detailedBrowserData = state<BrowserRow[]>([
|
|
162
|
+
{ name: 'Chrome', version: '120.0.6099', os: 'Windows', visitors: 450, percentage: 20 },
|
|
163
|
+
{ name: 'Chrome', version: '120.0.6099', os: 'macOS', visitors: 380, percentage: 17 },
|
|
164
|
+
{ name: 'Chrome', version: '120.0.6099', os: 'Linux', visitors: 220, percentage: 10 },
|
|
165
|
+
{ name: 'Chrome', version: '119.0.6045', os: 'Windows', visitors: 150, percentage: 7 },
|
|
166
|
+
{ name: 'Chrome', version: '119.0.6045', os: 'macOS', visitors: 100, percentage: 4 },
|
|
167
|
+
{ name: 'Safari', version: '17.2', os: 'macOS', visitors: 320, percentage: 14 },
|
|
168
|
+
{ name: 'Safari', version: '17.1', os: 'macOS', visitors: 180, percentage: 8 },
|
|
169
|
+
{ name: 'Safari', version: '16.5', os: 'iOS', visitors: 79, percentage: 4 },
|
|
170
|
+
{ name: 'Edge', version: '120.0.2210', os: 'Windows', visitors: 89, percentage: 4 },
|
|
171
|
+
{ name: 'Edge', version: '120.0.2210', os: 'macOS', visitors: 40, percentage: 2 },
|
|
172
|
+
{ name: 'Firefox', version: '121.0', os: 'Windows', visitors: 52, percentage: 2 },
|
|
173
|
+
{ name: 'Firefox', version: '121.0', os: 'macOS', visitors: 40, percentage: 2 },
|
|
174
|
+
])
|
|
175
|
+
|
|
176
|
+
const browserTrendData = state<Array<{ date: string, Chrome: number, Safari: number, Edge: number, Firefox: number, Other: number }>>([
|
|
177
|
+
{ date: '2023-09', Chrome: 800, Safari: 400, Edge: 80, Firefox: 60, Other: 30 },
|
|
178
|
+
{ date: '2023-10', Chrome: 850, Safari: 420, Edge: 85, Firefox: 65, Other: 32 },
|
|
179
|
+
{ date: '2023-11', Chrome: 900, Safari: 450, Edge: 90, Firefox: 70, Other: 35 },
|
|
180
|
+
{ date: '2023-12', Chrome: 950, Safari: 480, Edge: 95, Firefox: 75, Other: 38 },
|
|
181
|
+
{ date: '2024-01', Chrome: 1000, Safari: 500, Edge: 100, Firefox: 80, Other: 40 },
|
|
182
|
+
{ date: '2024-02', Chrome: 1100, Safari: 520, Edge: 110, Firefox: 85, Other: 42 },
|
|
183
|
+
{ date: '2024-03', Chrome: 1200, Safari: 550, Edge: 120, Firefox: 90, Other: 45 },
|
|
184
|
+
{ date: '2024-04', Chrome: 1250, Safari: 560, Edge: 125, Firefox: 92, Other: 47 },
|
|
185
|
+
{ date: '2024-05', Chrome: 1300, Safari: 579, Edge: 129, Firefox: 92, Other: 50 },
|
|
186
|
+
])
|
|
187
|
+
|
|
188
|
+
let chartHandles: Array<{ destroy: () => void }> = []
|
|
189
|
+
|
|
190
|
+
onMount(async () => {
|
|
191
|
+
const { Chart, registerables } = await import('@stacksjs/charts')
|
|
192
|
+
Chart.register(...registerables)
|
|
193
|
+
const Ctor = Chart as unknown as ChartCtor
|
|
194
|
+
|
|
195
|
+
const top = browserData().slice(0, 5)
|
|
196
|
+
const distribution = useChart({
|
|
197
|
+
Chart: Ctor,
|
|
198
|
+
id: 'browserDistributionChart',
|
|
199
|
+
type: 'pie',
|
|
200
|
+
data: {
|
|
201
|
+
labels: top.map(item => item.name),
|
|
202
|
+
datasets: [{
|
|
203
|
+
data: top.map(item => item.visitors),
|
|
204
|
+
backgroundColor: ['rgba(59, 130, 246, 0.8)', 'rgba(16, 185, 129, 0.8)', 'rgba(245, 158, 11, 0.8)', 'rgba(239, 68, 68, 0.8)', 'rgba(139, 92, 246, 0.8)'],
|
|
205
|
+
borderColor: 'rgba(255, 255, 255, 0.8)',
|
|
206
|
+
borderWidth: 1,
|
|
207
|
+
}],
|
|
208
|
+
},
|
|
209
|
+
options: {
|
|
210
|
+
responsive: true,
|
|
211
|
+
maintainAspectRatio: false,
|
|
212
|
+
plugins: {
|
|
213
|
+
legend: { position: 'right', labels: { padding: 20, font: { size: 12 } } },
|
|
214
|
+
tooltip: {
|
|
215
|
+
callbacks: {
|
|
216
|
+
label(context: { label?: string, raw?: number, dataset: { data: number[] } }) {
|
|
217
|
+
const label = context.label || ''
|
|
218
|
+
const value = context.raw || 0
|
|
219
|
+
const total = context.dataset.data.reduce((a: number, b: number) => a + b, 0)
|
|
220
|
+
const percentage = Math.round((value / total) * 100)
|
|
221
|
+
return `${label}: ${value} visitors (${percentage}%)`
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
const trend = useChart({
|
|
230
|
+
Chart: Ctor,
|
|
231
|
+
id: 'browserTrendChart',
|
|
232
|
+
type: 'line',
|
|
233
|
+
data: {
|
|
234
|
+
labels: browserTrendData().map(item => item.date),
|
|
235
|
+
datasets: [
|
|
236
|
+
{ label: 'Chrome', data: browserTrendData().map(item => item.Chrome), borderColor: 'rgba(59, 130, 246, 0.8)', backgroundColor: 'rgba(59, 130, 246, 0.1)', tension: 0.4, fill: false },
|
|
237
|
+
{ label: 'Safari', data: browserTrendData().map(item => item.Safari), borderColor: 'rgba(16, 185, 129, 0.8)', backgroundColor: 'rgba(16, 185, 129, 0.1)', tension: 0.4, fill: false },
|
|
238
|
+
{ label: 'Edge', data: browserTrendData().map(item => item.Edge), borderColor: 'rgba(245, 158, 11, 0.8)', backgroundColor: 'rgba(245, 158, 11, 0.1)', tension: 0.4, fill: false },
|
|
239
|
+
{ label: 'Firefox', data: browserTrendData().map(item => item.Firefox), borderColor: 'rgba(239, 68, 68, 0.8)', backgroundColor: 'rgba(239, 68, 68, 0.1)', tension: 0.4, fill: false },
|
|
240
|
+
{ label: 'Other', data: browserTrendData().map(item => item.Other), borderColor: 'rgba(139, 92, 246, 0.8)', backgroundColor: 'rgba(139, 92, 246, 0.1)', tension: 0.4, fill: false },
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
options: {
|
|
244
|
+
responsive: true,
|
|
245
|
+
maintainAspectRatio: false,
|
|
246
|
+
scales: {
|
|
247
|
+
x: { grid: { display: false } },
|
|
248
|
+
y: { beginAtZero: true, grid: { borderDash: [2, 4], color: 'rgba(160, 174, 192, 0.2)' }, ticks: { maxTicksLimit: 6 } },
|
|
249
|
+
},
|
|
250
|
+
plugins: { legend: { position: 'top' }, tooltip: { mode: 'index', intersect: false } },
|
|
251
|
+
},
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
chartHandles = [distribution.instance, trend.instance].filter((c): c is ChartLike => c !== null)
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
onDestroy(() => {
|
|
258
|
+
chartHandles.forEach(c => c.destroy())
|
|
259
|
+
chartHandles = []
|
|
260
|
+
})
|
|
261
|
+
</script>
|
|
262
|
+
|
|
263
|
+
@endsection
|