forked from polka_billy/porridger
215 lines
9.8 KiB
TypeScript
215 lines
9.8 KiB
TypeScript
import { FormEventHandler, useEffect, useState } from "react"
|
||
import { Form, Button, Card } from "react-bootstrap"
|
||
import { MapContainer, TileLayer } from 'react-leaflet'
|
||
import { latLng } from "leaflet"
|
||
|
||
import { ClickHandler, LocationMarker, TrashboxMarkers } from "../components"
|
||
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))
|
||
const [address, setAddress] = useState('')
|
||
|
||
const trashboxes = useTrashboxes(addressPosition)
|
||
const [selectedTrashbox, setSelectedTrashbox] = useState({ index: -1, category: '' })
|
||
|
||
useEffect(() => {
|
||
void (async () => {
|
||
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)
|
||
|
||
} catch (err) {
|
||
console.error(err)
|
||
}
|
||
})()
|
||
}, [address])
|
||
|
||
useEffect(() => {
|
||
void (async () => {
|
||
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" })) {
|
||
throw new Error("Malformed server response")
|
||
}
|
||
|
||
setAddress(fetchData.display_name)
|
||
|
||
} catch (err) {
|
||
console.error(err)
|
||
}
|
||
})()
|
||
}, [addressPosition])
|
||
|
||
const { doAdd, status } = useAddAnnouncement()
|
||
|
||
const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {
|
||
event.preventDefault()
|
||
event.stopPropagation()
|
||
|
||
const formData = new FormData(event.currentTarget)
|
||
|
||
formData.append("latitude", addressPosition.lat.toString())
|
||
formData.append("longtitude", addressPosition.lng.toString())
|
||
formData.append("address", address)
|
||
formData.set("bestBy", new Date((formData.get("bestBy") as number | null) || 0).getTime().toString())
|
||
|
||
void doAdd(formData)
|
||
}
|
||
|
||
return (
|
||
<Card className="m-4" style={{ height: 'calc(100vh - 3rem)' }}>
|
||
<Card.Body style={{ overflowY: "auto" }} >
|
||
<Form onSubmit={handleSubmit}>
|
||
<Form.Group className="mb-3" controlId="name">
|
||
<Form.Label>Заголовок объявления</Form.Label>
|
||
<Form.Control type="text" required name="name" />
|
||
</Form.Group>
|
||
|
||
<Form.Group className="mb-3" controlId="category">
|
||
<Form.Label>Категория</Form.Label>
|
||
<Form.Select required name="category">
|
||
<option value="" hidden>
|
||
Выберите категорию
|
||
</option>
|
||
{Array.from(categoryNames).map(
|
||
([category, categoryName]) =>
|
||
<option key={category} value={category}>{categoryName}</option>
|
||
)}
|
||
</Form.Select>
|
||
</Form.Group>
|
||
|
||
<Form.Group className="mb-3" controlId="bestBy">
|
||
<Form.Label>Срок годности</Form.Label>
|
||
<Form.Control type="date" required name="bestBy" />
|
||
</Form.Group>
|
||
|
||
<Form.Group className="mb-3" controlId="address">
|
||
<Form.Label>Адрес выдачи</Form.Label>
|
||
<div className="mb-3">
|
||
<MapContainer
|
||
scrollWheelZoom={false}
|
||
style={{ width: "100%", height: 400 }}
|
||
center={addressPosition}
|
||
zoom={13}
|
||
>
|
||
<TileLayer
|
||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||
/>
|
||
<LocationMarker
|
||
address={address}
|
||
position={addressPosition}
|
||
setPosition={setAddressPosition}
|
||
/>
|
||
<ClickHandler
|
||
setPosition={setAddressPosition}
|
||
/>
|
||
</MapContainer>
|
||
</div>
|
||
<p>Адрес: {address}</p>
|
||
</Form.Group>
|
||
|
||
<Form.Group className="mb-3" controlId="description">
|
||
<Form.Label>Описание</Form.Label>
|
||
<Form.Control as="textarea" name="description" rows={3} placeholder="Укажите свои контакты, а так же, когда вам будет удобно передать продукт" />
|
||
</Form.Group>
|
||
|
||
<Form.Group className="mb-3" controlId="src">
|
||
<Form.Label>Иллюстрация (фото или видео)</Form.Label>
|
||
<Form.Control
|
||
type="file"
|
||
name="src"
|
||
accept="video/mp4,video/mkv, video/x-m4v,video/*, image/*"
|
||
capture="environment"
|
||
/>
|
||
</Form.Group>
|
||
<Form.Group className="mb-3" controlId="metro">
|
||
<Form.Label>
|
||
Станция метро
|
||
</Form.Label>
|
||
<Form.Select name="metro">
|
||
<option value="">
|
||
Укажите ближайщую станцию метро
|
||
</option>
|
||
{lines.map(
|
||
line =>
|
||
<optgroup key={line} label={lineNames[line]}>
|
||
{Array.from(stations[line]).map(metro =>
|
||
<option key={metro} value={metro}>{metro}</option>
|
||
)}
|
||
</optgroup>
|
||
)}
|
||
</Form.Select>
|
||
</Form.Group>
|
||
|
||
<Form.Group className="mb-3" controlId="password">
|
||
<Form.Label>Пункт сбора мусора</Form.Label>
|
||
<div className="mb-3">
|
||
{trashboxes.loading
|
||
? (
|
||
<div style={{ height: 400 }}>
|
||
<p>Загрузка...</p>
|
||
</div>
|
||
) : (
|
||
trashboxes.error ? (
|
||
<p
|
||
style={{ height: 400 }}
|
||
className="text-danger"
|
||
>{trashboxes.error}</p>
|
||
) : (
|
||
<MapContainer
|
||
scrollWheelZoom={false}
|
||
style={{ width: "100%", height: 400 }}
|
||
center={addressPosition}
|
||
zoom={13}
|
||
className=""
|
||
>
|
||
<TileLayer
|
||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||
/>
|
||
<TrashboxMarkers
|
||
trashboxes={trashboxes.data}
|
||
selectTrashbox={setSelectedTrashbox}
|
||
/>
|
||
</MapContainer>
|
||
)
|
||
)
|
||
}
|
||
</div>
|
||
{selectedTrashbox.index > -1 ? (
|
||
<p>Выбран пункт сбора мусора на {
|
||
trashboxes.data[selectedTrashbox.index].Address
|
||
} с категорией {selectedTrashbox.category}</p>
|
||
) : (
|
||
<p>Выберите пунк сбора мусора и категорию</p>
|
||
)}
|
||
</Form.Group>
|
||
|
||
<Button variant="success" type="submit">
|
||
{status}
|
||
</Button>
|
||
</Form>
|
||
</Card.Body>
|
||
</Card>
|
||
)
|
||
}
|
||
|
||
export default AddPage
|