-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
295 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
packages/backend/prisma/migrations/20211202163102_todos/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
-- CreateEnum | ||
CREATE TYPE "TodoStatus" AS ENUM ('PENDING', 'FINISHED'); | ||
|
||
-- CreateTable | ||
CREATE TABLE "Todo" ( | ||
"id" SERIAL NOT NULL, | ||
"text" TEXT NOT NULL, | ||
"status" "TodoStatus" NOT NULL, | ||
"userId" INTEGER NOT NULL, | ||
|
||
CONSTRAINT "Todo_pkey" PRIMARY KEY ("id") | ||
); | ||
|
||
-- AddForeignKey | ||
ALTER TABLE "Todo" ADD CONSTRAINT "Todo_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { Context, protectedRoute } from "../Context"; | ||
import * as trpc from "@trpc/server"; | ||
import * as Yup from "yup"; | ||
import db from "../db"; | ||
|
||
const TodosController = trpc.router<Context>().merge( | ||
"", | ||
protectedRoute | ||
.query("own", { | ||
resolve: async ({ ctx }) => { | ||
const todos = db.todo.findMany({ | ||
where: { | ||
userId: ctx.user?.id!, | ||
}, | ||
}); | ||
return todos; | ||
}, | ||
}) | ||
.mutation("complete", { | ||
input: Yup.object({ | ||
todoId: Yup.number(), | ||
}), | ||
resolve: async ({ ctx, input }) => { | ||
return await db.todo.update({ | ||
where: { | ||
id: input.todoId, | ||
}, | ||
data: { | ||
status: "FINISHED", | ||
}, | ||
}); | ||
}, | ||
}) | ||
.mutation("create", { | ||
input: Yup.object({ | ||
text: Yup.string().required(), | ||
}), | ||
resolve: async ({ ctx, input }) => { | ||
return await db.todo.create({ | ||
data: { | ||
status: "PENDING", | ||
text: input.text, | ||
userId: ctx.user!.id, | ||
}, | ||
}); | ||
}, | ||
}) | ||
.mutation("delete", { | ||
input: Yup.object({ | ||
todoId: Yup.number().required(), | ||
}), | ||
resolve: async ({ ctx, input }) => { | ||
await db.todo.delete({ | ||
where: { | ||
id: input.todoId, | ||
}, | ||
}); | ||
}, | ||
}) | ||
); | ||
|
||
export default TodosController; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { Formik } from "formik"; | ||
import { useQueryClient } from "react-query"; | ||
|
||
import { trpc } from "../api/APIProvider"; | ||
import { useErrorNotificationToast } from "../hooks/useErrorNotificationToast"; | ||
|
||
const AddTodoForm = () => { | ||
const queryClient = useQueryClient(); | ||
|
||
const createTodoMutation = trpc.useMutation("todos/create", { | ||
onSuccess: () => { | ||
queryClient.invalidateQueries(["todos/own"], { exact: false }); | ||
}, | ||
}); | ||
useErrorNotificationToast(createTodoMutation.error?.message); | ||
|
||
return ( | ||
<Formik | ||
initialValues={{ text: "" }} | ||
onSubmit={async (values, { resetForm }) => { | ||
try { | ||
await createTodoMutation.mutateAsync(values); | ||
resetForm(); | ||
} catch (err) {} | ||
}} | ||
> | ||
{({ | ||
values, | ||
errors, | ||
touched, | ||
handleChange, | ||
handleBlur, | ||
handleSubmit, | ||
}) => ( | ||
<form onSubmit={handleSubmit}> | ||
<div className=""> | ||
<input | ||
type="text" | ||
name="text" | ||
onChange={handleChange} | ||
onBlur={handleBlur} | ||
value={values.text} | ||
placeholder="Enter to submit" | ||
className="block text-gray-700 text-sm font-bold mb-2 py-2 border px-4 bg-gray-50 w-full" | ||
/> | ||
{touched.text && errors.text} | ||
</div> | ||
</form> | ||
)} | ||
</Formik> | ||
); | ||
}; | ||
export default AddTodoForm; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { useRouter } from "next/dist/client/router"; | ||
import { useEffect } from "react"; | ||
import { useAuth } from "../contexts/auth"; | ||
|
||
const useRequiresAuth = () => { | ||
const { user, isAuthenticated } = useAuth(); | ||
const router = useRouter(); | ||
useEffect(() => { | ||
if (!isAuthenticated) { | ||
router.push("/login"); | ||
} | ||
}, [isAuthenticated]); | ||
}; | ||
|
||
export default useRequiresAuth; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { useQueryClient } from "react-query"; | ||
|
||
import { trpc } from "../api/APIProvider"; | ||
import AddTodoForm from "../components/AddTodoForm"; | ||
import { useAuth } from "../contexts/auth"; | ||
import { useErrorNotificationToast } from "../hooks/useErrorNotificationToast"; | ||
import useRequiresAuth from "../hooks/useRequiresAuth"; | ||
import MainLayout, { Card } from "../layouts/MainLayout"; | ||
|
||
import type { NextPage } from "next"; | ||
const Home: NextPage = () => { | ||
const { user, isAuthenticated } = useAuth(); | ||
const { data: ownTodos, refetch } = trpc.useQuery(["todos/own"], { | ||
enabled: isAuthenticated, | ||
}); | ||
|
||
useRequiresAuth(); | ||
|
||
const queryClient = useQueryClient(); | ||
const completeTodoMutation = trpc.useMutation("todos/complete", { | ||
onSuccess: () => { | ||
queryClient.invalidateQueries(["todos/own"], { exact: false }); | ||
}, | ||
}); | ||
useErrorNotificationToast(completeTodoMutation.error?.message); | ||
|
||
const deleteTodoMutation = trpc.useMutation("todos/delete", { | ||
onSuccess: () => { | ||
queryClient.invalidateQueries(["todos/own"], { exact: false }); | ||
}, | ||
}); | ||
useErrorNotificationToast(deleteTodoMutation.error?.message); | ||
|
||
return ( | ||
<MainLayout> | ||
<Card> | ||
<> | ||
<h1 className="font-black text-3xl mb-10">My Todos</h1> | ||
<div className="max-w-xl"> | ||
{ownTodos?.map((todo) => ( | ||
<div className="flex justify-between mb-5"> | ||
<p | ||
key={todo.id} | ||
className={ | ||
todo.status === "FINISHED" | ||
? "line-through text-green-400" | ||
: "" | ||
} | ||
> | ||
{todo.text} | ||
</p> | ||
<div className="flex gap-4"> | ||
<button | ||
onClick={() => | ||
completeTodoMutation.mutateAsync({ todoId: todo.id }) | ||
} | ||
> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
className="h-6 w-6" | ||
fill="none" | ||
viewBox="0 0 24 24" | ||
stroke="currentColor" | ||
> | ||
<path | ||
strokeLinecap="round" | ||
strokeLinejoin="round" | ||
strokeWidth={2} | ||
d="M5 13l4 4L19 7" | ||
/> | ||
</svg> | ||
</button> | ||
<button | ||
onClick={() => | ||
deleteTodoMutation.mutateAsync({ todoId: todo.id }) | ||
} | ||
> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
className="h-6 w-6" | ||
fill="none" | ||
viewBox="0 0 24 24" | ||
stroke="currentColor" | ||
> | ||
<path | ||
strokeLinecap="round" | ||
strokeLinejoin="round" | ||
strokeWidth={2} | ||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" | ||
/> | ||
</svg> | ||
</button> | ||
</div> | ||
</div> | ||
))} | ||
</div> | ||
|
||
<div className="max-w-sm"> | ||
<AddTodoForm /> | ||
</div> | ||
</> | ||
</Card> | ||
</MainLayout> | ||
); | ||
}; | ||
|
||
export default Home; |