From 156c58568eb3fc9c76e892544e5f1a5d0c2a3ca1 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:34:41 +0300 Subject: [PATCH] Implement UI for ProduceStructure --- rsconcept/frontend/README.md | 5 ++- .../frontend/src/components/InfoError.tsx | 19 ++++++++- .../frontend/src/context/RSFormContext.tsx | 23 +++++++++++ rsconcept/frontend/src/models/rsform.ts | 15 ++++++- .../src/pages/RSFormPage/RSEditContext.tsx | 40 ++++++++++++++++--- .../src/pages/RSFormPage/RSTabsMenu.tsx | 14 ++++++- rsconcept/frontend/src/utils/backendAPI.ts | 10 +++++ 7 files changed, 116 insertions(+), 10 deletions(-) diff --git a/rsconcept/frontend/README.md b/rsconcept/frontend/README.md index 7f16cde3..5ef803e0 100644 --- a/rsconcept/frontend/README.md +++ b/rsconcept/frontend/README.md @@ -1,11 +1,12 @@ # Frontend Developer guidelines Styling conventions + - static > conditional static > props. All dynamic styling should go in styles props - dimensions = rectangle + outer layout
-clsx className groupind and order +clsx className grouping and order
   - layer: z-position
   - outer layout: fixed bottom-1/2 left-0 -translate-x-1/2
@@ -18,4 +19,4 @@ Styling conventions
   - behavior modifiers: select-none disabled:cursor-not-allowed
   - transitions: 
   
