Component 0 - Copy this React, Tailwind Component to your project
Import React, { useState, useEffect } from "react"; import * as Yup from "yup"; import { updateProject, getUsers, getProjectStatuses } from "../../utils/api"; import { Form, Button, Alert, Badge } from "react bootstrap"; const getToken = () => localStorage.getItem("token"); const EditProject = ({ project, token, onSubmit }) => { const storedToken = token || getToken(); const [formData, setFormData] = useState({ name: "", description: "", startDate: "", endDate: "", createdBy: "", status: "", teamMembers: [], attachment: null, }); const [errors, setErrors] = useState({}); const [statusesList, setStatusesList] = useState([]); const [users, setUsers] = useState([]); useEffect(() => { const fetchData = async () => { try { const tokenToUse = token || getToken(); // Ensure token is present if (!tokenToUse) { console.error("Token is missing, cannot fetch users or statuses."); return; } const usersData = await getUsers(tokenToUse); const statuses = await getProjectStatuses(tokenToUse); setUsers(usersData); setStatusesList(statuses); } catch (err) { console.error("Error fetching users or statuses:", err); } }; fetchData(); }, [token]); useEffect(() => { if (project) { setFormData({ name: project.name || "", description: project.description || "", startDate: project.startDate || "", endDate: project.endDate || "", createdBy: project.createdBy ? String(project.createdBy) : "", // Ensure it's a string for comparison in the dropdown status: project.status || "", teamMembers: project.teamMembers?.map((id) => String(id)) || [], // Ensure teamMembers is an array of strings attachment: null, }); } }, [project]); const schema = Yup.object().shape({ name: Yup.string().required("Project name is required").max(350, "Project name cannot be longer than 350 characters"), description: Yup.string().required("Description is required").max(350, "Description cannot be longer than 350 characters"), createdBy: Yup.string().required("Created By is required"), status: Yup.string().required("Status is required"), teamMembers: Yup.array().of(Yup.string()).min(1, "At least one team member is required"), }); const handleChange = async (e) => { const { name, value } = e.target; setFormData((prev) => ({ ...prev, [name]: value })); try { await Yup.reach(schema, name).validate(value); setErrors((prev) => ({ ...prev, [name]: "" })); } catch (error) { setErrors((prev) => ({ ...prev, [name]: error.message })); } }; const handleMultiSelect = (e) => { const selected = Array.from(e.target.selectedOptions, (o) => o.value); setFormData((prev) => ({ ...prev, teamMembers: selected })); }; const handleRemoveMember = (id) => { setFormData((prev) => ({ ...prev, teamMembers: prev.teamMembers.filter((m) => m !== id), })); }; const getStatusInt = (status) => { const map = { completed: 1, onhold: 2, cancelled: 3, inprogress: 4, new: 5 }; return map[status] || 0; }; const handleSubmit = async (e) => { e.preventDefault(); try { await schema.validate(formData, { abortEarly: false }); setErrors({}); const mappedData = new FormData(); mappedData.append("name", formData.name); mappedData.append("description", formData.description); mappedData.append("startDate", formData.startDate); mappedData.append("endDate", formData.endDate); mappedData.append("createdBy", parseInt(formData.createdBy)); mappedData.append("status", getStatusInt(formData.status)); formData.teamMembers.forEach((id) => mappedData.append("memberId", parseInt(id)) ); if (formData.attachment) { mappedData.append("attachment", formData.attachment); } const updatedProject = await updateProject( project.id, mappedData, storedToken, true ); onSubmit && onSubmit(updatedProject); alert("Project updated successfully!"); const modalElement = document.getElementById("editModal"); if (modalElement) { const modalInstance = bootstrap.Modal.getInstance(modalElement); modalInstance?.hide(); } } catch (error) { if (error instanceof Yup.ValidationError) { const validationErrors = {}; error.inner.forEach((err) => { if (err.path) validationErrors[err.path] = err.message; }); setErrors(validationErrors); } else { console.error("Submission error:", error); } } }; return ( <Form onSubmit={handleSubmit} className="p 3"> {Object.keys(errors).length > 0 && ( <Alert variant="danger">Please fix the highlighted fields.</Alert> )} <Form.Group className="mb 3"> <Form.Label>Project Name</Form.Label> <Form.Control type="text" name="name" value={formData.name} onChange={handleChange} isInvalid={!!errors.name} /> <Form.Control.Feedback type="invalid">{errors.name}</Form.Control.Feedback> </Form.Group> <Form.Group className="mb 3"> <Form.Label>Project Description</Form.Label> <Form.Control as="textarea" name="description" value={formData.description} onChange={handleChange} isInvalid={!!errors.description} /> <Form.Control.Feedback type="invalid">{errors.description}</Form.Control.Feedback> </Form.Group> <Form.Group className="mb 3"> <Form.Label>Created By</Form.Label> <Form.Select name="createdBy" value={formData.createdBy} onChange={handleChange} isInvalid={!!errors.createdBy} > <option value="">Select Creator</option> {users.map((user) => ( <option key={user.id} value={user.id}> {user.userName} </option> ))} </Form.Select> <Form.Control.Feedback type="invalid">{errors.createdBy}</Form.Control.Feedback> </Form.Group> <Form.Group className="mb 3"> <Form.Label>Start Date</Form.Label> <Form.Control type="date" name="startDate" value={formData.startDate} onChange={handleChange} isInvalid={!!errors.startDate} /> <Form.Control.Feedback type="invalid">{errors.startDate}</Form.Control.Feedback> </Form.Group> <Form.Group className="mb 3"> <Form.Label>End Date</Form.Label> <Form.Control type="date" name="endDate" value={formData.endDate} onChange={handleChange} isInvalid={!!errors.endDate} /> <Form.Control.Feedback type="invalid">{errors.endDate}</Form.Control.Feedback> </Form.Group> <Form.Group className="mb 3"> <Form.Label>Status</Form.Label> <Form.Select name="status" value={formData.status} onChange={handleChange} isInvalid={!!errors.status} > <option value="">Select Status</option> {statusesList.map((status) => ( <option key={status.id} value={status.status}> {status.status} </option> ))} </Form.Select> <Form.Control.Feedback type="invalid">{errors.status}</Form.Control.Feedback> </Form.Group> <Form.Group className="mb 3"> <Form.Label>Attachment</Form.Label> <Form.Control type="file" name="attachment" onChange={(e) => setFormData((prev) => ({ ...prev, attachment: e.target.files[0] })) } /> {project?.attachment?.fileName && ( <div className="mt 2 text muted"> Current File: <strong>{project.attachment.fileName}</strong> </div> )} </Form.Group> <Form.Group className="mb 3"> <Form.Label>Assign Members</Form.Label> <div className="d flex flex wrap"> {users.map((user) => ( <Badge key={user.id} bg={formData.teamMembers.includes(user.id) ? "primary" : "light"} className="me 2" onClick={() => handleRemoveMember(user.id)} style={{ cursor: "pointer" }} > {user.userName} {formData.teamMembers.includes(user.id) && ( <span style={{ marginLeft: "5px" }}>×</span> )} </Badge> ))} </div> {errors.teamMembers && <div className="text danger">{errors.teamMembers}</div>} </Form.Group> <div className="text center"> <Button variant="primary" type="submit"> Update Project </Button> </div> </Form> ); }; export default EditProject;