import React, { useState, useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { ArrowLeft, Loader2 } from "lucide-react"; import { useForm } from "react-hook-form"; import Layout from "@/components/layout/layout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { toast } from "@/lib/custom-toast"; import { issuesApi, projectService, deliverablesApi, employeeService } from "@/lib/api"; import { IssueUpdateRequest, IssueApi, ProjectApi, Deliverable, Employee } from "@/types"; import { Dropdown, DropdownOption } from "@/components/ui/dropdown"; const IssueEdit = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [isSubmitting, setIsSubmitting] = useState(false); const [isLoading, setIsLoading] = useState(true); const [issue, setIssue] = useState(null); const [projects, setProjects] = useState([]); const [deliverables, setDeliverables] = useState([]); const [employees, setEmployees] = useState([]); const [filteredDeliverables, setFilteredDeliverables] = useState([]); const [selectedProjectId, setSelectedProjectId] = useState(0); const form = useForm({ defaultValues: { id: 0, biCode: "", type: "User Story", description: "", estimates: "", esUnit: 0, priority: "Medium", severity: "Medium", reportedBy: 0, assignedTo: 0, remainingHr: 0, title: "", projectId: 0, deliverablesId: 0 } }); // Step 1: Fetch the issue data first useEffect(() => { const fetchIssue = async () => { if (!id) { navigate("/issues"); return; } const issueId = parseInt(id); if (isNaN(issueId)) { navigate("/issues"); return; } try { setIsLoading(true); const issueData = await issuesApi.getById(issueId); setIssue(issueData); setSelectedProjectId(issueData.projectId); // Set form values form.reset({ ...issueData }); } catch (error) { console.error("Error fetching issue:", error); toast.error("Failed to load issue data. Please try again."); navigate("/issues"); } finally { setIsLoading(false); } }; fetchIssue(); }, [id, navigate, form]); // Step 2: Fetch projects separately useEffect(() => { const fetchProjects = async () => { try { const projectsData = await projectService.getAll(); setProjects(projectsData); } catch (error) { console.error("Error fetching projects:", error); toast.error("Failed to load projects. Some features may be limited."); } }; fetchProjects(); }, []); // Step 3: Fetch deliverables, but only after issue and projects are loaded useEffect(() => { if (!selectedProjectId) return; const fetchDeliverables = async () => { try { const deliverablesData = await deliverablesApi.getAll(); setDeliverables(deliverablesData); // Filter deliverables based on the current project const filtered = deliverablesData.filter(d => d.projectId === selectedProjectId); setFilteredDeliverables(filtered); } catch (error) { console.error("Error fetching deliverables:", error); toast.error("Failed to load deliverables. Some features may be limited."); } }; fetchDeliverables(); }, [selectedProjectId]); // Step 4: Fetch employees separately useEffect(() => { const fetchEmployees = async () => { try { const employeesData = await employeeService.getAll(); setEmployees(employeesData); } catch (error) { console.error("Error fetching employees:", error); toast.error("Failed to load employees. Some features may be limited."); } }; fetchEmployees(); }, []); // Handle project change to filter deliverables const handleProjectChange = (projectId: string) => { const numProjectId = parseInt(projectId); setSelectedProjectId(numProjectId); form.setValue("projectId", numProjectId); // Reset deliverable selection when project changes form.setValue("deliverablesId", 0); }; const handleSubmit = async (data: IssueUpdateRequest) => { if (!validateForm(data)) { return; } setIsSubmitting(true); try { await issuesApi.update(data); toast.success("Issue updated successfully"); navigate(`/issues/${data.id}`); } catch (error) { console.error("Error updating issue:", error); toast.error("Failed to update issue. Please try again."); } finally { setIsSubmitting(false); } }; const validateForm = (data: IssueUpdateRequest): boolean => { if (!data.title.trim()) { toast.error("Title is required"); return false; } if (!data.description.trim()) { toast.error("Description is required"); return false; } if (data.projectId <= 0) { toast.error("Project is required"); return false; } if (data.reportedBy <= 0) { toast.error("Reported By is required"); return false; } if (data.assignedTo <= 0) { toast.error("Assigned To is required"); return false; } if (data.deliverablesId <= 0) { toast.error("Deliverable is required"); return false; } return true; }; // Convert data to Dropdown options format const issueTypeOptions: DropdownOption[] = ["User Story", "Bug", "Task", "Documentation", "Enhancement"] .map(type => ({ label: type, value: type })); const priorityOptions: DropdownOption[] = ["Low", "Medium", "High", "Critical"] .map(priority => ({ label: priority, value: priority })); const severityOptions: DropdownOption[] = ["Low", "Medium", "High", "Critical"] .map(severity => ({ label: severity, value: severity })); const projectOptions: DropdownOption[] = projects.map(project => ({ label: project.projectName, value: project.id.toString() })); const deliverableOptions: DropdownOption[] = filteredDeliverables.map(deliverable => ({ label: deliverable.name, value: deliverable.id.toString() })); const employeeOptions: DropdownOption[] = employees.map(employee => ({ label: `${employee.firstName} ${employee.lastName}`, value: employee.id.toString() })); const estimateUnitOptions: DropdownOption[] = [ { label: "Hours", value: "Hours" }, { label: "Days", value: "Days" } ]; if (isLoading) { return (
); } return (

Edit Feature

Update issue #{id} details

Basic Information Edit the basic details of the Feature
( Title * )} /> ( BI Code )} />
( Description *