diff --git a/src/i18n/de.json b/src/i18n/de.json index 62c857a..8b50790 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -25,17 +25,17 @@ "al": "Abteilungsleitung", "c": "Coach" }, - "prefixPlaceholder": "Prefix für Kalendereinträge", - "prefixPreview": "Vorschau: {{calendarTitlePrefix}} Lagerdaten an Coach", + "designation": "Bezeichnung", + "defaultDesignation": "Lagerdaten", "resetValues": "Werte zurücksetzen", "download": "Herunterladen", "ics": { "download": "als Kalender", - "filename": "{{calendarTitlePrefix}}Lagerdaten.ics" + "filename": "{{calendarDesignation}}.ics" }, "csv": { "download": "als CSV", - "filename": "{{calendarTitlePrefix}}Lagerdaten.csv" + "filename": "{{calendarDesignation}}.csv" }, "excel": { "title": "Termine Lagerplanung", @@ -45,7 +45,7 @@ "afterCamp": "Nach dem Lager", "done": "Erledigt", "download": "als Excel", - "filename": "{{calendarTitlePrefix}}Lagerdaten.xlsx" + "filename": "{{calendarDesignation}}.xlsx" }, "table": { "what": "Was", diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 9b16a61..c6ffbe7 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -25,17 +25,17 @@ "al": "Responsable de groupe", "c": "Coach" }, - "prefixPlaceholder": "Préfixe pour les entrées de calendrier", - "prefixPreview": "Aperçu: {{calendarTitlePrefix}} Données relatives", + "designation": "Désignation", + "defaultDesignation": "Données du camp", "resetValues": "Réinitialiser les valeurs", "download": "Télécharger", "ics": { "download": "comme calendrier", - "filename": "{{calendarTitlePrefix}}Données du camp.ics" + "filename": "{{calendarDesignation}}.ics" }, "csv": { "download": "comme CSV", - "filename": "{{calendarTitlePrefix}}Données du camp.csv" + "filename": "{{calendarDesignation}}.csv" }, "excel": { "title": "Dates de planification du camp", @@ -45,7 +45,7 @@ "afterCamp": "Après le camp", "done": "Fait", "download": "comme Excel", - "filename": "{{calendarTitlePrefix}}Données du camp.xlsx" + "filename": "{{calendarDesignation}}.xlsx" }, "table": { "what": "Quoi", diff --git a/src/i18n/it.json b/src/i18n/it.json index 1dda494..93f0e3c 100644 --- a/src/i18n/it.json +++ b/src/i18n/it.json @@ -25,17 +25,17 @@ "al": "Capo Sezione", "c": "Coach" }, - "prefixPlaceholder": "Prefisso per le voci del calendario", - "prefixPreview": "Anteprima: {{calendarTitlePrefix}} Dati del campo", + "designation": "Designazione", + "defaultDesignation": "Dati del campo", "resetValues": "Azzeramento dei valori", "download": "Scaricare", "ics": { "download": "come calendario", - "filename": "{{calendarTitlePrefix}}Dati del campo.ics" + "filename": "{{calendarDesignation}}.ics" }, "csv": { "download": "come CSV", - "filename": "{{calendarTitlePrefix}}Dati del campo.csv" + "filename": "{{calendarDesignation}}.csv" }, "excel": { "title": "Date di pianificazione del campo", @@ -45,7 +45,7 @@ "afterCamp": "Dopo il campo", "done": "Fatto", "download": "come Excel", - "filename": "{{calendarTitlePrefix}}Dati del campo.xlsx" + "filename": "{{calendarDesignation}}.xlsx" }, "table": { "what": "Cosa", diff --git a/src/pages/calendar/CalendarPage.tsx b/src/pages/calendar/CalendarPage.tsx index bff50e7..7559e2e 100644 --- a/src/pages/calendar/CalendarPage.tsx +++ b/src/pages/calendar/CalendarPage.tsx @@ -5,7 +5,6 @@ import remarkGfm from 'remark-gfm' import { LinkComponent } from '../../helper/MarkdownComponents' import CalendarForm from './components/CalendarForm' import { faCalendarDays } from "@fortawesome/free-solid-svg-icons" -import './calendar.less' import { HApiCalendarPage } from "../../apis/hering-api"; type Props = { @@ -21,7 +20,7 @@ function CalendarPage(props: Props) { }, [calendarPage]); return
-
+

{calendarPage.title}

diff --git a/src/pages/calendar/calendar.less b/src/pages/calendar/calendar.less deleted file mode 100644 index 55bfec2..0000000 --- a/src/pages/calendar/calendar.less +++ /dev/null @@ -1,115 +0,0 @@ -.calendar { - overflow-x: auto; - - input, select { - border: 1px solid black; - width: 200px; - margin: 0; - padding: 4px; - box-sizing: border-box; - font-family: "Source Sans Pro", sans-serif; - font-size: 1rem; - border-radius: 3px; - } - - select { - padding: 4px 0; - } - - .calendar-table { - ul { - list-style-type: none; - padding-left: 2px; - margin: 5px 0; - } - - table { - width: 100%; - - tr { - vertical-align: baseline; - } - - thead tr th { - border-top: none; - font-weight: bold; - text-align: left; - } - - tbody tr td:nth-child(2) { - font-weight: 600; - } - - tbody tr td, - tbody tr th { - vertical-align: middle; - } - - a { - font-size: unset; - white-space: nowrap; - } - } - } - - .calendar-table.is-updating table { - opacity: 0.5; - } - - .calendar-form-container { - hr { - border: 0.5px solid #8c3c4f - } - - .calendar-form { - display: grid; - row-gap: 10px; - - .calendar-title-prefix-hint { - font-weight: 200; - font-size: 15px; - width: 200px; - } - - list-style-type: none; - padding: 0; - max-width: 500px; - - @media (max-width: 770px) { - label { - min-width: 80px; - } - } - @media (max-width: 590px) { - li { - flex-direction: column; - } - } - - hr { - width: 100%; - } - - label { - min-width: 220px; - max-width: 100%; - line-height: normal; - } - - li { - display: flex; - align-items: baseline; - justify-content: space-between; - row-gap: 4px; - column-gap: 32px; - - input, select, .input { - flex-grow: 1; - display: flex; - flex-direction: column; - width: 100%; - } - } - } - } -} \ No newline at end of file diff --git a/src/pages/calendar/components/CalendarForm.tsx b/src/pages/calendar/components/CalendarForm.tsx index 2b61de3..51625e5 100644 --- a/src/pages/calendar/components/CalendarForm.tsx +++ b/src/pages/calendar/components/CalendarForm.tsx @@ -1,33 +1,37 @@ -import React, { ChangeEvent, useEffect, useState } from 'react' +import React, { ChangeEvent, useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next'; import CalendarTable from './CalendarTable'; import { CalendarTask } from './Task'; import { addDays, format, isValid, parse, startOfDay } from "date-fns"; import Downloads from "./Downloads"; import Loading from "../../../components/loading/Loading"; -import { loadTasks, HApiTask } from "../../../apis/hering-api"; +import { HApiTask, loadTasks } from "../../../apis/hering-api"; import i18n from "i18next"; +import './calendar-form.less' const dateFormat = 'yyyy-MM-dd' const initialStartDate = format(Date.now(), dateFormat) const initialResponsible = 'all' +const initialPuffer = 0; const startDateCacheKey = 'start-date' const responsibleCacheKey = 'responsible' const bufferCacheKey = 'buffer' -const calendarPrefixCacheKey = 'calendar-prefix' +const calendarDesignationCacheKey = 'calendar-designation' function CalendarForm() { const { t } = useTranslation() + const defaultCalendarDesignation = t('calendarPage.defaultDesignation'); + const [isLoadingTasks, setIsLoadingTasks] = useState(true) const [isUpdatingTasks, setIsUpdatingTasks] = useState(true) const [startDate, setStartDate] = useState(initialStartDate) const [parsedStartDate, setParsedStartDate] = useState(new Date()) const [responsible, setResponsible] = useState(initialResponsible) - const [puffer, setPuffer] = useState(0) - const [calendarTitlePrefix, setCalendarTitlePrefix] = useState('') + const [puffer, setPuffer] = useState(initialPuffer) + const [designation, setDesignation] = useState(defaultCalendarDesignation) const [taskList, setTaskList] = useState(undefined) const [calendarTasks, setCalendarTasks] = useState([]) @@ -44,15 +48,15 @@ function CalendarForm() { } const onBufferChanged = (e: ChangeEvent) => { - const newBuffer = parseInt(e.currentTarget.value) || 0; + const newBuffer = parseInt(e.currentTarget.value) || initialPuffer; setPuffer(newBuffer) window.sessionStorage.setItem(bufferCacheKey, newBuffer.toString()); } - const onCalendarPrefixChanged = (e: ChangeEvent) => { + const onDesignationChanged = (e: ChangeEvent) => { const newPrefix = e.currentTarget.value - setCalendarTitlePrefix(newPrefix) - window.sessionStorage.setItem(calendarPrefixCacheKey, newPrefix); + setDesignation(newPrefix) + window.sessionStorage.setItem(calendarDesignationCacheKey, newPrefix); } const resetValues = () => { @@ -63,18 +67,22 @@ function CalendarForm() { setResponsible(initialResponsible) window.sessionStorage.removeItem(bufferCacheKey); - setPuffer(0) + setPuffer(initialPuffer) - window.sessionStorage.removeItem(calendarPrefixCacheKey); - setCalendarTitlePrefix('') + window.sessionStorage.removeItem(calendarDesignationCacheKey); + setDesignation(defaultCalendarDesignation) } - const hasActiveCache = () => { + const hasActiveCache = useCallback(() => { + const calendarDesignationCache = window.sessionStorage.getItem(calendarDesignationCacheKey) + return !!window.sessionStorage.getItem(startDateCacheKey) || !!window.sessionStorage.getItem(responsibleCacheKey) || !!window.sessionStorage.getItem(bufferCacheKey) - || !!window.sessionStorage.getItem(calendarPrefixCacheKey) - } + || !!calendarDesignationCache + || calendarDesignationCache?.length === 0 + + }, [startDate, responsible, puffer, designation]) useEffect(() => { const parsedStartDate = parse(startDate, dateFormat, Date.now()) @@ -144,8 +152,8 @@ function CalendarForm() { const buffer = window.sessionStorage.getItem(bufferCacheKey); setPuffer(parseInt(buffer || '') || 0) - const calendarPrefix = window.sessionStorage.getItem(calendarPrefixCacheKey); - setCalendarTitlePrefix(calendarPrefix || '') + const calendarPrefix = window.sessionStorage.getItem(calendarDesignationCacheKey); + setDesignation(calendarPrefix || t('calendarPage.defaultDesignation')) } loadCachedValues() @@ -153,67 +161,66 @@ function CalendarForm() { }, []); return ( -
-
-
-
    -
  • - - -
  • -
  • - - -
  • -
    -
  • - - -
  • -
  • - -
    - -
    - {t('calendarPage.prefixPreview', { calendarTitlePrefix: calendarTitlePrefix })} -
    -
    -
  • -
+
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ {hasActiveCache() && + + {t('calendarPage.resetValues')} + + } +
-
- {hasActiveCache() && - - } - +
+
{!isLoadingTasks && !!taskList - && } + && }
); } diff --git a/src/pages/calendar/components/CalendarTable.tsx b/src/pages/calendar/components/CalendarTable.tsx index c41d51e..82165cf 100644 --- a/src/pages/calendar/components/CalendarTable.tsx +++ b/src/pages/calendar/components/CalendarTable.tsx @@ -1,6 +1,7 @@ import React from 'react' import Task, { CalendarTask } from './Task'; import { useTranslation } from "react-i18next"; +import './calendar-table.less'; type Props = { tasks: CalendarTask[] diff --git a/src/pages/calendar/components/Downloads.tsx b/src/pages/calendar/components/Downloads.tsx index fef4045..0b1f4a3 100644 --- a/src/pages/calendar/components/Downloads.tsx +++ b/src/pages/calendar/components/Downloads.tsx @@ -11,7 +11,7 @@ import { CalendarTask } from "./Task"; type Props = { startDate: Date, tasks: CalendarTask[], - calendarTitlePrefix: string + designation: string } const downloadOptionStyle: React.CSSProperties = { @@ -20,30 +20,30 @@ const downloadOptionStyle: React.CSSProperties = { justifyContent: 'space-between' } -function Downloads({ startDate, tasks, calendarTitlePrefix }: Props) { +function Downloads({ startDate, tasks, designation }: Props) { const { t } = useTranslation() - const spacedCalendarPrefix = (calendarTitlePrefix ?? '').length > 0 - ? calendarTitlePrefix + ' ' - : '' + const fixedDesignation = (designation ?? '').length > 0 + ? designation + : t('calendarPage.defaultDesignation') return {t('calendarPage.download')} }>
downloadAsIcs(tasks, calendarTitlePrefix, t('calendarPage.ics.filename', { calendarTitlePrefix: spacedCalendarPrefix }))} + onClick={() => downloadAsIcs(tasks, fixedDesignation, t('calendarPage.ics.filename', { calendarDesignation: fixedDesignation }))} style={downloadOptionStyle}> {t('calendarPage.ics.download')} (.ics)
downloadAsExcel(startDate, tasks, t('calendarPage.excel.filename', { calendarTitlePrefix: spacedCalendarPrefix }))} + onClick={() => downloadAsExcel(startDate, tasks, t('calendarPage.excel.filename', { calendarDesignation: fixedDesignation }))} style={downloadOptionStyle}> {t('calendarPage.excel.download')} (.xlsx)
downloadAsCsv(tasks, t('calendarPage.csv.filename', { calendarTitlePrefix: spacedCalendarPrefix }))} + onClick={() => downloadAsCsv(tasks, t('calendarPage.csv.filename', { calendarDesignation: fixedDesignation }))} style={downloadOptionStyle}> {t('calendarPage.csv.download')} (.csv) diff --git a/src/pages/calendar/components/calendar-form.less b/src/pages/calendar/components/calendar-form.less new file mode 100644 index 0000000..f483627 --- /dev/null +++ b/src/pages/calendar/components/calendar-form.less @@ -0,0 +1,47 @@ +@import "../../../variables"; + +.calendar-form { + display: grid; + gap: 1em; + + .filter { + .filter-inputs { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 1em; + margin-bottom: 0.5em; + + @media (max-width: @header-nav-breakpoint) { + gap: 0.5em; + } + } + } + + .form-entry { + label { + color: @color-primary; + font-weight: 600; + } + + input, select { + border: 1px solid black; + width: 100%; + margin: 0; + padding: 4px; + box-sizing: border-box; + font-family: "Source Sans Pro", sans-serif; + font-size: 1rem; + border-radius: 3px; + } + + select { + padding: 4px 0; + } + } + + .download { + display: flex; + justify-content: end; + align-items: center; + } +} \ No newline at end of file diff --git a/src/pages/calendar/components/calendar-table.less b/src/pages/calendar/components/calendar-table.less new file mode 100644 index 0000000..7786f9f --- /dev/null +++ b/src/pages/calendar/components/calendar-table.less @@ -0,0 +1,39 @@ +.calendar-table { + ul { + list-style-type: none; + padding-left: 2px; + margin: 5px 0; + } + + table { + width: 100%; + + tr { + vertical-align: baseline; + } + + thead tr th { + border-top: none; + font-weight: bold; + text-align: left; + } + + tbody tr td:nth-child(2) { + font-weight: 600; + } + + tbody tr td, + tbody tr th { + vertical-align: middle; + } + + a { + font-size: unset; + white-space: nowrap; + } + } +} + +.calendar-table.is-updating table { + opacity: 0.5; +} \ No newline at end of file diff --git a/src/pages/calendar/components/download-ics.ts b/src/pages/calendar/components/download-ics.ts index aa82fed..b28e601 100644 --- a/src/pages/calendar/components/download-ics.ts +++ b/src/pages/calendar/components/download-ics.ts @@ -5,7 +5,7 @@ import { saveAs } from "../../../helper/FileHelper"; import { buildLinks } from "./download-shared"; import { HApiChapter } from "../../../apis/hering-api"; -export async function downloadAsIcs(tasks: CalendarTask[], calendarTitlePrefix: string, filename: string) { +export async function downloadAsIcs(tasks: CalendarTask[], designation: string, filename: string) { const events = tasks.map(function (task) { const deadline = task.deadline const alarms = [] @@ -19,7 +19,7 @@ export async function downloadAsIcs(tasks: CalendarTask[], calendarTitlePrefix: return { start: [deadline.getFullYear(), deadline.getMonth() + 1, deadline.getDate()], end: [deadline.getFullYear(), deadline.getMonth() + 1, deadline.getDate()], - title: `${calendarTitlePrefix} ${task.title}`, + title: `${designation} ${task.title}`, description: buildLinks(task), url: buildLinks(task), status: 'CONFIRMED', diff --git a/src/style.less b/src/style.less index 46dba16..57eb6c8 100644 --- a/src/style.less +++ b/src/style.less @@ -210,15 +210,6 @@ table { cursor: pointer; } -.btn-group { - display: flex; - flex-wrap: wrap; - gap: 0.5em; - align-items: center; - justify-content: flex-end; - margin-bottom: 2rem; -} - button.btn { padding: 0.4em 0.5em; line-height: normal;