Skip to content
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

Add promo video to form #1292

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions backend/samfundet/migrations/0003_recruitment_promo_media.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.2 on 2024-08-29 16:52

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('samfundet', '0002_alter_recruitmentapplication_recruiter_status'),
]

operations = [
migrations.AddField(
model_name='recruitment',
name='promo_media',
field=models.CharField(help_text='Youtube video id', max_length=12, null=True),
),
]
18 changes: 18 additions & 0 deletions backend/samfundet/migrations/0004_alter_recruitment_promo_media.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.2 on 2024-08-29 18:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('samfundet', '0003_recruitment_promo_media'),
]

operations = [
migrations.AlterField(
model_name='recruitment',
name='promo_media',
field=models.CharField(default=None, help_text='Youtube video id', max_length=12, null=True),
),
]
18 changes: 18 additions & 0 deletions backend/samfundet/migrations/0005_alter_recruitment_promo_media.py
Mathias-a marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.2 on 2024-09-10 18:58

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('samfundet', '0004_alter_recruitment_promo_media'),
]

operations = [
migrations.AlterField(
model_name='recruitment',
name='promo_media',
field=models.CharField(default=None, help_text='Youtube video id', max_length=11, null=True),
),
]
2 changes: 1 addition & 1 deletion backend/samfundet/models/recruitment.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Recruitment(CustomBaseModel):
organization = models.ForeignKey(null=False, blank=False, to=Organization, on_delete=models.CASCADE, help_text='The organization that is recruiting')

max_applications = models.PositiveIntegerField(null=True, blank=True, verbose_name='Max applications per applicant')
promo_media = models.CharField(max_length=11 ,help_text='Youtube video id', null=True, default=None)

def is_active(self) -> bool:
return self.visible_from < timezone.now() < self.actual_application_deadline
Expand Down Expand Up @@ -92,7 +93,6 @@ def save(self, *args: tuple, **kwargs: dict) -> None:
if not self.statistics:
RecruitmentStatistics.objects.create(self)


class RecruitmentPosition(CustomBaseModel):
name_nb = models.CharField(max_length=100, help_text='Name of the position')
name_en = models.CharField(max_length=100, help_text='Name of the position')
Expand Down
10 changes: 10 additions & 0 deletions backend/samfundet/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import re
import itertools
from typing import TYPE_CHECKING
from collections import defaultdict
Expand Down Expand Up @@ -701,11 +702,20 @@ class Meta:

class RecruitmentSerializer(CustomBaseSerializer):
seperate_positions = RecruitmentSeperatePositionSerializer(many=True, read_only=True)
promo_media = serializers.CharField(max_length=100)
Snorre98 marked this conversation as resolved.
Show resolved Hide resolved

class Meta:
model = Recruitment
fields = '__all__'

def validate_promo_media(self, value: str | None) -> str | None:
if (value is None):
return None
match = re.search(r'(youtu.*be.*)\/(watch\?v=|embed\/|v|shorts|)(.*?((?=[&#?])|$))', value)
if (match):
return match.group(3)
raise ValidationError("Invalid youtube url")

Snorre98 marked this conversation as resolved.
Show resolved Hide resolved

class RecruitmentPositionSerializer(CustomBaseSerializer):
total_applicants = serializers.SerializerMethodField(method_name='get_total_applicants', read_only=True)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import styles from './OrganizationRecruitmentPage.module.scss';
import { RecruitmentTabs, GangTypeContainer } from './Components';
import { Text, Page, Video, Logo, OccupiedFormModal, SamfundetLogoSpinner, ToggleSwitch } from '~/Components';
import { PersonalRow } from '~/Pages/RecruitmentPage';
import { OrgNameType, OrgNameTypeValue } from '~/types';
import { useDesktop } from '~/hooks';
import { useParams } from 'react-router-dom';
import { KEY } from '~/i18n/constants';
import { dbT } from '~/utils';
import { useTranslation } from 'react-i18next';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { getOrganization, getRecruitment } from '~/api';
import { Logo, OccupiedFormModal, Page, SamfundetLogoSpinner, Text, ToggleSwitch, Video } from '~/Components';
import { useOrganizationContext } from '~/context/OrgContextProvider';
import { RecruitmentDto } from '~/dto';
import { getOrganization, getRecruitment } from '~/api';
import classNames from 'classnames';
import { useDesktop } from '~/hooks';
import { KEY } from '~/i18n/constants';
import { PersonalRow } from '~/Pages/RecruitmentPage';
import { OrgNameType, OrgNameTypeValue } from '~/types';
import { dbT } from '~/utils';
import { GangTypeContainer, RecruitmentTabs } from './Components';
import styles from './OrganizationRecruitmentPage.module.scss';

