import { useState, useEffect } from "react"; import { languageStore } from "../store/LanguageStore"; import { Stack, Typography, Button, FormControl, Accordion, AccordionSummary, AccordionDetails, useTheme, TextField, Autocomplete, TableCell, TableContainer, Table, TableHead, TableRow, Paper, TableBody, IconButton, } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import DragIndicatorIcon from "@mui/icons-material/DragIndicator"; import { axiosInstance } from "../providers/data"; import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd"; import { articleStore } from "../store/ArticleStore"; import { ArticleEditModal } from "./modals/ArticleEditModal"; import { StationEditModal } from "./modals/StationEditModal"; import { stationStore } from "../store/StationStore"; function insertAtPosition(arr: T[], pos: number, value: T): T[] { const index = pos - 1; if (index >= arr.length) { arr.push(value); } else { arr.splice(index, 0, value); } return arr; } type Field = { label: string; data: keyof T; render?: (value: any) => React.ReactNode; }; type ExtraFieldConfig = { type: "number"; label: string; minValue: number; maxValue: (linkedItems: any[]) => number; }; type LinkedItemsProps = { parentId: string | number; parentResource: string; childResource: string; fields: Field[]; setItemsParent?: (items: T[]) => void; title: string; type: "show" | "edit"; extraField?: ExtraFieldConfig; dragAllowed?: boolean; onSave?: (items: T[]) => void; onUpdate?: () => void; dontRecurse?: boolean; disableCreation?: boolean; updatedLinkedItems?: T[]; refresh?: number; }; const reorder = (list: any[], startIndex: number, endIndex: number) => { const result = Array.from(list); const [removed] = result.splice(startIndex, 1); result.splice(endIndex, 0, removed); return result; }; export const LinkedItems = ( props: LinkedItemsProps ) => { const theme = useTheme(); return ( <> } sx={{ background: theme.palette.background.paper, borderBottom: `1px solid ${theme.palette.divider}`, }} > Привязанные {props.title} {!props.dontRecurse && ( <> )} ); }; export const LinkedItemsContents = < T extends { id: number; [key: string]: any } >({ parentId, parentResource, childResource, setItemsParent, fields, title, dragAllowed = false, type, onUpdate, disableCreation = false, updatedLinkedItems, refresh, }: LinkedItemsProps) => { const { language } = languageStore; const { setArticleModalOpenAction, setArticleIdAction } = articleStore; const { setStationModalOpenAction, setStationIdAction, setRouteIdAction } = stationStore; const [position, setPosition] = useState(1); const [items, setItems] = useState([]); const [linkedItems, setLinkedItems] = useState([]); const [selectedItemId, setSelectedItemId] = useState(null); const [pageNum, setPageNum] = useState(1); const [isLoading, setIsLoading] = useState(true); const [mediaOrder, setMediaOrder] = useState(1); let availableItems = items.filter( (item) => !linkedItems.some((linked) => linked.id === item.id) ); useEffect(() => { if (childResource == "station") { availableItems = availableItems.sort((a, b) => a.name.localeCompare(b.name) ); } }, [childResource, availableItems]); useEffect(() => { if (!updatedLinkedItems?.length) return; setLinkedItems(updatedLinkedItems); }, [updatedLinkedItems]); useEffect(() => { setItemsParent?.(linkedItems); }, [linkedItems, setItemsParent]); const onDragEnd = (result: any) => { if (!result.destination) return; const reorderedItems = reorder( linkedItems, result.source.index, result.destination.index ); setLinkedItems(reorderedItems); if (parentResource === "sight" && childResource === "article") { axiosInstance.post( `${import.meta.env.VITE_KRBL_API}/sight/${parentId}/article/order`, { articles: reorderedItems.map((item) => ({ id: item.id, })), } ); } else { axiosInstance.post( `${import.meta.env.VITE_KRBL_API}/route/${parentId}/station`, { stations: reorderedItems.map((item) => ({ id: item.id, })), } ); } }; useEffect(() => { if (parentId) { axiosInstance .get( `${ import.meta.env.VITE_KRBL_API }/${parentResource}/${parentId}/${childResource}` ) .then((response) => { setLinkedItems(response?.data || []); }) .catch(() => { setLinkedItems([]); }); } }, [parentId, parentResource, childResource, language, refresh]); useEffect(() => { if (type === "edit") { axiosInstance .get(`${import.meta.env.VITE_KRBL_API}/${childResource}/`) .then((response) => { setItems(response?.data || []); setIsLoading(false); }) .catch(() => { setItems([]); setIsLoading(false); }); } else { setIsLoading(false); } }, [childResource, type]); useEffect(() => { if (childResource === "article" && parentResource === "sight") { setPageNum(linkedItems.length + 1); } }, [linkedItems, childResource, parentResource]); const linkItem = () => { if (selectedItemId !== null) { const requestData = childResource === "article" ? { [`${childResource}_id`]: selectedItemId, page_num: pageNum, } : childResource === "media" ? { [`${childResource}_id`]: selectedItemId, media_order: mediaOrder, } : childResource === "station" ? { stations: insertAtPosition( linkedItems.map((item) => ({ id: item.id, })), position, { id: selectedItemId, } ), } : { [`${childResource}_id`]: selectedItemId }; axiosInstance .post( `${ import.meta.env.VITE_KRBL_API }/${parentResource}/${parentId}/${childResource}`, requestData ) .then(() => { axiosInstance .get( `${ import.meta.env.VITE_KRBL_API }/${parentResource}/${parentId}/${childResource}` ) .then((response) => { setLinkedItems(response?.data || []); setSelectedItemId(null); if (childResource === "article") { setPageNum(pageNum + 1); } onUpdate?.(); }); }) .catch((error) => { console.error("Error linking item:", error); }); } }; const deleteItem = (itemId: number) => { axiosInstance .delete( `${ import.meta.env.VITE_KRBL_API }/${parentResource}/${parentId}/${childResource}`, { data: { [`${childResource}_id`]: itemId }, } ) .then(() => { setLinkedItems((prev) => prev.filter((item) => item.id !== itemId)); onUpdate?.(); }) .catch((error) => { console.error("Error unlinking item:", error); }); }; return ( <> {linkedItems?.length > 0 && ( {type === "edit" && dragAllowed && ( )} {fields.map((field) => ( {field.label} ))} {type === "edit" && ( Действие )} {(provided) => ( {linkedItems.map((item, index) => ( {(provided) => ( { if ( childResource === "article" && type === "edit" ) { setArticleModalOpenAction(true); setArticleIdAction(item.id); } if ( childResource === "station" && type === "edit" ) { setStationModalOpenAction(true); setStationIdAction(item.id); setRouteIdAction(Number(parentId)); } }} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps} hover > {type === "edit" && dragAllowed && ( )} {index + 1} {fields.map((field, index) => ( {field.render ? field.render(item[field.data]) : item[field.data]} ))} {type === "edit" && ( )} )} ))} {provided.placeholder} )}
)} {linkedItems.length === 0 && !isLoading && ( {title} не найдены )} {type === "edit" && !disableCreation && ( Добавить {title} item.id === selectedItemId) || null } onChange={(_, newValue) => setSelectedItemId(newValue?.id || null)} options={availableItems} getOptionLabel={(item) => String(item[fields[0].data])} renderInput={(params) => ( )} isOptionEqualToValue={(option, value) => option.id === value?.id} filterOptions={(options, { inputValue }) => { const searchWords = inputValue .toLowerCase() .split(" ") .filter((word) => word.length > 0); return options.filter((option) => { const optionWords = String(option[fields[0].data]) .toLowerCase() .split(" "); return searchWords.every((searchWord) => optionWords.some((word) => word.startsWith(searchWord)) ); }); }} renderOption={(props, option) => (
  • {String(option[fields[0].data])}
  • )} /> {/* {childResource === "article" && ( { const newValue = Number(e.target.value); const minValue = linkedItems.length + 1; setPageNum(newValue < minValue ? minValue : newValue); }} fullWidth InputLabelProps={{ shrink: true }} /> )} */} {childResource === "media" && ( { const rawValue = e.target.value; const numericValue = Number(rawValue); const maxValue = linkedItems.length + 1; if (isNaN(numericValue)) { return; } else { let newValue = numericValue; if (newValue < 10 && newValue > 0) { setMediaOrder(numericValue); } if (newValue > maxValue) { newValue = maxValue; } setMediaOrder(newValue); } }} fullWidth InputLabelProps={{ shrink: true }} /> )} {childResource == "station" && ( { const newValue = Number(e.target.value); setPosition( newValue > linkedItems.length + 1 ? linkedItems.length + 1 : newValue ); }} > )}
    )} ); };