Added even more TypeScript, Header, Logotype and NothingFound components, some code refactors

This commit is contained in:
Dm1tr1y147 2020-09-19 20:59:55 +05:00
parent f6c9dd293c
commit 64340c4ef0
21 changed files with 436 additions and 268 deletions

View File

@ -2,41 +2,13 @@ import React, { useEffect, useRef, useState } from 'react';
import { Switch, Route, useHistory } from 'react-router-dom';
import './App.css';
import Header from './Header';
import Home from './Home';
import Navbar from './Navbar';
import { queryIsEmpty } from './Navbar/utils';
import SubjectList from './SubjectList';
import { ILoadingState, IFilterQuery } from './types';
const genName = (searchQuery: IFilterQuery, path: string): string => {
if (path === '/list' && searchQuery) {
let result = '';
if (searchQuery.class_num) {
result = result + searchQuery.class_num + ' класс';
}
if (searchQuery.predmet_type) {
result = result + ' ' + searchQuery.predmet_type;
}
if (searchQuery.teacher) {
result = result + ' ' + searchQuery.teacher;
}
if (searchQuery.search) {
result = result + ' поиск по "' + searchQuery.search + '"';
}
return result;
}
if (path === '/') {
return 'Банк семинаров';
}
return '';
};
import NothingFound from './NothingFound';
const useDidUpdate: typeof useEffect = (func, dependencies) => {
const didMountRef = useRef(false);
@ -76,10 +48,8 @@ const App = () => {
}, [searchQuery]);
return (
<div>
<header id="name">
<h1>{genName(searchQuery, history.location.pathname)}</h1>
</header>
<>
<Header query={searchQuery} loading={loading} setSearchQuery={setSearchQuery} />
<Navbar query={searchQuery} setSearchQuery={setSearchQuery} />
<Switch>
<Route exact path="/">
@ -95,10 +65,10 @@ const App = () => {
/>
</Route>
<Route path="*">
<h1>404</h1>
<NothingFound setLoading={setLoading} />
</Route>
</Switch>
</div>
</>
);
};

103
src/Header/index.tsx Normal file
View File

@ -0,0 +1,103 @@
import React, { Dispatch, SetStateAction, useEffect } from 'react';
import { motion, Transition } from 'framer-motion';
import { useHistory } from 'react-router-dom';
import { IFilterQuery, ILoadingState } from '../types';
import './main.css';
import Logotype from '../Logotype';
const genName = (
searchQuery: IFilterQuery,
path: string,
error: string
): string => {
if (error) {
return error.toString();
}
if (path === '/list' && searchQuery) {
let result = '';
if (searchQuery.class_num) {
result = result + searchQuery.class_num + ' класс';
}
if (searchQuery.predmet_type) {
result = result + ' ' + searchQuery.predmet_type;
}
if (searchQuery.teacher) {
result = result + ' ' + searchQuery.teacher;
}
if (searchQuery.search) {
result = result + ' поиск по "' + searchQuery.search + '"';
}
return result;
}
if (path === '/') {
return 'Банк семинаров';
}
return '';
};
const Header = ({
query,
loading,
setSearchQuery
}: {
query: IFilterQuery;
loading: ILoadingState;
setSearchQuery: Dispatch<SetStateAction<IFilterQuery>>;
}) => {
/*
* Hooks definitions
*/
const history = useHistory();
useEffect(() => {}, [loading]);
/*
* Animations
*/
const headerVariants = {
expanded: {
height: '100vh'
},
collapsed: {
height: '10vh'
}
};
const transition: Transition = {
duration: 0.5,
ease: 'linear'
};
return (
<motion.header
id="name"
variants={headerVariants}
transition={transition}
animate={loading.fetching ? 'expanded' : 'collapsed'}
initial="expanded"
>
<h1>{genName(query, history.location.pathname, loading.error)}</h1>
{loading.fetching ? (
<motion.div
id="loadingLogo"
animate={{ rotate: 360 }}
transition={{ loop: Infinity, ease: 'linear', duration: 3 }}
>
<Logotype setSearchQuery={setSearchQuery} />
</motion.div>
) : (
''
)}
</motion.header>
);
};
export default Header;

