import { useState, useEffect } from "react"; import { Stack, Typography, Button, Accordion, AccordionSummary, AccordionDetails, useTheme, TextField, Autocomplete, TableCell, TableContainer, Table, TableHead, TableRow, Paper, TableBody, } from "@mui/material"; import { observer } from "mobx-react-lite"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import { authInstance, languageStore, selectedCityStore } from "@shared"; type Field = { label: string; data: keyof T; render?: (value: any) => React.ReactNode; }; type LinkedSightsProps = { parentId: string | number; fields: Field[]; setItemsParent?: (items: T[]) => void; type: "show" | "edit"; onUpdate?: () => void; disableCreation?: boolean; updatedLinkedItems?: T[]; refresh?: number; }; export const LinkedSights = < T extends { id: number; name: string; [key: string]: any } >( props: LinkedSightsProps ) => { const theme = useTheme(); return ( <> } sx={{ background: theme.palette.background.paper, borderBottom: `1px solid ${theme.palette.divider}`, width: "100%", }} > Привязанные достопримечательности ); }; const LinkedSightsContentsInner = < T extends { id: number; name: string; [key: string]: any } >({ parentId, setItemsParent, fields, type, onUpdate, disableCreation = false, updatedLinkedItems, refresh, }: LinkedSightsProps) => { const { language } = languageStore; const [allItems, setAllItems] = useState([]); const [linkedItems, setLinkedItems] = useState([]); const [selectedItemId, setSelectedItemId] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => {}, [error]); const parentResource = "station"; const childResource = "sight"; const availableItems = allItems .filter((item) => !linkedItems.some((linked) => linked.id === item.id)) .filter((item) => { // Фильтруем по городу из навбара const selectedCityId = selectedCityStore.selectedCityId; if (selectedCityId && "city_id" in item) { return item.city_id === selectedCityId; } return true; }) .sort((a, b) => a.name.localeCompare(b.name)); useEffect(() => { if (updatedLinkedItems) { setLinkedItems(updatedLinkedItems); } }, [updatedLinkedItems]); useEffect(() => { setItemsParent?.(linkedItems); }, [linkedItems, setItemsParent]); const linkItem = () => { if (selectedItemId !== null) { setError(null); const requestData = { sight_id: selectedItemId, }; authInstance .post(`/${parentResource}/${parentId}/${childResource}`, requestData) .then(() => { const newItem = allItems.find((item) => item.id === selectedItemId); if (newItem) { setLinkedItems([...linkedItems, newItem]); } setSelectedItemId(null); onUpdate?.(); }) .catch((error) => { console.error("Error linking sight:", error); setError("Failed to link sight"); }); } }; const deleteItem = (itemId: number) => { setError(null); authInstance .delete(`/${parentResource}/${parentId}/${childResource}`, { data: { [`${childResource}_id`]: itemId }, }) .then(() => { setLinkedItems(linkedItems.filter((item) => item.id !== itemId)); onUpdate?.(); }) .catch((error) => { console.error("Error deleting sight:", error); setError("Failed to delete sight"); }); }; useEffect(() => { if (parentId) { setIsLoading(true); setError(null); authInstance .get(`/${parentResource}/${parentId}/${childResource}`) .then((response) => { setLinkedItems(response?.data || []); }) .catch((error) => { console.error("Error fetching linked sights:", error); setError("Failed to load linked sights"); setLinkedItems([]); }) .finally(() => { setIsLoading(false); }); } }, [parentId, language, refresh]); useEffect(() => { if (type === "edit") { setError(null); authInstance .get(`/${childResource}`) .then((response) => { setAllItems(response?.data || []); }) .catch((error) => { console.error("Error fetching all sights:", error); setError("Failed to load available sights"); setAllItems([]); }); } }, [type]); return ( <> {linkedItems?.length > 0 && ( {fields.map((field) => ( {field.label} ))} {type === "edit" && ( Действие )} {linkedItems.map((item, index) => ( {index + 1} {fields.map((field, idx) => ( {field.render ? field.render(item[field.data]) : item[field.data]} ))} {type === "edit" && ( )} ))}
)} {linkedItems.length === 0 && !isLoading && ( Достопримечательности не найдены )} {type === "edit" && !disableCreation && ( Добавить достопримечательность item.id === selectedItemId) || null } onChange={(_, newValue) => setSelectedItemId(newValue?.id || null)} options={availableItems} getOptionLabel={(item) => String(item.name)} renderInput={(params) => ( )} isOptionEqualToValue={(option, value) => option.id === value?.id} filterOptions={(options, { inputValue }) => { const searchWords = inputValue .toLowerCase() .split(" ") .filter(Boolean); return options.filter((option) => { const optionWords = String(option.name) .toLowerCase() .split(" "); return searchWords.every((searchWord) => optionWords.some((word) => word.startsWith(searchWord)) ); }); }} renderOption={(props, option) => (
  • {String(option.name)}
  • )} />
    )} {isLoading && ( Загрузка... )} {error && ( {error} )} ); }; export const LinkedSightsContents = observer( LinkedSightsContentsInner ) as typeof LinkedSightsContentsInner;