diff --git a/apps/web/package.json b/apps/web/package.json index 066f7476..f397fc32 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -28,7 +28,12 @@ "@radix-ui/react-switch": "^1.0.3", "@react-email/components": "^0.0.21", "@t3-oss/env-nextjs": "^0.10.1", + "@tanstack/react-query": "^5.51.11", "@tanstack/react-table": "^8.19.3", + "@trpc/client": "11.0.0-rc.466", + "@trpc/next": "11.0.0-rc.466", + "@trpc/react-query": "11.0.0-rc.466", + "@trpc/server": "11.0.0-rc.466", "@types/node": "20.14.11", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", @@ -62,6 +67,7 @@ "pg": "^8.12.0", "postcss": "8.4.39", "postgres": "^3.4.4", + "pusher-js": "8.4.0-rc2", "react": "18.3.1", "react-aria": "^3.33.1", "react-bootstrap": "^2.10.4", diff --git a/apps/web/src/app/api/trpc/[trpc]/route.ts b/apps/web/src/app/api/trpc/[trpc]/route.ts new file mode 100644 index 00000000..b30be48a --- /dev/null +++ b/apps/web/src/app/api/trpc/[trpc]/route.ts @@ -0,0 +1,34 @@ +import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; +import { type NextRequest } from "next/server"; + +import { env } from "@/env"; +import { appRouter } from "@/server/api/root"; +import { createTRPCContext } from "@/server/api/trpc"; + +/** + * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when + * handling a HTTP request (e.g. when you make requests from Client Components). + */ +const createContext = async (req: NextRequest) => { + return createTRPCContext({ + headers: req.headers, + }); +}; + +const handler = (req: NextRequest) => + fetchRequestHandler({ + endpoint: "/api/trpc", + req, + router: appRouter, + createContext: () => createContext(req), + onError: + env.NODE_ENV === "development" + ? ({ path, error }) => { + console.error( + `❌ tRPC failed on ${path ?? ""}: ${error.message}`, + ); + } + : undefined, + }); + +export { handler as GET, handler as POST }; diff --git a/apps/web/src/app/dash/layout.tsx b/apps/web/src/app/dash/layout.tsx index 6bbe6634..f5d1ece7 100644 --- a/apps/web/src/app/dash/layout.tsx +++ b/apps/web/src/app/dash/layout.tsx @@ -9,6 +9,8 @@ import { redirect } from "next/navigation"; import ProfileButton from "@/components/shared/ProfileButton"; import ClientToast from "@/components/shared/ClientToast"; +import { TRPCReactProvider } from "@/trpc/react"; + interface DashLayoutProps { children: React.ReactNode; } @@ -22,53 +24,55 @@ export default async function DashLayout({ children }: DashLayoutProps) { return ( <> - -
-
- {c.hackathonName -
-

Dashboard

+ + +
+
+ {c.hackathonName +
+

Dashboard

+
+
+ + + + + + + + + + +
+
-
- - - - - - - - - - +
+ {Object.entries(c.dashPaths.dash).map(([name, path]) => ( + + ))}
-
-
-
- {Object.entries(c.dashPaths.dash).map(([name, path]) => ( - - ))} -
- {children} + {children} + ); } diff --git a/apps/web/src/app/dash/tickets/layout.tsx b/apps/web/src/app/dash/tickets/layout.tsx new file mode 100644 index 00000000..57fa0115 --- /dev/null +++ b/apps/web/src/app/dash/tickets/layout.tsx @@ -0,0 +1,14 @@ +import TicketList from "@/components/dash/tickets/TicketList"; + +export default function TicketsLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+ +
{children}
+
+ ); +} diff --git a/apps/web/src/app/dash/tickets/new/page.tsx b/apps/web/src/app/dash/tickets/new/page.tsx new file mode 100644 index 00000000..82e0ddff --- /dev/null +++ b/apps/web/src/app/dash/tickets/new/page.tsx @@ -0,0 +1,60 @@ +"use client"; +import { useState } from "react"; +import { Input } from "@/components/shadcn/ui/input"; +import { Label } from "@/components/shadcn/ui/label"; +import { Textarea } from "@/components/shadcn/ui/textarea"; +import { Button } from "@/components/shadcn/ui/button"; +import { api } from "@/trpc/react"; +import { toast } from "sonner"; + +export default function Page() { + const [title, setTitle] = useState(""); + const [description, setDescription] = useState(""); + + const createTicket = api.tickets.create.useMutation(); + + async function runCreateTicket() { + if (title.length > 3 && description.length > 10) { + const result = await createTicket.mutateAsync({ + title, + description, + }); + if (result.success) { + toast.success("Ticket created successfully!"); + console.log( + "created ticket with ID " + + result.ticketID + + " and chat with ID " + + result.chatID, + ); + } + } else { + toast.error( + "Your title or description is too short! Please try again.", + ); + } + } + + return ( +
+
+

New Ticket

+
+
+ + setTitle(e.target.value)} /> +
+
+ +