-
\ No newline at end of file + diff --git a/rsconcept/frontend/src/components/InfoError.tsx b/rsconcept/frontend/src/components/InfoError.tsx index e152f3fd..2bd1b844 100644 --- a/rsconcept/frontend/src/components/InfoError.tsx +++ b/rsconcept/frontend/src/components/InfoError.tsx @@ -1,9 +1,12 @@ import axios, { type AxiosError } from 'axios'; +import clsx from 'clsx'; +import { urls } from '@/utils/constants'; import { isResponseHtml } from '@/utils/utils'; import AnimateFade from './AnimateFade'; import PrettyJson from './ui/PrettyJSON'; +import TextURL from './ui/TextURL'; export type ErrorData = string | Error | AxiosError | undefined; @@ -50,7 +53,21 @@ function DescribeError({ error }: { error: ErrorData }) { function InfoError({ error }: InfoErrorProps) { return ( - + +

+ Пожалуйста сделайте скриншот и отправьте вместе с описанием ситуации на почту{' '} + +
+ Для продолжения работы перезагрузите страницу +

); diff --git a/rsconcept/frontend/src/context/RSFormContext.tsx b/rsconcept/frontend/src/context/RSFormContext.tsx index f990f460..062cd572 100644 --- a/rsconcept/frontend/src/context/RSFormContext.tsx +++ b/rsconcept/frontend/src/context/RSFormContext.tsx @@ -7,9 +7,11 @@ import useRSFormDetails from '@/hooks/useRSFormDetails'; import { ILibraryItem, IVersionData } from '@/models/library'; import { ILibraryUpdateData } from '@/models/library'; import { + EntityID, IConstituentaList, IConstituentaMeta, ICstCreateData, + ICstID, ICstMovetoData, ICstRenameData, ICstSubstituteData, @@ -26,6 +28,7 @@ import { patchDeleteConstituenta, patchLibraryItem, patchMoveConstituenta, + patchProduceStructure, patchRenameConstituenta, patchResetAliases, patchSubstituteConstituenta, @@ -61,6 +64,7 @@ interface IRSFormContext { upload: (data: IRSFormUploadData, callback: () => void) => void; resetAliases: (callback: () => void) => void; + produceStructure: (data: ICstID, callback?: DataCallback) => void; cstCreate: (data: ICstCreateData, callback?: DataCallback) => void; cstRename: (data: ICstRenameData, callback?: DataCallback) => void; @@ -260,6 +264,24 @@ export const RSFormState = ({ schemaID, versionID, children }: RSFormStateProps) [schemaID, setError, schema, library, user, setSchema] ); + const produceStructure = useCallback( + (data: ICstID, callback?: DataCallback) => { + 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( (callback: DataCallback) => { setError(undefined); @@ -459,6 +481,7 @@ export const RSFormState = ({ schemaID, versionID, children }: RSFormStateProps) upload, claim, resetAliases, + produceStructure, subscribe, unsubscribe, cstUpdate, diff --git a/rsconcept/frontend/src/models/rsform.ts b/rsconcept/frontend/src/models/rsform.ts index d5c381f6..8741ac05 100644 --- a/rsconcept/frontend/src/models/rsform.ts +++ b/rsconcept/frontend/src/models/rsform.ts @@ -77,6 +77,11 @@ export interface IConstituentaMeta { term_forms: TermForm[]; } +/** + * Represents id for {@link IConstituenta}. + */ +export interface ICstID extends Pick {} + /** * Represents Constituenta. */ @@ -97,7 +102,7 @@ export interface IConstituenta extends IConstituentaMeta { * Represents Constituenta list. */ export interface IConstituentaList { - items: number[]; + items: EntityID[]; } /** @@ -152,6 +157,14 @@ export interface ICstCreatedResponse { schema: IRSFormData; } +/** + * Represents data response when creating producing structure of {@link IConstituenta}. + */ +export interface IProduceStructureResponse { + cst_list: EntityID[]; + schema: IRSFormData; +} + /** * Represents {@link IRSForm} statistics. */ diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx index 1bae7ddd..490d5c10 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx @@ -28,9 +28,11 @@ import { IVersionData } from '@/models/library'; import { UserAccessMode } from '@/models/miscellaneous'; import { CstType, + EntityID, IConstituenta, IConstituentaMeta, ICstCreateData, + ICstID, ICstMovetoData, ICstRenameData, ICstSubstituteData, @@ -46,6 +48,7 @@ interface IRSEditContext { isMutable: boolean; isContentEditable: boolean; isProcessing: boolean; + canProduceStructure: boolean; viewVersion: (version?: number) => void; @@ -65,6 +68,7 @@ interface IRSEditContext { toggleSubscribe: () => void; download: () => void; reindex: () => void; + produceStructure: () => void; substitute: () => void; createVersion: () => void; @@ -81,13 +85,13 @@ export const useRSEdit = () => { }; interface RSEditStateProps { - selected: number[]; + selected: EntityID[]; isModified: boolean; - setSelected: React.Dispatch>; + setSelected: React.Dispatch>; activeCst?: IConstituenta; onCreateCst?: (newCst: IConstituentaMeta) => void; - onDeleteCst?: (newActive?: number) => void; + onDeleteCst?: (newActive?: EntityID) => void; children: React.ReactNode; } @@ -130,7 +134,7 @@ export const RSEditState = ({ const [renameInitialData, setRenameInitialData] = useState(); const [showRenameCst, setShowRenameCst] = useState(false); - const [insertCstID, setInsertCstID] = useState(undefined); + const [insertCstID, setInsertCstID] = useState(undefined); const [showTemplates, setShowTemplates] = useState(false); useLayoutEffect( @@ -188,7 +192,7 @@ export const RSEditState = ({ ); const handleDeleteCst = useCallback( - (deleted: number[]) => { + (deleted: EntityID[]) => { if (!model.schema) { return; } @@ -370,6 +374,30 @@ export const RSEditState = ({ 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(() => { setInsertCstID(activeCst?.id); setShowTemplates(true); @@ -431,6 +459,7 @@ export const RSEditState = ({ isMutable, isContentEditable, isProcessing: model.processing, + canProduceStructure, viewVersion, @@ -450,6 +479,7 @@ export const RSEditState = ({ share, toggleSubscribe, reindex, + produceStructure, substitute, createVersion: () => setShowCreateVersion(true), diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx index 59ab7455..a6a06929 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSTabsMenu.tsx @@ -13,7 +13,7 @@ import { BiUpload } from 'react-icons/bi'; 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 Button from '@/components/ui/Button'; @@ -90,6 +90,11 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) { controller.promptTemplate(); } + function handleProduceStructure() { + editMenu.hide(); + controller.produceStructure(); + } + function handleChangeMode(newMode: UserAccessMode) { accessMenu.hide(); setMode(newMode); @@ -203,6 +208,13 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) { icon={} onClick={handleTemplates} /> + } + onClick={handleProduceStructure} + /> ) { + 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) { AxiosPatch({ title: `Substitution for constituenta id=${request.data.original} for schema id=${schema}`,