Added announcement disposal:
Added ann details button Added modal shown on its click Moved trashbox selection there Added trashboxes mock while testing in restricted area
This commit is contained in:
parent
47fca02858
commit
b93ab9794d
20
front/package-lock.json
generated
20
front/package-lock.json
generated
@ -8,7 +8,6 @@
|
||||
"name": "front",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@types/leaflet": "^1.9.3",
|
||||
"bootstrap": "^5.3.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"leaflet": "^1.9.4",
|
||||
@ -22,6 +21,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@types/leaflet": "^1.9.3",
|
||||
"@types/lodash": "^4.14.196",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
||||
@ -30,6 +31,7 @@
|
||||
"eslint": "^8.44.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.4.0"
|
||||
}
|
||||
@ -1059,7 +1061,8 @@
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
|
||||
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA=="
|
||||
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.12",
|
||||
@ -1071,10 +1074,17 @@
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.3.tgz",
|
||||
"integrity": "sha512-Caa1lYOgKVqDkDZVWkto2Z5JtVo09spEaUt2S69LiugbBpoqQu92HYFMGUbYezZbnBkyOxMNPXHSgRrRY5UyIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.196",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.196.tgz",
|
||||
"integrity": "sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
||||
@ -2417,6 +2427,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
|
@ -12,7 +12,6 @@
|
||||
"addFetchApiRoute": "bash utils/addFetchApiRoute.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/leaflet": "^1.9.3",
|
||||
"bootstrap": "^5.3.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"leaflet": "^1.9.4",
|
||||
@ -26,6 +25,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@types/leaflet": "^1.9.3",
|
||||
"@types/lodash": "^4.14.196",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
||||
@ -34,6 +35,7 @@
|
||||
"eslint": "^8.44.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.4.0"
|
||||
}
|
||||
|
19
front/src/api/dispose/index.ts
Normal file
19
front/src/api/dispose/index.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { API_URL } from '../../config'
|
||||
import { TrashboxDispose, DisposeResponse } from './types'
|
||||
|
||||
const composeDisposeURL = () => (
|
||||
API_URL + '/announcement/dispose?'
|
||||
)
|
||||
|
||||
const composeDisposeBody = (ann_id: number, trashbox: TrashboxDispose) => (
|
||||
JSON.stringify({
|
||||
ann_id,
|
||||
trashbox,
|
||||
})
|
||||
)
|
||||
|
||||
const processDispose = (data: DisposeResponse): boolean => {
|
||||
return data.Success
|
||||
}
|
||||
|
||||
export { composeDisposeURL, composeDisposeBody, processDispose }
|
23
front/src/api/dispose/types.ts
Normal file
23
front/src/api/dispose/types.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { composeDisposeBody } from '.'
|
||||
import { isObject } from '../../utils/types'
|
||||
import { Trashbox } from '../trashbox/types'
|
||||
|
||||
type TrashboxDispose = Omit<Trashbox, 'Categories' | 'Address'> & { Category: string }
|
||||
|
||||
type DisposeParams = Parameters<typeof composeDisposeBody>
|
||||
|
||||
type DisposeAnnParams = DisposeParams extends [ann_id: number, ...args: infer P] ? P : never
|
||||
|
||||
type DisposeResponse = {
|
||||
Success: boolean,
|
||||
}
|
||||
|
||||
const isDisposeResponse = (obj: unknown): obj is DisposeResponse => (
|
||||
isObject(obj, {
|
||||
'Success': 'boolean',
|
||||
})
|
||||
)
|
||||
|
||||
export type { TrashboxDispose, DisposeParams, DisposeAnnParams, DisposeResponse }
|
||||
|
||||
export { isDisposeResponse }
|
@ -1,6 +1,7 @@
|
||||
import { isArrayOf, isObject, isString } from '../../utils/types'
|
||||
|
||||
type Trashbox = {
|
||||
Name: string,
|
||||
Lat: number,
|
||||
Lng: number,
|
||||
Address: string,
|
||||
@ -9,6 +10,7 @@ type Trashbox = {
|
||||
|
||||
const isTrashbox = (obj: unknown): obj is Trashbox => (
|
||||
isObject(obj, {
|
||||
'Name': 'string',
|
||||
'Lat': 'number',
|
||||
'Lng': 'number',
|
||||
'Address': 'string',
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { Modal, Button } from 'react-bootstrap'
|
||||
import { MapContainer, Marker, Popup, TileLayer } from 'react-leaflet'
|
||||
import { CSSProperties } from 'react'
|
||||
import { CSSProperties, useState } from 'react'
|
||||
|
||||
import LineDot from './LineDot'
|
||||
import { categoryNames } from '../assets/category'
|
||||
import { useBook, useRemoveAnnouncement } from '../hooks/api'
|
||||
import { useBook, useDispose, useRemoveAnnouncement } from '../hooks/api'
|
||||
import { Announcement } from '../api/announcement/types'
|
||||
import { iconItem } from '../utils/markerIcons'
|
||||
import { useId } from '../hooks'
|
||||
import SelectDisposalTrashbox from './SelectDisposalTrashbox'
|
||||
import { LatLng } from 'leaflet'
|
||||
|
||||
type AnnouncementDetailsProps = {
|
||||
close: () => void,
|
||||
@ -27,33 +29,10 @@ const styles = {
|
||||
} as CSSProperties,
|
||||
}
|
||||
|
||||
function AnnouncementDetails({ close, refresh, announcement: {
|
||||
id, name, category, bestBy, description, lat, lng, address, metro, bookedBy, userId,
|
||||
} }: AnnouncementDetailsProps) {
|
||||
const { handleBook, bookButton } = useBook()
|
||||
|
||||
const removeRefresh = () => {
|
||||
close()
|
||||
refresh()
|
||||
}
|
||||
|
||||
const { handleRemove, removeButton } = useRemoveAnnouncement(removeRefresh)
|
||||
|
||||
const myId = useId()
|
||||
|
||||
return (
|
||||
<div
|
||||
className='modal'
|
||||
style={styles.container}
|
||||
>
|
||||
<Modal.Dialog style={{ minWidth: '50vw' }}>
|
||||
<Modal.Header closeButton onHide={close}>
|
||||
<Modal.Title>
|
||||
Подробнее
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
||||
<Modal.Body>
|
||||
const View = ({
|
||||
announcement: { name, category, bestBy, description, lat, lng, address, metro },
|
||||
}: { announcement: Announcement }) => (
|
||||
<>
|
||||
<h1>{name}</h1>
|
||||
|
||||
<span>{categoryNames[category]}</span>
|
||||
@ -76,17 +55,86 @@ function AnnouncementDetails({ close, refresh, announcement: {
|
||||
</Popup>
|
||||
</Marker>
|
||||
</MapContainer>
|
||||
</Modal.Body>
|
||||
</>
|
||||
)
|
||||
|
||||
<Modal.Footer>
|
||||
type ControlProps = {
|
||||
closeRefresh: () => void,
|
||||
announcement: Announcement,
|
||||
showDispose: () => void
|
||||
}
|
||||
|
||||
function Control({
|
||||
closeRefresh,
|
||||
announcement: { bookedBy, id, userId },
|
||||
showDispose
|
||||
}: ControlProps) {
|
||||
const { handleBook, bookButton } = useBook()
|
||||
|
||||
const { handleRemove, removeButton } = useRemoveAnnouncement(closeRefresh)
|
||||
|
||||
const myId = useId()
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>Забронировали {bookedBy} чел.</p>
|
||||
{(myId === userId) ? (
|
||||
<>
|
||||
<Button variant='success' onClick={showDispose}>Утилизировать</Button>
|
||||
<Button variant='success' onClick={() => void handleRemove(id)} {...removeButton} />
|
||||
</>
|
||||
) : (
|
||||
<Button variant='success' onClick={() => void handleBook(id)} {...bookButton} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function AnnouncementDetails({
|
||||
close,
|
||||
refresh,
|
||||
announcement,
|
||||
}: AnnouncementDetailsProps) {
|
||||
const closeRefresh = () => {
|
||||
close()
|
||||
refresh()
|
||||
}
|
||||
|
||||
const [disposeShow, setDisposeShow] = useState(false)
|
||||
|
||||
return (
|
||||
<div
|
||||
className='modal'
|
||||
style={styles.container}
|
||||
>
|
||||
<Modal.Dialog centered className='modal-dialog'>
|
||||
<Modal.Header closeButton onHide={close}>
|
||||
<Modal.Title>
|
||||
Подробнее
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
||||
<Modal.Body>
|
||||
<View announcement={announcement} />
|
||||
</Modal.Body>
|
||||
|
||||
<Modal.Footer>
|
||||
<Control closeRefresh={closeRefresh} showDispose={() => setDisposeShow(true)} announcement={announcement} />
|
||||
</Modal.Footer>
|
||||
</Modal.Dialog>
|
||||
<Modal centered show={disposeShow} onHide={() => setDisposeShow(false)} style={{ zIndex: 100000 }}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>
|
||||
Утилизация
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<SelectDisposalTrashbox
|
||||
annId={announcement.id}
|
||||
category={announcement.category}
|
||||
address={new LatLng(announcement.lat, announcement.lng)}
|
||||
closeRefresh={closeRefresh}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
119
front/src/components/SelectDisposalTrashbox.tsx
Normal file
119
front/src/components/SelectDisposalTrashbox.tsx
Normal file
@ -0,0 +1,119 @@
|
||||
import { Button, Modal } from 'react-bootstrap'
|
||||
import { MapContainer, TileLayer } from 'react-leaflet'
|
||||
import { CSSProperties, useState } from 'react'
|
||||
import { LatLng } from 'leaflet'
|
||||
|
||||
import { useDispose, useTrashboxes } from '../hooks/api'
|
||||
import { UseFetchReturn, gotError, gotResponse } from '../hooks/useFetch'
|
||||
import TrashboxMarkers from './TrashboxMarkers'
|
||||
import { Category } from '../assets/category'
|
||||
import { Trashbox } from '../api/trashbox/types'
|
||||
|
||||
type SelectDisposalTrashboxProps = {
|
||||
annId: number,
|
||||
category: Category,
|
||||
address: LatLng,
|
||||
closeRefresh: () => void,
|
||||
}
|
||||
|
||||
type SelectedTrashbox = {
|
||||
index: number,
|
||||
category: string,
|
||||
}
|
||||
|
||||
const styles = {
|
||||
map: {
|
||||
width: '100%',
|
||||
height: 400,
|
||||
} as CSSProperties,
|
||||
}
|
||||
|
||||
function SelectDisposalTrashbox({ annId, category, address, closeRefresh }: SelectDisposalTrashboxProps) {
|
||||
const trashboxes = useTrashboxes(address, category)
|
||||
|
||||
const [selectedTrashbox, setSelectedTrashbox] = useState<SelectedTrashbox>({ index: -1, category: '' })
|
||||
|
||||
const { handleDispose, disposeButton } = useDispose(closeRefresh)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal.Body>
|
||||
<div className='mb-3'>
|
||||
{gotResponse(trashboxes)
|
||||
? (
|
||||
gotError(trashboxes) ? (
|
||||
<p
|
||||
style={styles.map}
|
||||
className='text-danger'
|
||||
>{trashboxes.error}</p>
|
||||
) : (
|
||||
<MapContainer
|
||||
scrollWheelZoom={false}
|
||||
style={styles.map}
|
||||
center={address}
|
||||
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 style={styles.map}>
|
||||
<p>Загрузка...</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
</div>
|
||||
<DisplaySelected trashboxes={trashboxes} selectedTrashbox={selectedTrashbox} />
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
{...disposeButton}
|
||||
disabled={disposeButton.disabled || gotError(trashboxes) || !gotResponse(trashboxes) || selectedTrashbox.index < 0}
|
||||
variant='success'
|
||||
onClick={() => {
|
||||
if (gotResponse(trashboxes) && !gotError(trashboxes)) {
|
||||
const { Lat, Lng, Name } = trashboxes.data[selectedTrashbox.index]
|
||||
void handleDispose(annId, {
|
||||
Category: selectedTrashbox.category,
|
||||
Lat,
|
||||
Lng,
|
||||
Name,
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Modal.Footer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
type DisplaySelectedProps = {
|
||||
trashboxes: UseFetchReturn<Trashbox[]>,
|
||||
selectedTrashbox: SelectedTrashbox,
|
||||
}
|
||||
|
||||
function DisplaySelected({ trashboxes, selectedTrashbox }: DisplaySelectedProps) {
|
||||
if (gotResponse(trashboxes) && !gotError(trashboxes) && selectedTrashbox.index > -1) {
|
||||
return (
|
||||
<>
|
||||
<p className='mb-0'>Выбран пункт сбора мусора на {trashboxes.data[selectedTrashbox.index].Address}</p>
|
||||
<p className='mb-0'>с категорией "{selectedTrashbox.category}"</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<p className='mb-0'>Выберите пункт сбора мусора и категорию</p>
|
||||
)
|
||||
}
|
||||
|
||||
export default SelectDisposalTrashbox
|
@ -11,27 +11,32 @@ type TrashboxMarkersProps = {
|
||||
}) => void,
|
||||
}
|
||||
|
||||
function TrashboxMarkers({ trashboxes, selectTrashbox }: TrashboxMarkersProps) {
|
||||
return (
|
||||
const TrashboxMarkers = ({ trashboxes, selectTrashbox }: TrashboxMarkersProps) => (
|
||||
<>{trashboxes.map((trashbox, index) => (
|
||||
<Marker icon={iconTrash} key={`${trashbox.Lat}${trashbox.Lng}`} position={[trashbox.Lat, trashbox.Lng]}>
|
||||
<Popup>
|
||||
<p>{trashbox.Address}</p>
|
||||
<p>Тип мусора: <>
|
||||
<p className='fw-bold m-0'>{trashbox.Name}</p>
|
||||
<p className='m-0'>{trashbox.Address}</p>
|
||||
<p>Тип мусора:{' '}
|
||||
{trashbox.Categories.map((category, j) =>
|
||||
<span key={trashbox.Address + category}>
|
||||
<a href='#' onClick={() => selectTrashbox({ index, category })}>
|
||||
<a href='#' onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
selectTrashbox({ index, category })
|
||||
}}>
|
||||
{category}
|
||||
</a>
|
||||
{(j < trashbox.Categories.length - 1) ? ', ' : ''}
|
||||
</span>
|
||||
)}
|
||||
</></p>
|
||||
<p>{trashbox.Lat.toFixed(4)}, {trashbox.Lng.toFixed(4)}</p>
|
||||
</p>
|
||||
<p className='m-0'>
|
||||
{trashbox.Lat.toFixed(4)}, {trashbox.Lng.toFixed(4)}
|
||||
</p>
|
||||
</Popup>
|
||||
</Marker>
|
||||
))}</>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default TrashboxMarkers
|
||||
|
@ -13,3 +13,4 @@ export { default as StoriesPreview } from './StoriesPreview'
|
||||
export { default as Points } from './Points'
|
||||
export { default as SignOut } from './SignOut'
|
||||
export { default as Poetry } from './Poetry'
|
||||
export { default as SelectDisposalTrashbox } from './SelectDisposalTrashbox'
|
||||
|
@ -8,3 +8,4 @@ export { default as useRemoveAnnouncement } from './useRemoveAnnouncement'
|
||||
export { default as useSignIn } from './useSignIn'
|
||||
export { default as useSignUp } from './useSignUp'
|
||||
export { default as usePoetry } from './usePoetry'
|
||||
export { default as useDispose } from './useDispose'
|
||||
|
35
front/src/hooks/api/useDispose.ts
Normal file
35
front/src/hooks/api/useDispose.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import { useSendWithButton } from '..'
|
||||
import { composeDisposeBody, composeDisposeURL, processDispose } from '../../api/dispose'
|
||||
import { DisposeParams, isDisposeResponse } from '../../api/dispose/types'
|
||||
|
||||
const useDispose = (resolve: () => void) => {
|
||||
const { doSend, button } = useSendWithButton(
|
||||
'Выбор сделан',
|
||||
'Зачтено',
|
||||
true,
|
||||
composeDisposeURL(),
|
||||
'POST',
|
||||
true,
|
||||
isDisposeResponse,
|
||||
processDispose,
|
||||
)
|
||||
|
||||
const doSendWithClose = useCallback(async (...args: DisposeParams) => {
|
||||
const res = await doSend({}, {
|
||||
body: composeDisposeBody(...args),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
if (res) {
|
||||
resolve()
|
||||
}
|
||||
}, [doSend, resolve])
|
||||
|
||||
return { handleDispose: doSendWithClose, disposeButton: button }
|
||||
}
|
||||
|
||||
export default useDispose
|
@ -6,7 +6,7 @@ import { isRemoveAnnouncementResponse } from '../../api/removeAnnouncement/types
|
||||
|
||||
const useRemoveAnnouncement = (resolve: () => void) => {
|
||||
const { doSend, button } = useSendWithButton(
|
||||
'Закрыть',
|
||||
'Закрыть объявление',
|
||||
'Закрыто',
|
||||
true,
|
||||
composeRemoveAnnouncementURL(),
|
||||
|
@ -1,18 +1,41 @@
|
||||
import { LatLng } from 'leaflet'
|
||||
import { sampleSize, random } from 'lodash'
|
||||
|
||||
import { useFetch } from '../'
|
||||
import { composeTrashboxURL, processTrashbox } from '../../api/trashbox'
|
||||
import { isTrashboxResponse } from '../../api/trashbox/types'
|
||||
import { Trashbox } from '../../api/trashbox/types'
|
||||
import { UseFetchReturn } from '../useFetch'
|
||||
|
||||
const useTrashboxes = (position: LatLng) => (
|
||||
useFetch(
|
||||
composeTrashboxURL(position),
|
||||
'GET',
|
||||
true,
|
||||
isTrashboxResponse,
|
||||
processTrashbox,
|
||||
[],
|
||||
)
|
||||
import { faker } from '@faker-js/faker/locale/ru'
|
||||
import { Category, categories } from '../../assets/category'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
function genMockTrashbox(pos: LatLng): Trashbox {
|
||||
const loc = faker.location.nearbyGPSCoordinate({ origin: [pos.lat, pos.lng], radius: 1 })
|
||||
|
||||
return {
|
||||
Name: faker.company.name(),
|
||||
Address: faker.location.streetAddress(),
|
||||
Categories: faker.lorem.words({ max: 3, min: 1 }).split(' '),
|
||||
Lat: loc[0],
|
||||
Lng: loc[1],
|
||||
}
|
||||
}
|
||||
|
||||
const useTrashboxes = (position: LatLng, category: Category): UseFetchReturn<Trashbox[]> => (
|
||||
// useFetch(
|
||||
// composeTrashboxURL(position, category),
|
||||
// 'GET',
|
||||
// true,
|
||||
// isTrashboxResponse,
|
||||
// processTrashbox,
|
||||
// [],
|
||||
// )
|
||||
|
||||
{
|
||||
data: useMemo(() => new Array(3).fill(3).map(() => genMockTrashbox(position)), [position]),
|
||||
loading: false,
|
||||
error: null,
|
||||
refetch: () => { return },
|
||||
}
|
||||
)
|
||||
|
||||
export default useTrashboxes
|
||||
|
@ -4,11 +4,11 @@ import { MapContainer, TileLayer } from 'react-leaflet'
|
||||
import { latLng } from 'leaflet'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { ClickHandler, LocationMarker, TrashboxMarkers } from '../components'
|
||||
import { useAddAnnouncement, useTrashboxes } from '../hooks/api'
|
||||
import { ClickHandler, LocationMarker } from '../components'
|
||||
import { useAddAnnouncement } from '../hooks/api'
|
||||
import { categories, categoryNames } from '../assets/category'
|
||||
import { stations, lines, lineNames } from '../assets/metro'
|
||||
import { fallbackError, gotError, gotResponse } from '../hooks/useFetch'
|
||||
import { fallbackError, gotResponse } from '../hooks/useFetch'
|
||||
import { useOsmAddresses } from '../hooks/api'
|
||||
import CardLayout from '../components/CardLayout'
|
||||
|
||||
@ -22,9 +22,6 @@ const styles = {
|
||||
function AddPage() {
|
||||
const [addressPosition, setAddressPosition] = useState(latLng(59.972, 30.3227))
|
||||
|
||||
const trashboxes = useTrashboxes(addressPosition)
|
||||
const [selectedTrashbox, setSelectedTrashbox] = useState({ index: -1, category: '' })
|
||||
|
||||
const address = useOsmAddresses(addressPosition)
|
||||
|
||||
const { handleAdd, addButton } = useAddAnnouncement()
|
||||
@ -117,6 +114,7 @@ function AddPage() {
|
||||
capture='environment'
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className='mb-3' controlId='metro'>
|
||||
<Form.Label>
|
||||
Станция метро
|
||||
@ -136,50 +134,6 @@ function AddPage() {
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className='mb-3' controlId='trashbox'>
|
||||
<Form.Label>Пункт сбора мусора</Form.Label>
|
||||
<div className='mb-3'>
|
||||
{gotResponse(trashboxes)
|
||||
? (
|
||||
gotError(trashboxes) ? (
|
||||
<p
|
||||
style={styles.map}
|
||||
className='text-danger'
|
||||
>{trashboxes.error}</p>
|
||||
) : (
|
||||
<MapContainer
|
||||
scrollWheelZoom={false}
|
||||
style={styles.map}
|
||||
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 style={styles.map}>
|
||||
<p>Загрузка...</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{gotResponse(trashboxes) && !gotError(trashboxes) && selectedTrashbox.index > -1 ? (
|
||||
<p>Выбран пункт сбора мусора на {
|
||||
trashboxes.data[selectedTrashbox.index].Address
|
||||
} с категорией {selectedTrashbox.category}</p>
|
||||
) : (
|
||||
<p>Выберите пунк сбора мусора и категорию</p>
|
||||
)}
|
||||
</Form.Group>
|
||||
|
||||
<Button variant='success' type='submit' {...addButton} />
|
||||
</Form>
|
||||
</CardLayout>
|
||||
|
9
front/src/utils/dispose.ts
Normal file
9
front/src/utils/dispose.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Announcement } from '../api/announcement/types'
|
||||
|
||||
const DAY_MS = 24 * 60 * 60 * 1000
|
||||
|
||||
const isAnnExpired = (ann: Announcement) => (
|
||||
(ann.bestBy - Date.now()) < DAY_MS
|
||||
)
|
||||
|
||||
export { isAnnExpired }
|
Loading…
x
Reference in New Issue
Block a user