From 4e5aab6db74a8d1dfdfb4928f60ad47da52c89e8 Mon Sep 17 00:00:00 2001 From: N3WT DE COMPET Date: Sun, 18 May 2025 00:45:49 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Configuration=20des=20comp=C3=A9tences?= =?UTF-8?q?=20par=20cycle=20[#16]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Back-End/Establishment/views.py | 16 +- Back-End/GestionMessagerie/models.py | 1 + Back-End/School/models.py | 37 +- Back-End/School/serializers.py | 38 +- Back-End/School/signals.py | 9 +- Back-End/School/urls.py | 40 +- Back-End/School/views.py | 360 +++++++++++++++++- .../src/app/[locale]/admin/directory/page.js | 4 +- Front-End/src/app/[locale]/admin/layout.js | 2 +- .../structure/SchoolClassManagement/page.js | 2 +- .../src/app/[locale]/admin/structure/page.js | 30 ++ .../app/[locale]/admin/subscriptions/page.js | 4 +- .../app/[locale]/users/password/new/page.js | 2 +- Front-End/src/app/actions/schoolAction.js | 37 ++ Front-End/src/components/CheckBox.js | 2 - Front-End/src/components/DateTab.js | 2 +- .../components/Inscription/FilesToUpload.js | 4 +- Front-End/src/components/Modal.js | 8 +- .../src/components/PaymentPlanSelector.js | 2 +- .../Competencies/CompetenciesList.js | 310 +++++++++++++++ .../Structure/Competencies/TreeView.js | 144 +++++++ .../Structure/Configuration/ClassesSection.js | 6 +- .../Configuration/SpecialitiesSection.js | 6 +- .../Configuration/TeachersSection.js | 6 +- .../Structure/Files/FileUploadDocuSeal.js | 2 +- .../Structure/Files/FilesGroupsManagement.js | 2 +- .../Structure/Files/ParentFilesSection.js | 2 +- .../Tarification/DiscountsSection.js | 4 +- Front-End/src/utils/Url.js | 1 + 29 files changed, 1001 insertions(+), 82 deletions(-) create mode 100644 Front-End/src/components/Structure/Competencies/CompetenciesList.js create mode 100644 Front-End/src/components/Structure/Competencies/TreeView.js diff --git a/Back-End/Establishment/views.py b/Back-End/Establishment/views.py index df48324..76b7407 100644 --- a/Back-End/Establishment/views.py +++ b/Back-End/Establishment/views.py @@ -6,7 +6,9 @@ from rest_framework.views import APIView from rest_framework import status from .models import Establishment from .serializers import EstablishmentSerializer -from N3wtSchool.bdd import delete_object, getAllObjects, getObject +from N3wtSchool.bdd import delete_object, getAllObjects +from School.models import Competency, EstablishmentCompetency +from django.db.models import Q @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') @@ -20,7 +22,17 @@ class EstablishmentListCreateView(APIView): establishment_data = JSONParser().parse(request) establishment_serializer = EstablishmentSerializer(data=establishment_data) if establishment_serializer.is_valid(): - establishment_serializer.save() + establishment = establishment_serializer.save() + # Création des EstablishmentCompetency pour chaque compétence existante + competencies = Competency.objects.filter( + Q(end_of_cycle=True) | ~Q(level=None) + ) + for competency in competencies: + EstablishmentCompetency.objects.get_or_create( + establishment=establishment, + competency=competency, + defaults={'is_required': True} + ) return JsonResponse(establishment_serializer.data, safe=False, status=status.HTTP_201_CREATED) return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) diff --git a/Back-End/GestionMessagerie/models.py b/Back-End/GestionMessagerie/models.py index 51b1c0e..6dec066 100644 --- a/Back-End/GestionMessagerie/models.py +++ b/Back-End/GestionMessagerie/models.py @@ -1,5 +1,6 @@ from django.db import models from Auth.models import Profile +from django.utils import timezone class Messagerie(models.Model): id = models.AutoField(primary_key=True) diff --git a/Back-End/School/models.py b/Back-End/School/models.py index b5a48f6..d1cfa21 100644 --- a/Back-End/School/models.py +++ b/Back-End/School/models.py @@ -165,31 +165,24 @@ class Competency(models.Model): class EstablishmentCompetency(models.Model): """ Relation entre un établissement et une compétence. - Permet de définir quelles compétences sont sélectionnées par un établissement. + Peut référencer une compétence de référence OU une compétence custom propre à l'établissement. """ establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE) - competency = models.ForeignKey(Competency, on_delete=models.CASCADE) - is_selected = models.BooleanField(default=False) + competency = models.ForeignKey( + Competency, on_delete=models.CASCADE, null=True, blank=True, + help_text="Compétence de référence (optionnelle si custom)" + ) + custom_name = models.TextField(null=True, blank=True, help_text="Nom de la compétence custom") + custom_category = models.ForeignKey( + Category, on_delete=models.CASCADE, null=True, blank=True, + help_text="Catégorie de la compétence custom" + ) + is_required = models.BooleanField(default=True) class Meta: - unique_together = ('establishment', 'competency') + unique_together = ('establishment', 'competency', 'custom_name', 'custom_category') def __str__(self): - return f"{self.establishment.name} - {self.competency.name}" - - -# class StudentCompetency(models.Model): -# """ -# Relation entre un élève et une compétence. -# Permet d'attribuer une note à un élève pour une compétence. -# """ -# student = models.ForeignKey('Subscriptions.Student', on_delete=models.CASCADE, related_name='competency_scores') -# competency = models.ForeignKey(Competency, on_delete=models.CASCADE, related_name='student_scores') -# score = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) # Note attribuée -# comment = models.TextField(blank=True, null=True) # Commentaire facultatif - -# class Meta: -# unique_together = ('student', 'competency') - -# def __str__(self): -# return f"{self.student} - {self.competency.name} - Score: {self.score}" \ No newline at end of file + if self.competency: + return f"{self.establishment.name} - {self.competency.name}" + return f"{self.establishment.name} - {self.custom_name} (custom)" \ No newline at end of file diff --git a/Back-End/School/serializers.py b/Back-End/School/serializers.py index b348ec2..72cb883 100644 --- a/Back-End/School/serializers.py +++ b/Back-End/School/serializers.py @@ -1,13 +1,47 @@ from rest_framework import serializers -from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES, Discount, Fee, PaymentPlan, PaymentMode +from .models import ( + Teacher, + Speciality, + SchoolClass, + Planning, + LEVEL_CHOICES, + Discount, + Fee, + PaymentPlan, + PaymentMode, + Domain, + Category, + Competency, + EstablishmentCompetency +) from Auth.models import Profile, ProfileRole from Subscriptions.models import Student from Establishment.models import Establishment from Auth.serializers import ProfileRoleSerializer -from N3wtSchool import settings, bdd +from N3wtSchool import settings from django.utils import timezone import pytz +class DomainSerializer(serializers.ModelSerializer): + class Meta: + model = Domain + fields = '__all__' + +class CategorySerializer(serializers.ModelSerializer): + class Meta: + model = Category + fields = '__all__' + +class CompetencySerializer(serializers.ModelSerializer): + class Meta: + model = Competency + fields = '__all__' + +class EstablishmentCompetencySerializer(serializers.ModelSerializer): + class Meta: + model = EstablishmentCompetency + fields = '__all__' + class SpecialitySerializer(serializers.ModelSerializer): updated_date_formatted = serializers.SerializerMethodField() diff --git a/Back-End/School/signals.py b/Back-End/School/signals.py index 7f37ca5..1246564 100644 --- a/Back-End/School/signals.py +++ b/Back-End/School/signals.py @@ -2,7 +2,8 @@ import json import os from django.db.models.signals import post_migrate from django.dispatch import receiver -from School.models import Domain, Category, Competency +from Establishment.models import Establishment +from School.models import Domain, Category, Competency, EstablishmentCompetency @receiver(post_migrate) def load_json_data(sender, **kwargs): @@ -32,15 +33,15 @@ def load_json_data(sender, **kwargs): for domain_data in data['domaines']: # Vérifiez si le domaine existe déjà - domain, created = Domain.objects.get_or_create(name=domain_data['nom'], cycle=cycle) + domain, _ = Domain.objects.get_or_create(name=domain_data['nom'], cycle=cycle) for category_data in domain_data['categories']: # Vérifiez si la catégorie existe déjà - category, created = Category.objects.get_or_create(name=category_data['nom'], domain=domain) + category, _ = Category.objects.get_or_create(name=category_data['nom'], domain=domain) for competency_data in category_data['competences']: # Vérifiez si la compétence existe déjà - Competency.objects.get_or_create( + competency, _ = Competency.objects.get_or_create( name=competency_data['nom'], end_of_cycle=competency_data.get('fin_cycle', False), level=competency_data.get('niveau'), diff --git a/Back-End/School/urls.py b/Back-End/School/urls.py index ab08dad..a18880e 100644 --- a/Back-End/School/urls.py +++ b/Back-End/School/urls.py @@ -1,22 +1,18 @@ from django.urls import path, re_path from .views import ( - TeacherListCreateView, - TeacherDetailView, - SpecialityListCreateView, - SpecialityDetailView, - SchoolClassListCreateView, - SchoolClassDetailView, - PlanningListCreateView, - PlanningDetailView, - FeeListCreateView, - FeeDetailView, - DiscountListCreateView, - DiscountDetailView, - PaymentPlanListCreateView, - PaymentPlanDetailView, - PaymentModeListCreateView, - PaymentModeDetailView, + TeacherListCreateView, TeacherDetailView, + SpecialityListCreateView, SpecialityDetailView, + SchoolClassListCreateView, SchoolClassDetailView, + PlanningListCreateView, PlanningDetailView, + FeeListCreateView, FeeDetailView, + DiscountListCreateView, DiscountDetailView, + PaymentPlanListCreateView, PaymentPlanDetailView, + PaymentModeListCreateView, PaymentModeDetailView, + DomainListCreateView, DomainDetailView, + CategoryListCreateView, CategoryDetailView, + CompetencyListCreateView, CompetencyDetailView, + EstablishmentCompetencyListCreateView, EstablishmentCompetencyDetailView, ) urlpatterns = [ @@ -43,4 +39,16 @@ urlpatterns = [ re_path(r'^paymentModes$', PaymentModeListCreateView.as_view(), name="payment_mode_list_create"), re_path(r'^paymentModes/(?P[0-9]+)$', PaymentModeDetailView.as_view(), name="payment_mode_detail"), + + re_path(r'^domains$', DomainListCreateView.as_view(), name="domain_list_create"), + re_path(r'^domains/(?P[0-9]+)$', DomainDetailView.as_view(), name="domain_detail"), + + re_path(r'^categories$', CategoryListCreateView.as_view(), name="category_list_create"), + re_path(r'^categories/(?P[0-9]+)$', CategoryDetailView.as_view(), name="category_detail"), + + re_path(r'^competencies$', CompetencyListCreateView.as_view(), name="competency_list_create"), + re_path(r'^competencies/(?P[0-9]+)$', CompetencyDetailView.as_view(), name="competency_detail"), + + re_path(r'^establishmentCompetencies$', EstablishmentCompetencyListCreateView.as_view(), name="establishment_competency_list_create"), + re_path(r'^establishmentCompetencies/(?P[0-9]+)$', EstablishmentCompetencyDetailView.as_view(), name="establishment_competency_detail"), ] \ No newline at end of file diff --git a/Back-End/School/views.py b/Back-End/School/views.py index 4979c72..7448312 100644 --- a/Back-End/School/views.py +++ b/Back-End/School/views.py @@ -12,7 +12,11 @@ from .models import ( Discount, Fee, PaymentPlan, - PaymentMode + PaymentMode, + Domain, + Category, + Competency, + EstablishmentCompetency ) from .serializers import ( TeacherSerializer, @@ -22,9 +26,15 @@ from .serializers import ( DiscountSerializer, FeeSerializer, PaymentPlanSerializer, - PaymentModeSerializer + PaymentModeSerializer, + DomainSerializer, + CategorySerializer, + CompetencySerializer, + EstablishmentCompetencySerializer ) from N3wtSchool.bdd import delete_object, getAllObjects, getObject +from django.db.models import Q +from collections import defaultdict @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') @@ -411,3 +421,349 @@ class PaymentModeDetailView(APIView): def delete(self, request, id): return delete_object(PaymentMode, id) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class DomainListCreateView(APIView): + def get(self, request): + domains = Domain.objects.all() + serializer = DomainSerializer(domains, many=True) + return JsonResponse(serializer.data, safe=False) + + def post(self, request): + data = JSONParser().parse(request) + serializer = DomainSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class DomainDetailView(APIView): + def get(self, request, id): + try: + domain = Domain.objects.get(id=id) + serializer = DomainSerializer(domain) + return JsonResponse(serializer.data, safe=False) + except Domain.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + domain = Domain.objects.get(id=id) + except Domain.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = DomainSerializer(domain, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + domain = Domain.objects.get(id=id) + domain.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except Domain.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + +# Répète la même logique pour Category, Competency, EstablishmentCompetency + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CategoryListCreateView(APIView): + def get(self, request): + categories = Category.objects.all() + serializer = CategorySerializer(categories, many=True) + return JsonResponse(serializer.data, safe=False) + + def post(self, request): + data = JSONParser().parse(request) + serializer = CategorySerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CategoryDetailView(APIView): + def get(self, request, id): + try: + category = Category.objects.get(id=id) + serializer = CategorySerializer(category) + return JsonResponse(serializer.data, safe=False) + except Category.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + category = Category.objects.get(id=id) + except Category.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = CategorySerializer(category, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + category = Category.objects.get(id=id) + category.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except Category.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CompetencyListCreateView(APIView): + def get(self, request): + cycle = request.GET.get('cycle') + if cycle is None: + return JsonResponse({'error': 'cycle est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + competencies_list = getAllObjects(Competency) + competencies_list = competencies_list.filter( + category__domain__cycle=cycle + ).distinct() + serializer = CompetencySerializer(competencies_list, many=True) + print(f'len : {competencies_list.count()}') + return JsonResponse(serializer.data, safe=False) + + def post(self, request): + data = JSONParser().parse(request) + serializer = CompetencySerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CompetencyDetailView(APIView): + def get(self, request, id): + try: + competency = Competency.objects.get(id=id) + serializer = CompetencySerializer(competency) + return JsonResponse(serializer.data, safe=False) + except Competency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + competency = Competency.objects.get(id=id) + except Competency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = CompetencySerializer(competency, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + competency = Competency.objects.get(id=id) + competency.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except Competency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class EstablishmentCompetencyListCreateView(APIView): + def get(self, request): + establishment_id = request.GET.get('establishment_id') + cycle = request.GET.get('cycle') + if not establishment_id or not cycle: + return JsonResponse({'error': 'establishment_id et cycle sont requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + # Toutes les compétences du cycle + competencies = Competency.objects.filter(category__domain__cycle=cycle).select_related('category', 'category__domain') + + # Récupérer les EstablishmentCompetency pour l'établissement et le cycle + ec_qs = EstablishmentCompetency.objects.filter( + Q(competency__in=competencies) | Q(competency__isnull=True), + establishment_id=establishment_id + ).select_related('competency', 'custom_category') + + # Required = compétences de référence requises + required_ids = set(ec_qs.filter(is_required=True, competency__isnull=False).values_list('competency_id', flat=True)) + + # Custom = compétences custom (pas de lien vers Competency) + custom_ecs = ec_qs.filter(is_required=False, competency__isnull=True) + + # Organisation par domaine > catégorie + result = [] + selected_count = 0 + domaines = Domain.objects.filter(cycle=cycle) + for domaine in domaines: + domaine_dict = { + "domaine_id": domaine.id, + "domaine_nom": domaine.name, + "categories": [] + } + categories = domaine.categories.all() + for categorie in categories: + categorie_dict = { + "categorie_id": categorie.id, + "categorie_nom": categorie.name, + "competences": [] + } + + # Liste des noms de compétences custom pour cette catégorie + custom_for_cat = custom_ecs.filter(custom_category=categorie) + custom_names = set(ec.custom_name.strip().lower() for ec in custom_for_cat if ec.custom_name) + + # Compétences de référence (on saute celles qui sont déjà en custom) + competences = categorie.competencies.all() + for comp in competences: + if comp.name.strip().lower() in custom_names: + continue # On n'affiche pas la compétence de référence si une custom du même nom existe + if comp.id in required_ids: + state = "required" + selected_count += 1 + else: + state = "none" + categorie_dict["competences"].append({ + "competence_id": comp.id, + "nom": comp.name, + "state": state + }) + + # Ajout des compétences custom + for ec in custom_for_cat: + categorie_dict["competences"].append({ + "competence_id": ec.id, + "nom": ec.custom_name, + "state": "custom" + }) + selected_count += 1 + + domaine_dict["categories"].append(categorie_dict) + result.append(domaine_dict) + + return JsonResponse({ + "selected_count": selected_count, + "data": result + }, safe=False) + + def post(self, request): + """ + Crée une ou plusieurs compétences custom pour un établissement (is_required=False) + Attendu dans le body : + [ + { "establishment_id": ..., "category_id": ..., "nom": ... }, + ... + ] + """ + data = JSONParser().parse(request) + # Si data est un dict (un seul objet), on le met dans une liste + if isinstance(data, dict): + data = [data] + + created = [] + errors = [] + + for item in data: + establishment_id = item.get("establishment_id") + category_id = item.get("category_id") + nom = item.get("nom") + + if not establishment_id or not category_id or not nom: + errors.append({"error": "establishment_id, category_id et nom sont requis", "item": item}) + continue + + try: + category = Category.objects.get(id=category_id) + # Vérifier si une compétence custom du même nom existe déjà pour cet établissement et cette catégorie + ec_exists = EstablishmentCompetency.objects.filter( + establishment_id=establishment_id, + competency__isnull=True, + custom_name=nom, + ).exists() + if ec_exists: + errors.append({"error": "Une compétence custom de ce nom existe déjà pour cet établissement", "item": item}) + continue + + ec = EstablishmentCompetency.objects.create( + establishment_id=establishment_id, + competency=None, + custom_name=nom, + custom_category=category, + is_required=False + ) + created.append({ + "competence_id": ec.id, + "nom": ec.custom_name, + "state": "custom" + }) + except Exception as e: + errors.append({"error": str(e), "item": item}) + + status_code = status.HTTP_201_CREATED if created else status.HTTP_400_BAD_REQUEST + return JsonResponse({ + "created": created, + "errors": errors + }, status=status_code, safe=False) + + def delete(self, request): + """ + Supprime une ou plusieurs compétences custom (EstablishmentCompetency) à partir d'une liste d'IDs. + Attendu dans le body : + { + "ids": [1, 2, 3, ...] + } + """ + data = JSONParser().parse(request) + ids = data.get("ids", []) + deleted = [] + errors = [] + + for ec_id in ids: + try: + ec = EstablishmentCompetency.objects.get(id=ec_id) + ec.delete() + deleted.append(ec_id) + except EstablishmentCompetency.DoesNotExist: + errors.append({"id": ec_id, "error": "No object found"}) + + return JsonResponse({ + "deleted": deleted, + "errors": errors + }, safe=False) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class EstablishmentCompetencyDetailView(APIView): + def get(self, request, id): + try: + ec = EstablishmentCompetency.objects.get(id=id) + serializer = EstablishmentCompetencySerializer(ec) + return JsonResponse(serializer.data, safe=False) + except EstablishmentCompetency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + ec = EstablishmentCompetency.objects.get(id=id) + except EstablishmentCompetency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = EstablishmentCompetencySerializer(ec, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + ec = EstablishmentCompetency.objects.get(id=id) + ec.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except EstablishmentCompetency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) diff --git a/Front-End/src/app/[locale]/admin/directory/page.js b/Front-End/src/app/[locale]/admin/directory/page.js index 935e1d9..e052b0e 100644 --- a/Front-End/src/app/[locale]/admin/directory/page.js +++ b/Front-End/src/app/[locale]/admin/directory/page.js @@ -557,13 +557,13 @@ export default function Page() { /> {/* Popups */} setPopupVisible(false)} uniqueConfirmButton={true} /> setConfirmPopupVisible(false)} diff --git a/Front-End/src/app/[locale]/admin/layout.js b/Front-End/src/app/[locale]/admin/layout.js index dc0001b..d454130 100644 --- a/Front-End/src/app/[locale]/admin/layout.js +++ b/Front-End/src/app/[locale]/admin/layout.js @@ -182,7 +182,7 @@ export default function Layout({ children }) {