From f553aa2e67bf7bd40fa4f230fd02152f9a6d4c6f Mon Sep 17 00:00:00 2001 From: Zeke Abshire Date: Fri, 29 Nov 2024 00:10:50 -0600 Subject: [PATCH] formatted tables --- .../macro/data-table/data-table.tsx | 11 +- src/app/_components/ui/table.tsx | 2 +- .../list/[id]/_components/table/columns.tsx | 169 -------------- .../[id]/_components/table/task-table.tsx | 6 +- .../[id]/_components/table/use-columns.tsx | 221 ++++++++++++++++++ src/server/api/routers/lists.ts | 1 + 6 files changed, 236 insertions(+), 174 deletions(-) delete mode 100644 src/app/list/[id]/_components/table/columns.tsx create mode 100644 src/app/list/[id]/_components/table/use-columns.tsx diff --git a/src/app/_components/macro/data-table/data-table.tsx b/src/app/_components/macro/data-table/data-table.tsx index de85e4f..5fc5bdb 100644 --- a/src/app/_components/macro/data-table/data-table.tsx +++ b/src/app/_components/macro/data-table/data-table.tsx @@ -70,7 +70,11 @@ export const DataTable = ({ {headerGroup.headers.map((header) => { return ( - + {header.isPlaceholder ? null : flexRender( @@ -91,7 +95,10 @@ export const DataTable = ({ data-state={row.getIsSelected() && "selected"} > {row.getVisibleCells().map((cell) => ( - + {flexRender( cell.column.columnDef.cell, cell.getContext(), diff --git a/src/app/_components/ui/table.tsx b/src/app/_components/ui/table.tsx index 41be7f4..6b2cd91 100644 --- a/src/app/_components/ui/table.tsx +++ b/src/app/_components/ui/table.tsx @@ -9,7 +9,7 @@ const Table = React.forwardRef<
diff --git a/src/app/list/[id]/_components/table/columns.tsx b/src/app/list/[id]/_components/table/columns.tsx deleted file mode 100644 index e7875d0..0000000 --- a/src/app/list/[id]/_components/table/columns.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import { type ColumnDef } from "@tanstack/react-table"; -import { type VARIANT } from "~/lib/data/list-variants"; -import { tableStatuses, type STATUS } from "~/lib/data/task-status"; -import { tablePriorities, type PRIORITY } from "~/lib/data/task-priority"; -import { api } from "~/trpc/react"; -import { Checkbox } from "~/app/_components/ui/checkbox"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "~/app/_components/ui/dropdown-menu"; -import { Button } from "~/app/_components/ui/button"; -import { MoreHorizontal } from "lucide-react"; -import { DataTableColumnHeader } from "~/app/_components/macro/data-table/data-table-column-header"; - -type Variant = keyof typeof VARIANT; -type Status = keyof typeof STATUS; -type Priority = keyof typeof PRIORITY; -export type Row = { - done: boolean; - id: string; - title: string; - status: Status; - priority: Priority; -}; - -const columns: ColumnDef[] = [ - { - accessorKey: "done", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const task = row.original; - // TODO: Throttle - const { mutate } = api.tasks.update.useMutation(); - return ; - }, - enableSorting: false, - }, - { - accessorKey: "id", - header: ({ column }) => ( - - ), - }, - { - accessorKey: "title", - header: ({ column }) => ( - - ), - }, - { - accessorKey: "status", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const status = tableStatuses.find( - (status) => status.value === row.getValue("status"), - ); - - if (!status) { - return null; - } - - return ( -
- {status.icon && ( - - )} - {status.label} -
- ); - }, - filterFn: (row, id, value) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access - return value.includes(row.getValue(id)); - }, - }, - { - accessorKey: "priority", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const priority = tablePriorities.find( - (priority) => priority.value === row.getValue("priority"), - ); - - if (!priority) { - return null; - } - - return ( -
- {priority.icon && ( - - )} - {priority.label} -
- ); - }, - filterFn: (row, id, value) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access - return value.includes(row.getValue(id)); - }, - }, - { - id: "actions", - cell: ({ row }) => { - const task = row.original; - - return ( - - - - - - Actions - navigator.clipboard.writeText(task.id)} - > - Copy ID - - - View task details - - - ); - }, - }, -]; - -export const getColumns = ( - variant: Variant, - showId: boolean, -): ColumnDef[] => { - const newColumnNames: string[] = []; - - if (variant === "checklist" || variant === "project") - newColumnNames.push("done"); - - if (showId) newColumnNames.push("id"); - - newColumnNames.push("title"); - - if (variant === "project") { - newColumnNames.push("priority"); - newColumnNames.push("status"); - } - - newColumnNames.push("actions"); - - return columns.filter((col) => { - const id = ((col as unknown as { accessorKey?: string }).accessorKey ?? - col.id)!; - return newColumnNames.includes(id); - }); -}; diff --git a/src/app/list/[id]/_components/table/task-table.tsx b/src/app/list/[id]/_components/table/task-table.tsx index ed464f3..1b66cbf 100644 --- a/src/app/list/[id]/_components/table/task-table.tsx +++ b/src/app/list/[id]/_components/table/task-table.tsx @@ -6,9 +6,9 @@ import { type STATUS } from "~/lib/data/task-status"; import { type PRIORITY } from "~/lib/data/task-priority"; import { api } from "~/trpc/react"; import { - getColumns, type Row, -} from "~/app/list/[id]/_components/table/columns"; + useColumns, +} from "~/app/list/[id]/_components/table/use-columns"; type Variant = keyof typeof VARIANT; type Status = keyof typeof STATUS; @@ -16,9 +16,11 @@ type Priority = keyof typeof PRIORITY; export const TaskTable = ({ listId }: { listId: number }) => { const [list] = api.list.get.useSuspenseQuery({ listId }); + const getColumns = useColumns(listId); const data: Row[] = list?.tasks.map((ls) => ({ + taskId: ls.id, done: ls.isChecked, id: ls.visibleId ?? "", title: ls.title, diff --git a/src/app/list/[id]/_components/table/use-columns.tsx b/src/app/list/[id]/_components/table/use-columns.tsx new file mode 100644 index 0000000..52ec8cd --- /dev/null +++ b/src/app/list/[id]/_components/table/use-columns.tsx @@ -0,0 +1,221 @@ +"use client"; + +import { DataTable } from "~/app/_components/macro/data-table/data-table"; +import { type ColumnDef } from "@tanstack/react-table"; +import { type VARIANT } from "~/lib/data/list-variants"; +import { tableStatuses, type STATUS } from "~/lib/data/task-status"; +import { tablePriorities, type PRIORITY } from "~/lib/data/task-priority"; +import { api } from "~/trpc/react"; +import { Checkbox } from "~/app/_components/ui/checkbox"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "~/app/_components/ui/dropdown-menu"; +import { Button } from "~/app/_components/ui/button"; +import { MoreHorizontal } from "lucide-react"; +import { DataTableColumnHeader } from "~/app/_components/macro/data-table/data-table-column-header"; + +type Variant = keyof typeof VARIANT; +type Status = keyof typeof STATUS; +type Priority = keyof typeof PRIORITY; + +export type Row = { + taskId: number; + done: boolean; + id: string; + title: string; + status: Status; + priority: Priority; +}; + +export const useColumns = (listId: number) => { + const utils = api.useUtils(); + const { mutate } = api.tasks.update.useMutation({ + onMutate: async (taskUpdate) => { + await utils.list.get.cancel(); + const prevData = utils.list.get.getData({ listId }); + + if (!prevData) return; + + const tasks = prevData?.tasks.map((t) => { + if (t.id === taskUpdate.taskId) { + const clone = { ...t }; + if (taskUpdate.data.isChecked !== undefined) + clone.isChecked = taskUpdate.data.isChecked; + if (taskUpdate.data.title !== undefined) + clone.title = taskUpdate.data.title; + if (taskUpdate.data.status !== undefined) + clone.status = taskUpdate.data.status; + if (taskUpdate.data.priority !== undefined) + clone.priority = taskUpdate.data.priority; + + return clone; + } + + return t; + }); + utils.list.get.setData({ listId }, (old) => { + if (old) old.tasks = tasks; + return old; + }); + }, + onSettled: () => void utils.list.get.invalidate(), + }); + + const columns: ColumnDef[] = [ + { + accessorKey: "done", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const task = row.original; + + // TODO: Throttle + const handleToggleCheckbox = (checked: boolean) => { + mutate({ taskId: task.taskId, data: { isChecked: checked } }); + }; + + return ( + + ); + }, + enableSorting: false, + size: 0, + }, + { + accessorKey: "id", + header: ({ column }) => ( + + ), + size: 0, + }, + { + accessorKey: "title", + header: ({ column }) => ( + + ), + }, + { + accessorKey: "status", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const status = tableStatuses.find( + (status) => status.value === row.getValue("status"), + ); + + if (!status) { + return null; + } + + return ( +
+ {status.icon && ( + + )} + {status.label} +
+ ); + }, + filterFn: (row, id, value) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + return value.includes(row.getValue(id)); + }, + }, + { + accessorKey: "priority", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const priority = tablePriorities.find( + (priority) => priority.value === row.getValue("priority"), + ); + + if (!priority) { + return null; + } + + return ( +
+ {priority.icon && ( + + )} + {priority.label} +
+ ); + }, + filterFn: (row, id, value) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + return value.includes(row.getValue(id)); + }, + }, + { + id: "actions", + cell: ({ row }) => { + const task = row.original; + + return ( + + + + + + Actions + navigator.clipboard.writeText(task.id)} + > + Copy ID + + + View task details + + + ); + }, + size: 0, + }, + ]; + + const getColumns = (variant: Variant, showId: boolean): ColumnDef[] => { + const newColumnNames: string[] = []; + + if (variant === "checklist" || variant === "project") + newColumnNames.push("done"); + + if (showId) newColumnNames.push("id"); + + newColumnNames.push("title"); + + if (variant === "project") { + newColumnNames.push("priority"); + newColumnNames.push("status"); + } + + newColumnNames.push("actions"); + + return columns.filter((col) => { + const id = ((col as unknown as { accessorKey?: string }).accessorKey ?? + col.id)!; + return newColumnNames.includes(id); + }); + }; + + return getColumns; +}; diff --git a/src/server/api/routers/lists.ts b/src/server/api/routers/lists.ts index 1bfc830..f2d57b4 100644 --- a/src/server/api/routers/lists.ts +++ b/src/server/api/routers/lists.ts @@ -24,6 +24,7 @@ export const listRouter = createTRPCRouter({ getAll: protectedProcedure.query(async ({ ctx }) => { const allLists = await ctx.db.query.lists.findMany({ where: eq(lists.userId, ctx.session.user.id), + orderBy: (lists, { desc }) => [desc(lists.id)], }); return allLists; }),