feat: update label

This commit is contained in:
Henry
2024-08-27 23:34:52 +01:00
parent 2adcac85a4
commit 583256a46b
6 changed files with 145 additions and 38 deletions

View File

@@ -2,9 +2,10 @@ import { createContext, useContext, useState } from "react";
type ModalContextType = { type ModalContextType = {
isOpen: boolean; isOpen: boolean;
openModal: (contentType: string) => void; openModal: (contentType: string, entityId?: string) => void;
closeModal: () => void; closeModal: () => void;
modalContentType: string; modalContentType: string;
entityId: string;
}; };
interface Props { interface Props {
@@ -15,11 +16,13 @@ const ModalContext = createContext<ModalContextType | undefined>(undefined);
export const ModalProvider: React.FC<Props> = ({ children }) => { export const ModalProvider: React.FC<Props> = ({ children }) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [entityId, setEntityId] = useState("");
const [modalContentType, setModalContentType] = useState(""); const [modalContentType, setModalContentType] = useState("");
const openModal = (contentType: string) => { const openModal = (contentType: string, entityId?: string) => {
setIsOpen(true); setIsOpen(true);
setModalContentType(contentType); setModalContentType(contentType);
if (entityId) setEntityId(entityId);
}; };
const closeModal = () => { const closeModal = () => {
@@ -28,7 +31,7 @@ export const ModalProvider: React.FC<Props> = ({ children }) => {
return ( return (
<ModalContext.Provider <ModalContext.Provider
value={{ isOpen, openModal, closeModal, modalContentType }} value={{ isOpen, openModal, closeModal, modalContentType, entityId }}
> >
{children} {children}
</ModalContext.Provider> </ModalContext.Provider>

View File

@@ -7,6 +7,19 @@ import * as cardRepo from "~/server/db/repository/card.repo";
import * as labelRepo from "~/server/db/repository/label.repo"; import * as labelRepo from "~/server/db/repository/label.repo";
export const labelRouter = createTRPCRouter({ export const labelRouter = createTRPCRouter({
byPublicId: protectedProcedure
.input(z.object({ publicId: z.string().min(12) }))
.query(async ({ ctx, input }) => {
const label = await labelRepo.getByPublicId(ctx.db, input.publicId);
if (!label)
throw new TRPCError({
message: `Label with public ID ${input.publicId} not found`,
code: "NOT_FOUND",
});
return label;
}),
create: protectedProcedure create: protectedProcedure
.input( .input(
z.object({ z.object({
@@ -42,6 +55,19 @@ export const labelRouter = createTRPCRouter({
boardId: card.list.boardId, boardId: card.list.boardId,
}); });
return result;
}),
update: protectedProcedure
.input(
z.object({
publicId: z.string().min(12),
name: z.string().min(1).max(36),
colourCode: z.string().length(7),
}),
)
.mutation(async ({ ctx, input }) => {
const result = await labelRepo.update(ctx.db, input);
return result; return result;
}), }),
}); });

View File

@@ -52,10 +52,29 @@ export const getByPublicId = async (
) => { ) => {
const { data } = await db const { data } = await db
.from("label") .from("label")
.select(`id`) .select(`id, publicId, name, colourCode`)
.eq("publicId", labelPublicId) .eq("publicId", labelPublicId)
.limit(1) .limit(1)
.single(); .single();
return data; return data;
}; };
export const update = async (
db: SupabaseClient<Database>,
labelInput: {
publicId: string;
name: string;
colourCode: string;
},
) => {
const { data } = await db
.from("label")
.update({
name: labelInput.name,
colourCode: labelInput.colourCode,
})
.eq("publicId", labelInput.publicId);
return data;
};

View File

@@ -10,11 +10,10 @@ import Button from "~/components/Button";
import Input from "~/components/Input"; import Input from "~/components/Input";
import Toggle from "~/components/Toggle"; import Toggle from "~/components/Toggle";
import { type NewLabelInput } from "~/types/router.types"; type LabelFormInput = {
name: string;
type NewLabelFormInput = NewLabelInput & {
colour: Colour; colour: Colour;
isCreateAnotherEnabled: boolean; isCreateAnotherEnabled?: boolean;
}; };
type Colour = { type Colour = {
@@ -33,19 +32,38 @@ const colours = [
{ name: "Pink", code: "#db2777" }, { name: "Pink", code: "#db2777" },
]; ];
export function NewLabelForm({ cardPublicId }: { cardPublicId: string }) { export function LabelForm({
cardPublicId,
isEdit,
}: {
cardPublicId: string;
isEdit?: boolean;
}) {
const utils = api.useUtils(); const utils = api.useUtils();
const { closeModal } = useModal(); const { closeModal, entityId } = useModal();
const label = api.label.byPublicId.useQuery(
{
publicId: entityId,
},
{
enabled: isEdit && !!entityId,
},
);
const { control, register, reset, handleSubmit, setValue, watch } = const { control, register, reset, handleSubmit, setValue, watch } =
useForm<NewLabelFormInput>({ useForm<LabelFormInput>({
defaultValues: { values: {
name: "", name: isEdit && label.data?.name ? label.data.name : "",
colour: colours[0], colour: (isEdit && label.data?.colourCode
? colours.find((c) => c.code === label.data.colourCode)
: colours[0]) as Colour,
isCreateAnotherEnabled: false, isCreateAnotherEnabled: false,
}, },
}); });
console.log({ entityId });
const refetchCard = () => utils.card.byId.refetch({ id: cardPublicId }); const refetchCard = () => utils.card.byId.refetch({ id: cardPublicId });
const isCreateAnotherEnabled = watch("isCreateAnotherEnabled"); const isCreateAnotherEnabled = watch("isCreateAnotherEnabled");
@@ -69,21 +87,42 @@ export function NewLabelForm({ cardPublicId }: { cardPublicId: string }) {
}, },
}); });
const onSubmit = (values: NewLabelFormInput) => { const updateLabel = api.label.update.useMutation({
onSuccess: async () => {
await refetchCard();
closeModal();
reset({
name: "",
colour: colours[0],
});
},
});
const onSubmit = (values: LabelFormInput) => {
if (!values.colour?.code) return; if (!values.colour?.code) return;
createLabel.mutate({ if (isEdit) {
name: values.name, updateLabel.mutate({
cardPublicId, publicId: label.data?.publicId || "",
colourCode: values.colour.code, name: values.name,
}); colourCode: values.colour.code,
});
} else {
createLabel.mutate({
name: values.name,
cardPublicId,
colourCode: values.colour.code,
});
}
}; };
return ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="px-5 pt-5"> <div className="px-5 pt-5">
<div className="flex w-full items-center justify-between pb-4 text-neutral-900 dark:text-dark-1000"> <div className="flex w-full items-center justify-between pb-4 text-neutral-900 dark:text-dark-1000">
<h2 className="text-sm font-medium">New label</h2> <h2 className="text-sm font-medium">
{isEdit ? "Edit label" : "New label"}
</h2>
<button <button
className="rounded p-1 hover:bg-light-300 focus:outline-none dark:hover:bg-dark-300" className="rounded p-1 hover:bg-light-300 focus:outline-none dark:hover:bg-dark-300"
onClick={(e) => { onClick={(e) => {
@@ -163,16 +202,20 @@ export function NewLabelForm({ cardPublicId }: { cardPublicId: string }) {
</div> </div>
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600"> <div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
<Toggle {!isEdit && (
label="Create another" <Toggle
isChecked={isCreateAnotherEnabled} label="Create another"
onChange={() => isChecked={!!isCreateAnotherEnabled}
setValue("isCreateAnotherEnabled", !isCreateAnotherEnabled) onChange={() =>
} setValue("isCreateAnotherEnabled", !isCreateAnotherEnabled)
/> }
/>
)}
<div> <div>
<Button type="submit">Create label</Button> <Button type="submit">
{isEdit ? "Update label" : "Create label"}
</Button>
</div> </div>
</div> </div>
</form> </form>

View File

@@ -6,6 +6,8 @@ import { useForm } from "react-hook-form";
import { useModal } from "~/providers/modal"; import { useModal } from "~/providers/modal";
import { HiEllipsisHorizontal } from "react-icons/hi2";
interface LabelSelectorProps { interface LabelSelectorProps {
cardPublicId: string; cardPublicId: string;
labels: { labels: {
@@ -103,7 +105,7 @@ export default function LabelSelector({
{() => ( {() => (
<div <div
key={label.publicId} key={label.publicId}
className="flex items-center rounded-[5px] p-2 hover:bg-light-200 dark:hover:bg-dark-300" className="group flex items-center rounded-[5px] p-2 hover:bg-light-200 dark:hover:bg-dark-300"
onClick={() => { onClick={() => {
const newValue = !watch(label.publicId); const newValue = !watch(label.publicId);
setValue(label.publicId, newValue); setValue(label.publicId, newValue);
@@ -124,12 +126,23 @@ export default function LabelSelector({
{...register(label.publicId)} {...register(label.publicId)}
checked={watch(label.publicId)} checked={watch(label.publicId)}
/> />
<label <div className="flex w-full items-center justify-between">
htmlFor={label.publicId} <label
className="ml-3 text-sm" htmlFor={label.publicId}
> className="ml-3 text-sm"
{label.name} >
</label> {label.name}
</label>
<button
className="invisible group-hover:visible"
onClick={(event) => {
event.stopPropagation();
openModal("EDIT_LABEL", label.publicId);
}}
>
<HiEllipsisHorizontal size={20} />
</button>
</div>
</div> </div>
)} )}
</Menu.Item> </Menu.Item>

View File

@@ -9,7 +9,7 @@ import { DeleteCardConfirmation } from "./components/DeleteCardConfirmation";
import LabelSelector from "./components/LabelSelector"; import LabelSelector from "./components/LabelSelector";
import ListSelector from "./components/ListSelector"; import ListSelector from "./components/ListSelector";
import MemberSelector from "./components/MemberSelector"; import MemberSelector from "./components/MemberSelector";
import { NewLabelForm } from "./components/NewLabelForm"; import { LabelForm } from "./components/LabelForm";
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm"; import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
import Modal from "~/components/modal"; import Modal from "~/components/modal";
@@ -177,7 +177,10 @@ export default function CardPage() {
<Modal> <Modal>
{modalContentType === "NEW_LABEL" && ( {modalContentType === "NEW_LABEL" && (
<NewLabelForm cardPublicId={cardId} /> <LabelForm cardPublicId={cardId} />
)}
{modalContentType === "EDIT_LABEL" && (
<LabelForm cardPublicId={cardId} isEdit />
)} )}
{modalContentType === "DELETE_CARD" && ( {modalContentType === "DELETE_CARD" && (
<DeleteCardConfirmation <DeleteCardConfirmation