diff --git a/server.js b/server.js index 3c5f8a0..9420822 100644 --- a/server.js +++ b/server.js @@ -237,12 +237,23 @@ function getCachedStoreStatus(storeId) { } function normalizeJournalReminder(reminder = {}) { + const unit = ['days', 'weeks', 'months'].includes(reminder.beforeUnit) ? reminder.beforeUnit : 'days'; + const parsedBeforeValue = Number(reminder.beforeValue); + const parsedDaysBefore = Number(reminder.daysBefore); + const beforeValue = Number.isFinite(parsedBeforeValue) + ? Math.max(0, parsedBeforeValue) + : Number.isFinite(parsedDaysBefore) + ? Math.max(0, parsedDaysBefore) + : 42; + const daysBefore = unit === 'weeks' ? beforeValue * 7 : unit === 'months' ? beforeValue * 30 : beforeValue; return { enabled: !!reminder.enabled, interval: ['monthly', 'quarterly', 'yearly'].includes(reminder.interval) ? reminder.interval : 'yearly', - daysBefore: Number.isFinite(reminder.daysBefore) ? Math.max(0, reminder.daysBefore) : 42 + beforeUnit: unit, + beforeValue, + daysBefore }; } diff --git a/src/components/JournalPage.js b/src/components/JournalPage.js index ade6a2d..4d4422f 100644 --- a/src/components/JournalPage.js +++ b/src/components/JournalPage.js @@ -7,6 +7,16 @@ const intervalLabels = { quarterly: 'Vierteljährlich', yearly: 'Jährlich' }; +const reminderUnitLabels = { + days: 'Tage', + weeks: 'Wochen', + months: 'Monate' +}; +const reminderUnitSingular = { + days: 'Tag', + weeks: 'Woche', + months: 'Monat' +}; const JournalPage = ({ authorizedFetch, stores }) => { const [entries, setEntries] = useState([]); @@ -42,7 +52,8 @@ const JournalPage = ({ authorizedFetch, stores }) => { note: '', reminderEnabled: true, reminderInterval: 'yearly', - reminderDaysBefore: 42 + reminderBeforeValue: 42, + reminderBeforeUnit: 'days' }); const [images, setImages] = useState([]); const [existingImages, setExistingImages] = useState([]); @@ -105,6 +116,45 @@ const JournalPage = ({ authorizedFetch, stores }) => { return 12; }, []); + const getReminderOffset = useCallback((reminder) => { + const unit = ['days', 'weeks', 'months'].includes(reminder?.beforeUnit) ? reminder.beforeUnit : 'days'; + const rawValue = Number(reminder?.beforeValue); + const fallbackValue = Number(reminder?.daysBefore); + const value = Number.isFinite(rawValue) + ? Math.max(0, rawValue) + : Number.isFinite(fallbackValue) + ? Math.max(0, fallbackValue) + : 42; + return { unit, value }; + }, []); + + const subtractReminderOffset = useCallback( + (date, reminder) => { + const { unit, value } = getReminderOffset(reminder); + const copy = new Date(date.getTime()); + if (unit === 'weeks') { + copy.setDate(copy.getDate() - value * 7); + return copy; + } + if (unit === 'months') { + copy.setMonth(copy.getMonth() - value); + return copy; + } + copy.setDate(copy.getDate() - value); + return copy; + }, + [getReminderOffset] + ); + + const formatReminderOffset = useCallback( + (reminder) => { + const { unit, value } = getReminderOffset(reminder); + const label = value === 1 ? reminderUnitSingular[unit] : reminderUnitLabels[unit]; + return `${value} ${label}`; + }, + [getReminderOffset] + ); + const addMonths = useCallback((date, months) => { const copy = new Date(date.getTime()); copy.setMonth(copy.getMonth() + months); @@ -125,9 +175,6 @@ const JournalPage = ({ authorizedFetch, stores }) => { return null; } const intervalMonths = getIntervalMonths(entry.reminder.interval); - const daysBefore = Number.isFinite(entry.reminder.daysBefore) - ? Math.max(0, entry.reminder.daysBefore) - : 42; const todayStart = startOfDay(new Date()); let occurrence = startOfDay(baseDate); const guardYear = todayStart.getFullYear() + 200; @@ -137,16 +184,14 @@ const JournalPage = ({ authorizedFetch, stores }) => { if (occurrence.getFullYear() >= guardYear) { return null; } - let reminderDate = new Date(occurrence.getTime()); - reminderDate.setDate(reminderDate.getDate() - daysBefore); + let reminderDate = startOfDay(subtractReminderOffset(occurrence, entry.reminder)); if (reminderDate < todayStart) { occurrence = startOfDay(addMonths(occurrence, intervalMonths)); - reminderDate = new Date(occurrence.getTime()); - reminderDate.setDate(reminderDate.getDate() - daysBefore); + reminderDate = startOfDay(subtractReminderOffset(occurrence, entry.reminder)); } return reminderDate.getTime(); }, - [addMonths, getIntervalMonths, startOfDay] + [addMonths, getIntervalMonths, startOfDay, subtractReminderOffset] ); const filteredEntries = useMemo(() => { @@ -296,7 +341,8 @@ const JournalPage = ({ authorizedFetch, stores }) => { note: '', reminderEnabled: true, reminderInterval: 'yearly', - reminderDaysBefore: 42 + reminderBeforeValue: 42, + reminderBeforeUnit: 'days' }); }, [images]); @@ -389,7 +435,8 @@ const JournalPage = ({ authorizedFetch, stores }) => { reminder: { enabled: form.reminderEnabled, interval: form.reminderInterval, - daysBefore: Number(form.reminderDaysBefore) + beforeUnit: form.reminderBeforeUnit, + beforeValue: Number(form.reminderBeforeValue) }, images: imagePayload, keepImageIds: existingImages.map((image) => image.id) @@ -467,7 +514,8 @@ const JournalPage = ({ authorizedFetch, stores }) => { note: entry.note || '', reminderEnabled: entry.reminder?.enabled ?? true, reminderInterval: entry.reminder?.interval || 'yearly', - reminderDaysBefore: Number.isFinite(entry.reminder?.daysBefore) ? entry.reminder.daysBefore : 42 + reminderBeforeValue: getReminderOffset(entry.reminder).value, + reminderBeforeUnit: getReminderOffset(entry.reminder).unit }); setFormOpen(true); }; @@ -530,26 +578,44 @@ const JournalPage = ({ authorizedFetch, stores }) => {
-
-

Journal-Einträge

-
- - -
+
+

Journal-Einträge

+
+ +
+
{error ? (

{error}

@@ -613,7 +679,7 @@ const JournalPage = ({ authorizedFetch, stores }) => { {filteredEntries.map((entry) => { const reminder = entry.reminder || {}; const reminderLabel = reminder.enabled - ? `${intervalLabels[reminder.interval] || 'Jährlich'}, ${reminder.daysBefore} Tage vorher` + ? `${intervalLabels[reminder.interval] || 'Jährlich'}, ${formatReminderOffset(reminder)} vorher` : 'Keine Erinnerung'; const storeLabel = entry.storeName || @@ -754,9 +820,16 @@ const JournalPage = ({ authorizedFetch, stores }) => {
@@ -917,18 +990,33 @@ const JournalPage = ({ authorizedFetch, stores }) => { ))}
-
- +
+ +
- handleFormChange({ reminderDaysBefore: event.target.value }) + handleFormChange({ reminderBeforeValue: event.target.value }) } - className="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200" + className="w-24 border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200" /> +
+

Erinnerung wird standardmäßig jährlich eingeplant.