create-aron-app 0.1.0 → 0.1.1
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/package.json +5 -2
- package/templates/_base/.cursor/agents/skills/clerk/SKILL.md +89 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/SKILL.md +142 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/api-specs-context.sh +30 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/execute-request.sh +88 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-endpoint-detail.sh +165 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-tag-endpoints.sh +208 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-tags.js +14 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/SKILL.md +157 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-in.md +224 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-up.md +190 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-in.md +314 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-up.md +259 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/show-component.md +125 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/SKILL.md +94 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/api-routes.md +50 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/caching-auth.md +56 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/middleware-strategies.md +68 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/server-actions.md +56 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/server-vs-client.md +104 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-webhooks/SKILL.md +131 -0
- package/templates/_base/.cursor/agents/skills/shadcn/SKILL.md +241 -0
- package/templates/_base/.cursor/agents/skills/shadcn/agents/openai.yml +5 -0
- package/templates/_base/.cursor/agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/templates/_base/.cursor/agents/skills/shadcn/assets/shadcn.png +0 -0
- package/templates/_base/.cursor/agents/skills/shadcn/cli.md +257 -0
- package/templates/_base/.cursor/agents/skills/shadcn/customization.md +202 -0
- package/templates/_base/.cursor/agents/skills/shadcn/evals/evals.json +47 -0
- package/templates/_base/.cursor/agents/skills/shadcn/mcp.md +94 -0
- package/templates/_base/.cursor/agents/skills/shadcn/rules/base-vs-radix.md +306 -0
- package/templates/_base/.cursor/agents/skills/shadcn/rules/composition.md +195 -0
- package/templates/_base/.cursor/agents/skills/shadcn/rules/forms.md +192 -0
- package/templates/_base/.cursor/agents/skills/shadcn/rules/icons.md +101 -0
- package/templates/_base/.cursor/agents/skills/shadcn/rules/styling.md +162 -0
- package/templates/_base/.cursor/commands/builder.md +0 -0
- package/templates/_base/.cursor/commands/pr.md +7 -0
- package/templates/_base/.cursor/rules/api_architecture.mdc +268 -0
- package/templates/_base/.cursor/rules/coding_standards.mdc +64 -0
- package/templates/_base/.cursor/rules/convex_rules.mdc +675 -0
- package/templates/_base/.cursor/rules/frontend_rules.mdc +268 -0
- package/templates/_base/.env.convex.example +3 -0
- package/templates/_base/.github/workflows/ci.yml +29 -0
- package/templates/_base/.nvmrc +1 -0
- package/templates/_base/.vscode/settings.json +9 -0
- package/templates/_base/apps/api/auth.config.ts +18 -0
- package/templates/_base/apps/api/functions.ts +99 -0
- package/templates/_base/apps/api/project.json +22 -0
- package/templates/_base/apps/api/schema.ts +11 -0
- package/templates/_base/apps/api/todos/crud.ts +81 -0
- package/templates/_base/apps/api/todos/schema.ts +11 -0
- package/templates/_base/apps/api/todos/types.ts +22 -0
- package/templates/_base/apps/api/tsconfig.json +23 -0
- package/templates/_base/apps/api/types.ts +16 -0
- package/templates/_base/biome.json +114 -0
- package/templates/_base/convex.json +4 -0
- package/templates/_base/emails/project.json +16 -0
- package/templates/_base/emails/tsconfig.json +5 -0
- package/templates/_base/emails/welcome_email.tsx +53 -0
- package/templates/_base/nx.json +29 -0
- package/templates/_base/package.json +73 -0
- package/templates/_base/scripts/sync_convex_env.ts +63 -0
- package/templates/_base/shared/assets/image.d.ts +4 -0
- package/templates/_base/shared/assets/src/styles/global.css +73 -0
- package/templates/_base/shared/assets/tsconfig.json +5 -0
- package/templates/_base/shared/ui/src/base/alert_dialog.tsx +139 -0
- package/templates/_base/shared/ui/src/base/badge.tsx +33 -0
- package/templates/_base/shared/ui/src/base/basic_data_table.tsx +61 -0
- package/templates/_base/shared/ui/src/base/button.tsx +69 -0
- package/templates/_base/shared/ui/src/base/button_group.tsx +82 -0
- package/templates/_base/shared/ui/src/base/card.tsx +79 -0
- package/templates/_base/shared/ui/src/base/checkbox.tsx +26 -0
- package/templates/_base/shared/ui/src/base/command.tsx +165 -0
- package/templates/_base/shared/ui/src/base/dialog.tsx +129 -0
- package/templates/_base/shared/ui/src/base/dropdown_menu.tsx +232 -0
- package/templates/_base/shared/ui/src/base/form.tsx +161 -0
- package/templates/_base/shared/ui/src/base/input.tsx +129 -0
- package/templates/_base/shared/ui/src/base/label.tsx +19 -0
- package/templates/_base/shared/ui/src/base/popover.tsx +46 -0
- package/templates/_base/shared/ui/src/base/radio_group.tsx +49 -0
- package/templates/_base/shared/ui/src/base/resizable.tsx +55 -0
- package/templates/_base/shared/ui/src/base/scroll_area.tsx +44 -0
- package/templates/_base/shared/ui/src/base/select.tsx +151 -0
- package/templates/_base/shared/ui/src/base/separator.tsx +32 -0
- package/templates/_base/shared/ui/src/base/sheet.tsx +130 -0
- package/templates/_base/shared/ui/src/base/side_bar.tsx +688 -0
- package/templates/_base/shared/ui/src/base/skeleton.tsx +7 -0
- package/templates/_base/shared/ui/src/base/spinner.tsx +20 -0
- package/templates/_base/shared/ui/src/base/switch.tsx +27 -0
- package/templates/_base/shared/ui/src/base/table.tsx +91 -0
- package/templates/_base/shared/ui/src/base/text_area.tsx +21 -0
- package/templates/_base/shared/ui/src/base/tooltip.tsx +31 -0
- package/templates/_base/shared/ui/src/base/utils.ts +17 -0
- package/templates/_base/shared/ui/src/hooks/use_keyboard_press.tsx +48 -0
- package/templates/_base/shared/ui/src/hooks/use_keyboard_release.tsx +48 -0
- package/templates/_base/shared/ui/src/hooks/use_mobile.tsx +25 -0
- package/templates/_base/shared/ui/src/hooks/use_mouse_click.tsx +44 -0
- package/templates/_base/shared/ui/src/hooks/use_mouse_location.tsx +55 -0
- package/templates/_base/shared/ui/src/hooks/use_outside_click.tsx +29 -0
- package/templates/_base/shared/ui/src/hooks/use_query_params.tsx +33 -0
- package/templates/_base/shared/ui/tsconfig.json +8 -0
- package/templates/_base/shared/utils/src/convex.ts +3 -0
- package/templates/_base/shared/utils/src/time.ts +12 -0
- package/templates/_base/shared/utils/tsconfig.json +5 -0
- package/templates/_base/skills-lock.json +35 -0
- package/templates/_base/tsconfig.base.json +34 -0
- package/templates/nextjs/.env.example +8 -0
- package/templates/nextjs/index.d.ts +6 -0
- package/templates/nextjs/next-env.d.ts +5 -0
- package/templates/nextjs/next.config.js +22 -0
- package/templates/nextjs/postcss.config.js +17 -0
- package/templates/nextjs/project.json +22 -0
- package/templates/nextjs/src/app/(auth)/layout.tsx +21 -0
- package/templates/nextjs/src/app/(auth)/not-allowed/page.tsx +22 -0
- package/templates/nextjs/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx +15 -0
- package/templates/nextjs/src/app/(dashboard)/layout.tsx +27 -0
- package/templates/nextjs/src/app/(dashboard)/page.tsx +5 -0
- package/templates/nextjs/src/app/(dashboard)/todos/[id]/page.tsx +23 -0
- package/templates/nextjs/src/app/(dashboard)/todos/page.tsx +16 -0
- package/templates/nextjs/src/app/app.css +3 -0
- package/templates/nextjs/src/app/layout.tsx +26 -0
- package/templates/nextjs/src/convex.ts +11 -0
- package/templates/nextjs/src/middleware.ts +18 -0
- package/templates/nextjs/src/providers/convex_provider.tsx +44 -0
- package/templates/nextjs/src/surfaces/home_surface.tsx +22 -0
- package/templates/nextjs/src/surfaces/todos/all_todos_surface.tsx +97 -0
- package/templates/nextjs/src/surfaces/todos/create_todo_sheet.tsx +107 -0
- package/templates/nextjs/src/surfaces/todos/single_todo_surface.tsx +90 -0
- package/templates/nextjs/src/ui/sidebar/nav_link.tsx +36 -0
- package/templates/nextjs/src/ui/sidebar/sidebar.tsx +125 -0
- package/templates/nextjs/src/utils/font.ts +9 -0
- package/templates/nextjs/tsconfig.json +42 -0
- package/templates/react-router/.env.example +8 -0
- package/templates/react-router/postcss.config.js +15 -0
- package/templates/react-router/project.json +23 -0
- package/templates/react-router/public/favicon.ico +0 -0
- package/templates/react-router/react-router.config.ts +9 -0
- package/templates/react-router/src/app.css +3 -0
- package/templates/react-router/src/components/error_boundary.tsx +33 -0
- package/templates/react-router/src/layouts/sidebar/sidebar_aside/sidebar_aside.tsx +76 -0
- package/templates/react-router/src/layouts/sidebar/sidebar_aside/user_menu.tsx +36 -0
- package/templates/react-router/src/layouts/sidebar/sidebar_layout.tsx +22 -0
- package/templates/react-router/src/providers/api_auth_provider.tsx +38 -0
- package/templates/react-router/src/root.tsx +37 -0
- package/templates/react-router/src/routes/auth/layout.tsx +13 -0
- package/templates/react-router/src/routes/auth/sign-in.tsx +13 -0
- package/templates/react-router/src/routes/index.tsx +9 -0
- package/templates/react-router/src/routes/layout.tsx +26 -0
- package/templates/react-router/src/routes/todos/[id].tsx +22 -0
- package/templates/react-router/src/routes/todos/index.tsx +13 -0
- package/templates/react-router/src/routes.ts +12 -0
- package/templates/react-router/src/surfaces/home_surface.tsx +20 -0
- package/templates/react-router/src/surfaces/todos/all_todos_surface.tsx +87 -0
- package/templates/react-router/src/surfaces/todos/create_todo_sheet.tsx +102 -0
- package/templates/react-router/src/surfaces/todos/single_todo_surface.tsx +81 -0
- package/templates/react-router/tsconfig.json +20 -0
- package/templates/react-router/vite.config.ts +40 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Icons
|
|
2
|
+
|
|
3
|
+
**Always use the project's configured `iconLibrary` for imports.** Check the `iconLibrary` field from project context: `lucide` → `lucide-react`, `tabler` → `@tabler/icons-react`, etc. Never assume `lucide-react`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Icons in Button use data-icon attribute
|
|
8
|
+
|
|
9
|
+
Add `data-icon="inline-start"` (prefix) or `data-icon="inline-end"` (suffix) to the icon. No sizing classes on the icon.
|
|
10
|
+
|
|
11
|
+
**Incorrect:**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Button>
|
|
15
|
+
<SearchIcon className="mr-2 size-4" />
|
|
16
|
+
Search
|
|
17
|
+
</Button>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct:**
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
<Button>
|
|
24
|
+
<SearchIcon data-icon="inline-start"/>
|
|
25
|
+
Search
|
|
26
|
+
</Button>
|
|
27
|
+
|
|
28
|
+
<Button>
|
|
29
|
+
Next
|
|
30
|
+
<ArrowRightIcon data-icon="inline-end"/>
|
|
31
|
+
</Button>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## No sizing classes on icons inside components
|
|
37
|
+
|
|
38
|
+
Components handle icon sizing via CSS. Don't add `size-4`, `w-4 h-4`, or other sizing classes to icons inside `Button`, `DropdownMenuItem`, `Alert`, `Sidebar*`, or other shadcn components. Unless the user explicitly asks for custom icon sizes.
|
|
39
|
+
|
|
40
|
+
**Incorrect:**
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<Button>
|
|
44
|
+
<SearchIcon className="size-4" data-icon="inline-start" />
|
|
45
|
+
Search
|
|
46
|
+
</Button>
|
|
47
|
+
|
|
48
|
+
<DropdownMenuItem>
|
|
49
|
+
<SettingsIcon className="mr-2 size-4" />
|
|
50
|
+
Settings
|
|
51
|
+
</DropdownMenuItem>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Correct:**
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<Button>
|
|
58
|
+
<SearchIcon data-icon="inline-start" />
|
|
59
|
+
Search
|
|
60
|
+
</Button>
|
|
61
|
+
|
|
62
|
+
<DropdownMenuItem>
|
|
63
|
+
<SettingsIcon />
|
|
64
|
+
Settings
|
|
65
|
+
</DropdownMenuItem>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Pass icons as component objects, not string keys
|
|
71
|
+
|
|
72
|
+
Use `icon={CheckIcon}`, not a string key to a lookup map.
|
|
73
|
+
|
|
74
|
+
**Incorrect:**
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
const iconMap = {
|
|
78
|
+
check: CheckIcon,
|
|
79
|
+
alert: AlertIcon,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function StatusBadge({ icon }: { icon: string }) {
|
|
83
|
+
const Icon = iconMap[icon]
|
|
84
|
+
return <Icon />
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
<StatusBadge icon="check" />
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Correct:**
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
// Import from the project's configured iconLibrary (e.g. lucide-react, @tabler/icons-react).
|
|
94
|
+
import { CheckIcon } from "lucide-react"
|
|
95
|
+
|
|
96
|
+
function StatusBadge({ icon: Icon }: { icon: React.ComponentType }) {
|
|
97
|
+
return <Icon />
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
<StatusBadge icon={CheckIcon} />
|
|
101
|
+
```
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Styling & Customization
|
|
2
|
+
|
|
3
|
+
See [customization.md](../customization.md) for theming, CSS variables, and adding custom colors.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- Semantic colors
|
|
8
|
+
- Built-in variants first
|
|
9
|
+
- className for layout only
|
|
10
|
+
- No space-x-* / space-y-*
|
|
11
|
+
- Prefer size-* over w-* h-* when equal
|
|
12
|
+
- Prefer truncate shorthand
|
|
13
|
+
- No manual dark: color overrides
|
|
14
|
+
- Use cn() for conditional classes
|
|
15
|
+
- No manual z-index on overlay components
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Semantic colors
|
|
20
|
+
|
|
21
|
+
**Incorrect:**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
<div className="bg-blue-500 text-white">
|
|
25
|
+
<p className="text-gray-600">Secondary text</p>
|
|
26
|
+
</div>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Correct:**
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<div className="bg-primary text-primary-foreground">
|
|
33
|
+
<p className="text-muted-foreground">Secondary text</p>
|
|
34
|
+
</div>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## No raw color values for status/state indicators
|
|
40
|
+
|
|
41
|
+
For positive, negative, or status indicators, use Badge variants, semantic tokens like `text-destructive`, or define custom CSS variables — don't reach for raw Tailwind colors.
|
|
42
|
+
|
|
43
|
+
**Incorrect:**
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<span className="text-emerald-600">+20.1%</span>
|
|
47
|
+
<span className="text-green-500">Active</span>
|
|
48
|
+
<span className="text-red-600">-3.2%</span>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Correct:**
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<Badge variant="secondary">+20.1%</Badge>
|
|
55
|
+
<Badge>Active</Badge>
|
|
56
|
+
<span className="text-destructive">-3.2%</span>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If you need a success/positive color that doesn't exist as a semantic token, use a Badge variant or ask the user about adding a custom CSS variable to the theme (see [customization.md](../customization.md)).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Built-in variants first
|
|
64
|
+
|
|
65
|
+
**Incorrect:**
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<Button className="border border-input bg-transparent hover:bg-accent">
|
|
69
|
+
Click me
|
|
70
|
+
</Button>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Correct:**
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<Button variant="outline">Click me</Button>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## className for layout only
|
|
82
|
+
|
|
83
|
+
Use `className` for layout (e.g. `max-w-md`, `mx-auto`, `mt-4`), **not** for overriding component colors or typography. To change colors, use semantic tokens, built-in variants, or CSS variables.
|
|
84
|
+
|
|
85
|
+
**Incorrect:**
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<Card className="bg-blue-100 text-blue-900 font-bold">
|
|
89
|
+
<CardContent>Dashboard</CardContent>
|
|
90
|
+
</Card>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Correct:**
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
<Card className="max-w-md mx-auto">
|
|
97
|
+
<CardContent>Dashboard</CardContent>
|
|
98
|
+
</Card>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
To customize a component's appearance, prefer these approaches in order:
|
|
102
|
+
1. **Built-in variants** — `variant="outline"`, `variant="destructive"`, etc.
|
|
103
|
+
2. **Semantic color tokens** — `bg-primary`, `text-muted-foreground`.
|
|
104
|
+
3. **CSS variables** — define custom colors in the global CSS file (see [customization.md](../customization.md)).
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## No space-x-* / space-y-*
|
|
109
|
+
|
|
110
|
+
Use `gap-*` instead. `space-y-4` → `flex flex-col gap-4`. `space-x-2` → `flex gap-2`.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<div className="flex flex-col gap-4">
|
|
114
|
+
<Input />
|
|
115
|
+
<Input />
|
|
116
|
+
<Button>Submit</Button>
|
|
117
|
+
</div>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Prefer size-* over w-* h-* when equal
|
|
123
|
+
|
|
124
|
+
`size-10` not `w-10 h-10`. Applies to icons, avatars, skeletons, etc.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Prefer truncate shorthand
|
|
129
|
+
|
|
130
|
+
`truncate` not `overflow-hidden text-ellipsis whitespace-nowrap`.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## No manual dark: color overrides
|
|
135
|
+
|
|
136
|
+
Use semantic tokens — they handle light/dark via CSS variables. `bg-background text-foreground` not `bg-white dark:bg-gray-950`.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Use cn() for conditional classes
|
|
141
|
+
|
|
142
|
+
Use the `cn()` utility from the project for conditional or merged class names. Don't write manual ternaries in className strings.
|
|
143
|
+
|
|
144
|
+
**Incorrect:**
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
<div className={`flex items-center ${isActive ? "bg-primary text-primary-foreground" : "bg-muted"}`}>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Correct:**
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import { cn } from "@/lib/utils"
|
|
154
|
+
|
|
155
|
+
<div className={cn("flex items-center", isActive ? "bg-primary text-primary-foreground" : "bg-muted")}>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## No manual z-index on overlay components
|
|
161
|
+
|
|
162
|
+
`Dialog`, `Sheet`, `Drawer`, `AlertDialog`, `DropdownMenu`, `Popover`, `Tooltip`, `HoverCard` handle their own stacking. Never add `z-50` or `z-[999]`.
|
|
File without changes
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Create a pull request for the current changes.
|
|
2
|
+
|
|
3
|
+
1. Look at the staged and unstaged changes with `git diff`
|
|
4
|
+
2. Write a clear commit message based on what changed
|
|
5
|
+
3. Commit and push to the current branch
|
|
6
|
+
4. Use `gh pr create` to open a pull request with title/description
|
|
7
|
+
5. Return the PR URL when done
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Backend architecture overview for the Convex API. Covers folder structure, schema design patterns, function conventions, and getting started steps.
|
|
3
|
+
globs: apps/api/**/*.ts
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Backend API Architecture
|
|
7
|
+
|
|
8
|
+
This is the Convex backend located in `apps/api/`. It uses `convex-ents` for relational data modeling and `zQuery`/`zMutation` wrappers from `convex-helpers` for all public functions.
|
|
9
|
+
|
|
10
|
+
## Folder Structure
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
apps/api/
|
|
14
|
+
├── <entity>/
|
|
15
|
+
│ ├── schema.ts # Ent definition (table shape + edges + indexes)
|
|
16
|
+
│ ├── types.ts # Zod schemas and TypeScript types
|
|
17
|
+
│ └── crud.ts # CRUD operations (GET → UPDATE → CREATE → DELETE)
|
|
18
|
+
├── schema.ts # Main schema — spreads all entity ent objects
|
|
19
|
+
├── types.ts # Shared context types (QueryCtx, MutationCtx, Ent<T>)
|
|
20
|
+
└── functions.ts # zQuery / zMutation wrappers using convex-ents
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Example: `todos/`
|
|
24
|
+
|
|
25
|
+
The `todos/` directory is the canonical example of how to structure a new entity:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
apps/api/todos/
|
|
29
|
+
├── schema.ts # todoEnts — defines the todos table
|
|
30
|
+
├── types.ts # CreateTodo, UpdateTodo Zod types
|
|
31
|
+
└── crud.ts # getTodo, listTodos, updateTodo, createTodo, deleteTodo
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Adding a New Entity
|
|
35
|
+
|
|
36
|
+
1. Create `apps/api/<entity>/schema.ts` — define the ent and export `<entity>Ents`
|
|
37
|
+
2. Create `apps/api/<entity>/types.ts` — define Zod schemas and TypeScript types
|
|
38
|
+
3. Create `apps/api/<entity>/crud.ts` — implement CRUD using `zQuery`/`zMutation`
|
|
39
|
+
4. Spread the new ent in `apps/api/schema.ts`:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { todoEnts } from "@/api/todos/schema";
|
|
43
|
+
import { entityEnts } from "@/api/<entity>/schema";
|
|
44
|
+
import { defineEntSchema, getEntDefinitions } from "convex-ents";
|
|
45
|
+
|
|
46
|
+
const schema = defineEntSchema({
|
|
47
|
+
...todoEnts,
|
|
48
|
+
...entityEnts,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export default schema;
|
|
52
|
+
export const entDefinitions = getEntDefinitions(schema);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Schema Design Patterns
|
|
56
|
+
|
|
57
|
+
### Ent Definitions (`schema.ts`)
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { v } from "convex/values";
|
|
61
|
+
import { defineEnt } from "convex-ents";
|
|
62
|
+
|
|
63
|
+
export const entityEnts = {
|
|
64
|
+
entities: defineEnt({
|
|
65
|
+
title: v.string(),
|
|
66
|
+
userId: v.string(),
|
|
67
|
+
isCompleted: v.boolean(),
|
|
68
|
+
})
|
|
69
|
+
.edge("relatedEntity")
|
|
70
|
+
.index("by_user_id", ["userId"]),
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- Export as `{ tableName: defineEnt({...}) }` objects
|
|
75
|
+
- Always add indexes for fields you query by
|
|
76
|
+
- Name indexes `by_<field>` or `by_<field1>_and_<field2>`
|
|
77
|
+
|
|
78
|
+
### Types (`types.ts`)
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { z } from "zod";
|
|
82
|
+
|
|
83
|
+
export type CreateEntity = z.infer<typeof CreateEntity>;
|
|
84
|
+
export const CreateEntity = z.object({
|
|
85
|
+
title: z.string().min(1),
|
|
86
|
+
userId: z.string(),
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- All Zod schemas go here, not in schema.ts
|
|
91
|
+
- Use `zodToConvex()` from `convex-helpers/server/zod` when you need a Convex validator from a Zod schema
|
|
92
|
+
|
|
93
|
+
### CRUD (`crud.ts`)
|
|
94
|
+
|
|
95
|
+
Functions are always ordered: **GET → UPDATE → CREATE → DELETE**
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { zMutation, zQuery } from "@/api/functions";
|
|
99
|
+
import { zid } from "convex-helpers/server/zod";
|
|
100
|
+
import { z } from "zod";
|
|
101
|
+
|
|
102
|
+
export const getEntity = zQuery({
|
|
103
|
+
args: { entityId: zid("entities") },
|
|
104
|
+
handler: async (ctx, args) => ctx.table("entities").getX(args.entityId),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export const listEntities = zQuery({
|
|
108
|
+
args: { userId: z.string() },
|
|
109
|
+
handler: async (ctx, args) =>
|
|
110
|
+
ctx.table("entities", "by_user_id", (q) => q.eq("userId", args.userId)),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
export const updateEntity = zMutation({
|
|
114
|
+
args: { entityId: zid("entities"), title: z.string().optional() },
|
|
115
|
+
handler: async (ctx, args) => {
|
|
116
|
+
const { entityId, ...updates } = args;
|
|
117
|
+
const entity = await ctx.table("entities").getX(entityId);
|
|
118
|
+
return entity.patch(updates);
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
export const createEntity = zMutation({
|
|
123
|
+
args: { title: z.string().min(1), userId: z.string() },
|
|
124
|
+
handler: async (ctx, args) =>
|
|
125
|
+
ctx.table("entities").insert({ title: args.title, userId: args.userId, isCompleted: false }),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
export const deleteEntity = zMutation({
|
|
129
|
+
args: { entityId: zid("entities") },
|
|
130
|
+
handler: async (ctx, args) => {
|
|
131
|
+
const entity = await ctx.table("entities").getX(args.entityId);
|
|
132
|
+
await entity.delete();
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Documentation Style
|
|
138
|
+
|
|
139
|
+
- **NEVER** add JSDoc or any comments before exported function definitions
|
|
140
|
+
- Function names should be self-documenting
|
|
141
|
+
- Only add inline comments for complex business logic that can't be expressed through code alone
|
|
142
|
+
- NO comments like `// Get entity by ID` or `// Update entity` above functions
|
|
143
|
+
|
|
144
|
+
## Ent Methods
|
|
145
|
+
|
|
146
|
+
Prefer the throwing variants when you expect the entity to exist:
|
|
147
|
+
|
|
148
|
+
| Use | When |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `getX(id)` | Entity must exist — throws if not found |
|
|
151
|
+
| `get(id)` | Entity may not exist — returns `null` |
|
|
152
|
+
| `uniqueX(...)` | Index query must return exactly one result |
|
|
153
|
+
| `unique(...)` | Index query may return zero results |
|
|
154
|
+
| `firstX(...)` | Query must return at least one result |
|
|
155
|
+
| `first(...)` | Query may return zero results |
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// Good — entity is expected to exist
|
|
159
|
+
const todo = await ctx.table("todos").getX(todoId);
|
|
160
|
+
return todo.patch(updates);
|
|
161
|
+
|
|
162
|
+
// Good — entity may not exist
|
|
163
|
+
const entity = await ctx.table("entities").get(entityId);
|
|
164
|
+
if (!entity) {
|
|
165
|
+
throw new ConvexError("Entity not found");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Bad — redundant null-check on getX result
|
|
169
|
+
const todo = await ctx.table("todos").get(todoId);
|
|
170
|
+
if (!todo) {
|
|
171
|
+
throw new ConvexError("Todo not found");
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Error Handling
|
|
176
|
+
|
|
177
|
+
- Use `ConvexError` from `convex/values` for all business logic errors
|
|
178
|
+
- Trust `getX()` and `uniqueX()` to throw — don't add redundant null checks after them
|
|
179
|
+
- Only add explicit null checks when using `get()` or `first()`
|
|
180
|
+
- Keep error messages concise and actionable
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { ConvexError } from "convex/values";
|
|
184
|
+
|
|
185
|
+
// validate before the DB operation
|
|
186
|
+
if (count <= 0) {
|
|
187
|
+
throw new ConvexError("Limit reached");
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Common Patterns
|
|
192
|
+
|
|
193
|
+
### Edge Operations
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// Load a related entity
|
|
197
|
+
const related = await entity.edge("relatedEntity");
|
|
198
|
+
|
|
199
|
+
// Add to an edge collection
|
|
200
|
+
await entity.patch({
|
|
201
|
+
relation: { add: [relatedId] },
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Remove from an edge collection
|
|
205
|
+
await entity.patch({
|
|
206
|
+
relation: { remove: [relatedId] },
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Returning an Entity with Edges
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
const related = await entity.edge("relatedEntity");
|
|
214
|
+
return { ...entity, related };
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Standard Update Pattern
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
const { entityId, ...updates } = args;
|
|
221
|
+
const entity = await ctx.table("entities").getX(entityId);
|
|
222
|
+
return entity.patch(updates);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Index Queries
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// Query by named index
|
|
229
|
+
return ctx.table("entities", "by_user_id", (q) =>
|
|
230
|
+
q.eq("userId", args.userId)
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// Query with a filter (use indexes instead where possible)
|
|
234
|
+
return ctx
|
|
235
|
+
.table("entities")
|
|
236
|
+
.filter((q) => q.eq(q.field("fieldName"), value))
|
|
237
|
+
.first();
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Validation Helpers
|
|
241
|
+
|
|
242
|
+
- Validation functions belong in the shared `@/utils/` package
|
|
243
|
+
- Keep CRUD files focused solely on database operations
|
|
244
|
+
- Reuse validators across multiple entities rather than duplicating logic
|
|
245
|
+
|
|
246
|
+
## Getting Started
|
|
247
|
+
|
|
248
|
+
### 1. Install dependencies
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
bun install
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### 2. Start the Convex dev server
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
npx convex dev
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
This pushes the schema and regenerates types in `_generated/`.
|
|
261
|
+
|
|
262
|
+
### 3. Configure environment
|
|
263
|
+
|
|
264
|
+
Copy `.env.local.example` to `.env.local` and fill in:
|
|
265
|
+
- `CONVEX_DEPLOYMENT` — from Convex dashboard
|
|
266
|
+
- `NEXT_PUBLIC_CONVEX_URL` — from Convex dashboard
|
|
267
|
+
- `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` — from Clerk dashboard
|
|
268
|
+
- `CLERK_SECRET_KEY` — from Clerk dashboard
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Project-wide coding standards covering import ordering, naming conventions, code style, and testing policy. Apply across all TypeScript/TSX files.
|
|
3
|
+
globs: **/*.ts,**/*.tsx
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Coding Standards
|
|
7
|
+
|
|
8
|
+
## Naming Conventions
|
|
9
|
+
|
|
10
|
+
### Functions
|
|
11
|
+
- Use verb + noun: `getEntity`, `updateEntity`, `createEntity`, `deleteEntity`
|
|
12
|
+
- Be specific when needed: `getEntityByEmail`, `updateEntityField`
|
|
13
|
+
- Avoid abbreviations unless widely understood
|
|
14
|
+
- Avoid verbose qualifiers: prefer `updateEntity` over `updateEntityCoreDetails`
|
|
15
|
+
|
|
16
|
+
### Fields
|
|
17
|
+
- `camelCase` for all field names
|
|
18
|
+
- Use `userId` — not `clerkId` or other provider-specific names
|
|
19
|
+
|
|
20
|
+
### Variables
|
|
21
|
+
- Keep names concise and descriptive
|
|
22
|
+
- Use singular for single items (`todo`, `entity`) and plural for collections (`todos`, `entities`)
|
|
23
|
+
- Destructure to extract IDs: `const { entityId, ...updates } = args;`
|
|
24
|
+
|
|
25
|
+
## Code Style
|
|
26
|
+
|
|
27
|
+
### Formatting
|
|
28
|
+
- Single blank line between functions
|
|
29
|
+
- No blank lines at the start or end of a function body
|
|
30
|
+
- 2-space indentation
|
|
31
|
+
- Trailing commas in multi-line arrays and objects
|
|
32
|
+
|
|
33
|
+
### Conditional Logic
|
|
34
|
+
|
|
35
|
+
Prefer concise conditionals — avoid unnecessary intermediate variables:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// Good
|
|
39
|
+
const count = entity.limit ?? 0;
|
|
40
|
+
if (count <= 0) {
|
|
41
|
+
throw new ConvexError("Limit reached");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Bad
|
|
45
|
+
const remaining = entity.limit ?? 0;
|
|
46
|
+
if (!entity.limit || entity.limit <= 0) {
|
|
47
|
+
throw new ConvexError("Limit reached");
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Async / Await
|
|
52
|
+
- Always `await` async operations — never fire-and-forget unless intentional
|
|
53
|
+
- Chain operations when it improves readability
|
|
54
|
+
- Use `Promise.all()` for independent parallel operations
|
|
55
|
+
|
|
56
|
+
### Validation
|
|
57
|
+
- Validate business rules before any database operation
|
|
58
|
+
- Centralise reusable validation in `@/utils/`
|
|
59
|
+
- Throw `ConvexError` with clear, actionable messages
|
|
60
|
+
- Keep CRUD files thin — validation logic belongs in utils
|
|
61
|
+
|
|
62
|
+
## Testing
|
|
63
|
+
|
|
64
|
+
Test files **should not** be created unless explicitly requested. Focus on implementation quality over test coverage.
|