chore: update map

This commit is contained in:
2026-02-19 08:47:19 +01:00
parent 5afa907609
commit 3308adf1ea
2 changed files with 146 additions and 281 deletions
+3 -5
View File
@@ -6,7 +6,7 @@
"orientation": "portrait", "orientation": "portrait",
"icon": "./assets/icon.png", "icon": "./assets/icon.png",
"userInterfaceStyle": "dark", "userInterfaceStyle": "dark",
"newArchEnabled": true, "newArchEnabled": false,
"splash": { "splash": {
"image": "./assets/splash-icon.png", "image": "./assets/splash-icon.png",
"resizeMode": "contain", "resizeMode": "contain",
@@ -24,6 +24,7 @@
}, },
"edgeToEdgeEnabled": true, "edgeToEdgeEnabled": true,
"permissions": [ "permissions": [
"android.permission.ACCESS_BACKGROUND_LOCATION",
"android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION",
"android.permission.ACCESS_FINE_LOCATION" "android.permission.ACCESS_FINE_LOCATION"
] ]
@@ -31,10 +32,7 @@
"web": { "web": {
"favicon": "./assets/favicon.png" "favicon": "./assets/favicon.png"
}, },
"plugins": [ "plugins": ["expo-font", "expo-location"],
"expo-font",
"expo-location"
],
"extra": { "extra": {
"eas": { "eas": {
"projectId": "fcb7a0bc-5b2f-453d-ba16-9f97d9f0e440" "projectId": "fcb7a0bc-5b2f-453d-ba16-9f97d9f0e440"
@@ -21,7 +21,6 @@ import {
} from "react-native"; } from "react-native";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import * as Location from "expo-location"; import * as Location from "expo-location";
import MapView, { Marker, Polyline, PROVIDER_DEFAULT } from "react-native-maps";
import { spacing, fontSize, borderRadius } from "../../theme"; import { spacing, fontSize, borderRadius } from "../../theme";
import { useTheme } from "../../context/ThemeContext"; import { useTheme } from "../../context/ThemeContext";
import { import {
@@ -52,6 +51,12 @@ import Badge from "../../components/ui/Badge";
import Button from "../../components/ui/Button"; import Button from "../../components/ui/Button";
import AlertModal from "../../components/ui/AlertModal"; import AlertModal from "../../components/ui/AlertModal";
import { useAlert } from "../../hooks/useAlert"; import { useAlert } from "../../hooks/useAlert";
import TomTomMap, {
TomTomMapRef,
TomTomMarker,
TomTomRoute,
TomTomDestination,
} from "../../components/TomTomMap";
const { width: SCREEN_WIDTH } = Dimensions.get("window"); const { width: SCREEN_WIDTH } = Dimensions.get("window");
const MAP_HEIGHT = 260; const MAP_HEIGHT = 260;
@@ -91,8 +96,10 @@ export default function DashboardScreen() {
null, null,
); );
const appState = useRef(AppState.currentState); const appState = useRef(AppState.currentState);
const mapRef = useRef<MapView | null>(null);
const fullscreenMapRef = useRef<MapView | null>(null); // TomTom map refs
const mapRef = useRef<TomTomMapRef>(null);
const fullscreenMapRef = useRef<TomTomMapRef>(null);
const [mapFullscreen, setMapFullscreen] = useState(false); const [mapFullscreen, setMapFullscreen] = useState(false);
// TomTom routing // TomTom routing
@@ -117,6 +124,46 @@ export default function DashboardScreen() {
[colors], [colors],
); );
// --------------------------------------------------
// Construire les props pour TomTomMap
// --------------------------------------------------
// Marker du livreur (position courante)
const driverMarkers: TomTomMarker[] = useMemo(() => {
if (!lastCoords) return [];
return [
{
id: "driver",
latitude: lastCoords.lat,
longitude: lastCoords.lng,
color: colors.success,
label: "Ma position",
description: lastUpdate
? lastUpdate.toLocaleTimeString("fr-FR", {
hour: "2-digit",
minute: "2-digit",
})
: undefined,
},
];
}, [lastCoords, lastUpdate, colors.success]);
// Route TomTom
const tomtomRoute: TomTomRoute | null = useMemo(() => {
if (!routeInfo || routeInfo.coordinates.length === 0) return null;
return { coordinates: routeInfo.coordinates, color: "#4285F4" };
}, [routeInfo]);
// Destination TomTom
const tomtomDestination: TomTomDestination | null = useMemo(() => {
if (!destinationCoords) return null;
return {
latitude: destinationCoords.latitude,
longitude: destinationCoords.longitude,
color: colors.danger,
};
}, [destinationCoords, colors.danger]);
// -------------------------------------------------- // --------------------------------------------------
// Data // Data
// -------------------------------------------------- // --------------------------------------------------
@@ -137,7 +184,6 @@ export default function DashboardScreen() {
? deliveriesRes.deliveries ? deliveriesRes.deliveries
: []; : [];
// Include queue commands
const queueCommands: DeliveryItem[] = []; const queueCommands: DeliveryItem[] = [];
if (queueRes.success && queueRes.queue_info?.commands) { if (queueRes.success && queueRes.queue_info?.commands) {
for (const cmd of queueRes.queue_info.commands) { for (const cmd of queueRes.queue_info.commands) {
@@ -194,7 +240,6 @@ export default function DashboardScreen() {
setDeliveries(enriched); setDeliveries(enriched);
// Auto-calculate route for first in_progress / assigned delivery
const activeDelivery = const activeDelivery =
enriched.find( enriched.find(
(d) => (d) =>
@@ -227,7 +272,6 @@ export default function DashboardScreen() {
if (!lastCoords) return; if (!lastCoords) return;
setRouteLoading(true); setRouteLoading(true);
try { try {
// Geocode destination
const dest = await geocodeAddress(address); const dest = await geocodeAddress(address);
if (!dest) { if (!dest) {
setRouteLoading(false); setRouteLoading(false);
@@ -235,7 +279,6 @@ export default function DashboardScreen() {
} }
setDestinationCoords(dest); setDestinationCoords(dest);
// Calculate route
const result = await calculateRoute( const result = await calculateRoute(
{ latitude: lastCoords.lat, longitude: lastCoords.lng }, { latitude: lastCoords.lat, longitude: lastCoords.lng },
dest, dest,
@@ -245,30 +288,12 @@ export default function DashboardScreen() {
setInstructions(result.instructions); setInstructions(result.instructions);
setCurrentInstructionIdx(0); setCurrentInstructionIdx(0);
// Fit map to show both points // Fit la map pour afficher driver + destination
if (mapRef.current) { const coords = [
mapRef.current.fitToCoordinates( { latitude: lastCoords.lat, longitude: lastCoords.lng },
[ { latitude: dest.latitude, longitude: dest.longitude },
{ ];
latitude: lastCoords.lat, mapRef.current?.fitToCoordinates(coords);
longitude: lastCoords.lng,
},
{
latitude: dest.latitude,
longitude: dest.longitude,
},
],
{
edgePadding: {
top: 80,
right: 60,
bottom: 80,
left: 60,
},
animated: true,
},
);
}
} }
} catch { } catch {
/* silent */ /* silent */
@@ -286,7 +311,6 @@ export default function DashboardScreen() {
const sendCurrentLocation = useCallback(async () => { const sendCurrentLocation = useCallback(async () => {
if (sendingRef.current) return; if (sendingRef.current) return;
sendingRef.current = true; sendingRef.current = true;
try { try {
const loc = await Promise.race([ const loc = await Promise.race([
Location.getCurrentPositionAsync({ Location.getCurrentPositionAsync({
@@ -297,7 +321,6 @@ export default function DashboardScreen() {
), ),
]); ]);
// validation runtime + typage TS
if ( if (
!loc || !loc ||
typeof loc !== "object" || typeof loc !== "object" ||
@@ -313,7 +336,6 @@ export default function DashboardScreen() {
setLastCoords({ lat: latitude, lng: longitude }); setLastCoords({ lat: latitude, lng: longitude });
setLastUpdate(new Date()); setLastUpdate(new Date());
await updateMyLocation(latitude, longitude); await updateMyLocation(latitude, longitude);
} catch (err) { } catch (err) {
console.log("GPS error:", err); console.log("GPS error:", err);
@@ -440,7 +462,6 @@ export default function DashboardScreen() {
return ( return (
<View style={styles.instructionBar}> <View style={styles.instructionBar}>
{/* Current instruction */}
<View style={styles.currentInstruction}> <View style={styles.currentInstruction}>
<View style={styles.instructionIconBox}> <View style={styles.instructionIconBox}>
<Ionicons <Ionicons
@@ -464,7 +485,6 @@ export default function DashboardScreen() {
</Text> </Text>
</View> </View>
{/* Next instruction preview */}
{next && ( {next && (
<View style={styles.nextInstruction}> <View style={styles.nextInstruction}>
<Text style={styles.nextLabel}>Puis</Text> <Text style={styles.nextLabel}>Puis</Text>
@@ -482,7 +502,6 @@ export default function DashboardScreen() {
</View> </View>
)} )}
{/* Route summary + toggle all instructions */}
<View style={styles.routeSummary}> <View style={styles.routeSummary}>
<View style={styles.routeInfoChip}> <View style={styles.routeInfoChip}>
<Ionicons <Ionicons
@@ -534,7 +553,6 @@ export default function DashboardScreen() {
return ( return (
<Card style={{ marginBottom: spacing.m }}> <Card style={{ marginBottom: spacing.m }}>
{/* Header */}
<View style={styles.cardHeader}> <View style={styles.cardHeader}>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Text style={styles.deliveryId}> <Text style={styles.deliveryId}>
@@ -568,7 +586,6 @@ export default function DashboardScreen() {
/> />
</View> </View>
{/* Address + navigation */}
<TouchableOpacity <TouchableOpacity
style={styles.addressRow} style={styles.addressRow}
onPress={() => openNavigation(item.adresse)} onPress={() => openNavigation(item.adresse)}
@@ -589,7 +606,6 @@ export default function DashboardScreen() {
/> />
</TouchableOpacity> </TouchableOpacity>
{/* Calculate route button for active delivery */}
{isActive && lastCoords && ( {isActive && lastCoords && (
<TouchableOpacity <TouchableOpacity
style={styles.calcRouteBtn} style={styles.calcRouteBtn}
@@ -609,7 +625,6 @@ export default function DashboardScreen() {
</TouchableOpacity> </TouchableOpacity>
)} )}
{/* Items / Products */}
{item.items && item.items.length > 0 && ( {item.items && item.items.length > 0 && (
<View style={styles.itemsSection}> <View style={styles.itemsSection}>
<View style={styles.itemsHeader}> <View style={styles.itemsHeader}>
@@ -652,7 +667,6 @@ export default function DashboardScreen() {
</Text> </Text>
)} )}
{/* Client phone */}
{item.clientPhone && ( {item.clientPhone && (
<TouchableOpacity <TouchableOpacity
style={styles.phoneRow} style={styles.phoneRow}
@@ -669,7 +683,6 @@ export default function DashboardScreen() {
</TouchableOpacity> </TouchableOpacity>
)} )}
{/* Action buttons */}
{(item.status === "pending" || item.status === "assigned") && ( {(item.status === "pending" || item.status === "assigned") && (
<Button <Button
title="Démarrer la livraison" title="Démarrer la livraison"
@@ -696,76 +709,25 @@ export default function DashboardScreen() {
}; };
// -------------------------------------------------- // --------------------------------------------------
// Header with map + route + status + GPS // Header avec TomTomMap
// -------------------------------------------------- // --------------------------------------------------
const renderHeader = () => ( const renderHeader = () => (
<View> <View>
{/* Map with route */} {/* Carte TomTom */}
{lastCoords && ( {lastCoords ? (
<View style={styles.mapContainer}> <View style={styles.mapContainer}>
<MapView <TomTomMap
ref={mapRef} ref={mapRef}
provider={PROVIDER_DEFAULT}
style={styles.map} style={styles.map}
initialRegion={{ markers={driverMarkers}
latitude: lastCoords.lat, route={tomtomRoute}
longitude: lastCoords.lng, destination={tomtomDestination}
latitudeDelta: 0.02, initialCenter={{
longitudeDelta: 0.02,
}}
showsUserLocation={false}
showsMyLocationButton={false}
>
{/* Driver marker */}
<Marker
coordinate={{
latitude: lastCoords.lat, latitude: lastCoords.lat,
longitude: lastCoords.lng, longitude: lastCoords.lng,
}} }}
title="Ma position" initialZoom={14}
>
<View style={styles.driverMarkerOuter}>
<View style={styles.driverMarkerInner}>
<Ionicons
name="bicycle"
size={16}
color={colors.white}
/> />
</View>
</View>
</Marker>
{/* Destination marker */}
{destinationCoords && (
<Marker
coordinate={{
latitude: destinationCoords.latitude,
longitude: destinationCoords.longitude,
}}
title="Destination"
>
<View style={styles.destMarkerOuter}>
<View style={styles.destMarkerInner}>
<Ionicons
name="flag"
size={14}
color={colors.white}
/>
</View>
</View>
</Marker>
)}
{/* Route polyline */}
{routeInfo && routeInfo.coordinates.length > 0 && (
<Polyline
coordinates={routeInfo.coordinates}
strokeColor="#4285F4"
strokeWidth={5}
lineDashPattern={[0]}
/>
)}
</MapView>
{/* GPS overlay */} {/* GPS overlay */}
<View style={styles.gpsOverlay}> <View style={styles.gpsOverlay}>
@@ -793,7 +755,7 @@ export default function DashboardScreen() {
)} )}
</View> </View>
{/* Fullscreen button */} {/* Bouton plein écran */}
<TouchableOpacity <TouchableOpacity
style={styles.expandBtn} style={styles.expandBtn}
onPress={() => setMapFullscreen(true)} onPress={() => setMapFullscreen(true)}
@@ -806,7 +768,7 @@ export default function DashboardScreen() {
/> />
</TouchableOpacity> </TouchableOpacity>
{/* Route loading indicator */} {/* Indicateur calcul de route */}
{routeLoading && ( {routeLoading && (
<View style={styles.routeLoadingOverlay}> <View style={styles.routeLoadingOverlay}>
<Text style={styles.routeLoadingText}> <Text style={styles.routeLoadingText}>
@@ -815,10 +777,7 @@ export default function DashboardScreen() {
</View> </View>
)} )}
</View> </View>
)} ) : (
{/* No location fallback */}
{!lastCoords && (
<View style={styles.noMapBox}> <View style={styles.noMapBox}>
<Ionicons <Ionicons
name="location-outline" name="location-outline"
@@ -831,10 +790,10 @@ export default function DashboardScreen() {
</View> </View>
)} )}
{/* Navigation instructions */} {/* Instructions navigation */}
{renderInstructionBar()} {renderInstructionBar()}
{/* All instructions list */} {/* Liste complète des étapes */}
{showInstructions && instructions.length > 0 && ( {showInstructions && instructions.length > 0 && (
<View style={styles.allInstructionsBox}> <View style={styles.allInstructionsBox}>
<View style={styles.allInstructionsHeader}> <View style={styles.allInstructionsHeader}>
@@ -937,12 +896,14 @@ export default function DashboardScreen() {
</View> </View>
); );
// --------------------------------------------------
// Styles
// --------------------------------------------------
const styles = useMemo( const styles = useMemo(
() => () =>
StyleSheet.create({ StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bgPrimary }, container: { flex: 1, backgroundColor: colors.bgPrimary },
// Status
statusBar: { statusBar: {
padding: spacing.l, padding: spacing.l,
backgroundColor: colors.bgSecondary, backgroundColor: colors.bgSecondary,
@@ -979,8 +940,9 @@ export default function DashboardScreen() {
overflow: "hidden", overflow: "hidden",
marginBottom: spacing.m, marginBottom: spacing.m,
position: "relative", position: "relative",
height: MAP_HEIGHT,
}, },
map: { width: "100%", height: MAP_HEIGHT }, map: { flex: 1 },
gpsOverlay: { gpsOverlay: {
position: "absolute", position: "absolute",
bottom: spacing.s, bottom: spacing.s,
@@ -1001,40 +963,6 @@ export default function DashboardScreen() {
}, },
gpsOverlayTime: { color: colors.textMuted, fontSize: 10 }, gpsOverlayTime: { color: colors.textMuted, fontSize: 10 },
// Markers
driverMarkerOuter: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: colors.success + "40",
justifyContent: "center",
alignItems: "center",
},
driverMarkerInner: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: colors.success,
justifyContent: "center",
alignItems: "center",
},
destMarkerOuter: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: colors.danger + "40",
justifyContent: "center",
alignItems: "center",
},
destMarkerInner: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: colors.danger,
justifyContent: "center",
alignItems: "center",
},
noMapBox: { noMapBox: {
alignItems: "center", alignItems: "center",
paddingVertical: spacing.xl, paddingVertical: spacing.xl,
@@ -1048,7 +976,6 @@ export default function DashboardScreen() {
marginTop: spacing.s, marginTop: spacing.s,
}, },
// Route loading
routeLoadingOverlay: { routeLoadingOverlay: {
position: "absolute", position: "absolute",
top: 0, top: 0,
@@ -1065,7 +992,19 @@ export default function DashboardScreen() {
fontWeight: "600", fontWeight: "600",
}, },
// Navigation instructions expandBtn: {
position: "absolute",
top: spacing.s,
right: spacing.s,
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: "rgba(0,0,0,0.6)",
justifyContent: "center",
alignItems: "center",
},
// Instructions
instructionBar: { instructionBar: {
backgroundColor: colors.bgSecondary, backgroundColor: colors.bgSecondary,
borderRadius: borderRadius.md, borderRadius: borderRadius.md,
@@ -1202,10 +1141,7 @@ export default function DashboardScreen() {
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
}, },
stepText: { stepText: { color: colors.textWhite, fontSize: fontSize.sm },
color: colors.textWhite,
fontSize: fontSize.sm,
},
stepStreet: { stepStreet: {
color: colors.textMuted, color: colors.textMuted,
fontSize: fontSize.xs, fontSize: fontSize.xs,
@@ -1217,7 +1153,7 @@ export default function DashboardScreen() {
fontWeight: "600", fontWeight: "600",
}, },
// Calculate route button // Calc route button
calcRouteBtn: { calcRouteBtn: {
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
@@ -1252,7 +1188,6 @@ export default function DashboardScreen() {
fontWeight: "600", fontWeight: "600",
}, },
// Section
sectionTitle: { sectionTitle: {
color: colors.textWhite, color: colors.textWhite,
fontSize: fontSize.lg, fontSize: fontSize.lg,
@@ -1277,7 +1212,6 @@ export default function DashboardScreen() {
marginTop: 2, marginTop: 2,
}, },
// Address
addressRow: { addressRow: {
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
@@ -1294,7 +1228,6 @@ export default function DashboardScreen() {
fontSize: fontSize.sm, fontSize: fontSize.sm,
}, },
// Items
itemsSection: { itemsSection: {
backgroundColor: colors.bgInput, backgroundColor: colors.bgInput,
borderRadius: borderRadius.sm, borderRadius: borderRadius.sm,
@@ -1361,7 +1294,6 @@ export default function DashboardScreen() {
marginTop: spacing.xs, marginTop: spacing.xs,
}, },
// Phone
phoneRow: { phoneRow: {
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
@@ -1377,27 +1309,12 @@ export default function DashboardScreen() {
fontSize: fontSize.md, fontSize: fontSize.md,
}, },
// Expand button // Fullscreen map modal
expandBtn: {
position: "absolute",
top: spacing.s,
right: spacing.s,
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: "rgba(0,0,0,0.6)",
justifyContent: "center",
alignItems: "center",
},
// Fullscreen map
fullscreenContainer: { fullscreenContainer: {
flex: 1, flex: 1,
backgroundColor: colors.bgPrimary, backgroundColor: colors.bgPrimary,
}, },
fullscreenMap: { fullscreenMap: { ...StyleSheet.absoluteFillObject },
...StyleSheet.absoluteFillObject,
},
fullscreenTopBar: { fullscreenTopBar: {
position: "absolute", position: "absolute",
top: 0, top: 0,
@@ -1484,7 +1401,7 @@ export default function DashboardScreen() {
return ( return (
<View style={styles.container}> <View style={styles.container}>
{/* Fullscreen map modal */} {/* ── Modal plein écran avec TomTomMap ── */}
<Modal <Modal
visible={mapFullscreen} visible={mapFullscreen}
animationType="fade" animationType="fade"
@@ -1494,70 +1411,18 @@ export default function DashboardScreen() {
<StatusBar hidden={mapFullscreen} /> <StatusBar hidden={mapFullscreen} />
<View style={styles.fullscreenContainer}> <View style={styles.fullscreenContainer}>
{lastCoords && ( {lastCoords && (
<MapView <TomTomMap
ref={fullscreenMapRef} ref={fullscreenMapRef}
provider={PROVIDER_DEFAULT}
style={styles.fullscreenMap} style={styles.fullscreenMap}
initialRegion={{ markers={driverMarkers}
latitude: lastCoords.lat, route={tomtomRoute}
longitude: lastCoords.lng, destination={tomtomDestination}
latitudeDelta: 0.01, initialCenter={{
longitudeDelta: 0.01,
}}
showsUserLocation={false}
showsMyLocationButton={false}
showsCompass
showsScale
>
{/* Driver */}
<Marker
coordinate={{
latitude: lastCoords.lat, latitude: lastCoords.lat,
longitude: lastCoords.lng, longitude: lastCoords.lng,
}} }}
title="Ma position" initialZoom={15}
>
<View style={styles.driverMarkerOuter}>
<View style={styles.driverMarkerInner}>
<Ionicons
name="bicycle"
size={16}
color={colors.white}
/> />
</View>
</View>
</Marker>
{/* Destination */}
{destinationCoords && (
<Marker
coordinate={{
latitude: destinationCoords.latitude,
longitude: destinationCoords.longitude,
}}
title="Destination"
>
<View style={styles.destMarkerOuter}>
<View style={styles.destMarkerInner}>
<Ionicons
name="flag"
size={14}
color={colors.white}
/>
</View>
</View>
</Marker>
)}
{/* Route */}
{routeInfo && routeInfo.coordinates.length > 0 && (
<Polyline
coordinates={routeInfo.coordinates}
strokeColor="#4285F4"
strokeWidth={5}
/>
)}
</MapView>
)} )}
{/* Top bar */} {/* Top bar */}
@@ -1580,7 +1445,7 @@ export default function DashboardScreen() {
<View style={{ width: 40 }} /> <View style={{ width: 40 }} />
</View> </View>
{/* Fullscreen instruction bar */} {/* Instruction bar en plein écran */}
{instructions.length > 0 && ( {instructions.length > 0 && (
<View style={styles.fullscreenInstructionBar}> <View style={styles.fullscreenInstructionBar}>
<View style={styles.instructionIconBox}> <View style={styles.instructionIconBox}>
@@ -1675,7 +1540,7 @@ export default function DashboardScreen() {
</View> </View>
</Modal> </Modal>
{/* Status bar */} {/* ── Barre de statut ── */}
<View style={styles.statusBar}> <View style={styles.statusBar}>
<Text style={styles.statusLabel}>Mon statut:</Text> <Text style={styles.statusLabel}>Mon statut:</Text>
<View style={styles.statusBtns}> <View style={styles.statusBtns}>
@@ -1712,6 +1577,7 @@ export default function DashboardScreen() {
</View> </View>
</View> </View>
{/* ── Liste des livraisons ── */}
<FlatList <FlatList
data={deliveries} data={deliveries}
keyExtractor={(item, index) => (item.id ?? index).toString()} keyExtractor={(item, index) => (item.id ?? index).toString()}
@@ -1734,6 +1600,7 @@ export default function DashboardScreen() {
} }
ListHeaderComponent={renderHeader()} ListHeaderComponent={renderHeader()}
/> />
<AlertModal <AlertModal
visible={alert.visible} visible={alert.visible}
type={alert.type} type={alert.type}