-
Notifications
You must be signed in to change notification settings - Fork 1
Redirect to active samf recruitment #1861
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 8 commits
1e5f19d
5dc5860
a054375
535fc5d
47d0d61
77b97c0
34388c7
3d4a2cf
7a30eb5
8ff2c70
fe23411
e81d635
d70b36d
1c52c77
1ec48cf
6b86a80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,10 +7,11 @@ | |
| from rest_framework import status | ||
| from rest_framework.request import Request | ||
| from rest_framework.response import Response | ||
| from rest_framework.viewsets import ModelViewSet | ||
| from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet | ||
| from rest_framework.decorators import action | ||
| from rest_framework.permissions import IsAuthenticated, DjangoModelPermissionsOrAnonReadOnly | ||
|
|
||
| from django.utils import timezone | ||
| from django.db.models import QuerySet | ||
| from django.shortcuts import get_object_or_404 | ||
| from django.utils.decorators import method_decorator | ||
|
|
@@ -20,7 +21,7 @@ | |
| from root.custom_classes.permission_classes import RoleProtectedObjectPermissions, filter_queryset_by_permissions | ||
|
|
||
| from samfundet.serializers import RecruitmentSerializer, RecruitmentGangSerializer, RecruitmentForRecruiterSerializer, RecruitmentApplicationForGangSerializer | ||
| from samfundet.models.general import Gang | ||
| from samfundet.models.general import Gang, Organization | ||
| from samfundet.models.recruitment import Recruitment, RecruitmentApplication | ||
|
|
||
| # =============================== # | ||
|
|
@@ -46,6 +47,38 @@ def gangs(self, request: Request, **kwargs: Any) -> Response: | |
| return Response(serializer.data) | ||
|
|
||
|
|
||
| @method_decorator(ensure_csrf_cookie, 'dispatch') | ||
| class ActiveRecruitmentsView(ReadOnlyModelViewSet): | ||
| permission_classes = [DjangoModelPermissionsOrAnonReadOnly] | ||
| serializer_class = RecruitmentSerializer | ||
| queryset = Recruitment.objects.all() | ||
|
|
||
| def get_queryset(self) -> QuerySet[Recruitment]: | ||
| """Default queryset to show only active recruitments""" | ||
| now = timezone.now() | ||
| return Recruitment.objects.filter( | ||
| visible_from__lte=now, # __lte: less than or equal to (Django lookup type) | ||
| actual_application_deadline__gte=now, # __gte: greater than or equal to (Django lookup type) | ||
| ) | ||
|
|
||
| @action(detail=False, methods=['get'], url_path='samfundet') | ||
| def get_active_samf_recruitments(self, request: Request, **kwargs: Any) -> Response: | ||
| try: | ||
| samfundet_org = Organization.objects.get(name='Samfundet') | ||
|
|
||
| # Get active recruitments for Samfundet, using the overriden get_queryset method | ||
| active_samfundet_recruitments = self.get_queryset().filter(organization=samfundet_org) | ||
|
|
||
| if not active_samfundet_recruitments: | ||
| return Response({'message': 'No active recruitment for Samfundet'}, status=status.HTTP_404_NOT_FOUND) | ||
|
||
|
|
||
| serializer = self.get_serializer(active_samfundet_recruitments, many=True) | ||
| return Response(serializer.data) | ||
|
|
||
| except Organization.DoesNotExist: | ||
| return Response({'error': 'No organization named Samfundet exists'}, status=status.HTTP_404_NOT_FOUND) | ||
|
|
||
|
|
||
| # =============================== # | ||
| # Auth protected views # | ||
| # =============================== # | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,20 @@ | ||
| import { Icon } from '@iconify/react'; | ||
| import { useQuery } from '@tanstack/react-query'; | ||
| import { default as classNames } from 'classnames'; | ||
| import { useEffect, useState } from 'react'; | ||
| import { useCookies } from 'react-cookie'; | ||
| import { useTranslation } from 'react-i18next'; | ||
| import { useLocation, useNavigate } from 'react-router'; | ||
| import { Button, Link, ThemeSwitch } from '~/Components'; | ||
| import { getActiveRecruitments, logout, stopImpersonatingUser } from '~/api'; | ||
| import { getActiveSamfRecruitments, logout, stopImpersonatingUser } from '~/api'; | ||
| import { logoWhite } from '~/assets'; | ||
| import { useAuthContext } from '~/context/AuthContext'; | ||
| import { useGlobalContext } from '~/context/GlobalContextProvider'; | ||
| import type { RecruitmentDto } from '~/dto'; | ||
| import { useDesktop, useScrollY } from '~/hooks'; | ||
| import { STATUS } from '~/http_status_codes'; | ||
| import { KEY } from '~/i18n/constants'; | ||
| import { reverse } from '~/named-urls'; | ||
| import { recruitmentKeys } from '~/queryKeys'; | ||
| import { ROUTES } from '~/routes'; | ||
| import styles from './Navbar.module.scss'; | ||
| import { HamburgerMenu, LanguageButton, NavbarItem } from './components'; | ||
|
|
@@ -23,7 +25,6 @@ export function Navbar() { | |
| const { isMobileNavigation, setIsMobileNavigation } = useGlobalContext(); | ||
| const { t, i18n } = useTranslation(); | ||
| const { user, setUser } = useAuthContext(); | ||
| const [activeRecruitments, setActiveRecruitments] = useState<RecruitmentDto[]>(); | ||
| const navigate = useNavigate(); | ||
| const isDesktop = useDesktop(); | ||
| const [cookies, setCookie, removeCookie] = useCookies(); | ||
|
|
@@ -50,13 +51,12 @@ export function Navbar() { | |
| } | ||
| }, [isMobileNavigation, isDesktop]); | ||
|
|
||
| useEffect(() => { | ||
| getActiveRecruitments().then((response) => { | ||
| setActiveRecruitments(response.data); | ||
| }); | ||
| }, []); | ||
| const { data: activeSamfRecruitments } = useQuery({ | ||
| queryKey: recruitmentKeys.all, | ||
| queryFn: getActiveSamfRecruitments, | ||
| }); | ||
|
|
||
| const showActiveRecruitments = activeRecruitments !== undefined && activeRecruitments?.length > 0; | ||
| const showActiveRecruitments = activeSamfRecruitments !== undefined && activeSamfRecruitments?.length > 0; | ||
|
|
||
| // Return profile button for navbar if logged in. | ||
| const mobileProfileButton = ( | ||
|
|
@@ -130,13 +130,24 @@ export function Navbar() { | |
| route={ROUTES.frontend.sulten} | ||
| label={t(KEY.common_restaurant)} | ||
| /> | ||
| <NavbarItem | ||
| setExpandedDropdown={setExpandedDropdown} | ||
| expandedDropdown={expandedDropdown} | ||
| route={ROUTES.frontend.recruitment} | ||
| label={t(KEY.common_volunteer)} | ||
| labelClassName={showActiveRecruitments ? styles.active_recruitment : ''} | ||
| /> | ||
| {showActiveRecruitments && ( | ||
|
||
| <NavbarItem | ||
| setExpandedDropdown={setExpandedDropdown} | ||
| expandedDropdown={expandedDropdown} | ||
| route={ | ||
| activeSamfRecruitments.length === 1 | ||
| ? // goes to the one samf recruitment if there is only one | ||
| reverse({ | ||
| pattern: ROUTES.frontend.organization_recruitment, | ||
| urlParams: { recruitmentId: activeSamfRecruitments[0].id }, | ||
| }) | ||
| : // goes to the page with recruitment cards if there is multiple samf recruitments | ||
| ROUTES.frontend.recruitment | ||
| } | ||
|
||
| label={t(KEY.common_volunteer)} | ||
| labelClassName={styles.active_recruitment} | ||
| /> | ||
| )} | ||
| </div> | ||
| ); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,15 @@ | ||
| import type { LoaderFunctionArgs } from 'react-router'; | ||
| import { getGang, getRecruitment, getRecruitmentPosition, getRecruitmentSeparatePosition, getRole } from '~/api'; | ||
| import { type LoaderFunctionArgs, redirect } from 'react-router'; | ||
| import { | ||
| getActiveSamfRecruitments, | ||
| getGang, | ||
| getRecruitment, | ||
| getRecruitmentPosition, | ||
| getRecruitmentSeparatePosition, | ||
| getRole, | ||
| } from '~/api'; | ||
| import type { GangDto, RecruitmentDto, RecruitmentPositionDto, RecruitmentSeparatePositionDto, RoleDto } from '~/dto'; | ||
| import { reverse } from '~/named-urls'; | ||
| import { ROUTES } from '~/routes'; | ||
|
|
||
| export type RecruitmentLoader = { | ||
| recruitment: RecruitmentDto | undefined; | ||
|
|
@@ -26,6 +35,28 @@ export async function roleLoader({ params }: LoaderFunctionArgs): Promise<RoleLo | |
| return { role: await getRole(Number.parseInt(params.roleId as string)) }; | ||
| } | ||
|
|
||
| export async function samfRecruitmentLoader() { | ||
| try { | ||
| const activeSamfRecruitments = await getActiveSamfRecruitments(); | ||
|
Comment on lines
+39
to
+40
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tanstack?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Det er ikke tsx, altså, kan ikke kjøre en hook her |
||
|
|
||
| // Check if there's only one recruitment and redirect if needed | ||
| if (activeSamfRecruitments?.length === 1) { | ||
| return redirect( | ||
| reverse({ | ||
| pattern: ROUTES.frontend.recruitment_application_overview, | ||
| urlParams: { recruitmentId: activeSamfRecruitments[0].id }, | ||
| }), | ||
| ); | ||
| } | ||
|
|
||
| // Otherwise, return the recruitments data | ||
| return { activeSamfRecruitments }; | ||
| } catch (error) { | ||
| console.error('Error fetching recruitments:', error); | ||
| return { activeSamfRecruitments: [] }; | ||
| } | ||
| } | ||
|
|
||
| export async function recruitmentLoader({ params }: LoaderFunctionArgs): Promise<RecruitmentLoader> { | ||
| return { recruitment: (await getRecruitment(params.recruitmentId as string)).data }; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finn eller flytt name inn i en konstant noe sted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sånn her lissom? 8ff2c70
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^det ble feil,. Dette funker: e81d635