import React, { useState, useEffect } from 'react'; import { MapPin, CheckCircle, XCircle, AlertTriangle, School, Navigation, Clock, Loader2 } from 'lucide-react'; export default function App() { // GANTIKAN URL INI dengan URL Web App dari Google Apps Script anda const WEB_APP_URL = "https://script.google.com/macros/s/AKfycbxbWzoppgxYxstsBUkmPAfc1J0G31bUwncQtt_JvIzgPExjFm19qlisXlLbCmVCIvXt/exec"; const [schoolLocation, setSchoolLocation] = useState(null); const [allowedRadius] = useState(100); const [attendanceStatus, setAttendanceStatus] = useState('idle'); const [message, setMessage] = useState(''); const [staffList] = useState(['Cg Vironica', 'Ahmad Abu', 'Siti Aminah', 'Muthu Kumar', 'Tan Ah Chong']); const [selectedStaff, setSelectedStaff] = useState(''); const [records, setRecords] = useState([]); const [isSaving, setIsSaving] = useState(false); // Ambil lokasi sekolah dari localStorage jika ada useEffect(() => { const savedLoc = localStorage.getItem('school_loc'); if (savedLoc) setSchoolLocation(JSON.parse(savedLoc)); }, []); const getDistanceInMeters = (lat1, lon1, lat2, lon2) => { const R = 6371e3; const dLat = (lat2 - lat1) * Math.PI / 180; const dLon = (lon2 - lon1) * Math.PI / 180; const a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon/2) * Math.sin(dLon/2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); return R * c; }; const handleSetSchoolLocation = () => { navigator.geolocation.getCurrentPosition((pos) => { const loc = { lat: pos.coords.latitude, lng: pos.coords.longitude }; setSchoolLocation(loc); localStorage.setItem('school_loc', JSON.stringify(loc)); alert("Lokasi sekolah telah dikunci!"); }, (err) => alert(err.message), { enableHighAccuracy: true }); }; const submitToBackend = async (data) => { setIsSaving(true); try { await fetch(WEB_APP_URL, { method: 'POST', mode: 'no-cors', // Penting untuk Google Apps Script headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); setRecords(prev => [data, ...prev]); } catch (e) { console.error("Gagal simpan ke Cloud:", e); } finally { setIsSaving(false); } }; const handleCheckIn = () => { if (!schoolLocation || !selectedStaff) return; setAttendanceStatus('loading'); setMessage('Menjejak koordinat GPS...'); navigator.geolocation.getCurrentPosition((pos) => { const dist = getDistanceInMeters(schoolLocation.lat, schoolLocation.lng, pos.coords.latitude, pos.coords.longitude); const isPresent = dist <= allowedRadius; const newRecord = { name: selectedStaff, time: new Date().toLocaleTimeString(), distance: dist.toFixed(1), status: isPresent ? 'Hadir' : 'Luar Kawasan', lat: pos.coords.latitude, lng: pos.coords.longitude }; if (isPresent) { setAttendanceStatus('success'); setMessage(`Berjaya! Anda berada di sekolah.`); } else { setAttendanceStatus('failed'); setMessage(`Gagal. Jarak anda ${dist.toFixed(0)}m dari sekolah.`); } submitToBackend(newRecord); }, (err) => { setAttendanceStatus('error'); setMessage(err.message); }, { enableHighAccuracy: true }); }; return (
{/* HEADER NEON */}

GEO-HADIR

Live Cloud
{/* ADMIN CONFIG */}

Konfigurasi Sekolah

{schoolLocation && (
LAT: {schoolLocation.lat.toFixed(4)} LNG: {schoolLocation.lng.toFixed(4)}
)}
{/* ATTENDANCE CARD */}
{attendanceStatus !== 'idle' && (

{message}

)}
{/* LOG HISTORY */} {records.length > 0 && (

Log Kehadiran Awan

{records.map((r, i) => (
{r.status === 'Hadir' ? : }

{r.name}

{r.status} • {r.distance}m

{r.time}
))}
)}
); }