Files
projet_gestion_commande/frontend-admin/src/screens/delivery/AlertsScreen.tsx
T
2026-02-07 22:33:02 +01:00

452 lines
16 KiB
TypeScript

import React, { useState, useEffect, useCallback, useMemo } from "react";
import {
View,
Text,
StyleSheet,
FlatList,
RefreshControl,
Modal,
TouchableOpacity,
Animated,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { spacing, fontSize, borderRadius } from "../../theme";
import { useTheme } from "../../context/ThemeContext";
import {
getMyAlerts,
triggerPoliceAlert,
endAlert,
} from "../../api/api_delivery";
import type { Alert as AlertType } from "../../api/types";
import LoadingSpinner from "../../components/ui/LoadingSpinner";
import Card from "../../components/ui/Card";
import Badge from "../../components/ui/Badge";
import Button from "../../components/ui/Button";
import AlertModal from "../../components/ui/AlertModal";
import { useAlert } from "../../hooks/useAlert";
export default function AlertsScreen() {
const { colors } = useTheme();
const [alerts, setAlerts] = useState<AlertType[]>([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [triggering, setTriggering] = useState(false);
const [showConfirmModal, setShowConfirmModal] = useState(false);
const [showSuccessModal, setShowSuccessModal] = useState(false);
const [successMessage, setSuccessMessage] = useState("");
const [pulseAnim] = useState(() => new Animated.Value(1));
const { alert: alertModal, showError, hideAlert } = useAlert();
const loadData = useCallback(async () => {
try {
const res = await getMyAlerts();
if (res.success && res.alerts) setAlerts(res.alerts);
} catch {
/* ignore */
}
setLoading(false);
}, []);
useEffect(() => {
loadData();
}, [loadData]);
// Pulse animation when modal is open
useEffect(() => {
if (!showConfirmModal) return;
const anim = Animated.loop(
Animated.sequence([
Animated.timing(pulseAnim, {
toValue: 1.15,
duration: 800,
useNativeDriver: true,
}),
Animated.timing(pulseAnim, {
toValue: 1,
duration: 800,
useNativeDriver: true,
}),
]),
);
anim.start();
return () => anim.stop();
}, [showConfirmModal, pulseAnim]);
const onRefresh = async () => {
setRefreshing(true);
await loadData();
setRefreshing(false);
};
const handleConfirmTrigger = async () => {
setShowConfirmModal(false);
setTriggering(true);
const res = await triggerPoliceAlert();
setTriggering(false);
if (res.success) {
setSuccessMessage(res.message || "Alerte déclenchée avec succès");
setShowSuccessModal(true);
loadData();
} else {
showError("Erreur", res.error || "Erreur");
}
};
const handleEnd = async (alertId: number) => {
const res = await endAlert(alertId);
if (res.success) {
loadData();
} else {
showError("Erreur", res.error || "Erreur");
}
};
const renderAlert = ({ item }: { item: AlertType }) => (
<Card style={{ marginBottom: spacing.m }}>
<View style={styles.row}>
<Ionicons
name="alert-circle"
size={24}
color={
item.status === "true"
? colors.danger
: colors.textMuted
}
/>
<View style={{ flex: 1, marginLeft: spacing.m }}>
<Text style={styles.date}>
{new Date(item.created_at).toLocaleString("fr-FR")}
</Text>
</View>
<Badge
label={item.status === "true" ? "Active" : "Terminée"}
color={
item.status === "true" ? colors.danger : colors.success
}
/>
</View>
{item.status === "true" && (
<Button
title="Terminer l'alerte"
onPress={() => handleEnd(item.id)}
style={{
marginTop: spacing.s,
backgroundColor: colors.success,
}}
/>
)}
</Card>
);
const styles = useMemo(
() =>
StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bgPrimary },
row: { flexDirection: "row", alignItems: "center" },
date: { color: colors.textMuted, fontSize: fontSize.sm },
empty: {
color: colors.textMuted,
textAlign: "center",
marginTop: spacing.xxl,
fontSize: fontSize.md,
},
triggerSection: {
padding: spacing.l,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
// Modal
modalOverlay: {
flex: 1,
backgroundColor: "rgba(0, 0, 0, 0.85)",
justifyContent: "center",
alignItems: "center",
padding: spacing.xl,
},
modalContent: {
width: "100%",
backgroundColor: colors.bgCard,
borderRadius: borderRadius.lg,
padding: spacing.xl,
alignItems: "center",
borderWidth: 1,
borderColor: colors.danger + "40",
},
modalIconCircle: {
width: 90,
height: 90,
borderRadius: 45,
backgroundColor: colors.danger + "20",
justifyContent: "center",
alignItems: "center",
marginBottom: spacing.l,
},
modalIconInner: {
width: 68,
height: 68,
borderRadius: 34,
backgroundColor: colors.danger,
justifyContent: "center",
alignItems: "center",
},
modalTitle: {
fontSize: fontSize.xl,
fontWeight: "700",
color: colors.danger,
marginBottom: spacing.s,
},
modalMessage: {
fontSize: fontSize.sm,
color: colors.textSecondary,
textAlign: "center",
lineHeight: 20,
marginBottom: spacing.m,
},
modalWarning: {
fontSize: fontSize.md,
fontWeight: "600",
color: colors.textWhite,
textAlign: "center",
marginBottom: spacing.xl,
},
modalButtons: {
flexDirection: "row",
gap: spacing.m,
width: "100%",
},
modalCancelBtn: {
flex: 1,
paddingVertical: 14,
borderRadius: borderRadius.sm,
backgroundColor: colors.bgInput,
alignItems: "center",
justifyContent: "center",
},
modalCancelText: {
color: colors.textSecondary,
fontSize: fontSize.md,
fontWeight: "600",
},
modalConfirmBtn: {
flex: 1,
flexDirection: "row",
paddingVertical: 14,
borderRadius: borderRadius.sm,
backgroundColor: colors.danger,
alignItems: "center",
justifyContent: "center",
gap: spacing.xs,
},
modalConfirmText: {
color: colors.white,
fontSize: fontSize.md,
fontWeight: "700",
},
// Success Modal
successModalContent: {
width: "100%",
backgroundColor: colors.bgCard,
borderRadius: borderRadius.lg,
padding: spacing.xl,
alignItems: "center",
borderWidth: 1,
borderColor: colors.success + "40",
},
successIconCircle: {
width: 90,
height: 90,
borderRadius: 45,
backgroundColor: colors.success + "20",
justifyContent: "center",
alignItems: "center",
marginBottom: spacing.l,
},
successIconInner: {
width: 68,
height: 68,
borderRadius: 34,
backgroundColor: colors.success,
justifyContent: "center",
alignItems: "center",
},
successTitle: {
fontSize: fontSize.xl,
fontWeight: "700",
color: colors.success,
marginBottom: spacing.s,
},
successMessage: {
fontSize: fontSize.sm,
color: colors.textSecondary,
textAlign: "center",
lineHeight: 20,
marginBottom: spacing.s,
},
successHint: {
fontSize: fontSize.xs,
color: colors.textMuted,
textAlign: "center",
lineHeight: 18,
marginBottom: spacing.xl,
},
successBtn: {
width: "100%",
paddingVertical: 14,
borderRadius: borderRadius.sm,
backgroundColor: colors.success,
alignItems: "center",
justifyContent: "center",
},
successBtnText: {
color: colors.white,
fontSize: fontSize.md,
fontWeight: "700",
},
}),
[colors],
);
if (loading) return <LoadingSpinner message="Chargement alertes..." />;
return (
<View style={styles.container}>
{/* Confirmation Modal */}
<Modal
visible={showConfirmModal}
transparent
animationType="fade"
onRequestClose={() => setShowConfirmModal(false)}
>
<View style={styles.modalOverlay}>
<View style={styles.modalContent}>
{/* Animated alert icon */}
<Animated.View
style={[
styles.modalIconCircle,
{ transform: [{ scale: pulseAnim }] },
]}
>
<View style={styles.modalIconInner}>
<Ionicons
name="warning"
size={40}
color={colors.white}
/>
</View>
</Animated.View>
<Text style={styles.modalTitle}>Alerte Police</Text>
<Text style={styles.modalMessage}>
Vous êtes sur le point de déclencher une alerte
police. Cette action notifiera immédiatement
l'administration.
</Text>
<Text style={styles.modalWarning}>
Confirmez-vous le déclenchement ?
</Text>
<View style={styles.modalButtons}>
<TouchableOpacity
style={styles.modalCancelBtn}
onPress={() => setShowConfirmModal(false)}
activeOpacity={0.7}
>
<Text style={styles.modalCancelText}>
Annuler
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.modalConfirmBtn}
onPress={handleConfirmTrigger}
activeOpacity={0.7}
>
<Ionicons
name="alert-circle"
size={18}
color={colors.white}
/>
<Text style={styles.modalConfirmText}>
Déclencher
</Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
{/* Success Modal */}
<Modal
visible={showSuccessModal}
transparent
animationType="fade"
onRequestClose={() => setShowSuccessModal(false)}
>
<View style={styles.modalOverlay}>
<View style={styles.successModalContent}>
<View style={styles.successIconCircle}>
<View style={styles.successIconInner}>
<Ionicons
name="checkmark-sharp"
size={40}
color={colors.white}
/>
</View>
</View>
<Text style={styles.successTitle}>Alerte envoyée</Text>
<Text style={styles.successMessage}>
{successMessage}
</Text>
<Text style={styles.successHint}>
L'administration a é notifiée. Vous pourrez
terminer l'alerte quand la situation sera résolue.
</Text>
<TouchableOpacity
style={styles.successBtn}
onPress={() => setShowSuccessModal(false)}
activeOpacity={0.7}
>
<Text style={styles.successBtnText}>Compris</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
<View style={styles.triggerSection}>
<Button
title={triggering ? "Envoi..." : "Déclencher alerte police"}
onPress={() => setShowConfirmModal(true)}
disabled={triggering}
style={{ backgroundColor: colors.danger }}
/>
</View>
<FlatList
data={alerts}
keyExtractor={(item) => item.id.toString()}
renderItem={renderAlert}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
tintColor={colors.success}
/>
}
contentContainerStyle={{ padding: spacing.l }}
ListEmptyComponent={
<Text style={styles.empty}>Aucune alerte</Text>
}
/>
<AlertModal
visible={alertModal.visible}
type={alertModal.type}
title={alertModal.title}
message={alertModal.message}
onClose={hideAlert}
/>
</View>
);
}