diff --git a/frontend/public/index.html b/frontend/public/index.html index e9c5822c..0f716ebf 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -16,7 +16,7 @@ - RoboList + robolist diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index bea09826..a9b125ec 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -3,6 +3,6 @@ import App from "./App"; test("renders stompy urdf link", () => { render(); - const linkElement = screen.getByText(/robolist.xyz/i); + const linkElement = screen.getByText(/Listings/i); expect(linkElement).toBeInTheDocument(); }); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5b69685d..88e665ed 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,27 +1,42 @@ import "bootstrap/dist/css/bootstrap.min.css"; -import { BrowserRouter, Link, Route, Routes } from "react-router-dom"; -import "./App.css"; - import Authentication from "components/Authentication"; import Listings from "components/Listings"; import Home from "pages/Home"; import RobotDetails from "pages/Robot"; -import { Container } from "react-bootstrap"; +import { Container, Nav, Navbar } from "react-bootstrap"; +import { BrowserRouter, Link, Route, Routes } from "react-router-dom"; +import "./App.css"; const App = () => { return ( - -

- robolist.xyz -

-

Buy and sell robots

- - - } /> - } /> - } /> - + + + + + robolist + + + + + + + + + + + + + + } /> + } /> + } /> + +
); diff --git a/frontend/src/components/Authentication.tsx b/frontend/src/components/Authentication.tsx index a4b29673..738b86f5 100644 --- a/frontend/src/components/Authentication.tsx +++ b/frontend/src/components/Authentication.tsx @@ -15,7 +15,7 @@ const Authentication = () => { return (
{authenticated ? ( - + {email} ) : ( - + ✉️ { Log In 3 && !isValidEmail(email)} target={target.current} > {(props) => Invalid email} diff --git a/frontend/src/components/Listings.tsx b/frontend/src/components/Listings.tsx index d6b43fcf..ae776067 100644 --- a/frontend/src/components/Listings.tsx +++ b/frontend/src/components/Listings.tsx @@ -1,97 +1,64 @@ -import { Carousel } from "react-bootstrap"; -import Col from "react-bootstrap/Col"; -import Container from "react-bootstrap/Container"; -import Row from "react-bootstrap/Row"; +import { Breadcrumb, Card, Col, Container, Row } from "react-bootstrap"; +import { useNavigate } from "react-router-dom"; interface ListingsResponseItem { name: string; owner: string; - links: { name: string; url: string }[]; - images: { url: string; caption: string }[]; + description: string; + id: string; + photo?: string; } interface ListingsResponse { listings: ListingsResponseItem[]; } -const Listing = ({ name, owner, links, images }: ListingsResponseItem) => { - return ( - - -

{name}

-

{owner}

-
- {links && ( - -
    - {links.map((link, key) => ( -
  • - {link.name} -
  • - ))} -
-
- )} - {images && ( - - {images.map((image, key) => ( - - {image.caption} - -

{image.caption}

-
-
- ))} -
- )} - - ); -}; - const Listings = () => { const response: ListingsResponse = { listings: [ { name: "Stompy", owner: "K-Scale Labs", - links: [ - { - name: "URDF (with STLs)", - url: "https://media.kscale.dev/stompy/latest_stl_urdf.tar.gz", - }, - { - name: "URDF (with OBJs)", - url: "https://media.kscale.dev/stompy/latest_obj_urdf.tar.gz", - }, - { - name: "MJCF", - url: "https://media.kscale.dev/stompy/latest_mjcf.tar.gz", - }, - ], - images: [ - { - url: "https://media.robolist.xyz/logo.png", - caption: "Image 1", - }, - { - url: "https://media.robolist.xyz/logo.png", - caption: "Image 2", - }, - { - url: "https://media.robolist.xyz/logo.png", - caption: "Image 3", - }, - ], + description: "An open-source humanoid robot costing less than $10k", + id: "1", + photo: "https://media.robolist.xyz/stompy.png", }, ], }; + const navigate = useNavigate(); + return ( -

Listings

- {response.listings.map((item, key) => ( - - ))} + + navigate("/")}>Home + Listings + + + + {response.listings.map( + ({ name, owner, description, id, photo }, key) => ( + + navigate(`/robots/${id}`)}> + {photo && ( + + )} + + {name} + + {owner} + + {description} + + + + ), + )} + ); }; diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index a89336bc..0ee3c8f8 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -1,18 +1,16 @@ -import { Link } from "react-router-dom"; +import { Col, Row } from "react-bootstrap"; const RobotDetails = () => { return ( -
-

Home

-

Welcome to RoboList!

-
    -
  • - Robots -
  • -
  • - Robot 1 -
  • -
+
+ + +

robolist

+

+ Buy, sell and build robot hardware and software +

+ +
); }; diff --git a/frontend/src/pages/Robot.tsx b/frontend/src/pages/Robot.tsx index 608f7df7..06a34c6d 100644 --- a/frontend/src/pages/Robot.tsx +++ b/frontend/src/pages/Robot.tsx @@ -1,12 +1,223 @@ -import { useParams } from "react-router-dom"; +import { useState } from "react"; +import { + Breadcrumb, + Button, + ButtonGroup, + Carousel, + Col, + Container, + Modal, + Row, +} from "react-bootstrap"; +import { useNavigate, useParams } from "react-router-dom"; + +interface RobotDetailsResponse { + name: string; + owner: string; + links: { name: string; url: string }[]; + images: { url: string; caption: string }[]; + bom: { name: string; part_number: string; quantity: number; price: number }[]; +} const RobotDetails = () => { const { id } = useParams(); + const [show, setShow] = useState(false); + const [imageIndex, setImageIndex] = useState(0); + + const handleClose = () => setShow(false); + const handleShow = () => setShow(true); + + // This is a placeholder before the backend is hooked up. + const response: RobotDetailsResponse = { + name: "Stompy", + owner: "K-Scale Labs", + links: [ + { + name: "URDF (with STLs)", + url: "https://media.kscale.dev/stompy/latest_stl_urdf.tar.gz", + }, + { + name: "URDF (with OBJs)", + url: "https://media.kscale.dev/stompy/latest_obj_urdf.tar.gz", + }, + { + name: "MJCF", + url: "https://media.kscale.dev/stompy/latest_mjcf.tar.gz", + }, + ], + images: [ + { + url: "https://media.robolist.xyz/stompy.png", + caption: "Stompy the robot 1", + }, + { + url: "https://media.robolist.xyz/stompy.png", + caption: "Stompy the robot 2", + }, + { + url: "https://media.robolist.xyz/stompy.png", + caption: "Stompy the robot 3", + }, + ], + bom: [ + { + name: "Actuator", + part_number: "1234", + quantity: 10, + price: 100, + }, + { + name: "Sensor", + part_number: "5678", + quantity: 5, + price: 50, + }, + ], + }; + + const { name, owner, links, images } = response; + + const navigate = useNavigate(); + return ( -
-

Robot Details

-

Robot ID: {id}

-
+ + + navigate("/")}>Home + navigate("/robots/")}> + Listings + + {name} + + + + +

{name}

+

+ {owner} +
+ ID: {id} +

+ {links && ( +
+

Links

+
    + {links.map((link, key) => ( +
  • + {link.name} +
  • + ))} +
+
+ )} + + {images && ( + + + {images.map((image, key) => ( + +
+ {image.caption} { + setImageIndex(key); + handleShow(); + }} + /> +
+ +

{image.caption}

+
+
+ ))} +
+ + )} +
+ + + +

Bill of Materials

+ + + + + + + + + + + {response.bom.map((part, key) => ( + + + + + + + ))} + +
NamePart NumberQuantityPrice
{part.name}{part.part_number}{part.quantity}${part.price}
+ +
+ + + + + {images[imageIndex].caption} ({imageIndex + 1} of {images.length}) + + + + {images[imageIndex].caption} + + + + + + + + +
); };