Files
projet_gestion_commande/mobile/src/screens/client/OrderHistoryScreen.tsx
T
Xor290 d28dbf9fb0
Frontend Client - EAS Build / build (push) Has been cancelled
chore: build
2026-06-21 18:48:30 +02:00

1163 lines
55 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useCallback, useMemo } from "react";
import {
View,
Text,
FlatList,
TouchableOpacity,
StyleSheet,
RefreshControl,
Modal,
ScrollView,
} from "react-native";
import { useNavigation, useFocusEffect } from "@react-navigation/native";
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { Ionicons } from "@expo/vector-icons";
import {
getMyCompletedOrders,
getMyPenalties,
getPublicSettings,
getReferralBalance,
getMyPointsRewards,
claimMyReward,
formatOrderDate,
formatPrice,
} from "../../api/api";
import type {
PublicSettings,
PointsPoolInfo,
PointsRewardConfig,
RewardItemConfig,
} from "../../api/api";
import type {
CompletedOrder,
ClientStats,
PenaltyInfo,
} from "../../api/api_types";
import type { ClientStackParamList } from "../../navigation/types";
import StatusBadge from "../../components/StatusBadge";
import LoadingSpinner from "../../components/ui/LoadingSpinner";
import Card from "../../components/ui/Card";
import { useTheme } from "../../context/ThemeContext";
import {
spacing,
borderRadius,
fontSize,
fontWeight,
shadows,
} from "../../theme";
type Nav = NativeStackNavigationProp<ClientStackParamList>;
export default function OrderHistoryScreen() {
const navigation = useNavigation<Nav>();
const { colors } = useTheme();
const [orders, setOrders] = useState<CompletedOrder[]>([]);
const [stats, setStats] = useState<ClientStats | null>(null);
const [penalties, setPenalties] = useState<PenaltyInfo | null>(null);
const [appSettings, setAppSettings] = useState<PublicSettings>({
penalties_enabled: true,
show_amende_score: true,
points_enabled: true,
points_separated: true,
referral_enabled: false,
pool_names: ["Pool 1", "Pool 2"],
crypto_payment_enabled: false,
nowpayments_currencies: [],
crypto_only: false,
telegram_notifications_enabled: false,
two_fa_enabled: false,
contact_telegram: "",
client_color_primary: "",
client_color_secondary: "",
client_color_success: "",
client_color_danger: "",
client_color_warning: "",
});
const [referralBalance, setReferralBalance] = useState(0);
const [pointsRewards, setPointsRewards] = useState<{
enabled: boolean;
pools: PointsPoolInfo[];
reward: PointsRewardConfig | null;
} | null>(null);
const [claimingPool, setClaimingPool] = useState<string | null>(null);
const [claimFeedback, setClaimFeedback] = useState<{
pool: string;
type: "success" | "error";
text: string;
} | null>(null);
const [rewardPickerPool, setRewardPickerPool] = useState<string | null>(
null,
);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const fetchData = useCallback(async () => {
try {
const [histRes, penRes, settings, refRes, rewardsRes] =
await Promise.all([
getMyCompletedOrders(),
getMyPenalties(),
getPublicSettings(),
getReferralBalance(),
getMyPointsRewards(),
]);
if (histRes.success) {
setOrders(histRes.commands || []);
setStats(histRes.client_stats || null);
}
if (penRes.success) {
setPenalties(penRes.data || null);
}
if (refRes.success) {
setReferralBalance(refRes.balance);
}
setAppSettings(settings);
if (rewardsRes.success && rewardsRes.enabled) {
setPointsRewards(rewardsRes);
}
} catch {
/* ignore */
} finally {
setLoading(false);
setRefreshing(false);
}
}, []);
const handleClaim = (poolKey: string) => {
const items = pointsRewards?.reward?.reward_items ?? [];
if (items.length > 1) {
setClaimFeedback(null);
setRewardPickerPool(poolKey);
return;
}
doClaimReward(poolKey, undefined);
};
const doClaimReward = async (
poolKey: string,
productId: number | undefined,
) => {
setRewardPickerPool(null);
setClaimingPool(poolKey);
setClaimFeedback(null);
const res = await claimMyReward(poolKey, productId);
setClaimingPool(null);
if (res.success) {
const text =
res.product_added && res.product_name
? `🎁 ${res.product_name} ajouté à votre panier ! Commandez au moins un produit pour en profiter.`
: res.description || "Récompense réclamée !";
setClaimFeedback({ pool: poolKey, type: "success", text });
getMyPointsRewards().then((r) => {
if (r.success) setPointsRewards(r);
});
} else {
setClaimFeedback({
pool: poolKey,
type: "error",
text: res.error || "Erreur",
});
}
};
useFocusEffect(
useCallback(() => {
setLoading(true);
fetchData();
}, [fetchData]),
);
const onRefresh = () => {
setRefreshing(true);
fetchData();
};
const styles = useMemo(
() =>
StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bgPrimary },
listContent: {
padding: spacing.l,
paddingBottom: spacing.xxxl,
},
statsGrid: {
flexDirection: "row",
flexWrap: "wrap",
gap: spacing.m,
marginBottom: spacing.xl,
},
statCard: {
backgroundColor: colors.bgCard,
borderRadius: borderRadius.md,
padding: spacing.l,
alignItems: "center",
flex: 1,
minWidth: "45%",
borderWidth: 1,
borderColor: colors.borderLight,
},
statValue: {
color: colors.textWhite,
fontSize: fontSize.xxl,
fontWeight: fontWeight.bold,
marginTop: spacing.s,
},
statLabel: {
color: colors.textMuted,
fontSize: fontSize.xs,
marginTop: spacing.xs,
},
sectionTitle: {
color: colors.textSecondary,
fontSize: fontSize.sm,
fontWeight: fontWeight.medium,
textTransform: "uppercase",
letterSpacing: 1,
marginBottom: spacing.m,
},
rewardsSection: {
backgroundColor: colors.bgCard,
borderRadius: borderRadius.md,
borderWidth: 1,
borderColor: "#f59e0b44",
padding: spacing.m,
marginBottom: spacing.l,
},
rewardsSectionTitle: {
color: "#f59e0b",
fontSize: fontSize.xs,
fontWeight: fontWeight.bold,
textTransform: "uppercase",
letterSpacing: 0.8,
},
rewardsSectionDesc: {
color: colors.textMuted,
fontSize: fontSize.xs,
fontStyle: "italic",
marginBottom: spacing.m,
},
rewardPoolCard: {
backgroundColor: "rgba(245,158,11,0.07)",
borderRadius: borderRadius.sm,
borderWidth: 1,
borderColor: "rgba(245,158,11,0.2)",
padding: spacing.m,
marginTop: spacing.s,
},
rewardPoolName: {
color: colors.textPrimary,
fontSize: fontSize.sm,
fontWeight: fontWeight.semibold,
},
rewardPoolPts: {
color: "#f59e0b",
fontSize: fontSize.sm,
fontWeight: fontWeight.bold,
},
rewardAmountBadge: {
backgroundColor: "rgba(16,185,129,0.15)",
borderRadius: borderRadius.sm,
paddingHorizontal: spacing.s,
paddingVertical: 2,
flexDirection: "row" as const,
},
rewardAmountText: {
color: "#10b981",
fontSize: fontSize.xs,
fontWeight: fontWeight.bold,
},
progressBarBg: {
height: 5,
backgroundColor: "rgba(245,158,11,0.15)",
borderRadius: 3,
overflow: "hidden",
marginVertical: spacing.xs,
},
progressBarFill: {
height: "100%",
backgroundColor: "#f59e0b",
borderRadius: 3,
},
rewardAvailable: {
color: "#10b981",
fontSize: fontSize.xs,
fontWeight: fontWeight.semibold,
marginBottom: spacing.s,
},
rewardRemaining: {
color: colors.textMuted,
fontSize: fontSize.xs,
marginBottom: spacing.s,
},
feedbackSuccess: {
color: "#10b981",
fontSize: fontSize.xs,
fontStyle: "italic",
marginBottom: spacing.xs,
},
feedbackError: {
color: "#ef4444",
fontSize: fontSize.xs,
fontStyle: "italic",
marginBottom: spacing.xs,
},
claimBtn: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: spacing.xs,
backgroundColor: "#f59e0b",
borderRadius: borderRadius.sm,
paddingVertical: spacing.s,
paddingHorizontal: spacing.m,
},
claimBtnText: {
color: "#fff",
fontSize: fontSize.sm,
fontWeight: fontWeight.bold,
},
referralBtn: {
flexDirection: "row",
alignItems: "center",
gap: spacing.s,
backgroundColor: colors.bgCard,
borderRadius: borderRadius.sm,
borderWidth: 1,
borderColor: colors.accent + "44",
paddingVertical: spacing.m,
paddingHorizontal: spacing.l,
marginBottom: spacing.l,
},
referralBtnText: {
color: colors.accent,
fontSize: fontSize.sm,
fontWeight: fontWeight.semibold,
flex: 1,
},
emptyContainer: {
alignItems: "center",
paddingTop: spacing.xxxl,
},
emptyTitle: {
color: colors.textPrimary,
fontSize: fontSize.xl,
fontWeight: fontWeight.bold,
marginTop: spacing.l,
},
emptySubtitle: {
color: colors.textMuted,
fontSize: fontSize.md,
marginTop: spacing.s,
},
orderCard: {
backgroundColor: colors.bgCard,
borderRadius: borderRadius.md,
padding: spacing.l,
marginBottom: spacing.m,
borderWidth: 1,
borderColor: colors.borderLight,
},
orderHeader: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: spacing.m,
},
orderIdText: {
color: colors.textWhite,
fontSize: fontSize.md,
fontWeight: fontWeight.semibold,
},
orderRow: {
flexDirection: "row",
alignItems: "center",
marginBottom: spacing.xs,
},
orderDetail: {
color: colors.textSecondary,
fontSize: fontSize.sm,
marginLeft: spacing.s,
flex: 1,
},
orderFooter: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginTop: spacing.m,
paddingTop: spacing.m,
borderTopWidth: 1,
borderTopColor: colors.borderLight,
},
orderTotal: {
color: colors.success,
fontSize: fontSize.lg,
fontWeight: fontWeight.bold,
},
deliveredBadge: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: spacing.m,
paddingVertical: spacing.xs,
borderRadius: borderRadius.xl,
borderWidth: 1.5,
borderColor: "#7c3aed",
},
deliveredBadgeText: {
color: "#a78bfa",
fontSize: fontSize.xs,
fontWeight: fontWeight.bold,
textTransform: "uppercase",
letterSpacing: 0.5,
marginLeft: 4,
},
// Modal styles
modalOverlay: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.65)",
justifyContent: "center",
padding: spacing.l,
},
modalContent: {
backgroundColor: colors.bgCard,
borderRadius: borderRadius.md,
padding: spacing.l,
borderWidth: 1,
borderColor: "#f59e0b44",
maxHeight: "80%",
},
modalHeader: {
flexDirection: "row",
alignItems: "center",
gap: spacing.s,
marginBottom: spacing.m,
paddingBottom: spacing.m,
borderBottomWidth: 1,
borderBottomColor: colors.borderLight,
},
modalTitle: {
color: colors.textWhite,
fontSize: fontSize.md,
fontWeight: fontWeight.bold,
flex: 1,
},
modalSubtitle: {
color: colors.textMuted,
fontSize: fontSize.xs,
marginBottom: spacing.m,
},
pickerItem: {
flexDirection: "row",
alignItems: "center",
gap: spacing.m,
backgroundColor: "rgba(245,158,11,0.07)",
borderRadius: borderRadius.sm,
borderWidth: 1,
borderColor: "rgba(245,158,11,0.2)",
padding: spacing.m,
marginBottom: spacing.s,
},
pickerItemName: {
color: colors.textWhite,
fontSize: fontSize.sm,
fontWeight: fontWeight.semibold,
},
pickerItemQty: {
color: colors.textMuted,
fontSize: fontSize.xs,
marginTop: 2,
},
pickerItemPrice: {
color: "#10b981",
fontSize: fontSize.sm,
fontWeight: fontWeight.bold,
},
modalCancelBtn: {
marginTop: spacing.s,
alignItems: "center",
paddingVertical: spacing.m,
},
modalCancelText: {
color: colors.textMuted,
fontSize: fontSize.sm,
},
}),
[colors],
);
if (loading && !refreshing)
return <LoadingSpinner message="Chargement historique..." />;
const poolNames = stats?.pool_names?.length
? stats.pool_names
: appSettings.pool_names;
const poolPoints = stats?.pool_points ?? [stats?.points ?? 0];
const totalPoints = poolPoints.reduce((s, v) => s + (v || 0), 0);
const penaltyCount = penalties?.total_penalty || stats?.penalties || 0;
return (
<View style={styles.container}>
<FlatList
data={orders}
keyExtractor={(item) => String(item.id)}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
tintColor={colors.accent}
/>
}
contentContainerStyle={styles.listContent}
ListHeaderComponent={
<View>
<View style={styles.statsGrid}>
<View style={[styles.statCard, shadows.sm]}>
<Ionicons
name="receipt-outline"
size={24}
color={colors.accent}
/>
<Text style={styles.statValue}>
{stats?.total_commands || orders.length}
</Text>
<Text style={styles.statLabel}>Commandes</Text>
</View>
{appSettings.points_enabled &&
(poolNames.length <= 1 ? (
<View style={[styles.statCard, shadows.sm]}>
<Ionicons
name="trophy-outline"
size={24}
color={colors.warning}
/>
<Text style={styles.statValue}>
{poolPoints[0] || 0}
</Text>
<Text style={styles.statLabel}>
Pts {poolNames[0] ?? "Points"}
</Text>
</View>
) : (
<>
{poolNames.map((name, i) => {
const poolIconNames = [
"leaf-outline",
"medical-outline",
"flask-outline",
"color-fill-outline",
"star-outline",
] as const;
const poolIconColors = [
colors.categoryWeedHash,
"#e879f9",
"#fb923c",
"#38bdf8",
colors.accent,
];
const icon =
poolIconNames[i] ??
"star-outline";
const color =
poolIconColors[i] ??
colors.accent;
return (
<View
key={i}
style={[
styles.statCard,
shadows.sm,
]}
>
<Ionicons
name={icon}
size={24}
color={color}
/>
<Text
style={styles.statValue}
>
{poolPoints[i] || 0}
</Text>
<Text
style={styles.statLabel}
>
Pts {name}
</Text>
</View>
);
})}
{totalPoints > 0 && (
<View
style={[
styles.statCard,
shadows.sm,
]}
>
<Ionicons
name="trophy-outline"
size={24}
color={colors.warning}
/>
<Text style={styles.statValue}>
{totalPoints}
</Text>
<Text style={styles.statLabel}>
Total Points
</Text>
</View>
)}
</>
))}
{appSettings.show_amende_score && (
<View
style={[
styles.statCard,
shadows.sm,
penaltyCount > 0 && {
borderWidth: 1,
borderColor:
penaltyCount >= 3
? colors.danger + "88"
: colors.warning + "88",
backgroundColor:
penaltyCount >= 3
? colors.danger + "18"
: colors.warning + "18",
},
]}
>
<Ionicons
name={
penaltyCount > 0
? "warning"
: "shield-checkmark-outline"
}
size={24}
color={
penaltyCount >= 3
? colors.danger
: penaltyCount > 0
? colors.warning
: colors.textMuted
}
/>
<Text
style={[
styles.statValue,
penaltyCount >= 3 && {
color: colors.danger,
},
penaltyCount > 0 &&
penaltyCount < 3 && {
color: colors.warning,
},
]}
>
{penaltyCount}
</Text>
<Text style={styles.statLabel}>Amende</Text>
</View>
)}
</View>
{/* Section récompenses */}
{pointsRewards?.enabled &&
pointsRewards.reward &&
pointsRewards.pools.length > 0 && (
<View style={styles.rewardsSection}>
<View
style={{
flexDirection: "row",
alignItems: "center",
gap: spacing.xs,
marginBottom: spacing.xs,
}}
>
<Ionicons
name="trophy-outline"
size={14}
color="#f59e0b"
/>
<Text
style={styles.rewardsSectionTitle}
>
Récompenses
</Text>
</View>
{pointsRewards.reward.description !==
"" && (
<Text style={styles.rewardsSectionDesc}>
{pointsRewards.reward.description}
</Text>
)}
{(
pointsRewards.reward.reward_items ?? []
).filter(
(it) => it.price > 0 || it.product_name,
).length > 0 && (
<View
style={{
flexDirection: "row",
flexWrap: "wrap",
gap: 4,
marginBottom: spacing.s,
}}
>
{(
pointsRewards.reward
.reward_items ?? []
).map(
(
it: RewardItemConfig,
idx: number,
) => (
<View
key={idx}
style={[
styles.rewardAmountBadge,
{
flexDirection:
"row",
alignItems:
"center",
gap: 4,
},
]}
>
<Ionicons
name="gift-outline"
size={11}
color="#f59e0b"
/>
<Text
style={
styles.rewardAmountText
}
>
{it.product_name}
{it.quantity > 0 &&
it.quantity !== 1
? ` ×${it.quantity}`
: ""}
{it.price > 0
? ` — ${it.price}€`
: ""}
</Text>
</View>
),
)}
</View>
)}
{pointsRewards.pools.map((pool) => {
const threshold =
pointsRewards.reward!.threshold;
const progress = Math.min(
1,
(pool.points % threshold) /
threshold,
);
const remaining =
threshold -
(pool.points % threshold);
const isClaiming =
claimingPool === pool.key;
const feedback =
claimFeedback?.pool === pool.key
? claimFeedback
: null;
return (
<View
key={pool.key}
style={styles.rewardPoolCard}
>
<View
style={{
flexDirection: "row",
justifyContent:
"space-between",
marginBottom:
spacing.xs,
}}
>
<Text
style={
styles.rewardPoolName
}
>
{pool.name}
</Text>
<Text
style={
styles.rewardPoolPts
}
>
{pool.points} pts
</Text>
</View>
{pool.eligible_configs.length >
0 && (
<View
style={{
flexDirection:
"row",
flexWrap: "wrap",
gap: 4,
marginBottom:
spacing.xs,
}}
>
{pool.eligible_configs.flatMap(
(cfg) =>
cfg.all_products
? [
<View
key={
cfg.category
}
style={
styles.rewardAmountBadge
}
>
<Text
style={
styles.rewardAmountText
}
>
{
cfg.category
}
</Text>
</View>,
]
: (
cfg.product_names ??
[]
).map(
(
name,
) => (
<View
key={`${cfg.category}-${name}`}
style={
styles.rewardAmountBadge
}
>
<Text
style={
styles.rewardAmountText
}
>
{
name
}
</Text>
</View>
),
),
)}
</View>
)}
<View
style={styles.progressBarBg}
>
<View
style={[
styles.progressBarFill,
{
width: `${Math.round(progress * 100)}%` as any,
},
]}
/>
</View>
<Text
style={
pool.rewards_available >
0
? styles.rewardAvailable
: styles.rewardRemaining
}
>
{pool.rewards_available > 0
? `${pool.rewards_available} récompense${pool.rewards_available > 1 ? "s" : ""} disponible${pool.rewards_available > 1 ? "s" : ""}`
: `Encore ${remaining} pts pour une récompense`}
</Text>
{feedback && (
<Text
style={
feedback.type ===
"success"
? styles.feedbackSuccess
: styles.feedbackError
}
>
{feedback.text}
</Text>
)}
{pool.rewards_available > 0 && (
<TouchableOpacity
style={[
styles.claimBtn,
isClaiming && {
opacity: 0.5,
},
]}
onPress={() =>
handleClaim(
pool.key,
)
}
disabled={isClaiming}
>
<Ionicons
name="gift-outline"
size={14}
color="#fff"
/>
<Text
style={
styles.claimBtnText
}
>
{isClaiming
? "..."
: "Réclamer ma récompense"}
</Text>
</TouchableOpacity>
)}
</View>
);
})}
</View>
)}
{/* Bouton parrainage */}
{appSettings.referral_enabled && (
<TouchableOpacity
style={styles.referralBtn}
onPress={() =>
navigation.navigate("Parrainage")
}
>
<Ionicons
name="gift-outline"
size={18}
color={colors.accent}
/>
<Text style={styles.referralBtnText}>
Parrainage
{referralBalance > 0
? ` — ${referralBalance.toFixed(2)} €`
: ""}
</Text>
<Ionicons
name="chevron-forward"
size={16}
color={colors.textMuted}
/>
</TouchableOpacity>
)}
{orders.length > 0 && (
<Text style={styles.sectionTitle}>
Historique des commandes
</Text>
)}
</View>
}
ListEmptyComponent={
<View style={styles.emptyContainer}>
<Ionicons
name="time-outline"
size={80}
color={colors.textMuted}
/>
<Text style={styles.emptyTitle}>Aucun historique</Text>
<Text style={styles.emptySubtitle}>
Vos commandes terminees apparaitront ici
</Text>
</View>
}
renderItem={({ item: order }) => (
<TouchableOpacity
activeOpacity={0.7}
onPress={() =>
navigation.navigate("OrderDetails", {
orderId: order.id,
})
}
style={[styles.orderCard, shadows.sm]}
>
<View style={styles.orderHeader}>
<Text style={styles.orderIdText}>
Commande #{order.client_order_number}
</Text>
{order.status === "cancelled" ? (
<StatusBadge status={order.status} />
) : (
<View style={styles.deliveredBadge}>
<Ionicons
name="checkmark-circle"
size={12}
color="#a78bfa"
/>
<Text style={styles.deliveredBadgeText}>
Livrée
</Text>
</View>
)}
</View>
<View style={styles.orderRow}>
<Ionicons
name="location-outline"
size={14}
color={colors.textMuted}
/>
<Text style={styles.orderDetail} numberOfLines={1}>
{order.adresse || "N/A"}
</Text>
</View>
<View style={styles.orderRow}>
<Ionicons
name="time-outline"
size={14}
color={colors.textMuted}
/>
<Text style={styles.orderDetail}>
{formatOrderDate(order.created_at)}
</Text>
</View>
{order.livreur_assign && (
<View style={styles.orderRow}>
<Ionicons
name="bicycle-outline"
size={14}
color={colors.textMuted}
/>
<Text style={styles.orderDetail}>
{order.livreur_assign}
</Text>
</View>
)}
<View style={styles.orderFooter}>
<Text style={styles.orderTotal}>
{formatPrice(
(order.total_prix || 0) -
(order.referral_used || 0),
)}
</Text>
<Ionicons
name="chevron-forward"
size={18}
color={colors.textMuted}
/>
</View>
</TouchableOpacity>
)}
/>
{/* Modal sélection produit récompense */}
<Modal
visible={rewardPickerPool !== null}
transparent
animationType="fade"
onRequestClose={() => setRewardPickerPool(null)}
>
<TouchableOpacity
style={styles.modalOverlay}
activeOpacity={1}
onPress={() => setRewardPickerPool(null)}
>
{/* Inner TouchableOpacity blocks tap propagation to the overlay */}
<TouchableOpacity activeOpacity={1} onPress={() => {}}>
<View style={styles.modalContent}>
<View style={styles.modalHeader}>
<Ionicons
name="gift-outline"
size={18}
color="#f59e0b"
/>
<Text style={styles.modalTitle}>
Choisir votre récompense
</Text>
<TouchableOpacity
onPress={() => setRewardPickerPool(null)}
>
<Ionicons
name="close"
size={20}
color={colors.textMuted}
/>
</TouchableOpacity>
</View>
<Text style={styles.modalSubtitle}>
Sélectionnez le produit que vous souhaitez
recevoir
</Text>
<ScrollView showsVerticalScrollIndicator={false}>
{(
pointsRewards?.reward?.reward_items ?? []
).map((item: RewardItemConfig, idx: number) => (
<TouchableOpacity
key={idx}
style={styles.pickerItem}
onPress={() =>
doClaimReward(
rewardPickerPool!,
item.product_id,
)
}
activeOpacity={0.7}
>
<Ionicons
name="cube-outline"
size={22}
color="#f59e0b"
/>
<View style={{ flex: 1 }}>
<Text style={styles.pickerItemName}>
{item.product_name}
</Text>
{item.quantity > 0 &&
item.quantity !== 1 && (
<Text
style={
styles.pickerItemQty
}
>
×{item.quantity}
</Text>
)}
</View>
{item.price > 0 && (
<Text
style={styles.pickerItemPrice}
>
{item.price}
</Text>
)}
<Ionicons
name="chevron-forward"
size={16}
color={colors.textMuted}
/>
</TouchableOpacity>
))}
</ScrollView>
<TouchableOpacity
style={styles.modalCancelBtn}
onPress={() => setRewardPickerPool(null)}
>
<Text style={styles.modalCancelText}>
Annuler
</Text>
</TouchableOpacity>
</View>
</TouchableOpacity>
</TouchableOpacity>
</Modal>
</View>
);
}