799 lines
29 KiB
TypeScript
799 lines
29 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
||
import { router } from "expo-router";
|
||
import {
|
||
ActivityIndicator,
|
||
Alert,
|
||
Pressable,
|
||
ScrollView,
|
||
Share,
|
||
Switch,
|
||
Text,
|
||
TextInput,
|
||
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";
|
||
import * as Print from "expo-print";
|
||
import { QR_STAND_PRESETS, type QrStandPreset } from "@menulio/shared";
|
||
import { api } from "@/lib/api";
|
||
import { getActiveRestaurant, type ActiveRestaurant } from "@/lib/active-restaurant";
|
||
import {
|
||
COMPONENT_TEMPLATES,
|
||
TEMPLATE_ASPECT_RATIOS,
|
||
FONT_OPTIONS,
|
||
type QrCustomTextConfig,
|
||
} from "@/components/qr-templates";
|
||
|
||
|
||
interface QrResponse {
|
||
id: string;
|
||
redirectUrl: string;
|
||
targetUrl: string;
|
||
pngBase64: string;
|
||
}
|
||
|
||
export default function QrScreen() {
|
||
const [active, setActive] = useState<ActiveRestaurant | null>(null);
|
||
const [restaurantName, setRestaurantName] = useState<string>("Gourmet Bistro");
|
||
const [qr, setQr] = useState<QrResponse | null>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState<string | null>(null);
|
||
const [copied, setCopied] = useState(false);
|
||
const availablePresets = Object.values(QR_STAND_PRESETS).filter(
|
||
(p) => !!COMPONENT_TEMPLATES[p.key]
|
||
);
|
||
const [selectedKey, setSelectedKey] = useState<string>(
|
||
availablePresets[0]?.key || "qr-lumiere"
|
||
);
|
||
const [exportingPng, setExportingPng] = useState(false);
|
||
const [exportingPdf, setExportingPdf] = useState(false);
|
||
const [showCustomizePanel, setShowCustomizePanel] = useState(false);
|
||
const [selectedFontId, setSelectedFontId] = useState<string>("didot");
|
||
const [customText, setCustomText] = useState<QrCustomTextConfig>({
|
||
restaurantName: "Gourmet Bistro",
|
||
subtitle: "RESTAURANT & BAR | MODERN CUISINE",
|
||
tableNo: "MASA NO: 01",
|
||
ctaText: "MENÜYÜ GÖRMEK İÇİN OKUTUN",
|
||
wifiText: "WiFi: Gourmet_Guest | Şifre: lezzetli01",
|
||
socialText: "@gourmetbistro",
|
||
showTableNo: true,
|
||
showSubtitle: true,
|
||
showCta: true,
|
||
showWifi: true,
|
||
showSocial: true,
|
||
});
|
||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
const viewShotRef = useRef<any>(null);
|
||
|
||
const activePreset: QrStandPreset =
|
||
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
|
||
const TemplateComponent =
|
||
COMPONENT_TEMPLATES[selectedKey] ?? Object.values(COMPONENT_TEMPLATES)[0];
|
||
const aspectRatio = TEMPLATE_ASPECT_RATIOS[selectedKey] ?? 1;
|
||
const displayWidth = 300;
|
||
const displayHeight = Math.round(displayWidth / aspectRatio);
|
||
|
||
|
||
|
||
useEffect(() => {
|
||
(async () => {
|
||
const activeRest = await getActiveRestaurant();
|
||
if (!activeRest) return;
|
||
setActive(activeRest);
|
||
|
||
try {
|
||
const data = await api.post<QrResponse>(`/restaurants/${activeRest.restaurantId}/qr`);
|
||
setQr(data);
|
||
|
||
// Fetch detailed restaurant name
|
||
try {
|
||
const restDetail = await api.get<{ name?: string }>(`/restaurants/${activeRest.restaurantId}`);
|
||
if (restDetail?.name) {
|
||
setRestaurantName(restDetail.name);
|
||
setCustomText((prev) => ({
|
||
...prev,
|
||
restaurantName: restDetail.name,
|
||
}));
|
||
}
|
||
} catch {
|
||
// fallback
|
||
}
|
||
} catch (err) {
|
||
setError(err instanceof Error ? err.message : "QR oluşturulamadı");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
})();
|
||
}, []);
|
||
|
||
|
||
async function handleCopy() {
|
||
if (!qr?.targetUrl) return;
|
||
await Clipboard.setStringAsync(qr.targetUrl);
|
||
setCopied(true);
|
||
setTimeout(() => setCopied(false), 2000);
|
||
}
|
||
|
||
async function handleShareQuick() {
|
||
if (!qr?.targetUrl) return;
|
||
await Share.share({
|
||
title: "Dijital QR Menü",
|
||
message: `${qr.targetUrl}\nMenümüzü incelemek için QR kodu taratın veya linke tıklayın.`,
|
||
url: qr.targetUrl,
|
||
});
|
||
}
|
||
|
||
async function handleExportPng() {
|
||
if (!viewShotRef.current) return;
|
||
setExportingPng(true);
|
||
try {
|
||
const uri = await viewShotRef.current.capture?.();
|
||
if (uri) {
|
||
await Sharing.shareAsync(uri, {
|
||
mimeType: "image/png",
|
||
dialogTitle: "Masa Stant QR Görselini Paylaş / Kaydet",
|
||
});
|
||
}
|
||
} catch {
|
||
Alert.alert("Hata", "Görsel oluşturulamadı.");
|
||
} finally {
|
||
setExportingPng(false);
|
||
}
|
||
}
|
||
|
||
async function handleExportPdf() {
|
||
if (!qr || !active) return;
|
||
setExportingPdf(true);
|
||
try {
|
||
// ViewShot ile önce PNG al, sonra A5 HTML içine göm
|
||
const pngUri = await viewShotRef.current?.capture();
|
||
if (!pngUri) throw new Error("Görsel alınamadı");
|
||
|
||
const htmlContent = `
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8"/>
|
||
<style>
|
||
@page { size: A5 portrait; margin: 0; }
|
||
* { box-sizing: border-box; }
|
||
body {
|
||
margin: 0; padding: 0; width: 148mm; height: 210mm;
|
||
display: flex; align-items: center; justify-content: center;
|
||
background: #000000;
|
||
-webkit-print-color-adjust: exact;
|
||
}
|
||
.img-wrap {
|
||
width: 140mm;
|
||
height: 200mm;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: contain;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="img-wrap">
|
||
<img src="${pngUri}" />
|
||
</div>
|
||
</body>
|
||
</html>
|
||
`;
|
||
|
||
const { uri } = await Print.printToFileAsync({
|
||
html: htmlContent,
|
||
width: 420,
|
||
height: 595,
|
||
});
|
||
|
||
await Sharing.shareAsync(uri, {
|
||
mimeType: "application/pdf",
|
||
dialogTitle: "A5 Baskıya Hazır PDF İndir / Paylaş",
|
||
});
|
||
} catch {
|
||
Alert.alert("Hata", "PDF oluşturulamadı.");
|
||
} finally {
|
||
setExportingPdf(false);
|
||
}
|
||
}
|
||
|
||
|
||
if (loading) {
|
||
return (
|
||
<View style={{ flex: 1, backgroundColor: "#FAF8F5", alignItems: "center", justifyContent: "center" }}>
|
||
<ActivityIndicator size="large" color="#C8A96B" />
|
||
<Text style={{ marginTop: 12, color: "#78716C", fontSize: 14 }}>QR Kod ve Şablonlar Hazırlanıyor...</Text>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
if (error || !qr) {
|
||
return (
|
||
<View style={{ flex: 1, backgroundColor: "#FAF8F5", alignItems: "center", justifyContent: "center", padding: 24 }}>
|
||
<Ionicons name="alert-circle-outline" size={48} color="#DC2626" style={{ marginBottom: 12 }} />
|
||
<Text style={{ color: "#DC2626", fontSize: 16, fontWeight: "600", textAlign: "center" }}>
|
||
{error ?? "QR kod yüklenemedi"}
|
||
</Text>
|
||
<Pressable
|
||
onPress={() => router.back()}
|
||
style={{ marginTop: 20, backgroundColor: "#1C1917", paddingHorizontal: 20, paddingVertical: 10, borderRadius: 8 }}
|
||
>
|
||
<Text style={{ color: "#FFF", fontWeight: "600" }}>Geri Dön</Text>
|
||
</Pressable>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<ScrollView contentContainerStyle={{ flexGrow: 1, backgroundColor: "#0C0C0E", paddingVertical: 20 }}>
|
||
<View style={{ width: "100%", maxWidth: 440, alignSelf: "center", paddingHorizontal: 20 }}>
|
||
{/* Header Badge */}
|
||
<View
|
||
style={{
|
||
alignSelf: "center",
|
||
backgroundColor: "rgba(212, 175, 55, 0.15)",
|
||
paddingHorizontal: 14,
|
||
paddingVertical: 6,
|
||
borderRadius: 99,
|
||
marginBottom: 12,
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
gap: 6,
|
||
borderWidth: 1,
|
||
borderColor: "rgba(212, 175, 55, 0.3)",
|
||
}}
|
||
>
|
||
<Ionicons name="qr-code-outline" size={15} color="#D4AF37" />
|
||
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
|
||
MASA QR STANT JENERATÖRÜ
|
||
</Text>
|
||
</View>
|
||
|
||
<Text style={{ fontSize: 24, fontWeight: "800", color: "#FFFFFF", marginBottom: 4, textAlign: "center" }}>
|
||
Masa Stant Tasarımları
|
||
</Text>
|
||
<Text style={{ fontSize: 13, color: "#A1A1AA", textAlign: "center", marginBottom: 20, lineHeight: 18 }}>
|
||
Şablon seçin; QR kodunuz otomatik yerleşsin. Baskıya hazır A5 PDF veya PNG olarak indirin.
|
||
</Text>
|
||
|
||
{/* Horizontal Theme Selector */}
|
||
{availablePresets.length > 1 && (
|
||
<ScrollView
|
||
horizontal
|
||
showsHorizontalScrollIndicator={false}
|
||
contentContainerStyle={{ gap: 10, paddingHorizontal: 4, marginBottom: 20 }}
|
||
>
|
||
{availablePresets.map((preset) => {
|
||
const isSelected = preset.key === selectedKey;
|
||
|
||
return (
|
||
<Pressable
|
||
key={preset.key}
|
||
onPress={() => setSelectedKey(preset.key)}
|
||
style={{
|
||
backgroundColor: isSelected ? "#D4AF37" : "#1A1A1E",
|
||
borderWidth: 1.5,
|
||
borderColor: isSelected ? "#D4AF37" : "#2B2B32",
|
||
borderRadius: 14,
|
||
paddingHorizontal: 16,
|
||
paddingVertical: 10,
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
gap: 8,
|
||
}}
|
||
>
|
||
<View
|
||
style={{
|
||
width: 14,
|
||
height: 14,
|
||
borderRadius: 7,
|
||
backgroundColor: preset.plaqueColor || "#1A1A1E",
|
||
borderWidth: 1,
|
||
borderColor: "rgba(255,255,255,0.4)",
|
||
}}
|
||
/>
|
||
<Text
|
||
style={{
|
||
fontSize: 13,
|
||
fontWeight: "700",
|
||
color: isSelected ? "#0C0C0E" : "#FFFFFF",
|
||
}}
|
||
>
|
||
{preset.name}
|
||
</Text>
|
||
</Pressable>
|
||
);
|
||
})}
|
||
</ScrollView>
|
||
)}
|
||
|
||
{/* Live Stand Preview Wrapped in ViewShot */}
|
||
<View
|
||
style={{
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
padding: 16,
|
||
borderRadius: 28,
|
||
backgroundColor: "#18181B",
|
||
marginBottom: 20,
|
||
shadowColor: "#000",
|
||
shadowOffset: { width: 0, height: 10 },
|
||
shadowOpacity: 0.5,
|
||
shadowRadius: 20,
|
||
elevation: 8,
|
||
borderWidth: 1,
|
||
borderColor: "rgba(255,255,255,0.08)",
|
||
}}
|
||
>
|
||
<ViewShot ref={viewShotRef} options={{ format: "png", quality: 1.0 }}>
|
||
{/* Component-based template — no XML parsing, pixel-perfect */}
|
||
<View
|
||
style={{
|
||
width: displayWidth,
|
||
height: displayHeight,
|
||
borderRadius: 24,
|
||
overflow: "hidden",
|
||
backgroundColor: "#0C0C0E",
|
||
}}
|
||
>
|
||
{TemplateComponent ? (
|
||
<TemplateComponent
|
||
qrBase64={qr.pngBase64}
|
||
width={displayWidth}
|
||
height={displayHeight}
|
||
restaurantName={customText.restaurantName}
|
||
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}
|
||
</View>
|
||
</ViewShot>
|
||
</View>
|
||
|
||
{/* Metin & Font Özelleştirme Kartı (Açılır/Kapanır) */}
|
||
<View
|
||
style={{
|
||
backgroundColor: "#18181B",
|
||
borderRadius: 20,
|
||
borderWidth: 1,
|
||
borderColor: "rgba(255, 255, 255, 0.08)",
|
||
padding: 16,
|
||
marginBottom: 20,
|
||
}}
|
||
>
|
||
<Pressable
|
||
onPress={() => setShowCustomizePanel(!showCustomizePanel)}
|
||
style={{
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
}}
|
||
>
|
||
<View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
|
||
<View
|
||
style={{
|
||
width: 32,
|
||
height: 32,
|
||
borderRadius: 8,
|
||
backgroundColor: "rgba(212, 175, 55, 0.15)",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
}}
|
||
>
|
||
<Ionicons name="text-outline" size={18} color="#D4AF37" />
|
||
</View>
|
||
<View>
|
||
<Text style={{ color: "#FFFFFF", fontSize: 15, fontWeight: "700" }}>
|
||
Metin ve Yazı Tipi Özelleştir
|
||
</Text>
|
||
<Text style={{ color: "#A1A1AA", fontSize: 12 }}>
|
||
İstediğiniz alanları düzenleyin veya kaldırın
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
<Ionicons
|
||
name={showCustomizePanel ? "chevron-up" : "chevron-down"}
|
||
size={20}
|
||
color="#A1A1AA"
|
||
/>
|
||
</Pressable>
|
||
|
||
{showCustomizePanel && (
|
||
<View style={{ marginTop: 16, gap: 16, borderTopWidth: 1, borderTopColor: "rgba(255,255,255,0.06)", paddingTop: 16 }}>
|
||
{/* Yazı Tipi (Font) Seçici */}
|
||
<View>
|
||
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700", marginBottom: 8, letterSpacing: 0.5 }}>
|
||
YAZI TİPİ (FONT)
|
||
</Text>
|
||
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 8 }}>
|
||
{FONT_OPTIONS.map((f) => {
|
||
const isSelected = f.id === selectedFontId;
|
||
return (
|
||
<Pressable
|
||
key={f.id}
|
||
onPress={() => setSelectedFontId(f.id)}
|
||
style={{
|
||
backgroundColor: isSelected ? "#D4AF37" : "#242429",
|
||
paddingHorizontal: 14,
|
||
paddingVertical: 8,
|
||
borderRadius: 10,
|
||
borderWidth: 1,
|
||
borderColor: isSelected ? "#D4AF37" : "rgba(255,255,255,0.08)",
|
||
}}
|
||
>
|
||
<Text
|
||
style={{
|
||
color: isSelected ? "#0C0C0E" : "#FFFFFF",
|
||
fontSize: 12,
|
||
fontWeight: isSelected ? "700" : "500",
|
||
}}
|
||
>
|
||
{f.label}
|
||
</Text>
|
||
</Pressable>
|
||
);
|
||
})}
|
||
</ScrollView>
|
||
</View>
|
||
|
||
{/* Alan 1: Restoran Başlığı */}
|
||
<View style={{ gap: 6 }}>
|
||
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
|
||
RESTORAN BAŞLIĞI
|
||
</Text>
|
||
<TextInput
|
||
value={customText.restaurantName}
|
||
onChangeText={(t) => setCustomText((p) => ({ ...p, restaurantName: t }))}
|
||
placeholder="Restoran Adı"
|
||
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 2: Slogan / Alt Başlık */}
|
||
<View style={{ gap: 6 }}>
|
||
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
|
||
<Text style={{ color: "#D4AF37", fontSize: 12, fontWeight: "700" }}>
|
||
SLOGAN / ALT BAŞLIK
|
||
</Text>
|
||
<View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
|
||
<Text style={{ color: "#71717A", fontSize: 11 }}>
|
||
{customText.showSubtitle ? "Göster" : "Gizle"}
|
||
</Text>
|
||
<Switch
|
||
value={customText.showSubtitle}
|
||
onValueChange={(v) => setCustomText((p) => ({ ...p, showSubtitle: v }))}
|
||
trackColor={{ false: "#27272A", true: "#D4AF37" }}
|
||
thumbColor="#FFFFFF"
|
||
ios_backgroundColor="#27272A"
|
||
/>
|
||
</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>
|
||
|
||
|
||
{/* Output Action Buttons */}
|
||
<View style={{ width: "100%", gap: 10 }}>
|
||
{/* PDF Download Button */}
|
||
<Pressable
|
||
onPress={handleExportPdf}
|
||
disabled={exportingPdf}
|
||
style={({ pressed }) => ({
|
||
backgroundColor: "#D4AF37",
|
||
borderRadius: 14,
|
||
padding: 16,
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
gap: 8,
|
||
opacity: pressed || exportingPdf ? 0.85 : 1,
|
||
shadowColor: "#D4AF37",
|
||
shadowOffset: { width: 0, height: 4 },
|
||
shadowOpacity: 0.2,
|
||
shadowRadius: 8,
|
||
elevation: 3,
|
||
})}
|
||
>
|
||
{exportingPdf ? (
|
||
<ActivityIndicator size="small" color="#0C0C0E" />
|
||
) : (
|
||
<>
|
||
<Ionicons name="document-text-outline" size={20} color="#0C0C0E" />
|
||
<Text style={{ color: "#0C0C0E", fontSize: 15, fontWeight: "800" }}>
|
||
PDF Olarak İndir (A5 Matbaa Baskı)
|
||
</Text>
|
||
</>
|
||
)}
|
||
</Pressable>
|
||
|
||
{/* PNG Download Button */}
|
||
<Pressable
|
||
onPress={handleExportPng}
|
||
disabled={exportingPng}
|
||
style={({ pressed }) => ({
|
||
backgroundColor: "#1A1A1E",
|
||
borderWidth: 1.5,
|
||
borderColor: "#2B2B32",
|
||
borderRadius: 14,
|
||
padding: 16,
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
gap: 8,
|
||
opacity: pressed || exportingPng ? 0.85 : 1,
|
||
})}
|
||
>
|
||
{exportingPng ? (
|
||
<ActivityIndicator size="small" color="#FFFFFF" />
|
||
) : (
|
||
<>
|
||
<Ionicons name="image-outline" size={20} color="#FFFFFF" />
|
||
<Text style={{ color: "#FFFFFF", fontSize: 15, fontWeight: "700" }}>
|
||
PNG Olarak Kaydet / Paylaş
|
||
</Text>
|
||
</>
|
||
)}
|
||
</Pressable>
|
||
|
||
{/* Direct Link Share Button */}
|
||
<Pressable
|
||
onPress={handleShareQuick}
|
||
style={({ pressed }) => ({
|
||
backgroundColor: "#1A1A1E",
|
||
borderWidth: 1,
|
||
borderColor: "#2B2B32",
|
||
borderRadius: 14,
|
||
padding: 14,
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
gap: 8,
|
||
opacity: pressed ? 0.85 : 1,
|
||
})}
|
||
>
|
||
<Ionicons name="share-outline" size={18} color="#D4AF37" />
|
||
<Text style={{ color: "#D4AF37", fontSize: 14, fontWeight: "700" }}>
|
||
Menü Linkini Paylaş
|
||
</Text>
|
||
</Pressable>
|
||
|
||
{/* Quick Copy Link Box */}
|
||
<Pressable
|
||
onPress={handleCopy}
|
||
style={{
|
||
backgroundColor: "#141417",
|
||
borderWidth: 1,
|
||
borderColor: "#242429",
|
||
borderRadius: 14,
|
||
padding: 14,
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
marginTop: 4,
|
||
}}
|
||
>
|
||
<Text style={{ color: "#A1A1AA", fontSize: 12, flex: 1 }} numberOfLines={1}>
|
||
{qr.targetUrl}
|
||
</Text>
|
||
<Ionicons
|
||
name={copied ? "checkmark-circle" : "copy-outline"}
|
||
size={18}
|
||
color={copied ? "#10B981" : "#D4AF37"}
|
||
style={{ marginLeft: 8 }}
|
||
/>
|
||
</Pressable>
|
||
</View>
|
||
</View>
|
||
</ScrollView>
|
||
);
|
||
}
|