F: Implement different dialogs for operation creation
This commit is contained in:
parent
4f47c736ce
commit
073cd5412f
|
@ -5,9 +5,11 @@ from .data_access import (
|
|||
ArgumentSerializer,
|
||||
BlockSerializer,
|
||||
CreateBlockSerializer,
|
||||
CreateOperationSerializer,
|
||||
CreateSchemaSerializer,
|
||||
CreateSynthesisSerializer,
|
||||
DeleteBlockSerializer,
|
||||
DeleteOperationSerializer,
|
||||
ImportSchemaSerializer,
|
||||
MoveItemsSerializer,
|
||||
OperationSchemaSerializer,
|
||||
OperationSerializer,
|
||||
|
|
|
@ -2,8 +2,16 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
|
||||
class PositionSerializer(serializers.Serializer):
|
||||
''' Serializer: Position data. '''
|
||||
x = serializers.FloatField()
|
||||
y = serializers.FloatField()
|
||||
width = serializers.FloatField()
|
||||
height = serializers.FloatField()
|
||||
|
||||
|
||||
class NodeSerializer(serializers.Serializer):
|
||||
''' Block position. '''
|
||||
''' Oss node serializer. '''
|
||||
nodeID = serializers.CharField()
|
||||
x = serializers.FloatField()
|
||||
y = serializers.FloatField()
|
||||
|
|
|
@ -13,7 +13,7 @@ from apps.rsform.serializers import SubstitutionSerializerBase
|
|||
from shared import messages as msg
|
||||
|
||||
from ..models import Argument, Block, Inheritance, Operation, OperationSchema, OperationType
|
||||
from .basics import NodeSerializer, SubstitutionExSerializer
|
||||
from .basics import NodeSerializer, PositionSerializer, SubstitutionExSerializer
|
||||
|
||||
|
||||
class OperationSerializer(serializers.ModelSerializer):
|
||||
|
@ -58,10 +58,7 @@ class CreateBlockSerializer(serializers.Serializer):
|
|||
child=NodeSerializer()
|
||||
)
|
||||
item_data = BlockCreateData()
|
||||
width = serializers.FloatField()
|
||||
height = serializers.FloatField()
|
||||
position_x = serializers.FloatField()
|
||||
position_y = serializers.FloatField()
|
||||
position = PositionSerializer()
|
||||
children_operations = PKField(many=True, queryset=Operation.objects.all().only('oss_id'))
|
||||
children_blocks = PKField(many=True, queryset=Block.objects.all().only('oss_id'))
|
||||
|
||||
|
@ -193,30 +190,21 @@ class MoveItemsSerializer(serializers.Serializer):
|
|||
return attrs
|
||||
|
||||
|
||||
class CreateOperationSerializer(serializers.Serializer):
|
||||
''' Serializer: Operation creation. '''
|
||||
class CreateOperationData(serializers.ModelSerializer):
|
||||
''' Serializer: Operation creation data. '''
|
||||
alias = serializers.CharField()
|
||||
operation_type = serializers.ChoiceField(OperationType.choices)
|
||||
class CreateOperationData(serializers.ModelSerializer):
|
||||
''' Serializer: Operation creation data. '''
|
||||
alias = serializers.CharField()
|
||||
|
||||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
model = Operation
|
||||
fields = \
|
||||
'alias', 'operation_type', 'title', \
|
||||
'description', 'result', 'parent'
|
||||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
model = Operation
|
||||
fields = 'alias', 'title', 'description', 'parent'
|
||||
|
||||
layout = serializers.ListField(
|
||||
child=NodeSerializer()
|
||||
)
|
||||
|
||||
class CreateSchemaSerializer(serializers.Serializer):
|
||||
''' Serializer: Schema creation for new operation. '''
|
||||
layout = serializers.ListField(child=NodeSerializer())
|
||||
item_data = CreateOperationData()
|
||||
width = serializers.FloatField()
|
||||
height = serializers.FloatField()
|
||||
position_x = serializers.FloatField()
|
||||
position_y = serializers.FloatField()
|
||||
create_schema = serializers.BooleanField(default=False, required=False)
|
||||
arguments = PKField(many=True, queryset=Operation.objects.all().only('pk'), required=False)
|
||||
position = PositionSerializer()
|
||||
|
||||
def validate(self, attrs):
|
||||
oss = cast(LibraryItem, self.context['oss'])
|
||||
|
@ -225,14 +213,82 @@ class CreateOperationSerializer(serializers.Serializer):
|
|||
raise serializers.ValidationError({
|
||||
'parent': msg.parentNotInOSS()
|
||||
})
|
||||
return attrs
|
||||
|
||||
if 'arguments' not in attrs:
|
||||
return attrs
|
||||
|
||||
class ImportSchemaSerializer(serializers.Serializer):
|
||||
''' Serializer: Import schema to new operation. '''
|
||||
layout = serializers.ListField(child=NodeSerializer())
|
||||
item_data = CreateOperationData()
|
||||
position = PositionSerializer()
|
||||
|
||||
source = PKField(
|
||||
many=False,
|
||||
queryset=LibraryItem.objects.filter(item_type=LibraryItemType.RSFORM)
|
||||
) # type: ignore
|
||||
clone_source = serializers.BooleanField()
|
||||
|
||||
def validate(self, attrs):
|
||||
oss = cast(LibraryItem, self.context['oss'])
|
||||
parent = attrs['item_data'].get('parent')
|
||||
if parent is not None and parent.oss_id != oss.pk:
|
||||
raise serializers.ValidationError({
|
||||
'parent': msg.parentNotInOSS()
|
||||
})
|
||||
return attrs
|
||||
|
||||
|
||||
class CreateSynthesisSerializer(serializers.Serializer):
|
||||
''' Serializer: Synthesis operation creation. '''
|
||||
layout = serializers.ListField(child=NodeSerializer())
|
||||
item_data = CreateOperationData()
|
||||
position = PositionSerializer()
|
||||
|
||||
arguments = PKField(
|
||||
many=True,
|
||||
queryset=Operation.objects.all().only('pk')
|
||||
)
|
||||
substitutions = serializers.ListField(
|
||||
child=SubstitutionSerializerBase(),
|
||||
)
|
||||
|
||||
def validate(self, attrs):
|
||||
oss = cast(LibraryItem, self.context['oss'])
|
||||
parent = attrs['item_data'].get('parent')
|
||||
if parent is not None and parent.oss_id != oss.pk:
|
||||
raise serializers.ValidationError({
|
||||
'parent': msg.parentNotInOSS()
|
||||
})
|
||||
for operation in attrs['arguments']:
|
||||
if operation.oss_id != oss.pk:
|
||||
raise serializers.ValidationError({
|
||||
'arguments': msg.operationNotInOSS()
|
||||
})
|
||||
|
||||
schemas = [arg.result_id for arg in attrs['arguments'] if arg.result is not None]
|
||||
substitutions = attrs['substitutions']
|
||||
to_delete = {x['original'].pk for x in substitutions}
|
||||
deleted = set()
|
||||
for item in substitutions:
|
||||
original_cst = cast(Constituenta, item['original'])
|
||||
substitution_cst = cast(Constituenta, item['substitution'])
|
||||
if original_cst.schema_id not in schemas:
|
||||
raise serializers.ValidationError({
|
||||
f'{original_cst.pk}': msg.constituentaNotFromOperation()
|
||||
})
|
||||
if substitution_cst.schema_id not in schemas:
|
||||
raise serializers.ValidationError({
|
||||
f'{substitution_cst.pk}': msg.constituentaNotFromOperation()
|
||||
})
|
||||
if original_cst.pk in deleted or substitution_cst.pk in to_delete:
|
||||
raise serializers.ValidationError({
|
||||
f'{original_cst.pk}': msg.substituteDouble(original_cst.alias)
|
||||
})
|
||||
if original_cst.schema_id == substitution_cst.schema_id:
|
||||
raise serializers.ValidationError({
|
||||
'alias': msg.substituteTrivial(original_cst.alias)
|
||||
})
|
||||
deleted.add(original_cst.pk)
|
||||
return attrs
|
||||
|
||||
|
||||
|
|
|
@ -3,18 +3,18 @@ from rest_framework import serializers
|
|||
|
||||
from apps.library.serializers import LibraryItemSerializer
|
||||
|
||||
from .data_access import BlockSerializer, OperationSchemaSerializer, OperationSerializer
|
||||
from .data_access import OperationSchemaSerializer
|
||||
|
||||
|
||||
class OperationCreatedResponse(serializers.Serializer):
|
||||
''' Serializer: Create operation response. '''
|
||||
new_operation = OperationSerializer()
|
||||
new_operation = serializers.IntegerField()
|
||||
oss = OperationSchemaSerializer()
|
||||
|
||||
|
||||
class BlockCreatedResponse(serializers.Serializer):
|
||||
''' Serializer: Create block response. '''
|
||||
new_block = BlockSerializer()
|
||||
new_block = serializers.IntegerField()
|
||||
oss = OperationSchemaSerializer()
|
||||
|
||||
|
||||
|
|
|
@ -73,10 +73,12 @@ class TestOssBlocks(EndpointTester):
|
|||
'description': 'Тест кириллицы',
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1337,
|
||||
'position_y': 1337,
|
||||
'width': 0.42,
|
||||
'height': 0.42,
|
||||
'position': {
|
||||
'x': 1337,
|
||||
'y': 1337,
|
||||
'width': 0.42,
|
||||
'height': 0.42
|
||||
},
|
||||
'children_operations': [],
|
||||
'children_blocks': []
|
||||
}
|
||||
|
@ -86,14 +88,11 @@ class TestOssBlocks(EndpointTester):
|
|||
self.assertEqual(len(response.data['oss']['blocks']), 3)
|
||||
new_block = response.data['new_block']
|
||||
layout = response.data['oss']['layout']
|
||||
item = [item for item in layout if item['nodeID'] == 'b' + str(new_block['id'])][0]
|
||||
self.assertEqual(new_block['title'], data['item_data']['title'])
|
||||
self.assertEqual(new_block['description'], data['item_data']['description'])
|
||||
self.assertEqual(new_block['parent'], None)
|
||||
self.assertEqual(item['x'], data['position_x'])
|
||||
self.assertEqual(item['y'], data['position_y'])
|
||||
self.assertEqual(item['width'], data['width'])
|
||||
self.assertEqual(item['height'], data['height'])
|
||||
block_node = [item for item in layout if item['nodeID'] == 'b' + str(new_block)][0]
|
||||
self.assertEqual(block_node['x'], data['position']['x'])
|
||||
self.assertEqual(block_node['y'], data['position']['y'])
|
||||
self.assertEqual(block_node['width'], data['position']['width'])
|
||||
self.assertEqual(block_node['height'], data['position']['height'])
|
||||
self.operation1.refresh_from_db()
|
||||
|
||||
self.executeForbidden(data=data, item=self.unowned_id)
|
||||
|
@ -111,10 +110,12 @@ class TestOssBlocks(EndpointTester):
|
|||
'parent': self.invalid_id
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1337,
|
||||
'position_y': 1337,
|
||||
'width': 0.42,
|
||||
'height': 0.42,
|
||||
'position': {
|
||||
'x': 1337,
|
||||
'y': 1337,
|
||||
'width': 0.42,
|
||||
'height': 0.42
|
||||
},
|
||||
'children_operations': [],
|
||||
'children_blocks': []
|
||||
}
|
||||
|
@ -126,7 +127,8 @@ class TestOssBlocks(EndpointTester):
|
|||
data['item_data']['parent'] = self.block1.pk
|
||||
response = self.executeCreated(data=data)
|
||||
new_block = response.data['new_block']
|
||||
self.assertEqual(new_block['parent'], self.block1.pk)
|
||||
block_data = next((block for block in response.data['oss']['blocks'] if block['id'] == new_block), None)
|
||||
self.assertEqual(block_data['parent'], self.block1.pk)
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/create-block', method='post')
|
||||
|
@ -138,10 +140,12 @@ class TestOssBlocks(EndpointTester):
|
|||
'description': 'Тест кириллицы',
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1337,
|
||||
'position_y': 1337,
|
||||
'width': 0.42,
|
||||
'height': 0.42,
|
||||
'position': {
|
||||
'x': 1337,
|
||||
'y': 1337,
|
||||
'width': 0.42,
|
||||
'height': 0.42
|
||||
},
|
||||
'children_operations': [self.invalid_id],
|
||||
'children_blocks': []
|
||||
}
|
||||
|
@ -162,8 +166,8 @@ class TestOssBlocks(EndpointTester):
|
|||
new_block = response.data['new_block']
|
||||
self.operation1.refresh_from_db()
|
||||
self.block1.refresh_from_db()
|
||||
self.assertEqual(self.operation1.parent.pk, new_block['id'])
|
||||
self.assertEqual(self.block1.parent.pk, new_block['id'])
|
||||
self.assertEqual(self.operation1.parent.pk, new_block)
|
||||
self.assertEqual(self.block1.parent.pk, new_block)
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/create-block', method='post')
|
||||
|
@ -176,10 +180,12 @@ class TestOssBlocks(EndpointTester):
|
|||
'parent': self.block2.pk
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1337,
|
||||
'position_y': 1337,
|
||||
'width': 0.42,
|
||||
'height': 0.42,
|
||||
'position': {
|
||||
'x': 1337,
|
||||
'y': 1337,
|
||||
'width': 0.42,
|
||||
'height': 0.42
|
||||
},
|
||||
'children_operations': [],
|
||||
'children_blocks': [self.block1.pk]
|
||||
}
|
||||
|
|
|
@ -70,9 +70,10 @@ class TestOssOperations(EndpointTester):
|
|||
}])
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/create-operation', method='post')
|
||||
def test_create_operation(self):
|
||||
@decl_endpoint('/api/oss/{item}/create-schema', method='post')
|
||||
def test_create_schema(self):
|
||||
self.populateData()
|
||||
Editor.add(self.owned.model.pk, self.user2.pk)
|
||||
self.executeBadData(item=self.owned_id)
|
||||
|
||||
data = {
|
||||
|
@ -80,47 +81,50 @@ class TestOssOperations(EndpointTester):
|
|||
'alias': 'Test3',
|
||||
'title': 'Test title',
|
||||
'description': 'Тест кириллицы',
|
||||
|
||||
'parent': None
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1,
|
||||
'position_y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
|
||||
'position': {
|
||||
'x': 1,
|
||||
'y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
}
|
||||
}
|
||||
self.executeBadData(data=data)
|
||||
|
||||
data['item_data']['operation_type'] = 'invalid'
|
||||
self.executeBadData(data=data)
|
||||
|
||||
data['item_data']['operation_type'] = OperationType.INPUT
|
||||
self.executeNotFound(data=data, item=self.invalid_id)
|
||||
|
||||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
self.assertEqual(len(response.data['oss']['operations']), 4)
|
||||
new_operation = response.data['new_operation']
|
||||
new_operation_id = response.data['new_operation']
|
||||
new_operation = next(op for op in response.data['oss']['operations'] if op['id'] == new_operation_id)
|
||||
layout = response.data['oss']['layout']
|
||||
item = [item for item in layout if item['nodeID'] == 'o' + str(new_operation['id'])][0]
|
||||
operation_node = [item for item in layout if item['nodeID'] == 'o' + str(new_operation_id)][0]
|
||||
schema = LibraryItem.objects.get(pk=new_operation['result'])
|
||||
self.assertEqual(new_operation['alias'], data['item_data']['alias'])
|
||||
self.assertEqual(new_operation['operation_type'], data['item_data']['operation_type'])
|
||||
self.assertEqual(new_operation['operation_type'], OperationType.INPUT)
|
||||
self.assertEqual(new_operation['title'], data['item_data']['title'])
|
||||
self.assertEqual(new_operation['description'], data['item_data']['description'])
|
||||
self.assertEqual(new_operation['result'], None)
|
||||
self.assertEqual(new_operation['parent'], None)
|
||||
self.assertEqual(item['x'], data['position_x'])
|
||||
self.assertEqual(item['y'], data['position_y'])
|
||||
self.assertEqual(item['width'], data['width'])
|
||||
self.assertEqual(item['height'], data['height'])
|
||||
self.operation1.refresh_from_db()
|
||||
self.assertNotEqual(new_operation['result'], None)
|
||||
self.assertEqual(operation_node['x'], data['position']['x'])
|
||||
self.assertEqual(operation_node['y'], data['position']['y'])
|
||||
self.assertEqual(operation_node['width'], data['position']['width'])
|
||||
self.assertEqual(operation_node['height'], data['position']['height'])
|
||||
self.assertEqual(schema.alias, data['item_data']['alias'])
|
||||
self.assertEqual(schema.title, data['item_data']['title'])
|
||||
self.assertEqual(schema.description, data['item_data']['description'])
|
||||
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.getQ_editors())
|
||||
|
||||
self.executeForbidden(data=data, item=self.unowned_id)
|
||||
self.toggle_admin(True)
|
||||
self.executeCreated(data=data, item=self.unowned_id)
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/create-operation', method='post')
|
||||
def test_create_operation_parent(self):
|
||||
@decl_endpoint('/api/oss/{item}/create-schema', method='post')
|
||||
def test_create_schema_parent(self):
|
||||
self.populateData()
|
||||
data = {
|
||||
'item_data': {
|
||||
|
@ -128,14 +132,15 @@ class TestOssOperations(EndpointTester):
|
|||
'alias': 'Test3',
|
||||
'title': 'Test title',
|
||||
'description': '',
|
||||
'operation_type': OperationType.INPUT
|
||||
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1,
|
||||
'position_y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
'position': {
|
||||
'x': 1,
|
||||
'y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
}
|
||||
|
||||
}
|
||||
self.executeBadData(data=data, item=self.owned_id)
|
||||
|
@ -147,90 +152,40 @@ class TestOssOperations(EndpointTester):
|
|||
block_owned = self.owned.create_block(title='TestBlock2')
|
||||
data['item_data']['parent'] = block_owned.id
|
||||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
new_operation_id = response.data['new_operation']
|
||||
new_operation = next(op for op in response.data['oss']['operations'] if op['id'] == new_operation_id)
|
||||
self.assertEqual(len(response.data['oss']['operations']), 4)
|
||||
new_operation = response.data['new_operation']
|
||||
self.assertEqual(new_operation['parent'], block_owned.id)
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/create-operation', method='post')
|
||||
def test_create_operation_arguments(self):
|
||||
@decl_endpoint('/api/oss/{item}/create-synthesis', method='post')
|
||||
def test_create_synthesis(self):
|
||||
self.populateData()
|
||||
data = {
|
||||
'item_data': {
|
||||
'alias': 'Test4',
|
||||
'operation_type': OperationType.SYNTHESIS
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1,
|
||||
'position_y': 1,
|
||||
'width': 500,
|
||||
'height': 50,
|
||||
'arguments': [self.operation1.pk, self.operation3.pk]
|
||||
}
|
||||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
self.owned.refresh_from_db()
|
||||
new_operation = response.data['new_operation']
|
||||
arguments = self.owned.arguments()
|
||||
self.assertTrue(arguments.filter(operation__id=new_operation['id'], argument=self.operation1))
|
||||
self.assertTrue(arguments.filter(operation__id=new_operation['id'], argument=self.operation3))
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/create-operation', method='post')
|
||||
def test_create_operation_result(self):
|
||||
self.populateData()
|
||||
|
||||
self.operation1.result = None
|
||||
self.operation1.save()
|
||||
|
||||
data = {
|
||||
'item_data': {
|
||||
'alias': 'Test4',
|
||||
'operation_type': OperationType.INPUT,
|
||||
'result': self.ks1.model.pk
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1,
|
||||
'position_y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
}
|
||||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
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()
|
||||
Editor.add(self.owned.model.pk, self.user2.pk)
|
||||
data = {
|
||||
'item_data': {
|
||||
'alias': 'Test4',
|
||||
'title': 'Test title',
|
||||
'description': 'Comment',
|
||||
'operation_type': OperationType.INPUT,
|
||||
'result': self.ks1.model.pk
|
||||
'description': '',
|
||||
'parent': None
|
||||
},
|
||||
'create_schema': True,
|
||||
'layout': self.layout_data,
|
||||
'position_x': 1,
|
||||
'position_y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
'position': {
|
||||
'x': 1,
|
||||
'y': 1,
|
||||
'width': 500,
|
||||
'height': 50
|
||||
},
|
||||
'arguments': [self.operation1.pk, self.operation3.pk],
|
||||
'substitutions': []
|
||||
}
|
||||
self.executeBadData(data=data, item=self.owned_id)
|
||||
data['item_data']['result'] = None
|
||||
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.description, data['item_data']['description'])
|
||||
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.getQ_editors())
|
||||
new_operation_id = response.data['new_operation']
|
||||
new_operation = next(op for op in response.data['oss']['operations'] if op['id'] == new_operation_id)
|
||||
arguments = self.owned.arguments()
|
||||
self.assertTrue(arguments.filter(operation__id=new_operation_id, argument=self.operation1))
|
||||
self.assertTrue(arguments.filter(operation__id=new_operation_id, argument=self.operation3))
|
||||
self.assertNotEqual(new_operation['result'], None)
|
||||
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/delete-operation', method='patch')
|
||||
|
@ -497,3 +452,141 @@ class TestOssOperations(EndpointTester):
|
|||
self.assertEqual(len(items), 1)
|
||||
self.assertEqual(items[0].alias, 'X1')
|
||||
self.assertEqual(items[0].term_resolved, self.ks2X1.term_resolved)
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/import-schema', method='post')
|
||||
def test_import_schema(self):
|
||||
self.populateData()
|
||||
target_ks = RSForm.create(
|
||||
alias='KS_Target',
|
||||
title='Target',
|
||||
owner=self.user
|
||||
)
|
||||
data = {
|
||||
'item_data': {
|
||||
'alias': 'ImportedAlias',
|
||||
'title': 'Imported Title',
|
||||
'description': 'Imported Description',
|
||||
'parent': None
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position': {
|
||||
'x': 10,
|
||||
'y': 20,
|
||||
'width': 300,
|
||||
'height': 60
|
||||
},
|
||||
'source': target_ks.model.pk,
|
||||
'clone_source': False
|
||||
}
|
||||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
new_operation_id = response.data['new_operation']
|
||||
new_operation = next(op for op in response.data['oss']['operations'] if op['id'] == new_operation_id)
|
||||
layout = response.data['oss']['layout']
|
||||
operation_node = [item for item in layout if item['nodeID'] == 'o' + str(new_operation_id)][0]
|
||||
schema = LibraryItem.objects.get(pk=new_operation['result'])
|
||||
self.assertEqual(new_operation['alias'], data['item_data']['alias'])
|
||||
self.assertEqual(new_operation['title'], data['item_data']['title'])
|
||||
self.assertEqual(new_operation['description'], data['item_data']['description'])
|
||||
self.assertEqual(new_operation['operation_type'], OperationType.INPUT)
|
||||
self.assertEqual(schema.pk, target_ks.model.pk) # Not a clone
|
||||
self.assertEqual(operation_node['x'], data['position']['x'])
|
||||
self.assertEqual(operation_node['y'], data['position']['y'])
|
||||
self.assertEqual(operation_node['width'], data['position']['width'])
|
||||
self.assertEqual(operation_node['height'], data['position']['height'])
|
||||
self.assertEqual(schema.visible, target_ks.model.visible)
|
||||
self.assertEqual(schema.access_policy, target_ks.model.access_policy)
|
||||
self.assertEqual(schema.location, target_ks.model.location)
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/import-schema', method='post')
|
||||
def test_import_schema_clone(self):
|
||||
self.populateData()
|
||||
# Use ks2 as the source RSForm
|
||||
data = {
|
||||
'item_data': {
|
||||
'alias': 'ClonedAlias',
|
||||
'title': 'Cloned Title',
|
||||
'description': 'Cloned Description',
|
||||
'parent': None
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position': {
|
||||
'x': 42,
|
||||
'y': 1337,
|
||||
'width': 400,
|
||||
'height': 80
|
||||
},
|
||||
'source': self.ks2.model.pk,
|
||||
'clone_source': True
|
||||
}
|
||||
response = self.executeCreated(data=data, item=self.owned_id)
|
||||
new_operation_id = response.data['new_operation']
|
||||
new_operation = next(op for op in response.data['oss']['operations'] if op['id'] == new_operation_id)
|
||||
layout = response.data['oss']['layout']
|
||||
operation_node = [item for item in layout if item['nodeID'] == 'o' + str(new_operation_id)][0]
|
||||
schema = LibraryItem.objects.get(pk=new_operation['result'])
|
||||
self.assertEqual(new_operation['alias'], data['item_data']['alias'])
|
||||
self.assertEqual(new_operation['title'], data['item_data']['title'])
|
||||
self.assertEqual(new_operation['description'], data['item_data']['description'])
|
||||
self.assertEqual(new_operation['operation_type'], OperationType.INPUT)
|
||||
self.assertNotEqual(schema.pk, self.ks2.model.pk) # Should be a clone
|
||||
self.assertEqual(schema.alias, data['item_data']['alias'])
|
||||
self.assertEqual(schema.title, data['item_data']['title'])
|
||||
self.assertEqual(schema.description, data['item_data']['description'])
|
||||
self.assertEqual(operation_node['x'], data['position']['x'])
|
||||
self.assertEqual(operation_node['y'], data['position']['y'])
|
||||
self.assertEqual(operation_node['width'], data['position']['width'])
|
||||
self.assertEqual(operation_node['height'], data['position']['height'])
|
||||
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}/import-schema', method='post')
|
||||
def test_import_schema_bad_data(self):
|
||||
self.populateData()
|
||||
# Missing source
|
||||
data = {
|
||||
'item_data': {
|
||||
'alias': 'Bad',
|
||||
'title': 'Bad',
|
||||
'description': 'Bad',
|
||||
'parent': None
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position': {
|
||||
'x': 0, 'y': 0, 'width': 1, 'height': 1
|
||||
},
|
||||
# 'source' missing
|
||||
'clone_source': False
|
||||
}
|
||||
self.executeBadData(data=data, item=self.owned_id)
|
||||
# Invalid source
|
||||
data['source'] = self.invalid_id
|
||||
self.executeBadData(data=data, item=self.owned_id)
|
||||
# Invalid OSS
|
||||
data['source'] = self.ks1.model.pk
|
||||
self.executeNotFound(data=data, item=self.invalid_id)
|
||||
|
||||
@decl_endpoint('/api/oss/{item}/import-schema', method='post')
|
||||
def test_import_schema_permissions(self):
|
||||
self.populateData()
|
||||
data = {
|
||||
'item_data': {
|
||||
'alias': 'PermTest',
|
||||
'title': 'PermTest',
|
||||
'description': 'PermTest',
|
||||
'parent': None
|
||||
},
|
||||
'layout': self.layout_data,
|
||||
'position': {
|
||||
'x': 5, 'y': 5, 'width': 10, 'height': 10
|
||||
},
|
||||
'source': self.ks1.model.pk,
|
||||
'clone_source': False
|
||||
}
|
||||
# Not an editor
|
||||
self.logout()
|
||||
self.executeForbidden(data=data, item=self.owned_id)
|
||||
# As admin
|
||||
self.login()
|
||||
self.toggle_admin(True)
|
||||
self.executeCreated(data=data, item=self.owned_id)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
''' Endpoints for OSS. '''
|
||||
from copy import deepcopy
|
||||
from typing import Optional, cast
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponse
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||
from rest_framework import generics, serializers
|
||||
|
@ -23,6 +23,28 @@ from .. import models as m
|
|||
from .. import serializers as s
|
||||
|
||||
|
||||
def _create_clone(prototype: LibraryItem, operation: m.Operation, oss: LibraryItem) -> LibraryItem:
|
||||
''' Create clone of prototype schema for operation. '''
|
||||
prototype_schema = RSForm(prototype)
|
||||
clone = deepcopy(prototype)
|
||||
clone.pk = None
|
||||
clone.owner = oss.owner
|
||||
clone.title = operation.title
|
||||
clone.alias = operation.alias
|
||||
clone.description = operation.description
|
||||
clone.visible = False
|
||||
clone.read_only = False
|
||||
clone.access_policy = oss.access_policy
|
||||
clone.location = oss.location
|
||||
clone.save()
|
||||
for cst in prototype_schema.constituents():
|
||||
cst_copy = deepcopy(cst)
|
||||
cst_copy.pk = None
|
||||
cst_copy.schema = clone
|
||||
cst_copy.save()
|
||||
return clone
|
||||
|
||||
|
||||
@extend_schema(tags=['OSS'])
|
||||
@extend_schema_view()
|
||||
class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.RetrieveAPIView):
|
||||
|
@ -41,7 +63,9 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
'update_block',
|
||||
'delete_block',
|
||||
'move_items',
|
||||
'create_operation',
|
||||
'create_schema',
|
||||
'import_schema',
|
||||
'create_synthesis',
|
||||
'update_operation',
|
||||
'delete_operation',
|
||||
'create_input',
|
||||
|
@ -116,16 +140,17 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
|
||||
oss = m.OperationSchema(self.get_object())
|
||||
layout = serializer.validated_data['layout']
|
||||
position = serializer.validated_data['position']
|
||||
children_blocks: list[m.Block] = serializer.validated_data['children_blocks']
|
||||
children_operations: list[m.Operation] = serializer.validated_data['children_operations']
|
||||
with transaction.atomic():
|
||||
new_block = oss.create_block(**serializer.validated_data['item_data'])
|
||||
layout.append({
|
||||
'nodeID': 'b' + str(new_block.pk),
|
||||
'x': serializer.validated_data['position_x'],
|
||||
'y': serializer.validated_data['position_y'],
|
||||
'width': serializer.validated_data['width'],
|
||||
'height': serializer.validated_data['height'],
|
||||
'x': position['x'],
|
||||
'y': position['y'],
|
||||
'width': position['width'],
|
||||
'height': position['height'],
|
||||
})
|
||||
oss.update_layout(layout)
|
||||
if len(children_blocks) > 0:
|
||||
|
@ -140,7 +165,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
return Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
data={
|
||||
'new_block': s.BlockSerializer(new_block).data,
|
||||
'new_block': new_block.pk,
|
||||
'oss': s.OperationSchemaSerializer(oss.model).data
|
||||
}
|
||||
)
|
||||
|
@ -251,9 +276,9 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
)
|
||||
|
||||
@extend_schema(
|
||||
summary='create operation',
|
||||
summary='create empty conceptual schema',
|
||||
tags=['OSS'],
|
||||
request=s.CreateOperationSerializer(),
|
||||
request=s.CreateSchemaSerializer(),
|
||||
responses={
|
||||
c.HTTP_201_CREATED: s.OperationCreatedResponse,
|
||||
c.HTTP_400_BAD_REQUEST: None,
|
||||
|
@ -261,10 +286,10 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
c.HTTP_404_NOT_FOUND: None
|
||||
}
|
||||
)
|
||||
@action(detail=True, methods=['post'], url_path='create-operation')
|
||||
def create_operation(self, request: Request, pk) -> HttpResponse:
|
||||
''' Create Operation. '''
|
||||
serializer = s.CreateOperationSerializer(
|
||||
@action(detail=True, methods=['post'], url_path='create-schema')
|
||||
def create_schema(self, request: Request, pk) -> HttpResponse:
|
||||
''' Create schema. '''
|
||||
serializer = s.CreateSchemaSerializer(
|
||||
data=request.data,
|
||||
context={'oss': self.get_object()}
|
||||
)
|
||||
|
@ -272,43 +297,124 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
|
||||
oss = m.OperationSchema(self.get_object())
|
||||
layout = serializer.validated_data['layout']
|
||||
position = serializer.validated_data['position']
|
||||
data = serializer.validated_data['item_data']
|
||||
data['operation_type'] = m.OperationType.INPUT
|
||||
with transaction.atomic():
|
||||
new_operation = oss.create_operation(**serializer.validated_data['item_data'])
|
||||
layout.append({
|
||||
'nodeID': 'o' + str(new_operation.pk),
|
||||
'x': serializer.validated_data['position_x'],
|
||||
'y': serializer.validated_data['position_y'],
|
||||
'width': serializer.validated_data['width'],
|
||||
'height': serializer.validated_data['height']
|
||||
'x': position['x'],
|
||||
'y': position['y'],
|
||||
'width': position['width'],
|
||||
'height': position['height']
|
||||
})
|
||||
oss.update_layout(layout)
|
||||
oss.create_input(new_operation)
|
||||
|
||||
schema = new_operation.result
|
||||
if schema is not None:
|
||||
connected_operations = \
|
||||
m.Operation.objects \
|
||||
.filter(Q(result=schema) & ~Q(pk=new_operation.pk)) \
|
||||
.only('operation_type', 'oss_id')
|
||||
for operation in connected_operations:
|
||||
if operation.operation_type != m.OperationType.INPUT:
|
||||
raise serializers.ValidationError({
|
||||
'item_data': msg.operationResultFromAnotherOSS()
|
||||
})
|
||||
if operation.oss_id == new_operation.oss_id:
|
||||
raise serializers.ValidationError({
|
||||
'item_data': msg.operationInputAlreadyConnected()
|
||||
})
|
||||
if new_operation.operation_type == m.OperationType.INPUT and serializer.validated_data['create_schema']:
|
||||
oss.create_input(new_operation)
|
||||
if new_operation.operation_type != m.OperationType.INPUT and 'arguments' in serializer.validated_data:
|
||||
oss.set_arguments(
|
||||
target=new_operation.pk,
|
||||
arguments=serializer.validated_data['arguments']
|
||||
)
|
||||
return Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
data={
|
||||
'new_operation': s.OperationSerializer(new_operation).data,
|
||||
'new_operation': new_operation.pk,
|
||||
'oss': s.OperationSchemaSerializer(oss.model).data
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='import conceptual schema to new OSS operation',
|
||||
tags=['OSS'],
|
||||
request=s.ImportSchemaSerializer(),
|
||||
responses={
|
||||
c.HTTP_201_CREATED: s.OperationCreatedResponse,
|
||||
c.HTTP_400_BAD_REQUEST: None,
|
||||
c.HTTP_403_FORBIDDEN: None,
|
||||
c.HTTP_404_NOT_FOUND: None
|
||||
}
|
||||
)
|
||||
@action(detail=True, methods=['post'], url_path='import-schema')
|
||||
def import_schema(self, request: Request, pk) -> HttpResponse:
|
||||
''' Create operation with existing schema. '''
|
||||
serializer = s.ImportSchemaSerializer(
|
||||
data=request.data,
|
||||
context={'oss': self.get_object()}
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
oss = m.OperationSchema(self.get_object())
|
||||
layout = serializer.validated_data['layout']
|
||||
position = serializer.validated_data['position']
|
||||
data = serializer.validated_data['item_data']
|
||||
data['operation_type'] = m.OperationType.INPUT
|
||||
if not serializer.validated_data['clone_source']:
|
||||
data['result'] = serializer.validated_data['source']
|
||||
with transaction.atomic():
|
||||
new_operation = oss.create_operation(**serializer.validated_data['item_data'])
|
||||
layout.append({
|
||||
'nodeID': 'o' + str(new_operation.pk),
|
||||
'x': position['x'],
|
||||
'y': position['y'],
|
||||
'width': position['width'],
|
||||
'height': position['height']
|
||||
})
|
||||
oss.update_layout(layout)
|
||||
|
||||
if serializer.validated_data['clone_source']:
|
||||
prototype: LibraryItem = serializer.validated_data['source']
|
||||
new_operation.result = _create_clone(prototype, new_operation, oss.model)
|
||||
new_operation.save(update_fields=["result"])
|
||||
|
||||
return Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
data={
|
||||
'new_operation': new_operation.pk,
|
||||
'oss': s.OperationSchemaSerializer(oss.model).data
|
||||
}
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
summary='create synthesis operation',
|
||||
tags=['OSS'],
|
||||
request=s.CreateSynthesisSerializer(),
|
||||
responses={
|
||||
c.HTTP_201_CREATED: s.OperationCreatedResponse,
|
||||
c.HTTP_400_BAD_REQUEST: None,
|
||||
c.HTTP_403_FORBIDDEN: None,
|
||||
c.HTTP_404_NOT_FOUND: None
|
||||
}
|
||||
)
|
||||
@action(detail=True, methods=['post'], url_path='create-synthesis')
|
||||
def create_synthesis(self, request: Request, pk) -> HttpResponse:
|
||||
''' Create Synthesis operation from arguments. '''
|
||||
serializer = s.CreateSynthesisSerializer(
|
||||
data=request.data,
|
||||
context={'oss': self.get_object()}
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
oss = m.OperationSchema(self.get_object())
|
||||
layout = serializer.validated_data['layout']
|
||||
position = serializer.validated_data['position']
|
||||
data = serializer.validated_data['item_data']
|
||||
data['operation_type'] = m.OperationType.SYNTHESIS
|
||||
with transaction.atomic():
|
||||
new_operation = oss.create_operation(**serializer.validated_data['item_data'])
|
||||
layout.append({
|
||||
'nodeID': 'o' + str(new_operation.pk),
|
||||
'x': position['x'],
|
||||
'y': position['y'],
|
||||
'width': position['width'],
|
||||
'height': position['height']
|
||||
})
|
||||
oss.set_arguments(new_operation.pk, serializer.validated_data['arguments'])
|
||||
oss.set_substitutions(new_operation.pk, serializer.validated_data['substitutions'])
|
||||
oss.execute_operation(new_operation)
|
||||
oss.update_layout(layout)
|
||||
|
||||
return Response(
|
||||
status=c.HTTP_201_CREATED,
|
||||
data={
|
||||
'new_operation': new_operation.pk,
|
||||
'oss': s.OperationSchemaSerializer(oss.model).data
|
||||
}
|
||||
)
|
||||
|
|
|
@ -20,9 +20,9 @@ const DlgCloneLibraryItem = React.lazy(() =>
|
|||
const DlgCreateCst = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/dlg-create-cst').then(module => ({ default: module.DlgCreateCst }))
|
||||
);
|
||||
const DlgCreateOperation = React.lazy(() =>
|
||||
import('@/features/oss/dialogs/dlg-create-operation').then(module => ({
|
||||
default: module.DlgCreateOperation
|
||||
const DlgCreateSynthesis = React.lazy(() =>
|
||||
import('@/features/oss/dialogs/dlg-create-synthesis').then(module => ({
|
||||
default: module.DlgCreateSynthesis
|
||||
}))
|
||||
);
|
||||
const DlgCreateVersion = React.lazy(() =>
|
||||
|
@ -134,6 +134,12 @@ const DlgEditCst = React.lazy(() =>
|
|||
const DlgShowTermGraph = React.lazy(() =>
|
||||
import('@/features/oss/dialogs/dlg-show-term-graph').then(module => ({ default: module.DlgShowTermGraph }))
|
||||
);
|
||||
const DlgCreateSchema = React.lazy(() =>
|
||||
import('@/features/oss/dialogs/dlg-create-schema').then(module => ({ default: module.DlgCreateSchema }))
|
||||
);
|
||||
const DlgImportSchema = React.lazy(() =>
|
||||
import('@/features/oss/dialogs/dlg-import-schema').then(module => ({ default: module.DlgImportSchema }))
|
||||
);
|
||||
|
||||
export const GlobalDialogs = () => {
|
||||
const active = useDialogsStore(state => state.active);
|
||||
|
@ -146,8 +152,8 @@ export const GlobalDialogs = () => {
|
|||
return <DlgCstTemplate />;
|
||||
case DialogType.CREATE_CONSTITUENTA:
|
||||
return <DlgCreateCst />;
|
||||
case DialogType.CREATE_OPERATION:
|
||||
return <DlgCreateOperation />;
|
||||
case DialogType.CREATE_SYNTHESIS:
|
||||
return <DlgCreateSynthesis />;
|
||||
case DialogType.CREATE_BLOCK:
|
||||
return <DlgCreateBlock />;
|
||||
case DialogType.EDIT_BLOCK:
|
||||
|
@ -198,5 +204,9 @@ export const GlobalDialogs = () => {
|
|||
return <DlgEditCst />;
|
||||
case DialogType.SHOW_TERM_GRAPH:
|
||||
return <DlgShowTermGraph />;
|
||||
case DialogType.CREATE_SCHEMA:
|
||||
return <DlgCreateSchema />;
|
||||
case DialogType.IMPORT_SCHEMA:
|
||||
return <DlgImportSchema />;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,9 +8,11 @@ import {
|
|||
type IBlockCreatedResponse,
|
||||
type IConstituentaReference,
|
||||
type ICreateBlockDTO,
|
||||
type ICreateOperationDTO,
|
||||
type ICreateSchemaDTO,
|
||||
type ICreateSynthesisDTO,
|
||||
type IDeleteBlockDTO,
|
||||
type IDeleteOperationDTO,
|
||||
type IImportSchemaDTO,
|
||||
type IInputCreatedResponse,
|
||||
type IMoveItemsDTO,
|
||||
type IOperationCreatedResponse,
|
||||
|
@ -83,13 +85,40 @@ export const ossApi = {
|
|||
}
|
||||
}),
|
||||
|
||||
createOperation: ({ itemID, data }: { itemID: number; data: ICreateOperationDTO }) =>
|
||||
axiosPost<ICreateOperationDTO, IOperationCreatedResponse>({
|
||||
createSchema: ({ itemID, data }: { itemID: number; data: ICreateSchemaDTO }) =>
|
||||
axiosPost<ICreateSchemaDTO, IOperationCreatedResponse>({
|
||||
schema: schemaOperationCreatedResponse,
|
||||
endpoint: `/api/oss/${itemID}/create-operation`,
|
||||
endpoint: `/api/oss/${itemID}/create-schema`,
|
||||
request: {
|
||||
data: data,
|
||||
successMessage: response => infoMsg.newOperation(response.new_operation.alias)
|
||||
successMessage: response => {
|
||||
const alias = response.oss.operations.find(op => op.id === response.new_operation)?.alias;
|
||||
return infoMsg.newOperation(alias ?? 'ОШИБКА');
|
||||
}
|
||||
}
|
||||
}),
|
||||
createSynthesis: ({ itemID, data }: { itemID: number; data: ICreateSynthesisDTO }) =>
|
||||
axiosPost<ICreateSynthesisDTO, IOperationCreatedResponse>({
|
||||
schema: schemaOperationCreatedResponse,
|
||||
endpoint: `/api/oss/${itemID}/create-synthesis`,
|
||||
request: {
|
||||
data: data,
|
||||
successMessage: response => {
|
||||
const alias = response.oss.operations.find(op => op.id === response.new_operation)?.alias;
|
||||
return infoMsg.newOperation(alias ?? 'ОШИБКА');
|
||||
}
|
||||
}
|
||||
}),
|
||||
importSchema: ({ itemID, data }: { itemID: number; data: IImportSchemaDTO }) =>
|
||||
axiosPost<IImportSchemaDTO, IOperationCreatedResponse>({
|
||||
schema: schemaOperationCreatedResponse,
|
||||
endpoint: `/api/oss/${itemID}/import-schema`,
|
||||
request: {
|
||||
data: data,
|
||||
successMessage: response => {
|
||||
const alias = response.oss.operations.find(op => op.id === response.new_operation)?.alias;
|
||||
return infoMsg.newOperation(alias ?? 'ОШИБКА');
|
||||
}
|
||||
}
|
||||
}),
|
||||
updateOperation: ({ itemID, data }: { itemID: number; data: IUpdateOperationDTO }) =>
|
||||
|
|
|
@ -43,7 +43,9 @@ export type IDeleteBlockDTO = z.infer<typeof schemaDeleteBlock>;
|
|||
export type IMoveItemsDTO = z.infer<typeof schemaMoveItems>;
|
||||
|
||||
/** Represents {@link IOperation} data, used in Create action. */
|
||||
export type ICreateOperationDTO = z.infer<typeof schemaCreateOperation>;
|
||||
export type ICreateSchemaDTO = z.infer<typeof schemaCreateSchema>;
|
||||
export type ICreateSynthesisDTO = z.infer<typeof schemaCreateSynthesis>;
|
||||
export type IImportSchemaDTO = z.infer<typeof schemaImportSchema>;
|
||||
|
||||
/** Represents data response when creating {@link IOperation}. */
|
||||
export type IOperationCreatedResponse = z.infer<typeof schemaOperationCreatedResponse>;
|
||||
|
@ -90,6 +92,13 @@ export const schemaOperation = z.strictObject({
|
|||
result: z.number().nullable()
|
||||
});
|
||||
|
||||
export const schemaOperationData = schemaOperation.pick({
|
||||
alias: true,
|
||||
title: true,
|
||||
description: true,
|
||||
parent: true
|
||||
});
|
||||
|
||||
export const schemaBlock = z.strictObject({
|
||||
id: z.number(),
|
||||
oss: z.number(),
|
||||
|
@ -98,6 +107,13 @@ export const schemaBlock = z.strictObject({
|
|||
parent: z.number().nullable()
|
||||
});
|
||||
|
||||
export const schemaPosition = z.strictObject({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
width: z.number(),
|
||||
height: z.number()
|
||||
});
|
||||
|
||||
export const schemaCstSubstituteInfo = schemaSubstituteConstituents.extend({
|
||||
operation: z.number(),
|
||||
original_alias: z.string(),
|
||||
|
@ -106,12 +122,8 @@ export const schemaCstSubstituteInfo = schemaSubstituteConstituents.extend({
|
|||
substitution_term: z.string()
|
||||
});
|
||||
|
||||
export const schemaNodePosition = z.strictObject({
|
||||
nodeID: z.string(),
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
width: z.number(),
|
||||
height: z.number()
|
||||
export const schemaNodePosition = schemaPosition.extend({
|
||||
nodeID: z.string()
|
||||
});
|
||||
|
||||
export const schemaOssLayout = z.array(schemaNodePosition);
|
||||
|
@ -137,16 +149,13 @@ export const schemaCreateBlock = z.strictObject({
|
|||
description: z.string(),
|
||||
parent: z.number().nullable()
|
||||
}),
|
||||
position_x: z.number(),
|
||||
position_y: z.number(),
|
||||
width: z.number(),
|
||||
height: z.number(),
|
||||
position: schemaPosition,
|
||||
children_operations: z.array(z.number()),
|
||||
children_blocks: z.array(z.number())
|
||||
});
|
||||
|
||||
export const schemaBlockCreatedResponse = z.strictObject({
|
||||
new_block: schemaBlock,
|
||||
new_block: z.number(),
|
||||
oss: schemaOperationSchema
|
||||
});
|
||||
|
||||
|
@ -165,26 +174,35 @@ export const schemaDeleteBlock = z.strictObject({
|
|||
layout: schemaOssLayout
|
||||
});
|
||||
|
||||
export const schemaCreateOperation = z.strictObject({
|
||||
export const schemaCreateSchema = z.strictObject({
|
||||
layout: schemaOssLayout,
|
||||
item_data: z.strictObject({
|
||||
alias: z.string().nonempty(),
|
||||
operation_type: schemaOperationType,
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
parent: z.number().nullable(),
|
||||
result: z.number().nullable()
|
||||
}),
|
||||
position_x: z.number(),
|
||||
position_y: z.number(),
|
||||
width: z.number(),
|
||||
height: z.number(),
|
||||
item_data: schemaOperationData,
|
||||
position: schemaPosition
|
||||
});
|
||||
|
||||
export const schemaCreateSynthesis = z.strictObject({
|
||||
layout: schemaOssLayout,
|
||||
item_data: schemaOperationData,
|
||||
position: schemaPosition,
|
||||
arguments: z.array(z.number()),
|
||||
create_schema: z.boolean()
|
||||
substitutions: z.array(schemaSubstituteConstituents)
|
||||
});
|
||||
|
||||
export const schemaImportSchema = z.strictObject({
|
||||
layout: schemaOssLayout,
|
||||
item_data: schemaOperationData,
|
||||
position: z.strictObject({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
width: z.number(),
|
||||
height: z.number()
|
||||
}),
|
||||
source: z.number(),
|
||||
clone_source: z.boolean()
|
||||
});
|
||||
|
||||
export const schemaOperationCreatedResponse = z.strictObject({
|
||||
new_operation: schemaOperation,
|
||||
new_operation: z.number(),
|
||||
oss: schemaOperationSchema
|
||||
});
|
||||
|
||||
|
|
|
@ -5,14 +5,14 @@ import { useUpdateTimestamp } from '@/features/library/backend/use-update-timest
|
|||
import { KEYS } from '@/backend/configuration';
|
||||
|
||||
import { ossApi } from './api';
|
||||
import { type ICreateOperationDTO } from './types';
|
||||
import { type ICreateSchemaDTO } from './types';
|
||||
|
||||
export const useCreateOperation = () => {
|
||||
export const useCreateSchema = () => {
|
||||
const client = useQueryClient();
|
||||
const { updateTimestamp } = useUpdateTimestamp();
|
||||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-operation'],
|
||||
mutationFn: ossApi.createOperation,
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-schema'],
|
||||
mutationFn: ossApi.createSchema,
|
||||
onSuccess: response => {
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
|
||||
updateTimestamp(response.oss.id);
|
||||
|
@ -20,6 +20,6 @@ export const useCreateOperation = () => {
|
|||
onError: () => client.invalidateQueries()
|
||||
});
|
||||
return {
|
||||
createOperation: (data: { itemID: number; data: ICreateOperationDTO }) => mutation.mutateAsync(data)
|
||||
createSchema: (data: { itemID: number; data: ICreateSchemaDTO }) => mutation.mutateAsync(data)
|
||||
};
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
|
||||
|
||||
import { KEYS } from '@/backend/configuration';
|
||||
|
||||
import { ossApi } from './api';
|
||||
import { type ICreateSynthesisDTO } from './types';
|
||||
|
||||
export const useCreateSynthesis = () => {
|
||||
const client = useQueryClient();
|
||||
const { updateTimestamp } = useUpdateTimestamp();
|
||||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-synthesis'],
|
||||
mutationFn: ossApi.createSynthesis,
|
||||
onSuccess: response => {
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
|
||||
updateTimestamp(response.oss.id);
|
||||
},
|
||||
onError: () => client.invalidateQueries()
|
||||
});
|
||||
return {
|
||||
createSynthesis: (data: { itemID: number; data: ICreateSynthesisDTO }) => mutation.mutateAsync(data)
|
||||
};
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { useUpdateTimestamp } from '@/features/library/backend/use-update-timestamp';
|
||||
|
||||
import { KEYS } from '@/backend/configuration';
|
||||
|
||||
import { ossApi } from './api';
|
||||
import { type IImportSchemaDTO } from './types';
|
||||
|
||||
export const useImportSchema = () => {
|
||||
const client = useQueryClient();
|
||||
const { updateTimestamp } = useUpdateTimestamp();
|
||||
const mutation = useMutation({
|
||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'import-schema'],
|
||||
mutationFn: ossApi.importSchema,
|
||||
onSuccess: response => {
|
||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
|
||||
updateTimestamp(response.oss.id);
|
||||
},
|
||||
onError: () => client.invalidateQueries()
|
||||
});
|
||||
return {
|
||||
importSchema: (data: { itemID: number; data: IImportSchemaDTO }) => mutation.mutateAsync(data)
|
||||
};
|
||||
};
|
|
@ -49,10 +49,12 @@ export function DlgCreateBlock() {
|
|||
description: '',
|
||||
parent: initialParent
|
||||
},
|
||||
position_x: defaultX,
|
||||
position_y: defaultY,
|
||||
width: BLOCK_NODE_MIN_WIDTH,
|
||||
height: BLOCK_NODE_MIN_HEIGHT,
|
||||
position: {
|
||||
x: defaultX,
|
||||
y: defaultY,
|
||||
width: BLOCK_NODE_MIN_WIDTH,
|
||||
height: BLOCK_NODE_MIN_HEIGHT
|
||||
},
|
||||
children_blocks: initialChildren.filter(item => item.nodeType === NodeType.BLOCK).map(item => item.id),
|
||||
children_operations: initialChildren.filter(item => item.nodeType === NodeType.OPERATION).map(item => item.id),
|
||||
layout: manager.layout
|
||||
|
@ -66,13 +68,9 @@ export function DlgCreateBlock() {
|
|||
const isValid = !!title && !manager.oss.blocks.some(block => block.title === title);
|
||||
|
||||
function onSubmit(data: ICreateBlockDTO) {
|
||||
const rectangle = manager.newBlockPosition(data);
|
||||
data.position_x = rectangle.x;
|
||||
data.position_y = rectangle.y;
|
||||
data.width = rectangle.width;
|
||||
data.height = rectangle.height;
|
||||
data.position = manager.newBlockPosition(data);
|
||||
data.layout = manager.layout;
|
||||
void createBlock({ itemID: manager.oss.id, data: data }).then(response => onCreate?.(response.new_block.id));
|
||||
void createBlock({ itemID: manager.oss.id, data: data }).then(response => onCreate?.(response.new_block));
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { FormProvider, useForm, useWatch } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
import { HelpTopic } from '@/features/help';
|
||||
|
||||
import { ModalForm } from '@/components/modal';
|
||||
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/tabs';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { type ICreateOperationDTO, OperationType, schemaCreateOperation } from '../../backend/types';
|
||||
import { useCreateOperation } from '../../backend/use-create-operation';
|
||||
import { describeOperationType, labelOperationType } from '../../labels';
|
||||
import { type LayoutManager, OPERATION_NODE_HEIGHT, OPERATION_NODE_WIDTH } from '../../models/oss-layout-api';
|
||||
|
||||
import { TabInputOperation } from './tab-input-operation';
|
||||
import { TabSynthesisOperation } from './tab-synthesis-operation';
|
||||
|
||||
export interface DlgCreateOperationProps {
|
||||
manager: LayoutManager;
|
||||
initialParent: number | null;
|
||||
initialInputs: number[];
|
||||
defaultX: number;
|
||||
defaultY: number;
|
||||
onCreate?: (newID: number) => void;
|
||||
}
|
||||
|
||||
export const TabID = {
|
||||
INPUT: 0,
|
||||
SYNTHESIS: 1
|
||||
} as const;
|
||||
export type TabID = (typeof TabID)[keyof typeof TabID];
|
||||
|
||||
export function DlgCreateOperation() {
|
||||
const { createOperation } = useCreateOperation();
|
||||
|
||||
const { manager, initialInputs, initialParent, onCreate, defaultX, defaultY } = useDialogsStore(
|
||||
state => state.props as DlgCreateOperationProps
|
||||
);
|
||||
|
||||
const methods = useForm<ICreateOperationDTO>({
|
||||
resolver: zodResolver(schemaCreateOperation),
|
||||
defaultValues: {
|
||||
item_data: {
|
||||
operation_type: initialInputs.length === 0 ? OperationType.INPUT : OperationType.SYNTHESIS,
|
||||
alias: '',
|
||||
title: '',
|
||||
description: '',
|
||||
result: null,
|
||||
parent: initialParent
|
||||
},
|
||||
position_x: defaultX,
|
||||
position_y: defaultY,
|
||||
arguments: initialInputs,
|
||||
width: OPERATION_NODE_WIDTH,
|
||||
height: OPERATION_NODE_HEIGHT,
|
||||
create_schema: false,
|
||||
layout: manager.layout
|
||||
},
|
||||
mode: 'onChange'
|
||||
});
|
||||
const alias = useWatch({ control: methods.control, name: 'item_data.alias' });
|
||||
const [activeTab, setActiveTab] = useState(initialInputs.length === 0 ? TabID.INPUT : TabID.SYNTHESIS);
|
||||
const isValid = !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
|
||||
|
||||
function onSubmit(data: ICreateOperationDTO) {
|
||||
const target = manager.newOperationPosition(data);
|
||||
data.position_x = target.x;
|
||||
data.position_y = target.y;
|
||||
data.layout = manager.layout;
|
||||
void createOperation({ itemID: manager.oss.id, data: data }).then(response =>
|
||||
onCreate?.(response.new_operation.id)
|
||||
);
|
||||
}
|
||||
|
||||
function handleSelectTab(newTab: TabID, last: TabID) {
|
||||
if (last === newTab) {
|
||||
return;
|
||||
}
|
||||
if (newTab === TabID.INPUT) {
|
||||
methods.setValue('item_data.operation_type', OperationType.INPUT);
|
||||
methods.setValue('item_data.result', null);
|
||||
methods.setValue('arguments', []);
|
||||
} else {
|
||||
methods.setValue('item_data.operation_type', OperationType.SYNTHESIS);
|
||||
methods.setValue('arguments', initialInputs);
|
||||
}
|
||||
setActiveTab(newTab);
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
header='Создание операции'
|
||||
submitText='Создать'
|
||||
canSubmit={isValid}
|
||||
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
|
||||
className='w-180 px-6 h-128'
|
||||
helpTopic={HelpTopic.CC_OSS}
|
||||
>
|
||||
<Tabs
|
||||
className='grid'
|
||||
selectedIndex={activeTab}
|
||||
onSelect={(index, last) => handleSelectTab(index as TabID, last as TabID)}
|
||||
>
|
||||
<TabList className='z-pop mx-auto -mb-5 flex border divide-x rounded-none'>
|
||||
<TabLabel
|
||||
title={describeOperationType(OperationType.INPUT)}
|
||||
label={labelOperationType(OperationType.INPUT)}
|
||||
/>
|
||||
<TabLabel
|
||||
title={describeOperationType(OperationType.SYNTHESIS)}
|
||||
label={labelOperationType(OperationType.SYNTHESIS)}
|
||||
/>
|
||||
</TabList>
|
||||
<FormProvider {...methods}>
|
||||
<TabPanel>
|
||||
<TabInputOperation />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<TabSynthesisOperation />
|
||||
</TabPanel>
|
||||
</FormProvider>
|
||||
</Tabs>
|
||||
</ModalForm>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { DlgCreateOperation } from './dlg-create-operation';
|
|
@ -1,129 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
||||
|
||||
import { type ILibraryItem, LibraryItemType } from '@/features/library';
|
||||
import { useLibrary } from '@/features/library/backend/use-library';
|
||||
import { PickSchema } from '@/features/library/components/pick-schema';
|
||||
|
||||
import { MiniButton } from '@/components/control';
|
||||
import { IconReset } from '@/components/icons';
|
||||
import { Checkbox, Label, TextArea, TextInput } from '@/components/input';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { type ICreateOperationDTO } from '../../backend/types';
|
||||
import { SelectParent } from '../../components/select-parent';
|
||||
import { sortItemsForOSS } from '../../models/oss-api';
|
||||
|
||||
import { type DlgCreateOperationProps } from './dlg-create-operation';
|
||||
|
||||
export function TabInputOperation() {
|
||||
const { manager } = useDialogsStore(state => state.props as DlgCreateOperationProps);
|
||||
const { items: libraryItems } = useLibrary();
|
||||
const sortedItems = sortItemsForOSS(manager.oss, libraryItems);
|
||||
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
setValue,
|
||||
formState: { errors }
|
||||
} = useFormContext<ICreateOperationDTO>();
|
||||
const createSchema = useWatch({ control, name: 'create_schema' });
|
||||
|
||||
function baseFilter(item: ILibraryItem) {
|
||||
return !manager.oss.schemas.includes(item.id);
|
||||
}
|
||||
|
||||
function handleChangeCreateSchema(value: boolean) {
|
||||
if (value) {
|
||||
setValue('item_data.result', null);
|
||||
}
|
||||
setValue('create_schema', value);
|
||||
}
|
||||
|
||||
function handleSetInput(inputID: number) {
|
||||
const schema = libraryItems.find(item => item.id === inputID);
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
setValue('item_data.result', inputID);
|
||||
setValue('create_schema', false);
|
||||
setValue('item_data.alias', schema.alias);
|
||||
setValue('item_data.title', schema.title);
|
||||
setValue('item_data.description', schema.description, { shouldValidate: true });
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='cc-fade-in cc-column'>
|
||||
<TextInput
|
||||
id='operation_title' //
|
||||
label='Название'
|
||||
{...register('item_data.title')}
|
||||
error={errors.item_data?.title}
|
||||
/>
|
||||
<div className='flex gap-6'>
|
||||
<div className='grid gap-1'>
|
||||
<TextInput
|
||||
id='operation_alias' //
|
||||
label='Сокращение'
|
||||
className='w-80'
|
||||
{...register('item_data.alias')}
|
||||
error={errors.item_data?.alias}
|
||||
/>
|
||||
<Controller
|
||||
name='item_data.parent'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<SelectParent
|
||||
items={manager.oss.blocks}
|
||||
value={field.value ? manager.oss.blockByID.get(field.value) ?? null : null}
|
||||
placeholder='Родительский блок'
|
||||
onChange={value => field.onChange(value ? value.id : null)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<TextArea
|
||||
id='operation_comment' //
|
||||
label='Описание'
|
||||
noResize
|
||||
rows={3}
|
||||
{...register('item_data.description')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-between gap-3 items-center'>
|
||||
<div className='flex gap-3'>
|
||||
<Label text='Загружаемая концептуальная схема' />
|
||||
<MiniButton
|
||||
title='Сбросить выбор схемы'
|
||||
noPadding
|
||||
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
||||
onClick={() => setValue('item_data.result', null)}
|
||||
/>
|
||||
</div>
|
||||
<Checkbox
|
||||
value={createSchema} //
|
||||
onChange={handleChangeCreateSchema}
|
||||
label='Создать новую схему'
|
||||
/>
|
||||
</div>
|
||||
{!createSchema ? (
|
||||
<Controller
|
||||
control={control}
|
||||
name='item_data.result'
|
||||
render={({ field }) => (
|
||||
<PickSchema
|
||||
items={sortedItems}
|
||||
value={field.value}
|
||||
itemType={LibraryItemType.RSFORM}
|
||||
onChange={handleSetInput}
|
||||
rows={8}
|
||||
baseFilter={baseFilter}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
'use client';
|
||||
|
||||
import { Controller, useForm, useWatch } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
import { HelpTopic } from '@/features/help';
|
||||
|
||||
import { TextArea, TextInput } from '@/components/input';
|
||||
import { ModalForm } from '@/components/modal';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { type ICreateSchemaDTO, schemaCreateSchema } from '../backend/types';
|
||||
import { useCreateSchema } from '../backend/use-create-schema';
|
||||
import { SelectParent } from '../components/select-parent';
|
||||
import { type LayoutManager, OPERATION_NODE_HEIGHT, OPERATION_NODE_WIDTH } from '../models/oss-layout-api';
|
||||
|
||||
export interface DlgCreateSchemaProps {
|
||||
manager: LayoutManager;
|
||||
defaultX: number;
|
||||
defaultY: number;
|
||||
initialParent: number | null;
|
||||
onCreate?: (newID: number) => void;
|
||||
}
|
||||
|
||||
export function DlgCreateSchema() {
|
||||
const { createSchema } = useCreateSchema();
|
||||
|
||||
const { manager, initialParent, onCreate, defaultX, defaultY } = useDialogsStore(
|
||||
state => state.props as DlgCreateSchemaProps
|
||||
);
|
||||
|
||||
const {
|
||||
control,
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors }
|
||||
} = useForm<ICreateSchemaDTO>({
|
||||
resolver: zodResolver(schemaCreateSchema),
|
||||
defaultValues: {
|
||||
item_data: {
|
||||
alias: '',
|
||||
title: '',
|
||||
description: '',
|
||||
parent: initialParent
|
||||
},
|
||||
position: {
|
||||
x: defaultX,
|
||||
y: defaultY,
|
||||
width: OPERATION_NODE_WIDTH,
|
||||
height: OPERATION_NODE_HEIGHT
|
||||
},
|
||||
layout: manager.layout
|
||||
},
|
||||
mode: 'onChange'
|
||||
});
|
||||
const alias = useWatch({ control: control, name: 'item_data.alias' });
|
||||
const isValid = !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
|
||||
|
||||
function onSubmit(data: ICreateSchemaDTO) {
|
||||
data.position = manager.newOperationPosition(data);
|
||||
data.layout = manager.layout;
|
||||
void createSchema({ itemID: manager.oss.id, data: data }).then(response => onCreate?.(response.new_operation));
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
header='Создание операции: Пустая КС'
|
||||
submitText='Создать'
|
||||
canSubmit={isValid}
|
||||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||
className='w-180 px-6 cc-column'
|
||||
helpTopic={HelpTopic.CC_OSS}
|
||||
>
|
||||
<TextInput
|
||||
id='operation_title' //
|
||||
label='Название'
|
||||
{...register('item_data.title')}
|
||||
error={errors.item_data?.title}
|
||||
/>
|
||||
<div className='flex gap-6'>
|
||||
<div className='grid gap-1'>
|
||||
<TextInput
|
||||
id='operation_alias' //
|
||||
label='Сокращение'
|
||||
className='w-80'
|
||||
{...register('item_data.alias')}
|
||||
error={errors.item_data?.alias}
|
||||
/>
|
||||
<Controller
|
||||
name='item_data.parent'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<SelectParent
|
||||
items={manager.oss.blocks}
|
||||
value={field.value ? manager.oss.blockByID.get(field.value) ?? null : null}
|
||||
placeholder='Родительский блок'
|
||||
onChange={value => field.onChange(value ? value.id : null)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<TextArea
|
||||
id='operation_comment' //
|
||||
label='Описание'
|
||||
noResize
|
||||
rows={3}
|
||||
{...register('item_data.description')}
|
||||
/>
|
||||
</div>
|
||||
</ModalForm>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
'use client';
|
||||
|
||||
import { Suspense, useState } from 'react';
|
||||
import { FormProvider, useForm, useWatch } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
import { HelpTopic } from '@/features/help';
|
||||
|
||||
import { Loader } from '@/components/loader';
|
||||
import { ModalForm } from '@/components/modal';
|
||||
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/tabs';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { type ICreateSynthesisDTO, schemaCreateSynthesis } from '../../backend/types';
|
||||
import { useCreateSynthesis } from '../../backend/use-create-synthesis';
|
||||
import { type LayoutManager, OPERATION_NODE_HEIGHT, OPERATION_NODE_WIDTH } from '../../models/oss-layout-api';
|
||||
|
||||
import { TabArguments } from './tab-arguments';
|
||||
import { TabSubstitutions } from './tab-substitutions';
|
||||
|
||||
export interface DlgCreateSynthesisProps {
|
||||
manager: LayoutManager;
|
||||
initialParent: number | null;
|
||||
initialInputs: number[];
|
||||
defaultX: number;
|
||||
defaultY: number;
|
||||
onCreate?: (newID: number) => void;
|
||||
}
|
||||
|
||||
export const TabID = {
|
||||
ARGUMENTS: 0,
|
||||
SUBSTITUTIONS: 1
|
||||
} as const;
|
||||
export type TabID = (typeof TabID)[keyof typeof TabID];
|
||||
|
||||
export function DlgCreateSynthesis() {
|
||||
const { createSynthesis } = useCreateSynthesis();
|
||||
|
||||
const { manager, initialInputs, initialParent, onCreate, defaultX, defaultY } = useDialogsStore(
|
||||
state => state.props as DlgCreateSynthesisProps
|
||||
);
|
||||
|
||||
const methods = useForm<ICreateSynthesisDTO>({
|
||||
resolver: zodResolver(schemaCreateSynthesis),
|
||||
defaultValues: {
|
||||
item_data: {
|
||||
alias: '',
|
||||
title: '',
|
||||
description: '',
|
||||
parent: initialParent
|
||||
},
|
||||
position: {
|
||||
x: defaultX,
|
||||
y: defaultY,
|
||||
width: OPERATION_NODE_WIDTH,
|
||||
height: OPERATION_NODE_HEIGHT
|
||||
},
|
||||
arguments: initialInputs,
|
||||
substitutions: [],
|
||||
layout: manager.layout
|
||||
},
|
||||
mode: 'onChange'
|
||||
});
|
||||
const alias = useWatch({ control: methods.control, name: 'item_data.alias' });
|
||||
const [activeTab, setActiveTab] = useState<TabID>(TabID.ARGUMENTS);
|
||||
const isValid = !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
|
||||
|
||||
function onSubmit(data: ICreateSynthesisDTO) {
|
||||
data.position = manager.newOperationPosition(data);
|
||||
data.layout = manager.layout;
|
||||
void createSynthesis({ itemID: manager.oss.id, data: data }).then(response => onCreate?.(response.new_operation));
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
header='Создание операции'
|
||||
submitText='Создать'
|
||||
canSubmit={isValid}
|
||||
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
|
||||
className='w-180 px-6 h-128'
|
||||
helpTopic={HelpTopic.CC_OSS}
|
||||
>
|
||||
<Tabs className='grid' selectedIndex={activeTab} onSelect={index => setActiveTab(index as TabID)}>
|
||||
<TabList className='z-pop mx-auto -mb-5 flex border divide-x rounded-none'>
|
||||
<TabLabel title='Выбор аргументов операции' label='Аргументы' className='w-32' />
|
||||
<TabLabel titleHtml='Таблица отождествлений' label='Отождествления' className='w-32' />
|
||||
</TabList>
|
||||
<FormProvider {...methods}>
|
||||
<TabPanel>
|
||||
<TabArguments />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className='w-full h-full flex items-center justify-center'>
|
||||
<Loader circular />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<TabSubstitutions />
|
||||
</Suspense>
|
||||
</TabPanel>
|
||||
</FormProvider>
|
||||
</Tabs>
|
||||
</ModalForm>
|
||||
);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { DlgCreateSynthesis } from './dlg-create-synthesis';
|
|
@ -3,19 +3,19 @@ import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
|||
import { Label, TextArea, TextInput } from '@/components/input';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { type ICreateOperationDTO } from '../../backend/types';
|
||||
import { type ICreateSynthesisDTO } from '../../backend/types';
|
||||
import { PickMultiOperation } from '../../components/pick-multi-operation';
|
||||
import { SelectParent } from '../../components/select-parent';
|
||||
|
||||
import { type DlgCreateOperationProps } from './dlg-create-operation';
|
||||
import { type DlgCreateSynthesisProps } from './dlg-create-synthesis';
|
||||
|
||||
export function TabSynthesisOperation() {
|
||||
const { manager } = useDialogsStore(state => state.props as DlgCreateOperationProps);
|
||||
export function TabArguments() {
|
||||
const { manager } = useDialogsStore(state => state.props as DlgCreateSynthesisProps);
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
formState: { errors }
|
||||
} = useFormContext<ICreateOperationDTO>();
|
||||
} = useFormContext<ICreateSynthesisDTO>();
|
||||
const inputs = useWatch({ control, name: 'arguments' });
|
||||
|
||||
return (
|
|
@ -0,0 +1,50 @@
|
|||
'use client';
|
||||
|
||||
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
||||
|
||||
import { useRSForms } from '@/features/rsform/backend/use-rsforms';
|
||||
import { PickSubstitutions } from '@/features/rsform/components/pick-substitutions';
|
||||
|
||||
import { TextArea } from '@/components/input';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { type ICreateSynthesisDTO } from '../../backend/types';
|
||||
import { SubstitutionValidator } from '../../models/oss-api';
|
||||
|
||||
import { type DlgCreateSynthesisProps } from './dlg-create-synthesis';
|
||||
|
||||
export function TabSubstitutions() {
|
||||
const { manager } = useDialogsStore(state => state.props as DlgCreateSynthesisProps);
|
||||
const { control } = useFormContext<ICreateSynthesisDTO>();
|
||||
const inputs = useWatch({ control, name: 'arguments' });
|
||||
const substitutions = useWatch({ control, name: 'substitutions' });
|
||||
|
||||
const schemasIDs = inputs
|
||||
.map(id => manager.oss.operationByID.get(id)!)
|
||||
.map(operation => operation.result)
|
||||
.filter(id => id !== null);
|
||||
const schemas = useRSForms(schemasIDs);
|
||||
|
||||
const validator = new SubstitutionValidator(schemas, substitutions);
|
||||
const isCorrect = validator.validate();
|
||||
|
||||
return (
|
||||
<div className='cc-fade-in cc-column mt-9'>
|
||||
<Controller
|
||||
name='substitutions'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<PickSubstitutions
|
||||
schemas={schemas}
|
||||
rows={8}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
suggestions={validator.suggestions}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<TextArea disabled value={validator.msg} rows={4} className={isCorrect ? '' : 'border-(--acc-fg-red) border-2'} />
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -18,7 +18,7 @@ import { type LayoutManager } from '../../models/oss-layout-api';
|
|||
|
||||
import { TabArguments } from './tab-arguments';
|
||||
import { TabOperation } from './tab-operation';
|
||||
import { TabSynthesis } from './tab-synthesis';
|
||||
import { TabSubstitutions } from './tab-substitutions';
|
||||
|
||||
export interface DlgEditOperationProps {
|
||||
manager: LayoutManager;
|
||||
|
@ -28,7 +28,7 @@ export interface DlgEditOperationProps {
|
|||
export const TabID = {
|
||||
CARD: 0,
|
||||
ARGUMENTS: 1,
|
||||
SUBSTITUTION: 2
|
||||
SUBSTITUTIONS: 2
|
||||
} as const;
|
||||
export type TabID = (typeof TabID)[keyof typeof TabID];
|
||||
|
||||
|
@ -73,7 +73,7 @@ export function DlgEditOperation() {
|
|||
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
|
||||
className='w-160 px-6 h-128'
|
||||
helpTopic={HelpTopic.UI_SUBSTITUTIONS}
|
||||
hideHelpWhen={() => activeTab !== TabID.SUBSTITUTION}
|
||||
hideHelpWhen={() => activeTab !== TabID.SUBSTITUTIONS}
|
||||
>
|
||||
<Tabs className='grid' selectedIndex={activeTab} onSelect={index => setActiveTab(index as TabID)}>
|
||||
<TabList className='mb-3 mx-auto w-fit flex border divide-x rounded-none'>
|
||||
|
@ -109,7 +109,7 @@ export function DlgEditOperation() {
|
|||
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||
<TabPanel>
|
||||
<Suspense fallback={<Loader />}>
|
||||
<TabSynthesis />
|
||||
<TabSubstitutions />
|
||||
</Suspense>
|
||||
</TabPanel>
|
||||
) : null}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { SubstitutionValidator } from '../../models/oss-api';
|
|||
|
||||
import { type DlgEditOperationProps } from './dlg-edit-operation';
|
||||
|
||||
export function TabSynthesis() {
|
||||
export function TabSubstitutions() {
|
||||
const { manager } = useDialogsStore(state => state.props as DlgEditOperationProps);
|
||||
const { control } = useFormContext<IUpdateOperationDTO>();
|
||||
const inputs = useWatch({ control, name: 'arguments' });
|
|
@ -0,0 +1,158 @@
|
|||
'use client';
|
||||
|
||||
import { Controller, useForm, useWatch } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
import { HelpTopic } from '@/features/help';
|
||||
import { type ILibraryItem, LibraryItemType } from '@/features/library';
|
||||
import { useLibrary } from '@/features/library/backend/use-library';
|
||||
import { PickSchema } from '@/features/library/components/pick-schema';
|
||||
|
||||
import { Checkbox, TextArea, TextInput } from '@/components/input';
|
||||
import { ModalForm } from '@/components/modal';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { type IImportSchemaDTO, schemaImportSchema } from '../backend/types';
|
||||
import { useImportSchema } from '../backend/use-import-schema';
|
||||
import { SelectParent } from '../components/select-parent';
|
||||
import { sortItemsForOSS } from '../models/oss-api';
|
||||
import { type LayoutManager, OPERATION_NODE_HEIGHT, OPERATION_NODE_WIDTH } from '../models/oss-layout-api';
|
||||
|
||||
export interface DlgImportSchemaProps {
|
||||
manager: LayoutManager;
|
||||
defaultX: number;
|
||||
defaultY: number;
|
||||
initialParent: number | null;
|
||||
onCreate?: (newID: number) => void;
|
||||
}
|
||||
|
||||
export function DlgImportSchema() {
|
||||
const { importSchema } = useImportSchema();
|
||||
|
||||
const { manager, initialParent, onCreate, defaultX, defaultY } = useDialogsStore(
|
||||
state => state.props as DlgImportSchemaProps
|
||||
);
|
||||
const { items: libraryItems } = useLibrary();
|
||||
const sortedItems = sortItemsForOSS(manager.oss, libraryItems);
|
||||
|
||||
const {
|
||||
control,
|
||||
register,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors }
|
||||
} = useForm<IImportSchemaDTO>({
|
||||
resolver: zodResolver(schemaImportSchema),
|
||||
defaultValues: {
|
||||
item_data: {
|
||||
alias: '',
|
||||
title: '',
|
||||
description: '',
|
||||
parent: initialParent
|
||||
},
|
||||
position: {
|
||||
x: defaultX,
|
||||
y: defaultY,
|
||||
width: OPERATION_NODE_WIDTH,
|
||||
height: OPERATION_NODE_HEIGHT
|
||||
},
|
||||
layout: manager.layout,
|
||||
source: 0,
|
||||
clone_source: false
|
||||
},
|
||||
mode: 'onChange'
|
||||
});
|
||||
const alias = useWatch({ control: control, name: 'item_data.alias' });
|
||||
const clone_source = useWatch({ control: control, name: 'clone_source' });
|
||||
const isValid = !!alias && !manager.oss.operations.some(operation => operation.alias === alias);
|
||||
|
||||
function onSubmit(data: IImportSchemaDTO) {
|
||||
data.position = manager.newOperationPosition(data);
|
||||
data.layout = manager.layout;
|
||||
void importSchema({ itemID: manager.oss.id, data: data }).then(response => onCreate?.(response.new_operation));
|
||||
}
|
||||
|
||||
function baseFilter(item: ILibraryItem) {
|
||||
return !manager.oss.schemas.includes(item.id);
|
||||
}
|
||||
|
||||
function handleSetInput(inputID: number) {
|
||||
const schema = libraryItems.find(item => item.id === inputID);
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
setValue('source', inputID);
|
||||
setValue('item_data.alias', schema.alias);
|
||||
setValue('item_data.title', schema.title);
|
||||
setValue('item_data.description', schema.description, { shouldValidate: true });
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
header='Создание операции: Загрузка'
|
||||
submitText='Создать'
|
||||
canSubmit={isValid}
|
||||
onSubmit={event => void handleSubmit(onSubmit)(event)}
|
||||
className='w-180 px-6 cc-column'
|
||||
helpTopic={HelpTopic.CC_OSS}
|
||||
>
|
||||
<Controller
|
||||
control={control}
|
||||
name='source'
|
||||
render={({ field }) => (
|
||||
<PickSchema
|
||||
items={sortedItems}
|
||||
value={field.value}
|
||||
itemType={LibraryItemType.RSFORM}
|
||||
onChange={handleSetInput}
|
||||
rows={8}
|
||||
baseFilter={baseFilter}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name='clone_source'
|
||||
render={({ field }) => <Checkbox label='Клонировать схему' value={field.value} onChange={field.onChange} />}
|
||||
/>
|
||||
<TextInput
|
||||
id='operation_title' //
|
||||
label='Название'
|
||||
disabled={!clone_source}
|
||||
{...register('item_data.title')}
|
||||
error={errors.item_data?.title}
|
||||
/>
|
||||
<div className='flex gap-6'>
|
||||
<div className='grid gap-1'>
|
||||
<TextInput
|
||||
id='operation_alias' //
|
||||
label='Сокращение'
|
||||
className='w-80'
|
||||
disabled={!clone_source}
|
||||
{...register('item_data.alias')}
|
||||
error={errors.item_data?.alias}
|
||||
/>
|
||||
<Controller
|
||||
name='item_data.parent'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<SelectParent
|
||||
items={manager.oss.blocks}
|
||||
value={field.value ? manager.oss.blockByID.get(field.value) ?? null : null}
|
||||
placeholder='Родительский блок'
|
||||
onChange={value => field.onChange(value ? value.id : null)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<TextArea
|
||||
id='operation_comment' //
|
||||
label='Описание'
|
||||
rows={3}
|
||||
disabled={!clone_source}
|
||||
{...register('item_data.description')}
|
||||
/>
|
||||
</div>
|
||||
</ModalForm>
|
||||
);
|
||||
}
|
|
@ -1,4 +1,11 @@
|
|||
import { type ICreateBlockDTO, type ICreateOperationDTO, type INodePosition, type IOssLayout } from '../backend/types';
|
||||
import {
|
||||
type ICreateBlockDTO,
|
||||
type ICreateSchemaDTO,
|
||||
type ICreateSynthesisDTO,
|
||||
type IImportSchemaDTO,
|
||||
type INodePosition,
|
||||
type IOssLayout
|
||||
} from '../backend/types';
|
||||
|
||||
import { type IOperationSchema } from './oss';
|
||||
import { type Position2D, type Rectangle2D } from './oss-layout';
|
||||
|
@ -24,11 +31,11 @@ export class LayoutManager {
|
|||
}
|
||||
|
||||
/** Calculate insert position for a new {@link IOperation} */
|
||||
newOperationPosition(data: ICreateOperationDTO): Rectangle2D {
|
||||
const result = { x: data.position_x, y: data.position_y, width: data.width, height: data.height };
|
||||
newOperationPosition(data: ICreateSchemaDTO | ICreateSynthesisDTO | IImportSchemaDTO): Rectangle2D {
|
||||
const result = { ...data.position };
|
||||
const parentNode = this.layout.find(pos => pos.nodeID === `b${data.item_data.parent}`) ?? null;
|
||||
const operations = this.layout.filter(pos => pos.nodeID.startsWith('o'));
|
||||
if (data.arguments.length !== 0) {
|
||||
if ('arguments' in data && data.arguments.length !== 0) {
|
||||
const pos = calculatePositionFromArgs(
|
||||
operations.filter(node => data.arguments.includes(Number(node.nodeID.slice(1))))
|
||||
);
|
||||
|
@ -59,20 +66,16 @@ export class LayoutManager {
|
|||
.filter(node => !!node);
|
||||
const parentNode = this.layout.find(pos => pos.nodeID === `b${data.item_data.parent}`) ?? null;
|
||||
|
||||
let result: Rectangle2D = { x: data.position_x, y: data.position_y, width: data.width, height: data.height };
|
||||
let result: Rectangle2D = { ...data.position };
|
||||
|
||||
if (block_nodes.length !== 0 || operation_nodes.length !== 0) {
|
||||
result = calculatePositionFromChildren(
|
||||
{ x: data.position_x, y: data.position_y, width: data.width, height: data.height },
|
||||
operation_nodes,
|
||||
block_nodes
|
||||
);
|
||||
result = calculatePositionFromChildren(data.position, operation_nodes, block_nodes);
|
||||
} else if (parentNode) {
|
||||
result = {
|
||||
x: parentNode.x + MIN_DISTANCE,
|
||||
y: parentNode.y + MIN_DISTANCE,
|
||||
width: data.width,
|
||||
height: data.height
|
||||
width: data.position.width,
|
||||
height: data.position.height
|
||||
};
|
||||
} else {
|
||||
result = this.calculatePositionForFreeBlock(result);
|
||||
|
|
|
@ -65,8 +65,10 @@ export function OssFlow() {
|
|||
|
||||
const showCreateOperation = useDialogsStore(state => state.showCreateOperation);
|
||||
const showCreateBlock = useDialogsStore(state => state.showCreateBlock);
|
||||
const showCreateSchema = useDialogsStore(state => state.showCreateSchema);
|
||||
const showDeleteOperation = useDialogsStore(state => state.showDeleteOperation);
|
||||
const showEditBlock = useDialogsStore(state => state.showEditBlock);
|
||||
const showImportSchema = useDialogsStore(state => state.showImportSchema);
|
||||
|
||||
const { isOpen: isContextMenuOpen, menuProps, openContextMenu, hideContextMenu } = useContextMenu();
|
||||
const { handleDragStart, handleDrag, handleDragStop } = useDragging({ hideContextMenu });
|
||||
|
@ -101,6 +103,28 @@ export function OssFlow() {
|
|||
});
|
||||
}
|
||||
|
||||
function handleCreateSchema() {
|
||||
const targetPosition = screenToFlowPosition({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
||||
showCreateSchema({
|
||||
manager: new LayoutManager(schema, getLayout()),
|
||||
defaultX: targetPosition.x,
|
||||
defaultY: targetPosition.y,
|
||||
initialParent: extractBlockParent(selectedItems),
|
||||
onCreate: resetView
|
||||
});
|
||||
}
|
||||
|
||||
function handleImportSchema() {
|
||||
const targetPosition = screenToFlowPosition({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
|
||||
showImportSchema({
|
||||
manager: new LayoutManager(schema, getLayout()),
|
||||
defaultX: targetPosition.x,
|
||||
defaultY: targetPosition.y,
|
||||
initialParent: extractBlockParent(selectedItems),
|
||||
onCreate: resetView
|
||||
});
|
||||
}
|
||||
|
||||
function handleDeleteSelected() {
|
||||
if (selected.length !== 1) {
|
||||
return;
|
||||
|
@ -194,8 +218,8 @@ export function OssFlow() {
|
|||
<ToolbarOssGraph
|
||||
className='absolute z-pop top-8 right-1/2 translate-x-1/2'
|
||||
onCreateBlock={handleCreateBlock}
|
||||
onCreateSchema={handleCreateOperation}
|
||||
onImportSchema={handleCreateOperation}
|
||||
onCreateSchema={handleCreateSchema}
|
||||
onImportSchema={handleImportSchema}
|
||||
onCreateSynthesis={handleCreateOperation}
|
||||
onDelete={handleDeleteSelected}
|
||||
onResetPositions={resetGraph}
|
||||
|
|
|
@ -21,7 +21,8 @@ import {
|
|||
IconNewItem,
|
||||
IconReset,
|
||||
IconSave,
|
||||
IconSettings
|
||||
IconSettings,
|
||||
IconSynthesis
|
||||
} from '@/components/icons';
|
||||
import { type Styling } from '@/components/props';
|
||||
import { cn } from '@/components/utils';
|
||||
|
@ -184,13 +185,13 @@ export function ToolbarOssGraph({
|
|||
<DropdownButton
|
||||
text='Импорт КС'
|
||||
titleHtml={prepareTooltip('Импорт концептуальной схемы', 'Alt + 3')}
|
||||
icon={<IconDownload size='1.25rem' className='text-constructive' />}
|
||||
icon={<IconDownload size='1.25rem' className='text-primary' />}
|
||||
onClick={onImportSchema}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='Синтез'
|
||||
titleHtml={prepareTooltip('Синтез концептуальных схем', 'Alt + 4')}
|
||||
icon={<IconConceptBlock size='1.25rem' className='text-primary' />}
|
||||
icon={<IconSynthesis size='1.25rem' className='text-primary' />}
|
||||
onClick={onCreateSynthesis}
|
||||
/>
|
||||
{user.is_staff ? (
|
||||
|
|
|
@ -7,10 +7,12 @@ import { type DlgEditEditorsProps } from '@/features/library/dialogs/dlg-edit-ed
|
|||
import { type DlgEditVersionsProps } from '@/features/library/dialogs/dlg-edit-versions/dlg-edit-versions';
|
||||
import { type DlgChangeInputSchemaProps } from '@/features/oss/dialogs/dlg-change-input-schema';
|
||||
import { type DlgCreateBlockProps } from '@/features/oss/dialogs/dlg-create-block/dlg-create-block';
|
||||
import { type DlgCreateOperationProps } from '@/features/oss/dialogs/dlg-create-operation/dlg-create-operation';
|
||||
import { type DlgCreateSchemaProps } from '@/features/oss/dialogs/dlg-create-schema';
|
||||
import { type DlgCreateSynthesisProps } from '@/features/oss/dialogs/dlg-create-synthesis/dlg-create-synthesis';
|
||||
import { type DlgDeleteOperationProps } from '@/features/oss/dialogs/dlg-delete-operation';
|
||||
import { type DlgEditBlockProps } from '@/features/oss/dialogs/dlg-edit-block';
|
||||
import { type DlgEditOperationProps } from '@/features/oss/dialogs/dlg-edit-operation/dlg-edit-operation';
|
||||
import { type DlgImportSchemaProps } from '@/features/oss/dialogs/dlg-import-schema';
|
||||
import { type DlgRelocateConstituentsProps } from '@/features/oss/dialogs/dlg-relocate-constituents';
|
||||
import { type DlgShowTermGraphProps } from '@/features/oss/dialogs/dlg-show-term-graph/dlg-show-term-graph';
|
||||
import { type DlgCreateCstProps } from '@/features/rsform/dialogs/dlg-create-cst/dlg-create-cst';
|
||||
|
@ -41,7 +43,7 @@ export const DialogType = {
|
|||
CREATE_BLOCK: 7,
|
||||
EDIT_BLOCK: 8,
|
||||
|
||||
CREATE_OPERATION: 9,
|
||||
CREATE_SYNTHESIS: 9,
|
||||
EDIT_OPERATION: 10,
|
||||
DELETE_OPERATION: 11,
|
||||
CHANGE_INPUT_SCHEMA: 12,
|
||||
|
@ -63,7 +65,9 @@ export const DialogType = {
|
|||
SHOW_AST: 25,
|
||||
SHOW_TYPE_GRAPH: 26,
|
||||
GRAPH_PARAMETERS: 27,
|
||||
SHOW_TERM_GRAPH: 28
|
||||
SHOW_TERM_GRAPH: 28,
|
||||
CREATE_SCHEMA: 29,
|
||||
IMPORT_SCHEMA: 30
|
||||
} as const;
|
||||
export type DialogType = (typeof DialogType)[keyof typeof DialogType];
|
||||
|
||||
|
@ -79,7 +83,7 @@ interface DialogsStore {
|
|||
showCstTemplate: (props: DlgCstTemplateProps) => void;
|
||||
showCreateCst: (props: DlgCreateCstProps) => void;
|
||||
showCreateBlock: (props: DlgCreateBlockProps) => void;
|
||||
showCreateOperation: (props: DlgCreateOperationProps) => void;
|
||||
showCreateOperation: (props: DlgCreateSynthesisProps) => void;
|
||||
showDeleteCst: (props: DlgDeleteCstProps) => void;
|
||||
showEditEditors: (props: DlgEditEditorsProps) => void;
|
||||
showEditOperation: (props: DlgEditOperationProps) => void;
|
||||
|
@ -104,6 +108,8 @@ interface DialogsStore {
|
|||
showSubstituteCst: (props: DlgSubstituteCstProps) => void;
|
||||
showUploadRSForm: (props: DlgUploadRSFormProps) => void;
|
||||
showEditCst: (props: DlgEditCstProps) => void;
|
||||
showCreateSchema: (props: DlgCreateSchemaProps) => void;
|
||||
showImportSchema: (props: DlgImportSchemaProps) => void;
|
||||
}
|
||||
|
||||
export const useDialogsStore = create<DialogsStore>()(set => ({
|
||||
|
@ -118,7 +124,7 @@ export const useDialogsStore = create<DialogsStore>()(set => ({
|
|||
|
||||
showCstTemplate: props => set({ active: DialogType.CONSTITUENTA_TEMPLATE, props: props }),
|
||||
showCreateCst: props => set({ active: DialogType.CREATE_CONSTITUENTA, props: props }),
|
||||
showCreateOperation: props => set({ active: DialogType.CREATE_OPERATION, props: props }),
|
||||
showCreateOperation: props => set({ active: DialogType.CREATE_SYNTHESIS, props: props }),
|
||||
showCreateBlock: props => set({ active: DialogType.CREATE_BLOCK, props: props }),
|
||||
showDeleteCst: props => set({ active: DialogType.DELETE_CONSTITUENTA, props: props }),
|
||||
showEditEditors: props => set({ active: DialogType.EDIT_EDITORS, props: props }),
|
||||
|
@ -143,5 +149,7 @@ export const useDialogsStore = create<DialogsStore>()(set => ({
|
|||
showQR: props => set({ active: DialogType.SHOW_QR_CODE, props: props }),
|
||||
showSubstituteCst: props => set({ active: DialogType.SUBSTITUTE_CONSTITUENTS, props: props }),
|
||||
showUploadRSForm: props => set({ active: DialogType.UPLOAD_RSFORM, props: props }),
|
||||
showEditCst: props => set({ active: DialogType.EDIT_CONSTITUENTA, props: props })
|
||||
showEditCst: props => set({ active: DialogType.EDIT_CONSTITUENTA, props: props }),
|
||||
showCreateSchema: props => set({ active: DialogType.CREATE_SCHEMA, props: props }),
|
||||
showImportSchema: props => set({ active: DialogType.IMPORT_SCHEMA, props: props })
|
||||
}));
|
||||
|
|
Loading…
Reference in New Issue
Block a user