feat(qr-stands): clean SVG template renderer with exact QR placement and no redundant overlays

This commit is contained in:
AyrisAI
2026-08-20 23:39:56 +03:00
parent eb0b5d1886
commit f768b92873
3 changed files with 47 additions and 94 deletions
+2 -1
View File
@@ -82,7 +82,8 @@ export default function QrScreen() {
})(); })();
}, []); }, []);
const displayCategories = categories.length > 0 ? categories.slice(0, 4) : activePreset.categories; const displayCategories =
categories.length > 0 ? categories.slice(0, 4) : (activePreset.categories || []);
async function handleCopy() { async function handleCopy() {
if (!qr?.targetUrl) return; if (!qr?.targetUrl) return;
@@ -29,7 +29,7 @@ export function QrStandGeneratorClient({
const targetUrl = `https://menul.io/${slug}`; const targetUrl = `https://menul.io/${slug}`;
const displayCategories = const displayCategories =
categoriesList.length > 0 ? categoriesList.slice(0, 4) : activePreset.categories; categoriesList.length > 0 ? categoriesList.slice(0, 4) : (activePreset.categories || []);
async function handleDownloadPng() { async function handleDownloadPng() {
if (!standRef.current) return; if (!standRef.current) return;
@@ -139,7 +139,7 @@ export function QrStandGeneratorClient({
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div <div
className="w-4 h-4 rounded-full border border-white/20 shadow-sm" className="w-4 h-4 rounded-full border border-white/20 shadow-sm"
style={{ background: preset.plaqueBg }} style={{ background: preset.plaqueBg || preset.plaqueColor || "#1A1A1E" }}
/> />
<div> <div>
<div className="text-xs font-bold">{preset.name}</div> <div className="text-xs font-bold">{preset.name}</div>
@@ -208,51 +208,22 @@ export function QrStandGeneratorClient({
{activePreset.svgTemplateUrl ? ( {activePreset.svgTemplateUrl ? (
<div <div
ref={standRef} ref={standRef}
className={`w-[360px] sm:w-[380px] rounded-[2.5rem] relative overflow-hidden shadow-[0_25px_60px_rgba(0,0,0,0.6)] border-2 border-white/10 ${ className="w-[360px] sm:w-[380px] rounded-[2.5rem] relative overflow-hidden shadow-[0_25px_60px_rgba(0,0,0,0.6)] border-2 border-white/10 aspect-[400/650]"
activePreset.aspectRatio === "672/900" ? "aspect-[672/900]" : "aspect-[400/650]"
}`}
> >
{/* Exact SVG Template Vector Background Graphic */} {/* Exact SVG Template Vector Background Graphic */}
<img <img
src={activePreset.svgTemplateUrl} src={activePreset.svgTemplateUrl}
alt={activePreset.name} alt={activePreset.name}
className="absolute inset-0 w-full h-full object-cover pointer-events-none" className="absolute inset-0 w-full h-full object-contain pointer-events-none"
/> />
{/* Dynamic Restaurant Logo & Name Header Overlay */} {/* Dynamic QR Code Canvas Overlay - Fills the QR Code Box Exactly */}
<div <div className="absolute top-[33.08%] left-1/2 -translate-x-1/2 w-[55%] aspect-square rounded-[20px] p-3 shadow-xl flex items-center justify-center bg-white z-10 border border-zinc-200">
className={`absolute left-0 right-0 flex flex-col items-center justify-center px-6 text-center z-10 ${
activePreset.aspectRatio === "672/900" ? "top-[13%]" : "top-[15.5%]"
}`}
>
{logoUrl ? (
<img
src={logoUrl}
alt={restaurantName}
className="h-8 w-auto max-w-[120px] object-contain mb-1"
/>
) : null}
<span
className="font-black text-xl sm:text-2xl tracking-wider uppercase font-['Montserrat'] leading-tight drop-shadow-md"
style={{ color: activePreset.accentText }}
>
{restaurantName}
</span>
</div>
{/* Dynamic QR Code Canvas Overlay */}
<div
className={`absolute left-1/2 -translate-x-1/2 rounded-[20px] p-3 shadow-2xl flex items-center justify-center bg-white z-10 border border-zinc-200 ${
activePreset.aspectRatio === "672/900"
? "top-[37%] w-[52%] aspect-square"
: "top-[33.1%] w-[55%] aspect-square"
}`}
>
<QRCodeCanvas <QRCodeCanvas
value={targetUrl} value={targetUrl}
size={200} size={220}
bgColor={activePreset.qrBg || "#ffffff"} bgColor="#ffffff"
fgColor={activePreset.qrColor || "#111111"} fgColor="#111111"
level="H" level="H"
style={{ width: "100%", height: "100%" }} style={{ width: "100%", height: "100%" }}
includeMargin={false} includeMargin={false}
@@ -264,8 +235,8 @@ export function QrStandGeneratorClient({
ref={standRef} ref={standRef}
className="w-[360px] sm:w-[380px] min-h-[660px] rounded-[2.5rem] p-7 flex flex-col items-center text-center relative shadow-[0_25px_60px_rgba(0,0,0,0.6)] border-2" className="w-[360px] sm:w-[380px] min-h-[660px] rounded-[2.5rem] p-7 flex flex-col items-center text-center relative shadow-[0_25px_60px_rgba(0,0,0,0.6)] border-2"
style={{ style={{
background: activePreset.plaqueBg, background: activePreset.plaqueBg || activePreset.plaqueColor || "#1A1A1E",
borderColor: activePreset.borderAccent, borderColor: activePreset.borderAccent || "#D4AF37",
}} }}
> >
{/* Inner Line Accent */} {/* Inner Line Accent */}
+34 -53
View File
@@ -1,84 +1,65 @@
export interface QrStandPreset { export interface QrStandPreset {
key: string; key: string;
name: string; name: string;
categoryName: string; categoryName?: string;
bgGradient: string; svgTemplateUrl: string;
plaqueBg: string;
plaqueColor: string;
borderAccent: string;
qrBg: string;
qrColor: string;
logoBorder: string;
logoBg: string;
accentText: string;
headerText: string;
subText: string;
dividerColor: string;
categories: string[];
svgTemplateUrl?: string;
aspectRatio?: string; aspectRatio?: string;
bgGradient?: string;
plaqueBg?: string;
plaqueColor?: string;
borderAccent?: string;
qrBg?: string;
qrColor?: string;
logoBorder?: string;
logoBg?: string;
accentText?: string;
headerText?: string;
subText?: string;
dividerColor?: string;
categories?: string[];
} }
export const QR_STAND_PRESETS: Record<string, QrStandPreset> = { export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
"qr-1": { "qr-1": {
key: "qr-1", key: "qr-1",
name: "Gourmet Bistro Stand (QR-1)", name: "Gourmet Bistro Stand (QR-1)",
categoryName: "Özel Masa Kartı Şablonu", categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-1.svg",
aspectRatio: "400/650",
bgGradient: "radial-gradient(circle at 50% 50%, #1A1A1E 0%, #0C0C0E 100%)", bgGradient: "radial-gradient(circle at 50% 50%, #1A1A1E 0%, #0C0C0E 100%)",
plaqueBg: "linear-gradient(135deg, #1A1A1E 0%, #121215 100%)",
plaqueColor: "#1A1A1E", plaqueColor: "#1A1A1E",
borderAccent: "#D4AF37", borderAccent: "#D4AF37",
qrBg: "#ffffff",
qrColor: "#111111",
logoBorder: "#D4AF37",
logoBg: "rgba(26, 26, 30, 0.8)",
accentText: "#D4AF37", accentText: "#D4AF37",
headerText: "#FFFFFF", headerText: "#FFFFFF",
subText: "#8E8E93", subText: "#8E8E93",
dividerColor: "rgba(212, 175, 55, 0.3)", dividerColor: "rgba(212, 175, 55, 0.3)",
categories: ["BAŞLANGIÇLAR", "ANA YEMEKLER", "TATLILAR", "İÇECEKLER"],
svgTemplateUrl: "/qr-1.svg",
aspectRatio: "400/650",
},
"qr-4": {
key: "qr-4",
name: "Gourmet Gold Luxury Stand (QR-4)",
categoryName: "Özel Masa Kartı Şablonu",
bgGradient: "radial-gradient(circle at 50% 50%, #451A03 0%, #1c0a02 100%)",
plaqueBg: "linear-gradient(135deg, #000000 0%, #111111 100%)",
plaqueColor: "#000000",
borderAccent: "#D4AF37",
qrBg: "#ffffff",
qrColor: "#111111",
logoBorder: "#D4AF37",
logoBg: "rgba(0, 0, 0, 0.8)",
accentText: "#FFD700",
headerText: "#FFFFFF",
subText: "#A1A1AA",
dividerColor: "rgba(212, 175, 55, 0.3)",
categories: ["BAŞLANGIÇLAR", "ANA YEMEKLER", "TATLILAR", "İÇECEKLER"],
svgTemplateUrl: "/qr-4.svg",
aspectRatio: "400/650",
}, },
"qr-2": { "qr-2": {
key: "qr-2", key: "qr-2",
name: "Gourmet Bistro Chef Crest (QR-2)", name: "Gourmet Bistro Chef Crest (QR-2)",
categoryName: "Özel Masa Kartı Şablonu", categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-2.svg",
aspectRatio: "400/650",
bgGradient: "radial-gradient(circle at 50% 50%, #1A1A1E 0%, #0C0C0E 100%)", bgGradient: "radial-gradient(circle at 50% 50%, #1A1A1E 0%, #0C0C0E 100%)",
plaqueBg: "linear-gradient(135deg, #1A1A1E 0%, #121215 100%)",
plaqueColor: "#1A1A1E", plaqueColor: "#1A1A1E",
borderAccent: "#D4AF37", borderAccent: "#D4AF37",
qrBg: "#ffffff",
qrColor: "#111111",
logoBorder: "#D4AF37",
logoBg: "rgba(26, 26, 30, 0.8)",
accentText: "#D4AF37", accentText: "#D4AF37",
headerText: "#FFFFFF", headerText: "#FFFFFF",
subText: "#8E8E93", subText: "#8E8E93",
dividerColor: "rgba(212, 175, 55, 0.3)", dividerColor: "rgba(212, 175, 55, 0.3)",
categories: ["BAŞLANGIÇLAR", "ANA YEMEKLER", "TATLILAR", "İÇECEKLER"], },
svgTemplateUrl: "/qr-2.svg", "qr-4": {
key: "qr-4",
name: "Gourmet Gold Luxury Stand (QR-4)",
categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-4.svg",
aspectRatio: "400/650", aspectRatio: "400/650",
bgGradient: "radial-gradient(circle at 50% 50%, #451A03 0%, #1c0a02 100%)",
plaqueColor: "#000000",
borderAccent: "#D4AF37",
accentText: "#FFD700",
headerText: "#FFFFFF",
subText: "#A1A1AA",
dividerColor: "rgba(212, 175, 55, 0.3)",
}, },
}; };