├── .env.example
├── .eslintrc
├── .gitignore
├── .gitlab-ci.yml
├── .husky
├── pre-commit
└── pre-push
├── .prettierrc
├── README.md
├── babel.config.js
├── client
├── __tests__
│ ├── .eslintrc
│ └── dummy.test.js
├── assets
│ ├── fonts
│ │ ├── fa-brands-400.eot
│ │ ├── fa-brands-400.svg
│ │ ├── fa-brands-400.ttf
│ │ ├── fa-brands-400.woff
│ │ ├── fa-brands-400.woff2
│ │ ├── fa-regular-400.eot
│ │ ├── fa-regular-400.svg
│ │ ├── fa-regular-400.ttf
│ │ ├── fa-regular-400.woff
│ │ ├── fa-regular-400.woff2
│ │ ├── fa-solid-900.eot
│ │ ├── fa-solid-900.svg
│ │ ├── fa-solid-900.ttf
│ │ ├── fa-solid-900.woff
│ │ └── fa-solid-900.woff2
│ ├── images
│ │ ├── .gitkeep
│ │ ├── logo-new-text.png
│ │ ├── logo-new.png
│ │ └── wave.jpg
│ ├── locales.zip
│ ├── manifest.json
│ ├── robots.txt
│ ├── scss
│ │ └── main.scss
│ ├── sitemap.xml
│ └── static
│ │ ├── fonts
│ │ └── icons
│ │ │ ├── fontawesome
│ │ │ ├── FontAwesome.otf
│ │ │ ├── fontawesome-webfont.eot
│ │ │ ├── fontawesome-webfont.svg
│ │ │ ├── fontawesome-webfont.ttf
│ │ │ ├── fontawesome-webfont.woff
│ │ │ └── fontawesome-webfont.woff2
│ │ │ └── themify
│ │ │ ├── themify.eot
│ │ │ ├── themify.svg
│ │ │ ├── themify.ttf
│ │ │ └── themify.woff
│ │ └── images
│ │ ├── 404.png
│ │ ├── 500.png
│ │ ├── bg.jpg
│ │ ├── datatables
│ │ ├── sort_asc.png
│ │ ├── sort_asc_disabled.png
│ │ ├── sort_both.png
│ │ ├── sort_desc.png
│ │ └── sort_desc_disabled.png
│ │ ├── logo.png
│ │ └── logo.svg
├── components
│ ├── 404.jsx
│ ├── dummy-view.jsx
│ ├── head.jsx
│ └── home.jsx
├── config
│ ├── root.jsx
│ └── startup.jsx
├── html.js
├── index.html
├── install-sw.js
├── main.jsx
├── redux
│ ├── history.js
│ ├── index.js
│ ├── reducers
│ │ ├── auth.js
│ │ └── index.js
│ └── sockets
│ │ └── index.js
├── sw.js
└── vendors
│ └── .gitkeep
├── config
└── jest
│ ├── assetsTransformer.js
│ └── shim.js
├── docker
├── .dockerignore
├── DEV.Dockerfile
├── DEV.docker-compose.yml
├── PROD.Dockerfile
├── PROD.docker-compose.yml
└── nginx
│ └── default.conf
├── package.json
├── postcss.config.js
├── server
├── .babelrc
├── config.js
└── server.js
├── start.js
├── tailwind.config.js
├── webpack.development.config.js
├── webpack.production.config.js
├── webpack.ssr.config.js
└── yarn.lock
/.env.example:
--------------------------------------------------------------------------------
1 | PORT=8090
2 | NODE_ENV=dev
3 | APP="Skill Crucial"
4 | IS_STORYBOOK=true
5 | ENABLE_SOCKETS=false
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@babel/eslint-parser",
3 | "root": true,
4 | "extends": [
5 | "airbnb",
6 | "prettier",
7 | "plugin:react/recommended"
8 | ],
9 | "parserOptions": {
10 | "ecmaVersion": 6,
11 | "ecmaFeatures": {
12 | "jsx": true
13 | }
14 | },
15 | "plugins": [
16 | "prettier",
17 | "react-hooks"
18 | ],
19 | "env": {
20 | "browser": true,
21 | "node": true,
22 | "mocha": true,
23 | "es6": true
24 | },
25 | "globals": {
26 | "IS_PROD": true,
27 | "STRIPE_PUBLIC_KEY": true,
28 | "SENTRY_CLIENT_URL": true,
29 | "deferredPrompt": true,
30 | "axios": true,
31 | "globalThis": true,
32 | "ENABLE_SOCKETS": true
33 | },
34 | "rules": {
35 | "react/no-unstable-nested-components": [
36 | "warn",
37 | { "allowAsProps": true }
38 | ],
39 | "react/function-component-definition": [2, {
40 | "namedComponents": "arrow-function",
41 | "unnamedComponents": "arrow-function"
42 | }],
43 | "prettier/prettier": ["warn"],
44 | "space-before-function-paren": "off",
45 | "react/prefer-stateless-function": "warn",
46 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
47 | "jsx-a11y/anchor-is-valid": "off",
48 | "jsx-a11y/label-has-for": [ 2, {
49 | "required": {
50 | "every": [ "id" ]
51 | }
52 | }],
53 | "jsx-a11y/label-has-associated-control": [ 2, {
54 | "labelComponents": ["CustomInputLabel"],
55 | "labelAttributes": ["label"],
56 | "controlComponents": ["CustomInput"],
57 | "depth": 3
58 | }],
59 | "max-len": ["warn", {
60 | "ignoreComments": true,
61 | "ignoreStrings": true,
62 | "code": 240
63 | }],
64 | "react/jsx-uses-react": 1,
65 | "react/sort-comp": "off",
66 | "import/no-cycle": "off",
67 | "react/prop-types": "off",
68 | "linebreak-style": "off",
69 | "global-require": "off",
70 | "semi": "off",
71 | "arrow-body-style": "off",
72 | "comma-dangle": "off",
73 | "class-methods-use-this": "off",
74 | "quote-props": "off",
75 | "no-underscore-dangle": ["error", { "allow": ["__REDUX_DEVTOOLS_EXTENSION__", "_id", "_doc"] }],
76 | "react/destructuring-assignment": "off",
77 | "react/jsx-wrap-multilines": "off",
78 | "react/jsx-one-expression-per-line": "off",
79 | "react-hooks/rules-of-hooks": "error",
80 | "react-hooks/exhaustive-deps": "warn",
81 | "react/no-array-index-key": "warn",
82 | "jsx-a11y/control-has-associated-label": "warn",
83 | "jsx-a11y/no-autofocus": "warn",
84 | "max-classes-per-file": "warn",
85 | "react/jsx-props-no-spreading": ["off"],
86 | "camelcase": "off"
87 |
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### JetBrains template
3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
4 |
5 | *.iml
6 |
7 | ## Directory-based project format:
8 | .idea/
9 | # if you remove the above rule, at least ignore the following:
10 |
11 | # User-specific stuff:
12 | # .idea/workspace.xml
13 | # .idea/tasks.xml
14 | # .idea/dictionaries
15 |
16 | # Sensitive or high-churn files:
17 | # .idea/dataSources.ids
18 | # .idea/dataSources.xml
19 | # .idea/sqlDataSources.xml
20 | # .idea/dynamic.xml
21 | # .idea/uiDesigner.xml
22 |
23 | # Gradle:
24 | # .idea/gradle.xml
25 | # .idea/libraries
26 |
27 | # Mongo Explorer plugin:
28 | # .idea/mongoSettings.xml
29 |
30 | ## File-based project format:
31 | *.ipr
32 | *.iws
33 |
34 | ## Plugin-specific files:
35 |
36 | # IntelliJ
37 | /out/
38 |
39 | # mpeltonen/sbt-idea plugin
40 | .idea_modules/
41 |
42 | # JIRA plugin
43 | atlassian-ide-plugin.xml
44 |
45 | # Crashlytics plugin (for Android Studio and IntelliJ)
46 | com_crashlytics_export_strings.xml
47 | crashlytics.properties
48 | crashlytics-build.properties
49 |
50 | # NodeJS modules folder
51 | node_modules
52 |
53 | # npm debug log
54 | npm-debug.log
55 |
56 | # vs code
57 | .vscode
58 |
59 | # coverage
60 |
61 | /coverage
62 | /_build
63 | .env
64 | dist
65 | .DS_Store
66 | */.DS_Store
67 | yarn-error.log
68 | *.csv
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | variables:
2 | REVIEW_HOST: ${CI_COMMIT_REF_SLUG}.${PROJECT_PREFIX}
3 | DOCKER_NODE: TEST_mrtolerantru_node
4 | stages:
5 | - test
6 | - run
7 | - prod
8 |
9 | Test:
10 | stage: test
11 | script:
12 | - docker-compose -f docker/PROD.docker-compose.yml build --compress --parallel
13 | - docker-compose -f docker/PROD.docker-compose.yml run node yarn test
14 | - docker-compose -f docker/PROD.docker-compose.yml rm -f -s -v
15 | tags:
16 | - bash
17 | only:
18 | - merge_requests
19 |
20 | Build&Run:
21 | stage: run
22 | script:
23 | - docker-compose -f docker/PROD.docker-compose.yml -p ${CI_COMMIT_REF_SLUG}_${PROJECT_PREFIX} up -d --build
24 | tags:
25 | - bash
26 | only:
27 | - develop
28 | - master
29 |
30 | # Prod:
31 | # stage: prod
32 | # script:
33 | # docker-compose -f docker/docker-compose-production.yml -p ${CI_COMMIT_REF_SLUG}_${PROJECT_PREFIX} up -d --build
34 | # tags:
35 | # - bash
36 | # only:
37 | # - production
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn run lint
5 |
--------------------------------------------------------------------------------
/.husky/pre-push:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn run test
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 100,
3 | "trailingComma": "none",
4 | "tabWidth": 2,
5 | "semi": false,
6 | "singleQuote": true,
7 | "parser": "babel",
8 | "arrowParens": "always"
9 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SkillCrucial React Redux boilerplate
2 |
3 | ## Quick start
4 |
5 | 1. Clone this repo using:
6 | ```shell
7 | $ git clone git@github.com:ovasylenko/skillcrucial-react-redux-boilerplate.git
8 | ```
9 |
10 | 2. To install dependencies and clean the git repo run:
11 |
12 | ```shell
13 | $ yarn install
14 | ```
15 |
16 | *We recommend using `yarn` for installing packages, but you can use `npm` instead*:
17 |
18 | ```shell
19 | $ npm install
20 | ```
21 | 3. Create first build
22 |
23 | ```shell
24 | $ yarn run build:prod
25 | ```
26 | 4. Copy .env.example file to .env and make the necessary changes there
27 |
28 | 5. Run project in Dev mode
29 |
30 | ```shell
31 | $ yarn run dev
32 | ```
33 |
34 | ## Features
35 |
36 | * Redux
37 | * Modern ES6 for using template strings, JSX syntax, object destructuring arrow functions and more
38 | * Babel for old browser support
39 | * SASS/SCSS: make styles greate again, with no tears
40 | * React Router
41 | * Hot Module Replacement for comfortable development
42 |
43 | ## Project Structure
44 |
45 | #### `client/`
46 |
47 | You will write your app in this folder. You will spend most of your time in here.
48 |
49 | #### `client/components`
50 |
51 | This folder contains all your components
52 |
53 | #### `dist/assets`
54 | This directory contains compiled project files
55 |
56 | #### `webpack.development.config.js` `and webpack.production.frontend.config.js`
57 | Project environment configs. Webpack uses proper config depending on defined application environment.
58 | By default `webpack.development.config.js` is used unless you build the application with --config webpack.production.frontend.config.js variable.
59 |
60 |
61 | ## Command Line Commands
62 |
63 | #### Installation
64 |
65 | ```Shell
66 | yarn install
67 | ```
68 | Installs the dependencies.
69 |
70 | #### Development
71 |
72 | ```Shell
73 | yarn run dev
74 | ```
75 |
76 | Starts the development server running on `http://localhost:8080` using the webpack.development.config.js with Hot Module Replacement (HMR) (Changes in the application code will be hot-reloaded)
77 |
78 | ```Shell
79 | yarn run dev:server
80 | ```
81 |
82 | Starts the development server and makes your application accessible at http://localhost:8080.
83 |
84 | ```Shell
85 | yarn run clean
86 | ```
87 | Removes a directory "dist" from a project
88 |
89 | #### Building
90 |
91 | ```Shell
92 | yarn build:prod
93 | ```
94 |
95 | Prepares your app for deployment to production environment (using the webpack.production.frontend.config.js) (does not run tests). Optimizes and minifies all files, piping them to the `dist` folder.
96 |
97 |
98 | #### Testing
99 |
100 | ```Shell
101 | yarn run test
102 | ```
103 |
104 | Tests your application modern JavaScript Testing Framework - Jest with the unit tests specified in the `**/__tests__/*.spec.js` files
105 | throughout the application.
106 |
107 | ```Shell
108 | yarn run test:watch
109 | ```
110 |
111 | Watches changes to your application and re-runs tests whenever a file changes.
112 |
113 | ```Shell
114 | yarn run coverage
115 | ```
116 |
117 | Generates test coverage.
118 |
119 |
120 | It’s also possible to leave out the run in this command, each script can be executed with its name, e.g:
121 | yarn test:watch
122 | yarn test:coverage
123 |
124 | #### Linting
125 |
126 | ```Shell
127 | yarn run lint
128 | ```
129 | Will analyse your code for potential errors. Will check both: `./client/**/**.js` and `./server/**/**.js` files.
130 | Code linting is a type of static analysis that is frequently used to find problematic patterns or code that doesn’t adhere to certain style guidelines.
131 |
132 |
133 | ```Shell
134 | yarn run lint:server
135 | ```
136 |
137 | Will analyse only `server/**/**.js` files
138 |
139 | #### Docker
140 | Nginx web server working on 443, 80 ports on localhost
141 |
142 | ```run production
143 | docker-compose -f .\docker\PROD.docker-compose.yml up (Options: --build for build, -d to detach )
144 | docker-compose -f .\docker\PROD.docker-compose.yml down (To stop contaiters)
145 | ```
146 | ```run develop
147 | docker-compose -f .\docker\DEV.docker-compose.yml up (Options: --build for build, -d to detach )
148 | docker-compose -f .\docker\DEV.docker-compose.yml down (To stop contaiters)
149 | ```
150 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/env',
5 | {
6 | targets: {
7 | browsers: '> 0.25%, not dead'
8 | },
9 | loose: true
10 | }
11 | ],
12 | '@babel/react',
13 | '@babel/typescript'
14 | ],
15 |
16 | plugins: (process.env.NODE_ENV === 'development'
17 | ? [ 'react-refresh/babel']
18 | : []
19 | ).concat([
20 | '@babel/plugin-syntax-dynamic-import',
21 | '@babel/plugin-transform-runtime',
22 | ['@babel/plugin-proposal-decorators', { legacy: true }],
23 | [
24 | 'css-modules-transform',
25 | {
26 | extensions: ['.styl', '.css', '.scss']
27 | }
28 | ]
29 | ])
30 | }
31 |
--------------------------------------------------------------------------------
/client/__tests__/.eslintrc:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "env": {
4 | "jest": true
5 | }
6 | }
--------------------------------------------------------------------------------
/client/__tests__/dummy.test.js:
--------------------------------------------------------------------------------
1 | test('adds 1 + 2 to equal 3', () => {
2 | expect(1 + 2).toBe(3)
3 | })
4 |
--------------------------------------------------------------------------------
/client/assets/fonts/fa-brands-400.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-brands-400.eot
--------------------------------------------------------------------------------
/client/assets/fonts/fa-brands-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-brands-400.ttf
--------------------------------------------------------------------------------
/client/assets/fonts/fa-brands-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-brands-400.woff
--------------------------------------------------------------------------------
/client/assets/fonts/fa-brands-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-brands-400.woff2
--------------------------------------------------------------------------------
/client/assets/fonts/fa-regular-400.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-regular-400.eot
--------------------------------------------------------------------------------
/client/assets/fonts/fa-regular-400.svg:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
472 |
--------------------------------------------------------------------------------
/client/assets/fonts/fa-regular-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-regular-400.ttf
--------------------------------------------------------------------------------
/client/assets/fonts/fa-regular-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-regular-400.woff
--------------------------------------------------------------------------------
/client/assets/fonts/fa-regular-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-regular-400.woff2
--------------------------------------------------------------------------------
/client/assets/fonts/fa-solid-900.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-solid-900.eot
--------------------------------------------------------------------------------
/client/assets/fonts/fa-solid-900.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-solid-900.ttf
--------------------------------------------------------------------------------
/client/assets/fonts/fa-solid-900.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-solid-900.woff
--------------------------------------------------------------------------------
/client/assets/fonts/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/fonts/fa-solid-900.woff2
--------------------------------------------------------------------------------
/client/assets/images/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/images/.gitkeep
--------------------------------------------------------------------------------
/client/assets/images/logo-new-text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/images/logo-new-text.png
--------------------------------------------------------------------------------
/client/assets/images/logo-new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/images/logo-new.png
--------------------------------------------------------------------------------
/client/assets/images/wave.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/images/wave.jpg
--------------------------------------------------------------------------------
/client/assets/locales.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/locales.zip
--------------------------------------------------------------------------------
/client/assets/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SkillCrucial.com",
3 | "short_name": "SC",
4 | "theme_color": "#8bd8bd",
5 | "background_color": "#243665",
6 | "display": "browser",
7 | "scope": "/",
8 | "start_url": "/",
9 | "splash_pages": null
10 | }
--------------------------------------------------------------------------------
/client/assets/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
--------------------------------------------------------------------------------
/client/assets/scss/main.scss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/client/assets/sitemap.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/sitemap.xml
--------------------------------------------------------------------------------
/client/assets/static/fonts/icons/fontawesome/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/fonts/icons/fontawesome/FontAwesome.otf
--------------------------------------------------------------------------------
/client/assets/static/fonts/icons/fontawesome/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/fonts/icons/fontawesome/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/client/assets/static/fonts/icons/fontawesome/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/fonts/icons/fontawesome/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/client/assets/static/fonts/icons/fontawesome/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/fonts/icons/fontawesome/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/client/assets/static/fonts/icons/fontawesome/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/fonts/icons/fontawesome/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/client/assets/static/fonts/icons/themify/themify.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/fonts/icons/themify/themify.eot
--------------------------------------------------------------------------------
/client/assets/static/fonts/icons/themify/themify.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/fonts/icons/themify/themify.ttf
--------------------------------------------------------------------------------
/client/assets/static/fonts/icons/themify/themify.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/fonts/icons/themify/themify.woff
--------------------------------------------------------------------------------
/client/assets/static/images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/images/404.png
--------------------------------------------------------------------------------
/client/assets/static/images/500.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/images/500.png
--------------------------------------------------------------------------------
/client/assets/static/images/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/images/bg.jpg
--------------------------------------------------------------------------------
/client/assets/static/images/datatables/sort_asc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/images/datatables/sort_asc.png
--------------------------------------------------------------------------------
/client/assets/static/images/datatables/sort_asc_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/images/datatables/sort_asc_disabled.png
--------------------------------------------------------------------------------
/client/assets/static/images/datatables/sort_both.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/images/datatables/sort_both.png
--------------------------------------------------------------------------------
/client/assets/static/images/datatables/sort_desc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/images/datatables/sort_desc.png
--------------------------------------------------------------------------------
/client/assets/static/images/datatables/sort_desc_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/images/datatables/sort_desc_disabled.png
--------------------------------------------------------------------------------
/client/assets/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/assets/static/images/logo.png
--------------------------------------------------------------------------------
/client/assets/static/images/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/components/404.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { push } from 'connected-react-router'
3 | import { useDispatch } from 'react-redux'
4 |
5 | const NotFound = () => {
6 | useEffect(() => {}, [])
7 | const dispatch = useDispatch()
8 | return (
9 |
10 |
11 |
404
12 |
Page Not Found
13 |
It looks like you found a glitch in the matrix...
14 |
15 |
26 |
27 |
28 | )
29 | }
30 |
31 | NotFound.propTypes = {}
32 |
33 | NotFound.defaultProps = {}
34 |
35 | export default NotFound
36 |
--------------------------------------------------------------------------------
/client/components/dummy-view.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Head from './head'
3 |
4 | const Dummy = () => (
5 | <>
6 |
13 |
14 |
15 |
16 | `
17 | }
18 |
19 | export default Html
20 |
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/client/install-sw.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/install-sw.js
--------------------------------------------------------------------------------
/client/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import Root from './config/root'
4 |
5 | import './assets/scss/main.scss'
6 |
7 | const target = document.getElementById('root')
8 |
9 | ReactDOM.render(, target)
10 |
--------------------------------------------------------------------------------
/client/redux/history.js:
--------------------------------------------------------------------------------
1 | let createHistory
2 | if (typeof document !== 'undefined') {
3 | const { createBrowserHistory } = require('history')
4 | createHistory = createBrowserHistory
5 | } else {
6 | createHistory = require('history').createMemoryHistory
7 | }
8 | const history = createHistory
9 | export default history
10 |
--------------------------------------------------------------------------------
/client/redux/index.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux'
2 | import { routerMiddleware } from 'connected-react-router'
3 | import thunk from 'redux-thunk'
4 | import { composeWithDevTools } from 'redux-devtools-extension'
5 | import SockJS from 'sockjs-client'
6 |
7 | import rootReducer from './reducers'
8 | import createHistory from './history'
9 | import socketActions from './sockets'
10 |
11 | export const history = createHistory()
12 |
13 | const isBrowser = typeof window !== 'undefined'
14 |
15 | const initialState = {}
16 | const enhancers = []
17 | const middleware = [thunk, routerMiddleware(history)]
18 |
19 | const composeFunc = process.env.NODE_ENV === 'development' ? composeWithDevTools : compose
20 |
21 | const composedEnhancers = composeFunc(applyMiddleware(...middleware), ...enhancers)
22 |
23 | const store = createStore(rootReducer(history), initialState, composedEnhancers)
24 | let socket
25 |
26 | if (typeof ENABLE_SOCKETS !== 'undefined' && ENABLE_SOCKETS === 'true') {
27 | const initSocket = () => {
28 | socket = new SockJS(`${isBrowser ? window.location.origin : 'http://localhost'}/ws`)
29 |
30 | socket.onopen = () => {
31 | store.dispatch(socketActions.connected)
32 | }
33 |
34 | socket.onmessage = (message) => {
35 | // eslint-disable-next-line no-console
36 | console.log(message)
37 |
38 | // socket.close();
39 | }
40 |
41 | socket.onclose = () => {
42 | store.dispatch(socketActions.disconnected)
43 | setTimeout(() => {
44 | initSocket()
45 | }, 2000)
46 | }
47 | }
48 |
49 | initSocket()
50 | }
51 | export function getSocket() {
52 | return socket
53 | }
54 | export default store
55 |
--------------------------------------------------------------------------------
/client/redux/reducers/auth.js:
--------------------------------------------------------------------------------
1 | const initialState = {
2 | token: '',
3 | user: {
4 | name: ''
5 | }
6 | }
7 | /* eslint-disable default-param-last */
8 | export default (state = initialState, action) => {
9 | switch (action.type) {
10 | default:
11 | return state
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/client/redux/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { connectRouter } from 'connected-react-router'
3 | import auth from './auth'
4 |
5 | const createRootReducer = (history) =>
6 | combineReducers({
7 | router: connectRouter(history),
8 | auth
9 | })
10 |
11 | export default createRootReducer
12 |
--------------------------------------------------------------------------------
/client/redux/sockets/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | connected: (data) => ({
3 | type: 'SOCKET_CONNECTED',
4 | data
5 | }),
6 | message: (data) => {
7 | return JSON.parse(data)
8 | },
9 | disconnected: (data) => ({
10 | type: 'SOCKET_DISCONNECTED',
11 | data
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/client/sw.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/sw.js
--------------------------------------------------------------------------------
/client/vendors/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Codechief09/react-redux/cc057fa24d28c699bb2138b696f528d550611e46/client/vendors/.gitkeep
--------------------------------------------------------------------------------
/config/jest/assetsTransformer.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | process(src, filename, config, options) {
5 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/config/jest/shim.js:
--------------------------------------------------------------------------------
1 | // A solution for React 16 complaining of missing rAF.
2 |
3 | global.requestAnimationFrame = function(callback) {
4 | setTimeout(callback, 0);
5 | };
6 |
--------------------------------------------------------------------------------
/docker/.dockerignore:
--------------------------------------------------------------------------------
1 | .gitignore
2 | npm-debug.log
3 | .env
4 | docker
5 | .start-storybook
6 | .gitlab-ci.yml
7 | stores
8 | node_modules
--------------------------------------------------------------------------------
/docker/DEV.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:alpine
2 | CMD yarn dev
3 | WORKDIR /app
4 | COPY package*.json ./
5 | RUN yarn install
6 | # npm config set scripts-prepend-node-path true &&
7 | # COPY . .
--------------------------------------------------------------------------------
/docker/DEV.docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.7"
2 | services:
3 | node:
4 | build:
5 | context: ../
6 | dockerfile: docker/DEV.Dockerfile
7 | ports:
8 | - 8080:8081
9 | restart: always
10 | container_name: node-dev
11 | volumes:
12 | - ../.:/app:rw
13 | - /app/node_modules
--------------------------------------------------------------------------------
/docker/PROD.Dockerfile:
--------------------------------------------------------------------------------
1 | #--------------------------------------STAGE 1-----------------------------
2 | FROM node:alpine AS nodeServer
3 | CMD [ "yarn","start" ]
4 | WORKDIR /app
5 | COPY package*.json ./
6 | RUN npm config set scripts-prepend-node-path true && yarn install
7 | COPY . .
8 | RUN yarn build --silent --only=production --ignore-optional
9 | #--------------------------------------STAGE 2-----------------------------
10 | FROM nginx:alpine AS webServer
11 | WORKDIR /app
12 | COPY --from=nodeServer /app/dist/ /app/public/
13 | COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf
14 |
--------------------------------------------------------------------------------
/docker/PROD.docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.7"
2 | services:
3 | node:
4 | build:
5 | target: nodeServer
6 | context: ../
7 | dockerfile: docker/PROD.Dockerfile
8 | restart: always
9 | networks:
10 | - internal
11 | labels:
12 | - traefik.enable=false
13 |
14 | nginx:
15 | build:
16 | target: webServer
17 | context: ../
18 | dockerfile: docker/PROD.Dockerfile
19 | restart: always
20 | ports:
21 | - 80:80
22 | - 443:443
23 | networks:
24 | - internal
25 | - review
26 | labels:
27 | - traefik.enable=true
28 | - traefik.frontend.rule=Host:${REVIEW_HOST}
29 | - traefik.port=80
30 | - traefik.docker.network=review
31 | depends_on:
32 | - node
33 |
34 | networks:
35 | internal:
36 | review:
37 | external:
38 | name: review
39 |
--------------------------------------------------------------------------------
/docker/nginx/default.conf:
--------------------------------------------------------------------------------
1 | upstream backend {
2 | server node:3000;
3 | }
4 |
5 | server {
6 | listen 80 default_server;
7 |
8 | server_name _;
9 |
10 | root /app/public;
11 |
12 | location / {
13 | try_files $uri @backend;
14 | }
15 |
16 | location @backend {
17 | proxy_pass http://backend;
18 | proxy_set_header X-Real-IP $remote_addr;
19 | proxy_set_header Host $host;
20 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
21 | # Following is necessary for Websocket support
22 | proxy_http_version 1.1;
23 | proxy_set_header Upgrade $http_upgrade;
24 | proxy_set_header Connection "upgrade";
25 | }
26 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "boilerplate-skillcrucial",
3 | "version": "0.0.3",
4 | "description": "Boilerplate SkillCrucial",
5 | "main": "index.js",
6 | "scripts": {
7 | "docker-start-dev": "docker-compose -f \"docker\\DEV.docker-compose.yml\" up",
8 | "docker-stop-dev": "docker-compose -f \"docker\\DEV.docker-compose.yml\" down",
9 | "dev": "yarn run watch:server & yarn run watch:client ",
10 | "watch:client": "webpack serve --mode development --config ./webpack.development.config.js",
11 | "dev:server": "webpack --watch --config webpack.development.config.js",
12 | "start:dev": "webpack serve --mode development --config ./webpack.development.config.js --open",
13 | "start": "cross-env NODE_ENV=production && node start.js",
14 | "clean": "rimraf dist",
15 | "lint": "eslint './server/**/*.js' './client/**/*.js' './client/**/*.jsx'",
16 | "prettier": "prettier './client/**/*.js' './client/**/*.jsx' './server/**/*.js' --write",
17 | "lint:server": "eslint ./server/**/**.js",
18 | "test": "jest",
19 | "test:watch": "jest --watch",
20 | "coverage": "jest --coverage",
21 | "next:build": "next",
22 | "watch:server": "nodemon --legacy-watch start.js --watch server --watch client --exec \"npm run lint:server --scripts-prepend-node-path && node\"",
23 | "build:prod": "cross-env NODE_ENV=production webpack --config webpack.production.config.js --progress --profile --color",
24 | "build:ssr": "cross-env NODE_ENV=production webpack --config webpack.ssr.config.js --progress --profile --color",
25 | "build": "yarn run build:prod",
26 | "heroku-postbuild": "yarn run build:prod",
27 | "storybook": "start-storybook -p 6006",
28 | "build-storybook": "build-storybook",
29 | "prepare": "husky install"
30 | },
31 | "author": "",
32 | "license": "MIT",
33 | "jest": {
34 | "setupFiles": [
35 | "/config/jest/shim.js"
36 | ],
37 | "collectCoverageFrom": [
38 | "app/**/*.{js,jsx,ts,tsx}"
39 | ],
40 | "moduleNameMapper": {
41 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/config/jest/assetsTransformer.js",
42 | "\\.(css|less)$": "/config/jest/assetsTransformer.js"
43 | },
44 | "testMatch": [
45 | "**/?(*.)(spec|test).js?(x)"
46 | ],
47 | "transform": {
48 | "\\.js$": "babel-jest"
49 | }
50 | },
51 | "dependencies": {
52 | "@babel/cli": "^7.17.10",
53 | "@babel/core": "^7.18.2",
54 | "@babel/eslint-parser": "^7.18.2",
55 | "@babel/plugin-proposal-class-properties": "^7.17.12",
56 | "@babel/plugin-proposal-decorators": "^7.18.2",
57 | "@babel/plugin-proposal-export-default-from": "^7.17.12",
58 | "@babel/plugin-proposal-export-namespace-from": "^7.17.12",
59 | "@babel/plugin-proposal-function-sent": "^7.18.2",
60 | "@babel/plugin-proposal-json-strings": "^7.17.12",
61 | "@babel/plugin-proposal-numeric-separator": "^7.16.7",
62 | "@babel/plugin-proposal-throw-expressions": "^7.16.7",
63 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
64 | "@babel/plugin-syntax-import-meta": "^7.10.4",
65 | "@babel/plugin-transform-runtime": "^7.18.2",
66 | "@babel/preset-env": "^7.18.2",
67 | "@babel/preset-flow": "^7.17.12",
68 | "@babel/preset-react": "^7.17.12",
69 | "@babel/preset-stage-3": "^7.8.3",
70 | "@babel/preset-typescript": "^7.17.12",
71 | "@babel/register": "^7.17.7",
72 | "@babel/runtime": "^7.18.3",
73 | "@emotion/babel-plugin": "^11.9.2",
74 | "babel-core": "7.0.0-bridge.0",
75 | "babel-eslint": "10.1.0",
76 | "babel-jest": "^28.1.1",
77 | "babel-loader": "^8.2.5",
78 | "babel-plugin-css-modules-transform": "^1.6.2",
79 | "babel-polyfill": "^6.26.0",
80 | "classnames": "^2.3.1",
81 | "colors": "^1.4.0",
82 | "connected-react-router": "^6.9.2",
83 | "cookie-parser": "^1.4.6",
84 | "core-js": "^3.22.8",
85 | "cors": "^2.8.5",
86 | "cross-env": "^7.0.3",
87 | "dotenv": "^16.0.1",
88 | "express": "^4.18.1",
89 | "history": "4.10.1",
90 | "prop-types": "^15.8.1",
91 | "react": "^17.0.2",
92 | "react-dom": "^17.0.2",
93 | "react-helmet": "^6.1.0",
94 | "react-redux": "^7.2.6",
95 | "react-refresh": "^0.11.0",
96 | "react-router-dom": "5.3.0",
97 | "redux": "^4.2.0",
98 | "redux-devtools-extension": "^2.13.9",
99 | "redux-thunk": "^2.4.1",
100 | "sockjs": "^0.3.24",
101 | "sockjs-client": "^1.6.1",
102 | "string-replace-webpack-plugin": "^0.1.3",
103 | "uuid": "^8.3.2"
104 | },
105 | "devDependencies": {
106 | "@hot-loader/react-dom": "^17.0.2",
107 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
108 | "autoprefixer": "^10.4.7",
109 | "clean-webpack-plugin": "^4.0.0",
110 | "copy-webpack-plugin": "^11.0.0",
111 | "css-loader": "^6.7.1",
112 | "css-minimizer-webpack-plugin": "^4.0.0",
113 | "cssnano": "^5.1.11",
114 | "emotion-theming": "^11.0.0",
115 | "eslint": "^8.17.0",
116 | "eslint-config-airbnb": "^19.0.4",
117 | "eslint-config-prettier": "^8.5.0",
118 | "eslint-plugin-import": "^2.26.0",
119 | "eslint-plugin-jsx-a11y": "^6.5.1",
120 | "eslint-plugin-prettier": "^4.0.0",
121 | "eslint-plugin-react": "^7.30.0",
122 | "eslint-plugin-react-hooks": "^4.5.0",
123 | "eslint-webpack-plugin": "^3.1.1",
124 | "file-loader": "6.2.0",
125 | "git-revision-webpack-plugin": "^5.0.0",
126 | "glob": "^8.0.3",
127 | "hard-source-webpack-plugin": "^0.13.1",
128 | "html-webpack-plugin": "^5.5.0",
129 | "husky": "^8.0.1",
130 | "image-webpack-loader": "^8.1.0",
131 | "jest": "^28.1.1",
132 | "jest-cli": "^28.1.1",
133 | "mini-css-extract-plugin": "^2.6.0",
134 | "node-hot-loader": "^1.21.8",
135 | "node-sass": "^7.0.1",
136 | "nodemon": "^2.0.16",
137 | "postcss": "^8.4.14",
138 | "postcss-import": "^14.1.0",
139 | "postcss-loader": "^7.0.0",
140 | "postcss-preset-env": "^7.7.1",
141 | "prettier": "^2.6.2",
142 | "raw-loader": "^4.0.2",
143 | "regenerator-runtime": "^0.13.9",
144 | "rimraf": "3.0.2",
145 | "sass-loader": "^13.0.0",
146 | "style-loader": "^3.3.1",
147 | "tailwindcss": "^3.0.24",
148 | "terser-webpack-plugin": "^5.3.3",
149 | "thread-loader": "^3.0.4",
150 | "url-loader": "4.1.1",
151 | "webpack": "^5.73.0",
152 | "webpack-cli": "^4.9.2",
153 | "webpack-dev-server": "3.11.3",
154 | "webpack-node-externals": "^3.0.0",
155 | "webpack-shell-plugin": "^0.5.0"
156 | },
157 | "nodemonConfig": {
158 | "ignore": [
159 | "*.hot-update.json"
160 | ]
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import'),
4 | require('autoprefixer'),
5 | require('postcss-preset-env')(),
6 | require('tailwindcss')('./tailwind.config.js')
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/server/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/env", {
4 | "targets": {
5 | "browsers": ["last 2 versions"]
6 | }
7 | }],
8 | "@babel/react",
9 | "@babel/typescript"
10 | ],
11 |
12 | "plugins": [
13 | "@babel/plugin-proposal-export-default-from",
14 | "@babel/plugin-proposal-class-properties",
15 | "@babel/plugin-transform-runtime",
16 | ["@babel/plugin-proposal-decorators",{ "legacy": true }]
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/server/config.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config()
2 |
3 | const options = {
4 | port: process.env.PORT,
5 | app: process.env.APP,
6 | env: process.env.NODE_ENV,
7 | isSocketsEnabled: process.env.ENABLE_SOCKETS
8 | }
9 |
10 | export default options
11 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import path from 'path'
3 | import cors from 'cors'
4 | import sockjs from 'sockjs'
5 | import cookieParser from 'cookie-parser'
6 |
7 | import config from './config'
8 | import Html from '../client/html'
9 |
10 | require('colors')
11 |
12 | let connections = []
13 |
14 | const port = process.env.PORT || 8090
15 | const server = express()
16 |
17 | const middleware = [
18 | cors(),
19 | express.static(path.resolve(__dirname, '../dist')),
20 | express.urlencoded({ limit: '50mb', extended: true, parameterLimit: 50000 }),
21 | express.json({ limit: '50mb', extended: true }),
22 | cookieParser()
23 | ]
24 |
25 | middleware.forEach((it) => server.use(it))
26 |
27 | server.get('/', (req, res) => {
28 | res.send(`
29 | This is SkillCrucial Express Server!
30 |
31 | `)
32 | })
33 |
34 | server.get('/*', (req, res) => {
35 | const initialState = {
36 | location: req.url
37 | }
38 |
39 | return res.send(
40 | Html({
41 | body: '',
42 | initialState
43 | })
44 | )
45 | })
46 |
47 | server.use('/api/', (req, res) => {
48 | res.status(404)
49 | res.end()
50 | })
51 |
52 | const app = server.listen(port)
53 |
54 | if (config.isSocketsEnabled) {
55 | const echo = sockjs.createServer()
56 | echo.on('connection', (conn) => {
57 | connections.push(conn)
58 | conn.on('data', async () => {})
59 |
60 | conn.on('close', () => {
61 | connections = connections.filter((c) => c.readyState !== 3)
62 | })
63 | })
64 | echo.installHandlers(app, { prefix: '/ws' })
65 | }
66 | console.log(`Serving at http://localhost:${port}`)
67 |
--------------------------------------------------------------------------------
/start.js:
--------------------------------------------------------------------------------
1 | // Transpile all code following this line with babel and use 'env' (aka ES6) preset.
2 | require('@babel/register')()
3 |
4 | module.exports = require('./server/server.js')
5 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: [
3 | './client/**/*.html',
4 | './client/**/*.jsx',
5 | './client/**/*.js'
6 | ],
7 | theme: {},
8 | variants: {},
9 | plugins: []
10 | }
11 |
--------------------------------------------------------------------------------
/webpack.development.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path')
2 | require('dotenv').config()
3 | const fs = require('fs')
4 |
5 | const webpack = require('webpack')
6 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
7 | const CopyWebpackPlugin = require('copy-webpack-plugin')
8 | const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
9 | const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
10 | const ESLintPlugin = require('eslint-webpack-plugin')
11 | const { CleanWebpackPlugin } = require('clean-webpack-plugin')
12 | const HtmlWebpackPlugin = require('html-webpack-plugin')
13 |
14 | const CLIENT_PORT = 8087
15 | const APP_VERSION = 'development'
16 | const config = {
17 | stats: {
18 | modules: false
19 | },
20 | optimization: {
21 | moduleIds: 'named',
22 | chunkIds: 'named'
23 | },
24 | devtool: 'eval-source-map',
25 | entry: ['./main.jsx'],
26 | resolve: {
27 | extensions: ['.ts', '.tsx', '.js', '.jsx'],
28 | alias: {
29 | d3: 'd3/index.js',
30 | 'react-dom': '@hot-loader/react-dom'
31 | }
32 | },
33 | output: {
34 | filename: 'js/[name].bundle.js?v=COMMITHASH1',
35 | path: resolve(__dirname, 'dist'),
36 | publicPath: '/',
37 | chunkFilename: 'js/[name].[contenthash].js'
38 | },
39 | mode: 'development',
40 | context: resolve(__dirname, 'client'),
41 | devServer: {
42 | hotOnly: true,
43 | contentBase: resolve(__dirname, 'dist'),
44 | watchContentBase: true,
45 | host: '0.0.0.0',
46 | port: CLIENT_PORT,
47 | useLocalIp: true,
48 | historyApiFallback: true,
49 | overlay: {
50 | warnings: false,
51 | errors: true
52 | },
53 | proxy: [
54 | {
55 | context: ['/api', '/auth', '/ws', '/favicon.ico'],
56 | target: 'http://0.0.0.0:8090',
57 | secure: false,
58 | changeOrigin: true,
59 | ws: process.env.ENABLE_SOCKETS === 'true'
60 | }
61 | ]
62 | },
63 | module: {
64 | rules: [
65 | {
66 | test: /\.js|jsx$/,
67 | use: [
68 | {
69 | loader: require.resolve('babel-loader')
70 | }
71 | ],
72 | exclude: /node_modules/
73 | },
74 | {
75 | test: /\.css$/,
76 | use: [
77 | {
78 | loader: MiniCssExtractPlugin.loader,
79 | options: {
80 | publicPath: '../'
81 | }
82 | },
83 | { loader: 'css-loader', options: { sourceMap: true } },
84 | {
85 | loader: 'postcss-loader'
86 | }
87 | ]
88 | },
89 | {
90 | test: /\.txt$/i,
91 | use: 'raw-loader'
92 | },
93 | {
94 | test: /\.scss$/,
95 | exclude: /node_modules/,
96 | use: [
97 | {
98 | loader: MiniCssExtractPlugin.loader,
99 | options: {
100 | publicPath: '../'
101 | }
102 | },
103 | { loader: 'css-loader', options: { sourceMap: true } },
104 | {
105 | loader: 'postcss-loader'
106 | },
107 | {
108 | loader: 'sass-loader'
109 | }
110 | ]
111 | },
112 |
113 | {
114 | test: /\.(png|jpg|gif|webp)$/,
115 | use: [
116 | {
117 | loader: 'file-loader'
118 | }
119 | ]
120 | },
121 | {
122 | test: /\.eot$/,
123 | use: [
124 | {
125 | loader: 'file-loader'
126 | }
127 | ]
128 | },
129 | {
130 | test: /\.woff(2)$/,
131 | use: [
132 | {
133 | loader: 'file-loader'
134 | }
135 | ]
136 | },
137 | {
138 | test: /\.[ot]tf$/,
139 | use: [
140 | {
141 | loader: 'file-loader'
142 | }
143 | ]
144 | },
145 | {
146 | test: /\.svg$/,
147 | use: [
148 | {
149 | loader: 'file-loader',
150 | options: {
151 | name: '[name].[ext]',
152 | outputPath: 'fonts/'
153 | }
154 | },
155 | {
156 | loader: 'svg-url-loader',
157 | options: {
158 | limit: 10 * 1024,
159 | noquotes: true
160 | }
161 | }
162 | ]
163 | }
164 | ]
165 | },
166 | plugins: [
167 | new ESLintPlugin({
168 | extensions: ['js', 'jsx'],
169 | exclude: 'node_modules'
170 | }),
171 | new MiniCssExtractPlugin({
172 | filename: 'css/main.css',
173 | chunkFilename: 'css/[id].css',
174 | ignoreOrder: false
175 | }),
176 | new CopyWebpackPlugin(
177 | {
178 | patterns: [
179 | { from: 'assets/images', to: 'images' },
180 | { from: 'assets/fonts', to: 'fonts' },
181 | { from: 'assets/manifest.json', to: 'manifest.json' },
182 | {
183 | from: 'install-sw.js',
184 | to: 'js/install-sw.js',
185 | transform: (content) => {
186 | return content.toString().replace(/APP_VERSION/g, APP_VERSION)
187 | }
188 | },
189 | { from: 'vendors', to: 'vendors' },
190 | {
191 | from: 'html.js',
192 | to: 'html.js',
193 | transform: (content) => {
194 | return content.toString().replace(/COMMITHASH/g, APP_VERSION)
195 | }
196 | },
197 | {
198 | from: 'sw.js',
199 | to: 'sw.js',
200 | transform: (content) => {
201 | return content.toString().replace(/APP_VERSION/g, APP_VERSION)
202 | }
203 | }
204 | ]
205 | },
206 | { parallel: 100 }
207 | ),
208 | new ReactRefreshWebpackPlugin({
209 | overlay: {
210 | sockIntegration: 'wds'
211 | }
212 | }), // new HardSourceWebpackPlugin(),
213 | new webpack.HotModuleReplacementPlugin(),
214 | new CleanWebpackPlugin(),
215 | new HtmlWebpackPlugin({ template: 'index.html' }),
216 | new webpack.DefinePlugin(
217 | Object.keys(process.env).reduce(
218 | (res, key) => ({ ...res, [key]: JSON.stringify(process.env[key]) }),
219 | {
220 | APP_VERSION: JSON.stringify(APP_VERSION),
221 | 'windows.process': { cwd: () => '' }
222 | }
223 | )
224 | )
225 | ]
226 | }
227 |
228 | module.exports = config
229 |
--------------------------------------------------------------------------------
/webpack.production.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path')
2 | require('dotenv').config()
3 | const fs = require('fs')
4 |
5 | const webpack = require('webpack')
6 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
7 | const CopyWebpackPlugin = require('copy-webpack-plugin')
8 | const StringReplacePlugin = require('string-replace-webpack-plugin')
9 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
10 | const ESLintPlugin = require('eslint-webpack-plugin')
11 | const TerserJSPlugin = require('terser-webpack-plugin')
12 | const { CleanWebpackPlugin } = require('clean-webpack-plugin')
13 | const HtmlWebpackPlugin = require('html-webpack-plugin')
14 |
15 | const date = +new Date()
16 | const APP_VERSION = Buffer.from((date - (date % (1000 * 60 * 30))).toString())
17 | .toString('base64')
18 | .replace(/==/, '')
19 |
20 | const config = {
21 | optimization: {
22 | minimize: true,
23 | minimizer: [new TerserJSPlugin({ parallel: true })]
24 | },
25 | entry: {
26 | main: './main.jsx'
27 | },
28 | resolve: {
29 | extensions: ['.ts', '.tsx', '.js', '.jsx'],
30 |
31 | alias: {
32 | d3: 'd3/index.js'
33 | }
34 | },
35 | output: {
36 | filename: 'js/[name].bundle.js?v=COMMITHASH1',
37 | path: resolve(__dirname, 'dist'),
38 | publicPath: '/',
39 | chunkFilename: 'js/[name].js?id=[chunkhash]'
40 | },
41 | mode: 'production',
42 | context: resolve(__dirname, 'client'),
43 | devtool: false,
44 | performance: {
45 | hints: 'warning',
46 | maxEntrypointSize: 512000,
47 | maxAssetSize: 512000
48 | },
49 | module: {
50 | rules: [
51 | {
52 | test: /\.js|jsx$/,
53 | use: 'babel-loader',
54 | exclude: /node_modules/
55 | },
56 | {
57 | test: /\.css$/,
58 | use: [
59 | {
60 | loader: MiniCssExtractPlugin.loader,
61 | options: {
62 | publicPath: '../'
63 | }
64 | },
65 | { loader: 'css-loader', options: { sourceMap: true } },
66 | {
67 | loader: 'postcss-loader'
68 | }
69 | ]
70 | },
71 | {
72 | test: /\.txt$/i,
73 | use: 'raw-loader'
74 | },
75 | {
76 | test: /\.scss$/,
77 | exclude: /node_modules/,
78 | use: [
79 | {
80 | loader: MiniCssExtractPlugin.loader,
81 | options: {
82 | publicPath: '../'
83 | }
84 | },
85 |
86 | { loader: 'css-loader', options: { sourceMap: true } },
87 | {
88 | loader: 'postcss-loader'
89 | },
90 | {
91 | loader: 'sass-loader'
92 | }
93 | ]
94 | },
95 |
96 | {
97 | test: /\.(png|jpg|gif|webp)$/,
98 | use: [
99 | {
100 | loader: 'file-loader'
101 | }
102 | ]
103 | },
104 | {
105 | test: /\.eot$/,
106 | use: [
107 | {
108 | loader: 'file-loader'
109 | }
110 | ]
111 | },
112 | {
113 | test: /\.woff(2)$/,
114 | use: [
115 | {
116 | loader: 'file-loader'
117 | }
118 | ]
119 | },
120 | {
121 | test: /\.[ot]tf$/,
122 | use: [
123 | {
124 | loader: 'file-loader'
125 | }
126 | ]
127 | },
128 | {
129 | test: /\.svg$/,
130 | use: [
131 | {
132 | loader: 'file-loader',
133 | options: {
134 | name: '[name].[ext]',
135 | outputPath: 'fonts/'
136 | }
137 | },
138 | {
139 | loader: 'svg-url-loader',
140 | options: {
141 | limit: 10 * 1024,
142 | noquotes: true
143 | }
144 | }
145 | ]
146 | }
147 | ]
148 | },
149 | plugins: [
150 | new ESLintPlugin({
151 | extensions: ['js', 'jsx'],
152 | exclude: 'node_modules'
153 | }),
154 | new StringReplacePlugin(),
155 | new CopyWebpackPlugin(
156 | {
157 | patterns: [
158 | { from: 'assets/images', to: 'images' },
159 | { from: 'assets/fonts', to: 'fonts' },
160 | { from: 'assets/manifest.json', to: 'manifest.json' },
161 | {
162 | from: 'install-sw.js',
163 | to: 'js/install-sw.js',
164 | transform: (content) => {
165 | return content.toString().replace(/APP_VERSION/g, APP_VERSION)
166 | }
167 | },
168 | { from: 'vendors', to: 'vendors' },
169 | {
170 | from: 'html.js',
171 | to: 'html.js',
172 | transform: (content) => {
173 | return content.toString().replace(/COMMITHASH/g, APP_VERSION)
174 | }
175 | },
176 | {
177 | from: 'sw.js',
178 | to: 'sw.js',
179 | transform: (content) => {
180 | return content.toString().replace(/APP_VERSION/g, APP_VERSION)
181 | }
182 | }
183 | ]
184 | },
185 | { parallel: 100 }
186 | ), // `...`,
187 | new CssMinimizerPlugin({ parallel: 4 }),
188 | new MiniCssExtractPlugin({
189 | filename: 'css/[name].css',
190 | chunkFilename: 'css/[id].css',
191 | ignoreOrder: false
192 | }),
193 | new CleanWebpackPlugin(),
194 | new HtmlWebpackPlugin({ template: 'index.html' }),
195 | new webpack.DefinePlugin(
196 | Object.keys(process.env).reduce(
197 | (res, key) => ({ ...res, [key]: JSON.stringify(process.env[key]) }),
198 | {
199 | APP_VERSION: JSON.stringify(APP_VERSION)
200 | }
201 | )
202 | )
203 | ]
204 | }
205 |
206 | module.exports = config
207 |
--------------------------------------------------------------------------------
/webpack.ssr.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path')
2 | require('dotenv').config()
3 |
4 | const webpack = require('webpack')
5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
6 | const StringReplacePlugin = require('string-replace-webpack-plugin')
7 | const TerserJSPlugin = require('terser-webpack-plugin')
8 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
9 | const ESLintPlugin = require('eslint-webpack-plugin')
10 | const nodeExternals = require('webpack-node-externals')
11 |
12 | const date = +new Date()
13 |
14 |
15 | const APP_VERSION = Buffer.from((date - (date % (1000 * 60 * 30))).toString())
16 | .toString('base64')
17 | .replace(/==/, '')
18 |
19 | const config = {
20 | optimization: {
21 | minimize: true,
22 | minimizer: [new TerserJSPlugin({ parallel: true })]
23 | },
24 | target: 'node',
25 | mode: 'development',
26 | entry: {
27 | root: './config/root.jsx'
28 | },
29 | externals: [nodeExternals()],
30 | resolve: {
31 | extensions: ['.ts', '.tsx', '.js', '.jsx'],
32 |
33 | alias: {
34 | d3: 'd3/index.js'
35 | }
36 | },
37 | output: {
38 | filename: 'js/ssr/[name].bundle.js',
39 | path: resolve(__dirname, 'dist/assets'),
40 | publicPath: '/',
41 | chunkFilename: 'js/ssr/[name].js?id=[chunkhash]',
42 | libraryTarget: 'commonjs'
43 | },
44 | mode: 'production',
45 | context: resolve(__dirname, 'client'),
46 | devtool: false,
47 | performance: {
48 | hints: 'warning',
49 | maxEntrypointSize: 1512000,
50 | maxAssetSize: 1512000
51 | },
52 | module: {
53 | rules: [
54 | {
55 | test: /\.js|jsx$/,
56 | use: 'babel-loader',
57 | exclude: /node_modules/
58 | },
59 | {
60 | test: /\.css$/,
61 | use: [
62 | {
63 | loader: MiniCssExtractPlugin.loader,
64 | options: {
65 | publicPath: '../'
66 | }
67 | },
68 | { loader: 'css-loader', options: { sourceMap: true } },
69 | {
70 | loader: 'postcss-loader'
71 | }
72 | ]
73 | },
74 | {
75 | test: /\.txt$/i,
76 | use: 'raw-loader'
77 | },
78 | {
79 | test: /\.scss$/,
80 | exclude: /node_modules/,
81 | use: [
82 | {
83 | loader: MiniCssExtractPlugin.loader,
84 | options: {
85 | publicPath: '../'
86 | }
87 | },
88 |
89 | { loader: 'css-loader', options: { sourceMap: true } },
90 | {
91 | loader: 'postcss-loader'
92 | },
93 | {
94 | loader: 'sass-loader'
95 | }
96 | ]
97 | },
98 |
99 | {
100 | test: /\.(png|jpg|gif|webp)$/,
101 | use: [
102 | {
103 | loader: 'file-loader'
104 | }
105 | ]
106 | },
107 | {
108 | test: /\.eot$/,
109 | use: [
110 | {
111 | loader: 'file-loader'
112 | }
113 | ]
114 | },
115 | {
116 | test: /\.woff(2)$/,
117 | use: [
118 | {
119 | loader: 'file-loader'
120 | }
121 | ]
122 | },
123 | {
124 | test: /\.[ot]tf$/,
125 | use: [
126 | {
127 | loader: 'file-loader'
128 | }
129 | ]
130 | },
131 | {
132 | test: /\.svg$/,
133 | use: [
134 | {
135 | loader: 'file-loader',
136 | options: {
137 | name: '[name].[ext]',
138 | outputPath: 'fonts/'
139 | }
140 | },
141 | {
142 | loader: 'svg-url-loader',
143 | options: {
144 | limit: 10 * 1024,
145 | noquotes: true
146 | }
147 | }
148 | ]
149 | }
150 | ]
151 | },
152 | plugins: [
153 | new ESLintPlugin({
154 | extensions: ['js', 'jsx'],
155 | exclude: 'node_modules'
156 | }),
157 | new CssMinimizerPlugin({ parallel: 4 }),
158 | new StringReplacePlugin(),
159 | new MiniCssExtractPlugin({
160 | filename: 'css/ssr/[name].css',
161 | chunkFilename: 'css/ssr/[id].css',
162 | ignoreOrder: false
163 | }),
164 | new webpack.DefinePlugin(
165 | Object.keys(process.env).reduce(
166 | (res, key) => ({ ...res, [key]: JSON.stringify(process.env[key]) }),
167 | {
168 | APP_VERSION: JSON.stringify(APP_VERSION)
169 | }
170 | )
171 | )
172 | ]
173 | }
174 |
175 | module.exports = config
176 |
--------------------------------------------------------------------------------
13 | >
14 | )
15 |
16 | Dummy.propTypes = {}
17 |
18 | export default React.memo(Dummy)
19 |
--------------------------------------------------------------------------------
/client/components/head.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Helmet } from 'react-helmet'
4 |
5 | const Head = (props) => (
6 |
7 | SkillCrucial Boilerplate - {props.title}
8 |
9 |
10 |
11 |
12 | )
13 |
14 | Head.propTypes = {
15 | title: PropTypes.string
16 | }
17 |
18 | Head.defaultProps = {
19 | title: 'skillcrucial.com'
20 | }
21 |
22 | export default Head
23 |
--------------------------------------------------------------------------------
/client/components/home.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import Head from './head'
3 | // import wave from '../assets/images/wave.jpg'
4 |
5 | const Home = () => {
6 | const [counter, setCounterNew] = useState(0)
7 |
8 | return (
9 |
16 |
17 | )
18 | }
19 |
20 | Home.propTypes = {}
21 |
22 | export default Home
23 |
--------------------------------------------------------------------------------
/client/config/root.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Provider, useSelector } from 'react-redux'
3 | import { ConnectedRouter } from 'connected-react-router'
4 | import { Switch, Route, Redirect, StaticRouter } from 'react-router-dom'
5 |
6 | import store, { history } from '../redux'
7 |
8 | import Home from '../components/home'
9 | import DummyView from '../components/dummy-view'
10 | import NotFound from '../components/404'
11 |
12 | import Startup from './startup'
13 |
14 | const OnlyAnonymousRoute = ({ component: Component, ...rest }) => {
15 | const user = useSelector((state) => state.auth.user)
16 | const token = useSelector((state) => state.token)
17 | const func = (props) => {
18 | if (!!user && !!user.name && !!token)
19 | return
20 | }
21 | return
22 | }
23 |
24 | const PrivateRoute = ({ component: Component, ...rest }) => {
25 | const user = useSelector((state) => state.auth.user)
26 | const token = useSelector((state) => state.token)
27 |
28 | const func = (props) => {
29 | if (!!user && !!user.name && !!token) return
30 |
31 | return (
32 |
37 | )
38 | }
39 | return
40 | }
41 |
42 | const RouterSelector = (props) =>
43 | typeof window !== 'undefined' ? :
44 |
45 | const RootComponent = (props) => {
46 | return (
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | )
62 | }
63 |
64 | export default RootComponent
65 |
--------------------------------------------------------------------------------
/client/config/startup.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const Startup = (props) => {
5 | useEffect(() => {}, [])
6 |
7 | return props.children
8 | }
9 |
10 | Startup.propTypes = {
11 | children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
12 | }
13 |
14 | export default Startup
15 |
--------------------------------------------------------------------------------
/client/html.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | const Html = ({ body }) => {
3 | return `
4 |
5 |
6 |