@askrjs/cli 0.0.3 → 0.0.4
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/README.md +37 -0
- package/dist/add.d.ts +16 -0
- package/dist/add.js +33 -29
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +0 -2
- package/dist/create.d.ts +5 -0
- package/dist/create.js +104 -26
- package/dist/directory-swap-DWoHtx7C.js +35 -0
- package/dist/discovery-Difb7Y4G.js +0 -2
- package/dist/generate.d.ts +5 -0
- package/dist/generate.js +77 -12
- package/dist/is-direct-execution-Cdlr-ZUl.js +0 -2
- package/dist/openapi.d.ts +25 -0
- package/dist/openapi.js +44 -12
- package/dist/planner-VAj7qlxr.js +0 -2
- package/dist/range-YUs9eimn.js +0 -2
- package/dist/registry-CyrRHRG5.js +0 -2
- package/dist/skills/askr-ssr-ssg/SKILL.md +3 -1
- package/dist/{skills-Bh0mEhIx.js → skills-B7CbWur9.js} +89 -26
- package/dist/skills.d.ts +18 -0
- package/dist/skills.js +1 -1
- package/dist/specification-DXnDOC-0.js +0 -2
- package/dist/ssg-config.d.ts +41 -0
- package/dist/ssg-config.js +1 -0
- package/dist/ssg.d.ts +61 -0
- package/dist/ssg.js +360 -24
- package/dist/templates/full-stack/package.json +1 -1
- package/dist/templates/full-stack/src/server/action-registry.ts +0 -1
- package/dist/templates/full-stack/tsconfig.json +1 -1
- package/dist/templates/spa/package.json +2 -3
- package/dist/templates/spa/src/pages/app/_routes.tsx +2 -0
- package/dist/templates/spa/src/pages/public/_routes.tsx +2 -0
- package/dist/templates/spa/tsconfig.json +2 -1
- package/dist/templates/spa/vitest.config.ts +0 -4
- package/dist/templates/ssg/README.md +1 -1
- package/dist/templates/ssg/package.json +1 -1
- package/dist/templates/ssg/ssg.config.ts +7 -0
- package/dist/templates/ssg/tests/ssg-config.test.ts +2 -0
- package/dist/templates/ssg/tsconfig.json +2 -1
- package/dist/templates/ssg/vitest.config.ts +0 -4
- package/dist/templates/ssr/package.json +2 -2
- package/dist/templates/startkit/package.json +2 -2
- package/dist/templates/startkit/src/lib/mock-data.ts +17 -39
- package/dist/templates/startkit/tsconfig.json +2 -1
- package/dist/update.d.ts +41 -0
- package/dist/update.js +0 -2
- package/dist/writer-D8qe_7ud.js +0 -2
- package/package.json +13 -8
- package/dist/add.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/create.js.map +0 -1
- package/dist/discovery-Difb7Y4G.js.map +0 -1
- package/dist/generate.js.map +0 -1
- package/dist/is-direct-execution-Cdlr-ZUl.js.map +0 -1
- package/dist/openapi.js.map +0 -1
- package/dist/planner-VAj7qlxr.js.map +0 -1
- package/dist/range-YUs9eimn.js.map +0 -1
- package/dist/registry-CyrRHRG5.js.map +0 -1
- package/dist/skills-Bh0mEhIx.js.map +0 -1
- package/dist/specification-DXnDOC-0.js.map +0 -1
- package/dist/ssg.js.map +0 -1
- package/dist/templates/full-stack/src/vite-server.d.ts +0 -5
- package/dist/templates/spa/src/styles.d.ts +0 -1
- package/dist/templates/ssg/src/vite-env.d.ts +0 -5
- package/dist/templates/ssr/src/vite-server.d.ts +0 -7
- package/dist/templates/startkit/src/vite-env.d.ts +0 -5
- package/dist/update.js.map +0 -1
- package/dist/writer-D8qe_7ud.js.map +0 -1
- package/skills/askr-accessibility/SKILL.md +0 -68
- package/skills/askr-accessibility/agents/openai.yaml +0 -4
- package/skills/askr-agent-execution/SKILL.md +0 -85
- package/skills/askr-agent-execution/agents/openai.yaml +0 -4
- package/skills/askr-agent-workflows/SKILL.md +0 -57
- package/skills/askr-agent-workflows/agents/openai.yaml +0 -4
- package/skills/askr-api-integration/SKILL.md +0 -79
- package/skills/askr-api-integration/agents/openai.yaml +0 -4
- package/skills/askr-app-builder/SKILL.md +0 -99
- package/skills/askr-app-builder/agents/openai.yaml +0 -4
- package/skills/askr-auth-access/SKILL.md +0 -74
- package/skills/askr-auth-access/agents/openai.yaml +0 -4
- package/skills/askr-cli-vite/SKILL.md +0 -76
- package/skills/askr-cli-vite/agents/openai.yaml +0 -4
- package/skills/askr-dashboard-charts/SKILL.md +0 -99
- package/skills/askr-dashboard-charts/agents/openai.yaml +0 -4
- package/skills/askr-design-system/SKILL.md +0 -86
- package/skills/askr-design-system/agents/openai.yaml +0 -4
- package/skills/askr-env-config/SKILL.md +0 -74
- package/skills/askr-env-config/agents/openai.yaml +0 -4
- package/skills/askr-error-loading-empty/SKILL.md +0 -89
- package/skills/askr-error-loading-empty/agents/openai.yaml +0 -4
- package/skills/askr-file-upload-artifacts/SKILL.md +0 -77
- package/skills/askr-file-upload-artifacts/agents/openai.yaml +0 -4
- package/skills/askr-forms-tables-crud/SKILL.md +0 -98
- package/skills/askr-forms-tables-crud/agents/openai.yaml +0 -4
- package/skills/askr-mental-model/SKILL.md +0 -109
- package/skills/askr-mental-model/agents/openai.yaml +0 -4
- package/skills/askr-migration-react/SKILL.md +0 -81
- package/skills/askr-migration-react/agents/openai.yaml +0 -4
- package/skills/askr-observability-debugging/SKILL.md +0 -83
- package/skills/askr-observability-debugging/agents/openai.yaml +0 -4
- package/skills/askr-project-structure/SKILL.md +0 -79
- package/skills/askr-project-structure/agents/openai.yaml +0 -4
- package/skills/askr-query-mutation/SKILL.md +0 -115
- package/skills/askr-query-mutation/agents/openai.yaml +0 -4
- package/skills/askr-realtime-streaming/SKILL.md +0 -76
- package/skills/askr-realtime-streaming/agents/openai.yaml +0 -4
- package/skills/askr-resources-data/SKILL.md +0 -100
- package/skills/askr-resources-data/agents/openai.yaml +0 -4
- package/skills/askr-routing-layouts/SKILL.md +0 -107
- package/skills/askr-routing-layouts/agents/openai.yaml +0 -4
- package/skills/askr-runtime-reactivity/SKILL.md +0 -92
- package/skills/askr-runtime-reactivity/agents/openai.yaml +0 -4
- package/skills/askr-ssr-ssg/SKILL.md +0 -76
- package/skills/askr-ssr-ssg/agents/openai.yaml +0 -4
- package/skills/askr-testing-determinism/SKILL.md +0 -75
- package/skills/askr-testing-determinism/agents/openai.yaml +0 -4
- package/skills/askr-theming/SKILL.md +0 -86
- package/skills/askr-theming/agents/openai.yaml +0 -4
- package/skills/askr-ui-composition/SKILL.md +0 -92
- package/skills/askr-ui-composition/agents/openai.yaml +0 -4
- package/templates/full-stack/AGENTS.md +0 -11
- package/templates/full-stack/README.md +0 -17
- package/templates/full-stack/gitignore.template +0 -4
- package/templates/full-stack/index.html +0 -13
- package/templates/full-stack/package.json +0 -39
- package/templates/full-stack/server.ts +0 -9
- package/templates/full-stack/src/action-authorizations.ts +0 -10
- package/templates/full-stack/src/actions/create-message.ts +0 -10
- package/templates/full-stack/src/i18n.ts +0 -8
- package/templates/full-stack/src/main.tsx +0 -11
- package/templates/full-stack/src/pages/home.tsx +0 -25
- package/templates/full-stack/src/pages/layout.tsx +0 -43
- package/templates/full-stack/src/pages/not-found.tsx +0 -7
- package/templates/full-stack/src/routes.tsx +0 -19
- package/templates/full-stack/src/schemas.ts +0 -10
- package/templates/full-stack/src/server/action-registry.ts +0 -8
- package/templates/full-stack/src/server/actions/create-message.ts +0 -14
- package/templates/full-stack/src/server/app.ts +0 -76
- package/templates/full-stack/src/server/dependencies.ts +0 -55
- package/templates/full-stack/src/server/entry-server.ts +0 -8
- package/templates/full-stack/src/telemetry.ts +0 -3
- package/templates/full-stack/src/vite-server.d.ts +0 -5
- package/templates/full-stack/tests/actions/create-message.test.ts +0 -12
- package/templates/full-stack/tsconfig.json +0 -15
- package/templates/full-stack/vite.config.ts +0 -10
- package/templates/spa/AGENTS.md +0 -60
- package/templates/spa/README.md +0 -96
- package/templates/spa/gitignore.template +0 -16
- package/templates/spa/index.html +0 -13
- package/templates/spa/package.json +0 -35
- package/templates/spa/src/adapters/operations-client.ts +0 -95
- package/templates/spa/src/components/shared/metric-card.tsx +0 -27
- package/templates/spa/src/components/shared/status-badge.tsx +0 -24
- package/templates/spa/src/features/operations/operations.query.ts +0 -9
- package/templates/spa/src/main.tsx +0 -10
- package/templates/spa/src/pages/_layout.tsx +0 -9
- package/templates/spa/src/pages/_routes.tsx +0 -27
- package/templates/spa/src/pages/app/_layout.tsx +0 -94
- package/templates/spa/src/pages/app/_routes.tsx +0 -10
- package/templates/spa/src/pages/app/admin-home.tsx +0 -170
- package/templates/spa/src/pages/app/agent-runs.tsx +0 -93
- package/templates/spa/src/pages/app/settings.tsx +0 -91
- package/templates/spa/src/pages/auth/_layout.tsx +0 -38
- package/templates/spa/src/pages/auth/_routes.tsx +0 -6
- package/templates/spa/src/pages/auth/login.tsx +0 -63
- package/templates/spa/src/pages/not-found.tsx +0 -22
- package/templates/spa/src/pages/public/_layout.tsx +0 -47
- package/templates/spa/src/pages/public/_routes.tsx +0 -6
- package/templates/spa/src/pages/public/home.tsx +0 -143
- package/templates/spa/src/shared/format.ts +0 -12
- package/templates/spa/src/shared/navigation.ts +0 -15
- package/templates/spa/src/styles/components.css +0 -49
- package/templates/spa/src/styles/layout.css +0 -192
- package/templates/spa/src/styles/reset.css +0 -44
- package/templates/spa/src/styles/theme.css +0 -57
- package/templates/spa/src/styles/tokens.css +0 -20
- package/templates/spa/src/styles.css +0 -7
- package/templates/spa/src/styles.d.ts +0 -1
- package/templates/spa/tests/app.test.tsx +0 -37
- package/templates/spa/tests/components/shared.test.tsx +0 -12
- package/templates/spa/tests/resources.test.ts +0 -22
- package/templates/spa/tsconfig.json +0 -20
- package/templates/spa/tsconfig.node.json +0 -10
- package/templates/spa/vite.config.ts +0 -24
- package/templates/spa/vitest.config.ts +0 -17
- package/templates/ssg/AGENTS.md +0 -55
- package/templates/ssg/README.md +0 -39
- package/templates/ssg/gitignore.template +0 -17
- package/templates/ssg/index.html +0 -13
- package/templates/ssg/package.json +0 -35
- package/templates/ssg/src/app.tsx +0 -14
- package/templates/ssg/src/components/badge.tsx +0 -3
- package/templates/ssg/src/components/counter.tsx +0 -30
- package/templates/ssg/src/components/site-shell.tsx +0 -111
- package/templates/ssg/src/jsx.d.ts +0 -23
- package/templates/ssg/src/main.tsx +0 -8
- package/templates/ssg/src/pages/about.tsx +0 -82
- package/templates/ssg/src/pages/content.tsx +0 -63
- package/templates/ssg/src/pages/example.tsx +0 -97
- package/templates/ssg/src/pages/home.tsx +0 -51
- package/templates/ssg/src/resources/user.ts +0 -15
- package/templates/ssg/src/routes.tsx +0 -15
- package/templates/ssg/src/styles.css +0 -89
- package/templates/ssg/src/vite-env.d.ts +0 -5
- package/templates/ssg/ssg.config.ts +0 -34
- package/templates/ssg/tests/app.test.tsx +0 -19
- package/templates/ssg/tests/components/counter.test.tsx +0 -18
- package/templates/ssg/tests/resources.test.ts +0 -11
- package/templates/ssg/tests/ssg-config.test.ts +0 -11
- package/templates/ssg/tsconfig.json +0 -20
- package/templates/ssg/tsconfig.node.json +0 -10
- package/templates/ssg/vite.config.ts +0 -24
- package/templates/ssg/vitest.config.ts +0 -17
- package/templates/ssr/AGENTS.md +0 -28
- package/templates/ssr/README.md +0 -38
- package/templates/ssr/gitignore.template +0 -16
- package/templates/ssr/index.html +0 -13
- package/templates/ssr/package.json +0 -34
- package/templates/ssr/server.ts +0 -10
- package/templates/ssr/src/app.tsx +0 -21
- package/templates/ssr/src/components/counter.tsx +0 -26
- package/templates/ssr/src/entry-server.tsx +0 -12
- package/templates/ssr/src/main.tsx +0 -11
- package/templates/ssr/src/pages/about.tsx +0 -75
- package/templates/ssr/src/pages/example.tsx +0 -113
- package/templates/ssr/src/pages/home.tsx +0 -56
- package/templates/ssr/src/resources/user.ts +0 -15
- package/templates/ssr/src/routes.tsx +0 -13
- package/templates/ssr/src/styles.css +0 -148
- package/templates/ssr/src/vite-server.d.ts +0 -7
- package/templates/ssr/tests/app.test.tsx +0 -24
- package/templates/ssr/tests/components/counter.test.tsx +0 -13
- package/templates/ssr/tests/resources.test.ts +0 -11
- package/templates/ssr/tsconfig.json +0 -22
- package/templates/ssr/tsconfig.node.json +0 -10
- package/templates/ssr/tsconfig.server.json +0 -15
- package/templates/ssr/vite.config.ts +0 -25
- package/templates/ssr/vitest.config.ts +0 -13
- package/templates/startkit/AGENTS.md +0 -77
- package/templates/startkit/README.md +0 -192
- package/templates/startkit/gitignore.template +0 -16
- package/templates/startkit/index.html +0 -13
- package/templates/startkit/package.json +0 -35
- package/templates/startkit/src/components/app-header.tsx +0 -93
- package/templates/startkit/src/components/app-sidebar.tsx +0 -95
- package/templates/startkit/src/components/data-table.tsx +0 -80
- package/templates/startkit/src/components/empty-state.tsx +0 -13
- package/templates/startkit/src/components/page-header.tsx +0 -23
- package/templates/startkit/src/components/stat-card.tsx +0 -24
- package/templates/startkit/src/features/accounts/account-filters.tsx +0 -61
- package/templates/startkit/src/features/accounts/account-table.tsx +0 -79
- package/templates/startkit/src/lib/format.ts +0 -31
- package/templates/startkit/src/lib/mock-data.ts +0 -441
- package/templates/startkit/src/lib/routes.ts +0 -116
- package/templates/startkit/src/main.tsx +0 -12
- package/templates/startkit/src/pages/_layout.tsx +0 -56
- package/templates/startkit/src/pages/auth/_layout.tsx +0 -7
- package/templates/startkit/src/pages/auth/login.tsx +0 -129
- package/templates/startkit/src/pages/home.tsx +0 -83
- package/templates/startkit/src/pages/not-found.tsx +0 -24
- package/templates/startkit/src/pages/workspace/_layout.tsx +0 -17
- package/templates/startkit/src/pages/workspace/accounts/index.tsx +0 -217
- package/templates/startkit/src/pages/workspace/dashboard.tsx +0 -118
- package/templates/startkit/src/pages/workspace/settings.tsx +0 -218
- package/templates/startkit/src/router.tsx +0 -7
- package/templates/startkit/src/routes/auth-config.ts +0 -38
- package/templates/startkit/src/routes/auth.ts +0 -8
- package/templates/startkit/src/routes/index.ts +0 -28
- package/templates/startkit/src/routes/public.ts +0 -8
- package/templates/startkit/src/routes/workspace/accounts.ts +0 -8
- package/templates/startkit/src/routes/workspace/index.ts +0 -14
- package/templates/startkit/src/styles/components.css +0 -396
- package/templates/startkit/src/styles/layout.css +0 -192
- package/templates/startkit/src/styles/reset.css +0 -38
- package/templates/startkit/src/styles/theme.css +0 -76
- package/templates/startkit/src/styles/tokens.css +0 -67
- package/templates/startkit/src/styles.css +0 -7
- package/templates/startkit/src/toast.ts +0 -55
- package/templates/startkit/src/utils/join-classes.ts +0 -4
- package/templates/startkit/src/vite-env.d.ts +0 -5
- package/templates/startkit/tests/app.test.tsx +0 -32
- package/templates/startkit/tests/preferences.test.ts +0 -43
- package/templates/startkit/tests/resources.test.ts +0 -82
- package/templates/startkit/tsconfig.json +0 -20
- package/templates/startkit/tsconfig.node.json +0 -10
- package/templates/startkit/vite.config.ts +0 -32
- package/templates/startkit/vitest.config.ts +0 -13
|
@@ -1,56 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,217 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
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
|
-
}
|