integrate sight
data into /station/show
page
This commit is contained in:
parent
bf72f78020
commit
5d3c6fe7f9
@ -122,7 +122,6 @@ function App() {
|
|||||||
create: '/station/create',
|
create: '/station/create',
|
||||||
edit: '/station/edit/:id',
|
edit: '/station/edit/:id',
|
||||||
show: '/station/show/:id',
|
show: '/station/show/:id',
|
||||||
// добавить SHOW для station->sight (https://wn.krbl.ru/station/2/sight)
|
|
||||||
meta: {
|
meta: {
|
||||||
canDelete: true,
|
canDelete: true,
|
||||||
label: 'Остановки',
|
label: 'Остановки',
|
||||||
|
@ -39,7 +39,7 @@ export const SightShow = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
axios
|
axios
|
||||||
.get(`${BACKEND_URL}/article/`)
|
.get(`${BACKEND_URL}/article/`) // without "/" throws CORS error
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setArticles(response?.data || [])
|
setArticles(response?.data || [])
|
||||||
setArticlesLoading(false)
|
setArticlesLoading(false)
|
||||||
|
@ -1,13 +1,100 @@
|
|||||||
import {Stack, Typography} from '@mui/material'
|
import {Stack, Typography, Box, Grid2 as Grid, Button, MenuItem, Select, FormControl, InputLabel} from '@mui/material'
|
||||||
import {useShow} from '@refinedev/core'
|
import {useShow} from '@refinedev/core'
|
||||||
import {Show, TextFieldComponent as TextField} from '@refinedev/mui'
|
import {Show, TextFieldComponent as TextField} from '@refinedev/mui'
|
||||||
|
|
||||||
|
import {useEffect, useState} from 'react'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
import {BACKEND_URL} from '../../lib/constants'
|
||||||
|
|
||||||
|
type SightItem = {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
latitude: number
|
||||||
|
longitude: number
|
||||||
|
city_id: number
|
||||||
|
}
|
||||||
|
|
||||||
export const StationShow = () => {
|
export const StationShow = () => {
|
||||||
const {query} = useShow({})
|
const {query} = useShow({})
|
||||||
const {data, isLoading} = query
|
const {data, isLoading} = query
|
||||||
|
|
||||||
const record = data?.data
|
const record = data?.data
|
||||||
|
|
||||||
|
const [sights, setSights] = useState<SightItem[]>([])
|
||||||
|
const [linkedSights, setLinkedSights] = useState<SightItem[]>([])
|
||||||
|
const [selectedSightId, setSelectedSightId] = useState<number | ''>('')
|
||||||
|
const [sightsLoading, setSightsLoading] = useState<boolean>(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (record?.id) {
|
||||||
|
axios
|
||||||
|
.get(`${BACKEND_URL}/station/${record.id}/sight`)
|
||||||
|
.then((response) => {
|
||||||
|
setLinkedSights(response?.data || [])
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setLinkedSights([])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [record?.id])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
axios
|
||||||
|
.get(`${BACKEND_URL}/sight/`) // without "/" throws CORS error
|
||||||
|
.then((response) => {
|
||||||
|
setSights(response?.data || [])
|
||||||
|
setSightsLoading(false)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setSights([])
|
||||||
|
setSightsLoading(false)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const availableSights = sights.filter((sight) => !linkedSights.some((linked) => linked.id === sight.id))
|
||||||
|
|
||||||
|
const linkSight = () => {
|
||||||
|
if (selectedSightId) {
|
||||||
|
axios
|
||||||
|
.post(
|
||||||
|
`${BACKEND_URL}/station/${record?.id}/sight`,
|
||||||
|
{sight_id: selectedSightId},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
axios
|
||||||
|
.get(`${BACKEND_URL}/station/${record?.id}/sight`)
|
||||||
|
.then((response) => {
|
||||||
|
setLinkedSights(response?.data || [])
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setLinkedSights([])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error linking sight:', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteSight = (sightId: number) => {
|
||||||
|
axios
|
||||||
|
.delete(`${BACKEND_URL}/station/${record?.id}/sight`, {
|
||||||
|
data: {sight_id: sightId},
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
setLinkedSights((prevSights) => prevSights.filter((item) => item.id !== sightId))
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error deleting sight:', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const fields = [
|
const fields = [
|
||||||
{label: 'ID', data: 'id'},
|
{label: 'ID', data: 'id'},
|
||||||
{label: 'Name', data: 'name'},
|
{label: 'Name', data: 'name'},
|
||||||
@ -16,6 +103,14 @@ export const StationShow = () => {
|
|||||||
{label: 'Description', data: 'description'},
|
{label: 'Description', data: 'description'},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const sightFields = [
|
||||||
|
{label: 'ID', data: 'id' as keyof SightItem},
|
||||||
|
{label: 'Name', data: 'name' as keyof SightItem},
|
||||||
|
{label: 'Latitude', data: 'latitude' as keyof SightItem},
|
||||||
|
{label: 'Longitude', data: 'longitude' as keyof SightItem},
|
||||||
|
{label: 'City ID', data: 'city_id' as keyof SightItem},
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show isLoading={isLoading}>
|
<Show isLoading={isLoading}>
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
@ -24,9 +119,60 @@ export const StationShow = () => {
|
|||||||
<Typography variant="body1" fontWeight="bold">
|
<Typography variant="body1" fontWeight="bold">
|
||||||
{label}
|
{label}
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField value={record?.[data]} />
|
<TextField value={record?.[data] || ''} />
|
||||||
</Stack>
|
</Stack>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
<Stack gap={2}>
|
||||||
|
<Typography variant="body1" fontWeight="bold">
|
||||||
|
Linked Sights
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Grid container gap={2}>
|
||||||
|
{sightsLoading ? (
|
||||||
|
<Typography>Loading sights...</Typography>
|
||||||
|
) : linkedSights.length > 0 ? (
|
||||||
|
linkedSights.map((sight, index) => (
|
||||||
|
<Box key={index} sx={{border: '2px solid #dddddd25', padding: '20px', marginBottom: '8px'}}>
|
||||||
|
<Stack gap={0.5}>
|
||||||
|
{sightFields.map(({label, data}) => (
|
||||||
|
<Typography key={data}>
|
||||||
|
<strong>{label}:</strong> {sight?.[data]}
|
||||||
|
</Typography>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Button variant="outlined" color="error" onClick={() => deleteSight(sight?.id)} sx={{mt: 2}}>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Typography>No sights found</Typography>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Stack gap={2}>
|
||||||
|
<Typography variant="body1" fontWeight="bold">
|
||||||
|
Link Sight
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<InputLabel>Sight</InputLabel>
|
||||||
|
<Select value={selectedSightId} onChange={(e) => setSelectedSightId(Number(e.target.value))} label="Sight" fullWidth>
|
||||||
|
{availableSights.map((sight) => (
|
||||||
|
<MenuItem key={sight.id} value={sight.id}>
|
||||||
|
{sight.name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<Button variant="contained" onClick={linkSight} disabled={!selectedSightId}>
|
||||||
|
Link Sight
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Show>
|
</Show>
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user