improved layout customize page

This commit is contained in:
Codex Agent
2025-12-12 23:19:23 +01:00
parent a35f81705d
commit a35b56f09d
6 changed files with 626 additions and 440 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,7 @@ export default function MobileQrPrintPage() {
const [error, setError] = React.useState<string | null>(null);
const [loading, setLoading] = React.useState(true);
const [qrUrl, setQrUrl] = React.useState<string>('');
const [qrImage, setQrImage] = React.useState<string>('');
React.useEffect(() => {
if (!slug) return;
@@ -56,6 +57,7 @@ export default function MobileQrPrintPage() {
: initialLayout?.id ?? null;
setSelectedLayoutId(resolvedLayoutId);
setQrUrl(primaryInvite?.url ?? data.public_url ?? '');
setQrImage(primaryInvite?.qr_code_data_url ?? '');
setError(null);
} catch (err) {
if (!isAuthError(err)) {
@@ -103,7 +105,7 @@ export default function MobileQrPrintPage() {
>
{qrUrl ? (
<img
src={`https://api.qrserver.com/v1/create-qr-code/?size=220x220&data=${encodeURIComponent(qrUrl)}`}
src={qrImage || `https://api.qrserver.com/v1/create-qr-code/?size=220x220&data=${encodeURIComponent(qrUrl)}`}
alt="QR"
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
/>
@@ -118,16 +120,22 @@ export default function MobileQrPrintPage() {
</Text>
<XStack space="$2" width="100%" marginTop="$2">
<CTAButton
label={t('events.qr.download', 'Download')}
fullWidth={false}
onPress={() => {
if (qrUrl) {
toast.success(t('events.qr.downloadStarted', 'Download gestartet'));
} else {
toast.error(t('events.qr.missing', 'Kein QR-Link vorhanden'));
}
}}
/>
label={t('events.qr.download', 'Download')}
fullWidth={false}
onPress={() => {
if (!qrUrl) {
toast.error(t('events.qr.missing', 'Kein QR-Link vorhanden'));
return;
}
const link = document.createElement('a');
link.href = qrImage || `https://api.qrserver.com/v1/create-qr-code/?size=220x220&data=${encodeURIComponent(qrUrl)}`;
link.download = 'event-qr.png';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
toast.success(t('events.qr.downloadStarted', 'Download gestartet'));
}}
/>
<CTAButton
label={t('events.qr.share', 'Share')}
fullWidth={false}
@@ -190,6 +198,7 @@ export default function MobileQrPrintPage() {
try {
const invite = await createQrInvite(slug, { label: 'Mobile Link' });
setQrUrl(invite.url);
setQrImage(invite.qr_code_data_url ?? '');
setSelectedInvite(invite);
setSelectedLayoutId(invite.layouts?.[0]?.id ?? selectedLayoutId);
toast.success(t('events.qr.created', 'Neuer QR-Link erstellt'));

View File

@@ -682,6 +682,7 @@ export async function createFabricObject({
content: element.content,
});
const hasRotation = typeof element.rotation === 'number' && element.rotation !== 0;
const baseConfig = {
left: element.x,
top: element.y,
@@ -689,6 +690,9 @@ export async function createFabricObject({
selectable: !readOnly,
hasBorders: !readOnly,
hasControls: !readOnly,
originX: hasRotation ? 'center' : 'left',
originY: hasRotation ? 'center' : 'top',
angle: element.rotation ?? 0,
} as FabricObjectWithId;
switch (element.type) {
@@ -961,6 +965,8 @@ export async function loadImageObject(
} else {
// Use direct Image constructor approach for better compatibility
const img = new Image();
// Avoid tainting canvas when reading remote QR images / logos
img.crossOrigin = 'anonymous';
img.onload = () => {
if (resolved) return;
console.debug('[Invites][Fabric] image loaded', {

View File

@@ -6,6 +6,7 @@ import { FabricRenderOptions, renderFabricLayout } from './DesignerCanvas';
const PDF_PAGE_SIZES: Record<string, { width: number; height: number }> = {
a4: { width: 595.28, height: 841.89 },
a5: { width: 419.53, height: 595.28 },
letter: { width: 612, height: 792 },
};