layouts schick gemacht und packagelimits weiter implementiert
This commit is contained in:
@@ -156,7 +156,7 @@ function formatQrSizeLabel(sizePx: number | null, fallback: string): string {
|
||||
return `${sizePx}px`;
|
||||
}
|
||||
|
||||
export default function EventInvitesPage(): JSX.Element {
|
||||
export default function EventInvitesPage(): React.ReactElement {
|
||||
const { slug } = useParams<{ slug?: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation('management');
|
||||
@@ -205,8 +205,10 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
|
||||
const widthRatio = container.clientWidth / CANVAS_WIDTH;
|
||||
const heightRatio = container.clientHeight ? container.clientHeight / CANVAS_HEIGHT : Number.POSITIVE_INFINITY;
|
||||
const base = Math.min(widthRatio, heightRatio);
|
||||
const safeBase = Number.isFinite(base) && base > 0 ? Math.min(base, 1) : 1;
|
||||
const portraitRatio = 1754 / 1240; // A4 height/width for portrait priority
|
||||
const adjustedHeightRatio = heightRatio * portraitRatio;
|
||||
const base = Math.min(widthRatio, adjustedHeightRatio);
|
||||
const safeBase = Number.isFinite(base) && base > 0 ? base : 1;
|
||||
const clampedScale = clamp(safeBase, 0.1, 1);
|
||||
|
||||
setExportScale((prev) => (Math.abs(prev - clampedScale) < 0.001 ? prev : clampedScale));
|
||||
@@ -311,7 +313,7 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
const backgroundColor = normalizeHexColor(customization?.background_color ?? (layoutPreview.background as string | undefined)) ?? '#F8FAFC';
|
||||
const accentColor = normalizeHexColor(customization?.accent_color ?? (layoutPreview.accent as string | undefined)) ?? '#6366F1';
|
||||
const textColor = normalizeHexColor(customization?.text_color ?? (layoutPreview.text as string | undefined)) ?? '#111827';
|
||||
const secondaryColor = normalizeHexColor(customization?.secondary_color ?? (layoutPreview.secondary as string | undefined)) ?? '#1F2937';
|
||||
const secondaryColor = '#1F2937';
|
||||
const badgeColor = normalizeHexColor(customization?.badge_color ?? (layoutPreview.accent as string | undefined)) ?? accentColor;
|
||||
const gradient = normalizeGradient(customization?.background_gradient ?? layoutPreview.background_gradient ?? null);
|
||||
|
||||
@@ -323,7 +325,7 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
const formatBadges = formatKeys.map((format) => String(format).toUpperCase());
|
||||
const formatLabel = formatBadges.length ? formatBadges.join(' · ') : t('invites.export.meta.formatsNone', 'Keine Formate hinterlegt');
|
||||
|
||||
const qrSizePx = (layoutPreview.qr_size_px as number | undefined) ?? (exportLayout.qr?.size_px ?? null);
|
||||
const qrSizePx = (layoutPreview.qr_size_px as number | undefined) ?? 480;
|
||||
|
||||
return {
|
||||
backgroundStyle: buildBackgroundStyle(backgroundColor, gradient),
|
||||
@@ -348,11 +350,8 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
formatLabel,
|
||||
formatBadges,
|
||||
formats: formatKeys,
|
||||
paperLabel: formatPaperLabel(exportLayout.paper),
|
||||
orientationLabel:
|
||||
exportLayout.orientation === 'landscape'
|
||||
? t('invites.export.meta.orientationLandscape', 'Querformat')
|
||||
: t('invites.export.meta.orientationPortrait', 'Hochformat'),
|
||||
paperLabel: formatPaperLabel('a4'),
|
||||
orientationLabel: t('invites.export.meta.orientationPortrait', 'Hochformat'),
|
||||
qrSizeLabel: formatQrSizeLabel(qrSizePx, t('invites.export.meta.qrSizeFallback', 'Automatisch')),
|
||||
lastUpdated: selectedInvite.created_at ? formatDateTime(selectedInvite.created_at) : null,
|
||||
mode: customization?.mode === 'advanced' ? 'advanced' : 'standard',
|
||||
@@ -397,7 +396,7 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
exportLayout,
|
||||
baseForm,
|
||||
eventName,
|
||||
exportLayout.preview?.qr_size_px ?? exportLayout.qr?.size_px ?? 480
|
||||
exportLayout.preview?.qr_size_px ?? 480
|
||||
);
|
||||
}, [exportLayout, currentCustomization, selectedInvite?.url, eventName]);
|
||||
|
||||
@@ -428,7 +427,7 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
[selectedInvite?.id, exportLayout?.id, exportPreview?.mode]
|
||||
);
|
||||
|
||||
const exportLogo = currentCustomization?.logo_data_url ?? currentCustomization?.logo_url ?? exportLayout?.logo_url ?? null;
|
||||
const exportLogo = currentCustomization?.logo_data_url ?? currentCustomization?.logo_url ?? null;
|
||||
const exportQr = selectedInvite?.qr_code_data_url ?? null;
|
||||
|
||||
const handlePreviewSelect = React.useCallback((_id: string | null) => undefined, []);
|
||||
@@ -613,10 +612,10 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
} else if (normalizedFormat === 'pdf') {
|
||||
const pdfBytes = await generatePdfBytes(
|
||||
exportOptions,
|
||||
exportLayout.paper ?? 'a4',
|
||||
exportLayout.orientation ?? 'portrait',
|
||||
'a4',
|
||||
'portrait',
|
||||
);
|
||||
triggerDownloadFromBlob(new Blob([pdfBytes], { type: 'application/pdf' }), `${filenameStem}.pdf`);
|
||||
triggerDownloadFromBlob(new Blob([pdfBytes as any], { type: 'application/pdf' }), `${filenameStem}.pdf`);
|
||||
} else {
|
||||
setExportError(t('invites.customizer.errors.downloadFailed', 'Download fehlgeschlagen. Bitte versuche es erneut.'));
|
||||
}
|
||||
@@ -656,8 +655,8 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
try {
|
||||
const pdfBytes = await generatePdfBytes(
|
||||
exportOptions,
|
||||
exportLayout.paper ?? 'a4',
|
||||
exportLayout.orientation ?? 'portrait',
|
||||
'a4',
|
||||
'portrait',
|
||||
);
|
||||
|
||||
await openPdfInNewTab(pdfBytes);
|
||||
@@ -881,23 +880,25 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
ref={exportPreviewContainerRef}
|
||||
className="pointer-events-none w-full max-w-full"
|
||||
>
|
||||
<DesignerCanvas
|
||||
elements={exportElements}
|
||||
selectedId={null}
|
||||
onSelect={handlePreviewSelect}
|
||||
onChange={handlePreviewChange}
|
||||
background={exportPreview.backgroundColor}
|
||||
gradient={exportPreview.backgroundGradient}
|
||||
accent={exportPreview.accentColor}
|
||||
text={exportPreview.textColor}
|
||||
secondary={exportPreview.secondaryColor}
|
||||
badge={exportPreview.badgeColor}
|
||||
qrCodeDataUrl={exportQr}
|
||||
logoDataUrl={exportLogo}
|
||||
scale={exportScale}
|
||||
layoutKey={exportCanvasKey}
|
||||
readOnly
|
||||
/>
|
||||
<div className="aspect-[1240/1754] mx-auto max-w-full">
|
||||
<DesignerCanvas
|
||||
elements={exportElements}
|
||||
selectedId={null}
|
||||
onSelect={handlePreviewSelect}
|
||||
onChange={handlePreviewChange}
|
||||
background={exportPreview.backgroundColor}
|
||||
gradient={exportPreview.backgroundGradient}
|
||||
accent={exportPreview.accentColor}
|
||||
text={exportPreview.textColor}
|
||||
secondary={exportPreview.secondaryColor}
|
||||
badge={exportPreview.badgeColor}
|
||||
qrCodeDataUrl={exportQr}
|
||||
logoDataUrl={exportLogo}
|
||||
scale={exportScale}
|
||||
layoutKey={exportCanvasKey}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-3xl border border-dashed border-[var(--tenant-border-strong)] bg-[var(--tenant-layer)]/80 px-10 py-20 text-center text-sm text-[var(--tenant-foreground-soft)]">
|
||||
@@ -1153,7 +1154,7 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
function InviteCustomizerSkeleton(): JSX.Element {
|
||||
function InviteCustomizerSkeleton(): React.ReactElement {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="h-8 w-56 animate-pulse rounded-full bg-white/70" />
|
||||
|
||||
Reference in New Issue
Block a user