Created
March 5, 2021 12:03
-
-
Save taksenov/cc34caff2354e806e36ef0ffd0b8127f to your computer and use it in GitHub Desktop.
Find anti patterns
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from 'react'; | |
import { connect } from 'react-redux'; | |
import { bindActionCreators, Dispatch } from 'redux'; | |
import { DateInput } from 'semantic-ui-calendar-react'; | |
import JSON5 from 'json5'; | |
import get from 'lodash/get'; | |
import isEmpty from 'lodash/isEmpty'; | |
import toLower from 'lodash/toLower'; | |
import Dropzone from 'react-dropzone'; | |
import { Container, Button, Icon, Form, Grid, Divider } from 'semantic-ui-react'; | |
import styled from 'styled-components'; | |
import { ContentPageLayout } from '../../shared/layouts'; | |
import Preloader from '../../shared/components/Preloader'; | |
import Logo from './components/Logo'; | |
import SuccessBlock from './components/SuccessBlock'; | |
import FormErrorBlock from './components/FormErrorBlock'; | |
import { StoreTypes } from '../../core/reducers/types'; | |
// Actions | |
import { getMySitesRequest, getDateRequest } from '../../states/mysites/duck'; | |
// Selectors | |
import { getIsFetchingMySites, getMySitesData, isFetchingDate, getDateField } from '../../states/mysites/selectors'; | |
import styles from './MainPage.module.scss'; | |
import DropZoneDefaultImage from './assets/images/i-upload-arrow.svg'; | |
import ExcelImage from './assets/images/excel.svg'; | |
const EnabledButton = styled(Button)` | |
font-family: Tahoma, Helvetica, sans-serif !important; | |
background-color: rgba(54, 45, 144) !important; | |
color: #fff !important; | |
&:hover { | |
background-color: rgba(54, 45, 144, 0.9) !important; | |
} | |
`; | |
const getColor = (props: any) => { | |
if (props.isDragAccept) { | |
return 'rgba(0, 230, 118, 0.7)'; | |
} | |
if (props.isDragReject) { | |
return 'rgba(255, 23, 68, 0.7)'; | |
} | |
if (props.isDragActive) { | |
return 'rgba(33, 150, 243, 0.7)'; | |
} | |
return 'rgba(54, 45, 144, 0.3)'; | |
}; | |
const DropContainer = styled.div` | |
width: 100%; | |
flex: 1; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
padding: 20px; | |
border-width: 1px; | |
border-radius: 3px; | |
border-color: ${props => getColor(props)}; | |
border-style: dashed; | |
background-color: #ffffff; | |
outline: none; | |
transition: border 0.24s ease-in-out; | |
margin-bottom: 20px; | |
&:hover { | |
background-color: #eeeeee; | |
cursor: pointer; | |
} | |
`; | |
// Dispatcher interface | |
export interface IDispatch { | |
getMySitesRequest: (params: any) => void; | |
getDateRequest: () => void; | |
} | |
// Store interface | |
interface IStore { | |
isFetching: boolean; | |
isFetchingDate: boolean; | |
mySitesData: any; | |
dateField: any; | |
} | |
const mapStateToProps = (store: StoreTypes): IStore => ({ | |
isFetching: getIsFetchingMySites(store), | |
mySitesData: getMySitesData(store), | |
isFetchingDate: isFetchingDate(store), | |
dateField: getDateField(store), | |
}); | |
const mapDispatchToProps = (dispatch: Dispatch): IDispatch => | |
bindActionCreators( | |
{ | |
getMySitesRequest, | |
getDateRequest, | |
}, | |
dispatch, | |
); | |
type ITypes = IDispatch & IStore; | |
class MainPage extends React.Component<ITypes> { | |
state = { | |
priceFile: [], | |
countFile: [], | |
login: '', | |
password: '', | |
isFormReady: false, | |
isAuthError: false, | |
isCountError: false, | |
isPriceError: false, | |
isSuccess: false, | |
isFormError: false, | |
successMessage: '', | |
errorMessage: '', | |
errorPriceMessage: '', | |
errorCountMessage: '', | |
date: '', | |
}; | |
componentDidMount() { | |
const { getDateRequest } = this.props; | |
getDateRequest(); | |
} | |
componentDidUpdate(prevProps: any, prevState: any) { | |
const { mySitesData, isFetching, dateField, isFetchingDate } = this.props; | |
const { isFetching: prevIsFetching, isFetchingDate: prevIsFetchingDate } = prevProps; | |
const authError = get(mySitesData, 'auth', 'NO_FIELD'); | |
const priceError = get(mySitesData, 'price', 'NO_FIELD'); | |
const countError = get(mySitesData, 'count', 'NO_FIELD'); | |
const message = get(mySitesData, 'msg', 'NO_FIELD'); | |
let tempDateJSON = {}; | |
if (typeof dateField === 'string') { | |
tempDateJSON = JSON5.parse(dateField); | |
} | |
const date = get(tempDateJSON, 'date', 'NO_FIELD'); | |
if (date !== 'NO_FIELD' && prevIsFetchingDate && !isFetchingDate) { | |
this.setState({ | |
date: date, | |
}); | |
} | |
if (authError !== 'NO_FIELD' && prevIsFetching && !isFetching) { | |
this.setState({ | |
isAuthError: true, | |
isFormReady: false, | |
}); | |
} | |
if (priceError !== 'NO_FIELD' && prevIsFetching && !isFetching) { | |
this.setState({ | |
isPriceError: true, | |
isFormReady: false, | |
errorPriceMessage: priceError, | |
}); | |
} | |
if (countError !== 'NO_FIELD' && prevIsFetching && !isFetching) { | |
this.setState({ | |
isCountError: true, | |
isFormReady: false, | |
errorCountMessage: countError, | |
}); | |
} | |
if ( | |
(message === 'В магазине нет товаров (не подходящее время?)' || | |
message === '0 товаров успешно обновлены') && | |
prevIsFetching && | |
!isFetching | |
) { | |
this.setState({ | |
isFormError: true, | |
isFormReady: false, | |
errorMessage: message, | |
}); | |
} | |
if ( | |
message !== 'NO_FIELD' && | |
message !== 'В магазине нет товаров (не подходящее время?)' && | |
message !== '0 товаров успешно обновлены' && | |
prevIsFetching && | |
!isFetching | |
) { | |
this.setState( | |
{ | |
isSuccess: true, | |
isFormError: false, | |
isFormReady: false, | |
successMessage: message, | |
// Очистить форму | |
priceFile: [], | |
countFile: [], | |
login: '', | |
password: '', | |
}, | |
() => { | |
this.timeoutRedirect(); | |
}, | |
); | |
} | |
} | |
handleChangeLogin = (event: any) => { | |
const { priceFile, countFile, password, date } = this.state; | |
let isFormReady = false; | |
if (!isEmpty(priceFile) && !isEmpty(countFile) && password !== '' && date !== '' && event.target.value !== '') { | |
isFormReady = true; | |
} | |
this.setState({ | |
login: event.target.value, | |
isFormReady: isFormReady, | |
isAuthError: false, | |
isFormError: false, | |
errorMessage: '', | |
}); | |
}; | |
handleChangePassword = (event: any) => { | |
const { priceFile, countFile, login, date } = this.state; | |
let isFormReady = false; | |
if (!isEmpty(priceFile) && !isEmpty(countFile) && login !== '' && date !== '' && event.target.value !== '') { | |
isFormReady = true; | |
} | |
this.setState({ | |
password: event.target.value, | |
isFormReady: isFormReady, | |
isAuthError: false, | |
isFormError: false, | |
errorMessage: '', | |
}); | |
}; | |
getExtension = (files: any) => { | |
// eslint-disable-next-line | |
const extensionPattern = /\.([0-9a-z]+)(?:[\?#]|$)/i; | |
const checkedFileName = files[0].name.match(extensionPattern); | |
return get(checkedFileName, '1', 'NO_EXTENSION'); | |
}; | |
handleRejectedCheckPriceFile = (files: any) => { | |
// console.log('Rejected Price:', files); | |
const extension = this.getExtension(files); | |
if (extension !== 'NO_EXTENSION') { | |
if (toLower(extension) === 'xls' || toLower(extension) === 'xlsx') { | |
this.handleChangePriceFile(files); | |
} | |
} | |
}; | |
handleChangePriceFile = (files: any) => { | |
// console.log('Price:', files); | |
const { countFile, login, password, date } = this.state; | |
let isFormReady = false; | |
if (!isEmpty(countFile) && login !== '' && password !== '' && date !== '' && !isEmpty(files)) { | |
isFormReady = true; | |
} | |
this.setState({ | |
priceFile: files, | |
isFormReady: isFormReady, | |
isPriceError: false, | |
errorPriceMessage: '', | |
isFormError: false, | |
errorMessage: '', | |
}); | |
}; | |
handleResetPriceFile = (event: any) => { | |
event.stopPropagation(); | |
this.setState({ | |
priceFile: [], | |
isFormReady: false, | |
}); | |
}; | |
handleRejectedCheckCountFile = (files: any) => { | |
// console.log('Rejected Count:', files); | |
const extension = this.getExtension(files); | |
if (extension !== 'NO_EXTENSION') { | |
if (toLower(extension) === 'xls' || toLower(extension) === 'xlsx') { | |
this.handleChangeCountFile(files); | |
} | |
} | |
}; | |
handleChangeCountFile = (files: any) => { | |
// console.log('Count:', files); | |
const { priceFile, login, password, date } = this.state; | |
let isFormReady = false; | |
if (!isEmpty(priceFile) && login !== '' && password !== '' && date !== '' && !isEmpty(files)) { | |
isFormReady = true; | |
} | |
this.setState({ | |
countFile: files, | |
isFormReady: isFormReady, | |
isCountError: false, | |
errorCountMessage: '', | |
isFormError: false, | |
errorMessage: '', | |
}); | |
}; | |
handleResetCountFile = (event: any) => { | |
event.stopPropagation(); | |
this.setState({ | |
countFile: [], | |
isFormReady: false, | |
}); | |
}; | |
handleSubmit = (event: any) => { | |
event.preventDefault(); | |
const { getMySitesRequest } = this.props; | |
const { priceFile, countFile, login, password, date } = this.state; | |
getMySitesRequest({ | |
login, | |
password, | |
date, | |
file_with_price: priceFile[0], | |
file_with_count: countFile[0], | |
}); | |
}; | |
timeoutRedirect = () => { | |
setTimeout(() => { | |
this.redirect(); | |
}, 10000); | |
}; | |
redirect = () => { | |
window.location = 'https://post.lolkekazaza.ru/r/pricelist.php' as any; | |
}; | |
handleKeyPress = (event: any) => { | |
if (event.charCode === 13) { | |
// Prevent the default action to stop scrolling when space is pressed | |
event.preventDefault(); | |
this.redirect(); | |
} | |
}; | |
handleChange = (event: any, { name, value }: { name: string; value: string }) => { | |
const { priceFile, countFile, login, password } = this.state; | |
let isFormReady = false; | |
if (!isEmpty(priceFile) && !isEmpty(countFile) && login !== '' && password !== '' && value !== '') { | |
isFormReady = true; | |
} | |
if (this.state.hasOwnProperty(name)) { | |
this.setState({ | |
[name]: value, | |
isFormReady: isFormReady, | |
isPriceError: false, | |
errorPriceMessage: '', | |
isFormError: false, | |
errorMessage: '', | |
}); | |
} | |
}; | |
render() { | |
const { isFetching, isFetchingDate } = this.props; | |
const { | |
isFormReady, | |
isAuthError, | |
isCountError, | |
isPriceError, | |
login, | |
password, | |
priceFile, | |
countFile, | |
isSuccess, | |
successMessage, | |
errorMessage, | |
errorPriceMessage, | |
errorCountMessage, | |
isFormError, | |
} = this.state; | |
const fileNamePrice = get(priceFile, '[0].name', 'NOT_FILE'); | |
const fileNameCount = get(countFile, '[0].name', 'NOT_FILE'); | |
return ( | |
<div className={styles.container}> | |
{/* Preloader for Date */} | |
{isFetchingDate && ( | |
<ContentPageLayout.FullPage> | |
<Preloader size={10} /> | |
</ContentPageLayout.FullPage> | |
)} | |
{/* Content Data */} | |
{!isFetchingDate && ( | |
<ContentPageLayout.Content> | |
<Container className={styles.paddings}> | |
{/* Logo */} | |
<Logo /> | |
{/* Divider */} | |
<Divider horizontal> | |
<h1 className={styles.formHeader}>Gate</h1> | |
</Divider> | |
<Form size="large" onSubmit={this.handleSubmit} loading={isFetching ? true : false}> | |
{/* Auth */} | |
<Form.Field> | |
<Grid divided="vertically"> | |
<Grid.Row columns={3}> | |
{/* Login */} | |
<Grid.Column width={3}> | |
<label className={styles.labelDirection}>Логин</label> | |
</Grid.Column> | |
<Grid.Column tablet={11} mobile={11} computer={8}> | |
<Form.Input | |
value={login} | |
onChange={this.handleChangeLogin} | |
fluid | |
placeholder="Логин" | |
error={isAuthError ? true : false} | |
type="text" | |
autoComplete="username" | |
/> | |
</Grid.Column> | |
<Grid.Column only="computer" computer={5}> | |
<label className={styles.labelDirection}> | |
Укажите логин и пароль, которые используются для авторизации на сайте «Связной» | |
</label> | |
</Grid.Column> | |
{/* Password */} | |
<Grid.Column width={3}> | |
<label className={styles.labelDirection}>Пароль</label> | |
</Grid.Column> | |
<Grid.Column tablet={11} mobile={11} computer={8}> | |
<Form.Input | |
value={password} | |
onChange={this.handleChangePassword} | |
fluid | |
placeholder="Пароль" | |
error={isAuthError ? true : false} | |
type="password" | |
autoComplete="current-password" | |
/> | |
</Grid.Column> | |
</Grid.Row> | |
</Grid> | |
</Form.Field> | |
{/* Auth Error message */} | |
<Grid divided="vertically"> | |
<Grid.Row columns={2}> | |
<Grid.Column only="computer" width={3} /> | |
<Grid.Column width={11}> | |
{isAuthError && <label className={styles.errorLabel}>Авторизационные данные не корректны</label>} | |
</Grid.Column> | |
</Grid.Row> | |
</Grid> | |
{/* DatePicker */} | |
<Form.Field> | |
<Grid divided="vertically"> | |
<Grid.Row columns={3}> | |
{/* Login */} | |
<Grid.Column width={3}> | |
<label className={styles.labelDirection}>Дата поставки</label> | |
</Grid.Column> | |
<Grid.Column tablet={11} mobile={11} computer={8}> | |
<DateInput | |
name="date" | |
placeholder="Date" | |
value={this.state.date} | |
iconPosition="left" | |
onChange={this.handleChange} | |
animation="fade" | |
closable | |
dateFormat="DD.MM.YYYY" | |
/> | |
</Grid.Column> | |
<Grid.Column only="computer" computer={5}> | |
<label className={styles.labelDirection} /> | |
</Grid.Column> | |
</Grid.Row> | |
</Grid> | |
</Form.Field> | |
{/* DropeZones */} | |
<Grid divided="vertically"> | |
<Grid.Row columns={3}> | |
<Grid.Column width={3}> | |
<label>Документы</label> | |
</Grid.Column> | |
<Grid.Column tablet={11} mobile={11} computer={8}> | |
{/* Price File */} | |
<Dropzone | |
onDropRejected={this.handleRejectedCheckPriceFile} | |
onDropAccepted={this.handleChangePriceFile} | |
accept="application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | |
> | |
{({ getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, rejectedFiles }) => ( | |
<div className={styles.crossRelativeParent}> | |
<DropContainer | |
{...getRootProps({ | |
isDragActive, | |
isDragAccept, | |
isDragReject, | |
})} | |
> | |
<input {...getInputProps()} /> | |
<p className={styles.DropZoneHeader}>Загрузите накладную с заказом</p> | |
{fileNamePrice !== 'NOT_FILE' ? ( | |
<div className={styles.choosenFile}> | |
<span className={styles.btnclose} onClick={this.handleResetPriceFile} /> | |
<img src={ExcelImage} alt="XLS" width={64} height={64} /> | |
<span>{fileNamePrice}</span> | |
</div> | |
) : ( | |
<div className={styles.choosenFile}> | |
<img src={DropZoneDefaultImage} alt="Upload file" width={64} height={64} /> | |
<span className={styles.textForFormats}>xls, xlsx</span> | |
</div> | |
)} | |
</DropContainer> | |
</div> | |
)} | |
</Dropzone> | |
{/* Count File */} | |
<Dropzone | |
onDropRejected={this.handleRejectedCheckCountFile} | |
onDropAccepted={this.handleChangeCountFile} | |
accept="application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | |
> | |
{({ getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject }) => ( | |
<div className={styles.crossRelativeParent}> | |
<DropContainer | |
{...getRootProps({ | |
isDragActive, | |
isDragAccept, | |
isDragReject, | |
})} | |
> | |
<input {...getInputProps()} /> | |
<p className={styles.DropZoneHeader}>Загрузите накладную с наличием</p> | |
{fileNameCount !== 'NOT_FILE' ? ( | |
<div className={styles.choosenFile}> | |
<span className={styles.btnclose} onClick={this.handleResetCountFile} /> | |
<img src={ExcelImage} alt="XLS" width={64} height={64} /> | |
<span>{fileNameCount}</span> | |
</div> | |
) : ( | |
<div className={styles.choosenFile}> | |
<img src={DropZoneDefaultImage} alt="Upload file" width={64} height={64} /> | |
<span className={styles.textForFormats}>xls, xlsx</span> | |
</div> | |
)} | |
</DropContainer> | |
</div> | |
)} | |
</Dropzone> | |
</Grid.Column> | |
{/* This is mock */} | |
<Grid.Column only="computer" computer={5} /> | |
</Grid.Row> | |
</Grid> | |
{/* Files Error message */} | |
<Grid divided="vertically"> | |
<Grid.Row columns={2}> | |
<Grid.Column only="computer" width={3} /> | |
<Grid.Column width={11}> | |
{isCountError && ( | |
<label | |
className={styles.errorLabel} | |
>{`Ошибка в накладной с наличием: ${errorCountMessage}`}</label> | |
)} | |
</Grid.Column> | |
<Grid.Column only="computer" width={3} /> | |
<Grid.Column width={11}> | |
{isPriceError && ( | |
<label | |
className={styles.errorLabel} | |
>{`Ошибка в накладной с заказами: ${errorPriceMessage}`}</label> | |
)} | |
</Grid.Column> | |
</Grid.Row> | |
</Grid> | |
{/* Divider */} | |
<Divider /> | |
{/* Buttons */} | |
{!isSuccess && ( | |
<EnabledButton | |
size="massive" | |
icon | |
labelPosition="right" | |
type="submit" | |
disabled={isFormReady ? false : true} | |
> | |
Отправить | |
<Icon name="arrow right" /> | |
</EnabledButton> | |
)} | |
{isFormError && <FormErrorBlock errorMessage={errorMessage} />} | |
</Form> | |
{isSuccess && ( | |
<SuccessBlock | |
onClickRedirect={this.redirect} | |
handleKeyPress={this.handleKeyPress} | |
successMessage={successMessage} | |
/> | |
)} | |
{/* Version */} | |
<span className={styles.version}>Версия: 0.0.0</span> | |
</Container> | |
</ContentPageLayout.Content> | |
)} | |
</div> | |
); | |
} | |
} | |
export default connect( | |
mapStateToProps, | |
mapDispatchToProps, | |
)(MainPage); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment