@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,452 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
@title('Mail - Your Inbox')
|
|
5
|
+
<div class="bg-white dark:bg-neutral-800">
|
|
6
|
+
<div class="flex h-screen">
|
|
7
|
+
<EmailSidebar
|
|
8
|
+
:activeFolder="activeFolder"
|
|
9
|
+
:folders="folders"
|
|
10
|
+
:unreadCounts="unreadCounts"
|
|
11
|
+
@update:active-folder="handleFolderChange"
|
|
12
|
+
@compose="handleCompose"
|
|
13
|
+
/>
|
|
14
|
+
|
|
15
|
+
<div class="flex overflow-hidden flex-1 flex-col">
|
|
16
|
+
<div class="px-4 py-2 bg-white dark:bg-neutral-700 border-b border-gray-200 dark:border-neutral-600">
|
|
17
|
+
<div class="flex items-center justify-between">
|
|
18
|
+
<div class="font-medium text-gray-900 text-xl dark:text-white">Inbox</div>
|
|
19
|
+
<div class="flex items-center space-x-1">
|
|
20
|
+
<div class="flex bg-gray-100 dark:bg-neutral-600 rounded-md">
|
|
21
|
+
<button class="px-4 py-2 font-medium text-gray-700 text-sm dark:text-white bg-gray-200 dark:bg-neutral-500 rounded-md">All mail</button>
|
|
22
|
+
<button class="px-4 py-2 font-medium text-gray-700 text-sm dark:text-white">Unread</button>
|
|
23
|
+
</div>
|
|
24
|
+
<button class="p-2 text-gray-500 dark:text-neutral-400 hover:text-gray-700 dark:hover:text-white"><i class="text-lg i-hugeicons-waste"></i></button>
|
|
25
|
+
<button class="p-2 text-gray-500 dark:text-neutral-400 hover:text-gray-700 dark:hover:text-white"><i class="text-lg i-hugeicons-archive"></i></button>
|
|
26
|
+
<button class="p-2 text-gray-500 dark:text-neutral-400 hover:text-gray-700 dark:hover:text-white"><i class="text-lg i-hugeicons-spam"></i></button>
|
|
27
|
+
<div class="flex items-center ml-2">
|
|
28
|
+
<button class="p-2 text-gray-500 dark:text-neutral-400 hover:text-gray-700 dark:hover:text-white"><i class="text-lg i-hugeicons-clock-circle"></i></button>
|
|
29
|
+
<button class="p-2 text-gray-500 dark:text-neutral-400 hover:text-gray-700 dark:hover:text-white"><i class="text-lg i-hugeicons-arrow-left-01"></i></button>
|
|
30
|
+
<button class="p-2 text-gray-500 dark:text-neutral-400 hover:text-gray-700 dark:hover:text-white"><i class="text-lg i-hugeicons-arrow-right-01"></i></button>
|
|
31
|
+
<button class="p-2 text-gray-500 dark:text-neutral-400 hover:text-gray-700 dark:hover:text-white"><i class="text-lg i-hugeicons-more-vertical-circle"></i></button>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div class="flex overflow-hidden flex-1">
|
|
38
|
+
<div :show="!selectedEmail() && !isComposing()" class="flex flex-col w-full md:w-1/3 lg:w-2/5 border-gray-200 border-r dark:border-neutral-600">
|
|
39
|
+
<div class="px-4 py-2 bg-white dark:bg-neutral-700 border-b border-gray-200 dark:border-neutral-600">
|
|
40
|
+
<div class="relative">
|
|
41
|
+
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
|
42
|
+
<i class="text-gray-400 text-lg i-hugeicons-search-01"></i>
|
|
43
|
+
</div>
|
|
44
|
+
<input x-model="searchQuery" type="text" placeholder="Search" class="block placeholder-gray-500 dark:placeholder-gray-400 pl-10 pr-3 py-2 w-full text-gray-900 sm:text-sm dark:text-neutral-100 bg-gray-50 dark:bg-neutral-600 border border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="overflow-y-auto flex-1">
|
|
49
|
+
<div :if="isLoading()" class="p-8 text-center text-gray-500 dark:text-neutral-400">Loading…</div>
|
|
50
|
+
<div :if="loadError() && !isLoading()" class="p-8 text-center text-red-500">{{ loadError() }}</div>
|
|
51
|
+
<ul :if="!isLoading() && !loadError()" class="divide-gray-200 divide-y dark:divide-neutral-600">
|
|
52
|
+
<template :for="email in filteredEmails">
|
|
53
|
+
<li @click="openEmail(email)" class="{{ emailRowClass(email) }}">
|
|
54
|
+
<div class="px-4 py-3">
|
|
55
|
+
<div class="flex items-center justify-between">
|
|
56
|
+
<p class="font-medium text-gray-900 text-sm dark:text-white">{{ email.from }}</p>
|
|
57
|
+
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ formatDate(email.date) }}</p>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="mt-1">
|
|
60
|
+
<p class="text-gray-900 text-sm dark:text-white">{{ email.subject }}</p>
|
|
61
|
+
<p class="mt-1 text-gray-500 text-xs truncate dark:text-neutral-400">{{ email.preview }}</p>
|
|
62
|
+
</div>
|
|
63
|
+
<div :if="email.labels && email.labels.length > 0" class="flex flex-wrap gap-1 mt-2">
|
|
64
|
+
<template :for="label in email.labels">
|
|
65
|
+
<span class="{{ labelClass(label) }}">{{ label }}</span>
|
|
66
|
+
</template>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</li>
|
|
70
|
+
</template>
|
|
71
|
+
</ul>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div :show="selectedEmail() !== null" class="flex flex-col w-full md:w-2/3 lg:w-3/5 bg-white dark:bg-neutral-700">
|
|
76
|
+
<div class="flex items-center justify-between px-4 py-2 border-b border-gray-200 dark:border-neutral-600">
|
|
77
|
+
<div class="flex items-center space-x-2">
|
|
78
|
+
<button @click="closeEmail()" class="p-1 text-gray-500 dark:text-neutral-400 hover:bg-gray-100 dark:hover:bg-neutral-600 rounded"><i class="text-lg i-hugeicons-arrow-left-02"></i></button>
|
|
79
|
+
<h2 class="font-medium text-gray-900 text-lg dark:text-white">{{ selectedEmail()?.subject }}</h2>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="flex space-x-2">
|
|
82
|
+
<button @click="selectedEmailId() && toggleStar(selectedEmailId())" class="p-1 hover:bg-gray-100 dark:hover:bg-neutral-600 rounded">
|
|
83
|
+
<span :show="selectedEmail()?.starred" class="text-lg text-yellow-400">
|
|
84
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><path d="M13.7276 3.44418L15.4874 6.99288C15.7274 7.48687 16.3673 7.9607 16.9073 8.05143L20.0969 8.58575C22.1367 8.92853 22.6167 10.4206 21.1468 11.8925L18.6671 14.3927C18.2471 14.8161 18.0172 15.6327 18.1471 16.2175L18.8571 19.3125C19.417 21.7623 18.1271 22.71 15.9774 21.4296L12.9877 19.6452C12.4478 19.3226 11.5579 19.3226 11.0079 19.6452L8.01827 21.4296C5.8785 22.71 4.57865 21.7522 5.13859 19.3125L5.84851 16.2175C5.97849 15.6327 5.74852 14.8161 5.32856 14.3927L2.84884 11.8925C1.389 10.4206 1.85895 8.92853 3.89872 8.58575L7.08837 8.05143C7.61831 7.9607 8.25824 7.48687 8.49821 6.99288L10.258 3.44418C11.2179 1.51861 12.7777 1.51861 13.7276 3.44418Z"/></svg>
|
|
85
|
+
</span>
|
|
86
|
+
<i :show="!selectedEmail()?.starred" class="text-gray-500 text-lg dark:text-neutral-400 i-hugeicons-star"></i>
|
|
87
|
+
</button>
|
|
88
|
+
<button class="p-1 text-gray-500 dark:text-neutral-400 hover:bg-gray-100 dark:hover:bg-neutral-600 rounded"><i class="text-lg i-hugeicons-inbox"></i></button>
|
|
89
|
+
<button class="p-1 text-gray-500 dark:text-neutral-400 hover:bg-gray-100 dark:hover:bg-neutral-600 rounded"><i class="text-lg i-hugeicons-waste"></i></button>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="px-4 py-3 border-b border-gray-200 dark:border-neutral-600">
|
|
94
|
+
<div class="flex items-start justify-between">
|
|
95
|
+
<div class="flex items-center">
|
|
96
|
+
<div class="flex items-center justify-center h-10 w-10 font-medium text-gray-700 dark:text-white bg-gray-200 dark:bg-neutral-500 rounded-full">{{ selectedEmail()?.from?.charAt(0) }}</div>
|
|
97
|
+
<div class="ml-3">
|
|
98
|
+
<p class="font-medium text-gray-900 text-sm dark:text-white">{{ selectedEmail()?.from }}</p>
|
|
99
|
+
<p class="text-gray-500 text-xs dark:text-neutral-400">{{ selectedEmail()?.email }}</p>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<div class="text-gray-500 text-xs dark:text-neutral-400">{{ selectedEmailDate() }}</div>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="mt-2">
|
|
105
|
+
<p class="font-medium text-base text-gray-900 dark:text-white">{{ selectedEmail()?.subject }}</p>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="overflow-y-auto flex-1 p-4 text-gray-900 dark:text-white">
|
|
110
|
+
<div :if="selectedEmail()?.body" innerHTML="{{ selectedEmail()?.body }}" class="max-w-none prose dark:prose-invert"></div>
|
|
111
|
+
<div :if="selectedEmail()?.attachment" class="mt-4 p-3 border border-gray-200 rounded dark:border-neutral-600">
|
|
112
|
+
<div class="flex items-center">
|
|
113
|
+
<i class="text-gray-500 text-xl dark:text-neutral-400 i-hugeicons-file-01"></i>
|
|
114
|
+
<div class="ml-3">
|
|
115
|
+
<p class="font-medium text-gray-900 text-sm dark:text-white">Document.pdf</p>
|
|
116
|
+
<p class="text-gray-500 text-xs dark:text-neutral-400">1.2 MB</p>
|
|
117
|
+
</div>
|
|
118
|
+
<button class="ml-auto p-1 text-blue-700 dark:text-blue-100 bg-blue-100 dark:bg-blue-900 hover:bg-blue-200 dark:hover:bg-blue-800 rounded"><i class="text-lg i-hugeicons-download"></i></button>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="p-4 bg-white dark:bg-neutral-700 border-gray-200 border-t dark:border-neutral-600">
|
|
124
|
+
<div class="flex items-center">
|
|
125
|
+
<textarea x-model="replyText" placeholder="Reply..." class="flex-1 p-2 text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-600 border border-gray-300 rounded-md dark:border-neutral-600 focus:outline-none focus:ring-1 focus:ring-blue-500" rows="3"></textarea>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="flex justify-between mt-2">
|
|
128
|
+
<div class="flex items-center">
|
|
129
|
+
<label class="inline-flex items-center text-gray-500 text-sm dark:text-neutral-400">
|
|
130
|
+
<input type="checkbox" class="h-4 w-4 text-blue-600 bg-white dark:bg-neutral-600 border-gray-300 rounded dark:border-neutral-500 form-checkbox" />
|
|
131
|
+
<span class="ml-2">Mute this thread</span>
|
|
132
|
+
</label>
|
|
133
|
+
</div>
|
|
134
|
+
<button @click="sendReply()" class="px-4 py-2 font-medium text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md">Send</button>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<div :show="!selectedEmail() && !isComposing()" class="hidden md:flex flex-col items-center justify-center p-8 md:w-2/3 lg:w-3/5 bg-white dark:bg-neutral-700">
|
|
140
|
+
<div class="text-center">
|
|
141
|
+
<div class="flex items-center justify-center mb-6 mx-auto h-24 w-24 bg-blue-100 dark:bg-blue-900 rounded-full">
|
|
142
|
+
<i class="text-5xl text-blue-600 dark:text-blue-400 i-hugeicons-mail-02"></i>
|
|
143
|
+
</div>
|
|
144
|
+
<h3 class="mb-2 font-medium text-gray-900 text-xl dark:text-white">No message selected</h3>
|
|
145
|
+
<p class="mb-6 max-w-md text-gray-500 dark:text-neutral-400">Select an email from your inbox to view its contents here, or start a new conversation by clicking the compose button.</p>
|
|
146
|
+
<button @click="handleCompose()" class="inline-flex items-center px-4 py-2 font-medium text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md">
|
|
147
|
+
<i class="mr-2 text-lg i-hugeicons-plus-sign"></i>
|
|
148
|
+
Compose New Email
|
|
149
|
+
</button>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<div :show="isComposing()" class="flex flex-col w-full md:w-2/3 lg:w-3/5 bg-white dark:bg-neutral-700">
|
|
154
|
+
<div class="flex items-center justify-between px-4 py-2 border-b border-gray-200 dark:border-neutral-600">
|
|
155
|
+
<h2 class="font-medium text-gray-900 text-lg dark:text-white">New Message</h2>
|
|
156
|
+
<button @click="cancelCompose()" class="p-1 text-gray-500 dark:text-neutral-400 hover:bg-gray-100 dark:hover:bg-neutral-600 rounded"><i class="text-lg i-hugeicons-cancel-circle"></i></button>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<div class="overflow-y-auto flex-1 p-4">
|
|
160
|
+
<div class="space-y-4">
|
|
161
|
+
<div>
|
|
162
|
+
<label for="to" class="block font-medium text-gray-700 text-sm dark:text-neutral-300">To</label>
|
|
163
|
+
<input id="to" x-model="newEmailTo" type="text" placeholder="recipient@example.com" class="block mt-1 w-full text-gray-900 sm:text-sm dark:text-neutral-100 bg-white dark:bg-neutral-600 border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:ring-blue-500 shadow-sm" />
|
|
164
|
+
</div>
|
|
165
|
+
<div>
|
|
166
|
+
<label for="subject" class="block font-medium text-gray-700 text-sm dark:text-neutral-300">Subject</label>
|
|
167
|
+
<input id="subject" x-model="newEmailSubject" type="text" placeholder="Enter email subject" class="block mt-1 w-full text-gray-900 sm:text-sm dark:text-neutral-100 bg-white dark:bg-neutral-600 border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:ring-blue-500 shadow-sm" />
|
|
168
|
+
</div>
|
|
169
|
+
<div>
|
|
170
|
+
<label for="body" class="block font-medium text-gray-700 text-sm dark:text-neutral-300">Message</label>
|
|
171
|
+
<textarea id="body" x-model="newEmailBody" rows="15" placeholder="Write your message here..." class="block mt-1 w-full text-gray-900 sm:text-sm dark:text-neutral-100 bg-white dark:bg-neutral-600 border-gray-300 rounded-md dark:border-neutral-600 focus:border-blue-500 focus:ring-blue-500 shadow-sm"></textarea>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="flex justify-between p-4 border-gray-200 border-t dark:border-neutral-600">
|
|
177
|
+
<div class="flex space-x-2">
|
|
178
|
+
<button class="p-2 text-gray-500 dark:text-neutral-400 hover:bg-gray-100 dark:hover:bg-neutral-600 rounded"><i class="text-lg i-hugeicons-attachment-01"></i></button>
|
|
179
|
+
<button class="p-2 text-gray-500 dark:text-neutral-400 hover:bg-gray-100 dark:hover:bg-neutral-600 rounded"><i class="text-lg i-hugeicons-image-01"></i></button>
|
|
180
|
+
<button class="p-2 text-gray-500 dark:text-neutral-400 hover:bg-gray-100 dark:hover:bg-neutral-600 rounded"><i class="text-lg i-hugeicons-smile"></i></button>
|
|
181
|
+
</div>
|
|
182
|
+
<div>
|
|
183
|
+
<button @click="sendEmail()" class="px-4 py-2 font-medium text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md">Send</button>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
<script client>
|
|
193
|
+
// Mail folder list — moved out of the former `<script server>` block
|
|
194
|
+
// (stacksjs/stacks#1838). `EmailSidebar` resolves through stx's
|
|
195
|
+
// component auto-discovery (it lives at
|
|
196
|
+
// `resources/components/Dashboard/Email/EmailSidebar.stx`).
|
|
197
|
+
const folders = [
|
|
198
|
+
{ id: 'inbox', name: 'Inbox', icon: 'inbox' },
|
|
199
|
+
{ id: 'starred', name: 'Starred', icon: 'star' },
|
|
200
|
+
{ id: 'sent', name: 'Sent', icon: 'send' },
|
|
201
|
+
{ id: 'drafts', name: 'Drafts', icon: 'drafts' },
|
|
202
|
+
{ id: 'archive', name: 'Archive', icon: 'archive' },
|
|
203
|
+
{ id: 'spam', name: 'Spam', icon: 'spam' },
|
|
204
|
+
{ id: 'trash', name: 'Trash', icon: 'waste' },
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
interface Email {
|
|
208
|
+
id: string
|
|
209
|
+
from: string
|
|
210
|
+
email: string
|
|
211
|
+
subject: string
|
|
212
|
+
preview: string
|
|
213
|
+
body: string
|
|
214
|
+
date: string
|
|
215
|
+
read: boolean
|
|
216
|
+
starred: boolean
|
|
217
|
+
folder: string
|
|
218
|
+
attachment: boolean
|
|
219
|
+
labels?: string[]
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const emails = state<Email[]>([])
|
|
223
|
+
const activeFolder = state('inbox')
|
|
224
|
+
const selectedEmailId = state<string | null>(null)
|
|
225
|
+
const searchQuery = state('')
|
|
226
|
+
const isComposing = state(false)
|
|
227
|
+
const isLoading = state(false)
|
|
228
|
+
const loadError = state<string | null>(null)
|
|
229
|
+
const replyText = state('')
|
|
230
|
+
const newEmailTo = state('')
|
|
231
|
+
const newEmailSubject = state('')
|
|
232
|
+
const newEmailBody = state('')
|
|
233
|
+
|
|
234
|
+
const selectedEmail = derived(() => emails().find(e => e.id === selectedEmailId()) ?? null)
|
|
235
|
+
const selectedEmailDate = derived(() => {
|
|
236
|
+
const email = selectedEmail()
|
|
237
|
+
return email?.date ? new Date(email.date).toLocaleString() : ''
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
const filteredEmails = derived(() => {
|
|
241
|
+
let filtered = emails().filter((email) => {
|
|
242
|
+
if (activeFolder() === 'starred')
|
|
243
|
+
return email.starred
|
|
244
|
+
return email.folder === activeFolder()
|
|
245
|
+
})
|
|
246
|
+
const q = searchQuery().toLowerCase()
|
|
247
|
+
if (q) {
|
|
248
|
+
filtered = filtered.filter(email =>
|
|
249
|
+
email.subject.toLowerCase().includes(q)
|
|
250
|
+
|| email.from.toLowerCase().includes(q)
|
|
251
|
+
|| email.preview.toLowerCase().includes(q),
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
return filtered.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
const unreadCounts = derived(() => {
|
|
258
|
+
const counts: Record<string, number> = {}
|
|
259
|
+
for (const folder of ['inbox', 'starred', 'sent', 'drafts', 'archive', 'spam', 'trash']) {
|
|
260
|
+
if (folder === 'starred')
|
|
261
|
+
counts[folder] = emails().filter(e => e.starred && !e.read).length
|
|
262
|
+
else
|
|
263
|
+
counts[folder] = emails().filter(e => e.folder === folder && !e.read).length
|
|
264
|
+
}
|
|
265
|
+
return counts
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
function parseSender(from: string): { name: string; email: string } {
|
|
269
|
+
const match = from.match(/^"?([^"<]+)"?\s*<([^>]+)>$/)
|
|
270
|
+
if (match) {
|
|
271
|
+
return { name: match[1].trim(), email: match[2].trim() }
|
|
272
|
+
}
|
|
273
|
+
return { name: from, email: from }
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function mapInboxEmail(item: any): Email {
|
|
277
|
+
const sender = parseSender(item.from || '')
|
|
278
|
+
return {
|
|
279
|
+
id: item.messageId,
|
|
280
|
+
from: sender.name || sender.email,
|
|
281
|
+
email: sender.email,
|
|
282
|
+
subject: item.subject || '(no subject)',
|
|
283
|
+
preview: item.preview || '',
|
|
284
|
+
body: '',
|
|
285
|
+
date: item.date,
|
|
286
|
+
read: item.read === true,
|
|
287
|
+
starred: false,
|
|
288
|
+
folder: 'inbox',
|
|
289
|
+
attachment: item.hasAttachments === true,
|
|
290
|
+
labels: [],
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async function loadInbox() {
|
|
295
|
+
isLoading.set(true)
|
|
296
|
+
loadError.set(null)
|
|
297
|
+
try {
|
|
298
|
+
const res = await fetch('/api/dashboard/email/inbox', {
|
|
299
|
+
headers: { accept: 'application/json' },
|
|
300
|
+
})
|
|
301
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
|
302
|
+
const data = await res.json()
|
|
303
|
+
const mapped = (data.emails || []).map(mapInboxEmail)
|
|
304
|
+
emails.set(mapped)
|
|
305
|
+
}
|
|
306
|
+
catch (e) {
|
|
307
|
+
loadError.set(e instanceof Error ? e.message : String(e))
|
|
308
|
+
emails.set([])
|
|
309
|
+
}
|
|
310
|
+
finally {
|
|
311
|
+
isLoading.set(false)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function loadEmailBody(id: string) {
|
|
316
|
+
try {
|
|
317
|
+
const res = await fetch(`/api/dashboard/email/inbox/${encodeURIComponent(id)}`, {
|
|
318
|
+
headers: { accept: 'application/json' },
|
|
319
|
+
})
|
|
320
|
+
if (!res.ok) return
|
|
321
|
+
const data = await res.json()
|
|
322
|
+
const body = data.html || data.text || ''
|
|
323
|
+
emails.set(emails().map(e => e.id === id ? { ...e, body } : e))
|
|
324
|
+
}
|
|
325
|
+
catch (e) {
|
|
326
|
+
console.error('Failed to load email body:', e)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async function markAsRead(id: string) {
|
|
331
|
+
try {
|
|
332
|
+
const res = await fetch('/api/dashboard/email/read', {
|
|
333
|
+
method: 'POST',
|
|
334
|
+
headers: { 'content-type': 'application/json' },
|
|
335
|
+
body: JSON.stringify({ messageId: id }),
|
|
336
|
+
})
|
|
337
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
|
338
|
+
emails.set(emails().map(e => e.id === id ? { ...e, read: true } : e))
|
|
339
|
+
}
|
|
340
|
+
catch (e) {
|
|
341
|
+
console.error('Failed to mark email as read:', e)
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function formatDate(dateString: string) {
|
|
346
|
+
const date = new Date(dateString)
|
|
347
|
+
const now = new Date()
|
|
348
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
|
349
|
+
const yesterday = new Date(today)
|
|
350
|
+
yesterday.setDate(yesterday.getDate() - 1)
|
|
351
|
+
if (date >= today)
|
|
352
|
+
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
|
353
|
+
if (date >= yesterday)
|
|
354
|
+
return 'Yesterday'
|
|
355
|
+
return date.toLocaleDateString([], { month: 'short', day: 'numeric' })
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function emailRowClass(email: Email) {
|
|
359
|
+
const base = 'hover:bg-gray-50 dark:hover:bg-neutral-600 cursor-pointer'
|
|
360
|
+
return email.read
|
|
361
|
+
? `${base} bg-white dark:bg-neutral-700`
|
|
362
|
+
: `${base} bg-blue-50 dark:bg-neutral-700 font-semibold`
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function labelClass(label: string) {
|
|
366
|
+
const base = 'inline-flex items-center px-2 py-0.5 font-medium text-xs rounded'
|
|
367
|
+
switch (label) {
|
|
368
|
+
case 'important': return `${base} bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200`
|
|
369
|
+
case 'meeting': return `${base} bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200`
|
|
370
|
+
case 'personal': return `${base} bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200`
|
|
371
|
+
case 'work': return `${base} bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200`
|
|
372
|
+
case 'budget': return `${base} bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200`
|
|
373
|
+
default: return `${base} bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200`
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function openEmail(email: Email) {
|
|
378
|
+
selectedEmailId.set(email.id)
|
|
379
|
+
void loadEmailBody(email.id)
|
|
380
|
+
if (!email.read)
|
|
381
|
+
void markAsRead(email.id)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function closeEmail() {
|
|
385
|
+
selectedEmailId.set(null)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function toggleRead(id: string) {
|
|
389
|
+
const email = emails().find(e => e.id === id)
|
|
390
|
+
if (!email) return
|
|
391
|
+
if (email.read) {
|
|
392
|
+
emails.set(emails().map(e => e.id === id ? { ...e, read: false } : e))
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
void markAsRead(id)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function toggleStar(id: string) {
|
|
400
|
+
emails.set(emails().map(e => e.id === id ? { ...e, starred: !e.starred } : e))
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function handleFolderChange(folder: string) {
|
|
404
|
+
activeFolder.set(folder)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function handleCompose() {
|
|
408
|
+
isComposing.set(true)
|
|
409
|
+
newEmailTo.set('')
|
|
410
|
+
newEmailSubject.set('')
|
|
411
|
+
newEmailBody.set('')
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function cancelCompose() {
|
|
415
|
+
isComposing.set(false)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function sendEmail() {
|
|
419
|
+
emails.set([
|
|
420
|
+
...emails(),
|
|
421
|
+
{
|
|
422
|
+
id: `sent-${Date.now()}`,
|
|
423
|
+
from: 'Me',
|
|
424
|
+
email: 'me@stacksjs.com',
|
|
425
|
+
subject: newEmailSubject(),
|
|
426
|
+
preview: newEmailBody().substring(0, 100),
|
|
427
|
+
body: newEmailBody(),
|
|
428
|
+
date: new Date().toISOString(),
|
|
429
|
+
read: true,
|
|
430
|
+
starred: false,
|
|
431
|
+
folder: 'sent',
|
|
432
|
+
attachment: false,
|
|
433
|
+
},
|
|
434
|
+
])
|
|
435
|
+
isComposing.set(false)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function sendReply() {
|
|
439
|
+
replyText.set('')
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
onMount(async () => {
|
|
443
|
+
const route = useRoute()
|
|
444
|
+
if (route.query.folder && typeof route.query.folder === 'string')
|
|
445
|
+
activeFolder.set(route.query.folder)
|
|
446
|
+
if (route.query.compose === 'true')
|
|
447
|
+
isComposing.set(true)
|
|
448
|
+
await loadInbox()
|
|
449
|
+
})
|
|
450
|
+
</script>
|
|
451
|
+
|
|
452
|
+
@endsection
|