login-seiten neu designt, homepage neu designt. "so funktioniert's" ergänzt und Demo-Seite hinzugefügt. Paketansicht in mobile verbessert.
This commit is contained in:
@@ -54,6 +54,7 @@ import {
|
||||
triggerDownloadFromBlob,
|
||||
triggerDownloadFromDataUrl,
|
||||
} from './invite-layout/export-utils';
|
||||
import { buildDownloadFilename, normalizeEventDateSegment } from './invite-layout/fileNames';
|
||||
|
||||
export type { QrLayoutCustomization } from './invite-layout/schema';
|
||||
|
||||
@@ -182,6 +183,7 @@ function serializeElements(elements: LayoutElement[], context: LayoutSerializati
|
||||
type InviteLayoutCustomizerPanelProps = {
|
||||
invite: EventQrInvite | null;
|
||||
eventName: string;
|
||||
eventDate: string | null;
|
||||
saving: boolean;
|
||||
resetting: boolean;
|
||||
onSave: (customization: QrLayoutCustomization) => Promise<void>;
|
||||
@@ -199,6 +201,7 @@ const ZOOM_STEP = 0.05;
|
||||
export function InviteLayoutCustomizerPanel({
|
||||
invite,
|
||||
eventName,
|
||||
eventDate,
|
||||
saving,
|
||||
resetting,
|
||||
onSave,
|
||||
@@ -1391,7 +1394,12 @@ export function InviteLayoutCustomizerPanel({
|
||||
}
|
||||
|
||||
const normalizedFormat = format.toLowerCase();
|
||||
const filenameStem = `${invite.token || 'invite'}-${normalizedFormat}`;
|
||||
const eventDateSegment = normalizeEventDateSegment(eventDate);
|
||||
const filename = buildDownloadFilename(
|
||||
['Einladungslayout', eventName, activeLayout?.name ?? null, eventDateSegment],
|
||||
normalizedFormat,
|
||||
'einladungslayout',
|
||||
);
|
||||
setDownloadBusy(normalizedFormat);
|
||||
setError(null);
|
||||
|
||||
@@ -1412,14 +1420,14 @@ export function InviteLayoutCustomizerPanel({
|
||||
|
||||
if (normalizedFormat === 'png') {
|
||||
const dataUrl = await generatePngDataUrl(exportOptions);
|
||||
await triggerDownloadFromDataUrl(dataUrl, `${filenameStem}.png`);
|
||||
await triggerDownloadFromDataUrl(dataUrl, filename);
|
||||
} else if (normalizedFormat === 'pdf') {
|
||||
const pdfBytes = await generatePdfBytes(
|
||||
exportOptions,
|
||||
'a4',
|
||||
'portrait',
|
||||
);
|
||||
triggerDownloadFromBlob(new Blob([pdfBytes.buffer as ArrayBuffer], { type: 'application/pdf' }), `${filenameStem}.pdf`);
|
||||
triggerDownloadFromBlob(new Blob([pdfBytes.buffer as ArrayBuffer], { type: 'application/pdf' }), filename);
|
||||
} else {
|
||||
throw new Error(`Unsupported format: ${normalizedFormat}`);
|
||||
}
|
||||
@@ -1509,34 +1517,8 @@ export function InviteLayoutCustomizerPanel({
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-foreground">{t('invites.customizer.heading', 'Layout anpassen')}</h2>
|
||||
<p className="text-sm text-muted-foreground">{t('invites.customizer.copy', 'Bearbeite Texte, Farben und Positionen direkt neben der Live-Vorschau. Änderungen werden sofort sichtbar.')}</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button variant="outline" onClick={handleResetClick} disabled={resetting || saving}>
|
||||
{resetting ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <RotateCcw className="mr-2 h-4 w-4" />}
|
||||
{t('invites.customizer.actions.reset', 'Zurücksetzen')}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (formRef.current) {
|
||||
if (typeof formRef.current.requestSubmit === 'function') {
|
||||
formRef.current.requestSubmit();
|
||||
} else {
|
||||
formRef.current.submit();
|
||||
}
|
||||
}
|
||||
}}
|
||||
className="bg-gradient-to-r from-amber-500 via-orange-500 to-rose-500 text-white shadow-lg shadow-rose-500/20"
|
||||
disabled={saving || resetting}
|
||||
>
|
||||
{saving ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Save className="mr-2 h-4 w-4" />}
|
||||
{t('invites.customizer.actions.save', 'Layout speichern')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="hidden flex-wrap items-center justify-end gap-2 lg:flex">
|
||||
{renderActionButtons('inline')}
|
||||
</div>
|
||||
|
||||
{error ? (
|
||||
@@ -1845,7 +1827,7 @@ export function InviteLayoutCustomizerPanel({
|
||||
</Tabs>
|
||||
</section>
|
||||
|
||||
<div className={cn('mt-6 flex flex-col gap-2 sm:flex-row sm:justify-end', showFloatingActions ? 'hidden' : 'flex')}>
|
||||
<div className={cn('mt-6 flex flex-col gap-2 sm:flex-row sm:justify-end lg:hidden', showFloatingActions ? 'hidden' : 'flex')}>
|
||||
{renderActionButtons('inline')}
|
||||
</div>
|
||||
<div ref={actionsSentinelRef} className="h-1 w-full" />
|
||||
|
||||
Reference in New Issue
Block a user