mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-25 20:40:36 +03:00
Refactor backend-frontend interaction and add MoveCst
some bugs are still present in MoveCst and CreateCst
This commit is contained in:
parent
ffbeafc3f5
commit
920a7baff4
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 4.2.3 on 2023-07-23 11:55
|
||||
# Generated by Django 4.2.3 on 2023-07-24 19:06
|
||||
|
||||
import apps.rsform.models
|
||||
from django.conf import settings
|
||||
|
@ -37,13 +37,16 @@ class Migration(migrations.Migration):
|
|||
name='Constituenta',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('order', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1)], verbose_name='Позиция')),
|
||||
('alias', models.CharField(max_length=8, verbose_name='Имя')),
|
||||
('order', models.PositiveIntegerField(default=-1, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Позиция')),
|
||||
('alias', models.CharField(default='undefined', max_length=8, verbose_name='Имя')),
|
||||
('csttype', models.CharField(choices=[('basic', 'Base'), ('constant', 'Constant'), ('structure', 'Structured'), ('axiom', 'Axiom'), ('term', 'Term'), ('function', 'Function'), ('predicate', 'Predicate'), ('theorem', 'Theorem')], default='basic', max_length=10, verbose_name='Тип')),
|
||||
('convention', models.TextField(blank=True, default='', verbose_name='Комментарий/Конвенция')),
|
||||
('term', models.JSONField(default=apps.rsform.models._empty_term, verbose_name='Термин')),
|
||||
('term_raw', models.TextField(blank=True, default='', verbose_name='Термин (с отсылками)')),
|
||||
('term_resolved', models.TextField(blank=True, default='', verbose_name='Термин')),
|
||||
('term_forms', models.JSONField(default=apps.rsform.models._empty_forms, verbose_name='Словоформы')),
|
||||
('definition_formal', models.TextField(blank=True, default='', verbose_name='Родоструктурное определение')),
|
||||
('definition_text', models.JSONField(blank=True, default=apps.rsform.models._empty_definition, verbose_name='Текстовое определние')),
|
||||
('definition_raw', models.TextField(blank=True, default='', verbose_name='Текстовое определние (с отсылками)')),
|
||||
('definition_resolved', models.TextField(blank=True, default='', verbose_name='Текстовое определние')),
|
||||
('schema', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rsform.rsform', verbose_name='Концептуальная схема')),
|
||||
],
|
||||
options={
|
||||
|
|
|
@ -2,6 +2,7 @@ import json
|
|||
from django.db import models, transaction
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.urls import reverse
|
||||
from apps.users.models import User
|
||||
|
||||
import pyconcept
|
||||
|
@ -26,12 +27,8 @@ class Syntax(models.TextChoices):
|
|||
MATH = 'math'
|
||||
|
||||
|
||||
def _empty_term():
|
||||
return {'raw': '', 'resolved': '', 'forms': []}
|
||||
|
||||
|
||||
def _empty_definition():
|
||||
return {'raw': '', 'resolved': ''}
|
||||
def _empty_forms():
|
||||
return []
|
||||
|
||||
|
||||
class RSForm(models.Model):
|
||||
|
@ -91,7 +88,7 @@ class RSForm(models.Model):
|
|||
alias=alias,
|
||||
csttype=type
|
||||
)
|
||||
self._recreate_order()
|
||||
self._update_from_core()
|
||||
self.save()
|
||||
return Constituenta.objects.get(pk=result.pk)
|
||||
|
||||
|
@ -107,16 +104,40 @@ class RSForm(models.Model):
|
|||
alias=alias,
|
||||
csttype=type
|
||||
)
|
||||
self._recreate_order()
|
||||
self._update_from_core()
|
||||
self.save()
|
||||
return Constituenta.objects.get(pk=result.pk)
|
||||
|
||||
@transaction.atomic
|
||||
def move_cst(self, listCst: list['Constituenta'], target: int):
|
||||
''' Move list of constituents to specific position '''
|
||||
count_moved = 0
|
||||
count_top = 0
|
||||
count_bot = 0
|
||||
size = len(listCst)
|
||||
update_list = []
|
||||
for cst in self.constituents():
|
||||
if cst not in listCst:
|
||||
if count_top + 1 < target:
|
||||
cst.order = count_top + 1
|
||||
count_top += 1
|
||||
else:
|
||||
cst.order = target + size + count_bot
|
||||
count_bot += 1
|
||||
else:
|
||||
cst.order = target + count_moved
|
||||
count_moved += 1
|
||||
update_list.append(cst)
|
||||
Constituenta.objects.bulk_update(update_list, ['order'])
|
||||
self._update_from_core()
|
||||
self.save()
|
||||
|
||||
@transaction.atomic
|
||||
def delete_cst(self, listCst):
|
||||
''' Delete multiple constituents. Do not check if listCst are from this schema '''
|
||||
for cst in listCst:
|
||||
cst.delete()
|
||||
self._recreate_order()
|
||||
self._update_from_core()
|
||||
self.save()
|
||||
|
||||
@staticmethod
|
||||
|
@ -143,6 +164,9 @@ class RSForm(models.Model):
|
|||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('rsform-detail', kwargs={'pk': self.pk})
|
||||
|
||||
def _prepare_json_rsform(self: 'Constituenta') -> dict:
|
||||
return {
|
||||
'type': 'rsform',
|
||||
|
@ -152,7 +176,7 @@ class RSForm(models.Model):
|
|||
'items': []
|
||||
}
|
||||
|
||||
def _recreate_order(self):
|
||||
def _update_from_core(self) -> dict:
|
||||
checked = json.loads(pyconcept.check_schema(json.dumps(self.to_json())))
|
||||
update_list = self.constituents().only('id', 'order')
|
||||
if (len(checked['items']) != update_list.count()):
|
||||
|
@ -166,22 +190,12 @@ class RSForm(models.Model):
|
|||
order += 1
|
||||
break
|
||||
Constituenta.objects.bulk_update(update_list, ['order'])
|
||||
return checked
|
||||
|
||||
def _create_cst_from_json(self, items):
|
||||
order = 1
|
||||
for cst in items:
|
||||
# TODO: get rid of empty_term etc. Use None instead
|
||||
Constituenta.objects.create(
|
||||
alias=cst['alias'],
|
||||
schema=self,
|
||||
order=order,
|
||||
csttype=cst['cstType'],
|
||||
convention=cst.get('convention', 'Без названия'),
|
||||
definition_formal=cst['definition'].get('formal', '') if 'definition' in cst else '',
|
||||
term=cst.get('term', _empty_term()),
|
||||
definition_text=cst['definition']['text'] \
|
||||
if 'definition' in cst and 'text' in cst['definition'] else _empty_definition() # noqa: E502
|
||||
)
|
||||
Constituenta.import_json(cst, self, order)
|
||||
order += 1
|
||||
|
||||
|
||||
|
@ -194,11 +208,13 @@ class Constituenta(models.Model):
|
|||
)
|
||||
order = models.PositiveIntegerField(
|
||||
verbose_name='Позиция',
|
||||
validators=[MinValueValidator(1)]
|
||||
validators=[MinValueValidator(1)],
|
||||
default=-1,
|
||||
)
|
||||
alias = models.CharField(
|
||||
verbose_name='Имя',
|
||||
max_length=8
|
||||
max_length=8,
|
||||
default='undefined'
|
||||
)
|
||||
csttype = models.CharField(
|
||||
verbose_name='Тип',
|
||||
|
@ -211,18 +227,33 @@ class Constituenta(models.Model):
|
|||
default='',
|
||||
blank=True
|
||||
)
|
||||
term = models.JSONField(
|
||||
term_raw = models.TextField(
|
||||
verbose_name='Термин (с отсылками)',
|
||||
default='',
|
||||
blank=True
|
||||
)
|
||||
term_resolved = models.TextField(
|
||||
verbose_name='Термин',
|
||||
default=_empty_term
|
||||
default='',
|
||||
blank=True
|
||||
)
|
||||
term_forms = models.JSONField(
|
||||
verbose_name='Словоформы',
|
||||
default=_empty_forms
|
||||
)
|
||||
definition_formal = models.TextField(
|
||||
verbose_name='Родоструктурное определение',
|
||||
default='',
|
||||
blank=True
|
||||
)
|
||||
definition_text = models.JSONField(
|
||||
definition_raw = models.TextField(
|
||||
verbose_name='Текстовое определние (с отсылками)',
|
||||
default='',
|
||||
blank=True
|
||||
)
|
||||
definition_resolved = models.TextField(
|
||||
verbose_name='Текстовое определние',
|
||||
default=_empty_definition,
|
||||
default='',
|
||||
blank=True
|
||||
)
|
||||
|
||||
|
@ -230,9 +261,34 @@ class Constituenta(models.Model):
|
|||
verbose_name = 'Конституета'
|
||||
verbose_name_plural = 'Конституенты'
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('constituenta-detail', kwargs={'pk': self.pk})
|
||||
|
||||
def __str__(self):
|
||||
return self.alias
|
||||
|
||||
@staticmethod
|
||||
def import_json(data: dict, schema: RSForm, order: int) -> 'Constituenta':
|
||||
cst = Constituenta(
|
||||
alias=data['alias'],
|
||||
schema=schema,
|
||||
order=order,
|
||||
csttype=data['cstType'],
|
||||
convention=data.get('convention', 'Без названия')
|
||||
)
|
||||
if 'definition' in data:
|
||||
if 'formal' in data['definition']:
|
||||
cst.definition_formal = data['definition']['formal']
|
||||
if 'text' in data['definition']:
|
||||
cst.definition_raw = data['definition']['text'].get('raw', '')
|
||||
cst.definition_resolved = data['definition']['text'].get('resolved', '')
|
||||
if 'term' in data:
|
||||
cst.term_raw = data['definition']['text'].get('raw', '')
|
||||
cst.term_resolved = data['definition']['text'].get('resolved', '')
|
||||
cst.term_forms = data['definition']['text'].get('forms', [])
|
||||
cst.save()
|
||||
return cst
|
||||
|
||||
def to_json(self) -> str:
|
||||
return {
|
||||
'entityUID': self.id,
|
||||
|
@ -240,9 +296,16 @@ class Constituenta(models.Model):
|
|||
'cstType': self.csttype,
|
||||
'alias': self.alias,
|
||||
'convention': self.convention,
|
||||
'term': self.term,
|
||||
'term': {
|
||||
'raw': self.term_raw,
|
||||
'resolved': self.term_resolved,
|
||||
'forms': self.term_forms,
|
||||
},
|
||||
'definition': {
|
||||
'formal': self.definition_formal,
|
||||
'text': self.definition_text
|
||||
}
|
||||
'text': {
|
||||
'raw': self.definition_raw,
|
||||
'resolved': self.definition_resolved,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import json
|
||||
from rest_framework import serializers
|
||||
|
||||
import pyconcept
|
||||
from .models import Constituenta, RSForm
|
||||
|
||||
|
||||
|
@ -7,12 +9,6 @@ class FileSerializer(serializers.Serializer):
|
|||
file = serializers.FileField(allow_empty_file=False)
|
||||
|
||||
|
||||
class ItemsListSerlializer(serializers.Serializer):
|
||||
items = serializers.ListField(
|
||||
child=serializers.IntegerField()
|
||||
)
|
||||
|
||||
|
||||
class ExpressionSerializer(serializers.Serializer):
|
||||
expression = serializers.CharField()
|
||||
|
||||
|
@ -35,7 +31,60 @@ class ConstituentaSerializer(serializers.ModelSerializer):
|
|||
return super().update(instance, validated_data)
|
||||
|
||||
|
||||
class NewConstituentaSerializer(serializers.Serializer):
|
||||
class StandaloneCstSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField()
|
||||
|
||||
class Meta:
|
||||
model = Constituenta
|
||||
exclude = ('schema', )
|
||||
|
||||
def validate(self, attrs):
|
||||
try:
|
||||
attrs['object'] = Constituenta.objects.get(pk=attrs['id'])
|
||||
except Constituenta.DoesNotExist:
|
||||
raise serializers.ValidationError({f"{attrs['id']}": 'Конституента не существует'})
|
||||
return attrs
|
||||
|
||||
|
||||
class CstCreateSerializer(serializers.Serializer):
|
||||
alias = serializers.CharField(max_length=8)
|
||||
csttype = serializers.CharField(max_length=10)
|
||||
insert_after = serializers.IntegerField(required=False)
|
||||
|
||||
|
||||
class CstListSerlializer(serializers.Serializer):
|
||||
items = serializers.ListField(
|
||||
child=StandaloneCstSerializer()
|
||||
)
|
||||
|
||||
def validate(self, attrs):
|
||||
schema = self.context['schema']
|
||||
cstList = []
|
||||
for item in attrs['items']:
|
||||
cst = item['object']
|
||||
if (cst.schema != schema):
|
||||
raise serializers.ValidationError(
|
||||
{'items': f'Конституенты должны относиться к данной схеме: {item}'})
|
||||
cstList.append(cst)
|
||||
attrs['constituents'] = cstList
|
||||
return attrs
|
||||
|
||||
|
||||
class CstMoveSerlializer(CstListSerlializer):
|
||||
move_to = serializers.IntegerField()
|
||||
|
||||
|
||||
class RSFormDetailsSerlializer(serializers.BaseSerializer):
|
||||
class Meta:
|
||||
model = RSForm
|
||||
|
||||
def to_representation(self, instance: RSForm):
|
||||
trs = pyconcept.check_schema(json.dumps(instance.to_json()))
|
||||
trs = trs.replace('entityUID', 'id')
|
||||
result = json.loads(trs)
|
||||
result['id'] = instance.id
|
||||
result['time_update'] = instance.time_update
|
||||
result['time_create'] = instance.time_create
|
||||
result['is_common'] = instance.is_common
|
||||
result['owner'] = (instance.owner.pk if instance.owner is not None else None)
|
||||
return result
|
||||
|
|
|
@ -8,8 +8,7 @@ from apps.rsform.models import (
|
|||
RSForm,
|
||||
Constituenta,
|
||||
CstType,
|
||||
User,
|
||||
_empty_term, _empty_definition
|
||||
User
|
||||
)
|
||||
|
||||
|
||||
|
@ -23,6 +22,11 @@ class TestConstituenta(TestCase):
|
|||
cst = Constituenta.objects.create(alias=testStr, schema=self.schema1, order=1, convention='Test')
|
||||
self.assertEqual(str(cst), testStr)
|
||||
|
||||
def test_url(self):
|
||||
testStr = 'X1'
|
||||
cst = Constituenta.objects.create(alias=testStr, schema=self.schema1, order=1, convention='Test')
|
||||
self.assertEqual(cst.get_absolute_url(), f'/api/constituents/{cst.id}/')
|
||||
|
||||
def test_order_not_null(self):
|
||||
with self.assertRaises(IntegrityError):
|
||||
Constituenta.objects.create(alias='X1', schema=self.schema1)
|
||||
|
@ -52,28 +56,11 @@ class TestConstituenta(TestCase):
|
|||
self.assertEqual(cst.csttype, CstType.BASE)
|
||||
self.assertEqual(cst.convention, '')
|
||||
self.assertEqual(cst.definition_formal, '')
|
||||
self.assertEqual(cst.term, _empty_term())
|
||||
self.assertEqual(cst.definition_text, _empty_definition())
|
||||
|
||||
def test_create(self):
|
||||
cst = Constituenta.objects.create(
|
||||
alias='S1',
|
||||
schema=self.schema1,
|
||||
order=1,
|
||||
csttype=CstType.STRUCTURED,
|
||||
convention='Test convention',
|
||||
definition_formal='X1=X1',
|
||||
term={'raw': 'Текст @{12|3}', 'resolved': 'Текст тест', 'forms': []},
|
||||
definition_text={'raw': 'Текст1 @{12|3}', 'resolved': 'Текст1 тест'},
|
||||
)
|
||||
self.assertEqual(cst.schema, self.schema1)
|
||||
self.assertEqual(cst.order, 1)
|
||||
self.assertEqual(cst.alias, 'S1')
|
||||
self.assertEqual(cst.csttype, CstType.STRUCTURED)
|
||||
self.assertEqual(cst.convention, 'Test convention')
|
||||
self.assertEqual(cst.definition_formal, 'X1=X1')
|
||||
self.assertEqual(cst.term, {'raw': 'Текст @{12|3}', 'resolved': 'Текст тест', 'forms': []})
|
||||
self.assertEqual(cst.definition_text, {'raw': 'Текст1 @{12|3}', 'resolved': 'Текст1 тест'})
|
||||
self.assertEqual(cst.term_raw, '')
|
||||
self.assertEqual(cst.term_resolved, '')
|
||||
self.assertEqual(cst.term_forms, [])
|
||||
self.assertEqual(cst.definition_resolved, '')
|
||||
self.assertEqual(cst.definition_raw, '')
|
||||
|
||||
|
||||
class TestRSForm(TestCase):
|
||||
|
@ -87,6 +74,11 @@ class TestRSForm(TestCase):
|
|||
schema = RSForm.objects.create(title=testStr, owner=self.user1, alias='КС1')
|
||||
self.assertEqual(str(schema), testStr)
|
||||
|
||||
def test_url(self):
|
||||
testStr = 'Test123'
|
||||
schema = RSForm.objects.create(title=testStr, owner=self.user1, alias='КС1')
|
||||
self.assertEqual(schema.get_absolute_url(), f'/api/rsforms/{schema.id}/')
|
||||
|
||||
def test_create_default(self):
|
||||
schema = RSForm.objects.create(title='Test')
|
||||
self.assertIsNone(schema.owner)
|
||||
|
@ -191,6 +183,32 @@ class TestRSForm(TestCase):
|
|||
self.assertEqual(x1.order, 1)
|
||||
self.assertEqual(d2.order, 2)
|
||||
|
||||
def test_move_cst(self):
|
||||
schema = RSForm.objects.create(title='Test')
|
||||
x1 = schema.insert_last('X1', CstType.BASE)
|
||||
x2 = schema.insert_last('X2', CstType.BASE)
|
||||
d1 = schema.insert_last('D1', CstType.TERM)
|
||||
d2 = schema.insert_last('D2', CstType.TERM)
|
||||
schema.move_cst([x2, d2], 1)
|
||||
x1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
d1.refresh_from_db()
|
||||
d2.refresh_from_db()
|
||||
self.assertEqual(x1.order, 2)
|
||||
self.assertEqual(x2.order, 1)
|
||||
self.assertEqual(d1.order, 4)
|
||||
self.assertEqual(d2.order, 3)
|
||||
|
||||
def test_move_cst_down(self):
|
||||
schema = RSForm.objects.create(title='Test')
|
||||
x1 = schema.insert_last('X1', CstType.BASE)
|
||||
x2 = schema.insert_last('X2', CstType.BASE)
|
||||
schema.move_cst([x1], 2)
|
||||
x1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
self.assertEqual(x1.order, 2)
|
||||
self.assertEqual(x2.order, 1)
|
||||
|
||||
def test_to_json(self):
|
||||
schema = RSForm.objects.create(title='Test', alias='KS1', comment='Test')
|
||||
x1 = schema.insert_at(4, 'X1', CstType.BASE)
|
||||
|
|
|
@ -119,12 +119,14 @@ class TestRSFormViewset(APITestCase):
|
|||
|
||||
def test_details(self):
|
||||
schema = RSForm.objects.create(title='Test')
|
||||
schema.insert_at(1, 'X1', CstType.BASE)
|
||||
cst = schema.insert_at(1, 'X1', CstType.BASE)
|
||||
schema.insert_at(2, 'X2', CstType.BASE)
|
||||
response = self.client.get(f'/api/rsforms/{schema.id}/details/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data['title'], 'Test')
|
||||
self.assertEqual(len(response.data['items']), 1)
|
||||
self.assertEqual(len(response.data['items']), 2)
|
||||
self.assertEqual(response.data['items'][0]['parse']['status'], 'verified')
|
||||
self.assertEqual(response.data['items'][0]['id'], cst.id)
|
||||
|
||||
def test_check(self):
|
||||
schema = RSForm.objects.create(title='Test')
|
||||
|
@ -183,28 +185,28 @@ class TestRSFormViewset(APITestCase):
|
|||
response = self.client.post(f'/api/rsforms/{schema.id}/cst-create/',
|
||||
data=data, content_type='application/json')
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(response.data['alias'], 'X3')
|
||||
x3 = Constituenta.objects.get(alias=response.data['alias'])
|
||||
self.assertEqual(response.data['new_cst']['alias'], 'X3')
|
||||
x3 = Constituenta.objects.get(alias=response.data['new_cst']['alias'])
|
||||
self.assertEqual(x3.order, 3)
|
||||
|
||||
data = json.dumps({'alias': 'X4', 'csttype': 'basic', 'insert_after': x2.id})
|
||||
response = self.client.post(f'/api/rsforms/{schema.id}/cst-create/',
|
||||
data=data, content_type='application/json')
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(response.data['alias'], 'X4')
|
||||
x4 = Constituenta.objects.get(alias=response.data['alias'])
|
||||
self.assertEqual(response.data['new_cst']['alias'], 'X4')
|
||||
x4 = Constituenta.objects.get(alias=response.data['new_cst']['alias'])
|
||||
self.assertEqual(x4.order, 3)
|
||||
|
||||
def test_delete_constituenta(self):
|
||||
schema = self.rsform_owned
|
||||
data = json.dumps({'items': [1337]})
|
||||
data = json.dumps({'items': [{'id': 1337}]})
|
||||
response = self.client.post(f'/api/rsforms/{schema.id}/cst-multidelete/',
|
||||
data=data, content_type='application/json')
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
x1 = Constituenta.objects.create(schema=schema, alias='X1', csttype='basic', order=1)
|
||||
x2 = Constituenta.objects.create(schema=schema, alias='X2', csttype='basic', order=2)
|
||||
data = json.dumps({'items': [x1.id]})
|
||||
data = json.dumps({'items': [{'id': x1.id}]})
|
||||
response = self.client.post(f'/api/rsforms/{schema.id}/cst-multidelete/',
|
||||
data=data, content_type='application/json')
|
||||
x2.refresh_from_db()
|
||||
|
@ -215,11 +217,36 @@ class TestRSFormViewset(APITestCase):
|
|||
self.assertEqual(x2.order, 1)
|
||||
|
||||
x3 = Constituenta.objects.create(schema=self.rsform_unowned, alias='X1', csttype='basic', order=1)
|
||||
data = json.dumps({'items': [x3.id]})
|
||||
data = json.dumps({'items': [{'id': x3.id}]})
|
||||
response = self.client.post(f'/api/rsforms/{schema.id}/cst-multidelete/',
|
||||
data=data, content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_move_constituenta(self):
|
||||
schema = self.rsform_owned
|
||||
data = json.dumps({'items': [{'id': 1337}], 'move_to': 1})
|
||||
response = self.client.patch(f'/api/rsforms/{schema.id}/cst-moveto/',
|
||||
data=data, content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
x1 = Constituenta.objects.create(schema=schema, alias='X1', csttype='basic', order=1)
|
||||
x2 = Constituenta.objects.create(schema=schema, alias='X2', csttype='basic', order=2)
|
||||
data = json.dumps({'items': [{'id': x2.id}], 'move_to': 1})
|
||||
response = self.client.patch(f'/api/rsforms/{schema.id}/cst-moveto/',
|
||||
data=data, content_type='application/json')
|
||||
x1.refresh_from_db()
|
||||
x2.refresh_from_db()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data['id'], schema.id)
|
||||
self.assertEqual(x1.order, 2)
|
||||
self.assertEqual(x2.order, 1)
|
||||
|
||||
x3 = Constituenta.objects.create(schema=self.rsform_unowned, alias='X1', csttype='basic', order=1)
|
||||
data = json.dumps({'items': [{'id': x3.id}], 'move_to': 1})
|
||||
response = self.client.patch(f'/api/rsforms/{schema.id}/cst-moveto/',
|
||||
data=data, content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
|
||||
class TestFunctionalViews(APITestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -7,7 +7,7 @@ rsform_router = routers.SimpleRouter()
|
|||
rsform_router.register(r'rsforms', views.RSFormViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
path('constituents/<int:pk>/', views.ConstituentAPIView.as_view()),
|
||||
path('constituents/<int:pk>/', views.ConstituentAPIView.as_view(), name='constituenta-detail'),
|
||||
path('rsforms/import-trs/', views.TrsImportView.as_view()),
|
||||
path('rsforms/create-detailed/', views.create_rsform),
|
||||
path('func/parse-expression/', views.parse_expression),
|
||||
|
|
|
@ -54,7 +54,7 @@ class RSFormViewSet(viewsets.ModelViewSet):
|
|||
def cst_create(self, request, pk):
|
||||
''' Create new constituenta '''
|
||||
schema: models.RSForm = self.get_object()
|
||||
serializer = serializers.NewConstituentaSerializer(data=request.data)
|
||||
serializer = serializers.CstCreateSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
if ('insert_after' in serializer.validated_data):
|
||||
cstafter = models.Constituenta.objects.get(pk=serializer.validated_data['insert_after'])
|
||||
|
@ -63,28 +63,32 @@ class RSFormViewSet(viewsets.ModelViewSet):
|
|||
serializer.validated_data['csttype'])
|
||||
else:
|
||||
constituenta = schema.insert_last(serializer.validated_data['alias'], serializer.validated_data['csttype'])
|
||||
return Response(status=201, data=constituenta.to_json())
|
||||
schema.refresh_from_db()
|
||||
outSerializer = serializers.RSFormDetailsSerlializer(schema)
|
||||
response = Response(status=201, data={'new_cst': constituenta.to_json(), 'schema': outSerializer.data})
|
||||
response['Location'] = constituenta.get_absolute_url()
|
||||
return response
|
||||
|
||||
@action(detail=True, methods=['post'], url_path='cst-multidelete')
|
||||
def cst_multidelete(self, request, pk):
|
||||
''' Delete multiple constituents '''
|
||||
schema: models.RSForm = self.get_object()
|
||||
serializer = serializers.ItemsListSerlializer(data=request.data)
|
||||
serializer = serializers.CstListSerlializer(data=request.data, context={'schema': schema})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
listCst = []
|
||||
# TODO: consider moving validation to serializer
|
||||
try:
|
||||
for id in serializer.validated_data['items']:
|
||||
cst = models.Constituenta.objects.get(pk=id)
|
||||
if (cst.schema != schema):
|
||||
return Response({'error', 'Конституенты должны относиться к данной схеме'}, status=400)
|
||||
listCst.append(cst)
|
||||
except models.Constituenta.DoesNotExist:
|
||||
return Response(status=404)
|
||||
|
||||
schema.delete_cst(listCst)
|
||||
schema.delete_cst(serializer.validated_data['constituents'])
|
||||
return Response(status=202)
|
||||
|
||||
@action(detail=True, methods=['patch'], url_path='cst-moveto')
|
||||
def cst_moveto(self, request, pk):
|
||||
''' Delete multiple constituents '''
|
||||
schema: models.RSForm = self.get_object()
|
||||
serializer = serializers.CstMoveSerlializer(data=request.data, context={'schema': schema})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
schema.move_cst(serializer.validated_data['constituents'], serializer.validated_data['move_to'])
|
||||
schema.refresh_from_db()
|
||||
outSerializer = serializers.RSFormDetailsSerlializer(schema)
|
||||
return Response(status=200, data=outSerializer.data)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def claim(self, request, pk=None):
|
||||
schema: models.RSForm = self.get_object()
|
||||
|
@ -93,7 +97,7 @@ class RSFormViewSet(viewsets.ModelViewSet):
|
|||
else:
|
||||
schema.owner = self.request.user
|
||||
schema.save()
|
||||
return Response(status=200)
|
||||
return Response(status=200, data=serializers.RSFormSerializer(schema).data)
|
||||
|
||||
@action(detail=True, methods=['get'])
|
||||
def contents(self, request, pk):
|
||||
|
@ -105,14 +109,8 @@ class RSFormViewSet(viewsets.ModelViewSet):
|
|||
def details(self, request, pk):
|
||||
''' Detailed schema view including statuses '''
|
||||
schema: models.RSForm = self.get_object()
|
||||
result = pyconcept.check_schema(json.dumps(schema.to_json()))
|
||||
output_data = json.loads(result)
|
||||
output_data['id'] = schema.id
|
||||
output_data['time_update'] = schema.time_update
|
||||
output_data['time_create'] = schema.time_create
|
||||
output_data['is_common'] = schema.is_common
|
||||
output_data['owner'] = (schema.owner.pk if schema.owner is not None else None)
|
||||
return Response(output_data)
|
||||
serializer = serializers.RSFormDetailsSerlializer(schema)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def check(self, request, pk):
|
||||
|
|
|
@ -23,7 +23,6 @@ function App() {
|
|||
autoClose={3000}
|
||||
draggable={false}
|
||||
pauseOnFocusLoss={false}
|
||||
limit={5}
|
||||
/>
|
||||
<main className='min-h-[calc(100vh-7.5rem)] px-2 h-fit'>
|
||||
<Routes>
|
||||
|
|
|
@ -4,10 +4,7 @@ import type { TabProps } from 'react-tabs';
|
|||
function ConceptTab({children, className, ...otherProps} : TabProps) {
|
||||
return (
|
||||
<Tab
|
||||
className={
|
||||
'px-2 py-1 text-sm hover:cursor-pointer clr-tab'
|
||||
+ ' ' + className
|
||||
}
|
||||
className={`px-2 py-1 text-sm hover:cursor-pointer clr-tab ${className} whitespace-nowrap`}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -3,28 +3,34 @@ import { IConstituenta, IRSForm } from '../utils/models';
|
|||
import { useRSFormDetails } from '../hooks/useRSFormDetails';
|
||||
import { ErrorInfo } from '../components/BackendError';
|
||||
import { useAuth } from './AuthContext';
|
||||
import { BackendCallback, deleteRSForm, getTRSFile, patchConstituenta, patchRSForm, postClaimRSForm, postDeleteConstituenta, postNewConstituenta } from '../utils/backendAPI';
|
||||
import {
|
||||
BackendCallback, deleteRSForm, getTRSFile,
|
||||
patchConstituenta, patchMoveConstituenta, patchRSForm,
|
||||
postClaimRSForm, postDeleteConstituenta, postNewConstituenta
|
||||
} from '../utils/backendAPI';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
interface IRSFormContext {
|
||||
schema?: IRSForm
|
||||
active?: IConstituenta
|
||||
activeCst?: IConstituenta
|
||||
activeID?: number
|
||||
|
||||
error: ErrorInfo
|
||||
loading: boolean
|
||||
processing: boolean
|
||||
|
||||
isOwned: boolean
|
||||
isEditable: boolean
|
||||
isClaimable: boolean
|
||||
forceAdmin: boolean
|
||||
readonly: boolean
|
||||
isReadonly: boolean
|
||||
isTracking: boolean
|
||||
isForceAdmin: boolean
|
||||
|
||||
setActive: React.Dispatch<React.SetStateAction<IConstituenta | undefined>>
|
||||
setActiveID: React.Dispatch<React.SetStateAction<number | undefined>>
|
||||
toggleForceAdmin: () => void
|
||||
toggleReadonly: () => void
|
||||
toggleTracking: () => void
|
||||
|
||||
reload: () => Promise<void>
|
||||
update: (data: any, callback?: BackendCallback) => Promise<void>
|
||||
destroy: (callback?: BackendCallback) => Promise<void>
|
||||
claim: (callback?: BackendCallback) => Promise<void>
|
||||
|
@ -33,36 +39,19 @@ interface IRSFormContext {
|
|||
cstUpdate: (data: any, callback?: BackendCallback) => Promise<void>
|
||||
cstCreate: (data: any, callback?: BackendCallback) => Promise<void>
|
||||
cstDelete: (data: any, callback?: BackendCallback) => Promise<void>
|
||||
cstMoveTo: (data: any, callback?: BackendCallback) => Promise<void>
|
||||
}
|
||||
|
||||
export const RSFormContext = createContext<IRSFormContext>({
|
||||
schema: undefined,
|
||||
active: undefined,
|
||||
error: undefined,
|
||||
loading: false,
|
||||
processing: false,
|
||||
isOwned: false,
|
||||
isEditable: false,
|
||||
isClaimable: false,
|
||||
forceAdmin: false,
|
||||
readonly: false,
|
||||
isTracking: true,
|
||||
|
||||
setActive: () => {},
|
||||
toggleForceAdmin: () => {},
|
||||
toggleReadonly: () => {},
|
||||
toggleTracking: () => {},
|
||||
|
||||
reload: async () => {},
|
||||
update: async () => {},
|
||||
destroy: async () => {},
|
||||
claim: async () => {},
|
||||
download: async () => {},
|
||||
|
||||
cstUpdate: async () => {},
|
||||
cstCreate: async () => {},
|
||||
cstDelete: async () => {},
|
||||
})
|
||||
const RSFormContext = createContext<IRSFormContext | null>(null);
|
||||
export const useRSForm = () => {
|
||||
const context = useContext(RSFormContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useRSForm has to be used within <RSFormState.Provider>'
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
interface RSFormStateProps {
|
||||
schemaID: string
|
||||
|
@ -71,22 +60,27 @@ interface RSFormStateProps {
|
|||
|
||||
export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||
const { user } = useAuth();
|
||||
const { schema, reload, error, setError, loading } = useRSFormDetails({target: schemaID});
|
||||
const { schema, reload, error, setError, setSchema, loading } = useRSFormDetails({target: schemaID});
|
||||
const [processing, setProcessing] = useState(false)
|
||||
const [active, setActive] = useState<IConstituenta | undefined>(undefined);
|
||||
const [activeID, setActiveID] = useState<number | undefined>(undefined);
|
||||
|
||||
const [forceAdmin, setForceAdmin] = useState(false);
|
||||
const [readonly, setReadonly] = useState(false);
|
||||
const [isForceAdmin, setIsForceAdmin] = useState(false);
|
||||
const [isReadonly, setIsReadonly] = useState(false);
|
||||
|
||||
const isOwned = useMemo(() => user?.id === schema?.owner || false, [user, schema]);
|
||||
const isClaimable = useMemo(() => (user?.id !== schema?.owner || false), [user, schema]);
|
||||
const isOwned = useMemo(() => user?.id === schema?.owner || false, [user, schema?.owner]);
|
||||
const isClaimable = useMemo(() => user?.id !== schema?.owner || false, [user, schema?.owner]);
|
||||
const isEditable = useMemo(
|
||||
() => {
|
||||
return (
|
||||
!loading && !readonly &&
|
||||
(isOwned || (forceAdmin && user?.is_staff) || false)
|
||||
!loading && !isReadonly &&
|
||||
(isOwned || (isForceAdmin && user?.is_staff) || false)
|
||||
)
|
||||
}, [user, readonly, forceAdmin, isOwned, loading]);
|
||||
}, [user, isReadonly, isForceAdmin, isOwned, loading]);
|
||||
|
||||
const activeCst = useMemo(
|
||||
() => {
|
||||
return schema?.items && schema?.items.find((cst) => cst.id === activeID);
|
||||
}, [schema?.items, activeID]);
|
||||
|
||||
const isTracking = useMemo(
|
||||
() => {
|
||||
|
@ -106,9 +100,12 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => setError(error),
|
||||
onSucccess: callback
|
||||
onSucccess: async (response) => {
|
||||
await reload();
|
||||
if (callback) callback(response);
|
||||
}
|
||||
});
|
||||
}, [schemaID, setError]);
|
||||
}, [schemaID, setError, reload]);
|
||||
|
||||
const destroy = useCallback(
|
||||
async (callback?: BackendCallback) => {
|
||||
|
@ -128,9 +125,14 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => setError(error),
|
||||
onSucccess: callback
|
||||
onSucccess: async (response) => {
|
||||
schema!.owner = user!.id
|
||||
schema!.time_update = response.data['time_update']
|
||||
setSchema(schema)
|
||||
if (callback) callback(response);
|
||||
}
|
||||
});
|
||||
}, [schemaID, setError]);
|
||||
}, [schemaID, setError, schema, user, setSchema]);
|
||||
|
||||
const download = useCallback(
|
||||
async (callback: BackendCallback) => {
|
||||
|
@ -146,14 +148,14 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
const cstUpdate = useCallback(
|
||||
async (data: any, callback?: BackendCallback) => {
|
||||
setError(undefined);
|
||||
patchConstituenta(String(active!.entityUID), {
|
||||
patchConstituenta(String(activeID), {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => setError(error),
|
||||
onSucccess: callback
|
||||
});
|
||||
}, [active, setError]);
|
||||
}, [activeID, setError]);
|
||||
|
||||
const cstCreate = useCallback(
|
||||
async (data: any, callback?: BackendCallback) => {
|
||||
|
@ -163,9 +165,12 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => setError(error),
|
||||
onSucccess: callback
|
||||
onSucccess: async (response) => {
|
||||
setSchema(response.data['schema']);
|
||||
if (callback) callback(response);
|
||||
}
|
||||
});
|
||||
}, [schemaID, setError]);
|
||||
}, [schemaID, setError, setSchema]);
|
||||
|
||||
const cstDelete = useCallback(
|
||||
async (data: any, callback?: BackendCallback) => {
|
||||
|
@ -175,25 +180,42 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => setError(error),
|
||||
onSucccess: callback
|
||||
onSucccess: async (response) => {
|
||||
await reload();
|
||||
if (callback) callback(response);
|
||||
}
|
||||
});
|
||||
}, [schemaID, setError]);
|
||||
}, [schemaID, setError, reload]);
|
||||
|
||||
const cstMoveTo = useCallback(
|
||||
async (data: any, callback?: BackendCallback) => {
|
||||
setError(undefined);
|
||||
patchMoveConstituenta(schemaID, {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => setError(error),
|
||||
onSucccess: (response) => {
|
||||
setSchema(response.data);
|
||||
if (callback) callback(response);
|
||||
}
|
||||
});
|
||||
}, [schemaID, setError, setSchema]);
|
||||
|
||||
return (
|
||||
<RSFormContext.Provider value={{
|
||||
schema, error, loading, processing,
|
||||
active, setActive,
|
||||
forceAdmin, readonly,
|
||||
toggleForceAdmin: () => setForceAdmin(prev => !prev),
|
||||
toggleReadonly: () => setReadonly(prev => !prev),
|
||||
activeID, activeCst,
|
||||
setActiveID,
|
||||
isForceAdmin, isReadonly,
|
||||
toggleForceAdmin: () => setIsForceAdmin(prev => !prev),
|
||||
toggleReadonly: () => setIsReadonly(prev => !prev),
|
||||
isOwned, isEditable, isClaimable,
|
||||
isTracking, toggleTracking,
|
||||
reload, update, download, destroy, claim,
|
||||
cstUpdate, cstCreate, cstDelete,
|
||||
update, download, destroy, claim,
|
||||
cstUpdate, cstCreate, cstDelete, cstMoveTo,
|
||||
}}>
|
||||
{ children }
|
||||
</RSFormContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useRSForm = () => useContext(RSFormContext);
|
||||
}
|
|
@ -4,14 +4,20 @@ import { ErrorInfo } from '../components/BackendError';
|
|||
import { getRSFormDetails } from '../utils/backendAPI';
|
||||
|
||||
export function useRSFormDetails({target}: {target?: string}) {
|
||||
const [schema, setSchema] = useState<IRSForm | undefined>();
|
||||
const [schema, setInnerSchema] = useState<IRSForm | undefined>(undefined);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<ErrorInfo>(undefined);
|
||||
|
||||
function setSchema(schema?: IRSForm) {
|
||||
if (schema) CalculateStats(schema);
|
||||
setInnerSchema(schema);
|
||||
console.log(schema);
|
||||
}
|
||||
|
||||
const fetchData = useCallback(
|
||||
async () => {
|
||||
setError(undefined);
|
||||
setSchema(undefined);
|
||||
setInnerSchema(undefined);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
@ -19,11 +25,7 @@ export function useRSFormDetails({target}: {target?: string}) {
|
|||
showError: true,
|
||||
setLoading: setLoading,
|
||||
onError: error => setError(error),
|
||||
onSucccess: (response) => {
|
||||
CalculateStats(response.data)
|
||||
console.log(response.data);
|
||||
setSchema(response.data);
|
||||
}
|
||||
onSucccess: (response) => setSchema(response.data)
|
||||
});
|
||||
}, [target]);
|
||||
|
||||
|
@ -35,5 +37,5 @@ export function useRSFormDetails({target}: {target?: string}) {
|
|||
fetchData();
|
||||
}, [fetchData])
|
||||
|
||||
return { schema, reload, error, setError, loading };
|
||||
return { schema, setSchema, reload, error, setError, loading };
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useLayoutEffect, useState } from 'react';
|
||||
import { useRSForm } from '../../context/RSFormContext';
|
||||
import { CstType, EditMode, INewCstData } from '../../utils/models';
|
||||
import { toast } from 'react-toastify';
|
||||
|
@ -10,13 +10,10 @@ import ConstituentsSideList from './ConstituentsSideList';
|
|||
import { DumpBinIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
||||
import CreateCstModal from './CreateCstModal';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { RSFormTabsList } from './RSFormTabs';
|
||||
|
||||
function ConstituentEditor() {
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
active, schema, setActive, processing, isEditable, reload,
|
||||
activeCst, activeID, schema, setActiveID, processing, isEditable,
|
||||
cstDelete, cstUpdate, cstCreate
|
||||
} = useRSForm();
|
||||
|
||||
|
@ -31,23 +28,23 @@ function ConstituentEditor() {
|
|||
const [convention, setConvention] = useState('');
|
||||
const [typification, setTypification] = useState('N/A');
|
||||
|
||||
useEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
if (schema?.items && schema?.items.length > 0) {
|
||||
setActive((prev) => (prev || schema?.items![0]));
|
||||
setActiveID((prev) => (prev || schema?.items![0].id));
|
||||
}
|
||||
}, [schema, setActive])
|
||||
}, [schema, setActiveID])
|
||||
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
setAlias(active.alias);
|
||||
setType(getCstTypeLabel(active.cstType));
|
||||
setConvention(active.convention || '');
|
||||
setTerm(active.term?.raw || '');
|
||||
setTextDefinition(active.definition?.text?.raw || '');
|
||||
setExpression(active.definition?.formal || '');
|
||||
setTypification(active?.parse?.typification || 'N/A');
|
||||
useLayoutEffect(() => {
|
||||
if (activeCst) {
|
||||
setAlias(activeCst.alias);
|
||||
setType(getCstTypeLabel(activeCst.cstType));
|
||||
setConvention(activeCst.convention || '');
|
||||
setTerm(activeCst.term?.raw || '');
|
||||
setTextDefinition(activeCst.definition?.text?.raw || '');
|
||||
setExpression(activeCst.definition?.formal || '');
|
||||
setTypification(activeCst?.parse?.typification || 'N/A');
|
||||
}
|
||||
}, [active]);
|
||||
}, [activeCst]);
|
||||
|
||||
const handleSubmit =
|
||||
async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
|
@ -64,37 +61,31 @@ function ConstituentEditor() {
|
|||
'term': {
|
||||
'raw': term,
|
||||
'resolved': '',
|
||||
'forms': active?.term?.forms || [],
|
||||
'forms': activeCst?.term?.forms || [],
|
||||
}
|
||||
};
|
||||
cstUpdate(data)
|
||||
.then(() => {
|
||||
toast.success('Изменения сохранены');
|
||||
reload();
|
||||
});
|
||||
cstUpdate(data).then(() => toast.success('Изменения сохранены'));
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = useCallback(
|
||||
async () => {
|
||||
if (!active || !window.confirm('Вы уверены, что хотите удалить конституенту?')) {
|
||||
if (!activeID || !schema?.items || !window.confirm('Вы уверены, что хотите удалить конституенту?')) {
|
||||
return;
|
||||
}
|
||||
const data = {
|
||||
'items': [active.entityUID]
|
||||
'items': [activeID]
|
||||
}
|
||||
const index = schema?.items?.indexOf(active)
|
||||
await cstDelete(data);
|
||||
if (schema?.items && index && index + 1 < schema?.items?.length) {
|
||||
setActive(schema?.items[index + 1]);
|
||||
const index = schema.items.findIndex((cst) => cst.id === activeID);
|
||||
if (index !== -1 && index + 1 < schema.items.length) {
|
||||
setActiveID(schema.items[index + 1].id);
|
||||
}
|
||||
toast.success(`Конституента удалена: ${active.alias}`);
|
||||
reload();
|
||||
}, [active, schema, setActive, cstDelete, reload]);
|
||||
cstDelete(data).then(() => toast.success('Конституента удалена'));
|
||||
}, [activeID, schema, setActiveID, cstDelete]);
|
||||
|
||||
const handleAddNew = useCallback(
|
||||
async (csttype?: CstType) => {
|
||||
if (!active || !schema) {
|
||||
if (!activeID || !schema?.items) {
|
||||
return;
|
||||
}
|
||||
if (!csttype) {
|
||||
|
@ -103,14 +94,16 @@ function ConstituentEditor() {
|
|||
const data: INewCstData = {
|
||||
'csttype': csttype,
|
||||
'alias': createAliasFor(csttype, schema!),
|
||||
'insert_after': active.entityUID
|
||||
'insert_after': activeID
|
||||
}
|
||||
cstCreate(data, (response: AxiosResponse) => {
|
||||
navigate(`/rsforms/${schema.id}?tab=${RSFormTabsList.CST_EDIT}&active=${response.data['entityUID']}`);
|
||||
window.location.reload();
|
||||
cstCreate(data,
|
||||
async (response: AxiosResponse) => {
|
||||
// navigate(`/rsforms/${schema.id}?tab=${RSFormTabsList.CST_EDIT}&active=${response.data['new_cst']['id']}`);
|
||||
setActiveID(response.data['new_cst']['id']);
|
||||
toast.success(`Конституента добавлена: ${response.data['new_cst']['alias']}`);
|
||||
});
|
||||
}
|
||||
}, [active, schema, cstCreate, navigate]);
|
||||
}, [activeID, schema, cstCreate, setActiveID]);
|
||||
|
||||
const handleRename = useCallback(() => {
|
||||
toast.info('Переименование в разработке');
|
||||
|
@ -127,7 +120,7 @@ function ConstituentEditor() {
|
|||
show={showCstModal}
|
||||
toggle={() => setShowCstModal(!showCstModal)}
|
||||
onCreate={handleAddNew}
|
||||
defaultType={active?.cstType as CstType}
|
||||
defaultType={activeCst?.cstType as CstType}
|
||||
/>
|
||||
<form onSubmit={handleSubmit} className='flex-grow min-w-[50rem] max-w-min px-4 py-2 border'>
|
||||
<div className='flex items-start justify-between'>
|
||||
|
|
|
@ -11,7 +11,7 @@ interface ConstituentsSideListProps {
|
|||
}
|
||||
|
||||
function ConstituentsSideList({expression}: ConstituentsSideListProps) {
|
||||
const { schema, setActive } = useRSForm();
|
||||
const { schema, setActiveID } = useRSForm();
|
||||
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema?.items || []);
|
||||
const [filterText, setFilterText] = useLocalStorage('side-filter-text', '')
|
||||
const [onlyExpression, setOnlyExpression] = useLocalStorage('side-filter-flag', false);
|
||||
|
@ -27,7 +27,7 @@ function ConstituentsSideList({expression}: ConstituentsSideListProps) {
|
|||
if (diff.length > 0) {
|
||||
diff.forEach(
|
||||
(alias, i) => filtered.push({
|
||||
entityUID: -i,
|
||||
id: -i,
|
||||
alias: alias,
|
||||
convention: 'Конституента отсутствует',
|
||||
cstType: CstType.BASE
|
||||
|
@ -43,21 +43,21 @@ function ConstituentsSideList({expression}: ConstituentsSideListProps) {
|
|||
|
||||
const handleRowClicked = useCallback(
|
||||
(cst: IConstituenta, event: React.MouseEvent<Element, MouseEvent>) => {
|
||||
if (event.altKey && cst.entityUID > 0) {
|
||||
setActive(cst);
|
||||
if (event.altKey && cst.id > 0) {
|
||||
setActiveID(cst.id);
|
||||
}
|
||||
}, [setActive]);
|
||||
}, [setActiveID]);
|
||||
|
||||
const handleDoubleClick = useCallback(
|
||||
(cst: IConstituenta, event: React.MouseEvent<Element, MouseEvent>) => {
|
||||
if (cst.entityUID > 0) setActive(cst);
|
||||
}, [setActive]);
|
||||
if (cst.id > 0) setActiveID(cst.id);
|
||||
}, [setActiveID]);
|
||||
|
||||
const columns = useMemo(() =>
|
||||
[
|
||||
{
|
||||
id: 'id',
|
||||
selector: (cst: IConstituenta) => cst.entityUID,
|
||||
selector: (cst: IConstituenta) => cst.id,
|
||||
omit: true,
|
||||
},
|
||||
{
|
||||
|
@ -68,7 +68,7 @@ function ConstituentsSideList({expression}: ConstituentsSideListProps) {
|
|||
maxWidth: '62px',
|
||||
conditionalCellStyles: [
|
||||
{
|
||||
when: (cst: IConstituenta) => cst.entityUID <= 0,
|
||||
when: (cst: IConstituenta) => cst.id <= 0,
|
||||
classNames: ['bg-[#ffc9c9]', 'dark:bg-[#592b2b]']
|
||||
},
|
||||
],
|
||||
|
@ -81,7 +81,7 @@ function ConstituentsSideList({expression}: ConstituentsSideListProps) {
|
|||
wrap: true,
|
||||
conditionalCellStyles: [
|
||||
{
|
||||
when: (cst: IConstituenta) => cst.entityUID <= 0,
|
||||
when: (cst: IConstituenta) => cst.id <= 0,
|
||||
classNames: ['bg-[#ffc9c9]', 'dark:bg-[#592b2b]']
|
||||
},
|
||||
],
|
||||
|
@ -96,7 +96,7 @@ function ConstituentsSideList({expression}: ConstituentsSideListProps) {
|
|||
wrap: true,
|
||||
conditionalCellStyles: [
|
||||
{
|
||||
when: (cst: IConstituenta) => cst.entityUID <= 0,
|
||||
when: (cst: IConstituenta) => cst.id <= 0,
|
||||
classNames: ['bg-[#ffc9c9]', 'dark:bg-[#592b2b]']
|
||||
},
|
||||
],
|
||||
|
|
|
@ -15,8 +15,11 @@ interface ConstituentsTableProps {
|
|||
}
|
||||
|
||||
function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
||||
const { schema, isEditable, cstCreate, cstDelete, reload } = useRSForm();
|
||||
const [selected, setSelected] = useState<IConstituenta[]>([]);
|
||||
const {
|
||||
schema, isEditable,
|
||||
cstCreate, cstDelete, cstMoveTo
|
||||
} = useRSForm();
|
||||
const [selected, setSelected] = useState<number[]>([]);
|
||||
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
|
||||
|
||||
const [showCstModal, setShowCstModal] = useState(false);
|
||||
|
@ -28,32 +31,78 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
|||
}
|
||||
}, [onOpenEdit]);
|
||||
|
||||
const handleSelectionChange = useCallback(
|
||||
({selectedRows}: {
|
||||
allSelected: boolean;
|
||||
selectedCount: number;
|
||||
selectedRows: IConstituenta[];
|
||||
}) => {
|
||||
setSelected(selectedRows.map((cst) => cst.id));
|
||||
}, [setSelected]);
|
||||
|
||||
// Delete selected constituents
|
||||
const handleDelete = useCallback(() => {
|
||||
if (!window.confirm('Вы уверены, что хотите удалить выбранные конституенты?')) {
|
||||
if (!schema?.items || !window.confirm('Вы уверены, что хотите удалить выбранные конституенты?')) {
|
||||
return;
|
||||
}
|
||||
const data = {
|
||||
'items': selected.map(cst => cst.entityUID)
|
||||
'items': selected.map(id => { return {'id': id }; }),
|
||||
}
|
||||
const deletedNamed = selected.map(cst => cst.alias)
|
||||
cstDelete(data, (response: AxiosResponse) => {
|
||||
reload().then(() => toast.success(`Конституенты удалены: ${deletedNamed}`));
|
||||
});
|
||||
}, [selected, cstDelete, reload]);
|
||||
const deletedNamed = selected.map(id => schema.items?.find((cst) => cst.id === id)?.alias);
|
||||
cstDelete(data, () => toast.success(`Конституенты удалены: ${deletedNamed}`));
|
||||
}, [selected, schema?.items, cstDelete]);
|
||||
|
||||
const handleMoveUp = useCallback(() => {
|
||||
toast.info('Перемещение вверх');
|
||||
|
||||
}, []);
|
||||
// Move selected cst up
|
||||
const handleMoveUp = useCallback(
|
||||
() => {
|
||||
if (!schema?.items || selected.length === 0) {
|
||||
return;
|
||||
}
|
||||
const currentIndex = schema.items.reduce((prev, cst, index) => {
|
||||
if (selected.indexOf(cst.id) < 0) {
|
||||
return prev;
|
||||
} else if (prev === -1) {
|
||||
return index;
|
||||
}
|
||||
return Math.min(prev, index);
|
||||
}, -1);
|
||||
const insertIndex = Math.max(0, currentIndex - 1) + 1
|
||||
const data = {
|
||||
'items': selected.map(id => { return {'id': id }; }),
|
||||
'move_to': insertIndex
|
||||
}
|
||||
cstMoveTo(data).then(() => toast.info('Перемещение вверх ' + insertIndex));
|
||||
}, [selected, schema?.items, cstMoveTo]);
|
||||
|
||||
const handleMoveDown = useCallback(() => {
|
||||
toast.info('Перемещение вниз');
|
||||
}, []);
|
||||
|
||||
// Move selected cst down
|
||||
const handleMoveDown = useCallback(
|
||||
async () => {
|
||||
if (!schema?.items || selected.length === 0) {
|
||||
return;
|
||||
}
|
||||
const currentIndex = schema.items.reduce((prev, cst, index) => {
|
||||
if (selected.indexOf(cst.id) < 0) {
|
||||
return prev;
|
||||
} else if (prev === -1) {
|
||||
return index;
|
||||
}
|
||||
return Math.max(prev, index);
|
||||
}, -1);
|
||||
const insertIndex = Math.min(schema.items.length - 1, currentIndex + 1) + 1
|
||||
const data = {
|
||||
'items': selected.map(id => { return {'id': id }; }),
|
||||
'move_to': insertIndex
|
||||
}
|
||||
cstMoveTo(data).then(() => toast.info('Перемещение вниз ' + insertIndex));
|
||||
}, [selected, schema?.items, cstMoveTo]);
|
||||
|
||||
// Generate new names for all constituents
|
||||
const handleReindex = useCallback(() => {
|
||||
toast.info('Переиндексация');
|
||||
}, []);
|
||||
|
||||
// Add new constituent
|
||||
const handleAddNew = useCallback((csttype?: CstType) => {
|
||||
if (!csttype) {
|
||||
setShowCstModal(true);
|
||||
|
@ -63,20 +112,34 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
|||
'alias': createAliasFor(csttype, schema!)
|
||||
}
|
||||
if (selected.length > 0) {
|
||||
data['insert_after'] = selected[selected.length - 1].entityUID
|
||||
data['insert_after'] = selected[selected.length - 1]
|
||||
}
|
||||
cstCreate(data, (response: AxiosResponse) => {
|
||||
reload().then(() => toast.success(`Добавлена конституента ${response.data['alias']}`));
|
||||
});
|
||||
cstCreate(data, (response: AxiosResponse) =>
|
||||
toast.success(`Добавлена конституента ${response.data['new_cst']['alias']}`));
|
||||
}
|
||||
}, [schema, selected, reload, cstCreate]);
|
||||
}, [schema, selected, cstCreate]);
|
||||
|
||||
// Implement hotkeys for working with constituents table
|
||||
const handleTableKey = useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (!event.altKey) {
|
||||
return;
|
||||
}
|
||||
if (!isEditable || selected.length === 0) {
|
||||
return;
|
||||
}
|
||||
switch(event.key) {
|
||||
case 'ArrowUp': handleMoveUp(); return;
|
||||
case 'ArrowDown': handleMoveDown(); return;
|
||||
}
|
||||
console.log(event);
|
||||
}, [isEditable, selected, handleMoveUp, handleMoveDown]);
|
||||
|
||||
const columns = useMemo(() =>
|
||||
[
|
||||
{
|
||||
name: 'ID',
|
||||
id: 'id',
|
||||
selector: (cst: IConstituenta) => cst.entityUID,
|
||||
selector: (cst: IConstituenta) => cst.id,
|
||||
omit: true,
|
||||
},
|
||||
{
|
||||
|
@ -239,6 +302,7 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
|||
})}
|
||||
</div>}
|
||||
</div>
|
||||
<div className='w-full h-full' onKeyDown={handleTableKey} tabIndex={0}>
|
||||
<DataTableThemed
|
||||
data={schema!.items!}
|
||||
columns={columns}
|
||||
|
@ -256,11 +320,12 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
|||
|
||||
selectableRows
|
||||
selectableRowsHighlight
|
||||
onSelectedRowsChange={({selectedRows}) => setSelected(selectedRows)}
|
||||
onSelectedRowsChange={handleSelectionChange}
|
||||
onRowDoubleClicked={onOpenEdit}
|
||||
onRowClicked={handleRowClicked}
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import Button from '../../components/Common/Button';
|
||||
import Label from '../../components/Common/Label';
|
||||
import { useRSForm } from '../../context/RSFormContext';
|
||||
|
@ -30,18 +30,18 @@ function ExpressionEditor({
|
|||
id, label, disabled, isActive, placeholder, value, setValue,
|
||||
toggleEditMode, setTypification, onChange
|
||||
}: ExpressionEditorProps) {
|
||||
const { schema, active } = useRSForm();
|
||||
const { schema, activeCst } = useRSForm();
|
||||
const [isModified, setIsModified] = useState(false);
|
||||
const { parseData, checkExpression, resetParse, loading } = useCheckExpression({schema: schema});
|
||||
const expressionCtrl = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
setIsModified(false);
|
||||
resetParse();
|
||||
}, [active, resetParse]);
|
||||
}, [activeCst, resetParse]);
|
||||
|
||||
const handleCheckExpression = useCallback(() => {
|
||||
const prefix = active?.alias + (active?.cstType === CstType.STRUCTURED ? '::=' : ':==');
|
||||
const prefix = activeCst?.alias + (activeCst?.cstType === CstType.STRUCTURED ? '::=' : ':==');
|
||||
const expression = prefix + value;
|
||||
checkExpression(expression, (response: AxiosResponse) => {
|
||||
// TODO: update cursor position
|
||||
|
@ -49,7 +49,7 @@ function ExpressionEditor({
|
|||
setTypification(response.data['typification']);
|
||||
toast.success('проверка завершена');
|
||||
});
|
||||
}, [value, checkExpression, active, setTypification]);
|
||||
}, [value, checkExpression, activeCst, setTypification]);
|
||||
|
||||
const handleEdit = useCallback((id: TokenID, key?: string) => {
|
||||
if (!expressionCtrl.current) {
|
||||
|
@ -198,7 +198,7 @@ function ExpressionEditor({
|
|||
<div className='flex flex-col gap-2'>
|
||||
{isActive && <StatusBar
|
||||
isModified={isModified}
|
||||
constituenta={active}
|
||||
constituenta={activeCst}
|
||||
parseData={parseData}
|
||||
/>}
|
||||
<Button
|
||||
|
@ -210,7 +210,7 @@ function ExpressionEditor({
|
|||
{isActive && EditButtons}
|
||||
{!isActive && <StatusBar
|
||||
isModified={isModified}
|
||||
constituenta={active}
|
||||
constituenta={activeCst}
|
||||
parseData={parseData}
|
||||
/>}
|
||||
</div>
|
||||
|
|
|
@ -18,7 +18,7 @@ function RSFormCard() {
|
|||
const intl = useIntl();
|
||||
const { getUserLabel } = useUsers();
|
||||
const {
|
||||
schema, update, download, reload,
|
||||
schema, update, download,
|
||||
isEditable, isOwned, isClaimable, processing, destroy, claim
|
||||
} = useRSForm();
|
||||
const { user } = useAuth();
|
||||
|
@ -43,10 +43,7 @@ function RSFormCard() {
|
|||
'comment': comment,
|
||||
'is_common': common,
|
||||
};
|
||||
update(data, () => {
|
||||
toast.success('Изменения сохранены');
|
||||
reload();
|
||||
});
|
||||
update(data).then(() => toast.success('Изменения сохранены'));
|
||||
};
|
||||
|
||||
const handleDelete =
|
||||
|
@ -107,7 +104,7 @@ function RSFormCard() {
|
|||
tooltip={isClaimable ? 'Стать владельцем' : 'Вы уже являетесь владельцем' }
|
||||
disabled={!isClaimable || processing || !user}
|
||||
icon={<CrownIcon color={isOwned ? '' : 'text-green'}/>}
|
||||
onClick={() => claimOwnershipProc(claim, reload)}
|
||||
onClick={() => claimOwnershipProc(claim)}
|
||||
/>
|
||||
<Button
|
||||
tooltip={ isEditable ? 'Удалить схему' : 'Вы не можете редактировать данную схему'}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Tabs, TabList, TabPanel } from 'react-tabs';
|
|||
import ConstituentsTable from './ConstituentsTable';
|
||||
import { IConstituenta } from '../../utils/models';
|
||||
import { useRSForm } from '../../context/RSFormContext';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||
import ConceptTab from '../../components/Common/ConceptTab';
|
||||
import RSFormCard from './RSFormCard';
|
||||
import { Loader } from '../../components/Common/Loader';
|
||||
|
@ -19,13 +19,13 @@ export enum RSFormTabsList {
|
|||
}
|
||||
|
||||
function RSFormTabs() {
|
||||
const { setActive, active, error, schema, loading } = useRSForm();
|
||||
const { setActiveID, activeCst, activeID, error, schema, loading } = useRSForm();
|
||||
const [tabIndex, setTabIndex] = useLocalStorage('rsform_edit_tab', RSFormTabsList.CARD);
|
||||
const [init, setInit] = useState(false);
|
||||
|
||||
const onEditCst = (cst: IConstituenta) => {
|
||||
console.log(`Set active cst: ${cst.alias}`);
|
||||
setActive(cst);
|
||||
setActiveID(cst.id);
|
||||
setTabIndex(RSFormTabsList.CST_EDIT)
|
||||
};
|
||||
|
||||
|
@ -33,15 +33,15 @@ function RSFormTabs() {
|
|||
setTabIndex(index);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
if (schema) {
|
||||
const url = new URL(window.location.href);
|
||||
const activeQuery = url.searchParams.get('active');
|
||||
const activeCst = schema?.items?.find((cst) => cst.entityUID === Number(activeQuery)) || undefined;
|
||||
setActive(activeCst);
|
||||
const activeCst = schema?.items?.find((cst) => cst.id === Number(activeQuery)) || undefined;
|
||||
setActiveID(activeCst?.id);
|
||||
setInit(true);
|
||||
}
|
||||
}, [setActive, schema, setInit]);
|
||||
}, [setActiveID, schema, setInit]);
|
||||
|
||||
useEffect(() => {
|
||||
const url = new URL(window.location.href);
|
||||
|
@ -54,13 +54,13 @@ function RSFormTabs() {
|
|||
const url = new URL(window.location.href);
|
||||
let currentActive = url.searchParams.get('active');
|
||||
const currentTab = url.searchParams.get('tab');
|
||||
const saveHistory = tabIndex === RSFormTabsList.CST_EDIT && currentActive !== String(active?.entityUID);
|
||||
const saveHistory = tabIndex === RSFormTabsList.CST_EDIT && currentActive !== String(activeID);
|
||||
if (currentTab !== String(tabIndex)) {
|
||||
url.searchParams.set('tab', String(tabIndex));
|
||||
}
|
||||
if (active) {
|
||||
if (currentActive !== String(active.entityUID)) {
|
||||
url.searchParams.set('active', String(active.entityUID));
|
||||
if (activeID) {
|
||||
if (currentActive !== String(activeID)) {
|
||||
url.searchParams.set('active', String(activeID));
|
||||
}
|
||||
} else {
|
||||
url.searchParams.delete('active');
|
||||
|
@ -71,7 +71,7 @@ function RSFormTabs() {
|
|||
window.history.replaceState(null, '', url.toString());
|
||||
}
|
||||
}
|
||||
}, [tabIndex, active, init]);
|
||||
}, [tabIndex, activeID, init]);
|
||||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
|
|
|
@ -15,17 +15,17 @@ function TablistTools() {
|
|||
const navigate = useNavigate();
|
||||
const {user} = useAuth();
|
||||
const { schema,
|
||||
isOwned, isEditable, isTracking, readonly, forceAdmin,
|
||||
isOwned, isEditable, isTracking, isReadonly: readonly, isForceAdmin: forceAdmin,
|
||||
toggleTracking, toggleForceAdmin, toggleReadonly,
|
||||
claim, reload, destroy, download
|
||||
claim, destroy, download
|
||||
} = useRSForm();
|
||||
const schemaMenu = useDropdown();
|
||||
const editMenu = useDropdown();
|
||||
|
||||
const handleClaimOwner = useCallback(() => {
|
||||
editMenu.hide();
|
||||
claimOwnershipProc(claim, reload);
|
||||
}, [claim, reload, editMenu]);
|
||||
claimOwnershipProc(claim);
|
||||
}, [claim, editMenu]);
|
||||
|
||||
const handleDelete = useCallback(() => {
|
||||
schemaMenu.hide();
|
||||
|
@ -69,31 +69,31 @@ function TablistTools() {
|
|||
{ schemaMenu.isActive &&
|
||||
<Dropdown>
|
||||
<DropdownButton onClick={handleShare}>
|
||||
<div className='inline-flex items-center gap-2 justify-start'>
|
||||
<div className='inline-flex items-center justify-start gap-2'>
|
||||
<ShareIcon color='text-primary' size={4}/>
|
||||
<p>Поделиться</p>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
<DropdownButton onClick={handleClone}>
|
||||
<div className='inline-flex items-center gap-2 justify-start'>
|
||||
<div className='inline-flex items-center justify-start gap-2'>
|
||||
<CloneIcon color='text-primary' size={4}/>
|
||||
<p>Клонировать</p>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
<DropdownButton onClick={handleDownload}>
|
||||
<div className='inline-flex items-center gap-2 justify-start'>
|
||||
<div className='inline-flex items-center justify-start gap-2'>
|
||||
<DownloadIcon color='text-primary' size={4}/>
|
||||
<p>Выгрузить файл Экстеор</p>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
<DropdownButton disabled={!isEditable} onClick={handleUpload}>
|
||||
<div className='inline-flex items-center gap-2 justify-start'>
|
||||
<div className='inline-flex items-center justify-start gap-2'>
|
||||
<UploadIcon color={isEditable ? 'text-red' : ''} size={4}/>
|
||||
<p>Загрузить из Экстеора</p>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
<DropdownButton disabled={!isEditable} onClick={handleDelete}>
|
||||
<span className='inline-flex items-center gap-2 justify-start'>
|
||||
<span className='inline-flex items-center justify-start gap-2'>
|
||||
<DumpBinIcon color={isEditable ? 'text-red' : ''} size={4} />
|
||||
<p>Удалить схему</p>
|
||||
</span>
|
||||
|
|
|
@ -143,7 +143,7 @@ export async function postClaimRSForm(target: string, request?: IFrontRequest) {
|
|||
}
|
||||
|
||||
export async function postCheckExpression(schema: string, request?: IFrontRequest) {
|
||||
return AxiosPost({
|
||||
AxiosPost({
|
||||
title: `Check expression for RSForm id=${schema}: ${request?.data['expression']}`,
|
||||
endpoint: `${config.url.BASE}rsforms/${schema}/check/`,
|
||||
request: request
|
||||
|
@ -151,7 +151,7 @@ export async function postCheckExpression(schema: string, request?: IFrontReques
|
|||
}
|
||||
|
||||
export async function postNewConstituenta(schema: string, request?: IFrontRequest) {
|
||||
return AxiosPost({
|
||||
AxiosPost({
|
||||
title: `New Constituenta for RSForm id=${schema}: ${request?.data['alias']}`,
|
||||
endpoint: `${config.url.BASE}rsforms/${schema}/cst-create/`,
|
||||
request: request
|
||||
|
@ -159,13 +159,21 @@ export async function postNewConstituenta(schema: string, request?: IFrontReques
|
|||
}
|
||||
|
||||
export async function postDeleteConstituenta(schema: string, request?: IFrontRequest) {
|
||||
return AxiosPost({
|
||||
AxiosPost({
|
||||
title: `Delete Constituents for RSForm id=${schema}: ${request?.data['items'].toString()}`,
|
||||
endpoint: `${config.url.BASE}rsforms/${schema}/cst-multidelete/`,
|
||||
request: request
|
||||
});
|
||||
}
|
||||
|
||||
export async function patchMoveConstituenta(schema: string, request?: IFrontRequest) {
|
||||
AxiosPatch<IRSForm>({
|
||||
title: `Moving Constituents for RSForm id=${schema}: ${JSON.stringify(request?.data['items'])} to ${request?.data['move_to']}`,
|
||||
endpoint: `${config.url.BASE}rsforms/${schema}/cst-moveto/`,
|
||||
request: request
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ====== Helper functions ===========
|
||||
async function AxiosGet<ReturnType>({endpoint, request, title}: IAxiosRequest) {
|
||||
|
@ -228,13 +236,14 @@ async function AxiosDelete({endpoint, request, title}: IAxiosRequest) {
|
|||
});
|
||||
}
|
||||
|
||||
async function AxiosPatch({endpoint, request, title}: IAxiosRequest) {
|
||||
async function AxiosPatch<ReturnType>({endpoint, request, title}: IAxiosRequest) {
|
||||
if (title) console.log(`[[${title}]] is being patrially updated`);
|
||||
if (request?.setLoading) request?.setLoading(true);
|
||||
axios.patch(endpoint, request?.data)
|
||||
axios.patch<ReturnType>(endpoint, request?.data)
|
||||
.then((response) => {
|
||||
if (request?.setLoading) request?.setLoading(false);
|
||||
if (request?.onSucccess) request.onSucccess(response);
|
||||
return response.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (request?.setLoading) request?.setLoading(false);
|
||||
|
|
|
@ -74,7 +74,7 @@ export enum ParsingStatus {
|
|||
|
||||
// Constituenta data
|
||||
export interface IConstituenta {
|
||||
entityUID: number
|
||||
id: number
|
||||
alias: string
|
||||
cstType: CstType
|
||||
convention?: string
|
||||
|
|
|
@ -9,16 +9,12 @@ export function shareCurrentURLProc() {
|
|||
}
|
||||
|
||||
export async function claimOwnershipProc(
|
||||
claim: (callback: BackendCallback) => Promise<void>,
|
||||
reload: Function
|
||||
claim: (callback: BackendCallback) => Promise<void>,
|
||||
) {
|
||||
if (!window.confirm('Вы уверены, что хотите стать владельцем данной схемы?')) {
|
||||
return;
|
||||
}
|
||||
claim(() => {
|
||||
toast.success('Вы стали владельцем схемы');
|
||||
reload();
|
||||
});
|
||||
claim(() => toast.success('Вы стали владельцем схемы'));
|
||||
}
|
||||
|
||||
export async function deleteRSFormProc(
|
||||
|
|
Loading…
Reference in New Issue
Block a user