feat(aether): initial commit – self-hosted Cloud, Phasen 1–7

Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA.
Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb,
Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming,
Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office
(Collabora/OnlyOffice).

Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort)
sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 5 committed 2026-08-24 09:25:36 +02:00
commit 31a027c93a
225 files changed
+38042

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
/**
* Die Reiterleiste – eine Definition für Admin-Bereich und Einstellungen.
*
* Beide hatten sie zunächst eigen im Code stehen. Zwei Kopien derselben Leiste
* laufen mit der Zeit auseinander: Die eine bekommt eine andere Schriftstärke,
* die andere einen anderen Abstand, und beim Umschalten merkt man den
* Unterschied, ohne ihn benennen zu können. Genau das ist bei den Kopfleisten
* schon einmal passiert (siehe components/PageHeader.tsx).
*/
interface Props<T extends string> {
tabs: { id: T; label: string }[]
active: T
onChange: (id: T) => void
}
export default function Tabs<T extends string>({ tabs, active, onChange }: Props<T>) {
return (
<div style={{
display: 'flex', gap: 4, marginBottom: 24,
borderBottom: '1px solid var(--color-border)',
// Ein Pixel Innenrand, damit die Unterstreichung des aktiven Reiters
// genau auf der Trennlinie sitzt statt darunter.
paddingBottom: 1,
overflowX: 'auto',
}}>
{tabs.map(t => (
<button
key={t.id}
onClick={() => onChange(t.id)}
style={{
padding: '8px 18px', border: 'none', cursor: 'pointer', fontSize: 14,
background: 'transparent', whiteSpace: 'nowrap', flexShrink: 0,
color: active === t.id ? 'var(--color-primary)' : 'var(--color-text-muted)',
fontWeight: active === t.id ? 700 : 400,
borderBottom: active === t.id ? '2px solid var(--color-primary)' : '2px solid transparent',
marginBottom: -1,
transition: 'color 0.15s',
}}
>
{t.label}
</button>
))}
</div>
)
}