feat(mobile): add QrTemplate6 Stonehearth Grill, modal text editor and vertical positioning (offsetY) controls
This commit is contained in:
+427
-184
@@ -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<keyof QrCustomFields | null>(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<any>(null);
|
||||
|
||||
@@ -107,6 +127,7 @@ export default function QrScreen() {
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const activeRest = await getActiveRestaurant();
|
||||
@@ -389,7 +410,55 @@ export default function QrScreen() {
|
||||
</ViewShot>
|
||||
</View>
|
||||
|
||||
{/* Metin & Font & Boyut Özelleştirme Kartı (Açılır/Kapanır) */}
|
||||
{/* Hızlı Katman Seçici (Tıklayınca Modal Açar) */}
|
||||
<View style={{ width: "100%", marginBottom: 16 }}>
|
||||
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700", marginBottom: 8, letterSpacing: 0.5 }}>
|
||||
DÜZENLEMEK VE KONUMUNU DEĞİŞTİRMEK İÇİN TIKLAYIN:
|
||||
</Text>
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 8 }}>
|
||||
{FIELD_DEFINITIONS.map(({ key, label, icon }) => {
|
||||
const item = fields[key];
|
||||
return (
|
||||
<Pressable
|
||||
key={key}
|
||||
onPress={() => 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,
|
||||
})}
|
||||
>
|
||||
<Ionicons name={icon as any} size={14} color={item.visible ? "#D4AF37" : "#71717A"} />
|
||||
<Text
|
||||
style={{
|
||||
color: item.visible ? "#FFFFFF" : "#71717A",
|
||||
fontSize: 12,
|
||||
fontWeight: "700",
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
{item.offsetY ? (
|
||||
<View style={{ backgroundColor: "#D4AF37", borderRadius: 6, paddingHorizontal: 4, paddingVertical: 1 }}>
|
||||
<Text style={{ color: "#0C0C0E", fontSize: 9, fontWeight: "800" }}>
|
||||
{item.offsetY > 0 ? `+${item.offsetY}` : item.offsetY}px
|
||||
</Text>
|
||||
</View>
|
||||
) : null}
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* Metin & Font & Boyut Özelleştirme Kartı (Açılır/Kapanır Liste) */}
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: "#18181B",
|
||||
@@ -423,10 +492,10 @@ export default function QrScreen() {
|
||||
</View>
|
||||
<View>
|
||||
<Text style={{ color: "#FFFFFF", fontSize: 15, fontWeight: "700" }}>
|
||||
Metin, Font & Boyut Özelleştir
|
||||
Tüm Katmanları Listele
|
||||
</Text>
|
||||
<Text style={{ color: "#A1A1AA", fontSize: 12 }}>
|
||||
Çok satırlı metin (Enter), özel font ve boyut ayarı
|
||||
Tıkla & Düzenle, dikey konum kaydırma ve font ayarı
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -438,200 +507,374 @@ export default function QrScreen() {
|
||||
</Pressable>
|
||||
|
||||
{showCustomizePanel && (
|
||||
<View style={{ marginTop: 16, gap: 16, borderTopWidth: 1, borderTopColor: "rgba(255,255,255,0.06)", paddingTop: 16 }}>
|
||||
{/* Yardımcı Açıklama */}
|
||||
<View
|
||||
style={{
|
||||
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'a basabilirsiniz.
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* 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 }) => {
|
||||
<View style={{ marginTop: 16, gap: 12, borderTopWidth: 1, borderTopColor: "rgba(255,255,255,0.06)", paddingTop: 16 }}>
|
||||
{FIELD_DEFINITIONS.map(({ key, label, icon }) => {
|
||||
const item = fields[key];
|
||||
return (
|
||||
<View
|
||||
<Pressable
|
||||
key={key}
|
||||
style={{
|
||||
gap: 8,
|
||||
paddingBottom: 12,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: "rgba(255,255,255,0.06)",
|
||||
}}
|
||||
onPress={() => 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ı */}
|
||||
<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"}
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 10, flex: 1 }}>
|
||||
<Ionicons name={icon as any} size={18} color="#D4AF37" />
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text style={{ color: "#FFFFFF", fontSize: 13, fontWeight: "700" }}>
|
||||
{label}
|
||||
</Text>
|
||||
<Text style={{ color: "#A1A1AA", fontSize: 11 }} numberOfLines={1}>
|
||||
{item.text || "Boş"}
|
||||
</Text>
|
||||
<Switch
|
||||
value={item.visible}
|
||||
onValueChange={(v) =>
|
||||
setFields((prev) => ({
|
||||
...prev,
|
||||
[key]: { ...prev[key], visible: v },
|
||||
}))
|
||||
}
|
||||
trackColor={{ false: "#27272A", true: "#D4AF37" }}
|
||||
thumbColor="#FFFFFF"
|
||||
ios_backgroundColor="#27272A"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{item.visible && (
|
||||
<>
|
||||
{/* Çok Satırlı TextInput */}
|
||||
<TextInput
|
||||
value={item.text}
|
||||
onChangeText={(t) =>
|
||||
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 */}
|
||||
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 8 }}>
|
||||
{/* Font Hapları */}
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{ gap: 6 }}
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
{FONT_OPTIONS.map((f) => {
|
||||
const isSelected = item.fontId === f.id;
|
||||
return (
|
||||
<Pressable
|
||||
key={f.id}
|
||||
onPress={() =>
|
||||
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 style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: "#242429",
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 8,
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: "#D4AF37", fontSize: 10, fontWeight: "700" }}>
|
||||
{item.fontSize || 12}px
|
||||
</Text>
|
||||
</View>
|
||||
<Ionicons name="create-outline" size={18} color="#D4AF37" />
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* Modal: Tıklanan Metin Katmanını Detaylı Düzenleme */}
|
||||
<Modal
|
||||
visible={!!selectedEditField}
|
||||
animationType="slide"
|
||||
transparent
|
||||
onRequestClose={() => setSelectedEditField(null)}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: "rgba(0,0,0,0.75)",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
{selectedEditField && (() => {
|
||||
const def = FIELD_DEFINITIONS.find((d) => d.key === selectedEditField)!;
|
||||
const item = fields[selectedEditField];
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: "#18181B",
|
||||
borderTopLeftRadius: 28,
|
||||
borderTopRightRadius: 28,
|
||||
padding: 20,
|
||||
maxHeight: "85%",
|
||||
gap: 16,
|
||||
borderTopWidth: 1,
|
||||
borderColor: "rgba(212, 175, 55, 0.3)",
|
||||
}}
|
||||
>
|
||||
{/* Modal Başlığı & Kapat Butonu */}
|
||||
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
||||
<Ionicons name={def.icon as any} size={20} color="#D4AF37" />
|
||||
<Text style={{ color: "#FFFFFF", fontSize: 17, fontWeight: "800" }}>
|
||||
{def.label}
|
||||
</Text>
|
||||
</View>
|
||||
<Pressable
|
||||
onPress={() => setSelectedEditField(null)}
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 16,
|
||||
backgroundColor: "#242429",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Ionicons name="close" size={18} color="#FFFFFF" />
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ gap: 16, paddingBottom: 20 }}>
|
||||
{/* Görünürlük Anahtarı */}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#121215",
|
||||
padding: 12,
|
||||
borderRadius: 14,
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: "#FFFFFF", fontSize: 13, fontWeight: "700" }}>
|
||||
Stantta Göster
|
||||
</Text>
|
||||
<Switch
|
||||
value={item.visible}
|
||||
onValueChange={(v) =>
|
||||
setFields((prev) => ({
|
||||
...prev,
|
||||
[selectedEditField]: { ...prev[selectedEditField], visible: v },
|
||||
}))
|
||||
}
|
||||
trackColor={{ false: "#27272A", true: "#D4AF37" }}
|
||||
thumbColor="#FFFFFF"
|
||||
ios_backgroundColor="#27272A"
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* Metin Girişi (Multiline / Enter Destekli) */}
|
||||
<View style={{ gap: 6 }}>
|
||||
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
|
||||
METİN İÇERİĞİ (ENTER İLE YENİ SATIR)
|
||||
</Text>
|
||||
<TextInput
|
||||
value={item.text}
|
||||
onChangeText={(t) =>
|
||||
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,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* DİKEY KONUM / YUKARI - AŞAĞI TAŞIMA (offsetY) */}
|
||||
<View style={{ gap: 8, backgroundColor: "#121215", padding: 12, borderRadius: 14 }}>
|
||||
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
|
||||
DİKEY KONUM (YUKARI / AŞAĞI)
|
||||
</Text>
|
||||
<Text style={{ color: "#E5C378", fontSize: 12, fontWeight: "800" }}>
|
||||
{item.offsetY ? `${item.offsetY > 0 ? "+" : ""}${item.offsetY} px` : "Orijinal (0px)"}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{ flexDirection: "row", gap: 8 }}>
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
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,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="arrow-up" size={14} color="#D4AF37" />
|
||||
<Text style={{ color: "#FFFFFF", fontSize: 12, fontWeight: "700" }}>Yukarı (-3px)</Text>
|
||||
</Pressable>
|
||||
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
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,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="arrow-down" size={14} color="#D4AF37" />
|
||||
<Text style={{ color: "#FFFFFF", fontSize: 12, fontWeight: "700" }}>Aşağı (+3px)</Text>
|
||||
</Pressable>
|
||||
|
||||
{item.offsetY ? (
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
setFields((prev) => ({
|
||||
...prev,
|
||||
[selectedEditField]: {
|
||||
...prev[selectedEditField],
|
||||
offsetY: 0,
|
||||
},
|
||||
}))
|
||||
}
|
||||
style={{
|
||||
backgroundColor: "#3A2A2A",
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 10,
|
||||
borderRadius: 10,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: "#EF4444", fontSize: 11, fontWeight: "700" }}>Sıfırla</Text>
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* YAZI TİPİ (FONT) SEÇİMİ */}
|
||||
<View style={{ gap: 8 }}>
|
||||
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
|
||||
YAZI TİPİ (TÜRKÇE DESTEKLİ FONT)
|
||||
</Text>
|
||||
<View style={{ flexDirection: "row", flexWrap: "wrap", gap: 8 }}>
|
||||
{FONT_OPTIONS.map((f) => {
|
||||
const isSelected = item.fontId === f.id;
|
||||
return (
|
||||
<Pressable
|
||||
key={f.id}
|
||||
onPress={() =>
|
||||
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)",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: isSelected ? "#0C0C0E" : "#FFFFFF",
|
||||
fontSize: 12,
|
||||
fontWeight: isSelected ? "800" : "500",
|
||||
}}
|
||||
>
|
||||
{f.label}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* YAZI BOYUTU (FONT SIZE) */}
|
||||
<View style={{ gap: 8, backgroundColor: "#121215", padding: 12, borderRadius: 14 }}>
|
||||
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
|
||||
YAZI BOYUTU
|
||||
</Text>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
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,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="remove" size={18} color="#FFFFFF" />
|
||||
</Pressable>
|
||||
|
||||
<Text style={{ color: "#D4AF37", fontSize: 18, fontWeight: "800" }}>
|
||||
{item.fontSize || 12} px
|
||||
</Text>
|
||||
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
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,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="add" size={18} color="#FFFFFF" />
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Kaydet & Tamamla Butonu */}
|
||||
<Pressable
|
||||
onPress={() => setSelectedEditField(null)}
|
||||
style={{
|
||||
backgroundColor: "#D4AF37",
|
||||
borderRadius: 14,
|
||||
paddingVertical: 14,
|
||||
alignItems: "center",
|
||||
marginTop: 6,
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: "#0C0C0E", fontSize: 15, fontWeight: "800" }}>
|
||||
Tamamla & Uygula
|
||||
</Text>
|
||||
</Pressable>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
})()}
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
|
||||
|
||||
|
||||
{/* Output Action Buttons */}
|
||||
|
||||
@@ -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,
|
||||
})}
|
||||
|
||||
|
||||
<Text
|
||||
x={200}
|
||||
y={611}
|
||||
|
||||
@@ -258,6 +258,7 @@ const QrTemplate3 = ({
|
||||
text: table?.text || "MASA NO: 01",
|
||||
x: 200,
|
||||
y: 50,
|
||||
offsetY: table?.offsetY,
|
||||
fill: "#18181b",
|
||||
fontFamily: infoFont,
|
||||
fontSize: table?.fontSize || 11,
|
||||
@@ -282,6 +283,7 @@ const QrTemplate3 = ({
|
||||
text: rName?.text || "BELLA NAPOLI",
|
||||
x: 200,
|
||||
y: 130,
|
||||
offsetY: rName?.offsetY,
|
||||
fill: "#18181b",
|
||||
fontFamily: rNameFont,
|
||||
fontSize: rName?.fontSize || 26,
|
||||
@@ -295,6 +297,7 @@ const QrTemplate3 = ({
|
||||
text: sub?.text || "AUTENTICA CUCINA ITALIANA",
|
||||
x: 200,
|
||||
y: 222,
|
||||
offsetY: sub?.offsetY,
|
||||
fill: "#2e7d32",
|
||||
fontFamily: subFont,
|
||||
fontSize: sub?.fontSize || 11.5,
|
||||
@@ -308,6 +311,7 @@ const QrTemplate3 = ({
|
||||
text: cta?.text || "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||||
x: 200,
|
||||
y: 474,
|
||||
offsetY: cta?.offsetY,
|
||||
fill: "#b92b27",
|
||||
fontFamily: ctaFont,
|
||||
fontSize: cta?.fontSize || 13,
|
||||
@@ -321,6 +325,7 @@ const QrTemplate3 = ({
|
||||
text: wifi?.text || "WiFi: GourmetBistro | Şifre: lezzetli01",
|
||||
x: 200,
|
||||
y: 546,
|
||||
offsetY: wifi?.offsetY,
|
||||
fill: "#3e2723",
|
||||
fontFamily: infoFont,
|
||||
fontSize: wifi?.fontSize || 10.5,
|
||||
@@ -333,12 +338,14 @@ const QrTemplate3 = ({
|
||||
text: social?.text || "BİZİ TAKİP EDİN | @gourmetbistro",
|
||||
x: 200,
|
||||
y: 570,
|
||||
offsetY: social?.offsetY,
|
||||
fill: "#b92b27",
|
||||
fontFamily: infoFont,
|
||||
fontSize: social?.fontSize || 10.5,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 0.5,
|
||||
})}
|
||||
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
})}
|
||||
</G>
|
||||
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 (
|
||||
<Svg width={300} height={435} viewBox="0 0 400 580" {...props}>
|
||||
<Defs>
|
||||
<G id="b6">
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#c59b58"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.2}
|
||||
d="M0 0c10 25 12 55 5 85"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.1}
|
||||
d="M3 15c-11-5-15 9-1 9 4 0 4-6 1-9Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#9e4d28"
|
||||
strokeWidth={1.1}
|
||||
d="M5 22c11-6 17 6 3 10-4 0-4-6-3-10Zm2 20c-12-6-17 8-3 10 4 0 4-6 3-10Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.1}
|
||||
d="M8 50c12-6 16 8 2 12-4 0-4-7-2-12ZM6 70c-10-5-14 8-2 10 3 0 4-6 2-10Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#9e4d28"
|
||||
strokeWidth={1.1}
|
||||
d="M5 78c11-4 15 8 3 12-4 0-4-6-3-12Z"
|
||||
/>
|
||||
<Circle cx={-10} cy={18} r={1.5} fill="#c59b58" />
|
||||
<Circle cx={18} cy={46} r={1.5} fill="#9e4d28" />
|
||||
</G>
|
||||
<G id="a6">
|
||||
<Path
|
||||
fill="#a64423"
|
||||
d="M0-18C4-12 8-6 8 0c6-4 8 6 4 12 4-2 6 4 2 8S2 26 0 26s-10-2-14-6-2-10 2-8c-4-6-2-16 4-12 0-6 4-12 8-18"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
d="M-4 4v8c0 3 2 4 4 4v8c2-8 4-9 4-12V4H3v7c0 2-1.5 3-3 3s-3-1-3-3V4Z"
|
||||
/>
|
||||
<Path stroke="#f4efe6" d="M0 4v8" />
|
||||
</G>
|
||||
</Defs>
|
||||
<Path fill="#434b54" d="M0 0h400v580H0z" />
|
||||
<Rect
|
||||
width={360}
|
||||
height={548}
|
||||
x={20}
|
||||
y={16}
|
||||
fill="#f6efe3"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.5}
|
||||
rx={16}
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.2}
|
||||
d="M46 32h308a12 12 0 0 1 12 12v492a12 12 0 0 1-12 12H46a12 12 0 0 1-12-12V44a12 12 0 0 1 12-12Z"
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.2}
|
||||
d="M34 46a12 12 0 0 0 12-12m320 12a12 12 0 0 1-12-12M34 534a12 12 0 0 1 12 12m320-12a12 12 0 0 0-12 12"
|
||||
/>
|
||||
<Path stroke="#c59b58" d="M34 135h332M34 500h332" />
|
||||
<Use href="#a6" transform="matrix(.85 0 0 .85 200 52)" />
|
||||
<Use href="#b6" transform="matrix(.8 0 0 .8 42 42)" />
|
||||
<Use href="#b6" transform="matrix(-.8 0 0 .8 358 42)" />
|
||||
<Use href="#b6" transform="matrix(.95 0 0 .95 40 185)" />
|
||||
<Use href="#b6" transform="matrix(-.95 0 0 .95 360 185)" />
|
||||
<Use href="#a6" transform="matrix(.65 0 0 .65 68 320)" />
|
||||
<Use href="#a6" transform="matrix(.65 0 0 .65 332 320)" />
|
||||
<Use href="#b6" transform="matrix(.95 0 0 .95 40 360)" />
|
||||
<Use href="#b6" transform="matrix(-.95 0 0 .95 360 360)" />
|
||||
|
||||
{/* QR Placeholder / Gerçek QR */}
|
||||
<G transform="translate(95 205)">
|
||||
<Path
|
||||
fill="#fdfbf7"
|
||||
stroke="#122a42"
|
||||
strokeWidth={3}
|
||||
d="M0 0h210v210H0z"
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.5}
|
||||
d="M4 4h202v202H4z"
|
||||
/>
|
||||
{qrBase64 ? (
|
||||
<Image
|
||||
href={`data:image/png;base64,${qrBase64}`}
|
||||
x={10}
|
||||
y={10}
|
||||
width={190}
|
||||
height={190}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Path
|
||||
fill="#faf8f5"
|
||||
stroke="#122a42"
|
||||
strokeDasharray="4 3"
|
||||
d="M12 12h186v186H12z"
|
||||
/>
|
||||
<Circle
|
||||
cx={105}
|
||||
cy={95}
|
||||
r={16}
|
||||
fill="#f6efe3"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.2}
|
||||
/>
|
||||
<Use href="#a6" transform="matrix(.45 0 0 .45 105 93)" />
|
||||
<Text
|
||||
x={105}
|
||||
y={128}
|
||||
fill="#122a42"
|
||||
fontFamily="System, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight="bold"
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</G>
|
||||
|
||||
{/* Metinler */}
|
||||
<G textAnchor="middle">
|
||||
{/* 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,
|
||||
})}
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default QrTemplate6;
|
||||
@@ -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,
|
||||
})}
|
||||
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
|
||||
@@ -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({
|
||||
<SvgText
|
||||
key={idx}
|
||||
x={x}
|
||||
y={y + idx * computedLineHeight}
|
||||
y={startY + idx * computedLineHeight}
|
||||
fill={fill}
|
||||
fontFamily={fontFamily}
|
||||
fontSize={fontSize}
|
||||
@@ -121,6 +126,7 @@ export const COMPONENT_TEMPLATES: Record<
|
||||
"qr-3": QrTemplate3,
|
||||
"qr-4": QrTemplate4,
|
||||
"qr-5": QrTemplate5,
|
||||
"qr-6": QrTemplate6,
|
||||
};
|
||||
|
||||
// Her template'in aspect ratio'su (width/height)
|
||||
@@ -130,4 +136,5 @@ export const TEMPLATE_ASPECT_RATIOS: Record<string, number> = {
|
||||
"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
|
||||
};
|
||||
|
||||
@@ -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) => (
|
||||
<Svg width={400} height={580} {...props}>
|
||||
<Defs>
|
||||
<G id="b">
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#c59b58"
|
||||
strokeLinecap="round"
|
||||
strokeWidth={1.2}
|
||||
d="M0 0c10 25 12 55 5 85"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.1}
|
||||
d="M3 15c-11-5-15 9-1 9 4 0 4-6 1-9Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#9e4d28"
|
||||
strokeWidth={1.1}
|
||||
d="M5 22c11-6 17 6 3 10-4 0-4-6-3-10Zm2 20c-12-6-17 8-3 10 4 0 4-6 3-10Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.1}
|
||||
d="M8 50c12-6 16 8 2 12-4 0-4-7-2-12ZM6 70c-10-5-14 8-2 10 3 0 4-6 2-10Z"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
stroke="#9e4d28"
|
||||
strokeWidth={1.1}
|
||||
d="M5 78c11-4 15 8 3 12-4 0-4-6-3-12Z"
|
||||
/>
|
||||
<Circle cx={-10} cy={18} r={1.5} fill="#c59b58" />
|
||||
<Circle cx={18} cy={46} r={1.5} fill="#9e4d28" />
|
||||
</G>
|
||||
<G id="a">
|
||||
<Path
|
||||
fill="#a64423"
|
||||
d="M0-18C4-12 8-6 8 0c6-4 8 6 4 12 4-2 6 4 2 8S2 26 0 26s-10-2-14-6-2-10 2-8c-4-6-2-16 4-12 0-6 4-12 8-18"
|
||||
/>
|
||||
<Path
|
||||
fill="#f4efe6"
|
||||
d="M-4 4v8c0 3 2 4 4 4v8c2-8 4-9 4-12V4H3v7c0 2-1.5 3-3 3s-3-1-3-3V4Z"
|
||||
/>
|
||||
<Path stroke="#f4efe6" d="M0 4v8" />
|
||||
</G>
|
||||
</Defs>
|
||||
<Path fill="#434b54" d="M0 0h400v580H0z" />
|
||||
<Rect
|
||||
width={360}
|
||||
height={548}
|
||||
x={20}
|
||||
y={16}
|
||||
fill="#f6efe3"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.5}
|
||||
rx={16}
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.2}
|
||||
d="M46 32h308a12 12 0 0 1 12 12v492a12 12 0 0 1-12 12H46a12 12 0 0 1-12-12V44a12 12 0 0 1 12-12Z"
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#122a42"
|
||||
strokeWidth={1.2}
|
||||
d="M34 46a12 12 0 0 0 12-12m320 12a12 12 0 0 1-12-12M34 534a12 12 0 0 1 12 12m320-12a12 12 0 0 0-12 12"
|
||||
/>
|
||||
<Path stroke="#c59b58" d="M34 135h332M34 500h332" />
|
||||
<Use href="#a" transform="matrix(.85 0 0 .85 200 52)" />
|
||||
<Use href="#b" transform="matrix(.8 0 0 .8 42 42)" />
|
||||
<Use href="#b" transform="matrix(-.8 0 0 .8 358 42)" />
|
||||
<Use href="#b" transform="matrix(.95 0 0 .95 40 185)" />
|
||||
<Use href="#b" transform="matrix(-.95 0 0 .95 360 185)" />
|
||||
<Use href="#a" transform="matrix(.65 0 0 .65 68 320)" />
|
||||
<Use href="#a" transform="matrix(.65 0 0 .65 332 320)" />
|
||||
<Use href="#b" transform="matrix(.95 0 0 .95 40 360)" />
|
||||
<Use href="#b" transform="matrix(-.95 0 0 .95 360 360)" />
|
||||
<G transform="translate(200 465)">
|
||||
<Rect
|
||||
width={18}
|
||||
height={28}
|
||||
x={-9}
|
||||
y={-14}
|
||||
fill="#122a42"
|
||||
stroke="#122a42"
|
||||
rx={3.5}
|
||||
/>
|
||||
<Rect width={14} height={20} x={-7} y={-11} fill="#fff" rx={1.5} />
|
||||
<Path fill="#122a42" d="M-4.5-8.5h9v9h-9z" />
|
||||
<Path fill="#fff" d="M-3-7h6v6h-6z" />
|
||||
<Path fill="#122a42" d="M-1.5-5.5h3v3h-3z" />
|
||||
<Circle cy={11} r={1} fill="#fff" />
|
||||
</G>
|
||||
<Circle cx={258} cy={537} r={4.5} fill="#a64423" />
|
||||
<Path
|
||||
fill="#f6efe3"
|
||||
d="M259 541h-1.5v-4h-1v-1.5h1v-1c0-1 .5-1.5 2-1.5h1v1.5h-1c-.5 0-.5.3-.5.7v.3h1.5l-.3 1.5H259Z"
|
||||
/>
|
||||
<Rect
|
||||
width={9}
|
||||
height={9}
|
||||
x={266}
|
||||
y={532.5}
|
||||
fill="none"
|
||||
stroke="#a64423"
|
||||
strokeWidth={1.2}
|
||||
rx={2.5}
|
||||
/>
|
||||
<Circle cx={270.5} cy={537} r={2} fill="none" stroke="#a64423" />
|
||||
<Circle cx={273} cy={534.5} r={0.5} fill="#a64423" />
|
||||
<G transform="translate(95 205)">
|
||||
<Path
|
||||
fill="#fdfbf7"
|
||||
stroke="#122a42"
|
||||
strokeWidth={3}
|
||||
d="M0 0h210v210H0z"
|
||||
/>
|
||||
<Path
|
||||
fill="none"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.5}
|
||||
d="M4 4h202v202H4z"
|
||||
/>
|
||||
<Path
|
||||
fill="#faf8f5"
|
||||
stroke="#122a42"
|
||||
strokeDasharray="4 3"
|
||||
d="M12 12h186v186H12z"
|
||||
/>
|
||||
<Circle
|
||||
cx={105}
|
||||
cy={95}
|
||||
r={16}
|
||||
fill="#f6efe3"
|
||||
stroke="#c59b58"
|
||||
strokeWidth={1.2}
|
||||
/>
|
||||
<Use href="#a" transform="matrix(.45 0 0 .45 105 93)" />
|
||||
<Text
|
||||
x={105}
|
||||
y={128}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={800}
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"QR KOD ALANI"}
|
||||
</Text>
|
||||
</G>
|
||||
<Text
|
||||
x={172}
|
||||
y={55}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={8.5}
|
||||
fontWeight={800}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"EST."}
|
||||
</Text>
|
||||
<Text
|
||||
x={228}
|
||||
y={55}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={8.5}
|
||||
fontWeight={800}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"2018"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={84}
|
||||
fill="#a64423"
|
||||
fontFamily="'Playfair Display', Georgia, 'Times New Roman', serif"
|
||||
fontSize={19}
|
||||
fontWeight={900}
|
||||
letterSpacing={1.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"THE STONEHEARTH GRILL"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={102}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={8.5}
|
||||
fontWeight={700}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"EST. 2018"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={118}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={10}
|
||||
fontWeight={800}
|
||||
letterSpacing={2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"SCAN. ORDER. ENJOY."}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={158}
|
||||
fill="#122a42"
|
||||
fontFamily="'Playfair Display', Georgia, 'Times New Roman', serif"
|
||||
fontSize={15.5}
|
||||
fontWeight={900}
|
||||
letterSpacing={0.5}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"VIEW OUR MENU & ORDER ONLINE"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={176}
|
||||
fill="#222"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={9}
|
||||
fontWeight={500}
|
||||
letterSpacing={0.1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"Simply scan the QR code with your phone camera to access our"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={189}
|
||||
fill="#222"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={9}
|
||||
fontWeight={500}
|
||||
letterSpacing={0.1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"digital menu, daily specials, wine list, and place your order."}
|
||||
</Text>
|
||||
<Text
|
||||
x={162}
|
||||
y={468}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={800}
|
||||
letterSpacing={1}
|
||||
textAnchor="end"
|
||||
>
|
||||
{"NO APP"}
|
||||
</Text>
|
||||
<Text
|
||||
x={238}
|
||||
y={468}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={11}
|
||||
fontWeight={800}
|
||||
letterSpacing={1}
|
||||
>
|
||||
{"REQUIRED"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={515}
|
||||
fill="#122a42"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={10.5}
|
||||
fontWeight={800}
|
||||
letterSpacing={1}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"WWW.THESTONEHEARTHGRILL.COM"}
|
||||
</Text>
|
||||
<Text
|
||||
x={200}
|
||||
y={527}
|
||||
fill="#222"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={9}
|
||||
fontWeight={600}
|
||||
letterSpacing={0.2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"FREE WIFI: StonehearthGuest (PW: Grill720)"}
|
||||
</Text>
|
||||
<Text
|
||||
x={195}
|
||||
y={540}
|
||||
fill="#222"
|
||||
fontFamily="Inter, Arial, sans-serif"
|
||||
fontSize={9}
|
||||
fontWeight={600}
|
||||
letterSpacing={0.2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{"Follow Us @StonehearthGrill"}
|
||||
</Text>
|
||||
</Svg>
|
||||
)
|
||||
const Memo = memo(SvgComponent)
|
||||
export default Memo
|
||||
@@ -90,4 +90,19 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
|
||||
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)",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user