diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b8916ee7..ebd22785 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -16,6 +16,7 @@ import RobotDetails from "pages/RobotDetails"; import RobotForm from "pages/RobotForm"; import Robots from "pages/Robots"; import YourParts from "pages/YourParts"; +import EditPartForm from "pages/EditPartForm"; import YourRobots from "pages/YourRobots"; import { Container } from "react-bootstrap"; import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; @@ -39,6 +40,7 @@ const App = () => { } /> } /> } /> + } /> } /> } /> } /> diff --git a/frontend/src/hooks/api.tsx b/frontend/src/hooks/api.tsx index 22a841e1..60df89c1 100644 --- a/frontend/src/hooks/api.tsx +++ b/frontend/src/hooks/api.tsx @@ -208,4 +208,36 @@ export class api { } } } + public async deletePart(id: string | undefined): Promise { + const s = id; + try { + await this.api.delete(`parts/delete/${id}/`); + } catch (error) { + if (axios.isAxiosError(error)) { + console.error("Error deleting part:", error.response?.data); + throw new Error( + error.response?.data?.detail || "Error deleting part " + s, + ); + } else { + console.error("Unexpected error:", error); + throw new Error("Unexpected error"); + } + } + } + public async editPart(part: Part): Promise { + const s = part.part_name; + try { + await this.api.post(`parts/edit-part/${part.part_id}/`, part); + } catch (error) { + if (axios.isAxiosError(error)) { + console.error("Error editing part:", error.response?.data); + throw new Error( + error.response?.data?.detail || "Error editing part " + s, + ); + } else { + console.error("Unexpected error:", error); + throw new Error("Unexpected error"); + } + } + } } diff --git a/frontend/src/pages/EditPartForm.tsx b/frontend/src/pages/EditPartForm.tsx new file mode 100644 index 00000000..e3c0d1dd --- /dev/null +++ b/frontend/src/pages/EditPartForm.tsx @@ -0,0 +1,157 @@ +import { humanReadableError } from "constants/backend"; +import { useAlertQueue } from "hooks/alerts"; +import { api, Bom, Image, Part, Robot } from "hooks/api"; +import { useAuthentication } from "hooks/auth"; +import React, { ChangeEvent, FormEvent, useEffect, useState } from "react"; +import { Button, Col, Form, Row } from "react-bootstrap"; +import { useNavigate, useParams } from "react-router-dom"; + +const EditPartForm: React.FC = () => { + const auth = useAuthentication(); + const auth_api = new api(auth.api); + + // Parse the ID from the URL. + const { id } = useParams(); + + // States. + const [message, setMessage] = useState(null); + const [Part_name, setName] = useState(""); + const [Part_description, setDescription] = useState(""); + const [Part_images, setImages] = useState([]); + const [Part_id, setPartId] = useState(""); + + const { addAlert } = useAlertQueue(); + + useEffect(() => { + const fetchPart = async () => { + try { + const PartData = await auth_api.getPartById(id); + setName(PartData.part_name); + setDescription(PartData.description); + setImages(PartData.images); + setPartId(PartData.part_id); + } catch (err) { + addAlert(humanReadableError(err), "error"); + } + }; + fetchPart(); + }, [id]); + + const handleImageChange = ( + index: number, + e: ChangeEvent, + ) => { + const { name, value } = e.target; + const newImages = [...Part_images]; + newImages[index][name as keyof Image] = value; + setImages(newImages); + }; + const navigate = useNavigate(); + const handleAddImage = () => { + setImages([...Part_images, { url: "", caption: "" }]); + }; + + const handleRemoveImage = (index: number) => { + const newImages = Part_images.filter((_, i) => i !== index); + setImages(newImages); + }; + + const handleSubmit = async (event: FormEvent) => { + event.preventDefault(); + if (Part_images.length === 0) { + setMessage("Please upload at least one image."); + return; + } + const newFormData: Part = { + part_id: Part_id, + part_name: Part_name, + description: Part_description, + owner: "", + images: Part_images, + }; + try { + await auth_api.editPart(newFormData); + setMessage(`Part edited successfully.`); + navigate(`/parts/your/`); + } catch (error) { + setMessage("Error adding part "); + } + }; + + return ( + +

Edit Part

+ {message &&

{message}

} +
+ Name: + { + setName(e.target.value); + }} + value={Part_name} + required + /> + Description: + { + setDescription(e.target.value); + }} + value={Part_description} + required + /> + Images: + {Part_images.map((image, index) => ( + + + handleImageChange(index, e)} + required + /> + handleImageChange(index, e)} + required + /> + + + + + + ))} + + + + Submit: + + + + +
+ ); +}; + +export default EditPartForm; diff --git a/frontend/src/pages/PartDetails.tsx b/frontend/src/pages/PartDetails.tsx index 340cffb5..56f7216e 100644 --- a/frontend/src/pages/PartDetails.tsx +++ b/frontend/src/pages/PartDetails.tsx @@ -62,6 +62,24 @@ const PartDetails = () => { const navigate = useNavigate(); + useEffect(() => { + if (auth.isAuthenticated) { + try { + const fetchUserId = async () => { + const user_id = await auth_api.currentUser(); + setUserId(user_id); + }; + fetchUserId(); + } catch (err) { + if (err instanceof Error) { + setError(err.message); + } else { + setError("An unexpected error occurred"); + } + } + } + }, [auth.isAuthenticated]); + useEffect(() => { if (error) { addAlert(error, "error"); @@ -225,8 +243,7 @@ const PartDetails = () => { <> - {/* {part.owner === userId && ( */} - {part.owner === part.owner && ( + {part.owner === userId && ( <> @@ -243,7 +260,7 @@ const PartDetails = () => { navigate(`/edit-part/${id}/`); }} > - Edit Robot + Edit Part @@ -281,8 +298,8 @@ const PartDetails = () => {