import React, { useEffect, useRef, useMemo } from "react"; import { Modal, View, Text, TouchableOpacity, StyleSheet, Animated, } from "react-native"; import { Ionicons } from "@expo/vector-icons"; import { spacing, fontSize } from "../../theme"; import { useTheme } from "../../context/ThemeContext"; type AlertType = "success" | "error" | "confirm"; interface AlertModalProps { visible: boolean; type?: AlertType; title: string; message: string; onClose: () => void; onConfirm?: () => void; confirmText?: string; cancelText?: string; } export default function AlertModal({ visible, type = "error", title, message, onClose, onConfirm, confirmText = "Confirmer", cancelText = "Annuler", }: AlertModalProps) { const { colors } = useTheme(); const scale = useRef(new Animated.Value(0.85)).current; const opacity = useRef(new Animated.Value(0)).current; const CONFIG: Record< AlertType, { icon: keyof typeof Ionicons.glyphMap; color: string; barColor: string; } > = useMemo( () => ({ success: { icon: "checkmark-circle", color: colors.success, barColor: colors.success, }, error: { icon: "alert-circle", color: colors.danger, barColor: colors.danger, }, confirm: { icon: "help-circle", color: colors.warning, barColor: colors.warning, }, }), [colors], ); const cfg = CONFIG[type]; useEffect(() => { if (visible) { Animated.parallel([ Animated.spring(scale, { toValue: 1, useNativeDriver: true, tension: 65, friction: 8, }), Animated.timing(opacity, { toValue: 1, duration: 200, useNativeDriver: true, }), ]).start(); } else { scale.setValue(0.85); opacity.setValue(0); } }, [visible]); const styles = useMemo( () => StyleSheet.create({ overlay: { flex: 1, backgroundColor: "rgba(0,0,0,0.85)", justifyContent: "center", alignItems: "center", padding: spacing.xl, }, content: { backgroundColor: colors.bgSecondary, borderRadius: 20, width: "100%", borderWidth: 1, borderColor: "rgba(255,255,255,0.08)", overflow: "hidden", shadowColor: "#000", shadowOffset: { width: 0, height: 0 }, shadowOpacity: 0.3, shadowRadius: 30, elevation: 20, }, accentBar: { height: 3, width: "100%", }, body: { alignItems: "center", paddingHorizontal: spacing.xl, paddingTop: spacing.xl, paddingBottom: spacing.xl, }, iconCircle: { width: 56, height: 56, borderRadius: 28, justifyContent: "center", alignItems: "center", marginBottom: spacing.l, }, title: { color: colors.textWhite, fontSize: fontSize.lg, fontWeight: "700", textAlign: "center", marginBottom: spacing.s, }, message: { color: colors.textSecondary, fontSize: fontSize.md, textAlign: "center", lineHeight: 20, marginBottom: spacing.xl, }, buttons: { flexDirection: "row", gap: spacing.m, width: "100%", justifyContent: "center", }, btn: { flex: 1, paddingVertical: spacing.m, borderRadius: 12, alignItems: "center", }, btnCancel: { backgroundColor: "rgba(255,255,255,0.08)", }, btnCancelText: { color: colors.textSecondary, fontSize: 15, fontWeight: "600", }, btnConfirm: {}, btnConfirmText: { color: colors.textWhite, fontSize: 15, fontWeight: "700", }, btnOk: { flex: 0, paddingHorizontal: spacing.xxl, }, }), [colors], ); return ( {title} {message} {type === "confirm" ? ( <> {cancelText} { onConfirm?.(); onClose(); }} activeOpacity={0.7} > {confirmText} ) : ( OK )} ); }