import { json } from "@remix-run/node"; import { useLoaderData } from "@remix-run/react"; import { useState, useEffect } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { Progress } from "~/components/ui/progress"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"; import { ScrollArea } from "~/components/ui/scroll-area"; import { Separator } from "~/components/ui/separator"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "~/components/ui/alert-dialog"; import { Truck, MapPin, Clock, CheckCircle, AlertTriangle, XCircle, RotateCcw, Users, Target, TrendingUp, Calendar, RefreshCw, Phone, MessageSquare, Plus, Edit, Trash2, Navigation, Package, Timer, Zap } from "lucide-react"; // Interfaces interface CollectionArea { id: string; name: string; zone: string; priority: "high" | "medium" | "low"; status: "pending" | "in-progress" | "completed" | "overdue" | "cancelled"; scheduledTime: string; estimatedDuration: number; // minutes actualStartTime?: string; completedTime?: string; assignedTruck: string; assignedDriver: string; driverContact: string; estimatedVolume: number; // kg actualVolume?: number; // kg households: number; lastCollection: string; notes?: string; coordinates: { lat: number; lng: number }; urgentIssues?: string[]; } interface DailyStats { totalAreas: number; completedAreas: number; inProgressAreas: number; overdueAreas: number; totalTargetVolume: number; collectedVolume: number; activeTrucks: number; availableTrucks: number; estimatedCompletion: string; } interface TruckStatus { id: string; driver: string; contact: string; status: "active" | "available" | "maintenance" | "break"; currentLocation?: string; currentCapacity: number; // percentage assignedAreas: string[]; lastUpdate: string; } interface CollectionData { date: string; stats: DailyStats; areas: CollectionArea[]; trucks: TruckStatus[]; emergencyContacts: { supervisor: string; dispatcher: string; maintenance: string; }; } export const loader = async (): Promise => { // Mock data - dalam implementasi nyata, ambil dari database const today = new Date().toISOString().split("T")[0]; const collectionData: CollectionData = { date: today, stats: { totalAreas: 18, completedAreas: 12, inProgressAreas: 4, overdueAreas: 2, totalTargetVolume: 4500, collectedVolume: 3200, activeTrucks: 6, availableTrucks: 2, estimatedCompletion: "16:30" }, areas: [ { id: "area-001", name: "Kelurahan Merdeka Blok A", zone: "Zona Utara", priority: "high", status: "overdue", scheduledTime: "07:00", estimatedDuration: 90, assignedTruck: "B-001", assignedDriver: "Budi Santoso", driverContact: "081234567890", estimatedVolume: 280, actualVolume: 245, households: 120, lastCollection: "2025-07-05", coordinates: { lat: -6.2088, lng: 106.8456 }, urgentIssues: ["Jalan rusak", "Akses terbatas"], actualStartTime: "07:15", completedTime: "09:00" }, { id: "area-002", name: "Komplek Permata Indah", zone: "Zona Selatan", priority: "medium", status: "in-progress", scheduledTime: "08:30", estimatedDuration: 60, assignedTruck: "B-003", assignedDriver: "Sari Dewi", driverContact: "081234567891", estimatedVolume: 180, households: 85, lastCollection: "2025-07-05", coordinates: { lat: -6.22, lng: 106.83 }, actualStartTime: "08:45" }, { id: "area-003", name: "Jl. Sudirman Raya", zone: "Zona Tengah", priority: "high", status: "overdue", scheduledTime: "09:00", estimatedDuration: 120, assignedTruck: "B-004", assignedDriver: "Dedi Kurniawan", driverContact: "081234567892", estimatedVolume: 350, households: 200, lastCollection: "2025-07-04", coordinates: { lat: -6.215, lng: 106.84 }, urgentIssues: ["Volume sangat tinggi", "Kemacetan akses"], notes: "Perlu 2 trip untuk mengangkut semua" }, { id: "area-004", name: "Perumahan Indah Permai", zone: "Zona Timur", priority: "medium", status: "completed", scheduledTime: "10:00", estimatedDuration: 75, assignedTruck: "B-002", assignedDriver: "Andi Wijaya", driverContact: "081234567893", estimatedVolume: 200, actualVolume: 195, households: 95, lastCollection: "2025-07-05", coordinates: { lat: -6.195, lng: 106.86 }, actualStartTime: "10:15", completedTime: "11:30" }, { id: "area-005", name: "Pasar Tradisional Sentral", zone: "Zona Tengah", priority: "high", status: "in-progress", scheduledTime: "11:30", estimatedDuration: 45, assignedTruck: "B-005", assignedDriver: "Rini Astuti", driverContact: "081234567894", estimatedVolume: 420, households: 50, lastCollection: "2025-07-05", coordinates: { lat: -6.21, lng: 106.835 }, actualStartTime: "11:45", notes: "Sampah organik tinggi dari pasar" }, { id: "area-006", name: "Cluster Villa Harmoni", zone: "Zona Barat", priority: "low", status: "pending", scheduledTime: "13:00", estimatedDuration: 60, assignedTruck: "B-006", assignedDriver: "Toni Setiawan", driverContact: "081234567895", estimatedVolume: 150, households: 75, lastCollection: "2025-07-05", coordinates: { lat: -6.205, lng: 106.82 } } ], trucks: [ { id: "B-001", driver: "Budi Santoso", contact: "081234567890", status: "active", currentLocation: "Kelurahan Merdeka", currentCapacity: 85, assignedAreas: ["area-001"], lastUpdate: "10 menit yang lalu" }, { id: "B-002", driver: "Andi Wijaya", contact: "081234567893", status: "available", currentLocation: "Pool Kendaraan", currentCapacity: 0, assignedAreas: ["area-004"], lastUpdate: "5 menit yang lalu" }, { id: "B-003", driver: "Sari Dewi", contact: "081234567891", status: "active", currentLocation: "Komplek Permata", currentCapacity: 60, assignedAreas: ["area-002"], lastUpdate: "2 menit yang lalu" }, { id: "B-004", driver: "Dedi Kurniawan", contact: "081234567892", status: "active", currentLocation: "Dalam perjalanan", currentCapacity: 0, assignedAreas: ["area-003"], lastUpdate: "15 menit yang lalu" }, { id: "B-005", driver: "Rini Astuti", contact: "081234567894", status: "active", currentLocation: "Pasar Sentral", currentCapacity: 40, assignedAreas: ["area-005"], lastUpdate: "1 menit yang lalu" }, { id: "B-006", driver: "Toni Setiawan", contact: "081234567895", status: "available", currentLocation: "Pool Kendaraan", currentCapacity: 0, assignedAreas: ["area-006"], lastUpdate: "30 menit yang lalu" } ], emergencyContacts: { supervisor: "081234560001", dispatcher: "081234560002", maintenance: "081234560003" } }; return json(collectionData); }; export default function PengumpulanHarian() { const data = useLoaderData(); const [selectedArea, setSelectedArea] = useState(null); const [viewMode, setViewMode] = useState<"list" | "map">("list"); const [filterStatus, setFilterStatus] = useState("all"); const [filterPriority, setFilterPriority] = useState("all"); // Filter areas const filteredAreas = data.areas.filter((area) => { const statusMatch = filterStatus === "all" || area.status === filterStatus; const priorityMatch = filterPriority === "all" || area.priority === filterPriority; return statusMatch && priorityMatch; }); // Calculate progress percentage const progressPercentage = Math.round( (data.stats.completedAreas / data.stats.totalAreas) * 100 ); const volumePercentage = Math.round( (data.stats.collectedVolume / data.stats.totalTargetVolume) * 100 ); const getStatusBadge = (status: string) => { switch (status) { case "completed": return ( Selesai ); case "in-progress": return ( Berlangsung ); case "pending": return Menunggu; case "overdue": return Terlambat; case "cancelled": return ( Dibatalkan ); default: return {status}; } }; const getPriorityBadge = (priority: string) => { switch (priority) { case "high": return Tinggi; case "medium": return ( Sedang ); case "low": return Rendah; default: return {priority}; } }; const getStatusIcon = (status: string) => { switch (status) { case "completed": return ; case "in-progress": return ; case "pending": return ; case "overdue": return ; case "cancelled": return ; default: return ; } }; const getTruckStatusBadge = (status: string) => { switch (status) { case "active": return ( Aktif ); case "available": return Tersedia; case "maintenance": return ( Maintenance ); case "break": return Istirahat; default: return {status}; } }; return (
{/* Header */}

