Browse Source

CHANGE | auth, navigation, store, shared, services. Fixed webpack config

merge-requests/2/head
D-Klimishen 3 years ago
parent
commit
3bfdf84e5e
  1. 51
      .eslintrc
  2. 23
      config/env.js
  3. 66
      config/getHttpsConfig.js
  4. 29
      config/jest/babelTransform.js
  5. 28
      config/jest/fileTransform.js
  6. 134
      config/modules.js
  7. 76
      config/paths.js
  8. 35
      config/pnpTs.js
  9. 400
      config/webpack.config.dev.js
  10. 763
      config/webpack.config.js
  11. 500
      config/webpack.config.prod.js
  12. 63
      config/webpackDevServer.config.js
  13. 34775
      package-lock.json
  14. 188
      package.json
  15. 45
      scripts/build.js
  16. 68
      scripts/start.js
  17. 6
      scripts/test.js
  18. 43
      src/App.tsx
  19. 7
      src/api/account/requests.ts
  20. 4
      src/api/account/responses.interfaces.ts
  21. 14
      src/api/auth/requests.interfaces.ts
  22. 28
      src/api/auth/requests.ts
  23. 4
      src/api/auth/responses.interfaces.ts
  24. 53
      src/api/http.service.ts
  25. 3
      src/api/http.types.ts
  26. 2
      src/api/index.ts
  27. BIN
      src/assets/img/add_file.png
  28. BIN
      src/assets/img/ava.png
  29. 16
      src/assets/img/burger.svg
  30. BIN
      src/assets/img/default-avatar.jpg
  31. BIN
      src/assets/img/default-img.png
  32. BIN
      src/assets/img/document/DOCX.png
  33. BIN
      src/assets/img/document/JPEG.png
  34. BIN
      src/assets/img/document/PDF.png
  35. BIN
      src/assets/img/document/SVG.png
  36. BIN
      src/assets/img/document/XLSX.png
  37. BIN
      src/assets/img/document/doc.png
  38. BIN
      src/assets/img/document/document.png
  39. BIN
      src/assets/img/document/jpg.png
  40. BIN
      src/assets/img/document/png.png
  41. BIN
      src/assets/img/document/tif.png
  42. BIN
      src/assets/img/document/txt.png
  43. BIN
      src/assets/img/document/webp.png
  44. BIN
      src/assets/img/document/xls.png
  45. BIN
      src/assets/img/group_icon.png
  46. BIN
      src/assets/img/logo_task.png
  47. BIN
      src/assets/img/logo_task2.png
  48. BIN
      src/assets/img/send.png
  49. 45
      src/components/Fields/TreeSelectField.jsx
  50. 18
      src/config/index.js
  51. 20
      src/config/rest-client.js
  52. 43
      src/containers/App/App.jsx
  53. 27
      src/containers/App/MainWrapper.jsx
  54. 58
      src/containers/App/Router.jsx
  55. 79
      src/containers/App/RouterComponent.js
  56. 31
      src/containers/App/reducers.js
  57. 57
      src/containers/App/router/Router.tsx
  58. 2
      src/containers/App/router/index.ts
  59. 77
      src/containers/App/router/routes.config.ts
  60. 15
      src/containers/App/router/routes.enum.ts
  61. 13
      src/containers/App/store.js
  62. 40
      src/containers/LogIn/actions.js
  63. 68
      src/containers/LogIn/components/LogInForm.jsx
  64. 48
      src/containers/LogIn/components/fields/LoginField.jsx
  65. 80
      src/containers/LogIn/components/fields/PasswordField.jsx
  66. 25
      src/containers/LogIn/index.jsx
  67. 95
      src/containers/auth/components/SignInForm.tsx
  68. 39
      src/containers/auth/components/fields/LoginField.tsx
  69. 48
      src/containers/auth/components/fields/PasswordField.tsx
  70. 2
      src/containers/auth/components/fields/index.ts
  71. 2
      src/containers/auth/components/index.ts
  72. 1
      src/containers/auth/index.ts
  73. 1
      src/containers/auth/screens/index.ts
  74. 27
      src/containers/auth/screens/sign-in.screen.tsx
  75. 15
      src/index.tsx
  76. 71
      src/react-app-env.d.ts
  77. 31
      src/redux/reducers.js
  78. 2
      src/scss/containers/account.scss
  79. 2
      src/scss/containers/not-found.scss
  80. 4
      src/scss/settings/variable.scss
  81. 19
      src/services/domain/account.service.ts
  82. 103
      src/services/domain/auth.service.ts
  83. 1
      src/services/domain/index.ts
  84. 21
      src/services/system/global-container.service.ts
  85. 1
      src/services/system/index.ts
  86. 1
      src/shared/components/elements/index.ts
  87. 13
      src/shared/components/elements/loader.component.tsx
  88. 123
      src/shared/components/form/CheckBox.jsx
  89. 20
      src/shared/components/form/CheckBox.tsx
  90. 1
      src/shared/components/form/index.ts
  91. 3
      src/shared/components/index.ts
  92. 16
      src/shared/components/layouts/MainWrapper.tsx
  93. 1
      src/shared/components/layouts/index.ts
  94. 2
      src/shared/enums/index.ts
  95. 6
      src/shared/enums/storage-keys.enum.ts
  96. 9
      src/shared/enums/user.enum.ts
  97. 3
      src/shared/index.ts
  98. 2
      src/shared/interfaces/index.ts
  99. 4
      src/shared/interfaces/token-pair.interfaces.ts
  100. 37
      src/shared/interfaces/user.interfaces.ts
  101. Some files were not shown because too many files have changed in this diff Show More

51
.eslintrc

@ -1,51 +0,0 @@ @@ -1,51 +0,0 @@
{
"extends": "airbnb",
"parser": "babel-eslint",
"plugins": [
"react"
],
"env": {
"browser": true,
"node": true,
"mocha": true,
"es6": true
},
"rules": {
"jsx-a11y/anchor-is-valid": [
"warning",
{
"components": [
"Link"
],
"specialLink": [
"to",
"hrefLeft",
"hrefRight"
],
"aspects": [
"noHref",
"invalidHref",
"preferButton"
]
}
],
"max-len": [
"warning",
{
"code": 120,
"ignoreRegExpLiterals": true,
"tabWidth": 2
}
],
"no-console": [
1
],
"linebreak-style": "off",
"react/jsx-one-expression-per-line": [
0,
{
"allow": "literal"
}
]
}
}

23
config/env.js

@ -15,13 +15,13 @@ if (!NODE_ENV) { @@ -15,13 +15,13 @@ if (!NODE_ENV) {
}
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
var dotenvFiles = [
const dotenvFiles = [
`${paths.dotenv}.${NODE_ENV}.local`,
`${paths.dotenv}.${NODE_ENV}`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
`${paths.dotenv}.${NODE_ENV}`,
paths.dotenv,
].filter(Boolean);
@ -46,7 +46,7 @@ dotenvFiles.forEach(dotenvFile => { @@ -46,7 +46,7 @@ dotenvFiles.forEach(dotenvFile => {
// It works similar to `NODE_PATH` in Node itself:
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
// We also resolve them to make sure all tools using them work consistently.
const appDirectory = fs.realpathSync(process.cwd());
@ -57,7 +57,7 @@ process.env.NODE_PATH = (process.env.NODE_PATH || '') @@ -57,7 +57,7 @@ process.env.NODE_PATH = (process.env.NODE_PATH || '')
.join(path.delimiter);
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
// injected into the application via DefinePlugin in Webpack configuration.
// injected into the application via DefinePlugin in webpack configuration.
const REACT_APP = /^REACT_APP_/i;
function getClientEnvironment(publicUrl) {
@ -77,9 +77,22 @@ function getClientEnvironment(publicUrl) { @@ -77,9 +77,22 @@ function getClientEnvironment(publicUrl) {
// This should only be used as an escape hatch. Normally you would put
// images into the `src` and `import` them in code to get their paths.
PUBLIC_URL: publicUrl,
// We support configuring the sockjs pathname during development.
// These settings let a developer run multiple simultaneous projects.
// They are used as the connection `hostname`, `pathname` and `port`
// in webpackHotDevClient. They are used as the `sockHost`, `sockPath`
// and `sockPort` options in webpack-dev-server.
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
// Whether or not react-refresh is enabled.
// react-refresh is not 100% stable at this time,
// which is why it's disabled by default.
// It is defined here so it is available in the webpackHotDevClient.
FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
}
);
// Stringify all values so we can feed into Webpack DefinePlugin
// Stringify all values so we can feed into webpack DefinePlugin
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);

66
config/getHttpsConfig.js

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
'use strict';
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const chalk = require('react-dev-utils/chalk');
const paths = require('./paths');
// Ensure the certificate and key provided are valid and if not
// throw an easy to debug error
function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
let encrypted;
try {
// publicEncrypt will throw an error with an invalid cert
encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
} catch (err) {
throw new Error(
`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
);
}
try {
// privateDecrypt will throw an error with an invalid key
crypto.privateDecrypt(key, encrypted);
} catch (err) {
throw new Error(
`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
err.message
}`
);
}
}
// Read file and throw an error if it doesn't exist
function readEnvFile(file, type) {
if (!fs.existsSync(file)) {
throw new Error(
`You specified ${chalk.cyan(
type
)} in your env, but the file "${chalk.yellow(file)}" can't be found.`
);
}
return fs.readFileSync(file);
}
// Get the https config
// Return cert files if provided in env, otherwise just true or false
function getHttpsConfig() {
const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
const isHttps = HTTPS === 'true';
if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
const config = {
cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
};
validateKeyAndCerts({ ...config, keyFile, crtFile });
return config;
}
return isHttps;
}
module.exports = getHttpsConfig;

29
config/jest/babelTransform.js

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
'use strict';
const babelJest = require('babel-jest');
const hasJsxRuntime = (() => {
if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
return false;
}
try {
require.resolve('react/jsx-runtime');
return true;
} catch (e) {
return false;
}
})();
module.exports = babelJest.createTransformer({
presets: [
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
},
],
],
babelrc: false,
configFile: false,
});

