Compare commits

..

No commits in common. "6a0c4c9dac3597e066f6b6a8714dcd08e149a925" and "395b6c2d89b1e2388822c80f26a9077d197180cf" have entirely different histories.

9 changed files with 70 additions and 101 deletions

View File

@ -1,55 +0,0 @@
import { FormEventHandler } from "react"
import { Button, Form } from "react-bootstrap"
type AuthFormProps = {
register: boolean
handleAuth: FormEventHandler<HTMLFormElement>,
loading: boolean,
error: string
}
const AuthForm = ({ handleAuth, register, loading, error }: AuthFormProps) => {
const buttonText = loading ? "Загрузка..." : (error || (register ? "Зарегистрироваться" : "Войти"))
return (
<Form onSubmit={handleAuth}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Почта</Form.Label>
<Form.Control type="email" required />
</Form.Group>
{register && <>
<Form.Group className="mb-3" controlId="name">
<Form.Label>Имя</Form.Label>
<Form.Control type="text" required />
</Form.Group>
<Form.Group className="mb-3" controlId="surname">
<Form.Label>Фамилия</Form.Label>
<Form.Control type="text" required />
</Form.Group>
</>}
<Form.Group className="mb-3" controlId="password">
<Form.Label>Пароль</Form.Label>
<Form.Control type="password" required />
</Form.Group>
{register &&
<Form.Group className="mb-3" controlId="privacyPolicyConsent">
<Form.Check label="<a>условиями обработки персональных данных</a>">
<Form.Check.Input type="checkbox" required />
<Form.Check.Label>
Я согласен с <a href={`${document.location.origin}/privacy_policy.pdf`} target="_blank" rel="noopener noreferrer">условиями обработки персональных данных</a>
</Form.Check.Label>
</Form.Check>
</Form.Group>
}
<Button variant="success" type="submit">
{buttonText}
</Button>
</Form>
)
}
export default AuthForm

View File

