TIFNJK_E41222887/resources/js/components/AttendanceMapModal.tsx

97 lines
4.2 KiB
TypeScript

import React from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { MapPin } from 'lucide-react';
// Perbaikan issue icon marker Leaflet di React
import markerIcon2x from 'leaflet/dist/images/marker-icon-2x.png';
import markerIcon from 'leaflet/dist/images/marker-icon.png';
import markerShadow from 'leaflet/dist/images/marker-shadow.png';
delete (L.Icon.Default.prototype as any)._getIconUrl;
L.Icon.Default.mergeOptions({
iconUrl: markerIcon,
iconRetinaUrl: markerIcon2x,
shadowUrl: markerShadow,
});
interface AttendanceMapModalProps {
latitudeIn?: string | null;
longitudeIn?: string | null;
latitudeOut?: string | null;
longitudeOut?: string | null;
employeeName: string;
date: string;
}
export function AttendanceMapModal({ latitudeIn, longitudeIn, latitudeOut, longitudeOut, employeeName, date }: AttendanceMapModalProps) {
const hasIn = latitudeIn && longitudeIn;
const hasOut = latitudeOut && longitudeOut;
const hasData = hasIn || hasOut;
if (!hasData) {
return (
<span className="text-xs text-muted-foreground italic flex items-center gap-1">
<MapPin className="h-3 w-3" /> Tidak ada data lokasi
</span>
);
}
const posIn: [number, number] | null = hasIn ? [parseFloat(latitudeIn as string), parseFloat(longitudeIn as string)] : null;
const posOut: [number, number] | null = hasOut ? [parseFloat(latitudeOut as string), parseFloat(longitudeOut as string)] : null;
// Default center ke check in, atau check out
const center = posIn || posOut || [-6.200000, 106.816666];
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" size="sm" className="gap-1.5 h-8">
<MapPin className="h-3.5 w-3.5 text-blue-600" /> Lihat Peta
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[600px]">
<DialogHeader>
<DialogTitle>Detail Lokasi Absensi</DialogTitle>
<p className="text-sm text-muted-foreground mt-1">
<span className="font-medium text-foreground">{employeeName}</span> {date}
</p>
</DialogHeader>
<div className="h-[400px] w-full rounded-md overflow-hidden border mt-2">
<MapContainer center={center} zoom={16} style={{ height: '100%', width: '100%', zIndex: 1 }}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
/>
{posIn && (
<Marker position={posIn}>
<Popup>
<div className="font-semibold text-sm mb-1">📍 Lokasi Clock In</div>
<div className="text-xs text-muted-foreground">
Lat: {posIn[0]}<br />
Lng: {posIn[1]}
</div>
</Popup>
</Marker>
)}
{posOut && (
<Marker position={posOut}>
<Popup>
<div className="font-semibold text-sm mb-1">📍 Lokasi Clock Out</div>
<div className="text-xs text-muted-foreground">
Lat: {posOut[0]}<br />
Lng: {posOut[1]}
</div>
</Popup>
</Marker>
)}
</MapContainer>
</div>
</DialogContent>
</Dialog>
);
}