Merge remote-tracking branch 'origin/main' into main
This commit is contained in:
commit
7d07651323
@ -3,6 +3,7 @@ import { HeaderUi } from "../widgets/loyaout/ui/HeaderUi";
|
|||||||
import { FooterUi } from "../widgets/loyaout/ui/FooterUi";
|
import { FooterUi } from "../widgets/loyaout/ui/FooterUi";
|
||||||
import { AuctionPage } from "../pages/AuctionPage";
|
import { AuctionPage } from "../pages/AuctionPage";
|
||||||
import { AuthPage } from "../pages/AuthPage";
|
import { AuthPage } from "../pages/AuthPage";
|
||||||
|
import { PrivateRoute } from "./PrivateRoute";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BrowserRouter as Router,
|
BrowserRouter as Router,
|
||||||
@ -20,7 +21,9 @@ function App() {
|
|||||||
<div>
|
<div>
|
||||||
<Router>
|
<Router>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/auction" element={<AuctionPage />} />
|
<Route path='/auction' element={<PrivateRoute/>}>
|
||||||
|
<Route path='/auction' element={<AuctionPage/>}/>
|
||||||
|
</Route>
|
||||||
<Route path="/auth" element={<AuthPage />} />
|
<Route path="/auth" element={<AuthPage />} />
|
||||||
<Route path="*" element={<Navigate to="/auth" replace />} />
|
<Route path="*" element={<Navigate to="/auth" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
7
src/app/PrivateRoute.tsx
Normal file
7
src/app/PrivateRoute.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Navigate, Outlet } from 'react-router-dom';
|
||||||
|
import { getCookie } from "typescript-cookie";
|
||||||
|
|
||||||
|
export const PrivateRoute = () => {
|
||||||
|
return getCookie('authToken') ? <Outlet /> : <Navigate to="/auth" />;
|
||||||
|
}
|
18
src/entities/Ui/SearchUi/SearchUi.tsx
Normal file
18
src/entities/Ui/SearchUi/SearchUi.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Form from "react-bootstrap/Form";
|
||||||
|
|
||||||
|
import loupe from "../../../shared/images/loupe.png";
|
||||||
|
|
||||||
|
import styles from "./searchUi.module.scss";
|
||||||
|
|
||||||
|
export const SearchUi = () => {
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<Form.Control
|
||||||
|
type="text"
|
||||||
|
placeholder="Найти аукцион"
|
||||||
|
/>
|
||||||
|
<img src={loupe} alt='loupe' />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
1
src/entities/Ui/SearchUi/index.ts
Normal file
1
src/entities/Ui/SearchUi/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { SearchUi } from "./SearchUi"
|
25
src/entities/Ui/SearchUi/searchUi.module.scss
Normal file
25
src/entities/Ui/SearchUi/searchUi.module.scss
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 2px solid gray;
|
||||||
|
max-width: 300px;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
input {
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
}
|
95
src/features/auth/ui/AuthLoginFormUi/AuthLoginFormUi.tsx
Normal file
95
src/features/auth/ui/AuthLoginFormUi/AuthLoginFormUi.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
import { ButtonUi, ButtonUiType } from "../../../../shared/UI/ButtonUi";
|
||||||
|
|
||||||
|
import Form from 'react-bootstrap/Form';
|
||||||
|
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import * as yup from 'yup';
|
||||||
|
|
||||||
|
import { AuthResponsePayload } from '../../../../types';
|
||||||
|
import { api } from "../../../../query/query";
|
||||||
|
import { setCookie } from "typescript-cookie";
|
||||||
|
import styles from "./authLoginForm.module.scss";
|
||||||
|
|
||||||
|
export const AuthLoginFormUi = () => {
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const schema = yup.object().shape({
|
||||||
|
username: yup.string()
|
||||||
|
.min(3, 'Минимум 3 символа!')
|
||||||
|
.max(16, 'Максимум 16 символов!')
|
||||||
|
.required('Это поле обязательное.'),
|
||||||
|
|
||||||
|
password: yup.string().required('Это обязательное поле.').min(6, 'Минимум 6 символов!'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
handleSubmit,
|
||||||
|
handleChange,
|
||||||
|
handleBlur,
|
||||||
|
errors,
|
||||||
|
values,
|
||||||
|
setFieldError
|
||||||
|
} = useFormik({
|
||||||
|
initialValues: {
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
validationSchema: schema,
|
||||||
|
onSubmit: async (values): Promise<AuthResponsePayload> => {
|
||||||
|
return await api.post('/authorization', {
|
||||||
|
username: values.username,
|
||||||
|
password: values.password
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.data[0]) {
|
||||||
|
setFieldError('username', res.data[0])
|
||||||
|
setFieldError('password', res.data[0])
|
||||||
|
} else {
|
||||||
|
setCookie('authToken', res.data!.authToken)
|
||||||
|
setCookie('refreshToken', res.data!.refreshToken)
|
||||||
|
navigate("/auction")
|
||||||
|
}
|
||||||
|
return res.data as AuthResponsePayload
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form noValidate className={styles.container} onSubmit={handleSubmit}>
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Control
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
placeholder="username"
|
||||||
|
onChange={handleChange}
|
||||||
|
name="username"
|
||||||
|
onBlur={handleBlur}
|
||||||
|
value={values.username}
|
||||||
|
isInvalid={!!errors.username}
|
||||||
|
/>
|
||||||
|
<Form.Control.Feedback type="invalid">
|
||||||
|
{errors.username}
|
||||||
|
</Form.Control.Feedback>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Group>
|
||||||
|
<Form.Control
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
placeholder="password"
|
||||||
|
onChange={handleChange}
|
||||||
|
value={values.password}
|
||||||
|
name="password"
|
||||||
|
onBlur={handleBlur}
|
||||||
|
isInvalid={!!errors.password}
|
||||||
|
/>
|
||||||
|
<Form.Control.Feedback type="invalid">
|
||||||
|
{errors.password}
|
||||||
|
</Form.Control.Feedback>
|
||||||
|
</Form.Group>
|
||||||
|
<ButtonUi title={'Войти'} variant={ButtonUiType.PRIMARY} type={'submit'}/>
|
||||||
|
</Form >
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
.container {
|
||||||
|
padding: 20px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
row-gap: 15px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
1
src/features/auth/ui/AuthLoginFormUi/index.ts
Normal file
1
src/features/auth/ui/AuthLoginFormUi/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { AuthLoginFormUi } from "./AuthLoginFormUi";
|
@ -3,19 +3,19 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { BreadCrumbsUi } from "../../shared/UI/BreadCrumbsUi";
|
import { BreadCrumbsUi } from "../../shared/UI/BreadCrumbsUi";
|
||||||
import {ButtonUi, ButtonUiType} from "../../shared/UI/ButtonUi";
|
import {ButtonUi, ButtonUiType} from "../../shared/UI/ButtonUi";
|
||||||
import { LoaderUi } from "../../shared/UI/LoaderUi/LoaderUi";
|
import { LoaderUi } from "../../shared/UI/LoaderUi/LoaderUi";
|
||||||
import { DefaultDropDown } from "../../entities/DefaultDropDown";
|
import { AuctionFilters } from "../../widgets/AuctionFilters";
|
||||||
import { DefaultPagination } from "../../entities/DefaultPagination";
|
import { DefaultPagination } from "../../entities/DefaultPagination";
|
||||||
import { AddAuctionModal } from "../../widgets/AddAuctionModal";
|
import { AddAuctionModal } from "../../widgets/AddAuctionModal";
|
||||||
import { EditAuctionModal } from "../../widgets/EditAuctionModal";
|
import { EditAuctionModal } from "../../widgets/EditAuctionModal";
|
||||||
import Form from 'react-bootstrap/Form';
|
import { SearchUi } from "../../entities/Ui/SearchUi";
|
||||||
|
|
||||||
|
import Table from 'react-bootstrap/Table';
|
||||||
|
|
||||||
import { AuctionItem } from "../../types";
|
import { AuctionItem } from "../../types";
|
||||||
import { filterItems } from "../../constants/data";
|
|
||||||
|
|
||||||
import { api } from "../../query/query";
|
import { api } from "../../query/query";
|
||||||
|
import { getCookie } from "typescript-cookie";
|
||||||
|
|
||||||
import loupe from "../../shared/images/loupe.png"
|
|
||||||
import filterImg from "../../shared/images/filter.png"
|
|
||||||
import close from "../../shared/images/close.png"
|
|
||||||
import edit from "../../shared/images/edit.png"
|
import edit from "../../shared/images/edit.png"
|
||||||
|
|
||||||
import styles from "./auctionPage.module.scss"
|
import styles from "./auctionPage.module.scss"
|
||||||
@ -24,13 +24,7 @@ export const AuctionPage = () => {
|
|||||||
|
|
||||||
const [auctionItems, setAuctionItems] = useState<AuctionItem[]>([])
|
const [auctionItems, setAuctionItems] = useState<AuctionItem[]>([])
|
||||||
|
|
||||||
const [openAddModal, setOpenAddModal] = useState(false)
|
const [currentEditAuction, setCurrentEditAuction] = useState<AuctionItem>({
|
||||||
|
|
||||||
const [loader, setLoader] = useState(false)
|
|
||||||
|
|
||||||
const [openEditModal, setOpenEditModal] = useState(false)
|
|
||||||
|
|
||||||
const [currentEditAuction, setCurrentEditAuction] = useState({
|
|
||||||
uuid: '018c448e-c7d7-7092-b151-08f8d8bc410e',
|
uuid: '018c448e-c7d7-7092-b151-08f8d8bc410e',
|
||||||
dateCreate: "2023-12-07 16:54:17",
|
dateCreate: "2023-12-07 16:54:17",
|
||||||
name: '',
|
name: '',
|
||||||
@ -41,6 +35,12 @@ export const AuctionPage = () => {
|
|||||||
status: 'Сбор заявок'
|
status: 'Сбор заявок'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const [openAddModal, setOpenAddModal] = useState(false)
|
||||||
|
|
||||||
|
const [loader, setLoader] = useState(false)
|
||||||
|
|
||||||
|
const [openEditModal, setOpenEditModal] = useState(false)
|
||||||
|
|
||||||
const addNewAuction = (newAction: string) => {
|
const addNewAuction = (newAction: string) => {
|
||||||
setAuctionItems((prevValue) => [...prevValue, {
|
setAuctionItems((prevValue) => [...prevValue, {
|
||||||
uuid: '018c448e-c7d7-7092-b151-08f8d8bc480e',
|
uuid: '018c448e-c7d7-7092-b151-08f8d8bc480e',
|
||||||
@ -91,7 +91,7 @@ export const AuctionPage = () => {
|
|||||||
setAuctionItems(res.data)
|
setAuctionItems(res.data)
|
||||||
setLoader(false)
|
setLoader(false)
|
||||||
})
|
})
|
||||||
}, [])
|
}, [getCookie('authToken')])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.home}>
|
<div className={styles.home}>
|
||||||
@ -102,60 +102,42 @@ export const AuctionPage = () => {
|
|||||||
<div className={styles.home__info}>
|
<div className={styles.home__info}>
|
||||||
<div className={styles.info__top}>
|
<div className={styles.info__top}>
|
||||||
<ButtonUi event={() => setOpenAddModal(true)} title={'+ Добавить аукцион'} variant={ButtonUiType.PRIMARY} />
|
<ButtonUi event={() => setOpenAddModal(true)} title={'+ Добавить аукцион'} variant={ButtonUiType.PRIMARY} />
|
||||||
<div className={styles.info__search}>
|
<SearchUi />
|
||||||
<Form.Control
|
|
||||||
type="text"
|
|
||||||
placeholder="Найти аукцион"
|
|
||||||
/>
|
|
||||||
<img src={loupe} alt='loupe' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={styles.info__filters}>
|
|
||||||
<img className={styles.info__filtersImg} src={filterImg} alt="filter" />
|
|
||||||
<div className={styles.info__filters__items}>
|
|
||||||
{filterItems.map((item) => {
|
|
||||||
return <DefaultDropDown key={item.title} title={item.title} options={item.options} />
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<ButtonUi
|
|
||||||
title={'Сбросить фильтры'}
|
|
||||||
variant={ButtonUiType.PRIMARY}
|
|
||||||
img={<img src={close} alt="cross" />} />
|
|
||||||
<ButtonUi title={'Применить'} variant={ButtonUiType.INFO} />
|
|
||||||
</div>
|
</div>
|
||||||
|
<AuctionFilters />
|
||||||
<div className={styles.info__tableWrapper}>
|
<div className={styles.info__tableWrapper}>
|
||||||
{loader ?
|
{loader ?
|
||||||
<LoaderUi animation={'border'} variant={'primary'} />
|
<LoaderUi animation={'border'} variant={'primary'} />
|
||||||
:
|
:
|
||||||
<>
|
<>
|
||||||
<table>
|
<Table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr className={styles.tableItem}>
|
<tr className={styles.tableItem}>
|
||||||
<td>№</td>
|
<td>№</td>
|
||||||
<td>Название аукциона</td>
|
<td>Название аукциона</td>
|
||||||
<td>Прием заявок</td>
|
<td>Прием заявок</td>
|
||||||
<td>Проведение</td>
|
<td>Проведение</td>
|
||||||
<td>Статус</td>
|
<td>Статус</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{auctionItems.map((item) => {
|
{auctionItems.map((item) => {
|
||||||
return <tr className={styles.tableItem} key={item.uuid}>
|
return <tr className={styles.tableItem} key={item.uuid}>
|
||||||
<td>{item.uuid}</td>
|
<td>{item.uuid}</td>
|
||||||
<td className={styles.tableItem__name}>{item.name}</td>
|
<td className={styles.tableItem__name}>{item.name}</td>
|
||||||
<td>{getCorrectDate((item.requestsStartDate))}</td>
|
<td>{getCorrectDate((item.requestsStartDate))}</td>
|
||||||
<td>{item.dateCreate}</td>
|
<td>{item.dateCreate}</td>
|
||||||
<td>{item.status}</td>
|
<td>{item.status}</td>
|
||||||
<td className={styles.tableItem__edit}>
|
<td className={styles.tableItem__edit}>
|
||||||
<img src={edit} alt="edit" onClick={() => {
|
<img src={edit} alt="edit" onClick={() => {
|
||||||
setCurrentEditAuction(item)
|
setCurrentEditAuction(item)
|
||||||
setOpenEditModal(true)
|
setOpenEditModal(true)
|
||||||
}}/>
|
}}/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</Table>
|
||||||
<DefaultPagination pageCount={10} currentPage={2} />
|
<DefaultPagination pageCount={10} currentPage={2} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
&__info {
|
&__info {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 35px 0;
|
margin: 35px 0;
|
||||||
min-height: 168px;
|
min-height: 281px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
row-gap: 15px;
|
row-gap: 15px;
|
||||||
@ -23,8 +23,8 @@
|
|||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
width: 5px;
|
width: 3px;
|
||||||
height: 10px;
|
height: 5px;
|
||||||
background: gray;
|
background: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,79 +36,8 @@
|
|||||||
max-height: 40px;
|
max-height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__search {
|
|
||||||
display: flex;
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 2px solid gray;
|
|
||||||
max-width: 300px;
|
|
||||||
width: 100%;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
input {
|
|
||||||
border: none;
|
|
||||||
width: 100%;
|
|
||||||
outline: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__filters {
|
|
||||||
display: flex;
|
|
||||||
padding: 10px 15px;
|
|
||||||
background-color: #2f95f2;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
min-width: 1213px;
|
|
||||||
|
|
||||||
&Img {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__items {
|
|
||||||
display: flex;
|
|
||||||
column-gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 17px;
|
|
||||||
max-width: 200px;
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
width: 100%;
|
|
||||||
cursor: pointer;
|
|
||||||
column-gap: 8px;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 17px;
|
|
||||||
height: 17px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter {
|
|
||||||
&__reset {
|
|
||||||
background-color: #0668c2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__confirm {
|
|
||||||
background-color: #00529d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__tableWrapper {
|
&__tableWrapper {
|
||||||
min-width: 1243px;
|
min-width: 1213px;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.tableItem {
|
.tableItem {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@ -123,6 +52,8 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 15px;
|
width: 15px;
|
||||||
height: 15px;
|
height: 15px;
|
||||||
@ -133,34 +64,6 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
row-gap: 10px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
|
|
||||||
thead {
|
|
||||||
display: grid;
|
|
||||||
|
|
||||||
tr {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 30% 20% 16% 12% 14% 8%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody {
|
|
||||||
display: grid;
|
|
||||||
row-gap: 10px;
|
|
||||||
|
|
||||||
tr {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 30% 20% 16% 12% 14% 8%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,99 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
|
|
||||||
import { ButtonUi, ButtonUiType } from "../../shared/UI/ButtonUi";
|
import { AuthLoginFormUi } from "../../features/auth/ui/AuthLoginFormUi";
|
||||||
|
|
||||||
import Form from 'react-bootstrap/Form';
|
|
||||||
|
|
||||||
import { useFormik } from 'formik';
|
|
||||||
import * as yup from 'yup';
|
|
||||||
|
|
||||||
import { api } from "../../query/query";
|
|
||||||
import { setCookie } from "typescript-cookie";
|
|
||||||
import { AuthResponsePayload } from "../../types";
|
|
||||||
|
|
||||||
import styles from "./authPage.module.scss"
|
import styles from "./authPage.module.scss"
|
||||||
|
|
||||||
export const AuthPage = () => {
|
export const AuthPage = () => {
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const schema = yup.object().shape({
|
|
||||||
username: yup.string()
|
|
||||||
.min(3, 'Минимум 3 символа!')
|
|
||||||
.max(16, 'Максимум 16 символов!')
|
|
||||||
.required('Это поле обязательное.'),
|
|
||||||
|
|
||||||
password: yup.string().required('Это обязательное поле.').min(6, 'Минимум 6 символов!'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
handleSubmit,
|
|
||||||
handleChange,
|
|
||||||
handleBlur,
|
|
||||||
errors,
|
|
||||||
values,
|
|
||||||
setFieldError
|
|
||||||
} = useFormik({
|
|
||||||
initialValues: {
|
|
||||||
username: '',
|
|
||||||
password: '',
|
|
||||||
},
|
|
||||||
validationSchema: schema,
|
|
||||||
onSubmit: async (values): Promise<AuthResponsePayload> => {
|
|
||||||
return await api.post('/authorization', {
|
|
||||||
username: values.username,
|
|
||||||
password: values.password
|
|
||||||
}).then((res) => {
|
|
||||||
if (res.data[0]) {
|
|
||||||
setFieldError('username', res.data[0])
|
|
||||||
setFieldError('password', res.data[0])
|
|
||||||
} else {
|
|
||||||
setCookie('authToken', res.data!.authToken)
|
|
||||||
setCookie('refreshToken', res.data!.refreshToken)
|
|
||||||
navigate("/auction")
|
|
||||||
}
|
|
||||||
return res.data as AuthResponsePayload
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.auth}>
|
<div className={styles.auth}>
|
||||||
<h1 className={styles.auth__title}>Аукционная площадка для проведения закупок ГК Проф-Пресс</h1>
|
<h1 className={styles.auth__title}>Аукционная площадка для проведения закупок ГК Проф-Пресс</h1>
|
||||||
<Form noValidate className={styles.auth__form} onSubmit={handleSubmit}>
|
<AuthLoginFormUi />
|
||||||
<Form.Group>
|
|
||||||
<Form.Control
|
|
||||||
type="text"
|
|
||||||
id="username"
|
|
||||||
placeholder="username"
|
|
||||||
onChange={handleChange}
|
|
||||||
name="username"
|
|
||||||
onBlur={handleBlur}
|
|
||||||
value={values.username}
|
|
||||||
isInvalid={!!errors.username}
|
|
||||||
/>
|
|
||||||
<Form.Control.Feedback type="invalid">
|
|
||||||
{errors.username}
|
|
||||||
</Form.Control.Feedback>
|
|
||||||
</Form.Group>
|
|
||||||
<Form.Group>
|
|
||||||
<Form.Control
|
|
||||||
type="password"
|
|
||||||
id="password"
|
|
||||||
placeholder="password"
|
|
||||||
onChange={handleChange}
|
|
||||||
value={values.password}
|
|
||||||
name="password"
|
|
||||||
onBlur={handleBlur}
|
|
||||||
isInvalid={!!errors.password}
|
|
||||||
/>
|
|
||||||
<Form.Control.Feedback type="invalid">
|
|
||||||
{errors.password}
|
|
||||||
</Form.Control.Feedback>
|
|
||||||
</Form.Group>
|
|
||||||
<ButtonUi title={'Войти'} variant={ButtonUiType.PRIMARY} type={'submit'}/>
|
|
||||||
</Form >
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,4 @@
|
|||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__form {
|
|
||||||
padding: 20px 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
row-gap: 15px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ api.interceptors.response.use(undefined, async function (error) {
|
|||||||
if (error?.response?.status === 401) {
|
if (error?.response?.status === 401) {
|
||||||
removeCookie('authToken')
|
removeCookie('authToken')
|
||||||
removeCookie('refreshToken')
|
removeCookie('refreshToken')
|
||||||
|
window.location.replace("/auth")
|
||||||
}
|
}
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
})
|
})
|
||||||
|
28
src/widgets/AuctionFilters/AuctionFilters.tsx
Normal file
28
src/widgets/AuctionFilters/AuctionFilters.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { DefaultDropDown } from "../../entities/DefaultDropDown";
|
||||||
|
import { ButtonUi, ButtonUiType } from "../../shared/UI/ButtonUi";
|
||||||
|
|
||||||
|
import { filterItems } from "../../constants/data";
|
||||||
|
|
||||||
|
import filterImg from "../../shared/images/filter.png";
|
||||||
|
import close from "../../shared/images/close.png";
|
||||||
|
import styles from "./auctionFilters.module.scss";
|
||||||
|
|
||||||
|
export const AuctionFilters = () => {
|
||||||
|
return (
|
||||||
|
<div className={styles.filter}>
|
||||||
|
<img className={styles.filter__img} src={filterImg} alt="filter" />
|
||||||
|
<div className={styles.filter__items}>
|
||||||
|
{filterItems.map((item) => {
|
||||||
|
return <DefaultDropDown key={item.title} title={item.title} options={item.options} />
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<ButtonUi
|
||||||
|
title={'Сбросить фильтры'}
|
||||||
|
variant={ButtonUiType.PRIMARY}
|
||||||
|
img={<img src={close} alt="cross" />} />
|
||||||
|
<ButtonUi title={'Применить'} variant={ButtonUiType.INFO} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
36
src/widgets/AuctionFilters/auctionFilters.module.scss
Normal file
36
src/widgets/AuctionFilters/auctionFilters.module.scss
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
.filter {
|
||||||
|
display: flex;
|
||||||
|
padding: 10px 15px;
|
||||||
|
background-color: #2f95f2;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
min-width: 1213px;
|
||||||
|
|
||||||
|
&__img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__items {
|
||||||
|
display: flex;
|
||||||
|
column-gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 17px;
|
||||||
|
max-width: 200px;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
column-gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
src/widgets/AuctionFilters/index.ts
Normal file
1
src/widgets/AuctionFilters/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { AuctionFilters } from "./AuctionFilters"
|
Loading…
Reference in New Issue
Block a user