From 2eff1b27b987a9f439b339f4de8dc8b698ecd919 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:30:37 +0300 Subject: [PATCH] F: Improve OSS UI --- .../apps/oss/serializers/data_access.py | 1 + .../backend/apps/oss/tests/s_views/t_oss.py | 40 +++++++ rsconcept/backend/apps/oss/views/oss.py | 15 ++- .../src/components/info/TooltipOperation.tsx | 23 ++-- .../DlgCreateOperation/DlgCreateOperation.tsx | 16 ++- .../DlgCreateOperation/TabInputOperation.tsx | 56 ++++++--- .../TabSynthesisOperation.tsx | 4 + .../frontend/src/models/miscellaneous.ts | 28 +++++ rsconcept/frontend/src/models/oss.ts | 1 + .../OssPage/EditorOssGraph/InputNode.tsx | 23 ++-- .../EditorOssGraph/NodeContextMenu.tsx | 110 ++++++++++++++++++ .../OssPage/EditorOssGraph/OperationNode.tsx | 21 ++-- .../pages/OssPage/EditorOssGraph/OssFlow.tsx | 57 +++++++-- .../ViewConstituents/ConstituentsSearch.tsx | 2 +- .../ViewConstituents/ViewConstituents.tsx | 2 +- rsconcept/frontend/src/utils/constants.ts | 1 + 16 files changed, 327 insertions(+), 73 deletions(-) create mode 100644 rsconcept/frontend/src/pages/OssPage/EditorOssGraph/NodeContextMenu.tsx diff --git a/rsconcept/backend/apps/oss/serializers/data_access.py b/rsconcept/backend/apps/oss/serializers/data_access.py index a08d4a50..37ca8f11 100644 --- a/rsconcept/backend/apps/oss/serializers/data_access.py +++ b/rsconcept/backend/apps/oss/serializers/data_access.py @@ -44,6 +44,7 @@ class OperationCreateSerializer(serializers.Serializer): 'alias', 'operation_type', 'title', 'sync_text', \ 'comment', 'result', 'position_x', 'position_y' + create_schema = serializers.BooleanField(default=False, required=False) item_data = OperationData() arguments = PKField(many=True, queryset=Operation.objects.all(), required=False) positions = serializers.ListField( diff --git a/rsconcept/backend/apps/oss/tests/s_views/t_oss.py b/rsconcept/backend/apps/oss/tests/s_views/t_oss.py index c25b0b4f..8fca5af2 100644 --- a/rsconcept/backend/apps/oss/tests/s_views/t_oss.py +++ b/rsconcept/backend/apps/oss/tests/s_views/t_oss.py @@ -207,6 +207,46 @@ class TestOssViewset(EndpointTester): new_operation = response.data['new_operation'] self.assertEqual(new_operation['result'], self.ks1.model.pk) + @decl_endpoint('/api/oss/{item}/create-operation', method='post') + def test_create_operation_schema(self): + self.populateData() + data = { + 'item_data': { + 'alias': 'Test4', + 'title': 'Test title', + 'comment': 'Comment', + 'operation_type': OperationType.INPUT + }, + 'create_schema': True, + 'positions': [], + } + response = self.executeCreated(data=data, item=self.owned_id) + self.owned.refresh_from_db() + new_operation = response.data['new_operation'] + schema = LibraryItem.objects.get(pk=new_operation['result']) + self.assertEqual(schema.alias, data['item_data']['alias']) + self.assertEqual(schema.title, data['item_data']['title']) + self.assertEqual(schema.comment, data['item_data']['comment']) + self.assertEqual(schema.visible, False) + self.assertEqual(schema.access_policy, self.owned.model.access_policy) + self.assertEqual(schema.location, self.owned.model.location) + + @decl_endpoint('/api/oss/{item}/create-operation', method='post') + def test_create_operation_result(self): + self.populateData() + + data = { + 'item_data': { + 'alias': 'Test4', + 'operation_type': OperationType.INPUT, + 'result': self.ks1.model.pk + }, + 'positions': [], + } + response = self.executeCreated(data=data, item=self.owned_id) + self.owned.refresh_from_db() + new_operation = response.data['new_operation'] + self.assertEqual(new_operation['result'], self.ks1.model.pk) @decl_endpoint('/api/oss/{item}/delete-operation', method='patch') def test_delete_operation(self): diff --git a/rsconcept/backend/apps/oss/views/oss.py b/rsconcept/backend/apps/oss/views/oss.py index 784dc576..27b271fc 100644 --- a/rsconcept/backend/apps/oss/views/oss.py +++ b/rsconcept/backend/apps/oss/views/oss.py @@ -98,7 +98,20 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev oss = m.OperationSchema(self.get_object()) with transaction.atomic(): oss.update_positions(serializer.validated_data['positions']) - new_operation = oss.create_operation(**serializer.validated_data['item_data']) + data: dict = serializer.validated_data['item_data'] + if data['operation_type'] == m.OperationType.INPUT and serializer.validated_data['create_schema']: + schema = LibraryItem.objects.create( + item_type=LibraryItemType.RSFORM, + owner=oss.model.owner, + alias=data['alias'], + title=data['title'], + comment=data['comment'], + visible=False, + access_policy=oss.model.access_policy, + location=oss.model.location + ) + data['result'] = schema + new_operation = oss.create_operation(**data) if new_operation.operation_type != m.OperationType.INPUT and 'arguments' in serializer.validated_data: for argument in serializer.validated_data['arguments']: oss.add_argument(operation=new_operation, argument=argument) diff --git a/rsconcept/frontend/src/components/info/TooltipOperation.tsx b/rsconcept/frontend/src/components/info/TooltipOperation.tsx index 59ebfdab..b1c42b22 100644 --- a/rsconcept/frontend/src/components/info/TooltipOperation.tsx +++ b/rsconcept/frontend/src/components/info/TooltipOperation.tsx @@ -1,31 +1,34 @@ import Tooltip from '@/components/ui/Tooltip'; -import { IOperation } from '@/models/oss'; +import { OssNodeInternal } from '@/models/miscellaneous'; import { labelOperationType } from '@/utils/labels'; interface TooltipOperationProps { - data: IOperation; + node: OssNodeInternal; anchor: string; } -function TooltipOperation({ data, anchor }: TooltipOperationProps) { +function TooltipOperation({ node, anchor }: TooltipOperationProps) { return ( - -

Операция {data.alias}

+ +

{node.data.operation.alias}

- Тип: {labelOperationType(data.operation_type)} + Тип: {labelOperationType(node.data.operation.operation_type)}

- {data.title ? ( + {node.data.operation.title ? (

Название: - {data.title} + {node.data.operation.title}

) : null} - {data.comment ? ( + {node.data.operation.comment ? (

Комментарий: - {data.comment} + {node.data.operation.comment}

) : null} +

+ Положение: [{node.xPos}, {node.yPos}] +

); } diff --git a/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx b/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx index 0c889e95..5c30b176 100644 --- a/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCreateOperation/DlgCreateOperation.tsx @@ -41,8 +41,12 @@ function DlgCreateOperation({ hideWindow, oss, insertPosition, positions, onCrea const [inputs, setInputs] = useState([]); const [attachedID, setAttachedID] = useState(undefined); const [syncText, setSyncText] = useState(true); + const [createSchema, setCreateSchema] = useState(false); - const isValid = useMemo(() => alias !== '', [alias]); + const isValid = useMemo( + () => (alias !== '' && activeTab === TabID.INPUT) || inputs.length != 1, + [alias, activeTab, inputs] + ); useLayoutEffect(() => { if (attachedID) { @@ -68,7 +72,8 @@ function DlgCreateOperation({ hideWindow, oss, insertPosition, positions, onCrea result: activeTab === TabID.INPUT ? attachedID ?? null : null }, positions: positions, - arguments: activeTab === TabID.INPUT ? undefined : inputs.length > 0 ? inputs : undefined + arguments: activeTab === TabID.INPUT ? undefined : inputs.length > 0 ? inputs : undefined, + create_schema: createSchema }; onCreate(data); }; @@ -88,10 +93,12 @@ function DlgCreateOperation({ hideWindow, oss, insertPosition, positions, onCrea setAttachedID={setAttachedID} syncText={syncText} setSyncText={setSyncText} + createSchema={createSchema} + setCreateSchema={setCreateSchema} /> ), - [alias, comment, title, attachedID, syncText, oss] + [alias, comment, title, attachedID, syncText, oss, createSchema] ); const synthesisPanel = useMemo( @@ -105,11 +112,12 @@ function DlgCreateOperation({ hideWindow, oss, insertPosition, positions, onCrea setComment={setComment} title={title} setTitle={setTitle} + inputs={inputs} setInputs={setInputs} /> ), - [oss, alias, comment, title] + [oss, alias, comment, title, inputs] ); return ( diff --git a/rsconcept/frontend/src/dialogs/DlgCreateOperation/TabInputOperation.tsx b/rsconcept/frontend/src/dialogs/DlgCreateOperation/TabInputOperation.tsx index 4a3dbfe3..702c72dd 100644 --- a/rsconcept/frontend/src/dialogs/DlgCreateOperation/TabInputOperation.tsx +++ b/rsconcept/frontend/src/dialogs/DlgCreateOperation/TabInputOperation.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useCallback } from 'react'; +import { useCallback, useEffect } from 'react'; import { IconReset } from '@/components/Icons'; import PickSchema from '@/components/select/PickSchema'; @@ -27,6 +27,8 @@ interface TabInputOperationProps { setAttachedID: React.Dispatch>; syncText: boolean; setSyncText: React.Dispatch>; + createSchema: boolean; + setCreateSchema: React.Dispatch>; } function TabInputOperation({ @@ -40,10 +42,19 @@ function TabInputOperation({ attachedID, setAttachedID, syncText, - setSyncText + setSyncText, + createSchema, + setCreateSchema }: TabInputOperationProps) { const baseFilter = useCallback((item: ILibraryItem) => !oss.schemas.includes(item.id), [oss]); + useEffect(() => { + if (createSchema) { + setAttachedID(undefined); + setSyncText(true); + } + }, [createSchema, setAttachedID, setSyncText]); + return ( -
-