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/public/person.jpg b/public/person.jpg new file mode 100644 index 00000000..430ca557 Binary files /dev/null and b/public/person.jpg differ 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..4371da16 100644 --- a/src/components/buttons/Button.tsx +++ b/src/components/buttons/Button.tsx @@ -1,4 +1,6 @@ -import React from 'react'; +/* eslint-disable */ + +import React from "react"; const Button = ({ title }: { title: string }) => ( 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..8d72ffcc --- /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/AdminHeader.tsx b/src/components/layout/AdminHeader.tsx new file mode 100644 index 00000000..67c6cae2 --- /dev/null +++ b/src/components/layout/AdminHeader.tsx @@ -0,0 +1,38 @@ +/* eslint-disable */ +import React from "react"; +import { IoIosNotifications } from "react-icons/io"; +import { FaEnvelope } from "react-icons/fa"; +import "../../styles/AdminHeader.scss"; + +function AdminHeader() { + return ( +
+
+ Ecommerce logo +

+ e-Commerce Ninjas +

+
+
+ + 10 +
+
+ + 30 +
+
+ UI face +

+ Hi, Emmanuel +

+
+
+ ); +} + +export default AdminHeader; diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx index 2708ec45..991f2bdb 100644 --- a/src/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -1,17 +1,185 @@ -/* eslint-disable linebreak-style */ -import React from 'react'; +/* eslint-disable */ -const getYear = (): number => new Date().getFullYear(); +import React from "react"; +import { FaInstagramSquare } from "react-icons/fa"; +import { IoLogoFacebook } from "react-icons/io"; +import { FaSquareTwitter } from "react-icons/fa6"; +import { FaLinkedin } from "react-icons/fa6"; -const Footer: React.FC = () => ( -
-

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

-
-); +import "../../styles/Footer.scss"; -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..d98a9370 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -1,17 +1,171 @@ -/* 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 { FaChevronDown } from "react-icons/fa"; + +import "../../styles/Header.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/layout/SellerHeader.tsx b/src/components/layout/SellerHeader.tsx new file mode 100644 index 00000000..8b9a33ab --- /dev/null +++ b/src/components/layout/SellerHeader.tsx @@ -0,0 +1,32 @@ +/* eslint-disable */ +import React from "react"; +import { IoIosNotifications } from "react-icons/io"; +import { MdDarkMode } from "react-icons/md"; +import { MdLightMode } from "react-icons/md"; + +import "../../styles/SellerHeader.scss"; + +function SellerHeader() { + return ( +
+

Dashboard

+
+ + 10 +
+
+
+
+ + Light +
+
+ + Dark +
+
+
+ ); +} + +export default SellerHeader; 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..4d3c1d36 100644 --- a/src/pages/LandingPage.tsx +++ b/src/pages/LandingPage.tsx @@ -1,14 +1,22 @@ -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"; +import SearchInput from "../components/inputs/SearchInput"; +import Sidebar from "../components/sidebar/Sidebar"; +import AdminHeader from "../components/layout/AdminHeader"; +import SellerHeader from "../components/layout/SellerHeader"; 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()); @@ -16,11 +24,14 @@ const LandingPage: React.FC = () => { return ( <> + +
-

- {welcomeMessage.message} -

+

{welcomeMessage.message}

+
+
+