From 20074ffb969056d57b745e919a4f0387157cc4b6 Mon Sep 17 00:00:00 2001 From: KEvin Date: Mon, 28 Aug 2023 15:22:25 +0200 Subject: [PATCH 01/11] Add applicant information to admissions --- backend/samfundet/serializers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/samfundet/serializers.py b/backend/samfundet/serializers.py index 32e0a1ee2..97f13789b 100644 --- a/backend/samfundet/serializers.py +++ b/backend/samfundet/serializers.py @@ -512,8 +512,14 @@ class Meta: 'interview_location', ] +class ApplicantInfoSerializer(serializers.ModelSerializer): + + class Meta: + model = User + fields = ['id', 'first_name', 'last_name', 'email'] class RecruitmentAdmissionForGangSerializer(serializers.ModelSerializer): + user = ApplicantInfoSerializer(read_only = True) class Meta: model = RecruitmentAdmission From 002c180716990ab81cbc6971c2d58dee8204abad Mon Sep 17 00:00:00 2001 From: KEvin Date: Tue, 29 Aug 2023 09:30:13 +0200 Subject: [PATCH 02/11] Add id and include entire user object in admission dto --- frontend/src/dto.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/dto.ts b/frontend/src/dto.ts index 16c243963..4d61e6963 100644 --- a/frontend/src/dto.ts +++ b/frontend/src/dto.ts @@ -333,10 +333,11 @@ export type RecruitmentPositionDto = { }; export type RecruitmentAdmissionDto = { + id: number; admission_text: string; recruitment_position: number; recruitment: number; - user: number; + user: UserDto; priority: number; interview_time?: string; interview_location?: string; From 733cbd51cd43326fb45b99995938e2d461f3d6aa Mon Sep 17 00:00:00 2001 From: KEvin Date: Tue, 29 Aug 2023 09:31:36 +0200 Subject: [PATCH 03/11] Add translations for admission overview page --- frontend/src/i18n/constants.ts | 7 +++++++ frontend/src/i18n/translations.ts | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/frontend/src/i18n/constants.ts b/frontend/src/i18n/constants.ts index 84668b90f..1a210c1d4 100644 --- a/frontend/src/i18n/constants.ts +++ b/frontend/src/i18n/constants.ts @@ -68,6 +68,7 @@ export const KEY = { common_venues: 'common_venues', common_sulten: 'common_sulten', common_logout: 'common_logout', + common_go_back: 'common_go_back', common_create: 'common_create', common_search: 'common_search', common_choose: 'common_choose', @@ -147,6 +148,12 @@ export const KEY = { // Recruitment: recruitment_tags: 'recruitment_tags', recruitment_position: 'recruitment_position', + recruitment_applicant: 'recruitment_applicant', + recruitment_interview_time: 'recruitment_interview_time', + recruitment_interview_location: 'recruitment_interview_location', + recruitment_priority: 'recruitment_priority', + recruitment_recruiter_priority: 'recruitment_recruiter_priority', + recruitment_recruiter_status: 'recruitment_recruiter_status', recruitment_duration: 'recruitment_duration', recruitment_funksjonaer: 'recruitment_funksjonaer', recruitment_organization: 'recruitment_organization', diff --git a/frontend/src/i18n/translations.ts b/frontend/src/i18n/translations.ts index 4cfab9f42..bf668456b 100644 --- a/frontend/src/i18n/translations.ts +++ b/frontend/src/i18n/translations.ts @@ -58,6 +58,7 @@ export const nb: Record = { [KEY.common_create]: 'Opprett', [KEY.common_delete]: 'Slett', [KEY.common_logout]: 'Logg ut', + [KEY.common_go_back]: 'Tilbake', [KEY.common_search]: 'Søk', [KEY.common_choose]: 'Velg', [KEY.common_sulten]: 'Lyche', @@ -139,6 +140,12 @@ export const nb: Record = { // Recruitment: [KEY.recruitment_tags]: 'Tags', [KEY.recruitment_position]: 'Stilling', + [KEY.recruitment_applicant]: 'Søker', + [KEY.recruitment_interview_time]: 'Intervjutid', + [KEY.recruitment_interview_location]: 'Intervjusted', + [KEY.recruitment_priority]: 'Søkers prioritet', + [KEY.recruitment_recruiter_priority]: 'Prioritet', + [KEY.recruitment_recruiter_status]: 'Status', [KEY.recruitment_duration]: 'Varighet', [KEY.recruitment_funksjonaer]: 'Funksjonær', [KEY.recruitment_visible_from]: 'Synlig fra', @@ -268,6 +275,7 @@ export const en: Record = { [KEY.common_create]: 'Create', [KEY.common_delete]: 'Delete', [KEY.common_logout]: 'Log out', + [KEY.common_go_back]: 'Go back', [KEY.common_sulten]: 'Lyche', [KEY.common_search]: 'Search', [KEY.common_choose]: 'Choose', @@ -349,6 +357,12 @@ export const en: Record = { // Recruitment: [KEY.recruitment_tags]: 'Tags', [KEY.recruitment_position]: 'Position', + [KEY.recruitment_applicant]: 'Applicant', + [KEY.recruitment_interview_time]: 'Intervjutid', + [KEY.recruitment_interview_location]: 'Intervjusted', + [KEY.recruitment_priority]: 'Søkers prioritet', + [KEY.recruitment_recruiter_priority]: 'Prioritet', + [KEY.recruitment_recruiter_status]: 'Status', [KEY.recruitment_duration]: 'Duration', [KEY.recruitment_funksjonaer]: 'Functionary', [KEY.recruitment_organization]: 'Organization', From dea657ddc6c901252f0095e0002b0f6f4a16b1a4 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 29 Aug 2023 11:12:41 +0200 Subject: [PATCH 04/11] Add overview page for admissions --- backend/root/utils/routes.py | 20 +++- .../RecruitmentPositionOverviewPage.tsx | 100 ++++++++++++++++++ frontend/src/routes/backend.ts | 20 +++- 3 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 frontend/src/PagesAdmin/RecruitmentPositionOverviewPage/RecruitmentPositionOverviewPage.tsx diff --git a/backend/root/utils/routes.py b/backend/root/utils/routes.py index 56d96342b..97fca8328 100644 --- a/backend/root/utils/routes.py +++ b/backend/root/utils/routes.py @@ -5,7 +5,7 @@ DO NOT WRITE IN THIS FILE, AS IT WILL BE OVERWRITTEN ON NEXT UPDATE. THIS FILE WAS GENERATED BY: root.management.commands.generate_routes -LAST UPDATE: 2023-08-17 17:48:38.961443+00:00 +LAST UPDATE: 2023-08-29 08:57:57.685488+00:00 """ ############################################################ @@ -308,18 +308,36 @@ admin__samfundet_keyvalue_delete = 'admin:samfundet_keyvalue_delete' admin__samfundet_keyvalue_change = 'admin:samfundet_keyvalue_change' adminsamfundetkeyvalue__objectId = '' +admin__samfundet_recruitment_permissions = 'admin:samfundet_recruitment_permissions' +admin__samfundet_recruitment_permissions_manage_user = 'admin:samfundet_recruitment_permissions_manage_user' +admin__samfundet_recruitment_permissions_manage_group = 'admin:samfundet_recruitment_permissions_manage_group' admin__samfundet_recruitment_changelist = 'admin:samfundet_recruitment_changelist' admin__samfundet_recruitment_add = 'admin:samfundet_recruitment_add' admin__samfundet_recruitment_history = 'admin:samfundet_recruitment_history' admin__samfundet_recruitment_delete = 'admin:samfundet_recruitment_delete' admin__samfundet_recruitment_change = 'admin:samfundet_recruitment_change' adminsamfundetrecruitment__objectId = '' +admin__samfundet_recruitmentposition_permissions = 'admin:samfundet_recruitmentposition_permissions' +admin__samfundet_recruitmentposition_permissions_manage_user = 'admin:samfundet_recruitmentposition_permissions_manage_user' +admin__samfundet_recruitmentposition_permissions_manage_group = 'admin:samfundet_recruitmentposition_permissions_manage_group' admin__samfundet_recruitmentposition_changelist = 'admin:samfundet_recruitmentposition_changelist' admin__samfundet_recruitmentposition_add = 'admin:samfundet_recruitmentposition_add' admin__samfundet_recruitmentposition_history = 'admin:samfundet_recruitmentposition_history' admin__samfundet_recruitmentposition_delete = 'admin:samfundet_recruitmentposition_delete' admin__samfundet_recruitmentposition_change = 'admin:samfundet_recruitmentposition_change' adminsamfundetrecruitmentposition__objectId = '' +admin__samfundet_recruitmentadmission_permissions = 'admin:samfundet_recruitmentadmission_permissions' +admin__samfundet_recruitmentadmission_permissions_manage_user = 'admin:samfundet_recruitmentadmission_permissions_manage_user' +admin__samfundet_recruitmentadmission_permissions_manage_group = 'admin:samfundet_recruitmentadmission_permissions_manage_group' +admin__samfundet_recruitmentadmission_changelist = 'admin:samfundet_recruitmentadmission_changelist' +admin__samfundet_recruitmentadmission_add = 'admin:samfundet_recruitmentadmission_add' +admin__samfundet_recruitmentadmission_history = 'admin:samfundet_recruitmentadmission_history' +admin__samfundet_recruitmentadmission_delete = 'admin:samfundet_recruitmentadmission_delete' +admin__samfundet_recruitmentadmission_change = 'admin:samfundet_recruitmentadmission_change' +adminsamfundetrecruitmentadmission__objectId = '' +admin__samfundet_organization_permissions = 'admin:samfundet_organization_permissions' +admin__samfundet_organization_permissions_manage_user = 'admin:samfundet_organization_permissions_manage_user' +admin__samfundet_organization_permissions_manage_group = 'admin:samfundet_organization_permissions_manage_group' admin__samfundet_organization_changelist = 'admin:samfundet_organization_changelist' admin__samfundet_organization_add = 'admin:samfundet_organization_add' admin__samfundet_organization_history = 'admin:samfundet_organization_history' diff --git a/frontend/src/PagesAdmin/RecruitmentPositionOverviewPage/RecruitmentPositionOverviewPage.tsx b/frontend/src/PagesAdmin/RecruitmentPositionOverviewPage/RecruitmentPositionOverviewPage.tsx new file mode 100644 index 000000000..0c9bc23fd --- /dev/null +++ b/frontend/src/PagesAdmin/RecruitmentPositionOverviewPage/RecruitmentPositionOverviewPage.tsx @@ -0,0 +1,100 @@ +import { useEffect, useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { RecruitmentAdmissionDto } from '~/dto'; +import { getRecruitmentAdmissionsForGang } from '~/api'; +import { KEY } from '~/i18n/constants'; +import { Button, Link } from '~/Components'; +import { Table } from '~/Components/Table'; +import { AdminPageLayout } from '../AdminPageLayout/AdminPageLayout'; +import { ROUTES } from '~/routes'; +import { reverse } from '~/named-urls'; + +export function RecruitmentPositionOverviewPage() { + const recruitmentId = useParams().recruitmentId; + const gangId = useParams().gangId; + const positionId = useParams().positionId; + const navigate = useNavigate(); + const [recruitmentApplicants, setRecruitmentApplicants] = useState([]); + const [showSpinner, setShowSpinner] = useState(true); + const { t } = useTranslation(); + useEffect(() => { + recruitmentId && + gangId && + getRecruitmentAdmissionsForGang(gangId, recruitmentId).then((data) => { + setRecruitmentApplicants( + data.data.filter( + (recruitmentApplicant) => recruitmentApplicant.recruitment_position.toString() == positionId, + ), + ); + setShowSpinner(false); + }); + }, [recruitmentId, gangId, positionId]); + + const tableColumns = [ + { content: t(KEY.recruitment_applicant), sortable: true }, + { content: t(KEY.recruitment_priority), sortable: true }, + { content: t(KEY.recruitment_interview_time), sortable: true }, + { content: t(KEY.recruitment_interview_location), sortable: true }, + { content: t(KEY.recruitment_recruiter_priority), sortable: true }, + { content: t(KEY.recruitment_recruiter_status), sortable: true }, + ]; + const data = recruitmentApplicants.map(function (admission) { + return [ + { + content: ( + + {`${admission.user.first_name} ${admission.user.last_name}`} + + ), + }, + { content: admission.priority }, + { content: admission.interview_time }, + { content: admission.interview_location }, + { content: admission.recruiter_priority }, + { content: admission.recruiter_status }, + ]; + }); + const title = t(KEY.admin_information_manage_title); + const backendUrl = reverse({ + pattern: ROUTES.backend.admin__samfundet_recruitmentposition_change, + urlParams: { + objectId: positionId, + }, + }); + + const header = ( + + ); + + return ( + + + + ); +} diff --git a/frontend/src/routes/backend.ts b/frontend/src/routes/backend.ts index b71365332..89aad7ff3 100644 --- a/frontend/src/routes/backend.ts +++ b/frontend/src/routes/backend.ts @@ -4,7 +4,7 @@ THIS FILE IS AUTOGENERATED. DO NOT WRITE IN THIS FILE, AS IT WILL BE OVERWRITTEN ON NEXT UPDATE. THIS FILE WAS GENERATED BY: root.management.commands.generate_routes -LAST UPDATE: 2023-08-17 17:48:38.961443+00:00 +LAST UPDATE: 2023-08-29 08:57:57.685488+00:00 """ */ // ############################################################ @@ -307,18 +307,36 @@ export const ROUTES_BACKEND = { admin__samfundet_keyvalue_delete: '/admin/samfundet/keyvalue/:objectId/delete/', admin__samfundet_keyvalue_change: '/admin/samfundet/keyvalue/:objectId/change/', adminsamfundetkeyvalue__objectId: '/admin/samfundet/keyvalue/:objectId/', + admin__samfundet_recruitment_permissions: '/admin/samfundet/recruitment/:objectPk/permissions/', + admin__samfundet_recruitment_permissions_manage_user: '/admin/samfundet/recruitment/:objectPk/permissions/user-manage/:userId/', + admin__samfundet_recruitment_permissions_manage_group: '/admin/samfundet/recruitment/:objectPk/permissions/group-manage/:groupId/', admin__samfundet_recruitment_changelist: '/admin/samfundet/recruitment/', admin__samfundet_recruitment_add: '/admin/samfundet/recruitment/add/', admin__samfundet_recruitment_history: '/admin/samfundet/recruitment/:objectId/history/', admin__samfundet_recruitment_delete: '/admin/samfundet/recruitment/:objectId/delete/', admin__samfundet_recruitment_change: '/admin/samfundet/recruitment/:objectId/change/', adminsamfundetrecruitment__objectId: '/admin/samfundet/recruitment/:objectId/', + admin__samfundet_recruitmentposition_permissions: '/admin/samfundet/recruitmentposition/:objectPk/permissions/', + admin__samfundet_recruitmentposition_permissions_manage_user: '/admin/samfundet/recruitmentposition/:objectPk/permissions/user-manage/:userId/', + admin__samfundet_recruitmentposition_permissions_manage_group: '/admin/samfundet/recruitmentposition/:objectPk/permissions/group-manage/:groupId/', admin__samfundet_recruitmentposition_changelist: '/admin/samfundet/recruitmentposition/', admin__samfundet_recruitmentposition_add: '/admin/samfundet/recruitmentposition/add/', admin__samfundet_recruitmentposition_history: '/admin/samfundet/recruitmentposition/:objectId/history/', admin__samfundet_recruitmentposition_delete: '/admin/samfundet/recruitmentposition/:objectId/delete/', admin__samfundet_recruitmentposition_change: '/admin/samfundet/recruitmentposition/:objectId/change/', adminsamfundetrecruitmentposition__objectId: '/admin/samfundet/recruitmentposition/:objectId/', + admin__samfundet_recruitmentadmission_permissions: '/admin/samfundet/recruitmentadmission/:objectPk/permissions/', + admin__samfundet_recruitmentadmission_permissions_manage_user: '/admin/samfundet/recruitmentadmission/:objectPk/permissions/user-manage/:userId/', + admin__samfundet_recruitmentadmission_permissions_manage_group: '/admin/samfundet/recruitmentadmission/:objectPk/permissions/group-manage/:groupId/', + admin__samfundet_recruitmentadmission_changelist: '/admin/samfundet/recruitmentadmission/', + admin__samfundet_recruitmentadmission_add: '/admin/samfundet/recruitmentadmission/add/', + admin__samfundet_recruitmentadmission_history: '/admin/samfundet/recruitmentadmission/:objectId/history/', + admin__samfundet_recruitmentadmission_delete: '/admin/samfundet/recruitmentadmission/:objectId/delete/', + admin__samfundet_recruitmentadmission_change: '/admin/samfundet/recruitmentadmission/:objectId/change/', + adminsamfundetrecruitmentadmission__objectId: '/admin/samfundet/recruitmentadmission/:objectId/', + admin__samfundet_organization_permissions: '/admin/samfundet/organization/:objectPk/permissions/', + admin__samfundet_organization_permissions_manage_user: '/admin/samfundet/organization/:objectPk/permissions/user-manage/:userId/', + admin__samfundet_organization_permissions_manage_group: '/admin/samfundet/organization/:objectPk/permissions/group-manage/:groupId/', admin__samfundet_organization_changelist: '/admin/samfundet/organization/', admin__samfundet_organization_add: '/admin/samfundet/organization/add/', admin__samfundet_organization_history: '/admin/samfundet/organization/:objectId/history/', From b0ca36c103507214944da3020758815c23307b08 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 29 Aug 2023 11:13:07 +0200 Subject: [PATCH 05/11] Add navigation into admission overview page --- frontend/src/AppRoutes.tsx | 5 +++++ .../RecruitmentGangAdminPage/RecruitmentGangAdminPage.tsx | 8 +++++++- frontend/src/routes/frontend.ts | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/src/AppRoutes.tsx b/frontend/src/AppRoutes.tsx index dc0f84a5a..1102282f7 100644 --- a/frontend/src/AppRoutes.tsx +++ b/frontend/src/AppRoutes.tsx @@ -47,6 +47,7 @@ import { RecruitmentFormAdminPage } from './PagesAdmin/RecruitmentFormAdminPage' import { SaksdokumentAdminPage } from './PagesAdmin/SaksdokumentAdminPage'; import { PERM } from './permissions'; import { ROUTES } from './routes'; +import { RecruitmentPositionOverviewPage } from './PagesAdmin/RecruitmentPositionOverviewPage/RecruitmentPositionOverviewPage'; export function AppRoutes() { // Must be called within because it uses hook useLocation(). @@ -184,6 +185,10 @@ export function AppRoutes() { path={ROUTES.frontend.admin_recruitment_gang_position_create} element={} /> + } + /> } diff --git a/frontend/src/PagesAdmin/RecruitmentGangAdminPage/RecruitmentGangAdminPage.tsx b/frontend/src/PagesAdmin/RecruitmentGangAdminPage/RecruitmentGangAdminPage.tsx index 62287e6ed..8ec575e0d 100644 --- a/frontend/src/PagesAdmin/RecruitmentGangAdminPage/RecruitmentGangAdminPage.tsx +++ b/frontend/src/PagesAdmin/RecruitmentGangAdminPage/RecruitmentGangAdminPage.tsx @@ -31,8 +31,14 @@ export function RecruitmentGangAdminPage() { const tableColumns = [{ content: t(KEY.recruitment_position), sortable: true }]; const data = recruitmentPositions.map(function (recruitmentPosition) { + const pageUrl = reverse({ + pattern: ROUTES.frontend.admin_recruitment_gang_position_applicants_overview, + urlParams: { recruitmentId: recruitmentId, gangId: gangId, positionId: recruitmentPosition.id }, + }); return [ - { content: {dbT(recruitmentPosition, 'name')} }, + { + content: {dbT(recruitmentPosition, 'name')}, + }, { content: ( Date: Thu, 31 Aug 2023 16:23:17 +0200 Subject: [PATCH 06/11] Allow initial value for dropdowns --- frontend/src/Components/Dropdown/Dropdown.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/Components/Dropdown/Dropdown.tsx b/frontend/src/Components/Dropdown/Dropdown.tsx index 77e321e14..1b62660db 100644 --- a/frontend/src/Components/Dropdown/Dropdown.tsx +++ b/frontend/src/Components/Dropdown/Dropdown.tsx @@ -12,6 +12,7 @@ export type DropDownOption = { type DropdownProps = { className?: string; defaultValue?: DropDownOption; + initialValue?: T; options?: DropDownOption[]; label?: string | ReactElement; disabled?: boolean; @@ -22,6 +23,7 @@ type DropdownProps = { export function Dropdown({ options = [], defaultValue, + initialValue, onChange, className, label, @@ -50,9 +52,9 @@ export function Dropdown({ className={classNames(styles.samf_select, error && styles.error)} onChange={handleChange} disabled={disabled} - defaultValue={-1} + defaultValue={initialValue !== undefined ? options.map((e) => e.value).indexOf(initialValue) : -1} > - {defaultValue ? : } + {defaultValue ? : } {options.map((opt, index) => { return (