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,
|
ArgumentSerializer,
|
||||||
BlockSerializer,
|
BlockSerializer,
|
||||||
CreateBlockSerializer,
|
CreateBlockSerializer,
|
||||||
CreateOperationSerializer,
|
CreateSchemaSerializer,
|
||||||
|
CreateSynthesisSerializer,
|
||||||
DeleteBlockSerializer,
|
DeleteBlockSerializer,
|
||||||
DeleteOperationSerializer,
|
DeleteOperationSerializer,
|
||||||
|
ImportSchemaSerializer,
|
||||||
MoveItemsSerializer,
|
MoveItemsSerializer,
|
||||||
OperationSchemaSerializer,
|
OperationSchemaSerializer,
|
||||||
OperationSerializer,
|
OperationSerializer,
|
||||||
|
|
|
@ -2,8 +2,16 @@
|
||||||
from rest_framework import serializers
|
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):
|
class NodeSerializer(serializers.Serializer):
|
||||||
''' Block position. '''
|
''' Oss node serializer. '''
|
||||||
nodeID = serializers.CharField()
|
nodeID = serializers.CharField()
|
||||||
x = serializers.FloatField()
|
x = serializers.FloatField()
|
||||||
y = serializers.FloatField()
|
y = serializers.FloatField()
|
||||||
|
|
|
@ -13,7 +13,7 @@ from apps.rsform.serializers import SubstitutionSerializerBase
|
||||||
from shared import messages as msg
|
from shared import messages as msg
|
||||||
|
|
||||||
from ..models import Argument, Block, Inheritance, Operation, OperationSchema, OperationType
|
from ..models import Argument, Block, Inheritance, Operation, OperationSchema, OperationType
|
||||||
from .basics import NodeSerializer, SubstitutionExSerializer
|
from .basics import NodeSerializer, PositionSerializer, SubstitutionExSerializer
|
||||||
|
|
||||||
|
|
||||||
class OperationSerializer(serializers.ModelSerializer):
|
class OperationSerializer(serializers.ModelSerializer):
|
||||||
|
@ -58,10 +58,7 @@ class CreateBlockSerializer(serializers.Serializer):
|
||||||
child=NodeSerializer()
|
child=NodeSerializer()
|
||||||
)
|
)
|
||||||
item_data = BlockCreateData()
|
item_data = BlockCreateData()
|
||||||
width = serializers.FloatField()
|
position = PositionSerializer()
|
||||||
height = serializers.FloatField()
|
|
||||||
position_x = serializers.FloatField()
|
|
||||||
position_y = serializers.FloatField()
|
|
||||||
children_operations = PKField(many=True, queryset=Operation.objects.all().only('oss_id'))
|
children_operations = PKField(many=True, queryset=Operation.objects.all().only('oss_id'))
|
||||||
children_blocks = PKField(many=True, queryset=Block.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
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class CreateOperationSerializer(serializers.Serializer):
|
class CreateOperationData(serializers.ModelSerializer):
|
||||||
''' Serializer: Operation creation. '''
|
''' Serializer: Operation creation data. '''
|
||||||
class CreateOperationData(serializers.ModelSerializer):
|
alias = serializers.CharField()
|
||||||
''' Serializer: Operation creation data. '''
|
|
||||||
alias = serializers.CharField()
|
|
||||||
operation_type = serializers.ChoiceField(OperationType.choices)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
''' serializer metadata. '''
|
''' serializer metadata. '''
|
||||||
model = Operation
|
model = Operation
|
||||||
fields = \
|
fields = 'alias', 'title', 'description', 'parent'
|
||||||
'alias', 'operation_type', 'title', \
|
|
||||||
'description', 'result', 'parent'
|
|
||||||
|
|
||||||
layout = serializers.ListField(
|
|
||||||
child=NodeSerializer()
|
class CreateSchemaSerializer(serializers.Serializer):
|
||||||
)
|
''' Serializer: Schema creation for new operation. '''
|
||||||
|
layout = serializers.ListField(child=NodeSerializer())
|
||||||
item_data = CreateOperationData()
|
item_data = CreateOperationData()
|
||||||
width = serializers.FloatField()
|
position = PositionSerializer()
|
||||||
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)
|
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
oss = cast(LibraryItem, self.context['oss'])
|
oss = cast(LibraryItem, self.context['oss'])
|
||||||
|
@ -225,14 +213,82 @@ class CreateOperationSerializer(serializers.Serializer):
|
||||||
raise serializers.ValidationError({
|
raise serializers.ValidationError({
|
||||||
'parent': msg.parentNotInOSS()
|
'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']:
|
for operation in attrs['arguments']:
|
||||||
if operation.oss_id != oss.pk:
|
if operation.oss_id != oss.pk:
|
||||||
raise serializers.ValidationError({
|
raise serializers.ValidationError({
|
||||||
'arguments': msg.operationNotInOSS()
|
'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
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,18 +3,18 @@ from rest_framework import serializers
|
||||||
|
|
||||||
from apps.library.serializers import LibraryItemSerializer
|
from apps.library.serializers import LibraryItemSerializer
|
||||||
|
|
||||||
from .data_access import BlockSerializer, OperationSchemaSerializer, OperationSerializer
|
from .data_access import OperationSchemaSerializer
|
||||||
|
|
||||||
|
|
||||||
class OperationCreatedResponse(serializers.Serializer):
|
class OperationCreatedResponse(serializers.Serializer):
|
||||||
''' Serializer: Create operation response. '''
|
''' Serializer: Create operation response. '''
|
||||||
new_operation = OperationSerializer()
|
new_operation = serializers.IntegerField()
|
||||||
oss = OperationSchemaSerializer()
|
oss = OperationSchemaSerializer()
|
||||||
|
|
||||||
|
|
||||||
class BlockCreatedResponse(serializers.Serializer):
|
class BlockCreatedResponse(serializers.Serializer):
|
||||||
''' Serializer: Create block response. '''
|
''' Serializer: Create block response. '''
|
||||||
new_block = BlockSerializer()
|
new_block = serializers.IntegerField()
|
||||||
oss = OperationSchemaSerializer()
|
oss = OperationSchemaSerializer()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -73,10 +73,12 @@ class TestOssBlocks(EndpointTester):
|
||||||
'description': 'Тест кириллицы',
|
'description': 'Тест кириллицы',
|
||||||
},
|
},
|
||||||
'layout': self.layout_data,
|
'layout': self.layout_data,
|
||||||
'position_x': 1337,
|
'position': {
|
||||||
'position_y': 1337,
|
'x': 1337,
|
||||||
'width': 0.42,
|
'y': 1337,
|
||||||
'height': 0.42,
|
'width': 0.42,
|
||||||
|
'height': 0.42
|
||||||
|
},
|
||||||
'children_operations': [],
|
'children_operations': [],
|
||||||
'children_blocks': []
|
'children_blocks': []
|
||||||
}
|
}
|
||||||
|
@ -86,14 +88,11 @@ class TestOssBlocks(EndpointTester):
|
||||||
self.assertEqual(len(response.data['oss']['blocks']), 3)
|
self.assertEqual(len(response.data['oss']['blocks']), 3)
|
||||||
new_block = response.data['new_block']
|
new_block = response.data['new_block']
|
||||||
layout = response.data['oss']['layout']
|
layout = response.data['oss']['layout']
|
||||||
item = [item for item in layout if item['nodeID'] == 'b' + str(new_block['id'])][0]
|
block_node = [item for item in layout if item['nodeID'] == 'b' + str(new_block)][0]
|
||||||
self.assertEqual(new_block['title'], data['item_data']['title'])
|
self.assertEqual(block_node['x'], data['position']['x'])
|
||||||
self.assertEqual(new_block['description'], data['item_data']['description'])
|
self.assertEqual(block_node['y'], data['position']['y'])
|
||||||
self.assertEqual(new_block['parent'], None)
|
self.assertEqual(block_node['width'], data['position']['width'])
|
||||||
self.assertEqual(item['x'], data['position_x'])
|
self.assertEqual(block_node['height'], data['position']['height'])
|
||||||
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.operation1.refresh_from_db()
|
||||||
|
|
||||||
self.executeForbidden(data=data, item=self.unowned_id)
|
self.executeForbidden(data=data, item=self.unowned_id)
|
||||||
|
@ -111,10 +110,12 @@ class TestOssBlocks(EndpointTester):
|
||||||
'parent': self.invalid_id
|
'parent': self.invalid_id
|
||||||
},
|
},
|
||||||
'layout': self.layout_data,
|
'layout': self.layout_data,
|
||||||
'position_x': 1337,
|
'position': {
|
||||||
'position_y': 1337,
|
'x': 1337,
|
||||||
'width': 0.42,
|
'y': 1337,
|
||||||
'height': 0.42,
|
'width': 0.42,
|
||||||
|
'height': 0.42
|
||||||
|
},
|
||||||
'children_operations': [],
|
'children_operations': [],
|
||||||
'children_blocks': []
|
'children_blocks': []
|
||||||
}
|
}
|
||||||
|
@ -126,7 +127,8 @@ class TestOssBlocks(EndpointTester):
|
||||||
data['item_data']['parent'] = self.block1.pk
|
data['item_data']['parent'] = self.block1.pk
|
||||||
response = self.executeCreated(data=data)
|
response = self.executeCreated(data=data)
|
||||||
new_block = response.data['new_block']
|
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')
|
@decl_endpoint('/api/oss/{item}/create-block', method='post')
|
||||||
|
@ -138,10 +140,12 @@ class TestOssBlocks(EndpointTester):
|
||||||
'description': 'Тест кириллицы',
|
'description': 'Тест кириллицы',
|
||||||
},
|
},
|
||||||
'layout': self.layout_data,
|
'layout': self.layout_data,
|
||||||
'position_x': 1337,
|
'position': {
|
||||||
'position_y': 1337,
|
'x': 1337,
|
||||||
'width': 0.42,
|
'y': 1337,
|
||||||
'height': 0.42,
|
'width': 0.42,
|
||||||
|
'height': 0.42
|
||||||
|
},
|
||||||
'children_operations': [self.invalid_id],
|
'children_operations': [self.invalid_id],
|
||||||
'children_blocks': []
|
'children_blocks': []
|
||||||
}
|
}
|
||||||
|
@ -162,8 +166,8 @@ class TestOssBlocks(EndpointTester):
|
||||||
new_block = response.data['new_block']
|
new_block = response.data['new_block']
|
||||||
self.operation1.refresh_from_db()
|
self.operation1.refresh_from_db()
|
||||||
self.block1.refresh_from_db()
|
self.block1.refresh_from_db()
|
||||||
self.assertEqual(self.operation1.parent.pk, new_block['id'])
|
self.assertEqual(self.operation1.parent.pk, new_block)
|
||||||
self.assertEqual(self.block1.parent.pk, new_block['id'])
|
self.assertEqual(self.block1.parent.pk, new_block)
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/oss/{item}/create-block', method='post')
|
@decl_endpoint('/api/oss/{item}/create-block', method='post')
|
||||||
|
@ -176,10 +180,12 @@ class TestOssBlocks(EndpointTester):
|
||||||
'parent': self.block2.pk
|
'parent': self.block2.pk
|
||||||
},
|
},
|
||||||
'layout': self.layout_data,
|
'layout': self.layout_data,
|
||||||
'position_x': 1337,
|
'position': {
|
||||||
'position_y': 1337,
|
'x': 1337,
|
||||||
'width': 0.42,
|
'y': 1337,
|
||||||
'height': 0.42,
|
'width': 0.42,
|
||||||
|
'height': 0.42
|
||||||
|
},
|
||||||
'children_operations': [],
|
'children_operations': [],
|
||||||
'children_blocks': [self.block1.pk]
|
'children_blocks': [self.block1.pk]
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,9 +70,10 @@ class TestOssOperations(EndpointTester):
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/oss/{item}/create-operation', method='post')
|
@decl_endpoint('/api/oss/{item}/create-schema', method='post')
|
||||||
def test_create_operation(self):
|
def test_create_schema(self):
|
||||||
self.populateData()
|
self.populateData()
|
||||||
|
Editor.add(self.owned.model.pk, self.user2.pk)
|
||||||
self.executeBadData(item=self.owned_id)
|
self.executeBadData(item=self.owned_id)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
@ -80,47 +81,50 @@ class TestOssOperations(EndpointTester):
|
||||||
'alias': 'Test3',
|
'alias': 'Test3',
|
||||||
'title': 'Test title',
|
'title': 'Test title',
|
||||||
'description': 'Тест кириллицы',
|
'description': 'Тест кириллицы',
|
||||||
|
'parent': None
|
||||||
},
|
},
|
||||||
'layout': self.layout_data,
|
'layout': self.layout_data,
|
||||||
'position_x': 1,
|
'position': {
|
||||||
'position_y': 1,
|
'x': 1,
|
||||||
'width': 500,
|
'y': 1,
|
||||||
'height': 50
|
'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)
|
self.executeNotFound(data=data, item=self.invalid_id)
|
||||||
|
|
||||||
response = self.executeCreated(data=data, item=self.owned_id)
|
response = self.executeCreated(data=data, item=self.owned_id)
|
||||||
self.assertEqual(len(response.data['oss']['operations']), 4)
|
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']
|
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['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['title'], data['item_data']['title'])
|
||||||
self.assertEqual(new_operation['description'], data['item_data']['description'])
|
self.assertEqual(new_operation['description'], data['item_data']['description'])
|
||||||
self.assertEqual(new_operation['result'], None)
|
|
||||||
self.assertEqual(new_operation['parent'], None)
|
self.assertEqual(new_operation['parent'], None)
|
||||||
self.assertEqual(item['x'], data['position_x'])
|
self.assertNotEqual(new_operation['result'], None)
|
||||||
self.assertEqual(item['y'], data['position_y'])
|
self.assertEqual(operation_node['x'], data['position']['x'])
|
||||||
self.assertEqual(item['width'], data['width'])
|
self.assertEqual(operation_node['y'], data['position']['y'])
|
||||||
self.assertEqual(item['height'], data['height'])
|
self.assertEqual(operation_node['width'], data['position']['width'])
|
||||||
self.operation1.refresh_from_db()
|
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.executeForbidden(data=data, item=self.unowned_id)
|
||||||
self.toggle_admin(True)
|
self.toggle_admin(True)
|
||||||
self.executeCreated(data=data, item=self.unowned_id)
|
self.executeCreated(data=data, item=self.unowned_id)
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/oss/{item}/create-operation', method='post')
|
@decl_endpoint('/api/oss/{item}/create-schema', method='post')
|
||||||
def test_create_operation_parent(self):
|
def test_create_schema_parent(self):
|
||||||
self.populateData()
|
self.populateData()
|
||||||
data = {
|
data = {
|
||||||
'item_data': {
|
'item_data': {
|
||||||
|
@ -128,14 +132,15 @@ class TestOssOperations(EndpointTester):
|
||||||
'alias': 'Test3',
|
'alias': 'Test3',
|
||||||
'title': 'Test title',
|
'title': 'Test title',
|
||||||
'description': '',
|
'description': '',
|
||||||
'operation_type': OperationType.INPUT
|
|
||||||
|
|
||||||
},
|
},
|
||||||
'layout': self.layout_data,
|
'layout': self.layout_data,
|
||||||
'position_x': 1,
|
'position': {
|
||||||
'position_y': 1,
|
'x': 1,
|
||||||
'width': 500,
|
'y': 1,
|
||||||
'height': 50
|
'width': 500,
|
||||||
|
'height': 50
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
self.executeBadData(data=data, item=self.owned_id)
|
self.executeBadData(data=data, item=self.owned_id)
|
||||||
|
@ -147,90 +152,40 @@ class TestOssOperations(EndpointTester):
|
||||||
block_owned = self.owned.create_block(title='TestBlock2')
|
block_owned = self.owned.create_block(title='TestBlock2')
|
||||||
data['item_data']['parent'] = block_owned.id
|
data['item_data']['parent'] = block_owned.id
|
||||||
response = self.executeCreated(data=data, item=self.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)
|
self.assertEqual(len(response.data['oss']['operations']), 4)
|
||||||
new_operation = response.data['new_operation']
|
|
||||||
self.assertEqual(new_operation['parent'], block_owned.id)
|
self.assertEqual(new_operation['parent'], block_owned.id)
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/oss/{item}/create-operation', method='post')
|
@decl_endpoint('/api/oss/{item}/create-synthesis', method='post')
|
||||||
def test_create_operation_arguments(self):
|
def test_create_synthesis(self):
|
||||||
self.populateData()
|
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 = {
|
data = {
|
||||||
'item_data': {
|
'item_data': {
|
||||||
'alias': 'Test4',
|
'alias': 'Test4',
|
||||||
'title': 'Test title',
|
'title': 'Test title',
|
||||||
'description': 'Comment',
|
'description': '',
|
||||||
'operation_type': OperationType.INPUT,
|
'parent': None
|
||||||
'result': self.ks1.model.pk
|
|
||||||
},
|
},
|
||||||
'create_schema': True,
|
|
||||||
'layout': self.layout_data,
|
'layout': self.layout_data,
|
||||||
'position_x': 1,
|
'position': {
|
||||||
'position_y': 1,
|
'x': 1,
|
||||||
'width': 500,
|
'y': 1,
|
||||||
'height': 50
|
'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)
|
response = self.executeCreated(data=data, item=self.owned_id)
|
||||||
self.owned.refresh_from_db()
|
self.owned.refresh_from_db()
|
||||||
new_operation = response.data['new_operation']
|
new_operation_id = response.data['new_operation']
|
||||||
schema = LibraryItem.objects.get(pk=new_operation['result'])
|
new_operation = next(op for op in response.data['oss']['operations'] if op['id'] == new_operation_id)
|
||||||
self.assertEqual(schema.alias, data['item_data']['alias'])
|
arguments = self.owned.arguments()
|
||||||
self.assertEqual(schema.title, data['item_data']['title'])
|
self.assertTrue(arguments.filter(operation__id=new_operation_id, argument=self.operation1))
|
||||||
self.assertEqual(schema.description, data['item_data']['description'])
|
self.assertTrue(arguments.filter(operation__id=new_operation_id, argument=self.operation3))
|
||||||
self.assertEqual(schema.visible, False)
|
self.assertNotEqual(new_operation['result'], None)
|
||||||
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())
|
|
||||||
|
|
||||||
|
|
||||||
@decl_endpoint('/api/oss/{item}/delete-operation', method='patch')
|
@decl_endpoint('/api/oss/{item}/delete-operation', method='patch')
|
||||||
|
@ -497,3 +452,141 @@ class TestOssOperations(EndpointTester):
|
||||||
self.assertEqual(len(items), 1)
|
self.assertEqual(len(items), 1)
|
||||||
self.assertEqual(items[0].alias, 'X1')
|
self.assertEqual(items[0].alias, 'X1')
|
||||||
self.assertEqual(items[0].term_resolved, self.ks2X1.term_resolved)
|
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. '''
|
''' Endpoints for OSS. '''
|
||||||
|
from copy import deepcopy
|
||||||
from typing import Optional, cast
|
from typing import Optional, cast
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Q
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||||
from rest_framework import generics, serializers
|
from rest_framework import generics, serializers
|
||||||
|
@ -23,6 +23,28 @@ from .. import models as m
|
||||||
from .. import serializers as s
|
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(tags=['OSS'])
|
||||||
@extend_schema_view()
|
@extend_schema_view()
|
||||||
class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.RetrieveAPIView):
|
class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.RetrieveAPIView):
|
||||||
|
@ -41,7 +63,9 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
||||||
'update_block',
|
'update_block',
|
||||||
'delete_block',
|
'delete_block',
|
||||||
'move_items',
|
'move_items',
|
||||||
'create_operation',
|
'create_schema',
|
||||||
|
'import_schema',
|
||||||
|
'create_synthesis',
|
||||||
'update_operation',
|
'update_operation',
|
||||||
'delete_operation',
|
'delete_operation',
|
||||||
'create_input',
|
'create_input',
|
||||||
|
@ -116,16 +140,17 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
||||||
|
|
||||||
oss = m.OperationSchema(self.get_object())
|
oss = m.OperationSchema(self.get_object())
|
||||||
layout = serializer.validated_data['layout']
|
layout = serializer.validated_data['layout']
|
||||||
|
position = serializer.validated_data['position']
|
||||||
children_blocks: list[m.Block] = serializer.validated_data['children_blocks']
|
children_blocks: list[m.Block] = serializer.validated_data['children_blocks']
|
||||||
children_operations: list[m.Operation] = serializer.validated_data['children_operations']
|
children_operations: list[m.Operation] = serializer.validated_data['children_operations']
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
new_block = oss.create_block(**serializer.validated_data['item_data'])
|
new_block = oss.create_block(**serializer.validated_data['item_data'])
|
||||||
layout.append({
|
layout.append({
|
||||||
'nodeID': 'b' + str(new_block.pk),
|
'nodeID': 'b' + str(new_block.pk),
|
||||||
'x': serializer.validated_data['position_x'],
|
'x': position['x'],
|
||||||
'y': serializer.validated_data['position_y'],
|
'y': position['y'],
|
||||||
'width': serializer.validated_data['width'],
|
'width': position['width'],
|
||||||
'height': serializer.validated_data['height'],
|
'height': position['height'],
|
||||||
})
|
})
|
||||||
oss.update_layout(layout)
|
oss.update_layout(layout)
|
||||||
if len(children_blocks) > 0:
|
if len(children_blocks) > 0:
|
||||||
|
@ -140,7 +165,7 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
||||||
return Response(
|
return Response(
|
||||||
status=c.HTTP_201_CREATED,
|
status=c.HTTP_201_CREATED,
|
||||||
data={
|
data={
|
||||||
'new_block': s.BlockSerializer(new_block).data,
|
'new_block': new_block.pk,
|
||||||
'oss': s.OperationSchemaSerializer(oss.model).data
|
'oss': s.OperationSchemaSerializer(oss.model).data
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -251,9 +276,9 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
||||||
)
|
)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
summary='create operation',
|
summary='create empty conceptual schema',
|
||||||
tags=['OSS'],
|
tags=['OSS'],
|
||||||
request=s.CreateOperationSerializer(),
|
request=s.CreateSchemaSerializer(),
|
||||||
responses={
|
responses={
|
||||||
c.HTTP_201_CREATED: s.OperationCreatedResponse,
|
c.HTTP_201_CREATED: s.OperationCreatedResponse,
|
||||||
c.HTTP_400_BAD_REQUEST: None,
|
c.HTTP_400_BAD_REQUEST: None,
|
||||||
|
@ -261,10 +286,10 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
||||||
c.HTTP_404_NOT_FOUND: None
|
c.HTTP_404_NOT_FOUND: None
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@action(detail=True, methods=['post'], url_path='create-operation')
|
@action(detail=True, methods=['post'], url_path='create-schema')
|
||||||
def create_operation(self, request: Request, pk) -> HttpResponse:
|
def create_schema(self, request: Request, pk) -> HttpResponse:
|
||||||
''' Create Operation. '''
|
''' Create schema. '''
|
||||||
serializer = s.CreateOperationSerializer(
|
serializer = s.CreateSchemaSerializer(
|
||||||
data=request.data,
|
data=request.data,
|
||||||
context={'oss': self.get_object()}
|
context={'oss': self.get_object()}
|
||||||
)
|
)
|
||||||
|
@ -272,43 +297,124 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
||||||
|
|
||||||
oss = m.OperationSchema(self.get_object())
|
oss = m.OperationSchema(self.get_object())
|
||||||
layout = serializer.validated_data['layout']
|
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():
|
with transaction.atomic():
|
||||||
new_operation = oss.create_operation(**serializer.validated_data['item_data'])
|
new_operation = oss.create_operation(**serializer.validated_data['item_data'])
|
||||||
layout.append({
|
layout.append({
|
||||||
'nodeID': 'o' + str(new_operation.pk),
|
'nodeID': 'o' + str(new_operation.pk),
|
||||||
'x': serializer.validated_data['position_x'],
|
'x': position['x'],
|
||||||
'y': serializer.validated_data['position_y'],
|
'y': position['y'],
|
||||||
'width': serializer.validated_data['width'],
|
'width': position['width'],
|
||||||
'height': serializer.validated_data['height']
|
'height': position['height']
|
||||||
})
|
})
|
||||||
oss.update_layout(layout)
|
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(
|
return Response(
|
||||||
status=c.HTTP_201_CREATED,
|
status=c.HTTP_201_CREATED,
|
||||||
data={
|
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
|
'oss': s.OperationSchemaSerializer(oss.model).data
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,9 +20,9 @@ const DlgCloneLibraryItem = React.lazy(() =>
|
||||||
const DlgCreateCst = React.lazy(() =>
|
const DlgCreateCst = React.lazy(() =>
|
||||||
import('@/features/rsform/dialogs/dlg-create-cst').then(module => ({ default: module.DlgCreateCst }))
|
import('@/features/rsform/dialogs/dlg-create-cst').then(module => ({ default: module.DlgCreateCst }))
|
||||||
);
|
);
|
||||||
const DlgCreateOperation = React.lazy(() =>
|
const DlgCreateSynthesis = React.lazy(() =>
|
||||||
import('@/features/oss/dialogs/dlg-create-operation').then(module => ({
|
import('@/features/oss/dialogs/dlg-create-synthesis').then(module => ({
|
||||||
default: module.DlgCreateOperation
|
default: module.DlgCreateSynthesis
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
const DlgCreateVersion = React.lazy(() =>
|
const DlgCreateVersion = React.lazy(() =>
|
||||||
|
@ -134,6 +134,12 @@ const DlgEditCst = React.lazy(() =>
|
||||||
const DlgShowTermGraph = React.lazy(() =>
|
const DlgShowTermGraph = React.lazy(() =>
|
||||||
import('@/features/oss/dialogs/dlg-show-term-graph').then(module => ({ default: module.DlgShowTermGraph }))
|
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 = () => {
|
export const GlobalDialogs = () => {
|
||||||
const active = useDialogsStore(state => state.active);
|
const active = useDialogsStore(state => state.active);
|
||||||
|
@ -146,8 +152,8 @@ export const GlobalDialogs = () => {
|
||||||
return <DlgCstTemplate />;
|
return <DlgCstTemplate />;
|
||||||
case DialogType.CREATE_CONSTITUENTA:
|
case DialogType.CREATE_CONSTITUENTA:
|
||||||
return <DlgCreateCst />;
|
return <DlgCreateCst />;
|
||||||
case DialogType.CREATE_OPERATION:
|
case DialogType.CREATE_SYNTHESIS:
|
||||||
return <DlgCreateOperation />;
|
return <DlgCreateSynthesis />;
|
||||||
case DialogType.CREATE_BLOCK:
|
case DialogType.CREATE_BLOCK:
|
||||||
return <DlgCreateBlock />;
|
return <DlgCreateBlock />;
|
||||||
case DialogType.EDIT_BLOCK:
|
case DialogType.EDIT_BLOCK:
|
||||||
|
@ -198,5 +204,9 @@ export const GlobalDialogs = () => {
|
||||||
return <DlgEditCst />;
|
return <DlgEditCst />;
|
||||||
case DialogType.SHOW_TERM_GRAPH:
|
case DialogType.SHOW_TERM_GRAPH:
|
||||||
return <DlgShowTermGraph />;
|
return <DlgShowTermGraph />;
|
||||||
|
case DialogType.CREATE_SCHEMA:
|
||||||
|
return <DlgCreateSchema />;
|
||||||
|
case DialogType.IMPORT_SCHEMA:
|
||||||
|
return <DlgImportSchema />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,9 +8,11 @@ import {
|
||||||
type IBlockCreatedResponse,
|
type IBlockCreatedResponse,
|
||||||
type IConstituentaReference,
|
type IConstituentaReference,
|
||||||
type ICreateBlockDTO,
|
type ICreateBlockDTO,
|
||||||
type ICreateOperationDTO,
|
type ICreateSchemaDTO,
|
||||||
|
type ICreateSynthesisDTO,
|
||||||
type IDeleteBlockDTO,
|
type IDeleteBlockDTO,
|
||||||
type IDeleteOperationDTO,
|
type IDeleteOperationDTO,
|
||||||
|
type IImportSchemaDTO,
|
||||||
type IInputCreatedResponse,
|
type IInputCreatedResponse,
|
||||||
type IMoveItemsDTO,
|
type IMoveItemsDTO,
|
||||||
type IOperationCreatedResponse,
|
type IOperationCreatedResponse,
|
||||||
|
@ -83,13 +85,40 @@ export const ossApi = {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
createOperation: ({ itemID, data }: { itemID: number; data: ICreateOperationDTO }) =>
|
createSchema: ({ itemID, data }: { itemID: number; data: ICreateSchemaDTO }) =>
|
||||||
axiosPost<ICreateOperationDTO, IOperationCreatedResponse>({
|
axiosPost<ICreateSchemaDTO, IOperationCreatedResponse>({
|
||||||
schema: schemaOperationCreatedResponse,
|
schema: schemaOperationCreatedResponse,
|
||||||
endpoint: `/api/oss/${itemID}/create-operation`,
|
endpoint: `/api/oss/${itemID}/create-schema`,
|
||||||
request: {
|
request: {
|
||||||
data: data,
|
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 }) =>
|
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>;
|
export type IMoveItemsDTO = z.infer<typeof schemaMoveItems>;
|
||||||
|
|
||||||
/** Represents {@link IOperation} data, used in Create action. */
|
/** 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}. */
|
/** Represents data response when creating {@link IOperation}. */
|
||||||
export type IOperationCreatedResponse = z.infer<typeof schemaOperationCreatedResponse>;
|
export type IOperationCreatedResponse = z.infer<typeof schemaOperationCreatedResponse>;
|
||||||
|
@ -90,6 +92,13 @@ export const schemaOperation = z.strictObject({
|
||||||
result: z.number().nullable()
|
result: z.number().nullable()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const schemaOperationData = schemaOperation.pick({
|
||||||
|
alias: true,
|
||||||
|
title: true,
|
||||||
|
description: true,
|
||||||
|
parent: true
|
||||||
|
});
|
||||||
|
|
||||||
export const schemaBlock = z.strictObject({
|
export const schemaBlock = z.strictObject({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
oss: z.number(),
|
oss: z.number(),
|
||||||
|
@ -98,6 +107,13 @@ export const schemaBlock = z.strictObject({
|
||||||
parent: z.number().nullable()
|
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({
|
export const schemaCstSubstituteInfo = schemaSubstituteConstituents.extend({
|
||||||
operation: z.number(),
|
operation: z.number(),
|
||||||
original_alias: z.string(),
|
original_alias: z.string(),
|
||||||
|
@ -106,12 +122,8 @@ export const schemaCstSubstituteInfo = schemaSubstituteConstituents.extend({
|
||||||
substitution_term: z.string()
|
substitution_term: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaNodePosition = z.strictObject({
|
export const schemaNodePosition = schemaPosition.extend({
|
||||||
nodeID: z.string(),
|
nodeID: z.string()
|
||||||
x: z.number(),
|
|
||||||
y: z.number(),
|
|
||||||
width: z.number(),
|
|
||||||
height: z.number()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaOssLayout = z.array(schemaNodePosition);
|
export const schemaOssLayout = z.array(schemaNodePosition);
|
||||||
|
@ -137,16 +149,13 @@ export const schemaCreateBlock = z.strictObject({
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
parent: z.number().nullable()
|
parent: z.number().nullable()
|
||||||
}),
|
}),
|
||||||
position_x: z.number(),
|
position: schemaPosition,
|
||||||
position_y: z.number(),
|
|
||||||
width: z.number(),
|
|
||||||
height: z.number(),
|
|
||||||
children_operations: z.array(z.number()),
|
children_operations: z.array(z.number()),
|
||||||
children_blocks: z.array(z.number())
|
children_blocks: z.array(z.number())
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaBlockCreatedResponse = z.strictObject({
|
export const schemaBlockCreatedResponse = z.strictObject({
|
||||||
new_block: schemaBlock,
|
new_block: z.number(),
|
||||||
oss: schemaOperationSchema
|
oss: schemaOperationSchema
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -165,26 +174,35 @@ export const schemaDeleteBlock = z.strictObject({
|
||||||
layout: schemaOssLayout
|
layout: schemaOssLayout
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaCreateOperation = z.strictObject({
|
export const schemaCreateSchema = z.strictObject({
|
||||||
layout: schemaOssLayout,
|
layout: schemaOssLayout,
|
||||||
item_data: z.strictObject({
|
item_data: schemaOperationData,
|
||||||
alias: z.string().nonempty(),
|
position: schemaPosition
|
||||||
operation_type: schemaOperationType,
|
});
|
||||||
title: z.string(),
|
|
||||||
description: z.string(),
|
export const schemaCreateSynthesis = z.strictObject({
|
||||||
parent: z.number().nullable(),
|
layout: schemaOssLayout,
|
||||||
result: z.number().nullable()
|
item_data: schemaOperationData,
|
||||||
}),
|
position: schemaPosition,
|
||||||
position_x: z.number(),
|
|
||||||
position_y: z.number(),
|
|
||||||
width: z.number(),
|
|
||||||
height: z.number(),
|
|
||||||
arguments: z.array(z.number()),
|
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({
|
export const schemaOperationCreatedResponse = z.strictObject({
|
||||||
new_operation: schemaOperation,
|
new_operation: z.number(),
|
||||||
oss: schemaOperationSchema
|
oss: schemaOperationSchema
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,14 @@ import { useUpdateTimestamp } from '@/features/library/backend/use-update-timest
|
||||||
import { KEYS } from '@/backend/configuration';
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
import { ossApi } from './api';
|
import { ossApi } from './api';
|
||||||
import { type ICreateOperationDTO } from './types';
|
import { type ICreateSchemaDTO } from './types';
|
||||||
|
|
||||||
export const useCreateOperation = () => {
|
export const useCreateSchema = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
const { updateTimestamp } = useUpdateTimestamp();
|
const { updateTimestamp } = useUpdateTimestamp();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-operation'],
|
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'create-schema'],
|
||||||
mutationFn: ossApi.createOperation,
|
mutationFn: ossApi.createSchema,
|
||||||
onSuccess: response => {
|
onSuccess: response => {
|
||||||
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
|
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
|
||||||
updateTimestamp(response.oss.id);
|
updateTimestamp(response.oss.id);
|
||||||
|
@ -20,6 +20,6 @@ export const useCreateOperation = () => {
|
||||||
onError: () => client.invalidateQueries()
|
onError: () => client.invalidateQueries()
|
||||||
});
|
});
|
||||||
return {
|
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: '',
|
description: '',
|
||||||
parent: initialParent
|
parent: initialParent
|
||||||
},
|
},
|
||||||
position_x: defaultX,
|
position: {
|
||||||
position_y: defaultY,
|
x: defaultX,
|
||||||
width: BLOCK_NODE_MIN_WIDTH,
|
y: defaultY,
|
||||||
height: BLOCK_NODE_MIN_HEIGHT,
|
width: BLOCK_NODE_MIN_WIDTH,
|
||||||
|
height: BLOCK_NODE_MIN_HEIGHT
|
||||||
|
},
|
||||||
children_blocks: initialChildren.filter(item => item.nodeType === NodeType.BLOCK).map(item => item.id),
|
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),
|
children_operations: initialChildren.filter(item => item.nodeType === NodeType.OPERATION).map(item => item.id),
|
||||||
layout: manager.layout
|
layout: manager.layout
|
||||||
|
@ -66,13 +68,9 @@ export function DlgCreateBlock() {
|
||||||
const isValid = !!title && !manager.oss.blocks.some(block => block.title === title);
|
const isValid = !!title && !manager.oss.blocks.some(block => block.title === title);
|
||||||
|
|
||||||
function onSubmit(data: ICreateBlockDTO) {
|
function onSubmit(data: ICreateBlockDTO) {
|
||||||
const rectangle = manager.newBlockPosition(data);
|
data.position = manager.newBlockPosition(data);
|
||||||
data.position_x = rectangle.x;
|
|
||||||
data.position_y = rectangle.y;
|
|
||||||
data.width = rectangle.width;
|
|
||||||
data.height = rectangle.height;
|
|
||||||
data.layout = manager.layout;
|
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 (
|
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 { Label, TextArea, TextInput } from '@/components/input';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
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 { PickMultiOperation } from '../../components/pick-multi-operation';
|
||||||
import { SelectParent } from '../../components/select-parent';
|
import { SelectParent } from '../../components/select-parent';
|
||||||
|
|
||||||
import { type DlgCreateOperationProps } from './dlg-create-operation';
|
import { type DlgCreateSynthesisProps } from './dlg-create-synthesis';
|
||||||
|
|
||||||
export function TabSynthesisOperation() {
|
export function TabArguments() {
|
||||||
const { manager } = useDialogsStore(state => state.props as DlgCreateOperationProps);
|
const { manager } = useDialogsStore(state => state.props as DlgCreateSynthesisProps);
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
control,
|
control,
|
||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useFormContext<ICreateOperationDTO>();
|
} = useFormContext<ICreateSynthesisDTO>();
|
||||||
const inputs = useWatch({ control, name: 'arguments' });
|
const inputs = useWatch({ control, name: 'arguments' });
|
||||||
|
|
||||||
return (
|
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 { TabArguments } from './tab-arguments';
|
||||||
import { TabOperation } from './tab-operation';
|
import { TabOperation } from './tab-operation';
|
||||||
import { TabSynthesis } from './tab-synthesis';
|
import { TabSubstitutions } from './tab-substitutions';
|
||||||
|
|
||||||
export interface DlgEditOperationProps {
|
export interface DlgEditOperationProps {
|
||||||
manager: LayoutManager;
|
manager: LayoutManager;
|
||||||
|
@ -28,7 +28,7 @@ export interface DlgEditOperationProps {
|
||||||
export const TabID = {
|
export const TabID = {
|
||||||
CARD: 0,
|
CARD: 0,
|
||||||
ARGUMENTS: 1,
|
ARGUMENTS: 1,
|
||||||
SUBSTITUTION: 2
|
SUBSTITUTIONS: 2
|
||||||
} as const;
|
} as const;
|
||||||
export type TabID = (typeof TabID)[keyof typeof TabID];
|
export type TabID = (typeof TabID)[keyof typeof TabID];
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ export function DlgEditOperation() {
|
||||||
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
|
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
|
||||||
className='w-160 px-6 h-128'
|
className='w-160 px-6 h-128'
|
||||||
helpTopic={HelpTopic.UI_SUBSTITUTIONS}
|
helpTopic={HelpTopic.UI_SUBSTITUTIONS}
|
||||||
hideHelpWhen={() => activeTab !== TabID.SUBSTITUTION}
|
hideHelpWhen={() => activeTab !== TabID.SUBSTITUTIONS}
|
||||||
>
|
>
|
||||||
<Tabs className='grid' selectedIndex={activeTab} onSelect={index => setActiveTab(index as TabID)}>
|
<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'>
|
<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 ? (
|
{target.operation_type === OperationType.SYNTHESIS ? (
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<Suspense fallback={<Loader />}>
|
<Suspense fallback={<Loader />}>
|
||||||
<TabSynthesis />
|
<TabSubstitutions />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { SubstitutionValidator } from '../../models/oss-api';
|
||||||
|
|
||||||
import { type DlgEditOperationProps } from './dlg-edit-operation';
|
import { type DlgEditOperationProps } from './dlg-edit-operation';
|
||||||
|
|
||||||
export function TabSynthesis() {
|
export function TabSubstitutions() {
|
||||||
const { manager } = useDialogsStore(state => state.props as DlgEditOperationProps);
|
const { manager } = useDialogsStore(state => state.props as DlgEditOperationProps);
|
||||||
const { control } = useFormContext<IUpdateOperationDTO>();
|
const { control } = useFormContext<IUpdateOperationDTO>();
|
||||||
const inputs = useWatch({ control, name: 'arguments' });
|
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 IOperationSchema } from './oss';
|
||||||
import { type Position2D, type Rectangle2D } from './oss-layout';
|
import { type Position2D, type Rectangle2D } from './oss-layout';
|
||||||
|
@ -24,11 +31,11 @@ export class LayoutManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Calculate insert position for a new {@link IOperation} */
|
/** Calculate insert position for a new {@link IOperation} */
|
||||||
newOperationPosition(data: ICreateOperationDTO): Rectangle2D {
|
newOperationPosition(data: ICreateSchemaDTO | ICreateSynthesisDTO | IImportSchemaDTO): Rectangle2D {
|
||||||
const result = { x: data.position_x, y: data.position_y, width: data.width, height: data.height };
|
const result = { ...data.position };
|
||||||
const parentNode = this.layout.find(pos => pos.nodeID === `b${data.item_data.parent}`) ?? null;
|
const parentNode = this.layout.find(pos => pos.nodeID === `b${data.item_data.parent}`) ?? null;
|
||||||
const operations = this.layout.filter(pos => pos.nodeID.startsWith('o'));
|
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(
|
const pos = calculatePositionFromArgs(
|
||||||
operations.filter(node => data.arguments.includes(Number(node.nodeID.slice(1))))
|
operations.filter(node => data.arguments.includes(Number(node.nodeID.slice(1))))
|
||||||
);
|
);
|
||||||
|
@ -59,20 +66,16 @@ export class LayoutManager {
|
||||||
.filter(node => !!node);
|
.filter(node => !!node);
|
||||||
const parentNode = this.layout.find(pos => pos.nodeID === `b${data.item_data.parent}`) ?? null;
|
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) {
|
if (block_nodes.length !== 0 || operation_nodes.length !== 0) {
|
||||||
result = calculatePositionFromChildren(
|
result = calculatePositionFromChildren(data.position, operation_nodes, block_nodes);
|
||||||
{ x: data.position_x, y: data.position_y, width: data.width, height: data.height },
|
|
||||||
operation_nodes,
|
|
||||||
block_nodes
|
|
||||||
);
|
|
||||||
} else if (parentNode) {
|
} else if (parentNode) {
|
||||||
result = {
|
result = {
|
||||||
x: parentNode.x + MIN_DISTANCE,
|
x: parentNode.x + MIN_DISTANCE,
|
||||||
y: parentNode.y + MIN_DISTANCE,
|
y: parentNode.y + MIN_DISTANCE,
|
||||||
width: data.width,
|
width: data.position.width,
|
||||||
height: data.height
|
height: data.position.height
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
result = this.calculatePositionForFreeBlock(result);
|
result = this.calculatePositionForFreeBlock(result);
|
||||||
|
|
|
@ -65,8 +65,10 @@ export function OssFlow() {
|
||||||
|
|
||||||
const showCreateOperation = useDialogsStore(state => state.showCreateOperation);
|
const showCreateOperation = useDialogsStore(state => state.showCreateOperation);
|
||||||
const showCreateBlock = useDialogsStore(state => state.showCreateBlock);
|
const showCreateBlock = useDialogsStore(state => state.showCreateBlock);
|
||||||
|
const showCreateSchema = useDialogsStore(state => state.showCreateSchema);
|
||||||
const showDeleteOperation = useDialogsStore(state => state.showDeleteOperation);
|
const showDeleteOperation = useDialogsStore(state => state.showDeleteOperation);
|
||||||
const showEditBlock = useDialogsStore(state => state.showEditBlock);
|
const showEditBlock = useDialogsStore(state => state.showEditBlock);
|
||||||
|
const showImportSchema = useDialogsStore(state => state.showImportSchema);
|
||||||
|
|
||||||
const { isOpen: isContextMenuOpen, menuProps, openContextMenu, hideContextMenu } = useContextMenu();
|
const { isOpen: isContextMenuOpen, menuProps, openContextMenu, hideContextMenu } = useContextMenu();
|
||||||
const { handleDragStart, handleDrag, handleDragStop } = useDragging({ hideContextMenu });
|
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() {
|
function handleDeleteSelected() {
|
||||||
if (selected.length !== 1) {
|
if (selected.length !== 1) {
|
||||||
return;
|
return;
|
||||||
|
@ -194,8 +218,8 @@ export function OssFlow() {
|
||||||
<ToolbarOssGraph
|
<ToolbarOssGraph
|
||||||
className='absolute z-pop top-8 right-1/2 translate-x-1/2'
|
className='absolute z-pop top-8 right-1/2 translate-x-1/2'
|
||||||
onCreateBlock={handleCreateBlock}
|
onCreateBlock={handleCreateBlock}
|
||||||
onCreateSchema={handleCreateOperation}
|
onCreateSchema={handleCreateSchema}
|
||||||
onImportSchema={handleCreateOperation}
|
onImportSchema={handleImportSchema}
|
||||||
onCreateSynthesis={handleCreateOperation}
|
onCreateSynthesis={handleCreateOperation}
|
||||||
onDelete={handleDeleteSelected}
|
onDelete={handleDeleteSelected}
|
||||||
onResetPositions={resetGraph}
|
onResetPositions={resetGraph}
|
||||||
|
|
|
@ -21,7 +21,8 @@ import {
|
||||||
IconNewItem,
|
IconNewItem,
|
||||||
IconReset,
|
IconReset,
|
||||||
IconSave,
|
IconSave,
|
||||||
IconSettings
|
IconSettings,
|
||||||
|
IconSynthesis
|
||||||
} from '@/components/icons';
|
} from '@/components/icons';
|
||||||
import { type Styling } from '@/components/props';
|
import { type Styling } from '@/components/props';
|
||||||
import { cn } from '@/components/utils';
|
import { cn } from '@/components/utils';
|
||||||
|
@ -184,13 +185,13 @@ export function ToolbarOssGraph({
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Импорт КС'
|
text='Импорт КС'
|
||||||
titleHtml={prepareTooltip('Импорт концептуальной схемы', 'Alt + 3')}
|
titleHtml={prepareTooltip('Импорт концептуальной схемы', 'Alt + 3')}
|
||||||
icon={<IconDownload size='1.25rem' className='text-constructive' />}
|
icon={<IconDownload size='1.25rem' className='text-primary' />}
|
||||||
onClick={onImportSchema}
|
onClick={onImportSchema}
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Синтез'
|
text='Синтез'
|
||||||
titleHtml={prepareTooltip('Синтез концептуальных схем', 'Alt + 4')}
|
titleHtml={prepareTooltip('Синтез концептуальных схем', 'Alt + 4')}
|
||||||
icon={<IconConceptBlock size='1.25rem' className='text-primary' />}
|
icon={<IconSynthesis size='1.25rem' className='text-primary' />}
|
||||||
onClick={onCreateSynthesis}
|
onClick={onCreateSynthesis}
|
||||||
/>
|
/>
|
||||||
{user.is_staff ? (
|
{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 DlgEditVersionsProps } from '@/features/library/dialogs/dlg-edit-versions/dlg-edit-versions';
|
||||||
import { type DlgChangeInputSchemaProps } from '@/features/oss/dialogs/dlg-change-input-schema';
|
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 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 DlgDeleteOperationProps } from '@/features/oss/dialogs/dlg-delete-operation';
|
||||||
import { type DlgEditBlockProps } from '@/features/oss/dialogs/dlg-edit-block';
|
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 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 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 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';
|
import { type DlgCreateCstProps } from '@/features/rsform/dialogs/dlg-create-cst/dlg-create-cst';
|
||||||
|
@ -41,7 +43,7 @@ export const DialogType = {
|
||||||
CREATE_BLOCK: 7,
|
CREATE_BLOCK: 7,
|
||||||
EDIT_BLOCK: 8,
|
EDIT_BLOCK: 8,
|
||||||
|
|
||||||
CREATE_OPERATION: 9,
|
CREATE_SYNTHESIS: 9,
|
||||||
EDIT_OPERATION: 10,
|
EDIT_OPERATION: 10,
|
||||||
DELETE_OPERATION: 11,
|
DELETE_OPERATION: 11,
|
||||||
CHANGE_INPUT_SCHEMA: 12,
|
CHANGE_INPUT_SCHEMA: 12,
|
||||||
|
@ -63,7 +65,9 @@ export const DialogType = {
|
||||||
SHOW_AST: 25,
|
SHOW_AST: 25,
|
||||||
SHOW_TYPE_GRAPH: 26,
|
SHOW_TYPE_GRAPH: 26,
|
||||||
GRAPH_PARAMETERS: 27,
|
GRAPH_PARAMETERS: 27,
|
||||||
SHOW_TERM_GRAPH: 28
|
SHOW_TERM_GRAPH: 28,
|
||||||
|
CREATE_SCHEMA: 29,
|
||||||
|
IMPORT_SCHEMA: 30
|
||||||
} as const;
|
} as const;
|
||||||
export type DialogType = (typeof DialogType)[keyof typeof DialogType];
|
export type DialogType = (typeof DialogType)[keyof typeof DialogType];
|
||||||
|
|
||||||
|
@ -79,7 +83,7 @@ interface DialogsStore {
|
||||||
showCstTemplate: (props: DlgCstTemplateProps) => void;
|
showCstTemplate: (props: DlgCstTemplateProps) => void;
|
||||||
showCreateCst: (props: DlgCreateCstProps) => void;
|
showCreateCst: (props: DlgCreateCstProps) => void;
|
||||||
showCreateBlock: (props: DlgCreateBlockProps) => void;
|
showCreateBlock: (props: DlgCreateBlockProps) => void;
|
||||||
showCreateOperation: (props: DlgCreateOperationProps) => void;
|
showCreateOperation: (props: DlgCreateSynthesisProps) => void;
|
||||||
showDeleteCst: (props: DlgDeleteCstProps) => void;
|
showDeleteCst: (props: DlgDeleteCstProps) => void;
|
||||||
showEditEditors: (props: DlgEditEditorsProps) => void;
|
showEditEditors: (props: DlgEditEditorsProps) => void;
|
||||||
showEditOperation: (props: DlgEditOperationProps) => void;
|
showEditOperation: (props: DlgEditOperationProps) => void;
|
||||||
|
@ -104,6 +108,8 @@ interface DialogsStore {
|
||||||
showSubstituteCst: (props: DlgSubstituteCstProps) => void;
|
showSubstituteCst: (props: DlgSubstituteCstProps) => void;
|
||||||
showUploadRSForm: (props: DlgUploadRSFormProps) => void;
|
showUploadRSForm: (props: DlgUploadRSFormProps) => void;
|
||||||
showEditCst: (props: DlgEditCstProps) => void;
|
showEditCst: (props: DlgEditCstProps) => void;
|
||||||
|
showCreateSchema: (props: DlgCreateSchemaProps) => void;
|
||||||
|
showImportSchema: (props: DlgImportSchemaProps) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useDialogsStore = create<DialogsStore>()(set => ({
|
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 }),
|
showCstTemplate: props => set({ active: DialogType.CONSTITUENTA_TEMPLATE, props: props }),
|
||||||
showCreateCst: props => set({ active: DialogType.CREATE_CONSTITUENTA, 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 }),
|
showCreateBlock: props => set({ active: DialogType.CREATE_BLOCK, props: props }),
|
||||||
showDeleteCst: props => set({ active: DialogType.DELETE_CONSTITUENTA, props: props }),
|
showDeleteCst: props => set({ active: DialogType.DELETE_CONSTITUENTA, props: props }),
|
||||||
showEditEditors: props => set({ active: DialogType.EDIT_EDITORS, 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 }),
|
showQR: props => set({ active: DialogType.SHOW_QR_CODE, props: props }),
|
||||||
showSubstituteCst: props => set({ active: DialogType.SUBSTITUTE_CONSTITUENTS, props: props }),
|
showSubstituteCst: props => set({ active: DialogType.SUBSTITUTE_CONSTITUENTS, props: props }),
|
||||||
showUploadRSForm: props => set({ active: DialogType.UPLOAD_RSFORM, 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