97 lines
4.2 KiB
TypeScript
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='© <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>
|
|
);
|
|
}
|