improved layout customize page
This commit is contained in:
@@ -1842,6 +1842,31 @@
|
|||||||
"notificationsLoading": "Lade Einstellungen ...",
|
"notificationsLoading": "Lade Einstellungen ...",
|
||||||
"pref": {}
|
"pref": {}
|
||||||
},
|
},
|
||||||
|
"events": {
|
||||||
|
"qr": {
|
||||||
|
"title": "QR-Code & Druck-Layouts",
|
||||||
|
"heroTitle": "Einlass-QR-Code",
|
||||||
|
"description": "Scannen, um zur Gäste-App zu gelangen.",
|
||||||
|
"missing": "Kein QR-Link vorhanden",
|
||||||
|
"download": "Download",
|
||||||
|
"downloadStarted": "Download gestartet",
|
||||||
|
"share": "Teilen",
|
||||||
|
"shareSuccess": "Link kopiert",
|
||||||
|
"shareFailed": "Link konnte nicht kopiert werden",
|
||||||
|
"step1": "Schritt 1: Format wählen",
|
||||||
|
"layouts": "Druck-Layouts",
|
||||||
|
"preview": "Anpassen & Exportieren",
|
||||||
|
"createLink": "Neuen QR-Link erstellen",
|
||||||
|
"created": "Neuer QR-Link erstellt",
|
||||||
|
"createFailed": "Link konnte nicht erstellt werden.",
|
||||||
|
"format": {
|
||||||
|
"poster": "A4 Poster",
|
||||||
|
"posterSubtitle": "Hochformat für Aushänge",
|
||||||
|
"table": "A5 Tischkarte (faltbar)",
|
||||||
|
"tableSubtitle": "Quer, doppelt & gespiegelt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"keys": {
|
"keys": {
|
||||||
|
|||||||
@@ -1863,6 +1863,31 @@
|
|||||||
"notificationsLoading": "Loading settings ...",
|
"notificationsLoading": "Loading settings ...",
|
||||||
"pref": {}
|
"pref": {}
|
||||||
},
|
},
|
||||||
|
"events": {
|
||||||
|
"qr": {
|
||||||
|
"title": "QR Code & Print Layouts",
|
||||||
|
"heroTitle": "Entrance QR Code",
|
||||||
|
"description": "Scan to access the event guest app.",
|
||||||
|
"missing": "No QR link available",
|
||||||
|
"download": "Download",
|
||||||
|
"downloadStarted": "Download started",
|
||||||
|
"share": "Share",
|
||||||
|
"shareSuccess": "Link copied",
|
||||||
|
"shareFailed": "Could not copy link",
|
||||||
|
"step1": "Step 1: Choose format",
|
||||||
|
"layouts": "Print Layouts",
|
||||||
|
"preview": "Customize & Export",
|
||||||
|
"createLink": "Create new QR link",
|
||||||
|
"created": "New QR link created",
|
||||||
|
"createFailed": "Could not create link.",
|
||||||
|
"format": {
|
||||||
|
"poster": "A4 Poster",
|
||||||
|
"posterSubtitle": "Portrait for posters",
|
||||||
|
"table": "A5 Table card (foldable)",
|
||||||
|
"tableSubtitle": "Landscape, double-mirror"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"keys": {
|
"keys": {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,7 @@ export default function MobileQrPrintPage() {
|
|||||||
const [error, setError] = React.useState<string | null>(null);
|
const [error, setError] = React.useState<string | null>(null);
|
||||||
const [loading, setLoading] = React.useState(true);
|
const [loading, setLoading] = React.useState(true);
|
||||||
const [qrUrl, setQrUrl] = React.useState<string>('');
|
const [qrUrl, setQrUrl] = React.useState<string>('');
|
||||||
|
const [qrImage, setQrImage] = React.useState<string>('');
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!slug) return;
|
if (!slug) return;
|
||||||
@@ -56,6 +57,7 @@ export default function MobileQrPrintPage() {
|
|||||||
: initialLayout?.id ?? null;
|
: initialLayout?.id ?? null;
|
||||||
setSelectedLayoutId(resolvedLayoutId);
|
setSelectedLayoutId(resolvedLayoutId);
|
||||||
setQrUrl(primaryInvite?.url ?? data.public_url ?? '');
|
setQrUrl(primaryInvite?.url ?? data.public_url ?? '');
|
||||||
|
setQrImage(primaryInvite?.qr_code_data_url ?? '');
|
||||||
setError(null);
|
setError(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!isAuthError(err)) {
|
if (!isAuthError(err)) {
|
||||||
@@ -103,7 +105,7 @@ export default function MobileQrPrintPage() {
|
|||||||
>
|
>
|
||||||
{qrUrl ? (
|
{qrUrl ? (
|
||||||
<img
|
<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"
|
alt="QR"
|
||||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||||
/>
|
/>
|
||||||
@@ -118,16 +120,22 @@ export default function MobileQrPrintPage() {
|
|||||||
</Text>
|
</Text>
|
||||||
<XStack space="$2" width="100%" marginTop="$2">
|
<XStack space="$2" width="100%" marginTop="$2">
|
||||||
<CTAButton
|
<CTAButton
|
||||||
label={t('events.qr.download', 'Download')}
|
label={t('events.qr.download', 'Download')}
|
||||||
fullWidth={false}
|
fullWidth={false}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (qrUrl) {
|
if (!qrUrl) {
|
||||||
toast.success(t('events.qr.downloadStarted', 'Download gestartet'));
|
toast.error(t('events.qr.missing', 'Kein QR-Link vorhanden'));
|
||||||
} else {
|
return;
|
||||||
toast.error(t('events.qr.missing', 'Kein QR-Link vorhanden'));
|
}
|
||||||
}
|
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
|
<CTAButton
|
||||||
label={t('events.qr.share', 'Share')}
|
label={t('events.qr.share', 'Share')}
|
||||||
fullWidth={false}
|
fullWidth={false}
|
||||||
@@ -190,6 +198,7 @@ export default function MobileQrPrintPage() {
|
|||||||
try {
|
try {
|
||||||
const invite = await createQrInvite(slug, { label: 'Mobile Link' });
|
const invite = await createQrInvite(slug, { label: 'Mobile Link' });
|
||||||
setQrUrl(invite.url);
|
setQrUrl(invite.url);
|
||||||
|
setQrImage(invite.qr_code_data_url ?? '');
|
||||||
setSelectedInvite(invite);
|
setSelectedInvite(invite);
|
||||||
setSelectedLayoutId(invite.layouts?.[0]?.id ?? selectedLayoutId);
|
setSelectedLayoutId(invite.layouts?.[0]?.id ?? selectedLayoutId);
|
||||||
toast.success(t('events.qr.created', 'Neuer QR-Link erstellt'));
|
toast.success(t('events.qr.created', 'Neuer QR-Link erstellt'));
|
||||||
|
|||||||
@@ -682,6 +682,7 @@ export async function createFabricObject({
|
|||||||
content: element.content,
|
content: element.content,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const hasRotation = typeof element.rotation === 'number' && element.rotation !== 0;
|
||||||
const baseConfig = {
|
const baseConfig = {
|
||||||
left: element.x,
|
left: element.x,
|
||||||
top: element.y,
|
top: element.y,
|
||||||
@@ -689,6 +690,9 @@ export async function createFabricObject({
|
|||||||
selectable: !readOnly,
|
selectable: !readOnly,
|
||||||
hasBorders: !readOnly,
|
hasBorders: !readOnly,
|
||||||
hasControls: !readOnly,
|
hasControls: !readOnly,
|
||||||
|
originX: hasRotation ? 'center' : 'left',
|
||||||
|
originY: hasRotation ? 'center' : 'top',
|
||||||
|
angle: element.rotation ?? 0,
|
||||||
} as FabricObjectWithId;
|
} as FabricObjectWithId;
|
||||||
|
|
||||||
switch (element.type) {
|
switch (element.type) {
|
||||||
@@ -961,6 +965,8 @@ export async function loadImageObject(
|
|||||||
} else {
|
} else {
|
||||||
// Use direct Image constructor approach for better compatibility
|
// Use direct Image constructor approach for better compatibility
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
|
// Avoid tainting canvas when reading remote QR images / logos
|
||||||
|
img.crossOrigin = 'anonymous';
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
if (resolved) return;
|
if (resolved) return;
|
||||||
console.debug('[Invites][Fabric] image loaded', {
|
console.debug('[Invites][Fabric] image loaded', {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { FabricRenderOptions, renderFabricLayout } from './DesignerCanvas';
|
|||||||
|
|
||||||
const PDF_PAGE_SIZES: Record<string, { width: number; height: number }> = {
|
const PDF_PAGE_SIZES: Record<string, { width: number; height: number }> = {
|
||||||
a4: { width: 595.28, height: 841.89 },
|
a4: { width: 595.28, height: 841.89 },
|
||||||
|
a5: { width: 419.53, height: 595.28 },
|
||||||
letter: { width: 612, height: 792 },
|
letter: { width: 612, height: 792 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user