Web: LoginMobile: added module LoginMobile
This commit is contained in:
parent
80cdc0dee6
commit
b6882bb381
@ -23,10 +23,14 @@
|
||||
{
|
||||
"name": "ASC.Files.Client"
|
||||
"path": "./products/ASC.Files/Client"
|
||||
},
|
||||
{
|
||||
"name": "ASC.Web.LoginMobile"
|
||||
"path": "./web/ASC.Web.LoginMobile"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"window.zoomLevel": 1,
|
||||
"window.zoomLevel": 0,
|
||||
"files.autoSave": "afterDelay",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
|
@ -7,7 +7,8 @@
|
||||
"web/ASC.Web.Login",
|
||||
"web/ASC.Web.Client",
|
||||
"products/ASC.People/Client",
|
||||
"products/ASC.Files/Client"
|
||||
"products/ASC.Files/Client",
|
||||
"web/ASC.Web.LoginMobile"
|
||||
],
|
||||
"scripts": {
|
||||
"wipe": "rimraf node_modules yarn.lock web/**/node_modules products/**/node_modules",
|
||||
|
7
web/ASC.Web.LoginMobile/.babelrc
Normal file
7
web/ASC.Web.LoginMobile/.babelrc
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"presets": ["@babel/preset-react", "@babel/preset-env"],
|
||||
"plugins": [
|
||||
"@babel/plugin-transform-runtime",
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
]
|
||||
}
|
1
web/ASC.Web.LoginMobile/.gitattributes
vendored
Normal file
1
web/ASC.Web.LoginMobile/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
dist/* linguist-vendored=false
|
116
web/ASC.Web.LoginMobile/.gitignore
vendored
Normal file
116
web/ASC.Web.LoginMobile/.gitignore
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
15
web/ASC.Web.LoginMobile/Dockerfile
Normal file
15
web/ASC.Web.LoginMobile/Dockerfile
Normal file
@ -0,0 +1,15 @@
|
||||
FROM node:12
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY package.json ./
|
||||
COPY yarn.lock ./
|
||||
|
||||
RUN yarn install
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN yarn build
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD [ "yarn", "build:start" ]
|
11
web/ASC.Web.LoginMobile/jsconfig.json
Normal file
11
web/ASC.Web.LoginMobile/jsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"components": ["@appserver/components/src/components"],
|
||||
"ASCWebComponents": ["../../packages/asc-web-components/src"],
|
||||
"ASCWebCommon": ["../../packages/asc-web-common/src"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
58
web/ASC.Web.LoginMobile/package.json
Normal file
58
web/ASC.Web.LoginMobile/package.json
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "@appserver/loginmobile",
|
||||
"version": "0.1.0",
|
||||
"private": "true",
|
||||
"scripts": {
|
||||
"start": "webpack-cli serve",
|
||||
"build": "webpack --mode production",
|
||||
"serve": "serve dist -p 5021",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.12.10",
|
||||
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
||||
"@babel/plugin-proposal-export-default-from": "^7.12.1",
|
||||
"@babel/plugin-transform-runtime": "^7.12.1",
|
||||
"@babel/preset-env": "^7.12.7",
|
||||
"@babel/preset-react": "7.12.10",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"babel-loader": "8.2.2",
|
||||
"css-loader": "^3.6.0",
|
||||
"html-webpack-plugin": "4.5.0",
|
||||
"i18next": "^19.8.4",
|
||||
"json-loader": "^0.5.7",
|
||||
"react-i18next": "^11.7.3",
|
||||
"source-map-loader": "^1.1.2",
|
||||
"style-loader": "1.2.1",
|
||||
"webpack": "5.14.0",
|
||||
"webpack-cli": "4.3.1",
|
||||
"webpack-dev-server": "3.11.2",
|
||||
"serve": "11.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"attr-accept": "^2.2.2",
|
||||
"email-addresses": "^3.1.0",
|
||||
"moment": "^2.29.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"rc-tree": "^4.0.0-beta.2",
|
||||
"re-resizable": "^6.9.0",
|
||||
"react": "^16.14.0",
|
||||
"react-autosize-textarea": "^7.1.0",
|
||||
"react-custom-scrollbars": "^4.2.1",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-dropzone": "^11.2.4",
|
||||
"react-onclickoutside": "^6.9.0",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-resize-detector": "^5.2.0",
|
||||
"react-router": "^5.2.0",
|
||||
"react-text-mask": "^5.4.3",
|
||||
"react-toastify": "^6.1.0",
|
||||
"react-tooltip": "^4.2.11",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.6",
|
||||
"resize-image": "^0.1.0",
|
||||
"sjcl": "^1.0.8",
|
||||
"styled-components": "^5.2.1"
|
||||
}
|
||||
}
|
11
web/ASC.Web.LoginMobile/public/index.html
Normal file
11
web/ASC.Web.LoginMobile/public/index.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Login Mobile</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
9
web/ASC.Web.LoginMobile/src/App.js
Normal file
9
web/ASC.Web.LoginMobile/src/App.js
Normal file
@ -0,0 +1,9 @@
|
||||
import React from "react";
|
||||
import { Provider } from "react-redux";
|
||||
import Login from "./Login";
|
||||
|
||||
const App = () => {
|
||||
return <Login />;
|
||||
};
|
||||
|
||||
export default App;
|
527
web/ASC.Web.LoginMobile/src/Login.jsx
Normal file
527
web/ASC.Web.LoginMobile/src/Login.jsx
Normal file
@ -0,0 +1,527 @@
|
||||
import React, { Component, useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import styled, { css } from "styled-components";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router";
|
||||
|
||||
import { PageLayout, store, api, utils } from "ASCWebCommon";
|
||||
import { checkPwd } from "ASCWebCommon/desktop";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Text,
|
||||
TextInput,
|
||||
Link,
|
||||
toastr,
|
||||
Checkbox,
|
||||
HelpButton,
|
||||
PasswordInput,
|
||||
FieldContainer,
|
||||
tre,
|
||||
} from "ASCWebComponents";
|
||||
|
||||
import i18n from "./i18n";
|
||||
import ForgotPasswordModalDialog from "./sub-components/forgot-password-modal-dialog";
|
||||
import Register from "./sub-components/register-container";
|
||||
|
||||
import { fakeHashSettings } from "./api/fakeApi";
|
||||
|
||||
const { login, setIsLoaded, reloadPortalSettings } = store.auth.actions;
|
||||
const { getLanguage, isDesktopClient } = store.auth.selectors;
|
||||
const { sendInstructionsToChangePassword } = api.people;
|
||||
const { createPasswordHash, tryRedirectTo } = utils;
|
||||
|
||||
const LoginContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 120px auto 0 auto;
|
||||
max-width: 960px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
padding: 0 16px;
|
||||
max-width: 475px;
|
||||
}
|
||||
@media (max-width: 375px) {
|
||||
margin: 72px auto 0 auto;
|
||||
max-width: 311px;
|
||||
}
|
||||
|
||||
.greeting-title {
|
||||
width: 100%;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
text-align: left;
|
||||
}
|
||||
@media (max-width: 375px) {
|
||||
font-size: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
.auth-form-container {
|
||||
margin: 32px 213px 0 213px;
|
||||
width: 311px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
margin: 32px 0 0 0;
|
||||
width: 100%;
|
||||
}
|
||||
@media (max-width: 375px) {
|
||||
margin: 32px 0 0 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-forgot-wrapper {
|
||||
height: 36px;
|
||||
padding: 14px 0;
|
||||
|
||||
.login-checkbox-wrapper {
|
||||
display: flex;
|
||||
|
||||
.login-checkbox {
|
||||
float: left;
|
||||
span {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login-link {
|
||||
line-height: 18px;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.login-button {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.login-button-dialog {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.login-bottom-border {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #eceef1;
|
||||
}
|
||||
|
||||
.login-bottom-text {
|
||||
margin: 0 8px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const LoginFormWrapper = styled.div`
|
||||
display: grid;
|
||||
grid-template-rows: ${(props) =>
|
||||
props.enabledJoin
|
||||
? props.isDesktop
|
||||
? css`1fr 10px`
|
||||
: css`1fr 66px`
|
||||
: css`1fr`};
|
||||
width: 100%;
|
||||
height: calc(100vh-56px);
|
||||
`;
|
||||
|
||||
class Form extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
identifierValid: true,
|
||||
identifier: "",
|
||||
isLoading: false,
|
||||
isDisabled: false,
|
||||
passwordValid: true,
|
||||
password: "",
|
||||
isChecked: false,
|
||||
openDialog: false,
|
||||
email: "",
|
||||
emailError: false,
|
||||
errorText: "",
|
||||
socialButtons: [],
|
||||
};
|
||||
}
|
||||
|
||||
onChangeLogin = (event) => {
|
||||
this.setState({ identifier: event.target.value });
|
||||
!this.state.identifierValid && this.setState({ identifierValid: true });
|
||||
this.state.errorText && this.setState({ errorText: "" });
|
||||
};
|
||||
|
||||
onChangePassword = (event) => {
|
||||
this.setState({ password: event.target.value });
|
||||
!this.state.passwordValid && this.setState({ passwordValid: true });
|
||||
this.state.errorText && this.setState({ errorText: "" });
|
||||
};
|
||||
|
||||
onChangeEmail = (event) => {
|
||||
this.setState({ email: event.target.value, emailError: false });
|
||||
};
|
||||
|
||||
onChangeCheckbox = () => this.setState({ isChecked: !this.state.isChecked });
|
||||
|
||||
onClick = () => {
|
||||
this.setState({
|
||||
openDialog: true,
|
||||
isDisabled: true,
|
||||
email: this.state.identifier,
|
||||
});
|
||||
};
|
||||
|
||||
onKeyPress = (event) => {
|
||||
if (event.key === "Enter") {
|
||||
!this.state.isDisabled
|
||||
? this.onSubmit()
|
||||
: this.onSendPasswordInstructions();
|
||||
}
|
||||
};
|
||||
|
||||
onSendPasswordInstructions = () => {
|
||||
if (!this.state.email.trim()) {
|
||||
this.setState({ emailError: true });
|
||||
} else {
|
||||
this.setState({ isLoading: true });
|
||||
sendInstructionsToChangePassword(this.state.email)
|
||||
.then(
|
||||
(res) => toastr.success(res),
|
||||
(message) => toastr.error(message)
|
||||
)
|
||||
.finally(this.onDialogClose());
|
||||
}
|
||||
};
|
||||
|
||||
onDialogClose = () => {
|
||||
this.setState({
|
||||
openDialog: false,
|
||||
isDisabled: false,
|
||||
isLoading: false,
|
||||
email: "",
|
||||
emailError: false,
|
||||
});
|
||||
};
|
||||
|
||||
onSubmit = () => {
|
||||
const { errorText, identifier, password } = this.state;
|
||||
const {
|
||||
login,
|
||||
setIsLoaded,
|
||||
hashSettings,
|
||||
isDesktop,
|
||||
defaultPage,
|
||||
} = this.props;
|
||||
|
||||
errorText && this.setState({ errorText: "" });
|
||||
let hasError = false;
|
||||
|
||||
const userName = identifier.trim();
|
||||
|
||||
if (!userName) {
|
||||
hasError = true;
|
||||
this.setState({ identifierValid: !hasError });
|
||||
}
|
||||
|
||||
const pass = password.trim();
|
||||
|
||||
if (!pass) {
|
||||
hasError = true;
|
||||
this.setState({ passwordValid: !hasError });
|
||||
}
|
||||
|
||||
if (hasError) return false;
|
||||
|
||||
this.setState({ isLoading: true });
|
||||
const hash = createPasswordHash(pass, fakeHashSettings);
|
||||
|
||||
isDesktop && checkPwd();
|
||||
|
||||
login(userName, hash)
|
||||
.then(() => {
|
||||
if (!tryRedirectTo(defaultPage)) {
|
||||
setIsLoaded(true);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setState({
|
||||
errorText: error,
|
||||
identifierValid: !error,
|
||||
passwordValid: !error,
|
||||
isLoading: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const {
|
||||
match,
|
||||
t,
|
||||
hashSettings, // eslint-disable-line react/prop-types
|
||||
reloadPortalSettings, // eslint-disable-line react/prop-types
|
||||
organizationName,
|
||||
} = this.props;
|
||||
//const { error /*confirmedEmail*/ } = match.params;
|
||||
|
||||
document.title = `${t("Authorization")} – ${organizationName}`; //TODO: implement the setDocumentTitle() utility in ASC.Web.Common
|
||||
|
||||
//error && this.setState({ errorText: error });
|
||||
// confirmedEmail && this.setState({ identifier: confirmedEmail });
|
||||
window.addEventListener("keyup", this.onKeyPress);
|
||||
|
||||
if (!fakeHashSettings) {
|
||||
reloadPortalSettings();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("keyup", this.onKeyPress);
|
||||
}
|
||||
|
||||
settings = {
|
||||
minLength: 6,
|
||||
upperCase: false,
|
||||
digits: false,
|
||||
specSymbols: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { greetingTitle, match, t } = this.props;
|
||||
|
||||
const {
|
||||
identifierValid,
|
||||
identifier,
|
||||
isLoading,
|
||||
passwordValid,
|
||||
password,
|
||||
isChecked,
|
||||
openDialog,
|
||||
email,
|
||||
emailError,
|
||||
errorText,
|
||||
socialButtons,
|
||||
} = this.state;
|
||||
//const { confirmedEmail } = match.params;
|
||||
|
||||
//console.log("Login render");
|
||||
|
||||
return (
|
||||
<>
|
||||
<LoginContainer>
|
||||
<Text
|
||||
fontSize="32px"
|
||||
fontWeight={600}
|
||||
textAlign="center"
|
||||
className="greeting-title"
|
||||
>
|
||||
{greetingTitle}
|
||||
</Text>
|
||||
|
||||
<form className="auth-form-container">
|
||||
<FieldContainer
|
||||
isVertical={true}
|
||||
labelVisible={false}
|
||||
hasError={!identifierValid}
|
||||
errorMessage={errorText ? errorText : t("RequiredFieldMessage")} //TODO: Add wrong login server error
|
||||
>
|
||||
<TextInput
|
||||
id="login"
|
||||
name="login"
|
||||
hasError={!identifierValid}
|
||||
value={identifier}
|
||||
placeholder={t("RegistrationEmailWatermark")}
|
||||
size="large"
|
||||
scale={true}
|
||||
isAutoFocussed={true}
|
||||
tabIndex={1}
|
||||
isDisabled={isLoading}
|
||||
autoComplete="username"
|
||||
onChange={this.onChangeLogin}
|
||||
onKeyDown={this.onKeyPress}
|
||||
/>
|
||||
</FieldContainer>
|
||||
<FieldContainer
|
||||
isVertical={true}
|
||||
labelVisible={false}
|
||||
hasError={!passwordValid}
|
||||
errorMessage={errorText ? "" : t("RequiredFieldMessage")} //TODO: Add wrong password server error
|
||||
>
|
||||
<PasswordInput
|
||||
simpleView={true}
|
||||
passwordSettings={this.settings}
|
||||
id="password"
|
||||
inputName="password"
|
||||
placeholder={t("Password")}
|
||||
type="password"
|
||||
hasError={!passwordValid}
|
||||
inputValue={password}
|
||||
size="large"
|
||||
scale={true}
|
||||
tabIndex={1}
|
||||
isDisabled={isLoading}
|
||||
autoComplete="current-password"
|
||||
onChange={this.onChangePassword}
|
||||
onKeyDown={this.onKeyPress}
|
||||
/>
|
||||
</FieldContainer>
|
||||
<div className="login-forgot-wrapper">
|
||||
<div className="login-checkbox-wrapper">
|
||||
<Checkbox
|
||||
className="login-checkbox"
|
||||
isChecked={isChecked}
|
||||
onChange={this.onChangeCheckbox}
|
||||
label={<Text fontSize="13px">{t("Remember")}</Text>}
|
||||
/>
|
||||
{/*<HelpButton
|
||||
className="login-tooltip"
|
||||
helpButtonHeaderContent={t("CookieSettingsTitle")}
|
||||
tooltipContent={
|
||||
<Text fontSize="12px">{t("RememberHelper")}</Text>
|
||||
}
|
||||
/>*/}
|
||||
<Link
|
||||
fontSize="13px"
|
||||
color="#316DAA"
|
||||
className="login-link"
|
||||
type="page"
|
||||
isHovered={false}
|
||||
onClick={this.onClick}
|
||||
>
|
||||
{t("ForgotPassword")}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{openDialog && (
|
||||
<ForgotPasswordModalDialog
|
||||
openDialog={openDialog}
|
||||
isLoading={isLoading}
|
||||
email={email}
|
||||
emailError={emailError}
|
||||
onChangeEmail={this.onChangeEmail}
|
||||
onSendPasswordInstructions={this.onSendPasswordInstructions}
|
||||
onDialogClose={this.onDialogClose}
|
||||
t={t}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
id="button"
|
||||
className="login-button"
|
||||
primary
|
||||
size="large"
|
||||
scale={true}
|
||||
label={isLoading ? t("LoadingProcessing") : t("LoginButton")}
|
||||
tabIndex={1}
|
||||
isDisabled={isLoading}
|
||||
isLoading={isLoading}
|
||||
onClick={this.onSubmit}
|
||||
/>
|
||||
|
||||
{/*confirmedEmail && (
|
||||
<Text isBold={true} fontSize="16px">
|
||||
{t("MessageEmailConfirmed")} {t("MessageAuthorize")}
|
||||
</Text>
|
||||
)*/}
|
||||
{/* TODO: old error indication
|
||||
|
||||
<Text fontSize="14px" color="#c30">
|
||||
{errorText}
|
||||
</Text> */}
|
||||
|
||||
{socialButtons.length ? (
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<div className="login-bottom-border"></div>
|
||||
<Text className="login-bottom-text" color="#A3A9AE">
|
||||
{t("Or")}
|
||||
</Text>
|
||||
<div className="login-bottom-border"></div>
|
||||
</Box>
|
||||
) : null}
|
||||
</form>
|
||||
</LoginContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Form.propTypes = {
|
||||
login: PropTypes.func, //.isRequired,
|
||||
match: PropTypes.object, //.isRequired,
|
||||
hashSettings: PropTypes.object,
|
||||
reloadPortalSettings: PropTypes.func,
|
||||
setIsLoaded: PropTypes.func, //.isRequired,
|
||||
greetingTitle: PropTypes.string, //.isRequired,
|
||||
t: PropTypes.func, //.isRequired,
|
||||
i18n: PropTypes.object, //.isRequired,
|
||||
language: PropTypes.string, //.isRequired,
|
||||
socialButtons: PropTypes.array,
|
||||
organizationName: PropTypes.string,
|
||||
homepage: PropTypes.string,
|
||||
defaultPage: PropTypes.string,
|
||||
};
|
||||
|
||||
Form.defaultProps = {
|
||||
identifier: "",
|
||||
password: "",
|
||||
email: "",
|
||||
};
|
||||
|
||||
const FormWrapper = withTranslation()(Form);
|
||||
|
||||
// const LoginForm = (props) => <FormWrapper i18n={i18n} {...props} />;
|
||||
|
||||
// export default LoginForm;
|
||||
|
||||
//const RegisterWrapper = withTranslation()(Register);
|
||||
|
||||
const LoginForm = (props) => {
|
||||
const { language, enabledJoin, isDesktop } = props;
|
||||
|
||||
useEffect(() => {
|
||||
i18n.changeLanguage(language);
|
||||
}, [language]);
|
||||
|
||||
return (
|
||||
<LoginFormWrapper enabledJoin={enabledJoin} isDesktop={isDesktop}>
|
||||
<FormWrapper i18n={i18n} {...props} />
|
||||
{/*<Register />*/}
|
||||
</LoginFormWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
LoginForm.propTypes = {
|
||||
language: PropTypes.string, //.isRequired,
|
||||
isLoaded: PropTypes.bool,
|
||||
enabledJoin: PropTypes.bool,
|
||||
isDesktop: PropTypes.bool, //.isRequired,
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const { isLoaded, settings, isAuthenticated } = state.auth;
|
||||
const {
|
||||
greetingSettings,
|
||||
organizationName,
|
||||
hashSettings,
|
||||
enabledJoin,
|
||||
defaultPage,
|
||||
} = settings;
|
||||
|
||||
return {
|
||||
isAuthenticated,
|
||||
isLoaded,
|
||||
isDesktop: isDesktopClient(state),
|
||||
organizationName,
|
||||
language: getLanguage(state),
|
||||
greetingTitle: greetingSettings,
|
||||
hashSettings,
|
||||
enabledJoin,
|
||||
defaultPage,
|
||||
};
|
||||
}
|
||||
|
||||
export default LoginForm; //connect(mapStateToProps, {
|
||||
//login,
|
||||
//setIsLoaded,
|
||||
//reloadPortalSettings,
|
||||
//})(withRouter(LoginForm));
|
5
web/ASC.Web.LoginMobile/src/api/fakeApi.js
Normal file
5
web/ASC.Web.LoginMobile/src/api/fakeApi.js
Normal file
@ -0,0 +1,5 @@
|
||||
export const fakeHashSettings = {
|
||||
size: 256,
|
||||
iterations: 100000,
|
||||
salt: "0a783fef249b0cfa2b1ddc3825bf9a5c9c869bf65a4a45cbd5a0c93b16b5a47e",
|
||||
};
|
5
web/ASC.Web.LoginMobile/src/bootstrap.js
vendored
Normal file
5
web/ASC.Web.LoginMobile/src/bootstrap.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import App from "./App";
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById("root"));
|
37
web/ASC.Web.LoginMobile/src/i18n.js
Normal file
37
web/ASC.Web.LoginMobile/src/i18n.js
Normal file
@ -0,0 +1,37 @@
|
||||
import i18n from "i18next";
|
||||
import en from "./locales/en/translation.json";
|
||||
import ru from "./locales/ru/translation.json";
|
||||
//import { i18nBaseSettings } from "../../constants";
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
translation: en,
|
||||
},
|
||||
ru: {
|
||||
translation: ru,
|
||||
},
|
||||
};
|
||||
|
||||
//newInstance.init({ ...i18nBaseSettings, resources });
|
||||
|
||||
newInstance.init({
|
||||
resources: resources,
|
||||
lng: "en",
|
||||
fallbackLng: "en",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === "lowercase") return value.toLowerCase();
|
||||
return value;
|
||||
},
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default newInstance;
|
3
web/ASC.Web.LoginMobile/src/index.css
Normal file
3
web/ASC.Web.LoginMobile/src/index.css
Normal file
@ -0,0 +1,3 @@
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
1
web/ASC.Web.LoginMobile/src/index.js
Normal file
1
web/ASC.Web.LoginMobile/src/index.js
Normal file
@ -0,0 +1 @@
|
||||
import("./bootstrap");
|
30
web/ASC.Web.LoginMobile/src/locales/en/translation.json
Normal file
30
web/ASC.Web.LoginMobile/src/locales/en/translation.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"LoadingProcessing": "Loading...",
|
||||
"LoginButton": "Sign In",
|
||||
"Password": "Password",
|
||||
"RegistrationEmailWatermark": "E-mail",
|
||||
"MessageEmailConfirmed": "Your email was activated successfully.",
|
||||
"MessageAuthorize": "Please authorize yourself.",
|
||||
"ForgotPassword": "Forgot your password?",
|
||||
|
||||
"PasswordRecoveryTitle": "Password recovery",
|
||||
"PasswordRecoveryPlaceholder": "Your registration e-mail",
|
||||
"MessageSendPasswordRecoveryInstructionsOnEmail": "Please enter the email you used while registering on the portal. The password recovery instructions will be send to that email address.",
|
||||
|
||||
"RegisterTitle": "Registration request",
|
||||
"RegisterTextBodyBeforeDomainsList": "Registration is available to users with an email account at",
|
||||
"RegisterTextBodyAfterDomainsList": "To register, enter your email and click on Send request. An email message with a link to activate your account will be sent to the specified email. Please enter the email address where the invitation will be sent:",
|
||||
"RegisterPlaceholder": "Your registration e-mail",
|
||||
"RegisterSendButton": "Send request",
|
||||
"RegisterProcessSending": "Sending...",
|
||||
|
||||
"SendButton": "Send",
|
||||
"RequiredFieldMessage": "Required field",
|
||||
"CancelButton": "Cancel",
|
||||
"Remember": "Remember me",
|
||||
"RememberHelper": "The default session lifetime is 20 minutes. Check this option to set it to 1 year. To set your own value, go to the settings.",
|
||||
"CookieSettingsTitle": "Session Lifetime",
|
||||
"Authorization": "Authorization",
|
||||
"Or": "OR",
|
||||
"Register": "Register"
|
||||
}
|
30
web/ASC.Web.LoginMobile/src/locales/ru/translation.json
Normal file
30
web/ASC.Web.LoginMobile/src/locales/ru/translation.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"LoadingProcessing": "Загрузка...",
|
||||
"LoginButton": "Войти",
|
||||
"Password": "Пароль",
|
||||
"RegistrationEmailWatermark": "Регистрационный email",
|
||||
"MessageEmailConfirmed": "Ваш email успешно активирован.",
|
||||
"MessageAuthorize": "Пожалуйста авторизуйтесь.",
|
||||
"ForgotPassword": "Забыли пароль?",
|
||||
|
||||
"PasswordRecoveryTitle": "Восстановление пароля",
|
||||
"PasswordRecoveryPlaceholder": "Ваш регистрационный email",
|
||||
"MessageSendPasswordRecoveryInstructionsOnEmail": "Пожалуйста, введите адрес электронной почты, указанный при регистрации на портале. Инструкции для восстановления пароля будут отправлены на этот адрес электронной почты.",
|
||||
|
||||
"RegisterTitle": "Запрос на регистрацию",
|
||||
"RegisterTextBodyBeforeDomainsList": "Регистрация доступна для пользователей, которые имеют почтовый ящик на",
|
||||
"RegisterTextBodyAfterDomainsList": "Чтобы зарегистрироваться, введите свой email и нажмите кнопку Отправить запрос. Сообщение со ссылкой для активации вашей учётной записи будет отправлено на указанный адрес.",
|
||||
"RegisterPlaceholder": "Ваш регистрационный email",
|
||||
"RegisterSendButton": "Отправить запрос",
|
||||
"RegisterProcessSending": "Отправка...",
|
||||
|
||||
"SendButton": "Отправить",
|
||||
"RequiredFieldMessage": "Обязательное поле",
|
||||
"CancelButton": "Отмена",
|
||||
"Remember": "Запомнить",
|
||||
"RememberHelper": "Время существования сессии по умолчанию составляет 20 минут. Отметьте эту опцию, чтобы установить значение 1 год. Чтобы задать собственное значение, перейдите в настройки.",
|
||||
"CookieSettingsTitle": "Время жизни сессии",
|
||||
"Authorization": "Авторизация",
|
||||
"Or": "ИЛИ",
|
||||
"Register": "Регистрация"
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import Button from "@appserver/components/src/components/button";
|
||||
import TextInput from "@appserver/components/src/components/text-input";
|
||||
import Text from "@appserver/components/src/components/text";
|
||||
import ModalDialog from "@appserver/components/src/components/modal-dialog";
|
||||
import FieldContainer from "@appserver/components/src/components/field-container";
|
||||
|
||||
import ModalDialogContainer from "./modal-dialog-container";
|
||||
|
||||
class ForgotPasswordModalDialog extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
openDialog,
|
||||
isLoading,
|
||||
email,
|
||||
emailError,
|
||||
onChangeEmail,
|
||||
onSendPasswordInstructions,
|
||||
onDialogClose,
|
||||
t,
|
||||
} = this.props;
|
||||
return (
|
||||
<ModalDialogContainer>
|
||||
<ModalDialog
|
||||
visible={openDialog}
|
||||
bodyPadding="16px 0 0 0"
|
||||
onClose={onDialogClose}
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
<Text isBold={true} fontSize="21px">
|
||||
{t("PasswordRecoveryTitle")}
|
||||
</Text>
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<Text
|
||||
key="text-body"
|
||||
className="text-body"
|
||||
isBold={false}
|
||||
fontSize="13px"
|
||||
>
|
||||
{t("MessageSendPasswordRecoveryInstructionsOnEmail")}
|
||||
</Text>
|
||||
|
||||
<FieldContainer
|
||||
key="e-mail"
|
||||
isVertical={true}
|
||||
hasError={emailError}
|
||||
labelVisible={false}
|
||||
errorMessage={t("RequiredFieldMessage")}
|
||||
>
|
||||
<TextInput
|
||||
hasError={emailError}
|
||||
placeholder={t("PasswordRecoveryPlaceholder")}
|
||||
isAutoFocussed={true}
|
||||
id="e-mail"
|
||||
name="e-mail"
|
||||
type="text"
|
||||
size="base"
|
||||
scale={true}
|
||||
tabIndex={2}
|
||||
isDisabled={isLoading}
|
||||
value={email}
|
||||
onChange={onChangeEmail}
|
||||
/>
|
||||
</FieldContainer>
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="modal-dialog-button"
|
||||
key="SendBtn"
|
||||
label={isLoading ? t("LoadingProcessing") : t("SendButton")}
|
||||
size="big"
|
||||
scale={false}
|
||||
primary={true}
|
||||
onClick={onSendPasswordInstructions}
|
||||
isLoading={isLoading}
|
||||
isDisabled={isLoading}
|
||||
tabIndex={2}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
</ModalDialogContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ForgotPasswordModalDialog.propTypes = {
|
||||
openDialog: PropTypes.bool.isRequired,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
email: PropTypes.string.isRequired,
|
||||
emailError: PropTypes.bool.isRequired,
|
||||
onChangeEmail: PropTypes.func.isRequired,
|
||||
onSendPasswordInstructions: PropTypes.func.isRequired,
|
||||
onDialogClose: PropTypes.func.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ForgotPasswordModalDialog;
|
@ -0,0 +1,19 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
const ModalDialogContainer = styled.div`
|
||||
.modal-dialog-aside-footer {
|
||||
@media (max-width: 1024px) {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
.modal-dialog-button {
|
||||
@media (max-width: 1024px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.field-body {
|
||||
margin-top: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default ModalDialogContainer;
|
127
web/ASC.Web.LoginMobile/src/sub-components/register-container.js
Normal file
127
web/ASC.Web.LoginMobile/src/sub-components/register-container.js
Normal file
@ -0,0 +1,127 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import Box from "@appserver/components/src/components/box";
|
||||
import Text from "@appserver/components/src/components/text";
|
||||
import toastr from "@appserver/components/src/components/toast/toastr";
|
||||
|
||||
import RegisterModalDialog from "./register-modal-dialog";
|
||||
import styled from "styled-components";
|
||||
import PropTypes from "prop-types";
|
||||
import { sendRegisterRequest } from "@appserver/common/src/api/settings";
|
||||
import { I18nextProvider, withTranslation } from "react-i18next";
|
||||
import { getLanguage } from "@appserver/common/src/store/auth/selectors";
|
||||
import { connect } from "react-redux";
|
||||
import i18n from "../i18n";
|
||||
|
||||
const StyledRegister = styled(Box)`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 184;
|
||||
width: 100%;
|
||||
height: 66px;
|
||||
padding: 1.5em;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-color: #f8f9f9;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const Register = ({ t }) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [email, setEmail] = useState("");
|
||||
const [emailErr, setEmailErr] = useState(false);
|
||||
|
||||
const onRegisterClick = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
const onRegisterModalClose = () => {
|
||||
setVisible(false);
|
||||
setEmail("");
|
||||
setEmailErr(false);
|
||||
};
|
||||
const onChangeEmail = (e) => {
|
||||
setEmail(e.currentTarget.value);
|
||||
setEmailErr(false);
|
||||
};
|
||||
const onSendRegisterRequest = () => {
|
||||
if (!email.trim()) {
|
||||
setEmailErr(true);
|
||||
} else {
|
||||
setLoading(true);
|
||||
sendRegisterRequest(email)
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
toastr.success("Successfully sent");
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
toastr.error(error);
|
||||
})
|
||||
.finally(onRegisterModalClose);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledRegister onClick={onRegisterClick}>
|
||||
<Text color="#316DAA">{t("Register")}</Text>
|
||||
</StyledRegister>
|
||||
|
||||
{visible && (
|
||||
<RegisterModalDialog
|
||||
visible={visible}
|
||||
loading={loading}
|
||||
email={email}
|
||||
emailErr={emailErr}
|
||||
t={t}
|
||||
onChangeEmail={onChangeEmail}
|
||||
onRegisterModalClose={onRegisterModalClose}
|
||||
onSendRegisterRequest={onSendRegisterRequest}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Register.propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const RegisterTranslationWrapper = withTranslation()(Register);
|
||||
|
||||
const RegisterWrapper = (props) => {
|
||||
const { language, isAuthenticated, enabledJoin } = props;
|
||||
|
||||
useEffect(() => {
|
||||
i18n.changeLanguage(language);
|
||||
}, [language]);
|
||||
|
||||
return (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
{enabledJoin && !isAuthenticated && (
|
||||
<RegisterTranslationWrapper {...props} />
|
||||
)}
|
||||
</I18nextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
RegisterWrapper.propTypes = {
|
||||
language: PropTypes.string,
|
||||
isAuthenticated: PropTypes.bool,
|
||||
enabledJoin: PropTypes.bool,
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const { isAuthenticated, settings } = state.auth;
|
||||
const { enabledJoin } = settings;
|
||||
return {
|
||||
language: getLanguage(state),
|
||||
isAuthenticated,
|
||||
enabledJoin,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(RegisterWrapper);
|
@ -0,0 +1,105 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import Button from "@appserver/components/src/components/button";
|
||||
import TextInput from "@appserver/components/src/components/text-input";
|
||||
import Text from "@appserver/components/src/components/text";
|
||||
import ModalDialog from "@appserver/components/src/components/modal-dialog";
|
||||
import FieldContainer from "@appserver/components/src/components/field-container";
|
||||
|
||||
import ModalDialogContainer from "./modal-dialog-container";
|
||||
|
||||
const domains = ["mail.ru", "gmail.com", "yandex.ru"];
|
||||
const domainList = domains.map((domain, i) => (
|
||||
<span key={i}>
|
||||
<b>
|
||||
{domain}
|
||||
{i === domains.length - 1 ? "." : ", "}
|
||||
</b>
|
||||
</span>
|
||||
));
|
||||
|
||||
const RegisterModalDialog = ({
|
||||
visible,
|
||||
loading,
|
||||
email,
|
||||
emailErr,
|
||||
t,
|
||||
onChangeEmail,
|
||||
onRegisterModalClose,
|
||||
onSendRegisterRequest,
|
||||
}) => {
|
||||
return (
|
||||
<ModalDialogContainer>
|
||||
<ModalDialog
|
||||
visible={visible}
|
||||
bodyPadding="16px 0 0 0"
|
||||
onClose={onRegisterModalClose}
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
<Text isBold={true} fontSize="21px">
|
||||
{t("RegisterTitle")}
|
||||
</Text>
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<Text key="text-body" isBold={false} fontSize="13px">
|
||||
{t("RegisterTextBodyBeforeDomainsList")} {domainList}{" "}
|
||||
{t("RegisterTextBodyAfterDomainsList")}
|
||||
</Text>
|
||||
|
||||
<FieldContainer
|
||||
key="e-mail"
|
||||
isVertical={true}
|
||||
hasError={emailErr}
|
||||
labelVisible={false}
|
||||
errorMessage={t("RequiredFieldMessage")}
|
||||
>
|
||||
<TextInput
|
||||
hasError={emailErr}
|
||||
placeholder={t("RegisterPlaceholder")}
|
||||
isAutoFocussed={true}
|
||||
id="e-mail"
|
||||
name="e-mail"
|
||||
type="text"
|
||||
size="base"
|
||||
scale={true}
|
||||
tabIndex={3}
|
||||
isDisabled={loading}
|
||||
value={email}
|
||||
onChange={onChangeEmail}
|
||||
/>
|
||||
</FieldContainer>
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="modal-dialog-button"
|
||||
key="SendBtn"
|
||||
label={
|
||||
loading ? t("RegisterProcessSending") : t("RegisterSendButton")
|
||||
}
|
||||
size="big"
|
||||
scale={false}
|
||||
primary={true}
|
||||
onClick={onSendRegisterRequest}
|
||||
isLoading={loading}
|
||||
isDisabled={loading}
|
||||
tabIndex={3}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
</ModalDialogContainer>
|
||||
);
|
||||
};
|
||||
|
||||
RegisterModalDialog.propTypes = {
|
||||
visible: PropTypes.bool.isRequired,
|
||||
loading: PropTypes.bool.isRequired,
|
||||
email: PropTypes.string,
|
||||
emailErr: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
onChangeEmail: PropTypes.func.isRequired,
|
||||
onSendRegisterRequest: PropTypes.func.isRequired,
|
||||
onRegisterModalClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default RegisterModalDialog;
|
83
web/ASC.Web.LoginMobile/webpack.config.js
Normal file
83
web/ASC.Web.LoginMobile/webpack.config.js
Normal file
@ -0,0 +1,83 @@
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const path = require("path");
|
||||
const deps = require("./package.json").dependencies;
|
||||
|
||||
module.exports = {
|
||||
entry: "./src/index",
|
||||
mode: "development",
|
||||
|
||||
devServer: {
|
||||
contentBase: [path.join(__dirname, "public"), path.join(__dirname, "dist")],
|
||||
port: 5021,
|
||||
historyApiFallback: true,
|
||||
hot: false,
|
||||
hotOnly: false,
|
||||
},
|
||||
|
||||
output: {
|
||||
publicPath: "auto",
|
||||
chunkFilename: "[id].[contenthash].js",
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: [".jsx", ".js", ".json"],
|
||||
fallback: {
|
||||
crypto: false,
|
||||
},
|
||||
alias: {
|
||||
ASCWebComponents: path.resolve(
|
||||
__dirname,
|
||||
"../../packages/asc-web-components/src/"
|
||||
),
|
||||
ASCWebCommon: path.resolve(
|
||||
__dirname,
|
||||
"../../packages/asc-web-common/src"
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.m?js/,
|
||||
type: "javascript/auto",
|
||||
resolve: {
|
||||
fullySpecified: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.react.svg$/,
|
||||
use: ["@svgr/webpack"],
|
||||
},
|
||||
{ test: /\.json$/, loader: "json-loader" },
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ["style-loader", "css-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: ["@babel/preset-react", "@babel/preset-env"],
|
||||
plugins: [
|
||||
"@babel/plugin-transform-runtime",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-export-default-from",
|
||||
],
|
||||
},
|
||||
},
|
||||
"source-map-loader",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: "./public/index.html",
|
||||
}),
|
||||
],
|
||||
};
|
Loading…
Reference in New Issue
Block a user