F: Implement input schema change UI
This commit is contained in:
parent
01c0eb201e
commit
336b61957b
|
@ -6,6 +6,7 @@ from .data_access import (
|
|||
OperationCreateSerializer,
|
||||
OperationSchemaSerializer,
|
||||
OperationSerializer,
|
||||
OperationTargetSerializer
|
||||
OperationTargetSerializer,
|
||||
SetOperationInputSerializer
|
||||
)
|
||||
from .responses import NewOperationResponse, NewSchemaResponse
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.db.models import F
|
|||
from rest_framework import serializers
|
||||
from rest_framework.serializers import PrimaryKeyRelatedField as PKField
|
||||
|
||||
from apps.library.models import LibraryItem
|
||||
from apps.library.models import LibraryItem, LibraryItemType
|
||||
from apps.library.serializers import LibraryItemDetailsSerializer
|
||||
from shared import messages as msg
|
||||
|
||||
|
@ -66,9 +66,37 @@ class OperationTargetSerializer(serializers.Serializer):
|
|||
operation = cast(Operation, attrs['target'])
|
||||
if oss and operation.oss != oss:
|
||||
raise serializers.ValidationError({
|
||||
f'{operation.id}': msg.operationNotOwned(oss.title)
|
||||
'target': msg.operationNotInOSS(oss.title)
|
||||
})
|
||||
return attrs
|
||||
|
||||
|
||||
class SetOperationInputSerializer(serializers.Serializer):
|
||||
''' Serializer: Set input schema for operation. '''
|
||||
target = PKField(many=False, queryset=Operation.objects.all())
|
||||
input = PKField(
|
||||
many=False,
|
||||
queryset=LibraryItem.objects.filter(item_type=LibraryItemType.RSFORM),
|
||||
allow_null=True,
|
||||
default=None
|
||||
)
|
||||
sync_text = serializers.BooleanField(default=False, required=False)
|
||||
positions = serializers.ListField(
|
||||
child=OperationPositionSerializer(),
|
||||
default=[]
|
||||
)
|
||||
|
||||
def validate(self, attrs):
|
||||
oss = cast(LibraryItem, self.context['oss'])
|
||||
operation = cast(Operation, attrs['target'])
|
||||
if oss and operation.oss != oss:
|
||||
raise serializers.ValidationError({
|
||||
'target': msg.operationNotInOSS(oss.title)
|
||||
})
|
||||
if operation.operation_type != OperationType.INPUT:
|
||||
raise serializers.ValidationError({
|
||||
'target': msg.operationNotInput(operation.alias)
|
||||
})
|
||||
self.instance = operation
|
||||
return attrs
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from rest_framework import status
|
||||
|
||||
from apps.library.models import AccessPolicy, LibraryItem, LibraryItemType, LocationHead
|
||||
from apps.library.models import AccessPolicy, Editor, LibraryItem, LibraryItemType, LocationHead
|
||||
from apps.oss.models import Operation, OperationSchema, OperationType
|
||||
from apps.rsform.models import RSForm
|
||||
from shared.EndpointTester import EndpointTester, decl_endpoint
|
||||
|
@ -208,6 +208,7 @@ class TestOssViewset(EndpointTester):
|
|||
@decl_endpoint('/api/oss/{item}/create-operation', method='post')
|
||||
def test_create_operation_schema(self):
|
||||
self.populateData()
|
||||
Editor.add(self.owned.model, self.user2)
|
||||
data = {
|
||||
'item_data': {
|
||||
'alias': 'Test4',
|
||||
|
@ -228,6 +229,7 @@ class TestOssViewset(EndpointTester):
|
|||
self.assertEqual(schema.visible, False)
|
||||
self.assertEqual(schema.access_policy, self.owned.model.access_policy)
|
||||
self.assertEqual(schema.location, self.owned.model.location)
|
||||
self.assertIn(self.user2, schema.editors())
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/delete-operation', method='patch')
|
||||
def test_delete_operation(self):
|
||||
|
@ -287,3 +289,58 @@ class TestOssViewset(EndpointTester):
|
|||
|
||||
data['target'] = self.operation3.pk
|
||||
self.executeBadData(data=data)
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/set-input', method='patch')
|
||||
def test_set_input_null(self):
|
||||
self.populateData()
|
||||
self.executeBadData(item=self.owned_id)
|
||||
|
||||
data = {
|
||||
'sync_text': True,
|
||||
'positions': []
|
||||
}
|
||||
self.executeBadData(data=data)
|
||||
|
||||
data['target'] = self.operation1.pk
|
||||
data['input'] = None
|
||||
|
||||
data['target'] = self.operation1.pk
|
||||
self.toggle_admin(True)
|
||||
self.executeBadData(data=data, item=self.unowned_id)
|
||||
self.logout()
|
||||
self.executeForbidden(data=data, item=self.owned_id)
|
||||
|
||||
self.login()
|
||||
response = self.executeOK(data=data)
|
||||
self.operation1.refresh_from_db()
|
||||
self.assertEqual(self.operation1.sync_text, True)
|
||||
self.assertEqual(self.operation1.result, None)
|
||||
|
||||
data['input'] = self.ks1.model.pk
|
||||
self.ks1.model.alias = 'Test42'
|
||||
self.ks1.model.title = 'Test421'
|
||||
self.ks1.model.comment = 'TestComment42'
|
||||
self.ks1.save()
|
||||
response = self.executeOK(data=data)
|
||||
self.operation1.refresh_from_db()
|
||||
self.assertEqual(self.operation1.sync_text, True)
|
||||
self.assertEqual(self.operation1.result, self.ks1.model)
|
||||
self.assertEqual(self.operation1.alias, self.ks1.model.alias)
|
||||
self.assertEqual(self.operation1.title, self.ks1.model.title)
|
||||
self.assertEqual(self.operation1.comment, self.ks1.model.comment)
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/set-input', method='patch')
|
||||
def test_set_input_change_schema(self):
|
||||
self.populateData()
|
||||
self.operation2.result = None
|
||||
|
||||
data = {
|
||||
'sync_text': True,
|
||||
'positions': [],
|
||||
'target': self.operation1.pk,
|
||||
'input': self.ks2.model.pk
|
||||
}
|
||||
response = self.executeOK(data=data, item=self.owned_id)
|
||||
self.operation2.refresh_from_db()
|
||||
self.assertEqual(self.operation2.sync_text, True)
|
||||
self.assertEqual(self.operation2.result, self.ks2.model)
|
||||
|
|
|
@ -10,7 +10,7 @@ from rest_framework.decorators import action
|
|||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.library.models import LibraryItem, LibraryItemType
|
||||
from apps.library.models import Editor, LibraryItem, LibraryItemType
|
||||
from apps.library.serializers import LibraryItemSerializer
|
||||
from shared import messages as msg
|
||||
from shared import permissions
|
||||
|
@ -35,7 +35,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
'create_operation',
|
||||
'delete_operation',
|
||||
'update_positions',
|
||||
'create_input'
|
||||
'create_input',
|
||||
'set_input'
|
||||
]:
|
||||
permission_list = [permissions.ItemEditor]
|
||||
elif self.action in ['details']:
|
||||
|
@ -112,6 +113,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
access_policy=oss.model.access_policy,
|
||||
location=oss.model.location
|
||||
)
|
||||
Editor.set(schema, oss.model.editors())
|
||||
data['result'] = schema
|
||||
new_operation = oss.create_operation(**data)
|
||||
if new_operation.operation_type != m.OperationType.INPUT and 'arguments' in serializer.validated_data:
|
||||
|
@ -201,6 +203,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
access_policy=oss.model.access_policy,
|
||||
location=oss.model.location
|
||||
)
|
||||
Editor.set(schema, oss.model.editors())
|
||||
operation.result = schema
|
||||
operation.sync_text = True
|
||||
operation.save()
|
||||
|
@ -213,3 +216,44 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
'oss': s.OperationSchemaSerializer(oss.model).data
|
||||
}
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
summary='set input schema for target operation',
|
||||
tags=['OSS'],
|
||||
request=s.SetOperationInputSerializer(),
|
||||
responses={
|
||||
c.HTTP_200_OK: s.OperationSchemaSerializer,
|
||||
c.HTTP_400_BAD_REQUEST: None,
|
||||
c.HTTP_403_FORBIDDEN: None,
|
||||
c.HTTP_404_NOT_FOUND: None
|
||||
}
|
||||
)
|
||||
@action(detail=True, methods=['patch'], url_path='set-input')
|
||||
def set_input(self, request: Request, pk):
|
||||
''' Set input schema for target operation. '''
|
||||
serializer = s.SetOperationInputSerializer(
|
||||
data=request.data,
|
||||
context={'oss': self.get_object()}
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
operation: m.Operation = cast(m.Operation, serializer.validated_data['target'])
|
||||
result = serializer.validated_data['input']
|
||||
oss = m.OperationSchema(self.get_object())
|
||||
with transaction.atomic():
|
||||
oss.update_positions(serializer.validated_data['positions'])
|
||||
operation.result = result
|
||||
operation.sync_text = serializer.validated_data['sync_text']
|
||||
if result is not None and operation.sync_text:
|
||||
operation.title = result.title
|
||||
operation.comment = result.comment
|
||||
operation.alias = result.alias
|
||||
operation.save()
|
||||
|
||||
# update arguments
|
||||
|
||||
oss.refresh_from_db()
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data=s.OperationSchemaSerializer(oss.model).data
|
||||
)
|
||||
|
|
|
@ -212,7 +212,7 @@ class CstTargetSerializer(serializers.Serializer):
|
|||
cst = cast(Constituenta, attrs['target'])
|
||||
if schema and cst.schema != schema:
|
||||
raise serializers.ValidationError({
|
||||
f'{cst.id}': msg.constituentaNotOwned(schema.title)
|
||||
f'{cst.id}': msg.constituentaNotInRSform(schema.title)
|
||||
})
|
||||
if cst.cst_type not in [CstType.FUNCTION, CstType.STRUCTURED, CstType.TERM]:
|
||||
raise serializers.ValidationError({
|
||||
|
@ -234,7 +234,7 @@ class CstRenameSerializer(serializers.Serializer):
|
|||
cst = cast(Constituenta, attrs['target'])
|
||||
if cst.schema != schema:
|
||||
raise serializers.ValidationError({
|
||||
f'{cst.id}': msg.constituentaNotOwned(schema.title)
|
||||
f'{cst.id}': msg.constituentaNotInRSform(schema.title)
|
||||
})
|
||||
new_alias = self.initial_data['alias']
|
||||
if cst.alias == new_alias:
|
||||
|
@ -260,7 +260,7 @@ class CstListSerializer(serializers.Serializer):
|
|||
for item in attrs['items']:
|
||||
if item.schema != schema:
|
||||
raise serializers.ValidationError({
|
||||
f'{item.id}': msg.constituentaNotOwned(schema.title)
|
||||
f'{item.id}': msg.constituentaNotInRSform(schema.title)
|
||||
})
|
||||
return attrs
|
||||
|
||||
|
@ -300,11 +300,11 @@ class CstSubstituteSerializer(serializers.Serializer):
|
|||
})
|
||||
if original_cst.schema != schema:
|
||||
raise serializers.ValidationError({
|
||||
'original': msg.constituentaNotOwned(schema.title)
|
||||
'original': msg.constituentaNotInRSform(schema.title)
|
||||
})
|
||||
if substitution_cst.schema != schema:
|
||||
raise serializers.ValidationError({
|
||||
'substitution': msg.constituentaNotOwned(schema.title)
|
||||
'substitution': msg.constituentaNotInRSform(schema.title)
|
||||
})
|
||||
deleted.add(original_cst.pk)
|
||||
return attrs
|
||||
|
@ -325,14 +325,14 @@ class InlineSynthesisSerializer(serializers.Serializer):
|
|||
schema_out = cast(LibraryItem, attrs['receiver'])
|
||||
if user.is_anonymous or (schema_out.owner != user and not user.is_staff):
|
||||
raise PermissionDenied({
|
||||
'message': msg.schemaNotOwned(),
|
||||
'message': msg.schemaForbidden(),
|
||||
'object_id': schema_in.id
|
||||
})
|
||||
constituents = cast(list[Constituenta], attrs['items'])
|
||||
for cst in constituents:
|
||||
if cst.schema != schema_in:
|
||||
raise serializers.ValidationError({
|
||||
f'{cst.id}': msg.constituentaNotOwned(schema_in.title)
|
||||
f'{cst.id}': msg.constituentaNotInRSform(schema_in.title)
|
||||
})
|
||||
deleted = set()
|
||||
for item in attrs['substitutions']:
|
||||
|
@ -345,7 +345,7 @@ class InlineSynthesisSerializer(serializers.Serializer):
|
|||
})
|
||||
if substitution_cst.schema != schema_out:
|
||||
raise serializers.ValidationError({
|
||||
f'{substitution_cst.id}': msg.constituentaNotOwned(schema_out.title)
|
||||
f'{substitution_cst.id}': msg.constituentaNotInRSform(schema_out.title)
|
||||
})
|
||||
else:
|
||||
if substitution_cst not in constituents:
|
||||
|
@ -354,7 +354,7 @@ class InlineSynthesisSerializer(serializers.Serializer):
|
|||
})
|
||||
if original_cst.schema != schema_out:
|
||||
raise serializers.ValidationError({
|
||||
f'{original_cst.id}': msg.constituentaNotOwned(schema_out.title)
|
||||
f'{original_cst.id}': msg.constituentaNotInRSform(schema_out.title)
|
||||
})
|
||||
if original_cst.pk in deleted:
|
||||
raise serializers.ValidationError({
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
# pylint: skip-file
|
||||
|
||||
|
||||
def constituentaNotOwned(title: str):
|
||||
def constituentaNotInRSform(title: str):
|
||||
return f'Конституента не принадлежит схеме: {title}'
|
||||
|
||||
|
||||
def operationNotOwned(title: str):
|
||||
def operationNotInOSS(title: str):
|
||||
return f'Операция не принадлежит схеме: {title}'
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@ def substitutionNotInList():
|
|||
return 'Отождествляемая конституента отсутствует в списке'
|
||||
|
||||
|
||||
def schemaNotOwned():
|
||||
def schemaForbidden():
|
||||
return 'Нет доступа к схеме'
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
IOperationCreateData,
|
||||
IOperationCreatedResponse,
|
||||
IOperationSchemaData,
|
||||
IOperationSetInputData,
|
||||
IPositionsData,
|
||||
ITargetOperation
|
||||
} from '@/models/oss';
|
||||
|
@ -50,3 +51,10 @@ export function patchCreateInput(oss: string, request: FrontExchange<ITargetOper
|
|||
request: request
|
||||
});
|
||||
}
|
||||
|
||||
export function patchSetInput(oss: string, request: FrontExchange<IOperationSetInputData, IOperationSchemaData>) {
|
||||
AxiosPatch({
|
||||
endpoint: `/api/oss/${oss}/set-input`,
|
||||
request: request
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,7 +12,13 @@ import {
|
|||
patchSetOwner,
|
||||
postSubscribe
|
||||
} from '@/backend/library';
|
||||
import { patchCreateInput, patchDeleteOperation, patchUpdatePositions, postCreateOperation } from '@/backend/oss';
|
||||
import {
|
||||
patchCreateInput,
|
||||
patchDeleteOperation,
|
||||
patchSetInput,
|
||||
patchUpdatePositions,
|
||||
postCreateOperation
|
||||
} from '@/backend/oss';
|
||||
import { type ErrorData } from '@/components/info/InfoError';
|
||||
import { AccessPolicy, ILibraryItem } from '@/models/library';
|
||||
import { ILibraryUpdateData } from '@/models/library';
|
||||
|
@ -21,6 +27,7 @@ import {
|
|||
IOperationCreateData,
|
||||
IOperationSchema,
|
||||
IOperationSchemaData,
|
||||
IOperationSetInputData,
|
||||
IPositionsData,
|
||||
ITargetOperation
|
||||
} from '@/models/oss';
|
||||
|
@ -55,6 +62,7 @@ interface IOssContext {
|
|||
createOperation: (data: IOperationCreateData, callback?: DataCallback<IOperation>) => void;
|
||||
deleteOperation: (data: ITargetOperation, callback?: () => void) => void;
|
||||
createInput: (data: ITargetOperation, callback?: DataCallback<ILibraryItem>) => void;
|
||||
setInput: (data: IOperationSetInputData, callback?: () => void) => void;
|
||||
}
|
||||
|
||||
const OssContext = createContext<IOssContext | null>(null);
|
||||
|
@ -333,6 +341,27 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
|
|||
[itemID, library]
|
||||
);
|
||||
|
||||
const setInput = useCallback(
|
||||
(data: IOperationSetInputData, callback?: () => void) => {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
setProcessingError(undefined);
|
||||
patchSetInput(itemID, {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
library.setGlobalOSS(newData);
|
||||
library.localUpdateTimestamp(newData.id);
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
},
|
||||
[itemID, schema, library]
|
||||
);
|
||||
|
||||
return (
|
||||
<OssContext.Provider
|
||||
value={{
|
||||
|
@ -356,7 +385,8 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
|
|||
savePositions,
|
||||
createOperation,
|
||||
deleteOperation,
|
||||
createInput
|
||||
createInput,
|
||||
setInput
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -157,7 +157,11 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
|||
onSuccess: newData => {
|
||||
setSchema(Object.assign(schema, newData));
|
||||
library.localUpdateItem(newData);
|
||||
if (library.globalOSS?.schemas.includes(newData.id)) {
|
||||
library.reloadOSS(() => {
|
||||
if (callback) callback(newData);
|
||||
});
|
||||
} else if (callback) callback(newData);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
79
rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx
Normal file
79
rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx
Normal file
|
@ -0,0 +1,79 @@
|
|||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { IconReset } from '@/components/Icons';
|
||||
import PickSchema from '@/components/select/PickSchema';
|
||||
import Checkbox from '@/components/ui/Checkbox';
|
||||
import Label from '@/components/ui/Label';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||
import { IOperation, IOperationSchema } from '@/models/oss';
|
||||
|
||||
interface DlgChangeInputSchemaProps extends Pick<ModalProps, 'hideWindow'> {
|
||||
oss: IOperationSchema;
|
||||
target: IOperation;
|
||||
onSubmit: (newSchema: LibraryItemID | undefined, syncText: boolean) => void;
|
||||
}
|
||||
|
||||
function DlgChangeInputSchema({ oss, hideWindow, target, onSubmit }: DlgChangeInputSchemaProps) {
|
||||
const [selected, setSelected] = useState<LibraryItemID | undefined>(target.result ?? undefined);
|
||||
const [syncText, setSyncText] = useState(target.sync_text);
|
||||
|
||||
const baseFilter = useCallback(
|
||||
(item: ILibraryItem) => !oss.schemas.includes(item.id) || item.id === selected || item.id === target.result,
|
||||
[oss, selected, target]
|
||||
);
|
||||
|
||||
const isValid = useMemo(() => target.result !== selected, [target, selected]);
|
||||
|
||||
const handleSelectLocation = useCallback((newValue: LibraryItemID) => {
|
||||
setSelected(newValue);
|
||||
}, []);
|
||||
|
||||
function handleSubmit() {
|
||||
onSubmit(selected, syncText);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
overflowVisible
|
||||
header='Выбор концептуальной схемы'
|
||||
submitText='Подтвердить выбор'
|
||||
hideWindow={hideWindow}
|
||||
canSubmit={isValid}
|
||||
onSubmit={handleSubmit}
|
||||
className={clsx('w-[35rem]', 'pb-3 px-6 cc-column')}
|
||||
>
|
||||
<div className='flex justify-between gap-3 items-center'>
|
||||
<div className='flex gap-3'>
|
||||
<Label text='Загружаемая концептуальная схема' />
|
||||
<MiniButton
|
||||
title='Сбросить выбор схемы'
|
||||
noHover
|
||||
noPadding
|
||||
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
||||
onClick={() => setSelected(undefined)}
|
||||
disabled={selected == undefined}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<PickSchema
|
||||
value={selected} // prettier: split-line
|
||||
onSelectValue={handleSelectLocation}
|
||||
rows={8}
|
||||
baseFilter={baseFilter}
|
||||
/>
|
||||
<Checkbox
|
||||
value={syncText}
|
||||
setValue={setSyncText}
|
||||
label='Синхронизировать текст'
|
||||
titleHtml='Загрузить текстовые поля<br/> из концептуальной схемы'
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default DlgChangeInputSchema;
|
|
@ -80,7 +80,7 @@ function TabInputOperation({
|
|||
value={syncText}
|
||||
setValue={setSyncText}
|
||||
label='Синхронизировать текст'
|
||||
title='Брать текст из концептуальной схемы'
|
||||
titleHtml='Загрузить текстовые поля<br/> из концептуальной схемы'
|
||||
/>
|
||||
</FlexColumn>
|
||||
|
||||
|
|
|
@ -69,6 +69,14 @@ export interface IOperationCreateData extends IPositionsData {
|
|||
create_schema: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents {@link IOperation} data, used in setInput process.
|
||||
*/
|
||||
export interface IOperationSetInputData extends ITargetOperation {
|
||||
sync_text: boolean;
|
||||
input: LibraryItemID | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents {@link IOperation} Argument.
|
||||
*/
|
||||
|
|
|
@ -23,9 +23,18 @@ interface NodeContextMenuProps extends ContextMenuData {
|
|||
onHide: () => void;
|
||||
onDelete: (target: OperationID) => void;
|
||||
onCreateInput: (target: OperationID) => void;
|
||||
onEditSchema: (target: OperationID) => void;
|
||||
}
|
||||
|
||||
function NodeContextMenu({ operation, cursorX, cursorY, onHide, onDelete, onCreateInput }: NodeContextMenuProps) {
|
||||
function NodeContextMenu({
|
||||
operation,
|
||||
cursorX,
|
||||
cursorY,
|
||||
onHide,
|
||||
onDelete,
|
||||
onCreateInput,
|
||||
onEditSchema
|
||||
}: NodeContextMenuProps) {
|
||||
const controller = useOssEdit();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const ref = useRef(null);
|
||||
|
@ -44,8 +53,8 @@ function NodeContextMenu({ operation, cursorX, cursorY, onHide, onDelete, onCrea
|
|||
};
|
||||
|
||||
const handleEditSchema = () => {
|
||||
toast.error('Not implemented');
|
||||
handleHide();
|
||||
onEditSchema(operation.id);
|
||||
};
|
||||
|
||||
const handleEditOperation = () => {
|
||||
|
@ -97,9 +106,9 @@ function NodeContextMenu({ operation, cursorX, cursorY, onHide, onDelete, onCrea
|
|||
onClick={handleCreateSchema}
|
||||
/>
|
||||
) : null}
|
||||
{controller.isMutable && !operation.result && operation.operation_type === OperationType.INPUT ? (
|
||||
{controller.isMutable && operation.operation_type === OperationType.INPUT ? (
|
||||
<DropdownButton
|
||||
text='Загрузить схему'
|
||||
text={!operation.result ? 'Загрузить схему' : 'Изменить схему'}
|
||||
title='Выбрать схему для загрузки'
|
||||
icon={<IconConnect size='1rem' className='icon-primary' />}
|
||||
disabled={controller.isProcessing}
|
||||
|
|
|
@ -28,9 +28,7 @@ function OperationNode(node: OssNodeInternal) {
|
|||
noHover
|
||||
title='Связанная КС'
|
||||
hideTitle={!controller.showTooltip}
|
||||
onClick={() => {
|
||||
handleOpenSchema();
|
||||
}}
|
||||
onClick={handleOpenSchema}
|
||||
disabled={!hasFile}
|
||||
/>
|
||||
</Overlay>
|
||||
|
|
|
@ -148,6 +148,13 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
[controller, getPositions]
|
||||
);
|
||||
|
||||
const handleEditSchema = useCallback(
|
||||
(target: OperationID) => {
|
||||
controller.promptEditInput(target, getPositions());
|
||||
},
|
||||
[controller, getPositions]
|
||||
);
|
||||
|
||||
const handleFitView = useCallback(() => {
|
||||
flow.fitView({ duration: PARAMETER.zoomDuration });
|
||||
}, [flow]);
|
||||
|
@ -293,6 +300,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
|||
onHide={handleContextMenuHide}
|
||||
onDelete={handleDeleteOperation}
|
||||
onCreateInput={handleCreateInput}
|
||||
onEditSchema={handleEditSchema}
|
||||
{...menuProps}
|
||||
/>
|
||||
) : null}
|
||||
|
|
|
@ -10,12 +10,19 @@ import { useAuth } from '@/context/AuthContext';
|
|||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
import { useOSS } from '@/context/OssContext';
|
||||
import DlgChangeInputSchema from '@/dialogs/DlgChangeInputSchema';
|
||||
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
|
||||
import DlgCreateOperation from '@/dialogs/DlgCreateOperation';
|
||||
import DlgEditEditors from '@/dialogs/DlgEditEditors';
|
||||
import { AccessPolicy } from '@/models/library';
|
||||
import { AccessPolicy, LibraryItemID } from '@/models/library';
|
||||
import { Position2D } from '@/models/miscellaneous';
|
||||
import { IOperationCreateData, IOperationPosition, IOperationSchema, OperationID } from '@/models/oss';
|
||||
import {
|
||||
IOperationCreateData,
|
||||
IOperationPosition,
|
||||
IOperationSchema,
|
||||
IOperationSetInputData,
|
||||
OperationID
|
||||
} from '@/models/oss';
|
||||
import { UserID, UserLevel } from '@/models/user';
|
||||
import { information } from '@/utils/labels';
|
||||
|
||||
|
@ -45,6 +52,7 @@ export interface IOssEditContext {
|
|||
promptCreateOperation: (x: number, y: number, positions: IOperationPosition[]) => void;
|
||||
deleteOperation: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||
createInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||
promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
||||
}
|
||||
|
||||
const OssEditContext = createContext<IOssEditContext | null>(null);
|
||||
|
@ -79,10 +87,16 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
|
||||
const [showEditEditors, setShowEditEditors] = useState(false);
|
||||
const [showEditLocation, setShowEditLocation] = useState(false);
|
||||
const [showEditInput, setShowEditInput] = useState(false);
|
||||
|
||||
const [showCreateOperation, setShowCreateOperation] = useState(false);
|
||||
const [insertPosition, setInsertPosition] = useState<Position2D>({ x: 0, y: 0 });
|
||||
const [positions, setPositions] = useState<IOperationPosition[]>([]);
|
||||
const [targetOperationID, setTargetOperationID] = useState<OperationID | undefined>(undefined);
|
||||
const targetOperation = useMemo(
|
||||
() => (targetOperationID ? model.schema?.operationByID.get(targetOperationID) : undefined),
|
||||
[model, targetOperationID]
|
||||
);
|
||||
|
||||
useLayoutEffect(
|
||||
() =>
|
||||
|
@ -221,6 +235,28 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
[model, router]
|
||||
);
|
||||
|
||||
const promptEditInput = useCallback((target: OperationID, positions: IOperationPosition[]) => {
|
||||
setPositions(positions);
|
||||
setTargetOperationID(target);
|
||||
setShowEditInput(true);
|
||||
}, []);
|
||||
|
||||
const setTargetInput = useCallback(
|
||||
(newInput: LibraryItemID | undefined, syncText: boolean) => {
|
||||
if (!targetOperationID) {
|
||||
return;
|
||||
}
|
||||
const data: IOperationSetInputData = {
|
||||
target: targetOperationID,
|
||||
positions: positions,
|
||||
sync_text: syncText,
|
||||
input: newInput ?? null
|
||||
};
|
||||
model.setInput(data, () => toast.success(information.changesSaved));
|
||||
},
|
||||
[model, targetOperationID, positions]
|
||||
);
|
||||
|
||||
return (
|
||||
<OssEditContext.Provider
|
||||
value={{
|
||||
|
@ -246,7 +282,8 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
savePositions,
|
||||
promptCreateOperation,
|
||||
deleteOperation,
|
||||
createInput
|
||||
createInput,
|
||||
promptEditInput
|
||||
}}
|
||||
>
|
||||
{model.schema ? (
|
||||
|
@ -274,6 +311,14 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
onCreate={handleCreateOperation}
|
||||
/>
|
||||
) : null}
|
||||
{showEditInput ? (
|
||||
<DlgChangeInputSchema
|
||||
hideWindow={() => setShowEditInput(false)}
|
||||
oss={model.schema}
|
||||
target={targetOperation!}
|
||||
onSubmit={setTargetInput}
|
||||
/>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
) : null}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user