From d2a2f8193e8ab975096b4ea9a30f78ebd524bfe4 Mon Sep 17 00:00:00 2001 From: Winston Hsiao <96440583+Winston-Hsiao@users.noreply.github.com> Date: Fri, 2 Aug 2024 22:10:05 -0400 Subject: [PATCH] [ IMPROVEMENT ] Landing page redesign (#231) * base for landing page redesign * landing page image based on darkmode state * refactor/style improvements * footer, landing page refined, refactor code * react device detect for mobile specific layout * social link component for footer * initial landing page done * fix build error * resolve PR comments --- frontend/package-lock.json | 43 ++++++- frontend/package.json | 5 +- frontend/public/manifest.json | 4 +- frontend/src/App.tsx | 10 +- frontend/src/components/Container.tsx | 21 +++ frontend/src/components/footer/Footer.tsx | 97 ++++++++++++++ frontend/src/components/footer/SocialLink.tsx | 43 +++++++ frontend/src/components/home/Features.tsx | 66 ++++++++++ frontend/src/components/nav/Navbar.tsx | 14 +- .../hooks/{dark_mode.tsx => useDarkMode.tsx} | 0 frontend/src/images/LandingDark.png | Bin 0 -> 4440854 bytes frontend/src/images/LandingLight.png | Bin 0 -> 5700613 bytes frontend/src/pages/Home.tsx | 120 ++++++++++++++---- frontend/src/types/colors.ts | 4 + 14 files changed, 383 insertions(+), 44 deletions(-) create mode 100644 frontend/src/components/Container.tsx create mode 100644 frontend/src/components/footer/Footer.tsx create mode 100644 frontend/src/components/footer/SocialLink.tsx create mode 100644 frontend/src/components/home/Features.tsx rename frontend/src/hooks/{dark_mode.tsx => useDarkMode.tsx} (100%) create mode 100644 frontend/src/images/LandingDark.png create mode 100644 frontend/src/images/LandingLight.png create mode 100644 frontend/src/types/colors.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 52c7b07c..b0301b7f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { - "name": "frontend", - "version": "0.1.0", + "name": "k-scale-store", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "frontend", - "version": "0.1.0", + "name": "k-scale-store", + "version": "0.1.1", "dependencies": { "@hookform/resolvers": "^3.9.0", "@radix-ui/react-slot": "^1.1.0", @@ -28,6 +28,7 @@ "postcss": ">=8.4.31", "radix-ui": "^1.0.1", "react": "^18.3.1", + "react-device-detect": "^2.2.3", "react-dom": "^18.3.1", "react-dropzone": "^14.2.3", "react-hook-form": "^7.52.1", @@ -25328,6 +25329,18 @@ "node": ">=8" } }, + "node_modules/react-device-detect": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-device-detect/-/react-device-detect-2.2.3.tgz", + "integrity": "sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw==", + "dependencies": { + "ua-parser-js": "^1.0.33" + }, + "peerDependencies": { + "react": ">= 0.14.0", + "react-dom": ">= 0.14.0" + } + }, "node_modules/react-devtools-core": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-5.3.1.tgz", @@ -30644,6 +30657,28 @@ "node": ">=10" } }, + "node_modules/ua-parser-js": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.38.tgz", + "integrity": "sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "engines": { + "node": "*" + } + }, "node_modules/uid": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 8d9f4536..6bebf138 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { - "name": "frontend", - "version": "0.1.0", + "name": "k-scale-store", + "version": "0.1.1", "private": true, "dependencies": { "@hookform/resolvers": "^3.9.0", @@ -23,6 +23,7 @@ "postcss": ">=8.4.31", "radix-ui": "^1.0.1", "react": "^18.3.1", + "react-device-detect": "^2.2.3", "react-dom": "^18.3.1", "react-dropzone": "^14.2.3", "react-hook-form": "^7.52.1", diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json index 080d6c77..372be285 100644 --- a/frontend/public/manifest.json +++ b/frontend/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "K-Scale", + "name": "K-Scale Store", "icons": [ { "src": "favicon.ico", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c0662e0e..b6326243 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,8 +1,10 @@ +import Container from "components/Container"; +import Footer from "components/footer/Footer"; import Navbar from "components/nav/Navbar"; import NotFoundRedirect from "components/NotFoundRedirect"; import { AlertQueue, AlertQueueProvider } from "hooks/alerts"; import { AuthenticationProvider } from "hooks/auth"; -import { DarkModeProvider } from "hooks/dark_mode"; +import { DarkModeProvider } from "hooks/useDarkMode"; import About from "pages/About"; import APIKeys from "pages/APIKeys"; import Browse from "pages/Browse"; @@ -25,8 +27,7 @@ const App = () => {
- -
+ } /> } /> @@ -40,7 +41,8 @@ const App = () => { } /> } /> -
+ +
diff --git a/frontend/src/components/Container.tsx b/frontend/src/components/Container.tsx new file mode 100644 index 00000000..5c97cd91 --- /dev/null +++ b/frontend/src/components/Container.tsx @@ -0,0 +1,21 @@ +import { ReactNode } from "react"; +import { useLocation } from "react-router-dom"; + +interface ContainerProps { + children: ReactNode; +} + +const Container = (props: ContainerProps) => { + const { children } = props; + const location = useLocation(); + const { pathname } = location; + + // Landing page/home path + if (pathname === "/") { + return
{children}
; + } + + return
{children}
; +}; + +export default Container; diff --git a/frontend/src/components/footer/Footer.tsx b/frontend/src/components/footer/Footer.tsx new file mode 100644 index 00000000..c73ce56c --- /dev/null +++ b/frontend/src/components/footer/Footer.tsx @@ -0,0 +1,97 @@ +import { FaDiscord, FaGithub, FaLinkedinIn } from "react-icons/fa"; +import { Link, useLocation } from "react-router-dom"; +import { + DiscordPrimaryColor, + GithubPrimaryColor, + LinkedinPrimaryColor, +} from "types/colors"; +import SocialLink from "./SocialLink"; + +const Footer = () => { + const location = useLocation(); + const { pathname } = location; + + // Show/hide footer based on pathname + // - to hide footer on a page add path to this + const showFooter = + pathname?.startsWith("/browse") === false && + pathname?.startsWith("/some-other-path-to-hide-footer") === false; + + if (!showFooter) { + return null; + } + + return ( + + ); +}; + +export default Footer; diff --git a/frontend/src/components/footer/SocialLink.tsx b/frontend/src/components/footer/SocialLink.tsx new file mode 100644 index 00000000..2d668abf --- /dev/null +++ b/frontend/src/components/footer/SocialLink.tsx @@ -0,0 +1,43 @@ +import { FC, ReactNode } from "react"; + +interface SocialLinkProps { + href: string; + ariaLabel: string; + bgColor: string; + ringColor: string; + children: ReactNode; // social link icon + className?: string; +} + +const SocialLink: FC = ({ + href, + ariaLabel, + bgColor, + ringColor, + children, + className, +}) => { + return ( + + + + ); +}; + +export default SocialLink; diff --git a/frontend/src/components/home/Features.tsx b/frontend/src/components/home/Features.tsx new file mode 100644 index 00000000..afd34865 --- /dev/null +++ b/frontend/src/components/home/Features.tsx @@ -0,0 +1,66 @@ +import { FaGear, FaMessage, FaRobot } from "react-icons/fa6"; + +const Features = () => { + const features = [ + { + name: "Buy and sell robots", + description: "Buy and sell completed robots, parts, and designs.", + icon: , + }, + { + name: "Find parts", + description: + "Actuators, motors, and more! Buy, sell, and read about any part. Get recommendations and suggestions to help you with your project.", + icon: , + }, + { + name: "Join the converstation", + description: + "Join the K-Scale discord for Q&A and to see what other people are building.", + icon: , + }, + // { + // name: "Another feature item", + // description: "Description tbd", + // // icon: , // tbd + // }, + ]; + + return ( +
+
+

+ Robots made here. +

+

+ Everything you need to build your next robot +

+

+ Talk with professionals, researchers, and hobbyists. +

+
+ +
+
+ {features.map((feature) => ( +
+
+
+ +
+ {feature.name} +
+
+ {feature.description} +
+
+ ))} +
+
+
+ ); +}; + +export default Features; diff --git a/frontend/src/components/nav/Navbar.tsx b/frontend/src/components/nav/Navbar.tsx index 793d08a9..44bf4536 100644 --- a/frontend/src/components/nav/Navbar.tsx +++ b/frontend/src/components/nav/Navbar.tsx @@ -1,10 +1,10 @@ -import { useDarkMode } from "hooks/dark_mode"; +import { useDarkMode } from "hooks/useDarkMode"; import { useState } from "react"; import { FaMoon, FaSun, FaUserCircle } from "react-icons/fa"; import { useNavigate } from "react-router-dom"; import Sidebar from "./Sidebar"; -const SIZE = 20; +const ICON_SIZE = 20; const Navbar = () => { const [showSidebar, setShowSidebar] = useState(false); @@ -13,7 +13,7 @@ const Navbar = () => { return ( <> -