@ -6,7 +6,6 @@ import LocationMarker from './LocationMarker'
import TrashboxMarkers from './TrashboxMarkers'
import WithToken from './WithToken'
import ClickHandler from './ClickHandler'
import AuthForm from "./AuthForm"
export {
AnnouncementDetails,
@ -17,5 +16,4 @@ export {
TrashboxMarkers,
WithToken,
ClickHandler,
AuthForm,
}

View File

@ -2,14 +2,13 @@ import { useEffect, useRef, useState } from "react"
import { API_URL } from "../../config"
import { isLiteralUnion } from "../../utils/types"
import { handleHTTPErrors } from "../../utils"
const addErrors = ["Не удалось опубликовать объявление", 'Неверный ответ от сервера', 'Неизвестная ошибка'] as const
type AddError = typeof addErrors[number]
const isAddError = (obj: unknown): obj is AddError => isLiteralUnion(obj, addErrors)
const buttonStates = ["Опубликовать", "Загрузка...", "Опубликовано", "Отменено"] as const
const buttonStates = ["Опубликовать", "Загрузка", "Опубликовано", "Отменено"] as const
type ButtonState = typeof buttonStates[number] | AddError
type AddResponse = {
@ -27,14 +26,14 @@ const useAddAnnouncement = () => {
const abortControllerRef = useRef<AbortController>()
const doAdd = async (formData: FormData) => {
if (status === "Загрузка...") {
if (status === "Загрузка") {
abortControllerRef.current?.abort()
setStatus("Отменено")
timerIdRef.current = setTimeout(() => setStatus("Опубликовать"), 3000)
return
}
setStatus("Загрузка...")
setStatus("Загрузка")
const abortController = new AbortController()
abortControllerRef.current = abortController
@ -46,8 +45,6 @@ const useAddAnnouncement = () => {
signal: abortController.signal
})
handleHTTPErrors(res)
const data: unknown = await res.json()
if (!isAddResponse(data)) throw new Error('Неверный ответ от сервера')

View File

@ -1,7 +1,6 @@
import { useState } from "react"
import { API_URL } from "../../config"
import { isConst, isObject } from "../../utils/types"
import { handleHTTPErrors } from "../../utils"
interface AuthData {
email: string,
@ -58,9 +57,6 @@ function useAuth() {
'Content-Type': 'application/json'
}
})
handleHTTPErrors(res)
const signupData: unknown = await res.json()
if (!isSignUpResponse(signupData)) {

View File

@ -4,7 +4,6 @@ import { useNavigate } from "react-router-dom"
import { getToken } from "../../utils/auth"
import { API_URL } from "../../config"
import { isObject } from "../../utils/types"
import { handleHTTPErrors } from "../../utils"
type BookResponse = {
Success: boolean
@ -14,7 +13,7 @@ const isBookResponse = (obj: unknown): obj is BookResponse => isObject(obj, {
"Success": "boolean"
})
type BookStatus = "" | "Загрузка..." | "Забронировано" | "Ошибка бронирования"
type BookStatus = "" | "Загрузка" | "Забронировано" | "Ошибка бронирования"
function useBook(id: number) {
const navigate = useNavigate()
@ -25,7 +24,7 @@ function useBook(id: number) {
const token = getToken()
if (token) {
setStatus("Загрузка...")
setStatus("Загрузка")
try {
@ -40,8 +39,6 @@ function useBook(id: number) {
}
})
handleHTTPErrors(res)
const data: unknown = await res.json()
if (!isBookResponse(data)) {

View File

@ -1,6 +1,5 @@
import { useEffect, useRef, useState } from "react"
import { handleHTTPErrors, isAborted } from '../../utils'
import { isAborted } from '../../utils'
const useFetch = <T>(url: string, params: RequestInit | undefined, initialData: T, dataGuard: (obj: unknown) => obj is T) => {
const [data, setData] = useState(initialData)
@ -19,7 +18,17 @@ const useFetch = <T>(url: string, params: RequestInit | undefined, initialData:
fetch(url, { ...params, signal: abortControllerRef.current.signal })
.then(res => {
handleHTTPErrors(res)
if (!res.ok) {
switch (res.status) {
case 401:
throw new Error("Ошибка авторизации")
case 404:
throw new Error("Объект не найден")
default: {
throw new Error("Ошибка ответа от сервера")
}
}
}
return res.json()
})

View File

@ -9,7 +9,6 @@ import { useAddAnnouncement, useTrashboxes } from "../hooks/api"
import { categoryNames } from "../assets/category"
import { stations, lines, lineNames } from "../assets/metro"
import { isObject } from "../utils/types"
import { handleHTTPErrors } from "../utils"
function AddPage() {
const [addressPosition, setAddressPosition] = useState(latLng(59.972, 30.3227))
@ -23,8 +22,6 @@ function AddPage() {
try {
const res = await fetch(location.protocol + "//nominatim.openstreetmap.org/search?format=json&q=" + address)
handleHTTPErrors(res)
const fetchData: unknown = await res.json()
console.log("f", fetchData)
@ -40,8 +37,6 @@ function AddPage() {
try {
const res = await fetch(`${location.protocol}//nominatim.openstreetmap.org/reverse?format=json&accept-language=ru&lat=${addressPosition.lat}&lon=${addressPosition.lng}`)
handleHTTPErrors(res)
const fetchData: unknown = await res.json()
if (!isObject<{ display_name: string }>(fetchData, { "display_name": "string" })) {
@ -164,7 +159,7 @@ function AddPage() {
{trashboxes.loading
? (
<div style={{ height: 400 }}>
<p>Загрузка...</p>
<p>Загрузка</p>
</div>
) : (
trashboxes.error ? (

View File

@ -1,15 +1,14 @@
import { FormEventHandler } from 'react'
import { Card, Tabs, Tab } from "react-bootstrap"
import { Form, Button, Card, Tabs, Tab } from "react-bootstrap"
import { useNavigate } from "react-router-dom";
import { useAuth } from "../hooks/api";
import { setToken } from "../utils/auth";
import { AuthForm } from '../components';
function LoginPage() {
const navigate = useNavigate()
const { doAuth, loading, error } = useAuth()
const { doAuth } = useAuth() // TODO: Add loading and error handling
const handleAuth = (newAccount: boolean): FormEventHandler<HTMLFormElement> => async (event) => {
event.preventDefault();
@ -37,10 +36,57 @@ function LoginPage() {
<Card.Body>
<Tabs defaultActiveKey="register" fill justify className="mb-3">
<Tab eventKey="register" title="Регистрация">
<AuthForm handleAuth={handleAuth(true)} register={true} loading={loading} error={error} />
<Form onSubmit={handleAuth(true)}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Почта</Form.Label>
<Form.Control type="email" required />
</Form.Group>
<Form.Group className="mb-3" controlId="name">
<Form.Label>Имя</Form.Label>
<Form.Control type="text" required />
</Form.Group>
<Form.Group className="mb-3" controlId="surname">
<Form.Label>Фамилия</Form.Label>
<Form.Control type="text" required />
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Label>Пароль</Form.Label>
<Form.Control type="password" required />
</Form.Group>
<Form.Group className="mb-3" controlId="privacyPolicyConsent">
<Form.Check label="<a>условиями обработки персональных данных</a>">
<Form.Check.Input type="checkbox" required />
<Form.Check.Label>
Я согласен с <a href={`${document.location.origin}/privacy_policy.pdf`} target="_blank" rel="noopener noreferrer">условиями обработки персональных данных</a>
</Form.Check.Label>
</Form.Check>
</Form.Group>
<Button variant="success" type="submit">
Зарегистрироваться
</Button>
</Form>
</Tab>
<Tab eventKey="login" title="Вход">
<AuthForm handleAuth={handleAuth(false)} register={false} loading={loading} error={error} />
<Form onSubmit={handleAuth(false)}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Почта</Form.Label>
<Form.Control type="email" required />
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Label>Пароль</Form.Label>
<Form.Control type="password" required />
</Form.Group>
<Button variant="success" type="submit">
Войти
</Button>
</Form>
</Tab>
</Tabs>
</Card.Body>

View File

@ -1,17 +1,3 @@
const isAborted = (err: Error) => err.name === 'AbortError'
const handleHTTPErrors = (res: Response) => {
if (!res.ok) {
switch (res.status) {
case 401:
throw new Error("Ошибка авторизации")
case 404:
throw new Error("Объект не найден")
default: {
throw new Error("Ошибка ответа от сервера")
}
}
}
}
export { isAborted, handleHTTPErrors }
export { isAborted }