fix multi-delete

This commit is contained in:
andrzej 2024-06-27 23:12:53 +02:00
parent 1fca1a2b81
commit 97a537f5a2
6 changed files with 299 additions and 58 deletions

Binary file not shown.

View File

@ -22,12 +22,12 @@ export async function deleteRecord(id: number, pathname: Pathname) {
export async function deleteRecords(ids: number[], pathname: "/story" | "/publication" | "/submission") { export async function deleteRecords(ids: number[], pathname: "/story" | "/publication" | "/submission") {
const table = tableMap[pathname] const table = tableMap[pathname]
ids.forEach(async (id) => { ids.forEach(async (id) => {
const res = await prisma.story.delete({ const res = await prisma[table].delete({
where: { id } where: { id }
}) })
console.log(`deleted from ${table}: ${res.id}`) console.log(`deleted from ${table}: ${res.id}`)
}) })
revalidatePath(pathname)
redirect(pathname)
} }

8
src/app/lib/pluralize.ts Normal file
View File

@ -0,0 +1,8 @@
export default function pluralize(word: "story" | "publication" | "submission"): string {
const map = {
story: "stories",
publication: "publications",
submission: "submissions"
}
return map[word]
}

View File

@ -446,6 +446,60 @@ video {
display: none; display: none;
} }
:root {
--background: 220 0% 96%;
--foreground: 222.2 84% 4.9%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--primary: 144.91 90% 32%;
--primary-foreground: 75 10% 97.84%;
--secondary: 240 0% 100%;
--secondary-foreground: 150 95% 30%;
--accent: 150 55% 95%;
--accent-foreground: 155 100% 20%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 0% 100%;
--ring: 150 100% 40%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 40% 4%;
--foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--popover: 230 25% 10%;
--popover-foreground: 210 40% 98%;
--card: 222.2 20% 6%;
--card-foreground: 210 40% 98%;
--border: 217.2 20% 10%;
--input: 217.2 32.6% 17.5%;
--primary: 155 70% 35%;
--primary-foreground: 80 10% 97.84%;
--secondary: 200 50% 98%;
--secondary-foreground: 155 85% 30%;
--accent: 170 60% 10%;
--accent-foreground: 155 60% 65%;
--destructive: 5 90% 65%;
--destructive-foreground: 0 100% 10%;
--ring: 160 90% 45%;
}
* {
border-color: hsl(var(--border));
}
body {
background-color: hsl(var(--background));
color: hsl(var(--foreground));
}
*, ::before, ::after { *, ::before, ::after {
--tw-border-spacing-x: 0; --tw-border-spacing-x: 0;
--tw-border-spacing-y: 0; --tw-border-spacing-y: 0;
@ -652,22 +706,6 @@ video {
z-index: 100; z-index: 100;
} }
.col-span-full {
grid-column: 1 / -1;
}
.col-start-1 {
grid-column-start: 1;
}
.col-start-3 {
grid-column-start: 3;
}
.col-end-3 {
grid-column-end: 3;
}
.m-auto { .m-auto {
margin: auto; margin: auto;
} }
@ -872,6 +910,10 @@ video {
width: 1rem; width: 1rem;
} }
.w-40 {
width: 10rem;
}
.w-5\/6 { .w-5\/6 {
width: 83.333333%; width: 83.333333%;
} }
@ -917,14 +959,6 @@ video {
width: 100vw; width: 100vw;
} }
.w-36 {
width: 9rem;
}
.w-40 {
width: 10rem;
}
.min-w-\[8rem\] { .min-w-\[8rem\] {
min-width: 8rem; min-width: 8rem;
} }
@ -938,11 +972,6 @@ video {
min-width: fit-content; min-width: fit-content;
} }
.min-w-min {
min-width: -moz-min-content;
min-width: min-content;
}
.max-w-full { .max-w-full {
max-width: 100%; max-width: 100%;
} }
@ -1023,10 +1052,6 @@ video {
user-select: none; user-select: none;
} }
.grid-cols-12 {
grid-template-columns: repeat(12, minmax(0, 1fr));
}
.flex-row { .flex-row {
flex-direction: row; flex-direction: row;
} }
@ -1055,10 +1080,6 @@ video {
align-items: baseline; align-items: baseline;
} }
.justify-start {
justify-content: flex-start;
}
.justify-end { .justify-end {
justify-content: flex-end; justify-content: flex-end;
} }
@ -1394,6 +1415,10 @@ video {
padding-right: 0.5rem; padding-right: 0.5rem;
} }
.pr-4 {
padding-right: 1rem;
}
.pr-8 { .pr-8 {
padding-right: 2rem; padding-right: 2rem;
} }
@ -1402,10 +1427,6 @@ video {
padding-top: 0.25rem; padding-top: 0.25rem;
} }
.pr-4 {
padding-right: 1rem;
}
.text-left { .text-left {
text-align: left; text-align: left;
} }
@ -1668,6 +1689,194 @@ video {
animation-duration: 200ms; animation-duration: 200ms;
} }
/**/
/* @layer base { */
/* :root { */
/* --background: 43 62% 98%; */
/* --foreground: 43 73% 2%; */
/* --muted: 43 24% 85%; */
/* --muted-foreground: 43 10% 37%; */
/* --popover: 43 62% 98%; */
/* --popover-foreground: 43 73% 2%; */
/* --card: 43 62% 98%; */
/* --card-foreground: 43 73% 2%; */
/* --border: 43 15% 91%; */
/* --input: 43 15% 91%; */
/* --primary: 43 50% 69%; */
/* --primary-foreground: 43 50% 9%; */
/* --secondary: 43 6% 92%; */
/* --secondary-foreground: 43 6% 32%; */
/* --accent: 43 13% 83%; */
/* --accent-foreground: 43 13% 23%; */
/* --destructive: 8 84% 20%; */
/* --destructive-foreground: 8 84% 80%; */
/* --ring: 43 50% 69%; */
/* --radius: 0.5rem; */
/* } */
/**/
/* .dark { */
/* --background: 43 48% 4%; */
/* --foreground: 43 26% 97%; */
/* --muted: 43 24% 15%; */
/* --muted-foreground: 43 10% 63%; */
/* --popover: 43 48% 4%; */
/* --popover-foreground: 43 26% 97%; */
/* --card: 43 48% 4%; */
/* --card-foreground: 43 26% 97%; */
/* --border: 43 15% 13%; */
/* --input: 43 15% 13%; */
/* --primary: 43 50% 69%; */
/* --primary-foreground: 43 50% 9%; */
/* --secondary: 43 8% 18%; */
/* --secondary-foreground: 43 8% 78%; */
/* --accent: 43 14% 23%; */
/* --accent-foreground: 43 14% 83%; */
/* --destructive: 8 84% 52%; */
/* --destructive-foreground: 0 0% 100%; */
/* --ring: 43 50% 69%; */
/* } */
/* } */
/**/
/* @layer base { */
/* :root { */
/* --background: 258 70% 100%; */
/* --foreground: 258 77% 0%; */
/* --muted: 258 29% 85%; */
/* --muted-foreground: 258 10% 40%; */
/* --popover: 258 70% 100%; */
/* --popover-foreground: 258 77% 0%; */
/* --card: 258 70% 100%; */
/* --card-foreground: 258 77% 0%; */
/* --border: 220 13% 91%; */
/* --input: 220 13% 91%; */
/* --primary: 258 58% 37%; */
/* --primary-foreground: 258 58% 97%; */
/* --secondary: 258 19% 81%; */
/* --secondary-foreground: 258 19% 21%; */
/* --accent: 258 19% 81%; */
/* --accent-foreground: 258 19% 21%; */
/* --destructive: 19 98% 27%; */
/* --destructive-foreground: 19 98% 87%; */
/* --ring: 258 58% 37%; */
/* --radius: 0.5rem; */
/* } */
/**/
/* .dark { */
/* --background: 258 53% 3%; */
/* --foreground: 258 40% 97%; */
/* --muted: 258 29% 15%; */
/* --muted-foreground: 258 10% 60%; */
/* --popover: 258 53% 3%; */
/* --popover-foreground: 258 40% 97%; */
/* --card: 258 53% 3%; */
/* --card-foreground: 258 40% 97%; */
/* --border: 215 27.9% 16.9%; */
/* --input: 215 27.9% 16.9%; */
/* --primary: 258 58% 37%; */
/* --primary-foreground: 258 58% 97%; */
/* --secondary: 258 15% 10%; */
/* --secondary-foreground: 258 15% 70%; */
/* --accent: 258 15% 10%; */
/* --accent-foreground: 258 15% 70%; */
/* --destructive: 19 98% 46%; */
/* --destructive-foreground: 0 0% 100%; */
/* --ring: 258 58% 37%; */
/* } */
/* } */
.file\:border-0::file-selector-button { .file\:border-0::file-selector-button {
border-width: 0px; border-width: 0px;
} }

