'use client'; import axios from 'axios'; import clsx from 'clsx'; import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { TabList, TabPanel, Tabs } from 'react-tabs'; import { toast } from 'react-toastify'; import { urls } from '@/app/urls'; import InfoError, { ErrorData } from '@/components/info/InfoError'; import Divider from '@/components/ui/Divider'; import Loader from '@/components/ui/Loader'; import TabLabel from '@/components/ui/TabLabel'; import TextURL from '@/components/ui/TextURL'; import AnimateFade from '@/components/wrap/AnimateFade'; import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useLibrary } from '@/context/LibraryContext'; import { useBlockNavigation, useConceptNavigation } from '@/context/NavigationContext'; import { useRSForm } from '@/context/RSFormContext'; import useQueryStrings from '@/hooks/useQueryStrings'; import { ConstituentaID, IConstituenta, IConstituentaMeta } from '@/models/rsform'; import { PARAMETER, prefixes } from '@/utils/constants'; import { information, labelVersion, prompts } from '@/utils/labels'; import { OssTabID } from '../OssPage/OssTabs'; import EditorConstituenta from './EditorConstituenta'; import EditorRSForm from './EditorRSFormCard'; import EditorRSList from './EditorRSList'; import EditorTermGraph from './EditorTermGraph'; import MenuRSTabs from './MenuRSTabs'; import { RSEditState } from './RSEditContext'; export enum RSTabID { CARD = 0, CST_LIST = 1, CST_EDIT = 2, TERM_GRAPH = 3 } function RSTabs() { const router = useConceptNavigation(); const query = useQueryStrings(); const activeTab = query.get('tab') ? (Number(query.get('tab')) as RSTabID) : RSTabID.CARD; const version = query.get('v') ? Number(query.get('v')) : undefined; const cstQuery = query.get('active'); const { setNoFooter, calculateHeight } = useConceptOptions(); const { schema, loading, errorLoading, isArchive, itemID } = useRSForm(); const library = useLibrary(); const [isModified, setIsModified] = useState(false); useBlockNavigation(isModified); const [selected, setSelected] = useState([]); const activeCst: IConstituenta | undefined = useMemo(() => { if (!schema || selected.length === 0) { return undefined; } else { return schema.cstByID.get(selected.at(-1)!); } }, [schema, selected]); useLayoutEffect(() => { if (schema) { const oldTitle = document.title; document.title = schema.title; return () => { document.title = oldTitle; }; } }, [schema, schema?.title]); useLayoutEffect(() => { setNoFooter(activeTab === RSTabID.CST_EDIT || activeTab === RSTabID.CST_LIST); setIsModified(false); if (activeTab === RSTabID.CST_EDIT) { const cstID = Number(cstQuery); if (cstID && schema && schema.cstByID.has(cstID)) { setSelected([cstID]); } else if (schema && schema?.items.length > 0) { setSelected([schema.items[0].id]); } else { setSelected([]); } } return () => setNoFooter(false); }, [activeTab, cstQuery, setSelected, schema, setNoFooter, setIsModified]); const navigateTab = useCallback( (tab: RSTabID, activeID?: ConstituentaID) => { if (!schema) { return; } const url = urls.schema_props({ id: schema.id, tab: tab, active: activeID, version: version }); if (activeID) { if (tab === activeTab && tab !== RSTabID.CST_EDIT) { router.replace(url); } else { router.push(url); } } else if (tab !== activeTab && tab === RSTabID.CST_EDIT && schema.items.length > 0) { activeID = schema.items[0].id; router.replace(url); } else { router.push(url); } }, [router, schema, activeTab, version] ); function onSelectTab(index: number, last: number, event: Event) { if (last === index) { return; } if (event.type == 'keydown') { const kbEvent = event as KeyboardEvent; if (kbEvent.altKey) { if (kbEvent.code === 'ArrowLeft') { router.back(); return; } else if (kbEvent.code === 'ArrowRight') { router.forward(); return; } } } navigateTab(index, selected.length > 0 ? selected.at(-1) : undefined); } const onCreateCst = useCallback( (newCst: IConstituentaMeta) => { navigateTab(activeTab, newCst.id); if (activeTab === RSTabID.CST_LIST) { setTimeout(() => { const element = document.getElementById(`${prefixes.cst_list}${newCst.alias}`); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'end' }); } }, PARAMETER.refreshTimeout); } }, [activeTab, navigateTab] ); const onDeleteCst = useCallback( (newActive?: ConstituentaID) => { if (!newActive) { navigateTab(RSTabID.CST_LIST); } else if (activeTab === RSTabID.CST_EDIT) { navigateTab(activeTab, newActive); } else { navigateTab(activeTab); } }, [activeTab, navigateTab] ); const onOpenCst = useCallback( (cstID: ConstituentaID) => { if (cstID !== activeCst?.id || activeTab !== RSTabID.CST_EDIT) { navigateTab(RSTabID.CST_EDIT, cstID); } }, [navigateTab, activeCst, activeTab] ); const onDestroySchema = useCallback(() => { if (!schema || !window.confirm(prompts.deleteLibraryItem)) { return; } const backToOSS = library.globalOSS?.schemas.includes(schema.id); library.destroyItem(schema.id, () => { toast.success(information.itemDestroyed); if (backToOSS) { router.push(urls.oss(library.globalOSS!.id, OssTabID.GRAPH)); } else { router.push(urls.library); } }); }, [schema, library, router]); const panelHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]); const cardPanel = useMemo( () => ( ), [isModified, onDestroySchema] ); const listPanel = useMemo( () => ( ), [onOpenCst] ); const editorPanel = useMemo( () => ( ), [isModified, setIsModified, activeCst, onOpenCst] ); const graphPanel = useMemo( () => ( ), [onOpenCst] ); return ( {loading ? : null} {errorLoading ? : null} {schema && !loading ? ( Версия: ${labelVersion(schema)}`} /> Ошибок: ${schema.stats?.count_errors ?? 0}`} /> {cardPanel} {listPanel} {editorPanel} {graphPanel} ) : null} ); } export default RSTabs; // ====== Internals ========= function ProcessError({ error, isArchive, itemID }: { error: ErrorData; isArchive: boolean; itemID: string; }): React.ReactElement { if (axios.isAxiosError(error) && error.response) { if (error.response.status === 404) { return (

{`Концептуальная схема с указанным идентификатором ${isArchive ? 'и версией ' : ''}отсутствует`}

{isArchive ? : null} {isArchive ? : null}
); } else if (error.response.status === 403) { return (

Владелец ограничил доступ к данной схеме

); } } return ; }