Pengumpulan Harian URGENT

Monitoring dan koordinasi pengumpulan sampah -{" "} {new Date(data.date).toLocaleDateString("id-ID", { weekday: "long", year: "numeric", month: "long", day: "numeric" })}

Emergency Response Hubungi kontak darurat untuk situasi mendesak

Supervisor

{data.emergencyContacts.supervisor}

Dispatcher

{data.emergencyContacts.dispatcher}

Maintenance

{data.emergencyContacts.maintenance}

Tutup
{/* Stats Cards */}
Progress Harian
{data.stats.completedAreas}/{data.stats.totalAreas}

{progressPercentage}% area selesai • Est. selesai{" "} {data.stats.estimatedCompletion}

Volume Terkumpul
{data.stats.collectedVolume.toLocaleString()} kg

{volumePercentage}% dari target ( {data.stats.totalTargetVolume.toLocaleString()} kg)

Status Truk
{data.stats.activeTrucks}/ {data.stats.activeTrucks + data.stats.availableTrucks}
{data.stats.activeTrucks} Aktif
{data.stats.availableTrucks} Tersedia
Area Terlambat
{data.stats.overdueAreas}
Perlu penanganan segera
{data.stats.inProgressAreas} area sedang dikerjakan
{/* Filters and View Toggle */}
{/* Main Content */} Area Pengumpulan Status Truk Timeline Hari Ini
{filteredAreas.map((area) => (
{getStatusIcon(area.status)}
{area.name} {area.zone} {area.households} rumah Truk {area.assignedTruck}
{getPriorityBadge(area.priority)} {getStatusBadge(area.status)}
Jadwal & Progress
Target: {area.scheduledTime}
{area.actualStartTime && (
Mulai: {area.actualStartTime}
)} {area.completedTime && (
Selesai: {area.completedTime}
)}
{area.actualVolume ? `${area.actualVolume} kg` : `Est. ${area.estimatedVolume} kg`}
Driver & Kontak
{area.assignedDriver}
{area.driverContact}
Truk {area.assignedTruck}
Actions
Update Status - {area.name} Update status pengumpulan dan informasi terkait
{area.urgentIssues && area.urgentIssues.length > 0 && (
Isu Mendesak:
    {area.urgentIssues.map((issue, index) => (
  • • {issue}
  • ))}
)} {area.notes && (
Catatan: {area.notes}
)}
))}
{data.trucks.map((truck) => (
Truk {truck.id} {truck.driver}
{getTruckStatusBadge(truck.status)}
Kapasitas: {truck.currentCapacity}%
{truck.currentLocation}
Update: {truck.lastUpdate}
))}
Timeline Pengumpulan Hari Ini Jadwal dan progress pengumpulan sampah realtime
{data.areas .sort((a, b) => a.scheduledTime.localeCompare(b.scheduledTime) ) .map((area, index) => (
{getStatusIcon(area.status)}

{area.name}

{getPriorityBadge(area.priority)} {getStatusBadge(area.status)}
{area.scheduledTime} • {area.assignedDriver} • Truk{" "} {area.assignedTruck}
{area.actualStartTime && (
Dimulai: {area.actualStartTime}
)} {area.completedTime && (
Selesai: {area.completedTime} • Volume:{" "} {area.actualVolume} kg
)}
))}
); }