178 lines
5.6 KiB
TypeScript
178 lines
5.6 KiB
TypeScript
import {Stack, Typography, Box, Grid2 as Grid, Button, MenuItem, Select, FormControl, InputLabel, TextField} from '@mui/material'
|
||
import {useShow} from '@refinedev/core'
|
||
import {Show, TextFieldComponent} from '@refinedev/mui'
|
||
|
||
import {useEffect, useState} from 'react'
|
||
import axios from 'axios'
|
||
|
||
import {BACKEND_URL} from '../../lib/constants'
|
||
|
||
type ArticleItem = {
|
||
id: number
|
||
heading: string
|
||
body: string
|
||
}
|
||
|
||
export const SightShow = () => {
|
||
const {query} = useShow({})
|
||
const {data, isLoading} = query
|
||
const record = data?.data
|
||
|
||
const [articles, setArticles] = useState<ArticleItem[]>([])
|
||
const [linkedArticles, setLinkedArticles] = useState<ArticleItem[]>([])
|
||
const [selectedArticleId, setSelectedArticleId] = useState<number | ''>('')
|
||
const [pageNum, setPageNum] = useState<number>(1)
|
||
const [articlesLoading, setArticlesLoading] = useState<boolean>(true)
|
||
|
||
useEffect(() => {
|
||
if (record?.id) {
|
||
axios
|
||
.get(`${BACKEND_URL}/sight/${record.id}/article`)
|
||
.then((response) => {
|
||
setLinkedArticles(response?.data || [])
|
||
})
|
||
.catch(() => {
|
||
setLinkedArticles([])
|
||
})
|
||
}
|
||
}, [record?.id])
|
||
|
||
useEffect(() => {
|
||
axios
|
||
.get(`${BACKEND_URL}/article/`) // without "/" throws CORS error
|
||
.then((response) => {
|
||
setArticles(response?.data || [])
|
||
setArticlesLoading(false)
|
||
})
|
||
.catch(() => {
|
||
setArticles([])
|
||
setArticlesLoading(false)
|
||
})
|
||
}, [])
|
||
|
||
const availableArticles = articles.filter((article) => !linkedArticles.some((linked) => linked.id === article.id))
|
||
|
||
const linkArticle = () => {
|
||
if (selectedArticleId) {
|
||
const requestData = {
|
||
article_id: selectedArticleId,
|
||
page_num: pageNum,
|
||
}
|
||
|
||
axios
|
||
.post(`${BACKEND_URL}/sight/${record?.id}/article`, requestData, {
|
||
headers: {
|
||
accept: 'application/json',
|
||
'Content-Type': 'application/json',
|
||
},
|
||
})
|
||
.then(() => {
|
||
axios
|
||
.get(`${BACKEND_URL}/sight/${record?.id}/article`)
|
||
.then((response) => {
|
||
setLinkedArticles(response?.data || [])
|
||
setPageNum(pageNum + 1)
|
||
})
|
||
.catch(() => {
|
||
setLinkedArticles([])
|
||
})
|
||
})
|
||
.catch((error) => {
|
||
console.error('Error linking article:', error)
|
||
})
|
||
}
|
||
}
|
||
|
||
const deleteArticle = (articleId: number) => {
|
||
axios
|
||
.delete(`${BACKEND_URL}/sight/${record?.id}/article`, {
|
||
data: {article_id: articleId},
|
||
})
|
||
.then(() => {
|
||
setLinkedArticles((prev) => prev.filter((item) => item.id !== articleId))
|
||
})
|
||
.catch((error) => {
|
||
console.error('Error unlinking article:', error)
|
||
})
|
||
}
|
||
|
||
const fields = [
|
||
// {label: 'ID', data: 'id'},
|
||
{label: 'Название', data: 'name'},
|
||
{label: 'Широта', data: 'latitude'},
|
||
{label: 'Долгота', data: 'longitude'},
|
||
{label: 'ID города', data: 'city_id'},
|
||
]
|
||
|
||
return (
|
||
<Show isLoading={isLoading}>
|
||
<Stack gap={4}>
|
||
{fields.map(({label, data}) => (
|
||
<Stack key={data} gap={1}>
|
||
<Typography variant="body1" fontWeight="bold">
|
||
{label}
|
||
</Typography>
|
||
<TextFieldComponent value={record?.[data]} />
|
||
</Stack>
|
||
))}
|
||
|
||
<Stack gap={2}>
|
||
<Typography variant="body1" fontWeight="bold">
|
||
Привязанные статьи
|
||
</Typography>
|
||
|
||
<Grid container gap={2}>
|
||
{articlesLoading ? (
|
||
<Typography>Загрузка статей...</Typography>
|
||
) : linkedArticles.length > 0 ? (
|
||
linkedArticles.map((article) => (
|
||
<Box key={article.id} sx={{border: '2px solid #dddddd25', padding: '20px', marginBottom: '8px'}}>
|
||
<Stack gap={1}>
|
||
<Typography variant="h5">
|
||
<strong>{article.heading}</strong>
|
||
</Typography>
|
||
<Typography>{article.body}</Typography>
|
||
|
||
<Button variant="outlined" color="error" onClick={() => deleteArticle(article.id)} sx={{mt: 2}}>
|
||
Отвязать статью
|
||
</Button>
|
||
</Stack>
|
||
</Box>
|
||
))
|
||
) : (
|
||
<Typography>Статьи не найдены</Typography>
|
||
)}
|
||
</Grid>
|
||
|
||
<Stack gap={2}>
|
||
<Typography variant="body1" fontWeight="bold">
|
||
Привязать статью
|
||
</Typography>
|
||
|
||
<Stack gap={2.5}>
|
||
<FormControl fullWidth>
|
||
<InputLabel>Статья</InputLabel>
|
||
<Select value={selectedArticleId} onChange={(e) => setSelectedArticleId(Number(e.target.value))} label="Статья" fullWidth>
|
||
{availableArticles.map((article) => (
|
||
<MenuItem key={article.id} value={article.id}>
|
||
{article.heading}
|
||
</MenuItem>
|
||
))}
|
||
</Select>
|
||
</FormControl>
|
||
|
||
<FormControl fullWidth>
|
||
<TextField type="number" label="Номер страницы" name="page_num" value={pageNum} onChange={(e) => setPageNum(Number(e.target.value))} fullWidth InputLabelProps={{shrink: true}} />
|
||
</FormControl>
|
||
|
||
<Button variant="contained" onClick={linkArticle} disabled={!selectedArticleId}>
|
||
Привязать
|
||
</Button>
|
||
</Stack>
|
||
</Stack>
|
||
</Stack>
|
||
</Stack>
|
||
</Show>
|
||
)
|
||
}
|