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 = {
isOpen: boolean;
openModal: (contentType: string) => void;
openModal: (contentType: string, entityId?: string) => void;
closeModal: () => void;
modalContentType: string;
entityId: string;
};
interface Props {
@@ -15,11 +16,13 @@ const ModalContext = createContext<ModalContextType | undefined>(undefined);
export const ModalProvider: React.FC<Props> = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const [entityId, setEntityId] = useState("");
const [modalContentType, setModalContentType] = useState("");
const openModal = (contentType: string) => {
const openModal = (contentType: string, entityId?: string) => {
setIsOpen(true);
setModalContentType(contentType);
if (entityId) setEntityId(entityId);
};
const closeModal = () => {
@@ -28,7 +31,7 @@ export const ModalProvider: React.FC<Props> = ({ children }) => {
return (
<ModalContext.Provider
value={{ isOpen, openModal, closeModal, modalContentType }}
value={{ isOpen, openModal, closeModal, modalContentType, entityId }}
>
{children}
</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";
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
.input(
z.object({
@@ -42,6 +55,19 @@ export const labelRouter = createTRPCRouter({
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;
}),
});

View File

@@ -52,10 +52,29 @@ export const getByPublicId = async (
) => {
const { data } = await db
.from("label")
.select(`id`)
.select(`id, publicId, name, colourCode`)
.eq("publicId", labelPublicId)
.limit(1)
.single();
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 Toggle from "~/components/Toggle";
import { type NewLabelInput } from "~/types/router.types";
type NewLabelFormInput = NewLabelInput & {
type LabelFormInput = {
name: string;
colour: Colour;
isCreateAnotherEnabled: boolean;
isCreateAnotherEnabled?: boolean;
};
type Colour = {
@@ -33,19 +32,38 @@ const colours = [
{ name: "Pink", code: "#db2777" },
];
export function NewLabelForm({ cardPublicId }: { cardPublicId: string }) {
export function LabelForm({
cardPublicId,
isEdit,
}: {
cardPublicId: string;
isEdit?: boolean;
}) {
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 } =
useForm<NewLabelFormInput>({
defaultValues: {
name: "",
colour: colours[0],
useForm<LabelFormInput>({
values: {
name: isEdit && label.data?.name ? label.data.name : "",
colour: (isEdit && label.data?.colourCode
? colours.find((c) => c.code === label.data.colourCode)
: colours[0]) as Colour,
isCreateAnotherEnabled: false,
},
});
console.log({ entityId });
const refetchCard = () => utils.card.byId.refetch({ id: cardPublicId });
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;
createLabel.mutate({
name: values.name,
cardPublicId,
colourCode: values.colour.code,
});
if (isEdit) {
updateLabel.mutate({
publicId: label.data?.publicId || "",
name: values.name,
colourCode: values.colour.code,
});
} else {
createLabel.mutate({
name: values.name,
cardPublicId,
colourCode: values.colour.code,
});
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="px-5 pt-5">
<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
className="rounded p-1 hover:bg-light-300 focus:outline-none dark:hover:bg-dark-300"
onClick={(e) => {
@@ -163,16 +202,20 @@ export function NewLabelForm({ cardPublicId }: { cardPublicId: string }) {
</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">
<Toggle
label="Create another"
isChecked={isCreateAnotherEnabled}
onChange={() =>
setValue("isCreateAnotherEnabled", !isCreateAnotherEnabled)
}
/>
{!isEdit && (
<Toggle
label="Create another"
isChecked={!!isCreateAnotherEnabled}
onChange={() =>
setValue("isCreateAnotherEnabled", !isCreateAnotherEnabled)
}
/>
)}
<div>
<Button type="submit">Create label</Button>
<Button type="submit">
{isEdit ? "Update label" : "Create label"}
</Button>
</div>
</div>
</form>

View File

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

View File

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