@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,270 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
@title('Dashboard - Subscribers')
|
|
5
|
+
<div class="py-4 lg:py-8 min-h-screen dark:bg-neutral-800">
|
|
6
|
+
<div class="mb-8 px-4 sm:px-6 lg:px-8">
|
|
7
|
+
<h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-neutral-100">Last 30 days</h3>
|
|
8
|
+
<dl class="grid gap-5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 mt-5">
|
|
9
|
+
<div class="overflow-hidden relative pt-5 px-4 sm:pt-6 sm:px-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
10
|
+
<dt>
|
|
11
|
+
<div class="absolute p-3 bg-blue-500 rounded-md">
|
|
12
|
+
<svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" /></svg>
|
|
13
|
+
</div>
|
|
14
|
+
<p class="ml-16 font-medium text-gray-500 text-sm truncate">Total Subscribers</p>
|
|
15
|
+
</dt>
|
|
16
|
+
<dd class="flex items-baseline ml-16 pb-6 sm:pb-7">
|
|
17
|
+
<p class="font-semibold text-2xl text-gray-900 dark:text-neutral-100">1,234</p>
|
|
18
|
+
<p class="flex items-baseline ml-2 font-semibold text-green-600 text-sm">122</p>
|
|
19
|
+
</dd>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="overflow-hidden relative pt-5 px-4 sm:pt-6 sm:px-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
22
|
+
<dt>
|
|
23
|
+
<div class="absolute p-3 bg-blue-500 rounded-md">
|
|
24
|
+
<svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 9v.906a2.25 2.25 0 01-1.183 1.981l-6.478 3.488M2.25 9v.906a2.25 2.25 0 001.183 1.981l6.478 3.488m8.839 2.51l-4.66-2.51m0 0l-1.023-.55a2.25 2.25 0 00-2.134 0l-1.022.55m0 0l-4.661 2.51m16.5 1.615a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V8.844a2.25 2.25 0 011.183-1.98l7.5-4.04a2.25 2.25 0 012.134 0l7.5 4.04a2.25 2.25 0 011.183 1.98V19.5z" /></svg>
|
|
25
|
+
</div>
|
|
26
|
+
<p class="ml-16 font-medium text-gray-500 text-sm truncate">Unsubscribes</p>
|
|
27
|
+
</dt>
|
|
28
|
+
<dd class="flex items-baseline ml-16 pb-6 sm:pb-7">
|
|
29
|
+
<p class="font-semibold text-2xl text-gray-900 dark:text-neutral-100">12</p>
|
|
30
|
+
<p class="flex items-baseline ml-2 font-semibold text-green-600 text-sm">4</p>
|
|
31
|
+
</dd>
|
|
32
|
+
</div>
|
|
33
|
+
</dl>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="mb-8 px-4 sm:px-6 lg:px-8">
|
|
37
|
+
<div class="bg-white dark:bg-neutral-700 rounded-lg shadow">
|
|
38
|
+
<div class="p-6">
|
|
39
|
+
<div class="flex items-center justify-between mb-6">
|
|
40
|
+
<div>
|
|
41
|
+
<h3 class="font-medium text-base text-gray-900 dark:text-neutral-100">Subscriber Growth</h3>
|
|
42
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Track subscriber count over time</p>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="flex items-center space-x-4">
|
|
45
|
+
<select x-model="timeRange" @change="changeTimeRange()" class="pl-3 pr-8 py-1.5 h-9 text-gray-900 text-sm dark:text-neutral-100 bg-gray-50 dark:bg-neutral-600 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600">
|
|
46
|
+
<option value="7">Last 7 days</option>
|
|
47
|
+
<option value="30">Last 30 days</option>
|
|
48
|
+
<option value="90">Last 90 days</option>
|
|
49
|
+
<option value="365">Last year</option>
|
|
50
|
+
</select>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="relative h-[400px]">
|
|
54
|
+
<div :show="isLoading" class="flex absolute inset-0 z-10 items-center justify-center bg-opacity-75 bg-white dark:bg-neutral-700 dark:bg-opacity-75">
|
|
55
|
+
<div class="h-8 w-8 border-b-2 border-blue-500 rounded-full animate-spin"></div>
|
|
56
|
+
</div>
|
|
57
|
+
<canvas id="subscriber_growth_chart"></canvas>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="mb-8 px-4 sm:px-6 lg:px-8">
|
|
64
|
+
<div class="bg-white dark:bg-neutral-700 rounded-lg shadow">
|
|
65
|
+
<div class="p-6">
|
|
66
|
+
<div class="flex items-center justify-between mb-6">
|
|
67
|
+
<div>
|
|
68
|
+
<h3 class="font-medium text-base text-gray-900 dark:text-neutral-100">Subscriber Model Relationships</h3>
|
|
69
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Click any model to view details.</p>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="flex">
|
|
73
|
+
<div class="flex relative flex-1 flex-wrap gap-4 items-start justify-start p-4 h-[400px] bg-gray-50 dark:bg-neutral-600 rounded-lg">
|
|
74
|
+
<template :for="model in models">
|
|
75
|
+
<button type="button" @click="selectModel(model)" class="inline-flex flex-col items-center px-4 py-3 bg-white dark:bg-neutral-800 hover:bg-gray-100 dark:hover:bg-neutral-700 ring-1 ring-gray-200 rounded-lg dark:ring-neutral-600 shadow-sm">
|
|
76
|
+
<span class="text-3xl">{{ model.emoji }}</span>
|
|
77
|
+
<span class="mt-2 font-medium text-gray-900 text-sm dark:text-neutral-100">{{ model.name }}</span>
|
|
78
|
+
</button>
|
|
79
|
+
</template>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div :show="selectedModel() !== null" class="ml-6 p-4 w-64 bg-gray-50 dark:bg-neutral-600 rounded-lg">
|
|
83
|
+
<div class="flex items-center mb-4">
|
|
84
|
+
<span class="mr-2 text-2xl">{{ selectedModel()?.emoji }}</span>
|
|
85
|
+
<h4 class="font-semibold text-gray-900 text-lg dark:text-neutral-100">{{ selectedModel()?.name }}</h4>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="mb-4">
|
|
88
|
+
<p class="text-gray-600 text-sm dark:text-neutral-400">{{ selectedModel()?.description }}</p>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="mb-4">
|
|
91
|
+
<h5 class="mb-2 font-medium text-gray-700 text-sm dark:text-neutral-300">Properties</h5>
|
|
92
|
+
<ul class="space-y-1">
|
|
93
|
+
<template :for="prop in (selectedModel?.properties ?? [])">
|
|
94
|
+
<li class="font-mono text-gray-600 text-sm dark:text-neutral-400">{{ prop }}</li>
|
|
95
|
+
</template>
|
|
96
|
+
</ul>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="mb-6">
|
|
99
|
+
<h5 class="mb-2 font-medium text-gray-700 text-sm dark:text-neutral-300">Relationships</h5>
|
|
100
|
+
<ul class="space-y-1">
|
|
101
|
+
<template :for="rel in (selectedModel?.relationships ?? [])">
|
|
102
|
+
<li class="font-mono text-sm">
|
|
103
|
+
<a :href="resolveModelRoute(rel)" class="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300">{{ rel }}</a>
|
|
104
|
+
</li>
|
|
105
|
+
</template>
|
|
106
|
+
</ul>
|
|
107
|
+
</div>
|
|
108
|
+
<a :show="selectedModel()?.id !== 'subscriber'" :href="resolveModelRoute(selectedModel()?.id ?? '')" class="block px-4 py-2 w-full font-semibold text-center text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md shadow-sm">View Details</a>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="pt-12 px-4 sm:px-6 lg:px-8">
|
|
116
|
+
<div class="sm:flex sm:items-center">
|
|
117
|
+
<div class="sm:flex-auto">
|
|
118
|
+
<h1 class="font-semibold leading-6 text-base text-gray-900 dark:text-neutral-100">Subscribers</h1>
|
|
119
|
+
<p class="mt-2 text-gray-700 text-sm dark:text-neutral-400">A list of all your project subscribers.</p>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="sm:flex-none mt-4 sm:ml-16 sm:mt-0">
|
|
122
|
+
<button type="button" class="block px-3 py-2 font-semibold text-center text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 shadow-sm">Add Subscriber</button>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="flow-root mt-8">
|
|
126
|
+
<div class="overflow-x-auto -mx-4 -my-2 sm:-mx-6 lg:-mx-8">
|
|
127
|
+
<div class="inline-block py-2 sm:px-6 lg:px-8 min-w-full align-middle">
|
|
128
|
+
<div class="overflow-hidden ring-1 ring-black ring-opacity-5 sm:rounded-lg shadow">
|
|
129
|
+
<table class="min-w-full divide-gray-300 divide-y">
|
|
130
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
131
|
+
<tr>
|
|
132
|
+
<th class="pl-4 pr-3 py-3.5 sm:pl-6 font-semibold text-gray-900 text-left text-sm">ID</th>
|
|
133
|
+
<th class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm">Email</th>
|
|
134
|
+
<th class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm">User</th>
|
|
135
|
+
<th class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm">Status</th>
|
|
136
|
+
<th class="px-3 py-3.5 font-semibold text-gray-900 text-right text-sm">Unsubscribed At</th>
|
|
137
|
+
<th class="px-3 py-3.5 font-semibold text-gray-900 text-right text-sm">Created At</th>
|
|
138
|
+
<th class="relative pl-3 pr-4 py-3.5 sm:pr-6"><span class="sr-only">Edit</span></th>
|
|
139
|
+
</tr>
|
|
140
|
+
</thead>
|
|
141
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y">
|
|
142
|
+
<tr>
|
|
143
|
+
<td class="pl-4 pr-3 py-4 sm:pl-6 font-medium text-gray-900 text-sm whitespace-nowrap">1</td>
|
|
144
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap"><div class="flex items-center font-medium text-gray-900 text-sm dark:text-neutral-100">chris@stacksjs.com</div></td>
|
|
145
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap">Chris Breuer</td>
|
|
146
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap"><span class="inline-flex items-center px-2 py-1 font-medium text-green-700 text-xs bg-green-50 ring-1 ring-green-600/20 ring-inset rounded-md">Subscribed</span></td>
|
|
147
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap"></td>
|
|
148
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap">2024/01/02</td>
|
|
149
|
+
<td class="relative pl-3 pr-4 py-4 sm:pr-6 font-medium text-right text-sm whitespace-nowrap"><a href="#" class="text-blue-600 hover:text-blue-900">Edit</a></td>
|
|
150
|
+
</tr>
|
|
151
|
+
<tr>
|
|
152
|
+
<td class="pl-4 pr-3 py-4 sm:pl-6 font-medium text-gray-900 text-sm whitespace-nowrap">2</td>
|
|
153
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap"><div class="flex items-center font-medium text-gray-900 text-sm dark:text-neutral-100">buddy@stacksjs.com</div></td>
|
|
154
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap"></td>
|
|
155
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap"><span class="inline-flex items-center px-2 py-1 font-medium text-slate-700 text-xs bg-slate-50 ring-1 ring-inset ring-slate-600/20 rounded-md">Unsubscribed</span></td>
|
|
156
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap">2024/01/22 11:00am</td>
|
|
157
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap">2024/01/02</td>
|
|
158
|
+
<td class="relative pl-3 pr-4 py-4 sm:pr-6 font-medium text-right text-sm whitespace-nowrap"><a href="#" class="text-blue-600 hover:text-blue-900">Edit</a></td>
|
|
159
|
+
</tr>
|
|
160
|
+
</tbody>
|
|
161
|
+
</table>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<script client>
|
|
170
|
+
|
|
171
|
+
interface ChartLike { destroy: () => void, data: { labels: string[], datasets: Array<{ data: number[] }> }, update: () => void }
|
|
172
|
+
type ChartCtor = new (canvas: HTMLElement, config: { type: string, data: unknown, options?: unknown }) => ChartLike
|
|
173
|
+
|
|
174
|
+
interface ModelNode {
|
|
175
|
+
id: string
|
|
176
|
+
name: string
|
|
177
|
+
description: string
|
|
178
|
+
properties: string[]
|
|
179
|
+
relationships: string[]
|
|
180
|
+
emoji: string
|
|
181
|
+
color: string
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Model relationship graph — moved out of the former `<script server>` block
|
|
185
|
+
// (stacksjs/stacks#1838).
|
|
186
|
+
const models: ModelNode[] = [
|
|
187
|
+
{ id: 'subscriber', name: 'Subscriber', description: 'Manages newsletter and communication preferences for users and contacts. Tracks subscription status and email preferences.', properties: ['id', 'email', 'status'], relationships: ['subscriberEmails'], emoji: '📫', color: '#0D9488' },
|
|
188
|
+
{ id: 'subscriberEmail', name: 'SubscriberEmail', description: 'Tracks email communications sent to subscribers, including campaign history and engagement metrics.', properties: ['id', 'email', 'subscriber_id'], relationships: ['subscriber'], emoji: '✉️', color: '#2DD4BF' },
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
const timeRange = state('30')
|
|
192
|
+
const isLoading = state(false)
|
|
193
|
+
const selectedModel = state<ModelNode | null>(null)
|
|
194
|
+
|
|
195
|
+
function selectModel(model: ModelNode) {
|
|
196
|
+
selectedModel.set(model)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function resolveModelRoute(id: string) {
|
|
200
|
+
const routes: Record<string, string> = {
|
|
201
|
+
subscriber: '/models/subscribers', subscribers: '/models/subscribers',
|
|
202
|
+
subscriberEmail: '/models/subscriber-emails', subscriberEmails: '/models/subscriber-emails',
|
|
203
|
+
}
|
|
204
|
+
return routes[id] || '/models'
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function formatDateOffset(daysAgo: number) {
|
|
208
|
+
const date = new Date()
|
|
209
|
+
date.setDate(date.getDate() - daysAgo)
|
|
210
|
+
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function generateGrowthData(days: number, baseCount: number, dailyGrowth: number) {
|
|
214
|
+
return Array.from({ length: days }, (_, i) => {
|
|
215
|
+
const dayVariance = Math.random() * dailyGrowth * 0.5
|
|
216
|
+
const daysFromStart = days - 1 - i
|
|
217
|
+
return Math.floor(baseCount + (dailyGrowth * daysFromStart) + dayVariance)
|
|
218
|
+
}).reverse()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function buildSeries() {
|
|
222
|
+
const days = Number.parseInt(timeRange())
|
|
223
|
+
const labels = Array.from({ length: days }, (_, i) => formatDateOffset(days - 1 - i)).reverse()
|
|
224
|
+
return { labels, data: generateGrowthData(days, 500, 5) }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
let chartHandle: ChartLike | null = null
|
|
228
|
+
|
|
229
|
+
function changeTimeRange() {
|
|
230
|
+
if (!chartHandle)
|
|
231
|
+
return
|
|
232
|
+
const series = buildSeries()
|
|
233
|
+
chartHandle.data.labels = series.labels
|
|
234
|
+
chartHandle.data.datasets[0].data = series.data
|
|
235
|
+
chartHandle.update()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
onMount(async () => {
|
|
239
|
+
const { Chart, registerables } = await import('@stacksjs/charts')
|
|
240
|
+
Chart.register(...registerables)
|
|
241
|
+
const Ctor = Chart as unknown as ChartCtor
|
|
242
|
+
const series = buildSeries()
|
|
243
|
+
chartHandle = useChart({
|
|
244
|
+
Chart: Ctor,
|
|
245
|
+
id: 'subscriber_growth_chart',
|
|
246
|
+
type: 'line',
|
|
247
|
+
data: {
|
|
248
|
+
labels: series.labels,
|
|
249
|
+
datasets: [{ label: 'Total Subscribers', data: series.data, borderColor: 'rgb(14, 165, 233)', backgroundColor: 'rgba(14, 165, 233, 0.1)', fill: true, tension: 0.4 }],
|
|
250
|
+
},
|
|
251
|
+
options: {
|
|
252
|
+
responsive: true,
|
|
253
|
+
maintainAspectRatio: false,
|
|
254
|
+
scales: {
|
|
255
|
+
y: { type: 'linear', beginAtZero: true, grid: { color: 'rgba(200, 200, 200, 0.1)' }, ticks: { color: 'rgb(156, 163, 175)' } },
|
|
256
|
+
x: { type: 'category', grid: { display: false }, ticks: { color: 'rgb(156, 163, 175)' } },
|
|
257
|
+
},
|
|
258
|
+
plugins: { legend: { display: true, position: 'top', align: 'end', labels: { color: 'rgb(156, 163, 175)', boxWidth: 12, padding: 15 } } },
|
|
259
|
+
interaction: { mode: 'index', intersect: false },
|
|
260
|
+
},
|
|
261
|
+
}) as unknown as ChartLike
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
onDestroy(() => {
|
|
265
|
+
chartHandle?.destroy()
|
|
266
|
+
chartHandle = null
|
|
267
|
+
})
|
|
268
|
+
</script>
|
|
269
|
+
|
|
270
|
+
@endsection
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
<div class="py-4 lg:py-8 min-h-screen dark:bg-neutral-800">
|
|
5
|
+
<div class="mb-8 px-4 sm:px-6 lg:px-8">
|
|
6
|
+
<h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-neutral-100">Last 30 days</h3>
|
|
7
|
+
<dl class="grid gap-5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 mt-5">
|
|
8
|
+
<div class="overflow-hidden relative pt-5 px-4 sm:pt-6 sm:px-6 bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
9
|
+
<dt>
|
|
10
|
+
<div class="absolute p-3 bg-blue-500 rounded-md">
|
|
11
|
+
<svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" /></svg>
|
|
12
|
+
</div>
|
|
13
|
+
<p class="ml-16 font-medium text-gray-500 text-sm truncate">Total Teams</p>
|
|
14
|
+
</dt>
|
|
15
|
+
<dd class="flex items-baseline ml-16 pb-6 sm:pb-7">
|
|
16
|
+
<p class="font-semibold text-2xl text-gray-900 dark:text-neutral-100">1,234</p>
|
|
17
|
+
<p class="flex items-baseline ml-2 font-semibold text-green-600 text-sm">
|
|
18
|
+
<svg class="flex-shrink-0 self-center h-5 w-5 text-green-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" d="M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z" clip-rule="evenodd" /></svg>
|
|
19
|
+
<span class="sr-only">Increased by</span>
|
|
20
|
+
122
|
|
21
|
+
</p>
|
|
22
|
+
</dd>
|
|
23
|
+
</div>
|
|
24
|
+
</dl>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="mb-8 px-4 sm:px-6 lg:px-8">
|
|
28
|
+
<div class="bg-white dark:bg-neutral-700 rounded-lg shadow">
|
|
29
|
+
<div class="p-6">
|
|
30
|
+
<div class="flex items-center justify-between mb-6">
|
|
31
|
+
<div>
|
|
32
|
+
<h3 class="font-medium text-base text-gray-900 dark:text-neutral-100">Team Growth</h3>
|
|
33
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Track team creation over time</p>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="flex items-center space-x-4">
|
|
36
|
+
<select x-model="timeRange" @change="changeTimeRange()" class="pl-3 pr-8 py-1.5 h-9 text-gray-900 text-sm dark:text-neutral-100 bg-gray-50 dark:bg-neutral-600 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600">
|
|
37
|
+
<option value="7">Last 7 days</option>
|
|
38
|
+
<option value="30">Last 30 days</option>
|
|
39
|
+
<option value="90">Last 90 days</option>
|
|
40
|
+
<option value="365">Last year</option>
|
|
41
|
+
</select>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="relative h-[400px]">
|
|
45
|
+
<div :show="isLoading" class="flex absolute inset-0 z-10 items-center justify-center bg-opacity-75 bg-white dark:bg-neutral-700 dark:bg-opacity-75">
|
|
46
|
+
<div class="h-8 w-8 border-b-2 border-blue-500 rounded-full animate-spin"></div>
|
|
47
|
+
</div>
|
|
48
|
+
<canvas id="team_growth_chart"></canvas>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div class="mb-8 px-4 sm:px-6 lg:px-8">
|
|
55
|
+
<div class="bg-white dark:bg-neutral-700 rounded-lg shadow">
|
|
56
|
+
<div class="p-6">
|
|
57
|
+
<div class="flex items-center justify-between mb-6">
|
|
58
|
+
<div>
|
|
59
|
+
<h3 class="font-medium text-base text-gray-900 dark:text-neutral-100">Team Model Relationships</h3>
|
|
60
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Click any model to view details.</p>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<div class="flex">
|
|
64
|
+
<div class="flex relative flex-1 flex-wrap gap-4 items-start justify-start p-4 h-[400px] bg-gray-50 dark:bg-neutral-600 rounded-lg">
|
|
65
|
+
<template :for="model in models">
|
|
66
|
+
<button type="button" @click="selectModel(model)" class="inline-flex flex-col items-center px-4 py-3 bg-white dark:bg-neutral-800 hover:bg-gray-100 dark:hover:bg-neutral-700 ring-1 ring-gray-200 rounded-lg dark:ring-neutral-600 shadow-sm">
|
|
67
|
+
<span class="text-3xl">{{ model.emoji }}</span>
|
|
68
|
+
<span class="mt-2 font-medium text-gray-900 text-sm dark:text-neutral-100">{{ model.name }}</span>
|
|
69
|
+
</button>
|
|
70
|
+
</template>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div :show="selectedModel() !== null" class="ml-6 p-4 w-64 bg-gray-50 dark:bg-neutral-600 rounded-lg">
|
|
74
|
+
<div class="flex items-center mb-4">
|
|
75
|
+
<span class="mr-2 text-2xl">{{ selectedModel()?.emoji }}</span>
|
|
76
|
+
<h4 class="font-semibold text-gray-900 text-lg dark:text-neutral-100">{{ selectedModel()?.name }}</h4>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="mb-4">
|
|
79
|
+
<p class="text-gray-600 text-sm dark:text-neutral-400">{{ selectedModel()?.description }}</p>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="mb-4">
|
|
82
|
+
<h5 class="mb-2 font-medium text-gray-700 text-sm dark:text-neutral-300">Properties</h5>
|
|
83
|
+
<ul class="space-y-1">
|
|
84
|
+
<template :for="prop in (selectedModel?.properties ?? [])">
|
|
85
|
+
<li class="font-mono text-gray-600 text-sm dark:text-neutral-400">{{ prop }}</li>
|
|
86
|
+
</template>
|
|
87
|
+
</ul>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="mb-6">
|
|
90
|
+
<h5 class="mb-2 font-medium text-gray-700 text-sm dark:text-neutral-300">Relationships</h5>
|
|
91
|
+
<ul class="space-y-1">
|
|
92
|
+
<template :for="rel in (selectedModel?.relationships ?? [])">
|
|
93
|
+
<li class="font-mono text-sm">
|
|
94
|
+
<a :href="resolveModelRoute(rel)" class="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 duration-150 transition-colors">{{ rel }}</a>
|
|
95
|
+
</li>
|
|
96
|
+
</template>
|
|
97
|
+
</ul>
|
|
98
|
+
</div>
|
|
99
|
+
<a :href="resolveModelRoute(selectedModel()?.id ?? '')" class="block px-4 py-2 w-full font-semibold text-center text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md shadow-sm duration-150 transition-colors">View Details</a>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div class="pt-12 px-4 sm:px-6 lg:px-8">
|
|
107
|
+
<div class="sm:flex sm:items-center">
|
|
108
|
+
<div class="sm:flex-auto">
|
|
109
|
+
<h1 class="font-semibold leading-6 text-base text-gray-900 dark:text-neutral-100">Teams</h1>
|
|
110
|
+
<p class="mt-2 text-gray-700 text-sm dark:text-neutral-400">A list of all your teams.</p>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="sm:flex-none mt-4 sm:ml-16 sm:mt-0">
|
|
113
|
+
<button type="button" class="block px-3 py-2 font-semibold text-center text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 shadow-sm">Add Team</button>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="flow-root mt-8">
|
|
117
|
+
<div class="overflow-x-auto -mx-4 -my-2 sm:-mx-6 lg:-mx-8">
|
|
118
|
+
<div class="inline-block py-2 sm:px-6 lg:px-8 min-w-full align-middle">
|
|
119
|
+
<div class="overflow-hidden ring-1 ring-black ring-opacity-5 sm:rounded-lg shadow">
|
|
120
|
+
<table class="min-w-full divide-gray-300 divide-y">
|
|
121
|
+
<thead class="bg-gray-50 dark:bg-neutral-700/50">
|
|
122
|
+
<tr>
|
|
123
|
+
<th class="pl-4 pr-3 py-3.5 sm:pl-6 font-semibold text-gray-900 text-left text-sm">ID</th>
|
|
124
|
+
<th class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm">Name</th>
|
|
125
|
+
<th class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm">Company Name</th>
|
|
126
|
+
<th class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm">Type</th>
|
|
127
|
+
<th class="px-3 py-3.5 font-semibold text-gray-900 text-right text-sm">Created At</th>
|
|
128
|
+
<th class="relative pl-3 pr-4 py-3.5 sm:pr-6"><span class="sr-only">Edit</span></th>
|
|
129
|
+
</tr>
|
|
130
|
+
</thead>
|
|
131
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y">
|
|
132
|
+
<tr>
|
|
133
|
+
<td class="pl-4 pr-3 py-4 sm:pl-6 font-medium text-gray-900 text-sm whitespace-nowrap">1</td>
|
|
134
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap">
|
|
135
|
+
<div class="flex items-center">
|
|
136
|
+
<div class="flex-shrink-0 h-10 w-10"><img src="https://avatars.githubusercontent.com/u/6228425?v=4" alt="" class="h-10 w-10 rounded-full" /></div>
|
|
137
|
+
<div class="ml-4">
|
|
138
|
+
<div class="flex items-center font-medium text-gray-900 text-sm dark:text-neutral-100">Stacks.js</div>
|
|
139
|
+
<div class="text-gray-500 text-sm dark:text-neutral-400">chris@stacksjs.com</div>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</td>
|
|
143
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap">Stacks.js, Inc.</td>
|
|
144
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap"><span class="inline-flex items-center px-2 py-1 font-medium text-slate-700 text-xs bg-slate-50 ring-1 ring-inset ring-slate-600/20 rounded-md">Personal</span></td>
|
|
145
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap">2024/01/02</td>
|
|
146
|
+
<td class="relative pl-3 pr-4 py-4 sm:pr-6 font-medium text-right text-sm whitespace-nowrap"><a href="#" class="text-blue-600 hover:text-blue-900">Edit</a></td>
|
|
147
|
+
</tr>
|
|
148
|
+
<tr>
|
|
149
|
+
<td class="pl-4 pr-3 py-4 sm:pl-6 font-medium text-gray-900 text-sm whitespace-nowrap">2</td>
|
|
150
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap">
|
|
151
|
+
<div class="flex items-center">
|
|
152
|
+
<div class="flex-shrink-0 h-10 w-10"><img src="https://avatars.githubusercontent.com/u/6228425?v=4" alt="" class="h-10 w-10 rounded-full" /></div>
|
|
153
|
+
<div class="ml-4">
|
|
154
|
+
<div class="flex items-center font-medium text-gray-900 text-sm dark:text-neutral-100">Jetbrains</div>
|
|
155
|
+
<div class="text-gray-500 text-sm dark:text-neutral-400">support@jetbrains.com</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</td>
|
|
159
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap">Jetbrains, Inc.</td>
|
|
160
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap"><span class="inline-flex items-center px-2 py-1 font-medium text-slate-700 text-xs bg-slate-50 ring-1 ring-inset ring-slate-600/20 rounded-md">Professional</span></td>
|
|
161
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap">2024/01/02</td>
|
|
162
|
+
<td class="relative pl-3 pr-4 py-4 sm:pr-6 font-medium text-right text-sm whitespace-nowrap"><a href="#" class="text-blue-600 hover:text-blue-900">Edit</a></td>
|
|
163
|
+
</tr>
|
|
164
|
+
</tbody>
|
|
165
|
+
</table>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<script client>
|
|
174
|
+
|
|
175
|
+
interface ChartLike { destroy: () => void, data: { labels: string[], datasets: Array<{ data: number[] }> }, update: () => void }
|
|
176
|
+
type ChartCtor = new (canvas: HTMLElement, config: { type: string, data: unknown, options?: unknown }) => ChartLike
|
|
177
|
+
|
|
178
|
+
interface ModelNode {
|
|
179
|
+
id: string
|
|
180
|
+
name: string
|
|
181
|
+
description: string
|
|
182
|
+
properties: string[]
|
|
183
|
+
relationships: string[]
|
|
184
|
+
emoji: string
|
|
185
|
+
color: string
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Model relationship graph — moved out of the former `<script server>` block
|
|
189
|
+
// (stacksjs/stacks#1838).
|
|
190
|
+
const models: ModelNode[] = [
|
|
191
|
+
{ id: 'team', name: 'Team', description: 'Represents a collaborative group with shared resources and permissions. Teams can contain multiple users and are used for organizing access control.', properties: ['id', 'name', 'owner_id'], relationships: ['users', 'owner'], emoji: '👥', color: '#60A5FA' },
|
|
192
|
+
{ id: 'user', name: 'User', description: 'Core user model representing team members and owners. Users can belong to multiple teams and can own teams.', properties: ['id', 'name', 'email', 'password'], relationships: ['teams'], emoji: '👤', color: '#2563EB' },
|
|
193
|
+
]
|
|
194
|
+
|
|
195
|
+
const timeRange = state('30')
|
|
196
|
+
const isLoading = state(false)
|
|
197
|
+
const selectedModel = state<ModelNode | null>(null)
|
|
198
|
+
|
|
199
|
+
function selectModel(model: ModelNode) {
|
|
200
|
+
selectedModel.set(model)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function resolveModelRoute(id: string) {
|
|
204
|
+
const routes: Record<string, string> = {
|
|
205
|
+
user: '/models/users', users: '/models/users',
|
|
206
|
+
team: '/models/teams', teams: '/models/teams',
|
|
207
|
+
owner: '/models/users',
|
|
208
|
+
}
|
|
209
|
+
return routes[id] || '/models'
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function formatDateOffset(daysAgo: number) {
|
|
213
|
+
const date = new Date()
|
|
214
|
+
date.setDate(date.getDate() - daysAgo)
|
|
215
|
+
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function generateGrowthData(days: number, baseCount: number, dailyGrowth: number) {
|
|
219
|
+
return Array.from({ length: days }, (_, i) => {
|
|
220
|
+
const dayVariance = Math.random() * dailyGrowth * 0.5
|
|
221
|
+
const daysFromStart = days - 1 - i
|
|
222
|
+
return Math.floor(baseCount + (dailyGrowth * daysFromStart) + dayVariance)
|
|
223
|
+
}).reverse()
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function buildSeries() {
|
|
227
|
+
const days = Number.parseInt(timeRange())
|
|
228
|
+
const labels = Array.from({ length: days }, (_, i) => formatDateOffset(days - 1 - i)).reverse()
|
|
229
|
+
return { labels, data: generateGrowthData(days, 1000, 10) }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let chartHandle: ChartLike | null = null
|
|
233
|
+
|
|
234
|
+
function changeTimeRange() {
|
|
235
|
+
if (!chartHandle)
|
|
236
|
+
return
|
|
237
|
+
const series = buildSeries()
|
|
238
|
+
chartHandle.data.labels = series.labels
|
|
239
|
+
chartHandle.data.datasets[0].data = series.data
|
|
240
|
+
chartHandle.update()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
onMount(async () => {
|
|
244
|
+
const { Chart, registerables } = await import('@stacksjs/charts')
|
|
245
|
+
Chart.register(...registerables)
|
|
246
|
+
const Ctor = Chart as unknown as ChartCtor
|
|
247
|
+
const series = buildSeries()
|
|
248
|
+
chartHandle = useChart({
|
|
249
|
+
Chart: Ctor,
|
|
250
|
+
id: 'team_growth_chart',
|
|
251
|
+
type: 'line',
|
|
252
|
+
data: {
|
|
253
|
+
labels: series.labels,
|
|
254
|
+
datasets: [{ label: 'Total Teams', data: series.data, borderColor: 'rgb(234, 179, 8)', backgroundColor: 'rgba(234, 179, 8, 0.1)', fill: true, tension: 0.4 }],
|
|
255
|
+
},
|
|
256
|
+
options: {
|
|
257
|
+
responsive: true,
|
|
258
|
+
maintainAspectRatio: false,
|
|
259
|
+
scales: {
|
|
260
|
+
y: { type: 'linear', beginAtZero: true, grid: { color: 'rgba(200, 200, 200, 0.1)' }, ticks: { color: 'rgb(156, 163, 175)' } },
|
|
261
|
+
x: { type: 'category', grid: { display: false }, ticks: { color: 'rgb(156, 163, 175)' } },
|
|
262
|
+
},
|
|
263
|
+
plugins: { legend: { display: true, position: 'top', align: 'end', labels: { color: 'rgb(156, 163, 175)', boxWidth: 12, padding: 15 } } },
|
|
264
|
+
interaction: { mode: 'index', intersect: false },
|
|
265
|
+
},
|
|
266
|
+
}) as unknown as ChartLike
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
onDestroy(() => {
|
|
270
|
+
chartHandle?.destroy()
|
|
271
|
+
chartHandle = null
|
|
272
|
+
})
|
|
273
|
+
</script>
|
|
274
|
+
|
|
275
|
+
@endsection
|