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 12 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
111 changes: 111 additions & 0 deletions frontend/src/hooks/rob.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import axios from "axios";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to auto-generate these interfaces from the openapi.json file for the backend?


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

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

interface Part {
name: string;
owner: string;
description: string;
images: Image[];
part_id: string;
used_by: UsedBy[];
purchase_links: PurchaseLink[];
}
interface Bom {
id: string;
name: string;
quantity: number;
price: number;
}

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

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

class rob {
private api;

constructor(baseURL: string | undefined) {
this.api = axios.create({ baseURL });
}
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");
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this pattern is repeated 3 times, consider refactoring into a helper function (might need to do some typing voodoo to make work)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i feel like the effort to make it "generic" (there are a lot of subtly different args) would not be worthwhile atm

}
}
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 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 rob("http://127.0.0.1:8080/api");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't commit this to master, it is just for testing locally. use some env variable

48 changes: 43 additions & 5 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 rob from "hooks/rob";
import { useEffect, useState } from "react";
codekansas marked this conversation as resolved.
Show resolved Hide resolved
import {
Breadcrumb,
Button,
Expand All @@ -23,18 +24,57 @@ 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);

useEffect(() => {
const fetchPart = async () => {
try {
const partData = await rob.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: part.name,
owner: part.owner,
description: part.description,
images: part.images,
purchase_links: part.purchase_links,
used_by: part.used_by,
};
/*
// This is a placeholder before the backend is hooked up.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can just delete instead of commenting out

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",
},
Expand Down Expand Up @@ -62,11 +102,9 @@ const PartDetails = () => {
},
],
};

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

const navigate = useNavigate();

return (
<>
<Breadcrumb>
Expand Down
104 changes: 77 additions & 27 deletions frontend/src/pages/Parts.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,81 @@
import rob from "hooks/rob";
import { useEffect, useState } from "react";
import { Breadcrumb, Card, Col, Row } from "react-bootstrap";
import { useNavigate } from "react-router-dom";

interface PartsResponse {
robots: {
name: string;
owner: string;
description: string;
id: string;
photo?: string;
}[];
interface Image {
caption: string;
url: string;
}

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

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

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

// interface PartsResponse {
// parts: Part[];
// }

const Parts = () => {
const response: PartsResponse = {
robots: [
{
name: "RMD X8",
owner: "MyActuator",
description: "6:1 reduction ratio motor",
id: "1",
photo: "https://media.robolist.xyz/rmd_x8.png",
},
],
};
const [partsData, setParts] = useState<Part[] | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetch_parts = async () => {
try {
const partsQuery = await rob.getParts();
setParts(partsQuery);
} catch (err) {
if (err instanceof Error) {
setError(err.message);
} else {
setError("An unexpected error occurred");
}
}
};
fetch_parts();
}, []);
// const response: PartsResponse = {
// parts: [
// {
// name: "RMD X8",
// owner: "MyActuator",
// description: "6:1 reduction ratio motor",
// part_id: "1",
// images: "https://media.robolist.xyz/rmd_x8.png",
// },
// ],
// };

const navigate = useNavigate();

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

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

return (
<>
<Breadcrumb>
Expand All @@ -34,22 +84,22 @@ const Parts = () => {
</Breadcrumb>

<Row className="mt-5">
{response.robots.map(({ name, owner, description, id, photo }, key) => (
<Col key={key} md={3} sm={6} xs={12}>
<Card onClick={() => navigate(`/part/${id}`)}>
{photo && (
{partsData.map((part) => (
<Col key={part.part_id} md={3} sm={6} xs={12}>
<Card onClick={() => navigate(`/part/${part.part_id}`)}>
{part.images[0].url && (
<Card.Img
style={{ aspectRatio: "1/1" }}
variant="top"
src={photo}
src={part.images[0].url}
/>
)}
<Card.Body>
<Card.Title>{name}</Card.Title>
<Card.Title>{part.name}</Card.Title>
<Card.Subtitle className="mb-2 text-muted">
{owner}
{part.owner}
</Card.Subtitle>
<Card.Text>{description}</Card.Text>
<Card.Text>{part.description}</Card.Text>
</Card.Body>
</Card>
</Col>
Expand Down
Loading
Loading