Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parts and robots #46

Merged
merged 29 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a704fea
convert from using tokens to using api keys
is2ac2 Jun 3, 2024
8aaa3b0
convert frontend to only use api keys instead of refresh tokens
is2ac2 Jun 3, 2024
129cd27
call the logout endpoint
is2ac2 Jun 3, 2024
ce65ca4
Add back google authentication
is2ac2 Jun 3, 2024
dfaf27f
fix lint
is2ac2 Jun 3, 2024
5a73fd9
add otp wrapper
is2ac2 Jun 3, 2024
ca37d7d
add config fields
is2ac2 Jun 3, 2024
fb54ec6
added robot backend
is2ac2 Jun 3, 2024
632cc58
hooked up robot backend to frontend
is2ac2 Jun 3, 2024
d6b5dae
Connected robots page and individual robots to the dynamodb local bac…
is2ac2 Jun 4, 2024
de9e08a
connected parts and robots frontend to local dynamodb backend
is2ac2 Jun 4, 2024
236bd11
Merge branch 'master' into parts_and_robots
chennisden Jun 4, 2024
f72e870
added create robot
is2ac2 Jun 5, 2024
50717bc
minor fixes
is2ac2 Jun 5, 2024
3d35a71
Merge branch 'master' into parts_and_robots
chennisden Jun 5, 2024
ca8d490
fix everything
chennisden Jun 5, 2024
f280dc6
only add necessary cors stuff
chennisden Jun 5, 2024
4c9fa7b
frnotend improvements for robocreation button
chennisden Jun 5, 2024
0a76a47
change add robot path
chennisden Jun 5, 2024
8b10b6c
hooks/rob -> hooks/api
chennisden Jun 5, 2024
ffab9cb
delete unwanted dynamodb global var
chennisden Jun 5, 2024
146c1e5
Fix dynamodb -> Crud
chennisden Jun 5, 2024
35af3ce
email_utils -> utils/email
chennisden Jun 5, 2024
e994314
fix add_robot -> add/robot
chennisden Jun 5, 2024
5f803d0
fix add_robot -> add/robot
chennisden Jun 5, 2024
0bc5936
samesite lax
chennisden Jun 5, 2024
a65dae9
insert actual user id
chennisden Jun 5, 2024
c7f9a40
fix all for real
chennisden Jun 5, 2024
3590f62
pass tests
chennisden Jun 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import NotFound from "pages/NotFound";
import PartDetails from "pages/PartDetails";
import Parts from "pages/Parts";
import RobotDetails from "pages/RobotDetails";
import RobotForm from "pages/RobotForm";
import Robots from "pages/Robots";
import { Container } from "react-bootstrap";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
Expand All @@ -35,6 +36,7 @@ const App = () => {
<Route path="/parts/" element={<Parts />} />
<Route path="/part/:id" element={<PartDetails />} />
<Route path="/404" element={<NotFound />} />
<Route path="/add/robot" element={<RobotForm />} />
<Route path="*" element={<NotFoundRedirect />} />
</Routes>
</Container>
Expand Down
134 changes: 134 additions & 0 deletions frontend/src/hooks/api.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import axios from "axios";

export interface PurchaseLink {
url: string;
price: number;
name: string;
}

export interface UsedBy {
name: string;
id: string;
stars: number;
}

export interface Part {
name: string;
owner: string;
description: string;
images: Image[];
part_id: string;
used_by: UsedBy[];
purchase_links: PurchaseLink[];
}

export interface Bom {
id: string;
name: string;
quantity: number;
price: number;
}

export interface Image {
caption: string;
url: string;
}

export interface Robot {
robot_id: string;
name: string;
description: string;
owner: string;
bom: Bom[];
images: Image[];
}

class api {
private api;

constructor(baseURL: string | undefined) {
this.api = axios.create({
baseURL,
headers: {
"Content-Type": "application/json",
},
withCredentials: true, // Ensure credentials are sent
});
}
public async getRobots(): Promise<Robot[]> {
try {
const response = await this.api.get("/robots");
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
console.error("Error fetching robots:", error.response?.data);
throw new Error(
error.response?.data?.detail || "Error fetching robots",
);
} else {
console.error("Unexpected error:", error);
throw new Error("Unexpected error");
}
}
}
public async getRobotById(robotId: string | undefined): Promise<Robot> {
try {
const response = await this.api.get(`/robots/${robotId}`);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
console.error("Error fetching robot:", error.response?.data);
throw new Error(error.response?.data?.detail || "Error fetching robot");
} else {
console.error("Unexpected error:", error);
throw new Error("Unexpected error");
}
}
}
public async getPartById(partId: string | undefined): Promise<Part> {
try {
const response = await this.api.get(`/parts/${partId}`);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
console.error("Error fetching robot:", error.response?.data);
throw new Error(error.response?.data?.detail || "Error fetching robot");
} else {
console.error("Unexpected error:", error);
throw new Error("Unexpected error");
}
}
}
public async addRobot(robot: Robot): Promise<void> {
const s = robot.name;
try {
await this.api.post("/add/robot/", robot);
} catch (error) {
if (axios.isAxiosError(error)) {
console.error("Error adding robot:", error.response?.data);
throw new Error(
error.response?.data?.detail || "Error adding robot " + s,
);
} else {
console.error("Unexpected error:", error);
throw new Error("Unexpected error");
}
}
}
public async getParts(): Promise<Part[]> {
try {
const response = await this.api.get("/parts");
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
console.error("Error fetching parts:", error.response?.data);
throw new Error(error.response?.data?.detail || "Error fetching parts");
} else {
console.error("Unexpected error:", error);
throw new Error("Unexpected error");
}
}
}
}

