diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b7065899..5d36697b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,6 +18,7 @@ "nth-check": ">=2.0.1", "postcss": ">=8.4.31", "react": "^18.3.1", + "react-bootstrap-icons": "^1.11.4", "react-dom": "^18.3.1", "react-markdown": "^9.0.1", "react-scripts": "5.0.1", @@ -18729,6 +18730,18 @@ } } }, + "node_modules/react-bootstrap-icons": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/react-bootstrap-icons/-/react-bootstrap-icons-1.11.4.tgz", + "integrity": "sha512-lnkOpNEZ/Zr7mNxvjA9efuarCPSgtOuGA55XiRj7ASJnBjb1wEAdtJOd2Aiv9t07r7FLI1IgyZPg9P6jqWD/IA==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": ">=16.8.6" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 6a042ebf..e53d524b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,7 @@ "nth-check": ">=2.0.1", "postcss": ">=8.4.31", "react": "^18.3.1", + "react-bootstrap-icons": "^1.11.4", "react-dom": "^18.3.1", "react-markdown": "^9.0.1", "react-scripts": "5.0.1", @@ -42,11 +43,21 @@ ] }, "devDependencies": { + "@babel/eslint-parser": "^7.24.6", "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/preset-typescript": "^7.24.6", + "@eslint/compat": "*", + "@eslint/js": "*", "@types/jest": "^29.5.12", "axios": "^1.7.2", + "babel-eslint": "*", "bootstrap": "^5.3.3", + "eslint": "^8.0.0", + "eslint-config-prettier": "*", + "eslint-import-resolver-typescript": "*", + "eslint-plugin-import": "*", + "eslint-plugin-prettier": "*", + "eslint-plugin-react": "*", "globals": "^15.3.0", "nodemon": "^3.1.0", "prettier": "^3.2.5", @@ -54,16 +65,6 @@ "react-bootstrap": "^2.10.2", "react-router-dom": "^6.23.1", "ts-jest": "^29.1.4", - "eslint": "^8.0.0", - "@babel/eslint-parser": "^7.24.6", - "babel-eslint": "*", - "@eslint/compat": "*", - "@eslint/js": "*", - "eslint-config-prettier": "*", - "eslint-import-resolver-typescript": "*", - "eslint-plugin-prettier": "*", - "eslint-plugin-react": "*", - "eslint-plugin-import": "*", "typescript-eslint": "*" } } diff --git a/frontend/src/App.css b/frontend/src/App.css index e69de29b..fec129be 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -0,0 +1,15 @@ +body.dark { + background-color: #343a40 !important; + color: #ffffff !important; +} + +body.light { + background-color: #f8f9fa !important; + color: #000000 !important; +} + +.navbar { + width: 100% !important; + margin: 0; + padding: 5px; +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a02697ad..c3c140de 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,10 +1,11 @@ import "bootstrap/dist/css/bootstrap.min.css"; import TopNavbar from "components/nav/TopNavbar"; import NotFoundRedirect from "components/NotFoundRedirect"; -import ComponentDetails from "pages/ComponentDetails"; -import Components from "pages/Components"; +import { ThemeProvider } from "hooks/theme"; import Home from "pages/Home"; import NotFound from "pages/NotFound"; +import PartDetails from "pages/PartDetails"; +import Parts from "pages/Parts"; import RobotDetails from "pages/RobotDetails"; import Robots from "pages/Robots"; import { Container } from "react-bootstrap"; @@ -13,23 +14,25 @@ import "./App.css"; const App = () => { return ( - - - + + + + - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + - - + + ); }; diff --git a/frontend/src/components/nav/Sidebar.tsx b/frontend/src/components/nav/Sidebar.tsx index 7a348a41..aac2fa2c 100644 --- a/frontend/src/components/nav/Sidebar.tsx +++ b/frontend/src/components/nav/Sidebar.tsx @@ -10,16 +10,10 @@ const Sidebar = ({ show, onHide }: Props) => { return ( - Offcanvas + Settings - -

- Some text as placeholder. In real life you can have the elements - you have chosen. Like, text, images, lists, etc. -

