feat: overlapping event layout and calendar event attachments (#107)

- Overlapping timed events in week/day views now render side-by-side using a column-layout algorithm
- Calendar events support optional file attachments (images, PDFs, documents up to 5 MB)
- Attachment images shown in event popup; other files as download links
- Birthday modal redesigned with photo/avatar side-by-side with name/date fields
- DB migration 27: adds attachment_name, attachment_mime, attachment_size, attachment_data columns to calendar_events
- Server-side MIME allowlist and size validation for attachments
- i18n: all 15 locales include new attachment keys (de properly translated)

Co-Authored-By: Rafael Foster <rafaelfoster@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas Kalayci
2026-04-29 16:54:02 +02:00
23 changed files with 477 additions and 57 deletions
+42 -2
View File
@@ -391,8 +391,6 @@
.week-event {
position: absolute;
left: 2px;
right: 2px;
border-radius: var(--radius-xs);
padding: var(--space-0h) var(--space-1);
font-size: var(--text-xs);
@@ -424,6 +422,29 @@
opacity: 0.85;
}
.event-attachment-preview {
display: flex;
flex-direction: column;
gap: var(--space-2);
margin-top: var(--space-2);
}
.event-attachment-preview img {
width: 100%;
max-height: 180px;
object-fit: cover;
border-radius: var(--radius-md);
border: 1px solid var(--color-border-subtle);
}
.event-attachment-preview a {
display: inline-flex;
align-items: center;
gap: var(--space-2);
color: var(--color-accent);
word-break: break-word;
}
/* --------------------------------------------------------
* Tagesansicht
* -------------------------------------------------------- */
@@ -768,6 +789,25 @@
border-top: 1px solid var(--color-border);
}
.event-popup__attachment {
margin-top: var(--space-2);
}
.event-popup__attachment--image img {
width: 100%;
max-height: 220px;
object-fit: cover;
border-radius: var(--radius-md);
border: 1px solid var(--color-border-subtle);
}
.event-popup__attachment--file {
display: inline-flex;
align-items: center;
gap: var(--space-2);
color: var(--color-accent);
}
/* --------------------------------------------------------
* Ganztägige Ereignisse (oben in Wochen-/Tagesansicht)
* -------------------------------------------------------- */