40
src/Header/main.css Normal file
View File

@ -0,0 +1,40 @@
#name {
background-color: rgb(54, 54, 69);
padding: 2vh;
color: #ffffff;
border-bottom-left-radius: 20px;
border-bottom-right-radius: 20px;
position: relative;
z-index: 10;
height: 10vh;
overflow: hidden;
}
#name h1 {
text-align: center;
line-height: 6vh;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
#loadingLogo {
position: absolute;
width: 75vw;
height: 75vw;
left: 13.5vw;
top: calc((100vh - 75vw) / 2);
z-index: 1000;
background-color: white;
border-radius: 100%;
padding: 1vh;
}
@media (orientation: landscape) {
#loadingLogo {
top: 25vh;
left: calc((100vw - 50vh) / 2);
width: 50vh;
height: 50vh;
}
}

View File

@ -1,111 +0,0 @@
[{
"title": "Семинар 10",
"type_num": "Семинары",
"class_num": "1",
"post_date": "2020-09-15",
"predmet_type": "Математика",
"teacher": "Ню В.В",
"image": "/code/media/card_img/bxd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd0xb0xd1x80_10_xd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd_lN2D1k2.jpg",
"slug": "card-3-11-2020-09-15",
"card_id": 3
},
{
"title": "Семинар 10",
"type_num": "Семинары",
"class_num": "1",
"post_date": "2020-09-15",
"predmet_type": "Физика",
"teacher": "Ню В.В",
"image": "/code/media/card_img/bxd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd0xb0xd1x80_10_xd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd_lN2D1k2.jpg",
"slug": "card-3-11-2020-09-15",
"card_id": 3
},
{
"title": "Семинар 10",
"type_num": "Семинары",
"class_num": "1",
"post_date": "2020-09-15",
"predmet_type": "Математика",
"teacher": "Ню В.В",
"image": "/code/media/card_img/bxd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd0xb0xd1x80_10_xd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd_lN2D1k2.jpg",
"slug": "card-3-11-2020-09-15",
"card_id": 1
},
{
"title": "Very long text, too long to show it full",
"type_num": "Семинары",
"class_num": "2",
"post_date": "2020-09-15",
"predmet_type": "Математика",
"teacher": "Ню В.В",
"image": "/code/media/card_img/bxd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd0xb0xd1x80_10_xd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd_lN2D1k2.jpg",
"slug": "card-3-11-2020-09-15",
"card_id": 2
},
{
"title": "Семинар 10",
"type_num": "Семинары",
"class_num": "2",
"post_date": "2020-09-15",
"predmet_type": "Математика",
"teacher": "Ню В.В",
"image": "/code/media/card_img/bxd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd0xb0xd1x80_10_xd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd_lN2D1k2.jpg",
"slug": "card-3-11-2020-09-15",
"card_id": 3
},
{
"title": "Семинар 10",
"type_num": "Семинары",
"class_num": "3",
"post_date": "2020-09-15",
"predmet_type": "Математика",
"teacher": "Ню В.В",
"image": "/code/media/card_img/bxd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd0xb0xd1x80_10_xd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd_lN2D1k2.jpg",
"slug": "card-3-11-2020-09-15",
"card_id": 1
},
{
"title": "Very long text, too long to show it full",
"type_num": "Семинары",
"class_num": "3",
"post_date": "2020-09-15",
"predmet_type": "Математика",
"teacher": "Ню В.В",
"image": "/code/media/card_img/bxd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd0xb0xd1x80_10_xd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd_lN2D1k2.jpg",
"slug": "card-3-11-2020-09-15",
"card_id": 2
},
{
"title": "Семинар 10",
"type_num": "Семинары",
"class_num": "11",
"post_date": "2020-09-15",
"predmet_type": "Математика",
"teacher": "Ню В.В",
"image": "/code/media/card_img/bxd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd0xb0xd1x80_10_xd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd_lN2D1k2.jpg",
"slug": "card-3-11-2020-09-15",
"card_id": 3
},
{
"title": "Семинар 10",
"type_num": "Семинары",
"class_num": "10",
"post_date": "2020-09-15",
"predmet_type": "Математика",
"teacher": "Ню В.В",
"image": "/code/media/card_img/bxd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd0xb0xd1x80_10_xd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd_lN2D1k2.jpg",
"slug": "card-3-11-2020-09-15",
"card_id": 1
},
{
"title": "Very long text, too long to show it full",
"type_num": "Семинары",
"class_num": "11",
"post_date": "2020-09-15",
"predmet_type": "Математика",
"teacher": "Ню В.В",
"image": "/code/media/card_img/bxd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd0xb0xd1x80_10_xd0xa1xd0xb5xd0xbcxd0xb8xd0xbdxd_lN2D1k2.jpg",
"slug": "card-3-11-2020-09-15",
"card_id": 2
}
]

