@askrjs/cli 0.0.2
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/LICENSE +201 -0
- package/README.md +140 -0
- package/dist/add-DqhqRzBQ.js +555 -0
- package/dist/add-DqhqRzBQ.js.map +1 -0
- package/dist/add.js +3 -0
- package/dist/cli.js +91 -0
- package/dist/cli.js.map +1 -0
- package/dist/create.js +923 -0
- package/dist/create.js.map +1 -0
- package/dist/is-direct-execution-Cdlr-ZUl.js +20 -0
- package/dist/is-direct-execution-Cdlr-ZUl.js.map +1 -0
- package/dist/openapi.js +127 -0
- package/dist/openapi.js.map +1 -0
- package/dist/skills/askr-accessibility/SKILL.md +68 -0
- package/dist/skills/askr-accessibility/agents/openai.yaml +4 -0
- package/dist/skills/askr-agent-execution/SKILL.md +85 -0
- package/dist/skills/askr-agent-execution/agents/openai.yaml +4 -0
- package/dist/skills/askr-agent-workflows/SKILL.md +57 -0
- package/dist/skills/askr-agent-workflows/agents/openai.yaml +4 -0
- package/dist/skills/askr-api-integration/SKILL.md +79 -0
- package/dist/skills/askr-api-integration/agents/openai.yaml +4 -0
- package/dist/skills/askr-app-builder/SKILL.md +99 -0
- package/dist/skills/askr-app-builder/agents/openai.yaml +4 -0
- package/dist/skills/askr-auth-access/SKILL.md +74 -0
- package/dist/skills/askr-auth-access/agents/openai.yaml +4 -0
- package/dist/skills/askr-cli-vite/SKILL.md +76 -0
- package/dist/skills/askr-cli-vite/agents/openai.yaml +4 -0
- package/dist/skills/askr-dashboard-charts/SKILL.md +82 -0
- package/dist/skills/askr-dashboard-charts/agents/openai.yaml +4 -0
- package/dist/skills/askr-design-system/SKILL.md +86 -0
- package/dist/skills/askr-design-system/agents/openai.yaml +4 -0
- package/dist/skills/askr-env-config/SKILL.md +74 -0
- package/dist/skills/askr-env-config/agents/openai.yaml +4 -0
- package/dist/skills/askr-error-loading-empty/SKILL.md +89 -0
- package/dist/skills/askr-error-loading-empty/agents/openai.yaml +4 -0
- package/dist/skills/askr-file-upload-artifacts/SKILL.md +77 -0
- package/dist/skills/askr-file-upload-artifacts/agents/openai.yaml +4 -0
- package/dist/skills/askr-forms-tables-crud/SKILL.md +98 -0
- package/dist/skills/askr-forms-tables-crud/agents/openai.yaml +4 -0
- package/dist/skills/askr-mental-model/SKILL.md +109 -0
- package/dist/skills/askr-mental-model/agents/openai.yaml +4 -0
- package/dist/skills/askr-migration-react/SKILL.md +81 -0
- package/dist/skills/askr-migration-react/agents/openai.yaml +4 -0
- package/dist/skills/askr-observability-debugging/SKILL.md +83 -0
- package/dist/skills/askr-observability-debugging/agents/openai.yaml +4 -0
- package/dist/skills/askr-project-structure/SKILL.md +79 -0
- package/dist/skills/askr-project-structure/agents/openai.yaml +4 -0
- package/dist/skills/askr-query-mutation/SKILL.md +115 -0
- package/dist/skills/askr-query-mutation/agents/openai.yaml +4 -0
- package/dist/skills/askr-realtime-streaming/SKILL.md +76 -0
- package/dist/skills/askr-realtime-streaming/agents/openai.yaml +4 -0
- package/dist/skills/askr-resources-data/SKILL.md +100 -0
- package/dist/skills/askr-resources-data/agents/openai.yaml +4 -0
- package/dist/skills/askr-routing-layouts/SKILL.md +107 -0
- package/dist/skills/askr-routing-layouts/agents/openai.yaml +4 -0
- package/dist/skills/askr-runtime-reactivity/SKILL.md +92 -0
- package/dist/skills/askr-runtime-reactivity/agents/openai.yaml +4 -0
- package/dist/skills/askr-ssr-ssg/SKILL.md +76 -0
- package/dist/skills/askr-ssr-ssg/agents/openai.yaml +4 -0
- package/dist/skills/askr-testing-determinism/SKILL.md +75 -0
- package/dist/skills/askr-testing-determinism/agents/openai.yaml +4 -0
- package/dist/skills/askr-theming/SKILL.md +86 -0
- package/dist/skills/askr-theming/agents/openai.yaml +4 -0
- package/dist/skills/askr-ui-composition/SKILL.md +92 -0
- package/dist/skills/askr-ui-composition/agents/openai.yaml +4 -0
- package/dist/skills-Bh0mEhIx.js +682 -0
- package/dist/skills-Bh0mEhIx.js.map +1 -0
- package/dist/skills.js +3 -0
- package/dist/ssg.js +188 -0
- package/dist/ssg.js.map +1 -0
- package/dist/templates/full-stack/AGENTS.md +12 -0
- package/dist/templates/full-stack/README.md +18 -0
- package/dist/templates/full-stack/gitignore.template +4 -0
- package/dist/templates/full-stack/index.html +14 -0
- package/dist/templates/full-stack/package.json +37 -0
- package/dist/templates/full-stack/server.ts +13 -0
- package/dist/templates/full-stack/src/action-authorizations.ts +10 -0
- package/dist/templates/full-stack/src/actions/create-message.ts +10 -0
- package/dist/templates/full-stack/src/i18n.ts +8 -0
- package/dist/templates/full-stack/src/main.tsx +12 -0
- package/dist/templates/full-stack/src/pages/home.tsx +25 -0
- package/dist/templates/full-stack/src/pages/layout.tsx +33 -0
- package/dist/templates/full-stack/src/pages/not-found.tsx +4 -0
- package/dist/templates/full-stack/src/routes.tsx +20 -0
- package/dist/templates/full-stack/src/schemas.ts +11 -0
- package/dist/templates/full-stack/src/server/action-registry.ts +12 -0
- package/dist/templates/full-stack/src/server/actions/create-message.ts +14 -0
- package/dist/templates/full-stack/src/server/app.ts +66 -0
- package/dist/templates/full-stack/src/server/dependencies.ts +55 -0
- package/dist/templates/full-stack/src/server/entry-server.ts +8 -0
- package/dist/templates/full-stack/src/telemetry.ts +4 -0
- package/dist/templates/full-stack/src/vite-server.d.ts +6 -0
- package/dist/templates/full-stack/tests/actions/create-message.test.ts +13 -0
- package/dist/templates/full-stack/tsconfig.json +16 -0
- package/dist/templates/full-stack/vite.config.ts +11 -0
- package/dist/templates/spa/AGENTS.md +60 -0
- package/dist/templates/spa/README.md +96 -0
- package/dist/templates/spa/gitignore.template +16 -0
- package/dist/templates/spa/index.html +13 -0
- package/dist/templates/spa/package.json +35 -0
- package/dist/templates/spa/src/adapters/operations-client.ts +90 -0
- package/dist/templates/spa/src/components/shared/metric-card.tsx +27 -0
- package/dist/templates/spa/src/components/shared/status-badge.tsx +24 -0
- package/dist/templates/spa/src/features/operations/operations.query.ts +9 -0
- package/dist/templates/spa/src/main.tsx +10 -0
- package/dist/templates/spa/src/pages/_layout.tsx +9 -0
- package/dist/templates/spa/src/pages/_routes.tsx +27 -0
- package/dist/templates/spa/src/pages/app/_layout.tsx +94 -0
- package/dist/templates/spa/src/pages/app/_routes.tsx +10 -0
- package/dist/templates/spa/src/pages/app/admin-home.tsx +152 -0
- package/dist/templates/spa/src/pages/app/agent-runs.tsx +93 -0
- package/dist/templates/spa/src/pages/app/settings.tsx +91 -0
- package/dist/templates/spa/src/pages/auth/_layout.tsx +38 -0
- package/dist/templates/spa/src/pages/auth/_routes.tsx +6 -0
- package/dist/templates/spa/src/pages/auth/login.tsx +63 -0
- package/dist/templates/spa/src/pages/not-found.tsx +22 -0
- package/dist/templates/spa/src/pages/public/_layout.tsx +47 -0
- package/dist/templates/spa/src/pages/public/_routes.tsx +6 -0
- package/dist/templates/spa/src/pages/public/home.tsx +143 -0
- package/dist/templates/spa/src/shared/format.ts +12 -0
- package/dist/templates/spa/src/shared/navigation.ts +15 -0
- package/dist/templates/spa/src/styles/components.css +49 -0
- package/dist/templates/spa/src/styles/layout.css +192 -0
- package/dist/templates/spa/src/styles/reset.css +44 -0
- package/dist/templates/spa/src/styles/theme.css +57 -0
- package/dist/templates/spa/src/styles/tokens.css +20 -0
- package/dist/templates/spa/src/styles.css +7 -0
- package/dist/templates/spa/src/styles.d.ts +1 -0
- package/dist/templates/spa/tests/app.test.tsx +37 -0
- package/dist/templates/spa/tests/components/shared.test.tsx +12 -0
- package/dist/templates/spa/tests/resources.test.ts +22 -0
- package/dist/templates/spa/tsconfig.json +20 -0
- package/dist/templates/spa/tsconfig.node.json +10 -0
- package/dist/templates/spa/vite.config.ts +24 -0
- package/dist/templates/spa/vitest.config.ts +17 -0
- package/dist/templates/ssg/AGENTS.md +55 -0
- package/dist/templates/ssg/README.md +39 -0
- package/dist/templates/ssg/gitignore.template +17 -0
- package/dist/templates/ssg/index.html +13 -0
- package/dist/templates/ssg/package.json +35 -0
- package/dist/templates/ssg/src/app.tsx +14 -0
- package/dist/templates/ssg/src/components/badge.tsx +3 -0
- package/dist/templates/ssg/src/components/counter.tsx +30 -0
- package/dist/templates/ssg/src/components/site-shell.tsx +111 -0
- package/dist/templates/ssg/src/jsx.d.ts +23 -0
- package/dist/templates/ssg/src/main.tsx +8 -0
- package/dist/templates/ssg/src/pages/about.tsx +82 -0
- package/dist/templates/ssg/src/pages/content.tsx +63 -0
- package/dist/templates/ssg/src/pages/example.tsx +97 -0
- package/dist/templates/ssg/src/pages/home.tsx +51 -0
- package/dist/templates/ssg/src/resources/user.ts +15 -0
- package/dist/templates/ssg/src/routes.tsx +15 -0
- package/dist/templates/ssg/src/styles.css +89 -0
- package/dist/templates/ssg/src/vite-env.d.ts +5 -0
- package/dist/templates/ssg/ssg.config.ts +34 -0
- package/dist/templates/ssg/tests/app.test.tsx +19 -0
- package/dist/templates/ssg/tests/components/counter.test.tsx +18 -0
- package/dist/templates/ssg/tests/resources.test.ts +11 -0
- package/dist/templates/ssg/tests/ssg-config.test.ts +11 -0
- package/dist/templates/ssg/tsconfig.json +20 -0
- package/dist/templates/ssg/tsconfig.node.json +10 -0
- package/dist/templates/ssg/vite.config.ts +24 -0
- package/dist/templates/ssg/vitest.config.ts +17 -0
- package/dist/templates/ssr/AGENTS.md +28 -0
- package/dist/templates/ssr/README.md +38 -0
- package/dist/templates/ssr/gitignore.template +16 -0
- package/dist/templates/ssr/index.html +13 -0
- package/dist/templates/ssr/package.json +34 -0
- package/dist/templates/ssr/server.ts +16 -0
- package/dist/templates/ssr/src/app.tsx +21 -0
- package/dist/templates/ssr/src/components/counter.tsx +26 -0
- package/dist/templates/ssr/src/entry-server.tsx +10 -0
- package/dist/templates/ssr/src/main.tsx +11 -0
- package/dist/templates/ssr/src/pages/about.tsx +75 -0
- package/dist/templates/ssr/src/pages/example.tsx +113 -0
- package/dist/templates/ssr/src/pages/home.tsx +56 -0
- package/dist/templates/ssr/src/resources/user.ts +15 -0
- package/dist/templates/ssr/src/routes.tsx +13 -0
- package/dist/templates/ssr/src/styles.css +148 -0
- package/dist/templates/ssr/src/vite-server.d.ts +7 -0
- package/dist/templates/ssr/tests/app.test.tsx +24 -0
- package/dist/templates/ssr/tests/components/counter.test.tsx +13 -0
- package/dist/templates/ssr/tests/resources.test.ts +11 -0
- package/dist/templates/ssr/tsconfig.json +22 -0
- package/dist/templates/ssr/tsconfig.node.json +10 -0
- package/dist/templates/ssr/tsconfig.server.json +15 -0
- package/dist/templates/ssr/vite.config.ts +25 -0
- package/dist/templates/ssr/vitest.config.ts +13 -0
- package/dist/templates/startkit/AGENTS.md +77 -0
- package/dist/templates/startkit/README.md +192 -0
- package/dist/templates/startkit/gitignore.template +16 -0
- package/dist/templates/startkit/index.html +13 -0
- package/dist/templates/startkit/package.json +35 -0
- package/dist/templates/startkit/src/components/app-header.tsx +93 -0
- package/dist/templates/startkit/src/components/app-sidebar.tsx +95 -0
- package/dist/templates/startkit/src/components/data-table.tsx +80 -0
- package/dist/templates/startkit/src/components/empty-state.tsx +13 -0
- package/dist/templates/startkit/src/components/page-header.tsx +23 -0
- package/dist/templates/startkit/src/components/stat-card.tsx +24 -0
- package/dist/templates/startkit/src/features/accounts/account-filters.tsx +61 -0
- package/dist/templates/startkit/src/features/accounts/account-table.tsx +79 -0
- package/dist/templates/startkit/src/lib/format.ts +31 -0
- package/dist/templates/startkit/src/lib/mock-data.ts +441 -0
- package/dist/templates/startkit/src/lib/routes.ts +116 -0
- package/dist/templates/startkit/src/main.tsx +12 -0
- package/dist/templates/startkit/src/pages/_layout.tsx +56 -0
- package/dist/templates/startkit/src/pages/auth/_layout.tsx +7 -0
- package/dist/templates/startkit/src/pages/auth/login.tsx +129 -0
- package/dist/templates/startkit/src/pages/home.tsx +83 -0
- package/dist/templates/startkit/src/pages/not-found.tsx +24 -0
- package/dist/templates/startkit/src/pages/workspace/_layout.tsx +17 -0
- package/dist/templates/startkit/src/pages/workspace/accounts/index.tsx +217 -0
- package/dist/templates/startkit/src/pages/workspace/dashboard.tsx +118 -0
- package/dist/templates/startkit/src/pages/workspace/settings.tsx +218 -0
- package/dist/templates/startkit/src/router.tsx +7 -0
- package/dist/templates/startkit/src/routes/auth-config.ts +38 -0
- package/dist/templates/startkit/src/routes/auth.ts +8 -0
- package/dist/templates/startkit/src/routes/index.ts +28 -0
- package/dist/templates/startkit/src/routes/public.ts +8 -0
- package/dist/templates/startkit/src/routes/workspace/accounts.ts +8 -0
- package/dist/templates/startkit/src/routes/workspace/index.ts +14 -0
- package/dist/templates/startkit/src/styles/components.css +396 -0
- package/dist/templates/startkit/src/styles/layout.css +192 -0
- package/dist/templates/startkit/src/styles/reset.css +38 -0
- package/dist/templates/startkit/src/styles/theme.css +76 -0
- package/dist/templates/startkit/src/styles/tokens.css +67 -0
- package/dist/templates/startkit/src/styles.css +7 -0
- package/dist/templates/startkit/src/toast.ts +55 -0
- package/dist/templates/startkit/src/utils/join-classes.ts +4 -0
- package/dist/templates/startkit/src/vite-env.d.ts +5 -0
- package/dist/templates/startkit/tests/app.test.tsx +32 -0
- package/dist/templates/startkit/tests/preferences.test.ts +43 -0
- package/dist/templates/startkit/tests/resources.test.ts +82 -0
- package/dist/templates/startkit/tsconfig.json +20 -0
- package/dist/templates/startkit/tsconfig.node.json +10 -0
- package/dist/templates/startkit/vite.config.ts +32 -0
- package/dist/templates/startkit/vitest.config.ts +13 -0
- package/dist/update-DyW1na5V.js +1140 -0
- package/dist/update-DyW1na5V.js.map +1 -0
- package/dist/update.js +3 -0
- package/package.json +84 -0
- package/skills/askr-accessibility/SKILL.md +68 -0
- package/skills/askr-accessibility/agents/openai.yaml +4 -0
- package/skills/askr-agent-execution/SKILL.md +85 -0
- package/skills/askr-agent-execution/agents/openai.yaml +4 -0
- package/skills/askr-agent-workflows/SKILL.md +57 -0
- package/skills/askr-agent-workflows/agents/openai.yaml +4 -0
- package/skills/askr-api-integration/SKILL.md +79 -0
- package/skills/askr-api-integration/agents/openai.yaml +4 -0
- package/skills/askr-app-builder/SKILL.md +99 -0
- package/skills/askr-app-builder/agents/openai.yaml +4 -0
- package/skills/askr-auth-access/SKILL.md +74 -0
- package/skills/askr-auth-access/agents/openai.yaml +4 -0
- package/skills/askr-cli-vite/SKILL.md +76 -0
- package/skills/askr-cli-vite/agents/openai.yaml +4 -0
- package/skills/askr-dashboard-charts/SKILL.md +82 -0
- package/skills/askr-dashboard-charts/agents/openai.yaml +4 -0
- package/skills/askr-design-system/SKILL.md +86 -0
- package/skills/askr-design-system/agents/openai.yaml +4 -0
- package/skills/askr-env-config/SKILL.md +74 -0
- package/skills/askr-env-config/agents/openai.yaml +4 -0
- package/skills/askr-error-loading-empty/SKILL.md +89 -0
- package/skills/askr-error-loading-empty/agents/openai.yaml +4 -0
- package/skills/askr-file-upload-artifacts/SKILL.md +77 -0
- package/skills/askr-file-upload-artifacts/agents/openai.yaml +4 -0
- package/skills/askr-forms-tables-crud/SKILL.md +98 -0
- package/skills/askr-forms-tables-crud/agents/openai.yaml +4 -0
- package/skills/askr-mental-model/SKILL.md +109 -0
- package/skills/askr-mental-model/agents/openai.yaml +4 -0
- package/skills/askr-migration-react/SKILL.md +81 -0
- package/skills/askr-migration-react/agents/openai.yaml +4 -0
- package/skills/askr-observability-debugging/SKILL.md +83 -0
- package/skills/askr-observability-debugging/agents/openai.yaml +4 -0
- package/skills/askr-project-structure/SKILL.md +79 -0
- package/skills/askr-project-structure/agents/openai.yaml +4 -0
- package/skills/askr-query-mutation/SKILL.md +115 -0
- package/skills/askr-query-mutation/agents/openai.yaml +4 -0
- package/skills/askr-realtime-streaming/SKILL.md +76 -0
- package/skills/askr-realtime-streaming/agents/openai.yaml +4 -0
- package/skills/askr-resources-data/SKILL.md +100 -0
- package/skills/askr-resources-data/agents/openai.yaml +4 -0
- package/skills/askr-routing-layouts/SKILL.md +107 -0
- package/skills/askr-routing-layouts/agents/openai.yaml +4 -0
- package/skills/askr-runtime-reactivity/SKILL.md +92 -0
- package/skills/askr-runtime-reactivity/agents/openai.yaml +4 -0
- package/skills/askr-ssr-ssg/SKILL.md +76 -0
- package/skills/askr-ssr-ssg/agents/openai.yaml +4 -0
- package/skills/askr-testing-determinism/SKILL.md +75 -0
- package/skills/askr-testing-determinism/agents/openai.yaml +4 -0
- package/skills/askr-theming/SKILL.md +86 -0
- package/skills/askr-theming/agents/openai.yaml +4 -0
- package/skills/askr-ui-composition/SKILL.md +92 -0
- package/skills/askr-ui-composition/agents/openai.yaml +4 -0
- package/templates/full-stack/AGENTS.md +12 -0
- package/templates/full-stack/README.md +18 -0
- package/templates/full-stack/gitignore.template +4 -0
- package/templates/full-stack/index.html +14 -0
- package/templates/full-stack/package.json +37 -0
- package/templates/full-stack/server.ts +13 -0
- package/templates/full-stack/src/action-authorizations.ts +10 -0
- package/templates/full-stack/src/actions/create-message.ts +10 -0
- package/templates/full-stack/src/i18n.ts +8 -0
- package/templates/full-stack/src/main.tsx +12 -0
- package/templates/full-stack/src/pages/home.tsx +25 -0
- package/templates/full-stack/src/pages/layout.tsx +33 -0
- package/templates/full-stack/src/pages/not-found.tsx +4 -0
- package/templates/full-stack/src/routes.tsx +20 -0
- package/templates/full-stack/src/schemas.ts +11 -0
- package/templates/full-stack/src/server/action-registry.ts +12 -0
- package/templates/full-stack/src/server/actions/create-message.ts +14 -0
- package/templates/full-stack/src/server/app.ts +66 -0
- package/templates/full-stack/src/server/dependencies.ts +55 -0
- package/templates/full-stack/src/server/entry-server.ts +8 -0
- package/templates/full-stack/src/telemetry.ts +4 -0
- package/templates/full-stack/src/vite-server.d.ts +6 -0
- package/templates/full-stack/tests/actions/create-message.test.ts +13 -0
- package/templates/full-stack/tsconfig.json +16 -0
- package/templates/full-stack/vite.config.ts +11 -0
- package/templates/spa/AGENTS.md +60 -0
- package/templates/spa/README.md +96 -0
- package/templates/spa/gitignore.template +16 -0
- package/templates/spa/index.html +13 -0
- package/templates/spa/package.json +35 -0
- package/templates/spa/src/adapters/operations-client.ts +90 -0
- package/templates/spa/src/components/shared/metric-card.tsx +27 -0
- package/templates/spa/src/components/shared/status-badge.tsx +24 -0
- package/templates/spa/src/features/operations/operations.query.ts +9 -0
- package/templates/spa/src/main.tsx +10 -0
- package/templates/spa/src/pages/_layout.tsx +9 -0
- package/templates/spa/src/pages/_routes.tsx +27 -0
- package/templates/spa/src/pages/app/_layout.tsx +94 -0
- package/templates/spa/src/pages/app/_routes.tsx +10 -0
- package/templates/spa/src/pages/app/admin-home.tsx +152 -0
- package/templates/spa/src/pages/app/agent-runs.tsx +93 -0
- package/templates/spa/src/pages/app/settings.tsx +91 -0
- package/templates/spa/src/pages/auth/_layout.tsx +38 -0
- package/templates/spa/src/pages/auth/_routes.tsx +6 -0
- package/templates/spa/src/pages/auth/login.tsx +63 -0
- package/templates/spa/src/pages/not-found.tsx +22 -0
- package/templates/spa/src/pages/public/_layout.tsx +47 -0
- package/templates/spa/src/pages/public/_routes.tsx +6 -0
- package/templates/spa/src/pages/public/home.tsx +143 -0
- package/templates/spa/src/shared/format.ts +12 -0
- package/templates/spa/src/shared/navigation.ts +15 -0
- package/templates/spa/src/styles/components.css +49 -0
- package/templates/spa/src/styles/layout.css +192 -0
- package/templates/spa/src/styles/reset.css +44 -0
- package/templates/spa/src/styles/theme.css +57 -0
- package/templates/spa/src/styles/tokens.css +20 -0
- package/templates/spa/src/styles.css +7 -0
- package/templates/spa/src/styles.d.ts +1 -0
- package/templates/spa/tests/app.test.tsx +37 -0
- package/templates/spa/tests/components/shared.test.tsx +12 -0
- package/templates/spa/tests/resources.test.ts +22 -0
- package/templates/spa/tsconfig.json +20 -0
- package/templates/spa/tsconfig.node.json +10 -0
- package/templates/spa/vite.config.ts +24 -0
- package/templates/spa/vitest.config.ts +17 -0
- package/templates/ssg/AGENTS.md +55 -0
- package/templates/ssg/README.md +39 -0
- package/templates/ssg/gitignore.template +17 -0
- package/templates/ssg/index.html +13 -0
- package/templates/ssg/package.json +35 -0
- package/templates/ssg/src/app.tsx +14 -0
- package/templates/ssg/src/components/badge.tsx +3 -0
- package/templates/ssg/src/components/counter.tsx +30 -0
- package/templates/ssg/src/components/site-shell.tsx +111 -0
- package/templates/ssg/src/jsx.d.ts +23 -0
- package/templates/ssg/src/main.tsx +8 -0
- package/templates/ssg/src/pages/about.tsx +82 -0
- package/templates/ssg/src/pages/content.tsx +63 -0
- package/templates/ssg/src/pages/example.tsx +97 -0
- package/templates/ssg/src/pages/home.tsx +51 -0
- package/templates/ssg/src/resources/user.ts +15 -0
- package/templates/ssg/src/routes.tsx +15 -0
- package/templates/ssg/src/styles.css +89 -0
- package/templates/ssg/src/vite-env.d.ts +5 -0
- package/templates/ssg/ssg.config.ts +34 -0
- package/templates/ssg/tests/app.test.tsx +19 -0
- package/templates/ssg/tests/components/counter.test.tsx +18 -0
- package/templates/ssg/tests/resources.test.ts +11 -0
- package/templates/ssg/tests/ssg-config.test.ts +11 -0
- package/templates/ssg/tsconfig.json +20 -0
- package/templates/ssg/tsconfig.node.json +10 -0
- package/templates/ssg/vite.config.ts +24 -0
- package/templates/ssg/vitest.config.ts +17 -0
- package/templates/ssr/AGENTS.md +28 -0
- package/templates/ssr/README.md +38 -0
- package/templates/ssr/gitignore.template +16 -0
- package/templates/ssr/index.html +13 -0
- package/templates/ssr/package.json +34 -0
- package/templates/ssr/server.ts +16 -0
- package/templates/ssr/src/app.tsx +21 -0
- package/templates/ssr/src/components/counter.tsx +26 -0
- package/templates/ssr/src/entry-server.tsx +10 -0
- package/templates/ssr/src/main.tsx +11 -0
- package/templates/ssr/src/pages/about.tsx +75 -0
- package/templates/ssr/src/pages/example.tsx +113 -0
- package/templates/ssr/src/pages/home.tsx +56 -0
- package/templates/ssr/src/resources/user.ts +15 -0
- package/templates/ssr/src/routes.tsx +13 -0
- package/templates/ssr/src/styles.css +148 -0
- package/templates/ssr/src/vite-server.d.ts +7 -0
- package/templates/ssr/tests/app.test.tsx +24 -0
- package/templates/ssr/tests/components/counter.test.tsx +13 -0
- package/templates/ssr/tests/resources.test.ts +11 -0
- package/templates/ssr/tsconfig.json +22 -0
- package/templates/ssr/tsconfig.node.json +10 -0
- package/templates/ssr/tsconfig.server.json +15 -0
- package/templates/ssr/vite.config.ts +25 -0
- package/templates/ssr/vitest.config.ts +13 -0
- package/templates/startkit/AGENTS.md +77 -0
- package/templates/startkit/README.md +192 -0
- package/templates/startkit/gitignore.template +16 -0
- package/templates/startkit/index.html +13 -0
- package/templates/startkit/package.json +35 -0
- package/templates/startkit/src/components/app-header.tsx +93 -0
- package/templates/startkit/src/components/app-sidebar.tsx +95 -0
- package/templates/startkit/src/components/data-table.tsx +80 -0
- package/templates/startkit/src/components/empty-state.tsx +13 -0
- package/templates/startkit/src/components/page-header.tsx +23 -0
- package/templates/startkit/src/components/stat-card.tsx +24 -0
- package/templates/startkit/src/features/accounts/account-filters.tsx +61 -0
- package/templates/startkit/src/features/accounts/account-table.tsx +79 -0
- package/templates/startkit/src/lib/format.ts +31 -0
- package/templates/startkit/src/lib/mock-data.ts +441 -0
- package/templates/startkit/src/lib/routes.ts +116 -0
- package/templates/startkit/src/main.tsx +12 -0
- package/templates/startkit/src/pages/_layout.tsx +56 -0
- package/templates/startkit/src/pages/auth/_layout.tsx +7 -0
- package/templates/startkit/src/pages/auth/login.tsx +129 -0
- package/templates/startkit/src/pages/home.tsx +83 -0
- package/templates/startkit/src/pages/not-found.tsx +24 -0
- package/templates/startkit/src/pages/workspace/_layout.tsx +17 -0
- package/templates/startkit/src/pages/workspace/accounts/index.tsx +217 -0
- package/templates/startkit/src/pages/workspace/dashboard.tsx +118 -0
- package/templates/startkit/src/pages/workspace/settings.tsx +218 -0
- package/templates/startkit/src/router.tsx +7 -0
- package/templates/startkit/src/routes/auth-config.ts +38 -0
- package/templates/startkit/src/routes/auth.ts +8 -0
- package/templates/startkit/src/routes/index.ts +28 -0
- package/templates/startkit/src/routes/public.ts +8 -0
- package/templates/startkit/src/routes/workspace/accounts.ts +8 -0
- package/templates/startkit/src/routes/workspace/index.ts +14 -0
- package/templates/startkit/src/styles/components.css +396 -0
- package/templates/startkit/src/styles/layout.css +192 -0
- package/templates/startkit/src/styles/reset.css +38 -0
- package/templates/startkit/src/styles/theme.css +76 -0
- package/templates/startkit/src/styles/tokens.css +67 -0
- package/templates/startkit/src/styles.css +7 -0
- package/templates/startkit/src/toast.ts +55 -0
- package/templates/startkit/src/utils/join-classes.ts +4 -0
- package/templates/startkit/src/vite-env.d.ts +5 -0
- package/templates/startkit/tests/app.test.tsx +32 -0
- package/templates/startkit/tests/preferences.test.ts +43 -0
- package/templates/startkit/tests/resources.test.ts +82 -0
- package/templates/startkit/tsconfig.json +20 -0
- package/templates/startkit/tsconfig.node.json +10 -0
- package/templates/startkit/vite.config.ts +32 -0
- package/templates/startkit/vitest.config.ts +13 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import '../styles.css';
|
|
2
|
+
import {
|
|
3
|
+
ToastHost,
|
|
4
|
+
Toast,
|
|
5
|
+
ToastClose,
|
|
6
|
+
ToastDescription,
|
|
7
|
+
ToastTitle,
|
|
8
|
+
ToastViewport,
|
|
9
|
+
} from '@askrjs/ui/toast';
|
|
10
|
+
import {
|
|
11
|
+
clearToast,
|
|
12
|
+
bindToast,
|
|
13
|
+
setToastOpen,
|
|
14
|
+
toastMessage,
|
|
15
|
+
toastOpen,
|
|
16
|
+
} from '../toast';
|
|
17
|
+
import { state } from '@askrjs/askr';
|
|
18
|
+
|
|
19
|
+
export default function App({ children }: { children?: unknown }) {
|
|
20
|
+
const [messageState, setMessageState] = state(toastMessage());
|
|
21
|
+
const [openState, setOpenState] = state(toastOpen());
|
|
22
|
+
|
|
23
|
+
bindToast({
|
|
24
|
+
setMessage: setMessageState,
|
|
25
|
+
setOpen: setOpenState,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const messageTitle = () => messageState()?.title ?? '';
|
|
29
|
+
const messageDescription = () => messageState()?.description;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<ToastHost duration={2400}>
|
|
33
|
+
<div class="app-root">{children}</div>
|
|
34
|
+
|
|
35
|
+
<ToastViewport class="app-toast-viewport" />
|
|
36
|
+
{messageState() && (
|
|
37
|
+
<Toast
|
|
38
|
+
open={openState()}
|
|
39
|
+
onOpenChange={(open) => {
|
|
40
|
+
setToastOpen(open);
|
|
41
|
+
if (!open) {
|
|
42
|
+
clearToast();
|
|
43
|
+
}
|
|
44
|
+
}}
|
|
45
|
+
class="app-toast"
|
|
46
|
+
>
|
|
47
|
+
<ToastTitle>{messageTitle()}</ToastTitle>
|
|
48
|
+
{messageDescription() && (
|
|
49
|
+
<ToastDescription>{messageDescription()}</ToastDescription>
|
|
50
|
+
)}
|
|
51
|
+
<ToastClose aria-label="Dismiss notification">Dismiss</ToastClose>
|
|
52
|
+
</Toast>
|
|
53
|
+
)}
|
|
54
|
+
</ToastHost>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { state } from '@askrjs/askr';
|
|
2
|
+
import { Link, currentRoute, navigate } from '@askrjs/askr/router';
|
|
3
|
+
import { Button } from '@askrjs/ui/button';
|
|
4
|
+
import { Input } from '@askrjs/ui/input';
|
|
5
|
+
import { Field } from '@askrjs/themes/components';
|
|
6
|
+
import { LockKeyholeIcon, MailIcon } from '@askrjs/lucide';
|
|
7
|
+
import { signIn } from '../../lib/mock-data';
|
|
8
|
+
import {
|
|
9
|
+
dashboardRoute,
|
|
10
|
+
normalizeProtectedRouteTarget,
|
|
11
|
+
} from '../../lib/routes';
|
|
12
|
+
import { showToast } from '../../toast';
|
|
13
|
+
|
|
14
|
+
export default function LoginPage() {
|
|
15
|
+
const [emailState, setEmailState] = state('alex@example.com');
|
|
16
|
+
const [passwordState, setPasswordState] = state('askr1234');
|
|
17
|
+
const [errorTextState, setErrorTextState] = state('');
|
|
18
|
+
const [submittingState, setSubmittingState] = state(false);
|
|
19
|
+
const routeSnapshot = currentRoute();
|
|
20
|
+
|
|
21
|
+
const redirectTarget = () =>
|
|
22
|
+
normalizeProtectedRouteTarget(routeSnapshot.query.get('next')) ||
|
|
23
|
+
dashboardRoute.href;
|
|
24
|
+
|
|
25
|
+
const validate = () => {
|
|
26
|
+
if (!emailState().trim().includes('@')) {
|
|
27
|
+
return 'Enter a valid email address.';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (passwordState().trim().length < 8) {
|
|
31
|
+
return 'Password must be at least 8 characters.';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return '';
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const submit = async (event: Event) => {
|
|
38
|
+
event.preventDefault();
|
|
39
|
+
|
|
40
|
+
const validationError = validate();
|
|
41
|
+
if (validationError) {
|
|
42
|
+
setErrorTextState(validationError);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setSubmittingState(true);
|
|
47
|
+
setErrorTextState('');
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
await signIn({ email: emailState(), password: passwordState() });
|
|
51
|
+
showToast({
|
|
52
|
+
title: 'Signed in',
|
|
53
|
+
description: 'Welcome back. You can now access protected routes.',
|
|
54
|
+
});
|
|
55
|
+
navigate(redirectTarget(), { history: 'replace' });
|
|
56
|
+
} catch (error) {
|
|
57
|
+
setErrorTextState(
|
|
58
|
+
error instanceof Error ? error.message : 'Could not sign in.'
|
|
59
|
+
);
|
|
60
|
+
setSubmittingState(false);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<section class="auth-page panel">
|
|
66
|
+
<h1>Sign in</h1>
|
|
67
|
+
<p>Use your workspace account to continue.</p>
|
|
68
|
+
|
|
69
|
+
<form class="auth-form" onSubmit={submit}>
|
|
70
|
+
<Field id="login-email">
|
|
71
|
+
<label htmlFor="login-email">Email</label>
|
|
72
|
+
<label class="input-row">
|
|
73
|
+
<MailIcon size={15} aria-hidden="true" />
|
|
74
|
+
<Input
|
|
75
|
+
type="email"
|
|
76
|
+
id="login-email"
|
|
77
|
+
value={emailState()}
|
|
78
|
+
onInput={(event: Event) =>
|
|
79
|
+
setEmailState((event.target as HTMLInputElement).value)
|
|
80
|
+
}
|
|
81
|
+
/>
|
|
82
|
+
</label>
|
|
83
|
+
</Field>
|
|
84
|
+
|
|
85
|
+
<Field id="login-password">
|
|
86
|
+
<label htmlFor="login-password">Password</label>
|
|
87
|
+
<label class="input-row">
|
|
88
|
+
<LockKeyholeIcon size={15} aria-hidden="true" />
|
|
89
|
+
<Input
|
|
90
|
+
type="password"
|
|
91
|
+
id="login-password"
|
|
92
|
+
value={passwordState()}
|
|
93
|
+
onInput={(event: Event) =>
|
|
94
|
+
setPasswordState((event.target as HTMLInputElement).value)
|
|
95
|
+
}
|
|
96
|
+
/>
|
|
97
|
+
</label>
|
|
98
|
+
</Field>
|
|
99
|
+
|
|
100
|
+
{errorTextState() && (
|
|
101
|
+
<p class="field-error" role="alert">
|
|
102
|
+
{errorTextState()}
|
|
103
|
+
</p>
|
|
104
|
+
)}
|
|
105
|
+
|
|
106
|
+
<Button type="submit" disabled={submittingState()}>
|
|
107
|
+
{submittingState() ? 'Signing in...' : 'Sign in'}
|
|
108
|
+
</Button>
|
|
109
|
+
</form>
|
|
110
|
+
|
|
111
|
+
<div class="auth-links">
|
|
112
|
+
<Link href="/">Back to landing</Link>
|
|
113
|
+
<button
|
|
114
|
+
type="button"
|
|
115
|
+
class="link-button"
|
|
116
|
+
onClick={() =>
|
|
117
|
+
showToast({
|
|
118
|
+
title: 'Reset link sent',
|
|
119
|
+
description:
|
|
120
|
+
'This is a starter example. Replace with your auth flow.',
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
>
|
|
124
|
+
Forgot password?
|
|
125
|
+
</button>
|
|
126
|
+
</div>
|
|
127
|
+
</section>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Link } from '@askrjs/askr/router';
|
|
2
|
+
import {
|
|
3
|
+
ShieldCheckIcon,
|
|
4
|
+
WorkflowIcon,
|
|
5
|
+
LayoutPanelTopIcon,
|
|
6
|
+
SparklesIcon,
|
|
7
|
+
} from '@askrjs/lucide';
|
|
8
|
+
import { dashboardRoute, loginRoute } from '../lib/routes';
|
|
9
|
+
|
|
10
|
+
const navButtonClass = 'button button-secondary';
|
|
11
|
+
|
|
12
|
+
export default function LandingPage() {
|
|
13
|
+
return (
|
|
14
|
+
<section class="marketing-page">
|
|
15
|
+
<header class="marketing-header">
|
|
16
|
+
<Link href="/" class="brand-link">
|
|
17
|
+
<span class="brand-pill" aria-hidden="true">
|
|
18
|
+
A
|
|
19
|
+
</span>
|
|
20
|
+
<strong>{'{{appName}}'}</strong>
|
|
21
|
+
</Link>
|
|
22
|
+
<div class="marketing-actions">
|
|
23
|
+
<Link href={loginRoute.href} class={navButtonClass}>
|
|
24
|
+
{loginRoute.navLabel}
|
|
25
|
+
</Link>
|
|
26
|
+
</div>
|
|
27
|
+
</header>
|
|
28
|
+
|
|
29
|
+
<div class="hero-block panel">
|
|
30
|
+
<p class="eyebrow">Production-ready starter</p>
|
|
31
|
+
<h1>Build your Askr app like a real product from day one.</h1>
|
|
32
|
+
<p>
|
|
33
|
+
This starter combines Askr, askr-ui, askr-themes, and lucide icons in
|
|
34
|
+
a practical SaaS baseline with semantic pages, composed route trees,
|
|
35
|
+
and shared layouts.
|
|
36
|
+
</p>
|
|
37
|
+
<div class="hero-cta">
|
|
38
|
+
<Link href={dashboardRoute.href} class="button">
|
|
39
|
+
Open dashboard
|
|
40
|
+
</Link>
|
|
41
|
+
<Link href={loginRoute.href} class={navButtonClass}>
|
|
42
|
+
Try login flow
|
|
43
|
+
</Link>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<section class="feature-grid">
|
|
48
|
+
<article class="panel feature-card">
|
|
49
|
+
<WorkflowIcon size={18} aria-hidden="true" />
|
|
50
|
+
<h2>Composed routes</h2>
|
|
51
|
+
<p>
|
|
52
|
+
Public, auth, protected, and 404 routes are assembled from small
|
|
53
|
+
child route modules instead of one large router file.
|
|
54
|
+
</p>
|
|
55
|
+
</article>
|
|
56
|
+
<article class="panel feature-card">
|
|
57
|
+
<LayoutPanelTopIcon size={18} aria-hidden="true" />
|
|
58
|
+
<h2>Composed UI patterns</h2>
|
|
59
|
+
<p>
|
|
60
|
+
Reusable table, headers, empty states, stat cards, sidebar, and top
|
|
61
|
+
app header.
|
|
62
|
+
</p>
|
|
63
|
+
</article>
|
|
64
|
+
<article class="panel feature-card">
|
|
65
|
+
<ShieldCheckIcon size={18} aria-hidden="true" />
|
|
66
|
+
<h2>State and data flow</h2>
|
|
67
|
+
<p>
|
|
68
|
+
Deterministic mock data with loading, empty, error, and mutation
|
|
69
|
+
states.
|
|
70
|
+
</p>
|
|
71
|
+
</article>
|
|
72
|
+
<article class="panel feature-card">
|
|
73
|
+
<SparklesIcon size={18} aria-hidden="true" />
|
|
74
|
+
<h2>Calm visual baseline</h2>
|
|
75
|
+
<p>
|
|
76
|
+
CSS token system, neutral palette, one accent color, and restrained
|
|
77
|
+
depth.
|
|
78
|
+
</p>
|
|
79
|
+
</article>
|
|
80
|
+
</section>
|
|
81
|
+
</section>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Link } from '@askrjs/askr/router';
|
|
2
|
+
|
|
3
|
+
const navButtonClass = 'button button-secondary';
|
|
4
|
+
|
|
5
|
+
export default function NotFoundPage() {
|
|
6
|
+
return (
|
|
7
|
+
<section class="not-found-page panel">
|
|
8
|
+
<p class="eyebrow">404</p>
|
|
9
|
+
<h1>Page not found</h1>
|
|
10
|
+
<p>
|
|
11
|
+
The route you requested is not mapped in this starter. Use this page to
|
|
12
|
+
wire your own fallback analytics and recovery flow.
|
|
13
|
+
</p>
|
|
14
|
+
<div class="hero-cta">
|
|
15
|
+
<Link href="/dashboard" class="button">
|
|
16
|
+
Go to dashboard
|
|
17
|
+
</Link>
|
|
18
|
+
<Link href="/" class={navButtonClass}>
|
|
19
|
+
Back to landing
|
|
20
|
+
</Link>
|
|
21
|
+
</div>
|
|
22
|
+
</section>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Shell, ShellMain, ShellNav } from '@askrjs/themes/components';
|
|
2
|
+
import AppHeader from '../../components/app-header';
|
|
3
|
+
import AppSidebar from '../../components/app-sidebar';
|
|
4
|
+
|
|
5
|
+
export default function AppLayout(props: { children?: unknown }) {
|
|
6
|
+
return (
|
|
7
|
+
<Shell variant="sidebar" class="app-shell">
|
|
8
|
+
<ShellNav class="app-shell-nav">
|
|
9
|
+
<AppSidebar />
|
|
10
|
+
</ShellNav>
|
|
11
|
+
<ShellMain class="app-shell-main">
|
|
12
|
+
<AppHeader />
|
|
13
|
+
<div class="app-main">{props.children}</div>
|
|
14
|
+
</ShellMain>
|
|
15
|
+
</Shell>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { state } from '@askrjs/askr';
|
|
2
|
+
import { resource } from '@askrjs/askr/resources';
|
|
3
|
+
import {
|
|
4
|
+
AlertDialog,
|
|
5
|
+
AlertDialogAction,
|
|
6
|
+
AlertDialogCancel,
|
|
7
|
+
AlertDialogContent,
|
|
8
|
+
AlertDialogDescription,
|
|
9
|
+
AlertDialogOverlay,
|
|
10
|
+
AlertDialogPortal,
|
|
11
|
+
AlertDialogTitle,
|
|
12
|
+
AlertDialogTrigger,
|
|
13
|
+
} from '@askrjs/ui/alert-dialog';
|
|
14
|
+
import { Button } from '@askrjs/ui/button';
|
|
15
|
+
import {
|
|
16
|
+
Inline,
|
|
17
|
+
Pagination,
|
|
18
|
+
PaginationContent,
|
|
19
|
+
PaginationItem,
|
|
20
|
+
} from '@askrjs/themes/components';
|
|
21
|
+
import { ArchiveIcon, PlusIcon } from '@askrjs/lucide';
|
|
22
|
+
import PageHeader from '../../../components/page-header';
|
|
23
|
+
import AccountFilters from '../../../features/accounts/account-filters';
|
|
24
|
+
import AccountTable from '../../../features/accounts/account-table';
|
|
25
|
+
import {
|
|
26
|
+
archiveAccounts,
|
|
27
|
+
listAccounts,
|
|
28
|
+
type AccountRecord,
|
|
29
|
+
type AccountStatus,
|
|
30
|
+
} from '../../../lib/mock-data';
|
|
31
|
+
import { showToast } from '../../../toast';
|
|
32
|
+
|
|
33
|
+
export default function AccountsPage() {
|
|
34
|
+
const [queryState, setQueryState] = state('');
|
|
35
|
+
const [statusState, setStatusState] = state<AccountStatus | 'all'>('all');
|
|
36
|
+
const [pageState, setPageState] = state(1);
|
|
37
|
+
const [selectedIdsState, setSelectedIdsState] = state<string[]>([]);
|
|
38
|
+
const [archivingState, setArchivingState] = state(false);
|
|
39
|
+
|
|
40
|
+
const pageSize = 5;
|
|
41
|
+
|
|
42
|
+
const accountsResource = resource(
|
|
43
|
+
async ({ signal }) =>
|
|
44
|
+
listAccounts({
|
|
45
|
+
signal,
|
|
46
|
+
query: queryState(),
|
|
47
|
+
status: statusState(),
|
|
48
|
+
page: pageState(),
|
|
49
|
+
pageSize,
|
|
50
|
+
}),
|
|
51
|
+
[queryState(), statusState(), pageState()]
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const rows = () => accountsResource.value?.items ?? [];
|
|
55
|
+
const totalPages = () => accountsResource.value?.totalPages ?? 1;
|
|
56
|
+
|
|
57
|
+
const toggleRow = (id: string) => {
|
|
58
|
+
setSelectedIdsState((current) =>
|
|
59
|
+
current.includes(id)
|
|
60
|
+
? current.filter((value) => value !== id)
|
|
61
|
+
: [...current, id]
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const resetFilters = () => {
|
|
66
|
+
setQueryState('');
|
|
67
|
+
setStatusState('all');
|
|
68
|
+
setPageState(1);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const openRow = (row: AccountRecord) => {
|
|
72
|
+
showToast({
|
|
73
|
+
title: row.name,
|
|
74
|
+
description: `Open account ${row.id} details in your real product workflow.`,
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const archiveSelected = async () => {
|
|
79
|
+
if (selectedIdsState().length === 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
setArchivingState(true);
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const result = await archiveAccounts({ ids: selectedIdsState() });
|
|
87
|
+
setSelectedIdsState([]);
|
|
88
|
+
showToast({
|
|
89
|
+
title: 'Accounts archived',
|
|
90
|
+
description: `${result.archived} account records moved to archived status.`,
|
|
91
|
+
});
|
|
92
|
+
await accountsResource.refresh();
|
|
93
|
+
} catch (error) {
|
|
94
|
+
showToast({
|
|
95
|
+
title: 'Archive failed',
|
|
96
|
+
description:
|
|
97
|
+
error instanceof Error
|
|
98
|
+
? error.message
|
|
99
|
+
: 'Could not archive selected rows.',
|
|
100
|
+
});
|
|
101
|
+
} finally {
|
|
102
|
+
setArchivingState(false);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<section class="stack-lg">
|
|
108
|
+
<PageHeader
|
|
109
|
+
title="Accounts"
|
|
110
|
+
description="Search, filter, page, and run row actions against account records."
|
|
111
|
+
actions={
|
|
112
|
+
<Button
|
|
113
|
+
onPress={() =>
|
|
114
|
+
showToast({
|
|
115
|
+
title: 'Create account',
|
|
116
|
+
description:
|
|
117
|
+
'Wire this button into your real create-account form flow.',
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
>
|
|
121
|
+
<PlusIcon size={14} aria-hidden="true" /> Add account
|
|
122
|
+
</Button>
|
|
123
|
+
}
|
|
124
|
+
/>
|
|
125
|
+
|
|
126
|
+
<section class="panel stack-md">
|
|
127
|
+
<AccountFilters
|
|
128
|
+
query={queryState()}
|
|
129
|
+
status={statusState()}
|
|
130
|
+
onQueryChange={(next) => {
|
|
131
|
+
setQueryState(next);
|
|
132
|
+
setPageState(1);
|
|
133
|
+
}}
|
|
134
|
+
onStatusChange={(next) => {
|
|
135
|
+
setStatusState(next);
|
|
136
|
+
setPageState(1);
|
|
137
|
+
}}
|
|
138
|
+
onReset={resetFilters}
|
|
139
|
+
/>
|
|
140
|
+
|
|
141
|
+
<AccountTable
|
|
142
|
+
rows={rows}
|
|
143
|
+
selectedIds={selectedIdsState}
|
|
144
|
+
onToggleRow={toggleRow}
|
|
145
|
+
onOpenRow={openRow}
|
|
146
|
+
loading={accountsResource.pending && !accountsResource.value}
|
|
147
|
+
errorText={accountsResource.error?.message ?? null}
|
|
148
|
+
/>
|
|
149
|
+
|
|
150
|
+
<Inline align="center" gap="var(--ak-space-lg)" wrap="wrap">
|
|
151
|
+
<span class="muted">{selectedIdsState().length} selected</span>
|
|
152
|
+
|
|
153
|
+
<AlertDialog>
|
|
154
|
+
<AlertDialogTrigger asChild>
|
|
155
|
+
<Button
|
|
156
|
+
disabled={selectedIdsState().length === 0}
|
|
157
|
+
class="button-secondary"
|
|
158
|
+
>
|
|
159
|
+
<ArchiveIcon size={14} aria-hidden="true" /> Archive selected
|
|
160
|
+
</Button>
|
|
161
|
+
</AlertDialogTrigger>
|
|
162
|
+
<AlertDialogPortal>
|
|
163
|
+
<AlertDialogOverlay />
|
|
164
|
+
<AlertDialogContent class="panel stack-md">
|
|
165
|
+
<AlertDialogTitle>Archive selected accounts?</AlertDialogTitle>
|
|
166
|
+
<AlertDialogDescription>
|
|
167
|
+
This updates selected account records in the mock data source.
|
|
168
|
+
</AlertDialogDescription>
|
|
169
|
+
<div class="inline-end">
|
|
170
|
+
<AlertDialogCancel asChild>
|
|
171
|
+
<Button class="button-secondary">Cancel</Button>
|
|
172
|
+
</AlertDialogCancel>
|
|
173
|
+
<AlertDialogAction asChild>
|
|
174
|
+
<Button
|
|
175
|
+
onPress={() => void archiveSelected()}
|
|
176
|
+
disabled={archivingState()}
|
|
177
|
+
>
|
|
178
|
+
{archivingState() ? 'Archiving...' : 'Confirm archive'}
|
|
179
|
+
</Button>
|
|
180
|
+
</AlertDialogAction>
|
|
181
|
+
</div>
|
|
182
|
+
</AlertDialogContent>
|
|
183
|
+
</AlertDialogPortal>
|
|
184
|
+
</AlertDialog>
|
|
185
|
+
|
|
186
|
+
<Pagination aria-label="Accounts pages">
|
|
187
|
+
<PaginationContent>
|
|
188
|
+
<PaginationItem>
|
|
189
|
+
<Button
|
|
190
|
+
class="button-secondary"
|
|
191
|
+
disabled={pageState() <= 1}
|
|
192
|
+
onPress={() => setPageState((page) => page - 1)}
|
|
193
|
+
>
|
|
194
|
+
Previous
|
|
195
|
+
</Button>
|
|
196
|
+
</PaginationItem>
|
|
197
|
+
<PaginationItem>
|
|
198
|
+
<span aria-current="page">
|
|
199
|
+
Page {pageState()} of {totalPages()}
|
|
200
|
+
</span>
|
|
201
|
+
</PaginationItem>
|
|
202
|
+
<PaginationItem>
|
|
203
|
+
<Button
|
|
204
|
+
class="button-secondary"
|
|
205
|
+
disabled={pageState() >= totalPages()}
|
|
206
|
+
onPress={() => setPageState((page) => page + 1)}
|
|
207
|
+
>
|
|
208
|
+
Next
|
|
209
|
+
</Button>
|
|
210
|
+
</PaginationItem>
|
|
211
|
+
</PaginationContent>
|
|
212
|
+
</Pagination>
|
|
213
|
+
</Inline>
|
|
214
|
+
</section>
|
|
215
|
+
</section>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { state } from '@askrjs/askr';
|
|
2
|
+
import { resource } from '@askrjs/askr/resources';
|
|
3
|
+
import { Button } from '@askrjs/ui/button';
|
|
4
|
+
import {
|
|
5
|
+
BarChart3Icon,
|
|
6
|
+
CircleDollarSignIcon,
|
|
7
|
+
Clock3Icon,
|
|
8
|
+
UsersIcon,
|
|
9
|
+
} from '@askrjs/lucide';
|
|
10
|
+
import DataTable, { type DataTableColumn } from '../../components/data-table';
|
|
11
|
+
import EmptyState from '../../components/empty-state';
|
|
12
|
+
import PageHeader from '../../components/page-header';
|
|
13
|
+
import StatCard from '../../components/stat-card';
|
|
14
|
+
import { getDashboardData, type ActivityEntry } from '../../lib/mock-data';
|
|
15
|
+
import { formatRelativeDate } from '../../lib/format';
|
|
16
|
+
|
|
17
|
+
export default function DashboardPage() {
|
|
18
|
+
const [hideActivityState, setHideActivityState] = state(false);
|
|
19
|
+
const dashboardResource = resource(
|
|
20
|
+
async ({ signal }) => getDashboardData({ signal }),
|
|
21
|
+
[]
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const stats = () => dashboardResource.value?.stats ?? [];
|
|
25
|
+
const activities = () =>
|
|
26
|
+
hideActivityState() ? [] : (dashboardResource.value?.activities ?? []);
|
|
27
|
+
|
|
28
|
+
const columns: DataTableColumn<ActivityEntry>[] = [
|
|
29
|
+
{ key: 'actor', header: 'Actor', render: (row) => row.actor },
|
|
30
|
+
{ key: 'action', header: 'Action', render: (row) => row.action },
|
|
31
|
+
{ key: 'resource', header: 'Resource', render: (row) => row.resource },
|
|
32
|
+
{
|
|
33
|
+
key: 'timestamp',
|
|
34
|
+
header: 'When',
|
|
35
|
+
render: (row) => (
|
|
36
|
+
<span class="muted">{formatRelativeDate(row.timestamp)}</span>
|
|
37
|
+
),
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const iconByStatKey = {
|
|
42
|
+
mrr: CircleDollarSignIcon,
|
|
43
|
+
accounts: UsersIcon,
|
|
44
|
+
pending: Clock3Icon,
|
|
45
|
+
seats: BarChart3Icon,
|
|
46
|
+
} as const;
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<section class="stack-lg">
|
|
50
|
+
<PageHeader
|
|
51
|
+
title="Dashboard"
|
|
52
|
+
description="Overview of workspace health, growth, and recent team activity."
|
|
53
|
+
actions={
|
|
54
|
+
<Button
|
|
55
|
+
onPress={() => dashboardResource.refresh()}
|
|
56
|
+
disabled={dashboardResource.pending}
|
|
57
|
+
>
|
|
58
|
+
{dashboardResource.pending ? 'Refreshing...' : 'Refresh'}
|
|
59
|
+
</Button>
|
|
60
|
+
}
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
<div class="stat-grid">
|
|
64
|
+
{stats().map((stat) => {
|
|
65
|
+
const Icon =
|
|
66
|
+
iconByStatKey[stat.key as keyof typeof iconByStatKey] ??
|
|
67
|
+
BarChart3Icon;
|
|
68
|
+
return (
|
|
69
|
+
<StatCard
|
|
70
|
+
label={stat.label}
|
|
71
|
+
value={stat.value}
|
|
72
|
+
trend={stat.trend}
|
|
73
|
+
icon={Icon}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
})}
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<section class="panel stack-md">
|
|
80
|
+
<div class="section-head">
|
|
81
|
+
<div>
|
|
82
|
+
<h2>Recent activity</h2>
|
|
83
|
+
<p class="muted">
|
|
84
|
+
The activity table demonstrates loading, empty, and retry
|
|
85
|
+
behavior.
|
|
86
|
+
</p>
|
|
87
|
+
</div>
|
|
88
|
+
<Button
|
|
89
|
+
onPress={() => setHideActivityState((current) => !current)}
|
|
90
|
+
class="button-secondary"
|
|
91
|
+
disabled={dashboardResource.pending}
|
|
92
|
+
>
|
|
93
|
+
{hideActivityState() ? 'Show activity' : 'Show empty state'}
|
|
94
|
+
</Button>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
{dashboardResource.error ? (
|
|
98
|
+
<EmptyState
|
|
99
|
+
title="Could not load dashboard"
|
|
100
|
+
description={dashboardResource.error.message}
|
|
101
|
+
action={
|
|
102
|
+
<Button onPress={() => dashboardResource.refresh()}>Retry</Button>
|
|
103
|
+
}
|
|
104
|
+
/>
|
|
105
|
+
) : (
|
|
106
|
+
<DataTable
|
|
107
|
+
rows={activities}
|
|
108
|
+
rowKey={(row) => row.id}
|
|
109
|
+
columns={columns}
|
|
110
|
+
isLoading={dashboardResource.pending && !dashboardResource.value}
|
|
111
|
+
emptyTitle="No activity yet"
|
|
112
|
+
emptyDescription="Your activity feed will appear as soon as workspace events are recorded."
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
</section>
|
|
116
|
+
</section>
|
|
117
|
+
);
|
|
118
|
+
}
|