events werden nun erfolgreich gespeichert, branding wird nun erfolgreich gespeichert, emotionen können nun angelegt werden. Task Ansicht im Event admin verbessert, Buttons in FAB umgewandelt und vereinheitlicht. Teilen-Link Guest PWA schicker gemacht, SynGoogleFonts ausgebaut (mit Einzel-Family-Download).

This commit is contained in:
Codex Agent
2025-11-27 16:08:08 +01:00
parent bfa15cc48e
commit 96f8c5d63c
39 changed files with 1970 additions and 640 deletions

View File

@@ -1,7 +1,9 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
AlignCenter,
AlignLeft,
AlignRight,
BadgeCheck,
ChevronDown,
Download,
@@ -611,6 +613,16 @@ export function InviteLayoutCustomizerPanel({
[availableFonts, updateElement]
);
const handleFontOptionPreview = React.useCallback(
(family: string) => {
const font = availableFonts.find((entry) => entry.family === family);
if (font) {
void ensureFontLoaded(font);
}
},
[availableFonts]
);
React.useEffect(() => {
if (!invite) {
setAvailableLayouts([]);
@@ -1315,14 +1327,17 @@ export function InviteLayoutCustomizerPanel({
value={element.align ?? 'left'}
onValueChange={(value) => value && updateElementAlign(element.id, value as 'left' | 'center' | 'right')}
>
<ToggleGroupItem value="left" className="px-3">
{t('invites.customizer.elements.alignLeft', 'Links')}
<ToggleGroupItem value="left" className="px-3" title={t('invites.customizer.elements.alignLeft', 'Links')} aria-label={t('invites.customizer.elements.alignLeft', 'Links')}>
<AlignLeft className="h-4 w-4" />
<span className="sr-only">{t('invites.customizer.elements.alignLeft', 'Links')}</span>
</ToggleGroupItem>
<ToggleGroupItem value="center" className="px-3">
{t('invites.customizer.elements.alignCenter', 'Zentriert')}
<ToggleGroupItem value="center" className="px-3" title={t('invites.customizer.elements.alignCenter', 'Zentriert')} aria-label={t('invites.customizer.elements.alignCenter', 'Zentriert')}>
<AlignCenter className="h-4 w-4" />
<span className="sr-only">{t('invites.customizer.elements.alignCenter', 'Zentriert')}</span>
</ToggleGroupItem>
<ToggleGroupItem value="right" className="px-3">
{t('invites.customizer.elements.alignRight', 'Rechts')}
<ToggleGroupItem value="right" className="px-3" title={t('invites.customizer.elements.alignRight', 'Rechts')} aria-label={t('invites.customizer.elements.alignRight', 'Rechts')}>
<AlignRight className="h-4 w-4" />
<span className="sr-only">{t('invites.customizer.elements.alignRight', 'Rechts')}</span>
</ToggleGroupItem>
</ToggleGroup>
</div>
@@ -1352,7 +1367,21 @@ export function InviteLayoutCustomizerPanel({
<SelectContent>
<SelectItem value={DEFAULT_FONT_VALUE}>{t('invites.customizer.elements.fontPlaceholder', 'Standard')}</SelectItem>
{availableFonts.map((font) => (
<SelectItem key={font.family} value={font.family}>{font.family}</SelectItem>
<SelectItem
key={font.family}
value={font.family}
onMouseEnter={() => handleFontOptionPreview(font.family)}
onFocus={() => handleFontOptionPreview(font.family)}
>
<span className="flex items-center justify-between gap-3">
<span className="truncate" style={{ fontFamily: font.family }}>
{font.family}
</span>
<span className="text-xs text-muted-foreground" style={{ fontFamily: font.family }}>
AaBb
</span>
</span>
</SelectItem>
))}
</SelectContent>
</Select>
@@ -1360,6 +1389,7 @@ export function InviteLayoutCustomizerPanel({
value={element.fontFamily ?? ''}
onChange={(event) => handleElementFontChange(element.id, event.target.value)}
placeholder="z. B. Playfair Display"
style={element.fontFamily ? { fontFamily: element.fontFamily } : undefined}
/>
</div>
</div>
@@ -1503,7 +1533,7 @@ export function InviteLayoutCustomizerPanel({
const normalizedFormat = format.toLowerCase();
const eventDateSegment = normalizeEventDateSegment(eventDate);
const filename = buildDownloadFilename(
['Einladungslayout', eventName, activeLayout?.name ?? null, eventDateSegment],
['QR-Layout', eventName, activeLayout?.name ?? null, eventDateSegment],
normalizedFormat,
'einladungslayout',
);