feat(theme): implement single-file CSS theming engine with dark, light, high-contrast, and soft themes
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 16s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 16s
This commit is contained in:
1 parent
0f24cbdbf4
commit
b3e651b3b9
18 files changed
+1455
-804
No files matched your search
@@ -28,7 +28,9 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
const [setupEmail, setSetupEmail] = useState('');
|
||||
const [setupPassword, setSetupPassword] = useState('');
|
||||
const [instanceName, setInstanceName] = useState(authStatus.instanceName || 'MindSift');
|
||||
const [sharingMode, setSharingMode] = useState<'open' | 'teams_only' | 'strict_private'>('teams_only');
|
||||
const [sharingMode, setSharingMode] = useState<'open' | 'teams_only' | 'strict_private'>(
|
||||
'teams_only'
|
||||
);
|
||||
const [allowRegistration, setAllowRegistration] = useState(false);
|
||||
const [createSampleData, setCreateSampleData] = useState(false);
|
||||
|
||||
@@ -101,52 +103,50 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
// ============================================================================
|
||||
if (authStatus.needsSetup) {
|
||||
return (
|
||||
<div className="min-h-screen bg-canvas text-white flex flex-col items-center justify-center p-4 font-sans">
|
||||
<div className="w-full max-w-xl bg-panel border border-border shadow-2xl">
|
||||
<div className="min-h-screen bg-canvas text-main flex flex-col items-center justify-center p-4 font-sans">
|
||||
<div className="w-full max-w-xl panel-surface shadow-panel overflow-hidden">
|
||||
{/* Top Brand Bar */}
|
||||
<div className="p-4 bg-canvas border-b border-border flex items-center justify-between">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="w-8 h-8 bg-accent text-white flex items-center justify-center">
|
||||
<div className="w-8 h-8 bg-accent text-on-accent rounded-btn flex items-center justify-center">
|
||||
<Brain className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-base font-bold text-white tracking-tight leading-none">
|
||||
<h1 className="text-base font-bold text-main tracking-tight leading-none">
|
||||
MindSift Ersteinrichtung
|
||||
</h1>
|
||||
<p className="text-[11px] text-zinc-400 mt-0.5">
|
||||
<p className="text-[11px] text-muted mt-0.5">
|
||||
Initiales Administrator-Konto & Server-Konfiguration
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<span className="px-2 py-0.5 bg-accent/15 border border-accent text-accent text-[10px] font-semibold uppercase tracking-wider">
|
||||
First Boot
|
||||
</span>
|
||||
<span className="badge-accent uppercase tracking-wider">First Boot</span>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleCompleteSetup} className="p-5 space-y-5 text-xs">
|
||||
<p className="text-zinc-300 leading-relaxed">
|
||||
<p className="text-muted leading-relaxed">
|
||||
Willkommen auf deiner neuen MindSift-Instanz. Es existiert noch kein Benutzerkonto in
|
||||
der Datenbank. Lege jetzt das erste <strong>Server-Admin-Konto</strong> an und bestimme
|
||||
die Sicherheitseinstellungen.
|
||||
der Datenbank. Lege jetzt das erste <strong className="text-main">Server-Admin-Konto</strong> an und
|
||||
bestimme die Sicherheitseinstellungen.
|
||||
</p>
|
||||
|
||||
{error && (
|
||||
<div className="p-3 bg-red-950/40 border border-red-500 text-red-200 flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-red-400 flex-shrink-0" />
|
||||
<div className="alert-err flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-status-err flex-shrink-0" />
|
||||
<span>{error}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Section 1: Admin Account */}
|
||||
<div className="p-3.5 bg-canvas border border-border space-y-3">
|
||||
<h2 className="text-xs font-bold text-white uppercase tracking-wider flex items-center gap-1.5">
|
||||
<h2 className="text-xs font-bold text-main uppercase tracking-wider flex items-center gap-1.5">
|
||||
<Shield className="w-3.5 h-3.5 text-accent" />
|
||||
<span>1. Server-Administrator anlegen</span>
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1">Dein Anzeigename *</label>
|
||||
<label className="block text-muted mb-1">Dein Anzeigename *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={setupName}
|
||||
@@ -154,24 +154,24 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
placeholder="z.B. Admin / Dein Name"
|
||||
required
|
||||
autoFocus
|
||||
className="w-full h-8 px-2.5 input-field text-white"
|
||||
className="w-full h-8 px-2.5 input-field"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1">E-Mail-Adresse (Login) *</label>
|
||||
<label className="block text-muted mb-1">E-Mail-Adresse (Login) *</label>
|
||||
<input
|
||||
type="email"
|
||||
value={setupEmail}
|
||||
onChange={(e) => setSetupEmail(e.target.value)}
|
||||
placeholder="admin@deinedomain.de"
|
||||
required
|
||||
className="w-full h-8 px-2.5 input-field text-white"
|
||||
className="w-full h-8 px-2.5 input-field"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1">Administrator-Passwort *</label>
|
||||
<label className="block text-muted mb-1">Administrator-Passwort *</label>
|
||||
<input
|
||||
type="password"
|
||||
value={setupPassword}
|
||||
@@ -179,31 +179,31 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
placeholder="Mindestens 4 Zeichen"
|
||||
minLength={4}
|
||||
required
|
||||
className="w-full h-8 px-2.5 input-field text-white"
|
||||
className="w-full h-8 px-2.5 input-field"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section 2: Server & Isolation */}
|
||||
<div className="p-3.5 bg-canvas border border-border space-y-3">
|
||||
<h2 className="text-xs font-bold text-white uppercase tracking-wider flex items-center gap-1.5">
|
||||
<h2 className="text-xs font-bold text-main uppercase tracking-wider flex items-center gap-1.5">
|
||||
<Server className="w-3.5 h-3.5 text-accent" />
|
||||
<span>2. Instanz & Berechtigungen</span>
|
||||
</h2>
|
||||
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1">Name der Instanz</label>
|
||||
<label className="block text-muted mb-1">Name der Instanz</label>
|
||||
<input
|
||||
type="text"
|
||||
value={instanceName}
|
||||
onChange={(e) => setInstanceName(e.target.value)}
|
||||
placeholder="MindSift"
|
||||
className="w-full h-8 px-2.5 input-field text-white"
|
||||
className="w-full h-8 px-2.5 input-field"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1.5 flex items-center gap-1">
|
||||
<label className="block text-muted mb-1.5 flex items-center gap-1">
|
||||
<Share2 className="w-3 h-3 text-accent" />
|
||||
<span>Sichtbarkeit & Sharing-Isolation zwischen Benutzern</span>
|
||||
</label>
|
||||
@@ -213,12 +213,12 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
onClick={() => setSharingMode('strict_private')}
|
||||
className={`p-2.5 text-left border transition-colors ${
|
||||
sharingMode === 'strict_private'
|
||||
? 'bg-panel border-accent text-white'
|
||||
: 'border-border text-zinc-400 hover:text-white'
|
||||
? 'bg-panel border-accent text-main'
|
||||
: 'border-border text-muted hover:text-main'
|
||||
}`}
|
||||
>
|
||||
<p className="font-bold text-xs">Strikt Privat</p>
|
||||
<p className="text-[10px] text-zinc-400 mt-0.5">
|
||||
<p className="text-[10px] text-muted mt-0.5">
|
||||
Keiner sieht andere Nutzer. Komplett isoliert.
|
||||
</p>
|
||||
</button>
|
||||
@@ -228,12 +228,12 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
onClick={() => setSharingMode('teams_only')}
|
||||
className={`p-2.5 text-left border transition-colors ${
|
||||
sharingMode === 'teams_only'
|
||||
? 'bg-panel border-accent text-white'
|
||||
: 'border-border text-zinc-400 hover:text-white'
|
||||
? 'bg-panel border-accent text-main'
|
||||
: 'border-border text-muted hover:text-main'
|
||||
}`}
|
||||
>
|
||||
<p className="font-bold text-xs">Teams isoliert</p>
|
||||
<p className="text-[10px] text-zinc-400 mt-0.5">
|
||||
<p className="text-[10px] text-muted mt-0.5">
|
||||
Nutzer sehen nur Mitglieder ihrer eigenen Teams.
|
||||
</p>
|
||||
</button>
|
||||
@@ -243,12 +243,12 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
onClick={() => setSharingMode('open')}
|
||||
className={`p-2.5 text-left border transition-colors ${
|
||||
sharingMode === 'open'
|
||||
? 'bg-panel border-accent text-white'
|
||||
: 'border-border text-zinc-400 hover:text-white'
|
||||
? 'bg-panel border-accent text-main'
|
||||
: 'border-border text-muted hover:text-main'
|
||||
}`}
|
||||
>
|
||||
<p className="font-bold text-xs">Alle sehen alle</p>
|
||||
<p className="text-[10px] text-zinc-400 mt-0.5">
|
||||
<p className="text-[10px] text-muted mt-0.5">
|
||||
Offene Zusammenarbeit auf dem gesamten Server.
|
||||
</p>
|
||||
</button>
|
||||
@@ -258,11 +258,12 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
<div className="pt-2 border-t border-border space-y-2">
|
||||
<label className="flex items-center justify-between cursor-pointer select-none">
|
||||
<div>
|
||||
<span className="font-medium text-white block">
|
||||
<span className="font-medium text-main block">
|
||||
Öffentliche Selbstregistrierung erlauben
|
||||
</span>
|
||||
<span className="text-[11px] text-zinc-400">
|
||||
Wenn deaktiviert, können neue Nutzer nur per Einladungscode oder durch einen Admin angelegt werden.
|
||||
<span className="text-[11px] text-muted">
|
||||
Wenn deaktiviert, können neue Nutzer nur per Einladungscode oder durch einen
|
||||
Admin angelegt werden.
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
@@ -275,10 +276,10 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
|
||||
<label className="flex items-center justify-between cursor-pointer select-none pt-1">
|
||||
<div>
|
||||
<span className="font-medium text-white block">
|
||||
<span className="font-medium text-main block">
|
||||
Start-Beispieldaten anlegen
|
||||
</span>
|
||||
<span className="text-[11px] text-zinc-400">
|
||||
<span className="text-[11px] text-muted">
|
||||
Erstellt wenige Beispiel-Gedanken und ein Einführungsdokument zum Testen.
|
||||
</span>
|
||||
</div>
|
||||
@@ -299,7 +300,11 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
className="btn-primary h-9 px-5 text-xs font-semibold flex items-center gap-2 disabled:opacity-50"
|
||||
>
|
||||
<Check className="w-4 h-4" />
|
||||
<span>{submitting ? 'Richte Server ein…' : 'Ersteinrichtung abschließen & Starten'}</span>
|
||||
<span>
|
||||
{submitting
|
||||
? 'Richte Server ein…'
|
||||
: 'Ersteinrichtung abschließen & Starten'}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -312,24 +317,22 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
// STANDARD LOGIN / INVITE CODE / REGISTRATION VIEW
|
||||
// ============================================================================
|
||||
return (
|
||||
<div className="min-h-screen bg-canvas text-white flex flex-col items-center justify-center p-4 font-sans">
|
||||
<div className="w-full max-w-md bg-panel border border-border shadow-2xl">
|
||||
<div className="min-h-screen bg-canvas text-main flex flex-col items-center justify-center p-4 font-sans">
|
||||
<div className="w-full max-w-md panel-surface shadow-panel overflow-hidden">
|
||||
{/* Top Header */}
|
||||
<div className="p-4 bg-canvas border-b border-border flex items-center justify-between">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="w-7 h-7 bg-accent text-white flex items-center justify-center">
|
||||
<div className="w-7 h-7 bg-accent text-on-accent rounded-btn flex items-center justify-center">
|
||||
<Brain className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-sm font-bold text-white tracking-tight leading-none">
|
||||
<h1 className="text-sm font-bold text-main tracking-tight leading-none">
|
||||
{authStatus.instanceName || 'MindSift'}
|
||||
</h1>
|
||||
<p className="text-[11px] text-zinc-400 mt-0.5">
|
||||
Der Kopf frei in drei Sekunden
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-0.5">Der Kopf frei in drei Sekunden</p>
|
||||
</div>
|
||||
</div>
|
||||
<Lock className="w-4 h-4 text-zinc-500" />
|
||||
<Lock className="w-4 h-4 text-faint" />
|
||||
</div>
|
||||
|
||||
{/* Navigation Tabs */}
|
||||
@@ -342,8 +345,8 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
}}
|
||||
className={`flex-1 h-9 px-3 font-medium flex items-center justify-center gap-1.5 border-r border-border transition-colors ${
|
||||
authTab === 'login'
|
||||
? 'bg-panel text-white border-b-2 border-b-accent'
|
||||
: 'text-zinc-400 hover:text-white'
|
||||
? 'bg-panel text-main border-b-2 border-b-accent'
|
||||
: 'text-muted hover:text-main'
|
||||
}`}
|
||||
>
|
||||
<LogIn className="w-3.5 h-3.5" />
|
||||
@@ -358,8 +361,8 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
}}
|
||||
className={`flex-1 h-9 px-3 font-medium flex items-center justify-center gap-1.5 border-r border-border transition-colors ${
|
||||
authTab === 'invite'
|
||||
? 'bg-panel text-white border-b-2 border-b-accent'
|
||||
: 'text-zinc-400 hover:text-white'
|
||||
? 'bg-panel text-main border-b-2 border-b-accent'
|
||||
: 'text-muted hover:text-main'
|
||||
}`}
|
||||
>
|
||||
<KeyRound className="w-3.5 h-3.5" />
|
||||
@@ -375,8 +378,8 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
}}
|
||||
className={`flex-1 h-9 px-3 font-medium flex items-center justify-center gap-1.5 transition-colors ${
|
||||
authTab === 'register'
|
||||
? 'bg-panel text-white border-b-2 border-b-accent'
|
||||
: 'text-zinc-400 hover:text-white'
|
||||
? 'bg-panel text-main border-b-2 border-b-accent'
|
||||
: 'text-muted hover:text-main'
|
||||
}`}
|
||||
>
|
||||
<UserPlus className="w-3.5 h-3.5" />
|
||||
@@ -388,8 +391,8 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
{/* Form Body */}
|
||||
<div className="p-5 text-xs">
|
||||
{error && (
|
||||
<div className="mb-4 p-2.5 bg-red-950/40 border border-red-500 text-red-200 flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-red-400 flex-shrink-0" />
|
||||
<div className="mb-4 alert-err flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-status-err flex-shrink-0" />
|
||||
<span>{error}</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -397,7 +400,7 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
{authTab === 'login' && (
|
||||
<form onSubmit={handleLogin} className="space-y-3.5">
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1">E-Mail-Adresse</label>
|
||||
<label className="block text-muted mb-1">E-Mail-Adresse</label>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
@@ -405,19 +408,19 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
placeholder="name@beispiel.de"
|
||||
required
|
||||
autoFocus
|
||||
className="w-full h-8 px-2.5 input-field text-white"
|
||||
className="w-full h-8 px-2.5 input-field"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1">Passwort</label>
|
||||
<label className="block text-muted mb-1">Passwort</label>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="••••••••"
|
||||
required
|
||||
className="w-full h-8 px-2.5 input-field text-white"
|
||||
className="w-full h-8 px-2.5 input-field"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -433,7 +436,7 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
</div>
|
||||
|
||||
{!authStatus.allowRegistration && (
|
||||
<p className="text-[11px] text-zinc-500 text-center pt-2 border-t border-border">
|
||||
<p className="text-[11px] text-faint text-center pt-2 border-t border-border">
|
||||
Öffentliche Registrierung ist deaktiviert. Zugang nur mit Konto oder{' '}
|
||||
<button
|
||||
type="button"
|
||||
@@ -452,7 +455,7 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
<form onSubmit={handleRegisterOrInvite} className="space-y-3.5">
|
||||
{authTab === 'invite' && (
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1">Einladungscode *</label>
|
||||
<label className="block text-muted mb-1">Einladungscode *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={inviteCode}
|
||||
@@ -460,37 +463,37 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
placeholder="MS-XXXXXXXX"
|
||||
required
|
||||
autoFocus
|
||||
className="w-full h-8 px-2.5 input-field text-white font-mono uppercase"
|
||||
className="w-full h-8 px-2.5 input-field font-mono uppercase"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1">Anzeigename *</label>
|
||||
<label className="block text-muted mb-1">Anzeigename *</label>
|
||||
<input
|
||||
type="text"
|
||||
value={displayName}
|
||||
onChange={(e) => setDisplayName(e.target.value)}
|
||||
placeholder="Dein Name"
|
||||
required
|
||||
className="w-full h-8 px-2.5 input-field text-white"
|
||||
className="w-full h-8 px-2.5 input-field"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1">E-Mail-Adresse *</label>
|
||||
<label className="block text-muted mb-1">E-Mail-Adresse *</label>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="name@beispiel.de"
|
||||
required
|
||||
className="w-full h-8 px-2.5 input-field text-white"
|
||||
className="w-full h-8 px-2.5 input-field"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-zinc-400 mb-1">Passwort *</label>
|
||||
<label className="block text-muted mb-1">Passwort *</label>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
@@ -498,7 +501,7 @@ export const SetupAndAuthView: React.FC<SetupAndAuthViewProps> = ({
|
||||
placeholder="Mindestens 4 Zeichen"
|
||||
minLength={4}
|
||||
required
|
||||
className="w-full h-8 px-2.5 input-field text-white"
|
||||
className="w-full h-8 px-2.5 input-field"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in new issue
Block a user