From 7960974e6b63420d0e5559659b4b8925dc7bf160 Mon Sep 17 00:00:00 2001 From: Dm1tr1y147 Date: Tue, 22 Sep 2020 19:51:51 +0500 Subject: [PATCH] Added removeCard page, adapted admin design for big screens, some code refactors --- src/App.tsx | 28 ++++------- src/components/navigation/Header/main.css | 1 + src/components/navigation/Header/utils.ts | 14 ++++-- src/components/navigation/Navbar/index.tsx | 8 +-- src/components/navigation/Navbar/main.css | 8 ++- src/utils.ts | 58 ++++++++++++++-------- src/views/Admin/LogInForm/index.tsx | 19 +++++-- src/views/Admin/LogInForm/main.css | 5 ++ src/views/Admin/RemoveList/index.tsx | 40 +++++++++++++++ src/views/Admin/RemoveList/main.css | 36 ++++++++++++++ src/views/Admin/RemoveList/remove.svg | 41 +++++++++++++++ src/views/Admin/RemoveList/utils.ts | 17 +++++++ src/views/Admin/UploadForm/handlers.ts | 15 ++++++ src/views/Admin/UploadForm/index.tsx | 6 +-- src/views/Admin/UploadForm/main.css | 10 +++- src/views/Admin/index.tsx | 53 +++++++++++++++----- src/views/Admin/main.css | 56 +++++++++++++++++++-- src/views/Admin/types.ts | 6 +++ src/views/Admin/utils.ts | 25 ++++++++++ src/views/Home/index.tsx | 17 +------ src/views/Home/main.css | 4 +- yarn.lock | 6 +-- 22 files changed, 382 insertions(+), 91 deletions(-) create mode 100644 src/views/Admin/RemoveList/index.tsx create mode 100644 src/views/Admin/RemoveList/main.css create mode 100644 src/views/Admin/RemoveList/remove.svg create mode 100644 src/views/Admin/RemoveList/utils.ts create mode 100644 src/views/Admin/UploadForm/handlers.ts create mode 100644 src/views/Admin/types.ts create mode 100644 src/views/Admin/utils.ts diff --git a/src/App.tsx b/src/App.tsx index 5425c12..07c6f31 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useState } from 'react'; import { Switch, Route, useHistory } from 'react-router-dom'; import './App.css'; @@ -10,26 +10,14 @@ import SubjectList from './views/SubjectList'; import { ILoadingState, IFilterQuery } from './types'; import NothingFound from './views/NothingFound'; import Admin from './views/Admin'; - -const useDidUpdate: typeof useEffect = (func, dependencies) => { - const didMountRef = useRef(false); - - useEffect(() => { - if (didMountRef.current) { - func(); - } else { - didMountRef.current = true; - } - - return () => {}; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, dependencies); -}; +import { useDidUpdate } from './utils'; const App = () => { const history = useHistory(); - const [token, setToken] = useState(localStorage.getItem('token')); + const [token, setToken] = useState( + localStorage.getItem('token') + ); const [loading, setLoading] = useState({ fetching: true, @@ -72,7 +60,11 @@ const App = () => { /> - + diff --git a/src/components/navigation/Header/main.css b/src/components/navigation/Header/main.css index 5c65c6f..42d20b0 100644 --- a/src/components/navigation/Header/main.css +++ b/src/components/navigation/Header/main.css @@ -13,6 +13,7 @@ #name h1 { text-align: center; line-height: 6vh; + font-size: 4vh; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; diff --git a/src/components/navigation/Header/utils.ts b/src/components/navigation/Header/utils.ts index 1d5b50c..5011e27 100644 --- a/src/components/navigation/Header/utils.ts +++ b/src/components/navigation/Header/utils.ts @@ -10,18 +10,26 @@ const genName = ( } if (path === '/a/u') { - return 'Upload form'; + return 'Загрузить'; } if (path === '/a/l') { - return 'login'; + return 'Вход'; + } + + if (path === '/a/r') { + return 'Удалить'; } if (path === '/list' && searchQuery) { let result = ''; + if (searchQuery.type_num) { + result = result + searchQuery.type_num; + } + if (searchQuery.class_num) { - result = result + searchQuery.class_num + ' класс'; + result = result + ' ' + searchQuery.class_num + ' класс'; } if (searchQuery.predmet_type) { diff --git a/src/components/navigation/Navbar/index.tsx b/src/components/navigation/Navbar/index.tsx index 0bca228..a8e4d0a 100644 --- a/src/components/navigation/Navbar/index.tsx +++ b/src/components/navigation/Navbar/index.tsx @@ -125,7 +125,7 @@ const Navbar: React.FC = ({ setSearchQuery, query }) => { id="filters" ref={formRef} > -
+
@@ -152,7 +152,7 @@ const Navbar: React.FC = ({ setSearchQuery, query }) => {
-
+
@@ -169,7 +169,7 @@ const Navbar: React.FC = ({ setSearchQuery, query }) => {
-
+
@@ -186,7 +186,7 @@ const Navbar: React.FC = ({ setSearchQuery, query }) => {
-
+
diff --git a/src/components/navigation/Navbar/main.css b/src/components/navigation/Navbar/main.css index 69532fd..5bced4f 100644 --- a/src/components/navigation/Navbar/main.css +++ b/src/components/navigation/Navbar/main.css @@ -74,6 +74,12 @@ z-index: 1; } +.centerContent { + display: flex; + align-items: center; + flex-direction: column; +} + #filters label h2 { margin: 1.5vh 0; text-align: left; @@ -92,5 +98,5 @@ } body { - padding-bottom: 10.5vh; + padding-bottom: 10vh; } \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index da3e8f0..8904bc3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,6 @@ +import { Dispatch, SetStateAction, useEffect, useRef } from 'react'; +import { IData, ILoadingState } from './types'; + const useFocus = (focusRef: React.RefObject) => { const setFocus = () => { setTimeout(() => { @@ -8,27 +11,42 @@ const useFocus = (focusRef: React.RefObject) => { return setFocus; }; -const handleFormSubmit = ( - e: React.FormEvent, - uri: string, - callBack?: (res: Response) => void, - headers?: Headers | string[][] | Record | undefined -) => { - e.preventDefault(); - const data = new FormData(e.currentTarget); +const useDidUpdate: typeof useEffect = (func, dependencies) => { + const didMountRef = useRef(false); - const options: RequestInit = { - method: 'POST', - body: data, - headers: headers - }; + useEffect(() => { + if (didMountRef.current) { + func(); + } else { + didMountRef.current = true; + } - fetch('https://upml-bank.dmitriy.icu/' + uri, options) - .then((res) => { - if (!res.ok) throw res.statusText; - if (callBack) callBack(res); - }) - .catch((err) => console.log(err)); + return () => {}; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, dependencies); }; -export { useFocus, handleFormSubmit }; +const fetchCardList = ( + setData: Dispatch>, + setLoading?: Dispatch> +) => { + if (setLoading) setLoading({ fetching: true, error: '' }); + + const requestURL = 'https://upml-bank.dmitriy.icu/api/cards'; + + fetch(requestURL) + .then((res) => { + if (!res.ok) throw res.statusText; + return res.json(); + }) + .then((data) => { + setData(data); + if (setLoading) setLoading({ fetching: false, error: '' }); + }) + .catch((err) => { + if (setLoading) setLoading({ fetching: false, error: err }); + console.error(err); + }); +}; + +export { useFocus, useDidUpdate, fetchCardList }; diff --git a/src/views/Admin/LogInForm/index.tsx b/src/views/Admin/LogInForm/index.tsx index e7dcab8..e231bc1 100644 --- a/src/views/Admin/LogInForm/index.tsx +++ b/src/views/Admin/LogInForm/index.tsx @@ -1,8 +1,10 @@ -import React, { Dispatch, SetStateAction, useEffect } from 'react'; +import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { ILoadingState } from '../../../types'; -import { handleFormSubmit } from '../../../utils'; +import { handleFormSubmit } from '../utils'; +import { IErrorStatus } from '../types'; +import { handleLoginError } from '../UploadForm/handlers'; import { handleSuccessfulLogin } from './handlers'; import './main.css'; @@ -15,6 +17,10 @@ type props = { const LogInForm: React.FC = ({ setLoading, token, setToken }) => { const { push: historyPush } = useHistory(); + const [errorStatus, setErrorStatus] = useState({ + successful: true + }); + useEffect(() => { setLoading({ fetching: false, error: '' }); if (token) { @@ -26,8 +32,12 @@ const LogInForm: React.FC = ({ setLoading, token, setToken }) => {
- handleFormSubmit(e, 'api/login', (res: Response) => - handleSuccessfulLogin(res, setToken) + handleFormSubmit( + e, + 'api/login', + (err) => handleLoginError(err, setErrorStatus), + (res: Response) => handleSuccessfulLogin(res, setToken), + { 'Access-Control-Allow-Origin': '*' } ) } > @@ -38,6 +48,7 @@ const LogInForm: React.FC = ({ setLoading, token, setToken }) => { + {!errorStatus.successful ?

{errorStatus.errorMessage}

: ''} ); }; diff --git a/src/views/Admin/LogInForm/main.css b/src/views/Admin/LogInForm/main.css index 563573b..cf4d1cb 100644 --- a/src/views/Admin/LogInForm/main.css +++ b/src/views/Admin/LogInForm/main.css @@ -8,6 +8,10 @@ padding-top: 5vh; } +#logIn label { + font-size: 2.5vh; +} + #logIn input { height: 6vh; border: none; @@ -16,6 +20,7 @@ padding: 0 2vh; width: 100%; margin: 1vh 0; + font-size: 2.5vh; } #logIn input[type='submit'] { diff --git a/src/views/Admin/RemoveList/index.tsx b/src/views/Admin/RemoveList/index.tsx new file mode 100644 index 0000000..be63f51 --- /dev/null +++ b/src/views/Admin/RemoveList/index.tsx @@ -0,0 +1,40 @@ +import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; + +import { IData, ILoadingState } from '../../../types'; +import { fetchCardList } from '../../../utils'; +import './main.css'; +import removeIcon from './remove.svg'; +import { removeItem } from './utils'; + +type props = { + setLoading: Dispatch>; + token: string | null; + setToken: Dispatch>; +}; + +const RemoveList: React.FC = ({ setLoading, token, setToken }) => { + const [data, setData] = useState([]); + useEffect(() => fetchCardList(setData), []); + + return ( +
+ {data.map((el, index) => ( +
+

{el.title}

+

{el.class_num}

+

{el.predmet_type}

+

{el.teacher}

+ +
+ ))} +
+ ); +}; + +export default RemoveList; diff --git a/src/views/Admin/RemoveList/main.css b/src/views/Admin/RemoveList/main.css new file mode 100644 index 0000000..78bb397 --- /dev/null +++ b/src/views/Admin/RemoveList/main.css @@ -0,0 +1,36 @@ +#deleteList { + display: grid; + grid-template-columns: 1fr; + gap: 2vh; + padding: 2vh; + align-content: start; +} + +.deleteCard { + background-color: rgb(255, 109, 109); + color: white; + padding: 1.5vh; + border-radius: 5px; + position: relative; +} + +.deleteCard * { + padding: 0.2vh 0; +} + +.deleteCard button { + position: absolute; + top: 1.5vh; + right: 1.5vh; + height: 2.5h; + width: 2.5vh; + background: none; + border: none; + cursor: pointer; +} + +@media (orientation: landscape) { + #deleteList { + grid-template-columns: 1fr 1fr; + } +} \ No newline at end of file diff --git a/src/views/Admin/RemoveList/remove.svg b/src/views/Admin/RemoveList/remove.svg new file mode 100644 index 0000000..a5af545 --- /dev/null +++ b/src/views/Admin/RemoveList/remove.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/views/Admin/RemoveList/utils.ts b/src/views/Admin/RemoveList/utils.ts new file mode 100644 index 0000000..11e8bff --- /dev/null +++ b/src/views/Admin/RemoveList/utils.ts @@ -0,0 +1,17 @@ +const removeItem = async (slug: string, token: string) => { + try { + const res = await fetch( + `https://upml-bank.dmitriy.icu/api/card/${slug}/delete`, + { + method: 'delete', + headers: { Authentification: `Token ${token}` } + } + ); + if (!res.ok) throw res.statusText; + return res.text(); + } catch (err) { + console.log(); + } +}; + +export { removeItem }; diff --git a/src/views/Admin/UploadForm/handlers.ts b/src/views/Admin/UploadForm/handlers.ts new file mode 100644 index 0000000..e628a38 --- /dev/null +++ b/src/views/Admin/UploadForm/handlers.ts @@ -0,0 +1,15 @@ +import { Dispatch, SetStateAction } from 'react'; +import { IErrorStatus } from '../types'; + +const handleLoginError = ( + err: ErrorEvent, + setErrorStatus: Dispatch> +) => { + console.log(err); + setErrorStatus({ + successful: false, + errorMessage: err.toString() + }); +}; + +export { handleLoginError }; diff --git a/src/views/Admin/UploadForm/index.tsx b/src/views/Admin/UploadForm/index.tsx index 54db40a..07efacd 100644 --- a/src/views/Admin/UploadForm/index.tsx +++ b/src/views/Admin/UploadForm/index.tsx @@ -3,7 +3,7 @@ import { useHistory } from 'react-router-dom'; import Select from '../../../components/uploadForm/Select'; import { ILoadingState } from '../../../types'; -import { handleFormSubmit } from '../../../utils'; +import { handleFormSubmit } from '../utils'; import selectOptions from './selectOptions.json'; import './main.css'; @@ -26,8 +26,8 @@ const UploadForm: React.FC = ({ setLoading, token, setToken }) => {
- handleFormSubmit(e, 'api/card/create', undefined, { - Authorization: `Token ${localStorage.token}` + handleFormSubmit(e, 'api/card/create', () => {}, undefined, { + 'Authorization': `Token ${localStorage.token}` }) } > diff --git a/src/views/Admin/UploadForm/main.css b/src/views/Admin/UploadForm/main.css index 020147e..6a2b8d3 100644 --- a/src/views/Admin/UploadForm/main.css +++ b/src/views/Admin/UploadForm/main.css @@ -4,6 +4,10 @@ flex-direction: column; } +#uploadForm>label { + font-size: 2.5vh; +} + #uploadForm>input, #uploadForm>select, #uploadForm>aside { @@ -16,6 +20,7 @@ width: 100%; background-color: white; margin: 1vh 0; + font-size: 2.5vh; color: inherit; } @@ -25,4 +30,7 @@ justify-content: center; } -#uploadFrom input[type='file'] {} \ No newline at end of file +#uploadForm input[type='submit'] { + background-color: rgb(255, 109, 109); + color: white; +} \ No newline at end of file diff --git a/src/views/Admin/index.tsx b/src/views/Admin/index.tsx index 06b2993..7b2c4df 100644 --- a/src/views/Admin/index.tsx +++ b/src/views/Admin/index.tsx @@ -1,10 +1,18 @@ import React, { Dispatch, SetStateAction, useEffect } from 'react'; -import { Link, Redirect, Route, Switch, useRouteMatch } from 'react-router-dom'; +import { + Link, + Redirect, + Route, + Switch, + useLocation, + useRouteMatch +} from 'react-router-dom'; import { ILoadingState } from '../../types'; import LogInForm from './LogInForm'; import UploadForm from './UploadForm'; import './main.css'; +import RemoveList from './RemoveList'; type props = { token: string | null; @@ -15,6 +23,8 @@ type props = { const Admin: React.FC = ({ token, setToken, setLoading }) => { const { path, url } = useRouteMatch(); + const { pathname } = useLocation(); + useEffect(() => { setLoading({ fetching: false, error: '' }); }, [setLoading]); @@ -23,22 +33,34 @@ const Admin: React.FC = ({ token, setToken, setLoading }) => {
@@ -55,6 +77,13 @@ const Admin: React.FC = ({ token, setToken, setLoading }) => { setToken={setToken} /> + + + diff --git a/src/views/Admin/main.css b/src/views/Admin/main.css index 69d34c8..93974fe 100644 --- a/src/views/Admin/main.css +++ b/src/views/Admin/main.css @@ -1,6 +1,5 @@ #admin { - min-height: calc(79.5vh + 20px); - height: 100%; + min-height: 80vh; } #admin nav { @@ -23,11 +22,17 @@ #admin nav li { list-style-type: none; + font-size: 2.3vh; } #admin nav li a { color: rgb(255, 109, 109); - border-bottom: 1px solid rgb(255, 109, 109); + border-bottom: 2px solid rgb(255, 109, 109); +} + +#admin nav li.current a { + color: rgb(54, 54, 69); + border-bottom: none; } #admin nav li a, @@ -39,8 +44,8 @@ position: absolute; background-color: rgb(255, 109, 109); border: none; - padding: 1.5vh; - height: 6vh; + padding: 0 1.5vh; + line-height: 6vh; border-radius: 20px; font-size: 2.5vh; right: 2vh; @@ -49,4 +54,45 @@ #admin nav #logSwitch a { color: white; +} + +@media (orientation: landscape) { + #admin { + display: grid; + height: 80vh; + grid-template-columns: 1fr 5fr; + } + #admin nav { + height: calc(80vh + 40px); + } + #admin nav ul { + justify-content: start; + gap: 1.5vh; + margin-top: 1.25vh; + padding: 1.5vh 0; + } + #admin nav ul li { + background-color: rgb(255, 109, 109); + padding: 1.5vh; + border-radius: 20px; + } + #admin nav ul li.current { + background-color: rgb(54, 54, 69); + } + #admin nav ul li a { + color: white; + } + #admin nav ul li.current a { + color: white; + } + #admin nav #logSwitch { + top: initial; + right: initial; + left: 2vh; + bottom: calc(20px + 2vh); + } + #admin>* { + height: 80vh; + overflow-y: scroll; + } } \ No newline at end of file diff --git a/src/views/Admin/types.ts b/src/views/Admin/types.ts new file mode 100644 index 0000000..1d7cb1c --- /dev/null +++ b/src/views/Admin/types.ts @@ -0,0 +1,6 @@ +interface IErrorStatus { + successful: boolean; + errorMessage?: string; +} + +export type { IErrorStatus }; diff --git a/src/views/Admin/utils.ts b/src/views/Admin/utils.ts new file mode 100644 index 0000000..fba2122 --- /dev/null +++ b/src/views/Admin/utils.ts @@ -0,0 +1,25 @@ +const handleFormSubmit = ( + e: React.FormEvent, + uri: string, + errorHandler: (err: any) => void, + callBack?: (res: Response) => void, + headers?: Headers | string[][] | Record | undefined +) => { + e.preventDefault(); + const data = new FormData(e.currentTarget); + + const options: RequestInit = { + method: 'POST', + body: data, + headers: headers + }; + + fetch('https://upml-bank.dmitriy.icu/' + uri, options) + .then((res) => { + if (!res.ok) throw res.statusText; + if (callBack) callBack(res); + }) + .catch((err) => errorHandler(err)); +}; + +export {handleFormSubmit} \ No newline at end of file diff --git a/src/views/Home/index.tsx b/src/views/Home/index.tsx index 8379354..3936ea5 100644 --- a/src/views/Home/index.tsx +++ b/src/views/Home/index.tsx @@ -6,6 +6,7 @@ import NothingFound from '../NothingFound'; import { IData, IFilterQuery, ILoadingState } from '../../types'; import { handleShowMore } from './handlers'; import './main.css'; +import { fetchCardList } from '../../utils'; type props = { setLoading: Dispatch>; @@ -15,21 +16,7 @@ type props = { const Home: React.FC = ({ setLoading, setSearchQuery }) => { const [data, setData] = useState([]); - useEffect(() => { - setLoading({ fetching: true, error: '' }); - - const requestURL = 'https://upml-bank.dmitriy.icu/api/cards'; - fetch(requestURL) - .then((res) => res.json()) - .then((data) => { - setData(data); - setLoading({ fetching: false, error: '' }); - }) - .catch((err) => { - setLoading({ fetching: false, error: err }); - console.error(err); - }); - }, [setLoading]); + useEffect(() => fetchCardList(setData, setLoading), [setLoading]); const classes: number[] = [ ...Array.from(new Set(data.map((el) => parseInt(el.class_num)).sort())) diff --git a/src/views/Home/main.css b/src/views/Home/main.css index 4e3d607..3f10d50 100644 --- a/src/views/Home/main.css +++ b/src/views/Home/main.css @@ -1,5 +1,5 @@ .homeContainer { - min-height: calc(79.5vh + 20px); + min-height: calc(80vh + 20px); } .classContainer { @@ -7,7 +7,7 @@ padding-top: calc(20px + 2vh); margin-top: -20px; position: relative; - overflow: visible; + overflow: hidden; } .subjectContainer { diff --git a/yarn.lock b/yarn.lock index 2585976..1b9af02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2844,9 +2844,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001035: - version "1.0.30001035" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz#2bb53b8aa4716b2ed08e088d4dc816a5fe089a1e" - integrity sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ== + version "1.0.30001135" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001135.tgz" + integrity sha512-ziNcheTGTHlu9g34EVoHQdIu5g4foc8EsxMGC7Xkokmvw0dqNtX8BS8RgCgFBaAiSp2IdjvBxNdh0ssib28eVQ== capture-exit@^2.0.0: version "2.0.0"