diff --git a/package-lock.json b/package-lock.json index ec8bfd62..28b2c863 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", @@ -20092,6 +20093,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..8040d88c 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/categories/Categories.tsx b/src/components/categories/Categories.tsx new file mode 100644 index 00000000..8c05052d --- /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..9086d75f --- /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..bdd7b48e --- /dev/null +++ b/src/components/inputs/SearchInput.tsx @@ -0,0 +1,24 @@ +/* eslint-disable */ + +import React from "react"; +import "../../styles/_searchInput.scss"; +import { FiSearch } from "react-icons/fi"; + +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..3bf7eb66 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..40dd684c 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -1,17 +1,182 @@ -/* 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 "../../styles/_categories.scss"; + +import SearchInput from "../inputs/SearchInput"; + +function Header() { + const [isOpen, setIsOpen] = useState(false); + const [isOpen2, setIsOpen2] = useState(false); + + 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..ae3628d9 --- /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..a0b75e9b 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}