Files
Aether/frontend/src/components/calendar/DayDialog.tsx
T
marcandClaude Opus 5 0591a40d12 feat(calendar): Oberfläche auf Englisch, Standardkalender heißt „Personal"
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 <noreply@anthropic.com>
2026-09-18 18:38:30 +02:00

120 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect } from 'react'
import { useOverlayDismiss } from '../../hooks/useOverlayDismiss'
import type { CalendarEvent } from '../../api/client'
const weekdayLong = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
const monthLabels = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December',
]
export interface DayEvent extends CalendarEvent {
calendarId: string
/** Endgültige Farbe: eigene Farbe des Termins, sonst die des Kalenders. */
color: string
calendarName: string
startsAt: Date
endsAt: Date | null
}
/**
* Ein angeklickter Tag wird erst **geöffnet**, statt sofort einen neuen
* Termin anzulegen.
*
* Vorher legte jeder Klick auf eine Tageszelle direkt einen neuen Termin an –
* auch der Klick, mit dem man eigentlich nachsehen wollte, was an dem Tag
* ansteht. Hier steht zuerst, was da ist; einen neuen Termin gibt es auf
* Ansage.
*/
export default function DayDialog({ day, events, onPick, onCreate, onClose }: {
day: Date
events: DayEvent[]
onPick: (ev: DayEvent) => void
onCreate: () => void
onClose: () => void
}) {
const overlay = useOverlayDismiss(onClose)
useEffect(() => {
function onKey(e: KeyboardEvent) { if (e.key === 'Escape') onClose() }
window.addEventListener('keydown', onKey)
return () => window.removeEventListener('keydown', onKey)
}, [onClose])
const timeLabel = (ev: DayEvent) => {
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' })
return `${from} – ${to}`
}
return (
<div style={{
position: 'fixed', inset: 0, zIndex: 190, display: 'flex',
alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.6)', padding: 16,
}} {...overlay}>
<div style={{
background: 'var(--color-surface)', border: '1px solid var(--color-border)',
borderRadius: 14, width: 420, maxWidth: '100%', maxHeight: '80vh',
display: 'flex', flexDirection: 'column', overflow: 'hidden',
}}>
<div style={{ padding: '18px 22px 12px', borderBottom: '1px solid var(--color-border)' }}>
<div style={{ fontSize: 12, color: 'var(--color-text-muted)' }}>{weekdayLong[day.getDay()]}</div>
<div style={{ fontSize: 17, fontWeight: 600, color: 'var(--color-text)' }}>
{day.getDate()}. {monthLabels[day.getMonth()]} {day.getFullYear()}
</div>
</div>
<div style={{ padding: 12, overflowY: 'auto', flex: 1 }}>
{events.length === 0 && (
<p style={{ margin: '12px 10px', fontSize: 13, color: 'var(--color-text-muted)' }}>
Nichts an diesem Tag.
</p>
)}
{events.map(ev => (
<button
key={ev.calendarId + ev.uid + ev.start}
onClick={() => onPick(ev)}
style={{
display: 'flex', alignItems: 'center', gap: 10, width: '100%',
padding: '9px 10px', marginBottom: 4, borderRadius: 8, textAlign: 'left',
background: 'transparent', border: '1px solid var(--color-border)', cursor: 'pointer',
}}
className="aether-cal-cell"
>
<span style={{ width: 4, alignSelf: 'stretch', borderRadius: 2, background: ev.color, flexShrink: 0 }} />
<span style={{ flex: 1, minWidth: 0 }}>
<span style={{ display: 'block', fontSize: 13.5, color: 'var(--color-text)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{ev.summary || '(untitled)'}
{ev.recurring && <span style={{ marginLeft: 6, fontSize: 11, color: 'var(--color-text-muted)' }}>↻</span>}
</span>
<span style={{ display: 'block', fontSize: 11.5, color: 'var(--color-text-muted)' }}>
{timeLabel(ev)}
{ev.location ? ` · ${ev.location}` : ''}
{` · ${ev.calendarName}`}
</span>
</span>
</button>
))}
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, padding: '12px 16px', borderTop: '1px solid var(--color-border)' }}>
<button onClick={onCreate} style={{
padding: '8px 14px', borderRadius: 8, border: 'none',
background: 'var(--color-primary)', color: '#fff', fontSize: 13, fontWeight: 600, cursor: 'pointer',
}}>
+ Termin an diesem Tag
</button>
<button onClick={onClose} style={{
padding: '8px 16px', borderRadius: 8, border: '1px solid var(--color-border)',
background: 'transparent', color: 'var(--color-text-muted)', fontSize: 13, cursor: 'pointer',
}}>
Schließen
</button>
</div>
</div>
</div>
)
}