export function OrganizationRecruitmentPage() {
const isDesktop = useDesktop();
const embededId = '-nYQb8_TvQ4'; // TODO: Make this dynamic DO IN ISSUE #1121 for backend. #1274 for frontend
const { recruitmentID } = useParams<{ recruitmentID: string }>();
const [viewAllPositions, setViewAllPositions] = useState<boolean>(true);
const { t } = useTranslation();
Expand Down Expand Up @@ -76,9 +75,9 @@ export function OrganizationRecruitmentPage() {
{dbT(recruitment, 'name')}
</Text>
</div>
{embededId ? (
{recruitment?.promo_media ? (
<>
<Video embedId={embededId} className={styles.video}></Video>
<Video embedId={recruitment.promo_media} className={styles.video}></Video>
</>
) : (
<></>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { useNavigate, useParams, useRouteLoaderData } from 'react-router-dom';
import { toast } from 'react-toastify';
import { DropDownOption } from '~/Components/Dropdown/Dropdown';
import { SamfForm } from '~/Forms/SamfForm';
import { SamfError, SamfForm } from '~/Forms/SamfForm';
import { SamfFormField } from '~/Forms/SamfFormField';
import { getOrganizations, postRecruitment, putRecruitment } from '~/api';
import { OrganizationDto, RecruitmentDto } from '~/dto';
Expand All @@ -24,8 +24,18 @@ type FormType = {
reprioritization_deadline_for_applicant: string;
reprioritization_deadline_for_groups: string;
organization: number;
promo_media: string;
};

function youtubeLinkValidator(state: FormType): SamfError {
emilte marked this conversation as resolved.
Show resolved Hide resolved
const link = state.promo_media;
const regex = /(youtu.*be.*)\/(watch\?v=|embed\/|v|shorts|)(.*?((?=[&#?])|$))/;
if (link && !link.match(regex)) {
return 'Not valid youtbue link';
}
return true;
}

export function RecruitmentFormAdminPage() {
const { t } = useTranslation();
const navigate = useNavigate();
Expand Down Expand Up @@ -161,6 +171,14 @@ export function RecruitmentFormAdminPage() {
required={true}
/>
</div>
<div className={styles.row}>
<SamfFormField
field="promo_media"
type="text"
label={t(KEY.promo_media)}
validator={youtubeLinkValidator}
/>
</div>
</SamfForm>
</div>
</AdminPageLayout>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ export type RecruitmentDto = {
max_applications?: number;
organization: number;
seperate_positions?: RecruitmentSeperatePositionDto[];
promo_media?: string;
};

export type RecruitmentSeperatePositionDto = {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/i18n/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ export const KEY = {
recrutment_default_application_letter: 'recrutment_default_application_letter',
reprioritization_deadline_for_groups: 'reprioritization_deadline_for_groups',
max_applications: 'max_applications',
promo_media: 'promo_video',
Mathias-a marked this conversation as resolved.
Show resolved Hide resolved
recruitment_norwegian_applicants_only: 'recruitment_norwegian_applicants_only',
reprioritization_deadline_for_applicant: 'reprioritization_deadline_for_applicant',
recruitment_show_unprocessed_applicants: 'recruitment_show_unprocessed_applicants',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/i18n/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ export const nb: Record<KeyValues, string> = {
[KEY.recrutment_default_application_letter]: 'Standard søknadstekst',
[KEY.reprioritization_deadline_for_groups]: 'Flaggefrist',
[KEY.max_applications]: 'Maks søknader per bruker',
[KEY.promo_media]: 'Promo video',
[KEY.recruitment_norwegian_applicants_only]: 'Kun norsktalende søkere',
[KEY.reprioritization_deadline_for_applicant]: 'Omprioriteringsfrist',
[KEY.recruitment_show_unprocessed_applicants]: 'Vis ubehandlede søkere',
Expand Down Expand Up @@ -688,6 +689,7 @@ export const en: Record<KeyValues, string> = {
[KEY.recrutment_default_application_letter]: 'Default application letter',
[KEY.reprioritization_deadline_for_groups]: 'Group reprioritization deadline',
[KEY.max_applications]: 'Max applications per user',
[KEY.promo_media]: "Promo Video",
Mathias-a marked this conversation as resolved.
Show resolved Hide resolved
[KEY.reprioritization_deadline_for_applicant]: 'Reprioritization deadline',
[KEY.recruitment_show_unprocessed_applicants]: 'Show unprocessed applicants',
[KEY.recruitment_show_all_applicants]: 'Show all applicants',
Expand Down