You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2024 lines
57 KiB

2 months ago
import React, { useEffect, useState, useCallback, useRef } from "react";
import widthUtils from "../utils/widthUtils";
import fontSize from "../utils/fontsizeUtils";
import {
View,
Text,
StyleSheet,
ScrollView,
Image,
TouchableOpacity,
SafeAreaView,
StatusBar,
ActivityIndicator,
Dimensions,
2 months ago
TextInput,
FlatList,
Animated,
BackHandler,
} from "react-native";
import Ionicons from "@expo/vector-icons/Ionicons";
import { useNavigation, useRoute } from "@react-navigation/native";
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { RouteProp } from "@react-navigation/native";
import {
productApi,
ProductDetailParams,
Similars,
Sku,
ColorOptions,
} from "../services/api/productApi";
import { useTranslation } from "react-i18next";
import isSmallScreen from "../utils/isSmallScreen";
import MoreIcon from "../components/SendIcon";
import BackIcon from "../components/BackIcon";
import CardIcon from "../components/ShoppingCartIcon";
import CameraIcon from "../components/CameraIcon";
import HeartIcon from "../components/HeartIcon";
import HeartIconRed from "../components/HeartIconRed";
import WhiteCircleIcon from "../components/WhiteCircleIconIcon";
import ShoppingCartIcon from "../components/ShoppingCartIcon";
import XIconTop from "../components/XIconTop";
import XIconBottom from "../components/XIconBottom";
import RenderHtml from "react-native-render-html";
import { useWindowDimensions } from "react-native";
import { cartApi } from "../services/api/cart";
// 获取屏幕宽度
2 months ago
const { width: screenWidth, height: screenHeight } = Dimensions.get("window");
// 图标组件 - 使用React.memo优化渲染
2 months ago
const IconComponent = React.memo(
({ name, size, color }: { name: string; size: number; color: string }) => {
const Icon = Ionicons as any;
return <Icon name={name} size={size} color={color} />;
}
);
// 路由参数类型
type ProductDetailRouteParams = {
offer_id: string;
searchKeyword?: string;
2 months ago
price: number;
};
2 months ago
// 颜色选项组件
2 months ago
const ColorOption = React.memo(
({
color,
selected,
onSelect,
}: {
color: string;
selected: boolean;
onSelect: () => void;
}) => (
<TouchableOpacity
style={[styles.colorOption, selected && styles.colorOptionSelected]}
onPress={onSelect}
>
<Image source={{ uri: color }} style={styles.colorOptionImage} />
</TouchableOpacity>
)
);
2 months ago
// 尺寸选项组件
2 months ago
const SizeOption = React.memo(
({
size,
selected,
onSelect,
}: {
size: string;
selected: boolean;
onSelect: () => void;
}) => (
<TouchableOpacity
style={[styles.sizeOption, selected && styles.sizeOptionSelected]}
onPress={onSelect}
>
<Text style={[styles.sizeText, selected && styles.sizeTextSelected]}>
{size}
</Text>
</TouchableOpacity>
)
);
2 months ago
// 相关商品组件
2 months ago
const RelatedProductItem = React.memo(
({ product, onPress }: { product: any; onPress: () => void }) => (
<TouchableOpacity style={styles.relatedProductItem} onPress={onPress}>
<Image
source={{ uri: product.product_image_urls[0] }}
style={styles.relatedProductImage}
/>
<Text style={styles.relatedProductPrice}>$123</Text>
</TouchableOpacity>
)
);
2 months ago
export const ProductDetailScreen = () => {
2 months ago
const { t } = useTranslation();
const navigation = useNavigation<NativeStackNavigationProp<any>>();
2 months ago
const route =
useRoute<RouteProp<Record<string, ProductDetailRouteParams>, string>>();
const [product, setProduct] = useState<ProductDetailParams | null>(null);
const [loading, setLoading] = useState(true);
const [activeImageIndex, setActiveImageIndex] = useState(0);
2 months ago
const [heartStatus, setHeartStatus] = useState(true);
const [showAllSizes, setShowAllSizes] = useState(false);
const [showAllColors, setShowAllColors] = useState(false);
const [showBottomSheet, setShowBottomSheet] = useState(false);
const bottomSheetAnim = useRef(new Animated.Value(0)).current;
// 添加选择状态
const [selectedColor, setSelectedColor] = useState(0);
const [selectedSizes, setSelectedSizes] = useState<{ [key: string]: number }>(
{}
);
2 months ago
const [page, setPage] = useState(1);
const [loadingMore, setLoadingMore] = useState(false);
const [hasMoreProducts, setHasMoreProducts] = useState(true);
2 months ago
const [similarProducts, setSimilarProducts] = useState<Similars>([]);
const [colorOptions, setColorOptions] = useState<ColorOptions>();
const [sizeOptions, setSizeOptions] = useState<string[]>([]);
const [selectedColorIndex, setSelectedColorIndex] = useState<number>(0);
const [selectedSizeIndex, setSelectedSizeIndex] = useState<number>(0);
const [showImagePreview, setShowImagePreview] = useState(false);
const [previewImageIndex, setPreviewImageIndex] = useState(0);
const [showSuccessModal, setShowSuccessModal] = useState(false);
const [selectedSku, setSelectedSku] = useState<any>();
const [attributes, setAttributes] = useState<any[]>([]);
const [colorSelected, setColorSelected] = useState<any>();
const [sizeSelected, setSizeSelected] = useState<any>();
const [priceSelectedSku, setPriceSelectedSku] = useState<any>();
const [skuSize, setSkuSize] = useState<any>();
const [imageUrls, setImageUrls] = useState<any[]>([]);
const { width } = useWindowDimensions(); // 获取当前屏幕的宽度
const [imageHeights, setImageHeights] = useState<{ [key: string]: number }>({});
// 获取产品详情`
useEffect(() => {
fetchProductDetails();
}, []);
2 months ago
useEffect(() => {
if (showBottomSheet) {
Animated.spring(bottomSheetAnim, {
toValue: 1,
useNativeDriver: true,
bounciness: 0,
}).start();
} else {
bottomSheetAnim.setValue(0);
}
}, [showBottomSheet]);
2 months ago
// 获取产品详情的API调用
const fetchProductDetails = async () => {
if (!route.params?.offer_id) return;
setLoading(true);
2 months ago
try {
2 months ago
const res = await productApi.getProductDetail(route.params.offer_id);
res.price = route.params.price;
setProduct(res);
2 months ago
productApi.getSimilarProducts(route.params.offer_id).then((res) => {
setSimilarProducts(res);
});
if (res.skus === null) return;
let result = {} as any;
// 遍历数据
res.skus.forEach((item) => {
item.attributes.forEach((attribute) => {
const { attribute_name, value } = attribute;
// 如果结果对象中没有对应的属性名,则创建一个空数组
if (!result[attribute_name]) {
result[attribute_name] = [];
}
// 如果当前属性的值(value)已经存在于该组内,跳过
if (
!result[attribute_name].some(
(existingAttribute: any) => existingAttribute.value === value
)
) {
result[attribute_name].push(attribute);
}
});
});
const list = [];
// Iterate over each attribute and transform the data
for (const [attributeName, attributes] of Object.entries(result)) {
const withImage: any[] = [];
const withoutImage: any[] = [];
// @ts-ignore
attributes.forEach((attribute) => {
// Check if sku_image_url is not null or undefined
const hasImage =
attribute.sku_image_url !== null &&
attribute.sku_image_url !== undefined;
// Push the attribute to the appropriate array
if (hasImage) {
withImage.push(attribute);
} else {
withoutImage.push(attribute);
}
});
// Add has_image to the list item
list.push({
attribute_name: attributeName,
has_image: withImage.length > 0, // If there are any items with images, set has_image to true
attributes: [...withImage, ...withoutImage],
});
}
if (list.length == 0) {
setSkuSize(0);
} else if (list.length == 1) {
setSkuSize(1);
} else if (list.length == 2) {
setSkuSize(2);
}
setAttributes(list);
list.forEach((item) => {
if (item.has_image) {
setColorSelected(item.attributes[0].value);
setSelectedColorIndex(0 + item.attributes[0].value);
} else {
setSizeSelected(item.attributes[0].value);
setSelectedSizeIndex(0 + item.attributes[0].value);
}
});
const imageUrls = [];
const regex = /<img[^>]+src="([^"]+)"/g;
let match;
while ((match = regex.exec(res.description)) !== null) {
imageUrls.push(match[1]); // match[1] 是 src 属性的值
}
setImageUrls(imageUrls);
const priceSelectedSku = res.skus.find(item => item.consign_price === route.params.price)
setPriceSelectedSku(priceSelectedSku)
console.log(priceSelectedSku);
} catch (error) {
2 months ago
console.error("Error fetching product details:", error);
} finally {
setLoading(false);
}
};
// 返回上一页
const goBack = useCallback(() => {
navigation.goBack();
}, [navigation]);
2 months ago
// 处理相关商品点击
2 months ago
const handleRelatedProductPress = useCallback((product: any) => {}, []);
2 months ago
2 months ago
// 获取相关商品
const fetchRelatedProducts = useCallback(
async (pageNumber = 1) => {
if (!hasMoreProducts && pageNumber > 1) return;
2 months ago
2 months ago
try {
setLoadingMore(true);
2 months ago
2 months ago
// 模拟数据加载完毕的情况
if (pageNumber >= 3) {
setHasMoreProducts(false);
} else {
setPage(pageNumber);
}
} catch (error) {
console.error("Error fetching related products:", error);
} finally {
setLoadingMore(false);
2 months ago
}
2 months ago
},
[hasMoreProducts]
);
// 收藏
const changeHeartStatus = () => {
setHeartStatus(!heartStatus);
};
2 months ago
// 初始加载相关商品
useEffect(() => {
fetchRelatedProducts(1);
}, [fetchRelatedProducts]);
2 months ago
// 处理返回键
useEffect(() => {
const backAction = () => {
if (showImagePreview) {
setShowImagePreview(false);
return true;
}
return false;
};
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
backAction
);
return () => backHandler.remove();
}, [showImagePreview]);
// 选择颜色
const changeColor = (attribute: any, index: number) => {
setColorSelected(attribute.value);
setSelectedColorIndex(index + attribute.value);
if (skuSize === 0) return;
if (!sizeSelected) {
product?.skus.forEach((item: Sku) => {
item.attributes.forEach((item1: any) => {
if (item1.value === attribute.value) {
setPriceSelectedSku(item);
}
});
});
} else {
const targetValues = [attribute.value, sizeSelected];
const filteredData: Sku[] = product?.skus.filter((item) => {
const attrValues = item.attributes.map((attr) => attr.value);
return targetValues.every((target) => attrValues.includes(target));
}) as Sku[];
setPriceSelectedSku(filteredData[0]);
}
};
// 选择尺码
const changeSize = (size: any, index: number) => {
setSizeSelected(size.value);
setSelectedSizeIndex(index + size.value);
if (skuSize === 0) return;
if (!colorSelected) {
product?.skus.forEach((item: Sku) => {
item.attributes.forEach((item1: any) => {
if (item1.value === size.value) {
setPriceSelectedSku(item);
}
});
});
} else {
const targetValues = [size.value, colorSelected];
const filteredData: Sku[] = product?.skus.filter((item) => {
const attrValues = item.attributes.map((attr) => attr.value);
return targetValues.every((target) => attrValues.includes(target));
}) as Sku[];
setPriceSelectedSku(filteredData[0]);
}
};
// 添加到购物车
const handleAddToCart = async () => {
console.log(product?.offer_id);
console.log(priceSelectedSku);
const data = {
offer_id: product?.offer_id as number,
sku_id: priceSelectedSku?.sku_id as number,
quantity: 1,
2 months ago
}
2 months ago
const res = await cartApi(data)
console.log(res);
// const res = await cartApi.addToCart(data);
// console.log(res);
}
// 添加处理图片加载的函数
const handleImageLoad = (src: string, event: any) => {
const { width: imageWidth, height: imageHeight } = event.nativeEvent.source;
const aspectRatio = imageHeight / imageWidth;
const calculatedHeight = width * aspectRatio;
setImageHeights(prev => ({
...prev,
[src]: calculatedHeight
}));
};
if (loading) {
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
<View style={styles.header}>
2 months ago
<TouchableOpacity onPress={goBack}>
<IconComponent name="arrow-back" size={24} color="#333" />
</TouchableOpacity>
2 months ago
<Text style={styles.headerTitle}>{t("productDetail")}</Text>
2 months ago
<View style={styles.headerRight}>
<IconComponent name="ellipsis-vertical" size={20} color="#333" />
</View>
</View>
<View style={styles.loadingContainer}>
2 months ago
<ActivityIndicator size="large" color="#ff6600" />
2 months ago
<Text style={styles.loadingText}>{t("loadingProductInfo")}</Text>
</View>
</SafeAreaView>
);
}
if (!product) {
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
<View style={styles.header}>
2 months ago
<TouchableOpacity onPress={goBack}>
<IconComponent name="arrow-back" size={24} color="#333" />
</TouchableOpacity>
2 months ago
<Text style={styles.headerTitle}>{t("productDetail")}</Text>
2 months ago
<View style={styles.headerRight}>
<IconComponent name="ellipsis-vertical" size={20} color="#333" />
</View>
</View>
<View style={styles.errorContainer}>
2 months ago
<IconComponent
name="alert-circle-outline"
size={48}
color="#ff6600"
/>
<Text style={styles.errorText}>{t("productNotAvailable")}</Text>
</View>
</SafeAreaView>
);
}
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
2 months ago
{/* 头部搜索栏 */}
<View style={styles.header}>
<TouchableOpacity style={styles.backButton} onPress={goBack}>
2 months ago
<BackIcon size={20} />
</TouchableOpacity>
2 months ago
<View style={styles.searchContainer}>
<TextInput
style={styles.searchInput}
placeholder="Recherche"
placeholderTextColor="#666"
/>
<TouchableOpacity style={styles.searchIconButton}>
<CameraIcon size={22} />
</TouchableOpacity>
</View>
2 months ago
<View style={styles.headerRight}>
2 months ago
<TouchableOpacity style={styles.iconButton}>
<CardIcon size={24} />
</TouchableOpacity>
<TouchableOpacity style={styles.iconButton}>
<MoreIcon size={24} />
2 months ago
</TouchableOpacity>
</View>
</View>
2 months ago
<ScrollView
style={styles.scrollContainer}
contentContainerStyle={styles.scrollContentContainer}
2 months ago
>
2 months ago
{/* 产品图片 */}
<View style={styles.imageContainer}>
2 months ago
<FlatList
data={product.product_image_urls || []}
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
keyExtractor={(_, index) => `image-${index}`}
onScroll={(event) => {
const slideSize = event.nativeEvent.layoutMeasurement.width;
const index = Math.floor(
event.nativeEvent.contentOffset.x / slideSize
);
setActiveImageIndex(index);
}}
scrollEventThrottle={16}
renderItem={({ item }) => (
<View style={[styles.imageContainer, { width: screenWidth }]}>
<View style={{ position: "relative" }}>
<Image
source={{ uri: item }}
style={{ width: "100%", height: "100%" }}
resizeMode="cover"
/>
</View>
2 months ago
</View>
)}
/>
<View style={styles.pageIndicator}>
<Text style={styles.pageIndicatorText}>
{`${activeImageIndex + 1}/${
product.product_image_urls?.length || 1
}`}
</Text>
</View>
<TouchableOpacity
style={styles.heartIconButton}
onPress={changeHeartStatus}
>
{heartStatus ? <HeartIconRed size={18} /> : <HeartIcon size={18} />}
</TouchableOpacity>
</View>
2 months ago
{/* 价格信息 */}
<View style={styles.infoSection}>
2 months ago
<Text style={styles.productTitle}>
{product.subject ||
"Lanterne de nuit créative 3D cadeau d'anniversaire méduse lampe de table led..."}
</Text>
2 months ago
<View style={styles.priceContainer}>
2 months ago
<View style={styles.mainPriceContainer}>
<Text style={styles.productPrice}>
{priceSelectedSku?.consign_price}
</Text>
<Text style={styles.currencySymbol}>FCFA</Text>
</View>
<Text style={styles.originalPrice}>
{(priceSelectedSku?.offer_price * 1.4).toFixed(2)} FCFA
2 months ago
</Text>
<View style={styles.discountBadge}>
2 months ago
<Image
source={require("../../assets/favorable.png")}
style={{ width: 28, height: 28 }}
/>
2 months ago
</View>
<View style={styles.vipBadge}>
2 months ago
<Image
source={require("../../assets/vip1.png")}
style={{ width: 63, height: 21 }}
/>
</View>
2 months ago
</View>
2 months ago
<View style={styles.salesContainer}>
<Text style={styles.salesText}>
{product.sold_out} {product.sold_out === 0 ? "" : "+"}{" "}
{t("monthlySales")}
</Text>
2 months ago
</View>
</View>
2 months ago
{/* 颜色和尺码选择 */}
{attributes.map((item, index) => (
<View
style={item.has_image ? styles.optionSection : styles.optionSection}
key={index}
>
<Text style={styles.optionTitle}>
{t(item.attribute_name)}{" "}
{item.has_image ? `: ${colorSelected}` : ""}
</Text>
{item.has_image ? (
<View style={styles.colorOptions}>
{item.attributes.map(
(attribute: any, index: number, array: any) => {
if (showAllColors || index < 6) {
return (
<ColorOption
key={`color-${index}`}
color={attribute.sku_image_url}
selected={
index + attribute.value === selectedColorIndex
}
onSelect={() => {
changeColor(attribute, index);
}}
/>
);
} else if (index === 6) {
const remainingCount = array.length - 6;
return (
<TouchableOpacity
key="more-colors"
style={styles.moreColorsButton}
onPress={() => setShowAllColors(true)}
>
<Text style={styles.moreColorsText}>
+{remainingCount}
</Text>
</TouchableOpacity>
);
}
return null;
}
)}
</View>
) : (
<View style={styles.sizeOptions}>
{item.attributes.map((size: any, index: number, array: any) => {
if (showAllSizes || index < 6) {
return (
<SizeOption
key={`size-${index}`}
size={size.value}
selected={index + size.value === selectedSizeIndex}
onSelect={() => {
changeSize(size, index);
}}
/>
);
} else if (index === 6) {
const remainingCount = array.length - 6;
return (
<SizeOption
key="more-sizes"
size={`+${remainingCount}`}
selected={false}
onSelect={() => setShowAllSizes(true)}
/>
);
}
return null;
})}
</View>
)}
</View>
))}
2 months ago
{/* 相关商品 */}
<View style={styles.relatedProductsSection}>
<View style={styles.sectionHeader}>
2 months ago
<Text style={styles.sectionTitle}>{t("moreFromStore")}</Text>
2 months ago
<TouchableOpacity style={styles.viewAllButton}>
2 months ago
<Text style={styles.viewAllText}>View All</Text>
2 months ago
</TouchableOpacity>
</View>
2 months ago
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={similarProducts}
keyExtractor={(item) => `related-${item.offer_id}`}
renderItem={({ item }) => (
<RelatedProductItem
product={item}
onPress={() => handleRelatedProductPress(item)}
2 months ago
/>
2 months ago
)}
contentContainerStyle={styles.relatedProductsContainer}
style={styles.relatedProductsList}
/>
2 months ago
</View>
2 months ago
2 months ago
{/* 商品详情 */}
2 months ago
<View style={{ width: '100%' }}>
{imageUrls.map((src, index) => (
<View key={index} style={{ width: '100%' }}>
<Image
style={{
width: '100%',
height: imageHeights[src] || 200,
backgroundColor: '#f5f5f5'
}}
source={{ uri: src }}
resizeMode="contain"
onLoad={(event) => handleImageLoad(src, event)}
/>
</View>
))}
</View>
</ScrollView>
{/* 底部按钮 */}
<View style={styles.fixedBottomBar}>
<TouchableOpacity style={styles.chatNowButton}>
<WhiteCircleIcon color="#fff" size={isSmallScreen ? 18 : 20} />
<Text style={styles.chatNowText}>{t("chatNow")}</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.addToCartButton}
// onPress={() => setShowBottomSheet(true)}
onPress={handleAddToCart}
>
<ShoppingCartIcon color="#fff" size={isSmallScreen ? 18 : 20} />
<Text style={styles.addToCartText}>{t("addToCart")}</Text>
</TouchableOpacity>
</View>
{/* 底部弹出模态框 */}
{showBottomSheet && (
<View style={styles.bottomSheetOverlay}>
<TouchableOpacity
style={styles.overlayTouchable}
activeOpacity={1}
onPress={() => setShowBottomSheet(false)}
/>
<Animated.View
style={[
styles.bottomSheet,
{
transform: [
{
translateY: bottomSheetAnim.interpolate({
inputRange: [0, 1],
outputRange: [500, 0],
}),
},
],
},
]}
>
{/* 顶部价格信息 */}
<View style={styles.priceHeader}>
<View style={styles.mainPrice}>
<View style={{ position: "relative" }}>
<Image
source={{ uri: product.product_image_urls[0] }}
style={styles.smallProductImage}
/>
<View
style={{
position: "absolute",
top: 2,
right: 2,
width: 22,
height: 22,
backgroundColor: "#70615a",
opacity: 0.7,
}}
>
<View
style={{
position: "absolute",
top: 2,
right: 2,
justifyContent: "center",
alignItems: "center",
width: 12,
height: 12,
}}
>
<XIconTop size={12} />
</View>
<View
style={{
position: "absolute",
bottom: 2,
left: 2,
justifyContent: "center",
alignItems: "center",
width: 12,
height: 12,
}}
>
<XIconBottom size={12} />
</View>
</View>
</View>
<View style={{ paddingLeft: 10, flex: 1 }}>
<View style={{ flexDirection: "row" }}>
<Text style={styles.priceText}>3072</Text>
<Text style={styles.currency}>FCFA</Text>
</View>
<View style={styles.shopInfo}>
<View style={{ flex: 1, alignItems: "flex-start" }}>
<View style={styles.priceRow}>
<Text style={styles.shopName}>3072</Text>
<Text style={styles.currencySamll}>FAFC</Text>
</View>
<Text>123</Text>
</View>
<View style={{ flex: 1, alignItems: "flex-start" }}>
<View style={styles.priceRow}>
<Text style={styles.shopName}>3072</Text>
<Text style={styles.currencySamll}>FAFC</Text>
</View>
<Text>123</Text>
</View>
<View style={{ flex: 1, alignItems: "flex-start" }}>
<View style={styles.priceRow}>
<Text style={styles.shopName}>3072</Text>
<Text style={styles.currencySamll}>FAFC</Text>
</View>
<Text>123</Text>
</View>
</View>
</View>
2 months ago
</View>
2 months ago
<TouchableOpacity
style={styles.closeButton}
onPress={() => setShowBottomSheet(false)}
>
<Text style={styles.closeButtonText}>×</Text>
</TouchableOpacity>
2 months ago
</View>
2 months ago
<ScrollView
style={styles.bottomSheetContent}
showsVerticalScrollIndicator={false}
>
{/* 颜色选择 */}
<View style={styles.optionSection}>
<Text style={styles.optionTitle}>
Couleur :{" "}
{["Noir", "Brun", "Beige", "Couleur", "Bleu"][selectedColor]}
</Text>
<View style={styles.colorGrid}>
{["Noir", "Brun", "Beige", "Couleur", "Bleu"].map(
(colorName, index) => {
const imageUrl =
product?.product_image_urls &&
index < product.product_image_urls.length
? product.product_image_urls[index]
: "";
return (
<TouchableOpacity
key={`color-${index}`}
style={[
styles.colorOptionBase,
index === selectedColor &&
styles.colorOptionSelectedState,
]}
onPress={() => {
setSelectedColor(index);
setSelectedSizes((prev) => {
const newSizes = { ...prev };
if (index !== selectedColor) {
Object.keys(newSizes).forEach((size) => {
newSizes[size] = 0;
});
}
return newSizes;
});
}}
>
<View style={{ position: "relative" }}>
<Image
source={{ uri: imageUrl }}
style={styles.colorImage}
/>
<TouchableOpacity
onPress={() => {
setShowImagePreview(true);
setPreviewImageIndex(index);
}}
style={{
position: "absolute",
top: 2,
right: 2,
width: 22,
height: 22,
backgroundColor: "#70615a",
opacity: 0.7,
}}
>
<View
style={{
position: "absolute",
top: 2,
right: 2,
justifyContent: "center",
alignItems: "center",
width: 12,
height: 12,
}}
>
<XIconTop size={12} />
</View>
<View
style={{
position: "absolute",
bottom: 2,
left: 2,
justifyContent: "center",
alignItems: "center",
width: 12,
height: 12,
}}
>
<XIconBottom size={12} />
</View>
</TouchableOpacity>
</View>
<Text
style={[
styles.colorName,
index === selectedColor && { color: "#ec5e2a" },
]}
>
{colorName}
</Text>
{Object.values(selectedSizes).reduce(
(sum, current) => sum + current,
0
) > 0 &&
index === selectedColor && (
<View style={styles.colorQuantityBadge}>
<Text style={styles.colorQuantityText}>
x
{Object.values(selectedSizes).reduce(
(sum, current) => sum + current,
0
)}
</Text>
</View>
)}
</TouchableOpacity>
);
}
)}
</View>
2 months ago
</View>
2 months ago
{/* 尺码选择 */}
<View style={styles.optionSection}>
<Text style={styles.optionTitle}>Taille</Text>
<View style={styles.sizeList}>
{[
{ size: "35", stock: "231" },
{ size: "36", stock: "62" },
{ size: "37", stock: "88" },
{ size: "38", stock: "0" },
].map((item, index) => (
<View key={`size-${index}`} style={styles.sizeItem}>
<View style={styles.sizeInfo}>
<View style={styles.sizeRow}>
{selectedSizes[item.size] > 0 && (
<Text style={styles.sizeQuantityIndicator}>
x{selectedSizes[item.size]}
</Text>
)}
<Text style={styles.sizeNumber}>{item.size}</Text>
</View>
<Text style={styles.stockInfo}>
En stock {item.stock}
</Text>
</View>
<View style={styles.quantityControl}>
<TouchableOpacity
style={styles.quantityButton}
onPress={() => {
setSelectedSizes((prev) => ({
...prev,
[item.size]: Math.max(
0,
(prev[item.size] || 0) - 1
),
}));
}}
>
<Text style={styles.quantityButtonText}>-</Text>
</TouchableOpacity>
<Text style={styles.quantityText}>
{selectedSizes[item.size] || 0}
</Text>
<TouchableOpacity
style={styles.quantityButton}
onPress={() => {
if (
Number(item.stock) >
(selectedSizes[item.size] || 0)
) {
setSelectedSizes((prev) => ({
...prev,
[item.size]: (prev[item.size] || 0) + 1,
}));
}
}}
>
<Text style={styles.quantityButtonText}>+</Text>
</TouchableOpacity>
</View>
</View>
))}
</View>
</View>
</ScrollView>
{/* 底部总计和按钮 */}
<View style={styles.footer}>
<View style={styles.totalInfo}>
<Text style={styles.totalQuantity}>
Quantité Totale:{" "}
{Object.values(selectedSizes).reduce(
(sum, current) => sum + current,
0
)}
</Text>
<Text style={styles.totalAmount}>
Total:{" "}
{(
Object.values(selectedSizes).reduce(
(sum, current) => sum + current,
0
) * 3072
).toFixed(0)}{" "}
FCFA
</Text>
</View>
<TouchableOpacity
style={styles.modalAddToCartButton}
onPress={() => {
const totalQuantity = Object.values(selectedSizes).reduce(
(sum, current) => sum + current,
0
);
if (totalQuantity > 0) {
setShowBottomSheet(false);
setShowSuccessModal(true);
}
}}
>
<Text style={styles.modalAddToCartText}>Ajouter au panier</Text>
</TouchableOpacity>
2 months ago
</View>
2 months ago
</Animated.View>
</View>
)}
{/* 图片预览模态框 */}
{showImagePreview && (
<View style={styles.imagePreviewOverlay}>
<StatusBar barStyle="light-content" backgroundColor="#000" />
<TouchableOpacity
style={styles.imagePreviewCloseButton}
onPress={() => setShowImagePreview(false)}
>
<Text style={styles.imagePreviewCloseText}>×</Text>
</TouchableOpacity>
<View style={styles.imagePreviewCounter}>
<Text style={styles.imagePreviewCounterText}>
{`${previewImageIndex + 1}/${
product.product_image_urls?.length || 1
}`}
</Text>
</View>
<View style={styles.colorPreviewLabel}>
<Text style={styles.colorPreviewText}>
{["Noir", "Brun", "Beige", "Couleur", "Bleu"][previewImageIndex]}
</Text>
</View>
<FlatList
data={product.product_image_urls || []}
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
initialScrollIndex={previewImageIndex}
getItemLayout={(data, index) => ({
length: screenWidth,
offset: screenWidth * index,
index,
})}
keyExtractor={(_, index) => `preview-${index}`}
onScroll={(event) => {
const slideSize = event.nativeEvent.layoutMeasurement.width;
const index = Math.floor(
event.nativeEvent.contentOffset.x / slideSize
);
setPreviewImageIndex(index);
}}
scrollEventThrottle={16}
renderItem={({ item }) => (
<View style={styles.imagePreviewContainer}>
<Image
source={{ uri: item }}
style={styles.imagePreviewImage}
resizeMode="contain"
/>
2 months ago
</View>
2 months ago
)}
/>
</View>
)}
{/* 加入购物车成功弹窗 */}
{showSuccessModal && (
<View style={styles.successModalOverlay}>
<View style={styles.successModalContent}>
<View style={styles.successModalImageContainer}>
<Image
source={{ uri: product.product_image_urls[selectedColor] }}
style={styles.successModalImage}
/>
2 months ago
</View>
2 months ago
<Text style={styles.successModalText}>
Produit ajouté au panier !
</Text>
<TouchableOpacity
style={styles.successModalButton}
onPress={() => {
setShowSuccessModal(false);
navigation.navigate("Cart");
}}
>
<Text style={styles.successModalButtonText}>Voir le panier</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.successModalCloseButton}
onPress={() => setShowSuccessModal(false)}
>
<Text style={styles.successModalCloseText}>×</Text>
</TouchableOpacity>
2 months ago
</View>
</View>
2 months ago
)}
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
2 months ago
backgroundColor: "#fff",
},
header: {
2 months ago
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 15,
2 months ago
paddingVertical: 12,
backgroundColor: "#fff",
borderBottomWidth: 1,
2 months ago
borderBottomColor: "#eee",
},
backButton: {
padding: 5,
},
2 months ago
searchContainer: {
flex: 1,
2 months ago
height: widthUtils(50, 50).height,
backgroundColor: "#f5f5f5",
borderRadius: 20,
paddingHorizontal: 10,
flexDirection: "row",
alignItems: "center",
position: "relative",
},
searchInput: {
flex: 1,
fontSize: fontSize(15),
color: "#333",
height: widthUtils(50, 50).height,
paddingVertical: 0,
paddingRight: 30,
},
searchIconButton: {
position: "absolute",
right: 8,
top: 14,
padding: 0,
zIndex: 1,
},
headerRight: {
2 months ago
flexDirection: "row",
alignItems: "center",
2 months ago
},
2 months ago
iconButton: {
2 months ago
padding: 5,
2 months ago
marginLeft: 15,
},
imageContainer: {
2 months ago
height: screenHeight * 0.5,
backgroundColor: "#fff",
position: "relative",
},
2 months ago
priceContainer: {
2 months ago
flexDirection: "row",
alignItems: "flex-end",
marginBottom: 3,
},
mainPriceContainer: {
flexDirection: "row",
alignItems: "flex-start",
},
productPrice: {
2 months ago
fontSize: fontSize(30),
fontWeight: 700,
color: "#ff4500",
lineHeight: 30,
},
currencySymbol: {
fontSize: fontSize(14),
fontWeight: 700,
color: "#ff4500",
marginLeft: 2,
marginTop: 3,
2 months ago
},
originalPrice: {
2 months ago
fontSize: fontSize(12),
color: "#999",
textDecorationLine: "line-through",
2 months ago
marginLeft: 8,
2 months ago
marginBottom: 2,
2 months ago
},
discountBadge: {
paddingHorizontal: 6,
borderRadius: 3,
marginLeft: 8,
},
vipBadge: {
2 months ago
paddingHorizontal: 8,
2 months ago
paddingVertical: 2,
borderRadius: 3,
},
2 months ago
colorOptions: {
2 months ago
flexDirection: "row",
flexWrap: "wrap",
gap: 10,
marginTop: 5,
2 months ago
},
colorOption: {
2 months ago
width: 40,
height: 40,
2 months ago
borderRadius: 4,
borderWidth: 1,
2 months ago
borderColor: "#e0e0e0",
overflow: "hidden",
},
2 months ago
colorOptionSelected: {
2 months ago
borderColor: "#ff4500",
borderWidth: 2,
},
2 months ago
colorOptionImage: {
width: "100%",
height: "100%",
2 months ago
},
sizeOptions: {
2 months ago
flexDirection: "row",
flexWrap: "wrap",
gap: 10,
marginTop: 5,
2 months ago
},
sizeOption: {
2 months ago
// width: 40,
// height: 30,
2 months ago
borderRadius: 4,
2 months ago
backgroundColor: "#f5f5f5",
borderWidth: 0,
justifyContent: "center",
alignItems: "center",
paddingHorizontal: 0,
paddingVertical: 0,
2 months ago
},
sizeOptionSelected: {
2 months ago
backgroundColor: "#fff",
borderWidth: 1,
borderColor: "#ff4500",
2 months ago
},
sizeText: {
2 months ago
fontSize: fontSize(14),
color: "#333",
padding: 10,
2 months ago
},
sizeTextSelected: {
2 months ago
color: "#ff4500",
},
2 months ago
relatedProductsSection: {
2 months ago
backgroundColor: "#fff",
paddingTop: 15,
paddingBottom: 15,
borderTopWidth: 10,
borderTopColor: "#f5f5f5",
2 months ago
},
sectionHeader: {
2 months ago
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 15,
position: "relative",
},
sectionTitle: {
2 months ago
fontSize: fontSize(16),
fontWeight: 600,
color: "#333",
paddingBottom: 5,
position: "relative",
},
2 months ago
2 months ago
viewAllButton: {
2 months ago
flexDirection: "row",
alignItems: "center",
2 months ago
},
viewAllText: {
2 months ago
fontSize: fontSize(12),
color: "##747474",
fontFamily: "Segoe UI",
},
2 months ago
relatedProductsContainer: {
2 months ago
paddingLeft: 15,
paddingRight: 5,
paddingVertical: 5,
},
2 months ago
relatedProductItem: {
2 months ago
width: widthUtils(90, 90).width,
marginRight: 10,
2 months ago
},
relatedProductImage: {
2 months ago
width: "100%",
aspectRatio: 1,
2 months ago
borderRadius: 4,
marginBottom: 5,
},
relatedProductPrice: {
2 months ago
fontSize: fontSize(14),
fontWeight: "bold",
color: "#ff4500",
marginTop: 2,
2 months ago
},
loadMoreIndicator: {
2 months ago
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
2 months ago
paddingVertical: 10,
},
loadMoreText: {
2 months ago
fontSize: fontSize(14),
color: "#666",
2 months ago
marginLeft: 8,
},
2 months ago
noMoreProductsText: {
2 months ago
textAlign: "center",
fontSize: fontSize(14),
color: "#999",
2 months ago
paddingVertical: 10,
},
productDetails: {
marginTop: 10,
},
productDetailItem: {
marginBottom: 10,
},
productDetailImage: {
2 months ago
width: "100%",
2 months ago
height: 200,
borderRadius: 4,
},
2 months ago
detailPlaceholder: {
2 months ago
width: "100%",
2 months ago
height: 200,
borderRadius: 4,
2 months ago
backgroundColor: "#f0f0f0",
justifyContent: "center",
alignItems: "center",
},
2 months ago
detailPlaceholderText: {
2 months ago
fontSize: fontSize(14),
color: "#999",
},
bottomSpace: {
height: 60,
},
2 months ago
loadingContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
loadingText: {
fontSize: fontSize(14),
color: "#666",
marginTop: 10,
},
errorContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
padding: 20,
},
errorText: {
fontSize: fontSize(14),
fontWeight: "bold",
color: "#333",
marginTop: 15,
marginBottom: 20,
},
headerTitle: {
flex: 1,
fontSize: fontSize(16),
fontWeight: "bold",
color: "#333",
textAlign: "center",
marginHorizontal: 10,
},
headerIconButton: {
padding: 5,
},
paginationContainer: {
position: "absolute",
bottom: 15,
flexDirection: "row",
width: "100%",
justifyContent: "center",
alignItems: "center",
},
paginationDot: {
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: "rgba(255, 255, 255, 0.4)",
marginHorizontal: 4,
},
paginationDotActive: {
backgroundColor: "#fff",
width: 10,
height: 10,
borderRadius: 5,
},
infoSection: {
padding: 15,
paddingTop: 10,
paddingBottom: 10,
backgroundColor: "#fff",
},
optionSection: {
marginTop: 0,
backgroundColor: "#fff",
padding: 15,
borderTopColor: "#f5f5f5",
},
optionTitle: {
fontSize: fontSize(16),
fontWeight: 600,
color: "#333",
fontFamily: "Segoe UI",
marginBottom: 10,
},
moreColorsButton: {
width: 35,
height: 35,
borderRadius: 4,
backgroundColor: "#f5f5f5",
justifyContent: "center",
alignItems: "center",
marginRight: 10,
marginBottom: 10,
},
moreColorsText: {
fontSize: fontSize(14),
color: "#333",
},
vipText: {
color: "#fff",
fontSize: fontSize(14),
fontWeight: "bold",
},
productTitle: {
fontSize: fontSize(18),
color: "#333",
lineHeight: 22,
marginBottom: 8,
fontFamily: "PingFang SC",
fontWeight: "600",
},
salesContainer: {
marginBottom: 10,
},
salesText: {
fontSize: fontSize(14),
color: "#666",
},
scrollContainer: {
flex: 1,
},
scrollContentContainer: {
paddingBottom: 70,
},
imagePlaceholder: {
width: "100%",
height: "100%",
alignItems: "center",
justifyContent: "center",
},
imagePlaceholderText: {
fontSize: fontSize(14),
color: "#999",
},
colorCircle: {
width: 16,
height: 16,
borderRadius: 8,
marginRight: 6,
},
colorName: {
fontSize: fontSize(12),
color: "#333",
textAlign: "center",
paddingTop: 4,
backgroundColor: "#f4f4f4",
width: "100%",
paddingVertical: 1,
},
discountText: {
color: "#fff",
fontSize: fontSize(14),
fontWeight: "bold",
},
pageIndicator: {
position: "absolute",
bottom: 15,
right: 20,
backgroundColor: "rgba(0, 0, 0, 0.6)",
paddingHorizontal: 10,
paddingVertical: 5,
borderRadius: 15,
width: 50,
height: 26,
justifyContent: "center",
alignItems: "center",
},
pageIndicatorText: {
color: "#fff",
fontSize: fontSize(12),
fontWeight: "500",
textAlign: "center",
},
relatedProductsList: {
// 移除固定高度,让内容决定高度
},
detailsSection: {
backgroundColor: "#fff",
padding: 15,
marginTop: 10,
borderTopWidth: 10,
borderTopColor: "#f5f5f5",
},
detailsText: {
fontSize: fontSize(14),
color: "#333",
},
detailsImage: {
width: "100%",
aspectRatio: 1.5,
borderRadius: 4,
marginTop: 10,
},
heartIconButton: {
position: "absolute",
top: 20,
right: 20,
backgroundColor: "rgba(255, 255, 255, 1.0)",
borderRadius: 20,
width: 40,
height: 40,
justifyContent: "center",
alignItems: "center",
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 3,
elevation: 2,
zIndex: 10,
},
fixedBottomBar: {
position: "absolute",
bottom: 10,
left: 0,
right: 0,
2 months ago
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
paddingHorizontal: 15,
backgroundColor: "transparent",
zIndex: 100,
height: 60,
2 months ago
},
chatNowButton: {
width: "40%",
height: widthUtils(50, 50).height,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
paddingVertical: 0,
borderRadius: 30,
backgroundColor: "#4CAF50",
marginRight: 10,
elevation: 4,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 3,
},
chatNowText: {
fontSize: fontSize(18),
color: "#fff",
fontWeight: "bold",
marginLeft: 8,
},
addToCartButton: {
width: "60%",
height: isSmallScreen ? 30 : 50,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
paddingVertical: 0,
borderRadius: 30,
backgroundColor: "#FF5722",
marginLeft: 10,
elevation: 4,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 3,
},
addToCartText: {
fontSize: fontSize(18),
color: "#fff",
fontWeight: "bold",
marginLeft: 8,
},
bottomSheetOverlay: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
justifyContent: "flex-end",
zIndex: 200,
},
overlayTouchable: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
bottomSheet: {
backgroundColor: "#fff",
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
height: "85%",
zIndex: 1,
},
priceHeader: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "flex-start",
marginBottom: 20,
paddingBottom: 15,
paddingHorizontal: 15,
2 months ago
borderBottomWidth: 1,
borderBottomColor: "#eee",
paddingTop: 15,
},
2 months ago
mainPrice: {
flexDirection: "row",
borderRadius: 10,
flex: 1,
},
smallProductImage: {
width: 80,
height: 80,
borderRadius: 4,
marginRight: 10,
},
priceText: {
fontSize: fontSize(30),
fontWeight: "700",
color: "#ff4500",
},
currency: {
fontSize: fontSize(14),
fontWeight: "700",
fontFamily: "Segoe UI",
color: "#ff4500",
},
priceRanges: {
flexDirection: "row",
gap: 15,
},
priceRange: {
alignItems: "center",
2 months ago
},
2 months ago
rangePrice: {
fontSize: fontSize(16),
color: "#ff4500",
fontWeight: "500",
},
rangeQuantity: {
fontSize: fontSize(12),
color: "#666",
2 months ago
marginTop: 2,
},
2 months ago
closeButton: {
position: "absolute",
right: 15,
top: 15,
padding: 0,
width: 24,
height: 24,
justifyContent: "center",
alignItems: "center",
},
closeButtonText: {
fontSize: fontSize(24),
color: "#666",
lineHeight: 24,
textAlign: "center",
},
bottomSheetContent: {
flex: 1,
2 months ago
marginBottom: 15,
paddingHorizontal: 15,
},
2 months ago
colorGrid: {
flexDirection: "row",
flexWrap: "wrap",
gap: 10,
},
colorOptionBase: {
width: 90,
height: 116,
borderRadius: 4,
borderWidth: 1,
borderColor: "#e0e0e0",
overflow: "visible",
backgroundColor: "#fff",
marginBottom: 10,
},
colorOptionSelectedState: {
borderColor: "#ff4500",
borderWidth: 2,
},
colorImage: {
width: "100%",
height: 90,
borderTopLeftRadius: 3,
borderTopRightRadius: 3,
},
2 months ago
sizeList: {
gap: 15,
},
sizeItem: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: 10,
borderBottomWidth: 0,
backgroundColor: "#fff",
},
sizeInfo: {
flex: 1,
},
2 months ago
sizeRow: {
flexDirection: "row" as const,
alignItems: "center" as const,
},
sizeNumber: {
fontSize: fontSize(16),
color: "#333",
marginBottom: 4,
},
sizeQuantityIndicator: {
fontSize: fontSize(12),
marginLeft: 4,
marginBottom: 4,
width: 30,
backgroundColor: "#ff5217",
borderRadius: 15,
textAlign: "center",
color: "#fff",
fontFamily: "Segoe UI",
marginRight: 10,
},
2 months ago
stockInfo: {
fontSize: fontSize(12),
color: "#666",
},
quantityControl: {
flexDirection: "row",
alignItems: "center",
gap: 1,
},
quantityButton: {
width: 24,
height: 24,
borderRadius: 4,
backgroundColor: "#fff",
justifyContent: "center",
alignItems: "center",
borderWidth: 0,
},
quantityButtonText: {
fontSize: fontSize(18),
color: "#333",
lineHeight: 20,
textAlign: "center",
includeFontPadding: false,
},
quantityText: {
fontSize: fontSize(14),
color: "#333",
minWidth: 40,
width: 40,
height: 24,
textAlign: "center",
backgroundColor: "#f4f4f4",
lineHeight: 24,
borderRadius: 4,
textAlignVertical: "center",
paddingTop: 0,
paddingBottom: 0,
},
footer: {
borderTopWidth: 1,
borderTopColor: "#eee",
paddingVertical: 15,
backgroundColor: "#fff",
width: "100%",
},
totalInfo: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 15,
},
totalQuantity: {
fontSize: fontSize(14),
color: "#333",
marginRight: 15,
},
totalAmount: {
fontSize: fontSize(14),
color: "#ff4500",
fontWeight: "500",
flex: 1,
},
2 months ago
modalAddToCartButton: {
backgroundColor: "#ff4500",
height: 45,
borderRadius: 22,
justifyContent: "center",
alignItems: "center",
marginHorizontal: 15,
marginTop: 10,
},
2 months ago
modalAddToCartText: {
color: "#fff",
fontSize: fontSize(16),
fontWeight: "600",
},
2 months ago
colorQuantityBadge: {
position: "absolute",
top: -7,
left: 0,
backgroundColor: "#ff4500",
paddingHorizontal: 4,
paddingVertical: 2,
borderRadius: 4,
width: 30,
height: 20,
justifyContent: "center",
alignItems: "center",
zIndex: 9999,
},
colorQuantityText: {
color: "#fff",
fontSize: fontSize(10),
fontWeight: "500",
textAlign: "center",
},
shopInfo: {
marginTop: 10,
backgroundColor: "#f3f4f8",
flexDirection: "row",
justifyContent: "space-between",
paddingVertical: 5,
paddingHorizontal: 10,
borderRadius: 5,
},
shopName: {
fontFamily: "Segoe UI",
fontSize: fontSize(16),
fontWeight: "700",
color: "#373737",
},
currencySamll: {
fontFamily: "Segoe UI",
fontSize: fontSize(11),
fontWeight: "700",
color: "#373737",
marginLeft: 4,
},
priceRow: {
flexDirection: "row",
alignItems: "flex-start",
},
imagePreviewOverlay: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "#000",
zIndex: 1000,
},
imagePreviewCloseButton: {
position: "absolute",
top: 40,
right: 20,
zIndex: 1001,
},
imagePreviewCloseText: {
color: "#fff",
fontSize: fontSize(28),
fontWeight: "300",
},
imagePreviewCounter: {
position: "absolute",
top: 40,
left: 0,
right: 0,
alignItems: "center",
zIndex: 1001,
},
imagePreviewCounterText: {
color: "#fff",
fontSize: fontSize(16),
fontWeight: "400",
},
imagePreviewContainer: {
width: screenWidth,
height: "100%",
justifyContent: "center",
},
imagePreviewImage: {
width: "100%",
height: "100%",
},
colorPreviewLabel: {
position: "absolute",
bottom: 40,
left: 0,
right: 0,
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.5)",
paddingVertical: 10,
},
colorPreviewText: {
color: "#fff",
fontSize: fontSize(16),
fontWeight: "400",
textAlign: "center",
},
successModalOverlay: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0, 0, 0, 0.5)",
justifyContent: "center",
alignItems: "center",
zIndex: 2000,
},
successModalContent: {
backgroundColor: "#fff",
borderRadius: 10,
width: "90%",
alignItems: "center",
paddingVertical: 20,
position: "relative",
},
successModalImageContainer: {
width: 100,
height: 100,
marginBottom: 15,
},
successModalImage: {
width: "100%",
height: "100%",
borderRadius: 5,
},
successModalText: {
fontSize: fontSize(16),
color: "#333",
marginBottom: 20,
},
2 months ago
successModalButton: {
backgroundColor: "#ff4500",
paddingVertical: 12,
paddingHorizontal: 30,
borderRadius: 25,
},
successModalButtonText: {
color: "#fff",
fontSize: fontSize(16),
fontWeight: "600",
},
successModalCloseButton: {
position: "absolute",
right: 10,
top: 10,
padding: 5,
},
successModalCloseText: {
fontSize: fontSize(24),
color: "#666",
},
});