28
config/jest/fileTransform.js

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
'use strict';
const path = require('path');
const camelcase = require('camelcase');
// This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/en/webpack.html
@ -10,17 +11,26 @@ module.exports = { @@ -10,17 +11,26 @@ module.exports = {
const assetFilename = JSON.stringify(path.basename(filename));
if (filename.match(/\.svg$/)) {
return `module.exports = {
// Based on how SVGR generates a component name:
// https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
const pascalCaseFilename = camelcase(path.parse(filename).name, {
pascalCase: true,
});
const componentName = `Svg${pascalCaseFilename}`;
return `const React = require('react');
module.exports = {
__esModule: true,
default: ${assetFilename},
ReactComponent: (props) => ({
$$typeof: Symbol.for('react.element'),
type: 'svg',
ref: null,
key: null,
props: Object.assign({}, props, {
children: ${assetFilename}
})
ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
return {
$$typeof: Symbol.for('react.element'),
type: 'svg',
ref: ref,
key: null,
props: Object.assign({}, props, {
children: ${assetFilename}
})
};
}),
};`;
}

134
config/modules.js

@ -0,0 +1,134 @@ @@ -0,0 +1,134 @@
'use strict';
const fs = require('fs');
const path = require('path');
const paths = require('./paths');
const chalk = require('react-dev-utils/chalk');
const resolve = require('resolve');
/**
* Get additional module paths based on the baseUrl of a compilerOptions object.
*
* @param {Object} options
*/
function getAdditionalModulePaths(options = {}) {
const baseUrl = options.baseUrl;
if (!baseUrl) {
return '';
}
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is
// the default behavior.
if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
return null;
}
// Allow the user set the `baseUrl` to `appSrc`.
if (path.relative(paths.appSrc, baseUrlResolved) === '') {
return [paths.appSrc];
}
// If the path is equal to the root directory we ignore it here.
// We don't want to allow importing from the root directly as source files are
// not transpiled outside of `src`. We do allow importing them with the
// absolute path (e.g. `src/Components/Button.js`) but we set that up with
// an alias.
if (path.relative(paths.appPath, baseUrlResolved) === '') {
return null;
}
// Otherwise, throw an error.
throw new Error(
chalk.red.bold(
"Your project's `baseUrl` can only be set to `src` or `node_modules`." +
' Create React App does not support other values at this time.'
)
);
}
/**
* Get webpack aliases based on the baseUrl of a compilerOptions object.
*
* @param {*} options
*/
function getWebpackAliases(options = {}) {
const baseUrl = options.baseUrl;
if (!baseUrl) {
return {};
}
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
if (path.relative(paths.appPath, baseUrlResolved) === '') {
return {
src: paths.appSrc,
};
}
}
/**
* Get jest aliases based on the baseUrl of a compilerOptions object.
*
* @param {*} options
*/
function getJestAliases(options = {}) {
const baseUrl = options.baseUrl;
if (!baseUrl) {
return {};
}
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
if (path.relative(paths.appPath, baseUrlResolved) === '') {
return {
'^src/(.*)$': '<rootDir>/src/$1',
};
}
}
function getModules() {
// Check if TypeScript is setup
const hasTsConfig = fs.existsSync(paths.appTsConfig);
const hasJsConfig = fs.existsSync(paths.appJsConfig);
if (hasTsConfig && hasJsConfig) {
throw new Error(
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
);
}
let config;
// If there's a tsconfig.json we assume it's a
// TypeScript project and set up the config
// based on tsconfig.json
if (hasTsConfig) {
const ts = require(resolve.sync('typescript', {
basedir: paths.appNodeModules,
}));
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
// Otherwise we'll check if there is jsconfig.json
// for non TS projects.
} else if (hasJsConfig) {
config = require(paths.appJsConfig);
}
config = config || {};
const options = config.compilerOptions || {};
const additionalModulePaths = getAdditionalModulePaths(options);
return {
additionalModulePaths: additionalModulePaths,
webpackAliases: getWebpackAliases(options),
jestAliases: getJestAliases(options),
hasTsConfig,
};
}
module.exports = getModules();

76
config/paths.js

@ -2,56 +2,74 @@ @@ -2,56 +2,74 @@
const path = require('path');
const fs = require('fs');
const url = require('url');
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
// Make sure any symlinks in the project folder are resolved:
// https://github.com/facebook/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
const envPublicUrl = process.env.PUBLIC_URL;
function ensureSlash(inputPath, needsSlash) {
const hasSlash = inputPath.endsWith('/');
if (hasSlash && !needsSlash) {
return inputPath.substr(0, inputPath.length - 1);
} else if (!hasSlash && needsSlash) {
return `${inputPath}/`;
} else {
return inputPath;
}
}
const getPublicUrl = appPackageJson =>
envPublicUrl || require(appPackageJson).homepage;
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
// "public path" at which the app is served.
// Webpack needs to know it to put the right <script> hrefs into HTML even in
// webpack needs to know it to put the right <script> hrefs into HTML even in
// single-page apps that may serve index.html for nested URLs like /todos/42.
// We can't use a relative path in HTML because we don't want to load something
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
function getServedPath(appPackageJson) {
const publicUrl = getPublicUrl(appPackageJson);
const servedUrl =
envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/');
return ensureSlash(servedUrl, true);
}
const publicUrlOrPath = getPublicUrlOrPath(
process.env.NODE_ENV === 'development',
require(resolveApp('package.json')).homepage,
process.env.PUBLIC_URL
);
const buildPath = process.env.BUILD_PATH || 'build';
const moduleFileExtensions = [
'web.mjs',
'mjs',
'web.js',
'js',
'web.ts',
'ts',
'web.tsx',
'tsx',
'json',
'web.jsx',
'jsx',
];
// Resolve file paths in the same order as webpack
const resolveModule = (resolveFn, filePath) => {
const extension = moduleFileExtensions.find(extension =>
fs.existsSync(resolveFn(`${filePath}.${extension}`))
);
if (extension) {
return resolveFn(`${filePath}.${extension}`);
}
return resolveFn(`${filePath}.js`);
};
// config after eject: we're in ./config/
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp('build'),
appBuild: resolveApp(buildPath),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveApp('src/index.tsx'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
appTsConfig: resolveApp('tsconfig.json'),
appJsConfig: resolveApp('jsconfig.json'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveApp('src/setupTests.js'),
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
publicUrl: getPublicUrl(resolveApp('package.json')),
servedPath: getServedPath(resolveApp('package.json')),
swSrc: resolveModule(resolveApp, 'src/service-worker'),
publicUrlOrPath,
};
module.exports.moduleFileExtensions = moduleFileExtensions;

35
config/pnpTs.js

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
'use strict';
const { resolveModuleName } = require('ts-pnp');
exports.resolveModuleName = (
typescript,
moduleName,
containingFile,
compilerOptions,
resolutionHost
) => {
return resolveModuleName(
moduleName,
containingFile,
compilerOptions,
resolutionHost,
typescript.resolveModuleName
);
};
exports.resolveTypeReferenceDirective = (
typescript,
moduleName,
containingFile,
compilerOptions,
resolutionHost
) => {
return resolveModuleName(
moduleName,
containingFile,
compilerOptions,
resolutionHost,
typescript.resolveTypeReferenceDirective
);
};

400
config/webpack.config.dev.js

@ -1,400 +0,0 @@ @@ -1,400 +0,0 @@
'use strict';
const path = require('path');
const webpack = require('webpack');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const getClientEnvironment = require('./env');
const paths = require('./paths');
const ManifestPlugin = require('webpack-manifest-plugin');
const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
// Webpack uses `publicPath` to determine where the app is being served from.
// In development, we always serve from the root. This makes config easier.
const publicPath = '/';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
const publicUrl = '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
],
},
},
];
if (preProcessor) {
loaders.push(require.resolve(preProcessor));
}
return loaders;
};
// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
module.exports = {
mode: 'development',
// You may want 'eval' instead if you prefer to see the compiled output in DevTools.
// See the discussion in https://github.com/facebook/create-react-app/issues/343
devtool: 'cheap-module-source-map',
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
entry: [
// Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes.
// When you save a file, the client will either apply hot updates (in case
// of CSS changes), or refresh the page (in case of JS changes). When you
// make a syntax error, this client will display a syntax error overlay.
// Note: instead of the default WebpackDevServer client, we use a custom one
// to bring better experience for Create React App users. You can replace
// the line below with these two lines if you prefer the stock client:
// require.resolve('webpack-dev-server/client') + '?/',
// require.resolve('webpack/hot/dev-server'),
require.resolve('react-dev-utils/webpackHotDevClient'),
// Finally, this is your app's code:
paths.appIndexJs,
// We include the app code last so that if there is a runtime error during
// initialization, it doesn't blow up the WebpackDevServer client, and
// changing JS code would still trigger a refresh.
],
output: {
// Add /* filename */ comments to generated require()s in the output.
pathinfo: true,
// This does not produce a real file. It's just the virtual path that is
// served by WebpackDevServer in development. This is the JS bundle
// containing code from all our entry points, and the Webpack runtime.
filename: 'bundle.js',
// There are also additional JS chunk files if you use code splitting.
chunkFilename: 'static/js/[name].chunk.js',
// This is the URL that app is served from. We use "/" in development.
publicPath: publicPath,
path: path.resolve(__dirname, 'dist'),
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
},
optimization: {
// Automatically split vendor and commons
// https://twitter.com/wSokra/status/969633336732905474
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
splitChunks: {
chunks: 'all',
name: false,
},
// Keep the runtime chunk seperated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
runtimeChunk: true,
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules'].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx', '.ts', '.tsx'],
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
},
plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
// guards against forgotten dependencies and such.
PnpWebpackPlugin,
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
resolveLoader: {
plugins: [
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
// from the current package.
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
// First, run the linter.
// It's important to do this before Babel processes the JS.
// {
// test: /\.(js|jsx)$/,
// enforce: 'pre',
// use: [
// {
// options: {
// formatter: require.resolve('react-dev-utils/eslintFormatter'),
// eslintPath: require.resolve('eslint'),
// },
// loader: require.resolve('eslint-loader'),
// },
// ],
// include: paths.appSrc,
// },
{
// `mjs` support is still in its infancy in the ecosystem, so we don't
// support it.
// Modules who define their `browser` or `module` key as `mjs` force
// the use of this extension, so we need to tell webpack to fall back
// to auto mode (ES Module interop, allows ESM to import CommonJS).
test: /\.mjs$/,
include: /node_modules/,
type: 'javascript/auto',
},
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// "url" loader works like "file" loader except that it embeds assets
// smaller than specified limit in bytes as data URLs to avoid requests.
// A missing `test` is equivalent to a match.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process application JS with Babel.
// The preset includes JSX, Flow, and some ESnext features.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent: '@svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
// Don't waste time on Gzipping the cache
cacheCompression: false,
},
},
// Process any JS outside of the app with Babel.
// Unlike the application JS, we only compile the standard ES features.
{
test: /\.js$/,
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
// Don't waste time on Gzipping the cache
cacheCompression: false,
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
},
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use a plugin to extract that CSS to a file, but
// in development "style" loader enables hot editing of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
}),
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
// Opt-in support for SASS (using .scss or .sass extensions).
// Chains the sass-loader with the css-loader and the style-loader
// to immediately apply all styles to the DOM.
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders({ importLoaders: 2 }, 'sass-loader'),
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
// Exclude `js` files to keep "css" loader working as it injects
// its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
],
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
}),
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In development, this will be an empty string.
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
// This gives some necessary context to module not found errors, such as
// the requesting resource.
new ModuleNotFoundPlugin(paths.appPath),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
new webpack.DefinePlugin(env.stringified),
// This is necessary to emit hot updates (currently CSS only):
new webpack.HotModuleReplacementPlugin(),
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebook/create-react-app/issues/240
new CaseSensitivePathsPlugin(),
// If you require a missing module and then `npm install` it, you still have
// to restart the development server for Webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebook/create-react-app/issues/186
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
}),
],
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
};

763
config/webpack.config.js

@ -0,0 +1,763 @@ @@ -0,0 +1,763 @@
'use strict';
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const ESLintPlugin = require('eslint-webpack-plugin');
const paths = require('./paths');
const modules = require('./modules');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const postcssNormalize = require('postcss-normalize');
const appPackageJson = require(paths.appPackageJson);
// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
const webpackDevClientEntry = require.resolve(
'react-dev-utils/webpackHotDevClient'
);
const reactRefreshOverlayEntry = require.resolve(
'react-dev-utils/refreshOverlayInterop'
);
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
// makes for a smoother build process.
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === 'true';
const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === 'true';
const imageInlineSizeLimit = parseInt(
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
);
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
// Get the path to the uncompiled service worker (if it exists).
const swSrc = paths.swSrc;
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const hasJsxRuntime = (() => {
if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
return false;
}
try {
require.resolve('react/jsx-runtime');
return true;
} catch (e) {
return false;
}
})();
// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function (webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
// Variable used for enabling profiling in Production
// passed into alias object. Uses a flag if passed into the build command
const isEnvProductionProfile =
isEnvProduction && process.argv.includes('--profile');
// We will provide `paths.publicUrlOrPath` to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
// Get environment variables to inject into our app.
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
const shouldUseReactRefresh = env.raw.FAST_REFRESH;
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && require.resolve('style-loader'),
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
// css is located in `static/css`, use '../../' to locate index.html folder
// in production `paths.publicUrlOrPath` can be a relative path
options: paths.publicUrlOrPath.startsWith('.')
? { publicPath: '../../' }
: {},
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
// Adds PostCSS Normalize as the reset css with default options,
// so that it honors browserslist config in package.json
// which in turn let's users customize the target behavior as per their needs.
postcssNormalize(),
],
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
},
},
].filter(Boolean);
if (preProcessor) {
loaders.push(
{
loader: require.resolve('resolve-url-loader'),
options: {
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
root: paths.appSrc,
},
},
{
loader: require.resolve(preProcessor),
options: {
sourceMap: true,
},
}
);
}
return loaders;
};
return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
// Stop compilation early in production
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
entry:
isEnvDevelopment && !shouldUseReactRefresh
? [
// Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes.
// When you save a file, the client will either apply hot updates (in case
// of CSS changes), or refresh the page (in case of JS changes). When you
// make a syntax error, this client will display a syntax error overlay.
// Note: instead of the default WebpackDevServer client, we use a custom one
// to bring better experience for Create React App users. You can replace
// the line below with these two lines if you prefer the stock client:
//
// require.resolve('webpack-dev-server/client') + '?/',
// require.resolve('webpack/hot/dev-server'),
//
// When using the experimental react-refresh integration,
// the webpack plugin takes care of injecting the dev client for us.
webpackDevClientEntry,
// Finally, this is your app's code:
paths.appIndexJs,
// We include the app code last so that if there is a runtime error during
// initialization, it doesn't blow up the WebpackDevServer client, and
// changing JS code would still trigger a refresh.
]
: paths.appIndexJs,
output: {
// The build folder.
path: isEnvProduction ? paths.appBuild : undefined,
// Add /* filename */ comments to generated require()s in the output.
pathinfo: isEnvDevelopment,
// There will be one main bundle, and one file per asynchronous chunk.
// In development, it does not produce real files.
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
// TODO: remove this when upgrading to webpack 5
futureEmitAssets: true,
// There are also additional JS chunk files if you use code splitting.
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
// webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: paths.publicUrlOrPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
// Prevents conflicts when multiple webpack runtimes (from different apps)
// are used on the same page.
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
// this defaults to 'window', but by setting it to 'this' then
// module chunks which are built will work in web workers as well.
globalObject: 'this',
},
optimization: {
minimize: isEnvProduction,
minimizer: [
// This is only used in production mode
new TerserPlugin({
terserOptions: {
parse: {
// We want terser to parse ecma 8 code. However, we don't want it
// to apply any minification steps that turns valid ecma 5 code
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebook/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
// Disabled because of an issue with Terser breaking valid code:
// https://github.com/facebook/create-react-app/issues/5250
// Pending further investigation:
// https://github.com/terser-js/terser/issues/120
inline: 2,
},
mangle: {
safari10: true,
},
// Added for profiling in devtools
keep_classnames: isEnvProductionProfile,
keep_fnames: isEnvProductionProfile,
output: {
ecma: 5,
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
ascii_only: true,
},
},
sourceMap: shouldUseSourceMap,
}),
// This is only used in production mode
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: shouldUseSourceMap
? {
// `inline: false` forces the sourcemap to be output into a
// separate file
inline: false,
// `annotation: true` appends the sourceMappingURL to the end of
// the css file, helping the browser find the sourcemap
annotation: true,
}
: false,
},
cssProcessorPluginOptions: {
preset: ['default', { minifyFontValues: { removeQuotes: false } }],
},
}),
],
// Automatically split vendor and commons
// https://twitter.com/wSokra/status/969633336732905474
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
splitChunks: {
chunks: 'all',
name: isEnvDevelopment,
},
// Keep the runtime chunk separated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
// https://github.com/facebook/create-react-app/issues/5358
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`,
},
},
resolve: {
// This allows you to set a fallback for where webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
modules.additionalModulePaths || []
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
// Allows for better profiling with ReactDevTools
...(isEnvProductionProfile && {
'react-dom$': 'react-dom/profiling',
'scheduler/tracing': 'scheduler/tracing-profiling',
}),
...(modules.webpackAliases || {}),
},
plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
// guards against forgotten dependencies and such.
PnpWebpackPlugin,
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [
paths.appPackageJson,
reactRefreshOverlayEntry,
]),
],
},
resolveLoader: {
plugins: [
// Also related to Plug'n'Play, but this time it tells webpack to load its loaders
// from the current package.
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// TODO: Merge this config once `image/avif` is in the mime-db
// https://github.com/jshttp/mime-db
{
test: [/\.avif$/],
loader: require.resolve('url-loader'),
options: {
limit: imageInlineSizeLimit,
mimetype: 'image/avif',
name: 'static/media/[name].[hash:8].[ext]',
},
},
// "url" loader works like "file" loader except that it embeds assets
// smaller than specified limit in bytes as data URLs to avoid requests.
// A missing `test` is equivalent to a match.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: imageInlineSizeLimit,
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
presets: [
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
},
],
],
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'@svgr/webpack?-svgo,+titleProp,+ref![path]',
},
},
},
],
["module-resolver", {
"root": ["./src"],
"alias": {
"@": "./src",
}
}],
isEnvDevelopment &&
shouldUseReactRefresh &&
require.resolve('react-refresh/babel'),
].filter(Boolean),
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
compact: isEnvProduction,
},
},
// Process any JS outside of the app with Babel.
// Unlike the application JS, we only compile the standard ES features.
{
test: /\.(js|mjs)$/,
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
// Babel sourcemaps are needed for debugging into node_modules
// code. Without the options below, debuggers like VSCode
// show incorrect code and set breakpoints on the wrong lines.
sourceMaps: shouldUseSourceMap,
inputSourceMap: shouldUseSourceMap,
},
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use MiniCSSExtractPlugin to extract that CSS
// to a file, but in development "style" loader enables hot editing
// of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
}),
},
// Opt-in support for SASS (using .scss or .sass extensions).
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
},
'sass-loader'
),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
},
'sass-loader'
),
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
// Inlines the webpack runtime script. This script is too small to warrant
// a network request.
// https://github.com/facebook/create-react-app/issues/5358
isEnvProduction &&
shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
// It will be an empty string unless you specify "homepage"
// in `package.json`, in which case it will be the pathname of that URL.
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
// This gives some necessary context to module not found errors, such as
// the requesting resource.
new ModuleNotFoundPlugin(paths.appPath),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV is set to production
// during a production build.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
// This is necessary to emit hot updates (CSS and Fast Refresh):
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
// Experimental hot reloading for React .
// https://github.com/facebook/react/tree/master/packages/react-refresh
isEnvDevelopment &&
shouldUseReactRefresh &&
new ReactRefreshWebpackPlugin({
overlay: {
entry: webpackDevClientEntry,
// The expected exports are slightly different from what the overlay exports,
// so an interop is included here to enable feedback on module-level errors.
module: reactRefreshOverlayEntry,
// Since we ship a custom dev client and overlay integration,
// the bundled socket handling logic can be eliminated.
sockIntegration: false,
},
}),
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebook/create-react-app/issues/240
isEnvDevelopment && new CaseSensitivePathsPlugin(),
// If you require a missing module and then `npm install` it, you still have
// to restart the development server for webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebook/create-react-app/issues/186
isEnvDevelopment &&
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
isEnvProduction &&
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
// Generate an asset manifest file with the following content:
// - "files" key: Mapping of all asset filenames to their corresponding
// output file so that tools can pick it up without having to parse
// `index.html`
// - "entrypoints" key: Array of files which are included in `index.html`,
// can be used to reconstruct the HTML if necessary
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: paths.publicUrlOrPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
fileName => !fileName.endsWith('.map')
);
return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
},
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the webpack build.
isEnvProduction &&
fs.existsSync(swSrc) &&
new WorkboxWebpackPlugin.InjectManifest({
swSrc,
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
// Bump up the default maximum size (2mb) that's precached,
// to make lazy-loading failure scenarios less likely.
// See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
}),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: isEnvDevelopment,
checkSyntacticErrors: true,
resolveModuleNameModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
resolveTypeReferenceDirectiveModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
tsconfig: paths.appTsConfig,
reportFiles: [
// This one is specifically to match during CI tests,
// as micromatch doesn't match
// '../cra-template-typescript/template/src/App.tsx'
// otherwise.
'../**/src/**/*.{ts,tsx}',
'**/src/**/*.{ts,tsx}',
'!**/src/**/__tests__/**',
'!**/src/**/?(*.)(spec|test).*',
'!**/src/setupProxy.*',
'!**/src/setupTests.*',
],
silent: true,
// The formatter is invoked directly in WebpackDevServerUtils during development
formatter: isEnvProduction ? typescriptFormatter : undefined,
}),
!disableESLintPlugin &&
new ESLintPlugin({
// Plugin options
extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
context: paths.appSrc,
cache: true,
cacheLocation: path.resolve(
paths.appNodeModules,
'.cache/.eslintcache'
),
// ESLint class options
cwd: paths.appPath,
resolvePluginsRelativeTo: __dirname,
baseConfig: {
extends: [require.resolve('eslint-config-react-app/base')],
rules: {
...(!hasJsxRuntime && {
'react/react-in-jsx-scope': 'error',
}),
},
},
}),
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser.
// Tell webpack to provide empty mocks for them so importing them works.
node: {
module: 'empty',
dgram: 'empty',
dns: 'mock',
fs: 'empty',
http2: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
};
};

