diff --git a/backend/samfundet/migrations/0001_initial.py b/backend/samfundet/migrations/0001_initial.py index 0ea8f1fdf..5ecee6c5e 100644 --- a/backend/samfundet/migrations/0001_initial.py +++ b/backend/samfundet/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.9 on 2025-08-09 16:11 +# Generated by Django 5.2.10 on 2026-01-20 20:45 import datetime import django.contrib.auth.models @@ -138,8 +138,6 @@ class Migration(migrations.Migration): ('updated_at', models.DateTimeField(blank=True, editable=False, null=True)), ('message_nb', models.TextField(blank=True, null=True, verbose_name='Melding (norsk)')), ('message_en', models.TextField(blank=True, null=True, verbose_name='Melding (engelsk)')), - ('description_nb', models.TextField(blank=True, null=True, verbose_name='Beskrivelse (norsk)')), - ('description_en', models.TextField(blank=True, null=True, verbose_name='Beskrivelse (engelsk)')), ('start_dt', models.DateField(blank=True, verbose_name='Start dato')), ('end_dt', models.DateField(blank=True, verbose_name='Slutt dato')), ('created_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), diff --git a/backend/samfundet/models/general.py b/backend/samfundet/models/general.py index 84f9461ca..ab6b02ac6 100644 --- a/backend/samfundet/models/general.py +++ b/backend/samfundet/models/general.py @@ -258,9 +258,6 @@ class ClosedPeriod(CustomBaseModel): message_nb = models.TextField(blank=True, null=True, verbose_name='Melding (norsk)') message_en = models.TextField(blank=True, null=True, verbose_name='Melding (engelsk)') - description_nb = models.TextField(blank=True, null=True, verbose_name='Beskrivelse (norsk)') - description_en = models.TextField(blank=True, null=True, verbose_name='Beskrivelse (engelsk)') - start_dt = models.DateField(blank=True, null=False, verbose_name='Start dato') end_dt = models.DateField(blank=True, null=False, verbose_name='Slutt dato') diff --git a/frontend/src/Components/OpeningHours/OpeningHours.module.scss b/frontend/src/Components/OpeningHours/OpeningHours.module.scss index 25bf61013..f0ad13a49 100644 --- a/frontend/src/Components/OpeningHours/OpeningHours.module.scss +++ b/frontend/src/Components/OpeningHours/OpeningHours.module.scss @@ -10,8 +10,8 @@ align-items: center; @include theme-dark { - background-color: $black-2; - color: $white; + background-color: $theme-dark-bg; + color: $theme-dark-color; } } @@ -26,3 +26,10 @@ margin: 0 0.2em; } +.closedBox { + border: 2px $red_samf solid; + border-radius: 10px; + padding: 20px; + margin-bottom: 2rem; + margin-top: 1rem; +} diff --git a/frontend/src/Components/OpeningHours/OpeningHours.tsx b/frontend/src/Components/OpeningHours/OpeningHours.tsx index 6c4e9b57e..9bbec101a 100644 --- a/frontend/src/Components/OpeningHours/OpeningHours.tsx +++ b/frontend/src/Components/OpeningHours/OpeningHours.tsx @@ -1,9 +1,13 @@ +import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { TimeDuration } from '~/Components'; import { Link } from '~/Components/Link/Link'; import { Text } from '~/Components/Text/Text'; +import { getClosedPeriods } from '~/api'; +import { useGlobalContext } from '~/context/GlobalContextProvider'; import type { VenueDto } from '~/dto'; import { KEY } from '~/i18n/constants'; +import { dbT } from '~/utils'; import styles from './OpeningHours.module.scss'; type OpeningHoursProps = { @@ -14,6 +18,29 @@ type OpeningHoursProps = { export function OpeningHours({ venues, isLoading, isError }: OpeningHoursProps) { const { t } = useTranslation(); + const [isClosed, setIsClosed] = useState(false); + const [closedText, setClosedText] = useState('Samf is closed'); + const globalContext = useGlobalContext(); + + useEffect(() => { + if (globalContext.isClosed !== 'default') { + if (globalContext.isClosed === 'closed') { + setIsClosed(true); + setClosedText(t(KEY.admin_closed_message)); + } + return; + } + getClosedPeriods().then((periods) => { + const now = new Date(); + for (const period of periods) { + if (new Date(period.start_dt) < now && now < new Date(period.end_dt)) { + setIsClosed(true); + setClosedText(dbT(period, 'message')); + return; + } + } + }); + }); if (isLoading) { return {t(KEY.common_loading)}; @@ -25,34 +52,37 @@ export function OpeningHours({ venues, isLoading, isError }: OpeningHoursProps) const today = new Date().toISOString().split('T')[0]; const day = new Date().toLocaleDateString('en-US', { weekday: 'long' }).toLowerCase(); - return (
{t(KEY.common_opening_hours)} - - {venues.map((venue) => { - const openingTime = venue[`opening_${day}` as keyof VenueDto] as string; - const closingTime = venue[`closing_${day}` as keyof VenueDto] as string; - return ( - - - - - ); - })} -
- -

{venue.name}

