This commit is contained in:
Mikola
2023-12-08 13:35:56 +03:00
commit 78f796b59d
45 changed files with 32713 additions and 0 deletions

33
src/App.js Normal file
View File

@ -0,0 +1,33 @@
import Header from "./components/Header/Header";
import Home from "./pages/Home/Home";
import Auth from "./pages/Auth/Auth"
import Footer from "./components/Footer/Footer";
import {
BrowserRouter as Router,
Route,
Routes,
Navigate
} from "react-router-dom";
import 'bootstrap/dist/css/bootstrap.min.css';
function App() {
return (
<div className="App">
<Header />
<div className='container'>
<Router>
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/auth" element={<Auth />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</Router>
</div>
<Footer />
</div>
);
}
export default App;

8
src/App.test.js Normal file
View File

@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
src/assets/images/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
src/assets/images/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
src/assets/images/loupe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -0,0 +1,3 @@
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 0.5L5 4.5L9 0.5" stroke="#5B6871"/>
</svg>

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,18 @@
import React from "react";
import Breadcrumb from 'react-bootstrap/Breadcrumb';
const BreadCrumbs = ({links}) => {
return (
<Breadcrumb>
{links.map((link, index) => {
return (
<Breadcrumb.Item active key={index}>
{link.name}
</Breadcrumb.Item>
)
})}
</Breadcrumb>
);
};
export default BreadCrumbs;

View File

@ -0,0 +1,10 @@
.breadcrumbs {
display: flex;
padding: 15px 0;
column-gap: 10px;
a {
color: #b6b6b6;
text-decoration: none;
}
}

View File

