feat: Schritte 14–15 — Google Calendar OAuth + Apple CalDAV Sync + Settings-Seite

- server/services/google-calendar.js: OAuth 2.0, bidirektionaler Sync via
  Google Calendar API v3, inkrementeller syncToken, 410-Fallback auf Vollsync
- server/services/apple-calendar.js: CalDAV via tsdav (dynamic ESM import),
  minimaler ICS-Parser + ICS-Builder, bidirektionaler Sync
- server/routes/calendar.js: 7 neue Sync-Routen (google/auth, google/callback,
  google/sync, google/status, google/disconnect, apple/status, apple/sync)
- server/db.js: Migration 2 — sync_config Tabelle + idx_calendar_external_id
- server/db-schema-test.js: MIGRATIONS_SQL[2] für Tests synchronisiert
- server/auth.js: PATCH /me/password Endpoint
- server/index.js: Auto-Sync-Scheduler (setInterval, SYNC_INTERVAL_MINUTES)
- public/pages/settings.js: vollständige Settings-Seite (Konto, Passwort,
  Kalender-Sync-Status + Aktionen, Familienmitglieder-Verwaltung)
- public/styles/settings.css: neue Stylesheet-Datei
- public/index.html + public/sw.js: settings.css eingebunden und gecacht
- .env.example: SYNC_INTERVAL_MINUTES ergänzt
- README.md: vollständige Setup-Anleitung, Google/Apple-Sync-Dokumentation,
  modernes GitHub-Layout mit Badges und aufklappbaren Abschnitten

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ulsklyc
2026-03-24 22:53:44 +01:00
parent 81d4000ee1
commit 72d6d5126e
13 changed files with 1693 additions and 131 deletions
+276
View File
@@ -0,0 +1,276 @@
/**
* Modul: Einstellungen (Settings)
* Zweck: Styles für die Settings-Seite
* Abhängigkeiten: tokens.css
*/
/* --------------------------------------------------------
Seiten-Layout
-------------------------------------------------------- */
.settings-page {
max-width: 720px;
margin: 0 auto;
}
/* --------------------------------------------------------
Banner (Erfolg / Fehler nach OAuth-Callback)
-------------------------------------------------------- */
.settings-banner {
padding: 12px 16px;
border-radius: var(--radius-sm);
margin-bottom: 16px;
font-size: 14px;
font-weight: 500;
}
.settings-banner--success {
background: rgba(52, 199, 89, 0.12);
color: var(--color-success);
border: 1px solid rgba(52, 199, 89, 0.3);
}
.settings-banner--error {
background: rgba(255, 59, 48, 0.1);
color: var(--color-danger);
border: 1px solid rgba(255, 59, 48, 0.25);
}
/* --------------------------------------------------------
Sections
-------------------------------------------------------- */
.settings-section {
margin-bottom: 32px;
}
.settings-section__title {
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--color-text-secondary);
margin: 0 0 10px 4px;
}
/* --------------------------------------------------------
Cards
-------------------------------------------------------- */
.settings-card {
background: var(--color-surface);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
padding: 20px;
margin-bottom: 12px;
}
.settings-card--hidden {
display: none;
}
.settings-card__title {
font-size: 15px;
font-weight: 600;
margin: 0 0 16px;
color: var(--color-text-primary);
}
/* --------------------------------------------------------
Benutzerinfo
-------------------------------------------------------- */
.settings-user-info {
display: flex;
align-items: center;
gap: 16px;
}
.settings-user-info__name {
font-size: 17px;
font-weight: 600;
color: var(--color-text-primary);
}
.settings-user-info__username {
font-size: 13px;
color: var(--color-text-secondary);
margin-top: 2px;
}
/* --------------------------------------------------------
Avatar
-------------------------------------------------------- */
.settings-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: 700;
color: #fff;
flex-shrink: 0;
user-select: none;
}
.settings-avatar--sm {
width: 36px;
height: 36px;
font-size: 13px;
}
/* --------------------------------------------------------
Formulare
-------------------------------------------------------- */
.settings-form {
display: flex;
flex-direction: column;
gap: 14px;
}
.settings-form-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.form-error {
font-size: 13px;
color: var(--color-danger);
padding: 8px 12px;
background: rgba(255, 59, 48, 0.08);
border-radius: var(--radius-sm);
}
.form-hint {
font-size: 13px;
color: var(--color-text-secondary);
}
.form-input--color {
padding: 4px 8px;
height: 44px;
cursor: pointer;
}
/* --------------------------------------------------------
Sync-Karten
-------------------------------------------------------- */
.settings-sync-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 14px;
}
.settings-sync-logo {
width: 40px;
height: 40px;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.settings-sync-logo--google {
background: #f8f9fa;
border: 1px solid var(--color-border);
}
.settings-sync-logo--apple {
background: #1c1c1e;
color: #fff;
}
.settings-sync-info__name {
font-size: 15px;
font-weight: 600;
color: var(--color-text-primary);
}
.settings-sync-info__status {
font-size: 13px;
color: var(--color-text-secondary);
margin-top: 2px;
}
.settings-sync-info__status--connected {
color: var(--color-success);
}
.settings-sync-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
/* --------------------------------------------------------
Familienmitglieder
-------------------------------------------------------- */
.settings-members {
list-style: none;
margin: 0 0 16px;
padding: 0;
display: flex;
flex-direction: column;
gap: 10px;
}
.settings-member {
display: flex;
align-items: center;
gap: 12px;
}
.settings-member__info {
flex: 1;
min-width: 0;
}
.settings-member__name {
display: block;
font-size: 15px;
font-weight: 500;
color: var(--color-text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.settings-member__meta {
display: block;
font-size: 12px;
color: var(--color-text-secondary);
margin-top: 1px;
}
.settings-add-btn {
width: 100%;
}
/* --------------------------------------------------------
Abmelden
-------------------------------------------------------- */
.settings-logout-btn {
width: 100%;
}
/* --------------------------------------------------------
Dark Mode
-------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
.settings-sync-logo--google {
background: #2c2c2e;
border-color: var(--color-border);
}
}