import { useEffect, useRef, useState } from "react"; import { router } from "expo-router"; import { ActivityIndicator, Alert, Image, Pressable, ScrollView, Share, Text, 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"; interface QrResponse { id: string; redirectUrl: string; targetUrl: string; pngBase64: string; } export default function QrScreen() { const [active, setActive] = useState(null); const [restaurantName, setRestaurantName] = useState("THE URBAN BURGER"); const [qr, setQr] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [copied, setCopied] = useState(false); const [selectedKey, setSelectedKey] = useState("urban"); const [categories, setCategories] = useState([]); const [exportingPng, setExportingPng] = useState(false); const [exportingPdf, setExportingPdf] = useState(false); // eslint-disable-next-line @typescript-eslint/no-explicit-any const viewShotRef = useRef(null); const activePreset: QrStandPreset = (QR_STAND_PRESETS[selectedKey] || QR_STAND_PRESETS.urban)!; useEffect(() => { (async () => { const activeRest = await getActiveRestaurant(); if (!activeRest) return; setActive(activeRest); try { const data = await api.post(`/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); } } catch { // fallback } // Fetch categories for preview if (activeRest.menuId) { try { const menuRes = await api.get<{ categories: { name: string }[] }>(`/menus/${activeRest.menuId}`); if (menuRes?.categories?.length) { setCategories(menuRes.categories.map((c) => c.name)); } } catch { // ignore fallback } } } catch (err) { setError(err instanceof Error ? err.message : "QR oluşturulamadı"); } finally { setLoading(false); } })(); }, []); const displayCategories = categories.length > 0 ? categories.slice(0, 4) : activePreset.categories; 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 { const restTitle = restaurantName.toUpperCase(); const qrImageUri = `data:image/png;base64,${qr.pngBase64}`; const catsHtml = displayCategories .map( (c, i) => `
${c}
${ i < displayCategories.length - 1 ? '
' : "" }`, ) .join(""); const htmlContent = `
🔥 ${restTitle}
DIGITAL MENU
SCAN TO ORDER
Use your phone's camera
`; 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 ( QR Kod ve Şablonlar Hazırlanıyor... ); } if (error || !qr) { return ( {error ?? "QR kod yüklenemedi"} router.back()} style={{ marginTop: 20, backgroundColor: "#1C1917", paddingHorizontal: 20, paddingVertical: 10, borderRadius: 8 }} > Geri Dön ); } return ( {/* Header Badge */} MASA QR STANT JENERATÖRÜ Masa Stant Tasarımları Şablon seçin; QR kodunuz otomatik yerleşsin. Baskıya hazır A5 PDF veya PNG olarak indirin. {/* Horizontal Theme Selector (rendered only if multiple themes exist) */} {Object.values(QR_STAND_PRESETS).length > 1 && ( {Object.values(QR_STAND_PRESETS).map((preset) => { const isSelected = preset.key === selectedKey; return ( setSelectedKey(preset.key)} style={{ backgroundColor: isSelected ? "#1C1917" : "#FFFFFF", borderWidth: 1.5, borderColor: isSelected ? "#1C1917" : "#E7E5E4", borderRadius: 14, paddingHorizontal: 16, paddingVertical: 10, flexDirection: "row", alignItems: "center", gap: 8, }} > {preset.name} ); })} )} {/* Live Stand Preview Wrapped in ViewShot */} {/* Inner Border Accent Line */} {/* Top Restaurant Badge with Flame Icon */} {restaurantName.toUpperCase()} {/* Title Callout */} DIGITAL MENU{"\n"} SCAN TO ORDER {/* Auto Placed QR Code Container */} {/* Instructional Subtext */} Use your phone's camera {/* Category Quick Preview List Flanked by Icons */} {/* Floating Left Line Icon (Burger) */} {/* Floating Right Line Icon (Drink) */} {/* Center Category Stack */} {displayCategories.map((cat, idx) => ( {cat} {idx < displayCategories.length - 1 && ( )} ))} {/* Output Action Buttons */} {/* PDF Download Button */} ({ backgroundColor: "#C8A96B", borderRadius: 14, padding: 16, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8, opacity: pressed || exportingPdf ? 0.85 : 1, shadowColor: "#C8A96B", shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.2, shadowRadius: 10, elevation: 4, })} > {exportingPdf ? ( ) : ( <> A5 Baskı PDF Dosyası İndir / Paylaş )} {/* PNG Export Button */} ({ backgroundColor: "#1C1917", borderRadius: 14, padding: 15, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8, opacity: pressed || exportingPng ? 0.85 : 1, })} > {exportingPng ? ( ) : ( <> PNG Görsel Olarak Kaydet / Paylaş )} {/* Secondary Quick Share & Copy */} ({ flex: 1, backgroundColor: "#FFFFFF", borderWidth: 1, borderColor: "#E7E5E4", borderRadius: 12, padding: 12, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, opacity: pressed ? 0.85 : 1, })} > Linki Paylaş ({ flex: 1, backgroundColor: "#FFFFFF", borderWidth: 1, borderColor: "#E7E5E4", borderRadius: 12, padding: 12, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, opacity: pressed ? 0.85 : 1, })} > {copied ? "Kopyalandı!" : "Linki Kopyala"} {/* Back button */} router.back()} style={{ padding: 12, alignItems: "center", flexDirection: "row", justifyContent: "center", gap: 6 }} > Menü Editörüne Dön ); }