CasperSecurity
<x-app-layout>
<x-slot name="header">
<div class="page-title d-flex flex-column me-5">
<!--begin::Title-->
<h1 class="d-flex flex-column text-dark fw-bolder fs-2 mb-0">Admin Dashboard</h1>
<!--end::Title-->
<!--begin::Breadcrumb-->
<!-- <ul class="breadcrumb breadcrumb-separatorless fw-bold fs-7 pt-1">
<li class="breadcrumb-item text-dark">HR Dashboard</li>
</ul> -->
<!--end::Breadcrumb-->
</div>
</x-slot>
<x-slot name="container">
@livewire('dashboard.hr-dashboard-livewire')
</x-slot>
<x-slot name="externaljs">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script>
window.siteMarkers = {};
window.siteMapInstance = null;
window.initSiteMap = function (employees) {
var el = document.getElementById('site-map');
if (!el) return;
// If map already initialized on this container (Leaflet property)
if (el._leaflet_id) {
el.innerHTML = "";
el._leaflet_id = null;
}
var map = L.map('site-map').setView([20.5937, 78.9629], 5);
window.siteMapInstance = map;
// Satellite View (Esri World Imagery)
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
}).addTo(map);
if (employees && Array.isArray(employees)) {
employees.forEach(emp => {
if (emp.today_attendance && emp.today_attendance.in_latitude && emp.today_attendance.in_longitude) {
var lat = parseFloat(emp.today_attendance.in_latitude);
var lng = parseFloat(emp.today_attendance.in_longitude);
// Parse Location and Image
var inLoc = emp.today_attendance.in_location || '';
var parts = inLoc.split(' [Img: ');
var address = parts[0];
var imgRaw = parts.length > 1 ? parts[1].replace(']', '') : null;
var imgPath = imgRaw ? '/storage/' + imgRaw : null;
// Working Hours Calculation
var wh = parseFloat(emp.today_attendance.working_hours) || 0;
var hrs = Math.floor(wh);
var mins = Math.round((wh - hrs) * 60);
var workDuration = wh > 0 ? hrs + 'h ' + mins + 'm' : '-';
// Status
var statusBadge = '';
if (emp.today_attendance) {
statusBadge = '<span class="badge badge-light-success fs-9">Working</span>';
}
var initials = emp.fullname.split(' ').map(function (n) { return n[0]; }).join('').substring(0, 2).toUpperCase();
var markerHtml =
'<div style="background-color: #009ef7; color: white; border-radius: 50%; width: 35px; height: 35px; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 12px; border: 2px solid white; box-shadow: 0 3px 6px rgba(0,0,0,0.3);">' +
initials +
'</div>';
var icon = L.divIcon({
html: markerHtml,
className: '',
iconSize: [35, 35],
iconAnchor: [17, 17],
popupAnchor: [0, -17]
});
// Card-style Popup
var cardImage = imgPath ? '<div class="card-img-top" style="height: 120px; background-image: url(\'' + imgPath + '\'); background-size: cover; background-position: center; border-radius: 8px 8px 0 0;"></div>' : '';
var popupHtml =
'<div class="card shadow-none border-0" style="width: 200px;">' +
cardImage +
'<div class="card-body p-3">' +
'<div class="d-flex justify-content-between align-items-center mb-2">' +
'<h6 class="fw-bolder m-0 text-dark">' + emp.fullname + '</h6>' +
statusBadge +
'</div>' +
'<div class="d-flex flex-column text-gray-600 fs-8 gap-1">' +
'<div class="d-flex justify-content-between">' +
'<span>Check-In:</span>' +
'<span class="fw-bold text-dark">' + emp.today_attendance.attendance_time + '</span>' +
'</div>' +
'<div class="d-flex justify-content-between">' +
'<span>Check-Out:</span>' +
'<span class="fw-bold text-dark">' + (emp.today_attendance.attendance_out_time || '-') + '</span>' +
'</div>' +
'<div class="d-flex justify-content-between">' +
'<span>Work Hrs:</span>' +
'<span class="fw-bold text-success">' + workDuration + '</span>' +
'</div>' +
'</div>' +
'<div class="separator separator-dashed my-2"></div>' +
'<div class="d-flex align-items-start">' +
'<i class="fas fa-map-marker-alt text-danger me-2 mt-1"></i>' +
'<div class="text-gray-500 fs-9 lh-sm">' + address + '</div>' +
'</div>' +
'</div>' +
'</div>';
var marker = L.marker([lat, lng], { icon: icon }).addTo(map);
marker.on('click', function (e) {
map.flyTo(e.latlng, 18);
})
.bindPopup(popupHtml, {
minWidth: 200,
maxWidth: 220,
className: 'custom-popup'
});
// Store marker reference
window.siteMarkers[emp.user_id] = marker;
}
});
}
};
window.focusOnMapLocation = function (id, lat, lng) {
// Scroll to map container
document.getElementById('map-container').scrollIntoView({ behavior: 'smooth', block: 'center' });
if (window.siteMapInstance) {
window.siteMapInstance.flyTo([lat, lng], 18);
}
// Open Popup
if (window.siteMarkers && window.siteMarkers[id]) {
// Small delay to allow flyTo to start/finish or just cleaner UI
setTimeout(() => {
window.siteMarkers[id].openPopup();
}, 300);
}
};
</script>
</x-slot>
</x-app-layout>