diff --git a/apps/web/src/components/LoadingSpinner.tsx b/apps/web/src/components/LoadingSpinner.tsx index f5dc3292..059b8634 100644 --- a/apps/web/src/components/LoadingSpinner.tsx +++ b/apps/web/src/components/LoadingSpinner.tsx @@ -5,9 +5,9 @@ const LoadingSpinner = ({ size = "md" }: { size?: "sm" | "md" | "lg" }) => { diff --git a/apps/web/src/views/board/components/VisibilityButton.tsx b/apps/web/src/views/board/components/VisibilityButton.tsx new file mode 100644 index 00000000..1d52fbd4 --- /dev/null +++ b/apps/web/src/views/board/components/VisibilityButton.tsx @@ -0,0 +1,61 @@ +import { useEffect, useState } from "react"; +import { HiOutlineEye, HiOutlineEyeSlash } from "react-icons/hi2"; + +import Button from "~/components/Button"; +import { useBoard } from "~/providers/board"; +import { usePopup } from "~/providers/popup"; +import { api } from "~/utils/api"; + +const VisibilityButton = ({ + visibility, + boardPublicId, +}: { + visibility: "public" | "private"; + boardPublicId: string; +}) => { + const { refetchBoard } = useBoard(); + const { showPopup } = usePopup(); + const [stateVisibility, setStateVisibility] = useState<"public" | "private">( + visibility, + ); + + useEffect(() => { + setStateVisibility(visibility); + }, [visibility]); + + const isPublic = stateVisibility === "public"; + + const updateBoardVisibility = api.board.update.useMutation({ + onSuccess: async () => { + await refetchBoard(); + setStateVisibility(isPublic ? "private" : "public"); + }, + onError: () => { + showPopup({ + header: "Unable to update board visibility", + message: "Please try again later, or contact customer support.", + icon: "error", + }); + }, + }); + + const handleUpdateBoardVisibility = () => { + updateBoardVisibility.mutate({ + visibility: isPublic ? "private" : "public", + boardPublicId, + }); + }; + + return ( + : } + isLoading={updateBoardVisibility.isPending} + > + {isPublic ? "Public" : "Private"} + + ); +}; + +export default VisibilityButton; diff --git a/apps/web/src/views/board/index.tsx b/apps/web/src/views/board/index.tsx index 31620087..91ee75da 100644 --- a/apps/web/src/views/board/index.tsx +++ b/apps/web/src/views/board/index.tsx @@ -9,6 +9,7 @@ import { HiOutlinePlusSmall } from "react-icons/hi2"; import type { UpdateBoardInput } from "@kan/api/types"; +import Button from "~/components/Button"; import Modal from "~/components/modal"; import { NewWorkspaceForm } from "~/components/NewWorkspaceForm"; import { PageHead } from "~/components/PageHead"; @@ -27,6 +28,7 @@ import List from "./components/List"; import { NewCardForm } from "./components/NewCardForm"; import { NewListForm } from "./components/NewListForm"; import { UpdateBoardSlugForm } from "./components/UpdateBoardSlugForm"; +import VisibilityButton from "./components/VisibilityButton"; type PublicListId = string; @@ -161,18 +163,22 @@ export default function BoardPage() { )}
+ - +
diff --git a/packages/api/src/routers/board.ts b/packages/api/src/routers/board.ts index eca7402f..7b96ad80 100644 --- a/packages/api/src/routers/board.ts +++ b/packages/api/src/routers/board.ts @@ -188,6 +188,7 @@ export const boardRouter = createTRPCRouter({ .max(60) .regex(/^(?![-]+$)[a-zA-Z0-9-]+$/) .optional(), + visibility: z.enum(["public", "private"]).optional(), }), ) .output(z.custom>>()) @@ -196,6 +197,7 @@ export const boardRouter = createTRPCRouter({ name: input.name, slug: input.slug, boardPublicId: input.boardPublicId, + visibility: input.visibility, }); if (!result) diff --git a/packages/db/src/repository/board.repo.ts b/packages/db/src/repository/board.repo.ts index ded99232..539c290b 100644 --- a/packages/db/src/repository/board.repo.ts +++ b/packages/db/src/repository/board.repo.ts @@ -31,6 +31,7 @@ export const getByPublicId = async ( publicId, name, slug, + visibility, workspace ( publicId, members:workspace_members ( @@ -222,12 +223,18 @@ export const update = async ( boardInput: { name: string | undefined; slug: string | undefined; + visibility: "public" | "private" | undefined; boardPublicId: string; }, ) => { const { data } = await db .from("board") - .update({ name: boardInput.name, slug: boardInput.slug }) + .update({ + name: boardInput.name, + slug: boardInput.slug, + visibility: boardInput.visibility, + updatedAt: new Date().toISOString(), + }) .eq("publicId", boardInput.boardPublicId) .select(`publicId, name`) .limit(1)