315 lines
10 KiB
TypeScript
315 lines
10 KiB
TypeScript
import { useState } from "react";
|
||
import {
|
||
Alert,
|
||
Image,
|
||
KeyboardAvoidingView,
|
||
Modal,
|
||
Platform,
|
||
Pressable,
|
||
ScrollView,
|
||
Text,
|
||
TextInput,
|
||
View,
|
||
} from "react-native";
|
||
import { Ionicons } from "@expo/vector-icons";
|
||
import * as Haptics from "expo-haptics";
|
||
import * as ImagePicker from "expo-image-picker";
|
||
|
||
interface AddItemModalProps {
|
||
visible: boolean;
|
||
categoryName: string;
|
||
categoryId: string;
|
||
onClose: () => void;
|
||
onAdd: (categoryId: string, name: string, price: number, description?: string, imageUrl?: string) => Promise<void>;
|
||
}
|
||
|
||
export function AddItemModal({
|
||
visible,
|
||
categoryName,
|
||
categoryId,
|
||
onClose,
|
||
onAdd,
|
||
}: AddItemModalProps) {
|
||
const [name, setName] = useState("");
|
||
const [price, setPrice] = useState("");
|
||
const [description, setDescription] = useState("");
|
||
const [imageUrl, setImageUrl] = useState<string | null>(null);
|
||
const [adding, setAdding] = useState(false);
|
||
|
||
async function handlePickImage() {
|
||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {});
|
||
const permission = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
||
if (!permission.granted) {
|
||
Alert.alert("İzin Gerekli", "Ürün görseli seçebilmek için fotoğraf galerisi erişim izni gereklidir.");
|
||
return;
|
||
}
|
||
|
||
const result = await ImagePicker.launchImageLibraryAsync({
|
||
mediaTypes: ["images"],
|
||
allowsEditing: true,
|
||
aspect: [4, 3],
|
||
quality: 0.6,
|
||
base64: true,
|
||
});
|
||
|
||
if (!result.canceled && result.assets[0]) {
|
||
const asset = result.assets[0];
|
||
if (asset.base64) {
|
||
setImageUrl(`data:image/jpeg;base64,${asset.base64}`);
|
||
} else {
|
||
setImageUrl(asset.uri);
|
||
}
|
||
}
|
||
}
|
||
|
||
async function handleAdd() {
|
||
const parsedPrice = Number(price.replace(",", "."));
|
||
if (!name.trim()) {
|
||
Alert.alert("Eksik Bilgi", "Lütfen ürün adını giriniz.");
|
||
return;
|
||
}
|
||
if (Number.isNaN(parsedPrice) || parsedPrice < 0) {
|
||
Alert.alert("Geçersiz Fiyat", "Lütfen geçerli bir fiyat giriniz.");
|
||
return;
|
||
}
|
||
|
||
setAdding(true);
|
||
try {
|
||
await onAdd(
|
||
categoryId,
|
||
name.trim(),
|
||
parsedPrice,
|
||
description.trim() || undefined,
|
||
imageUrl || undefined,
|
||
);
|
||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {});
|
||
setName("");
|
||
setPrice("");
|
||
setDescription("");
|
||
setImageUrl(null);
|
||
onClose();
|
||
} catch (err) {
|
||
Alert.alert("Hata", err instanceof Error ? err.message : "Ürün eklenemedi.");
|
||
} finally {
|
||
setAdding(false);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<Modal
|
||
visible={visible}
|
||
animationType="slide"
|
||
transparent
|
||
onRequestClose={onClose}
|
||
>
|
||
<KeyboardAvoidingView
|
||
behavior={Platform.OS === "ios" ? "padding" : undefined}
|
||
style={{ flex: 1, backgroundColor: "rgba(0,0,0,0.5)", justifyContent: "flex-end" }}
|
||
>
|
||
<Pressable style={{ flex: 1 }} onPress={onClose} />
|
||
|
||
<View
|
||
style={{
|
||
backgroundColor: "#FFFFFF",
|
||
borderTopLeftRadius: 24,
|
||
borderTopRightRadius: 24,
|
||
maxHeight: "88%",
|
||
paddingBottom: Platform.OS === "ios" ? 34 : 20,
|
||
}}
|
||
>
|
||
<View style={{ alignItems: "center", paddingTop: 10, paddingBottom: 6 }}>
|
||
<View
|
||
style={{
|
||
width: 36,
|
||
height: 4,
|
||
borderRadius: 2,
|
||
backgroundColor: "#E7E5E4",
|
||
}}
|
||
/>
|
||
</View>
|
||
|
||
<View
|
||
style={{
|
||
flexDirection: "row",
|
||
justifyContent: "space-between",
|
||
alignItems: "center",
|
||
paddingHorizontal: 20,
|
||
paddingVertical: 12,
|
||
borderBottomWidth: 1,
|
||
borderBottomColor: "#F5F5F4",
|
||
}}
|
||
>
|
||
<View>
|
||
<Text style={{ fontSize: 18, fontWeight: "700", color: "#1C1917" }}>
|
||
Yeni Ürün Ekle
|
||
</Text>
|
||
<Text style={{ fontSize: 12, color: "#78716C", marginTop: 2 }}>
|
||
Kategori: <Text style={{ fontWeight: "700", color: "#C8A96B" }}>{categoryName}</Text>
|
||
</Text>
|
||
</View>
|
||
|
||
<Pressable
|
||
onPress={onClose}
|
||
style={{
|
||
width: 36,
|
||
height: 36,
|
||
borderRadius: 18,
|
||
backgroundColor: "#F5F5F4",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
}}
|
||
>
|
||
<Ionicons name="close" size={20} color="#78716C" />
|
||
</Pressable>
|
||
</View>
|
||
|
||
<ScrollView contentContainerStyle={{ padding: 20, gap: 16 }}>
|
||
{/* Image Picker */}
|
||
<View>
|
||
<Text style={{ fontSize: 13, fontWeight: "700", color: "#44403C", marginBottom: 8 }}>
|
||
Ürün Fotoğrafı (Opsiyonel)
|
||
</Text>
|
||
{imageUrl ? (
|
||
<View style={{ position: "relative", width: "100%", height: 160, borderRadius: 14, overflow: "hidden", borderWidth: 1, borderColor: "#E7E5E4" }}>
|
||
<Image source={{ uri: imageUrl }} style={{ width: "100%", height: 160 }} resizeMode="cover" />
|
||
<Pressable
|
||
onPress={() => setImageUrl(null)}
|
||
style={{
|
||
position: "absolute",
|
||
top: 10,
|
||
right: 10,
|
||
backgroundColor: "rgba(0,0,0,0.65)",
|
||
borderRadius: 14,
|
||
padding: 6,
|
||
}}
|
||
>
|
||
<Ionicons name="trash-outline" size={18} color="#FFFFFF" />
|
||
</Pressable>
|
||
</View>
|
||
) : (
|
||
<Pressable
|
||
onPress={handlePickImage}
|
||
style={({ pressed }) => ({
|
||
height: 100,
|
||
borderRadius: 14,
|
||
borderWidth: 1.5,
|
||
borderStyle: "dashed",
|
||
borderColor: "#D6D3D1",
|
||
backgroundColor: "#FAF8F5",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
gap: 6,
|
||
opacity: pressed ? 0.8 : 1,
|
||
})}
|
||
>
|
||
<Ionicons name="camera-outline" size={26} color="#C8A96B" />
|
||
<Text style={{ fontSize: 13, fontWeight: "600", color: "#78716C" }}>
|
||
Galeriden Fotoğraf Seç
|
||
</Text>
|
||
</Pressable>
|
||
)}
|
||
</View>
|
||
|
||
<View>
|
||
<Text style={{ fontSize: 13, fontWeight: "700", color: "#44403C", marginBottom: 6 }}>
|
||
Ürün Adı *
|
||
</Text>
|
||
<TextInput
|
||
value={name}
|
||
onChangeText={setName}
|
||
placeholder="Örn: Mercimek Çorbası"
|
||
placeholderTextColor="#A8A29E"
|
||
style={{
|
||
backgroundColor: "#FAFAFA",
|
||
borderWidth: 1,
|
||
borderColor: "#E7E5E4",
|
||
borderRadius: 12,
|
||
padding: 14,
|
||
fontSize: 15,
|
||
fontWeight: "600",
|
||
color: "#1C1917",
|
||
}}
|
||
/>
|
||
</View>
|
||
|
||
<View>
|
||
<Text style={{ fontSize: 13, fontWeight: "700", color: "#44403C", marginBottom: 6 }}>
|
||
Fiyat (₺) *
|
||
</Text>
|
||
<View
|
||
style={{
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
backgroundColor: "#FAFAFA",
|
||
borderWidth: 1,
|
||
borderColor: "#E7E5E4",
|
||
borderRadius: 12,
|
||
paddingHorizontal: 14,
|
||
}}
|
||
>
|
||
<TextInput
|
||
value={price}
|
||
onChangeText={setPrice}
|
||
placeholder="120"
|
||
placeholderTextColor="#A8A29E"
|
||
keyboardType="decimal-pad"
|
||
style={{
|
||
flex: 1,
|
||
paddingVertical: 14,
|
||
fontSize: 16,
|
||
fontWeight: "700",
|
||
color: "#C8A96B",
|
||
}}
|
||
/>
|
||
<Text style={{ fontSize: 16, fontWeight: "700", color: "#1C1917" }}>₺</Text>
|
||
</View>
|
||
</View>
|
||
|
||
<View>
|
||
<Text style={{ fontSize: 13, fontWeight: "700", color: "#44403C", marginBottom: 6 }}>
|
||
Açıklama (Opsiyonel)
|
||
</Text>
|
||
<TextInput
|
||
value={description}
|
||
onChangeText={setDescription}
|
||
placeholder="Kısa içerik veya porsiyon açıklaması..."
|
||
placeholderTextColor="#A8A29E"
|
||
multiline
|
||
numberOfLines={3}
|
||
style={{
|
||
backgroundColor: "#FAFAFA",
|
||
borderWidth: 1,
|
||
borderColor: "#E7E5E4",
|
||
borderRadius: 12,
|
||
padding: 14,
|
||
fontSize: 14,
|
||
color: "#1C1917",
|
||
minHeight: 70,
|
||
textAlignVertical: "top",
|
||
}}
|
||
/>
|
||
</View>
|
||
|
||
<Pressable
|
||
onPress={handleAdd}
|
||
disabled={adding || !name.trim() || !price.trim()}
|
||
style={({ pressed }) => ({
|
||
backgroundColor: !name.trim() || !price.trim() ? "#D6D3D1" : "#1C1917",
|
||
paddingVertical: 16,
|
||
borderRadius: 14,
|
||
alignItems: "center",
|
||
opacity: pressed ? 0.85 : 1,
|
||
marginTop: 8,
|
||
})}
|
||
>
|
||
<Text style={{ color: "#FFFFFF", fontSize: 15, fontWeight: "700" }}>
|
||
{adding ? "Ekleniyor..." : "Ürünü Menüye Ekle"}
|
||
</Text>
|
||
</Pressable>
|
||
</ScrollView>
|
||
</View>
|
||
</KeyboardAvoidingView>
|
||
</Modal>
|
||
);
|
||
}
|