From 2c3599839ba7f17fbab5416fb21b408fd51a9079 Mon Sep 17 00:00:00 2001 From: AyrisAI Date: Fri, 21 Aug 2026 01:00:29 +0300 Subject: [PATCH] feat(mobile): add QrTemplate6 Stonehearth Grill, modal text editor and vertical positioning (offsetY) controls --- apps/mobile/src/app/menu/qr.tsx | 611 ++++++++++++------ .../components/qr-templates/QrTemplate2.tsx | 6 + .../components/qr-templates/QrTemplate3.tsx | 7 + .../components/qr-templates/QrTemplate4.tsx | 7 + .../components/qr-templates/QrTemplate5.tsx | 6 + .../components/qr-templates/QrTemplate6.tsx | 260 ++++++++ .../qr-templates/QrTemplateLumiere.tsx | 6 + .../src/components/qr-templates/index.tsx | 11 +- apps/mobile/svgcomponentlist/6.tsx | 327 ++++++++++ packages/shared/src/qr-stands.ts | 15 + 10 files changed, 1070 insertions(+), 186 deletions(-) create mode 100644 apps/mobile/src/components/qr-templates/QrTemplate6.tsx create mode 100644 apps/mobile/svgcomponentlist/6.tsx diff --git a/apps/mobile/src/app/menu/qr.tsx b/apps/mobile/src/app/menu/qr.tsx index 08183d4..235aa40 100644 --- a/apps/mobile/src/app/menu/qr.tsx +++ b/apps/mobile/src/app/menu/qr.tsx @@ -3,6 +3,7 @@ import { router } from "expo-router"; import { ActivityIndicator, Alert, + Modal, Pressable, ScrollView, Share, @@ -12,6 +13,7 @@ import { View, } from "react-native"; import { Ionicons } from "@expo/vector-icons"; + import * as Clipboard from "expo-clipboard"; import ViewShot from "react-native-view-shot"; import * as Sharing from "expo-sharing"; @@ -91,6 +93,24 @@ export default function QrScreen() { }, }); + const [selectedEditField, setSelectedEditField] = useState(null); + + const FIELD_DEFINITIONS: Array<{ + key: keyof QrCustomFields; + label: string; + placeholder: string; + min: number; + max: number; + icon: string; + }> = [ + { key: "restaurantName", label: "Restoran Başlığı", placeholder: "Restoran Adı", min: 14, max: 48, icon: "restaurant-outline" }, + { key: "subtitle", label: "Slogan / Alt Başlık", placeholder: "Slogan (Enter ile alt satıra geçebilir)", min: 8, max: 26, icon: "chatbubble-outline" }, + { key: "ctaText", label: "Yönlendirme (CTA) Metni", placeholder: "Örn: MENÜYÜ GÖRMEK İÇİN OKUTUN", min: 9, max: 28, icon: "scan-outline" }, + { key: "tableNo", label: "Masa Numarası", placeholder: "Örn: MASA NO: 01", min: 8, max: 22, icon: "grid-outline" }, + { key: "wifiText", label: "WiFi & Şifre Bilgisi", placeholder: "Örn: WiFi: Bistro | Şifre: 12345", min: 7, max: 20, icon: "wifi-outline" }, + { key: "socialText", label: "Sosyal Medya / İletişim", placeholder: "Örn: @menulio", min: 7, max: 20, icon: "share-social-outline" }, + ]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any const viewShotRef = useRef(null); @@ -107,6 +127,7 @@ export default function QrScreen() { + useEffect(() => { (async () => { const activeRest = await getActiveRestaurant(); @@ -389,7 +410,55 @@ export default function QrScreen() { - {/* Metin & Font & Boyut Özelleştirme Kartı (Açılır/Kapanır) */} + {/* Hızlı Katman Seçici (Tıklayınca Modal Açar) */} + + + DÜZENLEMEK VE KONUMUNU DEĞİŞTİRMEK İÇİN TIKLAYIN: + + + {FIELD_DEFINITIONS.map(({ key, label, icon }) => { + const item = fields[key]; + return ( + setSelectedEditField(key)} + style={({ pressed }) => ({ + backgroundColor: item.visible ? "#242429" : "#1A1A1E", + paddingHorizontal: 12, + paddingVertical: 9, + borderRadius: 12, + borderWidth: 1.5, + borderColor: item.visible ? "rgba(212, 175, 55, 0.4)" : "#2B2B32", + flexDirection: "row", + alignItems: "center", + gap: 6, + opacity: pressed ? 0.8 : 1, + })} + > + + + {label} + + {item.offsetY ? ( + + + {item.offsetY > 0 ? `+${item.offsetY}` : item.offsetY}px + + + ) : null} + + ); + })} + + + + {/* Metin & Font & Boyut Özelleştirme Kartı (Açılır/Kapanır Liste) */} - Metin, Font & Boyut Özelleştir + Tüm Katmanları Listele - Çok satırlı metin (Enter), özel font ve boyut ayarı + Tıkla & Düzenle, dikey konum kaydırma ve font ayarı @@ -438,200 +507,374 @@ export default function QrScreen() { {showCustomizePanel && ( - - {/* Yardımcı Açıklama */} - - - - İpucu: Uzun metinlerde alt satıra geçmek için klavyeden Enter'a basabilirsiniz. - - - - {/* Alan Düzenleyicileri */} - {( - [ - { key: "restaurantName", label: "RESTORAN BAŞLIĞI", placeholder: "Restoran Adı", min: 14, max: 48 }, - { key: "subtitle", label: "SLOGAN / ALT BAŞLIK", placeholder: "Slogan (Enter ile alt satıra geçebilir)", min: 8, max: 24 }, - { key: "ctaText", label: "YÖNLENDİRME (CTA) METNİ", placeholder: "Yönlendirme Metni", min: 9, max: 28 }, - { key: "tableNo", label: "MASA NUMARASI", placeholder: "Örn: MASA NO: 01", min: 8, max: 22 }, - { key: "wifiText", label: "WIFI BİLGİSİ", placeholder: "Örn: WiFi: Bistro | Şifre: 12345", min: 7, max: 20 }, - { key: "socialText", label: "SOSYAL MEDYA / İLETİŞİM", placeholder: "Örn: @menulio", min: 7, max: 20 }, - ] as const - ).map(({ key, label, placeholder, min, max }) => { + + {FIELD_DEFINITIONS.map(({ key, label, icon }) => { const item = fields[key]; return ( - setSelectedEditField(key)} + style={({ pressed }) => ({ + backgroundColor: "#141417", + borderRadius: 14, + padding: 12, + borderWidth: 1, + borderColor: "rgba(255,255,255,0.06)", + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + opacity: pressed ? 0.8 : 1, + })} > - {/* Başlık ve Görünürlük Anahtarı */} - - {label} - - - {item.visible ? "Açık" : "Gizli"} + + + + + {label} + + + {item.text || "Boş"} - - setFields((prev) => ({ - ...prev, - [key]: { ...prev[key], visible: v }, - })) - } - trackColor={{ false: "#27272A", true: "#D4AF37" }} - thumbColor="#FFFFFF" - ios_backgroundColor="#27272A" - /> - - {item.visible && ( - <> - {/* Çok Satırlı TextInput */} - - setFields((prev) => ({ - ...prev, - [key]: { ...prev[key], text: t }, - })) - } - placeholder={placeholder} - placeholderTextColor="#71717A" - multiline - style={{ - backgroundColor: "#121215", - borderWidth: 1, - borderColor: "rgba(255,255,255,0.1)", - borderRadius: 12, - paddingHorizontal: 12, - paddingVertical: 8, - color: "#FFFFFF", - fontSize: 13, - minHeight: 42, - }} - /> - - {/* Font ve Boyut Kontrol Çubuğu */} - - {/* Font Hapları */} - - {FONT_OPTIONS.map((f) => { - const isSelected = item.fontId === f.id; - return ( - - setFields((prev) => ({ - ...prev, - [key]: { ...prev[key], fontId: f.id }, - })) - } - style={{ - backgroundColor: isSelected ? "#D4AF37" : "#242429", - paddingHorizontal: 9, - paddingVertical: 5, - borderRadius: 8, - borderWidth: 1, - borderColor: isSelected ? "#D4AF37" : "rgba(255,255,255,0.06)", - }} - > - - {f.label} - - - ); - })} - - - {/* Boyut Artır / Azalt */} - - - setFields((prev) => ({ - ...prev, - [key]: { - ...prev[key], - fontSize: Math.max(min, (prev[key].fontSize || 12) - 1), - }, - })) - } - style={{ padding: 6 }} - > - - - - {item.fontSize || 12} - - - setFields((prev) => ({ - ...prev, - [key]: { - ...prev[key], - fontSize: Math.min(max, (prev[key].fontSize || 12) + 1), - }, - })) - } - style={{ padding: 6 }} - > - - - - - - )} - + + + + {item.fontSize || 12}px + + + + + ); })} )} + {/* Modal: Tıklanan Metin Katmanını Detaylı Düzenleme */} + setSelectedEditField(null)} + > + + {selectedEditField && (() => { + const def = FIELD_DEFINITIONS.find((d) => d.key === selectedEditField)!; + const item = fields[selectedEditField]; + + return ( + + {/* Modal Başlığı & Kapat Butonu */} + + + + + {def.label} + + + setSelectedEditField(null)} + style={{ + width: 32, + height: 32, + borderRadius: 16, + backgroundColor: "#242429", + alignItems: "center", + justifyContent: "center", + }} + > + + + + + + {/* Görünürlük Anahtarı */} + + + Stantta Göster + + + setFields((prev) => ({ + ...prev, + [selectedEditField]: { ...prev[selectedEditField], visible: v }, + })) + } + trackColor={{ false: "#27272A", true: "#D4AF37" }} + thumbColor="#FFFFFF" + ios_backgroundColor="#27272A" + /> + + + {/* Metin Girişi (Multiline / Enter Destekli) */} + + + METİN İÇERİĞİ (ENTER İLE YENİ SATIR) + + + setFields((prev) => ({ + ...prev, + [selectedEditField]: { ...prev[selectedEditField], text: t }, + })) + } + placeholder={def.placeholder} + placeholderTextColor="#71717A" + multiline + style={{ + backgroundColor: "#121215", + borderWidth: 1, + borderColor: "rgba(255,255,255,0.12)", + borderRadius: 14, + paddingHorizontal: 14, + paddingVertical: 12, + color: "#FFFFFF", + fontSize: 14, + minHeight: 60, + }} + /> + + + {/* DİKEY KONUM / YUKARI - AŞAĞI TAŞIMA (offsetY) */} + + + + DİKEY KONUM (YUKARI / AŞAĞI) + + + {item.offsetY ? `${item.offsetY > 0 ? "+" : ""}${item.offsetY} px` : "Orijinal (0px)"} + + + + + setFields((prev) => ({ + ...prev, + [selectedEditField]: { + ...prev[selectedEditField], + offsetY: (prev[selectedEditField].offsetY || 0) - 3, + }, + })) + } + style={{ + flex: 1, + backgroundColor: "#242429", + paddingVertical: 10, + borderRadius: 10, + alignItems: "center", + flexDirection: "row", + justifyContent: "center", + gap: 4, + }} + > + + Yukarı (-3px) + + + + setFields((prev) => ({ + ...prev, + [selectedEditField]: { + ...prev[selectedEditField], + offsetY: (prev[selectedEditField].offsetY || 0) + 3, + }, + })) + } + style={{ + flex: 1, + backgroundColor: "#242429", + paddingVertical: 10, + borderRadius: 10, + alignItems: "center", + flexDirection: "row", + justifyContent: "center", + gap: 4, + }} + > + + Aşağı (+3px) + + + {item.offsetY ? ( + + setFields((prev) => ({ + ...prev, + [selectedEditField]: { + ...prev[selectedEditField], + offsetY: 0, + }, + })) + } + style={{ + backgroundColor: "#3A2A2A", + paddingHorizontal: 12, + paddingVertical: 10, + borderRadius: 10, + alignItems: "center", + justifyContent: "center", + }} + > + Sıfırla + + ) : null} + + + + {/* YAZI TİPİ (FONT) SEÇİMİ */} + + + YAZI TİPİ (TÜRKÇE DESTEKLİ FONT) + + + {FONT_OPTIONS.map((f) => { + const isSelected = item.fontId === f.id; + return ( + + setFields((prev) => ({ + ...prev, + [selectedEditField]: { ...prev[selectedEditField], fontId: f.id }, + })) + } + style={{ + backgroundColor: isSelected ? "#D4AF37" : "#242429", + paddingHorizontal: 14, + paddingVertical: 10, + borderRadius: 12, + borderWidth: 1.5, + borderColor: isSelected ? "#D4AF37" : "rgba(255,255,255,0.06)", + }} + > + + {f.label} + + + ); + })} + + + + {/* YAZI BOYUTU (FONT SIZE) */} + + + YAZI BOYUTU + + + + setFields((prev) => ({ + ...prev, + [selectedEditField]: { + ...prev[selectedEditField], + fontSize: Math.max(def.min, (prev[selectedEditField].fontSize || 12) - 1), + }, + })) + } + style={{ + backgroundColor: "#242429", + paddingHorizontal: 16, + paddingVertical: 10, + borderRadius: 10, + }} + > + + + + + {item.fontSize || 12} px + + + + setFields((prev) => ({ + ...prev, + [selectedEditField]: { + ...prev[selectedEditField], + fontSize: Math.min(def.max, (prev[selectedEditField].fontSize || 12) + 1), + }, + })) + } + style={{ + backgroundColor: "#242429", + paddingHorizontal: 16, + paddingVertical: 10, + borderRadius: 10, + }} + > + + + + + + {/* Kaydet & Tamamla Butonu */} + setSelectedEditField(null)} + style={{ + backgroundColor: "#D4AF37", + borderRadius: 14, + paddingVertical: 14, + alignItems: "center", + marginTop: 6, + }} + > + + Tamamla & Uygula + + + + + ); + })()} + + + + {/* Output Action Buttons */} diff --git a/apps/mobile/src/components/qr-templates/QrTemplate2.tsx b/apps/mobile/src/components/qr-templates/QrTemplate2.tsx index 1ec6152..9bf361c 100644 --- a/apps/mobile/src/components/qr-templates/QrTemplate2.tsx +++ b/apps/mobile/src/components/qr-templates/QrTemplate2.tsx @@ -223,6 +223,7 @@ const QrTemplate2 = ({ text: rName?.text || "The Daily Grind", x: 200, y: 110, + offsetY: rName?.offsetY, fill: "#2c1a0e", fontFamily: rNameFont, fontSize: rName?.fontSize || 28, @@ -236,6 +237,7 @@ const QrTemplate2 = ({ text: sub?.text || "COFFEE & KITCHEN", x: 200, y: 144, + offsetY: sub?.offsetY, fill: "#3a2312", fontFamily: subFont, fontSize: sub?.fontSize || 12, @@ -249,6 +251,7 @@ const QrTemplate2 = ({ text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN", x: 200, y: 486, + offsetY: cta?.offsetY, fill: "#2c1a0e", fontFamily: ctaFont, fontSize: cta?.fontSize || 15, @@ -262,6 +265,7 @@ const QrTemplate2 = ({ text: wifi?.text || "WiFi: DailyGrind | Şifre: coffee2026", x: 200, y: 520, + offsetY: wifi?.offsetY, fill: "#3a2312", fontFamily: infoFont, fontSize: wifi?.fontSize || 10.5, @@ -280,6 +284,7 @@ const QrTemplate2 = ({ .join(" • "), x: 200, y: 544, + offsetY: table?.offsetY || social?.offsetY, fill: "#8c6239", fontFamily: infoFont, fontSize: table?.fontSize || social?.fontSize || 10, @@ -287,6 +292,7 @@ const QrTemplate2 = ({ letterSpacing: 1, })} + ); diff --git a/apps/mobile/src/components/qr-templates/QrTemplate4.tsx b/apps/mobile/src/components/qr-templates/QrTemplate4.tsx index 67e7337..48702d9 100644 --- a/apps/mobile/src/components/qr-templates/QrTemplate4.tsx +++ b/apps/mobile/src/components/qr-templates/QrTemplate4.tsx @@ -191,6 +191,7 @@ const QrTemplate4 = ({ text: table?.text || "MASA NO: 01", x: 200, y: 56, + offsetY: table?.offsetY, fill: "#c59b4b", fontFamily: infoFont, fontSize: table?.fontSize || 11, @@ -204,6 +205,7 @@ const QrTemplate4 = ({ text: rName?.text || "THE CRIMSON PETAL", x: 200, y: 114, + offsetY: rName?.offsetY, fill: "#111111", fontFamily: rNameFont, fontSize: rName?.fontSize || 24, @@ -217,6 +219,7 @@ const QrTemplate4 = ({ text: sub?.text || "FINE DINING EXPERIENCE", x: 200, y: 154, + offsetY: sub?.offsetY, fill: "#71717a", fontFamily: subFont, fontSize: sub?.fontSize || 11, @@ -230,6 +233,7 @@ const QrTemplate4 = ({ text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN", x: 200, y: 440, + offsetY: cta?.offsetY, fill: "#111111", fontFamily: ctaFont, fontSize: cta?.fontSize || 15, @@ -243,6 +247,7 @@ const QrTemplate4 = ({ text: wifi?.text || "WiFi: CrimsonGuest | Şifre: petal2026", x: 200, y: 488, + offsetY: wifi?.offsetY, fill: "#71717a", fontFamily: infoFont, fontSize: wifi?.fontSize || 10.5, @@ -256,6 +261,7 @@ const QrTemplate4 = ({ text: social?.text || "@thecrimsonpetal", x: 200, y: 514, + offsetY: social?.offsetY, fill: "#c59b4b", fontFamily: infoFont, fontSize: social?.fontSize || 10.5, @@ -263,6 +269,7 @@ const QrTemplate4 = ({ letterSpacing: 0.5, })} + ); }; diff --git a/apps/mobile/src/components/qr-templates/QrTemplate5.tsx b/apps/mobile/src/components/qr-templates/QrTemplate5.tsx index eb181c9..5ed4a74 100644 --- a/apps/mobile/src/components/qr-templates/QrTemplate5.tsx +++ b/apps/mobile/src/components/qr-templates/QrTemplate5.tsx @@ -100,6 +100,7 @@ const QrTemplate5 = ({ text: table?.text || "MASA NO: 01", x: 190, y: 40, + offsetY: table?.offsetY, fill: "#003554", fontFamily: infoFont, fontSize: table?.fontSize || 11, @@ -113,6 +114,7 @@ const QrTemplate5 = ({ text: rName?.text || "THE URBAN FORK", x: 190, y: 86, + offsetY: rName?.offsetY, fill: "#003554", fontFamily: rNameFont, fontSize: rName?.fontSize || 24, @@ -126,6 +128,7 @@ const QrTemplate5 = ({ text: cta?.text || "DİJİTAL MENÜYÜ GÖRÜNTÜLE", x: 190, y: 160, + offsetY: cta?.offsetY, fill: "#003554", fontFamily: ctaFont, fontSize: cta?.fontSize || 13, @@ -139,6 +142,7 @@ const QrTemplate5 = ({ text: sub?.text || "FRESH FLAVORS DAILY", x: 190, y: 402, + offsetY: sub?.offsetY, fill: "#003554", fontFamily: subFont, fontSize: sub?.fontSize || 13, @@ -152,6 +156,7 @@ const QrTemplate5 = ({ text: wifi?.text || "WiFi: UrbanFork | Şifre: urban2026", x: 190, y: 430, + offsetY: wifi?.offsetY, fill: "#003554", fontFamily: infoFont, fontSize: wifi?.fontSize || 10, @@ -165,6 +170,7 @@ const QrTemplate5 = ({ text: social?.text || "@theurbanfork", x: 190, y: 454, + offsetY: social?.offsetY, fill: "#003554", fontFamily: infoFont, fontSize: social?.fontSize || 10, diff --git a/apps/mobile/src/components/qr-templates/QrTemplate6.tsx b/apps/mobile/src/components/qr-templates/QrTemplate6.tsx new file mode 100644 index 0000000..bd9e17b --- /dev/null +++ b/apps/mobile/src/components/qr-templates/QrTemplate6.tsx @@ -0,0 +1,260 @@ +import * as React from "react"; +import Svg, { + Defs, + G, + Path, + Circle, + Rect, + Use, + Text, + Image, +} from "react-native-svg"; +import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./index"; + +const QrTemplate6 = ({ + qrBase64, + fields, + ...props +}: QrTemplateProps) => { + const rName = fields?.restaurantName; + const sub = fields?.subtitle; + const table = fields?.tableNo; + const cta = fields?.ctaText; + const wifi = fields?.wifiText; + const social = fields?.socialText; + + const rNameFont = getFontFamily(rName?.fontId, "Georgia, 'Times New Roman', serif"); + const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif"); + const ctaFont = getFontFamily(cta?.fontId, "Georgia, 'Times New Roman', serif"); + const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif"); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* QR Placeholder / Gerçek QR */} + + + + {qrBase64 ? ( + + ) : ( + <> + + + + + {"QR KOD ALANI"} + + + )} + + + {/* Metinler */} + + {/* Masa Numarası */} + {table?.visible !== false && + renderSvgLines({ + text: table?.text || "MASA NO: 01", + x: 200, + y: 36, + offsetY: table?.offsetY, + fill: "#122a42", + fontFamily: infoFont, + fontSize: table?.fontSize || 10, + fontWeight: "bold", + letterSpacing: 1.5, + })} + + {/* Restoran Başlığı */} + {rName?.visible !== false && + renderSvgLines({ + text: rName?.text || "THE STONEHEARTH GRILL", + x: 200, + y: 84, + offsetY: rName?.offsetY, + fill: "#a64423", + fontFamily: rNameFont, + fontSize: rName?.fontSize || 19, + fontWeight: "bold", + letterSpacing: 1.5, + })} + + {/* Slogan */} + {sub?.visible !== false && + renderSvgLines({ + text: sub?.text || "SCAN. ORDER. ENJOY.", + x: 200, + y: 118, + offsetY: sub?.offsetY, + fill: "#122a42", + fontFamily: subFont, + fontSize: sub?.fontSize || 10, + fontWeight: "bold", + letterSpacing: 2, + })} + + {/* CTA */} + {cta?.visible !== false && + renderSvgLines({ + text: cta?.text || "VIEW OUR MENU & ORDER ONLINE", + x: 200, + y: 160, + offsetY: cta?.offsetY, + fill: "#122a42", + fontFamily: ctaFont, + fontSize: cta?.fontSize || 13, + fontWeight: "bold", + letterSpacing: 0.5, + })} + + {/* WiFi */} + {wifi?.visible !== false && + renderSvgLines({ + text: wifi?.text || "FREE WIFI: StonehearthGuest | PW: Grill720", + x: 200, + y: 524, + offsetY: wifi?.offsetY, + fill: "#222", + fontFamily: infoFont, + fontSize: wifi?.fontSize || 9, + fontWeight: "bold", + letterSpacing: 0.2, + })} + + {/* Sosyal Medya */} + {social?.visible !== false && + renderSvgLines({ + text: social?.text || "Follow Us @StonehearthGrill", + x: 200, + y: 540, + offsetY: social?.offsetY, + fill: "#222", + fontFamily: infoFont, + fontSize: social?.fontSize || 9, + fontWeight: "bold", + letterSpacing: 0.2, + })} + + + ); +}; + +export default QrTemplate6; diff --git a/apps/mobile/src/components/qr-templates/QrTemplateLumiere.tsx b/apps/mobile/src/components/qr-templates/QrTemplateLumiere.tsx index 095317f..74d3246 100644 --- a/apps/mobile/src/components/qr-templates/QrTemplateLumiere.tsx +++ b/apps/mobile/src/components/qr-templates/QrTemplateLumiere.tsx @@ -137,6 +137,7 @@ const QrTemplateLumiere = ({ text: rName?.text || "LUMIÈRE", x: 300, y: 148, + offsetY: rName?.offsetY, fill: "#d4af37", fontFamily: rNameFont, fontSize: rName?.fontSize || 30, @@ -150,6 +151,7 @@ const QrTemplateLumiere = ({ text: sub?.text || "RESTAURANT & BAR | MODERN CUISINE", x: 300, y: 178, + offsetY: sub?.offsetY, fill: "#e5c378", fontFamily: subFont, fontSize: sub?.fontSize || 11, @@ -163,6 +165,7 @@ const QrTemplateLumiere = ({ text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN", x: 300, y: 472, + offsetY: cta?.offsetY, fill: "#d4af37", fontFamily: ctaFont, fontSize: cta?.fontSize || 13, @@ -176,6 +179,7 @@ const QrTemplateLumiere = ({ text: social?.text || "Rezervasyon: 0212 555 0199", x: 300, y: 504, + offsetY: social?.offsetY, fill: "#e2ddd2", fontFamily: infoFont, fontSize: social?.fontSize || 10.5, @@ -194,12 +198,14 @@ const QrTemplateLumiere = ({ .join(" • "), x: 300, y: 526, + offsetY: table?.offsetY || wifi?.offsetY, fill: "#c5a059", fontFamily: infoFont, fontSize: table?.fontSize || wifi?.fontSize || 10, fontWeight: 600, letterSpacing: 1, })} + ); diff --git a/apps/mobile/src/components/qr-templates/index.tsx b/apps/mobile/src/components/qr-templates/index.tsx index 72e6713..65c4956 100644 --- a/apps/mobile/src/components/qr-templates/index.tsx +++ b/apps/mobile/src/components/qr-templates/index.tsx @@ -5,11 +5,13 @@ import QrTemplate2 from "./QrTemplate2"; import QrTemplate3 from "./QrTemplate3"; import QrTemplate4 from "./QrTemplate4"; import QrTemplate5 from "./QrTemplate5"; +import QrTemplate6 from "./QrTemplate6"; export interface TextItemConfig { text: string; fontSize?: number; fontId?: string; + offsetY?: number; // Dikey konum kaydırma (yukarı / aşağı) visible?: boolean; } @@ -62,11 +64,12 @@ export function getFontFamily(fontId?: string, defaultFamily = "Georgia, serif") return found ? found.fontFamily : defaultFamily; } -// Çok satırlı (Enter / \n) metinleri SVG içinde alt alta kusursuz render eden yardımcı fonksiyon +// Çok satırlı (Enter / \n) ve dikey kaydırmalı (offsetY) SVG metin render fonksiyonu export function renderSvgLines({ text, x, y, + offsetY = 0, lineHeight, fill, fontFamily, @@ -78,6 +81,7 @@ export function renderSvgLines({ text?: string; x: number; y: number; + offsetY?: number; lineHeight?: number; fill?: string; fontFamily?: string; @@ -89,6 +93,7 @@ export function renderSvgLines({ if (!text) return null; const lines = text.split("\n"); const computedLineHeight = lineHeight || (fontSize ? fontSize * 1.25 : 18); + const startY = y + (offsetY || 0); return ( <> @@ -96,7 +101,7 @@ export function renderSvgLines({ = { "qr-3": 400 / 650, // 400x650 = standart kart "qr-4": 400 / 580, // 400x580 = zarif altın çerçeve "qr-5": 380 / 500, // 380x500 = PNG arka planlı modern kart + "qr-6": 400 / 580, // 400x580 = taş & ızgara vintage stant }; diff --git a/apps/mobile/svgcomponentlist/6.tsx b/apps/mobile/svgcomponentlist/6.tsx new file mode 100644 index 0000000..a6d4be8 --- /dev/null +++ b/apps/mobile/svgcomponentlist/6.tsx @@ -0,0 +1,327 @@ +import Svg, { + SvgProps, + Defs, + G, + Path, + Circle, + Rect, + Use, + Text, +} from "react-native-svg" +import { memo } from "react" +const SvgComponent = (props: SvgProps) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {"QR KOD ALANI"} + + + + {"EST."} + + + {"2018"} + + + {"THE STONEHEARTH GRILL"} + + + {"EST. 2018"} + + + {"SCAN. ORDER. ENJOY."} + + + {"VIEW OUR MENU & ORDER ONLINE"} + + + {"Simply scan the QR code with your phone camera to access our"} + + + {"digital menu, daily specials, wine list, and place your order."} + + + {"NO APP"} + + + {"REQUIRED"} + + + {"WWW.THESTONEHEARTHGRILL.COM"} + + + {"FREE WIFI: StonehearthGuest (PW: Grill720)"} + + + {"Follow Us @StonehearthGrill"} + + +) +const Memo = memo(SvgComponent) +export default Memo diff --git a/packages/shared/src/qr-stands.ts b/packages/shared/src/qr-stands.ts index bcf8363..2d32249 100644 --- a/packages/shared/src/qr-stands.ts +++ b/packages/shared/src/qr-stands.ts @@ -90,4 +90,19 @@ export const QR_STAND_PRESETS: Record = { subText: "#0284c7", dividerColor: "rgba(0, 53, 84, 0.3)", }, + "qr-6": { + key: "qr-6", + name: "The Stonehearth Grill (QR-6)", + categoryName: "Özel Masa Kartı", + svgTemplateUrl: "/qr-6.svg", + aspectRatio: "400/580", + bgGradient: "radial-gradient(circle at 50% 50%, #434b54 0%, #1a2228 100%)", + plaqueColor: "#f6efe3", + borderAccent: "#c59b58", + accentText: "#a64423", + headerText: "#122a42", + subText: "#c59b58", + dividerColor: "rgba(197, 155, 88, 0.3)", + }, }; +