Fix auth translations and admin PWA UI
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-16 12:14:53 +01:00
parent 292c8f0b26
commit 918bff08aa
44 changed files with 2504 additions and 677 deletions

View File

@@ -5,12 +5,12 @@ import { ArrowLeft, RefreshCcw, Plus, ChevronDown } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Pressable } from '@tamagui/react-native-web-lite';
import { Input, TextArea } from 'tamagui';
import { Accordion } from '@tamagui/accordion';
import { HexColorPicker } from 'react-colorful';
import { Portal } from '@tamagui/portal';
import { MobileShell, HeaderActionButton } from './components/MobileShell';
import { MobileCard, CTAButton, PillBadge } from './components/Primitives';
import { MobileInput, MobileSelect, MobileTextArea } from './components/FormControls';
import {
TenantEvent,
EventQrInvite,
@@ -753,7 +753,7 @@ function BackgroundStep({
{presets.map((preset) => {
const isSelected = selectedPreset === preset.id;
return (
<Pressable key={preset.id} onPress={() => onSelectPreset(preset.id)} style={{ width: '48%' }}>
<Pressable key={preset.id ?? preset.labelKey} onPress={() => onSelectPreset(preset.id)} style={{ width: '48%' }}>
<YStack
aspectRatio={210 / 297}
maxHeight={220}
@@ -795,7 +795,7 @@ function BackgroundStep({
{t('events.qr.gradients', 'Gradienten')}
</Text>
<XStack flexWrap="wrap" gap="$2">
{gradientPresets.map((gradient, idx) => {
{gradientPresets.map((gradient) => {
const isSelected =
selectedGradient &&
selectedGradient.angle === gradient.angle &&
@@ -803,9 +803,10 @@ function BackgroundStep({
const style = {
backgroundImage: `linear-gradient(${gradient.angle}deg, ${gradient.stops.join(',')})`,
} as React.CSSProperties;
const key = `${gradient.labelKey}-${gradient.angle}`;
return (
<Pressable
key={idx}
key={key}
onPress={() => onSelectGradient(gradient)}
style={{ width: '30%' }}
aria-label={t(gradient.labelKey, gradient.label)}
@@ -829,10 +830,11 @@ function BackgroundStep({
{t('events.qr.colors', 'Vollfarbe')}
</Text>
<XStack flexWrap="wrap" gap="$2">
{solidPresets.map((color) => {
{solidPresets.map((color, idx) => {
const isSelected = selectedSolid === color;
const key = `${color}-${idx}`;
return (
<Pressable key={color} onPress={() => onSelectSolid(color)} style={{ width: '20%' }}>
<Pressable key={key} onPress={() => onSelectSolid(color)} style={{ width: '20%' }}>
<YStack
height={50}
borderRadius={12}
@@ -912,24 +914,20 @@ function TextStep({
<Text fontSize="$sm" fontWeight="700" color={textStrong}>
{t('events.qr.textFields', 'Texte')}
</Text>
<Input
<MobileInput
placeholder={t('events.qr.headline', 'Headline')}
value={textFields.headline}
onChangeText={(val) => updateField('headline', val)}
size="$4"
onChange={(event) => updateField('headline', event.target.value)}
/>
<Input
<MobileInput
placeholder={t('events.qr.subtitle', 'Subtitle')}
value={textFields.subtitle}
onChangeText={(val) => updateField('subtitle', val)}
size="$4"
onChange={(event) => updateField('subtitle', event.target.value)}
/>
<TextArea
<MobileTextArea
placeholder={t('events.qr.bottomNote', 'Unterer Hinweistext')}
value={textFields.description}
onChangeText={(val) => updateField('description', val)}
size="$4"
numberOfLines={3}
onChange={(event) => updateField('description', event.target.value)}
/>
</YStack>
@@ -939,13 +937,12 @@ function TextStep({
</Text>
{textFields.instructions.map((item, idx) => (
<XStack key={idx} alignItems="center" space="$2">
<TextArea
<MobileTextArea
value={item}
onChangeText={(val) => updateInstruction(idx, val)}
onChange={(event) => updateInstruction(idx, event.target.value)}
placeholder={t('events.qr.instructionsPlaceholder', 'Schritt hinzufügen')}
numberOfLines={2}
{...({ flex: 1 } as any)}
size="$4"
style={{ flex: 1 }}
compact
/>
<Pressable onPress={() => removeInstruction(idx)} disabled={textFields.instructions.length === 1}>
<XStack
@@ -1204,36 +1201,13 @@ function LayoutControls({
}) {
const { t } = useTranslation('management');
const [openColorSlot, setOpenColorSlot] = React.useState<string | null>(null);
const { border, surface, surfaceMuted, text, textStrong, muted, overlay, shadow, danger } = useAdminTheme();
const { border, surface, surfaceMuted, textStrong, muted, overlay, shadow, danger } = useAdminTheme();
const fontOptions = React.useMemo(() => {
const preset = ['Archivo Black', 'Manrope', 'Inter', 'Roboto', 'Lora'];
const tenant = tenantFonts.map((font) => font.family);
return Array.from(new Set([...tenant, ...preset]));
}, [tenantFonts]);
const numberInputStyle: React.CSSProperties = {
width: 90,
padding: '10px 12px',
border: `1px solid ${border}`,
borderRadius: 12,
fontSize: 14,
fontFamily: DEFAULT_BODY_FONT,
background: surfaceMuted,
color: text,
appearance: 'textfield',
};
const selectStyle: React.CSSProperties = {
width: '100%',
padding: '10px 12px',
border: `1px solid ${border}`,
borderRadius: 12,
fontSize: 14,
fontFamily: DEFAULT_BODY_FONT,
background: surfaceMuted,
color: text,
};
const StepperInput = ({
value,
min,
@@ -1260,11 +1234,9 @@ function LayoutControls({
</Text>
</XStack>
</Pressable>
<input
<MobileInput
type="text"
inputMode="decimal"
min={min}
max={max}
value={formatValue(value)}
onChange={(event) => {
const next = Number(event.target.value);
@@ -1272,7 +1244,13 @@ function LayoutControls({
onChange(next);
}
}}
style={numberInputStyle}
compact
style={{
width: 90,
textAlign: 'center',
fontFamily: DEFAULT_BODY_FONT,
backgroundColor: surfaceMuted,
}}
/>
<Pressable onPress={inc}>
<XStack width={36} height={36} borderRadius={10} alignItems="center" justifyContent="center" borderWidth={1} borderColor={border} backgroundColor={surface}>
@@ -1395,10 +1373,9 @@ function LayoutControls({
<Text fontSize="$xs" color={muted}>
{t('events.qr.fontFamily', 'Font Family')}
</Text>
<select
<MobileSelect
value={override.fontFamily ?? slot.fontFamily ?? ''}
onChange={(event) => onFontFamilyChange(event.target.value)}
style={selectStyle}
>
<option value="">{t('events.qr.defaultFont', 'Standard')}</option>
{fontOptions.map((family) => (
@@ -1406,7 +1383,7 @@ function LayoutControls({
{family}
</option>
))}
</select>
</MobileSelect>
</YStack>
<YStack flex={1} space="$1">
<Text fontSize="$xs" color={muted}>
@@ -1424,12 +1401,12 @@ function LayoutControls({
backgroundColor={override.color ?? slot.color ?? textStrong}
/>
</Pressable>
<input
type="text"
<MobileInput
value={override.color ?? slot.color ?? ''}
placeholder={ADMIN_COLORS.text}
onChange={(event) => onUpdateSlot(slotKey, { color: event.target.value })}
style={{ ...numberInputStyle, width: 110 }}
compact
style={{ width: 110, backgroundColor: surfaceMuted }}
/>
</XStack>
{openColorSlot === slotKey ? (
@@ -1496,15 +1473,14 @@ function LayoutControls({
<Text fontSize="$xs" color={muted}>
{t('events.qr.align', 'Align')}
</Text>
<select
<MobileSelect
value={override.align ?? slot.align ?? 'left'}
onChange={(event) => onUpdateSlot(slotKey, { align: event.target.value as SlotDefinition['align'] })}
style={selectStyle}
>
<option value="left">{t('common.left', 'Links')}</option>
<option value="center">{t('common.center', 'Zentriert')}</option>
<option value="right">{t('common.right', 'Rechts')}</option>
</select>
</MobileSelect>
</YStack>
<YStack flex={1} space="$1">
<Text fontSize="$xs" color={muted}>