Skip to content

Instantly share code, notes, and snippets.

@taksenov
Created March 5, 2021 12:03
Show Gist options
  • Save taksenov/cc34caff2354e806e36ef0ffd0b8127f to your computer and use it in GitHub Desktop.
Save taksenov/cc34caff2354e806e36ef0ffd0b8127f to your computer and use it in GitHub Desktop.
Find anti patterns
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}>
Укажите логин и пароль, которые используются для авторизации на сайте &laquo;Связной&raquo;
</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