572 lines
26 KiB
TypeScript
572 lines
26 KiB
TypeScript
import { View, Text, ScrollView, TouchableOpacity, StyleSheet, TextInput, ActivityIndicator, Alert } from 'react-native'
|
||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||
import { Ionicons } from '@expo/vector-icons'
|
||
import { useState } from 'react'
|
||
import { useRouter } from 'expo-router'
|
||
import { useAuth } from '@/hooks/useAuth'
|
||
import { trpc } from '@/lib/trpc'
|
||
import { authClient } from '@/lib/auth-client'
|
||
|
||
const AVATAR_PALETTES = [
|
||
{ bg: '#003B7E', text: '#FFFFFF' },
|
||
{ bg: '#1D4ED8', text: '#FFFFFF' },
|
||
{ bg: '#059669', text: '#FFFFFF' },
|
||
{ bg: '#4338CA', text: '#FFFFFF' },
|
||
{ bg: '#B45309', text: '#FFFFFF' },
|
||
{ bg: '#0F766E', text: '#FFFFFF' },
|
||
]
|
||
|
||
function getInitials(name: string) {
|
||
return name.split(' ').slice(0, 2).map((w) => w[0]?.toUpperCase() ?? '').join('')
|
||
}
|
||
function getPalette(name: string) {
|
||
if (!name) return AVATAR_PALETTES[0]
|
||
return AVATAR_PALETTES[name.charCodeAt(0) % AVATAR_PALETTES.length]
|
||
}
|
||
|
||
function InfoRow({ label, value }: { label: string; value?: string | null }) {
|
||
if (!value) return null
|
||
return (
|
||
<View style={styles.infoRow}>
|
||
<Text style={styles.infoLabel}>{label}</Text>
|
||
<Text style={styles.infoValue}>{value}</Text>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
export default function ProfilScreen() {
|
||
const { signOut } = useAuth()
|
||
const router = useRouter()
|
||
const utils = trpc.useUtils()
|
||
const { data: me } = trpc.members.me.useQuery()
|
||
const { data: unread } = trpc.messages.unreadCount.useQuery(undefined, { refetchInterval: 15_000 })
|
||
const [showPersonal, setShowPersonal] = useState(false)
|
||
const [showBetrieb, setShowBetrieb] = useState(false)
|
||
const [showSicherheit, setShowSicherheit] = useState(false)
|
||
const [showMitteilungen, setShowMitteilungen] = useState(false)
|
||
|
||
const [isEditing, setIsEditing] = useState(false)
|
||
const [isEditingBetrieb, setIsEditingBetrieb] = useState(false)
|
||
const [isChangingPassword, setIsChangingPassword] = useState(false)
|
||
const [passwordForm, setPasswordForm] = useState({ current: '', next: '', confirm: '' })
|
||
const [passwordLoading, setPasswordLoading] = useState(false)
|
||
const [passwordError, setPasswordError] = useState('')
|
||
const [editForm, setEditForm] = useState({ name: '', email: '', telefon: '', ort: '' })
|
||
const [editBetriebForm, setEditBetriebForm] = useState({ betrieb: '', sparte: '', istAusbildungsbetrieb: false })
|
||
|
||
const { mutate: updateMe, isPending: isUpdating } = trpc.members.updateMe.useMutation({
|
||
onSuccess: () => {
|
||
utils.members.me.invalidate()
|
||
setIsEditing(false)
|
||
Alert.alert('Erfolg', 'Profil wurde aktualisiert.')
|
||
},
|
||
onError: () => {
|
||
Alert.alert('Fehler', 'Profil konnte nicht aktualisiert werden.')
|
||
},
|
||
})
|
||
|
||
const handleEditPersonal = () => {
|
||
if (!me) return
|
||
setEditForm({
|
||
name: me.name || '',
|
||
email: me.email || '',
|
||
telefon: me.telefon || '',
|
||
ort: me.ort || '',
|
||
})
|
||
setIsEditing(true)
|
||
setShowPersonal(true)
|
||
}
|
||
|
||
const handleEditBetrieb = () => {
|
||
if (!me) return
|
||
setEditBetriebForm({
|
||
betrieb: me.betrieb || '',
|
||
sparte: me.sparte || '',
|
||
istAusbildungsbetrieb: me.istAusbildungsbetrieb ?? false,
|
||
})
|
||
setIsEditingBetrieb(true)
|
||
setShowBetrieb(true)
|
||
}
|
||
|
||
const handleSavePersonal = () => {
|
||
updateMe({
|
||
name: editForm.name,
|
||
email: editForm.email,
|
||
telefon: editForm.telefon,
|
||
ort: editForm.ort,
|
||
})
|
||
}
|
||
|
||
const handleSaveBetrieb = () => {
|
||
updateMe({
|
||
betrieb: editBetriebForm.betrieb,
|
||
sparte: editBetriebForm.sparte,
|
||
istAusbildungsbetrieb: editBetriebForm.istAusbildungsbetrieb,
|
||
})
|
||
setIsEditingBetrieb(false)
|
||
}
|
||
|
||
const name = me?.name ?? ''
|
||
const initials = getInitials(name)
|
||
const palette = getPalette(name)
|
||
const role = me?.org?.name ? 'Mitglied' : 'Mitglied'
|
||
const unreadCount = unread?.count ?? 0
|
||
|
||
return (
|
||
<SafeAreaView style={styles.safeArea} edges={['top']}>
|
||
<ScrollView contentContainerStyle={styles.content} showsVerticalScrollIndicator={false}>
|
||
|
||
{/* HERO */}
|
||
<View style={styles.hero}>
|
||
<View style={styles.avatarWrap}>
|
||
{/* Avatar — View+Text statt nur Text, damit Initials wirklich mittig */}
|
||
<View style={[styles.avatarCircle, { backgroundColor: palette.bg }]}>
|
||
<Text style={[styles.avatarText, { color: palette.text }]}>{initials}</Text>
|
||
</View>
|
||
<View style={styles.settingsBtn}>
|
||
<Ionicons name="settings-outline" size={15} color="#64748B" />
|
||
</View>
|
||
</View>
|
||
<Text style={styles.name}>{name || '–'}</Text>
|
||
<Text style={styles.role}>{me?.org?.name ?? 'Innung'}</Text>
|
||
<View style={styles.badgesRow}>
|
||
{me?.status === 'aktiv' && (
|
||
<View style={styles.statusBadge}>
|
||
<Text style={styles.statusBadgeText}>Aktiv</Text>
|
||
</View>
|
||
)}
|
||
<View style={[styles.statusBadge, styles.verifyBadge]}>
|
||
<Text style={[styles.statusBadgeText, styles.verifyBadgeText]}>Verifiziert</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
|
||
<Text style={styles.sectionTitle}>Mein Account</Text>
|
||
<View style={styles.menuCard}>
|
||
|
||
{/* PERSÖNLICHE DATEN */}
|
||
<TouchableOpacity
|
||
style={styles.menuRow}
|
||
activeOpacity={0.82}
|
||
onPress={() => {
|
||
if (showPersonal) {
|
||
setShowPersonal(false)
|
||
setIsEditing(false)
|
||
} else {
|
||
setShowPersonal(true)
|
||
}
|
||
}}
|
||
>
|
||
<View style={styles.menuLeft}>
|
||
<View style={styles.menuIcon}>
|
||
<Ionicons name="person-outline" size={18} color="#475569" />
|
||
</View>
|
||
<Text style={styles.menuLabel}>Persönliche Daten</Text>
|
||
</View>
|
||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||
{showPersonal && !isEditing && (
|
||
<TouchableOpacity onPress={handleEditPersonal} style={{ marginRight: 12, padding: 4 }}>
|
||
<Ionicons name="create-outline" size={18} color="#2563EB" />
|
||
</TouchableOpacity>
|
||
)}
|
||
<Ionicons name={showPersonal ? 'chevron-up' : 'chevron-down'} size={16} color="#94A3B8" />
|
||
</View>
|
||
</TouchableOpacity>
|
||
|
||
{showPersonal && (
|
||
<View style={styles.expandedCard}>
|
||
{isEditing ? (
|
||
<>
|
||
<View style={styles.infoRowEdit}>
|
||
<Text style={styles.infoLabel}>Name</Text>
|
||
<TextInput style={styles.input} value={editForm.name} onChangeText={t => setEditForm(f => ({ ...f, name: t }))} />
|
||
</View>
|
||
<View style={styles.infoRowEdit}>
|
||
<Text style={styles.infoLabel}>E-Mail</Text>
|
||
<TextInput style={styles.input} value={editForm.email} keyboardType="email-address" autoCapitalize="none" onChangeText={t => setEditForm(f => ({ ...f, email: t }))} />
|
||
</View>
|
||
<View style={styles.infoRowEdit}>
|
||
<Text style={styles.infoLabel}>Telefon</Text>
|
||
<TextInput style={styles.input} value={editForm.telefon} keyboardType="phone-pad" onChangeText={t => setEditForm(f => ({ ...f, telefon: t }))} />
|
||
</View>
|
||
<View style={styles.infoRowEdit}>
|
||
<Text style={styles.infoLabel}>Ort</Text>
|
||
<TextInput style={styles.input} value={editForm.ort} onChangeText={t => setEditForm(f => ({ ...f, ort: t }))} />
|
||
</View>
|
||
<View style={styles.editActionRow}>
|
||
<TouchableOpacity style={styles.cancelBtn} onPress={() => setIsEditing(false)}>
|
||
<Text style={styles.cancelBtnText}>Abbrechen</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity style={styles.saveBtn} onPress={handleSavePersonal} disabled={isUpdating}>
|
||
{isUpdating ? <ActivityIndicator color="#fff" size="small" /> : <Text style={styles.saveBtnText}>Speichern</Text>}
|
||
</TouchableOpacity>
|
||
</View>
|
||
</>
|
||
) : (
|
||
<>
|
||
<InfoRow label="Name" value={me?.name} />
|
||
<InfoRow label="E-Mail" value={me?.email} />
|
||
<InfoRow label="Telefon" value={me?.telefon} />
|
||
<InfoRow label="Ort" value={me?.ort} />
|
||
<InfoRow label="Mitglied seit" value={me?.seit ? String(me.seit) : null} />
|
||
{!me?.name && (
|
||
<Text style={styles.emptyHint}>Keine Daten vorhanden.</Text>
|
||
)}
|
||
</>
|
||
)}
|
||
</View>
|
||
)}
|
||
|
||
<View style={styles.menuRowBorderOnly} />
|
||
|
||
{/* BETRIEBSDATEN */}
|
||
<TouchableOpacity
|
||
style={styles.menuRow}
|
||
activeOpacity={0.82}
|
||
onPress={() => {
|
||
if (showBetrieb) {
|
||
setShowBetrieb(false)
|
||
setIsEditingBetrieb(false)
|
||
} else {
|
||
setShowBetrieb(true)
|
||
}
|
||
}}
|
||
>
|
||
<View style={styles.menuLeft}>
|
||
<View style={styles.menuIcon}>
|
||
<Ionicons name="business-outline" size={18} color="#475569" />
|
||
</View>
|
||
<Text style={styles.menuLabel}>Betriebsdaten</Text>
|
||
</View>
|
||
<View style={styles.menuRight}>
|
||
{showBetrieb && !isEditingBetrieb && (
|
||
<TouchableOpacity onPress={handleEditBetrieb} style={{ marginRight: 12, padding: 4 }}>
|
||
<Ionicons name="create-outline" size={18} color="#2563EB" />
|
||
</TouchableOpacity>
|
||
)}
|
||
<Ionicons name={showBetrieb ? 'chevron-up' : 'chevron-down'} size={16} color="#94A3B8" />
|
||
</View>
|
||
</TouchableOpacity>
|
||
|
||
{showBetrieb && (
|
||
<View style={styles.expandedCard}>
|
||
{isEditingBetrieb ? (
|
||
<>
|
||
<View style={styles.infoRowEdit}>
|
||
<Text style={styles.infoLabel}>Betrieb</Text>
|
||
<TextInput style={styles.input} value={editBetriebForm.betrieb} onChangeText={t => setEditBetriebForm(f => ({ ...f, betrieb: t }))} />
|
||
</View>
|
||
<View style={styles.infoRowEdit}>
|
||
<Text style={styles.infoLabel}>Sparte</Text>
|
||
<TextInput style={styles.input} value={editBetriebForm.sparte} onChangeText={t => setEditBetriebForm(f => ({ ...f, sparte: t }))} />
|
||
</View>
|
||
<TouchableOpacity
|
||
style={[styles.infoRowEdit, { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 10 }]}
|
||
onPress={() => setEditBetriebForm(f => ({ ...f, istAusbildungsbetrieb: !f.istAusbildungsbetrieb }))}
|
||
>
|
||
<Text style={styles.infoLabel}>Ausbildungsbetrieb</Text>
|
||
<Ionicons name={editBetriebForm.istAusbildungsbetrieb ? 'checkbox' : 'square-outline'} size={20} color="#2563EB" />
|
||
</TouchableOpacity>
|
||
|
||
<View style={styles.editActionRow}>
|
||
<TouchableOpacity style={styles.cancelBtn} onPress={() => setIsEditingBetrieb(false)}>
|
||
<Text style={styles.cancelBtnText}>Abbrechen</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity style={styles.saveBtn} onPress={handleSaveBetrieb} disabled={isUpdating}>
|
||
{isUpdating ? <ActivityIndicator color="#fff" size="small" /> : <Text style={styles.saveBtnText}>Speichern</Text>}
|
||
</TouchableOpacity>
|
||
</View>
|
||
</>
|
||
) : (
|
||
<>
|
||
<InfoRow label="Betrieb" value={me?.betrieb} />
|
||
<InfoRow label="Sparte" value={me?.sparte} />
|
||
<InfoRow label="Ausbildung" value={me?.istAusbildungsbetrieb ? 'Ja' : 'Nein'} />
|
||
{!me?.betrieb && !me?.sparte && (
|
||
<Text style={styles.emptyHint}>Keine Betriebsdaten vorhanden.</Text>
|
||
)}
|
||
</>
|
||
)}
|
||
</View>
|
||
)}
|
||
|
||
{!showBetrieb && (me?.betrieb || me?.sparte) && (
|
||
<View style={styles.subInfo}>
|
||
{me?.betrieb && <Text style={styles.subInfoText}>{me.betrieb}</Text>}
|
||
{me?.sparte && <Text style={styles.subInfoMuted}>{me.sparte}</Text>}
|
||
</View>
|
||
)}
|
||
|
||
<View style={styles.menuRowBorderOnly} />
|
||
|
||
{/* MITTEILUNGEN */}
|
||
<TouchableOpacity
|
||
style={styles.menuRow}
|
||
activeOpacity={0.82}
|
||
onPress={() => setShowMitteilungen((v) => !v)}
|
||
>
|
||
<View style={styles.menuLeft}>
|
||
<View style={styles.menuIcon}>
|
||
<Ionicons name="notifications-outline" size={18} color="#475569" />
|
||
</View>
|
||
<Text style={styles.menuLabel}>Mitteilungen</Text>
|
||
</View>
|
||
<View style={styles.menuRight}>
|
||
{unreadCount > 0 && (
|
||
<View style={styles.rowBadgeAlert}>
|
||
<Text style={[styles.rowBadgeText, { color: '#fff' }]}>{unreadCount}</Text>
|
||
</View>
|
||
)}
|
||
<Ionicons name={showMitteilungen ? 'chevron-up' : 'chevron-down'} size={16} color="#94A3B8" />
|
||
</View>
|
||
</TouchableOpacity>
|
||
|
||
{showMitteilungen && (
|
||
<View style={styles.expandedCard}>
|
||
{unreadCount > 0 ? (
|
||
<TouchableOpacity
|
||
style={styles.mitteilungenAction}
|
||
onPress={() => router.push('/(app)/chat')}
|
||
>
|
||
<Ionicons name="chatbubbles-outline" size={20} color="#2563EB" />
|
||
<View style={{ flex: 1 }}>
|
||
<Text style={styles.mitteilungenTitle}>
|
||
{unreadCount} ungelesene Nachricht{unreadCount > 1 ? 'en' : ''}
|
||
</Text>
|
||
<Text style={styles.mitteilungenSub}>Direkt zu den Nachrichten</Text>
|
||
</View>
|
||
<Ionicons name="chevron-forward" size={16} color="#94A3B8" />
|
||
</TouchableOpacity>
|
||
) : (
|
||
<Text style={styles.emptyHint}>Keine neuen Mitteilungen.</Text>
|
||
)}
|
||
</View>
|
||
)}
|
||
|
||
<View style={styles.menuRowBorderOnly} />
|
||
|
||
{/* SICHERHEIT */}
|
||
<TouchableOpacity
|
||
style={styles.menuRow}
|
||
activeOpacity={0.82}
|
||
onPress={() => setShowSicherheit(!showSicherheit)}
|
||
>
|
||
<View style={styles.menuLeft}>
|
||
<View style={styles.menuIcon}>
|
||
<Ionicons name="shield-checkmark-outline" size={18} color="#475569" />
|
||
</View>
|
||
<Text style={styles.menuLabel}>Sicherheit & Login</Text>
|
||
</View>
|
||
<Ionicons name={showSicherheit ? 'chevron-up' : 'chevron-down'} size={16} color="#94A3B8" />
|
||
</TouchableOpacity>
|
||
|
||
{showSicherheit && (
|
||
<View style={styles.expandedCard}>
|
||
<InfoRow label="Login-E-Mail" value={me?.email} />
|
||
<InfoRow label="Account Status" value={me?.status === 'aktiv' ? 'Aktiviert' : 'Inaktiv'} />
|
||
|
||
{!isChangingPassword ? (
|
||
<TouchableOpacity
|
||
style={[styles.saveBtn, { marginTop: 10, width: '100%' }]}
|
||
onPress={() => {
|
||
setPasswordForm({ current: '', next: '', confirm: '' })
|
||
setPasswordError('')
|
||
setIsChangingPassword(true)
|
||
}}
|
||
>
|
||
<Text style={styles.saveBtnText}>Passwort ändern</Text>
|
||
</TouchableOpacity>
|
||
) : (
|
||
<View style={{ marginTop: 10, gap: 8 }}>
|
||
<View style={styles.infoRowEdit}>
|
||
<Text style={styles.infoLabel}>Aktuelles Passwort</Text>
|
||
<TextInput
|
||
style={styles.input}
|
||
value={passwordForm.current}
|
||
onChangeText={t => setPasswordForm(f => ({ ...f, current: t }))}
|
||
secureTextEntry
|
||
placeholder="••••••••"
|
||
placeholderTextColor="#CBD5E1"
|
||
autoCapitalize="none"
|
||
/>
|
||
</View>
|
||
<View style={styles.infoRowEdit}>
|
||
<Text style={styles.infoLabel}>Neues Passwort</Text>
|
||
<TextInput
|
||
style={styles.input}
|
||
value={passwordForm.next}
|
||
onChangeText={t => setPasswordForm(f => ({ ...f, next: t }))}
|
||
secureTextEntry
|
||
placeholder="Mindestens 8 Zeichen"
|
||
placeholderTextColor="#CBD5E1"
|
||
autoCapitalize="none"
|
||
/>
|
||
</View>
|
||
<View style={styles.infoRowEdit}>
|
||
<Text style={styles.infoLabel}>Wiederholen</Text>
|
||
<TextInput
|
||
style={styles.input}
|
||
value={passwordForm.confirm}
|
||
onChangeText={t => setPasswordForm(f => ({ ...f, confirm: t }))}
|
||
secureTextEntry
|
||
placeholder="Neues Passwort wiederholen"
|
||
placeholderTextColor="#CBD5E1"
|
||
autoCapitalize="none"
|
||
/>
|
||
</View>
|
||
{!!passwordError && (
|
||
<Text style={styles.passwordError}>{passwordError}</Text>
|
||
)}
|
||
<View style={styles.editActionRow}>
|
||
<TouchableOpacity
|
||
style={styles.cancelBtn}
|
||
onPress={() => setIsChangingPassword(false)}
|
||
disabled={passwordLoading}
|
||
>
|
||
<Text style={styles.cancelBtnText}>Abbrechen</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity
|
||
style={styles.saveBtn}
|
||
disabled={passwordLoading}
|
||
onPress={async () => {
|
||
setPasswordError('')
|
||
if (!passwordForm.current) {
|
||
setPasswordError('Bitte aktuelles Passwort eingeben.')
|
||
return
|
||
}
|
||
if (passwordForm.next.length < 8) {
|
||
setPasswordError('Das neue Passwort muss mindestens 8 Zeichen haben.')
|
||
return
|
||
}
|
||
if (passwordForm.next !== passwordForm.confirm) {
|
||
setPasswordError('Die Passwörter stimmen nicht überein.')
|
||
return
|
||
}
|
||
setPasswordLoading(true)
|
||
const result = await authClient.changePassword({
|
||
currentPassword: passwordForm.current,
|
||
newPassword: passwordForm.next,
|
||
})
|
||
setPasswordLoading(false)
|
||
if (result.error) {
|
||
setPasswordError(result.error.message ?? 'Passwort konnte nicht geändert werden.')
|
||
return
|
||
}
|
||
setIsChangingPassword(false)
|
||
Alert.alert('Erfolg', 'Passwort wurde erfolgreich geändert.')
|
||
}}
|
||
>
|
||
{passwordLoading
|
||
? <ActivityIndicator color="#fff" size="small" />
|
||
: <Text style={styles.saveBtnText}>Speichern</Text>
|
||
}
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
)}
|
||
</View>
|
||
)}
|
||
</View>
|
||
|
||
{/* LOGOUT */}
|
||
<TouchableOpacity style={styles.logoutBtn} activeOpacity={0.84} onPress={() => void signOut()}>
|
||
<Ionicons name="log-out-outline" size={20} color="#B91C1C" />
|
||
<Text style={styles.logoutText}>Abmelden</Text>
|
||
</TouchableOpacity>
|
||
|
||
<Text style={styles.footer}>InnungsApp Version 2.4.0</Text>
|
||
</ScrollView>
|
||
</SafeAreaView>
|
||
)
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
safeArea: { flex: 1, backgroundColor: '#F8FAFC' },
|
||
content: { paddingHorizontal: 18, paddingBottom: 120, gap: 14 },
|
||
|
||
// Hero
|
||
hero: {
|
||
backgroundColor: '#FFFFFF', alignItems: 'center',
|
||
paddingTop: 24, paddingBottom: 18, borderRadius: 22,
|
||
borderWidth: 1, borderColor: '#E2E8F0', marginTop: 8,
|
||
},
|
||
avatarWrap: { position: 'relative' },
|
||
avatarCircle: {
|
||
width: 94, height: 94, borderRadius: 47,
|
||
alignItems: 'center', justifyContent: 'center',
|
||
borderWidth: 4, borderColor: '#FFFFFF',
|
||
shadowColor: '#000', shadowOffset: { width: 0, height: 2 },
|
||
shadowOpacity: 0.12, shadowRadius: 8, elevation: 3,
|
||
},
|
||
avatarText: { fontSize: 34, fontWeight: '800', lineHeight: 40, includeFontPadding: false },
|
||
settingsBtn: {
|
||
position: 'absolute', right: 0, bottom: 2,
|
||
width: 30, height: 30, borderRadius: 15,
|
||
backgroundColor: '#FFFFFF', borderWidth: 1, borderColor: '#E2E8F0',
|
||
alignItems: 'center', justifyContent: 'center',
|
||
},
|
||
name: { marginTop: 14, fontSize: 24, fontWeight: '800', color: '#0F172A' },
|
||
role: { marginTop: 2, fontSize: 12, fontWeight: '700', letterSpacing: 0.5, color: '#64748B', textTransform: 'uppercase' },
|
||
badgesRow: { marginTop: 10, flexDirection: 'row', gap: 8 },
|
||
statusBadge: { backgroundColor: '#DCFCE7', borderRadius: 999, paddingHorizontal: 10, paddingVertical: 4 },
|
||
statusBadgeText: { color: '#166534', fontSize: 11, fontWeight: '700' },
|
||
verifyBadge: { backgroundColor: '#DBEAFE' },
|
||
verifyBadgeText: { color: '#1D4ED8' },
|
||
|
||
// Section
|
||
sectionTitle: {
|
||
marginTop: 2, paddingLeft: 2, fontSize: 11,
|
||
textTransform: 'uppercase', letterSpacing: 1.1,
|
||
color: '#94A3B8', fontWeight: '800',
|
||
},
|
||
|
||
// Menu card
|
||
menuCard: { backgroundColor: '#FFFFFF', borderRadius: 18, borderWidth: 1, borderColor: '#E2E8F0', overflow: 'hidden' },
|
||
menuRow: { paddingHorizontal: 14, paddingVertical: 13, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
|
||
menuRowBorderOnly: { height: 1, backgroundColor: '#F1F5F9', marginHorizontal: 14 },
|
||
menuLeft: { flexDirection: 'row', alignItems: 'center', gap: 12 },
|
||
menuIcon: { width: 36, height: 36, borderRadius: 11, backgroundColor: '#F1F5F9', alignItems: 'center', justifyContent: 'center' },
|
||
menuLabel: { fontSize: 14, fontWeight: '700', color: '#1E293B' },
|
||
menuRight: { flexDirection: 'row', alignItems: 'center', gap: 7 },
|
||
rowBadgeActive: { backgroundColor: '#DCFCE7', paddingHorizontal: 8, paddingVertical: 3, borderRadius: 999 },
|
||
rowBadgeAlert: { backgroundColor: '#EF4444', paddingHorizontal: 8, paddingVertical: 3, borderRadius: 999 },
|
||
rowBadgeText: { fontSize: 10, fontWeight: '700' },
|
||
|
||
// Expanded card
|
||
expandedCard: { backgroundColor: '#F8FAFC', marginHorizontal: 14, marginBottom: 12, borderRadius: 12, padding: 12, gap: 6 },
|
||
infoRow: { flexDirection: 'row', justifyContent: 'space-between', paddingVertical: 5, borderBottomWidth: 1, borderBottomColor: '#E2E8F0' },
|
||
infoLabel: { fontSize: 12, color: '#64748B', fontWeight: '600', width: 100 },
|
||
infoValue: { fontSize: 13, color: '#0F172A', fontWeight: '500', flex: 1, textAlign: 'right' },
|
||
emptyHint: { fontSize: 13, color: '#94A3B8', textAlign: 'center', paddingVertical: 8 },
|
||
passwordError: { fontSize: 12, color: '#B91C1C', backgroundColor: '#FEF2F2', borderRadius: 6, paddingHorizontal: 10, paddingVertical: 6 },
|
||
|
||
infoRowEdit: { paddingTop: 6, paddingBottom: 2, borderBottomWidth: 1, borderBottomColor: '#E2E8F0', marginTop: 2 },
|
||
input: { fontSize: 13, color: '#0F172A', fontWeight: '500', paddingVertical: 4, paddingHorizontal: 0, marginTop: 2 },
|
||
editActionRow: { flexDirection: 'row', justifyContent: 'flex-end', gap: 10, marginTop: 14 },
|
||
cancelBtn: { paddingHorizontal: 16, paddingVertical: 8, borderRadius: 8, backgroundColor: '#E2E8F0' },
|
||
cancelBtnText: { color: '#475569', fontWeight: '600', fontSize: 13 },
|
||
saveBtn: { paddingHorizontal: 16, paddingVertical: 8, borderRadius: 8, backgroundColor: '#2563EB', minWidth: 90, alignItems: 'center' },
|
||
saveBtnText: { color: '#FFFFFF', fontWeight: '600', fontSize: 13 },
|
||
|
||
// Sub info (Betrieb)
|
||
subInfo: { paddingHorizontal: 14, paddingBottom: 12, gap: 4 },
|
||
subInfoText: { fontSize: 13, fontWeight: '600', color: '#334155' },
|
||
subInfoMuted: { fontSize: 12, color: '#64748B' },
|
||
ausbildungPill: { flexDirection: 'row', alignItems: 'center', gap: 4, marginTop: 4 },
|
||
ausbildungText: { fontSize: 11, color: '#15803D', fontWeight: '600' },
|
||
|
||
// Mitteilungen action
|
||
mitteilungenAction: { flexDirection: 'row', alignItems: 'center', gap: 12, padding: 4 },
|
||
mitteilungenTitle: { fontSize: 13, fontWeight: '700', color: '#1E293B' },
|
||
mitteilungenSub: { fontSize: 11, color: '#64748B', marginTop: 1 },
|
||
|
||
// Logout
|
||
logoutBtn: {
|
||
marginTop: 4, backgroundColor: '#FEF2F2', borderRadius: 14,
|
||
borderWidth: 1, borderColor: '#FECACA', paddingVertical: 14,
|
||
flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8,
|
||
},
|
||
logoutText: { color: '#B91C1C', fontSize: 14, fontWeight: '800', textTransform: 'uppercase', letterSpacing: 0.8 },
|
||
footer: { textAlign: 'center', marginTop: 4, fontSize: 10, fontWeight: '700', letterSpacing: 1, color: '#94A3B8', textTransform: 'uppercase' },
|
||
})
|