├── .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 | 8 | 9 | 10 | 15 | 16 | 19 | 22 | 25 | 28 | 31 | 34 | 37 | 40 | 43 | 46 | 49 | 52 | 55 | 58 | 61 | 64 | 67 | 70 | 73 | 76 | 79 | 82 | 85 | 88 | 91 | 94 | 97 | 100 | 103 | 106 | 109 | 112 | 115 | 118 | 121 | 124 | 127 | 130 | 133 | 136 | 139 | 142 | 145 | 148 | 151 | 154 | 157 | 160 | 163 | 166 | 169 | 172 | 175 | 178 | 181 | 184 | 187 | 190 | 193 | 196 | 199 | 202 | 205 | 208 | 211 | 214 | 217 | 220 | 223 | 226 | 229 | 232 | 235 | 238 | 241 | 244 | 247 | 250 | 253 | 256 | 259 | 262 | 265 | 268 | 271 | 274 | 277 | 280 | 283 | 286 | 289 | 292 | 295 | 298 | 301 | 304 | 307 | 310 | 313 | 316 | 319 | 322 | 325 | 328 | 331 | 334 | 337 | 340 | 343 | 346 | 349 | 352 | 355 | 358 | 361 | 364 | 367 | 370 | 373 | 376 | 379 | 382 | 385 | 388 | 391 | 394 | 397 | 400 | 403 | 406 | 409 | 412 | 415 | 418 | 421 | 424 | 427 | 430 | 433 | 436 | 439 | 442 | 445 | 448 | 451 | 454 | 457 | 460 | 463 | 466 | 469 | 470 | 471 | 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 | react-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 | 7 |
8 |
9 | wave 10 | Boilerplate 11 |
12 |
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 |
10 | 11 | wave 12 | 15 |
Hello World Dashboard {counter}
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 | 7 | 8 | 9 | 10 | 11 | 12 | 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 |

Client hosted at localhost:8087!

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 | --------------------------------------------------------------------------------