feat: update list name

This commit is contained in:
Henry
2024-01-02 17:38:13 +00:00
parent decbcadb07
commit 20ef0f5ca2
4 changed files with 180 additions and 92 deletions

View File

@@ -0,0 +1,108 @@
"use client";
import { type ReactNode } from "react";
import { HiOutlinePlusSmall } from "react-icons/hi2";
import { Draggable } from "react-beautiful-dnd";
import { useFormik } from "formik";
import { api } from "~/trpc/react";
import { useModal } from "~/app/providers/modal";
import ListDropdown from "./ListDropdown";
interface ListProps {
children: ReactNode;
index: number;
list: List;
setSelectedPublicListId: (publicListId: PublicListId) => void;
}
interface List {
publicId: string;
name: string;
}
interface FormValues {
listPublicId: string;
name: string;
}
type PublicListId = string;
export default function List({
children,
index,
list,
setSelectedPublicListId,
}: ListProps) {
const { openModal } = useModal();
const openNewCardForm = (publicListId: PublicListId) => {
openModal("NEW_CARD");
setSelectedPublicListId(publicListId);
};
const updateList = api.list.update.useMutation();
const formik = useFormik({
initialValues: {
listPublicId: list.publicId,
name: list.name,
},
onSubmit: (values: FormValues) => {
updateList.mutate({
listPublicId: values.listPublicId,
name: values.name,
});
},
enableReinitialize: true,
});
return (
<Draggable key={list.publicId} draggableId={list.publicId} index={index}>
{(provided) => (
<div
key={list.publicId}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
className="mr-5 h-fit min-w-[18rem] max-w-[18rem] rounded-md border border-dark-400 bg-dark-200 py-2 pl-2 pr-1"
>
<div className="flex justify-between">
<form
onSubmit={formik.handleSubmit}
className="focus-visible:outline-none"
>
<input
type="name"
id="name"
name="name"
value={formik.values.name}
onChange={formik.handleChange}
onBlur={formik.submitForm}
className="mb-4 block border-0 bg-transparent px-4 pt-1 text-sm font-medium text-dark-1000 focus:ring-0 focus-visible:outline-none"
/>
</form>
<div>
<button
className="mx-1 inline-flex h-fit items-center rounded-md p-1 px-1 text-sm font-semibold text-dark-50 hover:bg-dark-400"
onClick={() => openNewCardForm(list.publicId)}
>
<HiOutlinePlusSmall
className="h-5 w-5 text-dark-900"
aria-hidden="true"
/>
</button>
<ListDropdown
setSelectedPublicListId={() =>
setSelectedPublicListId(list.publicId)
}
/>
</div>
</div>
{children}
</div>
)}
</Draggable>
);
}

View File

