mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 21:10:38 +03:00
Implement UI for ProduceStructure
This commit is contained in:
parent
7d7016cc67
commit
156c58568e
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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='Отождествление'
|
||||||
|
|
|
@ -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}`,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user