From 0591a40d12637bf3809ae80f4b04b13e4d222e8d Mon Sep 17 00:00:00 2001 From: marc Date: Fri, 18 Sep 2026 18:38:30 +0200 Subject: [PATCH] =?UTF-8?q?feat(calendar):=20Oberfl=C3=A4che=20auf=20Engli?= =?UTF-8?q?sch,=20Standardkalender=20hei=C3=9Ft=20=E2=80=9EPersonal"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Kalenderseite war durchgehend deutsch, die übrige Oberfläche englisch – 80 Stellen in sechs Dateien. Kommentare bleiben deutsch; das Ersetzen hat Kommentarzeilen übersprungen. Shell, TagChip und UserMenu standen nur wegen ihrer Kommentare auf der Verdachtsliste, deren Oberfläche war längst englisch. Der automatisch angelegte Kalender hieß „Persönlich" und heißt jetzt „Personal". Migration 000027 zieht die bestehenden nach, bewusst eng gefasst: nur Zeilen mit Name *und* Farbe des Automaten. Wer sich selbst einen Kalender „Persönlich" angelegt hat, behält seinen Namen – das ist seine Beschriftung, nicht unsere. Nachgeprüft: Die API meldet danach 'Personal', nicht nur „Migration gelaufen". Dazu zwei Punkte aus Marcs Rückmeldung: fix(ui): Der Leerzustand der Freigabe-Links stand auf anderer Höhe als die übrigen – die Seite hatte ich beim Umbau übersehen. Nutzt jetzt EmptyState wie alle anderen. fix(ui): Beim Durchklicken von Ordner zu Ordner blitzte für einen Moment ein Feld grauer Kästen auf. Sie sahen aus wie Dateien, die gleich wieder verschwinden – „Geister". Der Platzhalter kommt jetzt erst nach 220 ms; ist die Liste vorher da, wird er nie sichtbar, und solange bleibt die vorherige stehen (load() löscht sie nicht). Nur beim allerersten Laden kommt er sofort: Da gibt es nichts, was stehenbleiben könnte. Nebenbei: Ein fehlgeschlagenes Laden eines Ordners ist nicht mehr stumm. Vorher sah es aus wie ein Ordner, der zufällig denselben Inhalt hat wie der vorige. Geprüft: go build/vet sauber, tsc sauber, 41 Vitest-Tests, alle 234 Prüfungen der sieben Selbsttest-Suiten grün. Co-Authored-By: Claude Opus 5 --- TESTING.md | 36 ++++++++--- .../000027_default_calendar_english.down.sql | 5 ++ .../000027_default_calendar_english.up.sql | 11 ++++ backend/internal/calendar/store.go | 4 +- .../000027_default_calendar_english.down.sql | 5 ++ .../000027_default_calendar_english.up.sql | 11 ++++ .../components/calendar/CalendarDialog.tsx | 4 +- .../src/components/calendar/DayDialog.tsx | 10 ++-- .../components/calendar/DeleteEventDialog.tsx | 8 +-- .../src/components/calendar/EventDialog.tsx | 60 +++++++++---------- frontend/src/components/calendar/controls.tsx | 4 +- frontend/src/pages/CalendarPage.tsx | 59 +++++++++--------- frontend/src/pages/FileBrowser.tsx | 45 ++++++++++++-- frontend/src/pages/SharedPage.tsx | 18 +++--- 14 files changed, 179 insertions(+), 101 deletions(-) create mode 100644 backend/db/migrations/000027_default_calendar_english.down.sql create mode 100644 backend/db/migrations/000027_default_calendar_english.up.sql create mode 100644 backend/pkg/db/migrations/000027_default_calendar_english.down.sql create mode 100644 backend/pkg/db/migrations/000027_default_calendar_english.up.sql diff --git a/TESTING.md b/TESTING.md index 2cf6bac..a7bbfcc 100644 --- a/TESTING.md +++ b/TESTING.md @@ -1564,17 +1564,35 @@ gewesen, an dem jemand die letzte Kopie entfernt. Steht jetzt einmal in - [ ] Kreisel dreht sich noch: Suchfeld tippen, Admin → Audit laden, Versionen-Dialog öffnen 🔎 -### Offen: der Sprachmix +### Der Sprachmix ist weg (18.09.2026) -`CalendarPage.tsx` ist durchgehend deutsch („Kalender", „Lädt…", „Keine -Termine"), die übrige Oberfläche englisch. Betroffen sind sechs Dateien: -CalendarPage, Shell, calendar/controls, calendar/EventDialog, TagChip, -UserMenu. +Die Kalenderseite war durchgehend deutsch, die übrige Oberfläche englisch. +Jetzt spricht alles Englisch – 80 Stellen in sechs Dateien. Shell, TagChip und +UserMenu standen nur wegen ihrer **Kommentare** auf der Verdachtsliste; deren +Oberfläche war längst englisch. -Bewusst **nicht** nebenbei erledigt: Eine halbe Übersetzung ist schlimmer als -gar keine, weil dann eine Seite in sich gemischt ist. Das gehört in einem Zug -gemacht – samt der Entscheidung, welche Sprache die Oberfläche eigentlich -spricht. +Kommentare bleiben deutsch. Das Ersetzen hat Kommentarzeilen deshalb +übersprungen. + +- [ ] Monatsansicht: Wochentage Mon–Sun, Monatsnamen englisch 🔎 +- [ ] Umschalter Month / Week / List, Knopf „Today" +- [ ] Termin anlegen: Title, Calendar, All day, Start/End, Repeat, Color, + Location, Description +- [ ] Wiederholung: Does not repeat / Daily / Weekly / Monthly / Yearly, + Ends: Never / After / On a date +- [ ] Serientermin löschen: „Only this event" / „Entire series" / „Cancel" +- [ ] Kalendermenü: Edit / Share… / Export (.ics) / Import (.ics) / Delete +- [ ] Leerer Zeitraum → „No events" + +### Der Standardkalender heißt „Personal" + +Automatisch angelegt wurde er als „Persönlich". Migration 000027 zieht die +bestehenden nach – **bewusst eng gefasst**: nur Zeilen mit Name *und* Farbe des +Automaten (`#4285f4`). Wer sich selbst einen Kalender „Persönlich" angelegt +hat, behält seinen Namen; das ist seine Beschriftung, nicht unsere. + +- [x] Nach der Migration meldet die API `'Personal'` – geprüft, nicht nur + „Migration gelaufen". --- diff --git a/backend/db/migrations/000027_default_calendar_english.down.sql b/backend/db/migrations/000027_default_calendar_english.down.sql new file mode 100644 index 0000000..82cbf1c --- /dev/null +++ b/backend/db/migrations/000027_default_calendar_english.down.sql @@ -0,0 +1,5 @@ +-- Zurück auf den deutschen Namen, nach derselben engen Regel. +UPDATE cal_calendars + SET name = 'Persönlich' + WHERE name = 'Personal' + AND color = '#4285f4'; diff --git a/backend/db/migrations/000027_default_calendar_english.up.sql b/backend/db/migrations/000027_default_calendar_english.up.sql new file mode 100644 index 0000000..816c06a --- /dev/null +++ b/backend/db/migrations/000027_default_calendar_english.up.sql @@ -0,0 +1,11 @@ +-- Der automatisch angelegte Kalender hieß "Persönlich", die übrige Oberfläche +-- spricht Englisch. Seit dem 18.09.2026 heißt er "Personal"; diese Migration +-- zieht die bereits angelegten nach. +-- +-- **Bewusst eng gefasst:** nur Zeilen, die Name *und* Farbe des Automaten +-- tragen. Wer sich selbst einen Kalender "Persönlich" angelegt hat, behält +-- seinen Namen – das ist seine Beschriftung, nicht unsere. +UPDATE cal_calendars + SET name = 'Personal' + WHERE name = 'Persönlich' + AND color = '#4285f4'; diff --git a/backend/internal/calendar/store.go b/backend/internal/calendar/store.go index 580a8b7..56cd00c 100644 --- a/backend/internal/calendar/store.go +++ b/backend/internal/calendar/store.go @@ -126,7 +126,7 @@ func isUniqueViolation(err error) bool { } // EnsureDefaultCalendar liefert den ersten Kalender des Nutzers, legt bei -// Bedarf "Persönlich" an. So findet ein frisch eingerichteter Client sofort +// Bedarf "Personal" an. So findet ein frisch eingerichteter Client sofort // ein Ziel vor, statt erst ein MKCALENDAR schicken zu müssen – nicht jeder // CalDAV-Client kann das. func (s *Store) EnsureDefaultCalendar(ctx context.Context, ownerID uuid.UUID) (*Calendar, error) { @@ -137,7 +137,7 @@ func (s *Store) EnsureDefaultCalendar(ctx context.Context, ownerID uuid.UUID) (* if len(cals) > 0 { return &cals[0], nil } - return s.CreateCalendar(ctx, ownerID, "Persönlich", "#4285f4", "") + return s.CreateCalendar(ctx, ownerID, "Personal", "#4285f4", "") } func (s *Store) CreateCalendar(ctx context.Context, ownerID uuid.UUID, name, color, description string) (*Calendar, error) { diff --git a/backend/pkg/db/migrations/000027_default_calendar_english.down.sql b/backend/pkg/db/migrations/000027_default_calendar_english.down.sql new file mode 100644 index 0000000..82cbf1c --- /dev/null +++ b/backend/pkg/db/migrations/000027_default_calendar_english.down.sql @@ -0,0 +1,5 @@ +-- Zurück auf den deutschen Namen, nach derselben engen Regel. +UPDATE cal_calendars + SET name = 'Persönlich' + WHERE name = 'Personal' + AND color = '#4285f4'; diff --git a/backend/pkg/db/migrations/000027_default_calendar_english.up.sql b/backend/pkg/db/migrations/000027_default_calendar_english.up.sql new file mode 100644 index 0000000..816c06a --- /dev/null +++ b/backend/pkg/db/migrations/000027_default_calendar_english.up.sql @@ -0,0 +1,11 @@ +-- Der automatisch angelegte Kalender hieß "Persönlich", die übrige Oberfläche +-- spricht Englisch. Seit dem 18.09.2026 heißt er "Personal"; diese Migration +-- zieht die bereits angelegten nach. +-- +-- **Bewusst eng gefasst:** nur Zeilen, die Name *und* Farbe des Automaten +-- tragen. Wer sich selbst einen Kalender "Persönlich" angelegt hat, behält +-- seinen Namen – das ist seine Beschriftung, nicht unsere. +UPDATE cal_calendars + SET name = 'Personal' + WHERE name = 'Persönlich' + AND color = '#4285f4'; diff --git a/frontend/src/components/calendar/CalendarDialog.tsx b/frontend/src/components/calendar/CalendarDialog.tsx index f2b4ac5..84a6668 100644 --- a/frontend/src/components/calendar/CalendarDialog.tsx +++ b/frontend/src/components/calendar/CalendarDialog.tsx @@ -34,7 +34,7 @@ export default function CalendarDialog({ calendar, onSave, onCancel }: Props) { borderRadius: 14, padding: 24, width: 340, maxWidth: '100%', }}>

