2023-07-29 03:31:21 +03:00
|
|
|
|
import { useCallback, useEffect, useLayoutEffect, useState } from 'react';
|
2023-07-29 15:37:49 +03:00
|
|
|
|
import { useNavigate } from 'react-router-dom';
|
2023-07-25 20:27:29 +03:00
|
|
|
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
2023-07-29 15:37:49 +03:00
|
|
|
|
import { toast } from 'react-toastify';
|
2023-07-25 20:27:29 +03:00
|
|
|
|
|
|
|
|
|
import BackendError from '../../components/BackendError';
|
2023-07-15 17:46:19 +03:00
|
|
|
|
import ConceptTab from '../../components/Common/ConceptTab';
|
|
|
|
|
import { Loader } from '../../components/Common/Loader';
|
2023-07-25 20:27:29 +03:00
|
|
|
|
import { useRSForm } from '../../context/RSFormContext';
|
|
|
|
|
import useLocalStorage from '../../hooks/useLocalStorage';
|
2023-07-29 15:37:49 +03:00
|
|
|
|
import { CstType,type IConstituenta, ICstCreateData, SyntaxTree } from '../../utils/models';
|
|
|
|
|
import { createAliasFor } from '../../utils/staticUI';
|
2023-07-28 18:23:37 +03:00
|
|
|
|
import DlgCloneRSForm from './DlgCloneRSForm';
|
2023-07-29 15:37:49 +03:00
|
|
|
|
import DlgCreateCst from './DlgCreateCst';
|
2023-07-29 03:31:21 +03:00
|
|
|
|
import DlgShowAST from './DlgShowAST';
|
2023-07-28 18:23:37 +03:00
|
|
|
|
import DlgUploadRSForm from './DlgUploadRSForm';
|
2023-07-28 00:03:37 +03:00
|
|
|
|
import EditorConstituenta from './EditorConstituenta';
|
|
|
|
|
import EditorItems from './EditorItems';
|
|
|
|
|
import EditorRSForm from './EditorRSForm';
|
|
|
|
|
import RSFormStats from './elements/RSFormStats';
|
2023-07-27 22:04:25 +03:00
|
|
|
|
import RSTabsMenu from './RSTabsMenu';
|
2023-07-15 17:46:19 +03:00
|
|
|
|
|
2023-07-27 22:04:25 +03:00
|
|
|
|
export enum RSTabsList {
|
2023-07-15 17:46:19 +03:00
|
|
|
|
CARD = 0,
|
|
|
|
|
CST_LIST = 1,
|
|
|
|
|
CST_EDIT = 2
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-27 22:04:25 +03:00
|
|
|
|
function RSTabs() {
|
2023-07-29 15:37:49 +03:00
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
const { setActiveID, activeID, error, schema, loading, cstCreate } = useRSForm();
|
|
|
|
|
const [activeTab, setActiveTab] = useLocalStorage('rsform_edit_tab', RSTabsList.CARD);
|
2023-07-21 00:09:05 +03:00
|
|
|
|
const [init, setInit] = useState(false);
|
2023-07-15 17:46:19 +03:00
|
|
|
|
|
2023-07-29 03:31:21 +03:00
|
|
|
|
const [showUpload, setShowUpload] = useState(false);
|
2023-07-29 15:37:49 +03:00
|
|
|
|
|
2023-07-29 03:31:21 +03:00
|
|
|
|
const [showClone, setShowClone] = useState(false);
|
2023-07-29 15:37:49 +03:00
|
|
|
|
|
2023-07-29 03:31:21 +03:00
|
|
|
|
const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]);
|
|
|
|
|
const [showAST, setShowAST] = useState(false);
|
2023-07-29 15:37:49 +03:00
|
|
|
|
|
|
|
|
|
const [defaultType, setDefaultType] = useState<CstType | undefined>(undefined);
|
|
|
|
|
const [insertPosition, setInsertPosition] = useState<number | undefined>(undefined);
|
|
|
|
|
const [showCreateCst, setShowCreateCst] = useState(false);
|
|
|
|
|
|
|
|
|
|
const handleAddNew = useCallback(
|
|
|
|
|
(type: CstType) => {
|
|
|
|
|
if (!schema?.items) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const data: ICstCreateData = {
|
|
|
|
|
cst_type: type,
|
|
|
|
|
alias: createAliasFor(type, schema),
|
|
|
|
|
insert_after: insertPosition ?? null
|
|
|
|
|
}
|
|
|
|
|
cstCreate(data, newCst => {
|
|
|
|
|
toast.success(`Конституента добавлена: ${newCst.alias}`);
|
|
|
|
|
if (activeTab === RSTabsList.CST_EDIT) {
|
|
|
|
|
navigate(`/rsforms/${schema.id}?tab=${RSTabsList.CST_EDIT}&active=${newCst.id}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}, [schema, cstCreate, insertPosition, navigate, activeTab]);
|
|
|
|
|
|
|
|
|
|
const onShowCreateCst = useCallback(
|
|
|
|
|
(position: number | undefined, type: CstType | undefined, skipDialog?: boolean) => {
|
|
|
|
|
if (skipDialog && type) {
|
|
|
|
|
handleAddNew(type);
|
|
|
|
|
} else {
|
|
|
|
|
setDefaultType(type);
|
|
|
|
|
setInsertPosition(position);
|
|
|
|
|
setShowCreateCst(true);
|
|
|
|
|
}
|
|
|
|
|
}, [handleAddNew]);
|
2023-07-29 03:31:21 +03:00
|
|
|
|
|
|
|
|
|
const onShowAST = useCallback(
|
|
|
|
|
(ast: SyntaxTree) => {
|
|
|
|
|
setSyntaxTree(ast);
|
|
|
|
|
setShowAST(true);
|
2023-07-29 15:37:49 +03:00
|
|
|
|
}, []);
|
2023-07-28 18:23:37 +03:00
|
|
|
|
|
2023-07-15 17:46:19 +03:00
|
|
|
|
const onEditCst = (cst: IConstituenta) => {
|
2023-07-24 22:34:03 +03:00
|
|
|
|
setActiveID(cst.id);
|
2023-07-29 15:37:49 +03:00
|
|
|
|
setActiveTab(RSTabsList.CST_EDIT)
|
2023-07-15 17:46:19 +03:00
|
|
|
|
};
|
|
|
|
|
|
2023-07-16 20:25:55 +03:00
|
|
|
|
const onSelectTab = (index: number) => {
|
2023-07-29 15:37:49 +03:00
|
|
|
|
setActiveTab(index);
|
2023-07-16 20:25:55 +03:00
|
|
|
|
};
|
|
|
|
|
|
2023-07-24 22:34:03 +03:00
|
|
|
|
useLayoutEffect(() => {
|
2023-07-21 00:09:05 +03:00
|
|
|
|
if (schema) {
|
|
|
|
|
const url = new URL(window.location.href);
|
|
|
|
|
const activeQuery = url.searchParams.get('active');
|
2023-07-28 01:37:26 +03:00
|
|
|
|
const activeCst = schema.items.find((cst) => cst.id === Number(activeQuery));
|
2023-07-24 22:34:03 +03:00
|
|
|
|
setActiveID(activeCst?.id);
|
2023-07-21 00:09:05 +03:00
|
|
|
|
setInit(true);
|
2023-07-28 01:37:26 +03:00
|
|
|
|
|
|
|
|
|
const oldTitle = document.title
|
|
|
|
|
document.title = schema.title
|
|
|
|
|
return () => {
|
|
|
|
|
document.title = oldTitle
|
|
|
|
|
}
|
2023-07-21 00:09:05 +03:00
|
|
|
|
}
|
2023-07-28 01:37:26 +03:00
|
|
|
|
}, [setActiveID, schema, schema?.title, setInit]);
|
2023-07-18 14:55:40 +03:00
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const url = new URL(window.location.href);
|
|
|
|
|
const tabQuery = url.searchParams.get('tab');
|
2023-07-29 15:37:49 +03:00
|
|
|
|
setActiveTab(Number(tabQuery) || RSTabsList.CARD);
|
|
|
|
|
}, [setActiveTab]);
|
2023-07-16 20:25:55 +03:00
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2023-07-21 00:09:05 +03:00
|
|
|
|
if (init) {
|
2023-07-21 01:50:57 +03:00
|
|
|
|
const url = new URL(window.location.href);
|
2023-07-25 20:27:29 +03:00
|
|
|
|
const currentActive = url.searchParams.get('active');
|
2023-07-21 01:50:57 +03:00
|
|
|
|
const currentTab = url.searchParams.get('tab');
|
2023-07-29 15:37:49 +03:00
|
|
|
|
const saveHistory = activeTab === RSTabsList.CST_EDIT && currentActive !== String(activeID);
|
|
|
|
|
if (currentTab !== String(activeTab)) {
|
|
|
|
|
url.searchParams.set('tab', String(activeTab));
|
2023-07-21 01:50:57 +03:00
|
|
|
|
}
|
2023-07-24 22:34:03 +03:00
|
|
|
|
if (activeID) {
|
|
|
|
|
if (currentActive !== String(activeID)) {
|
|
|
|
|
url.searchParams.set('active', String(activeID));
|
2023-07-21 01:50:57 +03:00
|
|
|
|
}
|
2023-07-21 00:09:05 +03:00
|
|
|
|
} else {
|
|
|
|
|
url.searchParams.delete('active');
|
|
|
|
|
}
|
2023-07-21 01:50:57 +03:00
|
|
|
|
if (saveHistory) {
|
|
|
|
|
window.history.pushState(null, '', url.toString());
|
|
|
|
|
} else {
|
|
|
|
|
window.history.replaceState(null, '', url.toString());
|
|
|
|
|
}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
}
|
2023-07-29 15:37:49 +03:00
|
|
|
|
}, [activeTab, activeID, init]);
|
2023-07-16 20:25:55 +03:00
|
|
|
|
|
2023-07-15 17:46:19 +03:00
|
|
|
|
return (
|
2023-07-20 17:11:03 +03:00
|
|
|
|
<div className='w-full'>
|
2023-07-15 17:46:19 +03:00
|
|
|
|
{ loading && <Loader /> }
|
|
|
|
|
{ error && <BackendError error={error} />}
|
|
|
|
|
{ schema && !loading &&
|
2023-07-28 18:23:37 +03:00
|
|
|
|
<>
|
2023-07-29 03:31:21 +03:00
|
|
|
|
{showUpload && <DlgUploadRSForm hideWindow={() => { setShowUpload(false); }}/>}
|
|
|
|
|
{showClone && <DlgCloneRSForm hideWindow={() => { setShowClone(false); }}/>}
|
2023-07-29 15:37:49 +03:00
|
|
|
|
{showAST &&
|
|
|
|
|
<DlgShowAST
|
|
|
|
|
syntaxTree={syntaxTree}
|
|
|
|
|
hideWindow={() => { setShowAST(false); }}
|
|
|
|
|
/>}
|
|
|
|
|
{showCreateCst &&
|
|
|
|
|
<DlgCreateCst
|
|
|
|
|
hideWindow={() => { setShowCreateCst(false); }}
|
|
|
|
|
onCreate={handleAddNew}
|
|
|
|
|
defaultType={defaultType}
|
|
|
|
|
/>}
|
2023-07-25 20:27:29 +03:00
|
|
|
|
<Tabs
|
2023-07-29 15:37:49 +03:00
|
|
|
|
selectedIndex={activeTab}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
onSelect={onSelectTab}
|
|
|
|
|
defaultFocus={true}
|
|
|
|
|
selectedTabClassName='font-bold'
|
|
|
|
|
>
|
2023-07-21 18:44:14 +03:00
|
|
|
|
<TabList className='flex items-start w-fit clr-bg-pop'>
|
2023-07-28 18:23:37 +03:00
|
|
|
|
<RSTabsMenu
|
2023-07-29 03:31:21 +03:00
|
|
|
|
showCloneDialog={() => setShowClone(true)}
|
|
|
|
|
showUploadDialog={() => setShowUpload(true)}
|
2023-07-28 18:23:37 +03:00
|
|
|
|
/>
|
2023-07-20 17:11:03 +03:00
|
|
|
|
<ConceptTab>Паспорт схемы</ConceptTab>
|
2023-07-21 18:44:14 +03:00
|
|
|
|
<ConceptTab className='border-x-2 clr-border min-w-[10rem] flex justify-between gap-2'>
|
2023-07-20 17:11:03 +03:00
|
|
|
|
<span>Конституенты</span>
|
2023-07-25 20:27:29 +03:00
|
|
|
|
<span>{`${schema.stats?.count_errors ?? 0} | ${schema.stats?.count_all ?? 0}`}</span>
|
2023-07-20 17:11:03 +03:00
|
|
|
|
</ConceptTab>
|
|
|
|
|
<ConceptTab>Редактор</ConceptTab>
|
|
|
|
|
</TabList>
|
2023-07-15 17:46:19 +03:00
|
|
|
|
|
2023-07-20 17:11:03 +03:00
|
|
|
|
<TabPanel className='flex items-start w-full gap-2'>
|
2023-07-28 00:03:37 +03:00
|
|
|
|
<EditorRSForm />
|
2023-07-20 17:11:03 +03:00
|
|
|
|
{schema.stats && <RSFormStats stats={schema.stats}/>}
|
|
|
|
|
</TabPanel>
|
2023-07-15 17:46:19 +03:00
|
|
|
|
|
2023-07-20 17:11:03 +03:00
|
|
|
|
<TabPanel className='w-full'>
|
2023-07-29 15:37:49 +03:00
|
|
|
|
<EditorItems onOpenEdit={onEditCst} onShowCreateCst={onShowCreateCst} />
|
2023-07-20 17:11:03 +03:00
|
|
|
|
</TabPanel>
|
2023-07-15 17:46:19 +03:00
|
|
|
|
|
2023-07-20 17:11:03 +03:00
|
|
|
|
<TabPanel>
|
2023-07-29 15:37:49 +03:00
|
|
|
|
<EditorConstituenta onShowAST={onShowAST} onShowCreateCst={onShowCreateCst} />
|
2023-07-20 17:11:03 +03:00
|
|
|
|
</TabPanel>
|
2023-07-29 15:37:49 +03:00
|
|
|
|
</Tabs>
|
|
|
|
|
</>
|
2023-07-15 17:46:19 +03:00
|
|
|
|
}
|
|
|
|
|
</div>);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-27 22:04:25 +03:00
|
|
|
|
export default RSTabs;
|