Spaces:
Sleeping
Sleeping
| import React, { useState } from "react"; | |
| import { useNavigate, useParams } from "react-router-dom"; | |
| import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; | |
| import { format, parseISO } from "date-fns"; | |
| import { toast } from "@/lib/custom-toast"; | |
| import { Calendar, Edit, Trash } from "lucide-react"; | |
| // UI Components | |
| import Layout from "@/components/layout/layout"; | |
| import { Button } from "@/components/ui/button"; | |
| import { | |
| Card, | |
| CardContent, | |
| CardDescription, | |
| CardHeader, | |
| CardTitle, | |
| } from "@/components/ui/card"; | |
| import { | |
| AlertDialog, | |
| AlertDialogAction, | |
| AlertDialogCancel, | |
| AlertDialogContent, | |
| AlertDialogDescription, | |
| AlertDialogFooter, | |
| AlertDialogHeader, | |
| AlertDialogTitle, | |
| } from "@/components/ui/alert-dialog"; | |
| import { Separator } from "@/components/ui/separator"; | |
| // Types & API | |
| import { Deliverable, ProjectApi } from "@/types"; | |
| import { deliverablesApi, projectService } from "@/lib/api"; | |
| const DeliverablePage = () => { | |
| const { id } = useParams<{ id: string }>(); | |
| const deliverableId = parseInt(id || "0", 10); | |
| const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); | |
| const navigate = useNavigate(); | |
| const queryClient = useQueryClient(); | |
| // Fetch deliverable data | |
| const { | |
| isLoading: isLoadingDeliverable, | |
| error: deliverableError, | |
| data: deliverable | |
| } = useQuery({ | |
| queryKey: ['deliverable', deliverableId], | |
| queryFn: () => deliverablesApi.getById(deliverableId), | |
| enabled: deliverableId > 0, | |
| }); | |
| // Fetch project data for the deliverable | |
| const { | |
| isLoading: isLoadingProject, | |
| error: projectError, | |
| data: project | |
| } = useQuery({ | |
| queryKey: ['project', deliverable?.projectId], | |
| queryFn: () => projectService.getById(deliverable?.projectId || 0), | |
| enabled: !!deliverable?.projectId, | |
| }); | |
| // Delete mutation | |
| const deleteDeliverableMutation = useMutation({ | |
| mutationFn: (id: number) => deliverablesApi.delete(id), | |
| onSuccess: () => { | |
| queryClient.invalidateQueries({ queryKey: ['deliverables'] }); | |
| toast.success('Deliverable deleted successfully'); | |
| navigate('/deliverables'); | |
| }, | |
| onError: (error) => { | |
| console.error('Delete error:', error); | |
| toast.error('Failed to delete deliverable'); | |
| } | |
| }); | |
| // Handle delete deliverable | |
| const handleDeleteDeliverable = () => { | |
| setDeleteDialogOpen(true); | |
| }; | |
| // Confirm delete | |
| const confirmDelete = () => { | |
| deleteDeliverableMutation.mutate(deliverableId); | |
| }; | |
| // Handle edit | |
| const handleEdit = () => { | |
| navigate(`/deliverable-edit/${deliverableId}`); | |
| }; | |
| if (isLoadingDeliverable || (deliverable?.projectId && isLoadingProject)) { | |
| return ( | |
| <Layout> | |
| <div className="flex items-center justify-center min-h-screen"> | |
| <p>Loading deliverable data...</p> | |
| </div> | |
| </Layout> | |
| ); | |
| } | |
| if (deliverableError || (deliverable?.projectId && projectError)) { | |
| return ( | |
| <Layout> | |
| <div className="flex flex-col items-center justify-center min-h-screen gap-4"> | |
| <p className="text-red-500">Error loading deliverable data</p> | |
| <Button | |
| variant="outline" | |
| onClick={() => navigate("/deliverables")} | |
| > | |
| Back to Deliverables | |
| </Button> | |
| </div> | |
| </Layout> | |
| ); | |
| } | |
| if (!deliverable) { | |
| return ( | |
| <Layout> | |
| <div className="flex flex-col items-center justify-center min-h-screen gap-4"> | |
| <p className="text-red-500">Deliverable not found</p> | |
| <Button | |
| variant="outline" | |
| onClick={() => navigate("/deliverables")} | |
| > | |
| Back to Deliverables | |
| </Button> | |
| </div> | |
| </Layout> | |
| ); | |
| } | |
| return ( | |
| <Layout> | |
| <div className="flex flex-col w-full min-h-screen p-4 md:p-6"> | |
| <div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6"> | |
| <div> | |
| <Button | |
| variant="outline" | |
| onClick={() => navigate("/deliverables")} | |
| className="mb-2" | |
| > | |
| Back to Deliverables | |
| </Button> | |
| <h1 className="text-2xl font-bold tracking-tight">{deliverable.name}</h1> | |
| {project && ( | |
| <p className="text-muted-foreground"> | |
| Project: {project.projectName} | |
| </p> | |
| )} | |
| </div> | |
| <div className="flex gap-2"> | |
| <Button | |
| variant="outline" | |
| onClick={handleEdit} | |
| className="flex items-center gap-1" | |
| > | |
| <Edit className="h-4 w-4" /> | |
| Edit | |
| </Button> | |
| <Button | |
| variant="destructive" | |
| onClick={handleDeleteDeliverable} | |
| className="flex items-center gap-1" | |
| > | |
| <Trash className="h-4 w-4" /> | |
| Delete | |
| </Button> | |
| </div> | |
| </div> | |
| <div className="grid gap-6"> | |
| <Card> | |
| <CardHeader> | |
| <CardTitle className="text-xl">Deliverable Details</CardTitle> | |
| </CardHeader> | |
| <CardContent className="space-y-4"> | |
| <div> | |
| <h3 className="text-sm font-medium text-muted-foreground mb-1">Name</h3> | |
| <p>{deliverable.name}</p> | |
| </div> | |
| <Separator /> | |
| <div> | |
| <h3 className="text-sm font-medium text-muted-foreground mb-1">Description</h3> | |
| <p className="whitespace-pre-line">{deliverable.description}</p> | |
| </div> | |
| <Separator /> | |
| <div> | |
| <h3 className="text-sm font-medium text-muted-foreground mb-1">Delivery Date</h3> | |
| <div className="flex items-center gap-2"> | |
| <Calendar className="h-4 w-4 text-muted-foreground" /> | |
| <span>{format(parseISO(deliverable.deliveryDate), "MMMM d, yyyy")}</span> | |
| </div> | |
| </div> | |
| <Separator /> | |
| <div> | |
| <h3 className="text-sm font-medium text-muted-foreground mb-1">Project</h3> | |
| <p>{project ? project.projectName : "Loading project..."}</p> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| <AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}> | |
| <AlertDialogContent> | |
| <AlertDialogHeader> | |
| <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle> | |
| <AlertDialogDescription> | |
| This action cannot be undone. This will permanently delete the deliverable. | |
| </AlertDialogDescription> | |
| </AlertDialogHeader> | |
| <AlertDialogFooter> | |
| <AlertDialogCancel>Cancel</AlertDialogCancel> | |
| <AlertDialogAction | |
| onClick={confirmDelete} | |
| className="bg-destructive hover:bg-destructive/90" | |
| > | |
| Delete | |
| </AlertDialogAction> | |
| </AlertDialogFooter> | |
| </AlertDialogContent> | |
| </AlertDialog> | |
| </div> | |
| </Layout> | |
| ); | |
| }; | |
| export default DeliverablePage; |