create-aron-app 0.1.6 → 0.1.7
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/dist/index.js +50 -115
- package/package.json +5 -2
- package/templates/{_base/.cursor → .cursor}/rules/api_architecture.mdc +27 -33
- package/templates/{_base/.cursor → .cursor}/rules/coding_standards.mdc +1 -1
- package/templates/{_base/.cursor → .cursor}/rules/convex_rules.mdc +78 -422
- package/templates/.cursor/rules/frontend_architecture_core.mdc +495 -0
- package/templates/.cursor/rules/frontend_architecture_nextjs.mdc +458 -0
- package/templates/.cursor/rules/frontend_architecture_reactrouter.mdc +473 -0
- package/templates/apps/api/_generated/api.d.ts +57 -0
- package/templates/apps/api/_generated/api.js +23 -0
- package/templates/apps/api/_generated/dataModel.d.ts +60 -0
- package/templates/apps/api/_generated/server.d.ts +143 -0
- package/templates/apps/api/_generated/server.js +93 -0
- package/templates/apps/api/http.ts +16 -0
- package/templates/apps/nextjs/.env.example +10 -0
- package/templates/{nextjs → apps/nextjs}/project.json +5 -5
- package/templates/{nextjs → apps/nextjs}/src/app/(auth)/not-allowed/page.tsx +1 -0
- package/templates/apps/nextjs/src/app/(dashboard)/layout.tsx +22 -0
- package/templates/apps/nextjs/src/app/(dashboard)/page.tsx +12 -0
- package/templates/{nextjs → apps/nextjs}/src/app/(dashboard)/todos/[id]/page.tsx +5 -2
- package/templates/apps/nextjs/src/app/(dashboard)/todos/page.tsx +19 -0
- package/templates/apps/nextjs/src/middleware.ts +18 -0
- package/templates/apps/nextjs/src/surfaces/home/bootstrap.ts +9 -0
- package/templates/apps/nextjs/src/surfaces/home/home.tsx +27 -0
- package/templates/apps/nextjs/src/surfaces/home/install.tsx +17 -0
- package/templates/apps/nextjs/src/surfaces/home/layout.tsx +44 -0
- package/templates/apps/nextjs/src/surfaces/home/main/create.tsx +34 -0
- package/templates/apps/nextjs/src/surfaces/sidebar/install.tsx +23 -0
- package/templates/apps/nextjs/src/surfaces/sidebar/layout.tsx +118 -0
- package/templates/apps/nextjs/src/surfaces/sidebar/nav_main/create.tsx +19 -0
- package/templates/apps/nextjs/src/surfaces/sidebar/nav_main/nav_config.ts +22 -0
- package/templates/apps/nextjs/src/surfaces/sidebar/nav_main/nav_main.tsx +25 -0
- package/templates/apps/nextjs/src/surfaces/sidebar/nav_secondary/create.tsx +21 -0
- package/templates/apps/nextjs/src/surfaces/sidebar/nav_secondary/nav_secondary.tsx +33 -0
- package/templates/apps/nextjs/src/surfaces/sidebar/sidebar.tsx +23 -0
- package/templates/{nextjs/src/ui/sidebar/nav_link.tsx → apps/nextjs/src/surfaces/sidebar/ui/sidebar_nav_link.tsx} +13 -10
- package/templates/apps/nextjs/src/surfaces/sidebar/user_menu/create.tsx +28 -0
- package/templates/apps/nextjs/src/surfaces/sidebar/user_menu/user_menu.tsx +42 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/all_todos.tsx +29 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/all_todos_controller.ts +61 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/bootstrap.ts +21 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/header/create.tsx +23 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/install.tsx +23 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/layout.tsx +44 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/main/create.tsx +49 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/main/main.tsx +70 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/main/new_todo_sheet/create.tsx +56 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/main/new_todo_sheet/new_todo_sheet.tsx +99 -0
- package/templates/apps/nextjs/src/surfaces/todos/all_todos/main/new_todo_sheet/schema.ts +11 -0
- package/templates/apps/nextjs/src/surfaces/todos/single_todo/bootstrap.ts +32 -0
- package/templates/apps/nextjs/src/surfaces/todos/single_todo/header/create.tsx +26 -0
- package/templates/apps/nextjs/src/surfaces/todos/single_todo/header/header.tsx +22 -0
- package/templates/apps/nextjs/src/surfaces/todos/single_todo/install.tsx +27 -0
- package/templates/apps/nextjs/src/surfaces/todos/single_todo/layout.tsx +55 -0
- package/templates/apps/nextjs/src/surfaces/todos/single_todo/main/create.tsx +38 -0
- package/templates/apps/nextjs/src/surfaces/todos/single_todo/main/main.tsx +49 -0
- package/templates/apps/nextjs/src/surfaces/todos/single_todo/single_todo.tsx +29 -0
- package/templates/apps/nextjs/src/surfaces/todos/single_todo/single_todo_controller.ts +13 -0
- package/templates/apps/nextjs/src/utils/auth.ts +18 -0
- package/templates/apps/react-router/.env.example +10 -0
- package/templates/apps/react-router/.react-router/types/+future.ts +9 -0
- package/templates/apps/react-router/.react-router/types/+routes.ts +71 -0
- package/templates/apps/react-router/.react-router/types/+server-build.d.ts +18 -0
- package/templates/apps/react-router/.react-router/types/src/+types/root.ts +59 -0
- package/templates/apps/react-router/.react-router/types/src/routes/(auth)/+types/layout.ts +62 -0
- package/templates/apps/react-router/.react-router/types/src/routes/(auth)/sign-in/+types/index.ts +65 -0
- package/templates/apps/react-router/.react-router/types/src/routes/(dashboard)/+types/index.ts +65 -0
- package/templates/apps/react-router/.react-router/types/src/routes/(dashboard)/+types/layout.ts +62 -0
- package/templates/apps/react-router/.react-router/types/src/routes/(dashboard)/todos/+types/[id].ts +65 -0
- package/templates/apps/react-router/.react-router/types/src/routes/(dashboard)/todos/+types/index.ts +65 -0
- package/templates/{react-router → apps/react-router}/project.json +4 -4
- package/templates/apps/react-router/src/app.css +3 -0
- package/templates/{react-router → apps/react-router}/src/components/error_boundary.tsx +1 -1
- package/templates/{react-router → apps/react-router}/src/providers/api_auth_provider.tsx +2 -0
- package/templates/{react-router/src/routes/auth → apps/react-router/src/routes/(auth)}/layout.tsx +1 -1
- package/templates/apps/react-router/src/routes/(dashboard)/index.tsx +19 -0
- package/templates/apps/react-router/src/routes/(dashboard)/layout.tsx +37 -0
- package/templates/apps/react-router/src/routes/(dashboard)/todos/[id].tsx +19 -0
- package/templates/apps/react-router/src/routes/(dashboard)/todos/index.tsx +19 -0
- package/templates/apps/react-router/src/routes.ts +12 -0
- package/templates/apps/react-router/src/surfaces/home/bootstrap.ts +9 -0
- package/templates/apps/react-router/src/surfaces/home/home.tsx +25 -0
- package/templates/apps/react-router/src/surfaces/home/install.tsx +17 -0
- package/templates/apps/react-router/src/surfaces/home/layout.tsx +35 -0
- package/templates/apps/react-router/src/surfaces/home/main/create.tsx +32 -0
- package/templates/apps/react-router/src/surfaces/sidebar/install.tsx +23 -0
- package/templates/apps/react-router/src/surfaces/sidebar/layout.tsx +110 -0
- package/templates/apps/react-router/src/surfaces/sidebar/nav_main/create.tsx +31 -0
- package/templates/apps/react-router/src/surfaces/sidebar/nav_main/nav_main.tsx +42 -0
- package/templates/apps/react-router/src/surfaces/sidebar/nav_secondary/create.tsx +21 -0
- package/templates/apps/react-router/src/surfaces/sidebar/nav_secondary/nav_secondary.tsx +31 -0
- package/templates/apps/react-router/src/surfaces/sidebar/sidebar.tsx +18 -0
- package/templates/apps/react-router/src/surfaces/sidebar/user_menu/create.tsx +26 -0
- package/templates/{react-router/src/layouts/sidebar/sidebar_aside → apps/react-router/src/surfaces/sidebar/user_menu}/user_menu.tsx +13 -9
- package/templates/apps/react-router/src/surfaces/todos/all_todos/all_todos.tsx +25 -0
- package/templates/apps/react-router/src/surfaces/todos/all_todos/all_todos_controller.ts +47 -0
- package/templates/apps/react-router/src/surfaces/todos/all_todos/bootstrap.ts +18 -0
- package/templates/apps/react-router/src/surfaces/todos/all_todos/header/create.tsx +21 -0
- package/templates/apps/react-router/src/surfaces/todos/all_todos/install.tsx +20 -0
- package/templates/apps/react-router/src/surfaces/todos/all_todos/layout.tsx +35 -0
- package/templates/apps/react-router/src/surfaces/todos/all_todos/main/create.tsx +47 -0
- package/templates/apps/react-router/src/surfaces/todos/all_todos/main/main.tsx +68 -0
- package/templates/apps/react-router/src/surfaces/todos/all_todos/main/new_todo_sheet/create.tsx +54 -0
- package/templates/apps/react-router/src/surfaces/todos/all_todos/main/new_todo_sheet/new_todo_sheet.tsx +97 -0
- package/templates/apps/react-router/src/surfaces/todos/all_todos/main/new_todo_sheet/schema.ts +11 -0
- package/templates/apps/react-router/src/surfaces/todos/single_todo/bootstrap.ts +36 -0
- package/templates/apps/react-router/src/surfaces/todos/single_todo/header/create.tsx +32 -0
- package/templates/apps/react-router/src/surfaces/todos/single_todo/header/header.tsx +25 -0
- package/templates/apps/react-router/src/surfaces/todos/single_todo/install.tsx +27 -0
- package/templates/apps/react-router/src/surfaces/todos/single_todo/layout.tsx +45 -0
- package/templates/apps/react-router/src/surfaces/todos/single_todo/main/create.tsx +35 -0
- package/templates/apps/react-router/src/surfaces/todos/single_todo/main/main.tsx +47 -0
- package/templates/apps/react-router/src/surfaces/todos/single_todo/single_todo.tsx +27 -0
- package/templates/apps/react-router/src/surfaces/todos/single_todo/single_todo_controller.ts +16 -0
- package/templates/{react-router → apps/react-router}/vite.config.ts +27 -3
- package/templates/{_base/biome.json → biome.json} +7 -0
- package/templates/bun.lock +3187 -0
- package/templates/{_base/emails → emails}/project.json +1 -1
- package/templates/{_base/nx.json → nx.json} +11 -0
- package/templates/package.json +92 -0
- package/templates/{_base/tsconfig.base.json → tsconfig.base.json} +4 -1
- package/templates/_base/.cursor/rules/frontend_rules.mdc +0 -268
- package/templates/_base/.env.convex.example +0 -3
- package/templates/_base/_gitignore +0 -58
- package/templates/_base/package.json +0 -73
- package/templates/nextjs/.env.example +0 -8
- package/templates/nextjs/src/app/(dashboard)/layout.tsx +0 -27
- package/templates/nextjs/src/app/(dashboard)/page.tsx +0 -5
- package/templates/nextjs/src/app/(dashboard)/todos/page.tsx +0 -16
- package/templates/nextjs/src/middleware.ts +0 -18
- package/templates/nextjs/src/surfaces/home_surface.tsx +0 -22
- package/templates/nextjs/src/surfaces/todos/all_todos_surface.tsx +0 -97
- package/templates/nextjs/src/surfaces/todos/create_todo_sheet.tsx +0 -107
- package/templates/nextjs/src/surfaces/todos/single_todo_surface.tsx +0 -90
- package/templates/nextjs/src/ui/sidebar/sidebar.tsx +0 -125
- package/templates/react-router/.env.example +0 -8
- package/templates/react-router/src/app.css +0 -3
- package/templates/react-router/src/layouts/sidebar/sidebar_aside/sidebar_aside.tsx +0 -76
- package/templates/react-router/src/layouts/sidebar/sidebar_layout.tsx +0 -22
- package/templates/react-router/src/routes/index.tsx +0 -9
- package/templates/react-router/src/routes/layout.tsx +0 -26
- package/templates/react-router/src/routes/todos/[id].tsx +0 -22
- package/templates/react-router/src/routes/todos/index.tsx +0 -13
- package/templates/react-router/src/routes.ts +0 -12
- package/templates/react-router/src/surfaces/home_surface.tsx +0 -20
- package/templates/react-router/src/surfaces/todos/all_todos_surface.tsx +0 -87
- package/templates/react-router/src/surfaces/todos/create_todo_sheet.tsx +0 -102
- package/templates/react-router/src/surfaces/todos/single_todo_surface.tsx +0 -81
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/SKILL.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/SKILL.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/api-specs-context.sh +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/execute-request.sh +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/extract-endpoint-detail.sh +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/extract-tag-endpoints.sh +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/extract-tags.js +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/SKILL.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-in.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-up.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-in.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-up.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-3/show-component.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/SKILL.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/api-routes.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/caching-auth.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/middleware-strategies.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/server-actions.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/server-vs-client.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-webhooks/SKILL.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/SKILL.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/agents/openai.yml +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/assets/shadcn-small.png +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/assets/shadcn.png +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/cli.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/customization.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/evals/evals.json +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/mcp.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/base-vs-radix.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/composition.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/forms.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/icons.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/styling.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/commands/builder.md +0 -0
- /package/templates/{_base/.cursor → .cursor}/commands/pr.md +0 -0
- /package/templates/{_base/.github → .github}/workflows/ci.yml +0 -0
- /package/templates/{_base/.nvmrc → .nvmrc} +0 -0
- /package/templates/{_base/.vscode → .vscode}/settings.json +0 -0
- /package/templates/{_base/apps → apps}/api/auth.config.ts +0 -0
- /package/templates/{_base/apps → apps}/api/functions.ts +0 -0
- /package/templates/{_base/apps → apps}/api/project.json +0 -0
- /package/templates/{_base/apps → apps}/api/schema.ts +0 -0
- /package/templates/{_base/apps → apps}/api/todos/crud.ts +0 -0
- /package/templates/{_base/apps → apps}/api/todos/schema.ts +0 -0
- /package/templates/{_base/apps → apps}/api/todos/types.ts +0 -0
- /package/templates/{_base/apps → apps}/api/tsconfig.json +0 -0
- /package/templates/{_base/apps → apps}/api/types.ts +0 -0
- /package/templates/{nextjs → apps/nextjs}/index.d.ts +0 -0
- /package/templates/{nextjs → apps/nextjs}/next-env.d.ts +0 -0
- /package/templates/{nextjs → apps/nextjs}/next.config.js +0 -0
- /package/templates/{nextjs → apps/nextjs}/postcss.config.js +0 -0
- /package/templates/{nextjs → apps/nextjs}/src/app/(auth)/layout.tsx +0 -0
- /package/templates/{nextjs → apps/nextjs}/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx +0 -0
- /package/templates/{nextjs → apps/nextjs}/src/app/app.css +0 -0
- /package/templates/{nextjs → apps/nextjs}/src/app/layout.tsx +0 -0
- /package/templates/{nextjs → apps/nextjs}/src/providers/convex_provider.tsx +0 -0
- /package/templates/{nextjs/src → apps/nextjs/src/utils}/convex.ts +0 -0
- /package/templates/{nextjs → apps/nextjs}/src/utils/font.ts +0 -0
- /package/templates/{nextjs → apps/nextjs}/tsconfig.json +0 -0
- /package/templates/{react-router → apps/react-router}/postcss.config.js +0 -0
- /package/templates/{react-router → apps/react-router}/public/favicon.ico +0 -0
- /package/templates/{react-router → apps/react-router}/react-router.config.ts +0 -0
- /package/templates/{react-router → apps/react-router}/src/root.tsx +0 -0
- /package/templates/{react-router/src/routes/auth/sign-in.tsx → apps/react-router/src/routes/(auth)/sign-in/index.tsx} +0 -0
- /package/templates/{react-router → apps/react-router}/tsconfig.json +0 -0
- /package/templates/{_base/convex.json → convex.json} +0 -0
- /package/templates/{_base/emails → emails}/tsconfig.json +0 -0
- /package/templates/{_base/emails → emails}/welcome_email.tsx +0 -0
- /package/templates/{_base/scripts → scripts}/sync_convex_env.ts +0 -0
- /package/templates/{_base/shared → shared}/assets/image.d.ts +0 -0
- /package/templates/{_base/shared → shared}/assets/src/styles/global.css +0 -0
- /package/templates/{_base/shared → shared}/assets/tsconfig.json +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/alert_dialog.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/badge.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/basic_data_table.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/button.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/button_group.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/card.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/checkbox.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/command.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/dialog.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/dropdown_menu.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/form.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/input.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/label.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/popover.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/radio_group.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/resizable.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/scroll_area.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/select.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/separator.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/sheet.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/side_bar.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/skeleton.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/spinner.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/switch.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/table.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/text_area.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/tooltip.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/base/utils.ts +0 -0
- /package/templates/{_base/shared → shared}/ui/src/hooks/use_keyboard_press.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/hooks/use_keyboard_release.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/hooks/use_mobile.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/hooks/use_mouse_click.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/hooks/use_mouse_location.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/hooks/use_outside_click.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/src/hooks/use_query_params.tsx +0 -0
- /package/templates/{_base/shared → shared}/ui/tsconfig.json +0 -0
- /package/templates/{_base/shared → shared}/utils/src/convex.ts +0 -0
- /package/templates/{_base/shared → shared}/utils/src/time.ts +0 -0
- /package/templates/{_base/shared → shared}/utils/tsconfig.json +0 -0
- /package/templates/{_base/skills-lock.json → skills-lock.json} +0 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Frontend surface architecture — React Router + Convex variant. Covers MobX decorator setup, clientLoader bootstrap pattern, and React Router-specific wiring. Extends frontend_architecture_core.
|
|
3
|
+
globs: apps/react-router/src/**
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Frontend Surface Architecture — React Router + Convex
|
|
8
|
+
|
|
9
|
+
This document covers the React Router-specific implementation of the surface architecture defined in `frontend_architecture_core`. Read that document first for the layer model, MobX philosophy, and component rules.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
### MobX
|
|
16
|
+
|
|
17
|
+
MobX 2022.3 (Stage 3) decorators. Requires TypeScript 5.0+ with `experimentalDecorators` **off**.
|
|
18
|
+
|
|
19
|
+
**`tsconfig.json`:**
|
|
20
|
+
```json
|
|
21
|
+
{ "compilerOptions": { "experimentalDecorators": false } }
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**`vite.config.ts`:**
|
|
25
|
+
```typescript
|
|
26
|
+
react({ babel: { plugins: [["@babel/plugin-proposal-decorators", { version: "2023-05" }]] } })
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Rules:
|
|
30
|
+
- Every `@observable` field must use the `accessor` keyword
|
|
31
|
+
- No `makeObservable` or `makeAutoObservable` needed when using decorators
|
|
32
|
+
- Direct observable mutations inside a Promise `.then` must be wrapped in an action — `@action` decorated methods satisfy this requirement, so no additional `runInAction` is needed
|
|
33
|
+
|
|
34
|
+
### Convex Client
|
|
35
|
+
|
|
36
|
+
The `ConvexReactClient` singleton is exported from the provider so presenters can call mutations directly outside of React:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// src/providers/api_auth_provider.tsx
|
|
40
|
+
export const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Rules:
|
|
44
|
+
- Presenters import `convex` from `@/web/providers/api_auth_provider` to call `convex.mutation()` directly
|
|
45
|
+
- `create.tsx` files use `useQuery(convexQuery(...))` and `useMutation({ mutationFn: useConvexMutation(...) })` for reactive data and hook-based mutations
|
|
46
|
+
|
|
47
|
+
### TanStack Query client for `clientLoader`
|
|
48
|
+
|
|
49
|
+
Export a **module singleton** `queryClient` from the same provider module that wires `ConvexQueryClient` (e.g. `api_auth_provider.tsx`). Dashboard and page `clientLoader` functions import it and call `queryClient.ensureQueryData(convexQuery(...))` so bootstrap can warm the cache before render.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## App routing: `(auth)` vs `(dashboard)`
|
|
54
|
+
|
|
55
|
+
Mirror the filesystem shape: **`routes/(auth)/…`** and **`routes/(dashboard)/…`**. Parentheses are route-group folders only; **URLs are declared in `routes.ts`**, not by folder names.
|
|
56
|
+
|
|
57
|
+
**`routes.ts` shape:**
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { index, layout, prefix, type RouteConfig, route } from "@react-router/dev/routes";
|
|
61
|
+
|
|
62
|
+
export default [
|
|
63
|
+
layout("./routes/(auth)/layout.tsx", [
|
|
64
|
+
route("sign-in/*", "./routes/(auth)/sign-in/index.tsx"),
|
|
65
|
+
]),
|
|
66
|
+
layout("./routes/(dashboard)/layout.tsx", [
|
|
67
|
+
index("./routes/(dashboard)/index.tsx"),
|
|
68
|
+
...prefix("todos", [
|
|
69
|
+
index("./routes/(dashboard)/todos/index.tsx"),
|
|
70
|
+
route(":id", "./routes/(dashboard)/todos/[id].tsx"),
|
|
71
|
+
]),
|
|
72
|
+
]),
|
|
73
|
+
] satisfies RouteConfig;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- One **`layout()`** for `(auth)` (sign-in only, no sidebar).
|
|
77
|
+
- One **`layout()`** for `(dashboard)` with `index` + `prefix("todos", …)`.
|
|
78
|
+
|
|
79
|
+
| Group | Role |
|
|
80
|
+
| --- | --- |
|
|
81
|
+
| `(auth)` | Sign-in pages. **No** sidebar. Optional `(auth)/layout.tsx` for centered chrome. |
|
|
82
|
+
| `(dashboard)` | **`clientLoader`** validates session (e.g. `ensureQueryData` on an auth-gated Convex query; on failure `redirect("/sign-in")`). Renders `SidebarProvider` + **Sidebar** surface + `SidebarInset` + `<Outlet />`. |
|
|
83
|
+
|
|
84
|
+
**Root** (`root.tsx`): providers + `<Outlet />` only — **no** sidebar and **no** per-route auth gate here.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Shell surface vs page surfaces
|
|
89
|
+
|
|
90
|
+
- **Shell:** `surfaces/sidebar/` — mounted only from `(dashboard)/layout.tsx`. **`SidebarLayout`** + **`installSidebar`** + lazy section creates (same mental model as Next).
|
|
91
|
+
- **Pages:** `surfaces/home/`, `surfaces/todos/all_todos/`, `surfaces/todos/single_todo/` — each route file imports `bootstrap*` and mounts the surface entry component with `loaderData`.
|
|
92
|
+
|
|
93
|
+
Naming matches `frontend_architecture_core`: folders **`all_todos`**, **`single_todo`**; components **`Home`**, **`AllTodos`**, **`SingleTodo`** (optional `Surface` suffix if used consistently).
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Sidebar surface (layout chrome)
|
|
98
|
+
|
|
99
|
+
Same structure as the Next.js rule doc: `sidebar.tsx` (entry), `layout.tsx` (`SidebarLayout` / `createSidebarLayout`), `install.tsx`, section folders with `create.tsx` + dumb `*.tsx`. **Slots are product-dependent.**
|
|
100
|
+
|
|
101
|
+
**Install timing:** Layout-mounted sidebar uses **`useEffect`** to call `installSidebar` once. **Route-mounted** surfaces use the **synchronous `useRef` install guard**.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Route → surface map
|
|
106
|
+
|
|
107
|
+
| URL | Mount |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `/` | `Home` |
|
|
110
|
+
| `/todos` | `AllTodos` |
|
|
111
|
+
| `/todos/:id` | `SingleTodo` |
|
|
112
|
+
| `(dashboard)` layout | `Sidebar` |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Dashboard layout `clientLoader` (auth gate)
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { redirect } from "react-router";
|
|
120
|
+
|
|
121
|
+
import { convexQuery } from "@convex-dev/react-query";
|
|
122
|
+
|
|
123
|
+
import { api } from "@/api/_generated/api";
|
|
124
|
+
import type { Route } from "@/router/app/routes/(dashboard)/+types/layout";
|
|
125
|
+
import { queryClient } from "@/web/providers/api_auth_provider";
|
|
126
|
+
|
|
127
|
+
export async function clientLoader(_args: Route.ClientLoaderArgs) {
|
|
128
|
+
try {
|
|
129
|
+
await queryClient.ensureQueryData(convexQuery(api.todos.crud.listTodos, {}));
|
|
130
|
+
} catch {
|
|
131
|
+
return redirect("/sign-in");
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Rules:
|
|
138
|
+
|
|
139
|
+
- Use a **real authenticated Convex query** (or dedicated `api.user.me`-style query) so unauthenticated users fail fast.
|
|
140
|
+
- Import **`queryClient`** from the provider module — do not instantiate a new `QueryClient` in the loader.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Route + Bootstrap Pattern
|
|
145
|
+
|
|
146
|
+
Each route that mounts a surface uses a **bootstrap** to validate params, handle redirects, and optionally **warm the TanStack Query cache** via `queryClient.ensureQueryData(convexQuery(...))` (import the **`queryClient` singleton** from `@/web/providers/api_auth_provider`). The bootstrap return value is passed into the surface as `bootstrap`; section `create.tsx` files spread `initialData: bootstrap.*` into `useQuery` so the first paint matches the server-loaded data.
|
|
147
|
+
|
|
148
|
+
### Router Types
|
|
149
|
+
|
|
150
|
+
The `@/router` alias maps to `./apps/react-router/.react-router/types/*`. After `react-router typegen`, import generated types from paths that mirror the filesystem, e.g.:
|
|
151
|
+
|
|
152
|
+
- `@/router/app/routes/(dashboard)/+types/layout` — dashboard layout `clientLoader`
|
|
153
|
+
- `@/router/app/routes/(dashboard)/todos/+types/[id]` — todo detail route
|
|
154
|
+
|
|
155
|
+
Use:
|
|
156
|
+
|
|
157
|
+
- `Route.ClientLoaderArgs` — params, request, etc. for `clientLoader`
|
|
158
|
+
- `Route.MetaArgs` — includes `loaderData` from `clientLoader`
|
|
159
|
+
- `Route.ComponentProps` — includes `loaderData` for the page component
|
|
160
|
+
|
|
161
|
+
### Route File (`routes/(dashboard)/todos/[id].tsx`)
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import type { Route } from "@/router/app/routes/(dashboard)/todos/+types/[id]";
|
|
165
|
+
import { bootstrapSingleTodo } from "@/web/surfaces/todos/single_todo/bootstrap";
|
|
166
|
+
import { SingleTodo } from "@/web/surfaces/todos/single_todo/single_todo";
|
|
167
|
+
|
|
168
|
+
export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
|
169
|
+
return bootstrapSingleTodo({ params });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function meta({ loaderData: bootstrap }: Route.MetaArgs) {
|
|
173
|
+
return [
|
|
174
|
+
{ title: "Todo — App Name" },
|
|
175
|
+
{ name: "description", content: "" },
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export default function SingleTodoPage({ loaderData: bootstrap }: Route.ComponentProps) {
|
|
180
|
+
return <SingleTodo bootstrap={bootstrap} />;
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Rules:
|
|
185
|
+
- Types come from `@/router/app/routes/(dashboard)/…/+types/…` — React Router generates these from the route module path
|
|
186
|
+
- `clientLoader` runs before render; bootstrap is the single source of initial context
|
|
187
|
+
- `meta` receives `loaderData` (bootstrap output) for document title and meta tags
|
|
188
|
+
- Component receives `loaderData` as `bootstrap` and passes it into the surface
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Layer Reference (React Router-specific)
|
|
193
|
+
|
|
194
|
+
### `bootstrap.ts`
|
|
195
|
+
|
|
196
|
+
Validates params, handles auth redirects, and pre-populates the TanStack Query cache with the primary entity. The fetched data is passed as `initialData` to queries in the `create` layer.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { redirect } from "react-router";
|
|
200
|
+
|
|
201
|
+
import { convexQuery } from "@convex-dev/react-query";
|
|
202
|
+
import type { Route } from "@/router/app/routes/(dashboard)/todos/+types/[id]";
|
|
203
|
+
import type { TodoId } from "@/api/todos/types";
|
|
204
|
+
import { api } from "@/api/_generated/api";
|
|
205
|
+
import { queryClient } from "@/web/providers/api_auth_provider";
|
|
206
|
+
|
|
207
|
+
export type SingleTodoBootstrapArgs = Pick<Route.ClientLoaderArgs, "params">;
|
|
208
|
+
export type SingleTodoBootstrap = Awaited<ReturnType<typeof bootstrapSingleTodo>>;
|
|
209
|
+
|
|
210
|
+
export const bootstrapSingleTodo = async ({ params }: SingleTodoBootstrapArgs) => {
|
|
211
|
+
if (!params.id) {
|
|
212
|
+
throw redirect("/todos");
|
|
213
|
+
}
|
|
214
|
+
const todoId = params.id as TodoId;
|
|
215
|
+
const todo = await queryClient.ensureQueryData(
|
|
216
|
+
convexQuery(api.todos.crud.getTodo, { todoId }),
|
|
217
|
+
);
|
|
218
|
+
return { todoId, todo };
|
|
219
|
+
};
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Rules:
|
|
223
|
+
- Use `queryClient.ensureQueryData` with `convexQuery` — pre-populates the TanStack Query cache
|
|
224
|
+
- Import **`queryClient`** from `@/web/providers/api_auth_provider` (singleton shared with `QueryClientProvider`)
|
|
225
|
+
- Auth failures/redirects belong here, not in components
|
|
226
|
+
|
|
227
|
+
### `[feature].tsx` — Surface Entry Component
|
|
228
|
+
|
|
229
|
+
No `"use client"` directive — React Router is fully client-side. **Does not instantiate controllers** — that happens in `install.tsx`.
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { useMemo, useRef } from "react";
|
|
233
|
+
import { useNavigate } from "react-router";
|
|
234
|
+
|
|
235
|
+
import type { SingleTodoBootstrap } from "@/web/surfaces/todos/single_todo/bootstrap";
|
|
236
|
+
import { installSingleTodo } from "@/web/surfaces/todos/single_todo/install";
|
|
237
|
+
import { createLayout } from "@/web/surfaces/todos/single_todo/layout";
|
|
238
|
+
|
|
239
|
+
export type SingleTodoProps = {
|
|
240
|
+
bootstrap: SingleTodoBootstrap;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export const SingleTodo = ({ bootstrap }: SingleTodoProps) => {
|
|
244
|
+
const navigate = useNavigate();
|
|
245
|
+
const { layout, Layout } = useMemo(() => createLayout(), []);
|
|
246
|
+
const installed = useRef(false);
|
|
247
|
+
|
|
248
|
+
if (!installed.current) {
|
|
249
|
+
installed.current = true;
|
|
250
|
+
installSingleTodo({ layout, bootstrap, navigate });
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return <Layout />;
|
|
254
|
+
};
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Rules:
|
|
258
|
+
- Use `useNavigate` from `react-router`
|
|
259
|
+
- No `"use client"` directive needed
|
|
260
|
+
- **Never instantiate controllers here** — pass deps to install, which creates the controller
|
|
261
|
+
- All imports use `@/` aliases — no relative imports
|
|
262
|
+
|
|
263
|
+
### `install.tsx`
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
import type { NavigateFunction } from "react-router";
|
|
267
|
+
|
|
268
|
+
import type { SingleTodoBootstrap } from "@/web/surfaces/todos/single_todo/bootstrap";
|
|
269
|
+
import type { SingleTodoLayoutController } from "@/web/surfaces/todos/single_todo/layout";
|
|
270
|
+
import { SingleTodoController } from "@/web/surfaces/todos/single_todo/single_todo_controller";
|
|
271
|
+
|
|
272
|
+
export type InstallSingleTodoOpts = {
|
|
273
|
+
layout: SingleTodoLayoutController;
|
|
274
|
+
bootstrap: SingleTodoBootstrap;
|
|
275
|
+
navigate: NavigateFunction;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
export const installSingleTodo = ({ layout, bootstrap, navigate }: InstallSingleTodoOpts) => {
|
|
279
|
+
const controller = new SingleTodoController();
|
|
280
|
+
|
|
281
|
+
import("@/web/surfaces/todos/single_todo/header/create").then(({ createHeader }) => {
|
|
282
|
+
layout.setHeader(createHeader({ navigate }));
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
import("@/web/surfaces/todos/single_todo/main/create").then(({ createMain }) => {
|
|
286
|
+
layout.setMain(createMain({ controller, bootstrap }));
|
|
287
|
+
});
|
|
288
|
+
};
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Rules:
|
|
292
|
+
- Use `NavigateFunction` from `react-router` for the navigate type
|
|
293
|
+
- **Controller instantiated here** — exactly once
|
|
294
|
+
- Dynamic `import()` paths use `@/` aliases
|
|
295
|
+
|
|
296
|
+
### `layout.tsx`
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import { action, observable } from "mobx";
|
|
300
|
+
import { observer } from "mobx-react-lite";
|
|
301
|
+
import type { ComponentType } from "react";
|
|
302
|
+
|
|
303
|
+
import { Skeleton } from "@/ui/base/skeleton";
|
|
304
|
+
|
|
305
|
+
export class LayoutController {
|
|
306
|
+
@observable.ref
|
|
307
|
+
accessor Header: ComponentType | undefined = undefined;
|
|
308
|
+
|
|
309
|
+
@observable.ref
|
|
310
|
+
accessor Main: ComponentType | undefined = undefined;
|
|
311
|
+
|
|
312
|
+
@action
|
|
313
|
+
setHeader(Header: ComponentType) {
|
|
314
|
+
this.Header = Header;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
@action
|
|
318
|
+
setMain(Main: ComponentType) {
|
|
319
|
+
this.Main = Main;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export const createLayout = () => {
|
|
324
|
+
const layout = new LayoutController();
|
|
325
|
+
return {
|
|
326
|
+
layout,
|
|
327
|
+
Layout: observer(() => {
|
|
328
|
+
const Header = layout.Header;
|
|
329
|
+
const Main = layout.Main;
|
|
330
|
+
return (
|
|
331
|
+
<div className="flex flex-col gap-8 w-full">
|
|
332
|
+
{Header ? <Header /> : <Skeleton className="h-12" />}
|
|
333
|
+
{Main ? <Main /> : <Skeleton className="h-64" />}
|
|
334
|
+
</div>
|
|
335
|
+
);
|
|
336
|
+
}),
|
|
337
|
+
};
|
|
338
|
+
};
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Rules:
|
|
342
|
+
- Use `@observable.ref accessor` for component slots
|
|
343
|
+
- `@action` decorated methods create an action boundary — calling them from Promise `.then` is safe without additional `runInAction`
|
|
344
|
+
|
|
345
|
+
### `[feature]_controller.ts`
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { action, computed, observable } from "mobx";
|
|
349
|
+
|
|
350
|
+
import type { TodoId } from "@/api/todos/types";
|
|
351
|
+
|
|
352
|
+
export class SingleTodoController {
|
|
353
|
+
@observable.ref
|
|
354
|
+
accessor isEditOpen = false;
|
|
355
|
+
|
|
356
|
+
@observable.ref
|
|
357
|
+
accessor selectedTodoId: TodoId | undefined = undefined;
|
|
358
|
+
|
|
359
|
+
@computed
|
|
360
|
+
get editTitle() {
|
|
361
|
+
return this.selectedTodoId ? "Edit Todo" : "New Todo";
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
@action
|
|
365
|
+
openEdit(todoId: TodoId) {
|
|
366
|
+
this.selectedTodoId = todoId;
|
|
367
|
+
this.isEditOpen = true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
@action
|
|
371
|
+
closeEdit() {
|
|
372
|
+
this.isEditOpen = false;
|
|
373
|
+
this.selectedTodoId = undefined;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### `[section]_presenter.ts`
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import { action, computed, observable } from "mobx";
|
|
382
|
+
import type { NavigateFunction } from "react-router";
|
|
383
|
+
|
|
384
|
+
import { api } from "@/api/_generated/api";
|
|
385
|
+
import { convex } from "@/web/providers/api_auth_provider";
|
|
386
|
+
|
|
387
|
+
export type NewTodoPresenterOpts = {
|
|
388
|
+
navigate: NavigateFunction;
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
export class NewTodoPresenter {
|
|
392
|
+
private readonly navigate: NavigateFunction;
|
|
393
|
+
|
|
394
|
+
constructor({ navigate }: NewTodoPresenterOpts) {
|
|
395
|
+
this.navigate = navigate;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
@observable
|
|
399
|
+
accessor input = "";
|
|
400
|
+
|
|
401
|
+
@observable
|
|
402
|
+
accessor isPending = false;
|
|
403
|
+
|
|
404
|
+
@computed
|
|
405
|
+
get isSubmitDisabled() {
|
|
406
|
+
return !this.input.trim() || this.isPending;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
@action
|
|
410
|
+
setInput(value: string) {
|
|
411
|
+
this.input = value;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
@action
|
|
415
|
+
async submit() {
|
|
416
|
+
this.isPending = true;
|
|
417
|
+
try {
|
|
418
|
+
await convex.mutation(api.todos.crud.createTodo, { title: this.input.trim() });
|
|
419
|
+
this.input = "";
|
|
420
|
+
} finally {
|
|
421
|
+
this.isPending = false;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Rules:
|
|
428
|
+
- `@action` decorated methods satisfy the action boundary — mutations inside an async `@action` method are automatically tracked, no `runInAction` needed
|
|
429
|
+
|
|
430
|
+
### `[section]/create.tsx`
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
import { convexQuery } from "@convex-dev/react-query";
|
|
434
|
+
import { useQuery } from "@tanstack/react-query";
|
|
435
|
+
import { observer } from "mobx-react-lite";
|
|
436
|
+
|
|
437
|
+
import { api } from "@/api/_generated/api";
|
|
438
|
+
import type { SingleTodoBootstrap } from "@/web/surfaces/todos/single_todo/bootstrap";
|
|
439
|
+
import type { SingleTodoController } from "@/web/surfaces/todos/single_todo/single_todo_controller";
|
|
440
|
+
import { Main } from "@/web/surfaces/todos/single_todo/main/main";
|
|
441
|
+
|
|
442
|
+
export type CreateMainOpts = {
|
|
443
|
+
controller: SingleTodoController;
|
|
444
|
+
bootstrap: SingleTodoBootstrap;
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
export const createMain = ({ controller, bootstrap }: CreateMainOpts) => {
|
|
448
|
+
return observer(() => {
|
|
449
|
+
const { data: todo } = useQuery({
|
|
450
|
+
initialData: bootstrap.todo,
|
|
451
|
+
...convexQuery(api.todos.crud.getTodo, { todoId: bootstrap.todoId }),
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
return (
|
|
455
|
+
<Main
|
|
456
|
+
todo={todo}
|
|
457
|
+
onToggle={(isCompleted) =>
|
|
458
|
+
controller.updateTodo({
|
|
459
|
+
todoId: bootstrap.todoId,
|
|
460
|
+
isCompleted,
|
|
461
|
+
})
|
|
462
|
+
}
|
|
463
|
+
onEdit={() => controller.openEdit(bootstrap.todoId)}
|
|
464
|
+
/>
|
|
465
|
+
);
|
|
466
|
+
});
|
|
467
|
+
};
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Rules:
|
|
471
|
+
- No `"use client"` directive needed
|
|
472
|
+
- Spread `convexQuery` with `initialData: bootstrap.[entity]` — eliminates loading flash, query stays reactive
|
|
473
|
+
- All imports use `@/` aliases — no relative imports
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated `api` utility.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type * as functions from "../functions.js";
|
|
12
|
+
import type * as http from "../http.js";
|
|
13
|
+
import type * as todos_crud from "../todos/crud.js";
|
|
14
|
+
import type * as todos_types from "../todos/types.js";
|
|
15
|
+
import type * as types from "../types.js";
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
ApiFromModules,
|
|
19
|
+
FilterApi,
|
|
20
|
+
FunctionReference,
|
|
21
|
+
} from "convex/server";
|
|
22
|
+
|
|
23
|
+
declare const fullApi: ApiFromModules<{
|
|
24
|
+
functions: typeof functions;
|
|
25
|
+
http: typeof http;
|
|
26
|
+
"todos/crud": typeof todos_crud;
|
|
27
|
+
"todos/types": typeof todos_types;
|
|
28
|
+
types: typeof types;
|
|
29
|
+
}>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A utility for referencing Convex functions in your app's public API.
|
|
33
|
+
*
|
|
34
|
+
* Usage:
|
|
35
|
+
* ```js
|
|
36
|
+
* const myFunctionReference = api.myModule.myFunction;
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare const api: FilterApi<
|
|
40
|
+
typeof fullApi,
|
|
41
|
+
FunctionReference<any, "public">
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A utility for referencing Convex functions in your app's internal API.
|
|
46
|
+
*
|
|
47
|
+
* Usage:
|
|
48
|
+
* ```js
|
|
49
|
+
* const myFunctionReference = internal.myModule.myFunction;
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare const internal: FilterApi<
|
|
53
|
+
typeof fullApi,
|
|
54
|
+
FunctionReference<any, "internal">
|
|
55
|
+
>;
|
|
56
|
+
|
|
57
|
+
export declare const components: {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated `api` utility.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { anyApi, componentsGeneric } from "convex/server";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A utility for referencing Convex functions in your app's API.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* ```js
|
|
18
|
+
* const myFunctionReference = api.myModule.myFunction;
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export const api = anyApi;
|
|
22
|
+
export const internal = anyApi;
|
|
23
|
+
export const components = componentsGeneric();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Generated data model types.
|
|
4
|
+
*
|
|
5
|
+
* THIS CODE IS AUTOMATICALLY GENERATED.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate, run `npx convex dev`.
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
DataModelFromSchemaDefinition,
|
|
13
|
+
DocumentByName,
|
|
14
|
+
TableNamesInDataModel,
|
|
15
|
+
SystemTableNames,
|
|
16
|
+
} from "convex/server";
|
|
17
|
+
import type { GenericId } from "convex/values";
|
|
18
|
+
import schema from "../schema.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The names of all of your Convex tables.
|
|
22
|
+
*/
|
|
23
|
+
export type TableNames = TableNamesInDataModel<DataModel>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The type of a document stored in Convex.
|
|
27
|
+
*
|
|
28
|
+
* @typeParam TableName - A string literal type of the table name (like "users").
|
|
29
|
+
*/
|
|
30
|
+
export type Doc<TableName extends TableNames> = DocumentByName<
|
|
31
|
+
DataModel,
|
|
32
|
+
TableName
|
|
33
|
+
>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* An identifier for a document in Convex.
|
|
37
|
+
*
|
|
38
|
+
* Convex documents are uniquely identified by their `Id`, which is accessible
|
|
39
|
+
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
|
|
40
|
+
*
|
|
41
|
+
* Documents can be loaded using `db.get(tableName, id)` in query and mutation functions.
|
|
42
|
+
*
|
|
43
|
+
* IDs are just strings at runtime, but this type can be used to distinguish them from other
|
|
44
|
+
* strings when type checking.
|
|
45
|
+
*
|
|
46
|
+
* @typeParam TableName - A string literal type of the table name (like "users").
|
|
47
|
+
*/
|
|
48
|
+
export type Id<TableName extends TableNames | SystemTableNames> =
|
|
49
|
+
GenericId<TableName>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A type describing your Convex data model.
|
|
53
|
+
*
|
|
54
|
+
* This type includes information about what tables you have, the type of
|
|
55
|
+
* documents stored in those tables, and the indexes defined on them.
|
|
56
|
+
*
|
|
57
|
+
* This type is used to parameterize methods like `queryGeneric` and
|
|
58
|
+
* `mutationGeneric` to make them type-safe.
|
|
59
|
+
*/
|
|
60
|
+
export type DataModel = DataModelFromSchemaDefinition<typeof schema>;
|