diff --git a/scipost_django/colleges/forms.py b/scipost_django/colleges/forms.py index ac2d097747820d14f6f8f61406ee1ac3f52d7ef7..8f4ab75e21020a85382614f9614e4593998d7f5d 100644 --- a/scipost_django/colleges/forms.py +++ b/scipost_django/colleges/forms.py @@ -35,6 +35,7 @@ from .models import ( FellowshipNominationComment, FellowshipNominationDecision, FellowshipNominationVotingRound, + FellowshipNominationEvent, FellowshipInvitation, ) from .constants import ( @@ -1088,6 +1089,65 @@ class FellowshipNominationVotingRoundStartForm(forms.ModelForm): ) +class FellowshipNominationVetoForm(forms.Form): + edadmin_comments = forms.CharField( + label="Comments for editorial administration", + widget=forms.Textarea(attrs={"rows": 4}), + required=True, + ) + + fellow_comments = forms.CharField( + label="Comments for voting Fellows", + widget=forms.Textarea(attrs={"rows": 4}), + required=True, + ) + + def __init__(self, *args, **kwargs): + self.fellow = kwargs.pop("fellow", None) + self.nomination = kwargs.pop("nomination", None) + super().__init__(*args, **kwargs) + + self.helper = FormHelper() + self.helper.attrs = { + "hx-post": reverse( + "colleges:_hx_nomination_veto", + kwargs={"nomination_id": self.nomination.id}, + ), + "hx-target": "closest .veto-btn-container", + "hx-swap": "outerHTML", + } + + self.helper.layout = Layout( + Div( + Div(Field("edadmin_comments"), css_class="col"), + Div(Field("fellow_comments"), css_class="col"), + Div( + ButtonHolder(Submit("submit", "Veto", css_class="btn btn-dark")), + css_class="col-auto d-flex align-items-end", + ), + css_class="row mb-0", + ), + ) + + def save(self): + self.nomination.vetoes.add(self.fellow) + self.nomination.save() + + # Fellow's comments are added as a regular comment + FellowshipNominationComment.objects.create( + nomination=self.nomination, + by=self.fellow.contributor, + text=self.cleaned_data["fellow_comments"], + ) + + # EdAdmin's comments are added as an event + FellowshipNominationEvent.objects.create( + nomination=self.nomination, + by=self.fellow.contributor, + description=f"Vetoed with justification: {self.cleaned_data['edadmin_comments']}", + ) + + ############### # Invitations # ############### diff --git a/scipost_django/colleges/templates/colleges/_hx_nomination_details_contents.html b/scipost_django/colleges/templates/colleges/_hx_nomination_details_contents.html index 0f5c52223890bc566cc600f381707365e87ba304..534e31b62e46591fc1397b2a4bd6389e100f6537 100644 --- a/scipost_django/colleges/templates/colleges/_hx_nomination_details_contents.html +++ b/scipost_django/colleges/templates/colleges/_hx_nomination_details_contents.html @@ -7,16 +7,29 @@ <div id="profile-{{ nomination.profile.id }}-specialties" class="border border-danger mb-4 d-none-empty"></div> - {% if nomination.nominator_comments %} - <div class="col-12 mb-3"> - <div class="card"> - <div class="card-header">Nominator motivation</div> - <div class="card-body">{{ nomination.nominated_by.profile.full_name }}: {{ nomination.nominator_comments }}</div> + <div class="col mb-3"> + <div class="card"> + <div class="card-header">Nominator motivation</div> + <div class="card-body"> + + {% if nomination.nominator_comments %} + <strong>{{ nomination.nominated_by.profile.full_name }}:</strong> {{ nomination.nominator_comments }} + {% else %} + No motivation provided. + {% endif %} + </div> </div> + </div> + + + {% if not is_ed_admin %} + {% include "colleges/_hx_nomination_veto_btn.html" %} {% endif %} - <div class="col-12 col-md mb-3"> + </div> + <div class="row mb-0"> + <div class="col-12 col-md-6 mb-3"> <div class="card"> <div class="card-header">Details</div> <div class="card-body p-0"> @@ -65,7 +78,7 @@ </div> </div> </div> - <div class="col-12 col-md mb-3"> + <div class="col-12 col-md-6 mb-3"> <div class="card"> <div class="card-header">Publications in SciPost Journals</div> <div class="card-body"> diff --git a/scipost_django/colleges/templates/colleges/_hx_nomination_veto.html b/scipost_django/colleges/templates/colleges/_hx_nomination_veto.html new file mode 100644 index 0000000000000000000000000000000000000000..5d999d68cba5005ed904fdc07e8ed4bff0a86afa --- /dev/null +++ b/scipost_django/colleges/templates/colleges/_hx_nomination_veto.html @@ -0,0 +1,10 @@ +{% load crispy_forms_tags %} + +<div class="veto-btn-container col-12 border border-dark p-2"> + <h3>Exercise your right to veto:</h3> + <p> + Use this form to veto a nomination by providing some justification for your decision. + Comments provided to fellows will be visible to all other fellows, while those to editorial administration will remain private. + </p> + {% crispy form %} +</div> diff --git a/scipost_django/colleges/templates/colleges/_hx_nomination_veto_btn.html b/scipost_django/colleges/templates/colleges/_hx_nomination_veto_btn.html new file mode 100644 index 0000000000000000000000000000000000000000..d72e65026e458f1737eb7c01c255b1c0d4b8d1e7 --- /dev/null +++ b/scipost_django/colleges/templates/colleges/_hx_nomination_veto_btn.html @@ -0,0 +1,18 @@ +<div class="col-auto d-flex flex-column justify-content-between mb-3 veto-btn-container"> + + {% if fellow in nomination.vetoes.all %} + <div class="bg-dark p-2 text-center"> + <span class="fs-5 text-white">Nomination + <br /> + Vetoed</span> + </div> + {% else %} + <div class="fs-5 text-muted align-self-center">Something wrong?</div> + <button class="btn btn-sm btn-dark" + hx-get="{% url "colleges:_hx_nomination_veto" nomination_id=nomination.id %}" + hx-target="closest .veto-btn-container"> + <span class="text-white">Veto</span> + </button> + {% endif %} + +</div> diff --git a/scipost_django/colleges/urls.py b/scipost_django/colleges/urls.py index 3627fbf1716e547ce92a6352b82d67f6fb4b9ccb..4435ae8d3b1f23e85c2e553cdc14244f18bab987 100644 --- a/scipost_django/colleges/urls.py +++ b/scipost_django/colleges/urls.py @@ -216,6 +216,11 @@ urlpatterns = [ views._hx_nomination_comments, name="_hx_nomination_comments", ), + path( + "_hx_veto", + views._hx_nomination_veto, + name="_hx_nomination_veto", + ), ] ), ), diff --git a/scipost_django/colleges/views.py b/scipost_django/colleges/views.py index 1e033d68abde26043a169582743f2f41e78671c7..b0d0aea1e9acfb537f7ee2a15841c0f35a276832 100644 --- a/scipost_django/colleges/views.py +++ b/scipost_django/colleges/views.py @@ -43,6 +43,7 @@ from .constants import ( from .forms import ( CollegeChoiceForm, FellowshipNominationSearchForm, + FellowshipNominationVetoForm, FellowshipNominationVotingRoundStartForm, FellowshipSearchForm, FellowshipDynSelForm, @@ -770,12 +771,14 @@ def _hx_nomination_round_remove_voter(request, round_id, voter_id): def _hx_nomination_details_contents(request, nomination_id): """For (re)loading the details if modified.""" nomination = get_object_or_404(FellowshipNomination, pk=nomination_id) + fellow = request.user.contributor.session_fellowship(request) start_email_sent = nomination.events.filter( description__contains="start email sent", ).exists() context = { "nomination": nomination, "start_email_sent": start_email_sent, + "fellow": fellow, } return render(request, "colleges/_hx_nomination_details_contents.html", context) @@ -942,6 +945,42 @@ def _hx_nomination_vote(request, round_id): return render(request, "colleges/_hx_nomination_vote.html", context) +@login_required +@user_passes_test(is_edadmin_or_advisory_or_active_regular_or_senior_fellow) +def _hx_nomination_veto(request, nomination_id): + fellow = request.user.contributor.session_fellowship(request) + nomination = get_object_or_404(FellowshipNomination, pk=nomination_id) + + # Check that vetoer is a sneior fellow is in the same college as the nomination + if ( + not fellow.contributor.fellowships.filter(college=nomination.college) + .senior() + .active() + .exists() + ): + return HTMXResponse( + """You are not an active senior fellow of this college.""", + tag="danger", + ) + + form = FellowshipNominationVetoForm( + request.POST or None, fellow=fellow, nomination=nomination + ) + + if form.is_valid(): + form.save() + return render( + request, + "colleges/_hx_nomination_veto_btn.html", + { + "nomination": nomination, + "fellow": fellow, + }, + ) + + return render(request, "colleges/_hx_nomination_veto.html", {"form": form}) + + @login_required @user_passes_test(is_edadmin) def _hx_voting_round_start_form(request, round_id):