500
config/webpack.config.prod.js

@ -1,500 +0,0 @@ @@ -1,500 +0,0 @@
'use strict';
const path = require('path');
const webpack = require('webpack');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const getClientEnvironment = require('./env');
const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
// Webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
const publicPath = paths.servedPath;
// Some apps do not use client-side routing with pushState.
// For these, "homepage" can be set to "." to enable relative asset paths.
const shouldUseRelativeAssetPaths = publicPath === './';
// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
const publicUrl = publicPath.slice(0, -1);
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);
// Assert this just to be safe.
// Development builds of React are slow and not intended for production.
if (env.stringified['process.env'].NODE_ENV !== '"production"') {
throw new Error('Production builds must have NODE_ENV=production.');
}
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
{
loader: MiniCssExtractPlugin.loader,
options: Object.assign(
{},
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
),
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
],
sourceMap: shouldUseSourceMap,
},
},
];
if (preProcessor) {
loaders.push({
loader: require.resolve(preProcessor),
options: {
sourceMap: shouldUseSourceMap,
},
});
}
return loaders;
};
// This is the production configuration.
// It compiles slowly and is focused on producing a fast and minimal bundle.
// The development configuration is different and lives in a separate file.
module.exports = {
mode: 'production',
// Don't attempt to continue if there are any errors.
bail: true,
// We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment.
devtool: shouldUseSourceMap ? 'source-map' : false,
// In production, we only want to load the app code.
entry: [paths.appIndexJs],
output: {
// The build folder.
path: paths.appBuild,
// Generated JS file names (with nested folders).
// There will be one main bundle, and one file per asynchronous chunk.
// We don't currently advertise code splitting but Webpack supports it.
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: publicPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/'),
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
// we want terser to parse ecma 8 code. However, we don't want it
// to apply any minfication steps that turns valid ecma 5 code
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebook/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
ascii_only: true,
},
},
// Use multi-process parallel running to improve the build speed
// Default number of concurrent runs: os.cpus().length - 1
parallel: true,
// Enable file caching
cache: true,
sourceMap: shouldUseSourceMap,
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: {
// `inline: false` forces the sourcemap to be output into a
// separate file
inline: false,
// `annotation: true` appends the sourceMappingURL to the end of
// the css file, helping the browser find the sourcemap
annotation: true,
},
},
}),
],
// Automatically split vendor and commons
// https://twitter.com/wSokra/status/969633336732905474
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
splitChunks: {
chunks: 'all',
name: false,
},
// Keep the runtime chunk seperated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
runtimeChunk: true,
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules'].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx'],
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
},
plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
// guards against forgotten dependencies and such.
PnpWebpackPlugin,
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
resolveLoader: {
plugins: [
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
// from the current package.
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
// First, run the linter.
// It's important to do this before Babel processes the JS.
// {
// test: /\.(js|jsx)$/,
// enforce: 'pre',
// use: [
// {
// options: {
// formatter: require.resolve('react-dev-utils/eslintFormatter'),
// eslintPath: require.resolve('eslint'),
// },
// loader: require.resolve('eslint-loader'),
// },
// ],
// include: paths.appSrc,
// },
{
// `mjs` support is still in its infancy in the ecosystem, so we don't
// support it.
// Modules who define their `browser` or `module` key as `mjs` force
// the use of this extension, so we need to tell webpack to fall back
// to auto mode (ES Module interop, allows ESM to import CommonJS).
test: /\.mjs$/,
include: /node_modules/,
type: 'javascript/auto',
},
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// "url" loader works just like "file" loader but it also embeds
// assets smaller than specified size as data URLs to avoid requests.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process application JS with Babel.
// The preset includes JSX, Flow, and some ESnext features.
{
test: /\.(js|jsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent: '@svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
],
cacheDirectory: true,
// Save disk space when time isn't as important
cacheCompression: true,
compact: true,
},
},
// Process any JS outside of the app with Babel.
// Unlike the application JS, we only compile the standard ES features.
{
test: /\.js$/,
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
// Save disk space when time isn't as important
cacheCompression: true,
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
},
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// `MiniCSSExtractPlugin` extracts styles into CSS
// files. If you use code splitting, async bundles will have their own separate CSS chunk file.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
loader: getStyleLoaders({
importLoaders: 1,
sourceMap: shouldUseSourceMap,
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
loader: getStyleLoaders({
importLoaders: 1,
sourceMap: shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
// Opt-in support for SASS. The logic here is somewhat similar
// as in the CSS routine, except that "sass-loader" runs first
// to compile SASS files into CSS.
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
loader: getStyleLoaders(
{
importLoaders: 2,
sourceMap: shouldUseSourceMap,
},
'sass-loader'
),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
loader: getStyleLoaders(
{
importLoaders: 2,
sourceMap: shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
// "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// it's runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|jsx)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
// Inlines the webpack runtime script. This script is too small to warrant
// a network request.
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In production, it will be an empty string unless you specify "homepage"
// in `package.json`, in which case it will be the pathname of that URL.
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
// This gives some necessary context to module not found errors, such as
// the requesting resource.
new ModuleNotFoundPlugin(paths.appPath),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the Webpack build.
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
exclude: [/\.map$/, /asset-manifest\.json$/],
importWorkboxFrom: 'cdn',
navigateFallback: publicUrl + '/index.html',
navigateFallbackBlacklist: [
// Exclude URLs starting with /_, as they're likely an API call
new RegExp('^/_'),
// Exclude URLs containing a dot, as they're likely a resource in
// public/ and not a SPA route
new RegExp('/[^/]+\\.[^/]+$'),
],
}),
],
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
};

63
config/webpackDevServer.config.js

@ -1,17 +1,20 @@ @@ -1,17 +1,20 @@
'use strict';
const fs = require('fs');
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const ignoredFiles = require('react-dev-utils/ignoredFiles');
const config = require('./webpack.config.dev');
const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware');
const paths = require('./paths');
const fs = require('fs');
const getHttpsConfig = require('./getHttpsConfig');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const host = process.env.HOST || '0.0.0.0';
const sockHost = process.env.WDS_SOCKET_HOST;
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node'
const sockPort = process.env.WDS_SOCKET_PORT;
module.exports = function(proxy, allowedHost) {
module.exports = function (proxy, allowedHost) {
return {
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
// websites from potentially accessing local content through DNS rebinding:
@ -44,24 +47,39 @@ module.exports = function(proxy, allowedHost) { @@ -44,24 +47,39 @@ module.exports = function(proxy, allowedHost) {
// Instead, we establish a convention that only files in `public` directory
// get served. Our build script will copy `public` into the `build` folder.
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
// Note that we only recommend to use `public` folder as an escape hatch
// for files like `favicon.ico`, `manifest.json`, and libraries that are
// for some reason broken when imported through Webpack. If you just want to
// for some reason broken when imported through webpack. If you just want to
// use an image, put it in `src` and `import` it from JavaScript instead.
contentBase: paths.appPublic,
contentBasePublicPath: paths.publicUrlOrPath,
// By default files from `contentBase` will not trigger a page reload.
watchContentBase: true,
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
// Enable hot reloading server. It will provide WDS_SOCKET_PATH endpoint
// for the WebpackDevServer client so it can learn when the files were
// updated. The WebpackDevServer client is included as an entry point
// in the Webpack development configuration. Note that only changes
// in the webpack development configuration. Note that only changes
// to CSS are currently hot reloaded. JS changes will refresh the browser.
hot: true,
// It is important to tell WebpackDevServer to use the same "root" path
// as we specified in the config. In development, we always serve from /.
publicPath: config.output.publicPath,
// Use 'ws' instead of 'sockjs-node' on server since we're using native
// websockets in `webpackHotDevClient`.
transportMode: 'ws',
// Prevent a WS client from getting injected as we're already including
// `webpackHotDevClient`.
injectClient: false,
// Enable custom sockjs pathname for websocket connection to hot reloading server.
// Enable custom sockjs hostname, pathname and port for websocket connection
// to hot reloading server.
sockHost,
sockPath,
sockPort,
// It is important to tell WebpackDevServer to use the same "publicPath" path as
// we specified in the webpack config. When homepage is '.', default to serving
// from the root.
// remove last slash so user can land on `/test` instead of `/test/`
publicPath: paths.publicUrlOrPath.slice(0, -1),
// WebpackDevServer is noisy by default so we emit custom message instead
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
quiet: true,
@ -72,34 +90,41 @@ module.exports = function(proxy, allowedHost) { @@ -72,34 +90,41 @@ module.exports = function(proxy, allowedHost) {
watchOptions: {
ignored: ignoredFiles(paths.appSrc),
},
// Enable HTTPS if the HTTPS environment variable is set to 'true'
https: protocol === 'https',
https: getHttpsConfig(),
host,
overlay: false,
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebook/create-react-app/issues/387.
disableDotRule: true,
index: paths.publicUrlOrPath,
},
public: allowedHost,
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
proxy,
before(app, server) {
if (fs.existsSync(paths.proxySetup)) {
// This registers user provided middleware for proxy reasons
require(paths.proxySetup)(app);
}
// Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware`
// middlewares before `redirectServedPath` otherwise will not have any effect
// This lets us fetch source contents from webpack for the error overlay
app.use(evalSourceMapMiddleware(server));
// This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware());
if (fs.existsSync(paths.proxySetup)) {
// This registers user provided middleware for proxy reasons
require(paths.proxySetup)(app);
}
},
after(app) {
// Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match
app.use(redirectServedPath(paths.publicUrlOrPath));
// This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination.
// We do this in development to avoid hitting the production cache if
// it used the same host and port.
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
app.use(noopServiceWorkerMiddleware());
app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
},
};
};

34775
package-lock.json generated

File diff suppressed because it is too large Load Diff

188
package.json

@ -1,56 +1,90 @@ @@ -1,56 +1,90 @@
{
"name": "bank-task",
"version": "1.0.0",
"name": "rws-web",
"version": "0.1.0",
"private": true,
"dependencies": {
"@babel/core": "7.12.3",
"@bitalikrty/redux-create-reducer": "^1.0.0",
"@material-ui/core": "^3.9.2",
"@mdi/font": "^3.5.95",
"@pmmmwh/react-refresh-webpack-plugin": "0.4.3",
"@svgr/webpack": "5.5.0",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/history": "^4.7.9",
"@types/jest": "^26.0.15",
"@types/node": "^12.0.0",
"@types/react": "^17.0.21",
"@types/react-dom": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
"antd": "^3.16.2",
"axios": "^0.21.4",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.6.0",
"babel-loader": "8.1.0",
"babel-plugin-module-resolver": "^4.1.0",
"babel-plugin-named-asset-import": "^0.3.7",
"babel-preset-react-app": "^10.0.0",
"bfj": "6.1.1",
"bootstrap": "^4.3.1",
"camelcase": "^6.1.0",
"case-sensitive-paths-webpack-plugin": "2.3.0",
"chalk": "^2.4.2",
"chart.js": "^2.7.3",
"classnames": "^2.2.6",
"dotenv": "^6.2.0",
"dotenv-expand": "4.2.0",
"css-loader": "4.3.0",
"dotenv": "8.2.0",
"dotenv-expand": "5.1.0",
"draft-js": "^0.10.5",
"draft-js-buttons": "^2.0.1",
"draftjs-to-html": "^0.8.4",
"eslint": "^7.11.0",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-testing-library": "^3.9.2",
"eslint-webpack-plugin": "^2.5.2",
"extract-text-webpack-plugin": "3.0.2",
"file-loader": "^3.0.1",
"fs-extra": "^7.0.1",
"file-loader": "6.1.1",
"fs-extra": "^9.0.1",
"highcharts": "^7.0.3",
"html-webpack-plugin": "^4.0.0-beta.5",
"html-webpack-plugin": "4.5.0",
"i18next": "^15.0.5",
"identity-obj-proxy": "3.0.0",
"immutability-helper": "^3.0.0",
"jest": "^24.1.0",
"jest": "26.6.0",
"jest-circus": "26.6.0",
"jest-pnp-resolver": "^1.2.0",
"jest-resolve": "^24.1.0",
"jest-resolve": "26.6.0",
"jest-watch-typeahead": "0.6.1",
"lodash": "^4.17.11",
"lodash.range": "^3.2.0",
"mdi-react": "^5.2.0",
"mini-css-extract-plugin": "^0.5.0",
"moment": "^2.24.0",
"optimize-css-assets-webpack-plugin": "5.0.1",
"pnp-webpack-plugin": "^1.3.1",
"postcss-flexbugs-fixes": "4.1.0",
"mdi-react": "^7.5.0",
"mini-css-extract-plugin": "0.11.3",
"moment": "^2.29.1",
"node-sass": "^4.11.0",
"optimize-css-assets-webpack-plugin": "5.0.4",
"pnp-webpack-plugin": "1.6.4",
"postcss-flexbugs-fixes": "4.2.1",
"postcss-loader": "3.0.0",
"postcss-preset-env": "^6.6.0",
"postcss-safe-parser": "4.0.1",
"postcss-normalize": "8.0.1",
"postcss-preset-env": "6.7.0",
"postcss-safe-parser": "5.0.2",
"prompts": "2.4.0",
"prop-types": "^15.7.2",
"rc-notification": "^3.3.1",
"rc-slider": "^8.6.6",
"rc-time-picker": "^3.6.2",
"rc-tooltip": "^3.7.3",
"react": "^17.0.2",
"react-app-polyfill": "^0.2.1",
"react-app-polyfill": "^2.0.0",
"react-big-calendar": "^0.20.3",
"react-chartjs-2": "^2.7.4",
"react-color": "^2.17.0",
"react-data-grid": "^6.0.2-0",
"react-datepicker": "^2.1.0",
"react-dev-utils": "^7.0.3",
"react-dev-utils": "^11.0.3",
"react-dimensions": "^1.3.1",
"react-dnd": "^7.0.2",
"react-dnd-html5-backend": "^7.0.2",
@ -61,13 +95,16 @@ @@ -61,13 +95,16 @@
"react-google-maps": "^9.4.5",
"react-highcharts": "^16.0.2",
"react-highlight": "^0.12.0",
"react-hook-form": "^7.15.3",
"react-hot-loader": "^4.13.0",
"react-i18next": "^10.2.0",
"react-images": "^0.5.19",
"react-input-mask": "^2.0.4",
"react-phone-input-2": "^2.9.5",
"react-redux": "^6.0.1",
"react-redux": "^7.2.5",
"react-refresh": "^0.8.3",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-router-dom": "^5.3.0",
"react-scroll-to-component": "^1.0.2",
"react-select": "^2.3.0",
"react-slick": "^0.23.2",
@ -82,17 +119,28 @@ @@ -82,17 +119,28 @@
"recharts": "^1.5.0",
"recompose": "^0.30.0",
"redux": "^4.1.1",
"redux-devtools-extension": "^2.13.8",
"redux-form": "^8.1.0",
"redux-thunk": "^2.3.0",
"resolve": "^1.10.0",
"resolve": "1.18.1",
"resolve-url-loader": "^3.1.2",
"sails.io.js": "^1.2.1",
"sass-loader": "^10.0.5",
"semver": "7.3.2",
"slick-carousel": "^1.8.1",
"smooth-scrollbar": "^8.3.1",
"socket.io-client": "^2.2.0",
"style-loader": "^0.23.1",
"terser-webpack-plugin": "^1.2.3",
"style-loader": "1.3.0",
"terser-webpack-plugin": "4.2.3",
"text-mask-addons": "^3.8.0",
"url-loader": "^1.1.2"
"ts-pnp": "1.2.0",
"typescript": "^4.1.2",
"url-loader": "4.1.1",
"web-vitals": "^1.0.1",
"webpack": "4.44.2",
"webpack-dev-server": "3.11.1",
"webpack-manifest-plugin": "2.2.0",
"workbox-webpack-plugin": "5.1.4"
},
"scripts": {
"start": "node scripts/start.js",
@ -100,68 +148,63 @@ @@ -100,68 +148,63 @@
"test": "node scripts/test.js"
},
"devDependencies": {
"@babel/core": "^7.3.4",
"@svgr/webpack": "^4.1.0",
"@types/react-dom": "^17.0.9",
"babel-core": "^6.26.3",
"babel-eslint": "^10.0.1",
"babel-jest": "^24.1.0",
"babel-loader": "^8.0.5",
"babel-plugin-named-asset-import": "^0.3.1",
"babel-preset-react-app": "^7.0.1",
"babel-runtime": "6.26.0",
"case-sensitive-paths-webpack-plugin": "^2.2.0",
"css-loader": "^2.1.0",
"eslint": "^5.15.0",
"@types/react-router-dom": "^5.1.5",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-react-app": "^3.0.7",
"eslint-config-react-app": "^6.0.0",
"eslint-loader": "^2.1.2",
"eslint-plugin-flowtype": "^3.4.2",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.12.4",
"node-sass": "^4.11.0",
"react-hot-loader": "^4.7.1",
"sass-loader": "^7.1.0",
"ts-loader": "^8.3.0",
"typescript": "^4.4.3",
"webpack": "^4.29.6",
"webpack-dev-server": "^3.1.14",
"webpack-manifest-plugin": "2.0.4",
"workbox-webpack-plugin": "^4.0.0"
"eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-react": "^7.21.5"
},
"eslintConfig": {
"extends": "react-app"
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"jest": {
"roots": [
"<rootDir>/src"
],
"collectCoverageFrom": [
"src/**/*.{js,jsx}"
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts"
],
"resolver": "jest-pnp-resolver",
"setupFiles": [
"react-app-polyfill/jsdom"
],
"setupFilesAfterEnv": [
"<rootDir>/src/setupTests.ts"
],
"testMatch": [
"<rootDir>/src/**/__tests__/**/*.{js,jsx}",
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx}"
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
],
"testEnvironment": "jsdom",
"testURL": "http://localhost",
"testRunner": "/Users/macbookpro/Desktop/work/____BANK/rws-web/node_modules/jest-circus/runner.js",
"transform": {
"^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest",
"^.+\\.(js|jsx|mjs|cjs|ts|tsx)$": "<rootDir>/config/jest/babelTransform.js",
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
"^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
"^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
},
"transformIgnorePatterns": [
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$",
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$",
"^.+\\.module\\.(css|sass|scss)$"
],
"modulePaths": [],
"moduleNameMapper": {
"^react-native$": "react-native-web",
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
@ -169,11 +212,20 @@ @@ -169,11 +212,20 @@
"moduleFileExtensions": [
"web.js",
"js",
"web.ts",
"ts",
"web.tsx",
"tsx",
"json",
"web.jsx",
"jsx",
"node"
]
],
"watchPlugins": [
"jest-watch-typeahead/filename",
"jest-watch-typeahead/testname"
],
"resetMocks": true
},
"babel": {
"presets": [

45
scripts/build.js

@ -16,11 +16,11 @@ require('../config/env'); @@ -16,11 +16,11 @@ require('../config/env');
const path = require('path');
const chalk = require('chalk');
const chalk = require('react-dev-utils/chalk');
const fs = require('fs-extra');
const webpack = require('webpack');
const bfj = require('bfj');
const config = require('../config/webpack.config.prod');
const webpack = require('webpack');
const configFactory = require('../config/webpack.config');
const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
@ -44,11 +44,13 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { @@ -44,11 +44,13 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// Process CLI arguments
const argv = process.argv.slice(2);
const writeStatsJson = argv.indexOf('--stats') !== -1;
// We require that you explictly set browsers and do not fall back to
// Generate configuration
const config = configFactory('production');
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
@ -96,7 +98,7 @@ checkBrowsers(paths.appPath, isInteractive) @@ -96,7 +98,7 @@ checkBrowsers(paths.appPath, isInteractive)
console.log();
const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrl;
const publicUrl = paths.publicUrlOrPath;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
@ -108,9 +110,19 @@ checkBrowsers(paths.appPath, isInteractive) @@ -108,9 +110,19 @@ checkBrowsers(paths.appPath, isInteractive)
);
},
err => {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
if (tscCompileOnError) {
console.log(
chalk.yellow(
'Compiled with the following type errors (you may want to check these before deploying your app):\n'
)
);
printBuildError(err);
} else {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
}
}
)
.catch(err => {
@ -124,7 +136,7 @@ checkBrowsers(paths.appPath, isInteractive) @@ -124,7 +136,7 @@ checkBrowsers(paths.appPath, isInteractive)
function build(previousFileSizes) {
console.log('Creating an optimized production build...');
let compiler = webpack(config);
const compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
let messages;
@ -132,8 +144,18 @@ function build(previousFileSizes) { @@ -132,8 +144,18 @@ function build(previousFileSizes) {
if (!err.message) {
return reject(err);
}
let errMessage = err.message;
// Add additional information for postcss errors
if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
errMessage +=
'\nCompileError: Begins at CSS selector ' +
err['postcssNode'].selector;
}
messages = formatWebpackMessages({
errors: [err.message],
errors: [errMessage],
warnings: [],
});
} else {
@ -169,6 +191,7 @@ function build(previousFileSizes) { @@ -169,6 +191,7 @@ function build(previousFileSizes) {
previousFileSizes,
warnings: messages.warnings,
};
if (writeStatsJson) {
return bfj
.write(paths.appBuild + '/bundle-stats.json', stats.toJson())

68
scripts/start.js

@ -16,7 +16,7 @@ require('../config/env'); @@ -16,7 +16,7 @@ require('../config/env');
const fs = require('fs');
const chalk = require('chalk');
const chalk = require('react-dev-utils/chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole');
@ -28,10 +28,14 @@ const { @@ -28,10 +28,14 @@ const {
prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
const semver = require('semver');
const paths = require('../config/paths');
const config = require('../config/webpack.config.dev');
const configFactory = require('../config/webpack.config');
const createDevServerConfig = require('../config/webpackDevServer.config');
const getClientEnvironment = require('../config/env');
const react = require(require.resolve('react', { paths: [paths.appPath] }));
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;
@ -56,12 +60,12 @@ if (process.env.HOST) { @@ -56,12 +60,12 @@ if (process.env.HOST) {
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
);
console.log(
`Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}`
`Learn more here: ${chalk.yellow('https://cra.link/advanced-config')}`
);
console.log();
}
// We require that you explictly set browsers and do not fall back to
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
@ -75,14 +79,43 @@ checkBrowsers(paths.appPath, isInteractive) @@ -75,14 +79,43 @@ checkBrowsers(paths.appPath, isInteractive)
// We have not found a port.
return;
}
const config = configFactory('development');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
const urls = prepareUrls(protocol, HOST, port);
const useTypeScript = fs.existsSync(paths.appTsConfig);
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
const urls = prepareUrls(
protocol,
HOST,
port,
paths.publicUrlOrPath.slice(0, -1)
);
const devSocket = {
warnings: warnings =>
devServer.sockWrite(devServer.sockets, 'warnings', warnings),
errors: errors =>
devServer.sockWrite(devServer.sockets, 'errors', errors),
};
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
const compiler = createCompiler({
appName,
config,
devSocket,
urls,
useYarn,
useTypeScript,
tscCompileOnError,
webpack,
});
// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
const proxyConfig = prepareProxy(
proxySetting,
paths.appPublic,
paths.publicUrlOrPath
);
// Serve webpack assets generated by the compiler over a web server.
const serverConfig = createDevServerConfig(
proxyConfig,
@ -97,16 +130,33 @@ checkBrowsers(paths.appPath, isInteractive) @@ -97,16 +130,33 @@ checkBrowsers(paths.appPath, isInteractive)
if (isInteractive) {
clearConsole();
}
if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) {
console.log(
chalk.yellow(
`Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
)
);
}
console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser);
});
['SIGINT', 'SIGTERM'].forEach(function(sig) {
process.on(sig, function() {
['SIGINT', 'SIGTERM'].forEach(function (sig) {
process.on(sig, function () {
devServer.close();
process.exit();
});
});
if (process.env.CI !== 'true') {
// Gracefully exit when stdin ends
process.stdin.on('end', function () {
devServer.close();
process.exit();
});
}
})
.catch(err => {
if (err && err.message) {

6
scripts/test.js

@ -38,11 +38,11 @@ function isInMercurialRepository() { @@ -38,11 +38,11 @@ function isInMercurialRepository() {
}
}
// Watch unless on CI, in coverage mode, or explicitly running all tests
// Watch unless on CI or explicitly running all tests
if (
!process.env.CI &&
argv.indexOf('--coverage') === -1 &&
argv.indexOf('--watchAll') === -1
argv.indexOf('--watchAll') === -1 &&
argv.indexOf('--watchAll=false') === -1
) {
// https://github.com/facebook/create-react-app/issues/5210
const hasSourceControl = isInGitRepository() || isInMercurialRepository();

43
src/App.tsx

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
import React from 'react';
import { hot } from 'react-hot-loader';
import { Provider } from 'react-redux';
import 'bootstrap/dist/css/bootstrap.css';
import 'antd/dist/antd.css';
import './scss/app.scss';
import { BrowserRouter } from 'react-router-dom';
import store from "@/store";
import { authService } from "@/services/domain";
import { AppRouter } from "@/containers/App/router";
const App = () => {
const handleAutoAuth = async () => {
await authService.autoAuth()
}
React.useEffect(() => {
handleAutoAuth()
}, [])
return (
<Provider store={store}>
{/* <ScrollToTop> */}
<BrowserRouter>
<AppRouter />
</BrowserRouter>
{/* </ScrollToTop> */}
</Provider>
);
}
export default hot(module)(App);
const mobileCheck = () => {
return window['mobilecheck'] = function () {
var check = false;
(function (a) { if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; })(navigator.userAgent || navigator.vendor || window['opera']);
return check;
};
}

7
src/api/account/requests.ts

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
import http from '../http.service'
import { ApiResponse } from '../http.types'
import { IFetchAccountResponse } from './responses.interfaces'
export const fetchAccount = (): ApiResponse<IFetchAccountResponse> => {
return http.get<IFetchAccountResponse>('account', {})
}

4
src/api/account/responses.interfaces.ts

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
import { IUser } from '@/shared';
export interface IFetchAccountResponse extends IUser { }

14
src/api/auth/requests.interfaces.ts

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
export interface ISignInPayload {
login: string
password: string
deviceName: string
}
export interface IRefreshTokenPayload {
refreshToken: string
}
export interface ILogoutPayload {
refreshToken: string
}

28
src/api/auth/requests.ts

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
import http from '../http.service'
import { ApiResponse } from '../http.types'
import * as Req from './requests.interfaces'
import * as Res from './responses.interfaces'
const signIn = (
params: Req.ISignInPayload,
): ApiResponse<Res.IAuthSuccessResponse> => {
return http.post<Res.IAuthSuccessResponse>('auth', params)
}
const sendRefreshToken = (
params: Req.IRefreshTokenPayload,
): ApiResponse<Res.IAuthSuccessResponse> => {
return http.post<Res.IAuthSuccessResponse>('auth/refresh-token', params)
}
const logout = (
params: Req.ILogoutPayload,
): ApiResponse<void> => {
return http.post<void>('auth.logout', params)
}
export const authApi = {
signIn,
sendRefreshToken,
logout
}

4
src/api/auth/responses.interfaces.ts

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
export interface IAuthSuccessResponse {
accessToken: string
refreshToken: string
}

53
src/api/http.service.ts

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import { config } from '@/config'
import { GlobalContainerService } from '../services/system/global-container.service'
import { authService } from "@/services/domain"
// import { authService } from '@/services/domain'
const store = () => GlobalContainerService.get('store')
const axiosInstance = axios.create({
baseURL: config.apiUrl,
headers: {
'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*",
"X-Requested-With": "XMLHttpRequest",
"Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"
},
timeout: 180000,
})
axiosInstance.interceptors.request.use((config: any) => {
const token = store().getState().auth.accessToken
if (token) {
config.headers['authorization'] = `Bearer ${token}`
}
return config
})
const request = async <T>(func: Function): Promise<AxiosResponse<T>> => {
try {
let response = await func()
return response as any as AxiosResponse
} catch (e: any) {
if (e.response.status === 401) {
await authService.refreshSession()
return (await func()) as any as AxiosResponse
}
throw e
}
}
const api = {
get: <T>(url: string, params?: AxiosRequestConfig) => request<T>(() => axiosInstance.get<T>(url, params)),
post: <T>(url: string, data: any, params?: AxiosRequestConfig) => request<T>(() => axiosInstance.post<T>(url, data, params)),
put: <T>(url: string, data: any, params?: AxiosRequestConfig) => request<T>(() => axiosInstance.put<T>(url, data, params)),
patch: <T>(url: string, data: any, params?: AxiosRequestConfig) => request<T>(() => axiosInstance.patch<T>(url, data, params)),
delete: <T>(url: string, params?: AxiosRequestConfig) => request<T>(() => axiosInstance.delete<T>(url, params)),
}
export default api

3
src/api/http.types.ts

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
import { AxiosResponse } from 'axios'
export type ApiResponse<T> = Promise<AxiosResponse<T>>

2
src/api/index.ts

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
export * from './auth/requests'
export * from './account/requests'

BIN
src/assets/img/add_file.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

BIN
src/assets/img/ava.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

16
src/assets/img/burger.svg

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="14px" viewBox="0 0 16 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 51.1 (57501) - http://www.bohemiancoding.com/sketch -->
<title>Group 10</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="header" transform="translate(-20.000000, -22.000000)" fill="#B1C3C8">
<g id="Group-10" transform="translate(20.000000, 22.000000)">
<rect id="Rectangle" x="0" y="0" width="16" height="2" rx="1"></rect>
<rect id="Rectangle-Copy" x="0" y="6" width="16" height="2" rx="1"></rect>
<rect id="Rectangle-Copy-3" x="0" y="12" width="16" height="2" rx="1"></rect>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 910 B

BIN
src/assets/img/default-avatar.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
src/assets/img/default-img.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
src/assets/img/document/DOCX.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src/assets/img/document/JPEG.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
src/assets/img/document/PDF.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/img/document/SVG.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/img/document/XLSX.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src/assets/img/document/doc.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/img/document/document.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

BIN
src/assets/img/document/jpg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/img/document/png.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/img/document/tif.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/img/document/txt.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/img/document/webp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/img/document/xls.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src/assets/img/group_icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
src/assets/img/logo_task.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/assets/img/logo_task2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
src/assets/img/send.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 B

45
src/components/Fields/TreeSelectField.jsx

@ -2,9 +2,10 @@ import React, { Component } from 'react' @@ -2,9 +2,10 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { TreeSelect } from 'antd';
import './TreeSelectField.scss'
const TreeNode = TreeSelect.TreeNode;
import './SelectField.scss';
const TreeNode = TreeSelect.TreeNode;
class TreeSelectField extends Component {
constructor(props) {
super(props)
@ -35,39 +36,39 @@ class TreeSelectField extends Component { @@ -35,39 +36,39 @@ class TreeSelectField extends Component {
}
renderTree = (tree) => {
let {value_field} = this.props;
if(Array.isArray(tree)){
return tree.map(item => <TreeNode value={item[value_field || "id"]} title={item.name} key={item.id}>
let { value_field } = this.props;
if (Array.isArray(tree)) {
return tree.map(item => <TreeNode value={item[value_field || "id"]} title={item.name} key={item.id}>
{item.children ? this.renderTree(item.children) : null}
</TreeNode>)
}
}
render() {
let {input, placeholder, value_field, multiple, tree, label, meta: { touched, error }} = this.props;
let { input, placeholder, value_field, multiple, tree, label, meta: { touched, error } } = this.props;
return (
<div className="form__form-group tree-select-field">
{label ? <span className="form__form-group-label">{label}</span> : null}
<div className="form__form-group-field">
<div className="form__form-group-input-wrap">
<div style={{position: 'relative'}}>
<TreeSelect
showSearch
style={{ width: '100%' }}
value={input.value}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
placeholder={placeholder}
allowClear
onChange={this.onChange}
onSearch={(value) => {
}}
multiple={multiple}
>
{this.renderTree(tree)}
</TreeSelect>
{!input.value || !input.value.length ? <span className="ant-select-arrow" style={{outline: 'currentcolor none medium'}}>
<div style={{ position: 'relative' }}>
<TreeSelect
showSearch
style={{ width: '100%' }}
value={input.value}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
placeholder={placeholder}
allowClear
onChange={this.onChange}
onSearch={(value) => {
}}
multiple={multiple}
>
{this.renderTree(tree)}
</TreeSelect>
{!input.value || !input.value.length ? <span className="ant-select-arrow" style={{ outline: 'currentcolor none medium' }}>
<i aria-label="icon: down" className="anticon anticon-down ant-select-arrow-icon">
<svg viewBox="64 64 896 896" className="" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg>
</i>

18
src/config/index.js

@ -1,6 +1,12 @@ @@ -1,6 +1,12 @@
export default {
// apiUrl: 'http://c3ac9e7a.ngrok.io',
// apiUrl: 'http://localhost:1337',
// apiUrl: 'https://api-tasks.ubg.ua',
// apiUrl: 'http://api.ls',
};
export const config = {
apiUrl: 'http://localhost:3000/admin',
socketUrl: 'http://localhost:3000',
}
export default config
// apiUrl: 'http://c3ac9e7a.ngrok.io',
// apiUrl: 'http://localhost:1337',
// apiUrl: 'https://api-tasks.ubg.ua',
// apiUrl: 'http://api.ls',

20
src/config/rest-client.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
var restClient = require('../lib/rest-client');
import config from './index';
import moment from 'moment';
var restClient = require('../lib/rest-client');
function checkHttpStatus(response) {
if (response.status >= 200 && response.status < 300) {
@ -15,16 +15,16 @@ function checkHttpStatus(response) { @@ -15,16 +15,16 @@ function checkHttpStatus(response) {
return Promise.reject(res)
});
return Promise.reject(response)
return Promise.reject(response)
// throw error;
}
}
function makeRequest({url, method, headers, bodyJSObject}) {
function makeRequest({ url, method, headers, bodyJSObject }) {
let header_tmp = Object.assign({}, this.headers, headers);
let body = bodyJSObject ? JSON.stringify(bodyJSObject) : undefined;
if(header_tmp.hasOwnProperty('Content-Type') && header_tmp['Content-Type'] == 'multipart/form-data'){
if (header_tmp.hasOwnProperty('Content-Type') && header_tmp['Content-Type'] == 'multipart/form-data') {
body = bodyJSObject;
delete header_tmp['Accept'];
delete header_tmp['Content-Type'];
@ -36,15 +36,15 @@ function makeRequest({url, method, headers, bodyJSObject}) { @@ -36,15 +36,15 @@ function makeRequest({url, method, headers, bodyJSObject}) {
body: body,
mode: 'cors'
}).then(checkHttpStatus).then(response => {
return response.json();
});
return response.json();
});
}
let token = '';
if(localStorage.getItem('remember_me') && localStorage.getItem('remember_me') == 'yes'){
if (localStorage.getItem('remember_me') && localStorage.getItem('remember_me') == 'yes') {
token = localStorage.getItem('token')
} else if(localStorage.getItem('expiret_date') && moment(localStorage.getItem('expiret_date')) > moment()){
} else if (localStorage.getItem('expiret_date') && moment(localStorage.getItem('expiret_date')) > moment()) {
token = localStorage.getItem('token');
} else {
token = ''
@ -52,7 +52,7 @@ if(localStorage.getItem('remember_me') && localStorage.getItem('remember_me') == @@ -52,7 +52,7 @@ if(localStorage.getItem('remember_me') && localStorage.getItem('remember_me') ==
restClient.config({
baseUrl: config.apiUrl + '/api',
baseUrl: config.apiUrl + '/api',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',

43
src/containers/App/App.jsx

@ -1,43 +0,0 @@ @@ -1,43 +0,0 @@
import React, { Component, Fragment } from 'react';
// eslint-disable-next-line import/no-extraneous-dependencies
import { hot } from 'react-hot-loader';
import { Provider } from 'react-redux';
// import { BrowserRouter } from 'react-router-dom';
import { Router as BrowserRouter } from 'react-router';
import 'bootstrap/dist/css/bootstrap.css';
import 'antd/dist/antd.css';
import '../../scss/app.scss';
import RouterComponent from './RouterComponent';
import store from './store';
import ScrollToTop from './ScrollToTop';
import history from '../../lib/histoty';
import { Route, Switch } from 'react-router-dom';
class App extends Component {
constructor(props) {
super(props)
window.mobilecheck = function() {
var check = false;
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
return check;
};
}
render() {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<ScrollToTop>
<Switch>
<Route path="/" component={RouterComponent} />
</Switch>
</ScrollToTop>
</BrowserRouter>
</Provider>
);
}
}
export default hot(module)(App);

27
src/containers/App/MainWrapper.jsx

@ -1,27 +0,0 @@ @@ -1,27 +0,0 @@
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { ThemeProps } from '../../shared/prop-types/ReducerProps';
class MainWrapper extends PureComponent {
static propTypes = {
theme: ThemeProps.isRequired,
children: PropTypes.element.isRequired,
};
render() {
const { theme, children } = this.props;
return (
<div className={theme.className}>
<div className="wrapper">
{children}
</div>
</div>
);
}
}
export default connect(state => ({
theme: state.theme,
}))(MainWrapper);

58
src/containers/App/Router.jsx

@ -1,58 +0,0 @@ @@ -1,58 +0,0 @@
import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import Layout from '../Layout/index';
import MainWrapper from './MainWrapper';
import LogIn from '../LogIn/index';
import ExamplePageOne from '../Example/index';
import ExamplePageTwo from '../ExampleTwo/index';
import Factory from '../Factory';
import User from '../User';
import Forgot from '../Forgot';
import Reset from '../Reset';
import Profile from '../Profile';
import GroupPermission from '../GroupPermissions';
import GroupPermissionView from '../GroupPermissions/view';
import Task from '../Task';
import Taxonomy from '../Taxonomy';
const Pages = () => (
<Switch>
<Route path="/pages/one" component={ExamplePageOne} />
<Route path="/pages/two" component={ExamplePageTwo} />
</Switch>
);
const wrappedRoutes = () => (
<div>
<Layout />
<div className="container__wrap">
<Route exact path="/" component={() => <Redirect to="/tasks" />} />
<Route path="/pages" component={Pages} />
<Route exact path="/factory" component={Factory} />
<Route exact path="/user" component={User} />
<Route exact path="/profile/:id" component={Profile} />
<Route exact path="/group_permission" component={GroupPermission} />
<Route exact path="/group_permission/:id" component={GroupPermissionView} />
<Route exact path="/tasks" component={Task} />
<Route exact path="/taxonomy" component={Taxonomy} />
<Route exact path="/taxonomy/:type" component={Taxonomy} />
</div>
</div>
);
const Router = () => (
<MainWrapper>
<main>
<Switch>
<Route exact path="/login" component={LogIn} />
<Route exact path="/forgot" component={Forgot} />
<Route exact path="/reset/:key" component={Reset} />
{/* <Route path="/" component={wrappedRoutes} /> */}
</Switch>
</main>
</MainWrapper>
);
export default Router;

79
src/containers/App/RouterComponent.js

@ -1,79 +0,0 @@ @@ -1,79 +0,0 @@
import React, { Component, Fragment } from 'react'
import { connect } from 'react-redux'
import { me } from './actions'
import Router from './Router';
import { disableRouter } from '../../lib/helper';
// import Notification from '../../components/Notification/Notification';
// import Socket from './Socket';
class RouterComponent extends Component {
constructor(props) {
super(props)
}
componentWillMount() {
this.props.me();
}
componentDidMount() {
}
componentWillReceiveProps(nextProps) {
if (nextProps.is_load) {
if (nextProps.is_logined) {
if (!disableRouter(['/login', '/forgot', '/reset/{*}'], nextProps.location.pathname) && nextProps.location.pathname != '/') {
nextProps.history.push('/');
}
} else {
if (nextProps.location.pathname == '/' || disableRouter(['/login', '/forgot', '/reset/{*}'], nextProps.location.pathname)) {
nextProps.history.push('/login');
}
}
}
}
componentWillUnmount() {
}
render() {
let { is_load } = this.props;
return (
<Fragment>
{/* <Notification/> */}
{/* <Socket/> */}
{!is_load ? <div className={`load${is_load ? '' : ' loaded'}`}>
<div className="load__icon-wrap">
<svg className="load__icon">
<path fill="#3498db" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
</svg>
</div>
</div> : null}
<div>
{is_load ? <Router /> : null}
</div>
</Fragment>
)
}
}
RouterComponent.propTypes = {
}
const mapStateToProps = (state, ownProps) => ({
is_load: state.auth.is_load,
is_logined: state.auth.is_logined,
})
const mapDispatchToProps = {
me
}
export default connect(mapStateToProps, mapDispatchToProps)(RouterComponent)
// export default RouterComponent

31
src/containers/App/reducers.js

@ -1,31 +0,0 @@ @@ -1,31 +0,0 @@
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import { sidebarReducer, themeReducer } from '../../redux/reducers/index';
import authReducer from '../../redux/reducers/authReducer';
import userNotificationReducer from '../../redux/reducers/userNotificationReducer';
import errorReducer from '../../redux/reducers/errorReducer';
import factoryReducer from '../Factory/reducer';
import userReducer from '../User/reducer';
import notificationReducer from '../../redux/reducers/notificationReducer';
import profileReducer from '../Profile/reducer';
import groupReducer from '../GroupPermissions/reducer';
import taskReducer from '../Task/reducer';
import taxonomyReducer from '../Taxonomy/reducer';
const objReducers = {
form: formReducer, // mounted under "form",
theme: themeReducer,
sidebar: sidebarReducer,
auth: authReducer,
factory: factoryReducer,
user: userReducer,
profile: profileReducer,
error: errorReducer,
notification: notificationReducer,
group_permission: groupReducer,
task: taskReducer,
taxonomy: taxonomyReducer,
user_notification: userNotificationReducer,
};
export default combineReducers(objReducers);

57
src/containers/App/router/Router.tsx

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
import React, { Fragment, useEffect } from 'react';
import { Route, Switch, Redirect, BrowserRouter, useHistory } from 'react-router-dom';
import { Loader, MainWrapper } from "@/shared";
import { useSelector } from "react-redux";
import { getThemeColor, sharedIsLoading } from "@/store/shared";
import { getAccessToken } from '@/store/auth';
import { RouteEnum } from "./routes.enum";
import { routesConfig } from './routes.config';
import Layout from "@/containers/Layout";
export const AppRouter = () => {
const isLoading = useSelector(sharedIsLoading)
const themeColor = useSelector(getThemeColor)
const accessToken = useSelector(getAccessToken)
if (isLoading) return <Loader />
const modules = () => {
if (!accessToken) return (
<>
{routesConfig.authRoutes.map((item) => {
return <Route path={item.path} component={item.component} />
})}
<Route component={() => <Redirect to={RouteEnum.Login} />} />
</>
)
return (
<div>
<Layout />
<div className="container__wrap">
{routesConfig.privateRoutes.map((item) => {
return <Route path={item.path} component={item.component} />
})}
<Route component={() => <Redirect to={RouteEnum.Tasks} />} />
</div>
</div>
)
}
return (
<Fragment>
{/* <Notification/> */}
{/* <Socket/> */}
<MainWrapper theme={themeColor}>
<main>
<Switch>
{modules()}
</Switch>
</main>
</MainWrapper>
</Fragment>
)
}

2
src/containers/App/router/index.ts

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
export * from './Router'
export * from './routes.enum'

77
src/containers/App/router/routes.config.ts

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
import { SignIn } from "@/containers/auth"
import Task from "@/containers/Task"
import { RouteEnum } from "./routes.enum"
const authRoutes = [
{
path: RouteEnum.Login,
component: SignIn
},
// {
// path: RouteEnum.Forgot,
// component: Forgot
// },
// {
// path: RouteEnum.Reset,
// component: Reset
// },
]
const privateRoutes = [
{
path: RouteEnum.Tasks,
component: Task
},
// {
// path: RouteEnum.Pages,
// component: Pages
// },
// {
// path: RouteEnum.Factory,
// component: Factory
// },
// {
// path: RouteEnum.User,
// component: User
// },
// {
// path: RouteEnum.Profile,
// component: Profile
// },
// {
// path: RouteEnum.GroupPermission,
// component: GroupPermission
// },
// {
// path: RouteEnum.GroupPermissionDetail,
// component: GroupPermissionView
// },
// {
// path: RouteEnum.Taxonomy,
// component: Taxonomy
// },
// {
// path: RouteEnum.TaxonomyType,
// component: Taxonomy
// },
// {
// path: '/pages.one',
// component: ExamplePageOne
// },
// {
// path: '/pages/two',
// component: ExamplePageTwo
// },
]
export const routesConfig = {
authRoutes,
privateRoutes,
}

15
src/containers/App/router/routes.enum.ts

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
export enum RouteEnum {
Login = "/login",
Forgot = "/forgot",
Reset = "/reset/:key",
Pages = "/pages",
Factory = "/factory",
User = "/user",
Profile = "/profile/:id",
GroupPermission = "/group_permission",
GroupPermissionDetail = "/group_permission/:id",
Tasks = "/tasks",
Taxonomy = "/taxonomy",
TaxonomyType = "/taxonomy/:type",
}

13
src/containers/App/store.js

@ -1,13 +0,0 @@ @@ -1,13 +0,0 @@
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers';
// eslint-disable-next-line no-underscore-dangle
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducers,
{},
composeEnhancers(applyMiddleware(thunk)),
);
export default store;

40
src/containers/LogIn/actions.js

@ -1,40 +0,0 @@ @@ -1,40 +0,0 @@
import restClient from '../../config/rest-client';
import processError from '../../lib/error';
import { getUsersNotification } from '../Layout/topbar/TopbarNotification/actions';
import moment from 'moment';
export function login(data) {
let remember_me = data.remember_me;
delete data.remember_me;
return (dispatch) => {
return restClient.post('auth', 'login', data).then((result) => {
if(remember_me){
localStorage.setItem('remember_me', 'yes');
} else {
localStorage.setItem('expiret_date', moment().add(1, 'days').toString());
}
localStorage.setItem('token', result.token);
localStorage.setItem('tmp_token', result.token);
dispatch({type: 'SET_TOKEN', token: result.token})
restClient.config({
headers: {
'Accept': 'application/json',
'Authorization': result.token ? 'Bearer ' + result.token : undefined,
'Content-Type': 'application/json'
}
});
dispatch({ type: 'GET_PROFILE', data: result.user });
dispatch(getUsersNotification())
return null;
}).catch(err => {
return processError(err, dispatch);
});
};
}

68
src/containers/LogIn/components/LogInForm.jsx

@ -1,68 +0,0 @@ @@ -1,68 +0,0 @@
import React, { PureComponent } from 'react';
import { Field, reduxForm } from 'redux-form';
import EyeIcon from 'mdi-react/EyeIcon';
import KeyVariantIcon from 'mdi-react/KeyVariantIcon';
import AccountOutlineIcon from 'mdi-react/AccountOutlineIcon';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import renderCheckBoxField from '../../../shared/components/form/CheckBox';
import LoginField from './fields/LoginField';
import PasswordField from './fields/PasswordField';
import validationFields from '../../../lib/validate'
import { connect } from 'react-redux'
import { login } from '../actions';
class LogInForm extends PureComponent {
constructor() {
super();
}
submit = (data) => {
this.props.login(data);
}
render() {
const { handleSubmit } = this.props;
return (
<form className="form" onSubmit={handleSubmit(this.submit)}>
<Field name='login' component={LoginField} />
<Field name='password' component={PasswordField} />
<div className="form__form-group">
<div className="form__form-group-field">
<Field
name="remember_me"
component={renderCheckBoxField}
label="Запамятати"
/>
</div>
</div>
<button className="btn btn-primary account__btn account__btn--small">Вхід</button>
</form>
);
}
}
const mapStateToProps = (state, ownProps) => ({
})
const mapDispatchToProps = {
login
}
LogInForm = reduxForm({
form: 'log_in_form',
validate: (values) => {
let fields = [
{name: 'login', rules: [{name: 'required', message: 'Заповнити обовязково'}]},
{name: 'password', rules: [{name: 'required', message: 'Заповнити обовязково'}]},
]
return validationFields(fields, values)
}
})(LogInForm);
export default connect(mapStateToProps, mapDispatchToProps)(LogInForm)

48
src/containers/LogIn/components/fields/LoginField.jsx

@ -1,48 +0,0 @@ @@ -1,48 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import AccountOutlineIcon from 'mdi-react/AccountOutlineIcon';
class LoginField extends Component {
constructor(props) {
super(props)
}
componentWillMount() {
}
componentDidMount() {
}
componentWillReceiveProps(nextProps) {
}
componentWillUnmount() {
}
render() {
const { input, label, type, placeholder, className, meta: { touched, error, warning } } = this.props;
return (
<div className="form__form-group">
<span className="form__form-group-label">Логін</span>
<div className="form__form-group-field">
<div className="form__form-group-icon">
<AccountOutlineIcon />
</div>
<input name="login" placeholder="Логін" type="text" onChange={input.onChange} onBlur={input.onBlur} />
</div>
{touched && ((error && <span className="form__form-group-error">{error}</span>))}
</div>
)
}
}
LoginField.propTypes = {
}
export default LoginField

80
src/containers/LogIn/components/fields/PasswordField.jsx

@ -1,80 +0,0 @@ @@ -1,80 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import KeyVariantIcon from 'mdi-react/KeyVariantIcon';
import EyeIcon from 'mdi-react/EyeIcon';
import { Link } from 'react-router-dom'
class PasswordField extends Component {
constructor(props) {
super(props)
this.state = {
showPassword: false
}
}
componentWillMount() {
}
componentDidMount() {
}
componentWillReceiveProps(nextProps) {
}
componentWillUnmount() {
}
render() {
let {showPassword} = this.state;
const { input, label, type, placeholder, className, meta: { touched, error, warning } } = this.props;
return (
<div className="form__form-group">
<span className="form__form-group-label">Пароль</span>
<div className="form__form-group-field">
<div className="form__form-group-icon">
<KeyVariantIcon />
</div>
<input
name="password"
placeholder="Пароль"
type={showPassword ? 'text' : 'password'}
onChange={input.onChange}
onBlur={input.onBlur}
/>
{/* <Field
name="password"
component="input"
type={showPassword ? 'text' : 'password'}
placeholder="Пароль"
/> */}
<button
className={`form__form-group-button${showPassword ? ' active' : ''}`}
onClick={e => {this.setState({showPassword: !this.state.showPassword})}}
type="button"
><EyeIcon />
</button>
</div>
{/* <div className="account__forgot-password">
<a href="/">Відновити пароль?</a>
</div> */}
{touched && ((error && <span className="form__form-group-error">{error}</span>))}
<div className="account__forgot-password">
<Link to="/forgot">Забули пароль?</Link>
</div>
</div>
)
}
}
PasswordField.propTypes = {
}
export default PasswordField

25
src/containers/LogIn/index.jsx

@ -1,25 +0,0 @@ @@ -1,25 +0,0 @@
import React from 'react';
import LogInForm from './components/LogInForm';
const LogIn = () => (
<div className="account">
<div className="account__wrapper">
<div className="account__card">
<div className="account__head" style={{border: 'none', paddingLeft: 0}}>
<h3 className="account__title">
<span className="account__logo">
<img style={{width: '140px', marginLeft: '-5px'}} src="/img/logo_task2.png" />
</span>
</h3>
<h4 className="account__subhead subhead"></h4>
</div>
<LogInForm onSubmit />
</div>
</div>
</div>
);
export default LogIn;
// if you want to add select, date-picker and time-picker in your app you need to uncomment the first
// four lines in /scss/components/form.scss to add styles

95
src/containers/auth/components/SignInForm.tsx

@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
import React from 'react';
import { Controller, useForm } from 'react-hook-form';
import { PasswordField, LoginField } from "./fields";
import { authService } from "@/services/domain";
import { CheckBoxForm } from "@/shared/components";
interface IProps {
}
interface ISignInForm {
login: string
password: string
rememberMe: boolean
}
export const SignInForm = (props: IProps) => {
const { register, handleSubmit, watch, control, formState: { errors } } = useForm<ISignInForm>();
const onSubmit = (data: ISignInForm) => {
authService.signIn(data)
}
return (
<form className="form" onSubmit={handleSubmit(onSubmit)}>
<LoginField
register={register(
'login',
{ required: 'Заповнити обовязково' }
)}
error={errors?.login?.message}
/>
<PasswordField
register={register(
'password',
{ required: 'Заповнити обовязково' }
)}
error={errors?.password?.message}
/>
<div className="form__form-group">
<div className="form__form-group-field">
<Controller
control={control}
name={'rememberMe'}
defaultValue={false}
render={({ field }) => <CheckBoxForm label={'Запамятати'} field={field} />}
/>
</div>
</div>
<button className="btn btn-primary account__btn account__btn--small">Вхід</button>
</form>
)
}
// class LogInFormwe extends PureComponent {
// // constructor() {
// // // super();
// // }
// render() {
// return (
// );
// }
// }
// const mapStateToProps = (state, ownProps) => ({
// })
// const mapDispatchToProps = {
// login
// }
// LogInForm = reduxForm({
// form: 'log_in_form',
// validate: (values) => {
// let fields = [
// { name: 'login', rules: [{ name: 'required', message: 'Заповнити обовязково' }] },
// { name: 'password', rules: [{ name: 'required', message: 'Заповнити обовязково' }] },
// ]
// return validationFields(fields, values)
// }
// })(LogInForm);
// export default connect(mapStateToProps, mapDispatchToProps)(LogInForm)

39
src/containers/auth/components/fields/LoginField.tsx

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
import React, { Component, CSSProperties } from 'react'
import { UseFormRegisterReturn } from 'react-hook-form';
import AccountOutlineIcon from 'mdi-react/AccountOutlineIcon';
interface IProps {
onChange?: any
onBlur?: any
value?: string
error?: string
register: UseFormRegisterReturn
}
export const LoginField = (props: IProps) => {
return (
<div className="form__form-group">
<span className="form__form-group-label">Логін</span>
<div className="form__form-group-field">
<div className="form__form-group-icon">
<AccountOutlineIcon />
</div>
<input
name="login"
placeholder="Логін"
type="text"
onChange={props.onChange}
onBlur={props.onBlur}
{...props.register}
/>
</div>
{props?.error && <span className="form__form-group-error">{props.error}</span>}
</div>
)
}

48
src/containers/auth/components/fields/PasswordField.tsx

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
import React, { Component, useState } from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'
import { UseFormRegisterReturn } from 'react-hook-form';
import KeyVariantIcon from 'mdi-react/KeyVariantIcon';
import EyeIcon from 'mdi-react/EyeIcon';
interface IProps {
onChange?: any
onBlur?: any
value?: string
error?: string
register: UseFormRegisterReturn
}
export const PasswordField = (props: IProps) => {
const [showPassword, setShowPassword] = useState<boolean>(false)
return (
<div className="form__form-group">
<span className="form__form-group-label">Пароль</span>
<div className="form__form-group-field">
<div className="form__form-group-icon">
<KeyVariantIcon />
</div>
<input
name="password"
placeholder="Пароль"
type={showPassword ? 'text' : 'password'}
onChange={props.onChange}
onBlur={props.onBlur}
{...props.register}
/>
<button
className={`form__form-group-button${showPassword ? ' active' : ''}`}
onClick={() => setShowPassword((prev) => !prev)}
type="button"
>
<EyeIcon />
</button>
</div>
{props.error && <span className="form__form-group-error">{props.error}</span>}
<div className="account__forgot-password">
<Link to="/forgot">Забули пароль?</Link>
</div>
</div>
)
}

2
src/containers/auth/components/fields/index.ts

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
export * from './LoginField'
export * from './PasswordField'

2
src/containers/auth/components/index.ts

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
export * from './fields'
export * from './SignInForm'

1
src/containers/auth/index.ts

@ -0,0 +1 @@ @@ -0,0 +1 @@
export * from './screens'

1
src/containers/auth/screens/index.ts

@ -0,0 +1 @@ @@ -0,0 +1 @@
export * from './sign-in.screen'

27
src/containers/auth/screens/sign-in.screen.tsx

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
import React from 'react';
import { SignInForm } from "../components";
import logo from '@/assets/img/logo_task2.png'
interface IProps { }
export const SignIn = (props: IProps) => {
return (
<div className="account">
<div className="account__wrapper">
<div className="account__card">
<div className="account__head" style={{ border: 'none', paddingLeft: 0 }}>
<h3 className="account__title">
<span className="account__logo">
<img style={{ width: '140px', marginLeft: '-5px' }} src={logo} />
</span>
</h3>
<h4 className="account__subhead subhead"></h4>
</div>
<SignInForm />
</div>
</div>
</div>
)
}

15
src/index.tsx

@ -1,8 +1,13 @@ @@ -1,8 +1,13 @@
import React from 'react';
import ReactDOM from "react-dom";
import App from './containers/App/App';
import ReactDOM from 'react-dom';
import App from "./App";
ReactDOM.render(
<App />,
document.getElementById('root'),
);
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
)

71
src/react-app-env.d.ts vendored

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
/// <reference types="node" />
/// <reference types="react" />
/// <reference types="react-dom" />
declare namespace NodeJS {
interface ProcessEnv {
readonly NODE_ENV: 'development' | 'production' | 'test';
readonly PUBLIC_URL: string;
}
}
declare module '*.avif' {
const src: string;
export default src;
}
declare module '*.bmp' {
const src: string;
export default src;
}
declare module '*.gif' {
const src: string;
export default src;
}
declare module '*.jpg' {
const src: string;
export default src;
}
declare module '*.jpeg' {
const src: string;
export default src;
}
declare module '*.png' {
const src: string;
export default src;
}
declare module '*.webp' {
const src: string;
export default src;
}
declare module '*.svg' {
import * as React from 'react';
export const ReactComponent: React.FunctionComponent<React.SVGProps<
SVGSVGElement
> & { title?: string }>;
const src: string;
export default src;
}
declare module '*.module.css' {
const classes: { readonly [key: string]: string };
export default classes;
}
declare module '*.module.scss' {
const classes: { readonly [key: string]: string };
export default classes;
}
declare module '*.module.sass' {
const classes: { readonly [key: string]: string };
export default classes;
}

31
src/redux/reducers.js

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import { sidebarReducer, themeReducer } from './reducers/index';
import authReducer from './reducers/authReducer';
import userNotificationReducer from './reducers/userNotificationReducer';
import errorReducer from './reducers/errorReducer';
import factoryReducer from '../containers/Factory/reducer';
import userReducer from '../containers/User/reducer';
import notificationReducer from './reducers/notificationReducer';
import profileReducer from '../containers/Profile/reducer';
import groupReducer from '../containers/GroupPermissions/reducer';
import taskReducer from '../containers/Task/reducer';
import taxonomyReducer from '../containers/Taxonomy/reducer';
const objReducers = {
form: formReducer, // mounted under "form",
theme: themeReducer,
sidebar: sidebarReducer,
auth: authReducer,
factory: factoryReducer,
user: userReducer,
profile: profileReducer,
error: errorReducer,
notification: notificationReducer,
group_permission: groupReducer,
task: taskReducer,
taxonomy: taxonomyReducer,
user_notification: userNotificationReducer,
};
export default combineReducers(objReducers);

2
src/scss/containers/account.scss

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
}
&.account--photo {
background: url(../shared/img/404/bg_404.png) no-repeat center;
background: url(../../shared/img/404/bg_404.png) no-repeat center;
background-size: cover;
.account__card {

2
src/scss/containers/not-found.scss

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
height: 100vh;
overflow: auto;
display: flex;
background: url(../shared/img/404/bg_404.png) no-repeat center;
background: url(../../shared/img/404/bg_404.png) no-repeat center;
background-size: cover;
button {

4
src/scss/settings/variable.scss

@ -4,7 +4,7 @@ $themes: ( @@ -4,7 +4,7 @@ $themes: (
colorBackgroundBody: #f2f4f7,
colorText: #646777,
colorTextAdditional: #646777,
logoImg: url(/img/logo_task2.png),
logoImg: url(../../assets/img/logo_task2.png),
colorHover: #fafbfe,
colorFolderHover: #f0eeee,
colorBorder: #eff1f5,
@ -21,7 +21,7 @@ $themes: ( @@ -21,7 +21,7 @@ $themes: (
colorBackgroundBody: #2a2a31,
colorText: #dddddd,
colorTextAdditional: #999999,
logoImg: url(../shared/img/logo/logo_dark.svg),
logoImg: url(../../shared/img/logo/logo_dark.svg),
colorHover: #38373f,
colorFolderHover: #ffffff1A,
colorBorder: #333246,

19
src/services/domain/account.service.ts

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
import { simpleDispatch } from '@/store/store-helpers'
import { SetLoadingAccount, SaveAccount } from '@/store/account'
import { fetchAccount } from '@/api'
const getAccount = async () => {
simpleDispatch(new SetLoadingAccount({ isLoading: true }))
try {
const { data } = await fetchAccount()
simpleDispatch(new SaveAccount({ account: data }))
} catch (e) {
console.log(e)
} finally {
simpleDispatch(new SetLoadingAccount({ isLoading: false }))
}
}
export const accountService = {
getAccount,
}

103
src/services/domain/auth.service.ts

@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
import { authApi } from "@/api"
import { ITokenPair, StorageKey } from "@/shared"
import { ResetAccount } from "@/store/account"
import { ResetTokens, SaveTokens } from "@/store/auth"
import { isLoading, Reset } from "@/store/shared"
import { simpleDispatch } from "@/store/store-helpers"
import moment from "moment"
import { GlobalContainerService } from "../system"
import { accountService } from "./account.service"
interface ISignIn {
login: string
password: string
rememberMe: boolean
}
const signIn = async (payload: ISignIn) => {
simpleDispatch(new isLoading({ isLoading: true }))
try {
const { data } = await authApi.signIn({ ...payload, deviceName: 'web' })
if (data) await _saveTokens(data)
await accountService.getAccount()
if (payload.rememberMe) {
localStorage.setItem(StorageKey.RememberMe, "true");
} else {
localStorage.setItem(StorageKey.ExpiryDate, moment().add(1, 'day').toString());
}
} catch (e) {
console.log('AUTH ERROR', e)
} finally {
simpleDispatch(new isLoading({ isLoading: false }))
}
}
const autoAuth = async () => {
try {
const existTokens = await _getTokensFromStorage()
if (!existTokens.refreshToken) throw new Error()
if (localStorage.getItem(StorageKey.RememberMe) && localStorage.getItem(StorageKey.RememberMe) === 'true') {
await refreshSession(existTokens.refreshToken)
} else if (localStorage.getItem(StorageKey.ExpiryDate) && moment(localStorage.getItem(StorageKey.ExpiryDate)) > moment()) {
await refreshSession(existTokens.refreshToken)
}
} catch (e) {
console.log('AUTO AUTH ERROR', e)
}
}
const logout = async (refreshToken: string) => {
try {
await authApi.logout({ refreshToken })
await _resetTokens()
simpleDispatch(new ResetAccount())
simpleDispatch(new Reset())
} catch (e) {
console.log('LOGOUT ERROR', e)
await _resetTokens()
}
}
const refreshSession = async (refreshToken?: string) => {
let token = refreshToken
if (!token) {
const existTokens = await _getTokensFromStorage()
token = existTokens?.refreshToken
}
if (!token) return
const { data } = await authApi.sendRefreshToken({
refreshToken: token,
})
await _saveTokens(data)
await accountService.getAccount()
}
const _resetTokens = async () => {
await localStorage.clear()
simpleDispatch(new ResetTokens())
}
const _getTokensFromStorage = async () => {
const accessToken = await GlobalContainerService.get('store').getState().auth.accessToken
const refreshToken = await localStorage.getItem(StorageKey.RefreshToken)
return { accessToken, refreshToken }
}
const _saveTokens = async (tokens: ITokenPair) => {
simpleDispatch(new SaveTokens(tokens))
await localStorage.setItem(StorageKey.RefreshToken, tokens.refreshToken)
}
export const authService = {
signIn,
autoAuth,
refreshSession,
logout,
}

1
src/services/domain/index.ts

@ -0,0 +1 @@ @@ -0,0 +1 @@
export * from './auth.service'

21
src/services/system/global-container.service.ts

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
interface IGlobalData {
store?: any
internetConnect?: boolean
}
const globalData: IGlobalData = {
store: null,
internetConnect: true,
}
const set = (key: string, value: any) => {
globalData[key] = value
}
const get = (key: string) => globalData[key]
export const GlobalContainerService = {
set,
get,
}

1
src/services/system/index.ts

@ -0,0 +1 @@ @@ -0,0 +1 @@
export * from './global-container.service'

1
src/shared/components/elements/index.ts

@ -0,0 +1 @@ @@ -0,0 +1 @@
export * from './loader.component'

13
src/shared/components/elements/loader.component.tsx

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
import React from "react"
export const Loader = () => {
return (
<div className={'load loaded'} >
<div className="load__icon-wrap" >
<svg className="load__icon" >
<path fill="#3498db" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
</svg>
</div>
</div>
)
}

123
src/shared/components/form/CheckBox.jsx

@ -1,123 +0,0 @@ @@ -1,123 +0,0 @@
/* eslint-disable jsx-a11y/label-has-for */
import React, { PureComponent } from 'react';
import CheckIcon from 'mdi-react/CheckIcon';
import CloseIcon from 'mdi-react/CloseIcon';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Checkbox } from 'antd';
class CheckBoxField extends PureComponent {
static propTypes = {
onChange: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.bool,
]).isRequired,
label: PropTypes.string,
defaultChecked: PropTypes.bool,
disabled: PropTypes.bool,
className: PropTypes.string,
color: PropTypes.string,
};
static defaultProps = {
label: '',
defaultChecked: false,
disabled: false,
className: '',
color: '',
};
componentDidMount() {
const { onChange, defaultChecked } = this.props;
onChange(defaultChecked);
}
render() {
const {
disabled, className, name, value, onChange, label, color,
} = this.props;
const CheckboxClass = classNames({
'checkbox-btn': true,
disabled,
});
return (
<label
className={`${CheckboxClass} ${className ? ` checkbox-btn--${className}` : ''}`}
htmlFor={name}
>
<input
className="checkbox-btn__checkbox"
type="checkbox"
id={name}
name={name}
onChange={onChange}
checked={value}
disabled={disabled}
/>
<span
className="checkbox-btn__checkbox-custom"
style={color ? { background: color, borderColor: color } : {}}
>
<CheckIcon />
</span>
{className === 'button'
? (
<span className="checkbox-btn__label-svg">
<CheckIcon className="checkbox-btn__label-check" />
<CloseIcon className="checkbox-btn__label-uncheck" />
</span>
) : ''}
<span className="checkbox-btn__label">
{label}
</span>
</label>
);
}
}
const renderCheckBoxField = (props) => {
const {
input, label, defaultChecked, disabled, className, color,
} = props;
return (
<Checkbox onChange={(e) => {input.onChange(e);}}>{label}</Checkbox>
// <CheckBoxField
// {...input}
// label={label}
// defaultChecked={defaultChecked}
// disabled={disabled}
// className={className}
// color={color}
// />
);
};
renderCheckBoxField.propTypes = {
input: PropTypes.shape({
onChange: PropTypes.func,
name: PropTypes.string,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.bool,
]),
}).isRequired,
label: PropTypes.string,
defaultChecked: PropTypes.bool,
disabled: PropTypes.bool,
className: PropTypes.string,
color: PropTypes.string,
};
renderCheckBoxField.defaultProps = {
label: '',
defaultChecked: false,
disabled: false,
className: '',
color: '',
};
export default renderCheckBoxField;

20
src/shared/components/form/CheckBox.tsx

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
import React, { PureComponent } from 'react';
import { Checkbox } from 'antd';
import { UseFormRegisterReturn } from 'react-hook-form';
interface IProps {
label: string
onChange?: (v: any) => any
field?: any
}
export const CheckBoxForm = (props: IProps) => {
return (
<Checkbox
onChange={(v) => props.onChange(v.target.checked)}
{...props?.field}
>
{props.label}
</Checkbox>
);
};

1
src/shared/components/form/index.ts

@ -0,0 +1 @@ @@ -0,0 +1 @@
export * from './CheckBox'

3
src/shared/components/index.ts

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
export * from './form'
export * from './layouts'
export * from './elements'

16
src/shared/components/layouts/MainWrapper.tsx

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
import React from 'react';
interface IProps {
theme: any
children: JSX.Element
}
export const MainWrapper = (props: IProps) => {
return (
<div className={props.theme}>
<div className="wrapper">
{props.children}
</div>
</div>
)
}

1
src/shared/components/layouts/index.ts

@ -0,0 +1 @@ @@ -0,0 +1 @@
export * from './MainWrapper'

2
src/shared/enums/index.ts

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
export * from './storage-keys.enum'
export * from './user.enum'

6
src/shared/enums/storage-keys.enum.ts

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
export enum StorageKey {
AccessToken = 'AccessToken',
RefreshToken = 'RefreshToken',
RememberMe = 'RememberMe',
ExpiryDate = 'ExpiryDate'
}

9
src/shared/enums/user.enum.ts

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
export enum EUserRole {
Admin = 'a',
User = 'u',
}
export enum EUserStatus {
Blocked = 'b',
Active = 'a',
}

3
src/shared/index.ts

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
export * from './enums'
export * from './interfaces'
export * from './components'

2
src/shared/interfaces/index.ts

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
export * from './token-pair.interfaces'
export * from './user.interfaces'

4
src/shared/interfaces/token-pair.interfaces.ts

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
export interface ITokenPair {
accessToken: string
refreshToken?: string
}

37
src/shared/interfaces/user.interfaces.ts

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
import { EUserRole, EUserStatus } from '../enums'
export interface IUser {
id: number
role: EUserRole
status: EUserStatus
info?: IUsersInfo
email: string
phoneNumber: string
}
export interface IUsersInfo {
userId: number
firstName: string
lastName: string
middleName?: string
position: string
dateOfBirth: string
avatarUrl?: string
isActivedApp: boolean
createdAt: string
updatedAt: string
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save