From 96bcc2afc7c248950f159356d105859026fb8832 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 18 Mar 2024 22:41:19 +0000 Subject: [PATCH] feat: create workspace --- src/app/boards/[...boardId]/page.tsx | 2 + src/app/boards/page.tsx | 2 + src/app/cards/[cardId]/page.tsx | 2 + src/app/components/NewWorkspaceForm.tsx | 79 +++++++++++++++++++++++++ src/app/components/SideNavigation.tsx | 41 +++++++------ src/app/components/WorkspaceMenu.tsx | 65 +++++++++++++------- src/app/members/page.tsx | 2 + src/app/providers/workspace.tsx | 2 - src/server/api/routers/board.ts | 2 - src/server/api/routers/card.ts | 2 - src/server/api/routers/workspace.ts | 36 +++++++++++ 11 files changed, 190 insertions(+), 45 deletions(-) create mode 100644 src/app/components/NewWorkspaceForm.tsx diff --git a/src/app/boards/[...boardId]/page.tsx b/src/app/boards/[...boardId]/page.tsx index 0740ba22..f27d7a1a 100644 --- a/src/app/boards/[...boardId]/page.tsx +++ b/src/app/boards/[...boardId]/page.tsx @@ -22,6 +22,7 @@ import BoardDropdown from "./components/BoardDropdown"; import { DeleteBoardConfirmation } from "./components/DeleteBoardConfirmation"; import { DeleteListConfirmation } from "./components/DeleteListConfirmation"; import List from "./components/List"; +import { NewWorkspaceForm } from "~/app/components/NewWorkspaceForm"; import { NewCardForm } from "./components/NewCardForm"; import { NewListForm } from "./components/NewListForm"; @@ -304,6 +305,7 @@ export default function BoardPage() { {modalContentType === "NEW_LIST" && ( )} + {modalContentType === "NEW_WORKSPACE" && } ); diff --git a/src/app/boards/page.tsx b/src/app/boards/page.tsx index 5469e7d0..0e213796 100644 --- a/src/app/boards/page.tsx +++ b/src/app/boards/page.tsx @@ -8,6 +8,7 @@ import Modal from "~/app/components/modal"; import { ImportBoardsForm } from "./components/ImportBoardsForm"; import { NewBoardForm } from "./components/NewBoardForm"; +import { NewWorkspaceForm } from "~/app/components/NewWorkspaceForm"; export default function BoardsPage() { const { openModal, modalContentType } = useModal(); @@ -48,6 +49,7 @@ export default function BoardsPage() { {modalContentType === "NEW_BOARD" && } {modalContentType === "IMPORT_BOARDS" && } + {modalContentType === "NEW_WORKSPACE" && }
diff --git a/src/app/cards/[cardId]/page.tsx b/src/app/cards/[cardId]/page.tsx index 7d0b2205..4ad64b3e 100644 --- a/src/app/cards/[cardId]/page.tsx +++ b/src/app/cards/[cardId]/page.tsx @@ -12,6 +12,7 @@ import LabelSelector from "./components/LabelSelector"; import ListSelector from "./components/ListSelector"; import MemberSelector from "./components/MemberSelector"; import { NewLabelForm } from "./components/NewLabelForm"; +import { NewWorkspaceForm } from "~/app/components/NewWorkspaceForm"; import Modal from "~/app/components/modal"; import { useModal } from "~/app/providers/modal"; @@ -162,6 +163,7 @@ export default function CardPage() { cardPublicId={cardId} /> )} + {modalContentType === "NEW_WORKSPACE" && }
); diff --git a/src/app/components/NewWorkspaceForm.tsx b/src/app/components/NewWorkspaceForm.tsx new file mode 100644 index 00000000..ac30a108 --- /dev/null +++ b/src/app/components/NewWorkspaceForm.tsx @@ -0,0 +1,79 @@ +"use client"; + +import { api } from "~/trpc/react"; + +import { HiXMark } from "react-icons/hi2"; + +import { useModal } from "~/app/providers/modal"; +import { useWorkspace } from "~/app/providers/workspace"; + +import { Formik, Form, Field } from "formik"; + +interface FormValues { + name: string; +} + +export function NewWorkspaceForm() { + const { closeModal } = useModal(); + const { switchWorkspace } = useWorkspace(); + + const createWorkspace = api.workspace.create.useMutation({ + onSuccess: (values) => { + try { + if (values?.publicId) { + switchWorkspace({ publicId: values.publicId, name: values.name }); + closeModal(); + } + } catch (e) { + console.log(e); + } + }, + }); + + return ( + <> +
+

New workspace

+ +
+ + { + createWorkspace.mutate({ + name: values.name, + }); + }} + > +
+ + +
+ +
+ +
+ + ); +} diff --git a/src/app/components/SideNavigation.tsx b/src/app/components/SideNavigation.tsx index 994d00bf..ed3b77dd 100644 --- a/src/app/components/SideNavigation.tsx +++ b/src/app/components/SideNavigation.tsx @@ -33,23 +33,28 @@ export default function SideNavigation({ user }: SideNavigationProps) { ]; return ( - + <> + + ); } diff --git a/src/app/components/WorkspaceMenu.tsx b/src/app/components/WorkspaceMenu.tsx index 64f391a8..23c6f8bc 100644 --- a/src/app/components/WorkspaceMenu.tsx +++ b/src/app/components/WorkspaceMenu.tsx @@ -3,10 +3,14 @@ import { Fragment } from "react"; import { Menu, Transition } from "@headlessui/react"; +import { useModal } from "~/app/providers/modal"; import { useWorkspace } from "~/app/providers/workspace"; -export default function UserMenu() { +import { HiCheck } from "react-icons/hi2"; + +export default function WorkspaceMenu() { const { workspace, availableWorkspaces, switchWorkspace } = useWorkspace(); + const { openModal } = useModal(); return ( @@ -32,26 +36,45 @@ export default function UserMenu() { leaveFrom="transform opacity-100 scale-100" leaveTo="transform opacity-0 scale-95" > - - {availableWorkspaces.map((availableWorkspace) => ( -
- - - -
- ))} + +
+ {availableWorkspaces.map((availableWorkspace) => ( +
+ + + +
+ ))} +
+
+ + + +
diff --git a/src/app/members/page.tsx b/src/app/members/page.tsx index 8d127cd9..e1dc5ab3 100644 --- a/src/app/members/page.tsx +++ b/src/app/members/page.tsx @@ -7,6 +7,7 @@ import Modal from "~/app/components/modal"; import { ImportBoardsForm } from "~/app/boards/components/ImportBoardsForm"; import { NewBoardForm } from "~/app/boards/components/NewBoardForm"; +import { NewWorkspaceForm } from "~/app/components/NewWorkspaceForm"; import MemberDropdown from "./components/MemberDropdown"; @@ -85,6 +86,7 @@ export default function MembersPage() { {modalContentType === "NEW_BOARD" && } {modalContentType === "IMPORT_BOARDS" && } + {modalContentType === "NEW_WORKSPACE" && } ); diff --git a/src/app/providers/workspace.tsx b/src/app/providers/workspace.tsx index fe50b5cb..5fc9f1f8 100644 --- a/src/app/providers/workspace.tsx +++ b/src/app/providers/workspace.tsx @@ -45,8 +45,6 @@ export const WorkspaceProvider: React.FC<{ children: ReactNode }> = ({ const { data } = api.workspace.all.useQuery(); - console.log({ data }); - const switchWorkspace = (_workspace: Workspace) => { localStorage.setItem("workspacePublicId", _workspace.publicId); diff --git a/src/server/api/routers/board.ts b/src/server/api/routers/board.ts index 8a1b2a47..849a7a3d 100644 --- a/src/server/api/routers/board.ts +++ b/src/server/api/routers/board.ts @@ -122,8 +122,6 @@ export const boardRouter = createTRPCRouter({ where: eq(workspaces.publicId, input.workspacePublicId), }) - console.log({ workspace }) - if (!workspace) return; return ctx.db.insert(boards).values({ diff --git a/src/server/api/routers/card.ts b/src/server/api/routers/card.ts index 0022d905..29c148f3 100644 --- a/src/server/api/routers/card.ts +++ b/src/server/api/routers/card.ts @@ -109,8 +109,6 @@ export const cardRouter = createTRPCRouter({ if (!card || !member) return; - console.log({ member }) - const memberExists = await tx.query.cardToWorkspaceMembers.findFirst({ where: and(eq(cardToWorkspaceMembers.cardId, card.id), eq(cardToWorkspaceMembers.workspaceMemberId, member.id)), }); diff --git a/src/server/api/routers/workspace.ts b/src/server/api/routers/workspace.ts index 05289730..75b8a84f 100644 --- a/src/server/api/routers/workspace.ts +++ b/src/server/api/routers/workspace.ts @@ -3,6 +3,8 @@ import { and, eq, isNull } from "drizzle-orm"; import { workspaces, workspaceMembers } from "~/server/db/schema"; +import { generateUID } from "~/utils/generateUID"; + import { createTRPCRouter, publicProcedure, @@ -61,4 +63,38 @@ export const workspaceRouter = createTRPCRouter({ } }); }), + create: publicProcedure + .input( + z.object({ + name: z.string().min(1), + }), + ) + .mutation(async ({ ctx, input }) => { + const userId = ctx.session?.user.id; + + if (!userId) return; + + const workspace = await ctx.db.insert(workspaces).values({ + publicId: generateUID(), + name: input.name, + slug: input.name.toLowerCase(), + createdBy: userId, + }); + + await ctx.db.insert(workspaceMembers).values({ + publicId: generateUID(), + userId, + workspaceId: Number(workspace.insertId), + createdBy: userId, + role: 'admin' + }) + + return ctx.db.query.workspaces.findFirst({ + where: eq(workspaces.id, Number(workspace.insertId)), + columns: { + publicId: true, + name: true, + }, + }); + }), }); \ No newline at end of file