create-aron-app 0.1.6 → 0.1.8

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.
Files changed (234) hide show
  1. package/README.md +24 -31
  2. package/dist/index.js +39 -115
  3. package/package.json +7 -4
  4. package/templates/.cursor/rules/backend.mdc +112 -0
  5. package/templates/.cursor/rules/coding_standards.mdc +145 -0
  6. package/templates/.cursor/rules/frontend_architecture.mdc +334 -0
  7. package/templates/.github/workflows/ci.yml +40 -0
  8. package/templates/apps/api/_generated/api.d.ts +57 -0
  9. package/templates/apps/api/_generated/api.js +23 -0
  10. package/templates/apps/api/_generated/dataModel.d.ts +60 -0
  11. package/templates/apps/api/_generated/server.d.ts +143 -0
  12. package/templates/apps/api/_generated/server.js +93 -0
  13. package/templates/apps/api/http.ts +16 -0
  14. package/templates/apps/web/.env.example +10 -0
  15. package/templates/apps/web/.react-router/types/+future.ts +9 -0
  16. package/templates/apps/web/.react-router/types/+routes.ts +76 -0
  17. package/templates/apps/web/.react-router/types/+server-build.d.ts +18 -0
  18. package/templates/apps/web/.react-router/types/src/+types/root.ts +59 -0
  19. package/templates/apps/web/.react-router/types/src/routes/(auth)/+types/layout.ts +62 -0
  20. package/templates/apps/web/.react-router/types/src/routes/(auth)/sign-in/+types/index.ts +65 -0
  21. package/templates/apps/web/.react-router/types/src/routes/(dashboard)/(todos)/+types/[id].ts +68 -0
  22. package/templates/apps/web/.react-router/types/src/routes/(dashboard)/(todos)/+types/index.ts +68 -0
  23. package/templates/apps/web/.react-router/types/src/routes/(dashboard)/(todos)/+types/layout.ts +65 -0
  24. package/templates/apps/web/.react-router/types/src/routes/(dashboard)/+types/index.ts +65 -0
  25. package/templates/apps/web/.react-router/types/src/routes/(dashboard)/+types/layout.ts +62 -0
  26. package/templates/{react-router → apps/web}/project.json +9 -2
  27. package/templates/{react-router → apps/web}/react-router.config.ts +1 -1
  28. package/templates/apps/web/src/app.css +3 -0
  29. package/templates/{react-router → apps/web}/src/components/error_boundary.tsx +1 -1
  30. package/templates/apps/web/src/libs/convex_query_client.ts +11 -0
  31. package/templates/apps/web/src/libs/react_query_client.ts +17 -0
  32. package/templates/apps/web/src/libs/server/auth.ts +32 -0
  33. package/templates/apps/web/src/libs/server/protected.ts +17 -0
  34. package/templates/apps/web/src/providers/api_auth_provider.tsx +26 -0
  35. package/templates/apps/web/src/providers/global_provider.tsx +28 -0
  36. package/templates/apps/web/src/providers/navigation_loading_bar_provider.tsx +72 -0
  37. package/templates/apps/web/src/root.tsx +68 -0
  38. package/templates/{react-router/src/routes/auth → apps/web/src/routes/(auth)}/layout.tsx +1 -1
  39. package/templates/apps/web/src/routes/(dashboard)/(todos)/[id].tsx +33 -0
  40. package/templates/apps/web/src/routes/(dashboard)/(todos)/index.tsx +26 -0
  41. package/templates/apps/web/src/routes/(dashboard)/(todos)/layout.tsx +9 -0
  42. package/templates/apps/web/src/routes/(dashboard)/index.tsx +20 -0
  43. package/templates/apps/web/src/routes/(dashboard)/layout.tsx +20 -0
  44. package/templates/apps/web/src/routes.ts +14 -0
  45. package/templates/apps/web/src/surfaces/home/bootstrap.ts +9 -0
  46. package/templates/apps/web/src/surfaces/home/home.tsx +25 -0
  47. package/templates/apps/web/src/surfaces/home/install.tsx +17 -0
  48. package/templates/apps/web/src/surfaces/home/layout.tsx +35 -0
  49. package/templates/apps/web/src/surfaces/home/main/create.tsx +32 -0
  50. package/templates/apps/web/src/surfaces/sidebar/install.tsx +19 -0
  51. package/templates/apps/web/src/surfaces/sidebar/layout.tsx +119 -0
  52. package/templates/apps/web/src/surfaces/sidebar/nav_main/create.tsx +31 -0
  53. package/templates/apps/web/src/surfaces/sidebar/nav_main/nav_main.tsx +42 -0
  54. package/templates/apps/web/src/surfaces/sidebar/sidebar.tsx +18 -0
  55. package/templates/apps/web/src/surfaces/sidebar/user_menu/create.tsx +26 -0
  56. package/templates/{react-router/src/layouts/sidebar/sidebar_aside → apps/web/src/surfaces/sidebar/user_menu}/user_menu.tsx +13 -9
  57. package/templates/apps/web/src/surfaces/todos/all_todos/all_todos.tsx +25 -0
  58. package/templates/apps/web/src/surfaces/todos/all_todos/all_todos_controller.ts +47 -0
  59. package/templates/apps/web/src/surfaces/todos/all_todos/bootstrap.ts +21 -0
  60. package/templates/apps/web/src/surfaces/todos/all_todos/header/create.tsx +21 -0
  61. package/templates/apps/web/src/surfaces/todos/all_todos/install.tsx +20 -0
  62. package/templates/apps/web/src/surfaces/todos/all_todos/layout.tsx +35 -0
  63. package/templates/apps/web/src/surfaces/todos/all_todos/main/create.tsx +47 -0
  64. package/templates/apps/web/src/surfaces/todos/all_todos/main/main.tsx +68 -0
  65. package/templates/apps/web/src/surfaces/todos/all_todos/main/new_todo_sheet/create.tsx +54 -0
  66. package/templates/apps/web/src/surfaces/todos/all_todos/main/new_todo_sheet/new_todo_sheet.tsx +97 -0
  67. package/templates/apps/web/src/surfaces/todos/all_todos/main/new_todo_sheet/schema.ts +11 -0
  68. package/templates/apps/web/src/surfaces/todos/single_todo/bootstrap.ts +35 -0
  69. package/templates/apps/web/src/surfaces/todos/single_todo/header/create.tsx +24 -0
  70. package/templates/apps/web/src/surfaces/todos/single_todo/header/header.tsx +25 -0
  71. package/templates/apps/web/src/surfaces/todos/single_todo/install.tsx +27 -0
  72. package/templates/apps/web/src/surfaces/todos/single_todo/layout.tsx +45 -0
  73. package/templates/apps/web/src/surfaces/todos/single_todo/main/create.tsx +35 -0
  74. package/templates/apps/web/src/surfaces/todos/single_todo/main/main.tsx +47 -0
  75. package/templates/apps/web/src/surfaces/todos/single_todo/single_todo.tsx +27 -0
  76. package/templates/apps/web/src/surfaces/todos/single_todo/single_todo_controller.ts +16 -0
  77. package/templates/{react-router → apps/web}/vite.config.ts +27 -3
  78. package/templates/{_base/biome.json → biome.json} +12 -0
  79. package/templates/bun.lock +1917 -0
  80. package/templates/{_base/emails → emails}/project.json +1 -1
  81. package/templates/package.json +91 -0
  82. package/templates/{_base/shared → shared}/assets/src/styles/global.css +14 -8
  83. package/templates/shared/ui/src/base/collapsible.tsx +31 -0
  84. package/templates/shared/ui/src/base/hover-card.tsx +42 -0
  85. package/templates/shared/ui/src/base/input-group.tsx +168 -0
  86. package/templates/shared/ui/src/base/panel.tsx +93 -0
  87. package/templates/{_base/shared → shared}/ui/src/hooks/use_mobile.tsx +1 -1
  88. package/templates/{_base/shared → shared}/ui/src/hooks/use_query_params.tsx +6 -7
  89. package/templates/{_base/shared → shared}/ui/tsconfig.json +1 -1
  90. package/templates/shared/utils/src/convex.ts +4 -0
  91. package/templates/{_base/tsconfig.base.json → tsconfig.base.json} +2 -1
  92. package/templates/_base/.cursor/commands/builder.md +0 -0
  93. package/templates/_base/.cursor/rules/api_architecture.mdc +0 -268
  94. package/templates/_base/.cursor/rules/coding_standards.mdc +0 -64
  95. package/templates/_base/.cursor/rules/convex_rules.mdc +0 -675
  96. package/templates/_base/.cursor/rules/frontend_rules.mdc +0 -268
  97. package/templates/_base/.env.convex.example +0 -3
  98. package/templates/_base/.github/workflows/ci.yml +0 -29
  99. package/templates/_base/_gitignore +0 -58
  100. package/templates/_base/package.json +0 -73
  101. package/templates/_base/shared/utils/src/convex.ts +0 -3
  102. package/templates/nextjs/.env.example +0 -8
  103. package/templates/nextjs/index.d.ts +0 -6
  104. package/templates/nextjs/next-env.d.ts +0 -5
  105. package/templates/nextjs/next.config.js +0 -22
  106. package/templates/nextjs/postcss.config.js +0 -17
  107. package/templates/nextjs/project.json +0 -22
  108. package/templates/nextjs/src/app/(auth)/layout.tsx +0 -21
  109. package/templates/nextjs/src/app/(auth)/not-allowed/page.tsx +0 -22
  110. package/templates/nextjs/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx +0 -15
  111. package/templates/nextjs/src/app/(dashboard)/layout.tsx +0 -27
  112. package/templates/nextjs/src/app/(dashboard)/page.tsx +0 -5
  113. package/templates/nextjs/src/app/(dashboard)/todos/[id]/page.tsx +0 -23
  114. package/templates/nextjs/src/app/(dashboard)/todos/page.tsx +0 -16
  115. package/templates/nextjs/src/app/app.css +0 -3
  116. package/templates/nextjs/src/app/layout.tsx +0 -26
  117. package/templates/nextjs/src/convex.ts +0 -11
  118. package/templates/nextjs/src/middleware.ts +0 -18
  119. package/templates/nextjs/src/providers/convex_provider.tsx +0 -44
  120. package/templates/nextjs/src/surfaces/home_surface.tsx +0 -22
  121. package/templates/nextjs/src/surfaces/todos/all_todos_surface.tsx +0 -97
  122. package/templates/nextjs/src/surfaces/todos/create_todo_sheet.tsx +0 -107
  123. package/templates/nextjs/src/surfaces/todos/single_todo_surface.tsx +0 -90
  124. package/templates/nextjs/src/ui/sidebar/nav_link.tsx +0 -36
  125. package/templates/nextjs/src/ui/sidebar/sidebar.tsx +0 -125
  126. package/templates/nextjs/src/utils/font.ts +0 -9
  127. package/templates/nextjs/tsconfig.json +0 -42
  128. package/templates/react-router/.env.example +0 -8
  129. package/templates/react-router/src/app.css +0 -3
  130. package/templates/react-router/src/layouts/sidebar/sidebar_aside/sidebar_aside.tsx +0 -76
  131. package/templates/react-router/src/layouts/sidebar/sidebar_layout.tsx +0 -22
  132. package/templates/react-router/src/providers/api_auth_provider.tsx +0 -38
  133. package/templates/react-router/src/root.tsx +0 -37
  134. package/templates/react-router/src/routes/index.tsx +0 -9
  135. package/templates/react-router/src/routes/layout.tsx +0 -26
  136. package/templates/react-router/src/routes/todos/[id].tsx +0 -22
  137. package/templates/react-router/src/routes/todos/index.tsx +0 -13
  138. package/templates/react-router/src/routes.ts +0 -12
  139. package/templates/react-router/src/surfaces/home_surface.tsx +0 -20
  140. package/templates/react-router/src/surfaces/todos/all_todos_surface.tsx +0 -87
  141. package/templates/react-router/src/surfaces/todos/create_todo_sheet.tsx +0 -102
  142. package/templates/react-router/src/surfaces/todos/single_todo_surface.tsx +0 -81
  143. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/SKILL.md +0 -0
  144. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/SKILL.md +0 -0
  145. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/api-specs-context.sh +0 -0
  146. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/execute-request.sh +0 -0
  147. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/extract-endpoint-detail.sh +0 -0
  148. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/extract-tag-endpoints.sh +0 -0
  149. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-backend-api/scripts/extract-tags.js +0 -0
  150. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/SKILL.md +0 -0
  151. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-in.md +0 -0
  152. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-up.md +0 -0
  153. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-in.md +0 -0
  154. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-up.md +0 -0
  155. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-custom-ui/core-3/show-component.md +0 -0
  156. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/SKILL.md +0 -0
  157. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/api-routes.md +0 -0
  158. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/caching-auth.md +0 -0
  159. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/middleware-strategies.md +0 -0
  160. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/server-actions.md +0 -0
  161. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-nextjs-patterns/references/server-vs-client.md +0 -0
  162. /package/templates/{_base/.cursor → .cursor}/agents/skills/clerk/clerk-webhooks/SKILL.md +0 -0
  163. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/SKILL.md +0 -0
  164. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/agents/openai.yml +0 -0
  165. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/assets/shadcn-small.png +0 -0
  166. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/assets/shadcn.png +0 -0
  167. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/cli.md +0 -0
  168. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/customization.md +0 -0
  169. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/evals/evals.json +0 -0
  170. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/mcp.md +0 -0
  171. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/base-vs-radix.md +0 -0
  172. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/composition.md +0 -0
  173. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/forms.md +0 -0
  174. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/icons.md +0 -0
  175. /package/templates/{_base/.cursor → .cursor}/agents/skills/shadcn/rules/styling.md +0 -0
  176. /package/templates/{_base/.cursor → .cursor}/commands/pr.md +0 -0
  177. /package/templates/{_base/.nvmrc → .nvmrc} +0 -0
  178. /package/templates/{_base/.vscode → .vscode}/settings.json +0 -0
  179. /package/templates/{_base/apps → apps}/api/auth.config.ts +0 -0
  180. /package/templates/{_base/apps → apps}/api/functions.ts +0 -0
  181. /package/templates/{_base/apps → apps}/api/project.json +0 -0
  182. /package/templates/{_base/apps → apps}/api/schema.ts +0 -0
  183. /package/templates/{_base/apps → apps}/api/todos/crud.ts +0 -0
  184. /package/templates/{_base/apps → apps}/api/todos/schema.ts +0 -0
  185. /package/templates/{_base/apps → apps}/api/todos/types.ts +0 -0
  186. /package/templates/{_base/apps → apps}/api/tsconfig.json +0 -0
  187. /package/templates/{_base/apps → apps}/api/types.ts +0 -0
  188. /package/templates/{react-router → apps/web}/postcss.config.js +0 -0
  189. /package/templates/{react-router → apps/web}/public/favicon.ico +0 -0
  190. /package/templates/{react-router/src/routes/auth/sign-in.tsx → apps/web/src/routes/(auth)/sign-in/index.tsx} +0 -0
  191. /package/templates/{react-router → apps/web}/tsconfig.json +0 -0
  192. /package/templates/{_base/convex.json → convex.json} +0 -0
  193. /package/templates/{_base/emails → emails}/tsconfig.json +0 -0
  194. /package/templates/{_base/emails → emails}/welcome_email.tsx +0 -0
  195. /package/templates/{_base/nx.json → nx.json} +0 -0
  196. /package/templates/{_base/scripts → scripts}/sync_convex_env.ts +0 -0
  197. /package/templates/{_base/shared → shared}/assets/image.d.ts +0 -0
  198. /package/templates/{_base/shared → shared}/assets/tsconfig.json +0 -0
  199. /package/templates/{_base/shared → shared}/ui/src/base/alert_dialog.tsx +0 -0
  200. /package/templates/{_base/shared → shared}/ui/src/base/badge.tsx +0 -0
  201. /package/templates/{_base/shared → shared}/ui/src/base/basic_data_table.tsx +0 -0
  202. /package/templates/{_base/shared → shared}/ui/src/base/button.tsx +0 -0
  203. /package/templates/{_base/shared → shared}/ui/src/base/button_group.tsx +0 -0
  204. /package/templates/{_base/shared → shared}/ui/src/base/card.tsx +0 -0
  205. /package/templates/{_base/shared → shared}/ui/src/base/checkbox.tsx +0 -0
  206. /package/templates/{_base/shared → shared}/ui/src/base/command.tsx +0 -0
  207. /package/templates/{_base/shared → shared}/ui/src/base/dialog.tsx +0 -0
  208. /package/templates/{_base/shared → shared}/ui/src/base/dropdown_menu.tsx +0 -0
  209. /package/templates/{_base/shared → shared}/ui/src/base/form.tsx +0 -0
  210. /package/templates/{_base/shared → shared}/ui/src/base/input.tsx +0 -0
  211. /package/templates/{_base/shared → shared}/ui/src/base/label.tsx +0 -0
  212. /package/templates/{_base/shared → shared}/ui/src/base/popover.tsx +0 -0
  213. /package/templates/{_base/shared → shared}/ui/src/base/radio_group.tsx +0 -0
  214. /package/templates/{_base/shared → shared}/ui/src/base/resizable.tsx +0 -0
  215. /package/templates/{_base/shared → shared}/ui/src/base/scroll_area.tsx +0 -0
  216. /package/templates/{_base/shared → shared}/ui/src/base/select.tsx +0 -0
  217. /package/templates/{_base/shared → shared}/ui/src/base/separator.tsx +0 -0
  218. /package/templates/{_base/shared → shared}/ui/src/base/sheet.tsx +0 -0
  219. /package/templates/{_base/shared → shared}/ui/src/base/side_bar.tsx +0 -0
  220. /package/templates/{_base/shared → shared}/ui/src/base/skeleton.tsx +0 -0
  221. /package/templates/{_base/shared → shared}/ui/src/base/spinner.tsx +0 -0
  222. /package/templates/{_base/shared → shared}/ui/src/base/switch.tsx +0 -0
  223. /package/templates/{_base/shared → shared}/ui/src/base/table.tsx +0 -0
  224. /package/templates/{_base/shared → shared}/ui/src/base/text_area.tsx +0 -0
  225. /package/templates/{_base/shared → shared}/ui/src/base/tooltip.tsx +0 -0
  226. /package/templates/{_base/shared → shared}/ui/src/base/utils.ts +0 -0
  227. /package/templates/{_base/shared → shared}/ui/src/hooks/use_keyboard_press.tsx +0 -0
  228. /package/templates/{_base/shared → shared}/ui/src/hooks/use_keyboard_release.tsx +0 -0
  229. /package/templates/{_base/shared → shared}/ui/src/hooks/use_mouse_click.tsx +0 -0
  230. /package/templates/{_base/shared → shared}/ui/src/hooks/use_mouse_location.tsx +0 -0
  231. /package/templates/{_base/shared → shared}/ui/src/hooks/use_outside_click.tsx +0 -0
  232. /package/templates/{_base/shared → shared}/utils/src/time.ts +0 -0
  233. /package/templates/{_base/shared → shared}/utils/tsconfig.json +0 -0
  234. /package/templates/{_base/skills-lock.json → skills-lock.json} +0 -0
