@ -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" |
||||
} |
||||
] |
||||
} |
||||
} |
@ -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; |
@ -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, |
||||
}); |
@ -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(); |
@ -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 |
||||
); |
||||
}; |
@ -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, |
||||
}; |
@ -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, |
||||
}; |
||||
}; |
@ -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, |
||||
}; |
@ -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; |
||||
}; |
||||
} |
@ -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', {}) |
||||
} |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
|
||||
import { IUser } from '@/shared'; |
||||
|
||||
export interface IFetchAccountResponse extends IUser { } |
@ -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 |
||||
} |
@ -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 |
||||
} |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export interface IAuthSuccessResponse { |
||||
accessToken: string |
||||
refreshToken: string |
||||
} |
@ -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 |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
import { AxiosResponse } from 'axios' |
||||
|
||||
export type ApiResponse<T> = Promise<AxiosResponse<T>> |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './auth/requests' |
||||
export * from './account/requests' |
After Width: | Height: | Size: 834 B |
After Width: | Height: | Size: 172 B |
After Width: | Height: | Size: 8.0 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 430 B |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 815 B |
@ -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',
|
||||
|
@ -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); |
@ -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); |
@ -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; |
@ -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
|
@ -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); |
@ -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> |
||||
) |
||||
} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './Router' |
||||
export * from './routes.enum' |
@ -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, |
||||
} |
||||
|
@ -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", |
||||
} |
@ -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; |
@ -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); |
||||
}); |
||||
}; |
||||
} |
@ -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) |
||||
|
@ -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 |
@ -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 |
@ -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 |
@ -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)
|
||||
|
@ -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> |
||||
) |
||||
} |
||||
|
@ -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> |
||||
) |
||||
} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './LoginField' |
||||
export * from './PasswordField' |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './fields' |
||||
export * from './SignInForm' |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './screens' |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './sign-in.screen' |
@ -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> |
||||
) |
||||
} |
@ -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') |
||||
) |
||||
|
@ -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; |
||||
} |
@ -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); |
@ -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, |
||||
} |
@ -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, |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './auth.service' |
@ -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, |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './global-container.service' |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './loader.component' |
@ -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> |
||||
) |
||||
} |
@ -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; |
@ -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> |
||||
); |
||||
}; |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './CheckBox' |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
export * from './form' |
||||
export * from './layouts' |
||||
export * from './elements' |
@ -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> |
||||
) |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './MainWrapper' |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './storage-keys.enum' |
||||
export * from './user.enum' |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
export enum StorageKey { |
||||
AccessToken = 'AccessToken', |
||||
RefreshToken = 'RefreshToken', |
||||
RememberMe = 'RememberMe', |
||||
ExpiryDate = 'ExpiryDate' |
||||
} |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
export enum EUserRole { |
||||
Admin = 'a', |
||||
User = 'u', |
||||
} |
||||
|
||||
export enum EUserStatus { |
||||
Blocked = 'b', |
||||
Active = 'a', |
||||
} |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
export * from './enums' |
||||
export * from './interfaces' |
||||
export * from './components' |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './token-pair.interfaces' |
||||
export * from './user.interfaces' |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export interface ITokenPair { |
||||
accessToken: string |
||||
refreshToken?: string |
||||
} |
@ -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 |
||||
} |