@@ -21,7 +21,7 @@ import Modal from "~/app/_components/modal";
import BoardDropdown from "./components/BoardDropdown";
import { DeleteBoardConfirmation } from "./components/DeleteBoardConfirmation";
import { DeleteListConfirmation } from "./components/DeleteListConfirmation";
import ListDropdown from "./components/ListDropdown";
import List from "./components/List";
import { NewCardForm } from "./components/NewCardForm";
import { NewListForm } from "./components/NewListForm";
@@ -95,11 +95,6 @@ export default function BoardPage() {
setSelectedPublicListId(publicBoardId);
};
const openNewCardForm = (publicListId: PublicListId) => {
openModal("NEW_CARD");
setSelectedPublicListId(publicListId);
};
const onDragEnd = ({
source,
destination,
@@ -198,94 +193,66 @@ export default function BoardPage() {
>
<div className="min-w-[2rem]" />
{boardData?.lists?.map((list: List, index) => (
<Draggable
key={list.publicId}
draggableId={list.publicId}
<List
index={index}
key={index}
list={list}
setSelectedPublicListId={(publicListId) =>
setSelectedPublicListId(publicListId)
}
>
{(provided) => (
<div
key={list.publicId}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
className="mr-5 h-fit min-w-[18rem] max-w-[18rem] rounded-md border border-dark-400 bg-dark-200 py-2 pl-2 pr-1"
>
<div className="flex justify-between">
<p className="mb-4 px-4 pt-1 text-sm font-medium text-dark-1000">
{list.name}
</p>
<div>
<button
className="mx-1 inline-flex h-fit items-center rounded-md p-1 px-1 text-sm font-semibold text-dark-50 hover:bg-dark-400"
onClick={() => openNewCardForm(list.publicId)}
<Droppable droppableId={`${list.publicId}`} type="CARD">
{(provided) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className="scrollbar-thumb-rounded-[4px] scrollbar-track-rounded-[4px] scrollbar-w-[8px] scrollbar scrollbar-thumb-dark-600 scrollbar-track-dark-100 h-full max-h-[calc(100vh-250px)] min-h-[2rem] overflow-y-auto pr-1"
>
{list.cards?.map((card, index) => (
<Draggable
key={card.publicId}
draggableId={card.publicId}
index={index}
>
<HiOutlinePlusSmall
className="h-5 w-5 text-dark-900"
aria-hidden="true"
/>
</button>
<ListDropdown
setSelectedPublicListId={() =>
setSelectedPublicListId(list.publicId)
}
/>
</div>
</div>
<Droppable droppableId={`${list.publicId}`} type="CARD">
{(provided) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className="scrollbar-thumb-rounded-[4px] scrollbar-track-rounded-[4px] scrollbar-w-[8px] scrollbar scrollbar-thumb-dark-600 scrollbar-track-dark-100 h-full max-h-[calc(100vh-250px)] min-h-[2rem] overflow-y-auto pr-1"
>
{list.cards?.map((card, index) => (
<Draggable
{(provided) => (
<Link
key={card.publicId}
draggableId={card.publicId}
index={index}
href={`/cards/${card.publicId}`}
className="mb-2 flex !cursor-pointer flex-col rounded-md border border-dark-200 bg-dark-500 px-3 py-2 text-sm text-dark-1000"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{(provided) => (
<Link
key={card.publicId}
href={`/cards/${card.publicId}`}
className="mb-2 flex !cursor-pointer flex-col rounded-md border border-dark-200 bg-dark-500 px-3 py-2 text-sm text-dark-1000"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div>{card.title}</div>
{card.labels?.length ? (
<div className="mt-2 flex justify-end space-x-1">
{card.labels.map(({ label }) => (
<span
key={label.publicId}
className="inline-flex w-fit items-center gap-x-1.5 rounded-full px-2 py-1 text-[10px] font-medium text-dark-1000 ring-1 ring-inset ring-dark-800"
>
<svg
fill={label.colourCode}
className="h-2 w-2"
viewBox="0 0 6 6"
aria-hidden="true"
>
<circle cx={3} cy={3} r={3} />
</svg>
<div>{label.name}</div>
</span>
))}
</div>
) : null}
</Link>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</div>
)}
</Draggable>
<div>{card.title}</div>
{card.labels?.length ? (
<div className="mt-2 flex justify-end space-x-1">
{card.labels.map(({ label }) => (
<span
key={label.publicId}
className="inline-flex w-fit items-center gap-x-1.5 rounded-full px-2 py-1 text-[10px] font-medium text-dark-1000 ring-1 ring-inset ring-dark-800"
>
<svg
fill={label.colourCode}
className="h-2 w-2"
viewBox="0 0 6 6"
aria-hidden="true"
>
<circle cx={3} cy={3} r={3} />
</svg>
<div>{label.name}</div>
</span>
))}
</div>
) : null}
</Link>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</List>
))}
<div className="min-w-[0.75rem]" />
{provided.placeholder}

View File

@@ -77,7 +77,7 @@ export const BoardProvider: React.FC<{ children: ReactNode }> = ({
},
});
const updateListMutation = api.list.update.useMutation({
const updateListMutation = api.list.reorder.useMutation({
onSuccess: async () => {
try {
await refetchBoard();

View File

@@ -1,5 +1,5 @@
import { z } from "zod";
import { desc, eq, sql } from "drizzle-orm";
import { and, desc, eq, sql, isNull } from "drizzle-orm";
import { boards, cards, lists } from "~/server/db/schema";
import { generateUID } from "~/utils/generateUID";
@@ -49,7 +49,7 @@ export const listRouter = createTRPCRouter({
});
})
}),
update: publicProcedure
reorder: publicProcedure
.input(
z.object({
boardId: z.string().min(12),
@@ -104,4 +104,17 @@ export const listRouter = createTRPCRouter({
await tx.execute(sql`UPDATE ${lists} SET ${lists.index} = ${lists.index} - 1 WHERE ${lists.boardId} = ${list.boardId} AND ${lists.index} > ${list.index} AND ${lists.deletedAt} IS NULL;`);
})
}),
update: publicProcedure
.input(
z.object({
listPublicId: z.string().min(12),
name: z.string().min(1),
}))
.mutation(({ ctx, input }) => {
const userId = ctx.session?.user.id;
if (!userId) return;
return ctx.db.update(lists).set({ name: input.name }).where(and(eq(lists.publicId, input.listPublicId), isNull(lists.deletedAt)));
}),
});