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