Add map error boundary and move Leaflet CSS

Import global Leaflet CSS in the app layout, create a MapErrorBoundary in the map page to catch map load failures and show a retry UI, and wrap the dynamic CollectionMap with that boundary. Adjust CollectionMap by removing the local Leaflet CSS import, cleaning up unused imports/types, and changing the container to absolute/full-bleed styling so the map fills its parent. These changes improve robustness against map loading errors and ensure Leaflet styles are applied app-wide.
This commit is contained in:
SJCoetz 2026-05-27 01:17:56 +02:00
parent 2c896eede8
commit 398d9edcbb
3 changed files with 41 additions and 7 deletions

View file

@ -1,4 +1,5 @@
import type { Metadata, Viewport } from "next";
import "leaflet/dist/leaflet.css";
import "./globals.css";
import { AuthProvider } from "@/components/AuthProvider";
import OfflineIndicator from "@/components/OfflineIndicator";

View file

@ -1,6 +1,6 @@
'use client';
import { useState, useEffect, useCallback, Suspense } from 'react';
import { useState, useEffect, useCallback, Suspense, Component } from 'react';
import { useRouter } from 'next/navigation';
import dynamic from 'next/dynamic';
import { OrderItem } from '@/lib/types';
@ -23,6 +23,32 @@ const CollectionMap = dynamic(() => import('@/components/CollectionMap'), {
),
});
class MapErrorBoundary extends Component<{ children: React.ReactNode }, { hasError: boolean; error: string }> {
constructor(props: { children: React.ReactNode }) {
super(props);
this.state = { hasError: false, error: '' };
}
static getDerivedStateFromError(error: Error) {
return { hasError: true, error: error.message };
}
render() {
if (this.state.hasError) {
return (
<div className="h-full flex flex-col items-center justify-center bg-gray-100 p-4">
<div className="w-16 h-16 bg-red-50 rounded-2xl flex items-center justify-center mb-4">
<AlertCircle size={32} className="text-red-400" />
</div>
<p className="text-gray-600 text-sm mb-4 text-center">Map failed to load.</p>
<button onClick={() => this.setState({ hasError: false })} className="px-5 py-2.5 bg-orange-500 text-white rounded-xl text-sm font-medium active:bg-orange-600">
Try Again
</button>
</div>
);
}
return this.props.children;
}
}
export default function MapPage() {
const router = useRouter();
const { isLoggedIn, isLoading: authLoading } = useAuth();
@ -131,7 +157,9 @@ export default function MapPage() {
<div className="w-8 h-8 border-[3px] border-orange-500 border-t-transparent rounded-full animate-spin" />
</div>
}>
<CollectionMap items={items} onNavigate={handleNavigate} />
<MapErrorBoundary>
<CollectionMap items={items} onNavigate={handleNavigate} />
</MapErrorBoundary>
</Suspense>
)}
</main>

View file

@ -2,12 +2,11 @@
import { useEffect, useMemo, useState, useCallback, useRef } from 'react';
import { MapContainer, TileLayer, Marker, Popup, Polyline, useMap } from 'react-leaflet';
import { OrderItem, Supplier, DriverLocation } from '@/lib/types';
import { OrderItem, Supplier } from '@/lib/types';
import { formatCurrency, calculateDistance, parseCoordinates } from '@/lib/utils';
import { getDriverTrail, isAuthenticated, getCurrentUser } from '@/lib/pocketbase';
import { Navigation, AlertTriangle, Route, Crosshair, Layers } from 'lucide-react';
import { Navigation, AlertTriangle, Route, Crosshair } from 'lucide-react';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
const defaultIcon = L.icon({
iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
@ -129,8 +128,14 @@ export default function CollectionMap({ items, onNavigate }: MapViewProps) {
};
return (
<div className="h-full w-full relative">
<MapContainer center={center} zoom={12} className="h-full w-full" zoomControl={false}>
<div className="absolute inset-0">
<MapContainer
center={center}
zoom={12}
className="absolute inset-0 z-0"
zoomControl={false}
style={{ height: '100%', width: '100%' }}
>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"