@@ -1,26 +0,0 @@
1
- import { ClerkProvider } from "@clerk/nextjs";
2
- import "./app.css";
3
-
4
- import { Toaster } from "sonner";
5
-
6
- import { ConvexClientProvider } from "@/web/providers/convex_provider";
7
- import { fontCn } from "@/web/utils/font";
8
-
9
- type RootLayoutProps = {
10
- children: React.ReactNode;
11
- };
12
-
13
- export default function RootLayout({ children }: RootLayoutProps) {
14
- return (
15
- <ClerkProvider dynamic>
16
- <ConvexClientProvider>
17
- <html lang="en" className={fontCn}>
18
- <body>
19
- {children}
20
- <Toaster />
21
- </body>
22
- </html>
23
- </ConvexClientProvider>
24
- </ClerkProvider>
25
- );
26
- }
@@ -1,11 +0,0 @@
1
- /*
2
- * Copyright (c) Aron Weston 2025.
3
- */
4
-
5
- import { ConvexClient } from "convex/browser";
6
-
7
- if (!process.env.NEXT_PUBLIC_CONVEX_URL) {
8
- throw new Error("Missing NEXT_PUBLIC_CONVEX_URL in your .env file");
9
- }
10
-
11
- export const convex = new ConvexClient(process.env.NEXT_PUBLIC_CONVEX_URL);
@@ -1,18 +0,0 @@
1
- import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
2
-
3
- const isPublicRoute = createRouteMatcher(["/sign-in(.*)"]);
4
-
5
- export default clerkMiddleware(async (auth, req) => {
6
- if (!isPublicRoute(req)) {
7
- await auth.protect();
8
- }
9
- });
10
-
11
- export const config = {
12
- matcher: [
13
- // Skip Next.js internals and all static files, unless found in search params
14
- "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
15
- // Always run for API routes
16
- "/(api|trpc)(.*)",
17
- ],
18
- };
@@ -1,44 +0,0 @@
1
- /*
2
- * Copyright (c) Aron Weston 2026.
3
- */
4
-
5
- "use client";
6
-
7
- import { useAuth } from "@clerk/nextjs";
8
- import { ConvexQueryClient } from "@convex-dev/react-query";
9
- import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
10
- import { ConvexReactClient } from "convex/react";
11
- import { ConvexProviderWithClerk } from "convex/react-clerk";
12
- import { type ReactNode, useState } from "react";
13
-
14
- if (!process.env.NEXT_PUBLIC_CONVEX_URL) {
15
- throw new Error("Missing NEXT_PUBLIC_CONVEX_URL in your .env file");
16
- }
17
-
18
- export function ConvexClientProvider({ children }: { children: ReactNode }) {
19
- const [{ convex, queryClient }] = useState(() => {
20
- const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL as string);
21
- const convexQueryClient = new ConvexQueryClient(convex);
22
- const queryClient = new QueryClient({
23
- defaultOptions: {
24
- queries: {
25
- queryKeyHashFn: convexQueryClient.hashFn(),
26
- queryFn: convexQueryClient.queryFn(),
27
- },
28
- },
29
- });
30
-
31
- convexQueryClient.connect(queryClient);
32
-
33
- return {
34
- convex,
35
- queryClient,
36
- };
37
- });
38
-
39
- return (
40
- <ConvexProviderWithClerk client={convex} useAuth={useAuth}>
41
- <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
42
- </ConvexProviderWithClerk>
43
- );
44
- }
@@ -1,22 +0,0 @@
1
- "use client";
2
-
3
- import { useUser } from "@clerk/nextjs";
4
- import Link from "next/link";
5
-
6
- import { Button } from "@/ui/base/button";
7
-
8
- export const HomeSurface = () => {
9
- const { user } = useUser();
10
-
11
- return (
12
- <main className="flex flex-1 flex-col items-center justify-center gap-4 p-8">
13
- <h1 className="text-3xl font-bold">
14
- Welcome{user?.firstName ? `, ${user.firstName}` : ""}
15
- </h1>
16
- <p className="text-muted-foreground">Get started by managing your todos.</p>
17
- <Button asChild>
18
- <Link href="/todos">View Todos</Link>
19
- </Button>
20
- </main>
21
- );
22
- };
@@ -1,97 +0,0 @@
1
- /*
2
- * Copyright (c) Aron Weston 2026.
3
- */
4
-
5
- "use client";
6
-
7
- import { useUser } from "@clerk/nextjs";
8
- import { convexQuery, useConvexMutation } from "@convex-dev/react-query";
9
- import { useMutation, useQuery } from "@tanstack/react-query";
10
- import { Trash2 } from "lucide-react";
11
- import Link from "next/link";
12
-
13
- import { api } from "@/api/_generated/api";
14
- import type { Id } from "@/api/_generated/dataModel";
15
- import { Button } from "@/ui/base/button";
16
- import { Checkbox } from "@/ui/base/checkbox";
17
- import { Spinner } from "@/ui/base/spinner";
18
- import { CreateTodoSheet } from "@/web/surfaces/todos/create_todo_sheet";
19
-
20
- export const AllTodosSurface = () => {
21
- const { user } = useUser();
22
-
23
- const { data: todos, isPending } = useQuery(
24
- convexQuery(
25
- api.todos.crud.listTodos,
26
- user?.id ? { userId: user.id } : "skip",
27
- ),
28
- );
29
-
30
- const { mutate: updateTodo } = useMutation({
31
- mutationFn: useConvexMutation(api.todos.crud.updateTodo),
32
- });
33
-
34
- const { mutate: deleteTodo } = useMutation({
35
- mutationFn: useConvexMutation(api.todos.crud.deleteTodo),
36
- });
37
-
38
- return (
39
- <main className="flex flex-1 flex-col overflow-auto p-6">
40
- <div className="flex items-center justify-between mb-6">
41
- <h1 className="text-2xl font-semibold">Todos</h1>
42
- <CreateTodoSheet />
43
- </div>
44
-
45
- {isPending && (
46
- <div className="flex flex-1 items-center justify-center">
47
- <Spinner />
48
- </div>
49
- )}
50
-
51
- {!isPending && todos?.length === 0 && (
52
- <div className="flex flex-1 flex-col items-center justify-center gap-2 text-muted-foreground">
53
- <p>No todos yet.</p>
54
- <CreateTodoSheet />
55
- </div>
56
- )}
57
-
58
- {todos && todos.length > 0 && (
59
- <ul className="flex flex-col gap-2 max-w-2xl">
60
- {todos.map((todo) => (
61
- <li
62
- key={todo._id}
63
- className="flex items-center gap-3 rounded-lg border bg-card px-4 py-3"
64
- >
65
- <Checkbox
66
- checked={todo.isCompleted}
67
- onCheckedChange={(checked) =>
68
- updateTodo({
69
- todoId: todo._id as Id<"todos">,
70
- isCompleted: !!checked,
71
- })
72
- }
73
- />
74
- <Link
75
- href={`/todos/${todo._id}`}
76
- className="flex-1 truncate text-sm hover:underline"
77
- style={{
78
- textDecoration: todo.isCompleted ? "line-through" : undefined,
79
- }}
80
- >
81
- {todo.title}
82
- </Link>
83
- <Button
84
- variant="ghost"
85
- size="icon"
86
- className="size-7 shrink-0 text-muted-foreground hover:text-destructive"
87
- onClick={() => deleteTodo({ todoId: todo._id as Id<"todos"> })}
88
- >
89
- <Trash2 className="size-4" />
90
- </Button>
91
- </li>
92
- ))}
93
- </ul>
94
- )}
95
- </main>
96
- );
97
- };
@@ -1,107 +0,0 @@
1
- "use client";
2
-
3
- import { useConvexMutation } from "@convex-dev/react-query";
4
- import { zodResolver } from "@hookform/resolvers/zod";
5
- import { useMutation } from "@tanstack/react-query";
6
- import { PlusIcon } from "lucide-react";
7
- import { useForm } from "react-hook-form";
8
- import { z } from "zod";
9
-
10
- import { api } from "@/api/_generated/api";
11
- import { Button } from "@/ui/base/button";
12
- import { DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/ui/base/dialog";
13
- import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/ui/base/form";
14
- import { Input } from "@/ui/base/input";
15
- import { Sheet, SheetContent, SheetTrigger } from "@/ui/base/sheet";
16
- import { TextArea } from "@/ui/base/text_area";
17
-
18
- type CreateTodoSchema = z.infer<typeof createTodoSchema>;
19
- const createTodoSchema = z.object({
20
- title: z.string().min(1, "Title is required"),
21
- description: z.string().optional(),
22
- });
23
-
24
- export const CreateTodoSheet = () => {
25
- const form = useForm<CreateTodoSchema>({
26
- resolver: zodResolver(createTodoSchema),
27
- defaultValues: {
28
- title: "",
29
- description: "",
30
- },
31
- });
32
-
33
- const { mutate: createTodo, isPending } = useMutation({
34
- mutationFn: useConvexMutation(api.todos.crud.createTodo),
35
- });
36
-
37
- const handleSubmit = form.handleSubmit(async (values) => {
38
- createTodo(values);
39
- form.reset();
40
- });
41
-
42
- return (
43
- <Sheet>
44
- <SheetTrigger asChild>
45
- <Button>
46
- <PlusIcon className="mr-2 size-4" />
47
- New Todo
48
- </Button>
49
- </SheetTrigger>
50
- <SheetContent className="sm:max-w-[440px]">
51
- <DialogHeader>
52
- <DialogTitle>Create Todo</DialogTitle>
53
- <DialogDescription>Add a new item to your todo list.</DialogDescription>
54
- </DialogHeader>
55
- <Form {...form}>
56
- <form
57
- onSubmit={handleSubmit}
58
- className="flex flex-col gap-4 pt-4"
59
- onKeyDown={(e) => {
60
- if (e.key === "Enter") {
61
- e.preventDefault();
62
- handleSubmit();
63
- }
64
- }}
65
- >
66
- <FormField
67
- control={form.control}
68
- name="title"
69
- render={({ field }) => (
70
- <FormItem>
71
- <FormLabel>Title</FormLabel>
72
- <FormControl>
73
- <Input placeholder="Buy groceries..." {...field} />
74
- </FormControl>
75
- <FormMessage />
76
- </FormItem>
77
- )}
78
- />
79
- <FormField
80
- control={form.control}
81
- name="description"
82
- render={({ field }) => (
83
- <FormItem>
84
- <FormLabel>Description</FormLabel>
85
- <FormControl>
86
- <TextArea
87
- placeholder="Optional details..."
88
- className="resize-none"
89
- rows={3}
90
- {...field}
91
- />
92
- </FormControl>
93
- <FormMessage />
94
- </FormItem>
95
- )}
96
- />
97
- <DialogFooter>
98
- <Button type="submit" disabled={isPending}>
99
- {isPending ? "Creating..." : "Create Todo"}
100
- </Button>
101
- </DialogFooter>
102
- </form>
103
- </Form>
104
- </SheetContent>
105
- </Sheet>
106
- );
107
- };
@@ -1,90 +0,0 @@
1
- /*
2
- * Copyright (c) Aron Weston 2026.
3
- */
4
-
5
- "use client";
6
-
7
- import { convexQuery, useConvexMutation } from "@convex-dev/react-query";
8
- import { useMutation, useQuery } from "@tanstack/react-query";
9
- import { ArrowLeft } from "lucide-react";
10
- import Link from "next/link";
11
-
12
- import { api } from "@/api/_generated/api";
13
- import type { TodoId } from "@/api/todos/types";
14
- import { Badge } from "@/ui/base/badge";
15
- import { Button } from "@/ui/base/button";
16
- import { Checkbox } from "@/ui/base/checkbox";
17
- import { Spinner } from "@/ui/base/spinner";
18
-
19
- type SingleTodoSurfaceProps = {
20
- todoId: TodoId;
21
- };
22
-
23
- export const SingleTodoSurface = ({ todoId }: SingleTodoSurfaceProps) => {
24
- const { data: todo, isPending } = useQuery(convexQuery(api.todos.crud.getTodo, { todoId }));
25
-
26
- const { mutate: updateTodo } = useMutation({
27
- mutationFn: useConvexMutation(api.todos.crud.updateTodo),
28
- });
29
-
30
- if (isPending) {
31
- return (
32
- <main className="flex flex-1 items-center justify-center">
33
- <Spinner />
34
- </main>
35
- );
36
- }
37
-
38
- if (!todo) {
39
- return (
40
- <main className="flex flex-1 flex-col items-center justify-center gap-4">
41
- <p className="text-muted-foreground">Todo not found.</p>
42
- <Button variant="outline" asChild>
43
- <Link href="/todos">
44
- <ArrowLeft className="mr-2 size-4" />
45
- Back to todos
46
- </Link>
47
- </Button>
48
- </main>
49
- );
50
- }
51
-
52
- return (
53
- <main className="flex flex-1 flex-col gap-6 overflow-auto p-6 max-w-2xl">
54
- <Button variant="ghost" className="w-fit -ml-2 text-muted-foreground" asChild>
55
- <Link href="/todos">
56
- <ArrowLeft className="mr-2 size-4" />
57
- Back
58
- </Link>
59
- </Button>
60
-
61
- <div className="flex items-start gap-3">
62
- <Checkbox
63
- className="mt-1"
64
- checked={todo.isCompleted}
65
- onCheckedChange={(checked) =>
66
- updateTodo({
67
- todoId,
68
- isCompleted: !!checked,
69
- })
70
- }
71
- />
72
- <div className="flex flex-col gap-2">
73
- <h1
74
- className="text-2xl font-semibold"
75
- style={{
76
- textDecoration: todo.isCompleted ? "line-through" : undefined,
77
- }}
78
- >
79
- {todo.title}
80
- </h1>
81
- <Badge variant={todo.isCompleted ? "secondary" : "default"}>
82
- {todo.isCompleted ? "Completed" : "In progress"}
83
- </Badge>
84
- </div>
85
- </div>
86
-
87
- {todo.description && <p className="text-muted-foreground">{todo.description}</p>}
88
- </main>
89
- );
90
- };
@@ -1,36 +0,0 @@
1
- /*
2
- * Copyright (c) Aron Weston 2026.
3
- */
4
-
5
- "use client";
6
-
7
- import type { LucideIcon } from "lucide-react";
8
- import Link from "next/link";
9
- import { usePathname } from "next/navigation";
10
-
11
- import { SidebarMenuButton, SidebarMenuItem } from "@/ui/base/side_bar";
12
-
13
- type NavLinkProps = {
14
- item: {
15
- id: string;
16
- title: string;
17
- url: string;
18
- icon: LucideIcon;
19
- };
20
- };
21
-
22
- export const NavLink = ({ item }: NavLinkProps) => {
23
- const pathname = usePathname();
24
- const isActive = pathname === item.url;
25
-
26
- return (
27
- <SidebarMenuItem>
28
- <SidebarMenuButton asChild isActive={isActive} tooltip={item.title}>
29
- <Link href={item.url}>
30
- <item.icon />
31
- <span>{item.title}</span>
32
- </Link>
33
- </SidebarMenuButton>
34
- </SidebarMenuItem>
35
- );
36
- };
@@ -1,125 +0,0 @@
1
- /*
2
- * Copyright (c) Aron Weston 2026.
3
- */
4
-
5
- "use client";
6
-
7
- import { UserButton, useUser } from "@clerk/nextjs";
8
- import {
9
- CheckSquareIcon,
10
- HomeIcon,
11
- type LucideIcon,
12
- SettingsIcon,
13
- } from "lucide-react";
14
- import Link from "next/link";
15
-
16
- import {
17
- Sidebar as BaseSidebar,
18
- SidebarHeader as BaseSidebarHeader,
19
- SidebarContent,
20
- SidebarFooter,
21
- SidebarGroup,
22
- SidebarGroupContent,
23
- SidebarMenu,
24
- SidebarMenuItem,
25
- } from "@/ui/base/side_bar";
26
- import { Skeleton } from "@/ui/base/skeleton";
27
- import { NavLink } from "@/web/ui/sidebar/nav_link";
28
-
29
- export type NavItem = {
30
- id: string;
31
- title: string;
32
- url: string;
33
- icon: LucideIcon;
34
- };
35
-
36
- export const nav: {
37
- main: NavItem[];
38
- secondary: NavItem[];
39
- } = {
40
- main: [
41
- {
42
- id: "dashboard",
43
- title: "Dashboard",
44
- url: "/",
45
- icon: HomeIcon,
46
- },
47
- {
48
- id: "todos",
49
- title: "Todos",
50
- url: "/todos",
51
- icon: CheckSquareIcon,
52
- },
53
- ],
54
- secondary: [
55
- {
56
- id: "settings",
57
- title: "Settings",
58
- url: "/settings",
59
- icon: SettingsIcon,
60
- },
61
- ],
62
- };
63
-
64
- export const Sidebar = ({
65
- ...props
66
- }: React.ComponentProps<typeof BaseSidebar>) => {
67
- const user = useUser();
68
-
69
- return (
70
- <BaseSidebar collapsible="offcanvas" {...props}>
71
- <BaseSidebarHeader>
72
- <SidebarMenu>
73
- <SidebarMenuItem>
74
- <Link href="/" className="flex flex-row items-center gap-3 w-full">
75
- <span className="truncate font-medium">App</span>
76
- </Link>
77
- </SidebarMenuItem>
78
- </SidebarMenu>
79
- </BaseSidebarHeader>
80
- <SidebarContent>
81
- <SidebarGroup className="pt-0">
82
- <SidebarGroupContent className="flex flex-col gap-2">
83
- <SidebarMenu>
84
- {nav.main.map((item) => (
85
- <NavLink key={item.id} item={item} />
86
- ))}
87
- </SidebarMenu>
88
- </SidebarGroupContent>
89
- </SidebarGroup>
90
- </SidebarContent>
91
- <SidebarFooter className="gap-0 p-2">
92
- <SidebarGroup>
93
- <SidebarGroupContent>
94
- <SidebarMenu>
95
- {nav.secondary.map((item) => (
96
- <NavLink key={item.id} item={item} />
97
- ))}
98
- </SidebarMenu>
99
- </SidebarGroupContent>
100
- </SidebarGroup>
101
- <SidebarMenu>
102
- {user.isLoaded ? (
103
- <SidebarMenuItem className="w-full">
104
- <div className="flex items-start gap-2 border rounded-md shadow-sm px-2 py-2 w-full">
105
- <UserButton />
106
- <div className="flex flex-col gap-0 text-left leading-tight w-fit">
107
- <span className="text-sm font-medium break-words truncate">
108
- {user.user?.fullName}
109
- </span>
110
- <span className="truncate text-xs font-light">
111
- {user.user?.emailAddresses[0].emailAddress}
112
- </span>
113
- </div>
114
- </div>
115
- </SidebarMenuItem>
116
- ) : (
117
- <SidebarMenuItem className="w-full">
118
- <Skeleton className="h-12 w-full rounded-lg border" />
119
- </SidebarMenuItem>
120
- )}
121
- </SidebarMenu>
122
- </SidebarFooter>
123
- </BaseSidebar>
124
- );
125
- };
@@ -1,9 +0,0 @@
1
- import { Inter } from "next/font/google";
2
-
3
- const inter = Inter({
4
- variable: "--font-inter",
5
- subsets: ["latin"],
6
- display: "swap",
7
- });
8
-
9
- export const fontCn = `${inter.className}`;
@@ -1,42 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "jsx": "preserve",
5
- "lib": ["dom", "dom.iterable", "esnext"],
6
- "noEmit": true,
7
- "emitDeclarationOnly": false,
8
- "esModuleInterop": true,
9
- "resolveJsonModule": true,
10
- "allowJs": true,
11
- "allowSyntheticDefaultImports": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "incremental": true,
14
- "plugins": [{"name": "next"}],
15
- "outDir": "dist",
16
- "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo"
17
- },
18
- "include": [
19
- ".next/types/**/*.ts",
20
- "index.d.ts",
21
- "next-env.d.ts",
22
- "next-env.d.ts",
23
- "src/**/*.js",
24
- "src/**/*.jsx",
25
- "src/**/*.ts",
26
- "src/**/*.tsx",
27
- "../../dist/apps/web/.next/types/**/*.ts",
28
- "../../apps/web/dist/.next/types/**/*.ts"
29
- ],
30
- "exclude": [
31
- "out-tsc",
32
- "dist",
33
- "node_modules",
34
- "jest.config.ts",
35
- "src/**/*.spec.ts",
36
- "src/**/*.test.ts",
37
- ".next",
38
- "eslint.config.js",
39
- "eslint.config.cjs",
40
- "eslint.config.mjs"
41
- ]
42
- }
@@ -1,8 +0,0 @@
1
- # Convex
2
- CONVEX_DEPLOYMENT=
3
- CONVEX_DEPLOYMENT_URL=
4
- VITE_CONVEX_URL=
5
-
6
- # Authentication
7
- CLERK_SECRET_KEY=
8
- VITE_CLERK_PUBLISHABLE_KEY=
@@ -1,3 +0,0 @@
1
- @import "../../../../shared/assets/src/styles/global.css";
2
- @source "../../../../shared/ui/src/**/*.{js,ts,jsx,tsx}";
3
- @source "../../../../apps/web/src/**/*.{js,ts,jsx,tsx}";