Implement UI for ProduceStructure

This commit is contained in:
IRBorisov 2024-03-15 12:34:41 +03:00
parent 7d7016cc67
commit 156c58568e
7 changed files with 116 additions and 10 deletions

View File

@ -1,11 +1,12 @@
# Frontend Developer guidelines # Frontend Developer guidelines
Styling conventions Styling conventions
- static > conditional static > props. All dynamic styling should go in styles props - static > conditional static > props. All dynamic styling should go in styles props
- dimensions = rectangle + outer layout - dimensions = rectangle + outer layout
<details> <details>
<summary>clsx className groupind and order</summary> <summary>clsx className grouping and order</summary>
<pre> <pre>
- layer: z-position - layer: z-position
- outer layout: fixed bottom-1/2 left-0 -translate-x-1/2 - outer layout: fixed bottom-1/2 left-0 -translate-x-1/2

View File

@ -1,9 +1,12 @@
import axios, { type AxiosError } from 'axios'; import axios, { type AxiosError } from 'axios';
import clsx from 'clsx';
import { urls } from '@/utils/constants';
import { isResponseHtml } from '@/utils/utils'; import { isResponseHtml } from '@/utils/utils';
import AnimateFade from './AnimateFade'; import AnimateFade from './AnimateFade';
import PrettyJson from './ui/PrettyJSON'; import PrettyJson from './ui/PrettyJSON';
import TextURL from './ui/TextURL';
export type ErrorData = string | Error | AxiosError | undefined; export type ErrorData = string | Error | AxiosError | undefined;
@ -50,7 +53,21 @@ function DescribeError({ error }: { error: ErrorData }) {
function InfoError({ error }: InfoErrorProps) { function InfoError({ error }: InfoErrorProps) {
return ( return (
<AnimateFade className='px-3 py-2 min-w-[25rem] w-full text-sm font-semibold select-text clr-text-red'> <AnimateFade
className={clsx(
'min-w-[25rem] w-full',
'px-3 py-2 flex flex-col',
'clr-text-red',
'text-sm font-semibold',
'select-text'
)}
>
<p className='clr-text-default font-normal'>
Пожалуйста сделайте скриншот и отправьте вместе с описанием ситуации на почту{' '}
<TextURL href={urls.mail_portal} text='portal@acconcept.ru' />
<br />
Для продолжения работы перезагрузите страницу
</p>
<DescribeError error={error} /> <DescribeError error={error} />
</AnimateFade> </AnimateFade>
); );

View File

@ -7,9 +7,11 @@ import useRSFormDetails from '@/hooks/useRSFormDetails';
import { ILibraryItem, IVersionData } from '@/models/library'; import { ILibraryItem, IVersionData } from '@/models/library';
import { ILibraryUpdateData } from '@/models/library'; import { ILibraryUpdateData } from '@/models/library';
import { import {
EntityID,
IConstituentaList, IConstituentaList,
IConstituentaMeta, IConstituentaMeta,
ICstCreateData, ICstCreateData,
ICstID,
ICstMovetoData, ICstMovetoData,
ICstRenameData, ICstRenameData,
ICstSubstituteData, ICstSubstituteData,
@ -26,6 +28,7 @@ import {
patchDeleteConstituenta, patchDeleteConstituenta,
patchLibraryItem, patchLibraryItem,
patchMoveConstituenta, patchMoveConstituenta,
patchProduceStructure,
patchRenameConstituenta, patchRenameConstituenta,
patchResetAliases, patchResetAliases,
patchSubstituteConstituenta, patchSubstituteConstituenta,
@ -61,6 +64,7 @@ interface IRSFormContext {
upload: (data: IRSFormUploadData, callback: () => void) => void; upload: (data: IRSFormUploadData, callback: () => void) => void;
resetAliases: (callback: () => void) => void; resetAliases: (callback: () => void) => void;
produceStructure: (data: ICstID, callback?: DataCallback<EntityID[]>) => void;
cstCreate: (data: ICstCreateData, callback?: DataCallback<IConstituentaMeta>) => void; cstCreate: (data: ICstCreateData, callback?: DataCallback<IConstituentaMeta>) => void;
cstRename: (data: ICstRenameData, callback?: DataCallback<IConstituentaMeta>) => void; cstRename: (data: ICstRenameData, callback?: DataCallback<IConstituentaMeta>) => void;
@ -260,6 +264,24 @@ export const RSFormState = ({ schemaID, versionID, children }: RSFormStateProps)
[schemaID, setError, schema, library, user, setSchema] [schemaID, setError, schema, library, user, setSchema]
); );
const produceStructure = useCallback(
(data: ICstID, callback?: DataCallback<EntityID[]>) => {
setError(undefined);
patchProduceStructure(schemaID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setError,
onSuccess: newData => {
setSchema(newData.schema);
library.localUpdateTimestamp(newData.schema.id);
if (callback) callback(newData.cst_list);
}
});
},
[setError, setSchema, library, schemaID]
);
const download = useCallback( const download = useCallback(
(callback: DataCallback<Blob>) => { (callback: DataCallback<Blob>) => {
setError(undefined); setError(undefined);
@ -459,6 +481,7 @@ export const RSFormState = ({ schemaID, versionID, children }: RSFormStateProps)
upload, upload,
claim, claim,
resetAliases, resetAliases,
produceStructure,
subscribe, subscribe,
unsubscribe, unsubscribe,
cstUpdate, cstUpdate,

View File

@ -77,6 +77,11 @@ export interface IConstituentaMeta {
term_forms: TermForm[]; term_forms: TermForm[];
} }
/**
* Represents id for {@link IConstituenta}.
*/
export interface ICstID extends Pick<IConstituentaMeta, 'id'> {}
/** /**
* Represents Constituenta. * Represents Constituenta.
*/ */
@ -97,7 +102,7 @@ export interface IConstituenta extends IConstituentaMeta {
* Represents Constituenta list. * Represents Constituenta list.
*/ */
export interface IConstituentaList { export interface IConstituentaList {
items: number[]; items: EntityID[];
} }
/** /**
@ -152,6 +157,14 @@ export interface ICstCreatedResponse {
schema: IRSFormData; schema: IRSFormData;
} }
/**
* Represents data response when creating producing structure of {@link IConstituenta}.
*/
export interface IProduceStructureResponse {
cst_list: EntityID[];
schema: IRSFormData;
}
/** /**
* Represents {@link IRSForm} statistics. * Represents {@link IRSForm} statistics.
*/ */

View File

@ -28,9 +28,11 @@ import { IVersionData } from '@/models/library';
import { UserAccessMode } from '@/models/miscellaneous'; import { UserAccessMode } from '@/models/miscellaneous';
import { import {
CstType, CstType,
EntityID,
IConstituenta, IConstituenta,
IConstituentaMeta, IConstituentaMeta,
ICstCreateData, ICstCreateData,
ICstID,
ICstMovetoData, ICstMovetoData,
ICstRenameData, ICstRenameData,
ICstSubstituteData, ICstSubstituteData,
@ -46,6 +48,7 @@ interface IRSEditContext {
isMutable: boolean; isMutable: boolean;
isContentEditable: boolean; isContentEditable: boolean;
isProcessing: boolean; isProcessing: boolean;
canProduceStructure: boolean;
viewVersion: (version?: number) => void; viewVersion: (version?: number) => void;
@ -65,6 +68,7 @@ interface IRSEditContext {
toggleSubscribe: () => void; toggleSubscribe: () => void;
download: () => void; download: () => void;
reindex: () => void; reindex: () => void;
produceStructure: () => void;
substitute: () => void; substitute: () => void;
createVersion: () => void; createVersion: () => void;
@ -81,13 +85,13 @@ export const useRSEdit = () => {
}; };
interface RSEditStateProps { interface RSEditStateProps {
selected: number[]; selected: EntityID[];
isModified: boolean; isModified: boolean;
setSelected: React.Dispatch<React.SetStateAction<number[]>>; setSelected: React.Dispatch<React.SetStateAction<EntityID[]>>;
activeCst?: IConstituenta; activeCst?: IConstituenta;
onCreateCst?: (newCst: IConstituentaMeta) => void; onCreateCst?: (newCst: IConstituentaMeta) => void;
onDeleteCst?: (newActive?: number) => void; onDeleteCst?: (newActive?: EntityID) => void;
children: React.ReactNode; children: React.ReactNode;
} }
@ -130,7 +134,7 @@ export const RSEditState = ({
const [renameInitialData, setRenameInitialData] = useState<ICstRenameData>(); const [renameInitialData, setRenameInitialData] = useState<ICstRenameData>();
const [showRenameCst, setShowRenameCst] = useState(false); const [showRenameCst, setShowRenameCst] = useState(false);
const [insertCstID, setInsertCstID] = useState<number | undefined>(undefined); const [insertCstID, setInsertCstID] = useState<EntityID | undefined>(undefined);
const [showTemplates, setShowTemplates] = useState(false); const [showTemplates, setShowTemplates] = useState(false);
useLayoutEffect( useLayoutEffect(
@ -188,7 +192,7 @@ export const RSEditState = ({
); );
const handleDeleteCst = useCallback( const handleDeleteCst = useCallback(
(deleted: number[]) => { (deleted: EntityID[]) => {
if (!model.schema) { if (!model.schema) {
return; return;
} }
@ -370,6 +374,30 @@ export const RSEditState = ({
const reindex = useCallback(() => model.resetAliases(() => toast.success('Имена конституент обновлены')), [model]); const reindex = useCallback(() => model.resetAliases(() => toast.success('Имена конституент обновлены')), [model]);
const canProduceStructure = useMemo(() => {
return (
!!activeCst &&
!!activeCst.parse.typification &&
activeCst.cst_type !== CstType.BASE &&
activeCst.cst_type !== CstType.CONSTANT
);
}, [activeCst]);
const produceStructure = useCallback(() => {
if (!activeCst) {
return;
}
const data: ICstID = {
id: activeCst.id
};
model.produceStructure(data, cstList => {
toast.success(`Добавлены конституенты: ${cstList.length}`);
if (cstList.length !== 0) {
setSelected(cstList);
}
});
}, [activeCst, setSelected, model]);
const promptTemplate = useCallback(() => { const promptTemplate = useCallback(() => {
setInsertCstID(activeCst?.id); setInsertCstID(activeCst?.id);
setShowTemplates(true); setShowTemplates(true);
@ -431,6 +459,7 @@ export const RSEditState = ({
isMutable, isMutable,
isContentEditable, isContentEditable,
isProcessing: model.processing, isProcessing: model.processing,
canProduceStructure,
viewVersion, viewVersion,
@ -450,6 +479,7 @@ export const RSEditState = ({
share, share,
toggleSubscribe, toggleSubscribe,
reindex, reindex,
produceStructure,
substitute, substitute,
createVersion: () => setShowCreateVersion(true), createVersion: () => setShowCreateVersion(true),

View File

@ -13,7 +13,7 @@ import {
BiUpload BiUpload
} from 'react-icons/bi'; } from 'react-icons/bi';
import { FiEdit } from 'react-icons/fi'; import { FiEdit } from 'react-icons/fi';
import { LuAlertTriangle, LuArchive, LuCrown, LuGlasses, LuReplace } from 'react-icons/lu'; import { LuAlertTriangle, LuArchive, LuCrown, LuGlasses, LuNetwork, LuReplace } from 'react-icons/lu';
import { VscLibrary } from 'react-icons/vsc'; import { VscLibrary } from 'react-icons/vsc';
import Button from '@/components/ui/Button'; import Button from '@/components/ui/Button';
@ -90,6 +90,11 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
controller.promptTemplate(); controller.promptTemplate();
} }
function handleProduceStructure() {
editMenu.hide();
controller.produceStructure();
}
function handleChangeMode(newMode: UserAccessMode) { function handleChangeMode(newMode: UserAccessMode) {
accessMenu.hide(); accessMenu.hide();
setMode(newMode); setMode(newMode);
@ -203,6 +208,13 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
icon={<BiDiamond size='1rem' className='icon-green' />} icon={<BiDiamond size='1rem' className='icon-green' />}
onClick={handleTemplates} onClick={handleTemplates}
/> />
<DropdownButton
disabled={!controller.isContentEditable || !controller.canProduceStructure}
text='Порождение структуры'
title='Раскрыть структуру типизации выделенной конституенты'
icon={<LuNetwork size='1rem' className='icon-primary' />}
onClick={handleProduceStructure}
/>
<DropdownButton <DropdownButton
disabled={!controller.isContentEditable} disabled={!controller.isContentEditable}
text='Отождествление' text='Отождествление'

View File

@ -27,10 +27,12 @@ import {
IConstituentaMeta, IConstituentaMeta,
ICstCreateData, ICstCreateData,
ICstCreatedResponse, ICstCreatedResponse,
ICstID,
ICstMovetoData, ICstMovetoData,
ICstRenameData, ICstRenameData,
ICstSubstituteData, ICstSubstituteData,
ICstUpdateData, ICstUpdateData,
IProduceStructureResponse,
IRSFormCreateData, IRSFormCreateData,
IRSFormData, IRSFormData,
IRSFormUploadData, IRSFormUploadData,
@ -323,6 +325,14 @@ export function patchRenameConstituenta(schema: string, request: FrontExchange<I
}); });
} }
export function patchProduceStructure(schema: string, request: FrontExchange<ICstID, IProduceStructureResponse>) {
AxiosPatch({
title: `Producing structure constituenta id=${request.data.id} for schema id=${schema}`,
endpoint: `/api/rsforms/${schema}/cst-produce-structure`,
request: request
});
}
export function patchSubstituteConstituenta(schema: string, request: FrontExchange<ICstSubstituteData, IRSFormData>) { export function patchSubstituteConstituenta(schema: string, request: FrontExchange<ICstSubstituteData, IRSFormData>) {
AxiosPatch({ AxiosPatch({
title: `Substitution for constituenta id=${request.data.original} for schema id=${schema}`, title: `Substitution for constituenta id=${request.data.original} for schema id=${schema}`,