@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,560 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
@title('Dashboard - Social Posts')
|
|
5
|
+
<PageLayout>
|
|
6
|
+
<div class="sm:flex sm:items-center sm:justify-between">
|
|
7
|
+
<PageHeader title="Social Media Posts" description="Create, schedule, and manage your social media posts for Bluesky" />
|
|
8
|
+
<div class="mt-4 sm:mt-0">
|
|
9
|
+
<button @click="openNewPostModal()" class="inline-flex items-center px-3 py-2 font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md shadow-sm">
|
|
10
|
+
<div class="mr-2 h-5 w-5 i-hugeicons-plus-sign"></div>
|
|
11
|
+
New Post
|
|
12
|
+
</button>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="grid gap-5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 mt-6">
|
|
17
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
18
|
+
<div class="flex items-center p-5">
|
|
19
|
+
<div class="p-2 h-10 w-10 bg-blue-100 dark:bg-blue-900 rounded-md">
|
|
20
|
+
<div class="h-6 w-6 text-blue-600 dark:text-blue-300 i-hugeicons-document-validation"></div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="flex-1 ml-5 w-0">
|
|
23
|
+
<dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-400">Total Posts</dt>
|
|
24
|
+
<dd class="font-medium text-gray-900 text-lg dark:text-white">{{ totalPosts() }}</dd>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
29
|
+
<div class="flex items-center p-5">
|
|
30
|
+
<div class="p-2 h-10 w-10 bg-green-100 dark:bg-green-900 rounded-md">
|
|
31
|
+
<div class="h-6 w-6 text-green-600 dark:text-green-300 i-hugeicons-checkmark-circle-02"></div>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="flex-1 ml-5 w-0">
|
|
34
|
+
<dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-400">Published</dt>
|
|
35
|
+
<dd class="font-medium text-gray-900 text-lg dark:text-white">{{ publishedCount() }}</dd>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
40
|
+
<div class="flex items-center p-5">
|
|
41
|
+
<div class="p-2 h-10 w-10 bg-blue-100 dark:bg-blue-900 rounded-md">
|
|
42
|
+
<div class="h-6 w-6 text-blue-600 dark:text-blue-300 i-hugeicons-clock-01"></div>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="flex-1 ml-5 w-0">
|
|
45
|
+
<dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-400">Scheduled</dt>
|
|
46
|
+
<dd class="font-medium text-gray-900 text-lg dark:text-white">{{ scheduledCount() }}</dd>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow">
|
|
51
|
+
<div class="flex items-center p-5">
|
|
52
|
+
<div class="p-2 h-10 w-10 bg-yellow-100 dark:bg-yellow-900 rounded-md">
|
|
53
|
+
<div class="h-6 w-6 text-yellow-600 dark:text-yellow-300 i-hugeicons-edit-01"></div>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="flex-1 ml-5 w-0">
|
|
56
|
+
<dt class="font-medium text-gray-500 text-sm truncate dark:text-neutral-400">Drafts</dt>
|
|
57
|
+
<dd class="font-medium text-gray-900 text-lg dark:text-white">{{ draftsCount() }}</dd>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between mt-6">
|
|
64
|
+
<div class="relative flex-1 max-w-sm">
|
|
65
|
+
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
|
66
|
+
<div class="h-5 w-5 text-gray-400 i-hugeicons-search-01"></div>
|
|
67
|
+
</div>
|
|
68
|
+
<input x-model="searchQuery" type="text" placeholder="Search posts..." class="block pl-10 pr-3 py-1.5 w-full text-gray-900 sm:leading-6 sm:text-sm dark:text-white placeholder:text-gray-400 dark:placeholder:text-neutral-500 dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600" />
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div class="flex flex-col gap-4 sm:flex-row">
|
|
72
|
+
<select x-model="statusFilter" class="block pl-3 pr-10 py-1.5 text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600">
|
|
73
|
+
<option value="all">All Statuses</option>
|
|
74
|
+
<template :for="status in statuses">
|
|
75
|
+
<option :value="status">{{ status }}</option>
|
|
76
|
+
</template>
|
|
77
|
+
</select>
|
|
78
|
+
|
|
79
|
+
<select x-model="platformFilter" class="block pl-3 pr-10 py-1.5 text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600">
|
|
80
|
+
<option value="all">All Platforms</option>
|
|
81
|
+
<template :for="platform in platforms">
|
|
82
|
+
<option :value="platform">{{ platform }}</option>
|
|
83
|
+
</template>
|
|
84
|
+
</select>
|
|
85
|
+
|
|
86
|
+
<select x-model="authorFilter" class="block pl-3 pr-10 py-1.5 text-gray-900 sm:leading-6 sm:text-sm dark:text-white dark:bg-neutral-800 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700 focus:ring-2 focus:ring-blue-600">
|
|
87
|
+
<option value="all">All Authors</option>
|
|
88
|
+
<template :for="author in authorList">
|
|
89
|
+
<option value="{{ author }}">{{ author }}</option>
|
|
90
|
+
</template>
|
|
91
|
+
</select>
|
|
92
|
+
|
|
93
|
+
<div class="flex rounded-md shadow-sm">
|
|
94
|
+
<button @click="setViewMode('detailed')" class="{{ viewModeButtonClass('detailed', 'l') }}">
|
|
95
|
+
<div class="h-5 w-5 i-hugeicons-grid"></div>
|
|
96
|
+
</button>
|
|
97
|
+
<button @click="setViewMode('compact')" class="{{ viewModeButtonClass('compact', 'r') }}">
|
|
98
|
+
<div class="h-5 w-5 i-hugeicons-list-view"></div>
|
|
99
|
+
</button>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div class="flex items-center mt-4 text-gray-500 text-sm dark:text-neutral-400">
|
|
105
|
+
<span class="mr-2">Sort by:</span>
|
|
106
|
+
<button @click="toggleSort('scheduledDate')" class="{{ sortButtonClass('scheduledDate', 'mr-3') }}">
|
|
107
|
+
Date <span class="ml-1">{{ sortIndicator('scheduledDate') }}</span>
|
|
108
|
+
</button>
|
|
109
|
+
<button @click="toggleSort('engagement')" class="{{ sortButtonClass('engagement', 'mr-3') }}">
|
|
110
|
+
Engagement <span class="ml-1">{{ sortIndicator('engagement') }}</span>
|
|
111
|
+
</button>
|
|
112
|
+
<button @click="toggleSort('author')" class="{{ sortButtonClass('author', '') }}">
|
|
113
|
+
Author <span class="ml-1">{{ sortIndicator('author') }}</span>
|
|
114
|
+
</button>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div :show="viewMode() === 'detailed'" class="mt-6">
|
|
118
|
+
<div :show="paginatedPosts().length === 0" class="py-12 text-center">
|
|
119
|
+
<div class="mx-auto h-12 w-12 text-gray-400 i-hugeicons-document-validation"></div>
|
|
120
|
+
<h3 class="mt-2 font-semibold text-gray-900 text-sm dark:text-white">No posts found</h3>
|
|
121
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Try adjusting your search or filter to find what you're looking for.</p>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div class="space-y-6">
|
|
125
|
+
<template :for="post in paginatedPosts">
|
|
126
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700 shadow-sm">
|
|
127
|
+
<div class="p-6">
|
|
128
|
+
<div class="flex items-start justify-between">
|
|
129
|
+
<div class="flex items-start space-x-4">
|
|
130
|
+
<img :src="post.authorAvatar" :alt="post.author" class="object-cover h-12 w-12 rounded-full" />
|
|
131
|
+
<div>
|
|
132
|
+
<h3 class="font-medium text-base text-gray-900 dark:text-white">{{ post.author }}</h3>
|
|
133
|
+
<div class="flex items-center mt-1">
|
|
134
|
+
<span class="inline-flex items-center px-2 py-1 font-medium text-blue-700 text-xs dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30 ring-1 ring-blue-600/20 ring-inset rounded-full">{{ post.platform }}</span>
|
|
135
|
+
<span class="ml-2 text-gray-500 text-sm dark:text-neutral-400">{{ formatDate(post.scheduledDate) }}</span>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<div class="flex flex-col items-end space-y-2">
|
|
141
|
+
<span class="{{ statusBadgeClass(post.status) }}">{{ post.status }}</span>
|
|
142
|
+
<span :show="post.threadId !== null" class="inline-flex items-center px-2 py-1 w-fit font-medium text-purple-700 text-xs dark:text-purple-400 bg-purple-50 dark:bg-purple-900/30 ring-1 ring-inset ring-purple-600/20 rounded-full">
|
|
143
|
+
Thread {{ post.isThreadStarter ? '(Starter)' : '' }}
|
|
144
|
+
</span>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<p class="mt-4 text-gray-500 text-sm dark:text-neutral-400">{{ post.content }}</p>
|
|
149
|
+
|
|
150
|
+
<div :show="post.images.length > 0" class="grid gap-2 grid-cols-2 mt-4">
|
|
151
|
+
<template :for="image in post.images">
|
|
152
|
+
<div class="aspect-square overflow-hidden relative rounded-lg">
|
|
153
|
+
<img :src="image" alt="Post image" class="object-cover h-full w-full" />
|
|
154
|
+
</div>
|
|
155
|
+
</template>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div :show="post.tags.length > 0" class="flex flex-wrap gap-2 mt-4">
|
|
159
|
+
<template :for="tag in post.tags">
|
|
160
|
+
<span class="inline-flex items-center px-2 py-1 font-medium text-gray-600 text-xs dark:text-neutral-300 bg-gray-50 dark:bg-neutral-800 ring-1 ring-gray-500/10 ring-inset rounded-full">#{{ tag }}</span>
|
|
161
|
+
</template>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div :show="post.status === 'Published'" class="flex items-center mt-4 text-gray-500 text-sm dark:text-neutral-400">
|
|
165
|
+
<div class="flex items-center"><div class="mr-1 h-4 w-4 i-hugeicons-heart"></div><span>{{ post.likes }} likes</span></div>
|
|
166
|
+
<div class="flex items-center ml-4"><div class="mr-1 h-4 w-4 i-hugeicons-refresh-02"></div><span>{{ post.reposts }} reposts</span></div>
|
|
167
|
+
<div class="flex items-center ml-4"><div class="mr-1 h-4 w-4 i-hugeicons-bubble-chat"></div><span>{{ post.replies }} replies</span></div>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div class="flex justify-end mt-4 space-x-3">
|
|
171
|
+
<button @click="duplicatePost(post.id)" class="inline-flex items-center px-2.5 py-1.5 font-semibold text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-600 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 shadow-sm">
|
|
172
|
+
<div class="mr-1 h-4 w-4 i-hugeicons-copy"></div>
|
|
173
|
+
Duplicate
|
|
174
|
+
</button>
|
|
175
|
+
<button @click="openEditPostModal(post.id)" class="p-1.5 text-gray-700 dark:text-neutral-300 bg-white dark:bg-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-600 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 shadow-sm" title="Edit post">
|
|
176
|
+
<div class="h-4 w-4 i-hugeicons-edit-01"></div>
|
|
177
|
+
</button>
|
|
178
|
+
<button @click="deletePost(post.id)" class="inline-flex items-center px-2.5 py-1.5 font-semibold text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-600 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 shadow-sm">
|
|
179
|
+
<div class="mr-1 h-4 w-4 i-hugeicons-waste"></div>
|
|
180
|
+
Delete
|
|
181
|
+
</button>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</template>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div :show="viewMode() === 'compact'" class="overflow-hidden mt-6 ring-1 ring-black ring-opacity-5 sm:rounded-lg shadow">
|
|
190
|
+
<table class="min-w-full divide-gray-300 divide-y dark:divide-neutral-700">
|
|
191
|
+
<thead class="bg-gray-50 dark:bg-neutral-700">
|
|
192
|
+
<tr>
|
|
193
|
+
<th scope="col" class="pl-4 pr-3 py-3.5 sm:pl-6 font-semibold text-gray-900 text-left text-sm dark:text-white">Content</th>
|
|
194
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-white">Platform</th>
|
|
195
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-white">Author</th>
|
|
196
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-white">Status</th>
|
|
197
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-right text-sm dark:text-white">Date</th>
|
|
198
|
+
<th scope="col" class="relative pl-3 pr-4 py-3.5 sm:pr-6"><span class="sr-only">Actions</span></th>
|
|
199
|
+
</tr>
|
|
200
|
+
</thead>
|
|
201
|
+
<tbody class="bg-white dark:bg-neutral-800 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
202
|
+
<template :for="post in paginatedPosts">
|
|
203
|
+
<tr class="hover:bg-gray-50 dark:hover:bg-neutral-700/50">
|
|
204
|
+
<td class="pl-4 pr-3 py-4 sm:pl-6 max-w-md text-gray-900 text-sm truncate dark:text-white">{{ truncate(post.content, 80) }}</td>
|
|
205
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-400">{{ post.platform }}</td>
|
|
206
|
+
<td class="px-3 py-4 text-gray-500 text-sm whitespace-nowrap dark:text-neutral-400">{{ post.author }}</td>
|
|
207
|
+
<td class="px-3 py-4 text-sm whitespace-nowrap"><span class="{{ statusBadgeClass(post.status) }}">{{ post.status }}</span></td>
|
|
208
|
+
<td class="px-3 py-4 text-gray-500 text-right text-sm whitespace-nowrap dark:text-neutral-400">{{ formatDate(post.scheduledDate) }}</td>
|
|
209
|
+
<td class="relative pl-3 pr-4 py-4 sm:pr-6 font-medium text-right text-sm whitespace-nowrap">
|
|
210
|
+
<div class="flex justify-end space-x-2">
|
|
211
|
+
<button @click="openEditPostModal(post.id)" class="text-blue-600 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300"><div class="h-5 w-5 i-hugeicons-edit-01"></div></button>
|
|
212
|
+
<button @click="deletePost(post.id)" class="text-red-600 dark:text-red-400 hover:text-red-900 dark:hover:text-red-300"><div class="h-5 w-5 i-hugeicons-waste"></div></button>
|
|
213
|
+
</div>
|
|
214
|
+
</td>
|
|
215
|
+
</tr>
|
|
216
|
+
</template>
|
|
217
|
+
<tr :show="paginatedPosts().length === 0">
|
|
218
|
+
<td colspan="6" class="px-4 py-8 text-center text-gray-500 text-sm dark:text-neutral-400">No posts found.</td>
|
|
219
|
+
</tr>
|
|
220
|
+
</tbody>
|
|
221
|
+
</table>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
<div :show="totalPages() > 1" class="flex items-center justify-between mt-6">
|
|
225
|
+
<p class="text-gray-700 text-sm dark:text-neutral-300">Page {{ currentPage() }} of {{ totalPages() }}</p>
|
|
226
|
+
<nav class="inline-flex -space-x-px rounded-md shadow-sm isolate">
|
|
227
|
+
<button @click="prevPage()" :disabled="currentPage() === 1" class="inline-flex relative items-center px-2 py-2 text-gray-400 dark:text-neutral-500 hover:bg-gray-50 dark:hover:bg-neutral-700 ring-1 ring-gray-300 ring-inset rounded-l-md dark:ring-neutral-600 disabled:opacity-50 disabled:cursor-not-allowed">
|
|
228
|
+
<div class="h-5 w-5 i-hugeicons-arrow-left-01"></div>
|
|
229
|
+
</button>
|
|
230
|
+
<button @click="nextPage()" :disabled="currentPage() === totalPages()" class="inline-flex relative items-center px-2 py-2 text-gray-400 dark:text-neutral-500 hover:bg-gray-50 dark:hover:bg-neutral-700 ring-1 ring-gray-300 ring-inset rounded-r-md dark:ring-neutral-600 disabled:opacity-50 disabled:cursor-not-allowed">
|
|
231
|
+
<div class="h-5 w-5 i-hugeicons-arrow-right-01"></div>
|
|
232
|
+
</button>
|
|
233
|
+
</nav>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
<div :show="showPostModal()" @click.self="closePostModal()" class="flex fixed inset-0 z-50 items-center justify-center p-4 bg-black bg-opacity-50">
|
|
237
|
+
<div class="overflow-y-auto p-6 max-h-[90vh] w-full sm:max-w-2xl bg-white dark:bg-neutral-800 rounded-lg shadow-xl">
|
|
238
|
+
<div class="flex items-start justify-between mb-4">
|
|
239
|
+
<h3 class="font-semibold text-gray-900 text-lg dark:text-white">{{ isNewPost() ? 'New Post' : 'Edit Post' }}</h3>
|
|
240
|
+
<button @click="closePostModal()" class="text-gray-400 dark:text-neutral-300 hover:text-gray-600">
|
|
241
|
+
<i class="h-5 w-5 i-hugeicons-cancel-01"></i>
|
|
242
|
+
</button>
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<div class="space-y-4">
|
|
246
|
+
<div>
|
|
247
|
+
<label class="block font-medium text-gray-900 text-sm dark:text-white">Content</label>
|
|
248
|
+
<textarea x-model="postContent" rows="4" placeholder="What's on your mind? Use #tags to categorise" class="block mt-2 py-1.5 w-full text-gray-900 sm:text-sm dark:text-white dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 shadow-sm"></textarea>
|
|
249
|
+
</div>
|
|
250
|
+
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2">
|
|
251
|
+
<div>
|
|
252
|
+
<label class="block font-medium text-gray-900 text-sm dark:text-white">Platform</label>
|
|
253
|
+
<select x-model="postPlatform" class="block mt-2 pl-3 pr-10 py-1.5 w-full text-gray-900 sm:text-sm dark:text-white dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600">
|
|
254
|
+
<template :for="platform in platforms">
|
|
255
|
+
<option :value="platform">{{ platform }}</option>
|
|
256
|
+
</template>
|
|
257
|
+
</select>
|
|
258
|
+
</div>
|
|
259
|
+
<div>
|
|
260
|
+
<label class="block font-medium text-gray-900 text-sm dark:text-white">Status</label>
|
|
261
|
+
<select x-model="postStatus" class="block mt-2 pl-3 pr-10 py-1.5 w-full text-gray-900 sm:text-sm dark:text-white dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600">
|
|
262
|
+
<template :for="status in statuses">
|
|
263
|
+
<option :value="status">{{ status }}</option>
|
|
264
|
+
</template>
|
|
265
|
+
</select>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
<div>
|
|
269
|
+
<label class="block font-medium text-gray-900 text-sm dark:text-white">Scheduled Date</label>
|
|
270
|
+
<input x-model="postScheduledDate" type="datetime-local" class="block mt-2 py-1.5 w-full text-gray-900 sm:text-sm dark:text-white dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 shadow-sm" />
|
|
271
|
+
</div>
|
|
272
|
+
<div>
|
|
273
|
+
<label class="block font-medium text-gray-900 text-sm dark:text-white">Author</label>
|
|
274
|
+
<input x-model="postAuthor" type="text" class="block mt-2 py-1.5 w-full text-gray-900 sm:text-sm dark:text-white dark:bg-neutral-700 border-0 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 focus:ring-2 focus:ring-blue-600 shadow-sm" />
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
<div class="flex justify-end mt-6 space-x-2">
|
|
279
|
+
<button @click="closePostModal()" class="inline-flex justify-center px-3 py-2 font-semibold text-gray-900 text-sm dark:text-white bg-white dark:bg-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-600 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-600 shadow-sm">Cancel</button>
|
|
280
|
+
<button @click="savePost()" :disabled="!postContent()" class="inline-flex justify-center px-3 py-2 font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md shadow-sm disabled:opacity-50">{{ isNewPost() ? 'Create' : 'Save' }}</button>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</PageLayout>
|
|
285
|
+
|
|
286
|
+
<script client>
|
|
287
|
+
// Filter options — moved out of the former `<script server>` block
|
|
288
|
+
// (stacksjs/stacks#1838).
|
|
289
|
+
const statuses = ['Published', 'Scheduled', 'Draft', 'Failed']
|
|
290
|
+
const platforms = ['Bluesky', 'Twitter', 'Instagram', 'Facebook']
|
|
291
|
+
|
|
292
|
+
interface SocialPost {
|
|
293
|
+
id: number
|
|
294
|
+
content: string
|
|
295
|
+
images: string[]
|
|
296
|
+
platform: string
|
|
297
|
+
status: 'Published' | 'Scheduled' | 'Draft' | 'Failed'
|
|
298
|
+
scheduledDate: string
|
|
299
|
+
publishedDate: string | null
|
|
300
|
+
author: string
|
|
301
|
+
authorAvatar: string
|
|
302
|
+
likes: number
|
|
303
|
+
reposts: number
|
|
304
|
+
replies: number
|
|
305
|
+
engagement: number
|
|
306
|
+
tags: string[]
|
|
307
|
+
isThreadStarter: boolean
|
|
308
|
+
threadId: number | null
|
|
309
|
+
threadSize: number
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const initialPosts: SocialPost[] = [
|
|
313
|
+
{ id: 1, content: 'Just launched our new summer menu! Come try our refreshing new salads and smoothies. Perfect for the hot weather! #SummerMenu #HealthyEating', images: ['https://images.unsplash.com/photo-1512621776951-a57141f2eefd?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=500&h=500&q=80'], platform: 'Bluesky', status: 'Published', scheduledDate: '2023-06-15T10:00:00', publishedDate: '2023-06-15T10:00:00', author: 'Marketing Team', authorAvatar: 'https://randomuser.me/api/portraits/women/44.jpg', likes: 28, reposts: 12, replies: 5, engagement: 4.5, tags: ['SummerMenu', 'HealthyEating'], isThreadStarter: false, threadId: null, threadSize: 0 },
|
|
314
|
+
{ id: 2, content: "We're excited to announce our partnership with local farmers to bring you the freshest ingredients possible! #LocalFarmers #FarmToTable", images: [], platform: 'Bluesky', status: 'Scheduled', scheduledDate: '2023-07-01T09:30:00', publishedDate: null, author: 'Marketing Team', authorAvatar: 'https://randomuser.me/api/portraits/women/44.jpg', likes: 0, reposts: 0, replies: 0, engagement: 0, tags: ['LocalFarmers', 'FarmToTable'], isThreadStarter: false, threadId: null, threadSize: 0 },
|
|
315
|
+
{ id: 3, content: "Thread: Our chef's journey to creating the perfect burger (1/4)\n\nIt all started with a passion for quality ingredients. #BurgerStory #CulinaryJourney", images: [], platform: 'Bluesky', status: 'Draft', scheduledDate: '2023-07-05T12:00:00', publishedDate: null, author: 'Chef Michael', authorAvatar: 'https://randomuser.me/api/portraits/men/32.jpg', likes: 0, reposts: 0, replies: 0, engagement: 0, tags: ['BurgerStory', 'CulinaryJourney'], isThreadStarter: true, threadId: 101, threadSize: 4 },
|
|
316
|
+
{ id: 4, content: 'Join us this weekend for our special brunch menu! Mimosas included with every order. #WeekendBrunch #Mimosas', images: [], platform: 'Bluesky', status: 'Failed', scheduledDate: '2023-06-20T08:00:00', publishedDate: null, author: 'Marketing Team', authorAvatar: 'https://randomuser.me/api/portraits/women/44.jpg', likes: 0, reposts: 0, replies: 0, engagement: 0, tags: ['WeekendBrunch', 'Mimosas'], isThreadStarter: false, threadId: null, threadSize: 0 },
|
|
317
|
+
{ id: 5, content: 'Customer appreciation week starts tomorrow! Enjoy 15% off all orders. #CustomerAppreciation', images: [], platform: 'Bluesky', status: 'Published', scheduledDate: '2023-06-25T11:00:00', publishedDate: '2023-06-25T11:00:00', author: 'Marketing Team', authorAvatar: 'https://randomuser.me/api/portraits/women/44.jpg', likes: 45, reposts: 23, replies: 15, engagement: 8.3, tags: ['CustomerAppreciation'], isThreadStarter: false, threadId: null, threadSize: 0 },
|
|
318
|
+
{ id: 6, content: "We're hiring! Looking for passionate chefs and servers to join our team. #NowHiring #RestaurantJobs", images: [], platform: 'Bluesky', status: 'Scheduled', scheduledDate: '2023-07-10T14:00:00', publishedDate: null, author: 'HR Team', authorAvatar: 'https://randomuser.me/api/portraits/men/67.jpg', likes: 0, reposts: 0, replies: 0, engagement: 0, tags: ['NowHiring', 'RestaurantJobs'], isThreadStarter: false, threadId: null, threadSize: 0 },
|
|
319
|
+
{ id: 7, content: 'Happy hour just got happier! Join us Monday-Friday from 4-6pm for half-price appetizers. #HappyHour #Cocktails', images: [], platform: 'Bluesky', status: 'Published', scheduledDate: '2023-06-10T16:00:00', publishedDate: '2023-06-10T16:00:00', author: 'Marketing Team', authorAvatar: 'https://randomuser.me/api/portraits/women/44.jpg', likes: 37, reposts: 18, replies: 8, engagement: 6.3, tags: ['HappyHour', 'Cocktails'], isThreadStarter: false, threadId: null, threadSize: 0 },
|
|
320
|
+
]
|
|
321
|
+
|
|
322
|
+
const posts = state<SocialPost[]>(initialPosts)
|
|
323
|
+
const searchQuery = state('')
|
|
324
|
+
const statusFilter = state('all')
|
|
325
|
+
const platformFilter = state('all')
|
|
326
|
+
const authorFilter = state('all')
|
|
327
|
+
const sortBy = state<'scheduledDate' | 'engagement' | 'author'>('scheduledDate')
|
|
328
|
+
const sortOrder = state<'asc' | 'desc'>('desc')
|
|
329
|
+
const viewMode = state<'detailed' | 'compact'>('compact')
|
|
330
|
+
const currentPage = state(1)
|
|
331
|
+
const itemsPerPage = 5
|
|
332
|
+
|
|
333
|
+
const showPostModal = state(false)
|
|
334
|
+
const isNewPost = state(false)
|
|
335
|
+
const editingPostId = state<number | null>(null)
|
|
336
|
+
const postContent = state('')
|
|
337
|
+
const postPlatform = state('Bluesky')
|
|
338
|
+
const postStatus = state<'Published' | 'Scheduled' | 'Draft' | 'Failed'>('Draft')
|
|
339
|
+
const postScheduledDate = state('')
|
|
340
|
+
const postAuthor = state('Marketing Team')
|
|
341
|
+
|
|
342
|
+
const totalPosts = derived(() => posts().length)
|
|
343
|
+
const publishedCount = derived(() => posts().filter(p => p.status === 'Published').length)
|
|
344
|
+
const scheduledCount = derived(() => posts().filter(p => p.status === 'Scheduled').length)
|
|
345
|
+
const draftsCount = derived(() => posts().filter(p => p.status === 'Draft').length)
|
|
346
|
+
|
|
347
|
+
const authorList = derived(() => {
|
|
348
|
+
const set = new Set<string>()
|
|
349
|
+
for (const p of posts())
|
|
350
|
+
set.add(p.author)
|
|
351
|
+
return Array.from(set).sort()
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
const filteredPosts = derived(() => {
|
|
355
|
+
const q = searchQuery().toLowerCase()
|
|
356
|
+
const status = statusFilter()
|
|
357
|
+
const platform = platformFilter()
|
|
358
|
+
const author = authorFilter()
|
|
359
|
+
const sort = sortBy()
|
|
360
|
+
const order = sortOrder()
|
|
361
|
+
return posts()
|
|
362
|
+
.filter((post) => {
|
|
363
|
+
const matchesSearch = !q
|
|
364
|
+
|| post.content.toLowerCase().includes(q)
|
|
365
|
+
|| post.tags.some(t => t.toLowerCase().includes(q))
|
|
366
|
+
|| post.author.toLowerCase().includes(q)
|
|
367
|
+
const matchesStatus = status === 'all' || post.status === status
|
|
368
|
+
const matchesPlatform = platform === 'all' || post.platform === platform
|
|
369
|
+
const matchesAuthor = author === 'all' || post.author === author
|
|
370
|
+
return matchesSearch && matchesStatus && matchesPlatform && matchesAuthor
|
|
371
|
+
})
|
|
372
|
+
.sort((a, b) => {
|
|
373
|
+
let cmp = 0
|
|
374
|
+
if (sort === 'scheduledDate')
|
|
375
|
+
cmp = new Date(a.scheduledDate).getTime() - new Date(b.scheduledDate).getTime()
|
|
376
|
+
else if (sort === 'engagement')
|
|
377
|
+
cmp = a.engagement - b.engagement
|
|
378
|
+
else
|
|
379
|
+
cmp = a.author.localeCompare(b.author)
|
|
380
|
+
return order === 'asc' ? cmp : -cmp
|
|
381
|
+
})
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
const totalPages = derived(() => Math.max(1, Math.ceil(filteredPosts().length / itemsPerPage)))
|
|
385
|
+
|
|
386
|
+
const paginatedPosts = derived(() => {
|
|
387
|
+
const start = (currentPage() - 1) * itemsPerPage
|
|
388
|
+
return filteredPosts().slice(start, start + itemsPerPage)
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
effect(() => {
|
|
392
|
+
filteredPosts()
|
|
393
|
+
currentPage.set(1)
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
effect(() => {
|
|
397
|
+
if (currentPage() > totalPages())
|
|
398
|
+
currentPage.set(totalPages())
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
function formatDate(date: string | null) {
|
|
402
|
+
if (!date)
|
|
403
|
+
return 'N/A'
|
|
404
|
+
return new Date(date).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function truncate(text: string, max: number) {
|
|
408
|
+
if (text.length <= max)
|
|
409
|
+
return text
|
|
410
|
+
return `${text.substring(0, max)}…`
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function statusBadgeClass(status: string) {
|
|
414
|
+
const base = 'inline-flex items-center rounded-full px-2 py-1 text-xs font-medium w-fit'
|
|
415
|
+
switch (status) {
|
|
416
|
+
case 'Published': return `${base} bg-green-50 text-green-700 ring-1 ring-inset ring-green-600/20 dark:bg-green-900/30 dark:text-green-400`
|
|
417
|
+
case 'Scheduled': return `${base} bg-blue-50 text-blue-700 ring-1 ring-inset ring-blue-600/20 dark:bg-blue-900/30 dark:text-blue-400`
|
|
418
|
+
case 'Draft': return `${base} bg-yellow-50 text-yellow-700 ring-1 ring-inset ring-yellow-600/20 dark:bg-yellow-900/30 dark:text-yellow-400`
|
|
419
|
+
case 'Failed': return `${base} bg-red-50 text-red-700 ring-1 ring-inset ring-red-600/20 dark:bg-red-900/30 dark:text-red-400`
|
|
420
|
+
default: return `${base} bg-gray-50 text-gray-700 ring-1 ring-inset ring-gray-600/20 dark:bg-neutral-900/30 dark:text-neutral-400`
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function viewModeButtonClass(mode: 'detailed' | 'compact', side: 'l' | 'r') {
|
|
425
|
+
const radius = side === 'l' ? 'rounded-l-md' : 'rounded-r-md -ml-px'
|
|
426
|
+
const base = `relative inline-flex items-center ${radius} px-3 py-2 text-sm font-semibold ring-1 ring-inset focus:z-10`
|
|
427
|
+
return viewMode() === mode
|
|
428
|
+
? `${base} bg-blue-600 text-white ring-blue-600`
|
|
429
|
+
: `${base} bg-white text-gray-900 ring-gray-300 hover:bg-gray-50 dark:bg-neutral-800 dark:text-white dark:ring-neutral-700 dark:hover:bg-neutral-700`
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function setViewMode(mode: 'detailed' | 'compact') {
|
|
433
|
+
viewMode.set(mode)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function sortButtonClass(key: string, extra: string) {
|
|
437
|
+
const active = sortBy() === key ? 'font-semibold text-blue-600 dark:text-blue-400' : ''
|
|
438
|
+
return `flex items-center hover:text-gray-700 dark:hover:text-gray-300 ${extra} ${active}`.trim()
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function sortIndicator(key: string) {
|
|
442
|
+
if (sortBy() !== key)
|
|
443
|
+
return ''
|
|
444
|
+
return sortOrder() === 'asc' ? '↑' : '↓'
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function toggleSort(key: 'scheduledDate' | 'engagement' | 'author') {
|
|
448
|
+
if (sortBy() === key)
|
|
449
|
+
sortOrder.set(sortOrder() === 'asc' ? 'desc' : 'asc')
|
|
450
|
+
else {
|
|
451
|
+
sortBy.set(key)
|
|
452
|
+
sortOrder.set('desc')
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function nextPage() {
|
|
457
|
+
if (currentPage() < totalPages())
|
|
458
|
+
currentPage.set(currentPage() + 1)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function prevPage() {
|
|
462
|
+
if (currentPage() > 1)
|
|
463
|
+
currentPage.set(currentPage() - 1)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function openNewPostModal() {
|
|
467
|
+
editingPostId.set(null)
|
|
468
|
+
isNewPost.set(true)
|
|
469
|
+
postContent.set('')
|
|
470
|
+
postPlatform.set('Bluesky')
|
|
471
|
+
postStatus.set('Draft')
|
|
472
|
+
postScheduledDate.set(new Date().toISOString().slice(0, 16))
|
|
473
|
+
postAuthor.set('Marketing Team')
|
|
474
|
+
showPostModal.set(true)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function openEditPostModal(id: number) {
|
|
478
|
+
const post = posts().find(p => p.id === id)
|
|
479
|
+
if (!post)
|
|
480
|
+
return
|
|
481
|
+
editingPostId.set(id)
|
|
482
|
+
isNewPost.set(false)
|
|
483
|
+
postContent.set(post.content)
|
|
484
|
+
postPlatform.set(post.platform)
|
|
485
|
+
postStatus.set(post.status)
|
|
486
|
+
postScheduledDate.set(post.scheduledDate.slice(0, 16))
|
|
487
|
+
postAuthor.set(post.author)
|
|
488
|
+
showPostModal.set(true)
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function closePostModal() {
|
|
492
|
+
showPostModal.set(false)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function extractTags(content: string): string[] {
|
|
496
|
+
const matches = content.match(/#[a-z0-9]+/gi)
|
|
497
|
+
if (!matches)
|
|
498
|
+
return []
|
|
499
|
+
return matches.map(t => t.substring(1))
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function savePost() {
|
|
503
|
+
const content = postContent().trim()
|
|
504
|
+
if (!content)
|
|
505
|
+
return
|
|
506
|
+
const tags = extractTags(content)
|
|
507
|
+
if (isNewPost()) {
|
|
508
|
+
const nextId = Math.max(0, ...posts().map(p => p.id)) + 1
|
|
509
|
+
posts.set([...posts(), {
|
|
510
|
+
id: nextId,
|
|
511
|
+
content,
|
|
512
|
+
images: [],
|
|
513
|
+
platform: postPlatform(),
|
|
514
|
+
status: postStatus(),
|
|
515
|
+
scheduledDate: postScheduledDate() || new Date().toISOString(),
|
|
516
|
+
publishedDate: postStatus() === 'Published' ? new Date().toISOString() : null,
|
|
517
|
+
author: postAuthor(),
|
|
518
|
+
authorAvatar: 'https://randomuser.me/api/portraits/women/44.jpg',
|
|
519
|
+
likes: 0,
|
|
520
|
+
reposts: 0,
|
|
521
|
+
replies: 0,
|
|
522
|
+
engagement: 0,
|
|
523
|
+
tags,
|
|
524
|
+
isThreadStarter: false,
|
|
525
|
+
threadId: null,
|
|
526
|
+
threadSize: 0,
|
|
527
|
+
}])
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
const id = editingPostId()
|
|
531
|
+
posts.set(posts().map(p => p.id === id
|
|
532
|
+
? { ...p, content, tags, platform: postPlatform(), status: postStatus(), scheduledDate: postScheduledDate() || p.scheduledDate, author: postAuthor() }
|
|
533
|
+
: p))
|
|
534
|
+
}
|
|
535
|
+
closePostModal()
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function deletePost(id: number) {
|
|
539
|
+
posts.set(posts().filter(p => p.id !== id))
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function duplicatePost(id: number) {
|
|
543
|
+
const original = posts().find(p => p.id === id)
|
|
544
|
+
if (!original)
|
|
545
|
+
return
|
|
546
|
+
const nextId = Math.max(0, ...posts().map(p => p.id)) + 1
|
|
547
|
+
posts.set([...posts(), {
|
|
548
|
+
...original,
|
|
549
|
+
id: nextId,
|
|
550
|
+
status: 'Draft',
|
|
551
|
+
publishedDate: null,
|
|
552
|
+
likes: 0,
|
|
553
|
+
reposts: 0,
|
|
554
|
+
replies: 0,
|
|
555
|
+
engagement: 0,
|
|
556
|
+
}])
|
|
557
|
+
}
|
|
558
|
+
</script>
|
|
559
|
+
|
|
560
|
+
@endsection
|