feat(mobile): add multiline text with Enter, per-text independent font selection and font size controls

This commit is contained in:
AyrisAI
2026-08-21 00:55:44 +03:00
parent b72846bd91
commit 2530ab4005
8 changed files with 1522 additions and 1608 deletions
+241 -288
View File
@@ -23,7 +23,8 @@ import {
COMPONENT_TEMPLATES, COMPONENT_TEMPLATES,
TEMPLATE_ASPECT_RATIOS, TEMPLATE_ASPECT_RATIOS,
FONT_OPTIONS, FONT_OPTIONS,
type QrCustomTextConfig, type QrCustomFields,
type TextItemConfig,
} from "@/components/qr-templates"; } from "@/components/qr-templates";
@@ -45,24 +46,49 @@ export default function QrScreen() {
(p) => !!COMPONENT_TEMPLATES[p.key] (p) => !!COMPONENT_TEMPLATES[p.key]
); );
const [selectedKey, setSelectedKey] = useState<string>( const [selectedKey, setSelectedKey] = useState<string>(
availablePresets[0]?.key || "qr-lumiere" availablePresets[0]?.key || "qr-1"
); );
const [exportingPng, setExportingPng] = useState(false); const [exportingPng, setExportingPng] = useState(false);
const [exportingPdf, setExportingPdf] = useState(false); const [exportingPdf, setExportingPdf] = useState(false);
const [showCustomizePanel, setShowCustomizePanel] = useState(false); const [showCustomizePanel, setShowCustomizePanel] = useState(false);
const [selectedFontId, setSelectedFontId] = useState<string>("didot");
const [customText, setCustomText] = useState<QrCustomTextConfig>({ const [fields, setFields] = useState<QrCustomFields>({
restaurantName: "Gourmet Bistro", restaurantName: {
subtitle: "RESTAURANT & BAR | MODERN CUISINE", text: "Gourmet Bistro",
tableNo: "MASA NO: 01", fontSize: 26,
ctaText: "MENÜYÜ GÖRMEK İÇİN OKUTUN", fontId: "serif",
wifiText: "WiFi: Gourmet_Guest | Şifre: lezzetli01", visible: true,
socialText: "@gourmetbistro", },
showTableNo: true, subtitle: {
showSubtitle: true, text: "RESTAURANT & BAR\nMODERN CUISINE",
showCta: true, fontSize: 11,
showWifi: true, fontId: "sans",
showSocial: true, visible: true,
},
tableNo: {
text: "MASA NO: 01",
fontSize: 11,
fontId: "sans",
visible: true,
},
ctaText: {
text: "MENÜYÜ GÖRMEK İÇİN OKUTUN",
fontSize: 13,
fontId: "serif",
visible: true,
},
wifiText: {
text: "WiFi: Gourmet_Guest | Şifre: lezzetli01",
fontSize: 10,
fontId: "sans",
visible: true,
},
socialText: {
text: "@gourmetbistro",
fontSize: 10,
fontId: "sans",
visible: true,
},
}); });
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -71,10 +97,6 @@ export default function QrScreen() {
const activePreset: QrStandPreset = const activePreset: QrStandPreset =
QR_STAND_PRESETS[selectedKey] || availablePresets[0] || Object.values(QR_STAND_PRESETS)[0]!; QR_STAND_PRESETS[selectedKey] || availablePresets[0] || Object.values(QR_STAND_PRESETS)[0]!;
const activeFont =
FONT_OPTIONS.find((f) => f.id === selectedFontId)?.fontFamily ||
"'Playfair Display', Georgia, serif";
// Component-based template sistemi // Component-based template sistemi
const TemplateComponent = const TemplateComponent =
COMPONENT_TEMPLATES[selectedKey] ?? Object.values(COMPONENT_TEMPLATES)[0]; COMPONENT_TEMPLATES[selectedKey] ?? Object.values(COMPONENT_TEMPLATES)[0];
@@ -84,6 +106,7 @@ export default function QrScreen() {
useEffect(() => { useEffect(() => {
(async () => { (async () => {
const activeRest = await getActiveRestaurant(); const activeRest = await getActiveRestaurant();
@@ -99,9 +122,12 @@ export default function QrScreen() {
const restDetail = await api.get<{ name?: string }>(`/restaurants/${activeRest.restaurantId}`); const restDetail = await api.get<{ name?: string }>(`/restaurants/${activeRest.restaurantId}`);
if (restDetail?.name) { if (restDetail?.name) {
setRestaurantName(restDetail.name); setRestaurantName(restDetail.name);
setCustomText((prev) => ({ setFields((prev) => ({
...prev, ...prev,
restaurantName: restDetail.name, restaurantName: {
...prev.restaurantName,
text: restDetail.name || "Gourmet Bistro",
},
})); }));
} }
} catch { } catch {
@@ -116,6 +142,7 @@ export default function QrScreen() {
}, []); }, []);
async function handleCopy() { async function handleCopy() {
if (!qr?.targetUrl) return; if (!qr?.targetUrl) return;
await Clipboard.setStringAsync(qr.targetUrl); await Clipboard.setStringAsync(qr.targetUrl);
@@ -355,25 +382,14 @@ export default function QrScreen() {
qrBase64={qr.pngBase64} qrBase64={qr.pngBase64}
width={displayWidth} width={displayWidth}
height={displayHeight} height={displayHeight}
restaurantName={customText.restaurantName} fields={fields}
subtitle={customText.subtitle}
tableNo={customText.tableNo}
ctaText={customText.ctaText}
wifiText={customText.wifiText}
socialText={customText.socialText}
fontFamily={activeFont}
showTableNo={customText.showTableNo}
showSubtitle={customText.showSubtitle}
showCta={customText.showCta}
showWifi={customText.showWifi}
showSocial={customText.showSocial}
/> />
) : null} ) : null}
</View> </View>
</ViewShot> </ViewShot>
</View> </View>
{/* Metin & Font Özelleştirme Kartı (Açılır/Kapanır) */} {/* Metin & Font & Boyut Özelleştirme Kartı (Açılır/Kapanır) */}
<View <View
style={{ style={{
backgroundColor: "#18181B", backgroundColor: "#18181B",
@@ -395,22 +411,22 @@ export default function QrScreen() {
<View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}> <View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
<View <View
style={{ style={{
width: 32, width: 34,
height: 32, height: 34,
borderRadius: 8, borderRadius: 10,
backgroundColor: "rgba(212, 175, 55, 0.15)", backgroundColor: "rgba(212, 175, 55, 0.15)",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
}} }}
> >
<Ionicons name="text-outline" size={18} color="#D4AF37" /> <Ionicons name="text-outline" size={19} color="#D4AF37" />
</View> </View>
<View> <View>
<Text style={{ color: "#FFFFFF", fontSize: 15, fontWeight: "700" }}> <Text style={{ color: "#FFFFFF", fontSize: 15, fontWeight: "700" }}>
Metin ve Yazı Tipi Özelleştir Metin, Font & Boyut Özelleştir
</Text> </Text>
<Text style={{ color: "#A1A1AA", fontSize: 12 }}> <Text style={{ color: "#A1A1AA", fontSize: 12 }}>
İstediğiniz alanları düzenleyin veya kaldırın Çok satırlı metin (Enter), özel font ve boyut ayarı
</Text> </Text>
</View> </View>
</View> </View>
@@ -423,264 +439,201 @@ export default function QrScreen() {
{showCustomizePanel && ( {showCustomizePanel && (
<View style={{ marginTop: 16, gap: 16, borderTopWidth: 1, borderTopColor: "rgba(255,255,255,0.06)", paddingTop: 16 }}> <View style={{ marginTop: 16, gap: 16, borderTopWidth: 1, borderTopColor: "rgba(255,255,255,0.06)", paddingTop: 16 }}>
{/* Yazı Tipi (Font) Seçici */} {/* Yardımcı Açıklama */}
<View> <View
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700", marginBottom: 8, letterSpacing: 0.5 }}> style={{
YAZI TİPİ (FONT) backgroundColor: "rgba(212, 175, 55, 0.08)",
borderRadius: 10,
padding: 10,
flexDirection: "row",
alignItems: "center",
gap: 8,
}}
>
<Ionicons name="information-circle-outline" size={16} color="#D4AF37" />
<Text style={{ color: "#E5C378", fontSize: 11.5, flex: 1 }}>
İpucu: Uzun metinlerde alt satıra geçmek için klavyeden Enter&apos;a basabilirsiniz.
</Text> </Text>
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 8 }}> </View>
{FONT_OPTIONS.map((f) => {
const isSelected = f.id === selectedFontId; {/* Alan Düzenleyicileri */}
return ( {(
<Pressable [
key={f.id} { key: "restaurantName", label: "RESTORAN BAŞLIĞI", placeholder: "Restoran Adı", min: 14, max: 48 },
onPress={() => setSelectedFontId(f.id)} { key: "subtitle", label: "SLOGAN / ALT BAŞLIK", placeholder: "Slogan (Enter ile alt satıra geçebilir)", min: 8, max: 24 },
style={{ { key: "ctaText", label: "YÖNLENDİRME (CTA) METNİ", placeholder: "Yönlendirme Metni", min: 9, max: 28 },
backgroundColor: isSelected ? "#D4AF37" : "#242429", { key: "tableNo", label: "MASA NUMARASI", placeholder: "Örn: MASA NO: 01", min: 8, max: 22 },
paddingHorizontal: 14, { key: "wifiText", label: "WIFI BİLGİSİ", placeholder: "Örn: WiFi: Bistro | Şifre: 12345", min: 7, max: 20 },
paddingVertical: 8, { key: "socialText", label: "SOSYAL MEDYA / İLETİŞİM", placeholder: "Örn: @menulio", min: 7, max: 20 },
borderRadius: 10, ] as const
borderWidth: 1, ).map(({ key, label, placeholder, min, max }) => {
borderColor: isSelected ? "#D4AF37" : "rgba(255,255,255,0.08)", const item = fields[key];
}} return (
> <View
<Text key={key}
style={{ style={{
color: isSelected ? "#0C0C0E" : "#FFFFFF", gap: 8,
fontSize: 12, paddingBottom: 12,
fontWeight: isSelected ? "700" : "500", borderBottomWidth: 1,
}} borderBottomColor: "rgba(255,255,255,0.06)",
> }}
{f.label} >
{/* Başlık ve Görünürlük Anahtarı */}
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>{label}</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
<Text style={{ color: item.visible ? "#10B981" : "#71717A", fontSize: 11, fontWeight: "600" }}>
{item.visible ? "Açık" : "Gizli"}
</Text> </Text>
</Pressable> <Switch
); value={item.visible}
})} onValueChange={(v) =>
</ScrollView> setFields((prev) => ({
</View> ...prev,
[key]: { ...prev[key], visible: v },
}))
}
trackColor={{ false: "#27272A", true: "#D4AF37" }}
thumbColor="#FFFFFF"
ios_backgroundColor="#27272A"
/>
</View>
</View>
{/* Alan 1: Restoran Başlığı */} {item.visible && (
<View style={{ gap: 6 }}> <>
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}> {/* Çok Satırlı TextInput */}
RESTORAN BAŞLIĞI <TextInput
</Text> value={item.text}
<TextInput onChangeText={(t) =>
value={customText.restaurantName} setFields((prev) => ({
onChangeText={(t) => setCustomText((p) => ({ ...p, restaurantName: t }))} ...prev,
placeholder="Restoran Adı" [key]: { ...prev[key], text: t },
placeholderTextColor="#71717A" }))
style={{ }
backgroundColor: "#121215", placeholder={placeholder}
borderWidth: 1, placeholderTextColor="#71717A"
borderColor: "rgba(255,255,255,0.1)", multiline
borderRadius: 12, style={{
paddingHorizontal: 14, backgroundColor: "#121215",
paddingVertical: 10, borderWidth: 1,
color: "#FFFFFF", borderColor: "rgba(255,255,255,0.1)",
fontSize: 14, borderRadius: 12,
}} paddingHorizontal: 12,
/> paddingVertical: 8,
</View> color: "#FFFFFF",
fontSize: 13,
minHeight: 42,
}}
/>
{/* Alan 2: Slogan / Alt Başlık */} {/* Font ve Boyut Kontrol Çubuğu */}
<View style={{ gap: 6 }}> <View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 8 }}>
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}> {/* Font Hapları */}
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}> <ScrollView
SLOGAN / ALT BAŞLIK horizontal
</Text> showsHorizontalScrollIndicator={false}
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}> contentContainerStyle={{ gap: 6 }}
<Text style={{ color: "#71717A", fontSize: 11 }}> style={{ flex: 1 }}
{customText.showSubtitle ? "Göster" : "Gizle"} >
</Text> {FONT_OPTIONS.map((f) => {
<Switch const isSelected = item.fontId === f.id;
value={customText.showSubtitle} return (
onValueChange={(v) => setCustomText((p) => ({ ...p, showSubtitle: v }))} <Pressable
trackColor={{ false: "#27272A", true: "#D4AF37" }} key={f.id}
thumbColor="#FFFFFF" onPress={() =>
ios_backgroundColor="#27272A" 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)",
}}
>
<Text
style={{
color: isSelected ? "#0C0C0E" : "#D4D4D8",
fontSize: 10.5,
fontWeight: isSelected ? "700" : "500",
}}
>
{f.label}
</Text>
</Pressable>
);
})}
</ScrollView>
{/* Boyut Artır / Azalt */}
<View
style={{
flexDirection: "row",
alignItems: "center",
backgroundColor: "#242429",
borderRadius: 8,
paddingHorizontal: 4,
borderWidth: 1,
borderColor: "rgba(255,255,255,0.06)",
}}
>
<Pressable
onPress={() =>
setFields((prev) => ({
...prev,
[key]: {
...prev[key],
fontSize: Math.max(min, (prev[key].fontSize || 12) - 1),
},
}))
}
style={{ padding: 6 }}
>
<Ionicons name="remove" size={13} color="#FFFFFF" />
</Pressable>
<Text
style={{
color: "#D4AF37",
fontSize: 11,
fontWeight: "700",
minWidth: 26,
textAlign: "center",
}}
>
{item.fontSize || 12}
</Text>
<Pressable
onPress={() =>
setFields((prev) => ({
...prev,
[key]: {
...prev[key],
fontSize: Math.min(max, (prev[key].fontSize || 12) + 1),
},
}))
}
style={{ padding: 6 }}
>
<Ionicons name="add" size={13} color="#FFFFFF" />
</Pressable>
</View>
</View>
</>
)}
</View> </View>
</View> );
{customText.showSubtitle && ( })}
<TextInput
value={customText.subtitle}
onChangeText={(t) => setCustomText((p) => ({ ...p, subtitle: t }))}
placeholder="Slogan / Açıklama"
placeholderTextColor="#71717A"
style={{
backgroundColor: "#121215",
borderWidth: 1,
borderColor: "rgba(255,255,255,0.1)",
borderRadius: 12,
paddingHorizontal: 14,
paddingVertical: 10,
color: "#FFFFFF",
fontSize: 14,
}}
/>
)}
</View>
{/* Alan 3: Masa Numarası */}
<View style={{ gap: 6 }}>
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
MASA NUMARASI
</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
<Text style={{ color: "#71717A", fontSize: 11 }}>
{customText.showTableNo ? "Göster" : "Gizle"}
</Text>
<Switch
value={customText.showTableNo}
onValueChange={(v) => setCustomText((p) => ({ ...p, showTableNo: v }))}
trackColor={{ false: "#27272A", true: "#D4AF37" }}
thumbColor="#FFFFFF"
ios_backgroundColor="#27272A"
/>
</View>
</View>
{customText.showTableNo && (
<TextInput
value={customText.tableNo}
onChangeText={(t) => setCustomText((p) => ({ ...p, tableNo: t }))}
placeholder="Örn: MASA NO: 01"
placeholderTextColor="#71717A"
style={{
backgroundColor: "#121215",
borderWidth: 1,
borderColor: "rgba(255,255,255,0.1)",
borderRadius: 12,
paddingHorizontal: 14,
paddingVertical: 10,
color: "#FFFFFF",
fontSize: 14,
}}
/>
)}
</View>
{/* Alan 4: Yönlendirme (CTA) */}
<View style={{ gap: 6 }}>
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
YÖNLENDİRME (CTA) METNİ
</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
<Text style={{ color: "#71717A", fontSize: 11 }}>
{customText.showCta ? "Göster" : "Gizle"}
</Text>
<Switch
value={customText.showCta}
onValueChange={(v) => setCustomText((p) => ({ ...p, showCta: v }))}
trackColor={{ false: "#27272A", true: "#D4AF37" }}
thumbColor="#FFFFFF"
ios_backgroundColor="#27272A"
/>
</View>
</View>
{customText.showCta && (
<TextInput
value={customText.ctaText}
onChangeText={(t) => setCustomText((p) => ({ ...p, ctaText: t }))}
placeholder="Örn: MENÜYÜ GÖRMEK İÇİN OKUTUN"
placeholderTextColor="#71717A"
style={{
backgroundColor: "#121215",
borderWidth: 1,
borderColor: "rgba(255,255,255,0.1)",
borderRadius: 12,
paddingHorizontal: 14,
paddingVertical: 10,
color: "#FFFFFF",
fontSize: 14,
}}
/>
)}
</View>
{/* Alan 5: WiFi Bilgileri */}
<View style={{ gap: 6 }}>
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
WIFI BİLGİSİ
</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
<Text style={{ color: "#71717A", fontSize: 11 }}>
{customText.showWifi ? "Göster" : "Gizle"}
</Text>
<Switch
value={customText.showWifi}
onValueChange={(v) => setCustomText((p) => ({ ...p, showWifi: v }))}
trackColor={{ false: "#27272A", true: "#D4AF37" }}
thumbColor="#FFFFFF"
ios_backgroundColor="#27272A"
/>
</View>
</View>
{customText.showWifi && (
<TextInput
value={customText.wifiText}
onChangeText={(t) => setCustomText((p) => ({ ...p, wifiText: t }))}
placeholder="Örn: WiFi: Bistro | Şifre: 12345"
placeholderTextColor="#71717A"
style={{
backgroundColor: "#121215",
borderWidth: 1,
borderColor: "rgba(255,255,255,0.1)",
borderRadius: 12,
paddingHorizontal: 14,
paddingVertical: 10,
color: "#FFFFFF",
fontSize: 14,
}}
/>
)}
</View>
{/* Alan 6: Sosyal Medya / İletişim */}
<View style={{ gap: 6 }}>
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
SOSYAL MEDYA / İLETİŞİM
</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
<Text style={{ color: "#71717A", fontSize: 11 }}>
{customText.showSocial ? "Göster" : "Gizle"}
</Text>
<Switch
value={customText.showSocial}
onValueChange={(v) => setCustomText((p) => ({ ...p, showSocial: v }))}
trackColor={{ false: "#27272A", true: "#D4AF37" }}
thumbColor="#FFFFFF"
ios_backgroundColor="#27272A"
/>
</View>
</View>
{customText.showSocial && (
<TextInput
value={customText.socialText}
onChangeText={(t) => setCustomText((p) => ({ ...p, socialText: t }))}
placeholder="Örn: @menulio"
placeholderTextColor="#71717A"
style={{
backgroundColor: "#121215",
borderWidth: 1,
borderColor: "rgba(255,255,255,0.1)",
borderRadius: 12,
paddingHorizontal: 14,
paddingVertical: 10,
color: "#FFFFFF",
fontSize: 14,
}}
/>
)}
</View>
</View> </View>
)} )}
</View> </View>
{/* Output Action Buttons */} {/* Output Action Buttons */}
<View style={{ width: "100%", gap: 10 }}> <View style={{ width: "100%", gap: 10 }}>
{/* PDF Download Button */} {/* PDF Download Button */}
@@ -8,313 +8,300 @@ import Svg, {
Text, Text,
Image, Image,
} from "react-native-svg"; } from "react-native-svg";
import type { QrTemplateProps } from "./index"; import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./index";
const QrTemplate2 = ({ const QrTemplate2 = ({
qrBase64, qrBase64,
restaurantName = "The Daily Grind", fields,
subtitle = "COFFEE & KITCHEN",
tableNo = "MASA NO: 04",
ctaText = "MENÜYÜ GÖRMEK İÇİN OKUTUN",
wifiText = "WiFi: DailyGrind | Şifre: coffee2026",
socialText = "@dailygrind",
fontFamily = "'Playfair Display', Georgia, serif",
showTableNo = true,
showSubtitle = true,
showCta = true,
showWifi = true,
showSocial = true,
...props ...props
}: QrTemplateProps) => ( }: QrTemplateProps) => {
<Svg const rName = fields?.restaurantName;
viewBox="0 0 400 650" const sub = fields?.subtitle;
width={300} const table = fields?.tableNo;
height={487.5} const cta = fields?.ctaText;
{...props} const wifi = fields?.wifiText;
> const social = fields?.socialText;
{/* Arka Plan */}
<Path fill="#252c36" d="M0 0h400v650H0z" />
{/* Ana Kart */} const rNameFont = getFontFamily(rName?.fontId, "Georgia, 'Times New Roman', serif");
<Rect const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
width={360} const ctaFont = getFontFamily(cta?.fontId, "Georgia, 'Times New Roman', serif");
height={610} const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
x={20}
y={20}
fill="#f6efe2"
stroke="#3a2312"
strokeWidth={1.5}
rx={20}
/>
<Rect
width={344}
height={594}
x={28}
y={28}
fill="none"
stroke="#8c6239"
strokeOpacity={0.4}
rx={14}
/>
<Rect
width={336}
height={586}
x={32}
y={32}
fill="none"
stroke="#8c6239"
strokeOpacity={0.25}
strokeWidth={0.5}
rx={10}
/>
{/* Sol Üst Botanik Dal */} return (
<G fill="none" stroke="#3a2312" strokeLinecap="round" strokeWidth={1.05}> <Svg viewBox="0 0 400 650" width={300} height={487.5} {...props}>
<Path d="M36 36c11.25 11.25 18.75 26.25 22.5 48.75" /> {/* Arka Plan */}
<Path <Path fill="#252c36" d="M0 0h400v650H0z" />
fill="#eadcc7"
d="M43.5 45c-3.75-7.5 7.5-10.5 9-3 0 7.5-9 3-9 3Zm6 11.25C45 51 57 46.5 60 53.25c0 6.75-10.5 3-10.5 3Zm4.5 13.5C60 64.5 69.75 72 64.5 78c-6 4.5-10.5-8.25-10.5-8.25Z"
/>
</G>
{/* Sağ Üst Kahve İkonu */} {/* Ana Kart */}
<G transform="translate(340 52)"> <Rect
<Circle r={32} fill="#eadcc7" stroke="#3a2312" strokeWidth={1.2} /> width={360}
<Circle height={610}
r={26} x={20}
y={20}
fill="#f6efe2" fill="#f6efe2"
stroke="#3a2312"
strokeWidth={1.5}
rx={20}
/>
<Rect
width={344}
height={594}
x={28}
y={28}
fill="none"
stroke="#8c6239" stroke="#8c6239"
strokeDasharray="2 2" strokeOpacity={0.4}
strokeWidth={0.8} rx={14}
/> />
<Circle r={21} fill="#7a4b26" stroke="#3a2312" strokeWidth={1.4} /> <Rect
<Path fill="#f6efe2" d="M0 10C-8 4-4-6 0-12 4-6 8 4 0 10" /> width={336}
<Path fill="#7a4b26" d="M0 4C-5 0-2-5 0-8 2-5 5 0 0 4" /> height={586}
<Circle cy={-14} r={1.8} fill="#f6efe2" /> x={32}
</G> y={32}
fill="none"
{/* Sol Orta Kahve Bardağı */} stroke="#8c6239"
<G transform="translate(48 250)"> strokeOpacity={0.25}
<Circle r={26} fill="#eadcc7" stroke="#3a2312" /> strokeWidth={0.5}
<Circle r={17} fill="#6a3b18" stroke="#3a2312" strokeWidth={1.2} /> rx={10}
<Path fill="#f6efe2" d="M-5-3c-3-4 5-7 5-1 0-6 8-3 5 1L0 7z" />
<Path fill="none" stroke="#3a2312" strokeWidth={1.5} d="M17-4c6 0 6 8 0 8" />
</G>
{/* Sağ Dal */}
<G fill="none" stroke="#3a2312" strokeWidth={0.91}>
<Path d="M352 230c-7 21-10.5 49-3.5 77" />
<Path
fill="#eadcc7"
d="M349.2 247.5c8.4-7 15.4 2.1 5.6 7.7-5.6 0-5.6-7.7-5.6-7.7Zm-2.8 24.5c-9.8-8.4-15.4 1.4-7 7 7 0 7-7 7-7Zm2.1 19.6c9.1-7 13.3 2.8 4.9 8.4-4.9 0-4.9-8.4-4.9-8.4Z"
/> />
</G>
{/* Dekoratif Çizgi */} {/* Sol Üst Botanik Dal */}
<Path <G fill="none" stroke="#3a2312" strokeLinecap="round" strokeWidth={1.05}>
fill="none" <Path d="M36 36c11.25 11.25 18.75 26.25 22.5 48.75" />
stroke="#8c6239" <Path
strokeLinecap="round" fill="#eadcc7"
strokeWidth={1.2} d="M43.5 45c-3.75-7.5 7.5-10.5 9-3 0 7.5-9 3-9 3Zm6 11.25C45 51 57 46.5 60 53.25c0 6.75-10.5 3-10.5 3Zm4.5 13.5C60 64.5 69.75 72 64.5 78c-6 4.5-10.5-8.25-10.5-8.25Z"
d="M142 66c6-2 12 2 18 0m80 0c6 2 12-2 18 0"
/>
{/* Sol Alt Dal */}
<G fill="none" stroke="#3a2312" strokeWidth={0.91}>
<Path d="M42 601.5c13-26 9.75-52 0-71.5" />
<Path
fill="#eadcc7"
d="M47.2 549.5c10.4-9.75 17.55 0 6.5 7.8Zm2.6 26c11.7-9.75 18.2 1.3 5.2 9.1Z"
/>
</G>
{/* Kahve Çekirdekleri */}
<G stroke="#3a2312" strokeWidth={1.2} transform="rotate(-20 1741.206 -8.75) scale(.8)">
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
<G stroke="#3a2312" strokeWidth={1.2} transform="rotate(35 -862.106 494.982) scale(.8)">
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
<G stroke="#3a2312" strokeWidth={1.2} transform="rotate(-45 602.558 -225.588) scale(.7)">
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
{/* Sağ Alt Fincan */}
<G transform="translate(325 590)">
<Ellipse cy={18} fill="#eadcc7" stroke="#3a2312" strokeWidth={1.2} rx={28} ry={7} />
<Path fill="#f6efe2" stroke="#3a2312" strokeWidth={1.4} d="M-18 2c0 14 36 14 36 0Z" />
<Ellipse cy={2} fill="#6a3b18" stroke="#3a2312" strokeWidth={1.2} rx={18} ry={4} />
<Path fill="none" stroke="#3a2312" strokeWidth={1.3} d="M16 4c8 0 8 8 0 10" />
<Path fill="none" stroke="#8c6239" strokeLinecap="round" d="M-6-4c-2-5 2-8 0-13M2-5c-2-5 2-8 0-14" />
</G>
{/* QR Köşe Çerçeveleri */}
<Path
fill="none"
stroke="#3a2312"
strokeLinecap="round"
strokeWidth={2}
d="M80 205c0-10 0-10 10-10m230 10c0-10 0-10-10-10M80 445c0 10 0 10 10 10m230-10c0 10 0 10-10 10"
/>
<Path stroke="#8c6239" strokeWidth={0.8} d="M145 608h30m50 0h30" />
{/* QR Placeholder / Gerçek QR */}
<G transform="translate(90 215)">
<Rect width={220} height={220} fill="#fbf8f2" stroke="#d8c7b0" strokeWidth={1.5} rx={20} />
{qrBase64 ? (
<Image
href={`data:image/png;base64,${qrBase64}`}
x={10}
y={10}
width={200}
height={200}
/> />
) : ( </G>
<>
<Rect {/* Sağ Üst Kahve İkonu */}
width={200} <G transform="translate(340 52)">
height={200} <Circle r={32} fill="#eadcc7" stroke="#3a2312" strokeWidth={1.2} />
<Circle
r={26}
fill="#f6efe2"
stroke="#8c6239"
strokeDasharray="2 2"
strokeWidth={0.8}
/>
<Circle r={21} fill="#7a4b26" stroke="#3a2312" strokeWidth={1.4} />
<Path fill="#f6efe2" d="M0 10C-8 4-4-6 0-12 4-6 8 4 0 10" />
<Path fill="#7a4b26" d="M0 4C-5 0-2-5 0-8 2-5 5 0 0 4" />
<Circle cy={-14} r={1.8} fill="#f6efe2" />
</G>
{/* Sol Orta Kahve Bardağı */}
<G transform="translate(48 250)">
<Circle r={26} fill="#eadcc7" stroke="#3a2312" />
<Circle r={17} fill="#6a3b18" stroke="#3a2312" strokeWidth={1.2} />
<Path fill="#f6efe2" d="M-5-3c-3-4 5-7 5-1 0-6 8-3 5 1L0 7z" />
<Path fill="none" stroke="#3a2312" strokeWidth={1.5} d="M17-4c6 0 6 8 0 8" />
</G>
{/* Sağ Dal */}
<G fill="none" stroke="#3a2312" strokeWidth={0.91}>
<Path d="M352 230c-7 21-10.5 49-3.5 77" />
<Path
fill="#eadcc7"
d="M349.2 247.5c8.4-7 15.4 2.1 5.6 7.7-5.6 0-5.6-7.7-5.6-7.7Zm-2.8 24.5c-9.8-8.4-15.4 1.4-7 7 7 0 7-7 7-7Zm2.1 19.6c9.1-7 13.3 2.8 4.9 8.4-4.9 0-4.9-8.4-4.9-8.4Z"
/>
</G>
{/* Dekoratif Çizgi */}
<Path
fill="none"
stroke="#8c6239"
strokeLinecap="round"
strokeWidth={1.2}
d="M142 66c6-2 12 2 18 0m80 0c6 2 12-2 18 0"
/>
{/* Sol Alt Dal */}
<G fill="none" stroke="#3a2312" strokeWidth={0.91}>
<Path d="M42 601.5c13-26 9.75-52 0-71.5" />
<Path
fill="#eadcc7"
d="M47.2 549.5c10.4-9.75 17.55 0 6.5 7.8Zm2.6 26c11.7-9.75 18.2 1.3 5.2 9.1Z"
/>
</G>
{/* Kahve Çekirdekleri */}
<G stroke="#3a2312" strokeWidth={1.2} transform="rotate(-20 1741.206 -8.75) scale(.8)">
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
<G stroke="#3a2312" strokeWidth={1.2} transform="rotate(35 -862.106 494.982) scale(.8)">
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
<G stroke="#3a2312" strokeWidth={1.2} transform="rotate(-45 602.558 -225.588) scale(.7)">
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
{/* Sağ Alt Fincan */}
<G transform="translate(325 590)">
<Ellipse cy={18} fill="#eadcc7" stroke="#3a2312" strokeWidth={1.2} rx={28} ry={7} />
<Path fill="#f6efe2" stroke="#3a2312" strokeWidth={1.4} d="M-18 2c0 14 36 14 36 0Z" />
<Ellipse cy={2} fill="#6a3b18" stroke="#3a2312" strokeWidth={1.2} rx={18} ry={4} />
<Path fill="none" stroke="#3a2312" strokeWidth={1.3} d="M16 4c8 0 8 8 0 10" />
<Path fill="none" stroke="#8c6239" strokeLinecap="round" d="M-6-4c-2-5 2-8 0-13M2-5c-2-5 2-8 0-14" />
</G>
{/* QR Köşe Çerçeveleri */}
<Path
fill="none"
stroke="#3a2312"
strokeLinecap="round"
strokeWidth={2}
d="M80 205c0-10 0-10 10-10m230 10c0-10 0-10-10-10M80 445c0 10 0 10 10 10m230-10c0 10 0 10-10 10"
/>
<Path stroke="#8c6239" strokeWidth={0.8} d="M145 608h30m50 0h30" />
{/* QR Placeholder / Gerçek QR */}
<G transform="translate(90 215)">
<Rect width={220} height={220} fill="#fbf8f2" stroke="#d8c7b0" strokeWidth={1.5} rx={20} />
{qrBase64 ? (
<Image
href={`data:image/png;base64,${qrBase64}`}
x={10} x={10}
y={10} y={10}
fill="#f5efe6" width={200}
stroke="#d8c7b0" height={200}
strokeDasharray="4 3"
rx={14}
/> />
<Circle cx={110} cy={98} r={18} fill="#fbf8f2" stroke="#8c6239" /> ) : (
<G fill="#3a2312" transform="matrix(.65 0 0 .65 110 98)"> <>
<Ellipse cx={-5} cy={2} rx={7} ry={5} transform="rotate(-30)" /> <Rect
<Ellipse cx={5} rx={7} ry={5} transform="rotate(40)" /> width={200}
</G> height={200}
<Text x={10}
x={110} y={10}
y={132} fill="#f5efe6"
fill="#8c6239" stroke="#d8c7b0"
fontFamily="Inter, Arial, sans-serif" strokeDasharray="4 3"
fontSize={11} rx={14}
fontWeight={700} />
letterSpacing={1} <Circle cx={110} cy={98} r={18} fill="#fbf8f2" stroke="#8c6239" />
textAnchor="middle" <G fill="#3a2312" transform="matrix(.65 0 0 .65 110 98)">
> <Ellipse cx={-5} cy={2} rx={7} ry={5} transform="rotate(-30)" />
{"QR KOD ALANI"} <Ellipse cx={5} rx={7} ry={5} transform="rotate(40)" />
</Text> </G>
</> <Text
)} x={110}
</G> y={132}
fill="#8c6239"
fontFamily="System, sans-serif"
fontSize={11}
fontWeight={700}
letterSpacing={1}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</>
)}
</G>
{/* Metinler */} {/* Metinler (Çok satırlı destekli) */}
<G textAnchor="middle"> <G textAnchor="middle">
{/* Üst Tag / Takı */}
<Text
x={200}
y={70}
fill="#3a2312"
fontFamily={fontFamily}
fontSize={14}
fontStyle="italic"
fontWeight={700}
letterSpacing={4}
>
{"THE"}
</Text>
{/* Restoran Adı */}
{restaurantName ? (
<Text <Text
x={200} x={200}
y={114} y={68}
fill="#2c1a0e"
fontFamily={fontFamily}
fontSize={30}
fontWeight={800}
letterSpacing={0.5}
>
{restaurantName}
</Text>
) : null}
{/* Slogan */}
{showSubtitle && subtitle ? (
<Text
x={200}
y={146}
fill="#3a2312" fill="#3a2312"
fontFamily="Inter, Arial, sans-serif" fontFamily={rNameFont}
fontSize={12} fontSize={14}
fontStyle="italic"
fontWeight={700} fontWeight={700}
letterSpacing={2.5} letterSpacing={4}
> >
{subtitle} {"THE"}
</Text> </Text>
) : null}
{/* CTA */} {/* Restoran Adı */}
{showCta && ctaText ? ( {rName?.visible !== false &&
renderSvgLines({
text: rName?.text || "The Daily Grind",
x: 200,
y: 110,
fill: "#2c1a0e",
fontFamily: rNameFont,
fontSize: rName?.fontSize || 28,
fontWeight: "bold",
letterSpacing: 0.5,
})}
{/* Slogan */}
{sub?.visible !== false &&
renderSvgLines({
text: sub?.text || "COFFEE & KITCHEN",
x: 200,
y: 144,
fill: "#3a2312",
fontFamily: subFont,
fontSize: sub?.fontSize || 12,
fontWeight: 700,
letterSpacing: 2.5,
})}
{/* CTA */}
{cta?.visible !== false &&
renderSvgLines({
text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN",
x: 200,
y: 486,
fill: "#2c1a0e",
fontFamily: ctaFont,
fontSize: cta?.fontSize || 15,
fontWeight: "bold",
letterSpacing: 1.2,
})}
{/* WiFi */}
{wifi?.visible !== false &&
renderSvgLines({
text: wifi?.text || "WiFi: DailyGrind | Şifre: coffee2026",
x: 200,
y: 520,
fill: "#3a2312",
fontFamily: infoFont,
fontSize: wifi?.fontSize || 10.5,
fontWeight: 700,
letterSpacing: 0.5,
})}
{/* Masa No & Sosyal */}
{(table?.visible !== false || social?.visible !== false) &&
renderSvgLines({
text: [
table?.visible !== false && table?.text ? table.text : "",
social?.visible !== false && social?.text ? social.text : "",
]
.filter(Boolean)
.join(" • "),
x: 200,
y: 544,
fill: "#8c6239",
fontFamily: infoFont,
fontSize: table?.fontSize || social?.fontSize || 10,
fontWeight: 600,
letterSpacing: 1,
})}
<Text <Text
x={200} x={200}
y={488} y={611}
fill="#2c1a0e"
fontFamily={fontFamily}
fontSize={16}
fontWeight={800}
letterSpacing={1.2}
>
{ctaText}
</Text>
) : null}
{/* WiFi */}
{showWifi && wifiText ? (
<Text
x={200}
y={520}
fill="#3a2312" fill="#3a2312"
fontFamily="Inter, Arial, sans-serif" fontFamily={rNameFont}
fontSize={10.5} fontSize={11}
fontStyle="italic"
fontWeight={700} fontWeight={700}
letterSpacing={0.5} letterSpacing={2}
> >
{wifiText} {"EST. 2026"}
</Text> </Text>
) : null} </G>
</Svg>
{/* Masa No & Sosyal */} );
{showTableNo || showSocial ? ( };
<Text
x={200}
y={544}
fill="#8c6239"
fontFamily="Inter, Arial, sans-serif"
fontSize={10}
fontWeight={600}
letterSpacing={1}
>
{[
showTableNo && tableNo ? tableNo : "",
showSocial && socialText ? socialText : "",
]
.filter(Boolean)
.join(" • ")}
</Text>
) : null}
<Text
x={200}
y={611}
fill="#3a2312"
fontFamily={fontFamily}
fontSize={11}
fontStyle="italic"
fontWeight={700}
letterSpacing={2}
>
{"EST. 2026"}
</Text>
</G>
</Svg>
);
export default QrTemplate2; export default QrTemplate2;
@@ -12,356 +12,336 @@ import Svg, {
Text, Text,
Image, Image,
} from "react-native-svg"; } from "react-native-svg";
import type { QrTemplateProps } from "./index"; import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./index";
const QrTemplate3 = ({ const QrTemplate3 = ({
qrBase64, qrBase64,
restaurantName = "BELLA NAPOLI", fields,
subtitle = "AUTENTICA CUCINA ITALIANA",
tableNo = "MASA NO: 01",
ctaText = "MENÜYÜ GÖRMEK İÇİN OKUTUN",
wifiText = "WiFi: GourmetBistro | Şifre: lezzetli01",
socialText = "@gourmetbistro",
fontFamily = "'Playfair Display', Georgia, serif",
showTableNo = true,
showSubtitle = true,
showCta = true,
showWifi = true,
showSocial = true,
...props ...props
}: QrTemplateProps) => ( }: QrTemplateProps) => {
<Svg const rName = fields?.restaurantName;
viewBox="0 0 400 650" const sub = fields?.subtitle;
width={300} const table = fields?.tableNo;
height={487.5} const cta = fields?.ctaText;
{...props} const wifi = fields?.wifiText;
> const social = fields?.socialText;
<Defs>
<LinearGradient id="card-grad-3" x1="0%" x2="0%" y1="0%" y2="100%">
<Stop offset="0%" stopColor="#fdfbf5" />
<Stop offset="50%" stopColor="#faf4e6" />
<Stop offset="100%" stopColor="#f7eed8" />
</LinearGradient>
<LinearGradient id="oven-fire-3" x1="0%" x2="0%" y1="100%" y2="0%">
<Stop offset="0%" stopColor="#c0392b" />
<Stop offset="50%" stopColor="#e67e22" />
<Stop offset="100%" stopColor="#f1c40f" />
</LinearGradient>
<LinearGradient id="wine-grad-3" x1="0%" x2="0%" y1="0%" y2="100%">
<Stop offset="0%" stopColor="#7b1123" />
<Stop offset="100%" stopColor="#4a0512" />
</LinearGradient>
<Pattern
id="checkered-border-3"
width={20}
height={20}
patternUnits="userSpaceOnUse"
>
<Path fill="#b92b27" d="M0 0h20v20H0z" />
<Path fill="#fdf8ee" d="M0 0h10v10H0zm10 10h10v10H10z" />
</Pattern>
</Defs>
{/* Arka Plan */} const rNameFont = getFontFamily(rName?.fontId, "Georgia, 'Playfair Display', serif");
<Path fill="url(#checkered-border-3)" d="M0 0h400v650H0z" /> const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
const ctaFont = getFontFamily(cta?.fontId, "Georgia, 'Playfair Display', serif");
const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
{/* Kart */} return (
<G id="layer-card"> <Svg viewBox="0 0 400 650" width={300} height={487.5} {...props}>
<Rect <Defs>
width={356} <LinearGradient id="card-grad-3" x1="0%" x2="0%" y1="0%" y2="100%">
height={606} <Stop offset="0%" stopColor="#fdfbf5" />
x={22} <Stop offset="50%" stopColor="#faf4e6" />
y={22} <Stop offset="100%" stopColor="#f7eed8" />
fill="url(#card-grad-3)" </LinearGradient>
stroke="#b92b27" <LinearGradient id="oven-fire-3" x1="0%" x2="0%" y1="100%" y2="0%">
strokeWidth={2} <Stop offset="0%" stopColor="#c0392b" />
rx={20} <Stop offset="50%" stopColor="#e67e22" />
/> <Stop offset="100%" stopColor="#f1c40f" />
<Rect </LinearGradient>
width={344} <LinearGradient id="wine-grad-3" x1="0%" x2="0%" y1="0%" y2="100%">
height={594} <Stop offset="0%" stopColor="#7b1123" />
x={28} <Stop offset="100%" stopColor="#4a0512" />
y={28} </LinearGradient>
fill="none" <Pattern
stroke="#8c6239" id="checkered-border-3"
strokeOpacity={0.5} width={20}
strokeWidth={0.8} height={20}
rx={15} patternUnits="userSpaceOnUse"
/> >
{showTableNo && tableNo ? ( <Path fill="#b92b27" d="M0 0h20v20H0z" />
<Path fill="#fdf8ee" d="M0 0h10v10H0zm10 10h10v10H10z" />
</Pattern>
</Defs>
{/* Arka Plan */}
<Path fill="url(#checkered-border-3)" d="M0 0h400v650H0z" />
{/* Kart */}
<G id="layer-card">
<Rect <Rect
width={140} width={356}
height={24} height={606}
x={130} x={22}
y={34} y={22}
fill="#faf4e6" fill="url(#card-grad-3)"
stroke="#b92b27"
strokeWidth={2}
rx={20}
/>
<Rect
width={344}
height={594}
x={28}
y={28}
fill="none"
stroke="#8c6239" stroke="#8c6239"
strokeWidth={1.2} strokeOpacity={0.5}
rx={12}
/>
) : null}
</G>
{/* İkonlar */}
<G id="layer-icons">
<Path
fill="none"
stroke="#c5a880"
strokeLinecap="round"
strokeWidth={1.5}
d="M68 88q12-13 28-2 6 5 10 2m226 0q-12-13-28-2-6 5-10 2"
/>
{/* Pizza Fırını */}
<G id="pizza-oven" transform="translate(200 192)">
<Rect
width={88}
height={6}
x={-44}
fill="#d7ccc8"
stroke="#5d4037"
rx={2}
/>
<Path
fill="#d7ccc8"
stroke="#5d4037"
strokeWidth={1.5}
d="M-40 0a40 38 0 0 1 80 0Z"
/>
<Path stroke="#8d6e63" d="m-30-25 7 6m6-15 4 8M0-38v9m17-5-4 8m17 1-7 6" />
<Path fill="#3e2723" stroke="#271c19" d="M-28 0a28 26 0 0 1 56 0Z" />
<Path fill="url(#oven-fire-3)" d="M-18 0q3-16 12-10 6-12 12-2 9-6 12 12Z" />
<Path fill="#fff9c4" d="M-10 0q5-10 10-6 5-6 10 6Z" />
</G>
{/* Sol Malzemeler */}
<G id="left-ingredients" transform="translate(108 185)">
<Ellipse
cx={-16}
cy={6}
fill="#d32f2f"
stroke="#8e0000"
strokeWidth={0.8} strokeWidth={0.8}
rx={14} rx={15}
ry={10}
transform="rotate(-25 -16 6)"
/> />
<Ellipse {table?.visible !== false && table?.text ? (
cx={-16}
cy={6}
fill="#e53935"
rx={10}
ry={6.5}
transform="rotate(-25 -16 6)"
/>
<Ellipse
cx={2}
cy={7}
fill="#c62828"
stroke="#8e0000"
strokeWidth={0.8}
rx={13}
ry={9}
transform="rotate(-10 2 7)"
/>
<Ellipse cx={2} cy={7} fill="#e53935" rx={9} ry={5.5} transform="rotate(-10 2 7)" />
<Circle cx={2} cy={7} r={1.5} fill="#fdd835" />
<Circle cx={16} cy={-2} r={14} fill="#e53935" stroke="#b71c1c" strokeWidth={0.8} />
<Ellipse cx={12} cy={-6} fill="#ff8a80" opacity={0.6} rx={4} ry={2} />
<Path
stroke="#2e7d32"
strokeLinecap="round"
strokeWidth={1.5}
d="M16-16v3m0 0-5 2m5-2 5 2m-5-2-3-2m3 2 3-2"
/>
</G>
{/* Sağ Malzemeler */}
<G id="right-ingredients" transform="translate(290 182)">
<Circle cx={-14} cy={2} r={15} fill="#e53935" stroke="#b71c1c" strokeWidth={0.8} />
<Ellipse cx={-18} cy={-3} fill="#ff8a80" opacity={0.6} rx={4} ry={2} />
<Path
stroke="#2e7d32"
strokeLinecap="round"
strokeWidth={1.5}
d="M-14-13v3m0 0-4 2m4-2 4 2"
/>
<Path fill="#2e7d32" stroke="#1b5e20" strokeWidth={0.8} d="M-2 0C8-16 26-12 24 2 22 14 6 10-2 0Z" />
<Path fill="none" stroke="#4caf50" strokeWidth={0.7} d="M-2 0q14-4 26 2" />
<Path fill="#388e3c" stroke="#1b5e20" strokeWidth={0.8} d="M8 6c10-6 24 4 18 16-8 6-18-4-18-16Z" />
<Path fill="none" stroke="#81c784" strokeWidth={0.7} d="M8 6q12 6 18 16" />
</G>
{/* Alt Pizza Dilimi */}
<G id="footer-pizza-slice" transform="rotate(-10 3278.565 44.969)">
<Path fill="#deac68" stroke="#8d6e63" strokeWidth={1.2} d="m0 0 46-18c4 8 6 32 0 40Z" />
<Path fill="#fbc02d" d="m2 0 40-15c3 7 5 25 0 33Z" />
<Circle cx={28} cy={-5} r={5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
<Circle cx={32} cy={8} r={4.5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
<Circle cx={16} r={3.5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
<Ellipse cx={23} cy={4} fill="#2e7d32" rx={2} ry={4} transform="rotate(30 23 4)" />
<Ellipse cx={34} cy={-11} fill="#2e7d32" rx={2} ry={3} transform="rotate(-40 34 -11)" />
</G>
{/* Şarap Kadehi */}
<G id="footer-wine-glass" transform="translate(345 562)">
<Path fill="none" stroke="#71717a" strokeWidth={1.2} d="M-14-24C-15-8-11 4 0 6 11 4 15-8 14-24Z" />
<Path fill="url(#wine-grad-3)" d="M-13-12C-11 0-7 4 0 5.5 7 4 11 0 13-12Z" />
<Ellipse cy={-12} fill="#991b1b" rx={13} ry={2} />
<Path stroke="#71717a" strokeWidth={1.4} d="M0 6v22" />
<Ellipse cy={28} fill="#e4e4e7" stroke="#71717a" strokeWidth={1.2} rx={11} ry={2} />
<Path fill="none" stroke="#fff" strokeLinecap="round" d="M-10-20c-2 8 1 18 6 23" opacity={0.6} />
</G>
{/* QR Köşe Aksanları */}
<G id="qr-corner-accents">
<Path fill="none" stroke="#2e7d32" strokeLinecap="round" strokeWidth={3} d="M78 227v-22h22" />
<Path fill="none" stroke="#b92b27" strokeLinecap="round" strokeWidth={3} d="M322 227v-22h-22M78 423v22h22" />
<Path fill="none" stroke="#2e7d32" strokeLinecap="round" strokeWidth={3} d="M322 423v22h-22" />
</G>
</G>
{/* QR Placeholder / Gerçek QR */}
<G id="qr-placeholder" transform="translate(90 215)">
<Rect width={220} height={220} fill="#fff" stroke="#e4e4e7" strokeWidth={2} rx={20} />
{qrBase64 ? (
<Image
href={`data:image/png;base64,${qrBase64}`}
x={10}
y={10}
width={200}
height={200}
/>
) : (
<>
<Rect <Rect
width={196} width={140}
height={196} height={24}
x={12} x={130}
y={12} y={34}
fill="#fafafa" fill="#faf4e6"
stroke="#e4e4e7" stroke="#8c6239"
strokeDasharray="4 3" strokeWidth={1.2}
rx={14} rx={12}
/> />
<Text ) : null}
x={110} </G>
y={115}
fill="#71717a" {/* İkonlar */}
fontFamily="Inter, Arial, sans-serif" <G id="layer-icons">
fontSize={12} <Path
fontWeight={700} fill="none"
textAnchor="middle" stroke="#c5a880"
> strokeLinecap="round"
{"QR KOD ALANI"} strokeWidth={1.5}
</Text> d="M68 88q12-13 28-2 6 5 10 2m226 0q-12-13-28-2-6 5-10 2"
</> />
)} {/* Pizza Fırını */}
</G> <G id="pizza-oven" transform="translate(200 192)">
<Rect
width={88}
height={6}
x={-44}
fill="#d7ccc8"
stroke="#5d4037"
rx={2}
/>
<Path
fill="#d7ccc8"
stroke="#5d4037"
strokeWidth={1.5}
d="M-40 0a40 38 0 0 1 80 0Z"
/>
<Path stroke="#8d6e63" d="m-30-25 7 6m6-15 4 8M0-38v9m17-5-4 8m17 1-7 6" />
<Path fill="#3e2723" stroke="#271c19" d="M-28 0a28 26 0 0 1 56 0Z" />
<Path fill="url(#oven-fire-3)" d="M-18 0q3-16 12-10 6-12 12-2 9-6 12 12Z" />
<Path fill="#fff9c4" d="M-10 0q5-10 10-6 5-6 10 6Z" />
</G>
{/* Sol Malzemeler */}
<G id="left-ingredients" transform="translate(108 185)">
<Ellipse
cx={-16}
cy={6}
fill="#d32f2f"
stroke="#8e0000"
strokeWidth={0.8}
rx={14}
ry={10}
transform="rotate(-25 -16 6)"
/>
<Ellipse
cx={-16}
cy={6}
fill="#e53935"
rx={10}
ry={6.5}
transform="rotate(-25 -16 6)"
/>
<Ellipse
cx={2}
cy={7}
fill="#c62828"
stroke="#8e0000"
strokeWidth={0.8}
rx={13}
ry={9}
transform="rotate(-10 2 7)"
/>
<Ellipse cx={2} cy={7} fill="#e53935" rx={9} ry={5.5} transform="rotate(-10 2 7)" />
<Circle cx={2} cy={7} r={1.5} fill="#fdd835" />
<Circle cx={16} cy={-2} r={14} fill="#e53935" stroke="#b71c1c" strokeWidth={0.8} />
<Ellipse cx={12} cy={-6} fill="#ff8a80" opacity={0.6} rx={4} ry={2} />
<Path
stroke="#2e7d32"
strokeLinecap="round"
strokeWidth={1.5}
d="M16-16v3m0 0-5 2m5-2 5 2m-5-2-3-2m3 2 3-2"
/>
</G>
{/* Sağ Malzemeler */}
<G id="right-ingredients" transform="translate(290 182)">
<Circle cx={-14} cy={2} r={15} fill="#e53935" stroke="#b71c1c" strokeWidth={0.8} />
<Ellipse cx={-18} cy={-3} fill="#ff8a80" opacity={0.6} rx={4} ry={2} />
<Path
stroke="#2e7d32"
strokeLinecap="round"
strokeWidth={1.5}
d="M-14-13v3m0 0-4 2m4-2 4 2"
/>
<Path fill="#2e7d32" stroke="#1b5e20" strokeWidth={0.8} d="M-2 0C8-16 26-12 24 2 22 14 6 10-2 0Z" />
<Path fill="none" stroke="#4caf50" strokeWidth={0.7} d="M-2 0q14-4 26 2" />
<Path fill="#388e3c" stroke="#1b5e20" strokeWidth={0.8} d="M8 6c10-6 24 4 18 16-8 6-18-4-18-16Z" />
<Path fill="none" stroke="#81c784" strokeWidth={0.7} d="M8 6q12 6 18 16" />
</G>
{/* Alt Pizza Dilimi */}
<G id="footer-pizza-slice" transform="rotate(-10 3278.565 44.969)">
<Path fill="#deac68" stroke="#8d6e63" strokeWidth={1.2} d="m0 0 46-18c4 8 6 32 0 40Z" />
<Path fill="#fbc02d" d="m2 0 40-15c3 7 5 25 0 33Z" />
<Circle cx={28} cy={-5} r={5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
<Circle cx={32} cy={8} r={4.5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
<Circle cx={16} r={3.5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
<Ellipse cx={23} cy={4} fill="#2e7d32" rx={2} ry={4} transform="rotate(30 23 4)" />
<Ellipse cx={34} cy={-11} fill="#2e7d32" rx={2} ry={3} transform="rotate(-40 34 -11)" />
</G>
{/* Şarap Kadehi */}
<G id="footer-wine-glass" transform="translate(345 562)">
<Path fill="none" stroke="#71717a" strokeWidth={1.2} d="M-14-24C-15-8-11 4 0 6 11 4 15-8 14-24Z" />
<Path fill="url(#wine-grad-3)" d="M-13-12C-11 0-7 4 0 5.5 7 4 11 0 13-12Z" />
<Ellipse cy={-12} fill="#991b1b" rx={13} ry={2} />
<Path stroke="#71717a" strokeWidth={1.4} d="M0 6v22" />
<Ellipse cy={28} fill="#e4e4e7" stroke="#71717a" strokeWidth={1.2} rx={11} ry={2} />
<Path fill="none" stroke="#fff" strokeLinecap="round" d="M-10-20c-2 8 1 18 6 23" opacity={0.6} />
</G>
{/* QR Köşe Aksanları */}
<G id="qr-corner-accents">
<Path fill="none" stroke="#2e7d32" strokeLinecap="round" strokeWidth={3} d="M78 227v-22h22" />
<Path fill="none" stroke="#b92b27" strokeLinecap="round" strokeWidth={3} d="M322 227v-22h-22M78 423v22h22" />
<Path fill="none" stroke="#2e7d32" strokeLinecap="round" strokeWidth={3} d="M322 423v22h-22" />
</G>
</G>
{/* QR Placeholder / Gerçek QR */}
<G id="qr-placeholder" transform="translate(90 215)">
<Rect width={220} height={220} fill="#fff" stroke="#e4e4e7" strokeWidth={2} rx={20} />
{qrBase64 ? (
<Image
href={`data:image/png;base64,${qrBase64}`}
x={10}
y={10}
width={200}
height={200}
/>
) : (
<>
<Rect
width={196}
height={196}
x={12}
y={12}
fill="#fafafa"
stroke="#e4e4e7"
strokeDasharray="4 3"
rx={14}
/>
<Text
x={110}
y={115}
fill="#71717a"
fontFamily="System, sans-serif"
fontSize={12}
fontWeight={700}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</>
)}
</G>
{/* Metinler (Çok satırlı destekli) */}
<G id="layer-texts">
{/* Masa No */}
{table?.visible !== false &&
renderSvgLines({
text: table?.text || "MASA NO: 01",
x: 200,
y: 50,
fill: "#18181b",
fontFamily: infoFont,
fontSize: table?.fontSize || 11,
fontWeight: "bold",
letterSpacing: 1.2,
})}
{/* Metinler */}
<G id="layer-texts">
{/* Masa No */}
{showTableNo && tableNo ? (
<Text <Text
x={200} x={200}
y={50} y={96}
fill="#18181b"
fontFamily="Inter, Arial, sans-serif"
fontSize={11}
fontWeight={800}
letterSpacing={1.2}
textAnchor="middle"
>
{tableNo}
</Text>
) : null}
<Text
x={200}
y={96}
fill="#3e2723"
fontFamily={fontFamily}
fontSize={24}
textAnchor="middle"
>
{"PIZZERIA"}
</Text>
{/* Restoran Adı */}
{restaurantName ? (
<Text
x={200}
y={132}
fill="#18181b"
fontFamily={fontFamily}
fontSize={28}
fontWeight={700}
letterSpacing={1}
textAnchor="middle"
>
{restaurantName}
</Text>
) : null}
{/* Slogan */}
{showSubtitle && subtitle ? (
<Text
x={200}
y={222}
fill="#2e7d32"
fontFamily="Inter, Arial, sans-serif"
fontSize={11.5}
fontWeight={800}
letterSpacing={1.8}
textAnchor="middle"
>
{subtitle}
</Text>
) : null}
{/* CTA */}
{showCta && ctaText ? (
<Text
x={200}
y={474}
fill="#b92b27"
fontFamily={fontFamily}
fontSize={14}
fontWeight={700}
letterSpacing={1.2}
textAnchor="middle"
>
{ctaText}
</Text>
) : null}
{/* WiFi */}
{showWifi && wifiText ? (
<Text
x={200}
y={546}
fill="#3e2723" fill="#3e2723"
fontFamily="Inter, Arial, sans-serif" fontFamily={rNameFont}
fontSize={10.5} fontSize={24}
fontWeight={700}
textAnchor="middle" textAnchor="middle"
> >
{wifiText} {"PIZZERIA"}
</Text> </Text>
) : null}
{/* Sosyal Medya */} {/* Restoran Adı */}
{showSocial && socialText ? ( {rName?.visible !== false &&
<Text renderSvgLines({
x={200} text: rName?.text || "BELLA NAPOLI",
y={570} x: 200,
fill="#b92b27" y: 130,
fontFamily="Inter, Arial, sans-serif" fill: "#18181b",
fontSize={10.5} fontFamily: rNameFont,
fontWeight={700} fontSize: rName?.fontSize || 26,
letterSpacing={0.5} fontWeight: "bold",
textAnchor="middle" letterSpacing: 1,
> })}
{socialText}
</Text> {/* Slogan (Enter ile çok satır olabilir) */}
) : null} {sub?.visible !== false &&
</G> renderSvgLines({
</Svg> text: sub?.text || "AUTENTICA CUCINA ITALIANA",
); x: 200,
y: 222,
fill: "#2e7d32",
fontFamily: subFont,
fontSize: sub?.fontSize || 11.5,
fontWeight: "bold",
letterSpacing: 1.8,
})}
{/* CTA */}
{cta?.visible !== false &&
renderSvgLines({
text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN",
x: 200,
y: 474,
fill: "#b92b27",
fontFamily: ctaFont,
fontSize: cta?.fontSize || 13,
fontWeight: "bold",
letterSpacing: 1.2,
})}
{/* WiFi */}
{wifi?.visible !== false &&
renderSvgLines({
text: wifi?.text || "WiFi: GourmetBistro | Şifre: lezzetli01",
x: 200,
y: 546,
fill: "#3e2723",
fontFamily: infoFont,
fontSize: wifi?.fontSize || 10.5,
fontWeight: "bold",
})}
{/* Sosyal Medya */}
{social?.visible !== false &&
renderSvgLines({
text: social?.text || "BİZİ TAKİP EDİN | @gourmetbistro",
x: 200,
y: 570,
fill: "#b92b27",
fontFamily: infoFont,
fontSize: social?.fontSize || 10.5,
fontWeight: "bold",
letterSpacing: 0.5,
})}
</G>
</Svg>
);
};
export default QrTemplate3; export default QrTemplate3;
@@ -9,271 +9,262 @@ import Svg, {
Text, Text,
Image, Image,
} from "react-native-svg"; } from "react-native-svg";
import type { QrTemplateProps } from "./index"; import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./index";
const QrTemplate4 = ({ const QrTemplate4 = ({
qrBase64, qrBase64,
restaurantName = "THE CRIMSON PETAL", fields,
subtitle = "FINE DINING EXPERIENCE",
tableNo = "MASA NO: 01",
ctaText = "MENÜYÜ GÖRMEK İÇİN OKUTUN",
wifiText = "WiFi: CrimsonGuest | Şifre: petal2026",
socialText = "@thecrimsonpetal",
fontFamily = "Georgia, 'Times New Roman', serif",
showTableNo = true,
showSubtitle = true,
showCta = true,
showWifi = true,
showSocial = true,
...props ...props
}: QrTemplateProps) => ( }: QrTemplateProps) => {
<Svg width={300} height={435} viewBox="0 0 400 580" {...props}> const rName = fields?.restaurantName;
<Defs> const sub = fields?.subtitle;
<G id="b4"> const table = fields?.tableNo;
<Path const cta = fields?.ctaText;
fill="none" const wifi = fields?.wifiText;
stroke="#c59b4b" const social = fields?.socialText;
strokeWidth={1.3}
d="M0-22c22-8 36 6 38 22-2 16-16 30-38 22Z"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.9}
d="M0-12c14-6 24 2 26 12-2 10-12 18-26 12Z"
/>
<Path stroke="#c59b4b" strokeWidth={0.8} d="M0 0h26" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.2}
d="M0-8c16-28 34-32 46-18C36-12 20-4 0-8ZM0 8c16 28 34 32 46 18C36 12 20 4 0 8Z"
/>
<Circle cx={48} cy={-28} r={1.5} fill="#c59b4b" />
<Circle cx={48} cy={28} r={1.5} fill="#c59b4b" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.1}
d="M0-35c24-21 52-15 56-1-14 6-32 12-56 1Zm0 70c24 21 52 15 56 1-14-6-32-12-56-1Z"
/>
<Path
fill="none"
stroke="#c59b4b"
d="M28-44c14-20 36-10 30 2-10 6-20 4-30-2Zm0 88c14 20 36 10 30-2-10-6-20-4-30 2Z"
/>
<Circle cx={60} cy={-44} r={1.2} fill="#c59b4b" />
<Circle cx={60} cy={44} r={1.2} fill="#c59b4b" />
<Circle cx={34} cy={-58} r={1.2} fill="#c59b4b" />
<Circle cx={34} cy={58} r={1.2} fill="#c59b4b" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
d="M0-60c22-15 36-35 32-55-6 15-16 30-32 55M0 60c22 15 36 35 32 55-6-15-16-30-32-55"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.9}
d="M14-78c8-6 12 2 6 6-4 0-6-4-6-6Zm0 156c8 6 12-2 6-6-4 0-6 4-6 6Z"
/>
<Circle cx={32} cy={-116} r={1.5} fill="#c59b4b" />
<Circle cx={32} cy={116} r={1.5} fill="#c59b4b" />
</G>
<G id="a4">
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.3}
d="M18 64c0-28 18-46 46-46"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.8}
d="M24 52c0-16 12-28 28-28"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
strokeWidth={0.9}
d="M28 32c4-6 10-4 8 2-2 4-8 2-6-4s10-6 14-2"
/>
<Circle cx={28} cy={28} r={1.8} fill="#c59b4b" />
<Circle cx={48} cy={18} r={1.4} fill="#c59b4b" />
<Circle cx={18} cy={48} r={1.4} fill="#c59b4b" />
</G>
</Defs>
<Path fill="#fdfdfd" d="M0 0h400v580H0z" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.2}
d="M20 14h360v552H20z"
/>
<Use href="#a4" />
<Use href="#a4" transform="matrix(-1 0 0 1 400 0)" />
<Use href="#a4" transform="matrix(1 0 0 -1 0 580)" />
<Use href="#a4" transform="rotate(180 200 290)" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
strokeWidth={1.2}
d="M132 62c8-4 16 2 24-2 4-2 2-6-2-4m114 6c-8-4-16 2-24-2-4-2-2-6 2-4"
/>
<Use href="#b4" transform="translate(0 310)" />
<Use href="#b4" transform="matrix(-1 0 0 1 400 310)" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="square"
strokeWidth={2.2}
d="M114 278v-58h54m118 58v-58h-54M114 342v58h54m118-58v58h-54"
/>
{/* QR Placeholder / Live QR */} const rNameFont = getFontFamily(rName?.fontId, "Georgia, 'Times New Roman', serif");
<G transform="translate(125 230)"> const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
<Rect const ctaFont = getFontFamily(cta?.fontId, "Georgia, 'Times New Roman', serif");
width={150} const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
height={150}
fill="#fff" return (
stroke="#eae5d9" <Svg width={300} height={435} viewBox="0 0 400 580" {...props}>
strokeWidth={1.5} <Defs>
rx={12} <G id="b4">
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.3}
d="M0-22c22-8 36 6 38 22-2 16-16 30-38 22Z"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.9}
d="M0-12c14-6 24 2 26 12-2 10-12 18-26 12Z"
/>
<Path stroke="#c59b4b" strokeWidth={0.8} d="M0 0h26" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.2}
d="M0-8c16-28 34-32 46-18C36-12 20-4 0-8ZM0 8c16 28 34 32 46 18C36 12 20 4 0 8Z"
/>
<Circle cx={48} cy={-28} r={1.5} fill="#c59b4b" />
<Circle cx={48} cy={28} r={1.5} fill="#c59b4b" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.1}
d="M0-35c24-21 52-15 56-1-14 6-32 12-56 1Zm0 70c24 21 52 15 56 1-14-6-32-12-56-1Z"
/>
<Path
fill="none"
stroke="#c59b4b"
d="M28-44c14-20 36-10 30 2-10 6-20 4-30-2Zm0 88c14 20 36 10 30-2-10-6-20-4-30 2Z"
/>
<Circle cx={60} cy={-44} r={1.2} fill="#c59b4b" />
<Circle cx={60} cy={44} r={1.2} fill="#c59b4b" />
<Circle cx={34} cy={-58} r={1.2} fill="#c59b4b" />
<Circle cx={34} cy={58} r={1.2} fill="#c59b4b" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
d="M0-60c22-15 36-35 32-55-6 15-16 30-32 55M0 60c22 15 36 35 32 55-6-15-16-30-32-55"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.9}
d="M14-78c8-6 12 2 6 6-4 0-6-4-6-6Zm0 156c8 6 12-2 6-6-4 0-6 4-6 6Z"
/>
<Circle cx={32} cy={-116} r={1.5} fill="#c59b4b" />
<Circle cx={32} cy={116} r={1.5} fill="#c59b4b" />
</G>
<G id="a4">
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.3}
d="M18 64c0-28 18-46 46-46"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={0.8}
d="M24 52c0-16 12-28 28-28"
/>
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="round"
strokeWidth={0.9}
d="M28 32c4-6 10-4 8 2-2 4-8 2-6-4s10-6 14-2"
/>
<Circle cx={28} cy={28} r={1.8} fill="#c59b4b" />
<Circle cx={48} cy={18} r={1.4} fill="#c59b4b" />
<Circle cx={18} cy={48} r={1.4} fill="#c59b4b" />
</G>
</Defs>
<Path fill="#fdfdfd" d="M0 0h400v580H0z" />
<Path
fill="none"
stroke="#c59b4b"
strokeWidth={1.2}
d="M20 14h360v552H20z"
/> />
{qrBase64 ? ( <Use href="#a4" />
<Image <Use href="#a4" transform="matrix(-1 0 0 1 400 0)" />
href={`data:image/png;base64,${qrBase64}`} <Use href="#a4" transform="matrix(1 0 0 -1 0 580)" />
x={8} <Use href="#a4" transform="rotate(180 200 290)" />
y={8} <Path
width={134} fill="none"
height={134} stroke="#c59b4b"
strokeLinecap="round"
strokeWidth={1.2}
d="M132 62c8-4 16 2 24-2 4-2 2-6-2-4m114 6c-8-4-16 2-24-2-4-2-2-6 2-4"
/>
<Use href="#b4" transform="translate(0 310)" />
<Use href="#b4" transform="matrix(-1 0 0 1 400 310)" />
<Path
fill="none"
stroke="#c59b4b"
strokeLinecap="square"
strokeWidth={2.2}
d="M114 278v-58h54m118 58v-58h-54M114 342v58h54m118-58v58h-54"
/>
{/* QR Placeholder / Live QR */}
<G transform="translate(125 230)">
<Rect
width={150}
height={150}
fill="#fff"
stroke="#eae5d9"
strokeWidth={1.5}
rx={12}
/> />
) : ( {qrBase64 ? (
<> <Image
<Rect href={`data:image/png;base64,${qrBase64}`}
width={134}
height={134}
x={8} x={8}
y={8} y={8}
fill="#fafaf8" width={134}
stroke="#c59b4b" height={134}
strokeDasharray="4 3"
strokeOpacity={0.35}
rx={8}
/> />
<Text ) : (
x={75} <>
y={79} <Rect
fill="#c59b4b" width={134}
fontFamily="Inter, Arial, sans-serif" height={134}
fontSize={10} x={8}
fontWeight={700} y={8}
letterSpacing={1} fill="#fafaf8"
textAnchor="middle" stroke="#c59b4b"
> strokeDasharray="4 3"
{"QR KOD ALANI"} strokeOpacity={0.35}
</Text> rx={8}
</> />
)} <Text
</G> x={75}
y={79}
fill="#c59b4b"
fontFamily="System, sans-serif"
fontSize={10}
fontWeight={700}
letterSpacing={1}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</>
)}
</G>
{/* Metinler */} {/* Metinler */}
<G fill="#111" textAnchor="middle"> <G fill="#111" textAnchor="middle">
{/* Masa No */} {/* Masa No */}
{showTableNo && tableNo ? ( {table?.visible !== false &&
<Text renderSvgLines({
x={200} text: table?.text || "MASA NO: 01",
y={56} x: 200,
fill="#c59b4b" y: 56,
fontFamily={fontFamily} fill: "#c59b4b",
fontSize={11} fontFamily: infoFont,
fontWeight={700} fontSize: table?.fontSize || 11,
letterSpacing={1.5} fontWeight: "bold",
> letterSpacing: 1.5,
{tableNo} })}
</Text>
) : null}
{/* Restoran Başlığı */} {/* Restoran Başlığı (Enter ile alt satıra geçebilir) */}
{restaurantName ? ( {rName?.visible !== false &&
<Text renderSvgLines({
x={200} text: rName?.text || "THE CRIMSON PETAL",
y={116} x: 200,
fill="#111111" y: 114,
fontFamily={fontFamily} fill: "#111111",
fontSize={26} fontFamily: rNameFont,
fontWeight={700} fontSize: rName?.fontSize || 24,
letterSpacing={2} fontWeight: "bold",
> letterSpacing: 1.5,
{restaurantName} })}
</Text>
) : null}
{/* Slogan */} {/* Slogan */}
{showSubtitle && subtitle ? ( {sub?.visible !== false &&
<Text renderSvgLines({
x={200} text: sub?.text || "FINE DINING EXPERIENCE",
y={154} x: 200,
fill="#71717a" y: 154,
fontFamily={fontFamily} fill: "#71717a",
fontSize={11} fontFamily: subFont,
fontWeight={600} fontSize: sub?.fontSize || 11,
letterSpacing={1.8} fontWeight: 600,
> letterSpacing: 1.8,
{subtitle} })}
</Text>
) : null}
{/* CTA Metni */} {/* CTA Metni */}
{showCta && ctaText ? ( {cta?.visible !== false &&
<Text renderSvgLines({
x={200} text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN",
y={440} x: 200,
fill="#111111" y: 440,
fontFamily={fontFamily} fill: "#111111",
fontSize={15} fontFamily: ctaFont,
fontWeight={700} fontSize: cta?.fontSize || 15,
letterSpacing={1} fontWeight: "bold",
> letterSpacing: 1,
{ctaText} })}
</Text>
) : null}
{/* WiFi Bilgisi */} {/* WiFi Bilgisi */}
{showWifi && wifiText ? ( {wifi?.visible !== false &&
<Text renderSvgLines({
x={200} text: wifi?.text || "WiFi: CrimsonGuest | Şifre: petal2026",
y={488} x: 200,
fill="#71717a" y: 488,
fontFamily="System, -apple-system, Roboto, sans-serif" fill: "#71717a",
fontSize={10.5} fontFamily: infoFont,
fontWeight={600} fontSize: wifi?.fontSize || 10.5,
letterSpacing={0.5} fontWeight: 600,
> letterSpacing: 0.5,
{wifiText} })}
</Text>
) : null}
{/* Sosyal Medya */} {/* Sosyal Medya */}
{showSocial && socialText ? ( {social?.visible !== false &&
<Text renderSvgLines({
x={200} text: social?.text || "@thecrimsonpetal",
y={514} x: 200,
fill="#c59b4b" y: 514,
fontFamily="System, -apple-system, Roboto, sans-serif" fill: "#c59b4b",
fontSize={10.5} fontFamily: infoFont,
fontWeight={600} fontSize: social?.fontSize || 10.5,
letterSpacing={0.5} fontWeight: 600,
> letterSpacing: 0.5,
{socialText} })}
</Text> </G>
) : null} </Svg>
</G> );
</Svg> };
);
export default QrTemplate4; export default QrTemplate4;
@@ -6,183 +6,174 @@ import Svg, {
Path, Path,
Text, Text,
} from "react-native-svg"; } from "react-native-svg";
import type { QrTemplateProps } from "./index"; import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./index";
// eslint-disable-next-line @typescript-eslint/no-require-imports // eslint-disable-next-line @typescript-eslint/no-require-imports
const bgImage = require("../../../assets/templates/5.png"); const bgImage = require("../../../assets/templates/5.png");
const QrTemplate5 = ({ const QrTemplate5 = ({
qrBase64, qrBase64,
restaurantName = "THE URBAN FORK", fields,
subtitle = "FRESH FLAVORS DAILY",
tableNo = "MASA NO: 01",
ctaText = "DİJİTAL MENÜYÜ GÖRÜNTÜLE",
wifiText = "WiFi: UrbanFork | Şifre: urban2026",
socialText = "@theurbanfork",
fontFamily = "System, -apple-system, Roboto, Arial, sans-serif",
showTableNo = true,
showSubtitle = true,
showCta = true,
showWifi = true,
showSocial = true,
...props ...props
}: QrTemplateProps) => ( }: QrTemplateProps) => {
<Svg width={300} height={394.7} viewBox="0 0 380 500" {...props}> const rName = fields?.restaurantName;
{/* PNG Arka Plan */} const sub = fields?.subtitle;
<Image const table = fields?.tableNo;
width={380} const cta = fields?.ctaText;
height={500} const wifi = fields?.wifiText;
href={bgImage} const social = fields?.socialText;
preserveAspectRatio="none"
/>
{/* QR Placeholder / Live QR */} const rNameFont = getFontFamily(rName?.fontId, "System, -apple-system, Roboto, sans-serif");
<G transform="translate(108 206)"> const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
<Rect const ctaFont = getFontFamily(cta?.fontId, "System, -apple-system, Roboto, sans-serif");
width={164} const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
height={164}
fill="#fff" return (
stroke="#dce5ea" <Svg width={300} height={394.7} viewBox="0 0 380 500" {...props}>
strokeWidth={1.2} {/* PNG Arka Plan */}
rx={16} <Image
width={380}
height={500}
href={bgImage}
preserveAspectRatio="none"
/> />
<Path
fill="none" {/* QR Placeholder / Live QR */}
stroke="#003554" <G transform="translate(108 206)">
strokeLinecap="round" <Rect
strokeWidth={3} width={164}
d="M6 30V12a6 6 0 0 1 6-6h18m128 24V12a6 6 0 0 0-6-6h-18M6 134v18a6 6 0 0 0 6 6h18m128-24v18a6 6 0 0 1-6 6h-18" height={164}
/> fill="#fff"
{qrBase64 ? ( stroke="#dce5ea"
<Image strokeWidth={1.2}
href={`data:image/png;base64,${qrBase64}`} rx={16}
x={12}
y={12}
width={140}
height={140}
/> />
) : ( <Path
<> fill="none"
<Rect stroke="#003554"
width={140} strokeLinecap="round"
height={140} strokeWidth={3}
d="M6 30V12a6 6 0 0 1 6-6h18m128 24V12a6 6 0 0 0-6-6h-18M6 134v18a6 6 0 0 0 6 6h18m128-24v18a6 6 0 0 1-6 6h-18"
/>
{qrBase64 ? (
<Image
href={`data:image/png;base64,${qrBase64}`}
x={12} x={12}
y={12} y={12}
fill="#fafcfd" width={140}
stroke="#003554" height={140}
strokeDasharray="4 3"
strokeOpacity={0.2}
rx={10}
/> />
<Text ) : (
x={82} <>
y={87} <Rect
fill="#003554" width={140}
fontFamily="Inter, Arial, sans-serif" height={140}
fontSize={11} x={12}
fontWeight={700} y={12}
letterSpacing={1} fill="#fafcfd"
textAnchor="middle" stroke="#003554"
> strokeDasharray="4 3"
{"QR KOD ALANI"} strokeOpacity={0.2}
</Text> rx={10}
</> />
)} <Text
</G> x={82}
y={87}
fill="#003554"
fontFamily="System, sans-serif"
fontSize={11}
fontWeight={700}
letterSpacing={1}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</>
)}
</G>
{/* Metinler */} {/* Metinler */}
<G fill="#003554" textAnchor="middle"> <G fill="#003554" textAnchor="middle">
{/* Masa Numarası */} {/* Masa Numarası */}
{showTableNo && tableNo ? ( {table?.visible !== false &&
<Text renderSvgLines({
x={190} text: table?.text || "MASA NO: 01",
y={40} x: 190,
fill="#003554" y: 40,
fontFamily={fontFamily} fill: "#003554",
fontSize={11} fontFamily: infoFont,
fontWeight={800} fontSize: table?.fontSize || 11,
letterSpacing={1.5} fontWeight: "bold",
> letterSpacing: 1.5,
{tableNo} })}
</Text>
) : null}
{/* Restoran Başlığı */} {/* Restoran Başlığı */}
{restaurantName ? ( {rName?.visible !== false &&
<Text renderSvgLines({
x={190} text: rName?.text || "THE URBAN FORK",
y={86} x: 190,
fill="#003554" y: 86,
fontFamily={fontFamily} fill: "#003554",
fontSize={24} fontFamily: rNameFont,
fontWeight={900} fontSize: rName?.fontSize || 24,
letterSpacing={1} fontWeight: "bold",
> letterSpacing: 1,
{restaurantName} })}
</Text>
) : null}
{/* Yönlendirme (CTA) */} {/* Yönlendirme (CTA) */}
{showCta && ctaText ? ( {cta?.visible !== false &&
<Text renderSvgLines({
x={190} text: cta?.text || "DİJİTAL MENÜYÜ GÖRÜNTÜLE",
y={160} x: 190,
fill="#003554" y: 160,
fontFamily={fontFamily} fill: "#003554",
fontSize={13} fontFamily: ctaFont,
fontWeight={800} fontSize: cta?.fontSize || 13,
letterSpacing={0.5} fontWeight: "bold",
> letterSpacing: 0.5,
{ctaText} })}
</Text>
) : null}
{/* Slogan */} {/* Slogan (Enter ile çok satır olabilir) */}
{showSubtitle && subtitle ? ( {sub?.visible !== false &&
<Text renderSvgLines({
x={190} text: sub?.text || "FRESH FLAVORS DAILY",
y={402} x: 190,
fill="#003554" y: 402,
fontFamily={fontFamily} fill: "#003554",
fontSize={14} fontFamily: subFont,
fontWeight={900} fontSize: sub?.fontSize || 13,
letterSpacing={0.8} fontWeight: "bold",
> letterSpacing: 0.8,
{subtitle} })}
</Text>
) : null}
{/* WiFi */} {/* WiFi */}
{showWifi && wifiText ? ( {wifi?.visible !== false &&
<Text renderSvgLines({
x={190} text: wifi?.text || "WiFi: UrbanFork | Şifre: urban2026",
y={430} x: 190,
fill="#003554" y: 430,
fontFamily="System, -apple-system, Roboto, sans-serif" fill: "#003554",
fontSize={10} fontFamily: infoFont,
fontWeight={700} fontSize: wifi?.fontSize || 10,
letterSpacing={0.2} fontWeight: 700,
> letterSpacing: 0.2,
{wifiText} })}
</Text>
) : null}
{/* Sosyal Medya */} {/* Sosyal Medya */}
{showSocial && socialText ? ( {social?.visible !== false &&
<Text renderSvgLines({
x={190} text: social?.text || "@theurbanfork",
y={454} x: 190,
fill="#003554" y: 454,
fontFamily="System, -apple-system, Roboto, sans-serif" fill: "#003554",
fontSize={10} fontFamily: infoFont,
fontWeight={700} fontSize: social?.fontSize || 10,
letterSpacing={0.2} fontWeight: 700,
> letterSpacing: 0.2,
{socialText} })}
</Text> </G>
) : null} </Svg>
</G> );
</Svg> };
);
export default QrTemplate5; export default QrTemplate5;
@@ -7,246 +7,202 @@ import Svg, {
Text, Text,
Image, Image,
} from "react-native-svg"; } from "react-native-svg";
import type { QrTemplateProps } from "./index"; import { type QrTemplateProps, getFontFamily, renderSvgLines } from "./index";
const QrTemplateLumiere = ({ const QrTemplateLumiere = ({
qrBase64, qrBase64,
restaurantName = "LUMIÈRE", fields,
subtitle = "RESTAURANT & BAR | MODERN CUISINE",
tableNo = "MASA NO: 01",
ctaText = "MENÜYÜ GÖRMEK İÇİN OKUTUN",
wifiText = "WiFi: Lumiere_Guest | Şifre: lumiere2026",
socialText = "Rezervasyon: 0212 555 0199",
fontFamily = "'Playfair Display', Georgia, serif",
showTableNo = true,
showSubtitle = true,
showCta = true,
showWifi = true,
showSocial = true,
...props ...props
}: QrTemplateProps) => ( }: QrTemplateProps) => {
<Svg const rName = fields?.restaurantName;
viewBox="0 0 600 600" const sub = fields?.subtitle;
width={300} const table = fields?.tableNo;
height={300} const cta = fields?.ctaText;
{...props} const wifi = fields?.wifiText;
> const social = fields?.socialText;
<Path fill="#0b0d13" d="M0 0h600v600H0z" />
<Rect
width={560}
height={560}
x={20}
y={20}
fill="#132034"
stroke="#d4af37"
strokeWidth={1.5}
rx={24}
/>
<Path
fill="none"
stroke="#d4af37"
strokeOpacity={0.85}
strokeWidth={1.5}
d="M56 40h488a16 16 0 0 1 16 16v488a16 16 0 0 1-16 16H56a16 16 0 0 1-16-16V56a16 16 0 0 1 16-16Z"
/>
<Rect
width={504}
height={504}
x={48}
y={48}
fill="none"
stroke="#c5a059"
strokeOpacity={0.45}
strokeWidth={0.8}
rx={8}
/>
{/* Köşe süslemeleri */}
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M40 68a18 18 0 0 0 28-28"
/>
<Circle cx={52} cy={52} r={2} fill="#d4af37" />
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M560 68a18 18 0 0 1-28-28"
/>
<Circle cx={548} cy={52} r={2} fill="#d4af37" />
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M40 532a18 18 0 0 1 28 28"
/>
<Circle cx={52} cy={548} r={2} fill="#d4af37" />
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M560 532a18 18 0 0 0-28 28"
/>
<Circle cx={548} cy={548} r={2} fill="#d4af37" />
{/* Merkez amblem */} const rNameFont = getFontFamily(rName?.fontId, "Georgia, 'Playfair Display', serif");
<G transform="translate(300 85)"> const subFont = getFontFamily(sub?.fontId, "System, -apple-system, Roboto, sans-serif");
<Path const ctaFont = getFontFamily(cta?.fontId, "Georgia, 'Playfair Display', serif");
fill="none" const infoFont = getFontFamily(wifi?.fontId, "System, -apple-system, Roboto, sans-serif");
stroke="#d4af37"
strokeLinejoin="round"
strokeWidth={2}
d="M0-26C-8-16-8-6 0 0c8-6 8-16 0-26Z"
/>
<Path fill="#d4af37" d="M0-18C-4-12-4-4 0 0c4-4 4-12 0-18" />
<Path
fill="none"
stroke="#d4af37"
strokeLinejoin="round"
strokeWidth={2}
d="M-3 3c-13-2-25 7-21 21C-12 24-3 12-3 3Zm6 0c13-2 25 7 21 21C12 24 3 12 3 3Z"
/>
<Circle cy={10} r={1.8} fill="#d4af37" />
</G>
{/* Yatay çizgi */} return (
<Path <Svg viewBox="0 0 600 600" width={300} height={300} {...props}>
stroke="#d4af37" <Path fill="#0b0d13" d="M0 0h600v600H0z" />
strokeOpacity={0.4}
strokeWidth={0.8}
d="M120 198h360"
/>
{/* QR Placeholder / Gerçek QR */}
<G transform="translate(190 215)">
<Rect <Rect
width={220} width={560}
height={220} height={560}
fill="#fff" x={20}
y={20}
fill="#132034"
stroke="#d4af37" stroke="#d4af37"
strokeWidth={2} strokeWidth={1.5}
rx={20} rx={24}
/> />
{qrBase64 ? ( <Path
<Image fill="none"
href={`data:image/png;base64,${qrBase64}`} stroke="#d4af37"
x={10} strokeOpacity={0.85}
y={10} strokeWidth={1.5}
width={200} d="M56 40h488a16 16 0 0 1 16 16v488a16 16 0 0 1-16 16H56a16 16 0 0 1-16-16V56a16 16 0 0 1 16-16Z"
height={200} />
<Rect
width={504}
height={504}
x={48}
y={48}
fill="none"
stroke="#c5a059"
strokeOpacity={0.45}
strokeWidth={0.8}
rx={8}
/>
{/* Köşe süslemeleri */}
<Path fill="none" stroke="#d4af37" strokeWidth={1.5} d="M40 68a18 18 0 0 0 28-28" />
<Circle cx={52} cy={52} r={2} fill="#d4af37" />
<Path fill="none" stroke="#d4af37" strokeWidth={1.5} d="M560 68a18 18 0 0 1-28-28" />
<Circle cx={548} cy={52} r={2} fill="#d4af37" />
<Path fill="none" stroke="#d4af37" strokeWidth={1.5} d="M40 532a18 18 0 0 1 28 28" />
<Circle cx={52} cy={548} r={2} fill="#d4af37" />
<Path fill="none" stroke="#d4af37" strokeWidth={1.5} d="M560 532a18 18 0 0 0-28 28" />
<Circle cx={548} cy={548} r={2} fill="#d4af37" />
{/* Merkez amblem */}
<G transform="translate(300 85)">
<Path
fill="none"
stroke="#d4af37"
strokeLinejoin="round"
strokeWidth={2}
d="M0-26C-8-16-8-6 0 0c8-6 8-16 0-26Z"
/> />
) : ( <Path fill="#d4af37" d="M0-18C-4-12-4-4 0 0c4-4 4-12 0-18" />
<> <Path
<Rect fill="none"
width={196} stroke="#d4af37"
height={196} strokeLinejoin="round"
x={12} strokeWidth={2}
y={12} d="M-3 3c-13-2-25 7-21 21C-12 24-3 12-3 3Zm6 0c13-2 25 7 21 21C12 24 3 12 3 3Z"
fill="#fafafa" />
stroke="#d4af37" <Circle cy={10} r={1.8} fill="#d4af37" />
strokeDasharray="4 3" </G>
strokeOpacity={0.4}
rx={14} {/* Yatay çizgi */}
<Path stroke="#d4af37" strokeOpacity={0.4} strokeWidth={0.8} d="M120 198h360" />
{/* QR Placeholder / Gerçek QR */}
<G transform="translate(190 215)">
<Rect width={220} height={220} fill="#fff" stroke="#d4af37" strokeWidth={2} rx={20} />
{qrBase64 ? (
<Image
href={`data:image/png;base64,${qrBase64}`}
x={10}
y={10}
width={200}
height={200}
/> />
<Text ) : (
x={110} <>
y={115} <Rect
fill="#71717a" width={196}
fontFamily="Inter, Arial, sans-serif" height={196}
fontSize={12} x={12}
fontWeight={600} y={12}
textAnchor="middle" fill="#fafafa"
> stroke="#d4af37"
{"QR KOD ALANI"} strokeDasharray="4 3"
</Text> strokeOpacity={0.4}
</> rx={14}
)} />
</G> <Text
x={110}
y={115}
fill="#71717a"
fontFamily="System, sans-serif"
fontSize={12}
fontWeight={600}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</>
)}
</G>
{/* Metinler */} {/* Metinler (Çok satırlı ve bağımsız font destekli) */}
<G textAnchor="middle"> <G textAnchor="middle">
{/* Restoran Adı */} {/* Restoran Adı */}
{restaurantName ? ( {rName?.visible !== false &&
<Text renderSvgLines({
x={300} text: rName?.text || "LUMIÈRE",
y={150} x: 300,
fill="#d4af37" y: 148,
fontFamily={fontFamily} fill: "#d4af37",
fontSize={32} fontFamily: rNameFont,
fontWeight={700} fontSize: rName?.fontSize || 30,
letterSpacing={4} fontWeight: "bold",
> letterSpacing: 4,
{restaurantName} })}
</Text>
) : null}
{/* Slogan / Alt Başlık */} {/* Slogan / Alt Başlık (Enter ile alt satıra geçebilir) */}
{showSubtitle && subtitle ? ( {sub?.visible !== false &&
<Text renderSvgLines({
x={300} text: sub?.text || "RESTAURANT & BAR | MODERN CUISINE",
y={180} x: 300,
fill="#e5c378" y: 178,
fontFamily="Inter, Arial, sans-serif" fill: "#e5c378",
fontSize={11} fontFamily: subFont,
fontWeight={600} fontSize: sub?.fontSize || 11,
letterSpacing={3} fontWeight: 600,
> letterSpacing: 2.5,
{subtitle} })}
</Text>
) : null}
{/* CTA / Yönlendirme */} {/* CTA */}
{showCta && ctaText ? ( {cta?.visible !== false &&
<Text renderSvgLines({
x={300} text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN",
y={472} x: 300,
fill="#d4af37" y: 472,
fontFamily={fontFamily} fill: "#d4af37",
fontSize={13} fontFamily: ctaFont,
fontWeight={700} fontSize: cta?.fontSize || 13,
letterSpacing={2} fontWeight: "bold",
> letterSpacing: 2,
{ctaText} })}
</Text>
) : null}
{/* Sosyal / İletişim */} {/* Sosyal Medya / İletişim */}
{showSocial && socialText ? ( {social?.visible !== false &&
<Text renderSvgLines({
x={300} text: social?.text || "Rezervasyon: 0212 555 0199",
y={508} x: 300,
fill="#e2ddd2" y: 504,
fontFamily="Inter, Arial, sans-serif" fill: "#e2ddd2",
fontSize={10.5} fontFamily: infoFont,
fontWeight={500} fontSize: social?.fontSize || 10.5,
letterSpacing={1} fontWeight: 500,
> letterSpacing: 1,
{socialText} })}
</Text>
) : null}
{/* Masa No & WiFi */} {/* Masa No & WiFi */}
{showTableNo || showWifi ? ( {(table?.visible !== false || wifi?.visible !== false) &&
<Text renderSvgLines({
x={300} text: [
y={528} table?.visible !== false && table?.text ? table.text : "",
fill="#c5a059" wifi?.visible !== false && wifi?.text ? wifi.text : "",
fontFamily="Inter, Arial, sans-serif" ]
fontSize={10} .filter(Boolean)
fontWeight={600} .join(" • "),
letterSpacing={1} x: 300,
> y: 526,
{[ fill: "#c5a059",
showTableNo && tableNo ? tableNo : "", fontFamily: infoFont,
showWifi && wifiText ? wifiText : "", fontSize: table?.fontSize || wifi?.fontSize || 10,
] fontWeight: 600,
.filter(Boolean) letterSpacing: 1,
.join(" • ")} })}
</Text> </G>
) : null} </Svg>
</G> );
</Svg> };
);
export default QrTemplateLumiere; export default QrTemplateLumiere;
@@ -1,77 +0,0 @@
import type React from "react";
import type { SvgProps } from "react-native-svg";
import QrTemplate1 from "./QrTemplateLumiere";
import QrTemplate2 from "./QrTemplate2";
import QrTemplate3 from "./QrTemplate3";
import QrTemplate4 from "./QrTemplate4";
import QrTemplate5 from "./QrTemplate5";
export interface QrCustomTextConfig {
restaurantName?: string;
subtitle?: string;
tableNo?: string;
ctaText?: string;
wifiText?: string;
socialText?: string;
fontFamily?: string;
// Görünürlük anahtarları
showTableNo?: boolean;
showSubtitle?: boolean;
showCta?: boolean;
showWifi?: boolean;
showSocial?: boolean;
}
export interface QrTemplateProps extends SvgProps, QrCustomTextConfig {
qrBase64?: string;
}
// Türkçe karakterleri (İ, ı, Ş, ş, Ğ, ğ, Ü, ü, Ö, ö, Ç, ç) eksiksiz destekleyen sistem fontları
export const FONT_OPTIONS = [
{
id: "sans",
label: "Modern Sans (Sistem)",
fontFamily: "System, -apple-system, Roboto, 'Helvetica Neue', Arial, sans-serif",
},
{
id: "serif",
label: "Zarif Serif (Georgia)",
fontFamily: "Georgia, 'Times New Roman', serif",
},
{
id: "times",
label: "Klasik (Times New Roman)",
fontFamily: "'Times New Roman', Times, Georgia, serif",
},
{
id: "palatino",
label: "Lüks Palatino",
fontFamily: "Palatino, 'Palatino Linotype', Georgia, serif",
},
{
id: "trebuchet",
label: "Dinamik (Trebuchet)",
fontFamily: "'Trebuchet MS', 'Segoe UI', Arial, sans-serif",
},
];
// Yeni component eklemek için buraya import edip COMPONENT_TEMPLATES'e kaydet
export const COMPONENT_TEMPLATES: Record<
string,
React.FC<QrTemplateProps>
> = {
"qr-1": QrTemplate1,
"qr-2": QrTemplate2,
"qr-3": QrTemplate3,
"qr-4": QrTemplate4,
"qr-5": QrTemplate5,
};
// Her template'in aspect ratio'su (width/height)
export const TEMPLATE_ASPECT_RATIOS: Record<string, number> = {
"qr-1": 1, // 600x600 = 1:1
"qr-2": 400 / 650, // 400x650 = standart kart
"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
};
@@ -0,0 +1,133 @@
import React from "react";
import { Text as SvgText, type SvgProps } from "react-native-svg";
import QrTemplate1 from "./QrTemplateLumiere";
import QrTemplate2 from "./QrTemplate2";
import QrTemplate3 from "./QrTemplate3";
import QrTemplate4 from "./QrTemplate4";
import QrTemplate5 from "./QrTemplate5";
export interface TextItemConfig {
text: string;
fontSize?: number;
fontId?: string;
visible?: boolean;
}
export interface QrCustomFields {
restaurantName: TextItemConfig;
subtitle: TextItemConfig;
tableNo: TextItemConfig;
ctaText: TextItemConfig;
wifiText: TextItemConfig;
socialText: TextItemConfig;
}
export interface QrTemplateProps extends SvgProps {
qrBase64?: string;
fields?: Partial<QrCustomFields>;
}
// Türkçe karakterleri (İ, ı, Ş, ş, Ğ, ğ, Ü, ü, Ö, ö, Ç, ç) eksiksiz destekleyen sistem fontları
export const FONT_OPTIONS = [
{
id: "sans",
label: "Modern Sans",
fontFamily: "System, -apple-system, Roboto, 'Helvetica Neue', Arial, sans-serif",
},
{
id: "serif",
label: "Zarif Serif",
fontFamily: "Georgia, 'Times New Roman', serif",
},
{
id: "times",
label: "Klasik (Times)",
fontFamily: "'Times New Roman', Times, Georgia, serif",
},
{
id: "palatino",
label: "Lüks Palatino",
fontFamily: "Palatino, 'Palatino Linotype', Georgia, serif",
},
{
id: "trebuchet",
label: "Dinamik",
fontFamily: "'Trebuchet MS', 'Segoe UI', Arial, sans-serif",
},
];
export function getFontFamily(fontId?: string, defaultFamily = "Georgia, serif"): string {
if (!fontId) return defaultFamily;
const found = FONT_OPTIONS.find((f) => f.id === fontId);
return found ? found.fontFamily : defaultFamily;
}
// Çok satırlı (Enter / \n) metinleri SVG içinde alt alta kusursuz render eden yardımcı fonksiyon
export function renderSvgLines({
text,
x,
y,
lineHeight,
fill,
fontFamily,
fontSize,
fontWeight = "normal",
letterSpacing,
textAnchor = "middle",
}: {
text?: string;
x: number;
y: number;
lineHeight?: number;
fill?: string;
fontFamily?: string;
fontSize?: number;
fontWeight?: string | number;
letterSpacing?: number;
textAnchor?: "middle" | "start" | "end";
}) {
if (!text) return null;
const lines = text.split("\n");
const computedLineHeight = lineHeight || (fontSize ? fontSize * 1.25 : 18);
return (
<>
{lines.map((line, idx) => (
<SvgText
key={idx}
x={x}
y={y + idx * computedLineHeight}
fill={fill}
fontFamily={fontFamily}
fontSize={fontSize}
fontWeight={fontWeight}
letterSpacing={letterSpacing}
textAnchor={textAnchor}
>
{line}
</SvgText>
))}
</>
);
}
// Yeni component eklemek için buraya import edip COMPONENT_TEMPLATES'e kaydet
export const COMPONENT_TEMPLATES: Record<
string,
React.FC<QrTemplateProps>
> = {
"qr-1": QrTemplate1,
"qr-2": QrTemplate2,
"qr-3": QrTemplate3,
"qr-4": QrTemplate4,
"qr-5": QrTemplate5,
};
// Her template'in aspect ratio'su (width/height)
export const TEMPLATE_ASPECT_RATIOS: Record<string, number> = {
"qr-1": 1, // 600x600 = 1:1
"qr-2": 400 / 650, // 400x650 = standart kart
"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
};