- {calendar ? 'Kalender bearbeiten' : 'Neuer Kalender'} + {calendar ? 'Edit calendar' : 'New calendar'}

@@ -48,7 +48,7 @@ export default function CalendarDialog({ calendar, onSave, onCancel }: Props) { }} /> - +
diff --git a/frontend/src/components/calendar/DayDialog.tsx b/frontend/src/components/calendar/DayDialog.tsx index f0987e4..356e303 100644 --- a/frontend/src/components/calendar/DayDialog.tsx +++ b/frontend/src/components/calendar/DayDialog.tsx @@ -2,10 +2,10 @@ import { useEffect } from 'react' import { useOverlayDismiss } from '../../hooks/useOverlayDismiss' import type { CalendarEvent } from '../../api/client' -const weekdayLong = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'] +const weekdayLong = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] const monthLabels = [ - 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', - 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember', + 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December', ] export interface DayEvent extends CalendarEvent { @@ -42,7 +42,7 @@ export default function DayDialog({ day, events, onPick, onCreate, onClose }: { }, [onClose]) const timeLabel = (ev: DayEvent) => { - if (ev.all_day) return 'ganztägig' + if (ev.all_day) return 'all day' const from = ev.startsAt.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) if (!ev.endsAt) return from const to = ev.endsAt.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) @@ -86,7 +86,7 @@ export default function DayDialog({ day, events, onPick, onCreate, onClose }: { - {ev.summary || '(ohne Titel)'} + {ev.summary || '(untitled)'} {ev.recurring && ↻} diff --git a/frontend/src/components/calendar/DeleteEventDialog.tsx b/frontend/src/components/calendar/DeleteEventDialog.tsx index 4f3b71b..d0fbb14 100644 --- a/frontend/src/components/calendar/DeleteEventDialog.tsx +++ b/frontend/src/components/calendar/DeleteEventDialog.tsx @@ -42,17 +42,17 @@ export default function DeleteEventDialog({ summary, recurring, onSingle, onSeri borderRadius: 14, padding: 24, width: 380, maxWidth: '100%', }}>

- {recurring ? 'Serientermin löschen' : 'Termin löschen'} + {recurring ? 'Delete recurring event' : 'Delete event'}

{recurring - ? <>„{summary || 'Ohne Titel'}" gehört zu einer Reihe. Was soll gelöscht werden? - : <>„{summary || 'Ohne Titel'}" unwiderruflich löschen?} + ? <>„{summary || 'Untitled'}" gehört zu einer Reihe. Was soll gelöscht werden? + : <>„{summary || 'Untitled'}" unwiderruflich löschen?}

{recurring && (
{!allDay && (
- + setStartTime(e.target.value)} style={inputStyle} />
)} @@ -245,13 +245,13 @@ export default function EventDialog({ event, defaultStart, calendars, calendarId
- + setEndDate(e.target.value)} style={inputStyle} />
{!allDay && (
- + setEndTime(e.target.value)} style={inputStyle} />
)} @@ -259,7 +259,7 @@ export default function EventDialog({ event, defaultStart, calendars, calendarId {/* ── Wiederholung ───────────────────────────────────────────── */}
- +
{freq !== 'NONE' && (
- alle + every setInterval(e.target.value)} style={{ ...inputStyle, width: 60 }} /> @@ -283,7 +283,7 @@ export default function EventDialog({ event, defaultStart, calendars, calendarId eine Reihe, die ihren eigenen ersten Termin auslässt, ist keine. */} {freq === 'WEEKLY' && (
- +
{weekdayCodes.map((code, i) => { const on = activeDays.includes(code) @@ -314,7 +314,7 @@ export default function EventDialog({ event, defaultStart, calendars, calendarId {/* Monatlich/jährlich: am festen Tag oder am n-ten Wochentag. */} {(freq === 'MONTHLY' || freq === 'YEARLY') && (
- + setEndChoice(e.target.value as EndChoice)} style={{ ...inputStyle, flex: 1 }}> - - - + + + {endChoice === 'COUNT' && (
setCount(e.target.value)} style={{ ...inputStyle, width: 70 }} /> - Mal + times
)} {endChoice === 'UNTIL' && ( @@ -352,17 +352,17 @@ export default function EventDialog({ event, defaultStart, calendars, calendarId {/* ── Farbe ──────────────────────────────────────────────────── */}
- +
- + setLocation(e.target.value)} style={inputStyle} />
- +