diff --git a/apps/family/api/urls.py b/apps/family/api/urls.py index e94776d7..35cf1409 100644 --- a/apps/family/api/urls.py +++ b/apps/family/api/urls.py @@ -1,6 +1,7 @@ # Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.urls import path + from .views import FamilyViewSet, FamilyMembershipViewSet, ChallengeViewSet, AchievementViewSet, BatchAchievementsAPIView @@ -13,6 +14,7 @@ def register_family_urls(router, path): router.register(path + '/challenge', ChallengeViewSet) router.register(path + '/achievement', AchievementViewSet) + urlpatterns = [ path('achievements/batch/', BatchAchievementsAPIView.as_view(), name='batch_achievements') -] \ No newline at end of file +] diff --git a/apps/family/api/views.py b/apps/family/api/views.py index d568c1c6..50ac0496 100644 --- a/apps/family/api/views.py +++ b/apps/family/api/views.py @@ -8,10 +8,6 @@ from rest_framework.views import APIView from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework import status -from django.views.decorators.csrf import csrf_exempt -from django.views.decorators.http import require_POST -from django.http import JsonResponse -import json from .serializers import FamilySerializer, FamilyMembershipSerializer, ChallengeSerializer, AchievementSerializer from ..models import Family, FamilyMembership, Challenge, Achievement @@ -71,6 +67,7 @@ class AchievementViewSet(ReadProtectedModelViewSet): class BatchAchievementsAPIView(APIView): permission_classes = [IsAuthenticated] + def post(self, request, format=None): print("POST de la view spéciale") family_ids = request.data.get('families', []) @@ -88,4 +85,4 @@ class BatchAchievementsAPIView(APIView): family.update_score() Family.update_ranking() - return Response({'status': 'ok'}, status=status.HTTP_201_CREATED) \ No newline at end of file + return Response({'status': 'ok'}, status=status.HTTP_201_CREATED) diff --git a/apps/family/tables.py b/apps/family/tables.py index f7eb2a16..871dfd35 100644 --- a/apps/family/tables.py +++ b/apps/family/tables.py @@ -3,6 +3,8 @@ import django_tables2 as tables from django.urls import reverse +from django.utils.translation import gettext_lazy as _ +from django_tables2 import A from .models import Family, Challenge, FamilyMembership, Achievement @@ -63,11 +65,28 @@ class AchievementTable(tables.Table): """ List recent achievements. """ + delete = tables.LinkColumn( + 'family:achievement_delete', + args=[A('id')], + verbose_name=_("Delete"), + text=_("Delete"), + orderable=False, + attrs={ + 'th': { + 'id': 'delete-membership-header' + }, + 'a': { + 'class': 'btn btn-danger', + 'data-type': 'delete-membership' + } + }, + ) + class Meta: attrs = { 'class': 'table table-condensed table-striped table-hover' } model = Achievement - fields = ('family', 'challenge', 'obtained_at', ) + fields = ('family', 'challenge', 'challenge__points', 'obtained_at', ) template_name = 'django_tables2/bootstrap4.html' - orderable = False + order_by = ('-obtained_at',) diff --git a/apps/family/templates/family/achievement_confirm_delete.html b/apps/family/templates/family/achievement_confirm_delete.html new file mode 100644 index 00000000..893e0afb --- /dev/null +++ b/apps/family/templates/family/achievement_confirm_delete.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% load i18n crispy_forms_tags %} + +{% block content %} +
+
+

{% trans "Delete achievement" %}

+
+
+
+ {% blocktrans %}Are you sure you want to delete this achievement? This action can't be undone.{% endblocktrans %} +
+
+ +
+{% endblock %} diff --git a/apps/family/templates/family/achievement_list.html b/apps/family/templates/family/achievement_list.html new file mode 100644 index 00000000..fe1fd62f --- /dev/null +++ b/apps/family/templates/family/achievement_list.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% comment %} +Copyright (C) 2018-2025 by BDE ENS Paris-Saclay +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% load i18n django_tables2 %} + +{% block content %} + +
+
+

+ {% trans "Recent achievements history" %} +

+ + {% trans "Return to management page" %} + +
+ {% render_table table %} +
+ +{% endblock %} \ No newline at end of file diff --git a/apps/family/templates/family/manage.html b/apps/family/templates/family/manage.html index 275337fa..0ecac60a 100644 --- a/apps/family/templates/family/manage.html +++ b/apps/family/templates/family/manage.html @@ -143,24 +143,21 @@ SPDX-License-Identifier: GPL-3.0-or-later - {# Mode switch #} - {# transaction history #} -
-
-

+

+ +
+ {% render_table table %}
- {% render_table table %}
{% endblock %} diff --git a/apps/family/urls.py b/apps/family/urls.py index ee491ecc..072cbada 100644 --- a/apps/family/urls.py +++ b/apps/family/urls.py @@ -18,5 +18,7 @@ urlpatterns = [ path('challenge/detail//', views.ChallengeDetailView.as_view(), name="challenge_detail"), path('challenge/update//', views.ChallengeUpdateView.as_view(), name="challenge_update"), path('manage/', views.FamilyManageView.as_view(), name="manage"), + path('achievements/', views.AchievementsView.as_view(), name="achievement_list"), + path('achievement/delete//', views.AchievementDeleteView.as_view(), name="achievement_delete"), path('api/family/', include('family.api.urls')), ] diff --git a/apps/family/views.py b/apps/family/views.py index 35f073fb..6325c445 100644 --- a/apps/family/views.py +++ b/apps/family/views.py @@ -7,6 +7,7 @@ from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin from django.db import transaction from django.views.generic import DetailView, UpdateView +from django.views.generic.edit import DeleteView from django.utils.translation import gettext_lazy as _ from django_tables2 import SingleTableView from permission.backends import PermissionBackend @@ -195,7 +196,7 @@ class ChallengeCreateView(ProtectQuerysetMixin, ProtectedCreateView): ) def get_success_url(self): - return reverse_lazy('family:challenge_list') + return reverse_lazy('family:manage') class ChallengeListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): @@ -264,7 +265,7 @@ class FamilyManageView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView # retrieves only Transaction that user has the right to see. return Achievement.objects.filter( PermissionBackend.filter_queryset(self.request, Achievement, "view") - ).order_by("-obtained_at").all()[:20] + ).order_by("-obtained_at").all() def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -277,3 +278,33 @@ class FamilyManageView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView context["can_add_challenge"] = PermissionBackend.check_perm(self.request, "family.add_challenge") return context + + def get_table(self, **kwargs): + table = super().get_table(**kwargs) + table.exclude = ('delete',) + table.orderable = False + return table + + +class AchievementsView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): + """ + List all achievements + """ + model = Achievement + table_class = AchievementTable + extra_context = {'title': _('Achievement list')} + + def get_table(self, **kwargs): + table = super().get_table(**kwargs) + table.orderable = True + return table + + +class AchievementDeleteView(ProtectQuerysetMixin, LoginRequiredMixin, DeleteView): + """ + Delete an Achievement + """ + model = Achievement + + def get_success_url(self): + return reverse_lazy('family:achievement_list') diff --git a/apps/treasury/views.py b/apps/treasury/views.py index eab144c3..eb2fd0d7 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -168,7 +168,7 @@ class InvoiceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): class InvoiceDeleteView(ProtectQuerysetMixin, LoginRequiredMixin, DeleteView): """ - Delete a non-validated WEI registration + Delete a non-locked Invoice """ model = Invoice extra_context = {"title": _("Delete invoice")}