Add delete to commission types and fix dragging
This commit is contained in:
19
src/actions/items/commissions/types/deleteType.ts
Normal file
19
src/actions/items/commissions/types/deleteType.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
"use server"
|
||||||
|
|
||||||
|
import prisma from "@/lib/prisma"
|
||||||
|
|
||||||
|
export async function deleteCommissionType(typeId: string) {
|
||||||
|
|
||||||
|
await prisma.commissionTypeOption.deleteMany({
|
||||||
|
where: { typeId },
|
||||||
|
})
|
||||||
|
|
||||||
|
await prisma.commissionTypeExtra.deleteMany({
|
||||||
|
where: { typeId },
|
||||||
|
})
|
||||||
|
|
||||||
|
await prisma.commissionType.delete({
|
||||||
|
where: { id: typeId },
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
@ -26,7 +26,7 @@ export default async function CommissionTypesEditPage({ params }: { params: { id
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex gap-4 justify-between pb-8">
|
<div className="flex gap-4 justify-between pb-8">
|
||||||
<h1 className="text-2xl font-bold mb-4">New Commission Type</h1>
|
<h1 className="text-2xl font-bold mb-4">Edit Commission Type</h1>
|
||||||
</div>
|
</div>
|
||||||
<EditTypeForm type={commissionType} allOptions={options} allExtras={extras} />
|
<EditTypeForm type={commissionType} allOptions={options} allExtras={extras} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,11 +29,15 @@ export default function SortableItemCard({ id, children }: Props) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
style={style}
|
style={style}>
|
||||||
{...attributes}
|
<div
|
||||||
{...listeners}
|
{...attributes}
|
||||||
className="cursor-grab"
|
{...listeners}
|
||||||
>
|
className="cursor-grab px-2 py-1 text-sm text-muted-foreground"
|
||||||
|
title="Drag to reorder"
|
||||||
|
>
|
||||||
|
☰
|
||||||
|
</div>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
|
import { deleteCommissionType } from "@/actions/items/commissions/types/deleteType";
|
||||||
import { updateCommissionTypeSortOrder } from "@/actions/items/commissions/types/updateCommissionTypeSortOrder";
|
import { updateCommissionTypeSortOrder } from "@/actions/items/commissions/types/updateCommissionTypeSortOrder";
|
||||||
import SortableItemCard from "@/components/drag/SortableItemCard";
|
import SortableItemCard from "@/components/drag/SortableItemCard";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||||
import { CommissionExtra, CommissionOption, CommissionType, CommissionTypeExtra, CommissionTypeOption } from "@/generated/prisma";
|
import { CommissionExtra, CommissionOption, CommissionType, CommissionTypeExtra, CommissionTypeOption } from "@/generated/prisma";
|
||||||
import {
|
import {
|
||||||
closestCenter,
|
closestCenter,
|
||||||
@ -17,9 +20,9 @@ import {
|
|||||||
rectSortingStrategy,
|
rectSortingStrategy,
|
||||||
SortableContext
|
SortableContext
|
||||||
} from "@dnd-kit/sortable";
|
} from "@dnd-kit/sortable";
|
||||||
import { PencilIcon } from "lucide-react";
|
import { PencilIcon, TrashIcon } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState, useTransition } from "react";
|
||||||
|
|
||||||
type CommissionTypeWithItems = CommissionType & {
|
type CommissionTypeWithItems = CommissionType & {
|
||||||
options: (CommissionTypeOption & {
|
options: (CommissionTypeOption & {
|
||||||
@ -33,6 +36,9 @@ type CommissionTypeWithItems = CommissionType & {
|
|||||||
export default function ListTypes({ types }: { types: CommissionTypeWithItems[] }) {
|
export default function ListTypes({ types }: { types: CommissionTypeWithItems[] }) {
|
||||||
const [items, setItems] = useState(types)
|
const [items, setItems] = useState(types)
|
||||||
const [isMounted, setIsMounted] = useState(false)
|
const [isMounted, setIsMounted] = useState(false)
|
||||||
|
const [dialogOpen, setDialogOpen] = useState(false)
|
||||||
|
const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null)
|
||||||
|
const [isPending, startTransition] = useTransition()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsMounted(true)
|
setIsMounted(true)
|
||||||
@ -56,70 +62,116 @@ export default function ListTypes({ types }: { types: CommissionTypeWithItems[]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const confirmDelete = () => {
|
||||||
|
if (!deleteTargetId) return
|
||||||
|
startTransition(async () => {
|
||||||
|
await deleteCommissionType(deleteTargetId)
|
||||||
|
setItems((prev) => prev.filter((i) => i.id !== deleteTargetId))
|
||||||
|
setDialogOpen(false)
|
||||||
|
setDeleteTargetId(null)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!isMounted) return null
|
if (!isMounted) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
<>
|
||||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
<SortableContext items={items.map((i) => i.id)} strategy={rectSortingStrategy}>
|
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||||
{items.map(type => (
|
<SortableContext items={items.map((i) => i.id)} strategy={rectSortingStrategy}>
|
||||||
<SortableItemCard key={type.id} id={type.id}>
|
{items.map(type => (
|
||||||
<Card>
|
<SortableItemCard key={type.id} id={type.id}>
|
||||||
<CardHeader>
|
<Card>
|
||||||
<CardTitle className="text-xl truncate">{type.name}</CardTitle>
|
<CardHeader>
|
||||||
<CardDescription>{type.description}</CardDescription>
|
<CardTitle className="text-xl truncate">{type.name}</CardTitle>
|
||||||
|
<CardDescription>{type.description}</CardDescription>
|
||||||
|
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-col justify-start gap-4">
|
<CardContent className="flex flex-col justify-start gap-4">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold">Options</h4>
|
<h4 className="font-semibold">Options</h4>
|
||||||
<ul className="pl-4 list-disc">
|
<ul className="pl-4 list-disc">
|
||||||
{type.options.map((opt) => (
|
{type.options.map((opt) => (
|
||||||
<li key={opt.id}>
|
<li key={opt.id}>
|
||||||
{opt.option?.name}:{" "}
|
{opt.option?.name}:{" "}
|
||||||
{opt.price !== null
|
{opt.price !== null
|
||||||
? `${opt.price}€`
|
? `${opt.price}€`
|
||||||
: opt.pricePercent
|
: opt.pricePercent
|
||||||
? `+${opt.pricePercent}%`
|
? `+${opt.pricePercent}%`
|
||||||
: opt.priceRange
|
: opt.priceRange
|
||||||
? `${opt.priceRange}€`
|
? `${opt.priceRange}€`
|
||||||
: "Included"}
|
: "Included"}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold">Extras</h4>
|
<h4 className="font-semibold">Extras</h4>
|
||||||
<ul className="pl-4 list-disc">
|
<ul className="pl-4 list-disc">
|
||||||
{type.extras.map((ext) => (
|
{type.extras.map((ext) => (
|
||||||
<li key={ext.id}>
|
<li key={ext.id}>
|
||||||
{ext.extra?.name}:{" "}
|
{ext.extra?.name}:{" "}
|
||||||
{ext.price !== null
|
{ext.price !== null
|
||||||
? `${ext.price}€`
|
? `${ext.price}€`
|
||||||
: ext.pricePercent
|
: ext.pricePercent
|
||||||
? `+${ext.pricePercent}%`
|
? `+${ext.pricePercent}%`
|
||||||
: ext.priceRange
|
: ext.priceRange
|
||||||
? `${ext.priceRange}€`
|
? `${ext.priceRange}€`
|
||||||
: "Included"}
|
: "Included"}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex items-center justify-between">
|
<CardFooter className="flex flex-col gap-2">
|
||||||
<Link
|
<Link
|
||||||
href={`/items/commissions/types/${type.id}/edit`}
|
href={`/items/commissions/types/${type.id}/edit`}
|
||||||
className="text-sm text-primary hover:underline flex items-center gap-1"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<PencilIcon className="h-4 w-4" />
|
<Button variant="default" className="w-full flex items-center gap-2">
|
||||||
Edit
|
<PencilIcon className="h-4 w-4" />
|
||||||
</Link>
|
Edit
|
||||||
</CardFooter>
|
</Button>
|
||||||
</Card>
|
</Link>
|
||||||
</SortableItemCard >
|
<Button
|
||||||
))}
|
variant="destructive"
|
||||||
</SortableContext>
|
className="w-full flex items-center gap-2"
|
||||||
</DndContext>
|
onClick={() => {
|
||||||
</div>
|
setDeleteTargetId(type.id)
|
||||||
|
setDialogOpen(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TrashIcon className="h-4 w-4" />
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</SortableItemCard >
|
||||||
|
))}
|
||||||
|
</SortableContext>
|
||||||
|
</DndContext>
|
||||||
|
</div >
|
||||||
|
|
||||||
|
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Delete this commission type?</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<p>This action cannot be undone. Are you sure you want to continue?</p>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={() => setDialogOpen(false)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
disabled={isPending}
|
||||||
|
onClick={confirmDelete}
|
||||||
|
>
|
||||||
|
Confirm Delete
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
Reference in New Issue
Block a user