@ -0,0 +1,25 @@
import React from "react";
import Dropdown from 'react-bootstrap/Dropdown';
const DropDownFilter = ({title}) => {
return (
<Dropdown data-bs-theme="dark">
<Dropdown.Toggle id="dropdown-button-dark-example" variant="primary">
{title}
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item href="#/action-1" active>
Action
</Dropdown.Item>
<Dropdown.Item href="#/action-2">Another action</Dropdown.Item>
<Dropdown.Item href="#/action-3">Something else</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item href="#/action-4">Separated link</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
};
export default DropDownFilter;

View File

@ -0,0 +1,16 @@
import React from "react";
import arrow from "../../assets/images/select.svg"
import "./filterItem.scss";
const FilterItem = ({title}) => {
return (
<div className="filterItem">
<p>{title}</p>
<img src={arrow} alt="arrow" />
</div>
);
};
export default FilterItem;

View File

@ -0,0 +1,11 @@
.filterItem {
display: flex;
column-gap: 8px;
padding: 5px 10px;
border-radius: 5px;
background-color: white;
align-items: center;
cursor: pointer;
width: 150px;
justify-content: space-between;
}

View File

@ -0,0 +1,70 @@
import React from "react";
import Button from 'react-bootstrap/Button';
import "./footer.scss";
const Footer = () => {
return (
<footer className="footer">
<div className="container footer__wrapper">
<div className="footer__info">
<div className="footer__infoTop">
<p>
Возникли вопросы по работе с сервисом, отправьте
заявку на консультацию
</p>
<span>прикрепить файл</span>
<Button variant="primary">
Получить консультацию
</Button>
</div>
<div className="footer__infoBottom">
<p>OOO "Издательский дом "Проф-Пресс", 2023</p>
<p>Политика конфиденциальности</p>
<p>Разработка сайта</p>
</div>
</div>
<div className="footer__items">
<div className="footer__item">
<div className="footer__itemTop">
<h3>Правила площадки</h3>
<a href="/">Правила участия</a>
</div>
<div className="footer__itemBottom">
<span>Г. Ростов-на-Дону, ул.<br/>Доватора, 111/11</span>
<span>пн-пт 8:00-18:00</span>
</div>
</div>
<div className="footer__item">
<div className="footer__itemTop">
<h3>Полезное</h3>
<a href="/">Размещение предложения</a>
<a href="/">Видео</a>
<a href="/">Статьи</a>
</div>
<div className="footer__itemBottom">
<span>+7 (861) 111-11-11</span>
<span>+7 (861) 111-11-11</span>
</div>
</div>
<div className="footer__item">
<div className="footer__itemTop">
<h3>Специалистам</h3>
<a href="/">Технические каталоги</a>
<a href="/">Готовые решения</a>
<a href="/">Нормы/ГОСТы</a>
<a href="/">Сотрудничество</a>
</div>
<div className="footer__itemBottom">
<span>vash-email@mail.ru</span>
<span>vash-email@mail.ru</span>
</div>
</div>
</div>
</div>
</footer>
);
};
export default Footer;

View File

@ -0,0 +1,115 @@
.footer {
padding: 50px 0 35px;
background-color: #2f95f2;
color: white;
height: 450px;
@media (max-width: 1155px) {
padding: 10px 0;
height: auto;
}
&__wrapper {
@media (max-width: 1155px) {
flex-direction: column;
row-gap: 20px;
}
}
&__info {
display: flex;
flex-direction: column;
max-width: 350px;
justify-content: space-between;
height: 100%;
margin-right: 200px;
@media (max-width: 1155px) {
margin-right: 0;
max-width: inherit;
flex-direction: row;
width: 100%;
}
&Top {
display: flex;
flex-direction: column;
@media (max-width: 1155px) {
max-width: 500px;
}
p {
font-weight: 700;
font-size: 20px;
}
span {
cursor: pointer;
margin: 20px 15px 25px;
}
button {
padding: 10px;
border-radius: 8px;
cursor: pointer;
outline: none;
border: none;
max-width: 250px;
color: white;
font-weight: 600;
background-color: #00529d;
}
}
&Bottom {
display: flex;
flex-direction: column;
}
}
&__items {
display: flex;
width: 100%;
justify-content: space-between;
height: 100%;
@media (max-width: 630px) {
flex-wrap: wrap;
row-gap: 15px;
}
}
&__item {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
@media (max-width: 630px) {
width: 200px;
height: 201px;
}
@media (max-width: 455px) {
height: auto;
}
&Top {
display: flex;
flex-direction: column;
row-gap: 10px;
a {
text-decoration: none;
color: white;
}
}
&Bottom {
display: flex;
flex-direction: column;
row-gap: 10px;
}
}
}

View File

@ -0,0 +1,78 @@
import React, { useState } from "react";
import NavigationItem from "../NavigationItem/NavigationItem";
import SocialItem from "../SocialItem/SocialItem";
import instagram from "../../assets/images/socials/instagram.png";
import whatsapp from "../../assets/images/socials/whatsapp.png";
import faceBook from "../../assets/images/socials/facebook.png";
import profile from "../../assets/images/profile-user.png";
import burger from "../../assets/images/burger-bar.png";
import close from "../../assets/images/closeBurger.png"
import "./header.scss";
const Header = () => {
const navigationItems = ["Аукционы", "Поставщики", "Одобрение заявок"]
const socials = [
{
name: "faceBook",
img: faceBook,
href: "#"
},
{
name: "instagram",
img: instagram,
href: "#"
},
{
name: "whatsapp",
img: whatsapp,
href: "#"
},
]
const [burgerOpen, setBurgerOpen] = useState(false)
return (
<header className="header">
<div className="container">
<h1 className="header__logo">Logo</h1>
<nav className="header__nav">
{navigationItems.map((item) => {
return <NavigationItem title={item} key={item} />
})}
</nav>
<div className="header__info">
<span className="header__infoPhone">+7 (861) 111-11-11</span>
<div className="header__infoSocials">
{socials.map((social) => {
return <SocialItem href={social.href} img={social.img} key={social.name} />
})}
</div>
<div className="header__infoProfile">
<img src={profile} alt="profile" />
</div>
</div>
<div className="header__burger" onClick={() => setBurgerOpen(!burgerOpen)}>
{burgerOpen ? <img className='burgerClose' src={close} alt='close'/> : <img className='burgerOpen' src={burger} alt='burger' /> }
</div>
{burgerOpen &&
<div className='header__dropDown'>
<nav className="header__dropDownNavMenu">
{navigationItems.map((item) => {
return <NavigationItem title={item} key={item} />
})}
</nav>
<div className="header__infoSocials">
{socials.map((social) => {
return <SocialItem href={social.href} img={social.img} key={social.name} />
})}
</div>
<span className="header__infoPhone">+7 (861) 111-11-11</span>
</div>
}
</div>
</header>
);
};
export default Header;

View File

@ -0,0 +1,108 @@
.header {
background: #2f95f2;
padding: 10px 0;
position: relative;
color: white;
&__logo {
font-size: 30px;
font-weight: 700;
margin-bottom: 0;
line-height: 1.4;
}
&__nav {
display: flex;
column-gap: 15px;
margin-left: 50px;
@media (max-width: 950px) {
display: none;
}
}
&__info {
margin-left: auto;
display: flex;
align-items: center;
column-gap: 15px;
@media (max-width: 950px) {
display: none;
}
&Phone {
font-size: 16px;
font-weight: 500;
}
&Socials {
display: flex;
column-gap: 8px;
}
&Profile {
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
border-radius: 50px;
width: 30px;
height: 30px;
background-color: #0d8afb;
img {
width: 28px;
height: 28px;
background-color: #fff;
border-radius: 14px;
}
}
}
&__burger {
display: none;
margin-left: auto;
width: 25px;
height: 25px;
cursor: pointer;
.burgerClose {
width: 20px;
height: 20px;
}
.burgerOpen {
width: 25px;
height: 25px;
}
@media (max-width: 950px) {
display: flex;
align-items: center;
justify-content: center;
}
}
&__dropDown {
display: none;
flex-direction: column;
border-radius: 8px;
background-color: #0270d5;
padding: 10px;
position: absolute;
right: 10px;
top: 55px;
row-gap: 10px;
@media (max-width: 950px) {
display: flex;
}
&NavMenu {
display: flex;
flex-direction: column;
row-gap: 5px;
}
}
}

View File

@ -0,0 +1,123 @@
import React, {useState, useEffect} from "react";
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import Form from 'react-bootstrap/Form';
export const Modals = ({
show,
onHide,
currentAuction,
setAuctionItems,
type
}) => {
const [newAuctionName, setNewAuctionName] = useState('')
const [addAuctionName, setAddNewAuctionName] = useState('')
useEffect(() => {
currentAuction ? setNewAuctionName(currentAuction.name) : setNewAuctionName('')
}, [currentAuction])
const editAuction = () => {
setAuctionItems((prevValue) => {
return prevValue.map((item) => {
if (item.number === currentAuction.number) {
return {...item, name: newAuctionName}
}
return item
})
})
onHide()
}
const addNewAuction = () => {
if (!addAuctionName) return
setAuctionItems((prevValue) => [...prevValue, {
number: prevValue.length + 1,
name: addAuctionName,
receptionDate: '17.04.23-18.04.23',
startDate: '18.04.23',
status: 'Сбор заявок'
}])
setAddNewAuctionName('')
onHide()
}
return (
<Modal
show={show}
onHide={onHide}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
{type === 'add' &&
<>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Добавить новый аукцион
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Название аукциона*</Form.Label>
<Form.Control
type="text"
placeholder="Название аукциона*"
value={addAuctionName}
onChange={(e) => setAddNewAuctionName(e.target.value)}
autoFocus
/>
</Form.Group>
<Form.Group
className="mb-3"
controlId="exampleForm.ControlTextarea1"
>
<Form.Label>Комментарий к аукциону</Form.Label>
<Form.Control as="textarea" rows={3} />
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button onClick={addNewAuction}>Добавить новый аукцион</Button>
</Modal.Footer>
</>
}
{type === 'edit' &&
<>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Редактировать аукцион {currentAuction.number}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput2">
<Form.Label>Название аукциона*</Form.Label>
<Form.Control
type="text"
placeholder="Название аукциона*"
value={newAuctionName}
onChange={(e) => setNewAuctionName(e.target.value)}
autoFocus
/>
</Form.Group>
<Form.Group
className="mb-3"
controlId="exampleForm.ControlTextarea1"
>
<Form.Label>Комментарий к аукциону</Form.Label>
<Form.Control as="textarea" rows={3} />
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button onClick={editAuction}>Сохранить изменения</Button>
</Modal.Footer>
</>
}
</Modal>
);
};
export default Modals;

View File

@ -0,0 +1,16 @@
import React from "react";
import arrow from "../../assets/images/select.svg"
import "./navigationItem.scss";
const NavigationItem = ({title}) => {
return (
<div className="navigationItem">
<h5>{title}</h5>
<img src={arrow} alt="arrow" />
</div>
);
};
export default NavigationItem;

View File

@ -0,0 +1,12 @@
.navigationItem {
display: flex;
cursor: pointer;
column-gap: 8px;
align-items: center;
h5 {
font-size: 15px;
font-weight: 500;
margin-bottom: 0;
}
}

View File

@ -0,0 +1,13 @@
import React from "react";
import "./socialItem.scss";
const SocialItem = ({img, href}) => {
return (
<a className="socialItem" href={href}>
<img src={img} alt='socialIcon' />
</a>
);
};
export default SocialItem;

View File

@ -0,0 +1,14 @@
.socialItem {
display: flex;
align-items: center;
justify-content: center;
border-radius: 50px;
background-color: #fff;
width: 30px;
height: 30px;
img {
width: 15px;
height: 15px;
}
}

52
src/index.css Normal file
View File

@ -0,0 +1,52 @@
body {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
margin: 0;
padding: 0;
}
html, div, span,
h1, h2, h3, h4, h5, h6, p,
a, table, caption, tbody, tfoot, thead, tr, th, td,
footer, header, menu, nav {
margin: 0;
padding: 0;
border: 0;
vertical-align: baseline;
}
footer, header, menu, nav, section {
display: block;
}
ol, ul {
list-style: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.container {
display: flex;
align-items: center;
max-width: 1310px;
padding: 0 15px;
margin: 0 auto;
height: 100%;
}

17
src/index.js Normal file
View File

@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

25
src/pages/Auth/Auth.js Normal file
View File

@ -0,0 +1,25 @@
import React from "react";
import Button from 'react-bootstrap/Button';
import "./auth.scss"
const Auth = () => {
return (
<div className="auth">
<h1 className="auth__title">Аукционная площадка для проведения закупок ГК Проф-Пресс</h1>
<div className="auth__form">
<input
placeholder="login"
type="text"
/>
<input
placeholder="password"
type="password"
/>
<Button variant="primary">Войти</Button>
</div>
</div>
)
}
export default Auth

35
src/pages/Auth/auth.scss Normal file
View File

@ -0,0 +1,35 @@
.auth {
padding: 100px 0;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
&__title {
font-size: 30px;
font-weight: 700;
max-width: 700px;
text-align: center;
}
&__form {
padding: 20px 0;
display: flex;
flex-direction: column;
row-gap: 15px;
width: 100%;
max-width: 400px;
input {
padding: 5px;
outline: none;
border-radius: 5px;
border: 1px solid gray;
}
button {
max-width: 200px;
width: 100%;
margin: 20px auto 0;
}
}
}

148
src/pages/Home/Home.js Normal file
View File

@ -0,0 +1,148 @@
import React, {useState} from "react";
import BreadCrumbs from "../../components/BreadCrumbs/BreadCrumbs";
import DropDownFilter from "../../components/DropDownFilter/DropDownFilter";
import Modals from "../../components/Modals/Modals";
import Pagination from 'react-bootstrap/Pagination';
import Button from 'react-bootstrap/Button';
import loupe from "../../assets/images/loupe.png"
import filterImg from "../../assets/images/filter.png"
import cross from "../../assets/images/close.png"
import edit from "../../assets/images/edit.png"
import "./home.scss"
const Home = () => {
const filterItems = ["Товарная группа", "Статус аукциона", "Новая заявка", "Выбрать период"]
const [auctionItems, setAuctionItems] = useState([
{
number: 1,
name: 'Аукцион на закупку в интересах компании ООО "Фабрика"',
receptionDate: '17.04.23-18.04.23',
startDate: '18.04.23',
status: 'Черновик'
},
{
number: 2,
name: 'Аукцион на закупку в интересах компании ООО "Пресс"',
receptionDate: '17.04.23-18.04.23',
startDate: '18.04.23',
status: 'Сбор заявок'
},
{
number: 3,
name: 'Аукцион на закупку в интересах компании ООО "Компания"',
receptionDate: '17.04.23-18.04.23',
startDate: '18.04.23',
status: 'Идут торги'
},
{
number: 4,
name: 'Аукцион на закупку в интересах компании ООО "Кот"',
receptionDate: '17.04.23-18.04.23',
startDate: '18.04.23',
status: 'В архиве'
},
])
const [openAddModal, setOpenAddModal] = useState(false)
const [openEditModal, setOpenEditModal] = useState(false)
const [currentEditAuction, setCurrentEditAuction] = useState(null)
return (
<div className="home">
<BreadCrumbs links={[
{ name: "Главная", link: "/" }
]} />
<h2 className="home__title">Аукционы</h2>
<div className="home__info">
<div className="info__top">
<Button variant="primary" onClick={() => setOpenAddModal(true)}>
+ Добавить аукцион
</Button>
<div className="info__search">
<input placeholder="Найти аукцион" type="text" />
<img src={loupe} alt='loupe' />
</div>
</div>
<div className="info__filters">
<img className="info__filtersImg" src={filterImg} alt="filter" />
<div className="info__filters__items">
{filterItems.map((item, index) => {
return <DropDownFilter key={index} title={item} />
})}
</div>
<Button variant="primary">
<img src={cross} alt="cross" />
Сбросить фильтры
</Button>
<Button variant="info">Применить</Button>
</div>
<div className="info__tableWrapper">
<table>
<thead>
<tr className="tableItem">
<td></td>
<td>Название аукциона</td>
<td className="center">Прием заявок</td>
<td className="center">Проведение</td>
<td className="center">Статус</td>
</tr>
</thead>
<tbody>
{auctionItems.map((item) => {
return <tr className="tableItem" key={item.number}>
<td>{item.number}</td>
<td className="tableItem__name">{item.name}</td>
<td className="center">{item.receptionDate}</td>
<td className="center">{item.startDate}</td>
<td className="center">{item.status}</td>
<td className="tableItem__edit">
<img src={edit} alt="edit" onClick={() => {
setCurrentEditAuction(item)
setOpenEditModal(true)
}}/>
</td>
</tr>
})}
</tbody>
</table>
<Pagination>
<Pagination.First />
<Pagination.Item>{1}</Pagination.Item>
<Pagination.Ellipsis />
<Pagination.Item>{10}</Pagination.Item>
<Pagination.Item>{11}</Pagination.Item>
<Pagination.Item active>{12}</Pagination.Item>
<Pagination.Item>{13}</Pagination.Item>
<Pagination.Ellipsis />
<Pagination.Item>{20}</Pagination.Item>
<Pagination.Last />
</Pagination>
</div>
</div>
{openAddModal &&
<Modals
type='add'
show={openAddModal}
onHide={() => setOpenAddModal(false)}
setAuctionItems={setAuctionItems}
/>
}
{currentEditAuction &&
<Modals
type='edit'
show={openEditModal}
onHide={() => setOpenEditModal(false)}
currentAuction={currentEditAuction}
setAuctionItems={setAuctionItems}
/>
}
</div>
);
};
export default Home;

159
src/pages/Home/home.scss Normal file
View File

@ -0,0 +1,159 @@
.home {
width: 100%;
padding: 15px 0 100px;
&__title {
font-size: 40px;
}
&__info {
width: 100%;
margin: 35px 0;
display: flex;
flex-direction: column;
row-gap: 15px;
overflow: auto;
&::-webkit-scrollbar-thumb {
background: #0255ff;
border-radius: 5px;
}
&::-webkit-scrollbar {
border-radius: 8px;
width: 5px;
height: 10px;
background: gray;
}
.info {
&__top {
display: flex;
justify-content: space-between;
min-width: 1213px;
}
&__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;
}
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 {
min-width: 1243px;
.tableItem {
padding: 10px;
border: 1px solid #cbcbcb;
&__name {
font-weight: 700;
}
.center {
text-align: center;
}
&__edit {
display: flex;
cursor: pointer;
align-items: center;
justify-content: center;
img {
width: 15px;
height: 15px;
}
}
}
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: 7% 50% 16% 12% 10% 5%;
}
}
tbody {
display: grid;
row-gap: 10px;
tr {
display: grid;
grid-template-columns: 7% 50% 16% 12% 10% 5%;
}
}
}
}
}
}
}

13
src/reportWebVitals.js Normal file
View File

@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
src/setupTests.js Normal file
View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';