From 635ae4c87d746c8792e05a14736fad1d4d80dc36 Mon Sep 17 00:00:00 2001 From: Simen Myrrusten <76472217+nemisis84@users.noreply.github.com> Date: Tue, 17 Sep 2024 20:55:05 +0200 Subject: [PATCH 1/6] Added backend endpoint to unprocessed applicants (#1380) * Added backend endpoint to unprocessed applicants * Formatting --- backend/samfundet/serializers.py | 41 ++++++++++++++++++++++++++++++++ backend/samfundet/urls.py | 5 ++++ backend/samfundet/views.py | 20 ++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/backend/samfundet/serializers.py b/backend/samfundet/serializers.py index a26401c3b..d2032f4ee 100644 --- a/backend/samfundet/serializers.py +++ b/backend/samfundet/serializers.py @@ -962,6 +962,47 @@ def get_interview_time(self, instance: RecruitmentApplication) -> str | None: return instance.interview.interview_time if instance.interview else None +class RecruitmentBasicUserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ['id', 'first_name', 'last_name', 'email', 'phone_number'] + + +class RecruitmentRecruitmentPositionSerializer(serializers.ModelSerializer): + class Meta: + model = RecruitmentPosition + fields = ['id', 'name_nb', 'name_en'] + + +class RecruitmentShowUnprocessedApplicationsSerializer(serializers.ModelSerializer): + user = RecruitmentBasicUserSerializer(read_only=True) + recruitment_position = RecruitmentRecruitmentPositionSerializer(read_only=True) + + class Meta: + model = RecruitmentApplication + fields = [ + 'id', + 'recruitment', + 'user', + 'applicant_priority', + 'recruitment_position', + 'recruiter_status', + 'recruiter_priority', + ] + read_only_fields = [ + 'id', + 'recruitment', + 'user', + 'applicant_priority', + 'recruitment_position', + 'recruiter_status', + 'recruiter_priority', + ] + + def get_recruitment_position(self, instance: RecruitmentApplication) -> str: + return instance.recruitment_position.name_nb + + class RecruitmentApplicationForGangSerializer(CustomBaseSerializer): user = ApplicantInfoSerializer(read_only=True) interview = InterviewSerializer(read_only=False) diff --git a/backend/samfundet/urls.py b/backend/samfundet/urls.py index 5d0f78919..2fa8e59cc 100644 --- a/backend/samfundet/urls.py +++ b/backend/samfundet/urls.py @@ -71,6 +71,11 @@ ########## Recruitment ########## path('active-recruitments/', views.ActiveRecruitmentsView.as_view(), name='active_recruitments'), path('recruitment-positions/', views.RecruitmentPositionsPerRecruitmentView.as_view(), name='recruitment_positions'), + path( + 'recruitment-show-unprocessed-applicants/', + views.RecruitmentUnprocessedApplicationsPerRecruitment.as_view(), + name='recruitment_show_unprocessed_applicants', + ), path( 'recruitment-positions-gang-for-applicant/', views.RecruitmentPositionsPerGangForApplicantView.as_view(), diff --git a/backend/samfundet/views.py b/backend/samfundet/views.py index 4a050d2a5..d8f9122ae 100644 --- a/backend/samfundet/views.py +++ b/backend/samfundet/views.py @@ -86,6 +86,7 @@ RecruitmentApplicationForApplicantSerializer, RecruitmentApplicationForRecruiterSerializer, RecruitmentApplicationUpdateForGangSerializer, + RecruitmentShowUnprocessedApplicationsSerializer, ) from .models.event import ( Event, @@ -696,6 +697,25 @@ def get_queryset(self) -> Response | None: return None +@method_decorator(ensure_csrf_cookie, 'dispatch') +class RecruitmentUnprocessedApplicationsPerRecruitment(ListAPIView): + permission_classes = [IsAuthenticated] + serializer_class = RecruitmentShowUnprocessedApplicationsSerializer + + def get_queryset(self) -> Response | None: + """ + Optionally restricts the returned positions to a given recruitment, + by filtering against a `recruitment` query parameter in the URL. + """ + recruitment = self.request.query_params.get('recruitment', None) + if recruitment is not None: + return RecruitmentApplication.objects.filter( + recruitment=recruitment, + recruiter_status=RecruitmentStatusChoices.NOT_SET, + ) + return None + + class ApplicantsWithoutThreeInterviewsCriteriaView(APIView): permission_classes = [IsAuthenticated] From c9fe1d778eb8dfc4242525b2e06b5e75624879d8 Mon Sep 17 00:00:00 2001 From: Simen Seeberg-Rommetveit <99171937+simensee@users.noreply.github.com> Date: Tue, 17 Sep 2024 21:00:14 +0200 Subject: [PATCH 2/6] 1290 remove not set option (#1378) * removed blank alternative dropdown --- frontend/src/Components/Dropdown/Dropdown.tsx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/frontend/src/Components/Dropdown/Dropdown.tsx b/frontend/src/Components/Dropdown/Dropdown.tsx index ac7b4b413..7b7684bb1 100644 --- a/frontend/src/Components/Dropdown/Dropdown.tsx +++ b/frontend/src/Components/Dropdown/Dropdown.tsx @@ -11,7 +11,7 @@ export type DropDownOption = { export type DropdownProps = { className?: string; classNameSelect?: string; - defaultValue?: DropDownOption; // issue 1089 + defaultValue?: DropDownOption; initialValue?: T; disableIcon?: boolean; options?: DropDownOption[]; @@ -41,13 +41,20 @@ export function Dropdown({ * @param e Standard onChange HTML event for dropdown */ function handleChange(e?: ChangeEvent) { - const choice = (e?.currentTarget.value ?? 0) as number; - if (choice >= 0 && choice <= options.length) { + const choice = parseInt(e?.currentTarget.value ?? '0', 10); + if (choice >= 0 && choice < options.length) { onChange?.(options[choice].value); } else { - onChange?.(defaultValue?.value ?? undefined); + onChange?.(defaultValue?.value ?? options[0]?.value); } } + + let initialIndex = 0; + if (initialValue !== undefined) { + initialIndex = options.findIndex((opt) => opt.value === initialValue); + } else if (defaultValue) { + initialIndex = options.findIndex((opt) => opt.value === defaultValue.value); + } return (