import { useCallback, useLayoutEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { TabList, TabPanel, Tabs } from 'react-tabs'; import { toast } from 'react-toastify'; import BackendError from '../../components/BackendError'; import ConceptTab from '../../components/Common/ConceptTab'; import { Loader } from '../../components/Common/Loader'; import { useLibrary } from '../../context/LibraryContext'; import { useRSForm } from '../../context/RSFormContext'; import { prefixes, TIMEOUT_UI_REFRESH } from '../../utils/constants'; import { CstType, ICstCreateData, SyntaxTree } from '../../utils/models'; import { createAliasFor } from '../../utils/staticUI'; import DlgCloneRSForm from './DlgCloneRSForm'; import DlgCreateCst from './DlgCreateCst'; import DlgDeleteCst from './DlgDeleteCst'; import DlgShowAST from './DlgShowAST'; import DlgUploadRSForm from './DlgUploadRSForm'; import EditorConstituenta from './EditorConstituenta'; import EditorItems from './EditorItems'; import EditorRSForm from './EditorRSForm'; import EditorTermGraph from './EditorTermGraph'; import RSFormStats from './elements/RSFormStats'; import RSTabsMenu from './RSTabsMenu'; export enum RSTabsList { CARD = 0, CST_LIST = 1, CST_EDIT = 2, TERM_GRAPH = 3 } function RSTabs() { const navigate = useNavigate(); const search = useLocation().search; const { error, schema, loading, cstCreate, cstDelete } = useRSForm(); const { destroySchema } = useLibrary(); const [activeTab, setActiveTab] = useState(RSTabsList.CARD); const [activeID, setActiveID] = useState(undefined) const [showUpload, setShowUpload] = useState(false); const [showClone, setShowClone] = useState(false); const [syntaxTree, setSyntaxTree] = useState([]); const [expression, setExpression] = useState(''); const [showAST, setShowAST] = useState(false); const [afterDelete, setAfterDelete] = useState<((items: number[]) => void) | undefined>(undefined); const [toBeDeleted, setToBeDeleted] = useState([]); const [showDeleteCst, setShowDeleteCst] = useState(false); const [defaultType, setDefaultType] = useState(undefined); const [insertWhere, setInsertWhere] = useState(undefined); const [showCreateCst, setShowCreateCst] = useState(false); useLayoutEffect(() => { if (schema) { const oldTitle = document.title document.title = schema.title return () => { document.title = oldTitle } } }, [schema]); useLayoutEffect(() => { const activeTab = Number(new URLSearchParams(search).get('tab')) ?? RSTabsList.CARD; const cstQuery = new URLSearchParams(search).get('active'); setActiveTab(activeTab); setActiveID(Number(cstQuery) ?? (schema && schema?.items.length > 0 && schema?.items[0])); }, [search, setActiveTab, setActiveID, schema]); function onSelectTab(index: number) { navigateTo(index, activeID); } const navigateTo = useCallback( (tab: RSTabsList, activeID?: number) => { if (activeID) { navigate(`/rsforms/${schema!.id}?tab=${tab}&active=${activeID}`, { replace: tab === activeTab && tab !== RSTabsList.CST_EDIT }); } else { navigate(`/rsforms/${schema!.id}?tab=${tab}`); } }, [navigate, schema, activeTab]); const handleCreateCst = useCallback( (type: CstType, selectedCst?: number) => { if (!schema?.items) { return; } const data: ICstCreateData = { cst_type: type, alias: createAliasFor(type, schema), insert_after: selectedCst ?? insertWhere ?? null } cstCreate(data, newCst => { toast.success(`Конституента добавлена: ${newCst.alias}`); navigateTo(activeTab, newCst.id); if (activeTab === RSTabsList.CST_EDIT || activeTab == RSTabsList.CST_LIST) { setTimeout(() => { const element = document.getElementById(`${prefixes.cst_list}${newCst.alias}`); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' }); } }, TIMEOUT_UI_REFRESH); } }); }, [schema, cstCreate, insertWhere, navigateTo, activeTab]); const promptCreateCst = useCallback( (selectedID: number | undefined, type: CstType | undefined, skipDialog?: boolean) => { if (skipDialog && type) { handleCreateCst(type, selectedID); } else { setDefaultType(type); setInsertWhere(selectedID); setShowCreateCst(true); } }, [handleCreateCst]); const handleDeleteCst = useCallback( (deleted: number[]) => { if (!schema) { return; } const data = { items: deleted.map(id => { return { id: id }; }) }; let activeIndex = schema.items.findIndex(cst => cst.id === activeID); cstDelete(data, () => { const deletedNames = deleted.map(id => schema.items.find(cst => cst.id === id)?.alias).join(', '); toast.success(`Конституенты удалены: ${deletedNames}`); if (deleted.length === schema.items.length) { navigateTo(RSTabsList.CST_LIST); } if (activeIndex) { while (activeIndex < schema.items.length && deleted.find(id => id === schema.items[activeIndex].id)) { ++activeIndex; } navigateTo(activeTab, schema.items[activeIndex].id); } if (afterDelete) afterDelete(deleted); }); }, [afterDelete, cstDelete, schema, activeID, activeTab, navigateTo]); const promptDeleteCst = useCallback( (selected: number[], callback?: (items: number[]) => void) => { setAfterDelete(() => ( (items: number[]) => { if (callback) callback(items); })); setToBeDeleted(selected); setShowDeleteCst(true) }, []); const onShowAST = useCallback( (expression: string, ast: SyntaxTree) => { setSyntaxTree(ast); setExpression(expression); setShowAST(true); }, []); const onOpenCst = useCallback( (cstID: number) => { navigateTo(RSTabsList.CST_EDIT, cstID) }, [navigateTo]); const onDestroySchema = useCallback( () => { if (!schema || !window.confirm('Вы уверены, что хотите удалить данную схему?')) { return; } destroySchema(schema.id, () => { toast.success('Схема удалена'); navigate('/library?filter=personal'); }); }, [schema, destroySchema, navigate]); return (
{ loading && } { error && } { schema && !loading && <> {showUpload && setShowUpload(false)} />} {showClone && setShowClone(false)} />} {showAST && setShowAST(false)} />} {showCreateCst && setShowCreateCst(false)} onCreate={handleCreateCst} defaultType={defaultType} />} {showDeleteCst && setShowDeleteCst(false)} onDelete={handleDeleteCst} selected={toBeDeleted} />} setShowClone(true)} showUploadDialog={() => setShowUpload(true)} /> Паспорт схемы Конституенты {`${schema.stats?.count_errors ?? 0} | ${schema.stats?.count_all ?? 0}`} Редактор Граф термов {schema.stats && } }
); } export default RSTabs;