From c5b73612f3178e21c9e3ec1f99fa144bca20eafd Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Mon, 10 Feb 2025 15:49:56 +0300 Subject: [PATCH] F: Rework RelocateConstituents dialog --- .../frontend/src/features/oss/backend/api.ts | 19 ++-- .../oss/backend/useRelocateConstituents.tsx | 3 +- .../DlgCreateOperation/TabInputOperation.tsx | 2 +- .../oss/dialogs/DlgRelocateConstituents.tsx | 99 +++++++++++++------ .../oss/pages/OssPage/OssEditContext.tsx | 24 +---- .../components/PickMultiConstituenta.tsx | 4 +- .../dialogs/DlgInlineSynthesis/TabSource.tsx | 2 +- 7 files changed, 87 insertions(+), 66 deletions(-) diff --git a/rsconcept/frontend/src/features/oss/backend/api.ts b/rsconcept/frontend/src/features/oss/backend/api.ts index 3f32b3e3..ddde17ac 100644 --- a/rsconcept/frontend/src/features/oss/backend/api.ts +++ b/rsconcept/frontend/src/features/oss/backend/api.ts @@ -12,7 +12,7 @@ import { OperationID, OperationType } from '@/features/oss/models/oss'; -import { ConstituentaID, IConstituentaReference, ITargetCst } from '@/features/rsform/models/rsform'; +import { IConstituentaReference, ITargetCst } from '@/features/rsform/models/rsform'; import { information } from '@/utils/labels'; /** @@ -130,10 +130,15 @@ export interface IOperationUpdateDTO extends ITargetOperation { /** * Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s. */ -export interface ICstRelocateDTO { - destination: LibraryItemID; - items: ConstituentaID[]; -} +export const schemaCstRelocate = z.object({ + destination: z.number(), + items: z.array(z.number()).refine(data => data.length > 0) +}); + +/** + * Represents data, used relocating {@link IConstituenta}s between {@link ILibraryItem}s. + */ +export type ICstRelocateDTO = z.infer; export const ossApi = { baseKey: 'oss', @@ -218,9 +223,9 @@ export const ossApi = { } }), - relocateConstituents: ({ itemID, data }: { itemID: LibraryItemID; data: ICstRelocateDTO }) => + relocateConstituents: (data: ICstRelocateDTO) => axiosPost({ - endpoint: `/api/oss/${itemID}/relocate-constituents`, + endpoint: `/api/oss/relocate-constituents`, request: { data: data, successMessage: information.changesSaved diff --git a/rsconcept/frontend/src/features/oss/backend/useRelocateConstituents.tsx b/rsconcept/frontend/src/features/oss/backend/useRelocateConstituents.tsx index cfc8e6b4..990ceb0e 100644 --- a/rsconcept/frontend/src/features/oss/backend/useRelocateConstituents.tsx +++ b/rsconcept/frontend/src/features/oss/backend/useRelocateConstituents.tsx @@ -1,7 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { libraryApi } from '@/features/library/backend/api'; -import { LibraryItemID } from '@/features/library/models/library'; import { rsformsApi } from '@/features/rsform/backend/api'; import { ICstRelocateDTO, ossApi } from './api'; @@ -20,6 +19,6 @@ export const useRelocateConstituents = () => { } }); return { - relocateConstituents: (data: { itemID: LibraryItemID; data: ICstRelocateDTO }) => mutation.mutate(data) + relocateConstituents: (data: ICstRelocateDTO) => mutation.mutate(data) }; }; diff --git a/rsconcept/frontend/src/features/oss/dialogs/DlgCreateOperation/TabInputOperation.tsx b/rsconcept/frontend/src/features/oss/dialogs/DlgCreateOperation/TabInputOperation.tsx index ea0c5c83..deec9d41 100644 --- a/rsconcept/frontend/src/features/oss/dialogs/DlgCreateOperation/TabInputOperation.tsx +++ b/rsconcept/frontend/src/features/oss/dialogs/DlgCreateOperation/TabInputOperation.tsx @@ -103,7 +103,7 @@ function TabInputOperation({ {!createSchema ? ( void; + positions: IOperationPosition[]; } function DlgRelocateConstituents() { - const { oss, initialTarget, onSubmit } = useDialogsStore(state => state.props as DlgRelocateConstituentsProps); + const { oss, initialTarget, positions } = useDialogsStore(state => state.props as DlgRelocateConstituentsProps); const { items: libraryItems } = useLibrary(); + const { updatePositions } = useUpdatePositions(); + const { relocateConstituents } = useRelocateConstituents(); + + const { + handleSubmit, + control, + setValue, + resetField, + formState: { isValid } + } = useForm({ + resolver: zodResolver(schemaCstRelocate), + defaultValues: { + items: [] + }, + mode: 'onChange' + }); + const destination = useWatch({ control, name: 'destination' }); + const destinationItem = destination ? libraryItems.find(item => item.id === destination) : undefined; const [directionUp, setDirectionUp] = useState(true); - const [destination, setDestination] = useState(undefined); - const [selected, setSelected] = useState([]); const [source, setSource] = useState( libraryItems.find(item => item.id === initialTarget?.result) ); - const isValid = !!destination && selected.length > 0; + + console.log(isValid); const operation = oss.items.find(item => item.result === source?.id); const sourceSchemas = libraryItems.filter(item => oss.schemas.includes(item.id)); @@ -53,37 +73,50 @@ function DlgRelocateConstituents() { const sourceData = useRSForm({ itemID: source?.id }); const filteredConstituents = (() => { - if (!sourceData.schema || !destination || !operation) { + if (!sourceData.schema || !destinationItem || !operation) { return []; } - const destinationOperation = oss.items.find(item => item.result === destination.id); + const destinationOperation = oss.items.find(item => item.result === destination); return getRelocateCandidates(operation.id, destinationOperation!.id, sourceData.schema, oss); })(); function toggleDirection() { setDirectionUp(prev => !prev); - setDestination(undefined); + resetField('destination'); } function handleSelectSource(newValue: ILibraryItem | undefined) { setSource(newValue); - setDestination(undefined); - setSelected([]); + resetField('destination'); + resetField('items'); } function handleSelectDestination(newValue: ILibraryItem | undefined) { - setDestination(newValue); - setSelected([]); + if (newValue) { + setValue('destination', newValue.id); + } else { + resetField('destination'); + } + resetField('items'); } - function handleSubmit() { - if (destination) { - onSubmit({ - destination: destination.id, - items: selected - }); + function onSubmit(data: ICstRelocateDTO) { + const positionsUnchanged = positions.every(item => { + const operation = oss.operationByID.get(item.id)!; + return operation.position_x === item.position_x && operation.position_y === item.position_y; + }); + if (positionsUnchanged) { + relocateConstituents(data); + } else { + updatePositions( + { + isSilent: true, + itemID: oss.id, + positions: positions + }, + () => relocateConstituents(data) + ); } - return true; } return ( @@ -91,7 +124,7 @@ function DlgRelocateConstituents() { header='Перенос конституент' submitText='Переместить' canSubmit={isValid} - onSubmit={handleSubmit} + onSubmit={event => void handleSubmit(onSubmit)(event)} className={clsx('w-[40rem] h-[33rem]', 'py-3 px-6')} helpTopic={HelpTopic.UI_RELOCATE_CST} > @@ -115,19 +148,25 @@ function DlgRelocateConstituents() { className='w-1/2' placeholder='Выберите целевую схему' items={destinationSchemas} - value={destination} + value={destinationItem} onChange={handleSelectDestination} /> {sourceData.isLoading ? : null} {!sourceData.isLoading && sourceData.schema ? ( - ( + + )} /> ) : null} diff --git a/rsconcept/frontend/src/features/oss/pages/OssPage/OssEditContext.tsx b/rsconcept/frontend/src/features/oss/pages/OssPage/OssEditContext.tsx index 264c6f36..4143914c 100644 --- a/rsconcept/frontend/src/features/oss/pages/OssPage/OssEditContext.tsx +++ b/rsconcept/frontend/src/features/oss/pages/OssPage/OssEditContext.tsx @@ -19,8 +19,6 @@ import { IOperationPosition } from '../../backend/api'; import { useOperationCreate } from '../../backend/useOperationCreate'; import { useOperationUpdate } from '../../backend/useOperationUpdate'; import { useOssSuspense } from '../../backend/useOSS'; -import { useRelocateConstituents } from '../../backend/useRelocateConstituents'; -import { useUpdatePositions } from '../../backend/useUpdatePositions'; import { IOperationSchema, OperationID, OperationType } from '../../models/oss'; import { calculateInsertPosition } from '../../models/ossAPI'; @@ -100,10 +98,8 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren state.showCreateOperation); const { deleteItem } = useDeleteItem(); - const { updatePositions } = useUpdatePositions(); const { operationCreate } = useOperationCreate(); const { operationUpdate } = useOperationUpdate(); - const { relocateConstituents } = useRelocateConstituents(); useEffect( () => @@ -220,25 +216,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren { - if ( - positions.every(item => { - const operation = schema.operationByID.get(item.id)!; - return operation.position_x === item.position_x && operation.position_y === item.position_y; - }) - ) { - relocateConstituents({ itemID: schema.id, data }); - } else { - updatePositions( - { - isSilent: true, - itemID: schema.id, // - positions: positions - }, - () => relocateConstituents({ itemID: schema.id, data }) - ); - } - } + positions: positions }); } diff --git a/rsconcept/frontend/src/features/rsform/components/PickMultiConstituenta.tsx b/rsconcept/frontend/src/features/rsform/components/PickMultiConstituenta.tsx index 16f2f510..12828812 100644 --- a/rsconcept/frontend/src/features/rsform/components/PickMultiConstituenta.tsx +++ b/rsconcept/frontend/src/features/rsform/components/PickMultiConstituenta.tsx @@ -19,7 +19,7 @@ import ToolbarGraphSelection from './ToolbarGraphSelection'; interface PickMultiConstituentaProps extends CProps.Styling { id?: string; value: ConstituentaID[]; - onChange: React.Dispatch>; + onChange: (newValue: ConstituentaID[]) => void; schema: IRSForm; items: IConstituenta[]; @@ -78,7 +78,7 @@ function PickMultiConstituenta({ newSelection.push(cst.id); } }); - onChange(prev => [...prev.filter(cst_id => !filtered.find(cst => cst.id === cst_id)), ...newSelection]); + onChange([...value.filter(cst_id => !filtered.find(cst => cst.id === cst_id)), ...newSelection]); } } diff --git a/rsconcept/frontend/src/features/rsform/dialogs/DlgInlineSynthesis/TabSource.tsx b/rsconcept/frontend/src/features/rsform/dialogs/DlgInlineSynthesis/TabSource.tsx index 7c692fe1..387f83d9 100644 --- a/rsconcept/frontend/src/features/rsform/dialogs/DlgInlineSynthesis/TabSource.tsx +++ b/rsconcept/frontend/src/features/rsform/dialogs/DlgInlineSynthesis/TabSource.tsx @@ -25,7 +25,7 @@ function TabSource({ selected, receiver, setSelected }: TabSourceProps) { items={sortedItems} itemType={LibraryItemType.RSFORM} rows={14} - value={selected} + value={selected ?? null} onChange={setSelected} />