@stacksjs/defaults 0.70.206 → 0.70.208
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/TODO.md +21 -0
- package/ai/AGENTS.md +79 -0
- package/ai/README.md +45 -0
- package/ai/claude/launch.json +12 -0
- package/ai/skills/stacks-actions/SKILL.md +128 -0
- package/ai/skills/stacks-ai/SKILL.md +276 -0
- package/ai/skills/stacks-alias/SKILL.md +94 -0
- package/ai/skills/stacks-analytics/SKILL.md +91 -0
- package/ai/skills/stacks-api/SKILL.md +738 -0
- package/ai/skills/stacks-arrays/SKILL.md +277 -0
- package/ai/skills/stacks-auth/SKILL.md +422 -0
- package/ai/skills/stacks-auto-imports/SKILL.md +138 -0
- package/ai/skills/stacks-brandkit/SKILL.md +811 -0
- package/ai/skills/stacks-browse/SKILL.md +127 -0
- package/ai/skills/stacks-browse/scripts/browse.ts +492 -0
- package/ai/skills/stacks-browser/SKILL.md +145 -0
- package/ai/skills/stacks-buddy/SKILL.md +676 -0
- package/ai/skills/stacks-build/SKILL.md +163 -0
- package/ai/skills/stacks-cache/SKILL.md +125 -0
- package/ai/skills/stacks-calendar/SKILL.md +113 -0
- package/ai/skills/stacks-chat/SKILL.md +371 -0
- package/ai/skills/stacks-cli/SKILL.md +112 -0
- package/ai/skills/stacks-cloud/SKILL.md +192 -0
- package/ai/skills/stacks-cms/SKILL.md +217 -0
- package/ai/skills/stacks-collections/SKILL.md +82 -0
- package/ai/skills/stacks-commerce/SKILL.md +114 -0
- package/ai/skills/stacks-composables/SKILL.md +161 -0
- package/ai/skills/stacks-config/SKILL.md +144 -0
- package/ai/skills/stacks-configuration/SKILL.md +154 -0
- package/ai/skills/stacks-cron/SKILL.md +123 -0
- package/ai/skills/stacks-crosswind/SKILL.md +190 -0
- package/ai/skills/stacks-dashboard/SKILL.md +173 -0
- package/ai/skills/stacks-database/SKILL.md +286 -0
- package/ai/skills/stacks-datetime/SKILL.md +269 -0
- package/ai/skills/stacks-dependencies/SKILL.md +111 -0
- package/ai/skills/stacks-deploy/SKILL.md +102 -0
- package/ai/skills/stacks-design-brutalist/SKILL.md +110 -0
- package/ai/skills/stacks-design-minimalist/SKILL.md +115 -0
- package/ai/skills/stacks-design-output/SKILL.md +55 -0
- package/ai/skills/stacks-design-soft/SKILL.md +156 -0
- package/ai/skills/stacks-design-taste/SKILL.md +1012 -0
- package/ai/skills/stacks-design-taste-codex/SKILL.md +294 -0
- package/ai/skills/stacks-desktop/SKILL.md +78 -0
- package/ai/skills/stacks-development/SKILL.md +298 -0
- package/ai/skills/stacks-dns/SKILL.md +149 -0
- package/ai/skills/stacks-docs/SKILL.md +109 -0
- package/ai/skills/stacks-email/SKILL.md +236 -0
- package/ai/skills/stacks-enums/SKILL.md +100 -0
- package/ai/skills/stacks-env/SKILL.md +181 -0
- package/ai/skills/stacks-error-handling/SKILL.md +137 -0
- package/ai/skills/stacks-events/SKILL.md +228 -0
- package/ai/skills/stacks-faker/SKILL.md +158 -0
- package/ai/skills/stacks-git/SKILL.md +158 -0
- package/ai/skills/stacks-guard/SKILL.md +98 -0
- package/ai/skills/stacks-health/SKILL.md +75 -0
- package/ai/skills/stacks-http/SKILL.md +316 -0
- package/ai/skills/stacks-i18n/SKILL.md +198 -0
- package/ai/skills/stacks-image-to-code/SKILL.md +629 -0
- package/ai/skills/stacks-imagegen-mobile/SKILL.md +1474 -0
- package/ai/skills/stacks-imagegen-web/SKILL.md +998 -0
- package/ai/skills/stacks-investigate/SKILL.md +119 -0
- package/ai/skills/stacks-jobs/SKILL.md +136 -0
- package/ai/skills/stacks-lint/SKILL.md +184 -0
- package/ai/skills/stacks-listeners/SKILL.md +99 -0
- package/ai/skills/stacks-logging/SKILL.md +78 -0
- package/ai/skills/stacks-mail/SKILL.md +120 -0
- package/ai/skills/stacks-middleware/SKILL.md +249 -0
- package/ai/skills/stacks-migrations/SKILL.md +105 -0
- package/ai/skills/stacks-models/SKILL.md +267 -0
- package/ai/skills/stacks-new-feature/SKILL.md +206 -0
- package/ai/skills/stacks-notifications/SKILL.md +210 -0
- package/ai/skills/stacks-objects/SKILL.md +205 -0
- package/ai/skills/stacks-office-hours/SKILL.md +134 -0
- package/ai/skills/stacks-orm/SKILL.md +342 -0
- package/ai/skills/stacks-path/SKILL.md +118 -0
- package/ai/skills/stacks-payments/SKILL.md +373 -0
- package/ai/skills/stacks-plan-review/SKILL.md +152 -0
- package/ai/skills/stacks-plugins/SKILL.md +149 -0
- package/ai/skills/stacks-push/SKILL.md +376 -0
- package/ai/skills/stacks-query-builder/SKILL.md +128 -0
- package/ai/skills/stacks-queue/SKILL.md +559 -0
- package/ai/skills/stacks-realtime/SKILL.md +442 -0
- package/ai/skills/stacks-redesign/SKILL.md +182 -0
- package/ai/skills/stacks-registry/SKILL.md +58 -0
- package/ai/skills/stacks-repl/SKILL.md +80 -0
- package/ai/skills/stacks-retro/SKILL.md +122 -0
- package/ai/skills/stacks-review/SKILL.md +135 -0
- package/ai/skills/stacks-router/SKILL.md +231 -0
- package/ai/skills/stacks-routes/SKILL.md +105 -0
- package/ai/skills/stacks-scaffolding/SKILL.md +162 -0
- package/ai/skills/stacks-scheduler/SKILL.md +363 -0
- package/ai/skills/stacks-search-engine/SKILL.md +112 -0
- package/ai/skills/stacks-security/SKILL.md +129 -0
- package/ai/skills/stacks-security-audit/SKILL.md +128 -0
- package/ai/skills/stacks-server/SKILL.md +458 -0
- package/ai/skills/stacks-shell/SKILL.md +62 -0
- package/ai/skills/stacks-slug/SKILL.md +87 -0
- package/ai/skills/stacks-sms/SKILL.md +385 -0
- package/ai/skills/stacks-socials/SKILL.md +303 -0
- package/ai/skills/stacks-storage/SKILL.md +576 -0
- package/ai/skills/stacks-strings/SKILL.md +416 -0
- package/ai/skills/stacks-stx/SKILL.md +176 -0
- package/ai/skills/stacks-technical-diagrams/LICENSE +22 -0
- package/ai/skills/stacks-technical-diagrams/SKILL.md +290 -0
- package/ai/skills/stacks-technical-diagrams/agents/openai.yaml +4 -0
- package/ai/skills/stacks-technical-diagrams/assets/template.html +1252 -0
- package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +289 -0
- package/ai/skills/stacks-technical-diagrams/bunfig.toml +1 -0
- package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +64 -0
- package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +88 -0
- package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +75 -0
- package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +70 -0
- package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +41 -0
- package/ai/skills/stacks-technical-diagrams/renderers/architecture/grid.mjs +62 -0
- package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +371 -0
- package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +82 -0
- package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +295 -0
- package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +91 -0
- package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +384 -0
- package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +84 -0
- package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +257 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +42 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +13 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +230 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/layout-report.mjs +40 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +88 -0
- package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +40 -0
- package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +103 -0
- package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +481 -0
- package/ai/skills/stacks-technical-diagrams/schemas/README.md +68 -0
- package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +121 -0
- package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +43 -0
- package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +221 -0
- package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +241 -0
- package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +186 -0
- package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +332 -0
- package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +297 -0
- package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +28 -0
- package/ai/skills/stacks-testing/SKILL.md +129 -0
- package/ai/skills/stacks-tunnel/SKILL.md +125 -0
- package/ai/skills/stacks-types/SKILL.md +178 -0
- package/ai/skills/stacks-ui/SKILL.md +246 -0
- package/ai/skills/stacks-utils/SKILL.md +444 -0
- package/ai/skills/stacks-validation/SKILL.md +371 -0
- package/ai/skills/stacks-whois/SKILL.md +315 -0
- package/assets/world-map-data.ts +121 -0
- package/bootstrap.ts +114 -0
- package/docs/api-examples.md +46 -0
- package/docs/index.md +24 -0
- package/docs/markdown-examples.md +87 -0
- package/functions/analytics.ts +97 -0
- package/functions/api.ts +143 -0
- package/functions/auth.ts +208 -0
- package/functions/billing/payments.ts +149 -0
- package/functions/blog.ts +120 -0
- package/functions/cms/categorizables.ts +107 -0
- package/functions/cms/pages.ts +108 -0
- package/functions/cms/posts.ts +100 -0
- package/functions/cms/taggables.ts +102 -0
- package/functions/commerce/coupons.ts +128 -0
- package/functions/commerce/customers.ts +128 -0
- package/functions/commerce/gift-cards.ts +119 -0
- package/functions/commerce/orders.ts +112 -0
- package/functions/commerce/payments.ts +119 -0
- package/functions/commerce/products/categories.ts +123 -0
- package/functions/commerce/products/manufacturers.ts +123 -0
- package/functions/commerce/products/products.ts +123 -0
- package/functions/commerce/products/reviews.ts +123 -0
- package/functions/commerce/products/units.ts +123 -0
- package/functions/commerce/products/variants.ts +123 -0
- package/functions/commerce/shippings/delivery-routes.ts +123 -0
- package/functions/commerce/shippings/digital-deliveries.ts +119 -0
- package/functions/commerce/shippings/drivers.ts +123 -0
- package/functions/commerce/shippings/license-keys.ts +123 -0
- package/functions/commerce/shippings/shipping-methods.ts +123 -0
- package/functions/commerce/shippings/shipping-rates.ts +119 -0
- package/functions/commerce/shippings/shipping-zones.ts +119 -0
- package/functions/commerce/taxes.ts +119 -0
- package/functions/commerce/waitlist/products.ts +123 -0
- package/functions/commerce/waitlist/restaurants.ts +123 -0
- package/functions/craft.ts +107 -0
- package/functions/dashboard.ts +153 -0
- package/functions/inbox.ts +95 -0
- package/functions/jobs.ts +86 -0
- package/functions/monitoring/errors.ts +295 -0
- package/functions/queue.ts +111 -0
- package/functions/realtime/websockets.ts +96 -0
- package/functions/realtime.ts +52 -0
- package/functions/releases.ts +49 -0
- package/functions/search.ts +126 -0
- package/functions/toasts.ts +95 -0
- package/functions/uploads.ts +216 -0
- package/ide/.gitignore +4 -0
- package/ide/cursor/rules/auto-imports.mdc +574 -0
- package/ide/cursor/rules/code-style.mdc +12 -0
- package/ide/cursor/rules/documentation.mdc +9 -0
- package/ide/cursor/rules/error-handling-validation.mdc +11 -0
- package/ide/cursor/rules/key-conventions.mdc +11 -0
- package/ide/cursor/rules/project-structure.mdc +169 -0
- package/ide/cursor/rules/readme.mdc +318 -0
- package/ide/cursor/rules/syntax-formatting.mdc +9 -0
- package/ide/cursor/rules/testing.mdc +10 -0
- package/ide/cursor/rules/typescript.mdc +9 -0
- package/ide/cursor/rules/ui-styling.mdc +0 -0
- package/ide/dictionary.txt +397 -0
- package/ide/jetbrains/.fleet/settings.json +4 -0
- package/ide/jetbrains/.idea/.gitignore +5 -0
- package/ide/jetbrains/.idea/.name +1 -0
- package/ide/jetbrains/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/ide/jetbrains/.idea/jsLinters/eslint.xml +7 -0
- package/ide/jetbrains/.idea/modules.xml +8 -0
- package/ide/jetbrains/.idea/stacks.iml +22 -0
- package/ide/jetbrains/.idea/vcs.xml +6 -0
- package/ide/jetbrains/.idea/webResources.xml +14 -0
- package/ide/jetbrains/IntelliJ IDEA Global Settings +0 -0
- package/ide/jetbrains/installed.txt +4 -0
- package/ide/jetbrains/options/github-copilot.xml +5 -0
- package/ide/jetbrains/options/nodejs.xml +7 -0
- package/ide/jetbrains/options/templates.xml +124 -0
- package/ide/jetbrains/templates/JavaScript.xml +6 -0
- package/ide/jetbrains/templates/Stacks.xml +8 -0
- package/ide/vscode/.vscode/README.md +23 -0
- package/ide/vscode/.vscode/extensions.json +5 -0
- package/ide/vscode/.vscode/settings.json +176 -0
- package/ide/vscode/.vscode/stacks.code-snippets +181 -0
- package/ide/vscode/LICENSE.md +21 -0
- package/ide/vscode/README.md +36 -0
- package/ide/vscode/logo.png +0 -0
- package/ide/vscode/package.json +79 -0
- package/ide/zed/.zed/settings.json +139 -0
- package/lang/README.md +28 -0
- package/lang/de.yml +14 -0
- package/lang/en.yml +15 -0
- package/package.json +19 -3
- package/project/bootstrap +161 -0
- package/project/buddy +175 -0
- package/project/storage/framework/tsconfig.app.json +50 -0
- package/project/storage/framework/tsconfig.base.json +58 -0
- package/routes/core.ts +78 -0
- package/routes/dashboard-api.ts +233 -0
- package/routes/dashboard.ts +739 -0
- package/routes/email.ts +129 -0
- package/stores/[name].stx +16 -0
- package/stores/git.ts +71 -0
- package/stores/payment.ts +474 -0
- package/stores/queue.ts +43 -0
- package/stores/user.ts +29 -0
- package/stx-components-plugin.ts +25 -0
- package/styles/design-tokens.css +597 -0
- package/types/dashboard.ts +70 -0
- package/types/defaults.ts +438 -0
- package/vcs/github/CODE_OF_CONDUCT.md +83 -0
- package/vcs/github/CONTRIBUTING.md +187 -0
- package/vcs/github/EXPLANATIONS.md +41 -0
- package/vcs/github/FUNDING.yml +1 -0
- package/vcs/github/ISSUE_TEMPLATE/bug_report.yml +49 -0
- package/vcs/github/ISSUE_TEMPLATE/config.yml +10 -0
- package/vcs/github/ISSUE_TEMPLATE/feature_request.yml +44 -0
- package/vcs/github/SECURITY.md +70 -0
- package/vcs/github/renovate.json +5 -0
- package/vcs/github/stale.yml +14 -0
- package/vcs/github/workflows/README.md +16 -0
- package/vcs/github/workflows/ci.yml +114 -0
- package/vcs/github/workflows/export-size.yml +25 -0
- package/vcs/github/workflows/labeler.yml +13 -0
- package/vcs/github/workflows/release.yml +219 -0
- package/views/[...all].stx +8 -0
- package/views/about.md +3 -0
- package/views/auth/login.stx +1 -0
- package/views/auth/signup.stx +85 -0
- package/views/dashboard/.discovered-models.json +542 -0
- package/views/dashboard/AUDIT.md +904 -0
- package/views/dashboard/[...all].stx +89 -0
- package/views/dashboard/access-tokens/index.stx +177 -0
- package/views/dashboard/actions/index.stx +129 -0
- package/views/dashboard/analytics/blog/index.stx +356 -0
- package/views/dashboard/analytics/browsers/index.stx +263 -0
- package/views/dashboard/analytics/commerce/sales/index.stx +329 -0
- package/views/dashboard/analytics/commerce/web/index.stx +379 -0
- package/views/dashboard/analytics/countries/index.stx +289 -0
- package/views/dashboard/analytics/devices/index.stx +305 -0
- package/views/dashboard/analytics/events/index.stx +536 -0
- package/views/dashboard/analytics/index.stx +88 -0
- package/views/dashboard/analytics/marketing/index.stx +315 -0
- package/views/dashboard/analytics/pages/index.stx +271 -0
- package/views/dashboard/analytics/referrers/index.stx +319 -0
- package/views/dashboard/analytics/web/index.stx +356 -0
- package/views/dashboard/buddy/index.stx +198 -0
- package/views/dashboard/ci/index.stx +462 -0
- package/views/dashboard/cloud/index.stx +452 -0
- package/views/dashboard/commands/index.stx +295 -0
- package/views/dashboard/commerce/categories/index.stx +406 -0
- package/views/dashboard/commerce/coupons/index.stx +485 -0
- package/views/dashboard/commerce/customers/index.stx +410 -0
- package/views/dashboard/commerce/dashboard/index.stx +287 -0
- package/views/dashboard/commerce/delivery/delivery-routes.stx +305 -0
- package/views/dashboard/commerce/delivery/digital-delivery.stx +332 -0
- package/views/dashboard/commerce/delivery/drivers.stx +397 -0
- package/views/dashboard/commerce/delivery/index.stx +18 -0
- package/views/dashboard/commerce/delivery/license-keys.stx +380 -0
- package/views/dashboard/commerce/delivery/shipping-methods.stx +346 -0
- package/views/dashboard/commerce/delivery/shipping-rates.stx +338 -0
- package/views/dashboard/commerce/delivery/shipping-zones.stx +302 -0
- package/views/dashboard/commerce/delivery/tabs.ts +25 -0
- package/views/dashboard/commerce/gift-cards/index.stx +561 -0
- package/views/dashboard/commerce/manufacturers/index.stx +308 -0
- package/views/dashboard/commerce/orders/index.stx +247 -0
- package/views/dashboard/commerce/payments/index.stx +535 -0
- package/views/dashboard/commerce/pos/index.stx +653 -0
- package/views/dashboard/commerce/printers/devices/index.stx +273 -0
- package/views/dashboard/commerce/printers/receipts/index.stx +308 -0
- package/views/dashboard/commerce/products/detail.stx +563 -0
- package/views/dashboard/commerce/products/index.stx +370 -0
- package/views/dashboard/commerce/reviews/index.stx +656 -0
- package/views/dashboard/commerce/reviews/index.ts +0 -0
- package/views/dashboard/commerce/taxes/index.stx +377 -0
- package/views/dashboard/commerce/units/index.stx +317 -0
- package/views/dashboard/commerce/variants/index.stx +319 -0
- package/views/dashboard/commerce/waitlist/products.stx +942 -0
- package/views/dashboard/commerce/waitlist/restaurant.stx +1024 -0
- package/views/dashboard/components/index.stx +330 -0
- package/views/dashboard/composables/index.ts +19 -0
- package/views/dashboard/composables/useChart.ts +77 -0
- package/views/dashboard/composables/useNavigation.ts +99 -0
- package/views/dashboard/composables/useRole.ts +58 -0
- package/views/dashboard/composables/useTheme.ts +74 -0
- package/views/dashboard/content/authors/index.stx +171 -0
- package/views/dashboard/content/blog/index.stx +493 -0
- package/views/dashboard/content/categories/index.stx +169 -0
- package/views/dashboard/content/comments/index.stx +189 -0
- package/views/dashboard/content/dashboard.stx +383 -0
- package/views/dashboard/content/files/index.stx +706 -0
- package/views/dashboard/content/pages/index.stx +174 -0
- package/views/dashboard/content/posts/index.stx +269 -0
- package/views/dashboard/content/seo/index.stx +240 -0
- package/views/dashboard/content/tags/index.stx +167 -0
- package/views/dashboard/data/activity/index.stx +261 -0
- package/views/dashboard/data/dashboard/index.stx +116 -0
- package/views/dashboard/data/subscribers.stx +270 -0
- package/views/dashboard/data/teams.stx +275 -0
- package/views/dashboard/data/users.stx +279 -0
- package/views/dashboard/dependencies/index.stx +178 -0
- package/views/dashboard/deployments/[id].stx +234 -0
- package/views/dashboard/deployments/index.stx +9 -0
- package/views/dashboard/dns/index.stx +163 -0
- package/views/dashboard/environment/index.stx +283 -0
- package/views/dashboard/errors/index.stx +274 -0
- package/views/dashboard/forgot-password.stx +57 -0
- package/views/dashboard/functions/index.stx +159 -0
- package/views/dashboard/health/index.stx +248 -0
- package/views/dashboard/inbox/activity.stx +350 -0
- package/views/dashboard/inbox/index.stx +452 -0
- package/views/dashboard/inbox/settings.stx +290 -0
- package/views/dashboard/index.stx +252 -0
- package/views/dashboard/insights/index.stx +529 -0
- package/views/dashboard/jobs/[id].stx +430 -0
- package/views/dashboard/jobs/history.stx +222 -0
- package/views/dashboard/jobs/index.stx +214 -0
- package/views/dashboard/kanban/[id].stx +899 -0
- package/views/dashboard/kanban/index.stx +260 -0
- package/views/dashboard/layouts/default.stx +565 -0
- package/views/dashboard/layouts/guest.stx +10 -0
- package/views/dashboard/login.stx +48 -0
- package/views/dashboard/logs/index.stx +540 -0
- package/views/dashboard/mailboxes/index.stx +149 -0
- package/views/dashboard/management/permissions/index.stx +490 -0
- package/views/dashboard/marketing/campaigns/index.stx +604 -0
- package/views/dashboard/marketing/lists/index.stx +514 -0
- package/views/dashboard/marketing/reviews/index.stx +475 -0
- package/views/dashboard/marketing/social-posts/index.stx +560 -0
- package/views/dashboard/models/[model].stx +195 -0
- package/views/dashboard/models/index.stx +159 -0
- package/views/dashboard/monitoring/errors/index.stx +415 -0
- package/views/dashboard/notifications/dashboard.stx +383 -0
- package/views/dashboard/notifications/email.stx +336 -0
- package/views/dashboard/notifications/history.stx +373 -0
- package/views/dashboard/notifications/sms.stx +332 -0
- package/views/dashboard/packages/index.stx +195 -0
- package/views/dashboard/queries/[id].stx +381 -0
- package/views/dashboard/queries/history.stx +227 -0
- package/views/dashboard/queries/index.stx +335 -0
- package/views/dashboard/queries/slow.stx +318 -0
- package/views/dashboard/queue/index.stx +427 -0
- package/views/dashboard/realtime/index.stx +429 -0
- package/views/dashboard/register.stx +46 -0
- package/views/dashboard/releases/index.stx +389 -0
- package/views/dashboard/requests/index.stx +462 -0
- package/views/dashboard/serverless/index.stx +475 -0
- package/views/dashboard/servers/[id].stx +299 -0
- package/views/dashboard/servers/index.stx +483 -0
- package/views/dashboard/settings/billing.stx +50 -0
- package/views/dashboard/settings/index.stx +256 -0
- package/views/dashboard/settings/mail.stx +252 -0
- package/views/dashboard/stores/auth.ts +123 -0
- package/views/dashboard/stores/ci.ts +342 -0
- package/views/dashboard/stores/index.ts +129 -0
- package/views/dashboard/stores/kanban.ts +723 -0
- package/views/dashboard/stores/rbac.ts +350 -0
- package/views/dashboard/stx.config.ts +34 -0
- package/views/dashboard/teams/[id].stx +170 -0
- package/views/forum/category.stx +92 -0
- package/views/forum/index.stx +209 -0
- package/views/forum/new-topic.stx +113 -0
- package/views/forum/profile.stx +1 -0
- package/views/forum/topic.stx +143 -0
- package/views/hello-world/[name].stx +47 -0
- package/views/index.stx +21 -0
- package/views/system-tray/index.stx +147 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
<div class="min-h-screen">
|
|
5
|
+
<header class="flex sticky top-0 z-10 flex-wrap gap-6 items-center justify-between px-4 py-4 sm:px-6 lg:px-8 bg-white dark:bg-neutral-900 shadow-sm">
|
|
6
|
+
<div>
|
|
7
|
+
<div class="flex gap-2 items-center">
|
|
8
|
+
<button @click="goBack()" class="p-1 hover:bg-gray-100 dark:hover:bg-neutral-800 rounded-md transition-colors">
|
|
9
|
+
<div class="h-5 w-5 text-gray-500 dark:text-neutral-400 i-hugeicons-arrow-left"></div>
|
|
10
|
+
</button>
|
|
11
|
+
<h1 class="font-semibold leading-7 text-gray-900 text-lg dark:text-white">Query Details</h1>
|
|
12
|
+
</div>
|
|
13
|
+
<p class="mt-1 pl-7 leading-6 text-gray-500 text-sm dark:text-neutral-400">Detailed information about the database query</p>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="flex-shrink-0">
|
|
16
|
+
<StxLink to="/queries" class="inline-flex gap-x-1.5 items-center px-3 py-2 font-semibold text-sm text-white bg-blue-600 hover:bg-blue-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 shadow-sm">
|
|
17
|
+
<div class="-ml-0.5 h-5 w-5 i-hugeicons-apps-04" aria-hidden="true"></div>
|
|
18
|
+
Dashboard
|
|
19
|
+
</StxLink>
|
|
20
|
+
</div>
|
|
21
|
+
</header>
|
|
22
|
+
|
|
23
|
+
<main class="px-4 py-8 sm:px-6 lg:px-8">
|
|
24
|
+
<div :show="isLoading" class="flex items-center justify-center py-12">
|
|
25
|
+
<div class="h-8 w-8 text-blue-600 dark:text-blue-400 animate-spin i-hugeicons-loading-01"></div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div :show="hasQuery" class="space-y-6">
|
|
29
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-900 sm:rounded-lg shadow">
|
|
30
|
+
<div class="px-4 py-5 sm:px-6">
|
|
31
|
+
<div class="flex flex-wrap gap-4 items-start justify-between">
|
|
32
|
+
<div>
|
|
33
|
+
<div class="flex flex-wrap gap-3 items-center mb-2">
|
|
34
|
+
<span class="{{ getStatusColor(queryStatus()) }} inline-flex items-center px-2.5 py-1 font-medium text-sm ring-1 ring-inset rounded-md">{{ queryStatus() }}</span>
|
|
35
|
+
<span class="{{ queryTypeBadgeClass() }} inline-flex items-center px-2.5 py-1 font-medium text-sm ring-1 ring-inset rounded-md">{{ queryType() }}</span>
|
|
36
|
+
<span class="font-medium text-blue-600 text-lg dark:text-blue-400">{{ formattedDuration() }}</span>
|
|
37
|
+
</div>
|
|
38
|
+
<h3 class="font-medium text-gray-900 text-sm dark:text-white">{{ querySource() }}</h3>
|
|
39
|
+
<p class="mt-1 max-w-2xl text-gray-500 text-sm dark:text-neutral-400">{{ queryFileLine() }}</p>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="text-right">
|
|
42
|
+
<p class="text-gray-500 text-sm dark:text-neutral-400">Executed</p>
|
|
43
|
+
<p class="font-medium text-gray-900 text-sm dark:text-white">{{ queryExecutedAt() }}</p>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="border-gray-200 border-t dark:border-neutral-700">
|
|
49
|
+
<dl>
|
|
50
|
+
<div class="sm:grid sm:gap-4 sm:grid-cols-3 px-4 py-5 sm:px-6 bg-gray-50 dark:bg-neutral-800">
|
|
51
|
+
<dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Query</dt>
|
|
52
|
+
<dd class="sm:col-span-2 mt-1 sm:mt-0 text-gray-900 text-sm dark:text-white">
|
|
53
|
+
<pre class="overflow-x-auto p-3 font-mono text-gray-100 text-xs bg-gray-800 rounded-md">{{ queryText() }}</pre>
|
|
54
|
+
</dd>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="sm:grid sm:gap-4 sm:grid-cols-3 px-4 py-5 sm:px-6 bg-white dark:bg-neutral-900">
|
|
57
|
+
<dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Normalized Query</dt>
|
|
58
|
+
<dd class="sm:col-span-2 mt-1 sm:mt-0 text-gray-900 text-sm dark:text-white">
|
|
59
|
+
<pre class="overflow-x-auto p-3 font-mono text-gray-800 text-xs dark:text-neutral-200 bg-gray-100 dark:bg-neutral-800 rounded-md">{{ queryNormalized() }}</pre>
|
|
60
|
+
</dd>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="sm:grid sm:gap-4 sm:grid-cols-3 px-4 py-5 sm:px-6 bg-gray-50 dark:bg-neutral-800">
|
|
63
|
+
<dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Bindings</dt>
|
|
64
|
+
<dd class="sm:col-span-2 mt-1 sm:mt-0 text-gray-900 text-sm dark:text-white">
|
|
65
|
+
<pre :show="hasBindings" class="overflow-x-auto p-3 font-mono text-gray-800 text-xs dark:text-neutral-200 bg-gray-100 dark:bg-neutral-700 rounded-md">{{ queryBindings() }}</pre>
|
|
66
|
+
<p :show="noBindings" class="text-gray-500 text-sm dark:text-neutral-400">No bindings</p>
|
|
67
|
+
</dd>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="sm:grid sm:gap-4 sm:grid-cols-3 px-4 py-5 sm:px-6 bg-white dark:bg-neutral-900">
|
|
70
|
+
<dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Connection</dt>
|
|
71
|
+
<dd class="sm:col-span-2 mt-1 sm:mt-0 text-gray-900 text-sm dark:text-white">{{ queryConnection() }}</dd>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="sm:grid sm:gap-4 sm:grid-cols-3 px-4 py-5 sm:px-6 bg-gray-50 dark:bg-neutral-800">
|
|
74
|
+
<dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Affected Tables</dt>
|
|
75
|
+
<dd class="sm:col-span-2 mt-1 sm:mt-0 text-gray-900 text-sm dark:text-white">
|
|
76
|
+
<div class="flex flex-wrap gap-2">
|
|
77
|
+
<template :for="table in affectedTables">
|
|
78
|
+
<span class="inline-flex items-center px-2 py-1 font-medium text-blue-700 text-xs dark:text-blue-300 bg-blue-50 dark:bg-blue-900/20 ring-1 ring-blue-700/10 ring-inset rounded-md dark:ring-blue-400/30">{{ table }}</span>
|
|
79
|
+
</template>
|
|
80
|
+
</div>
|
|
81
|
+
</dd>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="sm:grid sm:gap-4 sm:grid-cols-3 px-4 py-5 sm:px-6 bg-white dark:bg-neutral-900">
|
|
84
|
+
<dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Indexes</dt>
|
|
85
|
+
<dd class="sm:col-span-2 mt-1 sm:mt-0 text-gray-900 text-sm dark:text-white">
|
|
86
|
+
<div class="space-y-4">
|
|
87
|
+
<div>
|
|
88
|
+
<h4 class="mb-1 font-medium text-gray-600 text-xs dark:text-neutral-400">Used Indexes</h4>
|
|
89
|
+
<div :show="hasIndexesUsed" class="flex flex-wrap gap-2">
|
|
90
|
+
<template :for="index in indexesUsed">
|
|
91
|
+
<span class="inline-flex items-center px-2 py-1 font-medium text-green-700 text-xs dark:text-green-300 bg-green-50 dark:bg-green-900/20 ring-1 ring-green-700/10 ring-inset rounded-md dark:ring-green-400/30">{{ index }}</span>
|
|
92
|
+
</template>
|
|
93
|
+
</div>
|
|
94
|
+
<p :show="noIndexesUsed" class="text-gray-500 text-xs dark:text-neutral-400">No indexes used</p>
|
|
95
|
+
</div>
|
|
96
|
+
<div>
|
|
97
|
+
<h4 class="mb-1 font-medium text-gray-600 text-xs dark:text-neutral-400">Missing Indexes</h4>
|
|
98
|
+
<div :show="hasMissingIndexes" class="flex flex-wrap gap-2">
|
|
99
|
+
<template :for="index in missingIndexes">
|
|
100
|
+
<span class="inline-flex items-center px-2 py-1 font-medium text-red-700 text-xs dark:text-red-300 bg-red-50 dark:bg-red-900/20 ring-1 ring-inset ring-red-700/10 rounded-md dark:ring-red-400/30">{{ index }}</span>
|
|
101
|
+
</template>
|
|
102
|
+
</div>
|
|
103
|
+
<p :show="noMissingIndexes" class="text-gray-500 text-xs dark:text-neutral-400">No missing indexes identified</p>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</dd>
|
|
107
|
+
</div>
|
|
108
|
+
</dl>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-900 sm:rounded-lg shadow">
|
|
113
|
+
<div class="px-4 py-5 sm:px-6">
|
|
114
|
+
<h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Performance Details</h3>
|
|
115
|
+
<p class="mt-1 max-w-2xl text-gray-500 text-sm dark:text-neutral-400">Execution metrics and optimization information.</p>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="border-gray-200 border-t dark:border-neutral-700">
|
|
118
|
+
<dl>
|
|
119
|
+
<div class="sm:grid sm:gap-4 sm:grid-cols-3 px-4 py-5 sm:px-6 bg-gray-50 dark:bg-neutral-800">
|
|
120
|
+
<dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Memory Usage</dt>
|
|
121
|
+
<dd class="sm:col-span-2 mt-1 sm:mt-0 text-gray-900 text-sm dark:text-white">{{ queryMemoryUsage() }} MB</dd>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="sm:grid sm:gap-4 sm:grid-cols-3 px-4 py-5 sm:px-6 bg-white dark:bg-neutral-900">
|
|
124
|
+
<dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Rows Affected</dt>
|
|
125
|
+
<dd class="sm:col-span-2 mt-1 sm:mt-0 text-gray-900 text-sm dark:text-white">{{ queryRowsAffected() }}</dd>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="sm:grid sm:gap-4 sm:grid-cols-3 px-4 py-5 sm:px-6 bg-gray-50 dark:bg-neutral-800">
|
|
128
|
+
<dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Explain Plan</dt>
|
|
129
|
+
<dd class="sm:col-span-2 mt-1 sm:mt-0 text-gray-900 text-sm dark:text-white">
|
|
130
|
+
<pre class="overflow-x-auto p-3 font-mono text-gray-800 text-xs whitespace-pre-wrap dark:text-neutral-200 bg-gray-100 dark:bg-neutral-700 rounded-md">{{ queryExplainPlan() }}</pre>
|
|
131
|
+
</dd>
|
|
132
|
+
</div>
|
|
133
|
+
<div :show="hasOptimizationSuggestions" class="sm:grid sm:gap-4 sm:grid-cols-3 px-4 py-5 sm:px-6 bg-white dark:bg-neutral-900">
|
|
134
|
+
<dt class="font-medium text-gray-500 text-sm dark:text-neutral-400">Optimization Suggestions</dt>
|
|
135
|
+
<dd class="sm:col-span-2 mt-1 sm:mt-0 text-gray-900 text-sm dark:text-white">
|
|
136
|
+
<ul class="space-y-2">
|
|
137
|
+
<template :for="suggestion in optimizationSuggestions">
|
|
138
|
+
<li class="flex items-start">
|
|
139
|
+
<div class="flex-shrink-0 mr-2 mt-0.5 h-5 w-5 text-yellow-500 i-hugeicons-lightbulb-02"></div>
|
|
140
|
+
<span class="text-gray-800 text-sm dark:text-neutral-200">{{ suggestion }}</span>
|
|
141
|
+
</li>
|
|
142
|
+
</template>
|
|
143
|
+
</ul>
|
|
144
|
+
</dd>
|
|
145
|
+
</div>
|
|
146
|
+
</dl>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<div class="overflow-hidden bg-white dark:bg-neutral-900 sm:rounded-lg shadow">
|
|
151
|
+
<div class="px-4 py-5 sm:px-6">
|
|
152
|
+
<h3 class="font-semibold leading-6 text-base text-gray-900 dark:text-white">Tags</h3>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="px-4 py-5 sm:px-6 border-gray-200 border-t dark:border-neutral-700">
|
|
155
|
+
<div class="flex flex-wrap gap-2">
|
|
156
|
+
<template :for="tag in queryTags">
|
|
157
|
+
<span class="inline-flex items-center px-2 py-1 font-medium text-gray-700 text-xs dark:text-neutral-300 bg-gray-50 dark:bg-neutral-800 ring-1 ring-gray-300 ring-inset rounded-md dark:ring-neutral-700">{{ tag }}</span>
|
|
158
|
+
</template>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div :show="notFound" class="overflow-hidden bg-white dark:bg-neutral-900 sm:rounded-lg shadow">
|
|
165
|
+
<div class="px-4 py-5 sm:p-6 text-center">
|
|
166
|
+
<div class="mb-4 mx-auto h-12 w-12 text-gray-400 i-hugeicons-file-broken"></div>
|
|
167
|
+
<h3 class="font-semibold text-base text-gray-900 dark:text-white">Query not found</h3>
|
|
168
|
+
<p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">The requested query could not be found or has been deleted.</p>
|
|
169
|
+
<div class="mt-6">
|
|
170
|
+
<StxLink to="/queries" 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">Return to Dashboard</StxLink>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</main>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<script client>
|
|
178
|
+
interface QueryRecord {
|
|
179
|
+
id: string
|
|
180
|
+
query: string
|
|
181
|
+
duration: number
|
|
182
|
+
normalized_query: string
|
|
183
|
+
connection: string
|
|
184
|
+
status: 'completed' | 'failed' | 'slow'
|
|
185
|
+
error?: string
|
|
186
|
+
executed_at: string
|
|
187
|
+
bindings?: unknown[]
|
|
188
|
+
trace?: string
|
|
189
|
+
model?: string
|
|
190
|
+
method?: string
|
|
191
|
+
file?: string
|
|
192
|
+
line?: number
|
|
193
|
+
memory_usage?: number
|
|
194
|
+
rows_affected?: number
|
|
195
|
+
transaction_id?: string
|
|
196
|
+
tags?: string[]
|
|
197
|
+
optimization_suggestions?: string[]
|
|
198
|
+
affected_tables?: string[]
|
|
199
|
+
indexes_used?: string[]
|
|
200
|
+
missing_indexes?: string[]
|
|
201
|
+
explain_plan?: string
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const route = useRoute()
|
|
205
|
+
const isLoading = state(true)
|
|
206
|
+
const query = state<QueryRecord | null>(null)
|
|
207
|
+
|
|
208
|
+
const hasQuery = derived(() => !isLoading() && query() !== null)
|
|
209
|
+
const notFound = derived(() => !isLoading() && query() === null)
|
|
210
|
+
|
|
211
|
+
function queryStatus(): string { return query()?.status ?? '' }
|
|
212
|
+
function queryText(): string { return query()?.query ?? '' }
|
|
213
|
+
function queryNormalized(): string { return query()?.normalized_query ?? '' }
|
|
214
|
+
function queryConnection(): string { return query()?.connection ?? '' }
|
|
215
|
+
function queryExecutedAt(): string { return query()?.executed_at ?? '' }
|
|
216
|
+
function queryMemoryUsage(): number { return query()?.memory_usage ?? 0 }
|
|
217
|
+
function queryRowsAffected(): number { return query()?.rows_affected ?? 0 }
|
|
218
|
+
function queryExplainPlan(): string { return query()?.explain_plan ?? '' }
|
|
219
|
+
function queryTags(): string[] { return query()?.tags ?? [] }
|
|
220
|
+
function affectedTables(): string[] { return query()?.affected_tables ?? [] }
|
|
221
|
+
function indexesUsed(): string[] { return query()?.indexes_used ?? [] }
|
|
222
|
+
function missingIndexes(): string[] { return query()?.missing_indexes ?? [] }
|
|
223
|
+
function optimizationSuggestions(): string[] { return query()?.optimization_suggestions ?? [] }
|
|
224
|
+
function querySource(): string {
|
|
225
|
+
const q = query()
|
|
226
|
+
if (q?.model && q?.method)
|
|
227
|
+
return `${q.model}.${q.method}`
|
|
228
|
+
return 'Unknown Source'
|
|
229
|
+
}
|
|
230
|
+
function queryFileLine(): string {
|
|
231
|
+
const q = query()
|
|
232
|
+
if (!q?.file)
|
|
233
|
+
return ''
|
|
234
|
+
return `${q.file}:${q.line ?? ''}`
|
|
235
|
+
}
|
|
236
|
+
function formattedDuration(): string {
|
|
237
|
+
return `${(query()?.duration ?? 0).toFixed(2)} ms`
|
|
238
|
+
}
|
|
239
|
+
function queryBindings(): string {
|
|
240
|
+
return JSON.stringify(query()?.bindings ?? [], null, 2)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const hasBindings = derived(() => (query()?.bindings?.length ?? 0) > 0)
|
|
244
|
+
const noBindings = derived(() => (query()?.bindings?.length ?? 0) === 0)
|
|
245
|
+
const hasIndexesUsed = derived(() => (query()?.indexes_used?.length ?? 0) > 0)
|
|
246
|
+
const noIndexesUsed = derived(() => (query()?.indexes_used?.length ?? 0) === 0)
|
|
247
|
+
const hasMissingIndexes = derived(() => (query()?.missing_indexes?.length ?? 0) > 0)
|
|
248
|
+
const noMissingIndexes = derived(() => (query()?.missing_indexes?.length ?? 0) === 0)
|
|
249
|
+
const hasOptimizationSuggestions = derived(() => (query()?.optimization_suggestions?.length ?? 0) > 0)
|
|
250
|
+
|
|
251
|
+
const statusColors: Record<string, string> = {
|
|
252
|
+
completed: 'text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/50 ring-green-600/20',
|
|
253
|
+
slow: 'text-yellow-700 dark:text-yellow-300 bg-yellow-50 dark:bg-yellow-900/50 ring-yellow-600/20',
|
|
254
|
+
failed: 'text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-900/50 ring-red-600/20',
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const queryTypeBadges: Record<string, string> = {
|
|
258
|
+
SELECT: 'bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300 ring-blue-700/10 dark:ring-blue-400/30',
|
|
259
|
+
INSERT: 'bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-300 ring-green-700/10 dark:ring-green-400/30',
|
|
260
|
+
UPDATE: 'bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 ring-amber-700/10 dark:ring-amber-400/30',
|
|
261
|
+
DELETE: 'bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 ring-red-700/10 dark:ring-red-400/30',
|
|
262
|
+
OTHER: 'bg-gray-50 dark:bg-neutral-900/20 text-gray-700 dark:text-neutral-300 ring-gray-700/10 dark:ring-neutral-400/30',
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function getStatusColor(status: string): string {
|
|
266
|
+
return statusColors[status] || 'text-gray-700 dark:text-neutral-300 bg-gray-50 dark:bg-neutral-900/50 ring-gray-600/20'
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function queryType(): string {
|
|
270
|
+
const upper = queryText().trim().toUpperCase()
|
|
271
|
+
if (upper.startsWith('SELECT'))
|
|
272
|
+
return 'SELECT'
|
|
273
|
+
if (upper.startsWith('INSERT'))
|
|
274
|
+
return 'INSERT'
|
|
275
|
+
if (upper.startsWith('UPDATE'))
|
|
276
|
+
return 'UPDATE'
|
|
277
|
+
if (upper.startsWith('DELETE'))
|
|
278
|
+
return 'DELETE'
|
|
279
|
+
return 'OTHER'
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function queryTypeBadgeClass(): string {
|
|
283
|
+
return queryTypeBadges[queryType()] || queryTypeBadges.OTHER as string
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function goBack(): void {
|
|
287
|
+
if (typeof window !== 'undefined' && window.history.length > 1)
|
|
288
|
+
window.history.back()
|
|
289
|
+
else
|
|
290
|
+
navigate('/queries')
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function fetchQuery(id: string): Promise<void> {
|
|
294
|
+
isLoading.set(true)
|
|
295
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
296
|
+
|
|
297
|
+
if (id === '1') {
|
|
298
|
+
query.set({
|
|
299
|
+
id: '1',
|
|
300
|
+
query: 'SELECT * FROM users WHERE email = ? LIMIT 1',
|
|
301
|
+
normalized_query: 'SELECT * FROM users WHERE email = ? LIMIT ?',
|
|
302
|
+
duration: 3.2,
|
|
303
|
+
connection: 'mysql',
|
|
304
|
+
status: 'completed',
|
|
305
|
+
executed_at: '2024-03-14 10:16:32',
|
|
306
|
+
bindings: ['user@example.com', 1],
|
|
307
|
+
model: 'User',
|
|
308
|
+
method: 'findByEmail',
|
|
309
|
+
file: 'app/Models/User.php',
|
|
310
|
+
line: 45,
|
|
311
|
+
memory_usage: 2.4,
|
|
312
|
+
rows_affected: 1,
|
|
313
|
+
tags: ['auth'],
|
|
314
|
+
affected_tables: ['users'],
|
|
315
|
+
indexes_used: ['users.email_index'],
|
|
316
|
+
missing_indexes: [],
|
|
317
|
+
explain_plan: 'EXPLAIN: id-1, select_type-SIMPLE, table-users, partitions-NULL, type-ref, possible_keys-email_index, key-email_index, key_len-767, ref-const, rows-1, filtered-100.00, Extra-Using where',
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
else if (id === '2') {
|
|
321
|
+
query.set({
|
|
322
|
+
id: '2',
|
|
323
|
+
query: 'SELECT p.*, c.name as category_name FROM posts p JOIN categories c ON p.category_id = c.id WHERE p.published_at IS NOT NULL ORDER BY p.published_at DESC LIMIT 10',
|
|
324
|
+
normalized_query: 'SELECT p.*, c.name as category_name FROM posts p JOIN categories c ON p.category_id = c.id WHERE p.published_at IS NOT NULL ORDER BY p.published_at DESC LIMIT ?',
|
|
325
|
+
duration: 45.7,
|
|
326
|
+
connection: 'mysql',
|
|
327
|
+
status: 'slow',
|
|
328
|
+
executed_at: '2024-03-14 10:15:30',
|
|
329
|
+
bindings: [10],
|
|
330
|
+
model: 'Post',
|
|
331
|
+
method: 'getRecentPublishedWithCategory',
|
|
332
|
+
file: 'app/Models/Post.php',
|
|
333
|
+
line: 78,
|
|
334
|
+
memory_usage: 5.8,
|
|
335
|
+
rows_affected: 10,
|
|
336
|
+
tags: ['content', 'frontend'],
|
|
337
|
+
affected_tables: ['posts', 'categories'],
|
|
338
|
+
indexes_used: ['categories.primary'],
|
|
339
|
+
missing_indexes: ['posts.published_at'],
|
|
340
|
+
optimization_suggestions: [
|
|
341
|
+
'Add index on posts.published_at column',
|
|
342
|
+
'Consider paginating results',
|
|
343
|
+
'Limit selected columns instead of using SELECT *',
|
|
344
|
+
],
|
|
345
|
+
explain_plan: 'EXPLAIN: id-1, select_type-SIMPLE, table-p, partitions-NULL, type-ALL, possible_keys-NULL, key-NULL, key_len-NULL, ref-NULL, rows-200, filtered-50.00, Extra-Using where; Using filesort...',
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
query.set({
|
|
350
|
+
id,
|
|
351
|
+
query: 'SELECT * FROM example_table WHERE id = ?',
|
|
352
|
+
normalized_query: 'SELECT * FROM example_table WHERE id = ?',
|
|
353
|
+
duration: 5.2,
|
|
354
|
+
connection: 'mysql',
|
|
355
|
+
status: 'completed',
|
|
356
|
+
executed_at: '2024-03-14 10:00:00',
|
|
357
|
+
bindings: [id],
|
|
358
|
+
model: 'Example',
|
|
359
|
+
method: 'findById',
|
|
360
|
+
file: 'app/Models/Example.php',
|
|
361
|
+
line: 42,
|
|
362
|
+
memory_usage: 1.5,
|
|
363
|
+
rows_affected: 1,
|
|
364
|
+
tags: ['example'],
|
|
365
|
+
affected_tables: ['example_table'],
|
|
366
|
+
indexes_used: ['example_table.primary'],
|
|
367
|
+
missing_indexes: [],
|
|
368
|
+
explain_plan: 'EXPLAIN: id-1, select_type-SIMPLE, table-example_table, partitions-NULL, type-const, possible_keys-PRIMARY, key-PRIMARY, key_len-4, ref-const, rows-1, filtered-100.00',
|
|
369
|
+
})
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
isLoading.set(false)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
onMount(async () => {
|
|
376
|
+
const id = route.params.id as string
|
|
377
|
+
await fetchQuery(id)
|
|
378
|
+
})
|
|
379
|
+
</script>
|
|
380
|
+
|
|
381
|
+
@endsection
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
@extends('layouts/default')
|
|
2
|
+
|
|
3
|
+
@section('content')
|
|
4
|
+
@title('Queries - History')
|
|
5
|
+
<div class="min-h-screen">
|
|
6
|
+
<header class="flex sticky top-0 z-10 flex-wrap gap-6 items-center justify-between px-4 py-4 sm:px-6 lg:px-8 bg-white dark:bg-neutral-900 shadow-sm">
|
|
7
|
+
<div>
|
|
8
|
+
<h1 class="font-semibold leading-7 text-gray-900 text-lg dark:text-white">Query History</h1>
|
|
9
|
+
<p class="mt-1 leading-6 text-gray-500 text-sm dark:text-neutral-400">View and analyze past database queries</p>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="flex flex-wrap gap-3 items-center">
|
|
12
|
+
<div class="inline-flex rounded-md shadow-sm">
|
|
13
|
+
<button type="button" @click="setTimeRange('day')" class="inline-flex relative items-center px-3 py-2 font-medium text-sm rounded-l-md ring-1 ring-gray-300 ring-inset dark:ring-neutral-700 {{ timeRange() === 'day' ? 'bg-blue-600 text-white' : 'bg-white text-gray-700 dark:bg-neutral-800 dark:text-neutral-200 hover:bg-gray-50 dark:hover:bg-neutral-700' }}">Day</button>
|
|
14
|
+
<button type="button" @click="setTimeRange('week')" class="inline-flex relative items-center -ml-px px-3 py-2 font-medium text-sm ring-1 ring-gray-300 ring-inset dark:ring-neutral-700 {{ timeRange() === 'week' ? 'bg-blue-600 text-white' : 'bg-white text-gray-700 dark:bg-neutral-800 dark:text-neutral-200 hover:bg-gray-50 dark:hover:bg-neutral-700' }}">Week</button>
|
|
15
|
+
<button type="button" @click="setTimeRange('month')" class="inline-flex relative items-center -ml-px px-3 py-2 font-medium text-sm rounded-r-md ring-1 ring-gray-300 ring-inset dark:ring-neutral-700 {{ timeRange() === 'month' ? 'bg-blue-600 text-white' : 'bg-white text-gray-700 dark:bg-neutral-800 dark:text-neutral-200 hover:bg-gray-50 dark:hover:bg-neutral-700' }}">Month</button>
|
|
16
|
+
</div>
|
|
17
|
+
<div>
|
|
18
|
+
<select id="connection" name="connection" x-model="selectedConnection" class="block pl-3 pr-10 py-2 w-full text-sm dark:text-neutral-200 bg-white dark:bg-neutral-800 border-gray-300 rounded-md dark:border-neutral-700 focus:border-blue-600 focus:outline-none focus:ring-blue-600">
|
|
19
|
+
<option value="all">All connections</option>
|
|
20
|
+
<option value="mysql">MySQL</option>
|
|
21
|
+
<option value="postgres">PostgreSQL</option>
|
|
22
|
+
<option value="sqlite">SQLite</option>
|
|
23
|
+
<option value="mongodb">MongoDB</option>
|
|
24
|
+
</select>
|
|
25
|
+
</div>
|
|
26
|
+
<div>
|
|
27
|
+
<select id="status" name="status" x-model="selectedStatus" class="block pl-3 pr-10 py-2 w-full text-sm dark:text-neutral-200 bg-white dark:bg-neutral-800 border-gray-300 rounded-md dark:border-neutral-700 focus:border-blue-600 focus:outline-none focus:ring-blue-600">
|
|
28
|
+
<option value="all">All statuses</option>
|
|
29
|
+
<option value="completed">Completed</option>
|
|
30
|
+
<option value="slow">Slow</option>
|
|
31
|
+
<option value="failed">Failed</option>
|
|
32
|
+
</select>
|
|
33
|
+
</div>
|
|
34
|
+
<div>
|
|
35
|
+
<select id="type" name="type" x-model="selectedType" class="block pl-3 pr-10 py-2 w-full text-sm dark:text-neutral-200 bg-white dark:bg-neutral-800 border-gray-300 rounded-md dark:border-neutral-700 focus:border-blue-600 focus:outline-none focus:ring-blue-600">
|
|
36
|
+
<option value="all">All types</option>
|
|
37
|
+
<option value="SELECT">SELECT</option>
|
|
38
|
+
<option value="INSERT">INSERT</option>
|
|
39
|
+
<option value="UPDATE">UPDATE</option>
|
|
40
|
+
<option value="DELETE">DELETE</option>
|
|
41
|
+
<option value="OTHER">OTHER</option>
|
|
42
|
+
</select>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="relative w-full sm:w-auto">
|
|
45
|
+
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
|
46
|
+
<div class="h-5 w-5 text-gray-400 i-hugeicons-search-01"></div>
|
|
47
|
+
</div>
|
|
48
|
+
<input type="text" x-model="searchQuery" placeholder="Search queries..." class="block pl-10 py-2 w-full text-sm dark:text-neutral-200 bg-white dark:bg-neutral-800 border-gray-300 rounded-md dark:border-neutral-700 focus:border-blue-600 focus:outline-none focus:ring-blue-600" />
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</header>
|
|
52
|
+
|
|
53
|
+
<main class="px-4 py-8 sm:px-6 lg:px-8">
|
|
54
|
+
<div class="mt-4">
|
|
55
|
+
<div class="relative">
|
|
56
|
+
<div :show="isLoading" class="flex absolute inset-0 z-10 items-center justify-center bg-white/50 dark:bg-neutral-900/50">
|
|
57
|
+
<div class="h-8 w-8 text-blue-600 dark:text-blue-400 animate-spin i-hugeicons-loading-01"></div>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="overflow-hidden ring-1 ring-black ring-opacity-5 sm:rounded-lg shadow">
|
|
60
|
+
<table class="min-w-full divide-gray-300 divide-y dark:divide-neutral-700">
|
|
61
|
+
<thead class="bg-gray-50 dark:bg-neutral-800">
|
|
62
|
+
<tr>
|
|
63
|
+
<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">Query</th>
|
|
64
|
+
<th scope="col" class="hidden lg:table-cell px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-white">Connection</th>
|
|
65
|
+
<th scope="col" class="hidden sm:table-cell px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-white">Duration</th>
|
|
66
|
+
<th scope="col" class="hidden lg:table-cell px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-white">Model / Method</th>
|
|
67
|
+
<th scope="col" class="px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-white">Status</th>
|
|
68
|
+
<th scope="col" class="hidden sm:table-cell px-3 py-3.5 font-semibold text-gray-900 text-left text-sm dark:text-white">Executed At</th>
|
|
69
|
+
<th scope="col" class="relative pl-3 pr-4 py-3.5 sm:pr-6"><span class="sr-only">View</span></th>
|
|
70
|
+
</tr>
|
|
71
|
+
</thead>
|
|
72
|
+
<tbody class="bg-white dark:bg-neutral-900 divide-gray-200 divide-y dark:divide-neutral-700">
|
|
73
|
+
<template :for="query in filteredQueries">
|
|
74
|
+
<tr>
|
|
75
|
+
<td class="pl-4 pr-3 py-4 sm:pl-6 text-sm">
|
|
76
|
+
<div class="flex gap-2 items-center mb-1">
|
|
77
|
+
<span class="{{ queryTypeBadgeClass(query.query) }} inline-flex items-center px-2 py-1 font-medium text-xs ring-1 ring-inset rounded-md">{{ getQueryType(query.query) }}</span>
|
|
78
|
+
<div class="overflow-hidden font-medium font-mono text-ellipsis text-gray-900 text-xs dark:text-white" title="{{ query.query }}">{{ formatQuery(query.query) }}</div>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="hidden xl:block mt-1 text-gray-500 dark:text-neutral-400">
|
|
81
|
+
<template :for="tag in query.tags">
|
|
82
|
+
<span class="inline-flex items-center mr-1 px-2 py-1 font-medium text-xs rounded-md">{{ tag }}</span>
|
|
83
|
+
</template>
|
|
84
|
+
</div>
|
|
85
|
+
</td>
|
|
86
|
+
<td class="hidden lg:table-cell px-3 py-4 text-gray-500 text-sm dark:text-neutral-400">{{ query.connection }}</td>
|
|
87
|
+
<td class="hidden sm:table-cell px-3 py-4 text-gray-500 text-sm dark:text-neutral-400 {{ query.duration > 10 ? 'text-yellow-600 dark:text-yellow-400' : '' }}">{{ formatDuration(query.duration) }}</td>
|
|
88
|
+
<td class="hidden lg:table-cell px-3 py-4 text-gray-500 text-sm dark:text-neutral-400">
|
|
89
|
+
<div>{{ query.model || '-' }}</div>
|
|
90
|
+
<div class="text-gray-400 text-xs dark:text-neutral-500">{{ query.method || '-' }}</div>
|
|
91
|
+
</td>
|
|
92
|
+
<td class="px-3 py-4 text-sm">
|
|
93
|
+
<span class="{{ getQueryStatusColor(query.status) }} inline-flex items-center px-2 py-1 font-medium text-xs ring-1 ring-inset rounded-md">{{ query.status }}</span>
|
|
94
|
+
</td>
|
|
95
|
+
<td class="hidden sm:table-cell px-3 py-4 text-gray-500 text-sm dark:text-neutral-400">{{ query.executed_at }}</td>
|
|
96
|
+
<td class="relative pl-3 pr-4 py-4 sm:pr-6 font-medium text-right text-sm">
|
|
97
|
+
<StxLink to="/queries/{{ query.id }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300">View<span class="sr-only">, {{ query.id }}</span></StxLink>
|
|
98
|
+
</td>
|
|
99
|
+
</tr>
|
|
100
|
+
</template>
|
|
101
|
+
<tr :show="hasNoQueries">
|
|
102
|
+
<td colspan="7" class="py-8 text-center text-gray-500 text-sm dark:text-neutral-400">No queries found matching your criteria.</td>
|
|
103
|
+
</tr>
|
|
104
|
+
</tbody>
|
|
105
|
+
</table>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</main>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<script client>
|
|
113
|
+
interface QueryRecord {
|
|
114
|
+
id: string
|
|
115
|
+
query: string
|
|
116
|
+
duration: number
|
|
117
|
+
normalized_query: string
|
|
118
|
+
connection: string
|
|
119
|
+
status: 'completed' | 'failed' | 'slow'
|
|
120
|
+
error?: string
|
|
121
|
+
executed_at: string
|
|
122
|
+
bindings?: unknown[]
|
|
123
|
+
model?: string
|
|
124
|
+
method?: string
|
|
125
|
+
file?: string
|
|
126
|
+
line?: number
|
|
127
|
+
memory_usage?: number
|
|
128
|
+
rows_affected?: number
|
|
129
|
+
tags?: string[]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const queryHistory = state<QueryRecord[]>([
|
|
133
|
+
{ id: '1', query: 'SELECT * FROM users WHERE email = ? LIMIT 1', normalized_query: 'SELECT * FROM users WHERE email = ? LIMIT ?', duration: 3.2, connection: 'mysql', status: 'completed', executed_at: '2024-03-14 10:16:32', bindings: ['user@example.com', 1], model: 'User', method: 'findByEmail', file: 'app/Models/User.php', line: 45, memory_usage: 2.4, rows_affected: 1, tags: ['auth'] },
|
|
134
|
+
{ id: '2', query: 'SELECT p.*, c.name as category_name FROM posts p JOIN categories c ON p.category_id = c.id WHERE p.published_at IS NOT NULL ORDER BY p.published_at DESC LIMIT 10', normalized_query: 'SELECT p.*, c.name as category_name FROM posts p JOIN categories c ON p.category_id = c.id WHERE p.published_at IS NOT NULL ORDER BY p.published_at DESC LIMIT ?', duration: 45.7, connection: 'mysql', status: 'slow', executed_at: '2024-03-14 10:15:30', bindings: [10], model: 'Post', method: 'getRecentPublishedWithCategory', file: 'app/Models/Post.php', line: 78, memory_usage: 5.8, rows_affected: 10, tags: ['content', 'frontend'] },
|
|
135
|
+
{ id: '3', query: 'INSERT INTO user_logs (user_id, action, ip_address, created_at) VALUES (?, ?, ?, ?)', normalized_query: 'INSERT INTO user_logs (user_id, action, ip_address, created_at) VALUES (?, ?, ?, ?)', duration: 2.1, connection: 'mysql', status: 'completed', executed_at: '2024-03-14 10:14:55', bindings: [42, 'login', '192.168.1.1', '2024-03-14 10:14:55'], model: 'UserLog', method: 'logAction', file: 'app/Services/LogService.php', line: 28, memory_usage: 1.8, rows_affected: 1, tags: ['logging'] },
|
|
136
|
+
{ id: '4', query: 'UPDATE products SET stock = stock - 1 WHERE id = ? AND stock > 0', normalized_query: 'UPDATE products SET stock = stock - ? WHERE id = ? AND stock > ?', duration: 4.5, connection: 'mysql', status: 'completed', executed_at: '2024-03-14 10:12:18', bindings: [1, 156, 0], model: 'Product', method: 'decrementStock', file: 'app/Models/Product.php', line: 112, memory_usage: 2.1, rows_affected: 1, tags: ['inventory', 'order'] },
|
|
137
|
+
{ id: '5', query: 'DELETE FROM carts WHERE user_id = ? AND updated_at < ?', normalized_query: 'DELETE FROM carts WHERE user_id = ? AND updated_at < ?', duration: 2.8, connection: 'mysql', status: 'completed', executed_at: '2024-03-14 10:10:05', bindings: [78, '2024-03-13 10:10:05'], model: 'Cart', method: 'clearOldCarts', file: 'app/Jobs/ClearOldCarts.php', line: 35, memory_usage: 1.9, rows_affected: 3, tags: ['cleanup', 'scheduled'] },
|
|
138
|
+
{ id: '6', query: 'SELECT COUNT(*) FROM orders WHERE status = ?', normalized_query: 'SELECT COUNT(*) FROM orders WHERE status = ?', duration: 78.4, connection: 'mysql', status: 'slow', executed_at: '2024-03-14 10:08:22', bindings: ['pending'], model: 'Order', method: 'countPendingOrders', file: 'app/Models/Order.php', line: 67, memory_usage: 3.1, rows_affected: 1, tags: ['reporting'] },
|
|
139
|
+
{ id: '7', query: 'SELECT AVG(total) FROM orders WHERE created_at BETWEEN ? AND ?', normalized_query: 'SELECT AVG(total) FROM orders WHERE created_at BETWEEN ? AND ?', duration: 65.2, connection: 'mysql', status: 'slow', executed_at: '2024-03-14 10:05:11', bindings: ['2023-03-01 00:00:00', '2023-03-31 23:59:59'], model: 'Order', method: 'getAverageOrderTotal', file: 'app/Reports/MonthlyReport.php', line: 134, memory_usage: 4.2, rows_affected: 1, tags: ['reporting', 'monthly'] },
|
|
140
|
+
{ id: '8', query: 'UPDATE users SET last_login_at = ? WHERE id = ?', normalized_query: 'UPDATE users SET last_login_at = ? WHERE id = ?', duration: 3.5, connection: 'mysql', status: 'completed', executed_at: '2024-03-14 10:02:47', bindings: ['2024-03-14 10:02:47', 42], model: 'User', method: 'updateLastLogin', file: 'app/Http/Controllers/Auth/LoginController.php', line: 89, memory_usage: 1.7, rows_affected: 1, tags: ['auth'] },
|
|
141
|
+
{ id: '9', query: 'SELECT * FROM failed_jobs', normalized_query: 'SELECT * FROM failed_jobs', duration: 12.8, connection: 'mysql', status: 'completed', executed_at: '2024-03-14 10:00:00', bindings: [], method: 'listFailedJobs', file: 'app/Console/Commands/ListFailedJobs.php', line: 42, memory_usage: 2.9, rows_affected: 5, tags: ['jobs', 'console'] },
|
|
142
|
+
{ id: '10', query: 'TRUNCATE TABLE cache', normalized_query: 'TRUNCATE TABLE cache', duration: 125.3, connection: 'mysql', status: 'slow', executed_at: '2024-03-14 09:55:00', bindings: [], method: 'flush', file: 'app/Console/Commands/CacheClear.php', line: 28, memory_usage: 1.5, rows_affected: 0, tags: ['cache', 'console'] },
|
|
143
|
+
{ id: '11', query: 'SELECT * FROM users WHERE role = ? AND last_login_at < ? LIMIT 100', normalized_query: 'SELECT * FROM users WHERE role = ? AND last_login_at < ? LIMIT ?', duration: 8.7, connection: 'mysql', status: 'completed', executed_at: '2024-03-14 09:50:12', bindings: ['customer', '2023-03-14 09:50:12', 100], model: 'User', method: 'getInactiveCustomers', file: 'app/Jobs/SendInactiveCustomerEmail.php', line: 37, memory_usage: 6.4, rows_affected: 23, tags: ['email', 'scheduled'] },
|
|
144
|
+
{ id: '12', query: 'SELECT * FROM products WHERE stock < ? LIMIT 50', normalized_query: 'SELECT * FROM products WHERE stock < ? LIMIT ?', duration: 7.2, connection: 'mysql', status: 'completed', executed_at: '2024-03-14 09:45:00', bindings: [10, 50], model: 'Product', method: 'getLowStockProducts', file: 'app/Console/Commands/SendLowStockAlert.php', line: 45, memory_usage: 4.8, rows_affected: 12, tags: ['inventory', 'console'] },
|
|
145
|
+
])
|
|
146
|
+
|
|
147
|
+
const timeRange = state('day')
|
|
148
|
+
const selectedConnection = state('all')
|
|
149
|
+
const selectedStatus = state('all')
|
|
150
|
+
const selectedType = state('all')
|
|
151
|
+
const searchQuery = state('')
|
|
152
|
+
const isLoading = state(false)
|
|
153
|
+
|
|
154
|
+
const queryStatusColors: Record<string, string> = {
|
|
155
|
+
completed: 'text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/50 ring-green-600/20',
|
|
156
|
+
slow: 'text-yellow-700 dark:text-yellow-300 bg-yellow-50 dark:bg-yellow-900/50 ring-yellow-600/20',
|
|
157
|
+
failed: 'text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-900/50 ring-red-600/20',
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const queryTypeBadges: Record<string, string> = {
|
|
161
|
+
SELECT: 'bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300 ring-blue-700/10 dark:ring-blue-400/30',
|
|
162
|
+
INSERT: 'bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-300 ring-green-700/10 dark:ring-green-400/30',
|
|
163
|
+
UPDATE: 'bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 ring-amber-700/10 dark:ring-amber-400/30',
|
|
164
|
+
DELETE: 'bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 ring-red-700/10 dark:ring-red-400/30',
|
|
165
|
+
OTHER: 'bg-gray-50 dark:bg-neutral-900/20 text-gray-700 dark:text-neutral-300 ring-gray-700/10 dark:ring-neutral-400/30',
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function setTimeRange(range: string): void {
|
|
169
|
+
timeRange.set(range)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function getQueryType(query: string): string {
|
|
173
|
+
const upper = query.trim().toUpperCase()
|
|
174
|
+
if (upper.startsWith('SELECT'))
|
|
175
|
+
return 'SELECT'
|
|
176
|
+
if (upper.startsWith('INSERT'))
|
|
177
|
+
return 'INSERT'
|
|
178
|
+
if (upper.startsWith('UPDATE'))
|
|
179
|
+
return 'UPDATE'
|
|
180
|
+
if (upper.startsWith('DELETE'))
|
|
181
|
+
return 'DELETE'
|
|
182
|
+
return 'OTHER'
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function queryTypeBadgeClass(query: string): string {
|
|
186
|
+
return queryTypeBadges[getQueryType(query)] || queryTypeBadges.OTHER as string
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function getQueryStatusColor(status: string): string {
|
|
190
|
+
return queryStatusColors[status] || 'text-gray-700 dark:text-neutral-300 bg-gray-50 dark:bg-neutral-900/50 ring-gray-600/20'
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function formatQuery(q: string): string {
|
|
194
|
+
return q.length > 100 ? `${q.substring(0, 100)}...` : q
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function formatDuration(d: number): string {
|
|
198
|
+
return `${d.toFixed(2)} ms`
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const filteredQueries = derived(() => {
|
|
202
|
+
let list = [...queryHistory()]
|
|
203
|
+
const conn = selectedConnection()
|
|
204
|
+
const status = selectedStatus()
|
|
205
|
+
const type = selectedType()
|
|
206
|
+
const q = searchQuery().toLowerCase()
|
|
207
|
+
|
|
208
|
+
if (conn !== 'all')
|
|
209
|
+
list = list.filter(qy => qy.connection === conn)
|
|
210
|
+
if (status !== 'all')
|
|
211
|
+
list = list.filter(qy => qy.status === status)
|
|
212
|
+
if (type !== 'all')
|
|
213
|
+
list = list.filter(qy => getQueryType(qy.query) === type)
|
|
214
|
+
if (q) {
|
|
215
|
+
list = list.filter(qy =>
|
|
216
|
+
qy.query.toLowerCase().includes(q)
|
|
217
|
+
|| (qy.model?.toLowerCase().includes(q) ?? false)
|
|
218
|
+
|| (qy.method?.toLowerCase().includes(q) ?? false),
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
return list
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const hasNoQueries = derived(() => filteredQueries().length === 0)
|
|
225
|
+
</script>
|
|
226
|
+
|
|
227
|
+
@endsection
|