Files
projet_gestion_commande/frontend-admin/src/components/ui/AlertModal.tsx
T
2026-03-06 18:41:10 +01:00

267 lines
8.9 KiB
TypeScript

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 (
<Modal
visible={visible}
transparent
animationType="fade"
onRequestClose={onClose}
>
<View style={styles.overlay}>
<Animated.View
style={[
styles.content,
{ transform: [{ scale }], opacity },
]}
>
<View
style={[
styles.accentBar,
{ backgroundColor: cfg.barColor },
]}
/>
<View style={styles.body}>
<View
style={[
styles.iconCircle,
{ backgroundColor: cfg.color + "20" },
]}
>
<Ionicons
name={cfg.icon}
size={32}
color={cfg.color}
/>
</View>
<Text style={styles.title}>{title}</Text>
<Text style={styles.message}>{message}</Text>
<View style={styles.buttons}>
{type === "confirm" ? (
<>
<TouchableOpacity
style={[styles.btn, styles.btnCancel]}
onPress={onClose}
activeOpacity={0.7}
>
<Text style={styles.btnCancelText}>
{cancelText}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.btn,
styles.btnConfirm,
{ backgroundColor: cfg.color },
]}
onPress={() => {
onConfirm?.();
onClose();
}}
activeOpacity={0.7}
>
<Text style={styles.btnConfirmText}>
{confirmText}
</Text>
</TouchableOpacity>
</>
) : (
<TouchableOpacity
style={[
styles.btn,
styles.btnOk,
{ backgroundColor: cfg.color },
]}
onPress={onClose}
activeOpacity={0.7}
>
<Text style={styles.btnConfirmText}>
OK
</Text>
</TouchableOpacity>
)}
</View>
</View>
</Animated.View>
</View>
</Modal>
);
}