-
diff --git a/frontend/src/components/nav/TopNavbar.tsx b/frontend/src/components/nav/TopNavbar.tsx index 50319f92..42f8ce05 100644 --- a/frontend/src/components/nav/TopNavbar.tsx +++ b/frontend/src/components/nav/TopNavbar.tsx @@ -1,14 +1,17 @@ import Sidebar from "components/nav/Sidebar"; +import { useTheme } from "hooks/theme"; import { useState } from "react"; import { Container, Nav, Navbar } from "react-bootstrap"; +import { GearFill, MoonFill, SunFill } from "react-bootstrap-icons"; import { Link } from "react-router-dom"; const TopNavbar = () => { const [showSidebar, setShowSidebar] = useState(false); + const { theme, setTheme } = useTheme(); return ( <> - + robolist @@ -16,13 +19,14 @@ const TopNavbar = () => { diff --git a/frontend/src/hooks/theme.tsx b/frontend/src/hooks/theme.tsx index 3ccbc1a1..74f0521b 100644 --- a/frontend/src/hooks/theme.tsx +++ b/frontend/src/hooks/theme.tsx @@ -17,11 +17,11 @@ interface ThemeColors { const COLORS: { [key in Theme]: ThemeColors } = { light: { - backgroundColor: "#f5f2ef", + backgroundColor: "#ffffff", color: "#201a42", }, dark: { - backgroundColor: "#201a42", + backgroundColor: "#000000", color: "#f5f2ef", }, }; @@ -62,6 +62,8 @@ export const ThemeProvider = (props: ThemeProviderProps) => { useEffect(() => { document.body.setAttribute("data-bs-theme", theme); + document.body.classList.toggle("dark-mode", theme === "dark"); + document.body.classList.toggle("light-mode", theme === "light"); document.body.style.backgroundColor = COLORS[theme].backgroundColor; document.body.style.color = COLORS[theme].color; }, [theme]); diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 0ee3c8f8..03db6453 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -1,16 +1,37 @@ -import { Col, Row } from "react-bootstrap"; +import { Card, Col, Row } from "react-bootstrap"; +import { useNavigate } from "react-router-dom"; const RobotDetails = () => { + const navigate = useNavigate(); + return (
- - + +

robolist

Buy, sell and build robot hardware and software

- -
+
+ + + navigate(`/robots`)}> + + Robots + Buy and sell robot + + + + + navigate(`/parts`)}> + + Parts + Buy and sell robot parts + + + + +
); }; diff --git a/frontend/src/pages/ComponentDetails.tsx b/frontend/src/pages/PartDetails.tsx similarity index 95% rename from frontend/src/pages/ComponentDetails.tsx rename to frontend/src/pages/PartDetails.tsx index def5ed28..c95fc6db 100644 --- a/frontend/src/pages/ComponentDetails.tsx +++ b/frontend/src/pages/PartDetails.tsx @@ -12,7 +12,7 @@ import { import Markdown from "react-markdown"; import { Link, useNavigate, useParams } from "react-router-dom"; -interface ComponentDetailsResponse { +interface PartDetailsResponse { name: string; owner: string; description: string; @@ -21,7 +21,7 @@ interface ComponentDetailsResponse { used_by: { name: string; id: string; stars: number }[]; } -const ComponentDetails = () => { +const PartDetails = () => { const { id } = useParams(); const [show, setShow] = useState(false); const [imageIndex, setImageIndex] = useState(0); @@ -30,7 +30,7 @@ const ComponentDetails = () => { const handleShow = () => setShow(true); // This is a placeholder before the backend is hooked up. - const response: ComponentDetailsResponse = { + const response: PartDetailsResponse = { name: "RMD X8", owner: "MyActuator", description: `The RMD X8 is a quasi-direct drive motor from MyActuator.`, @@ -72,14 +72,14 @@ const ComponentDetails = () => { navigate("/")}>Home - navigate("/components/")}> - Components + navigate("/parts/")}> + Parts {name} - - + +

{name}

@@ -248,4 +248,4 @@ const ComponentDetails = () => { ); }; -export default ComponentDetails; +export default PartDetails; diff --git a/frontend/src/pages/Components.tsx b/frontend/src/pages/Parts.tsx similarity index 84% rename from frontend/src/pages/Components.tsx rename to frontend/src/pages/Parts.tsx index ec7335a5..f6edbd80 100644 --- a/frontend/src/pages/Components.tsx +++ b/frontend/src/pages/Parts.tsx @@ -1,7 +1,7 @@ import { Breadcrumb, Card, Col, Container, Row } from "react-bootstrap"; import { useNavigate } from "react-router-dom"; -interface ComponentsResponse { +interface PartsResponse { robots: { name: string; owner: string; @@ -11,8 +11,8 @@ interface ComponentsResponse { }[]; } -const Components = () => { - const response: ComponentsResponse = { +const Parts = () => { + const response: PartsResponse = { robots: [ { name: "RMD X8", @@ -30,13 +30,13 @@ const Components = () => { navigate("/")}>Home - Components + Parts {response.robots.map(({ name, owner, description, id, photo }, key) => ( - navigate(`/component/${id}`)}> + navigate(`/part/${id}`)}> {photo && ( { ); }; -export default Components; +export default Parts; diff --git a/frontend/src/pages/RobotDetails.tsx b/frontend/src/pages/RobotDetails.tsx index 364e69a0..94ee687a 100644 --- a/frontend/src/pages/RobotDetails.tsx +++ b/frontend/src/pages/RobotDetails.tsx @@ -133,7 +133,7 @@ Stompy is designed to be a versatile platform for research and development in le {response.bom.map((part, key) => ( - {part.name} + {part.name} {part.quantity} ${part.price}