- -
- -
+ {isClosed ? ( +
{closedText}
+ ) : ( + + {venues.map((venue) => { + const openingTime = venue[`opening_${day}` as keyof VenueDto] as string; + const closingTime = venue[`closing_${day}` as keyof VenueDto] as string; + return ( + + + + + ); + })} +
+ +

{venue.name}

+ +
+ +
+ )}
); } diff --git a/frontend/src/Components/OpeningHours/index.ts b/frontend/src/Components/OpeningHours/index.ts index beb1448d2..37a62d505 100644 --- a/frontend/src/Components/OpeningHours/index.ts +++ b/frontend/src/Components/OpeningHours/index.ts @@ -1 +1,2 @@ export { OpeningHours } from './OpeningHours'; +export { OpeningHoursContainer } from './OpeningHoursContainer'; diff --git a/frontend/src/Components/Page/Page.module.scss b/frontend/src/Components/Page/Page.module.scss index b388b279c..932619c89 100644 --- a/frontend/src/Components/Page/Page.module.scss +++ b/frontend/src/Components/Page/Page.module.scss @@ -14,8 +14,6 @@ $side-padding: 1em; // Limit max content size for large screens. max-width: $content-max-size; - - // Center all content. margin: auto; @include for-mobile-only { diff --git a/frontend/src/Components/index.ts b/frontend/src/Components/index.ts index 1ffd6c92e..8fef60473 100644 --- a/frontend/src/Components/index.ts +++ b/frontend/src/Components/index.ts @@ -51,7 +51,7 @@ export { Navbar } from './Navbar'; export { NotificationBadge } from './NotificationBadge'; export { NumberInput } from './NumberInput'; export { OccupiedForm, OccupiedFormModal } from './OccupiedForm'; -export { OpeningHours } from './OpeningHours'; +export { OpeningHours, OpeningHoursContainer } from './OpeningHours'; export { Page } from './Page'; export { PagedPagination } from './Pagination'; export { PermissionRoute } from './PermissionRoute'; diff --git a/frontend/src/Pages/HomePage/HomePage.tsx b/frontend/src/Pages/HomePage/HomePage.tsx index 7e14ecc40..74d88c513 100644 --- a/frontend/src/Pages/HomePage/HomePage.tsx +++ b/frontend/src/Pages/HomePage/HomePage.tsx @@ -1,6 +1,7 @@ import { type ReactNode, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'react-toastify'; +import { OpeningHoursContainer } from '~/Components'; import { EventCarousel, LargeCard } from '~/Pages/HomePage/components'; import { getHomeData } from '~/api'; import type { HomePageDto, HomePageElementDto } from '~/dto'; @@ -50,6 +51,7 @@ export function HomePage() { return ( <> +
{/**/} {isLoading && skeleton} diff --git a/frontend/src/PagesAdmin/AdminPageLayout/AdminPageLayout.module.scss b/frontend/src/PagesAdmin/AdminPageLayout/AdminPageLayout.module.scss index 92b3009ab..eb91dd4b8 100644 --- a/frontend/src/PagesAdmin/AdminPageLayout/AdminPageLayout.module.scss +++ b/frontend/src/PagesAdmin/AdminPageLayout/AdminPageLayout.module.scss @@ -36,6 +36,7 @@ $header-bg-dark: #111111; flex-direction: row; padding-bottom: 1.5em; gap: 1em; + align-items: center; } .spinner_container { diff --git a/frontend/src/PagesAdmin/ClosedPeriodAdminPage/ClosedPeriodAdminPage.module.scss b/frontend/src/PagesAdmin/ClosedPeriodAdminPage/ClosedPeriodAdminPage.module.scss index 2a8635da1..0132b5b57 100644 --- a/frontend/src/PagesAdmin/ClosedPeriodAdminPage/ClosedPeriodAdminPage.module.scss +++ b/frontend/src/PagesAdmin/ClosedPeriodAdminPage/ClosedPeriodAdminPage.module.scss @@ -1,3 +1,20 @@ @use 'src/mixins' as *; @use 'src/constants' as *; + +.admin_closed_override_container { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; +} + +.admin_closed_radio { + margin-top: 10px; +} + +.edit_buttons { + display: flex; + flex-direction: column; + gap: 5px; +} diff --git a/frontend/src/PagesAdmin/ClosedPeriodAdminPage/ClosedPeriodAdminPage.tsx b/frontend/src/PagesAdmin/ClosedPeriodAdminPage/ClosedPeriodAdminPage.tsx index 9c20e233b..8020bf956 100644 --- a/frontend/src/PagesAdmin/ClosedPeriodAdminPage/ClosedPeriodAdminPage.tsx +++ b/frontend/src/PagesAdmin/ClosedPeriodAdminPage/ClosedPeriodAdminPage.tsx @@ -2,8 +2,10 @@ import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { toast } from 'react-toastify'; import { Button, TimeDisplay } from '~/Components'; +import { Dropdown } from '~/Components'; import { Table } from '~/Components/Table'; import { deleteClosedPeriod, getClosedPeriods } from '~/api'; +import { useGlobalContext } from '~/context/GlobalContextProvider'; import type { ClosedPeriodDto } from '~/dto'; import { useTitle } from '~/hooks'; import { KEY } from '~/i18n/constants'; @@ -16,6 +18,7 @@ export function ClosedPeriodAdminPage() { const [closedPeriods, setClosedPeriods] = useState([]); const [showSpinner, setShowSpinner] = useState(true); const { t } = useTranslation(); + const globalContext = useGlobalContext(); useTitle(t(KEY.command_menu_shortcut_closed)); const getAllClosedPeriods = useCallback(() => { @@ -51,9 +54,30 @@ export function ClosedPeriodAdminPage() { } const header = ( - + <> + + {t(KEY.admin_closed_period_closing_status)} + globalContext.setIsClosed(value)} + /> + ); const backendUrl = ROUTES.backend.admin__samfundet_closedperiod_changelist; @@ -67,21 +91,21 @@ export function ClosedPeriodAdminPage() {
({ cells: [ - element.message_no, - element.description_no, + element.message_nb, + element.message_en, { content: }, { content: }, { content: ( -
+