View File

@ -11,9 +11,12 @@ export default function FormContextMenu({ pathname, row }) {
return ( return (
<Dialog> <Dialog>
<ContextMenuContent > <ContextMenuContent >
<Link href={`${pathname}/${row.original.id}`}> {pathname !== "/submission" ?
<ContextMenuItem>Inspect</ContextMenuItem> <Link href={`${pathname}/${row.original.id}`}>
</Link> <ContextMenuItem>Inspect</ContextMenuItem>
</Link>
: ""
}
<ContextMenuSeparator /> <ContextMenuSeparator />
<DialogTrigger asChild> <DialogTrigger asChild>
<ContextMenuItem className="text-destructive">Delete</ContextMenuItem> <ContextMenuItem className="text-destructive">Delete</ContextMenuItem>

View File

@ -42,6 +42,8 @@ import { usePathname } from "next/navigation"
import FormContextMenu from "./contextMenu" import FormContextMenu from "./contextMenu"
import { deleteRecords } from "app/lib/del" import { deleteRecords } from "app/lib/del"
import { Pathname } from "app/types" import { Pathname } from "app/types"
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTrigger } from "@/components/ui/dialog"
import pluralize from "app/lib/pluralize"
interface DataTableProps<TData, TValue> { interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[] columns: ColumnDef<TData, TValue>[]
@ -118,17 +120,36 @@ export function DataTable<TData, TValue>({
{children} {children}
<Button variant="destructive" disabled={!table.getIsSomeRowsSelected()} <Dialog>
onClick={() => { <DialogTrigger asChild>
const selectedRows = table.getState().rowSelection <Button variant="destructive" disabled={!(table.getIsSomeRowsSelected() || table.getIsAllRowsSelected())}>
const rowIds = Object.keys(selectedRows) <Trash2 />
const recordIds = rowIds.map(id => Number(table.getRow(id).original.id)) </Button>
console.table(recordIds) </DialogTrigger>
deleteRecords(recordIds, pathname) <DialogContent>
}} <DialogHeader>
> {`Delete ${Object.keys(table.getState().rowSelection).length} ${pluralize(pathname.slice(1))}?`}
<Trash2 /> </DialogHeader>
</Button> <DialogDescription>
{`Deleting ${pluralize(pathname.slice(1))} cannot be undone!`}
</DialogDescription>
<DialogFooter>
<DialogClose asChild>
<Button variant="destructive"
onClick={() => {
const selectedRows = table.getState().rowSelection
const rowIds = Object.keys(selectedRows)
const recordIds = rowIds.map(id => Number(table.getRow(id).original.id))
console.table(recordIds)
deleteRecords(recordIds, pathname)
}}>
Yes, delete them!</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>