chore: fix map dashboard admin

This commit is contained in:
2026-02-19 10:06:21 +01:00
parent 3f418e62ab
commit f6e4e51a34
@@ -14,10 +14,8 @@ import {
TouchableOpacity,
Modal,
StatusBar,
Dimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import MapView, { Marker, Polyline, PROVIDER_DEFAULT } from "react-native-maps";
import { spacing, fontSize, borderRadius } from "../../theme";
import { useTheme } from "../../context/ThemeContext";
import {
@@ -31,6 +29,12 @@ import { STATUS_LABELS, getStatusColors } from "../../utils/constants";
import LoadingSpinner from "../../components/ui/LoadingSpinner";
import Card from "../../components/ui/Card";
import Badge from "../../components/ui/Badge";
import TomTomMap, {
TomTomMapRef,
TomTomMarker,
TomTomRoute,
TomTomDestination,
} from "../../components/TomTomMap";
const MAP_HEIGHT = 280;
@@ -47,9 +51,9 @@ export default function DeliveryScreen() {
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
// Map
const mapRef = useRef<MapView | null>(null);
const fullscreenMapRef = useRef<MapView | null>(null);
// TomTomMap refs
const mapRef = useRef<TomTomMapRef>(null);
const fullscreenMapRef = useRef<TomTomMapRef>(null);
const [mapFullscreen, setMapFullscreen] = useState(false);
// Selected livreur route tracking
@@ -88,6 +92,44 @@ export default function DeliveryScreen() {
(l) => l.location.latitude !== 0 && l.location.longitude !== 0,
);
// --------------------------------------------------
// Construire les markers TomTom pour tous les livreurs
// --------------------------------------------------
const tomtomMarkers: TomTomMarker[] = useMemo(() => {
return livreursWithGPS.map((l) => ({
id: l.username,
latitude: l.location.latitude,
longitude: l.location.longitude,
color: statusColors[l.status] || colors.textMuted,
label: l.username,
description: `${STATUS_LABELS[l.status] || l.status}${
l.stats.current_command
? ` · Cmd #${l.stats.current_command}`
: ""
}`,
isSelected: selectedLivreur?.username === l.username,
}));
}, [livreursWithGPS, selectedLivreur, statusColors, colors.textMuted]);
// 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]);
// --------------------------------------------------
// Track livreur — calcul de route
// --------------------------------------------------
const trackLivreur = useCallback(
async (livreur: DeliveryPerson) => {
setSelectedLivreur(livreur);
@@ -121,30 +163,15 @@ export default function DeliveryScreen() {
const result = await calculateRoute(origin, dest);
if (result) {
setRouteInfo(result.route);
// Fit la map sur les deux points
const ref = mapFullscreen ? fullscreenMapRef : mapRef;
if (ref.current) {
ref.current.fitToCoordinates(
[
{
latitude: origin.latitude,
longitude: origin.longitude,
},
{
latitude: dest.latitude,
longitude: dest.longitude,
},
],
{
edgePadding: {
top: 80,
right: 60,
bottom: 80,
left: 60,
},
animated: true,
},
);
}
ref.current?.fitToCoordinates([
{
latitude: origin.latitude,
longitude: origin.longitude,
},
{ latitude: dest.latitude, longitude: dest.longitude },
]);
}
} catch {
/* silent */
@@ -160,21 +187,28 @@ export default function DeliveryScreen() {
setDestinationCoords(null);
};
const fitAllMarkers = (ref: React.RefObject<MapView | null>) => {
if (ref.current && livreursWithGPS.length > 0) {
ref.current.fitToCoordinates(
livreursWithGPS.map((l) => ({
latitude: l.location.latitude,
longitude: l.location.longitude,
})),
{
edgePadding: { top: 60, right: 60, bottom: 60, left: 60 },
animated: true,
},
// --------------------------------------------------
// onMarkerPress depuis TomTomMap
// --------------------------------------------------
const handleMarkerPress = useCallback(
(markerId: string) => {
const livreur = livreursWithGPS.find(
(l) => l.username === markerId,
);
}
};
if (livreur) {
if (selectedLivreur?.username === markerId) {
clearRoute();
} else {
trackLivreur(livreur);
}
}
},
[livreursWithGPS, selectedLivreur, trackLivreur],
);
// --------------------------------------------------
// Styles
// --------------------------------------------------
const styles = useMemo(
() =>
StyleSheet.create({
@@ -208,46 +242,9 @@ export default function DeliveryScreen() {
overflow: "hidden",
marginBottom: spacing.m,
position: "relative",
height: MAP_HEIGHT,
},
map: { width: "100%", height: MAP_HEIGHT },
markerOuter: {
width: 34,
height: 34,
borderRadius: 17,
justifyContent: "center",
alignItems: "center",
},
markerSelected: {
borderWidth: 2,
borderColor: colors.accent,
width: 40,
height: 40,
borderRadius: 20,
},
markerInner: {
width: 26,
height: 26,
borderRadius: 13,
justifyContent: "center",
alignItems: "center",
},
destMarkerOuter: {
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: colors.danger + "40",
justifyContent: "center",
alignItems: "center",
},
destMarkerInner: {
width: 22,
height: 22,
borderRadius: 11,
backgroundColor: colors.danger,
justifyContent: "center",
alignItems: "center",
},
map: { flex: 1 },
routeOverlay: {
position: "absolute",
@@ -393,6 +390,7 @@ export default function DeliveryScreen() {
marginTop: spacing.xxl,
},
// Fullscreen
fullscreenContainer: {
flex: 1,
backgroundColor: colors.bgPrimary,
@@ -472,77 +470,9 @@ export default function DeliveryScreen() {
[colors],
);
const renderMapMarkers = () =>
livreursWithGPS.map((l) => {
const isSelected = selectedLivreur?.username === l.username;
const markerColor = statusColors[l.status] || colors.textMuted;
return (
<Marker
key={l.username}
coordinate={{
latitude: l.location.latitude,
longitude: l.location.longitude,
}}
title={l.username}
description={`${STATUS_LABELS[l.status] || l.status}${l.stats.current_command ? ` · Cmd #${l.stats.current_command}` : ""}`}
onPress={() => trackLivreur(l)}
>
<View
style={[
styles.markerOuter,
{ backgroundColor: markerColor + "40" },
isSelected && styles.markerSelected,
]}
>
<View
style={[
styles.markerInner,
{ backgroundColor: markerColor },
]}
>
<Ionicons
name="bicycle"
size={14}
color={colors.white}
/>
</View>
</View>
</Marker>
);
});
const renderRouteOverlay = () => {
if (!destinationCoords) return null;
return (
<>
<Marker
coordinate={{
latitude: destinationCoords.latitude,
longitude: destinationCoords.longitude,
}}
title="Destination"
>
<View style={styles.destMarkerOuter}>
<View style={styles.destMarkerInner}>
<Ionicons
name="flag"
size={12}
color={colors.white}
/>
</View>
</View>
</Marker>
{routeInfo && routeInfo.coordinates.length > 0 && (
<Polyline
coordinates={routeInfo.coordinates}
strokeColor="#4285F4"
strokeWidth={4}
/>
)}
</>
);
};
// --------------------------------------------------
// Render livreur card
// --------------------------------------------------
const renderLivreur = ({ item }: { item: DeliveryPerson }) => {
const isSelected = selectedLivreur?.username === item.username;
const hasGPS = item.location.latitude !== 0;
@@ -658,27 +588,28 @@ export default function DeliveryScreen() {
);
};
// --------------------------------------------------
// Header avec TomTomMap
// --------------------------------------------------
const renderHeader = () => (
<View>
{livreursWithGPS.length > 0 && (
{livreursWithGPS.length > 0 ? (
<View style={styles.mapContainer}>
<MapView
<TomTomMap
ref={mapRef}
provider={PROVIDER_DEFAULT}
style={styles.map}
initialRegion={{
markers={tomtomMarkers}
route={tomtomRoute}
destination={tomtomDestination}
initialCenter={{
latitude: livreursWithGPS[0].location.latitude,
longitude: livreursWithGPS[0].location.longitude,
latitudeDelta: 0.05,
longitudeDelta: 0.05,
}}
onMapReady={() => fitAllMarkers(mapRef)}
showsUserLocation={false}
>
{renderMapMarkers()}
{renderRouteOverlay()}
</MapView>
initialZoom={12}
onMarkerPress={handleMarkerPress}
/>
{/* Overlay info route sélectionnée */}
{routeInfo && selectedLivreur && (
<View style={styles.routeOverlay}>
<Text style={styles.routeOverlayUser}>
@@ -720,7 +651,7 @@ export default function DeliveryScreen() {
<View style={styles.mapBtns}>
<TouchableOpacity
style={styles.mapBtn}
onPress={() => fitAllMarkers(mapRef)}
onPress={() => mapRef.current?.fitAllMarkers()}
>
<Ionicons
name="locate-outline"
@@ -740,9 +671,7 @@ export default function DeliveryScreen() {
</TouchableOpacity>
</View>
</View>
)}
{livreursWithGPS.length === 0 && !loading && (
) : !loading ? (
<View style={styles.noMapBox}>
<Ionicons
name="location-outline"
@@ -753,7 +682,7 @@ export default function DeliveryScreen() {
Aucun livreur avec GPS actif
</Text>
</View>
)}
) : null}
<Text style={styles.sectionTitle}>
Livreurs ({livreurs.length})
@@ -765,7 +694,7 @@ export default function DeliveryScreen() {
return (
<View style={styles.container}>
{/* Fullscreen map modal */}
{/* ── Modal plein écran TomTomMap ── */}
<Modal
visible={mapFullscreen}
animationType="fade"
@@ -775,27 +704,23 @@ export default function DeliveryScreen() {
<StatusBar hidden={mapFullscreen} />
<View style={styles.fullscreenContainer}>
{livreursWithGPS.length > 0 && (
<MapView
<TomTomMap
ref={fullscreenMapRef}
provider={PROVIDER_DEFAULT}
style={styles.fullscreenMap}
initialRegion={{
markers={tomtomMarkers}
route={tomtomRoute}
destination={tomtomDestination}
initialCenter={{
latitude: livreursWithGPS[0].location.latitude,
longitude:
livreursWithGPS[0].location.longitude,
latitudeDelta: 0.05,
longitudeDelta: 0.05,
}}
onMapReady={() => fitAllMarkers(fullscreenMapRef)}
showsUserLocation={false}
showsCompass
showsScale
>
{renderMapMarkers()}
{renderRouteOverlay()}
</MapView>
initialZoom={12}
onMarkerPress={handleMarkerPress}
/>
)}
{/* Top bar */}
<View style={styles.fullscreenTopBar}>
<TouchableOpacity
style={styles.closeBtn}
@@ -812,7 +737,9 @@ export default function DeliveryScreen() {
</Text>
<TouchableOpacity
style={styles.closeBtn}
onPress={() => fitAllMarkers(fullscreenMapRef)}
onPress={() =>
fullscreenMapRef.current?.fitAllMarkers()
}
>
<Ionicons
name="locate-outline"
@@ -822,6 +749,7 @@ export default function DeliveryScreen() {
</TouchableOpacity>
</View>
{/* Route bar plein écran */}
{routeInfo && selectedLivreur && (
<View style={styles.fullscreenRouteBar}>
<View style={{ flex: 1 }}>
@@ -848,6 +776,7 @@ export default function DeliveryScreen() {
</View>
)}
{/* Bottom legend */}
<View style={styles.fullscreenBottomBar}>
<View style={styles.legendRow}>
<View style={styles.legendItem}>
@@ -888,7 +817,7 @@ export default function DeliveryScreen() {
</View>
</Modal>
{/* Summary */}
{/* ── Résumé stats ── */}
<View style={styles.summaryRow}>
<View
style={[
@@ -919,6 +848,7 @@ export default function DeliveryScreen() {
</View>
</View>
{/* ── Liste livreurs ── */}
<FlatList
data={livreurs}
keyExtractor={(item) => item.username}