2024-06-07 20:17:03 +03:00
|
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import axios from 'axios';
|
|
|
|
|
import clsx from 'clsx';
|
2024-12-13 21:30:49 +03:00
|
|
|
|
import { useEffect, useState } from 'react';
|
2024-06-07 20:17:03 +03:00
|
|
|
|
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 Loader from '@/components/ui/Loader';
|
2024-09-11 21:15:28 +03:00
|
|
|
|
import Overlay from '@/components/ui/Overlay';
|
2024-06-07 20:17:03 +03:00
|
|
|
|
import TabLabel from '@/components/ui/TabLabel';
|
|
|
|
|
import TextURL from '@/components/ui/TextURL';
|
2024-08-24 19:40:54 +03:00
|
|
|
|
import { useAuth } from '@/context/AuthContext';
|
2024-06-07 20:17:03 +03:00
|
|
|
|
import { useLibrary } from '@/context/LibraryContext';
|
|
|
|
|
import { useBlockNavigation, useConceptNavigation } from '@/context/NavigationContext';
|
|
|
|
|
import { useOSS } from '@/context/OssContext';
|
|
|
|
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
2024-07-23 23:03:58 +03:00
|
|
|
|
import { OperationID } from '@/models/oss';
|
2025-01-14 21:57:32 +03:00
|
|
|
|
import { useAppLayoutStore } from '@/stores/appLayout';
|
2024-06-07 20:17:03 +03:00
|
|
|
|
import { information, prompts } from '@/utils/labels';
|
|
|
|
|
|
|
|
|
|
import EditorRSForm from './EditorOssCard';
|
|
|
|
|
import EditorTermGraph from './EditorOssGraph';
|
2024-06-26 19:47:05 +03:00
|
|
|
|
import MenuOssTabs from './MenuOssTabs';
|
2024-06-07 20:17:03 +03:00
|
|
|
|
import { OssEditState } from './OssEditContext';
|
|
|
|
|
|
|
|
|
|
export enum OssTabID {
|
|
|
|
|
CARD = 0,
|
|
|
|
|
GRAPH = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function OssTabs() {
|
|
|
|
|
const router = useConceptNavigation();
|
|
|
|
|
const query = useQueryStrings();
|
2024-07-28 11:38:14 +03:00
|
|
|
|
const activeTab = query.get('tab') ? (Number(query.get('tab')) as OssTabID) : OssTabID.GRAPH;
|
2024-08-24 19:40:54 +03:00
|
|
|
|
const { user } = useAuth();
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
2025-01-14 21:57:32 +03:00
|
|
|
|
const hideFooter = useAppLayoutStore(state => state.hideFooter);
|
2024-08-16 12:44:09 +03:00
|
|
|
|
const { schema, loading, loadingError: errorLoading } = useOSS();
|
2024-06-07 20:17:03 +03:00
|
|
|
|
const { destroyItem } = useLibrary();
|
|
|
|
|
|
|
|
|
|
const [isModified, setIsModified] = useState(false);
|
2024-07-23 23:03:58 +03:00
|
|
|
|
const [selected, setSelected] = useState<OperationID[]>([]);
|
2024-08-24 19:40:54 +03:00
|
|
|
|
useBlockNavigation(
|
|
|
|
|
isModified &&
|
|
|
|
|
schema !== undefined &&
|
|
|
|
|
user !== undefined &&
|
|
|
|
|
(user.is_staff || user.id == schema.owner || schema.editors.includes(user.id))
|
|
|
|
|
);
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
2024-12-12 20:57:45 +03:00
|
|
|
|
useEffect(() => {
|
2024-06-07 20:17:03 +03:00
|
|
|
|
if (schema) {
|
|
|
|
|
const oldTitle = document.title;
|
|
|
|
|
document.title = schema.title;
|
|
|
|
|
return () => {
|
|
|
|
|
document.title = oldTitle;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}, [schema, schema?.title]);
|
|
|
|
|
|
2024-12-12 20:57:45 +03:00
|
|
|
|
useEffect(() => {
|
2025-01-14 21:57:32 +03:00
|
|
|
|
hideFooter(activeTab === OssTabID.GRAPH);
|
|
|
|
|
}, [activeTab, hideFooter]);
|
2024-08-14 21:50:10 +03:00
|
|
|
|
|
2024-12-13 21:30:49 +03:00
|
|
|
|
function navigateTab(tab: OssTabID) {
|
|
|
|
|
if (!schema) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const url = urls.oss_props({
|
|
|
|
|
id: schema.id,
|
|
|
|
|
tab: tab
|
|
|
|
|
});
|
|
|
|
|
router.push(url);
|
|
|
|
|
}
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-13 21:30:49 +03:00
|
|
|
|
function onDestroySchema() {
|
2024-08-23 19:09:18 +03:00
|
|
|
|
if (!schema || !window.confirm(prompts.deleteOSS)) {
|
2024-06-07 20:17:03 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
destroyItem(schema.id, () => {
|
|
|
|
|
toast.success(information.itemDestroyed);
|
|
|
|
|
router.push(urls.library);
|
|
|
|
|
});
|
2024-12-13 21:30:49 +03:00
|
|
|
|
}
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
|
|
|
|
return (
|
2024-07-23 23:03:58 +03:00
|
|
|
|
<OssEditState selected={selected} setSelected={setSelected}>
|
2024-06-07 20:17:03 +03:00
|
|
|
|
{loading ? <Loader /> : null}
|
|
|
|
|
{errorLoading ? <ProcessError error={errorLoading} /> : null}
|
|
|
|
|
{schema && !loading ? (
|
|
|
|
|
<Tabs
|
|
|
|
|
selectedIndex={activeTab}
|
|
|
|
|
onSelect={onSelectTab}
|
|
|
|
|
defaultFocus
|
|
|
|
|
selectedTabClassName='clr-selected'
|
|
|
|
|
className='flex flex-col mx-auto min-w-fit'
|
|
|
|
|
>
|
2024-09-11 21:15:28 +03:00
|
|
|
|
<Overlay position='top-0 right-1/2 translate-x-1/2' layer='z-sticky'>
|
2024-12-18 14:54:45 +03:00
|
|
|
|
<TabList className={clsx('w-fit', 'flex items-stretch', 'border-b-2 border-x-2 divide-x-2', 'bg-prim-200')}>
|
2024-09-11 21:15:28 +03:00
|
|
|
|
<MenuOssTabs onDestroy={onDestroySchema} />
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
2024-09-11 21:15:28 +03:00
|
|
|
|
<TabLabel label='Карточка' title={schema.title ?? ''} />
|
|
|
|
|
<TabLabel label='Граф' />
|
|
|
|
|
</TabList>
|
|
|
|
|
</Overlay>
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
2024-12-12 13:17:24 +03:00
|
|
|
|
<div className='overflow-x-hidden'>
|
2024-12-13 21:30:49 +03:00
|
|
|
|
<TabPanel>
|
|
|
|
|
<EditorRSForm
|
|
|
|
|
isModified={isModified} // prettier: split lines
|
|
|
|
|
setIsModified={setIsModified}
|
|
|
|
|
onDestroy={onDestroySchema}
|
|
|
|
|
/>
|
|
|
|
|
</TabPanel>
|
|
|
|
|
|
|
|
|
|
<TabPanel>
|
|
|
|
|
<EditorTermGraph isModified={isModified} setIsModified={setIsModified} />
|
|
|
|
|
</TabPanel>
|
2024-12-12 13:17:24 +03:00
|
|
|
|
</div>
|
2024-06-07 20:17:03 +03:00
|
|
|
|
</Tabs>
|
|
|
|
|
) : null}
|
|
|
|
|
</OssEditState>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default OssTabs;
|
|
|
|
|
|
|
|
|
|
// ====== Internals =========
|
|
|
|
|
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
|
|
|
|
if (axios.isAxiosError(error) && error.response) {
|
|
|
|
|
if (error.response.status === 404) {
|
|
|
|
|
return (
|
|
|
|
|
<div className='flex flex-col items-center p-2 mx-auto'>
|
|
|
|
|
<p>{`Операционная схема с указанным идентификатором отсутствует`}</p>
|
|
|
|
|
<div className='flex justify-center'>
|
|
|
|
|
<TextURL text='Библиотека' href='/library' />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
} else if (error.response.status === 403) {
|
|
|
|
|
return (
|
|
|
|
|
<div className='flex flex-col items-center p-2 mx-auto'>
|
|
|
|
|
<p>Владелец ограничил доступ к данной схеме</p>
|
|
|
|
|
<TextURL text='Библиотека' href='/library' />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return <InfoError error={error} />;
|
|
|
|
|
}
|