feat: set board visibility
This commit is contained in:
@@ -5,9 +5,9 @@ const LoadingSpinner = ({ size = "md" }: { size?: "sm" | "md" | "lg" }) => {
|
|||||||
<svg
|
<svg
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"animate-spin",
|
"animate-spin",
|
||||||
size === "sm" && "h-4 w-4",
|
size === "sm" && "h-3 w-3",
|
||||||
size === "md" && "h-5 w-5",
|
size === "md" && "h-4 w-4",
|
||||||
size === "lg" && "h-6 w-6",
|
size === "lg" && "h-5 w-5",
|
||||||
)}
|
)}
|
||||||
viewBox="0 0 100 100"
|
viewBox="0 0 100 100"
|
||||||
>
|
>
|
||||||
|
|||||||
61
apps/web/src/views/board/components/VisibilityButton.tsx
Normal file
61
apps/web/src/views/board/components/VisibilityButton.tsx
Normal file
@@ -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 (
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={handleUpdateBoardVisibility}
|
||||||
|
iconLeft={isPublic ? <HiOutlineEye /> : <HiOutlineEyeSlash />}
|
||||||
|
isLoading={updateBoardVisibility.isPending}
|
||||||
|
>
|
||||||
|
{isPublic ? "Public" : "Private"}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VisibilityButton;
|
||||||
@@ -9,6 +9,7 @@ import { HiOutlinePlusSmall } from "react-icons/hi2";
|
|||||||
|
|
||||||
import type { UpdateBoardInput } from "@kan/api/types";
|
import type { UpdateBoardInput } from "@kan/api/types";
|
||||||
|
|
||||||
|
import Button from "~/components/Button";
|
||||||
import Modal from "~/components/modal";
|
import Modal from "~/components/modal";
|
||||||
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
|
||||||
import { PageHead } from "~/components/PageHead";
|
import { PageHead } from "~/components/PageHead";
|
||||||
@@ -27,6 +28,7 @@ import List from "./components/List";
|
|||||||
import { NewCardForm } from "./components/NewCardForm";
|
import { NewCardForm } from "./components/NewCardForm";
|
||||||
import { NewListForm } from "./components/NewListForm";
|
import { NewListForm } from "./components/NewListForm";
|
||||||
import { UpdateBoardSlugForm } from "./components/UpdateBoardSlugForm";
|
import { UpdateBoardSlugForm } from "./components/UpdateBoardSlugForm";
|
||||||
|
import VisibilityButton from "./components/VisibilityButton";
|
||||||
|
|
||||||
type PublicListId = string;
|
type PublicListId = string;
|
||||||
|
|
||||||
@@ -161,18 +163,22 @@ export default function BoardPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
<VisibilityButton
|
||||||
|
visibility={boardData.visibility}
|
||||||
|
boardPublicId={boardId}
|
||||||
|
/>
|
||||||
<Filters boardData={boardData} position="left" />
|
<Filters boardData={boardData} position="left" />
|
||||||
<button
|
<Button
|
||||||
type="button"
|
iconLeft={
|
||||||
className="mr-2 inline-flex items-center gap-x-1.5 rounded-md bg-light-1000 px-3 py-2 text-sm font-semibold text-light-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 dark:bg-dark-1000 dark:text-dark-50"
|
<HiOutlinePlusSmall
|
||||||
|
className="-mr-0.5 h-5 w-5"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
}
|
||||||
onClick={() => openNewListForm(boardId)}
|
onClick={() => openNewListForm(boardId)}
|
||||||
>
|
>
|
||||||
<HiOutlinePlusSmall
|
|
||||||
className="-mr-0.5 h-5 w-5"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
New list
|
New list
|
||||||
</button>
|
</Button>
|
||||||
<BoardDropdown />
|
<BoardDropdown />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
.max(60)
|
.max(60)
|
||||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/)
|
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/)
|
||||||
.optional(),
|
.optional(),
|
||||||
|
visibility: z.enum(["public", "private"]).optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof boardRepo.update>>>())
|
.output(z.custom<Awaited<ReturnType<typeof boardRepo.update>>>())
|
||||||
@@ -196,6 +197,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
name: input.name,
|
name: input.name,
|
||||||
slug: input.slug,
|
slug: input.slug,
|
||||||
boardPublicId: input.boardPublicId,
|
boardPublicId: input.boardPublicId,
|
||||||
|
visibility: input.visibility,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export const getByPublicId = async (
|
|||||||
publicId,
|
publicId,
|
||||||
name,
|
name,
|
||||||
slug,
|
slug,
|
||||||
|
visibility,
|
||||||
workspace (
|
workspace (
|
||||||
publicId,
|
publicId,
|
||||||
members:workspace_members (
|
members:workspace_members (
|
||||||
@@ -222,12 +223,18 @@ export const update = async (
|
|||||||
boardInput: {
|
boardInput: {
|
||||||
name: string | undefined;
|
name: string | undefined;
|
||||||
slug: string | undefined;
|
slug: string | undefined;
|
||||||
|
visibility: "public" | "private" | undefined;
|
||||||
boardPublicId: string;
|
boardPublicId: string;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const { data } = await db
|
const { data } = await db
|
||||||
.from("board")
|
.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)
|
.eq("publicId", boardInput.boardPublicId)
|
||||||
.select(`publicId, name`)
|
.select(`publicId, name`)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user