├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── components ├── adSlider │ └── adSlider.js ├── banner │ └── banner.js ├── footer │ ├── footer.js │ └── style.less ├── itemsSlider │ ├── itemsSlider.js │ └── style.less ├── layout.js ├── loading │ └── loading.js ├── meta │ └── meta.js ├── navbar │ ├── lowerNav.js │ ├── navbar.js │ ├── style.less │ └── upperNav.js ├── partners │ ├── partners.js │ └── style.less ├── product │ ├── controls.js │ ├── gridItem.js │ ├── gridItem.less │ ├── listItem.js │ ├── listItem.less │ ├── product.js │ ├── productPreview.js │ ├── productPreview.less │ └── rating.js ├── searchPage │ ├── searchControls.js │ ├── searchFilter.js │ └── style.less ├── tabbedItems │ └── tabbedItems.js └── widget │ ├── style.less │ └── widget.js ├── documentation └── images │ ├── captured.gif │ └── captured2.gif ├── next.config.js ├── now.json ├── package-lock.json ├── package.json ├── pages ├── _app.js ├── _document.js ├── _error.js ├── about.js ├── app.less ├── index.js ├── items │ └── [slug].js └── search.js ├── public ├── favicon.ico ├── images │ ├── banner.gif │ ├── banner.png │ ├── item1.jpg │ ├── item2.jpg │ ├── logo.png │ ├── logo2.png │ ├── partner.jpg │ ├── sample.jpg │ ├── sample2.jpg │ └── slider.jpg └── manifest.json ├── server.js ├── static └── manifest.json ├── styles ├── antd.less ├── index.less ├── searchPage.less └── variables.less └── utils ├── URLS.js └── axios-instance.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | [ 4 | "inline-import", 5 | { 6 | "extensions": [ 7 | ".css" 8 | ] 9 | } 10 | ], 11 | [ 12 | "import", 13 | { 14 | "libraryName": "antd" 15 | } 16 | ] 17 | ], 18 | "presets": [ 19 | [ 20 | "next/babel", 21 | { 22 | // "styled-jsx": { 23 | // "plugins": [ 24 | // "styled-jsx-plugin-sass" 25 | // ] 26 | // } 27 | } 28 | ] 29 | ] 30 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .package-lock.json 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | .env* 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 mohammadou1 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nextstore 2 | An eCommerce platform built with NextJS 3 | Still in development phase, will implement backend as soon as my frontend is finished. 4 | Using Ant Design for templating. 5 | 6 | Please make sure to leave your opinion, it does matter no matter how small it is. 7 | 8 | 9 | This project is still in development phase, after finishing the frontend I'll start developing it's backend as NodeJS 10 | to try it, 11 | 12 | 13 | online demo: [NextStore](https://nextstore.itmohou.now.sh/) 14 | just clone and 15 | 16 | ```npm run dev``` 17 | or 18 | ```yarn dev``` 19 | 20 | 21 | 22 | ![NextStore](https://github.com/mohammadou1/nextstore/blob/master/documentation/images/captured.gif) 23 | 24 | ![NextStore](https://github.com/mohammadou1/nextstore/blob/master/documentation/images/captured2.gif) 25 | 26 | -------------------------------------------------------------------------------- /components/adSlider/adSlider.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import Slider from "react-slick"; 3 | import Link from 'next/link'; 4 | const settings = { 5 | arrows: true, 6 | infinite: true, 7 | autoplay: true, 8 | autoplaySpeed: 3000, 9 | slidesToShow: 1, 10 | lazyLoad:true, 11 | slidesToScroll: 1, 12 | 13 | }; 14 | const PrevArrow = props => { 15 | const { className, onClick } = props; 16 | return (
17 | 18 |
); 19 | } 20 | const NextArrow = props => { 21 | const { className, onClick } = props; 22 | return (
23 | 24 |
); 25 | } 26 | 27 | 28 | 29 | 30 | const adSlider = () => { 31 | 32 | 33 | const [isSliding, setIsSliding] = useState(false); 34 | 35 | // * to check if slick slider is being dragged to prevent conflict between click and swipe. 36 | const isSlidingHandler = e => { 37 | if (isSliding) { 38 | e.preventDefault(); 39 | } 40 | } 41 | 42 | const ads = [ 43 | { id: 1, src: "/images/slider.jpg", href: "items/some-item-slug" }, 44 | { id: 2, src: "/images/slider.jpg", href: "items/some-item-slug" }, 45 | ].map((ad, idx) =>
46 | 47 | 49 | 50 | 51 | 52 | 53 | 54 |
); 55 | 56 | return ( 57 | setIsSliding(true)} 58 | afterChange={() => setIsSliding(false)} 59 | prevArrow={} 60 | nextArrow={} {...settings}> 61 | {ads} 62 | 63 | ); 64 | } 65 | 66 | export default adSlider; 67 | -------------------------------------------------------------------------------- /components/banner/banner.js: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | const banner = ({ imageSrc, children, href, hrefAs, alt }) => { 3 | return ( 4 | 5 |
6 | 7 | 8 | {alt 9 |
10 | {children} 11 |
12 |
13 | 14 |
15 | 16 | ); 17 | } 18 | export default banner; 19 | 20 | -------------------------------------------------------------------------------- /components/footer/footer.js: -------------------------------------------------------------------------------- 1 | import './style.less'; 2 | import { 3 | Row, 4 | Col, 5 | Input, 6 | Form, 7 | } from 'antd'; 8 | import Link from 'next/link'; 9 | 10 | const aboutLinks = [ 11 | { title: 'About Us', href: '/about', }, 12 | { title: 'Privacy & Policy', href: '/privacy-policy', }, 13 | { title: 'FAQ', href: '/faq', }, 14 | { title: 'Contact Us', href: '/contact', as: 'contact-us' }, 15 | ].map((link, idx) =>
  • 16 | 17 | 18 | {link.title} 19 | 20 | 21 |
  • ); 22 | 23 | const pagesLinks = [ 24 | { title: 'Home', href: '/', }, 25 | { title: 'Brands', href: '/brands', }, 26 | { title: 'Collections', href: '/faq', }, 27 | { title: 'Offers', href: '/offers', as: '/special-offers' }, 28 | ].map((link, idx) =>
  • 29 | 30 | 31 | {link.title} 32 | 33 | 34 |
  • ); 35 | 36 | const accountLinks = [ 37 | { title: 'My Account', href: '/account', }, 38 | { title: 'Order History', href: '/history', as: '/order-history' }, 39 | { title: 'Terms & Conditions', href: '/terms', as: '/terms-conditions' }, 40 | { title: 'Delivery Information', href: '/delivery', as: '/delivery-information' }, 41 | ].map((link, idx) =>
  • 42 | 43 | 44 | {link.title} 45 | 46 | 47 |
  • ); 48 | 49 | const footer = () => { 50 | return ( 51 | 111 | ); 112 | } 113 | 114 | export default footer; 115 | -------------------------------------------------------------------------------- /components/footer/style.less: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables.less"; 2 | 3 | .footer { 4 | background-color: @primary--color; 5 | padding: 35px 25px 0 35px; 6 | background: linear-gradient( 7 | to right, 8 | @primary--color 60%, 9 | darken(@primary--color, 10%) 10 | ); 11 | 12 | @media (max-width: 768px) { 13 | text-align: center; 14 | } 15 | 16 | .ant-input-group-wrapper { 17 | max-width: 400px; 18 | margin-top: 1rem; 19 | 20 | input { 21 | height: 40px; 22 | } 23 | } 24 | 25 | hr { 26 | border-width: 0; 27 | border-top-width: 1px; 28 | margin-top: 25px; 29 | } 30 | 31 | h5 { 32 | color: @secondary--color; 33 | font-weight: bold; 34 | font-size: 17px; 35 | margin-bottom: 1.5rem; 36 | 37 | @media (max-width: 768px) { 38 | margin-top: 2rem; 39 | margin-bottom: 1rem; 40 | } 41 | } 42 | 43 | p { 44 | color: #ffffff; 45 | font-size: 14px; 46 | margin: 0; 47 | } 48 | 49 | ul { 50 | margin: 0; 51 | padding: 0; 52 | list-style: none; 53 | 54 | li { 55 | line-height: 30px; 56 | } 57 | 58 | a { 59 | font-size: 14px; 60 | color: #ffffff; 61 | 62 | &:hover { 63 | color: @secondary--color; 64 | } 65 | } 66 | } 67 | 68 | .footer-end { 69 | padding: 10px 0; 70 | 71 | p { 72 | font-size: 13px; 73 | } 74 | 75 | a { 76 | color: @secondary--color; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /components/itemsSlider/itemsSlider.js: -------------------------------------------------------------------------------- 1 | import Slider from "react-slick"; 2 | import Product from '../product/product'; 3 | import './style.less'; 4 | 5 | 6 | const itemsSlider = props => { 7 | const { items = [], slidesToShow = 4, slidesToShowMobile, slidesToShowTablet } = props; 8 | 9 | const settings = { 10 | arrows: false, 11 | infinite: false, 12 | slidesToShow, 13 | slidesToScroll: 1, 14 | responsive: [ 15 | { 16 | breakpoint: 768, 17 | settings: { 18 | slidesToShow: slidesToShowMobile || slidesToShow 19 | }, 20 | }, 21 | { 22 | breakpoint: 992, 23 | settings: { 24 | slidesToShow: slidesToShowTablet || slidesToShow 25 | }, 26 | }, 27 | { 28 | breakpoint: 1100, 29 | settings: { 30 | slidesToShow: slidesToShow 31 | }, 32 | }, 33 | ] 34 | 35 | }; 36 | 37 | return ( 38 | 39 | {items.map(item => )} 40 | 41 | ); 42 | } 43 | 44 | export default itemsSlider; 45 | -------------------------------------------------------------------------------- /components/itemsSlider/style.less: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables.less"; 2 | 3 | .products-slider { 4 | .slick-slide { 5 | &:not(:nth-child(1)) { 6 | .product-item { 7 | border-left: 1px solid @line-color; 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /components/layout.js: -------------------------------------------------------------------------------- 1 | // import stylesheet from 'antd/dist/antd.min.css'; 2 | import '../styles/index.less'; 3 | import Navbar from './navbar/navbar'; 4 | import Footer from './footer/footer'; 5 | import Router from 'next/router'; 6 | import NProgress from 'nprogress'; 7 | 8 | Router.events.on('routeChangeStart', _ => NProgress.start()); 9 | Router.events.on('routeChangeComplete', _ => NProgress.done()); 10 | Router.events.on('routeChangeError', _ => NProgress.done()); 11 | const Layout = ({ children }) => { 12 | 13 | return (
    14 | {/*