mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
Refactor async functions. Implement DeleteCst
This commit is contained in:
parent
e58fd183e9
commit
ffbeafc3f5
|
@ -92,6 +92,7 @@ class RSForm(models.Model):
|
||||||
csttype=type
|
csttype=type
|
||||||
)
|
)
|
||||||
self._recreate_order()
|
self._recreate_order()
|
||||||
|
self.save()
|
||||||
return Constituenta.objects.get(pk=result.pk)
|
return Constituenta.objects.get(pk=result.pk)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
|
@ -107,8 +108,17 @@ class RSForm(models.Model):
|
||||||
csttype=type
|
csttype=type
|
||||||
)
|
)
|
||||||
self._recreate_order()
|
self._recreate_order()
|
||||||
|
self.save()
|
||||||
return Constituenta.objects.get(pk=result.pk)
|
return Constituenta.objects.get(pk=result.pk)
|
||||||
|
|
||||||
|
@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.save()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def import_json(owner: User, data: dict, is_common: bool = True) -> 'RSForm':
|
def import_json(owner: User, data: dict, is_common: bool = True) -> 'RSForm':
|
||||||
|
|
|
@ -7,6 +7,12 @@ 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()
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,20 @@ class TestRSForm(TestCase):
|
||||||
self.assertEqual(cst2.schema, schema)
|
self.assertEqual(cst2.schema, schema)
|
||||||
self.assertEqual(cst1.order, 1)
|
self.assertEqual(cst1.order, 1)
|
||||||
|
|
||||||
|
def test_delete_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.delete_cst([x2, d1])
|
||||||
|
x1.refresh_from_db()
|
||||||
|
d2.refresh_from_db()
|
||||||
|
schema.refresh_from_db()
|
||||||
|
self.assertEqual(schema.constituents().count(), 2)
|
||||||
|
self.assertEqual(x1.order, 1)
|
||||||
|
self.assertEqual(d2.order, 2)
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -173,14 +173,14 @@ class TestRSFormViewset(APITestCase):
|
||||||
|
|
||||||
def test_create_constituenta(self):
|
def test_create_constituenta(self):
|
||||||
data = json.dumps({'alias': 'X3', 'csttype': 'basic'})
|
data = json.dumps({'alias': 'X3', 'csttype': 'basic'})
|
||||||
response = self.client.post(f'/api/rsforms/{self.rsform_unowned.id}/new-constituenta/',
|
response = self.client.post(f'/api/rsforms/{self.rsform_unowned.id}/cst-create/',
|
||||||
data=data, content_type='application/json')
|
data=data, content_type='application/json')
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
schema = self.rsform_owned
|
schema = self.rsform_owned
|
||||||
Constituenta.objects.create(schema=schema, alias='X1', csttype='basic', order=1)
|
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)
|
||||||
response = self.client.post(f'/api/rsforms/{schema.id}/new-constituenta/',
|
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['alias'], 'X3')
|
||||||
|
@ -188,13 +188,38 @@ class TestRSFormViewset(APITestCase):
|
||||||
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}/new-constituenta/',
|
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['alias'], 'X4')
|
||||||
x4 = Constituenta.objects.get(alias=response.data['alias'])
|
x4 = Constituenta.objects.get(alias=response.data['alias'])
|
||||||
self.assertEqual(x4.order, 3)
|
self.assertEqual(x4.order, 3)
|
||||||
|
|
||||||
|
def test_delete_constituenta(self):
|
||||||
|
schema = self.rsform_owned
|
||||||
|
data = json.dumps({'items': [1337]})
|
||||||
|
response = self.client.post(f'/api/rsforms/{schema.id}/cst-multidelete/',
|
||||||
|
data=data, content_type='application/json')
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
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]})
|
||||||
|
response = self.client.post(f'/api/rsforms/{schema.id}/cst-multidelete/',
|
||||||
|
data=data, content_type='application/json')
|
||||||
|
x2.refresh_from_db()
|
||||||
|
schema.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, 202)
|
||||||
|
self.assertEqual(schema.constituents().count(), 1)
|
||||||
|
self.assertEqual(x2.alias, 'X2')
|
||||||
|
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]})
|
||||||
|
response = self.client.post(f'/api/rsforms/{schema.id}/cst-multidelete/',
|
||||||
|
data=data, content_type='application/json')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
|
||||||
class TestFunctionalViews(APITestCase):
|
class TestFunctionalViews(APITestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -42,7 +42,7 @@ class RSFormViewSet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
if self.action in ['update', 'destroy', 'partial_update',
|
if self.action in ['update', 'destroy', 'partial_update',
|
||||||
'new_constituenta']:
|
'cst_create', 'cst_multidelete']:
|
||||||
permission_classes = [utils.ObjectOwnerOrAdmin]
|
permission_classes = [utils.ObjectOwnerOrAdmin]
|
||||||
elif self.action in ['create', 'claim']:
|
elif self.action in ['create', 'claim']:
|
||||||
permission_classes = [permissions.IsAuthenticated]
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
@ -50,9 +50,9 @@ class RSFormViewSet(viewsets.ModelViewSet):
|
||||||
permission_classes = [permissions.AllowAny]
|
permission_classes = [permissions.AllowAny]
|
||||||
return [permission() for permission in permission_classes]
|
return [permission() for permission in permission_classes]
|
||||||
|
|
||||||
@action(detail=True, methods=['post'], url_path='new-constituenta')
|
@action(detail=True, methods=['post'], url_path='cst-create')
|
||||||
def new_constituenta(self, request, pk):
|
def cst_create(self, request, pk):
|
||||||
''' View schema contents (including constituents) '''
|
''' Create new constituenta '''
|
||||||
schema: models.RSForm = self.get_object()
|
schema: models.RSForm = self.get_object()
|
||||||
serializer = serializers.NewConstituentaSerializer(data=request.data)
|
serializer = serializers.NewConstituentaSerializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
@ -65,6 +65,26 @@ class RSFormViewSet(viewsets.ModelViewSet):
|
||||||
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())
|
return Response(status=201, data=constituenta.to_json())
|
||||||
|
|
||||||
|
@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.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)
|
||||||
|
return Response(status=202)
|
||||||
|
|
||||||
@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()
|
||||||
|
|
|
@ -7,9 +7,9 @@ import { getAuth, postLogin, postLogout, postSignup } from '../utils/backendAPI'
|
||||||
|
|
||||||
interface IAuthContext {
|
interface IAuthContext {
|
||||||
user: ICurrentUser | undefined
|
user: ICurrentUser | undefined
|
||||||
login: (username: string, password: string, onSuccess?: () => void) => void
|
login: (username: string, password: string) => Promise<void>
|
||||||
logout: (onSuccess?: () => void) => void
|
logout: (onSuccess?: () => void) => Promise<void>
|
||||||
signup: (data: IUserSignupData, onSuccess?: () => void) => void
|
signup: (data: IUserSignupData) => Promise<void>
|
||||||
loading: boolean
|
loading: boolean
|
||||||
error: ErrorInfo
|
error: ErrorInfo
|
||||||
setError: (error: ErrorInfo) => void
|
setError: (error: ErrorInfo) => void
|
||||||
|
@ -17,9 +17,9 @@ interface IAuthContext {
|
||||||
|
|
||||||
export const AuthContext = createContext<IAuthContext>({
|
export const AuthContext = createContext<IAuthContext>({
|
||||||
user: undefined,
|
user: undefined,
|
||||||
login: () => {},
|
login: async () => {},
|
||||||
logout: () => {},
|
logout: async () => {},
|
||||||
signup: () => {},
|
signup: async () => {},
|
||||||
loading: false,
|
loading: false,
|
||||||
error: '',
|
error: '',
|
||||||
setError: () => {}
|
setError: () => {}
|
||||||
|
@ -63,18 +63,17 @@ export const AuthState = ({ children }: AuthStateProps) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logout(onSuccess?: () => void) {
|
async function logout() {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
postLogout({
|
postLogout({
|
||||||
showError: true,
|
showError: true,
|
||||||
onSucccess: response => {
|
onSucccess: response => {
|
||||||
loadCurrentUser();
|
loadCurrentUser();
|
||||||
if(onSuccess) onSuccess();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function signup(data: IUserSignupData, onSuccess?: () => void) {
|
async function signup(data: IUserSignupData) {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
postSignup({
|
postSignup({
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -83,7 +82,6 @@ export const AuthState = ({ children }: AuthStateProps) => {
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
onSucccess: response => {
|
onSucccess: response => {
|
||||||
loadCurrentUser();
|
loadCurrentUser();
|
||||||
if(onSuccess) onSuccess();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ 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, postNewConstituenta } from '../utils/backendAPI';
|
import { BackendCallback, deleteRSForm, getTRSFile, patchConstituenta, patchRSForm, postClaimRSForm, postDeleteConstituenta, postNewConstituenta } from '../utils/backendAPI';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
interface IRSFormContext {
|
interface IRSFormContext {
|
||||||
|
@ -23,14 +23,16 @@ interface IRSFormContext {
|
||||||
toggleForceAdmin: () => void
|
toggleForceAdmin: () => void
|
||||||
toggleReadonly: () => void
|
toggleReadonly: () => void
|
||||||
toggleTracking: () => void
|
toggleTracking: () => void
|
||||||
reload: () => void
|
|
||||||
update: (data: any, callback?: BackendCallback) => void
|
|
||||||
destroy: (callback: BackendCallback) => void
|
|
||||||
claim: (callback: BackendCallback) => void
|
|
||||||
download: (callback: BackendCallback) => void
|
|
||||||
|
|
||||||
cstUpdate: (data: any, callback: BackendCallback) => void
|
reload: () => Promise<void>
|
||||||
cstCreate: (data: any, callback: BackendCallback) => void
|
update: (data: any, callback?: BackendCallback) => Promise<void>
|
||||||
|
destroy: (callback?: BackendCallback) => Promise<void>
|
||||||
|
claim: (callback?: BackendCallback) => Promise<void>
|
||||||
|
download: (callback: BackendCallback) => Promise<void>
|
||||||
|
|
||||||
|
cstUpdate: (data: any, callback?: BackendCallback) => Promise<void>
|
||||||
|
cstCreate: (data: any, callback?: BackendCallback) => Promise<void>
|
||||||
|
cstDelete: (data: any, callback?: BackendCallback) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RSFormContext = createContext<IRSFormContext>({
|
export const RSFormContext = createContext<IRSFormContext>({
|
||||||
|
@ -50,14 +52,16 @@ export const RSFormContext = createContext<IRSFormContext>({
|
||||||
toggleForceAdmin: () => {},
|
toggleForceAdmin: () => {},
|
||||||
toggleReadonly: () => {},
|
toggleReadonly: () => {},
|
||||||
toggleTracking: () => {},
|
toggleTracking: () => {},
|
||||||
reload: () => {},
|
|
||||||
update: () => {},
|
|
||||||
destroy: () => {},
|
|
||||||
claim: () => {},
|
|
||||||
download: () => {},
|
|
||||||
|
|
||||||
cstUpdate: () => {},
|
reload: async () => {},
|
||||||
cstCreate: () => {},
|
update: async () => {},
|
||||||
|
destroy: async () => {},
|
||||||
|
claim: async () => {},
|
||||||
|
download: async () => {},
|
||||||
|
|
||||||
|
cstUpdate: async () => {},
|
||||||
|
cstCreate: async () => {},
|
||||||
|
cstDelete: async () => {},
|
||||||
})
|
})
|
||||||
|
|
||||||
interface RSFormStateProps {
|
interface RSFormStateProps {
|
||||||
|
@ -76,22 +80,26 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
|
|
||||||
const isOwned = useMemo(() => user?.id === schema?.owner || false, [user, schema]);
|
const isOwned = useMemo(() => user?.id === schema?.owner || false, [user, schema]);
|
||||||
const isClaimable = useMemo(() => (user?.id !== schema?.owner || false), [user, schema]);
|
const isClaimable = useMemo(() => (user?.id !== schema?.owner || false), [user, schema]);
|
||||||
const isEditable = useMemo(() => {
|
const isEditable = useMemo(
|
||||||
|
() => {
|
||||||
return (
|
return (
|
||||||
!readonly &&
|
!loading && !readonly &&
|
||||||
(isOwned || (forceAdmin && user?.is_staff) || false)
|
(isOwned || (forceAdmin && user?.is_staff) || false)
|
||||||
)
|
)
|
||||||
}, [user, readonly, forceAdmin, isOwned]);
|
}, [user, readonly, forceAdmin, isOwned, loading]);
|
||||||
|
|
||||||
const isTracking = useMemo(() => {
|
const isTracking = useMemo(
|
||||||
|
() => {
|
||||||
return true;
|
return true;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toggleTracking = useCallback(() => {
|
const toggleTracking = useCallback(
|
||||||
|
() => {
|
||||||
toast('not implemented yet');
|
toast('not implemented yet');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function update(data: any, callback?: BackendCallback) {
|
const update = useCallback(
|
||||||
|
async (data: any, callback?: BackendCallback) => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
patchRSForm(schemaID, {
|
patchRSForm(schemaID, {
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -100,9 +108,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
onSucccess: callback
|
onSucccess: callback
|
||||||
});
|
});
|
||||||
}
|
}, [schemaID, setError]);
|
||||||
|
|
||||||
async function destroy(callback: BackendCallback) {
|
const destroy = useCallback(
|
||||||
|
async (callback?: BackendCallback) => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
deleteRSForm(schemaID, {
|
deleteRSForm(schemaID, {
|
||||||
showError: true,
|
showError: true,
|
||||||
|
@ -110,9 +119,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
onSucccess: callback
|
onSucccess: callback
|
||||||
});
|
});
|
||||||
}
|
}, [schemaID, setError]);
|
||||||
|
|
||||||
async function claim(callback: BackendCallback) {
|
const claim = useCallback(
|
||||||
|
async (callback?: BackendCallback) => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
postClaimRSForm(schemaID, {
|
postClaimRSForm(schemaID, {
|
||||||
showError: true,
|
showError: true,
|
||||||
|
@ -120,9 +130,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
onSucccess: callback
|
onSucccess: callback
|
||||||
});
|
});
|
||||||
}
|
}, [schemaID, setError]);
|
||||||
|
|
||||||
async function download(callback: BackendCallback) {
|
const download = useCallback(
|
||||||
|
async (callback: BackendCallback) => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
getTRSFile(schemaID, {
|
getTRSFile(schemaID, {
|
||||||
showError: true,
|
showError: true,
|
||||||
|
@ -130,9 +141,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
onSucccess: callback
|
onSucccess: callback
|
||||||
});
|
});
|
||||||
}
|
}, [schemaID, setError]);
|
||||||
|
|
||||||
async function cstUpdate(data: any, callback?: BackendCallback) {
|
const cstUpdate = useCallback(
|
||||||
|
async (data: any, callback?: BackendCallback) => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
patchConstituenta(String(active!.entityUID), {
|
patchConstituenta(String(active!.entityUID), {
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -141,9 +153,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
onSucccess: callback
|
onSucccess: callback
|
||||||
});
|
});
|
||||||
}
|
}, [active, setError]);
|
||||||
|
|
||||||
async function cstCreate(data: any, callback?: BackendCallback) {
|
const cstCreate = useCallback(
|
||||||
|
async (data: any, callback?: BackendCallback) => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
postNewConstituenta(schemaID, {
|
postNewConstituenta(schemaID, {
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -152,7 +165,19 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
onSucccess: callback
|
onSucccess: callback
|
||||||
});
|
});
|
||||||
}
|
}, [schemaID, setError]);
|
||||||
|
|
||||||
|
const cstDelete = useCallback(
|
||||||
|
async (data: any, callback?: BackendCallback) => {
|
||||||
|
setError(undefined);
|
||||||
|
postDeleteConstituenta(schemaID, {
|
||||||
|
data: data,
|
||||||
|
showError: true,
|
||||||
|
setLoading: setProcessing,
|
||||||
|
onError: error => setError(error),
|
||||||
|
onSucccess: callback
|
||||||
|
});
|
||||||
|
}, [schemaID, setError]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RSFormContext.Provider value={{
|
<RSFormContext.Provider value={{
|
||||||
|
@ -164,7 +189,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
isOwned, isEditable, isClaimable,
|
isOwned, isEditable, isClaimable,
|
||||||
isTracking, toggleTracking,
|
isTracking, toggleTracking,
|
||||||
reload, update, download, destroy, claim,
|
reload, update, download, destroy, claim,
|
||||||
cstUpdate, cstCreate
|
cstUpdate, cstCreate, cstDelete,
|
||||||
}}>
|
}}>
|
||||||
{ children }
|
{ children }
|
||||||
</RSFormContext.Provider>
|
</RSFormContext.Provider>
|
||||||
|
|
|
@ -5,13 +5,13 @@ import { getActiveUsers } from '../utils/backendAPI'
|
||||||
|
|
||||||
interface IUsersContext {
|
interface IUsersContext {
|
||||||
users: IUserInfo[]
|
users: IUserInfo[]
|
||||||
reload: () => void
|
reload: () => Promise<void>
|
||||||
getUserLabel: (userID?: number) => string
|
getUserLabel: (userID?: number) => string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UsersContext = createContext<IUsersContext>({
|
export const UsersContext = createContext<IUsersContext>({
|
||||||
users: [],
|
users: [],
|
||||||
reload: () => {},
|
reload: async () => {},
|
||||||
getUserLabel: () => ''
|
getUserLabel: () => ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,8 @@ export function useRSFormDetails({target}: {target?: string}) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<ErrorInfo>(undefined);
|
const [error, setError] = useState<ErrorInfo>(undefined);
|
||||||
|
|
||||||
const fetchData = useCallback(async () => {
|
const fetchData = useCallback(
|
||||||
|
async () => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
setSchema(undefined);
|
setSchema(undefined);
|
||||||
if (!target) {
|
if (!target) {
|
||||||
|
@ -20,17 +21,18 @@ export function useRSFormDetails({target}: {target?: string}) {
|
||||||
onError: error => setError(error),
|
onError: error => setError(error),
|
||||||
onSucccess: (response) => {
|
onSucccess: (response) => {
|
||||||
CalculateStats(response.data)
|
CalculateStats(response.data)
|
||||||
|
console.log(response.data);
|
||||||
setSchema(response.data);
|
setSchema(response.data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [target]);
|
}, [target]);
|
||||||
|
|
||||||
async function reload() {
|
async function reload() {
|
||||||
fetchData()
|
fetchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData()
|
fetchData();
|
||||||
}, [fetchData])
|
}, [fetchData])
|
||||||
|
|
||||||
return { schema, reload, error, setError, loading };
|
return { schema, reload, error, setError, loading };
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useAuth } from '../context/AuthContext';
|
||||||
|
|
||||||
function HomePage() {
|
function HomePage() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const {user} = useAuth();
|
||||||
|
if (user) {
|
||||||
|
navigate('/rsforms?filter=personal');
|
||||||
|
} else {
|
||||||
|
navigate('/rsforms?filter=common');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col items-center justify-center w-full py-2'>
|
<div className='flex flex-col items-center justify-center w-full py-2'>
|
||||||
<p>Home page</p>
|
<p>Home page</p>
|
||||||
|
|
|
@ -30,7 +30,8 @@ function LoginPage() {
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
login(username, password, () => { navigate('/rsforms?filter=personal'); });
|
login(username, password)
|
||||||
|
.then(() => navigate('/rsforms?filter=personal'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ export function NotFoundPage() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className='text-xl font-semibold'>Error 404 - Not Found</h1>
|
<h1 className='text-xl font-semibold'>Error 404 - Not Found</h1>
|
||||||
<p className='mt-2'>Данная страница не существует</p>
|
<p className='mt-2'>Данная страница не существует или запрашиваемый объект отсутствует в базы данных</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,26 @@
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { EditMode } from '../../utils/models';
|
import { CstType, EditMode, INewCstData } from '../../utils/models';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import TextArea from '../../components/Common/TextArea';
|
import TextArea from '../../components/Common/TextArea';
|
||||||
import ExpressionEditor from './ExpressionEditor';
|
import ExpressionEditor from './ExpressionEditor';
|
||||||
import SubmitButton from '../../components/Common/SubmitButton';
|
import SubmitButton from '../../components/Common/SubmitButton';
|
||||||
import { getCstTypeLabel } from '../../utils/staticUI';
|
import { createAliasFor, getCstTypeLabel } from '../../utils/staticUI';
|
||||||
import ConstituentsSideList from './ConstituentsSideList';
|
import ConstituentsSideList from './ConstituentsSideList';
|
||||||
import { SaveIcon } from '../../components/Icons';
|
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() {
|
function ConstituentEditor() {
|
||||||
|
const navigate = useNavigate();
|
||||||
const {
|
const {
|
||||||
active, schema, setActive, processing, cstUpdate, isEditable, reload
|
active, schema, setActive, processing, isEditable, reload,
|
||||||
|
cstDelete, cstUpdate, cstCreate
|
||||||
} = useRSForm();
|
} = useRSForm();
|
||||||
|
|
||||||
|
const [showCstModal, setShowCstModal] = useState(false);
|
||||||
const [editMode, setEditMode] = useState(EditMode.TEXT);
|
const [editMode, setEditMode] = useState(EditMode.TEXT);
|
||||||
|
|
||||||
const [alias, setAlias] = useState('');
|
const [alias, setAlias] = useState('');
|
||||||
|
@ -42,7 +49,8 @@ function ConstituentEditor() {
|
||||||
}
|
}
|
||||||
}, [active]);
|
}, [active]);
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit =
|
||||||
|
async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!processing) {
|
if (!processing) {
|
||||||
const data = {
|
const data = {
|
||||||
|
@ -59,14 +67,51 @@ function ConstituentEditor() {
|
||||||
'forms': active?.term?.forms || [],
|
'forms': active?.term?.forms || [],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cstUpdate(data, (response) => {
|
cstUpdate(data)
|
||||||
console.log(response);
|
.then(() => {
|
||||||
toast.success('Изменения сохранены');
|
toast.success('Изменения сохранены');
|
||||||
reload();
|
reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDelete = useCallback(
|
||||||
|
async () => {
|
||||||
|
if (!active || !window.confirm('Вы уверены, что хотите удалить конституенту?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
'items': [active.entityUID]
|
||||||
|
}
|
||||||
|
const index = schema?.items?.indexOf(active)
|
||||||
|
await cstDelete(data);
|
||||||
|
if (schema?.items && index && index + 1 < schema?.items?.length) {
|
||||||
|
setActive(schema?.items[index + 1]);
|
||||||
|
}
|
||||||
|
toast.success(`Конституента удалена: ${active.alias}`);
|
||||||
|
reload();
|
||||||
|
}, [active, schema, setActive, cstDelete, reload]);
|
||||||
|
|
||||||
|
const handleAddNew = useCallback(
|
||||||
|
async (csttype?: CstType) => {
|
||||||
|
if (!active || !schema) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!csttype) {
|
||||||
|
setShowCstModal(true);
|
||||||
|
} else {
|
||||||
|
const data: INewCstData = {
|
||||||
|
'csttype': csttype,
|
||||||
|
'alias': createAliasFor(csttype, schema!),
|
||||||
|
'insert_after': active.entityUID
|
||||||
|
}
|
||||||
|
cstCreate(data, (response: AxiosResponse) => {
|
||||||
|
navigate(`/rsforms/${schema.id}?tab=${RSFormTabsList.CST_EDIT}&active=${response.data['entityUID']}`);
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [active, schema, cstCreate, navigate]);
|
||||||
|
|
||||||
const handleRename = useCallback(() => {
|
const handleRename = useCallback(() => {
|
||||||
toast.info('Переименование в разработке');
|
toast.info('Переименование в разработке');
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -78,8 +123,21 @@ function ConstituentEditor() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-start w-full gap-2'>
|
<div className='flex items-start w-full gap-2'>
|
||||||
|
<CreateCstModal
|
||||||
|
show={showCstModal}
|
||||||
|
toggle={() => setShowCstModal(!showCstModal)}
|
||||||
|
onCreate={handleAddNew}
|
||||||
|
defaultType={active?.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'>
|
||||||
|
<button type='submit'
|
||||||
|
title='Сохранить изменения'
|
||||||
|
className='px-1 py-1 font-bold rounded whitespace-nowrap disabled:cursor-not-allowed clr-btn-primary'
|
||||||
|
disabled={!isEditable}
|
||||||
|
>
|
||||||
|
<SaveIcon size={5} />
|
||||||
|
</button>
|
||||||
<div className='flex items-start justify-center w-full gap-4'>
|
<div className='flex items-start justify-center w-full gap-4'>
|
||||||
<span className='mr-12'>
|
<span className='mr-12'>
|
||||||
<label
|
<label
|
||||||
|
@ -103,13 +161,22 @@ function ConstituentEditor() {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-end'>
|
<div className='flex justify-end'>
|
||||||
<button type='submit'
|
<button type='button'
|
||||||
title='Сохранить изменения'
|
title='Создать конституенты после данной'
|
||||||
className={'px-1 py-1 whitespace-nowrap font-bold disabled:cursor-not-allowed rounded clr-btn-primary'}
|
className='px-1 py-1 font-bold rounded-full whitespace-nowrap disabled:cursor-not-allowed clr-btn-clear'
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
>
|
onClick={() => handleAddNew()}
|
||||||
<SaveIcon size={5} />
|
>
|
||||||
</button>
|
<SmallPlusIcon size={5} color={isEditable ? 'text-green': ''} />
|
||||||
|
</button>
|
||||||
|
<button type='button'
|
||||||
|
title='Удалить редактируемую конституенту'
|
||||||
|
className='px-1 py-1 font-bold rounded-full whitespace-nowrap disabled:cursor-not-allowed clr-btn-clear'
|
||||||
|
disabled={!isEditable}
|
||||||
|
onClick={handleDelete}
|
||||||
|
>
|
||||||
|
<DumpBinIcon size={5} color={isEditable ? 'text-red': ''} />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TextArea id='term' label='Термин'
|
<TextArea id='term' label='Термин'
|
||||||
|
|
|
@ -129,7 +129,7 @@ function ConstituentsSideList({expression}: ConstituentsSideListProps) {
|
||||||
columns={columns}
|
columns={columns}
|
||||||
keyField='id'
|
keyField='id'
|
||||||
noContextMenu
|
noContextMenu
|
||||||
noDataComponent={<span className='p-2 flex flex-col justify-center text-center'>
|
noDataComponent={<span className='flex flex-col justify-center p-2 text-center'>
|
||||||
<p>Список конституент пуст</p>
|
<p>Список конституент пуст</p>
|
||||||
<p>Измените параметры фильтра</p>
|
<p>Измените параметры фильтра</p>
|
||||||
</span>}
|
</span>}
|
||||||
|
|
|
@ -15,9 +15,9 @@ interface ConstituentsTableProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
||||||
const { schema, isEditable, cstCreate, reload } = useRSForm();
|
const { schema, isEditable, cstCreate, cstDelete, reload } = useRSForm();
|
||||||
const [selectedRows, setSelectedRows] = useState<IConstituenta[]>([]);
|
const [selected, setSelected] = useState<IConstituenta[]>([]);
|
||||||
const nothingSelected = useMemo(() => selectedRows.length === 0, [selectedRows]);
|
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
|
||||||
|
|
||||||
const [showCstModal, setShowCstModal] = useState(false);
|
const [showCstModal, setShowCstModal] = useState(false);
|
||||||
|
|
||||||
|
@ -29,11 +29,21 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
||||||
}, [onOpenEdit]);
|
}, [onOpenEdit]);
|
||||||
|
|
||||||
const handleDelete = useCallback(() => {
|
const handleDelete = useCallback(() => {
|
||||||
toast.info('Удаление конституент');
|
if (!window.confirm('Вы уверены, что хотите удалить выбранные конституенты?')) {
|
||||||
}, []);
|
return;
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
'items': selected.map(cst => cst.entityUID)
|
||||||
|
}
|
||||||
|
const deletedNamed = selected.map(cst => cst.alias)
|
||||||
|
cstDelete(data, (response: AxiosResponse) => {
|
||||||
|
reload().then(() => toast.success(`Конституенты удалены: ${deletedNamed}`));
|
||||||
|
});
|
||||||
|
}, [selected, cstDelete, reload]);
|
||||||
|
|
||||||
const handleMoveUp = useCallback(() => {
|
const handleMoveUp = useCallback(() => {
|
||||||
toast.info('Перемещение вверх');
|
toast.info('Перемещение вверх');
|
||||||
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleMoveDown = useCallback(() => {
|
const handleMoveDown = useCallback(() => {
|
||||||
|
@ -49,18 +59,17 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
||||||
setShowCstModal(true);
|
setShowCstModal(true);
|
||||||
} else {
|
} else {
|
||||||
let data: INewCstData = {
|
let data: INewCstData = {
|
||||||
csttype: csttype,
|
'csttype': csttype,
|
||||||
alias: createAliasFor(csttype, schema!)
|
'alias': createAliasFor(csttype, schema!)
|
||||||
}
|
}
|
||||||
if (selectedRows.length > 0) {
|
if (selected.length > 0) {
|
||||||
data['insert_after'] = selectedRows[selectedRows.length - 1].entityUID
|
data['insert_after'] = selected[selected.length - 1].entityUID
|
||||||
}
|
}
|
||||||
cstCreate(data, (response: AxiosResponse) => {
|
cstCreate(data, (response: AxiosResponse) => {
|
||||||
reload();
|
reload().then(() => toast.success(`Добавлена конституента ${response.data['alias']}`));
|
||||||
toast.info(`Добавлена конституента ${response.data['alias']}`);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [schema, selectedRows, reload, cstCreate]);
|
}, [schema, selected, reload, cstCreate]);
|
||||||
|
|
||||||
const columns = useMemo(() =>
|
const columns = useMemo(() =>
|
||||||
[
|
[
|
||||||
|
@ -182,7 +191,7 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
||||||
/>
|
/>
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<div className='sticky top-[4rem] z-10 flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem] clr-app'>
|
<div className='sticky top-[4rem] z-10 flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem] clr-app'>
|
||||||
<div className='mr-3 whitespace-nowrap'>Выбраны <span className='ml-2'><b>{selectedRows.length}</b> из {schema?.stats?.count_all || 0}</span></div>
|
<div className='mr-3 whitespace-nowrap'>Выбраны <span className='ml-2'><b>{selected.length}</b> из {schema?.stats?.count_all || 0}</span></div>
|
||||||
{isEditable && <div className='flex justify-start w-full gap-1'>
|
{isEditable && <div className='flex justify-start w-full gap-1'>
|
||||||
<Button
|
<Button
|
||||||
tooltip='Переместить вверх'
|
tooltip='Переместить вверх'
|
||||||
|
@ -247,7 +256,7 @@ function ConstituentsTable({onOpenEdit}: ConstituentsTableProps) {
|
||||||
|
|
||||||
selectableRows
|
selectableRows
|
||||||
selectableRowsHighlight
|
selectableRowsHighlight
|
||||||
onSelectedRowsChange={({selectedRows}) => setSelectedRows(selectedRows)}
|
onSelectedRowsChange={({selectedRows}) => setSelected(selectedRows)}
|
||||||
onRowDoubleClicked={onOpenEdit}
|
onRowDoubleClicked={onOpenEdit}
|
||||||
onRowClicked={handleRowClicked}
|
onRowClicked={handleRowClicked}
|
||||||
dense
|
dense
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import Modal from '../../components/Common/Modal';
|
import Modal from '../../components/Common/Modal';
|
||||||
import { CstType } from '../../utils/models';
|
import { CstType } from '../../utils/models';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import { CstTypeSelector } from '../../utils/staticUI';
|
import { CstTypeSelector, getCstTypeLabel } from '../../utils/staticUI';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
interface CreateCstModalProps {
|
interface CreateCstModalProps {
|
||||||
show: boolean
|
show: boolean
|
||||||
toggle: () => void
|
toggle: () => void
|
||||||
|
defaultType?: CstType
|
||||||
onCreate: (type: CstType) => void
|
onCreate: (type: CstType) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function CreateCstModal({show, toggle, onCreate}: CreateCstModalProps) {
|
function CreateCstModal({show, toggle, defaultType, onCreate}: CreateCstModalProps) {
|
||||||
const [validated, setValidated] = useState(false);
|
const [validated, setValidated] = useState(false);
|
||||||
const [selectedType, setSelectedType] = useState<CstType|undefined>(undefined);
|
const [selectedType, setSelectedType] = useState<CstType|undefined>(undefined);
|
||||||
|
|
||||||
|
@ -18,6 +19,10 @@ function CreateCstModal({show, toggle, onCreate}: CreateCstModalProps) {
|
||||||
if (selectedType) onCreate(selectedType);
|
if (selectedType) onCreate(selectedType);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedType(defaultType);
|
||||||
|
}, [defaultType]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValidated(selectedType !== undefined);
|
setValidated(selectedType !== undefined);
|
||||||
}, [selectedType]
|
}, [selectedType]
|
||||||
|
@ -34,6 +39,8 @@ function CreateCstModal({show, toggle, onCreate}: CreateCstModalProps) {
|
||||||
<Select
|
<Select
|
||||||
options={CstTypeSelector}
|
options={CstTypeSelector}
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
|
filterOption={null}
|
||||||
|
value={selectedType && {value: selectedType, label: getCstTypeLabel(selectedType)}}
|
||||||
onChange={(data) => setSelectedType(data?.value)}
|
onChange={(data) => setSelectedType(data?.value)}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import RSFormStats from './RSFormStats';
|
||||||
import useLocalStorage from '../../hooks/useLocalStorage';
|
import useLocalStorage from '../../hooks/useLocalStorage';
|
||||||
import TablistTools from './TablistTools';
|
import TablistTools from './TablistTools';
|
||||||
|
|
||||||
enum TabsList {
|
export enum RSFormTabsList {
|
||||||
CARD = 0,
|
CARD = 0,
|
||||||
CST_LIST = 1,
|
CST_LIST = 1,
|
||||||
CST_EDIT = 2
|
CST_EDIT = 2
|
||||||
|
@ -20,13 +20,13 @@ enum TabsList {
|
||||||
|
|
||||||
function RSFormTabs() {
|
function RSFormTabs() {
|
||||||
const { setActive, active, error, schema, loading } = useRSForm();
|
const { setActive, active, error, schema, loading } = useRSForm();
|
||||||
const [tabIndex, setTabIndex] = useLocalStorage('rsform_edit_tab', TabsList.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);
|
setActive(cst);
|
||||||
setTabIndex(TabsList.CST_EDIT)
|
setTabIndex(RSFormTabsList.CST_EDIT)
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSelectTab = (index: number) => {
|
const onSelectTab = (index: number) => {
|
||||||
|
@ -46,7 +46,7 @@ function RSFormTabs() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
const tabQuery = url.searchParams.get('tab');
|
const tabQuery = url.searchParams.get('tab');
|
||||||
setTabIndex(Number(tabQuery) || TabsList.CARD);
|
setTabIndex(Number(tabQuery) || RSFormTabsList.CARD);
|
||||||
}, [setTabIndex]);
|
}, [setTabIndex]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -54,7 +54,7 @@ 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 === TabsList.CST_EDIT && currentActive !== String(active?.entityUID);
|
const saveHistory = tabIndex === RSFormTabsList.CST_EDIT && currentActive !== String(active?.entityUID);
|
||||||
if (currentTab !== String(tabIndex)) {
|
if (currentTab !== String(tabIndex)) {
|
||||||
url.searchParams.set('tab', String(tabIndex));
|
url.searchParams.set('tab', String(tabIndex));
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@ function RSFormsTable({schemas}: RSFormsTableProps) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { getUserLabel } = useUsers();
|
const { getUserLabel } = useUsers();
|
||||||
|
|
||||||
const openRSForm = (row: IRSForm, event: React.MouseEvent<Element, MouseEvent>) => {
|
const openRSForm = (schema: IRSForm, event: React.MouseEvent<Element, MouseEvent>) => {
|
||||||
navigate(`/rsforms/${row.id}`);
|
navigate(`/rsforms/${schema.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = useMemo(() =>
|
const columns = useMemo(() =>
|
||||||
|
@ -68,7 +68,7 @@ function RSFormsTable({schemas}: RSFormsTableProps) {
|
||||||
highlightOnHover
|
highlightOnHover
|
||||||
pointerOnHover
|
pointerOnHover
|
||||||
|
|
||||||
noDataComponent={<span className='p-2 flex flex-col justify-center text-center'>
|
noDataComponent={<span className='flex flex-col justify-center p-2 text-center'>
|
||||||
<p>Список схем пуст</p>
|
<p>Список схем пуст</p>
|
||||||
<p>Измените фильтр</p>
|
<p>Измените фильтр</p>
|
||||||
</span>}
|
</span>}
|
||||||
|
|
|
@ -35,7 +35,7 @@ function RegisterPage() {
|
||||||
'first_name': firstName,
|
'first_name': firstName,
|
||||||
'last_name': lastName,
|
'last_name': lastName,
|
||||||
};
|
};
|
||||||
signup(data, () => { setSuccess(true); });
|
signup(data).then(() => setSuccess(true));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
AxiosPost({
|
return 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,84 +151,92 @@ export async function postCheckExpression(schema: string, request?: IFrontReques
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function postNewConstituenta(schema: string, request?: IFrontRequest) {
|
export async function postNewConstituenta(schema: string, request?: IFrontRequest) {
|
||||||
AxiosPost({
|
return 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}/new-constituenta/`,
|
endpoint: `${config.url.BASE}rsforms/${schema}/cst-create/`,
|
||||||
|
request: request
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function postDeleteConstituenta(schema: string, request?: IFrontRequest) {
|
||||||
|
return AxiosPost({
|
||||||
|
title: `Delete Constituents for RSForm id=${schema}: ${request?.data['items'].toString()}`,
|
||||||
|
endpoint: `${config.url.BASE}rsforms/${schema}/cst-multidelete/`,
|
||||||
request: request
|
request: request
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ====== Helper functions ===========
|
// ====== Helper functions ===========
|
||||||
function AxiosGet<ReturnType>({endpoint, request, title}: IAxiosRequest) {
|
async function AxiosGet<ReturnType>({endpoint, request, title}: IAxiosRequest) {
|
||||||
if (title) console.log(`[[${title}]] requested`);
|
if (title) console.log(`[[${title}]] requested`);
|
||||||
if (request?.setLoading) request?.setLoading(true);
|
if (request?.setLoading) request?.setLoading(true);
|
||||||
axios.get<ReturnType>(endpoint)
|
axios.get<ReturnType>(endpoint)
|
||||||
.then(function (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);
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch((error) => {
|
||||||
if (request?.setLoading) request?.setLoading(false);
|
if (request?.setLoading) request?.setLoading(false);
|
||||||
if (request?.showError) toast.error(error.message);
|
if (request?.showError) toast.error(error.message);
|
||||||
if (request?.onError) request.onError(error);
|
if (request?.onError) request.onError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function AxiosGetBlob({endpoint, request, title}: IAxiosRequest) {
|
async function AxiosGetBlob({endpoint, request, title}: IAxiosRequest) {
|
||||||
if (title) console.log(`[[${title}]] requested`);
|
if (title) console.log(`[[${title}]] requested`);
|
||||||
if (request?.setLoading) request?.setLoading(true);
|
if (request?.setLoading) request?.setLoading(true);
|
||||||
axios.get(endpoint, {responseType: 'blob'})
|
axios.get(endpoint, {responseType: 'blob'})
|
||||||
.then(function (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);
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch((error) => {
|
||||||
if (request?.setLoading) request?.setLoading(false);
|
if (request?.setLoading) request?.setLoading(false);
|
||||||
if (request?.showError) toast.error(error.message);
|
if (request?.showError) toast.error(error.message);
|
||||||
if (request?.onError) request.onError(error);
|
if (request?.onError) request.onError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function AxiosPost({endpoint, request, title}: IAxiosRequest) {
|
async function AxiosPost({endpoint, request, title}: IAxiosRequest) {
|
||||||
if (title) console.log(`[[${title}]] posted`);
|
if (title) console.log(`[[${title}]] posted`);
|
||||||
if (request?.setLoading) request?.setLoading(true);
|
if (request?.setLoading) request?.setLoading(true);
|
||||||
axios.post(endpoint, request?.data)
|
axios.post(endpoint, request?.data)
|
||||||
.then(function (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);
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch((error) => {
|
||||||
if (request?.setLoading) request?.setLoading(false);
|
if (request?.setLoading) request?.setLoading(false);
|
||||||
if (request?.showError) toast.error(error.message);
|
if (request?.showError) toast.error(error.message);
|
||||||
if (request?.onError) request.onError(error);
|
if (request?.onError) request.onError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function AxiosDelete({endpoint, request, title}: IAxiosRequest) {
|
async function AxiosDelete({endpoint, request, title}: IAxiosRequest) {
|
||||||
if (title) console.log(`[[${title}]] is being deleted`);
|
if (title) console.log(`[[${title}]] is being deleted`);
|
||||||
if (request?.setLoading) request?.setLoading(true);
|
if (request?.setLoading) request?.setLoading(true);
|
||||||
axios.delete(endpoint)
|
axios.delete(endpoint)
|
||||||
.then(function (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);
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch((error) => {
|
||||||
if (request?.setLoading) request?.setLoading(false);
|
if (request?.setLoading) request?.setLoading(false);
|
||||||
if (request?.showError) toast.error(error.message);
|
if (request?.showError) toast.error(error.message);
|
||||||
if (request?.onError) request.onError(error);
|
if (request?.onError) request.onError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function AxiosPatch({endpoint, request, title}: IAxiosRequest) {
|
async function AxiosPatch({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(endpoint, request?.data)
|
||||||
.then(function (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);
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch((error) => {
|
||||||
if (request?.setLoading) request?.setLoading(false);
|
if (request?.setLoading) request?.setLoading(false);
|
||||||
if (request?.showError) toast.error(error.message);
|
if (request?.showError) toast.error(error.message);
|
||||||
if (request?.onError) request.onError(error);
|
if (request?.onError) request.onError(error);
|
||||||
|
|
|
@ -8,8 +8,8 @@ export function shareCurrentURLProc() {
|
||||||
toast.success(`Ссылка скопирована: ${url}`);
|
toast.success(`Ссылка скопирована: ${url}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function claimOwnershipProc(
|
export async function claimOwnershipProc(
|
||||||
claim: (callback: BackendCallback) => void,
|
claim: (callback: BackendCallback) => Promise<void>,
|
||||||
reload: Function
|
reload: Function
|
||||||
) {
|
) {
|
||||||
if (!window.confirm('Вы уверены, что хотите стать владельцем данной схемы?')) {
|
if (!window.confirm('Вы уверены, что хотите стать владельцем данной схемы?')) {
|
||||||
|
@ -21,8 +21,8 @@ export function claimOwnershipProc(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteRSFormProc(
|
export async function deleteRSFormProc(
|
||||||
destroy: (callback: BackendCallback) => void,
|
destroy: (callback: BackendCallback) => Promise<void>,
|
||||||
navigate: Function
|
navigate: Function
|
||||||
) {
|
) {
|
||||||
if (!window.confirm('Вы уверены, что хотите удалить данную схему?')) {
|
if (!window.confirm('Вы уверены, что хотите удалить данную схему?')) {
|
||||||
|
@ -34,8 +34,8 @@ export function deleteRSFormProc(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function downloadRSFormProc(
|
export async function downloadRSFormProc(
|
||||||
download: (callback: BackendCallback) => void,
|
download: (callback: BackendCallback) => Promise<void>,
|
||||||
fileName: string
|
fileName: string
|
||||||
) {
|
) {
|
||||||
download((response) => {
|
download((response) => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user