View File

@ -2,6 +2,7 @@ import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import Card from '../Card';
import NothingFound from '../NothingFound';
import { IData, IFilterQuery, ILoadingState } from '../types';
import './main.css';
@ -38,60 +39,75 @@ const Home = ({
];
const handleShowMore = (predmet_type: string, class_num: number) => {
setSearchQuery((prev): IFilterQuery => {
return { ...prev, predmet_type, class_num: class_num.toString() };
});
setSearchQuery(
(prev): IFilterQuery => {
return {
...prev,
predmet_type,
class_num: class_num.toString()
};
}
);
};
return (
<main className="homeContainer">
{classes.map((class_num, index) => (
<div key={index} className="classContainer">
<h1>{class_num} класс</h1>
{subjects.map((subject, jndex) =>
data.filter(
(el) =>
parseInt(el.class_num) === class_num &&
el.predmet_type === subject
).length ? (
<div key={jndex} className="subjectContainer">
<h2>{subject}</h2>
<div className="carousel">
<div className="carouselInner">
{data
.filter(
(el) =>
parseInt(el.class_num) ===
class_num &&
el.predmet_type === subject
)
.map((el, kndex) => (
<Card key={kndex} data={el} />
))}
</div>
<div className="showMore">
<Link
onClick={() =>
handleShowMore(
subject,
class_num
{classes.length ? (
classes.map((class_num, index) => (
<div key={index} className="classContainer">
<h1>{class_num} класс</h1>
{subjects.map((subject, jndex) =>
data.filter(
(el) =>
parseInt(el.class_num) === class_num &&
el.predmet_type === subject
).length ? (
<div key={jndex} className="subjectContainer">
<h2>{subject}</h2>
<div className="carousel">
<div className="carouselInner">
{data
.filter(
(el) =>
parseInt(
el.class_num
) === class_num &&
el.predmet_type ===
subject
)
}
to={'/list'}
>
Больше &rarr;
</Link>
.map((el, kndex) => (
<Card
key={kndex}
data={el}
/>
))}
</div>
<div className="showMore">
<Link
onClick={() =>
handleShowMore(
subject,
class_num
)
}
to={'/list'}
>
Больше &rarr;
</Link>
</div>
</div>
</div>
</div>
) : (
''
)
)}
) : (
''
)
)}
<div className="curve"></div>
</div>
))}
<div className="curve"></div>
</div>
))
) : (
<NothingFound />
)}
</main>
);
};

View File

@ -7,6 +7,7 @@
padding-top: calc(20px + 2vh);
margin-top: -20px;
position: relative;
overflow: visible;
}
.subjectContainer {
@ -69,11 +70,11 @@
}
.classContainer:nth-child(odd) .showMore {
background: linear-gradient(to right, rgba(244, 244, 244, 0) 25%, rgb(244, 244, 244) 70%);
background: linear-gradient( to right, rgba(244, 244, 244, 0) 25%, rgb(244, 244, 244) 70%);
}
.classContainer:nth-child(even) .showMore {
background: linear-gradient(to right, rgba(255, 255, 255, 0) 25%, rgb(255, 255, 255) 70%);
background: linear-gradient( to right, rgba(255, 255, 255, 0) 25%, rgb(255, 255, 255) 70%);
}
@ -84,4 +85,14 @@
.showMore a {
color: rgb(54, 54, 69);
text-decoration: none;
}
@media (orientation: landscape) {
.classContainer {
padding-left: 20vw;
padding-right: 20vw;
}
.classContainer .curve {
left: 2vh;
}
}

