first commit

This commit is contained in:
AyrisAI
2026-08-20 01:51:59 +03:00
commit 97b83c7fd4
109 changed files with 21215 additions and 0 deletions
+872
View File
@@ -0,0 +1,872 @@
import { useCallback, useEffect, useState } from "react";
import { router, useFocusEffect } from "expo-router";
import {
ActivityIndicator,
Alert,
Image,
Linking,
Pressable,
ScrollView,
Text,
TextInput,
View,
} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { Ionicons } from "@expo/vector-icons";
import * as Clipboard from "expo-clipboard";
import * as Haptics from "expo-haptics";
import * as ImagePicker from "expo-image-picker";
import { api } from "@/lib/api";
import { supabase } from "@/lib/supabase";
import { getActiveRestaurant, clearActiveRestaurant, type ActiveRestaurant } from "@/lib/active-restaurant";
import { getPublicMenuUrl, getPublicMenuDisplayUrl } from "@/lib/urls";
interface RestaurantDetail {
id: string;
name: string;
slug: string;
logo_url: string | null;
phone: string | null;
address: string | null;
}
interface DomainRow {
id: string;
hostname: string;
is_custom: boolean;
status: "pending" | "verified" | "failed";
verified_at: string | null;
created_at: string;
}
const THEME_OPTIONS = [
{ key: "elegant", name: "Elegant Gold", color: "#C8A96B", desc: "Lüks altın & ipeksi krem" },
{ key: "modern", name: "Modern Sapphire", color: "#2563EB", desc: "Kraliyet safiri & beyaz" },
{ key: "dark", name: "Luxury Dark", color: "#F59E0B", desc: "Kehribar & gece siyahı" },
{ key: "minimal", name: "Nordic Minimal", color: "#18181B", desc: "Sade grafit & kar beyazı" },
{ key: "classic", name: "Classic Bistro", color: "#8B1E1E", desc: "Toskana bordo & rustik" },
];
export default function AccountScreen() {
const insets = useSafeAreaInsets();
const [active, setActive] = useState<ActiveRestaurant | null>(null);
const [userEmail, setUserEmail] = useState<string>("");
const [userId, setUserId] = useState<string>("");
const [loading, setLoading] = useState(true);
// Restaurant details state
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [address, setAddress] = useState("");
const [logoUrl, setLogoUrl] = useState<string | null>(null);
const [slug, setSlug] = useState("");
const [savingRest, setSavingRest] = useState(false);
// Theme state
const [selectedTheme, setSelectedTheme] = useState("elegant");
const [savingTheme, setSavingTheme] = useState(false);
// Custom Domain state
const [domains, setDomains] = useState<DomainRow[]>([]);
const [newDomainInput, setNewDomainInput] = useState("");
const [cnameTarget, setCnameTarget] = useState("cname.menul.io");
const [addingDomain, setAddingDomain] = useState(false);
const [verifyingDomainId, setVerifyingDomainId] = useState<string | null>(null);
const [copiedSlug, setCopiedSlug] = useState(false);
const loadData = useCallback(async () => {
try {
const { data: authData } = await supabase.auth.getUser();
if (authData.user) {
setUserEmail(authData.user.email ?? "");
setUserId(authData.user.id);
}
const cached = await getActiveRestaurant();
if (!cached) {
router.replace("/(onboarding)/restaurant");
return;
}
setActive(cached);
// Fetch restaurant details
const rest = await api.get<RestaurantDetail>(`/restaurants/${cached.restaurantId}`);
setName(rest.name);
setPhone(rest.phone ?? "");
setAddress(rest.address ?? "");
setLogoUrl(rest.logo_url ?? null);
setSlug(rest.slug);
// Fetch theme
const themeRes = await api.get<{ themeKey: string }>(`/restaurants/${cached.restaurantId}/theme`).catch(() => ({ themeKey: "elegant" }));
setSelectedTheme(themeRes.themeKey || "elegant");
// Fetch custom domains
const domainRes = await api.get<{ domains: DomainRow[]; cnameTarget: string }>(`/restaurants/${cached.restaurantId}/domains`).catch(() => null);
if (domainRes?.domains) {
setDomains(domainRes.domains);
if (domainRes.cnameTarget) setCnameTarget(domainRes.cnameTarget);
}
} catch (err) {
Alert.alert("Hata", err instanceof Error ? err.message : "Hesap bilgileri yüklenemedi.");
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
loadData();
}, [loadData]);
useFocusEffect(
useCallback(() => {
loadData();
}, [loadData]),
);
async function handlePickLogo() {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {});
const permission = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (!permission.granted) {
Alert.alert("İzin Gerekli", "Restoran logosu seçebilmek için fotoğraf galerisi izni gereklidir.");
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ["images"],
allowsEditing: true,
aspect: [1, 1],
quality: 0.6,
base64: true,
});
if (!result.canceled && result.assets[0]) {
const asset = result.assets[0];
const newLogo = asset.base64 ? `data:image/jpeg;base64,${asset.base64}` : asset.uri;
setLogoUrl(newLogo);
}
}
async function handleSaveRestaurant() {
if (!active || !name.trim()) return;
setSavingRest(true);
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium).catch(() => {});
try {
await api.patch(`/restaurants/${active.restaurantId}`, {
name: name.trim(),
phone: phone.trim() || null,
address: address.trim() || null,
logoUrl: logoUrl || null,
});
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {});
Alert.alert("Başarılı 🎉", "Restoran bilgileri ve logo güncellendi.");
} catch (err) {
Alert.alert("Hata", err instanceof Error ? err.message : "Güncellenemedi.");
} finally {
setSavingRest(false);
}
}
async function handleSelectTheme(themeKey: string) {
if (!active) return;
setSelectedTheme(themeKey);
setSavingTheme(true);
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium).catch(() => {});
try {
await api.put(`/restaurants/${active.restaurantId}/theme`, { themeKey });
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {});
} catch (err) {
Alert.alert("Hata", err instanceof Error ? err.message : "Tema güncellenemedi.");
} finally {
setSavingTheme(false);
}
}
async function handleAddCustomDomain() {
if (!active || !newDomainInput.trim()) {
Alert.alert("Eksik Bilgi", "Lütfen bağlamak istediğiniz alan adını girin (Örn: menu.kebapciahmet.com)");
return;
}
setAddingDomain(true);
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium).catch(() => {});
try {
const res = await api.post<{ domain: DomainRow }>(`/restaurants/${active.restaurantId}/domains`, {
hostname: newDomainInput.trim(),
});
setDomains((prev) => [res.domain, ...prev]);
setNewDomainInput("");
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {});
Alert.alert(
"Alan Adı Eklendi 🎉",
`Lütfen DNS sağlayıcınızda (GoDaddy, Cloudflare vb.) ${res.domain.hostname} için CNAME kaydını ${cnameTarget} adresine yönlendirin.`,
);
} catch (err) {
Alert.alert("Hata", err instanceof Error ? err.message : "Alan adı eklenemedi.");
} finally {
setAddingDomain(false);
}
}
async function handleVerifyDomain(domainId: string) {
if (!active) return;
setVerifyingDomainId(domainId);
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {});
try {
const res = await api.post<{ verified: boolean; domain: DomainRow; message: string }>(
`/restaurants/${active.restaurantId}/domains/${domainId}/verify`,
);
if (res.verified) {
setDomains((prev) => prev.map((d) => (d.id === domainId ? res.domain : d)));
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {});
Alert.alert("Tebrikler! 🎉", res.message);
} else {
setDomains((prev) => prev.map((d) => (d.id === domainId ? res.domain : d)));
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning).catch(() => {});
Alert.alert("Doğrulanamadı", res.message);
}
} catch (err) {
Alert.alert("Hata", err instanceof Error ? err.message : "Doğrulama başarısız.");
} finally {
setVerifyingDomainId(null);
}
}
async function handleDeleteDomain(domainId: string, hostname: string) {
if (!active) return;
Alert.alert(
"Alan Adını Kaldır",
`"${hostname}" alan adı restoranınızdan kaldırılacak. Emin misiniz?`,
[
{ text: "Vazgeç", style: "cancel" },
{
text: "Kaldır",
style: "destructive",
onPress: async () => {
try {
await api.delete(`/restaurants/${active.restaurantId}/domains/${domainId}`);
setDomains((prev) => prev.filter((d) => d.id !== domainId));
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {});
} catch (err) {
Alert.alert("Hata", err instanceof Error ? err.message : "Kaldırılamadı.");
}
},
},
],
);
}
async function handleCopyUrl() {
const url = getPublicMenuUrl(slug);
await Clipboard.setStringAsync(url);
setCopiedSlug(true);
setTimeout(() => setCopiedSlug(false), 2000);
}
function handleOpenMenu() {
const webUrl = getPublicMenuUrl(slug);
Linking.openURL(webUrl).catch(() => {
Alert.alert("Bilgi", `Menü adresi: ${webUrl}`);
});
}
function handleSignOut() {
Alert.alert(
"Çıkış Yap",
"Hesabınızdan çıkış yapmak istediğinize emin misiniz?",
[
{ text: "Vazgeç", style: "cancel" },
{
text: "Çıkış Yap",
style: "destructive",
onPress: async () => {
await clearActiveRestaurant();
await supabase.auth.signOut();
router.replace("/(auth)/login");
},
},
],
);
}
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, fontWeight: "600" }}>
Hesap bilgileri yükleniyor...
</Text>
</View>
);
}
return (
<View style={{ flex: 1, backgroundColor: "#FAF8F5" }}>
{/* Top App Header */}
<View
style={{
paddingTop: Math.max(insets.top, 16),
paddingHorizontal: 20,
paddingBottom: 14,
backgroundColor: "#FFFFFF",
borderBottomWidth: 1,
borderBottomColor: "#E7E5E4",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<View style={{ flexDirection: "row", alignItems: "center", gap: 12 }}>
<Pressable
onPress={() => router.back()}
style={{
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: "#FAF8F5",
alignItems: "center",
justifyContent: "center",
borderWidth: 1,
borderColor: "#E7E5E4",
}}
>
<Ionicons name="arrow-back" size={18} color="#1C1917" />
</Pressable>
<Text style={{ fontSize: 20, fontWeight: "800", color: "#1C1917" }}>
Hesabım & Ayarlar
</Text>
</View>
<Pressable
onPress={handleSignOut}
style={{
paddingHorizontal: 12,
paddingVertical: 6,
backgroundColor: "#FEF2F2",
borderRadius: 8,
borderWidth: 1,
borderColor: "#FEE2E2",
}}
>
<Text style={{ color: "#DC2626", fontSize: 12, fontWeight: "700" }}>Çıkış Yap</Text>
</Pressable>
</View>
<ScrollView contentContainerStyle={{ padding: 20, paddingBottom: 60, gap: 24 }}>
{/* Restaurant Profile Card with Logo Picker */}
<View
style={{
backgroundColor: "#FFFFFF",
borderRadius: 20,
padding: 20,
borderWidth: 1,
borderColor: "#E7E5E4",
shadowColor: "#000",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.04,
shadowRadius: 12,
elevation: 2,
}}
>
<View style={{ flexDirection: "row", alignItems: "center", gap: 14 }}>
{/* Logo Avatar Picker */}
<Pressable
onPress={handlePickLogo}
style={{
width: 64,
height: 64,
borderRadius: 32,
backgroundColor: "#1C1917",
alignItems: "center",
justifyContent: "center",
position: "relative",
borderWidth: 2,
borderColor: "#C8A96B",
overflow: "hidden",
}}
>
{logoUrl ? (
<Image source={{ uri: logoUrl }} style={{ width: "100%", height: "100%" }} resizeMode="cover" />
) : (
<Text style={{ color: "#C8A96B", fontSize: 24, fontWeight: "800" }}>
{name.charAt(0).toUpperCase() || userEmail.charAt(0).toUpperCase() || "M"}
</Text>
)}
<View
style={{
position: "absolute",
bottom: 0,
left: 0,
right: 0,
backgroundColor: "rgba(0,0,0,0.6)",
alignItems: "center",
paddingVertical: 2,
}}
>
<Ionicons name="camera" size={12} color="#FFFFFF" />
</View>
</Pressable>
<View style={{ flex: 1 }}>
<Text style={{ fontSize: 17, fontWeight: "800", color: "#1C1917" }}>
{name || "Restoran Yöneticisi"}
</Text>
<Text style={{ fontSize: 13, color: "#78716C", marginTop: 2 }}>{userEmail}</Text>
<Pressable
onPress={handlePickLogo}
style={{
alignSelf: "flex-start",
backgroundColor: "#FDF8F0",
paddingHorizontal: 8,
paddingVertical: 3,
borderRadius: 6,
marginTop: 6,
borderWidth: 1,
borderColor: "rgba(200, 169, 107, 0.4)",
}}
>
<Text style={{ color: "#926E27", fontSize: 11, fontWeight: "700" }}>
📷 Logo Değiştir
</Text>
</Pressable>
</View>
</View>
</View>
{/* Live Menu URL & Quick Actions */}
<View
style={{
backgroundColor: "#FFFFFF",
borderRadius: 20,
padding: 20,
borderWidth: 1,
borderColor: "#E7E5E4",
gap: 14,
}}
>
<Text style={{ fontSize: 16, fontWeight: "800", color: "#1C1917" }}>
Dijital Menü Linki & Kısayollar
</Text>
<View
style={{
flexDirection: "row",
alignItems: "center",
backgroundColor: "#FAF8F5",
borderRadius: 12,
paddingHorizontal: 12,
paddingVertical: 10,
borderWidth: 1,
borderColor: "#E7E5E4",
}}
>
<Ionicons name="globe-outline" size={18} color="#C8A96B" style={{ marginRight: 8 }} />
<Text numberOfLines={1} style={{ flex: 1, fontSize: 13, fontWeight: "600", color: "#1C1917" }}>
{getPublicMenuDisplayUrl(slug)}
</Text>
<Pressable onPress={handleCopyUrl} style={{ paddingHorizontal: 8, paddingVertical: 4 }}>
<Text style={{ fontSize: 12, fontWeight: "700", color: copiedSlug ? "#10B981" : "#C8A96B" }}>
{copiedSlug ? "Kopyalandı!" : "Kopyala"}
</Text>
</Pressable>
</View>
<View style={{ flexDirection: "row", gap: 10 }}>
<Pressable
onPress={() => router.push("/menu/qr")}
style={{
flex: 1,
backgroundColor: "#FAF8F5",
borderWidth: 1,
borderColor: "#E7E5E4",
borderRadius: 12,
padding: 12,
alignItems: "center",
flexDirection: "row",
justifyContent: "center",
gap: 6,
}}
>
<Ionicons name="qr-code-outline" size={16} color="#1C1917" />
<Text style={{ fontSize: 13, fontWeight: "700", color: "#1C1917" }}>QR Kod</Text>
</Pressable>
<Pressable
onPress={handleOpenMenu}
style={{
flex: 1,
backgroundColor: "#FAF8F5",
borderWidth: 1,
borderColor: "#E7E5E4",
borderRadius: 12,
padding: 12,
alignItems: "center",
flexDirection: "row",
justifyContent: "center",
gap: 6,
}}
>
<Ionicons name="open-outline" size={16} color="#1C1917" />
<Text style={{ fontSize: 13, fontWeight: "700", color: "#1C1917" }}>Menüyü </Text>
</Pressable>
</View>
</View>
{/* Custom Domain Section */}
<View
style={{
backgroundColor: "#FFFFFF",
borderRadius: 20,
padding: 20,
borderWidth: 1,
borderColor: "#E7E5E4",
gap: 14,
}}
>
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
<Ionicons name="link-outline" size={20} color="#C8A96B" />
<Text style={{ fontSize: 16, fontWeight: "800", color: "#1C1917" }}>
Özel Alan Adı (Custom Domain)
</Text>
</View>
<Text style={{ fontSize: 12, color: "#78716C", lineHeight: 18 }}>
Menünüzü kendi web sitenizin alt alan adında (Örn: <Text style={{ fontWeight: "700" }}>menu.restoraniniz.com</Text>) yayınlayın.
</Text>
{/* Add Domain Input */}
<View style={{ gap: 8 }}>
<View
style={{
flexDirection: "row",
alignItems: "center",
backgroundColor: "#FAF8F5",
borderWidth: 1,
borderColor: "#E7E5E4",
borderRadius: 12,
paddingHorizontal: 12,
}}
>
<TextInput
value={newDomainInput}
onChangeText={setNewDomainInput}
placeholder="Örn: menu.kebapciahmet.com"
placeholderTextColor="#A8A29E"
autoCapitalize="none"
autoCorrect={false}
style={{
flex: 1,
paddingVertical: 12,
fontSize: 14,
fontWeight: "600",
color: "#1C1917",
}}
/>
<Pressable
onPress={handleAddCustomDomain}
disabled={addingDomain || !newDomainInput.trim()}
style={{
backgroundColor: !newDomainInput.trim() ? "#D6D3D1" : "#1C1917",
paddingHorizontal: 14,
paddingVertical: 8,
borderRadius: 8,
}}
>
<Text style={{ color: "#FFFFFF", fontSize: 12, fontWeight: "700" }}>
{addingDomain ? "Ekleniyor..." : "Bağla"}
</Text>
</Pressable>
</View>
</View>
{/* Existing Domains List */}
{domains.length > 0 ? (
<View style={{ gap: 10, marginTop: 4 }}>
{domains.map((dom) => {
const isVerified = dom.status === "verified";
const isChecking = verifyingDomainId === dom.id;
return (
<View
key={dom.id}
style={{
backgroundColor: "#FAF8F5",
borderRadius: 14,
padding: 14,
borderWidth: 1,
borderColor: isVerified ? "#BBF7D0" : "#FDE68A",
}}
>
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
<View style={{ flex: 1, marginRight: 8 }}>
<Text style={{ fontSize: 14, fontWeight: "700", color: "#1C1917" }}>
{dom.hostname}
</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: 6, marginTop: 4 }}>
<View
style={{
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: isVerified ? "#10B981" : "#F59E0B",
}}
/>
<Text
style={{
fontSize: 11,
fontWeight: "700",
color: isVerified ? "#059669" : "#D97706",
}}
>
{isVerified ? "Yayında & Doğrulandı ✓" : "DNS Doğrulaması Bekleniyor"}
</Text>
</View>
</View>
<View style={{ flexDirection: "row", gap: 6 }}>
{!isVerified ? (
<Pressable
onPress={() => handleVerifyDomain(dom.id)}
disabled={isChecking}
style={{
backgroundColor: "#1C1917",
paddingHorizontal: 10,
paddingVertical: 6,
borderRadius: 8,
}}
>
<Text style={{ color: "#FFFFFF", fontSize: 11, fontWeight: "700" }}>
{isChecking ? "Kontrol..." : "Doğrula"}
</Text>
</Pressable>
) : null}
<Pressable
onPress={() => handleDeleteDomain(dom.id, dom.hostname)}
style={{
backgroundColor: "#FEF2F2",
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 8,
}}
>
<Ionicons name="trash-outline" size={14} color="#DC2626" />
</Pressable>
</View>
</View>
{/* CNAME instructions if pending */}
{!isVerified ? (
<View
style={{
marginTop: 10,
padding: 10,
backgroundColor: "#FFFFFF",
borderRadius: 8,
borderWidth: 1,
borderColor: "#E7E5E4",
}}
>
<Text style={{ fontSize: 11, fontWeight: "700", color: "#44403C" }}>
DNS Kaydı Talimatı:
</Text>
<Text style={{ fontSize: 11, color: "#78716C", marginTop: 2 }}>
Tür: <Text style={{ fontWeight: "700", color: "#1C1917" }}>CNAME</Text> | Hedef:{" "}
<Text style={{ fontWeight: "700", color: "#C8A96B" }}>{cnameTarget}</Text>
</Text>
</View>
) : null}
</View>
);
})}
</View>
) : null}
</View>
{/* Restaurant Details Form */}
<View
style={{
backgroundColor: "#FFFFFF",
borderRadius: 20,
padding: 20,
borderWidth: 1,
borderColor: "#E7E5E4",
gap: 14,
}}
>
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
<Ionicons name="business-outline" size={20} color="#C8A96B" />
<Text style={{ fontSize: 16, fontWeight: "800", color: "#1C1917" }}>
Restoran Bilgileri
</Text>
</View>
{/* Restaurant Name */}
<View>
<Text style={{ fontSize: 12, fontWeight: "700", color: "#78716C", marginBottom: 6 }}>
Restoran Adı
</Text>
<TextInput
value={name}
onChangeText={setName}
placeholder="Restoran Adı"
style={{
backgroundColor: "#FAF8F5",
borderWidth: 1,
borderColor: "#E7E5E4",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 14,
color: "#1C1917",
}}
/>
</View>
{/* Phone */}
<View>
<Text style={{ fontSize: 12, fontWeight: "700", color: "#78716C", marginBottom: 6 }}>
Telefon Numarası
</Text>
<TextInput
value={phone}
onChangeText={setPhone}
placeholder="05xx xxx xx xx"
keyboardType="phone-pad"
style={{
backgroundColor: "#FAF8F5",
borderWidth: 1,
borderColor: "#E7E5E4",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 14,
color: "#1C1917",
}}
/>
</View>
{/* Address */}
<View>
<Text style={{ fontSize: 12, fontWeight: "700", color: "#78716C", marginBottom: 6 }}>
Adres / Lokasyon
</Text>
<TextInput
value={address}
onChangeText={setAddress}
placeholder="Restoran adresi"
multiline
numberOfLines={2}
style={{
backgroundColor: "#FAF8F5",
borderWidth: 1,
borderColor: "#E7E5E4",
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 12,
fontSize: 14,
color: "#1C1917",
minHeight: 64,
}}
/>
</View>
<Pressable
onPress={handleSaveRestaurant}
disabled={savingRest}
style={({ pressed }) => ({
backgroundColor: "#1C1917",
paddingVertical: 14,
borderRadius: 12,
alignItems: "center",
opacity: pressed || savingRest ? 0.85 : 1,
marginTop: 4,
})}
>
<Text style={{ color: "#FFFFFF", fontSize: 14, fontWeight: "700" }}>
{savingRest ? "Kaydediliyor..." : "Restoran Bilgilerini Güncelle"}
</Text>
</Pressable>
</View>
{/* Theme Selector */}
<View
style={{
backgroundColor: "#FFFFFF",
borderRadius: 20,
padding: 20,
borderWidth: 1,
borderColor: "#E7E5E4",
gap: 14,
}}
>
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
<Ionicons name="color-palette-outline" size={20} color="#C8A96B" />
<Text style={{ fontSize: 16, fontWeight: "800", color: "#1C1917" }}>
Menü Şablonu (Tema)
</Text>
</View>
<Text style={{ fontSize: 12, color: "#78716C" }}>
Restoranınızın konseptine uygun tasarımı seçin. Seçtiğiniz tema QR menünüzde anında aktif olur.
</Text>
<View style={{ gap: 10 }}>
{THEME_OPTIONS.map((theme) => {
const isSelected = selectedTheme === theme.key;
return (
<Pressable
key={theme.key}
onPress={() => handleSelectTheme(theme.key)}
style={({ pressed }) => ({
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
padding: 14,
borderRadius: 14,
backgroundColor: isSelected ? "#FAF8F5" : "#FAFAFA",
borderWidth: 2,
borderColor: isSelected ? theme.color : "#E7E5E4",
opacity: pressed ? 0.9 : 1,
})}
>
<View style={{ flexDirection: "row", alignItems: "center", gap: 12 }}>
<View
style={{
width: 20,
height: 20,
borderRadius: 10,
backgroundColor: theme.color,
}}
/>
<View>
<Text style={{ fontSize: 14, fontWeight: "700", color: "#1C1917" }}>
{theme.name}
</Text>
<Text style={{ fontSize: 11, color: "#78716C", marginTop: 2 }}>
{theme.desc}
</Text>
</View>
</View>
<View
style={{
width: 22,
height: 22,
borderRadius: 11,
borderWidth: 2,
borderColor: isSelected ? theme.color : "#D6D3D1",
backgroundColor: isSelected ? theme.color : "transparent",
alignItems: "center",
justifyContent: "center",
}}
>
{isSelected ? <Ionicons name="checkmark" size={14} color="#FFFFFF" /> : null}
</View>
</Pressable>
);
})}
</View>
</View>
</ScrollView>
</View>
);
}