Added announcement adding page

This commit is contained in:
Dmitriy Shishkov 2023-05-17 10:50:06 +03:00
parent 811cde6f30
commit 929dd8af36
Signed by: dm1sh
GPG Key ID: 027994B0AA357688
5 changed files with 289 additions and 2 deletions

View File

@ -0,0 +1,27 @@
import { Marker, Popup, useMapEvents } from "react-leaflet"
const LocationMarker = ({ address, position, setPosition }) => {
const map = useMapEvents({
dragend: () => {
setPosition(map.getCenter())
},
zoomend: () => {
setPosition(map.getCenter())
},
resize: () => {
setPosition(map.getCenter())
}
})
return (
<Marker position={position}>
<Popup>
{address}
{position.lat.toFixed(4)}, {position.lng.toFixed(4)}
</Popup>
</Marker>
)
}
export default LocationMarker

View File

@ -0,0 +1,26 @@
import { Marker, Popup } from "react-leaflet"
const TrashboxMarkers = ({ trashboxes, selectTrashbox }) => {
return (
<>{trashboxes.map((trashbox, index) => (
<Marker key={trashbox.Lat + "" + trashbox.Lng} position={[trashbox.Lat, trashbox.Lng]}>
<Popup>
<p>{trashbox.Address}</p>
<p>Тип мусора: <>
{trashbox.Categories.map((category, j) =>
<span key={trashbox.Address + category}>
<a href="#" onClick={() => selectTrashbox({ index, category })}>
{category}
</a>
{(j < trashbox.Categories.length - 1) ? ', ' : ''}
</span>
)}
</></p>
<p>{trashbox.Lat.toFixed(4)}, {trashbox.Lng.toFixed(4)}</p>
</Popup>
</Marker>
))}</>
)
}
export default TrashboxMarkers

View File

@ -0,0 +1,31 @@
import { useState } from "react"
import { API_URL } from "../../config"
const useAddAnnouncement = () => {
const [status, setStatus] = useState("Опубликовать")
const doAdd = async (formData) => {
setStatus(true)
try {
const res = await fetch(API_URL + "/announcement", {
method: 'PUT',
body: formData,
})
const data = await res.json()
if (!data.Answer) {
throw new Error("Не удалось опубликовать объявление")
}
setStatus("Опубликовано")
} catch (err) {
setStatus(err.message ?? err)
setTimeout(() => setStatus("Опубликовать"), 10000)
}
}
return {doAdd, status}
}
export default useAddAnnouncement

View File

@ -0,0 +1,12 @@
import { API_URL } from "../../config"
import useFetch from "./useFetch"
const useTrashboxes = (position) => {
return useFetch(
API_URL + "/trashbox?" + new URLSearchParams({ lat: position.lat, lng: position.lng }),
undefined,
[]
)
}
export default useTrashboxes

View File

@ -1,8 +1,199 @@
import { useEffect, useState } from "react"
import { Form, Button, Card } from "react-bootstrap"
import { MapContainer, TileLayer } from 'react-leaflet'
import { categoryNames } from "../assets/category"
import { latLng } from "leaflet"
import { metros } from "../assets/metro"
import LocationMarker from "../components/LocationMarker"
import TrashboxMarkers from "../components/TrashboxMarkers"
import { useAddAnnouncement, useTrashboxes } from "../hooks/api"
function AddPage() {
const [addressPosition, setAddressPosition] = useState(latLng(59.972, 30.3227))
const [address, setAddress] = useState('')
// TODO
const { data: trashboxes, trashboxes_loading, trashboxes_error } = useTrashboxes(addressPosition)
const [selectedTrashbox, setSelectedTrashbox] = useState({ index: -1, category: '' })
return <></>
useEffect(() => {
(async () => {
try {
const res = await fetch(location.protocol + "//nominatim.openstreetmap.org/search?format=json&q=" + address)
const fetchData = await res.json()
console.log("f", fetchData)
} catch (err) {
console.error(err)
}
})()
}, [address])
useEffect(() => {
(async () => {
try {
const res = await fetch(location.protocol + "//nominatim.openstreetmap.org/reverse?format=json&accept-language=ru&lat=" + addressPosition.lat + "&lon=" + addressPosition.lng)
const fetchData = await res.json()
setAddress(fetchData.display_name)
} catch (err) {
console.error(err)
}
})()
}, [addressPosition])
const {doAdd, status} = useAddAnnouncement()
const handleSubmit = (event) => {
event.preventDefault()
event.stopPropagation()
const formData = new FormData(event.target)
formData.append("latitude", addressPosition.lat)
formData.append("longtitude", addressPosition.lng)
formData.append("address", address)
formData.set("bestBy", new Date(formData.get("bestBy")).getTime())
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='&copy; <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}
/>
</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>
{Object.entries(metros).map(
([branch, stations]) =>
stations.map(metro =>
<option key={metro} value={metro}>{metro}</option>
)
)}
</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='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<TrashboxMarkers
trashboxes={trashboxes}
selectTrashbox={setSelectedTrashbox}
/>
</MapContainer>
)
)
}
</div>
{selectedTrashbox.index > -1 ? (
<p>Выбран пункт сбора мусора на {
trashboxes[selectedTrashbox.index].Address
} с категорией {selectedTrashbox.category}</p>
) : (
<p>Выберите пунк сбора мусора и категорию</p>
)}
</Form.Group>
<Button variant="success" type="submit">
{status}
</Button>
</Form>
</Card.Body>
</Card>
)
}
export default AddPage