feat: redesign web menu layout, add header cover banner & dynamic header layout, refine mobile inputs and theme selector
This commit is contained in:
@@ -13,3 +13,5 @@ build/
|
||||
supabase/.branches/
|
||||
supabase/.temp/
|
||||
coverage/
|
||||
*.ipa
|
||||
*.apk
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import { Link, router } from "expo-router";
|
||||
import { ActivityIndicator, Pressable, Text, TextInput, View } from "react-native";
|
||||
import { ActivityIndicator, Image, Pressable, Text, TextInput, View } from "react-native";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { supabase } from "@/lib/supabase";
|
||||
|
||||
@@ -30,28 +30,24 @@ export default function LoginScreen() {
|
||||
<View style={{ maxWidth: 400, width: "100%", alignSelf: "center" }}>
|
||||
{/* Brand Icon Header */}
|
||||
<View style={{ alignItems: "center", marginBottom: 28 }}>
|
||||
<View
|
||||
<Image
|
||||
source={require("../../../assets/icon.png")}
|
||||
style={{
|
||||
width: 64,
|
||||
height: 64,
|
||||
borderRadius: 32,
|
||||
backgroundColor: "#1C1917",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: 18,
|
||||
marginBottom: 14,
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.15,
|
||||
shadowRadius: 10,
|
||||
elevation: 4,
|
||||
shadowOpacity: 0.12,
|
||||
shadowRadius: 8,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="restaurant-outline" size={30} color="#C8A96B" />
|
||||
</View>
|
||||
<Text style={{ fontSize: 26, fontWeight: "800", color: "#1C1917", letterSpacing: -0.5 }}>
|
||||
Menulio'ya Giriş Yap
|
||||
resizeMode="contain"
|
||||
/>
|
||||
<Text style={{ fontSize: 28, fontWeight: "800", color: "#1C1917", letterSpacing: -0.5 }}>
|
||||
MenuL.io
|
||||
</Text>
|
||||
<Text style={{ fontSize: 13, color: "#78716C", marginTop: 4 }}>
|
||||
<Text style={{ fontSize: 13, color: "#57534E", marginTop: 4, fontWeight: "500" }}>
|
||||
AI destekli dijital restoran menü yöneticisi
|
||||
</Text>
|
||||
</View>
|
||||
@@ -81,22 +77,22 @@ export default function LoginScreen() {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#FFFFFF",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 14,
|
||||
marginBottom: 12,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="mail-outline" size={18} color="#A8A29E" style={{ marginRight: 10 }} />
|
||||
<Ionicons name="mail-outline" size={18} color="#57534E" style={{ marginRight: 10 }} />
|
||||
<TextInput
|
||||
placeholder="E-posta adresiniz"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
autoCapitalize="none"
|
||||
keyboardType="email-address"
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917" }}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917", fontWeight: "500" }}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -106,27 +102,27 @@ export default function LoginScreen() {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#FFFFFF",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 14,
|
||||
marginBottom: 20,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="lock-closed-outline" size={18} color="#A8A29E" style={{ marginRight: 10 }} />
|
||||
<Ionicons name="lock-closed-outline" size={18} color="#57534E" style={{ marginRight: 10 }} />
|
||||
<TextInput
|
||||
placeholder="Şifreniz"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
secureTextEntry={!showPassword}
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917" }}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917", fontWeight: "500" }}
|
||||
/>
|
||||
<Pressable onPress={() => setShowPassword(!showPassword)} style={{ padding: 4 }}>
|
||||
<Ionicons
|
||||
name={showPassword ? "eye-off-outline" : "eye-outline"}
|
||||
size={18}
|
||||
color="#A8A29E"
|
||||
color="#57534E"
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import { Link, router } from "expo-router";
|
||||
import { ActivityIndicator, Pressable, Text, TextInput, View } from "react-native";
|
||||
import { ActivityIndicator, Image, Pressable, Text, TextInput, View } from "react-native";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { supabase } from "@/lib/supabase";
|
||||
|
||||
@@ -35,28 +35,24 @@ export default function RegisterScreen() {
|
||||
<View style={{ maxWidth: 400, width: "100%", alignSelf: "center" }}>
|
||||
{/* Brand Icon Header */}
|
||||
<View style={{ alignItems: "center", marginBottom: 28 }}>
|
||||
<View
|
||||
<Image
|
||||
source={require("../../../assets/icon.png")}
|
||||
style={{
|
||||
width: 64,
|
||||
height: 64,
|
||||
borderRadius: 32,
|
||||
backgroundColor: "#1C1917",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: 18,
|
||||
marginBottom: 14,
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.15,
|
||||
shadowRadius: 10,
|
||||
elevation: 4,
|
||||
shadowOpacity: 0.12,
|
||||
shadowRadius: 8,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="sparkles" size={28} color="#C8A96B" />
|
||||
</View>
|
||||
<Text style={{ fontSize: 26, fontWeight: "800", color: "#1C1917", letterSpacing: -0.5 }}>
|
||||
Hesap Oluştur
|
||||
resizeMode="contain"
|
||||
/>
|
||||
<Text style={{ fontSize: 28, fontWeight: "800", color: "#1C1917", letterSpacing: -0.5 }}>
|
||||
MenuL.io
|
||||
</Text>
|
||||
<Text style={{ fontSize: 13, color: "#78716C", marginTop: 4 }}>
|
||||
<Text style={{ fontSize: 13, color: "#57534E", marginTop: 4, fontWeight: "500" }}>
|
||||
Dakikalar içinde restoranınızı ve QR menünüzü hazırlayın
|
||||
</Text>
|
||||
</View>
|
||||
@@ -86,22 +82,22 @@ export default function RegisterScreen() {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#FFFFFF",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 14,
|
||||
marginBottom: 12,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="mail-outline" size={18} color="#A8A29E" style={{ marginRight: 10 }} />
|
||||
<Ionicons name="mail-outline" size={18} color="#57534E" style={{ marginRight: 10 }} />
|
||||
<TextInput
|
||||
placeholder="E-posta adresiniz"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
autoCapitalize="none"
|
||||
keyboardType="email-address"
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917" }}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917", fontWeight: "500" }}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -111,27 +107,27 @@ export default function RegisterScreen() {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#FFFFFF",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 14,
|
||||
marginBottom: 20,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="lock-closed-outline" size={18} color="#A8A29E" style={{ marginRight: 10 }} />
|
||||
<Ionicons name="lock-closed-outline" size={18} color="#57534E" style={{ marginRight: 10 }} />
|
||||
<TextInput
|
||||
placeholder="Güçlü bir şifre belirleyin"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
secureTextEntry={!showPassword}
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917" }}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917", fontWeight: "500" }}
|
||||
/>
|
||||
<Pressable onPress={() => setShowPassword(!showPassword)} style={{ padding: 4 }}>
|
||||
<Ionicons
|
||||
name={showPassword ? "eye-off-outline" : "eye-outline"}
|
||||
size={18}
|
||||
color="#A8A29E"
|
||||
color="#57534E"
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
@@ -96,20 +96,20 @@ export default function OnboardingRestaurantStep() {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#FFFFFF",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 14,
|
||||
marginBottom: 12,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="storefront-outline" size={18} color="#A8A29E" style={{ marginRight: 10 }} />
|
||||
<Ionicons name="storefront-outline" size={18} color="#57534E" style={{ marginRight: 10 }} />
|
||||
<TextInput
|
||||
placeholder="Restoran adı (Örn: Kebapçı Ahmet)"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917" }}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917", fontWeight: "500" }}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -119,21 +119,21 @@ export default function OnboardingRestaurantStep() {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#FFFFFF",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 14,
|
||||
marginBottom: 12,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="call-outline" size={18} color="#A8A29E" style={{ marginRight: 10 }} />
|
||||
<Ionicons name="call-outline" size={18} color="#57534E" style={{ marginRight: 10 }} />
|
||||
<TextInput
|
||||
placeholder="Telefon numarası (opsiyonel)"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
keyboardType="phone-pad"
|
||||
value={phone}
|
||||
onChangeText={setPhone}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917" }}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917", fontWeight: "500" }}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -143,20 +143,20 @@ export default function OnboardingRestaurantStep() {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#FFFFFF",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 14,
|
||||
marginBottom: 24,
|
||||
}}
|
||||
>
|
||||
<Ionicons name="location-outline" size={18} color="#A8A29E" style={{ marginRight: 10 }} />
|
||||
<Ionicons name="location-outline" size={18} color="#57534E" style={{ marginRight: 10 }} />
|
||||
<TextInput
|
||||
placeholder="Adres / Şehir (opsiyonel)"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
value={address}
|
||||
onChangeText={setAddress}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917" }}
|
||||
style={{ flex: 1, paddingVertical: 14, fontSize: 15, color: "#1C1917", fontWeight: "500" }}
|
||||
/>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -560,8 +560,8 @@ export default function AccountScreen() {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#FAF8F5",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 12,
|
||||
}}
|
||||
@@ -570,7 +570,7 @@ export default function AccountScreen() {
|
||||
value={newDomainInput}
|
||||
onChangeText={setNewDomainInput}
|
||||
placeholder="Örn: menu.kebapciahmet.com"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
style={{
|
||||
@@ -728,15 +728,17 @@ export default function AccountScreen() {
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
placeholder="Restoran Adı"
|
||||
placeholderTextColor="#57534E"
|
||||
style={{
|
||||
backgroundColor: "#FAF8F5",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 10,
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 12,
|
||||
fontSize: 14,
|
||||
color: "#1C1917",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -750,16 +752,18 @@ export default function AccountScreen() {
|
||||
value={phone}
|
||||
onChangeText={setPhone}
|
||||
placeholder="05xx xxx xx xx"
|
||||
placeholderTextColor="#57534E"
|
||||
keyboardType="phone-pad"
|
||||
style={{
|
||||
backgroundColor: "#FAF8F5",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 10,
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 12,
|
||||
fontSize: 14,
|
||||
color: "#1C1917",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -773,17 +777,19 @@ export default function AccountScreen() {
|
||||
value={address}
|
||||
onChangeText={setAddress}
|
||||
placeholder="Restoran adresi"
|
||||
placeholderTextColor="#57534E"
|
||||
multiline
|
||||
numberOfLines={2}
|
||||
style={{
|
||||
backgroundColor: "#FAF8F5",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 10,
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 12,
|
||||
fontSize: 14,
|
||||
color: "#1C1917",
|
||||
fontWeight: "500",
|
||||
minHeight: 64,
|
||||
}}
|
||||
/>
|
||||
@@ -806,85 +812,6 @@ export default function AccountScreen() {
|
||||
</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>
|
||||
);
|
||||
|
||||
@@ -101,12 +101,12 @@ export function AddCategoryModal({
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
placeholder="Örn: Tatlılar, Başlangıçlar, Sıcak İçecekler"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
autoFocus
|
||||
style={{
|
||||
backgroundColor: "#FAFAFA",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
padding: 14,
|
||||
fontSize: 15,
|
||||
|
||||
@@ -218,11 +218,11 @@ export function AddItemModal({
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
placeholder="Örn: Mercimek Çorbası"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
style={{
|
||||
backgroundColor: "#FAFAFA",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
padding: 14,
|
||||
fontSize: 15,
|
||||
@@ -241,8 +241,8 @@ export function AddItemModal({
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#FAFAFA",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 14,
|
||||
}}
|
||||
@@ -251,7 +251,7 @@ export function AddItemModal({
|
||||
value={price}
|
||||
onChangeText={setPrice}
|
||||
placeholder="120"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
keyboardType="decimal-pad"
|
||||
style={{
|
||||
flex: 1,
|
||||
@@ -273,13 +273,13 @@ export function AddItemModal({
|
||||
value={description}
|
||||
onChangeText={setDescription}
|
||||
placeholder="Kısa içerik veya porsiyon açıklaması..."
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
multiline
|
||||
numberOfLines={3}
|
||||
style={{
|
||||
backgroundColor: "#FAFAFA",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
padding: 14,
|
||||
fontSize: 14,
|
||||
|
||||
@@ -311,11 +311,11 @@ export function ItemDetailSheet({
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
placeholder="Örn: Izgara Levrek"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
style={{
|
||||
backgroundColor: "#FAFAFA",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
padding: 14,
|
||||
fontSize: 15,
|
||||
@@ -335,8 +335,8 @@ export function ItemDetailSheet({
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#FAFAFA",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 14,
|
||||
}}
|
||||
@@ -345,7 +345,7 @@ export function ItemDetailSheet({
|
||||
value={price}
|
||||
onChangeText={setPrice}
|
||||
placeholder="0.00"
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
keyboardType="decimal-pad"
|
||||
style={{
|
||||
flex: 1,
|
||||
@@ -368,13 +368,13 @@ export function ItemDetailSheet({
|
||||
value={description}
|
||||
onChangeText={setDescription}
|
||||
placeholder="İçindekiler, porsiyon bilgisi, pişirme tarzı..."
|
||||
placeholderTextColor="#A8A29E"
|
||||
placeholderTextColor="#57534E"
|
||||
multiline
|
||||
numberOfLines={3}
|
||||
style={{
|
||||
backgroundColor: "#FAFAFA",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
borderWidth: 1.5,
|
||||
borderColor: "#D6D3D1",
|
||||
borderRadius: 12,
|
||||
padding: 14,
|
||||
fontSize: 14,
|
||||
|
||||
@@ -285,7 +285,7 @@ export function ThemeSelectorSheet({
|
||||
Menü Şablonları & Canlı Önizleme
|
||||
</Text>
|
||||
<Text style={{ fontSize: 12, color: "#78716C", marginTop: 2 }}>
|
||||
5 lüks şablonu inceleyin, demolarını paylaşın veya menünüze uygulayın
|
||||
Şablonları inceleyin, demolarını görün veya menünüze uygulayın
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -323,61 +323,21 @@ export function ThemeSelectorSheet({
|
||||
elevation: isSelected ? 3 : 1,
|
||||
}}
|
||||
>
|
||||
{/* Top Row: Color Pip, Name, Category Badge & Active Check */}
|
||||
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }}>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
|
||||
<View
|
||||
style={{
|
||||
width: 18,
|
||||
height: 18,
|
||||
borderRadius: 9,
|
||||
backgroundColor: theme.color,
|
||||
borderWidth: 2,
|
||||
borderColor: "#FFFFFF",
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 2,
|
||||
elevation: 2,
|
||||
}}
|
||||
/>
|
||||
<View>
|
||||
<Text style={{ fontSize: 16, fontWeight: "800", color: "#1C1917" }}>
|
||||
{theme.name}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: theme.badgeBg,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 3,
|
||||
borderRadius: 6,
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: 11, fontWeight: "700", color: theme.badgeText }}>
|
||||
{theme.category}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Description */}
|
||||
<Text style={{ fontSize: 13, color: "#57534E", lineHeight: 18, marginBottom: 8 }}>
|
||||
{theme.desc}
|
||||
</Text>
|
||||
|
||||
{/* Meta details */}
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 6, marginBottom: 14 }}>
|
||||
<Ionicons name="sparkles" size={13} color={theme.color} />
|
||||
<Text style={{ fontSize: 11, color: "#78716C", fontWeight: "600" }}>
|
||||
Renk: <Text style={{ color: "#1C1917", fontWeight: "700" }}>{theme.accent}</Text> • {theme.suitableFor}
|
||||
{/* Top Row: Theme Name */}
|
||||
<View style={{ marginBottom: 6 }}>
|
||||
<Text style={{ fontSize: 17, fontWeight: "800", color: "#1C1917" }}>
|
||||
{theme.name}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Action Bar: Canlı Demo, Paylaş, Uygula */}
|
||||
<View style={{ flexDirection: "row", gap: 8, borderTopWidth: 1, borderTopColor: "#F5F5F4", paddingTop: 12 }}>
|
||||
{/* Canlı Demo Aç */}
|
||||
{/* Description */}
|
||||
<Text style={{ fontSize: 13, color: "#57534E", lineHeight: 18, marginBottom: 12 }}>
|
||||
{theme.desc}
|
||||
</Text>
|
||||
|
||||
{/* Action Bar: Canlı Demo & Şablon Seç */}
|
||||
<View style={{ flexDirection: "row", gap: 10, borderTopWidth: 1, borderTopColor: "#F5F5F4", paddingTop: 12 }}>
|
||||
{/* Canlı Demo Önizle */}
|
||||
<Pressable
|
||||
onPress={() => handleOpenDemo(theme.key)}
|
||||
style={({ pressed }) => ({
|
||||
@@ -385,43 +345,25 @@ export function ThemeSelectorSheet({
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 4,
|
||||
gap: 6,
|
||||
backgroundColor: "#F5F5F4",
|
||||
paddingVertical: 10,
|
||||
borderRadius: 10,
|
||||
paddingVertical: 12,
|
||||
borderRadius: 12,
|
||||
opacity: pressed ? 0.8 : 1,
|
||||
})}
|
||||
>
|
||||
<Ionicons name="eye-outline" size={15} color="#1C1917" />
|
||||
<Text style={{ fontSize: 12, fontWeight: "700", color: "#1C1917" }}>
|
||||
Demo Menü
|
||||
<Ionicons name="eye-outline" size={17} color="#1C1917" />
|
||||
<Text style={{ fontSize: 13, fontWeight: "700", color: "#1C1917" }}>
|
||||
Demo Önizle
|
||||
</Text>
|
||||
</Pressable>
|
||||
|
||||
{/* Demo Linkini Gönder / Paylaş */}
|
||||
<Pressable
|
||||
onPress={() => handleShareDemo(theme)}
|
||||
style={({ pressed }) => ({
|
||||
width: 40,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "#FAF8F5",
|
||||
borderWidth: 1,
|
||||
borderColor: "#E7E5E4",
|
||||
paddingVertical: 10,
|
||||
borderRadius: 10,
|
||||
opacity: pressed ? 0.8 : 1,
|
||||
})}
|
||||
>
|
||||
<Ionicons name="share-outline" size={16} color="#78716C" />
|
||||
</Pressable>
|
||||
|
||||
{/* Bu Şablonu Uygula / Aktif Rozeti */}
|
||||
<Pressable
|
||||
onPress={() => handleApplyTheme(theme.key)}
|
||||
disabled={isSelected || isApplying}
|
||||
style={({ pressed }) => ({
|
||||
flex: 1.3,
|
||||
flex: 1,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
@@ -429,8 +371,8 @@ export function ThemeSelectorSheet({
|
||||
backgroundColor: isSelected ? "#ECFDF5" : theme.color,
|
||||
borderWidth: isSelected ? 1 : 0,
|
||||
borderColor: "#A7F3D0",
|
||||
paddingVertical: 10,
|
||||
borderRadius: 10,
|
||||
paddingVertical: 12,
|
||||
borderRadius: 12,
|
||||
opacity: pressed ? 0.85 : 1,
|
||||
})}
|
||||
>
|
||||
@@ -438,15 +380,15 @@ export function ThemeSelectorSheet({
|
||||
<ActivityIndicator size="small" color="#FFFFFF" />
|
||||
) : isSelected ? (
|
||||
<>
|
||||
<Ionicons name="checkmark-circle" size={16} color="#059669" />
|
||||
<Text style={{ fontSize: 12, fontWeight: "800", color: "#059669" }}>
|
||||
<Ionicons name="checkmark-circle" size={17} color="#059669" />
|
||||
<Text style={{ fontSize: 13, fontWeight: "800", color: "#059669" }}>
|
||||
Aktif Şablon
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Ionicons name="checkmark" size={15} color="#FFFFFF" />
|
||||
<Text style={{ fontSize: 12, fontWeight: "800", color: "#FFFFFF" }}>
|
||||
<Ionicons name="checkmark" size={16} color="#FFFFFF" />
|
||||
<Text style={{ fontSize: 13, fontWeight: "800", color: "#FFFFFF" }}>
|
||||
Şablonu Seç
|
||||
</Text>
|
||||
</>
|
||||
|
||||
@@ -20,7 +20,7 @@ export default async function DemoMenuPage({ searchParams }: PageProps) {
|
||||
restaurant={DEMO_RESTAURANT}
|
||||
categories={DEMO_CATEGORIES}
|
||||
initialThemeKey={theme}
|
||||
allowThemeSwitching={true}
|
||||
allowThemeSwitching={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export default async function PublicMenuPage({ params, searchParams }: PageProps
|
||||
let restaurant: any = null;
|
||||
const { data: bySlug } = await supabase
|
||||
.from("restaurants")
|
||||
.select("id, name, slug, logo_url, phone, address")
|
||||
.select("id, name, slug, logo_url, cover_url, phone, address")
|
||||
.eq("slug", slug)
|
||||
.maybeSingle();
|
||||
|
||||
@@ -84,7 +84,7 @@ export default async function PublicMenuPage({ params, searchParams }: PageProps
|
||||
// 2. Try finding restaurant by custom domain hostname
|
||||
const { data: domainRow } = await supabase
|
||||
.from("domains")
|
||||
.select("restaurant_id, restaurants(id, name, slug, logo_url, phone, address)")
|
||||
.select("restaurant_id, restaurants(id, name, slug, logo_url, cover_url, phone, address)")
|
||||
.eq("hostname", slug)
|
||||
.maybeSingle();
|
||||
|
||||
@@ -144,6 +144,7 @@ export default async function PublicMenuPage({ params, searchParams }: PageProps
|
||||
name: restaurant.name,
|
||||
slug: restaurant.slug,
|
||||
logo_url: restaurant.logo_url,
|
||||
cover_url: restaurant.cover_url,
|
||||
phone: restaurant.phone,
|
||||
address: restaurant.address,
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface RestaurantData {
|
||||
name: string;
|
||||
slug: string;
|
||||
logo_url: string | null;
|
||||
cover_url?: string | null;
|
||||
phone?: string | null;
|
||||
address?: string | null;
|
||||
}
|
||||
@@ -43,7 +44,7 @@ export function PublicMenuClient({
|
||||
categories,
|
||||
initialThemeKey = "elegant",
|
||||
customThemeConfig,
|
||||
allowThemeSwitching = true,
|
||||
allowThemeSwitching = false,
|
||||
}: PublicMenuClientProps) {
|
||||
const [selectedThemeKey, setSelectedThemeKey] = useState<string>(initialThemeKey);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
@@ -176,26 +177,38 @@ export function PublicMenuClient({
|
||||
|
||||
{/* Header Hero Banner */}
|
||||
<header
|
||||
style={{ background: theme.headerGradient }}
|
||||
className="relative text-white px-4 sm:px-6 pt-8 sm:pt-10 pb-7 sm:pb-8 rounded-b-3xl shadow-lg overflow-hidden"
|
||||
style={{ background: restaurant.cover_url ? "none" : theme.headerGradient }}
|
||||
className="relative text-white px-4 sm:px-6 pt-8 sm:pt-10 pb-7 sm:pb-8 rounded-b-3xl shadow-lg overflow-hidden min-h-[200px]"
|
||||
>
|
||||
{/* Background Cover Image if available */}
|
||||
{restaurant.cover_url && (
|
||||
<>
|
||||
<img
|
||||
src={restaurant.cover_url}
|
||||
alt={restaurant.name}
|
||||
className="absolute inset-0 w-full h-full object-cover z-0"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-black/65 via-black/45 to-black/85 z-0 backdrop-blur-[1px]" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Subtle Ambient Glow */}
|
||||
<div
|
||||
className="absolute top-0 right-0 w-64 h-64 rounded-full blur-3xl opacity-20 pointer-events-none"
|
||||
style={{ backgroundColor: theme.config.primaryColor }}
|
||||
/>
|
||||
{!restaurant.cover_url && (
|
||||
<div
|
||||
className="absolute top-0 right-0 w-64 h-64 rounded-full blur-3xl opacity-20 pointer-events-none"
|
||||
style={{ backgroundColor: theme.config.primaryColor }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Top Bar: Table & Action Buttons */}
|
||||
<div className="flex items-center justify-between mb-4 sm:mb-5 relative z-10">
|
||||
<div className="flex items-center justify-between mb-2 relative z-10">
|
||||
{tableNumber ? (
|
||||
<div className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-white/10 backdrop-blur-md border border-white/15 text-xs font-semibold tracking-wide">
|
||||
<div className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-black/40 backdrop-blur-md border border-white/20 text-xs font-semibold tracking-wide text-white">
|
||||
<span className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse" />
|
||||
Masa {tableNumber}
|
||||
</div>
|
||||
) : (
|
||||
<div className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-white/10 backdrop-blur-md border border-white/15 text-xs font-medium tracking-wide">
|
||||
<span>✨</span> Dijital QR Menü
|
||||
</div>
|
||||
<div />
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-1.5 sm:gap-2">
|
||||
@@ -230,54 +243,84 @@ export function PublicMenuClient({
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Live Theme Switcher Trigger */}
|
||||
{allowThemeSwitching && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowThemePicker(!showThemePicker)}
|
||||
className="px-2.5 py-1 sm:py-1.5 rounded-full bg-white/10 hover:bg-white/20 active:scale-95 transition-all flex items-center gap-1.5 border border-white/15 backdrop-blur-md text-[11px] sm:text-xs font-semibold"
|
||||
aria-label="Tema Değiştir"
|
||||
>
|
||||
<span className="w-2 h-2 sm:w-2.5 sm:h-2.5 rounded-full" style={{ backgroundColor: theme.config.primaryColor }} />
|
||||
<span>Tema</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Restaurant Identity */}
|
||||
<div className="flex items-center gap-3 sm:gap-4 relative z-10">
|
||||
{restaurant.logo_url ? (
|
||||
<img
|
||||
src={restaurant.logo_url}
|
||||
alt={restaurant.name}
|
||||
className="w-14 h-14 sm:w-16 sm:h-16 rounded-2xl object-cover border-2 shadow-md flex-shrink-0"
|
||||
style={{ borderColor: theme.config.primaryColor }}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="w-14 h-14 sm:w-16 sm:h-16 rounded-2xl flex items-center justify-center font-bold text-xl sm:text-2xl flex-shrink-0 shadow-inner border border-white/20"
|
||||
style={{
|
||||
background: "rgba(255, 255, 255, 0.12)",
|
||||
color: theme.config.primaryColor,
|
||||
}}
|
||||
>
|
||||
{restaurant.name.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
{/* Restaurant Identity - Dynamic Header Layout (Centered or Left) */}
|
||||
{theme.config.headerLayout === "left" ? (
|
||||
/* Left-Aligned Header Layout */
|
||||
<div className="flex items-center gap-3.5 sm:gap-4 relative z-10 pt-1 pb-1">
|
||||
{restaurant.logo_url ? (
|
||||
<img
|
||||
src={restaurant.logo_url}
|
||||
alt={restaurant.name}
|
||||
className="w-14 h-14 sm:w-16 sm:h-16 rounded-2xl object-cover border-2 shadow-md flex-shrink-0"
|
||||
style={{ borderColor: theme.config.primaryColor }}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="w-14 h-14 sm:w-16 sm:h-16 rounded-2xl flex items-center justify-center font-bold text-xl sm:text-2xl flex-shrink-0 shadow-inner border border-white/20"
|
||||
style={{
|
||||
background: "rgba(255, 255, 255, 0.12)",
|
||||
color: theme.config.primaryColor,
|
||||
}}
|
||||
>
|
||||
{restaurant.name.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl sm:text-2xl font-bold tracking-tight leading-snug break-words">
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-xl sm:text-2xl font-bold tracking-tight leading-snug break-words text-white">
|
||||
{restaurant.name}
|
||||
</h1>
|
||||
<p className="text-[11px] sm:text-xs text-white/80 mt-0.5 flex items-center gap-1.5 font-medium">
|
||||
<span>{totalItemsCount} Özel Lezzet</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
/* Centered Header Layout */
|
||||
<div className="flex flex-col items-center text-center relative z-10 pt-1 pb-2">
|
||||
{/* Centered Logo Avatar */}
|
||||
<div className="relative group">
|
||||
{restaurant.logo_url ? (
|
||||
<img
|
||||
src={restaurant.logo_url}
|
||||
alt={restaurant.name}
|
||||
className="w-18 h-18 sm:w-20 sm:h-20 rounded-full object-cover border-2 shadow-2xl transition-transform duration-300 group-hover:scale-105"
|
||||
style={{ borderColor: theme.config.primaryColor }}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="w-18 h-18 sm:w-20 sm:h-20 rounded-full flex items-center justify-center font-black text-2xl sm:text-3xl shadow-2xl border-2 backdrop-blur-md"
|
||||
style={{
|
||||
background: "rgba(255, 255, 255, 0.15)",
|
||||
borderColor: theme.config.primaryColor,
|
||||
color: "#FFFFFF",
|
||||
}}
|
||||
>
|
||||
{restaurant.name.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
{/* Decorative Ambient Glow Ring */}
|
||||
<div
|
||||
className="absolute -inset-1 rounded-full blur-md opacity-30 pointer-events-none"
|
||||
style={{ backgroundColor: theme.config.primaryColor }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Centered Restaurant Title */}
|
||||
<h1 className="text-2xl sm:text-3xl font-extrabold tracking-tight leading-snug mt-3 text-white drop-shadow-md max-w-md">
|
||||
{restaurant.name}
|
||||
</h1>
|
||||
<p className="text-[11px] sm:text-xs text-white/70 mt-0.5 flex flex-wrap items-center gap-1.5">
|
||||
|
||||
{/* Centered Subtitle Badge Pill */}
|
||||
<div className="mt-2 inline-flex items-center gap-1.5 px-3.5 py-1 rounded-full bg-black/35 backdrop-blur-md border border-white/15 text-xs font-medium text-white/90 shadow-sm">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-amber-400 animate-pulse" />
|
||||
<span>{totalItemsCount} Özel Lezzet</span>
|
||||
<span>•</span>
|
||||
<span className="capitalize">{theme.name} Şablonu</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Live Search Bar */}
|
||||
<div className="mt-6 relative z-10">
|
||||
@@ -810,12 +853,6 @@ export function PublicMenuClient({
|
||||
>
|
||||
₺{item.price.toFixed(0)}
|
||||
</span>
|
||||
<span
|
||||
className="w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold text-stone-950 shadow-sm transition-transform group-hover:scale-110"
|
||||
style={{ backgroundColor: theme.config.primaryColor }}
|
||||
>
|
||||
+
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@@ -905,48 +942,19 @@ export function PublicMenuClient({
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
{/* Floating Quick Action Bar (Garson Çağır / Hesap İste / Başa Dön) */}
|
||||
<aside aria-label="Masa Servis İşlemleri" className="fixed bottom-5 left-1/2 -translate-x-1/2 z-40 max-w-sm w-full px-4 flex items-center justify-between gap-2 pointer-events-none">
|
||||
<div className="flex items-center gap-2 pointer-events-auto shadow-2xl rounded-full p-1 bg-stone-900/90 backdrop-blur-lg border border-stone-700 text-white">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
triggerServiceAction(
|
||||
tableNumber
|
||||
? `Masa ${tableNumber} için Garson Çağrıldı! Garsonunuz en kısa sürede masanızda olacaktır.`
|
||||
: "Garson çağrıldı! Garsonunuz hemen masanızda olacaktır.",
|
||||
)
|
||||
}
|
||||
className="px-3.5 py-2 rounded-full hover:bg-white/15 active:scale-95 transition-all text-xs font-bold flex items-center gap-1.5"
|
||||
>
|
||||
<span>👋</span> Garson Çağır
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
triggerServiceAction(
|
||||
tableNumber
|
||||
? `Masa ${tableNumber} için Hesap İsteği iletildi!`
|
||||
: "Hesap isteği iletildi!",
|
||||
)
|
||||
}
|
||||
className="px-3.5 py-2 rounded-full hover:bg-white/15 active:scale-95 transition-all text-xs font-bold flex items-center gap-1.5"
|
||||
>
|
||||
<span>💳</span> Hesap İste
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{showBackToTop && (
|
||||
{/* Floating Back to Top Button */}
|
||||
{showBackToTop && (
|
||||
<aside aria-label="Başa Dön" className="fixed bottom-5 right-5 z-40">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => window.scrollTo({ top: 0, behavior: "smooth" })}
|
||||
className="w-11 h-11 rounded-full bg-stone-900 text-white shadow-2xl flex items-center justify-center pointer-events-auto active:scale-90 transition-all border border-stone-700"
|
||||
className="w-11 h-11 rounded-full bg-stone-900 text-white shadow-2xl flex items-center justify-center active:scale-90 transition-all border border-stone-700 font-bold"
|
||||
aria-label="Başa Dön"
|
||||
>
|
||||
↑
|
||||
</button>
|
||||
)}
|
||||
</aside>
|
||||
</aside>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Item Detail Modal */}
|
||||
@@ -1003,30 +1011,17 @@ export function PublicMenuClient({
|
||||
{selectedItem.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Quality Badges */}
|
||||
<div className="pt-2 flex flex-wrap gap-2 text-xs">
|
||||
<span className="px-3 py-1 rounded-full bg-emerald-50 text-emerald-700 font-semibold border border-emerald-200">
|
||||
🌱 Taze & Günlük
|
||||
</span>
|
||||
<span className="px-3 py-1 rounded-full bg-amber-50 text-amber-700 font-semibold border border-amber-200">
|
||||
⭐ Şefin İmzası
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Modal Footer */}
|
||||
<div className="p-4 border-t" style={{ borderColor: theme.cardBorder }}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setSelectedItem(null);
|
||||
triggerServiceAction(`"${selectedItem.name}" sipariş tercihleriniz garsona iletildi!`);
|
||||
}}
|
||||
onClick={() => setSelectedItem(null)}
|
||||
className="w-full py-3.5 rounded-2xl font-bold text-white shadow-lg active:scale-98 transition-all flex items-center justify-center gap-2 text-sm"
|
||||
style={{ backgroundColor: theme.config.primaryColor }}
|
||||
>
|
||||
<span>➕</span> Garsona Sipariş Olarak Bildir
|
||||
Kapat
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { PublicMenuClient, type MenuCategory, type RestaurantData } from "@/components/PublicMenuClient";
|
||||
import { THEME_PRESETS, type ThemePreset } from "@/lib/theme";
|
||||
import { THEME_PRESETS } from "@/lib/theme";
|
||||
|
||||
interface TemplateGalleryClientProps {
|
||||
restaurant: RestaurantData;
|
||||
@@ -14,199 +13,86 @@ interface TemplateGalleryClientProps {
|
||||
export function TemplateGalleryClient({
|
||||
restaurant,
|
||||
categories,
|
||||
initialThemeKey = "elegant",
|
||||
}: TemplateGalleryClientProps) {
|
||||
const [selectedThemeKey, setSelectedThemeKey] = useState<string>(initialThemeKey);
|
||||
const [viewMode, setViewMode] = useState<"phone" | "fullscreen">("phone");
|
||||
|
||||
const currentPreset: ThemePreset = THEME_PRESETS[selectedThemeKey] || THEME_PRESETS.elegant!;
|
||||
const presetsList = Object.values(THEME_PRESETS);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-stone-950 text-stone-100 flex flex-col font-sans">
|
||||
{/* Top Navbar */}
|
||||
<header className="border-b border-stone-800/80 bg-stone-900/90 backdrop-blur-xl sticky top-0 z-50 px-4 sm:px-6 py-3.5">
|
||||
<div className="max-w-7xl mx-auto flex flex-col md:flex-row items-center justify-between gap-3">
|
||||
<div className="flex items-center justify-between w-full md:w-auto">
|
||||
<div className="flex items-center gap-3">
|
||||
<Link href="/" className="font-black text-xl tracking-tight text-amber-400">
|
||||
MENULIO
|
||||
</Link>
|
||||
<span className="text-stone-600 hidden sm:inline">/</span>
|
||||
<span className="text-xs font-semibold text-stone-400 hidden sm:inline">Şablon Galerisi</span>
|
||||
</div>
|
||||
|
||||
{/* View Mode Toggle (Visible on desktop/tablet) */}
|
||||
<div className="flex items-center bg-stone-800/80 p-1 rounded-xl border border-stone-700 md:hidden">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setViewMode("phone")}
|
||||
className={`px-2.5 py-1 rounded-lg text-xs font-bold transition-all ${
|
||||
viewMode === "phone" ? "bg-amber-400 text-stone-950 shadow" : "text-stone-400"
|
||||
}`}
|
||||
>
|
||||
📱 Mobil
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setViewMode("fullscreen")}
|
||||
className={`px-2.5 py-1 rounded-lg text-xs font-bold transition-all ${
|
||||
viewMode === "fullscreen" ? "bg-amber-400 text-stone-950 shadow" : "text-stone-400"
|
||||
}`}
|
||||
>
|
||||
🖥️ Tam
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 5 Theme Pills - Smooth Horizontal Scroll */}
|
||||
<div className="flex items-center gap-2 overflow-x-auto w-full md:w-auto pb-1 md:pb-0 no-scrollbar">
|
||||
{Object.values(THEME_PRESETS).map((preset) => {
|
||||
const isActive = selectedThemeKey === preset.key;
|
||||
return (
|
||||
<button
|
||||
key={preset.key}
|
||||
type="button"
|
||||
onClick={() => setSelectedThemeKey(preset.key)}
|
||||
className={`px-3.5 py-1.5 rounded-xl text-xs font-bold transition-all flex items-center gap-2 whitespace-nowrap border flex-shrink-0 ${
|
||||
isActive
|
||||
? "bg-white text-stone-950 border-white shadow-lg scale-105"
|
||||
: "bg-stone-800/90 text-stone-300 border-stone-700 hover:border-stone-500 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className="w-2.5 h-2.5 rounded-full"
|
||||
style={{ backgroundColor: preset.config.primaryColor }}
|
||||
/>
|
||||
<span>{preset.name}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="hidden md:flex items-center gap-3">
|
||||
{/* View Mode Toggle (Desktop) */}
|
||||
<div className="flex items-center bg-stone-800/80 p-1 rounded-xl border border-stone-700">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setViewMode("phone")}
|
||||
className={`px-3 py-1 rounded-lg text-xs font-bold transition-all ${
|
||||
viewMode === "phone" ? "bg-amber-400 text-stone-950 shadow" : "text-stone-400 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
📱 Telefon
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setViewMode("fullscreen")}
|
||||
className={`px-3 py-1 rounded-lg text-xs font-bold transition-all ${
|
||||
viewMode === "fullscreen" ? "bg-amber-400 text-stone-950 shadow" : "text-stone-400 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
🖥️ Tam Ekran
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/"
|
||||
className="text-xs font-bold px-4 py-2 rounded-xl bg-amber-500 hover:bg-amber-400 text-stone-950 transition-all shadow-md shadow-amber-500/20"
|
||||
>
|
||||
Uygulamayı İndir →
|
||||
<div className="max-w-7xl mx-auto flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<Link href="/" className="font-black text-xl tracking-tight text-amber-400">
|
||||
MENULIO
|
||||
</Link>
|
||||
<span className="text-stone-600 hidden sm:inline">/</span>
|
||||
<span className="text-xs font-semibold text-stone-400 hidden sm:inline">
|
||||
Şablon Galerisi
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/"
|
||||
className="text-xs font-bold px-4 py-2 rounded-xl bg-amber-500 hover:bg-amber-400 text-stone-950 transition-all shadow-md shadow-amber-500/20"
|
||||
>
|
||||
Ana Sayfa →
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<main className="flex-1 w-full flex flex-col items-center justify-center p-0 sm:p-6 lg:p-8">
|
||||
{viewMode === "fullscreen" ? (
|
||||
/* Fullscreen Fluid View */
|
||||
<div className="w-full flex-1 min-h-[85vh]">
|
||||
<PublicMenuClient
|
||||
restaurant={restaurant}
|
||||
categories={categories}
|
||||
initialThemeKey={selectedThemeKey}
|
||||
allowThemeSwitching={true}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
/* Responsive Device Simulator / Side-by-Side on Desktop */
|
||||
<div className="w-full max-w-6xl mx-auto grid grid-cols-1 lg:grid-cols-12 gap-8 items-center justify-center py-4">
|
||||
{/* Left Info Panel (Visible on Desktop) */}
|
||||
<div className="hidden lg:flex lg:col-span-5 flex-col space-y-6 pr-4">
|
||||
<div className="space-y-2">
|
||||
<span className="text-amber-400 text-xs font-bold uppercase tracking-wider">
|
||||
ŞABLON DETAYI
|
||||
</span>
|
||||
<h2 className="text-3xl font-extrabold text-white">
|
||||
{currentPreset.name}
|
||||
</h2>
|
||||
<p className="text-sm text-stone-400 leading-relaxed">
|
||||
{currentPreset.key === "elegant" && "Fine dining restoranlar, şarap evleri ve lüks steakhouse mekanlar için altın & krem tonlarında yüksek prestijli tasarım."}
|
||||
{currentPreset.key === "modern" && "Kafeler, burgerciler ve dinamik mekanlar için canlı mavi safir tonları ve hızlı arama odaklı ızgara düzeni."}
|
||||
{currentPreset.key === "dark" && "Gece kulüpleri, kokteyl barlar ve lounge mekanlar için obsidian siyahı ve kehribar parıltılı lüks mod."}
|
||||
{currentPreset.key === "minimal" && "Butik kahveciler, fırınlar ve üçüncü nesil mekanlar için ferah ve monokromatik Nordic mizanpaj."}
|
||||
{currentPreset.key === "classic" && "Geleneksel brasserie'ler, meyhaneler ve trattoria'lar için Toskana bordo ve sıcak rustik doku."}
|
||||
</p>
|
||||
{/* Main Content Area - Stacked Vertically */}
|
||||
<main className="flex-1 w-full max-w-4xl mx-auto py-10 px-4 space-y-16">
|
||||
{presetsList.map((preset) => (
|
||||
<section
|
||||
key={preset.key}
|
||||
className="flex flex-col items-center gap-8 border-b border-stone-800/80 pb-16 last:border-0"
|
||||
>
|
||||
{/* Theme Info */}
|
||||
<div className="text-center max-w-xl space-y-3">
|
||||
<div className="inline-flex items-center gap-2.5 px-3.5 py-1.5 rounded-full bg-stone-900 border border-stone-800 text-xs font-bold text-stone-300">
|
||||
<span
|
||||
className="w-2.5 h-2.5 rounded-full"
|
||||
style={{ backgroundColor: preset.config.primaryColor }}
|
||||
/>
|
||||
<span className="uppercase tracking-wider text-[11px]">{preset.key} Şablonu</span>
|
||||
</div>
|
||||
|
||||
{/* Theme Specs */}
|
||||
<div className="p-5 rounded-2xl bg-stone-900 border border-stone-800 space-y-3.5 text-xs">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-stone-400">Vurgu Rengi</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className="w-4 h-4 rounded-full border border-white/20"
|
||||
style={{ backgroundColor: currentPreset.config.primaryColor }}
|
||||
/>
|
||||
<span className="font-mono text-stone-200">{currentPreset.config.primaryColor}</span>
|
||||
</div>
|
||||
</div>
|
||||
<h2 className="text-2xl sm:text-3xl font-extrabold text-white">
|
||||
{preset.name}
|
||||
</h2>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-stone-400">Tipografi Ailesi</span>
|
||||
<span className="font-semibold text-stone-200 uppercase">{currentPreset.fontFamily}</span>
|
||||
</div>
|
||||
<p className="text-xs sm:text-sm text-stone-400 leading-relaxed">
|
||||
Tipografi: <span className="uppercase text-stone-200 font-semibold">{preset.fontFamily}</span> • Mizanpaj: <span className="uppercase text-stone-200 font-semibold">{preset.config.productLayout}</span>
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-stone-400">Ürün Mizanpajı</span>
|
||||
<span className="font-semibold text-stone-200 uppercase">{currentPreset.config.productLayout}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-stone-400">Kategori Gezintisi</span>
|
||||
<span className="font-semibold text-stone-200 uppercase">{currentPreset.config.categoryLayout}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Direct Demo Link */}
|
||||
<Link
|
||||
href={`/demo?theme=${selectedThemeKey}`}
|
||||
target="_blank"
|
||||
className="w-full py-3.5 rounded-xl font-bold text-center bg-stone-800 hover:bg-stone-700 text-stone-200 border border-stone-700 transition-all text-xs flex items-center justify-center gap-2"
|
||||
>
|
||||
<span>↗</span> Yeni Sekmede Canlı Menüyü Aç
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Right Phone Mockup Container */}
|
||||
<div className="lg:col-span-7 flex justify-center w-full">
|
||||
<div className="w-full max-w-[420px] rounded-[36px] sm:rounded-[44px] overflow-hidden shadow-2xl border-0 sm:border-[8px] border-stone-800 bg-stone-900 relative">
|
||||
{/* Dynamic Island (Desktop only) */}
|
||||
<div className="hidden sm:block w-24 h-4 bg-stone-800 rounded-full mx-auto mt-2 mb-1" />
|
||||
|
||||
{/* Simulated Screen Body */}
|
||||
<div className="overflow-y-auto max-h-[85vh] sm:max-h-[780px] rounded-none sm:rounded-[32px] no-scrollbar">
|
||||
<PublicMenuClient
|
||||
restaurant={restaurant}
|
||||
categories={categories}
|
||||
initialThemeKey={selectedThemeKey}
|
||||
allowThemeSwitching={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-2">
|
||||
<Link
|
||||
href={`/demo?theme=${preset.key}`}
|
||||
target="_blank"
|
||||
className="inline-flex items-center gap-2 px-4 py-2.5 rounded-xl bg-stone-900 hover:bg-stone-800 text-stone-200 border border-stone-700/80 text-xs font-bold transition-all shadow-md"
|
||||
>
|
||||
<span>↗</span> Canlı Demoyu Sekmede Aç
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Phone Mockup Preview */}
|
||||
<div className="w-full max-w-[420px] rounded-[36px] sm:rounded-[44px] overflow-hidden shadow-2xl border-0 sm:border-[8px] border-stone-800 bg-stone-900 relative">
|
||||
{/* Dynamic Island */}
|
||||
<div className="hidden sm:block w-24 h-4 bg-stone-800 rounded-full mx-auto mt-2.5 mb-1" />
|
||||
|
||||
{/* Screen Body */}
|
||||
<div className="overflow-y-auto max-h-[720px] rounded-none sm:rounded-[32px] no-scrollbar">
|
||||
<PublicMenuClient
|
||||
restaurant={restaurant}
|
||||
categories={categories}
|
||||
initialThemeKey={preset.key}
|
||||
allowThemeSwitching={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ export const DEMO_RESTAURANT: RestaurantData = {
|
||||
name: "Gusto & Co. Brasserie",
|
||||
slug: "gusto-brasserie",
|
||||
logo_url: null,
|
||||
cover_url: "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=1200&auto=format&fit=crop&q=80",
|
||||
phone: "+90 (212) 555 0192",
|
||||
address: "Nişantaşı, Abdi İpekçi Cad. No: 42, İstanbul",
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@ export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||
background: "#F7F3EC",
|
||||
productLayout: "card",
|
||||
categoryLayout: "tabs",
|
||||
headerLayout: "centered",
|
||||
},
|
||||
cardBg: "#FCFAF6",
|
||||
cardBorder: "#E4DBCF",
|
||||
@@ -44,6 +45,7 @@ export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||
background: "#0D1117",
|
||||
productLayout: "card",
|
||||
categoryLayout: "tabs",
|
||||
headerLayout: "left",
|
||||
},
|
||||
cardBg: "#FCF7EC",
|
||||
cardBorder: "rgba(255, 42, 133, 0.3)",
|
||||
@@ -63,6 +65,7 @@ export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||
background: "#EEEAE4",
|
||||
productLayout: "card",
|
||||
categoryLayout: "tabs",
|
||||
headerLayout: "centered",
|
||||
},
|
||||
cardBg: "#F7F4EF",
|
||||
cardBorder: "rgba(36, 70, 58, 0.2)",
|
||||
@@ -82,6 +85,7 @@ export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||
background: "#FFFFFF",
|
||||
productLayout: "card",
|
||||
categoryLayout: "tabs",
|
||||
headerLayout: "left",
|
||||
},
|
||||
cardBg: "#FFFFFF",
|
||||
cardBorder: "#000000",
|
||||
@@ -101,6 +105,7 @@ export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||
background: "#121316",
|
||||
productLayout: "card",
|
||||
categoryLayout: "tabs",
|
||||
headerLayout: "centered",
|
||||
},
|
||||
cardBg: "#1C1E24",
|
||||
cardBorder: "rgba(255, 255, 255, 0.08)",
|
||||
@@ -120,6 +125,7 @@ export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||
background: "#FAF8F5",
|
||||
productLayout: "card",
|
||||
categoryLayout: "accordion",
|
||||
headerLayout: "centered",
|
||||
},
|
||||
cardBg: "#FFFFFF",
|
||||
cardBorder: "rgba(200, 169, 107, 0.18)",
|
||||
@@ -139,6 +145,7 @@ export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||
background: "#F8FAFC",
|
||||
productLayout: "card",
|
||||
categoryLayout: "tabs",
|
||||
headerLayout: "left",
|
||||
},
|
||||
cardBg: "#FFFFFF",
|
||||
cardBorder: "rgba(226, 232, 240, 0.9)",
|
||||
@@ -158,6 +165,7 @@ export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||
background: "#0F0F12",
|
||||
productLayout: "card",
|
||||
categoryLayout: "accordion",
|
||||
headerLayout: "centered",
|
||||
},
|
||||
cardBg: "#18181D",
|
||||
cardBorder: "rgba(255, 255, 255, 0.07)",
|
||||
@@ -177,6 +185,7 @@ export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||
background: "#FAFAFA",
|
||||
productLayout: "list",
|
||||
categoryLayout: "flat",
|
||||
headerLayout: "left",
|
||||
},
|
||||
cardBg: "#FFFFFF",
|
||||
cardBorder: "#E4E4E7",
|
||||
@@ -196,6 +205,7 @@ export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||
background: "#FFF9F2",
|
||||
productLayout: "list",
|
||||
categoryLayout: "accordion",
|
||||
headerLayout: "centered",
|
||||
},
|
||||
cardBg: "#FFFFFF",
|
||||
cardBorder: "rgba(139, 30, 30, 0.15)",
|
||||
|
||||
@@ -7,6 +7,7 @@ export const themeConfigSchema = z.object({
|
||||
background: z.string(),
|
||||
productLayout: z.enum(["card", "list"]),
|
||||
categoryLayout: z.enum(["accordion", "tabs", "flat"]),
|
||||
headerLayout: z.enum(["centered", "left"]).optional().default("centered"),
|
||||
});
|
||||
|
||||
export type ThemeConfig = z.infer<typeof themeConfigSchema>;
|
||||
|
||||
@@ -27,6 +27,7 @@ create table restaurants (
|
||||
name text not null,
|
||||
slug text not null unique,
|
||||
logo_url text,
|
||||
cover_url text,
|
||||
phone text,
|
||||
address text,
|
||||
created_by uuid not null references users (id) on delete restrict,
|
||||
|
||||
Reference in New Issue
Block a user