26
src/Logotype/index.tsx Normal file
View File

@ -0,0 +1,26 @@
import React, { Dispatch, SetStateAction } from 'react';
import { Link } from 'react-router-dom';
import { emptyQuery } from '../Navbar/utils';
import { IFilterQuery } from '../types';
import LogoImage from './logo.png';
import './main.css'
const Logotype = ({
setSearchQuery
}: {
setSearchQuery: Dispatch<SetStateAction<IFilterQuery>>;
}) => {
return (
<Link
to="/"
onClick={() => {
emptyQuery(setSearchQuery);
}}
>
<img id="logo" src={LogoImage} alt="Логотип ЮФМЛ" />
</Link>
);
};
export default Logotype;

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

21
src/Logotype/main.css Normal file
View File

@ -0,0 +1,21 @@
#logo {
height: 6vh;
width: 6vh;
background-color: white;
border-radius: 100%;
padding: 1vh;
box-sizing: border-box;
}
#loadingLogo #logo {
height: calc(75vw - 2vh);
width: calc(75vw - 2vh);
padding: 0;
}
@media (orientation: landscape) {
#loadingLogo #logo {
width: 48vh;
height: 48vh;
}
}

View File

@ -7,16 +7,13 @@ import React, {
useState
} from 'react';
import { motion } from 'framer-motion';
import { Link } from 'react-router-dom';
import './main.css';
import LogoImage from './logo.png';
import FilterIcon from './filter.svg';
import SearchIcon from './search.svg';
import { IFilterQuery } from '../types';
import { emptyQuery } from './utils';
import { useFocus } from '../utils';
import Logotype from '../Logotype';
const Navbar = ({
setSearchQuery,
@ -42,7 +39,6 @@ const Navbar = ({
useEffect(() => {
if (formRef.current) {
for (const [key, value] of Object.entries(query)) {
console.log(key, value);
if (formRef.current.elements.namedItem(key)) {
(formRef.current.elements.namedItem(
key
@ -143,14 +139,7 @@ const Navbar = ({
animate={filtersCollapsed ? 'closed' : 'open'}
>
<nav>
<Link
to="/"
onClick={() => {
emptyQuery(setSearchQuery);
}}
>
<img id="logo" src={LogoImage} alt="Логотип ЮФМЛ" />
</Link>
<Logotype setSearchQuery={setSearchQuery} />
<div id="spacing"></div>
@ -175,6 +164,11 @@ const Navbar = ({
transition={transition}
aria-label="Поиск"
ref={searchInput}
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSearchButton();
}
}}
type="search"
name="search"
id="searchInput"

View File

@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -20,15 +20,6 @@
z-index: 1000;
}
#logo {
height: 6vh;
width: 6vh;
background-color: white;
border-radius: 100%;
padding: 1vh;
box-sizing: border-box;
}
#spacing {
flex-grow: 1;
}

View File

@ -2,7 +2,7 @@ import { Dispatch, SetStateAction } from 'react';
import { IFilterQuery } from '../types';
const queryIsEmpty = (q: IFilterQuery): boolean => {
for (const [_, value] of Object.entries(q)) {
for (const value of Object.values(q)) {
if (value) {
return false;
}

83
src/NothingFound/icon.svg Normal file
View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 466.482 466.482" style="enable-background:new 0 0 466.482 466.482;" xml:space="preserve">
<g>
<path style="fill:#363645;" d="M233.824,148.642L126.673,119.94c-4.365-1.187-8.795,1.422-9.958,5.747
c-1.162,4.333,1.414,8.795,5.747,9.957l107.151,28.702c0.707,0.187,1.414,0.276,2.105,0.276c3.593,0,6.877-2.398,7.844-6.023
C240.741,154.259,238.164,149.796,233.824,148.642z" />
<path style="fill:#363645;" d="M257.795,160.591c0.301-0.423,0.553-0.886,0.935-1.244
C258.356,159.713,258.104,160.168,257.795,160.591z" />
<path style="fill:#363645;" d="M256.933,161.867c-0.187,0.415-0.236,0.878-0.35,1.333
C256.697,162.753,256.746,162.29,256.933,161.867z" />
<path style="fill:#363645;" d="M261.42,157.665c-0.585,0.219-1.065,0.585-1.569,0.927
C260.355,158.258,260.835,157.892,261.42,157.665z" />
<path style="fill:#363645;" d="M256.616,398.588c-1.447-4.251,0.821-8.868,5.072-10.315l10.754-3.666V170.996l-5.462,1.894
c-4.259,1.479-8.876-0.772-10.34-5.015c-0.333-0.967-0.431-1.951-0.398-2.918c0,0.089-0.049,0.171-0.049,0.26v220.293
L86.729,342.265v-220.91l-3.967,1.301l-12.29,11.055v214.863c0,3.715,2.52,6.95,6.121,7.877l185.722,47.39
c0.667,0.171,1.341,0.252,2.008,0.252C260.924,404.099,257.762,401.969,256.616,398.588z" />
<path style="fill:#363645;" d="M264.306,404.099c0.902,0,1.788-0.146,2.634-0.439h-0.008
C266.062,403.961,265.176,404.099,264.306,404.099z" />
<path style="fill:#363645;" d="M74.032,108.918c0.553-0.382,1.105-0.772,1.74-1.008C75.121,108.145,74.585,108.528,74.032,108.918z
" />
<path style="fill:#363645;" d="M414.823,111.429c-0.098-0.171-0.268-0.268-0.374-0.423c0.797,1.187,1.357,2.544,1.439,4.048
c0.187,3.609-2.04,6.917-5.454,8.096l-6.373,2.211l42.374,74.003l-116.19,40.245l-32.474-46.829
c-2.561-3.674-7.633-4.609-11.315-2.04c-3.69,2.552-4.601,7.625-2.048,11.315l35.985,51.893c1.553,2.227,4.072,3.495,6.682,3.495
c0.886,0,1.788-0.138,2.658-0.447l65.411-22.662v-11.421c0-4.495,3.642-8.129,8.129-8.129s8.129,3.633,8.129,8.129v5.788
l49.617-17.184c2.284-0.797,4.105-2.561,4.95-4.836c0.845-2.26,0.642-4.788-0.561-6.893L414.823,111.429z" />
<path style="fill:#363645;" d="M412.384,108.845c0.276,0.195,0.504,0.431,0.748,0.65
C412.88,109.275,412.652,109.031,412.384,108.845z" />
<path style="fill:#363645;" d="M409.775,107.617c0.244,0.065,0.455,0.203,0.691,0.285
C410.222,107.82,410.019,107.682,409.775,107.617z" />
<path style="fill:#363645;" d="M395.151,342.761l-122.717,41.846v11.356c0,2.512-1.162,4.885-3.146,6.422
c-0.715,0.553-1.512,0.975-2.349,1.268l138.958-47.39c3.292-1.122,5.503-4.219,5.503-7.69V228.7l-16.257,5.633v108.427H395.151z" />
<path style="fill:#363645;" d="M272.434,395.97v-11.356l-10.754,3.666c-4.251,1.447-6.519,6.064-5.072,10.315
c1.154,3.382,4.308,5.511,7.69,5.511c0.87,0,1.756-0.138,2.626-0.439h0.008c0.837-0.284,1.634-0.707,2.349-1.268
C271.272,400.847,272.434,398.474,272.434,395.97z" />
<path style="fill:#363645;" d="M403.28,214.784c-4.487,0-8.129,3.633-8.129,8.129v11.421l16.257-5.633v-5.788
C411.409,218.418,407.767,214.784,403.28,214.784z" />
<path style="fill:#363645;" d="M413.132,109.503c0.488,0.439,0.935,0.935,1.317,1.504
C414.067,110.438,413.636,109.95,413.132,109.503z" />
<path style="fill:#363645;" d="M74.032,108.918c-0.285,0.195-0.626,0.284-0.878,0.512L2.696,172.808
c-2.902,2.601-3.544,6.917-1.52,10.25c1.512,2.495,4.178,3.918,6.958,3.918c0.943,0,1.902-0.171,2.837-0.504l43.09-16.046
c4.211-1.569,6.348-6.243,4.78-10.453c-1.569-4.219-6.251-6.332-10.453-4.788l-3.048,1.138l25.134-22.606v-18.249
C70.455,112.706,71.927,110.381,74.032,108.918z" />
<path style="fill:#363645;" d="M410.466,107.902c0.675,0.236,1.325,0.528,1.918,0.943
C411.799,108.438,411.148,108.145,410.466,107.902z" />
<path style="fill:#363645;" d="M86.713,115.469v5.893l129.293-42.431l162.571,38.058L261.64,157.543
c-0.081,0.024-0.138,0.098-0.219,0.13c0.902-0.341,1.861-0.577,2.878-0.577c4.487,0,8.129,3.633,8.129,8.129v5.779l131.626-45.642
l-3.349-5.844c-2.227-3.894-0.878-8.868,3.016-11.095c1.91-1.081,4.072-1.292,6.048-0.797c-0.057-0.016-0.098-0.049-0.154-0.065
L217.485,62.585c-1.455-0.333-2.975-0.268-4.389,0.187L76.048,107.747c-0.106,0.033-0.179,0.122-0.285,0.163
c0.886-0.333,1.821-0.569,2.821-0.569C83.079,107.341,86.713,110.974,86.713,115.469z" />
<path style="fill:#363645;" d="M258.73,159.347c0.325-0.309,0.748-0.504,1.122-0.756
C259.477,158.843,259.055,159.038,258.73,159.347z" />
<path style="fill:#363645;" d="M256.226,164.956c0.016-0.618,0.203-1.179,0.358-1.756
C256.429,163.786,256.25,164.346,256.226,164.956z" />
<path style="fill:#363645;" d="M257.795,160.591c-0.301,0.423-0.65,0.805-0.862,1.276
C257.153,161.396,257.502,161.014,257.795,160.591z" />
<path style="fill:#363645;" d="M86.713,121.354v-5.893c0-4.495-3.642-8.129-8.129-8.129c-1,0-1.935,0.236-2.821,0.569
c-0.634,0.236-1.187,0.626-1.74,1.008c-2.105,1.471-3.568,3.788-3.568,6.552v18.249l12.29-11.055L86.713,121.354z" />
<path style="fill:#363645;" d="M264.306,157.088c-1.024,0-1.983,0.236-2.878,0.577c-0.585,0.228-1.073,0.585-1.569,0.927
c-0.374,0.252-0.788,0.447-1.122,0.756c-0.382,0.366-0.634,0.821-0.935,1.244s-0.642,0.805-0.862,1.276
c-0.187,0.423-0.236,0.886-0.35,1.333c-0.154,0.577-0.333,1.138-0.358,1.756c-0.033,0.967,0.057,1.951,0.398,2.918
c1.471,4.243,6.088,6.495,10.34,5.015l5.462-1.894v-5.779C272.434,160.721,268.793,157.088,264.306,157.088z" />
<path style="fill:#363645;" d="M403.727,108.414c-3.894,2.227-5.243,7.202-3.016,11.096l3.349,5.844l6.373-2.211
c3.414-1.187,5.641-4.487,5.454-8.096c-0.081-1.504-0.642-2.853-1.439-4.048c-0.382-0.569-0.821-1.065-1.317-1.504
c-0.244-0.228-0.48-0.463-0.748-0.65c-0.593-0.406-1.244-0.707-1.918-0.943c-0.236-0.081-0.439-0.219-0.691-0.285
C407.799,107.113,405.637,107.324,403.727,108.414z" />
</g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,25 @@
import React, { useEffect, Dispatch, SetStateAction } from 'react';
import './main.css';
import icon from './icon.svg';
import { ILoadingState } from '../types';
const NothingFound = ({
setLoading
}: {
setLoading?: Dispatch<SetStateAction<ILoadingState>>;
}) => {
useEffect(() => {
if (!setLoading) return;
setLoading({ error: '404', fetching: false });
}, [setLoading]);
return (
<div id="nothingFound">
<img src={icon} alt="" />
<h1>Ничего не найдено</h1>
</div>
);
};
export default NothingFound;

13
src/NothingFound/main.css Normal file
View File

@ -0,0 +1,13 @@
#nothingFound {
display: flex;
flex-direction: column;
justify-content: center;
height: 75vh;
text-align: center;
margin-top: -1vh;
}
#nothingFound img {
max-width: 40vmin;
margin: 0 auto;
}

View File

@ -1,25 +1,26 @@
import React, { useEffect, useState, Dispatch } from 'react';
import React, { useEffect, useState, Dispatch, SetStateAction } from 'react';
import { useHistory } from 'react-router-dom';
import { IData, ILoadingState, IFilterQuery } from '../types';
import { queryIsEmpty } from '../Navbar/utils';
import Card from '../Card';
import './main.css';
import NothingFound from '../NothingFound';
const SubjectList = ({
setLoading,
searchQuery
}: {
setLoading: Dispatch<ILoadingState>;
setLoading: Dispatch<SetStateAction<ILoadingState>>;
searchQuery: IFilterQuery;
}) => {
const [data, setData] = useState<IData[]>([]);
const history = useHistory();
const { push: historyPush } = useHistory();
useEffect(() => {
if (queryIsEmpty(searchQuery)) {
history.push('/');
historyPush('/');
return;
}
@ -28,22 +29,27 @@ const SubjectList = ({
'https://upml-bank.dmitriy.icu/api/cards?' +
new URLSearchParams({ ...searchQuery }).toString();
fetch(fetchURL)
.then((res) => res.json())
.then((res) => {
if (!res.ok) throw res.statusText;
return res.json();
})
.then((data) => {
setData(data);
setLoading({ fetching: false, error: '' });
})
.catch((err) => {
console.error(err);
setLoading({ fetching: false, error: err });
console.log(err);
});
}, [setLoading, searchQuery]);
}, [setLoading, searchQuery, historyPush]);
return (
<main className="subjectList">
{data.map((el, index) => (
<Card key={index} data={el} />
))}
{data.length ? (
data.map((el, index) => <Card key={index} data={el} />)
) : (
<NothingFound />
)}
</main>
);
};

View File

@ -4,4 +4,10 @@
gap: 1.5vh;
padding: 2vh;
align-items: stretch;
}
@media (orientation: landscape) {
.subjectList {
padding: 2vh 20vw;
}
}

View File

@ -21,23 +21,4 @@ body {
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}
#name {
background-color: rgb(54, 54, 69);
padding: 2vh;
color: #ffffff;
border-bottom-left-radius: 20px;
border-bottom-right-radius: 20px;
position: relative;
z-index: 10;
height: 10vh;
}
#name h1 {
text-align: center;
line-height: 6vh;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}

View File

@ -1,14 +0,0 @@
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router } from "react-router-dom";
import "./index.css";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>,
document.getElementById("root")
);

14
src/index.tsx Normal file
View File

@ -0,0 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import './index.css';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>,
document.getElementById('root')
);