├── .prettierignore
├── client
├── .env.example
├── app
│ ├── styles
│ │ ├── core
│ │ │ ├── _rtl.scss
│ │ │ ├── _fixes.scss
│ │ │ ├── _homepage.scss
│ │ │ ├── _users.scss
│ │ │ ├── page404.scss
│ │ │ ├── _shop.scss
│ │ │ ├── utils
│ │ │ │ └── _fonts.scss
│ │ │ ├── _radio.scss
│ │ │ ├── _checkout.scss
│ │ │ ├── _badge.scss
│ │ │ ├── _merchant.scss
│ │ │ ├── _table.scss
│ │ │ ├── _newsletter.scss
│ │ │ ├── _coming-soon.scss
│ │ │ ├── _address.scss
│ │ │ ├── _account.scss
│ │ │ ├── _brand.scss
│ │ │ ├── _login.scss
│ │ │ ├── _category.scss
│ │ │ ├── _spinner.scss
│ │ │ ├── _share.scss
│ │ │ ├── core.scss
│ │ │ ├── _switch.scss
│ │ │ ├── _checkbox.scss
│ │ │ ├── _subpage.scss
│ │ │ └── _search.scss
│ │ ├── _custom.scss
│ │ └── style.scss
│ ├── contexts
│ │ └── Socket
│ │ │ ├── context.js
│ │ │ ├── useSocket.js
│ │ │ ├── index.js
│ │ │ └── provider.js
│ ├── containers
│ │ ├── Homepage
│ │ │ ├── constants.js
│ │ │ ├── actions.js
│ │ │ ├── banners.json
│ │ │ └── reducer.js
│ │ ├── Shop
│ │ │ ├── constants.js
│ │ │ ├── actions.js
│ │ │ └── reducer.js
│ │ ├── Application
│ │ │ ├── constants.js
│ │ │ ├── actions.js
│ │ │ └── reducer.js
│ │ ├── Dashboard
│ │ │ ├── constants.js
│ │ │ ├── actions.js
│ │ │ └── reducer.js
│ │ ├── NavigationMenu
│ │ │ ├── constants.js
│ │ │ ├── actions.js
│ │ │ └── reducer.js
│ │ ├── Authentication
│ │ │ ├── constants.js
│ │ │ ├── actions.js
│ │ │ ├── reducer.js
│ │ │ └── index.js
│ │ ├── WishList
│ │ │ ├── constants.js
│ │ │ ├── reducer.js
│ │ │ ├── index.js
│ │ │ └── actions.js
│ │ ├── Contact
│ │ │ ├── constants.js
│ │ │ └── reducer.js
│ │ ├── Newsletter
│ │ │ ├── constants.js
│ │ │ ├── reducer.js
│ │ │ ├── index.js
│ │ │ └── actions.js
│ │ ├── Account
│ │ │ ├── constants.js
│ │ │ ├── index.js
│ │ │ └── reducer.js
│ │ ├── Users
│ │ │ ├── constants.js
│ │ │ ├── reducer.js
│ │ │ └── actions.js
│ │ ├── ResetPassword
│ │ │ ├── constants.js
│ │ │ ├── reducer.js
│ │ │ └── index.js
│ │ ├── ForgotPassword
│ │ │ ├── constants.js
│ │ │ ├── reducer.js
│ │ │ └── actions.js
│ │ ├── Login
│ │ │ ├── constants.js
│ │ │ └── reducer.js
│ │ ├── Cart
│ │ │ ├── constants.js
│ │ │ └── reducer.js
│ │ ├── Signup
│ │ │ ├── constants.js
│ │ │ └── reducer.js
│ │ ├── Navigation
│ │ │ ├── constants.js
│ │ │ ├── reducer.js
│ │ │ └── actions.js
│ │ ├── Order
│ │ │ ├── constants.js
│ │ │ └── index.js
│ │ ├── Review
│ │ │ └── constants.js
│ │ ├── BrandsPage
│ │ │ └── index.js
│ │ ├── Address
│ │ │ ├── constants.js
│ │ │ ├── index.js
│ │ │ ├── Add.js
│ │ │ ├── List.js
│ │ │ └── Edit.js
│ │ ├── Brand
│ │ │ ├── constants.js
│ │ │ ├── Add.js
│ │ │ ├── index.js
│ │ │ ├── List.js
│ │ │ └── Edit.js
│ │ ├── Support
│ │ │ └── index.js
│ │ ├── Notification
│ │ │ └── index.js
│ │ ├── Category
│ │ │ ├── constants.js
│ │ │ ├── index.js
│ │ │ ├── Add.js
│ │ │ └── List.js
│ │ ├── Merchant
│ │ │ ├── constants.js
│ │ │ ├── index.js
│ │ │ └── Add.js
│ │ ├── AuthSuccess
│ │ │ └── index.js
│ │ ├── Product
│ │ │ ├── index.js
│ │ │ ├── constants.js
│ │ │ ├── Add.js
│ │ │ └── List.js
│ │ ├── AccountSecurity
│ │ │ └── index.js
│ │ ├── ProductsShop
│ │ │ └── index.js
│ │ ├── BrandsShop
│ │ │ └── index.js
│ │ └── CategoryShop
│ │ │ └── index.js
│ ├── utils
│ │ ├── store.js
│ │ ├── app.js
│ │ ├── token.js
│ │ ├── base64.js
│ │ ├── validation.js
│ │ ├── date.js
│ │ ├── select.js
│ │ ├── index.js
│ │ └── error.js
│ ├── index.js
│ ├── components
│ │ ├── Common
│ │ │ ├── Page404
│ │ │ │ └── index.js
│ │ │ ├── ComingSoon
│ │ │ │ └── index.js
│ │ │ ├── NotFound
│ │ │ │ └── index.js
│ │ │ ├── Tooltip
│ │ │ │ └── index.js
│ │ │ ├── CarouselSlider
│ │ │ │ ├── utils.js
│ │ │ │ └── index.js
│ │ │ ├── Popover
│ │ │ │ └── index.js
│ │ │ ├── LoadingIndicator
│ │ │ │ └── index.js
│ │ │ ├── SignupProvider
│ │ │ │ └── index.js
│ │ │ ├── DropdownConfirm
│ │ │ │ └── index.js
│ │ │ ├── CartIcon
│ │ │ │ └── index.js
│ │ │ ├── Badge
│ │ │ │ └── index.js
│ │ │ ├── Pagination
│ │ │ │ └── index.js
│ │ │ ├── Checkbox
│ │ │ │ └── index.js
│ │ │ ├── Switch
│ │ │ │ └── index.js
│ │ │ └── Radio
│ │ │ │ └── index.js
│ │ ├── Manager
│ │ │ ├── SearchResultMeta
│ │ │ │ └── index.js
│ │ │ ├── UserSearch
│ │ │ │ └── index.js
│ │ │ ├── OrderSearch
│ │ │ │ └── index.js
│ │ │ ├── MerchantSearch
│ │ │ │ └── index.js
│ │ │ ├── UserRole
│ │ │ │ └── index.js
│ │ │ ├── SubPage
│ │ │ │ └── index.js
│ │ │ ├── CategoryList
│ │ │ │ └── index.js
│ │ │ ├── DisabledAccount
│ │ │ │ └── Merchant.js
│ │ │ ├── Support
│ │ │ │ └── AddMessage.js
│ │ │ ├── OrderDetails
│ │ │ │ └── index.js
│ │ │ ├── ProductList
│ │ │ │ └── index.js
│ │ │ ├── BrandList
│ │ │ │ └── index.js
│ │ │ ├── UserList
│ │ │ │ └── index.js
│ │ │ ├── OrderSummary
│ │ │ │ └── index.js
│ │ │ ├── AddressList
│ │ │ │ └── index.js
│ │ │ └── Dashboard
│ │ │ │ ├── Customer.js
│ │ │ │ └── Merchant.js
│ │ └── Store
│ │ │ ├── AddToWishList
│ │ │ └── index.js
│ │ │ ├── BrandList
│ │ │ └── index.js
│ │ │ ├── Checkout
│ │ │ └── index.js
│ │ │ ├── CartSummary
│ │ │ └── index.js
│ │ │ ├── ProductReviews
│ │ │ └── index.js
│ │ │ ├── MiniBrand
│ │ │ └── index.js
│ │ │ └── SocialShare
│ │ │ └── index.js
│ ├── scrollToTop.js
│ ├── constants
│ │ └── index.js
│ ├── store.js
│ └── app.js
├── vercel.json
├── public
│ ├── images
│ │ ├── pwa.png
│ │ ├── bars.png
│ │ ├── favicon.ico
│ │ ├── banners
│ │ │ ├── banner-1.jpg
│ │ │ ├── banner-2.jpg
│ │ │ ├── banner-3.jpg
│ │ │ ├── banner-4.jpg
│ │ │ ├── banner-5.jpg
│ │ │ ├── banner-6.jpg
│ │ │ └── banner-7.jpg
│ │ ├── placeholder-image.png
│ │ ├── chevron-down.svg
│ │ ├── bag.svg
│ │ ├── close.svg
│ │ ├── arrow-right.svg
│ │ └── social-icons
│ │ │ ├── facebook.svg
│ │ │ ├── instagram.svg
│ │ │ ├── pinterest.svg
│ │ │ └── twitter.svg
│ └── index.html
├── .babelrc
├── Dockerfile
└── webpack
│ └── webpack.common.js
├── server
├── config
│ ├── tax.js
│ └── keys.js
├── middleware
│ ├── auth.js
│ └── role.js
├── utils
│ ├── utils.js
│ ├── auth.js
│ ├── db.js
│ ├── storage.js
│ └── seed.js
├── vercel.json
├── nodemon.json
├── routes
│ ├── index.js
│ └── api
│ │ ├── newsletter.js
│ │ ├── index.js
│ │ └── contact.js
├── Dockerfile
├── models
│ ├── contact.js
│ ├── order.js
│ ├── wishlist.js
│ ├── address.js
│ ├── brand.js
│ ├── category.js
│ ├── merchant.js
│ ├── review.js
│ ├── product.js
│ ├── user.js
│ └── cart.js
├── .env.example
├── services
│ └── mailchimp.js
├── constants
│ └── index.js
├── index.js
├── package.json
└── socket
│ └── index.js
├── SECURITY.md
├── .gitignore
├── .vscode
└── settings.json
├── dockercompose.yml
├── .github
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── LICENSE
└── package.json
/.prettierignore:
--------------------------------------------------------------------------------
1 | README.md
2 |
--------------------------------------------------------------------------------
/client/.env.example:
--------------------------------------------------------------------------------
1 | API_URL=http://localhost:3000/api
--------------------------------------------------------------------------------
/client/app/styles/core/_rtl.scss:
--------------------------------------------------------------------------------
1 | // rtl styles
2 |
--------------------------------------------------------------------------------
/client/app/styles/core/_fixes.scss:
--------------------------------------------------------------------------------
1 | // fixes styles
2 |
--------------------------------------------------------------------------------
/client/app/styles/_custom.scss:
--------------------------------------------------------------------------------
1 | // Here you can add other styles
2 |
--------------------------------------------------------------------------------
/client/app/styles/core/_homepage.scss:
--------------------------------------------------------------------------------
1 | // .homepage {
2 | // }
3 |
--------------------------------------------------------------------------------
/client/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [{ "src": "/[^.]+", "dest": "/", "status": 200 }]
3 | }
4 |
--------------------------------------------------------------------------------
/client/public/images/pwa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/pwa.png
--------------------------------------------------------------------------------
/client/public/images/bars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/bars.png
--------------------------------------------------------------------------------
/client/public/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/favicon.ico
--------------------------------------------------------------------------------
/server/config/tax.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stateCode: 'CA',
3 | stateName: 'California',
4 | stateTaxRate: 0.05
5 | };
6 |
--------------------------------------------------------------------------------
/client/public/images/banners/banner-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/banners/banner-1.jpg
--------------------------------------------------------------------------------
/client/public/images/banners/banner-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/banners/banner-2.jpg
--------------------------------------------------------------------------------
/client/public/images/banners/banner-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/banners/banner-3.jpg
--------------------------------------------------------------------------------
/client/public/images/banners/banner-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/banners/banner-4.jpg
--------------------------------------------------------------------------------
/client/public/images/banners/banner-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/banners/banner-5.jpg
--------------------------------------------------------------------------------
/client/public/images/banners/banner-6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/banners/banner-6.jpg
--------------------------------------------------------------------------------
/client/public/images/banners/banner-7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/banners/banner-7.jpg
--------------------------------------------------------------------------------
/client/public/images/placeholder-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snowMan128/ecommerce/HEAD/client/public/images/placeholder-image.png
--------------------------------------------------------------------------------
/client/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react"],
3 | "plugins": ["@babel/plugin-proposal-class-properties"]
4 | }
5 |
--------------------------------------------------------------------------------
/client/app/contexts/Socket/context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const SocketContext = React.createContext();
4 |
5 | export default SocketContext;
6 |
--------------------------------------------------------------------------------
/client/app/containers/Homepage/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Homepage constants
4 | *
5 | */
6 |
7 | export const DEFAULT_ACTION = 'src/Homepage/DEFAULT_ACTION';
8 |
--------------------------------------------------------------------------------
/client/app/containers/Shop/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Products constants
4 | *
5 | */
6 |
7 | export const DEFAULT_ACTION = 'src/Products/DEFAULT_ACTION';
8 |
--------------------------------------------------------------------------------
/server/middleware/auth.js:
--------------------------------------------------------------------------------
1 | const passport = require('passport');
2 |
3 | const auth = passport.authenticate('jwt', { session: false });
4 |
5 | module.exports = auth;
6 |
--------------------------------------------------------------------------------
/client/app/containers/Application/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Application constants
4 | *
5 | */
6 |
7 | export const DEFAULT_ACTION = 'src/Application/DEFAULT_ACTION';
8 |
--------------------------------------------------------------------------------
/client/app/containers/Dashboard/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Dashboard constants
4 | *
5 | */
6 |
7 | export const TOGGLE_DASHBOARD_MENU = 'src/Dashboard/TOGGLE_DASHBOARD_MENU';
8 |
--------------------------------------------------------------------------------
/client/app/containers/NavigationMenu/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * NavigationMenu constants
4 | *
5 | */
6 |
7 | export const DEFAULT_ACTION = 'src/NavigationMenu/DEFAULT_ACTION';
8 |
--------------------------------------------------------------------------------
/client/app/utils/store.js:
--------------------------------------------------------------------------------
1 | export const sortOptions = [
2 | { value: 0, label: 'Newest First' },
3 | { value: 1, label: 'Price High to Low' },
4 | { value: 2, label: 'Price Low to High' }
5 | ];
6 |
--------------------------------------------------------------------------------
/server/utils/utils.js:
--------------------------------------------------------------------------------
1 | exports.asyncForEach = async (array, callback) => {
2 | for (let index = 0; index < array.length; index++) {
3 | await callback(array[index], index, array);
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/client/app/styles/core/_users.scss:
--------------------------------------------------------------------------------
1 | .users-dashboard {
2 | .u-list {
3 | .user-box {
4 | border-radius: $border-radius-default;
5 | box-shadow: $box-shadow-primary;
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/client/app/contexts/Socket/useSocket.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 |
3 | import SocketContext from './context';
4 |
5 | const useSocket = () => useContext(SocketContext);
6 |
7 | export default useSocket;
8 |
--------------------------------------------------------------------------------
/client/app/contexts/Socket/index.js:
--------------------------------------------------------------------------------
1 | import SocketProvider from './provider';
2 | import SocketContext from './context';
3 | import useSocket from './useSocket';
4 |
5 | export { SocketProvider, SocketContext, useSocket };
6 |
--------------------------------------------------------------------------------
/client/app/containers/Authentication/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Authentication constants
4 | *
5 | */
6 |
7 | export const SET_AUTH = 'src/Authentication/SET_AUTH';
8 | export const CLEAR_AUTH = 'src/Authentication/CLEAR_AUTH';
9 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Vulnerability
4 |
5 | Please submit a pull request to report security bugs.
6 |
7 | I will confirm the problem, audit code to find potential similar problems and coordinate the fix.
8 |
--------------------------------------------------------------------------------
/client/app/styles/core/page404.scss:
--------------------------------------------------------------------------------
1 | .page-404 {
2 | text-align: center;
3 | color: $font-custom-color;
4 | font-size: $font-size-large;
5 | font-weight: $font-weight-normal;
6 | min-height: 250px;
7 | @include center();
8 | }
9 |
--------------------------------------------------------------------------------
/client/app/containers/WishList/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * WishList constants
4 | *
5 | */
6 |
7 | export const FETCH_WISHLIST = 'src/WishList/FETCH_WISHLIST';
8 | export const SET_WISHLIST_LOADING = 'src/WishList/SET_WISHLIST_LOADING';
9 |
--------------------------------------------------------------------------------
/client/app/styles/core/_shop.scss:
--------------------------------------------------------------------------------
1 | .shop-page {
2 | .main {
3 | background-color: $theme-white;
4 | }
5 |
6 | .shop-toolbar {
7 | border-radius: $border-radius-primary;
8 | box-shadow: $box-shadow-primary;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 |
4 | # production
5 | build
6 | dist
7 |
8 | # misc
9 | package.json.bak
10 | .DS_Store
11 |
12 | # config file
13 | .env
14 |
15 | # debug
16 | npm-debug.log*
17 | yarn-debug.log*
18 | yarn-error.log*
--------------------------------------------------------------------------------
/client/app/containers/Shop/actions.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Shop actions
4 | *
5 | */
6 |
7 | import { DEFAULT_ACTION } from './constants';
8 |
9 | export const defaultAction = () => {
10 | return {
11 | type: DEFAULT_ACTION
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/server/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "builds": [
4 | {
5 | "src": "index.js",
6 | "use": "@vercel/node"
7 | }
8 | ],
9 | "routes": [
10 | {
11 | "src": "/(.*)",
12 | "dest": "/"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/client/app/containers/Homepage/actions.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Homepage actions
4 | *
5 | */
6 |
7 | import { DEFAULT_ACTION } from './constants';
8 |
9 | export const defaultAction = () => {
10 | return {
11 | type: DEFAULT_ACTION
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/client/app/containers/Application/actions.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Application actions
4 | *
5 | */
6 |
7 | import { DEFAULT_ACTION } from './constants';
8 |
9 | export const defaultAction = () => {
10 | return {
11 | type: DEFAULT_ACTION
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/client/app/containers/NavigationMenu/actions.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * NavigationMenu actions
4 | *
5 | */
6 |
7 | import { DEFAULT_ACTION } from './constants';
8 |
9 | export const defaultAction = () => {
10 | return {
11 | type: DEFAULT_ACTION
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/client/public/images/chevron-down.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/client/app/containers/Dashboard/actions.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Dashboard actions
4 | *
5 | */
6 |
7 | import { TOGGLE_DASHBOARD_MENU } from './constants';
8 |
9 | export const toggleDashboardMenu = () => {
10 | return {
11 | type: TOGGLE_DASHBOARD_MENU
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/client/app/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * index.js
4 | * This is the entry file for the application
5 | */
6 |
7 | import React from 'react';
8 | import ReactDOM from 'react-dom';
9 |
10 | import App from './app';
11 |
12 | ReactDOM.render(, document.getElementById('root'));
13 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true,
3 | "prettier.singleQuote": true,
4 | "prettier.arrowParens": "avoid",
5 | "prettier.jsxSingleQuote": true,
6 | "prettier.trailingComma": "none",
7 | "jumpToAliasFile.alias": {
8 | "app": "client/app"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/server/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "ignore": [
3 | "**/*.test.ts",
4 | "**/*.spec.ts",
5 | "node_modules",
6 | "client",
7 | "build",
8 | "dist"
9 | ],
10 | "verbose": false,
11 | "watch": ["./"],
12 | "env": {
13 | "NODE_ENV": "development"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/client/app/styles/core/utils/_fonts.scss:
--------------------------------------------------------------------------------
1 | // @import '~@fontsource/poppins/index.css';
2 | @import '~@fontsource/poppins/300.css';
3 | @import '~@fontsource/poppins/400.css';
4 | @import '~@fontsource/poppins/500.css';
5 | @import '~@fontsource/poppins/600.css';
6 | @import '~@fontsource/poppins/700.css';
7 |
--------------------------------------------------------------------------------
/client/app/components/Common/Page404/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Page404
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | const Page404 = () => {
10 | return (
11 |
The page you are looking for was not found.
12 | );
13 | };
14 |
15 | export default Page404;
16 |
--------------------------------------------------------------------------------
/client/app/containers/Contact/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Contact constants
4 | *
5 | */
6 |
7 | export const CONTACT_FORM_CHANGE = 'src/Contact/CONTACT_FORM_CHANGE';
8 | export const SET_CONTACT_FORM_ERRORS = 'src/Contact/SET_CONTACT_FORM_ERRORS';
9 | export const CONTACT_FORM_RESET = 'src/Contact/CONTACT_FORM_RESET';
10 |
--------------------------------------------------------------------------------
/client/app/containers/Homepage/banners.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "imageUrl": "/images/banners/banner-3.jpg",
4 | "link": "",
5 | "title": "",
6 | "content": "\n"
7 | },
8 | {
9 | "imageUrl": "/images/banners/banner-4.jpg",
10 | "link": "",
11 | "title": "",
12 | "content": "\n"
13 | }
14 | ]
15 |
--------------------------------------------------------------------------------
/client/app/containers/Newsletter/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Newsletter constants
4 | *
5 | */
6 |
7 | export const NEWSLETTER_CHANGE = 'src/Newsletter/NEWSLETTER_CHANGE';
8 | export const SET_NEWSLETTER_FORM_ERRORS =
9 | 'src/Newsletter/SET_NEWSLETTER_FORM_ERRORS';
10 | export const NEWSLETTER_RESET = 'src/Newsletter/NEWSLETTER_RESET';
11 |
--------------------------------------------------------------------------------
/client/app/styles/style.scss:
--------------------------------------------------------------------------------
1 | /* MERN Theme */
2 |
3 | // Import utilities
4 | @import 'core/utils/fonts';
5 | @import 'core/utils/reset';
6 |
7 | // Import Bootstrap source files
8 | @import 'node_modules/bootstrap/scss/bootstrap';
9 |
10 | // Import core styles
11 | @import 'core/core';
12 |
13 | // Import custom styles
14 | @import 'custom';
15 |
--------------------------------------------------------------------------------
/client/app/components/Common/ComingSoon/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * ComingSoon
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | const ComingSoon = props => {
10 | return (
11 |
12 |
Coming soon
13 | {props.children}
14 |
15 | );
16 | };
17 |
18 | export default ComingSoon;
19 |
--------------------------------------------------------------------------------
/client/app/containers/Account/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Account constants
4 | *
5 | */
6 |
7 | export const ACCOUNT_CHANGE = 'src/Account/ACCOUNT_CHANGE';
8 | export const FETCH_PROFILE = 'src/Account/FETCH_PROFILE';
9 | export const CLEAR_ACCOUNT = 'src/Account/CLEAR_ACCOUNT';
10 | export const SET_PROFILE_LOADING = 'src/Account/SET_PROFILE_LOADING';
11 |
--------------------------------------------------------------------------------
/client/app/containers/Authentication/actions.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Authentication actions
4 | *
5 | */
6 |
7 | import { SET_AUTH, CLEAR_AUTH } from './constants';
8 |
9 | export const setAuth = () => {
10 | return {
11 | type: SET_AUTH
12 | };
13 | };
14 |
15 | export const clearAuth = () => {
16 | return {
17 | type: CLEAR_AUTH
18 | };
19 | };
20 |
--------------------------------------------------------------------------------
/client/app/containers/Users/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Users constants
4 | *
5 | */
6 |
7 | export const FETCH_USERS = 'src/Users/FETCH_USERS';
8 | export const FETCH_SEARCHED_USERS = 'src/Users/FETCH_SEARCHED_USERS';
9 | export const SET_ADVANCED_FILTERS = 'src/Users/SET_ADVANCED_FILTERS';
10 | export const SET_USERS_LOADING = 'src/Users/SET_USERS_LOADING';
11 |
--------------------------------------------------------------------------------
/client/app/styles/core/_radio.scss:
--------------------------------------------------------------------------------
1 | .radio {
2 | ul {
3 | list-style: none;
4 | margin: 0 0 1px;
5 | padding: 0;
6 | }
7 |
8 | li:not(:last-child) {
9 | margin-bottom: 5px;
10 | }
11 |
12 | label {
13 | display: flex;
14 | align-items: center;
15 | }
16 |
17 | input[type='radio'] {
18 | margin: 0 10px 0 0;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/client/app/utils/app.js:
--------------------------------------------------------------------------------
1 | import { ROLES, EMAIL_PROVIDER } from '../constants';
2 |
3 | export const isProviderAllowed = provider =>
4 | provider === EMAIL_PROVIDER.Google || provider === EMAIL_PROVIDER.Facebook;
5 |
6 | export const isDisabledMerchantAccount = user =>
7 | user.role === ROLES.Merchant &&
8 | user.merchant &&
9 | user.merchant.isActive === false;
10 |
--------------------------------------------------------------------------------
/client/app/containers/ResetPassword/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * ResetPassword constants
4 | *
5 | */
6 |
7 | export const RESET_PASSWORD_CHANGE = 'src/ResetPassword/RESET_PASSWORD_CHANGE';
8 | export const RESET_PASSWORD_RESET = 'src/ResetPassword/RESET_PASSWORD_RESET';
9 | export const SET_RESET_PASSWORD_FORM_ERRORS =
10 | 'src/ResetPassword/SET_RESET_PASSWORD_FORM_ERRORS';
11 |
--------------------------------------------------------------------------------
/client/app/styles/core/_checkout.scss:
--------------------------------------------------------------------------------
1 | .easy-checkout {
2 | border-top: $border-default;
3 |
4 | .checkout-actions {
5 | padding: 10px;
6 | text-align: center;
7 |
8 | .input-btn {
9 | margin-right: 10px;
10 |
11 | @include media-breakpoint-down(xs) {
12 | width: 100%;
13 | margin-bottom: 10px;
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/client/app/components/Manager/SearchResultMeta/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * SearchResultMeta
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | const SearchResultMeta = props => {
10 | const { count, label } = props;
11 |
12 | return (
13 |
14 | {count} {label}
15 |
16 | );
17 | };
18 |
19 | export default SearchResultMeta;
20 |
--------------------------------------------------------------------------------
/client/app/utils/token.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * token.js
4 | * axios default headers setup
5 | */
6 |
7 | import axios from 'axios';
8 |
9 | const setToken = token => {
10 | if (token) {
11 | axios.defaults.headers.common['Authorization'] = token;
12 | } else {
13 | delete axios.defaults.headers.common['Authorization'];
14 | }
15 | };
16 |
17 | export default setToken;
18 |
--------------------------------------------------------------------------------
/server/routes/index.js:
--------------------------------------------------------------------------------
1 | const router = require('express').Router();
2 | const apiRoutes = require('./api');
3 |
4 | const keys = require('../config/keys');
5 | const { apiURL } = keys.app;
6 |
7 | const api = `/${apiURL}`;
8 |
9 | // api routes
10 | router.use(api, apiRoutes);
11 | router.use(api, (req, res) => res.status(404).json('No API route found'));
12 |
13 | module.exports = router;
14 |
--------------------------------------------------------------------------------
/client/app/containers/ForgotPassword/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * ForgotPassword constants
4 | *
5 | */
6 |
7 | export const FORGOT_PASSWORD_CHANGE =
8 | 'src/ForgotPassword/FORGOT_PASSWORD_CHANGE';
9 | export const FORGOT_PASSWORD_RESET = 'src/ForgotPassword/FORGOT_PASSWORD_RESET';
10 | export const SET_FORGOT_PASSWORD_FORM_ERRORS =
11 | 'src/ForgotPassword/SET_FORGOT_PASSWORD_FORM_ERRORS';
12 |
--------------------------------------------------------------------------------
/dockercompose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | client:
4 | build: client
5 | ports:
6 | - '8080:8080'
7 | server:
8 | build: server
9 | environment:
10 | - PORT=3000
11 | - BASE_API_URL=api
12 | - CLIENT_URL=http://localhost:8080
13 | - JWT_SECRET=update_your_JWT_secret
14 | - MONGO_URI=update_your_mongo_URI
15 | ports:
16 | - '3000:3000'
17 |
--------------------------------------------------------------------------------
/client/app/utils/base64.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * base64.js
4 | * this helper formulate buffer data to base64 string
5 | */
6 |
7 | export const arrayBufferToBase64 = buffer => {
8 | let binary = '';
9 | let bytes = [].slice.call(new Uint8Array(buffer.data.data));
10 | bytes.forEach((b) => binary += String.fromCharCode(b));
11 | return `data:${buffer.contentType};base64,${window.btoa(binary)}`;
12 | };
13 |
--------------------------------------------------------------------------------
/client/app/containers/Login/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Login constants
4 | *
5 | */
6 |
7 | export const LOGIN_CHANGE = 'src/Login/LOGIN_CHANGE';
8 | export const LOGIN_RESET = 'src/Login/LOGIN_RESET';
9 | export const SET_LOGIN_LOADING = 'src/Login/SET_LOGIN_LOADING';
10 | export const SET_LOGIN_FORM_ERRORS = 'src/Login/SET_LOGIN_FORM_ERRORS';
11 | export const SET_LOGIN_SUBMITTING = 'src/Login/SET_LOGIN_SUBMITTING';
12 |
--------------------------------------------------------------------------------
/client/app/containers/Cart/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Cart constants
4 | *
5 | */
6 |
7 | export const HANDLE_CART = 'src/Cart/HANDLE_CART';
8 | export const ADD_TO_CART = 'src/Cart/ADD_TO_CART';
9 | export const REMOVE_FROM_CART = 'src/Cart/REMOVE_FROM_CART';
10 | export const HANDLE_CART_TOTAL = 'src/Cart/HANDLE_CART_TOTAL';
11 | export const SET_CART_ID = 'src/Cart/SET_CART_ID';
12 | export const CLEAR_CART = 'src/Cart/CLEAR_CART';
13 |
--------------------------------------------------------------------------------
/client/app/containers/Application/reducer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Application reducer
4 | *
5 | */
6 |
7 | import { DEFAULT_ACTION } from './constants';
8 |
9 | const initialState = {};
10 |
11 | const applicationReducer = (state = initialState, action) => {
12 | switch (action.type) {
13 | case DEFAULT_ACTION:
14 | return state;
15 | default:
16 | return state;
17 | }
18 | };
19 |
20 | export default applicationReducer;
21 |
--------------------------------------------------------------------------------
/client/app/components/Common/NotFound/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * NotFound
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | const NotFound = props => {
10 | const { message, className, children } = props;
11 | return (
12 |
13 | {message ? message : children}
14 |
15 | );
16 | };
17 |
18 | NotFound.defaultProps = {
19 | className: ''
20 | };
21 |
22 | export default NotFound;
23 |
--------------------------------------------------------------------------------
/client/app/containers/Homepage/reducer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Homepage reducer
4 | *
5 | */
6 |
7 | import { DEFAULT_ACTION } from './constants';
8 |
9 | const initialState = {};
10 |
11 | const homepageReducer = (state = initialState, action) => {
12 | let newState;
13 | switch (action.type) {
14 | case DEFAULT_ACTION:
15 | return newState;
16 | default:
17 | return state;
18 | }
19 | };
20 |
21 | export default homepageReducer;
22 |
--------------------------------------------------------------------------------
/server/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use official Node.js 14 as base image
2 | FROM node:16.20.2-buster-slim
3 |
4 | # Set working directory
5 | WORKDIR /usr/src/app
6 |
7 | # Copy package.json and package-lock.json
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm install
12 |
13 | # Copy the rest of the server code
14 | COPY . .
15 |
16 | # Expose port 3000 for the server
17 | EXPOSE 3000
18 |
19 | # Start the server
20 | CMD [ "npm", "start" ]
21 |
--------------------------------------------------------------------------------
/client/app/containers/Shop/reducer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Products reducer
4 | *
5 | */
6 |
7 | import { DEFAULT_ACTION } from './constants';
8 |
9 | const initialState = {};
10 |
11 | const productsReducer = (state = initialState, action) => {
12 | switch (action.type) {
13 | case DEFAULT_ACTION:
14 | return {
15 | ...state
16 | };
17 | default:
18 | return state;
19 | }
20 | };
21 |
22 | export default productsReducer;
23 |
--------------------------------------------------------------------------------
/server/middleware/role.js:
--------------------------------------------------------------------------------
1 | const check =
2 | (...roles) =>
3 | (req, res, next) => {
4 | if (!req.user) {
5 | return res.status(401).send('Unauthorized');
6 | }
7 |
8 | const hasRole = roles.find(role => req.user.role === role);
9 | if (!hasRole) {
10 | return res.status(403).send('You are not allowed to make this request.');
11 | }
12 |
13 | return next();
14 | };
15 |
16 | const role = { check };
17 | module.exports = role;
18 |
--------------------------------------------------------------------------------
/client/app/containers/NavigationMenu/reducer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * NavigationMenu reducer
4 | *
5 | */
6 |
7 | import { DEFAULT_ACTION } from './constants';
8 |
9 | const initialState = {};
10 |
11 | const navigationMenuReducer = (state = initialState, action) => {
12 | switch (action.type) {
13 | case DEFAULT_ACTION:
14 | return {
15 | ...state
16 | };
17 | default:
18 | return state;
19 | }
20 | };
21 |
22 | export default navigationMenuReducer;
23 |
--------------------------------------------------------------------------------
/client/app/containers/Signup/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Signup constants
4 | *
5 | */
6 |
7 | export const SIGNUP_CHANGE = 'src/Signup/SIGNUP_CHANGE';
8 | export const SIGNUP_RESET = 'src/Signup/SIGNUP_RESET';
9 | export const SET_SIGNUP_LOADING = 'src/Signup/SET_SIGNUP_LOADING';
10 | export const SET_SIGNUP_SUBMITTING = 'src/Signup/SET_SIGNUP_SUBMITTING';
11 | export const SET_SIGNUP_FORM_ERRORS = 'src/Signup/SET_SIGNUP_FORM_ERRORS';
12 | export const SUBSCRIBE_CHANGE = 'src/Signup/SUBSCRIBE_CHANGE';
13 |
--------------------------------------------------------------------------------
/server/models/contact.js:
--------------------------------------------------------------------------------
1 | const Mongoose = require('mongoose');
2 | const { Schema } = Mongoose;
3 |
4 | // Contact Schema
5 | const ContactSchema = new Schema({
6 | name: {
7 | type: String,
8 | trim: true
9 | },
10 | email: {
11 | type: String
12 | },
13 | message: {
14 | type: String,
15 | trim: true
16 | },
17 | updated: Date,
18 | created: {
19 | type: Date,
20 | default: Date.now
21 | }
22 | });
23 |
24 | module.exports = Mongoose.model('Contact', ContactSchema);
25 |
--------------------------------------------------------------------------------
/server/utils/auth.js:
--------------------------------------------------------------------------------
1 | const jwt = require('jsonwebtoken');
2 |
3 | const checkAuth = async req => {
4 | try {
5 | if (!req.headers.authorization) {
6 | return null;
7 | }
8 |
9 | const token =
10 | (await jwt.decode(req.headers.authorization.split(' ')[1])) ||
11 | req.headers.authorization;
12 |
13 | if (!token) {
14 | return null;
15 | }
16 |
17 | return token;
18 | } catch (error) {
19 | return null;
20 | }
21 | };
22 |
23 | module.exports = checkAuth;
24 |
--------------------------------------------------------------------------------
/client/app/components/Common/Tooltip/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Tooltip
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import { UncontrolledTooltip } from 'reactstrap';
10 |
11 | const Tooltip = props => {
12 | const { target, placement, children } = props;
13 |
14 | return (
15 |
16 | {children}
17 |
18 | );
19 | };
20 |
21 | Tooltip.defaultProps = {
22 | placement: 'top'
23 | };
24 |
25 | export default Tooltip;
26 |
--------------------------------------------------------------------------------
/client/app/containers/Navigation/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Navigation constants
4 | *
5 | */
6 |
7 | export const TOGGLE_MENU = 'src/Navigation/TOGGLE_MENU';
8 | export const TOGGLE_CART = 'src/Navigation/TOGGLE_CART';
9 | export const TOGGLE_BRAND = 'src/Navigation/TOGGLE_BRAND';
10 | export const SEARCH_CHANGE = 'src/Navigation/SEARCH_CHANGE';
11 | export const SUGGESTIONS_FETCH_REQUEST =
12 | 'src/Navigation/SUGGESTIONS_FETCH_REQUEST';
13 | export const SUGGESTIONS_CLEAR_REQUEST =
14 | 'src/Navigation/SUGGESTIONS_CLEAR_REQUEST';
15 |
--------------------------------------------------------------------------------
/client/app/styles/core/_badge.scss:
--------------------------------------------------------------------------------
1 | .custom-badge {
2 | padding: 8px 10px;
3 | // border-radius: $border-radius-default;
4 | font-weight: $font-weight-normal;
5 | }
6 |
7 | .custom-badge-primary {
8 | background-color: $primary-bg;
9 | color: $white;
10 | }
11 |
12 | .custom-badge-secondary {
13 | background-color: $secondary-bg;
14 | }
15 |
16 | .custom-badge-dark {
17 | background-color: $dark-bg;
18 | color: $white;
19 | }
20 |
21 | .custom-badge-danger {
22 | background-color: $danger-bg;
23 | color: $white;
24 | }
25 |
--------------------------------------------------------------------------------
/client/app/styles/core/_merchant.scss:
--------------------------------------------------------------------------------
1 | .sell {
2 | .agreement-banner-text {
3 | background-color: $theme-white;
4 | padding: 30px 16px 30px;
5 | border-radius: $border-radius-primary;
6 | }
7 |
8 | .agreement-banner {
9 | margin: 0 auto;
10 | width: 250px;
11 |
12 | @include media-breakpoint-up(md) {
13 | width: 300px;
14 | }
15 | }
16 | }
17 |
18 | .merchant-dashboard {
19 | .merchant-box {
20 | border-radius: $border-radius-default;
21 | box-shadow: $box-shadow-secondary;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/client/app/styles/core/_table.scss:
--------------------------------------------------------------------------------
1 | .table-section {
2 | .search {
3 | margin: 15px 0px;
4 |
5 | .search-label {
6 | width: 100%;
7 | }
8 | }
9 |
10 | .react-bootstrap-table {
11 | .table {
12 | overflow-x: scroll;
13 | display: block;
14 | margin: 20px 0px;
15 | color: $font-custom-color;
16 |
17 | td,
18 | th {
19 | white-space: nowrap;
20 | text-overflow: unset;
21 | overflow-wrap: break-word;
22 | width: 100%;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/client/app/containers/Dashboard/reducer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Dashboard reducer
4 | *
5 | */
6 |
7 | import { TOGGLE_DASHBOARD_MENU } from './constants';
8 |
9 | const initialState = {
10 | isMenuOpen: false
11 | };
12 |
13 | const dashboardReducer = (state = initialState, action) => {
14 | switch (action.type) {
15 | case TOGGLE_DASHBOARD_MENU:
16 | return {
17 | ...state,
18 | isMenuOpen: !state.isMenuOpen
19 | };
20 | default:
21 | return state;
22 | }
23 | };
24 |
25 | export default dashboardReducer;
26 |
--------------------------------------------------------------------------------
/server/models/order.js:
--------------------------------------------------------------------------------
1 | const Mongoose = require('mongoose');
2 | const { Schema } = Mongoose;
3 |
4 | // Order Schema
5 | const OrderSchema = new Schema({
6 | cart: {
7 | type: Schema.Types.ObjectId,
8 | ref: 'Cart'
9 | },
10 | user: {
11 | type: Schema.Types.ObjectId,
12 | ref: 'User'
13 | },
14 | total: {
15 | type: Number,
16 | default: 0
17 | },
18 | updated: Date,
19 | created: {
20 | type: Date,
21 | default: Date.now
22 | }
23 | });
24 |
25 | module.exports = Mongoose.model('Order', OrderSchema);
26 |
--------------------------------------------------------------------------------
/client/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use official Node.js 14 as base image
2 | FROM node:16.20.2-buster-slim
3 |
4 | # Set working directory
5 | WORKDIR /usr/src/app
6 |
7 | # Copy package.json and package-lock.json
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm install
12 |
13 | # Copy the rest of the client code
14 | COPY . .
15 |
16 | RUN mv .env.example .env
17 |
18 | # Build the client for production
19 | RUN npm run build
20 |
21 | # Expose port 8080 for the client
22 | EXPOSE 8080
23 |
24 | # Start the client
25 | CMD [ "npm", "run", "dev" ]
26 |
--------------------------------------------------------------------------------
/client/app/containers/Order/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Order constants
4 | *
5 | */
6 |
7 | export const FETCH_ORDERS = 'src/Orders/FETCH_ORDERS';
8 | export const FETCH_SEARCHED_ORDERS = 'src/Orders/FETCH_SEARCHED_ORDERS';
9 | export const FETCH_ORDER = 'src/Order/FETCH_ORDER';
10 | export const UPDATE_ORDER_STATUS = 'src/Order/UPDATE_ORDER_STATUS';
11 | export const SET_ORDERS_LOADING = 'src/Orders/SET_ORDERS_LOADING';
12 | export const SET_ADVANCED_FILTERS = 'src/Orders/SET_ADVANCED_FILTERS';
13 | export const CLEAR_ORDERS = 'src/Orders/CLEAR_ORDERS';
14 |
--------------------------------------------------------------------------------
/client/app/scrollToTop.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * scrollToTop.js
4 | * scroll restoration
5 | */
6 |
7 | import React from 'react';
8 |
9 | import { withRouter } from 'react-router-dom';
10 |
11 | class ScrollToTop extends React.Component {
12 | componentDidUpdate(prevProps) {
13 | if (this.props.location.pathname !== prevProps.location.pathname) {
14 | window.scroll({
15 | top: 0,
16 | behavior: 'smooth'
17 | });
18 | }
19 | }
20 |
21 | render() {
22 | return this.props.children;
23 | }
24 | }
25 |
26 | export default withRouter(ScrollToTop);
27 |
--------------------------------------------------------------------------------
/client/app/components/Manager/UserSearch/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * UserSearch
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import SearchBar from '../../Common/SearchBar';
10 |
11 | const UserSearch = props => {
12 | return (
13 |
14 |
22 |
23 | );
24 | };
25 |
26 | export default UserSearch;
27 |
--------------------------------------------------------------------------------
/server/.env.example:
--------------------------------------------------------------------------------
1 | PORT=3000
2 | MONGO_URI=mongodb://127.0.0.1:27017/mern_ecommerce
3 | JWT_SECRET=
4 | MAILCHIMP_KEY=
5 | MAILCHIMP_LIST_KEY=
6 | MAILGUN_KEY=
7 | MAILGUN_DOMAIN=
8 | MAILGUN_EMAIL_SENDER=
9 | GOOGLE_CLIENT_ID=
10 | GOOGLE_CLIENT_SECRET=
11 | GOOGLE_CALLBACK_URL=http://localhost:3000/api/auth/google/callback
12 | FACEBOOK_CLIENT_ID=
13 | FACEBOOK_CLIENT_SECRET=
14 | FACEBOOK_CALLBACK_URL=http://localhost:3000/api/auth/facebook/callback
15 | CLIENT_URL=http://localhost:8080
16 | BASE_API_URL=api
17 | AWS_ACCESS_KEY_ID=
18 | AWS_SECRET_ACCESS_KEY=
19 | AWS_REGION=us-east-2
20 | AWS_BUCKET_NAME=
--------------------------------------------------------------------------------
/client/app/styles/core/_newsletter.scss:
--------------------------------------------------------------------------------
1 | .newsletter-form {
2 | p {
3 | margin: 0;
4 | }
5 | }
6 |
7 | .subscribe {
8 | .inline-btn-box {
9 | .input-text-block {
10 | @include media-breakpoint-between(md, lg) {
11 | flex-direction: column;
12 |
13 | .input-text {
14 | border-radius: $border-radius-default;
15 | }
16 |
17 | .custom-btn-primary {
18 | margin-top: 10px;
19 | border-left: $border-default !important;
20 | border-radius: $border-radius-default !important;
21 | }
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/client/app/components/Manager/OrderSearch/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * OrderSearch
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import SearchBar from '../../Common/SearchBar';
10 |
11 | const OrderSearch = props => {
12 | return (
13 |
14 |
22 |
23 | );
24 | };
25 |
26 | export default OrderSearch;
27 |
--------------------------------------------------------------------------------
/client/public/images/bag.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/client/app/components/Common/CarouselSlider/utils.js:
--------------------------------------------------------------------------------
1 | export const responsiveOneItemCarousel = {
2 | desktop: {
3 | breakpoint: {
4 | max: 3000,
5 | min: 1024
6 | },
7 | items: 1,
8 | slidesToSlide: 1,
9 | partialVisibilityGutter: 0
10 | },
11 | mobile: {
12 | breakpoint: {
13 | max: 464,
14 | min: 0
15 | },
16 | items: 1,
17 | slidesToSlide: 1,
18 | partialVisibilityGutter: 0
19 | },
20 | tablet: {
21 | breakpoint: {
22 | max: 1024,
23 | min: 200
24 | },
25 | items: 1,
26 | slidesToSlide: 1,
27 | partialVisibilityGutter: 0
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/client/app/components/Manager/MerchantSearch/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * MerchantSearch
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import SearchBar from '../../Common/SearchBar';
10 |
11 | const MerchantSearch = props => {
12 | return (
13 |
14 |
22 |
23 | );
24 | };
25 |
26 | export default MerchantSearch;
27 |
--------------------------------------------------------------------------------
/client/app/styles/core/_coming-soon.scss:
--------------------------------------------------------------------------------
1 | .coming-soon {
2 | animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
3 | animation-iteration-count: 1;
4 | transform: translate3d(0, 0, 0);
5 | backface-visibility: hidden;
6 | perspective: 1000px;
7 | text-align: center;
8 | }
9 |
10 | @keyframes shake {
11 | 10%,
12 | 90% {
13 | transform: translate3d(-1px, 0, 0);
14 | }
15 |
16 | 20%,
17 | 80% {
18 | transform: translate3d(2px, 0, 0);
19 | }
20 |
21 | 30%,
22 | 50%,
23 | 70% {
24 | transform: translate3d(-4px, 0, 0);
25 | }
26 |
27 | 40%,
28 | 60% {
29 | transform: translate3d(4px, 0, 0);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/server/models/wishlist.js:
--------------------------------------------------------------------------------
1 | const Mongoose = require('mongoose');
2 | const { Schema } = Mongoose;
3 |
4 | // Wishlist Schema
5 | const WishlistSchema = new Schema({
6 | product: {
7 | type: Schema.Types.ObjectId,
8 | ref: 'Product',
9 | default: null
10 | },
11 | user: {
12 | type: Schema.Types.ObjectId,
13 | ref: 'User',
14 | default: null
15 | },
16 | isLiked: {
17 | type: Boolean,
18 | default: false
19 | },
20 | updated: {
21 | type: Date,
22 | default: Date.now
23 | },
24 | created: {
25 | type: Date,
26 | default: Date.now
27 | }
28 | });
29 |
30 | module.exports = Mongoose.model('Wishlist', WishlistSchema);
31 |
--------------------------------------------------------------------------------
/client/app/components/Common/Popover/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Popover
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import { UncontrolledPopover, PopoverHeader, PopoverBody } from 'reactstrap';
10 |
11 | const Popover = props => {
12 | const { target, placement, popoverTitle, children } = props;
13 |
14 | return (
15 |
16 | {popoverTitle && {popoverTitle}}
17 | {children}
18 |
19 | );
20 | };
21 |
22 | Popover.defaultProps = {
23 | placement: 'top'
24 | };
25 |
26 | export default Popover;
27 |
--------------------------------------------------------------------------------
/client/app/containers/Authentication/reducer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Authentication reducer
4 | *
5 | */
6 |
7 | import { SET_AUTH, CLEAR_AUTH } from './constants';
8 |
9 | const initialState = {
10 | authenticated: false,
11 | isLoading: false
12 | };
13 |
14 | const authenticationReducer = (state = initialState, action) => {
15 | switch (action.type) {
16 | case SET_AUTH:
17 | return {
18 | ...state,
19 | authenticated: true
20 | };
21 | case CLEAR_AUTH:
22 | return {
23 | ...state,
24 | authenticated: false
25 | };
26 | default:
27 | return state;
28 | }
29 | };
30 |
31 | export default authenticationReducer;
32 |
--------------------------------------------------------------------------------
/client/app/containers/Review/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Review constants
4 | *
5 | */
6 |
7 | export const FETCH_REVIEWS = 'src/Review/FETCH_REVIEWS';
8 | export const ADD_REVIEW = 'src/Review/ADD_REVIEW';
9 | export const REMOVE_REVIEW = 'src/Review/REMOVE_REVIEW';
10 | export const FETCH_PRODUCT_REVIEWS = 'src/Review/FETCH_PRODUCT_REVIEWS';
11 | export const REVIEW_CHANGE = 'src/Review/REVIEW_CHANGE';
12 | export const SET_REVIEWS_LOADING = 'src/Review/SET_REVIEWS_LOADING';
13 | export const RESET_REVIEW = 'src/Review/RESET_REVIEW';
14 | export const SET_REVIEW_FORM_ERRORS = 'src/Review/SET_REVIEW_FORM_ERRORS';
15 | export const SET_ADVANCED_FILTERS = 'src/Review/SET_ADVANCED_FILTERS';
16 |
--------------------------------------------------------------------------------
/client/app/styles/core/_address.scss:
--------------------------------------------------------------------------------
1 | .address-dashboard {
2 | .a-list {
3 | .address-box {
4 | height: 100%;
5 | border-radius: $border-radius-default;
6 | box-shadow: $box-shadow-secondary;
7 | @include transition();
8 |
9 | &:hover {
10 | background-color: $secondary-bg;
11 | @include transition();
12 | }
13 |
14 | .address-icon {
15 | width: 35px;
16 | height: 35px;
17 |
18 | @include media-breakpoint-up(lg) {
19 | width: 50px;
20 | height: 50px;
21 | }
22 | }
23 |
24 | .address-desc {
25 | @include text-ellipsis(2);
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/server/services/mailchimp.js:
--------------------------------------------------------------------------------
1 | const Mailchimp = require('mailchimp-api-v3');
2 |
3 | const keys = require('../config/keys');
4 |
5 | const { key, listKey } = keys.mailchimp;
6 |
7 | class MailchimpService {
8 | init() {
9 | try {
10 | return new Mailchimp(key);
11 | } catch (error) {
12 | console.warn('Missing mailgun keys');
13 | }
14 | }
15 | }
16 |
17 | const mailchimp = new MailchimpService().init();
18 |
19 | exports.subscribeToNewsletter = async email => {
20 | try {
21 | return await mailchimp.post(`lists/${listKey}/members`, {
22 | email_address: email,
23 | status: 'subscribed'
24 | });
25 | } catch (error) {
26 | return error;
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/client/app/containers/WishList/reducer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * WishList reducer
4 | *
5 | */
6 |
7 | import { FETCH_WISHLIST, SET_WISHLIST_LOADING } from './constants';
8 |
9 | const initialState = {
10 | wishlist: [],
11 | isLoading: false,
12 | wishlistForm: {}
13 | };
14 |
15 | const wishListReducer = (state = initialState, action) => {
16 | switch (action.type) {
17 | case FETCH_WISHLIST:
18 | return {
19 | ...state,
20 | wishlist: action.payload
21 | };
22 | case SET_WISHLIST_LOADING:
23 | return {
24 | ...state,
25 | isLoading: action.payload
26 | };
27 | default:
28 | return state;
29 | }
30 | };
31 |
32 | export default wishListReducer;
33 |
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | MERN Store
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/client/webpack/webpack.common.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const CopyWebpackPlugin = require('copy-webpack-plugin');
3 |
4 | const CURRENT_WORKING_DIR = process.cwd();
5 |
6 | module.exports = {
7 | entry: [path.join(CURRENT_WORKING_DIR, 'app/index.js')],
8 | resolve: {
9 | extensions: ['.js', '.json', '.css', '.scss', '.html'],
10 | alias: {
11 | app: 'app'
12 | }
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.(js|jsx)$/,
18 | loader: 'babel-loader',
19 | exclude: /(node_modules)/
20 | }
21 | ]
22 | },
23 | plugins: [
24 | new CopyWebpackPlugin([
25 | {
26 | from: 'public'
27 | }
28 | ])
29 | ]
30 | };
31 |
--------------------------------------------------------------------------------
/server/models/address.js:
--------------------------------------------------------------------------------
1 | const Mongoose = require('mongoose');
2 | const { Schema } = Mongoose;
3 |
4 | // Address Schema
5 | const AddressSchema = new Schema({
6 | user: {
7 | type: Schema.Types.ObjectId,
8 | ref: 'User'
9 | },
10 | address: {
11 | type: String
12 | },
13 | city: {
14 | type: String
15 | },
16 | state: {
17 | type: String
18 | },
19 | country: {
20 | type: String
21 | },
22 | zipCode: {
23 | type: String
24 | },
25 | isDefault: {
26 | type: Boolean,
27 | default: false
28 | },
29 | updated: Date,
30 | created: {
31 | type: Date,
32 | default: Date.now
33 | }
34 | });
35 |
36 | module.exports = Mongoose.model('Address', AddressSchema);
37 |
--------------------------------------------------------------------------------
/client/app/components/Common/LoadingIndicator/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * LoadingIndicator
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | const LoadingIndicator = props => {
10 | const { inline, backdrop } = props;
11 |
12 | return (
13 |
24 | );
25 | };
26 |
27 | LoadingIndicator.defaultProps = {
28 | inline: false,
29 | backdrop: false
30 | };
31 |
32 | export default LoadingIndicator;
33 |
--------------------------------------------------------------------------------
/server/utils/db.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | const chalk = require('chalk');
3 | const mongoose = require('mongoose');
4 |
5 | const keys = require('../config/keys');
6 | const { database } = keys;
7 |
8 | const setupDB = async () => {
9 | try {
10 | // Connect to MongoDB
11 | mongoose.set('useCreateIndex', true);
12 | mongoose
13 | .connect(database.url, {
14 | useNewUrlParser: true,
15 | useUnifiedTopology: true,
16 | useFindAndModify: false
17 | })
18 | .then(() =>
19 | console.log(`${chalk.green('✓')} ${chalk.blue('MongoDB Connected!')}`)
20 | )
21 | .catch(err => console.log(err));
22 | } catch (error) {
23 | return null;
24 | }
25 | };
26 |
27 | module.exports = setupDB;
28 |
--------------------------------------------------------------------------------
/client/app/components/Store/AddToWishList/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * AddToWishList
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import Checkbox from '../../Common/Checkbox';
10 | import { HeartIcon } from '../../Common/Icon';
11 |
12 | const AddToWishList = props => {
13 | const { id, liked, enabled, updateWishlist } = props;
14 |
15 | return (
16 |
17 | }
23 | onChange={(_, value) => {
24 | updateWishlist(value, id);
25 | }}
26 | />
27 |
28 | );
29 | };
30 |
31 | export default AddToWishList;
32 |
--------------------------------------------------------------------------------
/client/app/styles/core/_account.scss:
--------------------------------------------------------------------------------
1 | .account {
2 | .info {
3 | margin-bottom: 10px;
4 | @include flex();
5 | // flex-wrap: wrap;
6 | justify-content: flex-start;
7 | flex-direction: row;
8 | align-items: center;
9 | @include media-breakpoint-down(xs) {
10 | flex-direction: column;
11 | align-items: normal;
12 | }
13 |
14 | .desc {
15 | flex: 1;
16 | @include media-breakpoint-down(xs) {
17 | @include flex();
18 | justify-content: space-between;
19 | align-items: center;
20 | }
21 |
22 | .provider-email {
23 | text-transform: capitalize;
24 | }
25 | }
26 |
27 | p,
28 | span {
29 | margin: 0;
30 | display: inline;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/client/app/components/Common/SignupProvider/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * SignupProvider
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import { GoogleIcon, FacebookIcon } from '../Icon';
10 | import { API_URL } from '../../../constants';
11 |
12 | const SignupProvider = () => {
13 | return (
14 |
25 | );
26 | };
27 |
28 | export default SignupProvider;
29 |
--------------------------------------------------------------------------------
/client/app/utils/validation.js:
--------------------------------------------------------------------------------
1 | import Validator from 'validatorjs';
2 | import DOMPurify from 'dompurify';
3 |
4 | export const allFieldsValidation = (data, rules, options) => {
5 | const validation = new Validator(data, rules, options);
6 | const validationResponse = { isValid: validation.passes() };
7 | if (!validationResponse.isValid) {
8 | validationResponse.errors = validation.errors.all();
9 | }
10 |
11 | return validationResponse;
12 | };
13 |
14 | export const santizeFields = data => {
15 | const fields = { ...data };
16 |
17 | for (const field in fields) {
18 | if (typeof field === 'string') {
19 | fields[field] = DOMPurify.sanitize(fields[field], {
20 | USE_PROFILES: { html: false }
21 | });
22 | }
23 | }
24 | return fields;
25 | };
26 |
--------------------------------------------------------------------------------
/server/constants/index.js:
--------------------------------------------------------------------------------
1 | exports.ROLES = {
2 | Admin: 'ROLE ADMIN',
3 | Member: 'ROLE MEMBER',
4 | Merchant: 'ROLE MERCHANT'
5 | };
6 |
7 | exports.MERCHANT_STATUS = {
8 | Rejected: 'Rejected',
9 | Approved: 'Approved',
10 | Waiting_Approval: 'Waiting Approval'
11 | };
12 |
13 | exports.CART_ITEM_STATUS = {
14 | Processing: 'Processing',
15 | Shipped: 'Shipped',
16 | Delivered: 'Delivered',
17 | Cancelled: 'Cancelled',
18 | Not_processed: 'Not processed'
19 | };
20 |
21 | exports.REVIEW_STATUS = {
22 | Rejected: 'Rejected',
23 | Approved: 'Approved',
24 | Waiting_Approval: 'Waiting Approval'
25 | };
26 |
27 | exports.EMAIL_PROVIDER = {
28 | Email: 'Email',
29 | Google: 'Google',
30 | Facebook: 'Facebook'
31 | };
32 |
33 | exports.JWT_COOKIE = 'x-jwt-cookie';
34 |
--------------------------------------------------------------------------------
/client/app/containers/BrandsPage/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * BrandsPage
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import { connect } from 'react-redux';
10 |
11 | import actions from '../../actions';
12 |
13 | import BrandList from '../../components/Store/BrandList';
14 |
15 | class BrandsPage extends React.PureComponent {
16 | componentDidMount() {
17 | this.props.fetchStoreBrands();
18 | }
19 |
20 | render() {
21 | const { brands } = this.props;
22 |
23 | return (
24 |
25 |
26 |
27 | );
28 | }
29 | }
30 |
31 | const mapStateToProps = state => {
32 | return {
33 | brands: state.brand.storeBrands
34 | };
35 | };
36 |
37 | export default connect(mapStateToProps, actions)(BrandsPage);
38 |
--------------------------------------------------------------------------------
/client/app/components/Manager/UserRole/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * UserRole
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import { ROLES } from '../../../constants';
10 | import Badge from '../../Common/Badge';
11 |
12 | const UserRole = props => {
13 | const { className, user } = props;
14 |
15 | return (
16 | <>
17 | {user.role === ROLES.Admin ? (
18 |
19 | Admin
20 |
21 | ) : user.role === ROLES.Merchant ? (
22 |
23 | Merchant
24 |
25 | ) : (
26 | Member
27 | )}
28 | >
29 | );
30 | };
31 |
32 | UserRole.defaultProps = {
33 | className: ''
34 | };
35 |
36 | export default UserRole;
37 |
--------------------------------------------------------------------------------
/client/app/components/Manager/SubPage/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * SubPage
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import Button from '../../Common/Button';
10 |
11 | const SubPage = props => {
12 | const { title, actionTitle, handleAction, children } = props;
13 |
14 | return (
15 |
16 |
17 |
{title}
18 | {actionTitle && (
19 |
20 |
26 |
27 | )}
28 |
29 |
{children}
30 |
31 | );
32 | };
33 |
34 | export default SubPage;
35 |
--------------------------------------------------------------------------------
/client/app/containers/Address/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Address constants
4 | *
5 | */
6 |
7 | export const FETCH_ADDRESS = 'src/Address/FETCH_ADDRESS';
8 | export const FETCH_ADDRESSES = 'src/Address/FETCH_ADDRESSES';
9 | export const ADDRESS_CHANGE = 'src/Address/ADDRESS_CHANGE';
10 | export const ADDRESS_EDIT_CHANGE = 'src/Address/ADDRESS_EDIT_CHANGE';
11 | export const SET_ADDRESS_FORM_ERRORS = 'src/Address/SET_ADDRESS_FORM_ERRORS';
12 | export const SET_ADDRESS_FORM_EDIT_ERRORS =
13 | 'src/Address/SET_ADDRESS_FORM_EDIT_ERRORS';
14 | export const RESET_ADDRESS = 'src/Address/RESET_ADDRESS';
15 | export const ADD_ADDRESS = 'src/Address/ADD_ADDRESS';
16 | export const REMOVE_ADDRESS = 'src/Address/REMOVE_ADDRESS';
17 | export const SET_ADDRESS_LOADING = 'src/Address/SET_ADDRESS_LOADING';
18 | export const ADDRESS_SELECT = 'src/Brand/BRAND_SELECT';
19 |
--------------------------------------------------------------------------------
/client/app/containers/Authentication/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Authentication
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import { connect } from 'react-redux';
10 | import { Redirect } from 'react-router-dom';
11 |
12 | import actions from '../../actions';
13 |
14 | export default function (ComposedComponent) {
15 | class Authentication extends React.PureComponent {
16 | render() {
17 | const { authenticated } = this.props;
18 |
19 | if (!authenticated) {
20 | return ;
21 | } else {
22 | return ;
23 | }
24 | }
25 | }
26 |
27 | const mapStateToProps = state => {
28 | return {
29 | authenticated: state.authentication.authenticated
30 | };
31 | };
32 |
33 | return connect(mapStateToProps, actions)(Authentication);
34 | }
35 |
--------------------------------------------------------------------------------
/server/routes/api/newsletter.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const router = express.Router();
3 |
4 | const mailchimp = require('../../services/mailchimp');
5 | const mailgun = require('../../services/mailgun');
6 |
7 | router.post('/subscribe', async (req, res) => {
8 | const email = req.body.email;
9 |
10 | if (!email) {
11 | return res.status(400).json({ error: 'You must enter an email address.' });
12 | }
13 |
14 | const result = await mailchimp.subscribeToNewsletter(email);
15 |
16 | if (result.status === 400) {
17 | return res.status(400).json({ error: result.title });
18 | }
19 |
20 | await mailgun.sendEmail(email, 'newsletter-subscription');
21 |
22 | res.status(200).json({
23 | success: true,
24 | message: 'You have successfully subscribed to the newsletter'
25 | });
26 | });
27 |
28 | module.exports = router;
29 |
--------------------------------------------------------------------------------
/client/app/containers/Brand/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Brand constants
4 | *
5 | */
6 |
7 | export const FETCH_BRANDS = 'src/Brand/FETCH_BRANDS';
8 | export const FETCH_STORE_BRANDS = 'src/Brand/FETCH_STORE_BRANDS';
9 | export const FETCH_BRAND = 'src/Brand/FETCH_BRAND';
10 | export const BRAND_CHANGE = 'src/Brand/BRAND_CHANGE';
11 | export const BRAND_EDIT_CHANGE = 'src/Brand/BRAND_EDIT_CHANGE';
12 | export const SET_BRAND_FORM_ERRORS = 'src/Brand/SET_BRAND_FORM_ERRORS';
13 | export const SET_BRAND_FORM_EDIT_ERRORS =
14 | 'src/Brand/SET_BRAND_FORM_EDIT_ERRORS';
15 | export const RESET_BRAND = 'src/Brand/RESET_BRAND';
16 | export const ADD_BRAND = 'src/Brand/ADD_BRAND';
17 | export const REMOVE_BRAND = 'src/Brand/REMOVE_BRAND';
18 | export const FETCH_BRANDS_SELECT = 'src/Brand/FETCH_BRANDS_SELECT';
19 | export const SET_BRANDS_LOADING = 'src/Brand/SET_BRANDS_LOADING';
20 |
--------------------------------------------------------------------------------
/client/public/images/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
38 |
--------------------------------------------------------------------------------
/client/app/components/Manager/CategoryList/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * CategoryList
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import { Link } from 'react-router-dom';
10 |
11 | const CategoryList = props => {
12 | const { categories } = props;
13 |
14 | return (
15 |
16 | {categories.map((category, index) => (
17 |
22 |
23 |
{category.name}
24 |
25 |
{category.description}
26 |
27 | ))}
28 |
29 | );
30 | };
31 |
32 | export default CategoryList;
33 |
--------------------------------------------------------------------------------
/client/app/containers/Newsletter/reducer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Newsletter reducer
4 | *
5 | */
6 |
7 | import {
8 | NEWSLETTER_CHANGE,
9 | SET_NEWSLETTER_FORM_ERRORS,
10 | NEWSLETTER_RESET
11 | } from './constants';
12 |
13 | const initialState = {
14 | email: '',
15 | formErrors: {}
16 | };
17 |
18 | const newsletterReducer = (state = initialState, action) => {
19 | switch (action.type) {
20 | case NEWSLETTER_CHANGE:
21 | return {
22 | ...state,
23 | email: action.payload
24 | };
25 | case SET_NEWSLETTER_FORM_ERRORS:
26 | return {
27 | ...state,
28 | formErrors: action.payload
29 | };
30 | case NEWSLETTER_RESET:
31 | return {
32 | ...state,
33 | email: '',
34 | formErrors: {}
35 | };
36 | default:
37 | return state;
38 | }
39 | };
40 |
41 | export default newsletterReducer;
42 |
--------------------------------------------------------------------------------
/client/app/containers/Support/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Support
4 | *
5 | */
6 |
7 | import React from 'react';
8 | import { connect } from 'react-redux';
9 |
10 | import actions from '../../actions';
11 |
12 | import { default as SupportManager } from '../../components/Manager/Support';
13 |
14 | class Support extends React.PureComponent {
15 | render() {
16 | const { user } = this.props;
17 |
18 | return (
19 |
20 |
Support
21 |
22 |
23 |
24 |
25 |
26 | );
27 | }
28 | }
29 |
30 | const mapStateToProps = state => {
31 | return {
32 | user: state.account.user,
33 | resetFormData: state.resetPassword.resetFormData,
34 | formErrors: state.resetPassword.formErrors
35 | };
36 | };
37 |
38 | export default connect(mapStateToProps, actions)(Support);
39 |
--------------------------------------------------------------------------------
/client/app/components/Common/DropdownConfirm/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * DropdownConfirm
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import {
10 | UncontrolledButtonDropdown,
11 | DropdownMenu,
12 | DropdownToggle
13 | } from 'reactstrap';
14 |
15 | const DropdownConfirm = props => {
16 | const { className, label, children } = props;
17 |
18 | return (
19 |
20 |
21 |
22 |
23 | {label}
24 |
25 |
26 |
27 | {children}
28 |
29 |
30 | );
31 | };
32 |
33 | DropdownConfirm.defaultProps = {
34 | label: ''
35 | };
36 |
37 | export default DropdownConfirm;
38 |
--------------------------------------------------------------------------------
/client/app/components/Manager/DisabledAccount/Merchant.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * DisabledMerchantAccount
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | const DisabledMerchantAccount = props => {
10 | const { user } = props;
11 |
12 | return (
13 |
17 |
Hi, {user.firstName}
18 |
19 |
Unfortunately it seems your account has been disabled.
20 |
21 | Please contact admin to request access again.
22 |
23 |
24 |
25 | Call us 951-999-9999
26 |
27 |
28 |
29 | );
30 | };
31 |
32 | export default DisabledMerchantAccount;
33 |
--------------------------------------------------------------------------------
/client/app/contexts/Socket/provider.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import io from 'socket.io-client';
4 |
5 | import SocketContext from './context';
6 | import { SOCKET_URL } from '../../constants';
7 |
8 | const SocketProvider = ({ children }) => {
9 | const [socket, setSocket] = useState(null);
10 |
11 | const connect = () => {
12 | const token = localStorage.getItem('token');
13 | const sk = io(SOCKET_URL, {
14 | autoConnect: false
15 | });
16 |
17 | if (token) {
18 | sk.auth = { token };
19 | sk.connect();
20 | sk.auth;
21 | setSocket(sk);
22 | }
23 | };
24 |
25 | const disconnect = () => {
26 | if (socket) {
27 | socket.close();
28 | }
29 | };
30 |
31 | return (
32 |
33 | {children}
34 |
35 | );
36 | };
37 |
38 | export default SocketProvider;
39 |
--------------------------------------------------------------------------------
/client/app/containers/Notification/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Notification
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import { connect } from 'react-redux';
10 | import Notifications from 'react-notification-system-redux';
11 |
12 | import actions from '../../actions';
13 |
14 | class Notification extends React.PureComponent {
15 | componentDidMount() {}
16 |
17 | render() {
18 | const { notifications } = this.props;
19 |
20 | const style = {
21 | NotificationItem: {
22 | DefaultStyle: {
23 | margin: '10px 5px 2px 1px'
24 | },
25 |
26 | success: {
27 | color: 'red'
28 | }
29 | }
30 | };
31 | return ;
32 | }
33 | }
34 |
35 | const mapStateToProps = state => {
36 | return {
37 | notifications: state.notifications
38 | };
39 | };
40 |
41 | export default connect(mapStateToProps, actions)(Notification);
42 |
--------------------------------------------------------------------------------
/client/app/containers/Category/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Category constants
4 | *
5 | */
6 |
7 | export const FETCH_CATEGORIES = 'src/Category/FETCH_CATEGORIES';
8 | export const FETCH_STORE_CATEGORIES = 'src/Category/FETCH_STORE_CATEGORIES';
9 | export const FETCH_CATEGORY = 'src/Category/FETCH_CATEGORY';
10 | export const CATEGORY_CHANGE = 'src/Category/CATEGORY_CHANGE';
11 | export const CATEGORY_EDIT_CHANGE = 'src/Category/CATEGORY_EDIT_CHANGE';
12 | export const SET_CATEGORY_FORM_ERRORS = 'src/Category/SET_CATEGORY_FORM_ERRORS';
13 | export const SET_CATEGORY_FORM_EDIT_ERRORS =
14 | 'src/Category/SET_CATEGORY_FORM_EDIT_ERRORS';
15 | export const RESET_CATEGORY = 'src/Category/RESET_CATEGORY';
16 | export const CATEGORY_SELECT = 'src/Category/CATEGORY_SELECT';
17 | export const ADD_CATEGORY = 'src/Category/ADD_CATEGORY';
18 | export const REMOVE_CATEGORY = 'src/Category/REMOVE_CATEGORY';
19 | export const SET_CATEGORIES_LOADING = 'src/Product/SET_CATEGORIES_LOADING';
20 |
--------------------------------------------------------------------------------
/client/app/containers/Merchant/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Merchant constants
4 | *
5 | */
6 |
7 | export const FETCH_MERCHANTS = 'src/Merchant/FETCH_MERCHANTS';
8 | export const REMOVE_MERCHANT = 'src/Merchant/REMOVE_MERCHANT';
9 | export const SET_ADVANCED_FILTERS = 'src/Merchant/SET_ADVANCED_FILTERS';
10 | export const FETCH_SEARCHED_MERCHANTS = 'src/Merchant/FETCH_SEARCHED_MERCHANTS';
11 | export const MERCHANT_CHANGE = 'src/Merchant/MERCHANT_CHANGE';
12 | export const SET_MERCHANT_FORM_ERRORS = 'src/Merchant/SET_MERCHANT_FORM_ERRORS';
13 | export const SET_MERCHANTS_LOADING = 'src/Merchant/SET_MERCHANTS_LOADING';
14 | export const SET_MERCHANTS_SUBMITTING = 'src/Merchant/SET_MERCHANTS_SUBMITTING';
15 | export const RESET_MERCHANT = 'src/Merchant/RESET_MERCHANT';
16 | export const SIGNUP_CHANGE = 'src/Merchant/SIGNUP_CHANGE';
17 | export const SET_SIGNUP_FORM_ERRORS = 'src/Merchant/SET_SIGNUP_FORM_ERRORS';
18 | export const SIGNUP_RESET = 'src/Merchant/SIGNUP_RESET';
19 |
--------------------------------------------------------------------------------
/client/app/components/Common/CartIcon/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * CartIcon
4 | *
5 | */
6 |
7 | import React from 'react';
8 |
9 | import { BagIcon } from '../Icon';
10 | import Button from '../Button';
11 |
12 | const CartIcon = props => {
13 | const { className, onClick, cartItems } = props;
14 |
15 | const Icon = (
16 |
17 |
18 | {cartItems.length > 0 && (
19 |
20 | {cartItems.length >= 99 ? '99+' : cartItems.length}
21 |
22 | )}
23 |
24 | );
25 |
26 | const items = cartItems.length;
27 |
28 | return (
29 |