F: Improve constituent relocation in OSS

This commit is contained in:
Ivan 2025-07-25 13:12:26 +03:00
parent e00b8da6ee
commit 98f811e252
5 changed files with 34 additions and 14 deletions

View File

@ -1,3 +1,5 @@
import { globalIDs } from '@/utils/constants';
import { type Button } from '../props'; import { type Button } from '../props';
import { cn } from '../utils'; import { cn } from '../utils';
@ -15,7 +17,17 @@ interface SubmitButtonProps extends Button {
/** /**
* Displays submit type button with icon and text. * Displays submit type button with icon and text.
*/ */
export function SubmitButton({ text = 'ОК', icon, disabled, loading, className, ...restProps }: SubmitButtonProps) { export function SubmitButton({
text = 'ОК',
icon,
title,
titleHtml,
hideTitle,
disabled,
loading,
className,
...restProps
}: SubmitButtonProps) {
return ( return (
<button <button
type='submit' type='submit'
@ -28,6 +40,10 @@ export function SubmitButton({ text = 'ОК', icon, disabled, loading, className
loading && 'cursor-progress', loading && 'cursor-progress',
className className
)} )}
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
data-tooltip-html={titleHtml}
data-tooltip-content={title}
data-tooltip-hidden={hideTitle}
disabled={disabled || loading} disabled={disabled || loading}
{...restProps} {...restProps}
> >

View File

@ -180,8 +180,7 @@ export const ossApi = {
}), }),
relocateConstituents: (data: IRelocateConstituentsDTO) => relocateConstituents: (data: IRelocateConstituentsDTO) =>
axiosPost<IRelocateConstituentsDTO, IOperationSchemaDTO>({ axiosPost<IRelocateConstituentsDTO>({
schema: schemaOperationSchema,
endpoint: `/api/oss/relocate-constituents`, endpoint: `/api/oss/relocate-constituents`,
request: { request: {
data: data, data: data,

View File

@ -10,8 +10,7 @@ export const useRelocateConstituents = () => {
const mutation = useMutation({ const mutation = useMutation({
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'relocate-constituents'], mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'relocate-constituents'],
mutationFn: ossApi.relocateConstituents, mutationFn: ossApi.relocateConstituents,
onSuccess: async data => { onSuccess: async () => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
await Promise.allSettled([ await Promise.allSettled([
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }), client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] }) client.invalidateQueries({ queryKey: [KEYS.rsform] })

View File

@ -35,18 +35,14 @@ export function DlgRelocateConstituents() {
const { updateLayout: updatePositions } = useUpdateLayout(); const { updateLayout: updatePositions } = useUpdateLayout();
const { relocateConstituents } = useRelocateConstituents(); const { relocateConstituents } = useRelocateConstituents();
const { const { handleSubmit, control, setValue } = useForm<IRelocateConstituentsDTO>({
handleSubmit,
control,
setValue,
formState: { isValid }
} = useForm<IRelocateConstituentsDTO>({
resolver: zodResolver(schemaRelocateConstituents), resolver: zodResolver(schemaRelocateConstituents),
defaultValues: { defaultValues: {
items: [] items: []
}, },
mode: 'onChange' mode: 'onChange'
}); });
const selected = useWatch({ control, name: 'items' });
const destination = useWatch({ control, name: 'destination' }); const destination = useWatch({ control, name: 'destination' });
const destinationItem = destination ? libraryItems.find(item => item.id === destination) ?? null : null; const destinationItem = destination ? libraryItems.find(item => item.id === destination) ?? null : null;
@ -77,6 +73,11 @@ export function DlgRelocateConstituents() {
return getRelocateCandidates(operation.id, destinationOperation!.id, sourceData.schema, oss); return getRelocateCandidates(operation.id, destinationOperation!.id, sourceData.schema, oss);
})(); })();
const moveTarget = filteredConstituents
.filter(item => !item.is_inherited && selected.includes(item.id))
.map(item => item.id);
const isValid = moveTarget.length > 0;
function toggleDirection() { function toggleDirection() {
setDirectionUp(prev => !prev); setDirectionUp(prev => !prev);
setValue('destination', null); setValue('destination', null);
@ -98,6 +99,7 @@ export function DlgRelocateConstituents() {
} }
function onSubmit(data: IRelocateConstituentsDTO) { function onSubmit(data: IRelocateConstituentsDTO) {
data.items = moveTarget;
if (!layout || JSON.stringify(layout) === JSON.stringify(oss.layout)) { if (!layout || JSON.stringify(layout) === JSON.stringify(oss.layout)) {
return relocateConstituents(data); return relocateConstituents(data);
} else { } else {
@ -114,6 +116,7 @@ export function DlgRelocateConstituents() {
header='Перенос конституент' header='Перенос конституент'
submitText='Переместить' submitText='Переместить'
canSubmit={isValid && destinationItem !== undefined} canSubmit={isValid && destinationItem !== undefined}
submitInvalidTooltip='Необходимо выбрать хотя бы одну собственную конституенту'
onSubmit={event => void handleSubmit(onSubmit)(event)} onSubmit={event => void handleSubmit(onSubmit)(event)}
className='w-160 h-132 py-3 px-6' className='w-160 h-132 py-3 px-6'
helpTopic={HelpTopic.UI_RELOCATE_CST} helpTopic={HelpTopic.UI_RELOCATE_CST}

View File

@ -446,11 +446,12 @@ export function getRelocateCandidates(
return []; return [];
} }
const addedCst = schema.items.filter(item => !item.is_inherited);
if (node.outputs.includes(destination)) { if (node.outputs.includes(destination)) {
return addedCst; return schema.items;
} }
const addedCst = schema.items.filter(item => !item.is_inherited).map(cst => cst.id);
const unreachableBases: number[] = []; const unreachableBases: number[] = [];
for (const cst of schema.items.filter(item => item.is_inherited)) { for (const cst of schema.items.filter(item => item.is_inherited)) {
if (cst.parent_schema == destinationSchema) { if (cst.parent_schema == destinationSchema) {
@ -466,5 +467,7 @@ export function getRelocateCandidates(
unreachableBases.push(cst.id); unreachableBases.push(cst.id);
} }
const unreachable = schema.graph.expandAllOutputs(unreachableBases); const unreachable = schema.graph.expandAllOutputs(unreachableBases);
return addedCst.filter(cst => !unreachable.includes(cst.id)); return schema.items.filter(
cst => cst.parent_schema === destinationSchema || (addedCst.includes(cst.id) && !unreachable.includes(cst.id))
);
} }