style(mobile): render 1:1 pixel-perfect Urban Burger tabletop plaque layout

This commit is contained in:
AyrisAI
2026-08-20 18:28:23 +03:00
parent 3a15dd15b9
commit e63de8584d
2 changed files with 140 additions and 81 deletions
+134 -81
View File
@@ -28,7 +28,7 @@ interface QrResponse {
export default function QrScreen() { export default function QrScreen() {
const [active, setActive] = useState<ActiveRestaurant | null>(null); const [active, setActive] = useState<ActiveRestaurant | null>(null);
const [restaurantName, setRestaurantName] = useState<string>("RESTORAN"); const [restaurantName, setRestaurantName] = useState<string>("THE URBAN BURGER");
const [qr, setQr] = useState<QrResponse | null>(null); const [qr, setQr] = useState<QrResponse | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@@ -127,7 +127,7 @@ export default function QrScreen() {
const catsHtml = displayCategories const catsHtml = displayCategories
.map( .map(
(c, i) => (c, i) =>
`<div style="padding: 4px 0;">${c}</div>${ `<div style="padding: 5px 0;">${c}</div>${
i < displayCategories.length - 1 i < displayCategories.length - 1
? '<div style="height: 1px; background: ' + activePreset.dividerColor + '; margin: 2px 0;"></div>' ? '<div style="height: 1px; background: ' + activePreset.dividerColor + '; margin: 2px 0;"></div>'
: "" : ""
@@ -146,51 +146,65 @@ export default function QrScreen() {
margin: 0; padding: 0; width: 148mm; height: 210mm; margin: 0; padding: 0; width: 148mm; height: 210mm;
box-sizing: border-box; box-sizing: border-box;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background: ${activePreset.bgGradient}; background: #1c3e2b;
display: flex; align-items: center; justify-content: center; display: flex; align-items: center; justify-content: center;
-webkit-print-color-adjust: exact; -webkit-print-color-adjust: exact;
} }
.plaque { .plaque {
width: 120mm; height: 180mm; width: 125mm; height: 185mm;
background: ${activePreset.plaqueBg}; background: ${activePreset.plaqueColor};
border: 3px solid ${activePreset.borderAccent}; border-radius: 32px;
border-radius: 24px; padding: 24px; box-sizing: border-box;
padding: 20px; box-sizing: border-box;
display: flex; flex-direction: column; align-items: center; text-align: center; display: flex; flex-direction: column; align-items: center; text-align: center;
color: ${activePreset.headerText}; color: ${activePreset.headerText};
position: relative; position: relative;
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
}
.inner-border {
position: absolute; top: 10px; left: 10px; right: 10px; bottom: 10px;
border: 1px solid ${activePreset.borderAccent};
border-radius: 24px; pointer-events: none;
} }
.badge { .badge {
border: 2px solid ${activePreset.logoBorder}; border: 2px solid ${activePreset.logoBorder};
background: ${activePreset.logoBg}; background: ${activePreset.logoBg};
color: ${activePreset.accentText}; color: ${activePreset.accentText};
padding: 6px 16px; border-radius: 12px; padding: 6px 18px; border-radius: 12px;
font-weight: 900; font-size: 16px; letter-spacing: 2px; font-weight: 900; font-size: 16px; letter-spacing: 2px;
margin-bottom: 12px; margin-bottom: 14px; display: inline-flex; align-items: center; gap: 6px;
} }
.title { .title {
font-size: 20px; font-weight: 900; letter-spacing: 1px; font-size: 22px; font-weight: 900; letter-spacing: 1px;
line-height: 1.1; margin-bottom: 16px; line-height: 1.1; margin-bottom: 18px; color: #ffffff;
} }
.qr-box { .qr-box {
background: ${activePreset.qrBg}; background: ${activePreset.qrBg};
padding: 14px; border-radius: 20px; padding: 16px; border-radius: 24px;
display: inline-block; margin-bottom: 16px; display: inline-block; margin-bottom: 14px;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
} }
.qr-img { width: 150px; height: 150px; display: block; } .qr-img { width: 160px; height: 160px; display: block; border-radius: 8px; }
.sub { font-size: 11px; opacity: 0.8; margin-bottom: 12px; color: ${activePreset.subText}; } .sub { font-size: 12px; opacity: 0.9; margin-bottom: 16px; color: ${activePreset.subText}; font-weight: 600; }
.cats { font-size: 13px; font-weight: 800; letter-spacing: 2px; width: 80%; margin: 0 auto; } .footer-wrap { width: 100%; position: relative; }
.icon-left { position: absolute; left: 10px; bottom: 15px; color: ${activePreset.accentText}; font-size: 28px; }
.icon-right { position: absolute; right: 10px; bottom: 15px; color: ${activePreset.accentText}; font-size: 28px; }
.cats { font-size: 14px; font-weight: 900; letter-spacing: 2px; width: 75%; margin: 0 auto; color: #ffffff; }
</style> </style>
</head> </head>
<body> <body>
<div class="plaque"> <div class="plaque">
<div class="badge">${restTitle}</div> <div class="inner-border"></div>
<div class="title">DİJİTAL MENÜ<br><span style="letter-spacing:2px;">KAMERANIZLA TARATIN</span></div> <div class="badge">🔥 ${restTitle}</div>
<div class="title">DIGITAL MENU<br><span style="letter-spacing:2px;">SCAN TO ORDER</span></div>
<div class="qr-box"> <div class="qr-box">
<img class="qr-img" src="${qrImageUri}" /> <img class="qr-img" src="${qrImageUri}" />
</div> </div>
<div class="sub">Telefonunuzun kamerasını doğrultun</div> <div class="sub">Use your phone's camera</div>
<div class="cats">${catsHtml}</div> <div class="footer-wrap">
<div class="icon-left">🍔</div>
<div class="icon-right">🥤</div>
<div class="cats">${catsHtml}</div>
</div>
</div> </div>
</body> </body>
</html> </html>
@@ -298,7 +312,7 @@ export default function QrScreen() {
width: 14, width: 14,
height: 14, height: 14,
borderRadius: 7, borderRadius: 7,
backgroundColor: preset.qrColor, backgroundColor: preset.plaqueColor,
borderWidth: 1, borderWidth: 1,
borderColor: "rgba(255,255,255,0.4)", borderColor: "rgba(255,255,255,0.4)",
}} }}
@@ -322,89 +336,114 @@ export default function QrScreen() {
style={{ style={{
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
padding: 16, padding: 18,
borderRadius: 24, borderRadius: 28,
backgroundColor: "#18181B", backgroundColor: "#1c3e2b", // Ambient forest green tabletop environment backdrop
marginBottom: 20, marginBottom: 20,
shadowColor: "#000", shadowColor: "#000",
shadowOffset: { width: 0, height: 6 }, shadowOffset: { width: 0, height: 10 },
shadowOpacity: 0.15, shadowOpacity: 0.35,
shadowRadius: 16, shadowRadius: 20,
elevation: 6, elevation: 8,
}} }}
> >
<ViewShot ref={viewShotRef} options={{ format: "png", quality: 1.0 }}> <ViewShot ref={viewShotRef} options={{ format: "png", quality: 1.0 }}>
<View <View
style={{ style={{
width: 300, width: 300,
minHeight: 480, minHeight: 520,
borderRadius: 28, borderRadius: 36,
padding: 24, padding: 22,
alignItems: "center", alignItems: "center",
backgroundColor: activePreset.qrColor === "#ffffff" ? "#18181b" : activePreset.qrColor, backgroundColor: activePreset.plaqueColor,
borderWidth: 2, borderWidth: 2,
borderColor: activePreset.borderAccent, borderColor: "rgba(0,0,0,0.25)",
position: "relative", position: "relative",
shadowColor: "#000",
shadowOffset: { width: 0, height: 15 },
shadowOpacity: 0.4,
shadowRadius: 30,
}} }}
> >
{/* Top Restaurant Badge */} {/* Inner Border Accent Line */}
<View <View
style={{ style={{
borderWidth: 1.5, position: "absolute",
top: 10,
left: 10,
right: 10,
bottom: 10,
borderRadius: 28,
borderWidth: 1,
borderColor: activePreset.borderAccent,
pointerEvents: "none",
}}
/>
{/* Top Restaurant Badge with Flame Icon */}
<View
style={{
borderWidth: 2,
borderColor: activePreset.logoBorder, borderColor: activePreset.logoBorder,
backgroundColor: activePreset.logoBg, backgroundColor: activePreset.logoBg,
paddingHorizontal: 14, paddingHorizontal: 16,
paddingVertical: 6, paddingVertical: 6,
borderRadius: 10, borderRadius: 12,
marginBottom: 14, marginBottom: 16,
alignItems: "center", alignItems: "center",
flexDirection: "row",
gap: 6,
}} }}
> >
<Text <Text
style={{ style={{
color: activePreset.accentText, color: activePreset.accentText,
fontSize: 15, fontSize: 16,
fontWeight: "900", fontWeight: "900",
letterSpacing: 1, letterSpacing: 1.5,
textAlign: "center", textAlign: "center",
}} }}
> >
{restaurantName.toUpperCase()} {restaurantName.toUpperCase()}
</Text> </Text>
<Ionicons name="flame" size={16} color={activePreset.accentText} />
</View> </View>
{/* Title Callout */} {/* Title Callout */}
<Text <Text
style={{ style={{
color: activePreset.headerText, color: activePreset.headerText,
fontSize: 18, fontSize: 22,
fontWeight: "900", fontWeight: "900",
textAlign: "center", textAlign: "center",
lineHeight: 22, lineHeight: 25,
marginBottom: 16, marginBottom: 18,
letterSpacing: 0.5,
}} }}
> >
DİJİTAL MENÜ{"\n"} DIGITAL MENU{"\n"}
<Text style={{ letterSpacing: 1 }}>KAMERANIZLA TARATIN</Text> <Text style={{ letterSpacing: 1.5 }}>SCAN TO ORDER</Text>
</Text> </Text>
{/* Auto Placed QR Code Container */} {/* Auto Placed QR Code Container */}
<View <View
style={{ style={{
backgroundColor: activePreset.qrBg, backgroundColor: activePreset.qrBg,
padding: 12, padding: 14,
borderRadius: 20, borderRadius: 24,
marginBottom: 14, marginBottom: 14,
shadowColor: "#000", shadowColor: "#000",
shadowOffset: { width: 0, height: 4 }, shadowOffset: { width: 0, height: 6 },
shadowOpacity: 0.15, shadowOpacity: 0.2,
shadowRadius: 8, shadowRadius: 12,
elevation: 4, elevation: 5,
borderWidth: 1,
borderColor: "rgba(0,0,0,0.1)",
}} }}
> >
<Image <Image
source={{ uri: `data:image/png;base64,${qr.pngBase64}` }} source={{ uri: `data:image/png;base64,${qr.pngBase64}` }}
style={{ width: 170, height: 170, borderRadius: 8 }} style={{ width: 175, height: 175, borderRadius: 10 }}
resizeMode="contain" resizeMode="contain"
/> />
</View> </View>
@@ -413,42 +452,56 @@ export default function QrScreen() {
<Text <Text
style={{ style={{
color: activePreset.subText, color: activePreset.subText,
fontSize: 11, fontSize: 12,
fontWeight: "600", fontWeight: "600",
marginBottom: 14, marginBottom: 16,
textAlign: "center", textAlign: "center",
}} }}
> >
Telefonunuzun kamerasını doğrultun Use your phone's camera
</Text> </Text>
{/* Category Quick Preview List */} {/* Category Quick Preview List Flanked by Icons */}
<View style={{ width: "85%", alignItems: "center", gap: 3 }}> <View style={{ width: "100%", position: "relative", alignItems: "center" }}>
{displayCategories.map((cat, idx) => ( {/* Floating Left Line Icon (Burger) */}
<View key={idx} style={{ width: "100%", alignItems: "center" }}> <View style={{ position: "absolute", left: 0, bottom: 8, opacity: 0.9 }}>
<Text <Ionicons name="fast-food-outline" size={26} color={activePreset.accentText} />
style={{ </View>
color: activePreset.headerText,
fontSize: 12, {/* Floating Right Line Icon (Drink) */}
fontWeight: "800", <View style={{ position: "absolute", right: 0, bottom: 8, opacity: 0.9 }}>
letterSpacing: 1.5, <Ionicons name="wine-outline" size={26} color={activePreset.accentText} />
textTransform: "uppercase", </View>
}}
> {/* Center Category Stack */}
{cat} <View style={{ width: "68%", alignItems: "center", gap: 2 }}>
</Text> {displayCategories.map((cat, idx) => (
{idx < displayCategories.length - 1 && ( <View key={idx} style={{ width: "100%", alignItems: "center" }}>
<View <Text
style={{ style={{
width: "100%", color: activePreset.headerText,
height: 1, fontSize: 13,
backgroundColor: activePreset.dividerColor, fontWeight: "900",
marginVertical: 2, letterSpacing: 2,
textTransform: "uppercase",
paddingVertical: 2,
}} }}
/> >
)} {cat}
</View> </Text>
))} {idx < displayCategories.length - 1 && (
<View
style={{
width: "100%",
height: 1,
backgroundColor: activePreset.dividerColor,
marginVertical: 2,
}}
/>
)}
</View>
))}
</View>
</View> </View>
</View> </View>
</ViewShot> </ViewShot>
+6
View File
@@ -4,6 +4,7 @@ export interface QrStandPreset {
categoryName: string; categoryName: string;
bgGradient: string; bgGradient: string;
plaqueBg: string; plaqueBg: string;
plaqueColor: string;
borderAccent: string; borderAccent: string;
qrBg: string; qrBg: string;
qrColor: string; qrColor: string;
@@ -23,6 +24,7 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
categoryName: "Burger & Fast Casual", categoryName: "Burger & Fast Casual",
bgGradient: "radial-gradient(circle at 50% 50%, #1a3a29 0%, #0e2016 100%)", bgGradient: "radial-gradient(circle at 50% 50%, #1a3a29 0%, #0e2016 100%)",
plaqueBg: "linear-gradient(135deg, #a64d2a 0%, #8e3f20 100%)", plaqueBg: "linear-gradient(135deg, #a64d2a 0%, #8e3f20 100%)",
plaqueColor: "#a64d2a",
borderAccent: "rgba(255, 255, 255, 0.15)", borderAccent: "rgba(255, 255, 255, 0.15)",
qrBg: "#f8f3eb", qrBg: "#f8f3eb",
qrColor: "#a64d2a", qrColor: "#a64d2a",
@@ -40,6 +42,7 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
categoryName: "Fine Dining & Steakhouse", categoryName: "Fine Dining & Steakhouse",
bgGradient: "radial-gradient(circle at 50% 50%, #18181b 0%, #09090b 100%)", bgGradient: "radial-gradient(circle at 50% 50%, #18181b 0%, #09090b 100%)",
plaqueBg: "linear-gradient(135deg, #1c1917 0%, #0c0a09 100%)", plaqueBg: "linear-gradient(135deg, #1c1917 0%, #0c0a09 100%)",
plaqueColor: "#1c1917",
borderAccent: "rgba(200, 169, 107, 0.4)", borderAccent: "rgba(200, 169, 107, 0.4)",
qrBg: "#faf8f5", qrBg: "#faf8f5",
qrColor: "#1c1917", qrColor: "#1c1917",
@@ -57,6 +60,7 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
categoryName: "Kafe, Fırın & Kahve", categoryName: "Kafe, Fırın & Kahve",
bgGradient: "radial-gradient(circle at 50% 50%, #e7e5e4 0%, #d6d3d1 100%)", bgGradient: "radial-gradient(circle at 50% 50%, #e7e5e4 0%, #d6d3d1 100%)",
plaqueBg: "linear-gradient(135deg, #ffffff 0%, #f5f5f4 100%)", plaqueBg: "linear-gradient(135deg, #ffffff 0%, #f5f5f4 100%)",
plaqueColor: "#ffffff",
borderAccent: "rgba(0, 0, 0, 0.08)", borderAccent: "rgba(0, 0, 0, 0.08)",
qrBg: "#18181b", qrBg: "#18181b",
qrColor: "#ffffff", qrColor: "#ffffff",
@@ -74,6 +78,7 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
categoryName: "Pizzeria & İtalyan", categoryName: "Pizzeria & İtalyan",
bgGradient: "radial-gradient(circle at 50% 50%, #3f1212 0%, #240a0a 100%)", bgGradient: "radial-gradient(circle at 50% 50%, #3f1212 0%, #240a0a 100%)",
plaqueBg: "linear-gradient(135deg, #8b1e1e 0%, #6e1616 100%)", plaqueBg: "linear-gradient(135deg, #8b1e1e 0%, #6e1616 100%)",
plaqueColor: "#8b1e1e",
borderAccent: "rgba(255, 235, 200, 0.2)", borderAccent: "rgba(255, 235, 200, 0.2)",
qrBg: "#fffbeb", qrBg: "#fffbeb",
qrColor: "#8b1e1e", qrColor: "#8b1e1e",
@@ -91,6 +96,7 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
categoryName: "Bar, Pub & Nightlife", categoryName: "Bar, Pub & Nightlife",
bgGradient: "radial-gradient(circle at 50% 50%, #0f172a 0%, #020617 100%)", bgGradient: "radial-gradient(circle at 50% 50%, #0f172a 0%, #020617 100%)",
plaqueBg: "linear-gradient(135deg, #1e1b4b 0%, #0f172a 100%)", plaqueBg: "linear-gradient(135deg, #1e1b4b 0%, #0f172a 100%)",
plaqueColor: "#1e1b4b",
borderAccent: "rgba(99, 102, 241, 0.4)", borderAccent: "rgba(99, 102, 241, 0.4)",
qrBg: "#e0e7ff", qrBg: "#e0e7ff",
qrColor: "#312e81", qrColor: "#312e81",