export default new api("http://127.0.0.1:8080/api");
31 changes: 25 additions & 6 deletions frontend/src/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { Card, Col, Row } from "react-bootstrap";
import { Button, Card, Col, Row } from "react-bootstrap";
import { useNavigate } from "react-router-dom";

const Home = () => {
const navigate = useNavigate();

return (
<Col className="pt-5">
<Row className="mb-5">
<div className="flex-column pt-5 gap-4" style={{ display: "flex" }}>
<Row className="mb-4">
<h1 className="display-4">robolist</h1>
<p className="lead">Buy and sell robots and robot parts</p>
</Row>
<Row>
<Col md={6} sm={12} className="mt-2">
<Col md={6} sm={12}>
<Card onClick={() => navigate(`/robots`)}>
<Card.Body>
<Card.Title>Robots</Card.Title>
<Card.Text>Buy and sell robot</Card.Text>
</Card.Body>
</Card>
</Col>
<Col md={6} sm={12} className="mt-2">
<Col md={6} sm={12}>
<Card onClick={() => navigate(`/parts`)}>
<Card.Body>
<Card.Title>Parts</Card.Title>
Expand All @@ -28,7 +28,26 @@ const Home = () => {
</Card>
</Col>
</Row>
</Col>
<Row>
<Col sm={12}>
<Button
variant="success"
size="lg"
style={{
backgroundColor: "light-green",
borderColor: "black",
padding: "10px",
width: "100%",
}}
onClick={() => {
navigate("/add/robot");
}}
>
Make a Robot
</Button>
</Col>
</Row>
</div>
);
};

Expand Down
74 changes: 38 additions & 36 deletions frontend/src/pages/PartDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState } from "react";
import api from "hooks/api";
import { useEffect, useState } from "react";
import {
Breadcrumb,
Button,
Expand All @@ -23,50 +24,51 @@ interface PartDetailsResponse {
const PartDetails = () => {
const { id } = useParams();
const [show, setShow] = useState(false);
const [part, setPart] = useState<PartDetailsResponse | null>(null);
const [imageIndex, setImageIndex] = useState(0);
const [error, setError] = useState<string | null>(null);

const handleClose = () => setShow(false);
const handleShow = () => setShow(true);

// This is a placeholder before the backend is hooked up.
useEffect(() => {
const fetchPart = async () => {
try {
const partData = await api.getPartById(id);
setPart(partData);
} catch (err) {
if (err instanceof Error) {
setError(err.message);
} else {
setError("An unexpected error occurred");
}
}
};
fetchPart();
}, [id]);

const navigate = useNavigate();

useEffect(() => {
if (error) {
navigate("/404"); // Redirect to a 404 page
}
}, [error, navigate]);

if (!part) {
return <p>Loading</p>;
}

const response: PartDetailsResponse = {
name: "RMD X8",
owner: "MyActuator",
description: `The RMD X8 is a quasi-direct drive motor from MyActuator.`,
images: [
{
url: "https://media.robolist.xyz/rmd_x8.png",
caption: "Actuator 1",
},
{
url: "https://media.robolist.xyz/rmd_x8.png",
caption: "Actuator 2",
},
{
url: "https://media.robolist.xyz/rmd_x8.png",
caption: "Actuator 3",
},
],
purchase_links: [
{
name: "RobotShop",
url: "https://www.robotshop.com/products/myactuator-rmd-x8-v3-can-bus-16-helical-mc-x-500-o-brushless-servo-driver",
price: 389,
},
],
used_by: [
{
name: "Stompy",
id: "1234",
stars: 5,
},
],
name: part.name,
owner: part.owner,
description: part.description,
images: part.images,
purchase_links: part.purchase_links,
used_by: part.used_by,
};

const { name, owner, description, images } = response;

const navigate = useNavigate();

return (
<>
<Breadcrumb>
Expand Down
Loading
Loading