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}
}
+ {
+ 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 = () => {