diff --git a/apps/cli/template/base/packages/client/package.json b/apps/cli/template/base/packages/client/package.json
index ee38a20..38b4718 100644
--- a/apps/cli/template/base/packages/client/package.json
+++ b/apps/cli/template/base/packages/client/package.json
@@ -24,6 +24,7 @@
},
"dependencies": {
"@hookform/resolvers": "^3.10.0",
+ "@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-slot": "^1.1.2",
diff --git a/apps/cli/template/base/packages/client/src/components/header.tsx b/apps/cli/template/base/packages/client/src/components/header.tsx
index f370e69..894fbbd 100644
--- a/apps/cli/template/base/packages/client/src/components/header.tsx
+++ b/apps/cli/template/base/packages/client/src/components/header.tsx
@@ -15,6 +15,15 @@ export default function Header() {
>
Home
+
+ Todos
+
diff --git a/apps/cli/template/base/packages/client/src/components/ui/card.tsx b/apps/cli/template/base/packages/client/src/components/ui/card.tsx
new file mode 100644
index 0000000..d05bbc6
--- /dev/null
+++ b/apps/cli/template/base/packages/client/src/components/ui/card.tsx
@@ -0,0 +1,92 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Card({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+}
diff --git a/apps/cli/template/base/packages/client/src/components/ui/checkbox.tsx b/apps/cli/template/base/packages/client/src/components/ui/checkbox.tsx
new file mode 100644
index 0000000..defeb01
--- /dev/null
+++ b/apps/cli/template/base/packages/client/src/components/ui/checkbox.tsx
@@ -0,0 +1,30 @@
+import * as React from "react"
+import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
+import { CheckIcon } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+function Checkbox({
+ className,
+ ...props
+}: React.ComponentProps
) {
+ return (
+
+
+
+
+
+ )
+}
+
+export { Checkbox }
diff --git a/apps/cli/template/base/packages/client/src/routes/todos.tsx b/apps/cli/template/base/packages/client/src/routes/todos.tsx
new file mode 100644
index 0000000..3fe08d3
--- /dev/null
+++ b/apps/cli/template/base/packages/client/src/routes/todos.tsx
@@ -0,0 +1,128 @@
+import { Button } from "@/components/ui/button";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import { Checkbox } from "@/components/ui/checkbox";
+import { Input } from "@/components/ui/input";
+import { trpc } from "@/utils/trpc";
+import { createFileRoute } from "@tanstack/react-router";
+import { Loader2, Trash2 } from "lucide-react";
+import { useState } from "react";
+
+export const Route = createFileRoute("/todos")({
+ component: TodosRoute,
+});
+
+function TodosRoute() {
+ const [newTodoText, setNewTodoText] = useState("");
+
+ const todos = trpc.todo.getAll.useQuery();
+ const createMutation = trpc.todo.create.useMutation({
+ onSuccess: () => {
+ todos.refetch();
+ setNewTodoText("");
+ },
+ });
+ const toggleMutation = trpc.todo.toggle.useMutation({
+ onSuccess: () => todos.refetch(),
+ });
+ const deleteMutation = trpc.todo.delete.useMutation({
+ onSuccess: () => todos.refetch(),
+ });
+
+ const handleAddTodo = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (newTodoText.trim()) {
+ createMutation.mutate({ text: newTodoText });
+ }
+ };
+
+ const handleToggleTodo = (id: number, completed: boolean) => {
+ toggleMutation.mutate({ id, completed: !completed });
+ };
+
+ const handleDeleteTodo = (id: number) => {
+ deleteMutation.mutate({ id });
+ };
+
+ return (
+
+
+
+ Todo List
+ Manage your tasks efficiently
+
+
+
+
+ {todos.isLoading ? (
+
+
+
+ ) : todos.data?.length === 0 ? (
+
+ No todos yet. Add one above!
+
+ ) : (
+
+ )}
+
+
+
+ );
+}
diff --git a/apps/cli/template/base/packages/server/src/routers/index.ts b/apps/cli/template/base/packages/server/src/routers/index.ts
index b62cd06..91b444d 100644
--- a/apps/cli/template/base/packages/server/src/routers/index.ts
+++ b/apps/cli/template/base/packages/server/src/routers/index.ts
@@ -1,9 +1,11 @@
import { router, publicProcedure } from "../lib/trpc";
+import { todoRouter } from "./todo";
export const appRouter = router({
healthCheck: publicProcedure.query(() => {
return "OK";
}),
+ todo: todoRouter,
});
export type AppRouter = typeof appRouter;
diff --git a/apps/cli/template/with-auth/packages/client/src/components/header.tsx b/apps/cli/template/with-auth/packages/client/src/components/header.tsx
index 184a111..30b36b9 100644
--- a/apps/cli/template/with-auth/packages/client/src/components/header.tsx
+++ b/apps/cli/template/with-auth/packages/client/src/components/header.tsx
@@ -16,6 +16,15 @@ export default function Header() {
>
Home
+
+ Todos
+
{
@@ -11,6 +13,7 @@ export const appRouter = router({
user: ctx.session.user,
};
}),
+ todo: todoRouter,
});
export type AppRouter = typeof appRouter;
diff --git a/apps/cli/template/with-drizzle-postgres/packages/server/src/routers/todo.ts b/apps/cli/template/with-drizzle-postgres/packages/server/src/routers/todo.ts
new file mode 100644
index 0000000..e2b1896
--- /dev/null
+++ b/apps/cli/template/with-drizzle-postgres/packages/server/src/routers/todo.ts
@@ -0,0 +1,44 @@
+import { z } from "zod";
+import { router, publicProcedure } from "../lib/trpc";
+import { todo } from "../db/schema";
+import { eq } from "drizzle-orm";
+import { db } from "../db";
+
+export const todoRouter = router({
+ getAll: publicProcedure.query(async () => {
+ return await db.select().from(todo).all();
+ }),
+
+ create: publicProcedure
+ .input(z.object({ text: z.string().min(1) }))
+ .mutation(async ({ input }) => {
+ return await db
+ .insert(todo)
+ .values({
+ text: input.text,
+ })
+ .returning()
+ .get();
+ }),
+
+ toggle: publicProcedure
+ .input(z.object({ id: z.number(), completed: z.boolean() }))
+ .mutation(async ({ input }) => {
+ return await db
+ .update(todo)
+ .set({ completed: input.completed })
+ .where(eq(todo.id, input.id))
+ .returning()
+ .get();
+ }),
+
+ delete: publicProcedure
+ .input(z.object({ id: z.number() }))
+ .mutation(async ({ input }) => {
+ return await db
+ .delete(todo)
+ .where(eq(todo.id, input.id))
+ .returning()
+ .get();
+ }),
+});
diff --git a/apps/cli/template/with-drizzle-sqlite/packages/server/src/routers/todo.ts b/apps/cli/template/with-drizzle-sqlite/packages/server/src/routers/todo.ts
new file mode 100644
index 0000000..e2b1896
--- /dev/null
+++ b/apps/cli/template/with-drizzle-sqlite/packages/server/src/routers/todo.ts
@@ -0,0 +1,44 @@
+import { z } from "zod";
+import { router, publicProcedure } from "../lib/trpc";
+import { todo } from "../db/schema";
+import { eq } from "drizzle-orm";
+import { db } from "../db";
+
+export const todoRouter = router({
+ getAll: publicProcedure.query(async () => {
+ return await db.select().from(todo).all();
+ }),
+
+ create: publicProcedure
+ .input(z.object({ text: z.string().min(1) }))
+ .mutation(async ({ input }) => {
+ return await db
+ .insert(todo)
+ .values({
+ text: input.text,
+ })
+ .returning()
+ .get();
+ }),
+
+ toggle: publicProcedure
+ .input(z.object({ id: z.number(), completed: z.boolean() }))
+ .mutation(async ({ input }) => {
+ return await db
+ .update(todo)
+ .set({ completed: input.completed })
+ .where(eq(todo.id, input.id))
+ .returning()
+ .get();
+ }),
+
+ delete: publicProcedure
+ .input(z.object({ id: z.number() }))
+ .mutation(async ({ input }) => {
+ return await db
+ .delete(todo)
+ .where(eq(todo.id, input.id))
+ .returning()
+ .get();
+ }),
+});
diff --git a/apps/cli/template/with-prisma-postgres/packages/server/prisma/schema.prisma b/apps/cli/template/with-prisma-postgres/packages/server/prisma/schema.prisma
index 6612705..c1210cb 100644
--- a/apps/cli/template/with-prisma-postgres/packages/server/prisma/schema.prisma
+++ b/apps/cli/template/with-prisma-postgres/packages/server/prisma/schema.prisma
@@ -14,63 +14,3 @@ model Todo {
@@map("todo")
}
-
-model User {
- id String @id @map("_id")
- name String
- email String
- emailVerified Boolean
- image String?
- createdAt DateTime
- updatedAt DateTime
- sessions Session[]
- accounts Account[]
-
- @@unique([email])
- @@map("user")
-}
-
-model Session {
- id String @id @map("_id")
- expiresAt DateTime
- token String
- createdAt DateTime
- updatedAt DateTime
- ipAddress String?
- userAgent String?
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
-
- @@unique([token])
- @@map("session")
-}
-
-model Account {
- id String @id @map("_id")
- accountId String
- providerId String
- userId String
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
- accessToken String?
- refreshToken String?
- idToken String?
- accessTokenExpiresAt DateTime?
- refreshTokenExpiresAt DateTime?
- scope String?
- password String?
- createdAt DateTime
- updatedAt DateTime
-
- @@map("account")
-}
-
-model Verification {
- id String @id @map("_id")
- identifier String
- value String
- expiresAt DateTime
- createdAt DateTime?
- updatedAt DateTime?
-
- @@map("verification")
-}
diff --git a/apps/cli/template/with-prisma-postgres/packages/server/src/routers/todo.ts b/apps/cli/template/with-prisma-postgres/packages/server/src/routers/todo.ts
new file mode 100644
index 0000000..5f2d3f9
--- /dev/null
+++ b/apps/cli/template/with-prisma-postgres/packages/server/src/routers/todo.ts
@@ -0,0 +1,55 @@
+import { TRPCError } from "@trpc/server";
+import { z } from "zod";
+import prisma from "../../prisma";
+import { publicProcedure, router } from "../lib/trpc";
+
+export const todoRouter = router({
+ getAll: publicProcedure.query(async () => {
+ return await prisma.todo.findMany({
+ orderBy: {
+ id: "asc"
+ }
+ });
+ }),
+
+ create: publicProcedure
+ .input(z.object({ text: z.string().min(1) }))
+ .mutation(async ({ input }) => {
+ return await prisma.todo.create({
+ data: {
+ text: input.text,
+ },
+ });
+ }),
+
+ toggle: publicProcedure
+ .input(z.object({ id: z.number(), completed: z.boolean() }))
+ .mutation(async ({ input }) => {
+ try {
+ return await prisma.todo.update({
+ where: { id: input.id },
+ data: { completed: input.completed },
+ });
+ } catch (error) {
+ throw new TRPCError({
+ code: "NOT_FOUND",
+ message: "Todo not found",
+ });
+ }
+ }),
+
+ delete: publicProcedure
+ .input(z.object({ id: z.number() }))
+ .mutation(async ({ input }) => {
+ try {
+ return await prisma.todo.delete({
+ where: { id: input.id },
+ });
+ } catch (error) {
+ throw new TRPCError({
+ code: "NOT_FOUND",
+ message: "Todo not found",
+ });
+ }
+ }),
+});
diff --git a/apps/cli/template/with-prisma-sqlite/packages/server/src/routers/todo.ts b/apps/cli/template/with-prisma-sqlite/packages/server/src/routers/todo.ts
new file mode 100644
index 0000000..5f2d3f9
--- /dev/null
+++ b/apps/cli/template/with-prisma-sqlite/packages/server/src/routers/todo.ts
@@ -0,0 +1,55 @@
+import { TRPCError } from "@trpc/server";
+import { z } from "zod";
+import prisma from "../../prisma";
+import { publicProcedure, router } from "../lib/trpc";
+
+export const todoRouter = router({
+ getAll: publicProcedure.query(async () => {
+ return await prisma.todo.findMany({
+ orderBy: {
+ id: "asc"
+ }
+ });
+ }),
+
+ create: publicProcedure
+ .input(z.object({ text: z.string().min(1) }))
+ .mutation(async ({ input }) => {
+ return await prisma.todo.create({
+ data: {
+ text: input.text,
+ },
+ });
+ }),
+
+ toggle: publicProcedure
+ .input(z.object({ id: z.number(), completed: z.boolean() }))
+ .mutation(async ({ input }) => {
+ try {
+ return await prisma.todo.update({
+ where: { id: input.id },
+ data: { completed: input.completed },
+ });
+ } catch (error) {
+ throw new TRPCError({
+ code: "NOT_FOUND",
+ message: "Todo not found",
+ });
+ }
+ }),
+
+ delete: publicProcedure
+ .input(z.object({ id: z.number() }))
+ .mutation(async ({ input }) => {
+ try {
+ return await prisma.todo.delete({
+ where: { id: input.id },
+ });
+ } catch (error) {
+ throw new TRPCError({
+ code: "NOT_FOUND",
+ message: "Todo not found",
+ });
+ }
+ }),
+});