pmtool / src /pages /deliverable-edit.tsx
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
10.4 kB
import React, { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "@/lib/custom-toast";
import { format, parseISO } from "date-fns";
// UI Components
import Layout from "@/components/layout/layout";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
// Types & API
import { Deliverable, DeliverableUpdateRequest, ProjectApi } from "@/types";
import { deliverablesApi, projectService } from "@/lib/api";
// Form schema
const formSchema = z.object({
name: z.string().min(1, "Name is required"),
description: z.string().min(1, "Description is required"),
deliveryDate: z.string().min(1, "Delivery date is required"),
projectId: z.string().min(1, "Project is required"),
});
type FormData = z.infer<typeof formSchema>;
const DeliverableEdit = () => {
const { id } = useParams<{ id: string }>();
const deliverableId = parseInt(id || "0", 10);
const navigate = useNavigate();
const queryClient = useQueryClient();
// Add debug log for ID
console.log(`Editing deliverable with ID: ${deliverableId}, raw ID param: ${id}`);
const [deliverable, setDeliverable] = useState<Deliverable | null>(null);
const [projects, setProjects] = useState<ProjectApi[]>([]);
const [isLoadingDeliverable, setIsLoadingDeliverable] = useState(true);
const [isLoadingProjects, setIsLoadingProjects] = useState(true);
const [error, setError] = useState<string | null>(null);
// Form setup
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
description: "",
deliveryDate: format(new Date(), "yyyy-MM-dd"),
projectId: "",
},
});
// Fetch deliverable data
useEffect(() => {
const fetchDeliverable = async () => {
if (!deliverableId) return;
try {
setIsLoadingDeliverable(true);
const data = await deliverablesApi.getById(deliverableId);
setDeliverable(data);
// Set form values
form.reset({
name: data.name,
description: data.description,
deliveryDate: format(parseISO(data.deliveryDate), "yyyy-MM-dd"),
projectId: data.projectId.toString(),
});
} catch (err) {
console.error("Error fetching deliverable:", err);
setError("Failed to load deliverable data");
} finally {
setIsLoadingDeliverable(false);
}
};
fetchDeliverable();
}, [deliverableId, form]);
// Fetch projects
useEffect(() => {
const fetchProjects = async () => {
try {
setIsLoadingProjects(true);
const fetchedProjects = await projectService.getAll();
setProjects(fetchedProjects);
} catch (err) {
console.error("Error fetching projects:", err);
// Don't set an error here, as we can still edit without projects list
} finally {
setIsLoadingProjects(false);
}
};
fetchProjects();
}, []);
// Update deliverable mutation
const updateDeliverableMutation = useMutation({
mutationFn: (data: DeliverableUpdateRequest) => deliverablesApi.update(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["deliverables"] });
queryClient.invalidateQueries({ queryKey: ["deliverable", deliverableId] });
toast.success("Deliverable updated successfully");
navigate("/deliverables");
},
onError: (error) => {
console.error("Update error:", error);
toast.error("Failed to update deliverable");
},
});
// Form submission handler
const onSubmit = (data: FormData) => {
const deliverableData: DeliverableUpdateRequest = {
id: deliverableId,
name: data.name,
description: data.description,
deliveryDate: new Date(data.deliveryDate).toISOString(),
projectId: parseInt(data.projectId, 10),
};
updateDeliverableMutation.mutate(deliverableData);
};
if (isLoadingDeliverable) {
return (
<Layout>
<div className="flex items-center justify-center min-h-screen">
<p>Loading deliverable data...</p>
</div>
</Layout>
);
}
if (error) {
return (
<Layout>
<div className="flex flex-col items-center justify-center min-h-screen gap-4">
<p className="text-red-500">{error}</p>
<Button variant="outline" onClick={() => navigate("/deliverables")}>
Back to Deliverables
</Button>
</div>
</Layout>
);
}
// Get today's date formatted for the date input
const today = format(new Date(), "yyyy-MM-dd");
return (
<Layout>
<div className="flex flex-col w-full min-h-screen p-4 md:p-6">
<div className="flex items-center justify-between mb-6">
<h1 className="text-2xl font-bold tracking-tight">Edit Deliverable</h1>
<Button variant="outline" onClick={() => navigate(`/deliverable/${deliverableId}`)}>
Cancel
</Button>
</div>
<div className="max-w-2xl w-full">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="Enter deliverable name" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<Textarea
placeholder="Enter deliverable description"
className="min-h-[120px]"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="deliveryDate"
render={({ field }) => (
<FormItem>
<FormLabel>Delivery Date</FormLabel>
<FormControl>
<Input type="date" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="projectId"
render={({ field }) => (
<FormItem>
<FormLabel>Project</FormLabel>
<FormControl>
<select
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
value={field.value}
onChange={field.onChange}
>
<option value="" disabled>
Select a project
</option>
{isLoadingProjects ? (
<option value="" disabled>
Loading projects...
</option>
) : projects.length === 0 ? (
<option value="" disabled>
No projects available
</option>
) : (
projects.map((project) => (
<option key={project.id} value={project.id}>
{project.projectName || `Project #${project.id}`}
</option>
))
)}
{deliverable && (
<option value={deliverable.projectId}>
Current Project (ID: {deliverable.projectId})
</option>
)}
</select>
</FormControl>
<FormDescription>
The project this deliverable belongs to
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end gap-4 pt-4">
<Button
type="button"
variant="outline"
onClick={() => navigate(`/deliverable/${deliverableId}`)}
>
Cancel
</Button>
<Button
type="submit"
disabled={updateDeliverableMutation.isPending}
className="bg-primary text-white hover:bg-primary/90"
>
{updateDeliverableMutation.isPending ? "Updating..." : "Update Deliverable"}
</Button>
</div>
</form>
</Form>
</div>
</div>
</Layout>
);
};
export default DeliverableEdit;