From 5f25e965f68d14f9d2b9cbcce45ae93e953f4a01 Mon Sep 17 00:00:00 2001 From: Fabrice-Dush Date: Tue, 2 Jul 2024 17:54:51 +0300 Subject: [PATCH 1/7] Reusable Components --- package-lock.json | 10 +- package.json | 1 + src/App.tsx | 12 +- src/components/buttons/Button.tsx | 41 +++- src/components/buttons/Button2.tsx | 9 + src/components/categories/Categories.tsx | 83 +++++++ src/components/inputs/Input.tsx | 58 +++++ src/components/inputs/SearchInput.tsx | 24 ++ src/components/layout/Footer.tsx | 282 ++++++++++++++++++++-- src/components/layout/Header.tsx | 198 +++++++++++++-- src/components/sidebar/Sidebar.tsx | 93 +++++++ src/index.tsx | 16 +- src/pages/LandingPage.tsx | 24 +- src/pages/Login.tsx | 6 +- src/pages/NotFound.tsx | 6 +- src/router.tsx | 14 +- src/store/features/welcomeSlice.tsx | 35 ++- src/store/store.ts | 12 +- src/stories/Button.stories.ts | 10 +- src/styles/Button.scss | 74 ++++++ src/styles/Categories.scss | 186 ++++++++++++++ src/styles/Colors.scss | 12 + src/styles/Footer.scss | 137 +++++++++++ src/styles/Header.scss | 294 +++++++++++++++++++++++ src/styles/Input.scss | 158 ++++++++++++ src/styles/LandingPage.scss | 10 +- src/styles/SearchInput.scss | 56 +++++ src/styles/Sidebar.scss | 134 +++++++++++ src/styles/style.scss | 27 +++ src/test/Button.test.tsx | 20 +- src/utils/axios/axiosInstance.ts | 10 +- 31 files changed, 1940 insertions(+), 112 deletions(-) create mode 100644 src/components/buttons/Button2.tsx create mode 100644 src/components/categories/Categories.tsx create mode 100644 src/components/inputs/Input.tsx create mode 100644 src/components/inputs/SearchInput.tsx create mode 100644 src/components/sidebar/Sidebar.tsx create mode 100644 src/styles/Button.scss create mode 100644 src/styles/Categories.scss create mode 100644 src/styles/Colors.scss create mode 100644 src/styles/Footer.scss create mode 100644 src/styles/Header.scss create mode 100644 src/styles/Input.scss create mode 100644 src/styles/SearchInput.scss create mode 100644 src/styles/Sidebar.scss create mode 100644 src/styles/style.scss diff --git a/package-lock.json b/package-lock.json index ec8bfd62..abae6074 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "mini-css-extract-plugin": "^2.9.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-icons": "^5.2.1", "react-redux": "^9.1.2", "react-router-dom": "^6.24.0", "sass": "^1.77.6", @@ -7070,7 +7071,6 @@ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.6.tgz", "integrity": "sha512-8qpnGVincVDLEcQXWaHOf6zmlbwTKc6Us6PPu4CRnPXCzo2OGBS5cwgMMOWdxDpEz1mkbvXHpEy99M5Yvt682w==", "dev": true, - "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.4.0", "@babel/runtime": "^7.9.2", @@ -20092,6 +20092,14 @@ "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==", "dev": true }, + "node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index f87243ab..8e58f7a1 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "mini-css-extract-plugin": "^2.9.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-icons": "^5.2.1", "react-redux": "^9.1.2", "react-router-dom": "^6.24.0", "sass": "^1.77.6", diff --git a/src/App.tsx b/src/App.tsx index abd182e0..a7f836ef 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,9 @@ -/* eslint-disable linebreak-style */ -import React from 'react'; -import { BrowserRouter as Router } from 'react-router-dom'; -import AppRouter from './router'; +/* eslint-disable*/ +import React from "react"; +import { BrowserRouter as Router } from "react-router-dom"; +import AppRouter from "./router"; +import "./styles/style.scss"; +import Sidebar from "./components/sidebar/Sidebar"; const App: React.FC = () => ( @@ -9,4 +11,4 @@ const App: React.FC = () => ( ); -export default App; \ No newline at end of file +export default App; diff --git a/src/components/buttons/Button.tsx b/src/components/buttons/Button.tsx index 7a279b71..292c5c45 100644 --- a/src/components/buttons/Button.tsx +++ b/src/components/buttons/Button.tsx @@ -1,7 +1,40 @@ -import React from 'react'; +/* eslint-disable */ -const Button = ({ title }: { title: string }) => ( - -); +import React from "react"; +import "../../styles/Button.scss"; + +interface ButtonProps { + children: React.ReactNode; + variant?: "primary" | "secondary" | "outline"; + disabled?: boolean; + onClick?: () => void; + icon?: React.ReactNode; + iconPosition?: "start" | "end"; +} + +function Button({ + children, + variant = "outline", + disabled = false, + onClick, + icon, + iconPosition = "start", +}: ButtonProps) { + return ( + + ); +} export default Button; diff --git a/src/components/buttons/Button2.tsx b/src/components/buttons/Button2.tsx new file mode 100644 index 00000000..4371da16 --- /dev/null +++ b/src/components/buttons/Button2.tsx @@ -0,0 +1,9 @@ +/* eslint-disable */ + +import React from "react"; + +const Button = ({ title }: { title: string }) => ( + +); + +export default Button; diff --git a/src/components/categories/Categories.tsx b/src/components/categories/Categories.tsx new file mode 100644 index 00000000..ffe03e1b --- /dev/null +++ b/src/components/categories/Categories.tsx @@ -0,0 +1,83 @@ +/* eslint-disable */ + +import React from "react"; +import "../../styles/Categories.scss"; + +function Categories() { + return ( +
+
+
+ Product image +
+

Men's shoes

+ +
+
+
+ Product image +
+

Phones

+ +
+
+
+ Product image +
+ + +
+
+
+
+
+
+
+
+ Product image +
+

Women's shoes

+ +
+
+
+ Product image +
+

Accessories

+ +
+
+
+
+ ); +} + +export default Categories; diff --git a/src/components/inputs/Input.tsx b/src/components/inputs/Input.tsx new file mode 100644 index 00000000..9b52c52f --- /dev/null +++ b/src/components/inputs/Input.tsx @@ -0,0 +1,58 @@ +/* eslint-disable */ + +import React, { ReactNode } from "react"; +import "../../styles/Input.scss"; + +interface InputLabelProps extends React.InputHTMLAttributes { + label: string; + type?: "text" | "date" | "password" | "select" | "textarea" | "search"; + children?: ReactNode; +} + +function InputLabel({ + label, + type = "text", + children, + ...props +}: InputLabelProps) { + return ( +
+ {type === "select" ? ( + + ) : type === "textarea" ? ( + + ) : ( + + )} + + +
+ ); +} + +function InputDefault( + props: React.JSX.IntrinsicAttributes & + React.ClassAttributes & + React.InputHTMLAttributes +) { + return ; +} + +function InputRounded( + props: React.JSX.IntrinsicAttributes & + React.ClassAttributes & + React.InputHTMLAttributes +) { + return ; +} + +export { InputLabel, InputDefault, InputRounded }; diff --git a/src/components/inputs/SearchInput.tsx b/src/components/inputs/SearchInput.tsx new file mode 100644 index 00000000..d38f1ec0 --- /dev/null +++ b/src/components/inputs/SearchInput.tsx @@ -0,0 +1,24 @@ +/* eslint-disable */ + +import React from "react"; +import { FiSearch } from "react-icons/fi"; +import "../../styles/SearchInput.scss"; + +interface SearchInputProps { + className: string; + placeholder?: string; +} + +function SearchInput({ className, placeholder }: SearchInputProps) { + return ( +
+
+ +
+ + +
+ ); +} + +export default SearchInput; diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx index 2708ec45..8e6083c6 100644 --- a/src/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -1,17 +1,269 @@ -/* eslint-disable linebreak-style */ -import React from 'react'; +/* eslint-disable */ -const getYear = (): number => new Date().getFullYear(); +import React from "react"; +import "../../styles/Footer.scss"; -const Footer: React.FC = () => ( -
-

- © - {getYear()} - {' '} - Ninja E-Commerce Store -

-
-); - -export default Footer; \ No newline at end of file +function Footer() { + return ( + + ); +} +export default Footer; diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index bd999e16..0c363d32 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -1,17 +1,181 @@ -/* eslint-disable linebreak-style */ -/* eslint-disable import/no-extraneous-dependencies */ -import React from 'react'; -import { Link } from 'react-router-dom'; - -const Header: React.FC = () => ( -
- -
-); - -export default Header; \ No newline at end of file +/* eslint-disable */ +import React, { useState } from "react"; +import { FaLocationDot } from "react-icons/fa6"; +import { IoMdMailUnread } from "react-icons/io"; +import { FaPhoneVolume } from "react-icons/fa6"; +import { FaBuildingCircleCheck } from "react-icons/fa6"; +import { FaRegUser } from "react-icons/fa"; +import { IoCartOutline } from "react-icons/io5"; +import { IoLogOutSharp } from "react-icons/io5"; +import { FaUserClock } from "react-icons/fa6"; + +import "../../styles/Header.scss"; + +import SearchInput from "../inputs/SearchInput"; + +function Header() { + const [isOpen, setIsOpen] = useState(true); + const [isOpen2, setIsOpen2] = useState(true); + + const categories = Array.from({ length: 5 }, (_, i) => i + 1); + + function handleSetIsOpen() { + setIsOpen((isOpen) => !isOpen); + } + + function handleSetIsOpen2() { + setIsOpen2((isOpen) => !isOpen); + } + + return ( + <> +
+
+
+ Ecommerce logo +

+ e-Commerce Ninjas +

+
+
+
+ +

Our office

+

KK 4 Rd, Kigali, Rwanda

+
+
+
+ +
+

Email us

+

+ support@ecommerce-ninjas.com +

+
+
+ +

Contact us

+

+250782355872

+
+
+
+
+
+
+
+ + Shopping Categories + + + > + +
+ {isOpen && ( +
+ +
+ )} +
+ +
+ + Cart + $ 100 +
+
+ + User + Account + {isOpen2 && ( + + )} +
+
+
+
+ +
+
+
+
+ + ); +} + +export default Header; diff --git a/src/components/sidebar/Sidebar.tsx b/src/components/sidebar/Sidebar.tsx new file mode 100644 index 00000000..92e2e38f --- /dev/null +++ b/src/components/sidebar/Sidebar.tsx @@ -0,0 +1,93 @@ +/* eslint-disable */ + +import React, { useState } from "react"; +import { IoLogOutSharp } from "react-icons/io5"; +import { IoIosPeople } from "react-icons/io"; +import { FaBuildingCircleCheck } from "react-icons/fa6"; +import { AiFillDashboard } from "react-icons/ai"; +import { FaShop } from "react-icons/fa6"; +import { FaBoxArchive } from "react-icons/fa6"; + +import "../../styles/Sidebar.scss"; + +function Sidebar() { + const [isActive, setIsActive] = useState(false); + + function handleSetActive() { + setIsActive((isActive) => !isActive); + } + + return ( +
+
+

+ e-CommerceNinjas +

+ +
+
+ ); +} + +export default Sidebar; diff --git a/src/index.tsx b/src/index.tsx index 7b2dfb30..6237650c 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,13 +1,13 @@ -/* eslint-disable linebreak-style */ -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import { Provider } from 'react-redux'; -import { store } from './store/store'; -import App from './App'; +/* eslint-disable*/ +import React from "react"; +import { createRoot } from "react-dom/client"; +import { Provider } from "react-redux"; +import { store } from "./store/store"; +import App from "./App"; -const root = createRoot(document.getElementById('root')!); +const root = createRoot(document.getElementById("root")!); root.render( -); \ No newline at end of file +); diff --git a/src/pages/LandingPage.tsx b/src/pages/LandingPage.tsx index 884a4781..e20f63fa 100644 --- a/src/pages/LandingPage.tsx +++ b/src/pages/LandingPage.tsx @@ -1,14 +1,18 @@ -import React, { useEffect } from 'react'; -import { useAppDispatch, useAppSelector } from '../store/store'; -import { loadWelcomeMessage } from '../store/features/welcomeSlice'; -import { IWelcomeMessage } from '../utils/types/store'; -import Header from '../components/layout/Header'; -import Footer from '../components/layout/Footer'; -import '../styles/LandingPage.scss'; +/* eslint-disable */ + +import React, { useEffect } from "react"; +import { useAppDispatch, useAppSelector } from "../store/store"; +import { loadWelcomeMessage } from "../store/features/welcomeSlice"; +import { IWelcomeMessage } from "../utils/types/store"; +import Header from "../components/layout/Header"; +import Footer from "../components/layout/Footer"; +import "../styles/LandingPage.scss"; const LandingPage: React.FC = () => { const dispatch = useAppDispatch(); - const welcomeMessage: IWelcomeMessage = useAppSelector((state) => state.initialMessage.welcomeMessage); + const welcomeMessage: IWelcomeMessage = useAppSelector( + (state) => state.initialMessage.welcomeMessage + ); useEffect(() => { dispatch(loadWelcomeMessage()); @@ -18,9 +22,7 @@ const LandingPage: React.FC = () => { <>
-

- {welcomeMessage.message} -

+

{welcomeMessage.message}