Compare commits
	
		
			6 Commits 
		
	
	
		
			b9431a67b1
			...
			9a3d8de8a6
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								 | 
						9a3d8de8a6 | |
| 
							
							
								 | 
						bed92f8ab0 | |
| 
							
							
								 | 
						a677ad959e | |
| 
							
							
								 | 
						c0e8aeccbc | |
| 
							
							
								 | 
						5ef8ff38e8 | |
| 
							
							
								 | 
						6080584ac5 | 
| 
						 | 
					@ -25,4 +25,11 @@ public class AdminController : Controller
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return View("~/Views/Admin/Transport/SpjAdmin/History/Index.cshtml");
 | 
					        return View("~/Views/Admin/Transport/SpjAdmin/History/Index.cshtml");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [HttpGet("history/details/{id}")]
 | 
				
			||||||
 | 
					    public IActionResult Details(int id)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ViewData["Id"] = id;
 | 
				
			||||||
 | 
					        return View("~/Views/Admin/Transport/SpjAdmin/History/Details.cshtml");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,8 +23,8 @@
 | 
				
			||||||
                    <i class="w-5 h-5 text-orange-600" data-lucide="file-text"></i>
 | 
					                    <i class="w-5 h-5 text-orange-600" data-lucide="file-text"></i>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div>
 | 
					                <div>
 | 
				
			||||||
                    <h2 class="text-lg font-bold text-gray-900">Data SPJ</h2>
 | 
					                    <h2 class="text-lg font-bold text-gray-900">Muhammad Yusuf</h2>
 | 
				
			||||||
                    <p class="text-xs text-gray-500">Surat Perintah Jalan</p>
 | 
					                    <p class="text-xs text-gray-500">Data SPJ</p>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,10 +6,10 @@
 | 
				
			||||||
<div class="max-w-sm mx-auto bg-gray-50 min-h-screen">
 | 
					<div class="max-w-sm mx-auto bg-gray-50 min-h-screen">
 | 
				
			||||||
    <div class="bg-gradient-to-r from-orange-500 to-orange-600 text-white px-4 py-4 sticky top-0 z-10 shadow-lg">
 | 
					    <div class="bg-gradient-to-r from-orange-500 to-orange-600 text-white px-4 py-4 sticky top-0 z-10 shadow-lg">
 | 
				
			||||||
        <div class="flex items-center justify-between">
 | 
					        <div class="flex items-center justify-between">
 | 
				
			||||||
            <a href="@Url.Action("Index", "Home")" class="p-2 hover:bg-white/10 rounded-full transition-all duration-200">
 | 
					            <a href="@Url.Action("Index", "Admin")" class="p-2 hover:bg-white/10 rounded-full transition-all duration-200">
 | 
				
			||||||
                <i class="w-5 h-5" data-lucide="chevron-left"></i>
 | 
					                <i class="w-5 h-5" data-lucide="chevron-left"></i>
 | 
				
			||||||
            </a>
 | 
					            </a>
 | 
				
			||||||
            <h1 class="text-lg font-bold">Riwayat Perjalanan</h1>
 | 
					            <h1 class="text-lg font-bold">Riwayat SPJ</h1>
 | 
				
			||||||
            <div class="w-9"></div>
 | 
					            <div class="w-9"></div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,7 @@
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            new {
 | 
					            new {
 | 
				
			||||||
                Id = 1,
 | 
					                Id = 1,
 | 
				
			||||||
 | 
					                Nama = "Yusuf",
 | 
				
			||||||
                NoSpj = "SPJ/07-2025/PKM/000478",
 | 
					                NoSpj = "SPJ/07-2025/PKM/000478",
 | 
				
			||||||
                Plat = "B 5678 ABC",
 | 
					                Plat = "B 5678 ABC",
 | 
				
			||||||
                Kode = "JRC 007",
 | 
					                Kode = "JRC 007",
 | 
				
			||||||
| 
						 | 
					@ -29,6 +30,7 @@
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            new {
 | 
					            new {
 | 
				
			||||||
                Id = 2,
 | 
					                Id = 2,
 | 
				
			||||||
 | 
					                Nama = "Ahmad Rizki",
 | 
				
			||||||
                NoSpj = "SPJ/07-2025/PKM/000476",
 | 
					                NoSpj = "SPJ/07-2025/PKM/000476",
 | 
				
			||||||
                Plat = "B 9632 TOR",
 | 
					                Plat = "B 9632 TOR",
 | 
				
			||||||
                Kode = "JRC 005",
 | 
					                Kode = "JRC 005",
 | 
				
			||||||
| 
						 | 
					@ -39,6 +41,7 @@
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            new {
 | 
					            new {
 | 
				
			||||||
                Id = 3,
 | 
					                Id = 3,
 | 
				
			||||||
 | 
					                Nama = "Siti Aminah",
 | 
				
			||||||
                NoSpj = "SPJ/07-2025/PKM/000477",
 | 
					                NoSpj = "SPJ/07-2025/PKM/000477",
 | 
				
			||||||
                Plat = "B 1234 XYZ",
 | 
					                Plat = "B 1234 XYZ",
 | 
				
			||||||
                Kode = "JRC 006",
 | 
					                Kode = "JRC 006",
 | 
				
			||||||
| 
						 | 
					@ -49,6 +52,7 @@
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            new {
 | 
					            new {
 | 
				
			||||||
                Id = 4,
 | 
					                Id = 4,
 | 
				
			||||||
 | 
					                Nama = "Budi Santoso",
 | 
				
			||||||
                NoSpj = "SPJ/07-2025/PKM/000479",
 | 
					                NoSpj = "SPJ/07-2025/PKM/000479",
 | 
				
			||||||
                Plat = "B 9876 DEF",
 | 
					                Plat = "B 9876 DEF",
 | 
				
			||||||
                Kode = "JRC 008",
 | 
					                Kode = "JRC 008",
 | 
				
			||||||
| 
						 | 
					@ -59,6 +63,7 @@
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            new {
 | 
					            new {
 | 
				
			||||||
                Id = 5,
 | 
					                Id = 5,
 | 
				
			||||||
 | 
					                Nama = "Dewi Lestari",
 | 
				
			||||||
                NoSpj = "SPJ/07-2025/PKM/000480",
 | 
					                NoSpj = "SPJ/07-2025/PKM/000480",
 | 
				
			||||||
                Plat = "B 4321 GHI",
 | 
					                Plat = "B 4321 GHI",
 | 
				
			||||||
                Kode = "JRC 009",
 | 
					                Kode = "JRC 009",
 | 
				
			||||||
| 
						 | 
					@ -73,15 +78,15 @@
 | 
				
			||||||
    <div class="px-4 py-4 space-y-3">
 | 
					    <div class="px-4 py-4 space-y-3">
 | 
				
			||||||
        @foreach (var spj in spjList)
 | 
					        @foreach (var spj in spjList)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            <a href="@Url.Action("Details", "History", new { id = spj.Id })" class="block">
 | 
					            <a href="@Url.Action("Details", "Admin", new { id = spj.Id })" class="block">
 | 
				
			||||||
                <div class="bg-white rounded-2xl p-4 shadow-sm border border-gray-100 hover:shadow-lg hover:border-orange-200 transition-all duration-300 relative overflow-hidden">
 | 
					                <div class="bg-white rounded-2xl p-4 shadow-sm border border-gray-100 hover:shadow-lg hover:border-orange-200 transition-all duration-300 relative overflow-hidden">
 | 
				
			||||||
                <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-orange-400 to-orange-500"></div>
 | 
					                <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-orange-400 to-orange-500"></div>
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                <div class="flex items-start justify-between mb-3">
 | 
					                <div class="flex items-start justify-between mb-3">
 | 
				
			||||||
                    <div class="flex-1 min-w-0">
 | 
					                    <div class="flex-1 min-w-0">
 | 
				
			||||||
                        <div class="flex items-center gap-2 mb-1">
 | 
					                        <div class="flex items-center gap-2 mb-1">
 | 
				
			||||||
                            <div class="w-2 h-2 bg-orange-400 rounded-full"></div>
 | 
					                             <i class="w-4 h-4 text-green-600" data-lucide="user"></i>
 | 
				
			||||||
                            <span class="text-xs font-medium text-gray-500 uppercase tracking-wider">No. SPJ</span>
 | 
					                            <span class="text-xs font-medium text-gray-500 uppercase tracking-wider">@spj.Nama</span>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                        <div class="font-bold text-gray-900 text-sm">@spj.NoSpj</div>
 | 
					                        <div class="font-bold text-gray-900 text-sm">@spj.NoSpj</div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,220 +1,183 @@
 | 
				
			||||||
@{
 | 
					@{
 | 
				
			||||||
    Layout = "~/Views/Admin/Transport/SpjAdmin/Shared/_Layout.cshtml";
 | 
					    Layout = "~/Views/Admin/Transport/SpjAdmin/Shared/_Layout.cshtml";
 | 
				
			||||||
    ViewData["Title"] = "Home Page";
 | 
					    ViewData["Title"] = "Admin Dashboard";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 <div class="container max-w-sm mx-auto bg-white min-h-screen">
 | 
					<div class="container max-w-sm mx-auto bg-gradient-to-br from-gray-50 to-gray-100 min-h-screen relative overflow-hidden">
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
        <div class="absolute top-0 max-w-sm container mx-auto bg-orange-500 text-white rounded-br-[125px] h-[250px] flex flex-row justify-between items-start px-6 py-6 shadow-lg z-20">
 | 
					    <div class="relative z-10">
 | 
				
			||||||
            <div class="flex flex-col">
 | 
					        <div class="bg-gradient-to-br from-orange-500 via-orange-600 to-red-500 rounded-b-[2rem] shadow-2xl p-6 mb-6">
 | 
				
			||||||
                <h1 class="text-md font-bold leading-tight text-white">Bonny Agung Putra</h1>
 | 
					            
 | 
				
			||||||
                <p class="text-xs opacity-90 font-medium text-orange-100">Driver UPST</span></p>
 | 
					            
 | 
				
			||||||
                <div class="mt-5 flex items-center gap-2">
 | 
					            <div class="relative z-10 pt-8 pb-4">
 | 
				
			||||||
                    <i class="w-4 h-4 text-white" data-lucide="map-pin"></i>
 | 
					                
 | 
				
			||||||
                    <span class="text-sm opacity-90">Lokasi Anda:</span>
 | 
					                <div class="bg-white/10 backdrop-blur-sm rounded-2xl p-6 border border-white/20 shadow-lg">
 | 
				
			||||||
                </div>
 | 
					                    <div class="flex items-center justify-between">
 | 
				
			||||||
                <p id="userLocation" class="font-semibold text-xs tracking-wide cursor-pointer underline text-white hover:text-orange-200 transition">
 | 
					                        
 | 
				
			||||||
                    Mendeteksi lokasi...
 | 
					                        <div class="flex items-center space-x-4">
 | 
				
			||||||
                </p>
 | 
					                            <div class="relative">
 | 
				
			||||||
            </div>
 | 
					                                <div class="w-16 h-16 rounded-2xl bg-gradient-to-br from-white to-orange-100 p-1 shadow-lg">
 | 
				
			||||||
            <div class="flex items-center justify-center">
 | 
					                                    <div class="w-full h-full rounded-xl bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center shadow-inner">
 | 
				
			||||||
                <div class="relative flex flex-col items-center justify-center">
 | 
					                                        <i class="w-8 h-8 text-white" data-lucide="user"></i>
 | 
				
			||||||
                    <div class="w-12 h-12 rounded-full border-3 border-white overflow-hidden shadow-md flex items-center justify-center cursor-pointer group" id="profileMenuButton">
 | 
					                                    </div>
 | 
				
			||||||
                        <i class="w-8 h-8 text-white" data-lucide="user"></i>
 | 
					                                </div>
 | 
				
			||||||
                    </div>
 | 
					                                <div class="absolute -bottom-1 -right-1 w-5 h-5 bg-green-400 rounded-full border-2 border-white shadow-sm">
 | 
				
			||||||
                    <div id="profileMenuDropdown" class="absolute top-12 right-0 mt-2 w-32 bg-white rounded shadow-lg py-2 z-50 hidden">
 | 
					                                    <div class="w-full h-full bg-green-500 rounded-full animate-pulse"></div>
 | 
				
			||||||
                        <form method="post" asp-controller="Auth" asp-action="Logout">
 | 
					                                </div>
 | 
				
			||||||
                            <button type="submit" class="hover:cursor-pointer flex items-center gap-2 w-full text-left px-4 py-2 text-sm font-semibold text-red-600 hover:bg-red-50 transition rounded-md">
 | 
					                            </div>
 | 
				
			||||||
                                <i class="w-4 h-4" data-lucide="log-out"></i>
 | 
					
 | 
				
			||||||
                                Logout
 | 
					                            
 | 
				
			||||||
 | 
					                            <div class="flex flex-col">
 | 
				
			||||||
 | 
					                                <h1 class="text-xl font-bold text-white leading-tight">Yusuf</h1>
 | 
				
			||||||
 | 
					                                <div class="flex items-center space-x-2">
 | 
				
			||||||
 | 
					                                    <span class="bg-orange-700/30 text-orange-100 px-3 py-1 rounded-full text-sm font-base backdrop-blur-sm border border-orange-400/20">
 | 
				
			||||||
 | 
					                                        Administrator
 | 
				
			||||||
 | 
					                                    </span>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        <div class="relative">
 | 
				
			||||||
 | 
					                            <button id="profileMenuButton" class="w-12 h-12 rounded-xl bg-white/20 backdrop-blur-sm border border-white/30 flex items-center justify-center hover:bg-white/30 transition-all duration-300 group shadow-lg hover:shadow-xl hover:scale-105">
 | 
				
			||||||
 | 
					                                <i class="w-5 h-5 text-white group-hover:rotate-180 transition-transform duration-300" data-lucide="settings"></i>
 | 
				
			||||||
                            </button>
 | 
					                            </button>
 | 
				
			||||||
                        </form>
 | 
					                            
 | 
				
			||||||
 | 
					                            <div id="profileMenuDropdown" class="absolute top-14 right-0 w-48 bg-white rounded-xl shadow-2xl py-2 z-50 hidden border border-gray-100 overflow-hidden">
 | 
				
			||||||
 | 
					                                <div class="px-4 py-3 border-b border-gray-100">
 | 
				
			||||||
 | 
					                                    <p class="text-sm font-semibold text-gray-900">Yusuf</p>
 | 
				
			||||||
 | 
					                                    <p class="text-xs text-gray-500">Administrator</p>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                
 | 
				
			||||||
 | 
					                                <div class="py-1">
 | 
				
			||||||
 | 
					                                    <div class="border-t border-gray-100 mt-1"></div>
 | 
				
			||||||
 | 
					                                    <form method="post" asp-controller="Auth" asp-action="Logout">
 | 
				
			||||||
 | 
					                                        <button type="submit" class="flex items-center gap-3 w-full text-left px-4 py-2 text-sm font-semibold text-red-600 hover:bg-red-50 transition-colors">
 | 
				
			||||||
 | 
					                                            <i class="w-4 h-4 text-red-500" data-lucide="log-out"></i>
 | 
				
			||||||
 | 
					                                            <span>Logout</span>
 | 
				
			||||||
 | 
					                                        </button>
 | 
				
			||||||
 | 
					                                    </form>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
 | 
					                <div class="mt-4 text-center">
 | 
				
			||||||
 | 
					                    <h2 class="text-white/90 text-lg font-semibold">Selamat Datang!</h2>
 | 
				
			||||||
 | 
					                    <p class="text-orange-100/70 text-sm">Siap kelola sistem eSPJ dengan efisien</p>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        <div class="bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100 mx-4 px-6 py-12 mt-40 rounded-3xl relative overflow-hidden shadow-2xl z-21 border border-slate-200">
 | 
					        <div class="px-6 mb-6">
 | 
				
			||||||
            <div class="absolute top-0 left-0 w-full h-full pointer-events-none">
 | 
					            <div class="grid grid-cols-2 gap-4">
 | 
				
			||||||
                <div class="absolute top-4 right-8 w-6 h-6 bg-orange-300 rounded-full opacity-60 animate-bounce" style="animation-delay: 0.5s;"></div>
 | 
					                <div class="bg-white rounded-xl p-4 shadow-sm border border-gray-100 hover:shadow-md transition-shadow">
 | 
				
			||||||
                <div class="absolute top-12 left-12 w-4 h-4 bg-blue-400 rounded-full opacity-40 animate-pulse" style="animation-delay: 1s;"></div>
 | 
					                    <div class="flex items-center justify-between">
 | 
				
			||||||
                <div class="absolute bottom-8 left-8 w-5 h-5 bg-indigo-300 rounded-full opacity-50 animate-bounce" style="animation-delay: 1.5s;"></div>
 | 
					                        <div>
 | 
				
			||||||
                <div class="absolute bottom-16 right-16 w-3 h-3 bg-slate-400 rounded-full opacity-30 animate-pulse" style="animation-delay: 2s;"></div>
 | 
					                            <p class="text-gray-500 text-xs font-medium">Total Driver</p>
 | 
				
			||||||
            </div>
 | 
					                            <p class="text-2xl font-bold text-gray-900">10</p>
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            <div class="relative z-10 text-center">
 | 
					 | 
				
			||||||
                <div class="w-24 h-24 mx-auto mb-6 relative">
 | 
					 | 
				
			||||||
                    <div class="w-full h-full bg-gradient-to-br from-orange-400 to-orange-600 rounded-full flex items-center justify-center shadow-xl animate-pulse">
 | 
					 | 
				
			||||||
                        <div class="w-20 h-20 bg-white rounded-full flex items-center justify-center">
 | 
					 | 
				
			||||||
                            <i class="w-10 h-10 text-orange-500" data-lucide="clipboard-list"></i>
 | 
					 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                        <div class="w-10 h-10 rounded-lg bg-green-100 flex items-center justify-center">
 | 
				
			||||||
                    <div class="absolute inset-0 border-4 border-orange-300 rounded-full animate-ping opacity-30"></div>
 | 
					                            <i class="w-5 h-5 text-green-600" data-lucide="users"></i>
 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                <div class="space-y-4">
 | 
					 | 
				
			||||||
                    <h2 class="text-xl font-bold bg-gradient-to-r from-slate-700 to-slate-900 bg-clip-text text-transparent">
 | 
					 | 
				
			||||||
                        Belum Ada SPJ
 | 
					 | 
				
			||||||
                    </h2>
 | 
					 | 
				
			||||||
                    <p class="text-sm text-slate-600 leading-relaxed px-4 mb-2">
 | 
					 | 
				
			||||||
                        Anda belum memiliki <span class="font-semibold text-orange-600">Surat Perintah Jalan</span> yang aktif saat ini.
 | 
					 | 
				
			||||||
                    </p>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <div class="bg-white/70 backdrop-blur-sm border border-white/30 rounded-2xl p-5 mb-6 text-left">
 | 
					 | 
				
			||||||
                    <div class="space-y-2 text-xs text-slate-600">
 | 
					 | 
				
			||||||
                        <div class="flex items-start gap-2">
 | 
					 | 
				
			||||||
                            <div class="w-1 h-1 bg-orange-400 rounded-full mt-1.5 flex-shrink-0"></div>
 | 
					 | 
				
			||||||
                            <p>SPJ akan diterbitkan oleh admin sesuai jadwal kerja</p>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                        <div class="flex items-start gap-2">
 | 
					 | 
				
			||||||
                            <div class="w-1 h-1 bg-orange-400 rounded-full mt-1.5 flex-shrink-0"></div>
 | 
					 | 
				
			||||||
                            <p>Periksa koneksi internet dan aktifkan lokasi GPS</p>
 | 
					 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                <button id="refreshButton" class="bg-gradient-to-r from-orange-500 to-orange-600 hover:from-orange-600 hover:to-orange-700 text-white px-6 py-3 rounded-2xl text-sm font-bold shadow-lg hover:shadow-xl transition-all duration-300 flex items-center gap-2 mx-auto transform hover:scale-105">
 | 
					                <div class="bg-white rounded-xl p-4 shadow-sm border border-gray-100 hover:shadow-md transition-shadow">
 | 
				
			||||||
                    <i class="w-4 h-4" data-lucide="refresh-cw" id="refreshIcon"></i>
 | 
					                    <div class="flex items-center justify-between">
 | 
				
			||||||
                    <span id="refreshText">Refresh Halaman</span>
 | 
					                        <div>
 | 
				
			||||||
                </button>
 | 
					                            <p class="text-gray-500 text-xs font-medium">Total SPJ</p>
 | 
				
			||||||
 | 
					                            <p class="text-2xl font-bold text-gray-900">15</p>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <div class="w-10 h-10 rounded-lg bg-blue-100 flex items-center justify-center">
 | 
				
			||||||
 | 
					                            <i class="w-5 h-5 text-blue-600" data-lucide="map"></i>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        <partial name="~/Views/Admin/Transport/SpjAdmin/Shared/Components/_Navigation.cshtml" />
 | 
					 | 
				
			||||||
      
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    <partial name="~/Views/Admin/Transport/SpjAdmin/Shared/Components/_Navigation.cshtml" />
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<register-block dynamic-section="scripts" key="jsHomeAdmin">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<register-block dynamic-section="scripts" key="jsHomeKosong">
 | 
					 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
    @@keyframes float {
 | 
					    @@keyframes float {
 | 
				
			||||||
        0%, 100% { transform: translateY(0px); }
 | 
					        0%, 100% { transform: translateY(0) rotate(0deg); }
 | 
				
			||||||
        50% { transform: translateY(-10px); }
 | 
					        50% { transform: translateY(-10px) rotate(2deg); }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    @@keyframes shimmer {
 | 
					 | 
				
			||||||
        0% { background-position: -200px 0; }
 | 
					 | 
				
			||||||
        100% { background-position: calc(200px + 100%) 0; }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    .float-animation {
 | 
					    .float-animation {
 | 
				
			||||||
        animation: float 3s ease-in-out infinite;
 | 
					        animation: float 3s ease-in-out infinite;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    .shimmer {
 | 
					    .float-animation-delayed {
 | 
				
			||||||
        background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
 | 
					        animation: float 4s ease-in-out infinite;
 | 
				
			||||||
        background-size: 200px 100%;
 | 
					 | 
				
			||||||
        animation: shimmer 2s infinite;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    .refresh-spin {
 | 
					 | 
				
			||||||
        animation: spin 1s linear infinite;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    @@keyframes spin {
 | 
					 | 
				
			||||||
        from { transform: rotate(0deg); }
 | 
					 | 
				
			||||||
        to { transform: rotate(360deg); }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					 | 
				
			||||||
document.addEventListener("DOMContentLoaded", function () {
 | 
					 | 
				
			||||||
    const userLocationEl = document.getElementById("userLocation");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    function reverseGeocode(lat, lng) {
 | 
					 | 
				
			||||||
        fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`)
 | 
					 | 
				
			||||||
            .then(res => res.json())
 | 
					 | 
				
			||||||
            .then(data => {
 | 
					 | 
				
			||||||
                const address = data.display_name || `${lat}, ${lng}`;
 | 
					 | 
				
			||||||
                userLocationEl.textContent = address;
 | 
					 | 
				
			||||||
                localStorage.setItem("user_latitude", lat);
 | 
					 | 
				
			||||||
                localStorage.setItem("user_longitude", lng);
 | 
					 | 
				
			||||||
                localStorage.setItem("user_address", address);
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .catch(() => {
 | 
					 | 
				
			||||||
                userLocationEl.textContent = `${lat}, ${lng}`;
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    function getLocationUpdate() {
 | 
					 | 
				
			||||||
        if ("geolocation" in navigator) {
 | 
					 | 
				
			||||||
            userLocationEl.textContent = "Mendeteksi lokasi baru...";
 | 
					 | 
				
			||||||
            navigator.geolocation.getCurrentPosition(
 | 
					 | 
				
			||||||
                function (position) {
 | 
					 | 
				
			||||||
                    const lat = position.coords.latitude.toFixed(6);
 | 
					 | 
				
			||||||
                    const lng = position.coords.longitude.toFixed(6);
 | 
					 | 
				
			||||||
                    reverseGeocode(lat, lng);
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                function () {
 | 
					 | 
				
			||||||
                    userLocationEl.textContent = "Lokasi tidak diizinkan";
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            userLocationEl.textContent = "Browser tidak mendukung lokasi";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const savedAddress = localStorage.getItem("user_address");
 | 
					 | 
				
			||||||
    if (savedAddress) {
 | 
					 | 
				
			||||||
        userLocationEl.textContent = savedAddress;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        getLocationUpdate();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    userLocationEl.addEventListener("click", function () {
 | 
					 | 
				
			||||||
        getLocationUpdate();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const refreshBtn = document.getElementById('refreshButton');
 | 
					 | 
				
			||||||
    const refreshIcon = document.getElementById('refreshIcon');
 | 
					 | 
				
			||||||
    const refreshText = document.getElementById('refreshText');
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    if (refreshBtn) {
 | 
					 | 
				
			||||||
        refreshBtn.addEventListener("click", function() {
 | 
					 | 
				
			||||||
            refreshIcon.style.animation = 'spin 1s linear infinite';
 | 
					 | 
				
			||||||
            refreshText.textContent = 'Memuat...';
 | 
					 | 
				
			||||||
            refreshBtn.disabled = true;
 | 
					 | 
				
			||||||
            refreshBtn.style.opacity = '0.8';
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            setTimeout(() => {
 | 
					 | 
				
			||||||
                location.reload();
 | 
					 | 
				
			||||||
            }, 1000);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const mainIcon = document.querySelector('[data-lucide="clipboard-list"]');
 | 
					 | 
				
			||||||
    if (mainIcon) {
 | 
					 | 
				
			||||||
        mainIcon.closest('.w-24').classList.add('float-animation');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const mainCard = document.querySelector('.bg-gradient-to-br');
 | 
					 | 
				
			||||||
    if (mainCard) {
 | 
					 | 
				
			||||||
        mainCard.style.opacity = '0';
 | 
					 | 
				
			||||||
        mainCard.style.transform = 'translateY(20px)';
 | 
					 | 
				
			||||||
        mainCard.style.transition = 'all 0.6s ease-out';
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        setTimeout(() => {
 | 
					 | 
				
			||||||
            mainCard.style.opacity = '1';
 | 
					 | 
				
			||||||
            mainCard.style.transform = 'translateY(0)';
 | 
					 | 
				
			||||||
        }, 100);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
    document.addEventListener("DOMContentLoaded", function () {
 | 
					    document.addEventListener("DOMContentLoaded", function () {
 | 
				
			||||||
        const btn = document.getElementById("profileMenuButton");
 | 
					        const btn = document.getElementById("profileMenuButton");
 | 
				
			||||||
        const dropdown = document.getElementById("profileMenuDropdown");
 | 
					        const dropdown = document.getElementById("profileMenuDropdown");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        btn.addEventListener("click", function (e) {
 | 
					        if (btn && dropdown) {
 | 
				
			||||||
            e.stopPropagation();
 | 
					            btn.addEventListener("click", function (e) {
 | 
				
			||||||
            dropdown.classList.toggle("hidden");
 | 
					                e.stopPropagation();
 | 
				
			||||||
        });
 | 
					                e.preventDefault();
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
        document.addEventListener("click", function () {
 | 
					                if (dropdown.classList.contains("hidden")) {
 | 
				
			||||||
            dropdown.classList.add("hidden");
 | 
					                    dropdown.classList.remove("hidden");
 | 
				
			||||||
 | 
					                    dropdown.style.opacity = "0";
 | 
				
			||||||
 | 
					                    dropdown.style.transform = "translateY(-10px) scale(0.95)";
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    setTimeout(() => {
 | 
				
			||||||
 | 
					                        dropdown.style.transition = "all 0.2s ease-out";
 | 
				
			||||||
 | 
					                        dropdown.style.opacity = "1";
 | 
				
			||||||
 | 
					                        dropdown.style.transform = "translateY(0) scale(1)";
 | 
				
			||||||
 | 
					                    }, 10);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    dropdown.style.transition = "all 0.15s ease-in";
 | 
				
			||||||
 | 
					                    dropdown.style.opacity = "0";
 | 
				
			||||||
 | 
					                    dropdown.style.transform = "translateY(-10px) scale(0.95)";
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    setTimeout(() => {
 | 
				
			||||||
 | 
					                        dropdown.classList.add("hidden");
 | 
				
			||||||
 | 
					                    }, 150);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            document.addEventListener("click", function (e) {
 | 
				
			||||||
 | 
					                if (!btn.contains(e.target) && !dropdown.contains(e.target)) {
 | 
				
			||||||
 | 
					                    dropdown.style.transition = "all 0.15s ease-in";
 | 
				
			||||||
 | 
					                    dropdown.style.opacity = "0";
 | 
				
			||||||
 | 
					                    dropdown.style.transform = "translateY(-10px) scale(0.95)";
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    setTimeout(() => {
 | 
				
			||||||
 | 
					                        dropdown.classList.add("hidden");
 | 
				
			||||||
 | 
					                    }, 150);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            dropdown.addEventListener("click", function (e) {
 | 
				
			||||||
 | 
					                e.stopPropagation();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const statCards = document.querySelectorAll('.grid > div');
 | 
				
			||||||
 | 
					        statCards.forEach((card, index) => {
 | 
				
			||||||
 | 
					            card.style.opacity = "0";
 | 
				
			||||||
 | 
					            card.style.transform = "translateY(20px)";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            setTimeout(() => {
 | 
				
			||||||
 | 
					                card.style.transition = "all 0.3s ease-out";
 | 
				
			||||||
 | 
					                card.style.opacity = "1";
 | 
				
			||||||
 | 
					                card.style.transform = "translateY(0)";
 | 
				
			||||||
 | 
					            }, 300 + (index * 100));
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -93,26 +93,12 @@
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <div class="bg-gray-50 border border-gray-200 rounded-lg p-3 text-sm">
 | 
					 | 
				
			||||||
                <div class="flex items-start">
 | 
					 | 
				
			||||||
                    <i class="w-4 h-4 text-gray-600 mr-2 mt-0.5" data-lucide="lightbulb"></i>
 | 
					 | 
				
			||||||
                    <div class="text-gray-700">
 | 
					 | 
				
			||||||
                        <p class="font-medium mb-1">Tips Scanning:</p>
 | 
					 | 
				
			||||||
                        <ul class="text-xs space-y-1">
 | 
					 | 
				
			||||||
                            <li>• Pastikan QR code dalam pencahayaan yang cukup</li>
 | 
					 | 
				
			||||||
                            <li>• Jaga jarak 15-30cm dari kamera</li>
 | 
					 | 
				
			||||||
                            <li>• Arahkan kamera secara tegak lurus ke QR code</li>
 | 
					 | 
				
			||||||
                            <li>• Pastikan QR code tidak buram atau rusak</li>
 | 
					 | 
				
			||||||
                            <li>• <strong>Klik "Izinkan/Allow" saat browser meminta akses kamera</strong></li>
 | 
					 | 
				
			||||||
                        </ul>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="border-t pt-4">
 | 
					        <div class="border-t pt-4">
 | 
				
			||||||
            <h3 class="text-gray-700 font-medium mb-3">Atau input manual:</h3>
 | 
					            <h3 class="text-gray-700 font-medium mb-3">Atau input manual:</h3>
 | 
				
			||||||
            <form id="manual-form" method="post" action="@Url.Action("ProcessScan", "Scan")">
 | 
					            <form id="manual-form" method="post" action="@Url.Action("ProcessScan", "Scan")">
 | 
				
			||||||
 | 
					                @Html.AntiForgeryToken()
 | 
				
			||||||
                <div class="flex gap-2">
 | 
					                <div class="flex gap-2">
 | 
				
			||||||
                    <input type="text" 
 | 
					                    <input type="text" 
 | 
				
			||||||
                           id="manual-barcode" 
 | 
					                           id="manual-barcode" 
 | 
				
			||||||
| 
						 | 
					@ -152,7 +138,23 @@
 | 
				
			||||||
    <partial name="~/Views/Admin/Transport/SpjAdmin/Shared/Components/_Navigation.cshtml" />
 | 
					    <partial name="~/Views/Admin/Transport/SpjAdmin/Shared/Components/_Navigation.cshtml" />
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div id="scan-modal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden flex items-center justify-center">
 | 
				
			||||||
 | 
					    <div class="bg-white py-4 rounded-2xl shadow-2xl max-w-sm w-full border border-gray-100">
 | 
				
			||||||
 | 
					        <div class="p-8 text-center">
 | 
				
			||||||
 | 
					            <div id="modal-icon" class="mx-auto mb-6">
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <h3 id="modal-title" class="text-xl font-bold mb-3 text-gray-800"></h3>
 | 
				
			||||||
 | 
					            <p id="modal-message" class="text-gray-600 mb-6 leading-relaxed"></p>
 | 
				
			||||||
 | 
					            <button id="modal-close" class="bg-orange-500 hover:bg-orange-600 text-white px-8 py-3 rounded-xl transition-all duration-200 font-medium shadow-lg hover:shadow-xl transform hover:-translate-y-0.5">
 | 
				
			||||||
 | 
					                OK
 | 
				
			||||||
 | 
					            </button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<register-block dynamic-section="scripts" key="jsScan">
 | 
					<register-block dynamic-section="scripts" key="jsScan">
 | 
				
			||||||
 | 
					    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
 | 
				
			||||||
    <script src="https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js" type="text/javascript"></script>
 | 
					    <script src="https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js" type="text/javascript"></script>
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    <script>
 | 
					    <script>
 | 
				
			||||||
| 
						 | 
					@ -196,6 +198,13 @@
 | 
				
			||||||
                this.confirmBtn.addEventListener('click', () => this.confirmScan());
 | 
					                this.confirmBtn.addEventListener('click', () => this.confirmScan());
 | 
				
			||||||
                this.retryBtn.addEventListener('click', () => this.retryScan());
 | 
					                this.retryBtn.addEventListener('click', () => this.retryScan());
 | 
				
			||||||
                this.manualForm.addEventListener('submit', (e) => this.handleManualSubmit(e));
 | 
					                this.manualForm.addEventListener('submit', (e) => this.handleManualSubmit(e));
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                $('#modal-close').on('click', () => this.hideModal());
 | 
				
			||||||
 | 
					                $('#scan-modal').on('click', (e) => {
 | 
				
			||||||
 | 
					                    if (e.target.id === 'scan-modal') {
 | 
				
			||||||
 | 
					                        this.hideModal();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            checkBrowserSupport() {
 | 
					            checkBrowserSupport() {
 | 
				
			||||||
| 
						 | 
					@ -324,15 +333,21 @@
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            handleBarcodeDetected(decodedText, decodedResult) {
 | 
					            handleBarcodeDetected(decodedText, decodedResult) {
 | 
				
			||||||
 | 
					                console.log(`QR Code terdeteksi: "${decodedText}"`);
 | 
				
			||||||
 | 
					                console.log(`Panjang kode: ${decodedText.length}`);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
                if (decodedText && decodedText.length >= 5) {
 | 
					                if (decodedText && decodedText.length >= 5) {
 | 
				
			||||||
 | 
					                    console.log(`✅ Kode valid, memproses: ${decodedText}`);
 | 
				
			||||||
                    this.flashSuccess();
 | 
					                    this.flashSuccess();
 | 
				
			||||||
 | 
					                    this.playSuccessSound();
 | 
				
			||||||
 | 
					                    this.vibrate();
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    this.detectedCode = decodedText;
 | 
					                    this.detectedCode = decodedText;
 | 
				
			||||||
                    this.showResult(decodedText);
 | 
					 | 
				
			||||||
                    this.stopScanner();
 | 
					                    this.stopScanner();
 | 
				
			||||||
                    this.playSuccessSound();
 | 
					 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    this.vibrate();
 | 
					                    this.processScanCode(decodedText);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    console.log(`❌ Kode terlalu pendek: ${decodedText}`);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -368,11 +383,98 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            confirmScan() {
 | 
					            confirmScan() {
 | 
				
			||||||
                if (this.detectedCode) {
 | 
					                if (this.detectedCode) {
 | 
				
			||||||
                    this.manualInput.value = this.detectedCode;
 | 
					                    this.processScanCode(this.detectedCode);
 | 
				
			||||||
                    this.manualForm.submit();
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            processScanCode(code) {
 | 
				
			||||||
 | 
					                this.showModal('loading', 'Memproses...', 'Sedang memverifikasi kode SPJ...', false);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Testing mode - uncomment kalau testing udah selesai
 | 
				
			||||||
 | 
					                this.mockResponse(code);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // ini bagian ajax yang asli
 | 
				
			||||||
 | 
					                /*
 | 
				
			||||||
 | 
					                $.ajax({
 | 
				
			||||||
 | 
					                    url: @Url.Action("ProcessScan", "Scan")', // nanti tinggal ganti aja yaa
 | 
				
			||||||
 | 
					                    type: 'POST',
 | 
				
			||||||
 | 
					                    data: {
 | 
				
			||||||
 | 
					                        barcode: code
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    success: (response) => {
 | 
				
			||||||
 | 
					                        if (response.success) {
 | 
				
			||||||
 | 
					                            this.showModal('success', 'Scan Berhasil!', 'SPJ berhasil ditemukan dan diproses.', true);
 | 
				
			||||||
 | 
					                            setTimeout(() => {
 | 
				
			||||||
 | 
					                                this.hideResult();
 | 
				
			||||||
 | 
					                                this.manualInput.value = '';
 | 
				
			||||||
 | 
					                            }, 2000);
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            this.showModal('error', 'SPJ Tidak Ditemukan', response.message || 'Kode SPJ tidak ditemukan dalam database.', true);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    error: (xhr, status, error) => {
 | 
				
			||||||
 | 
					                        let errorMessage = 'Terjadi kesalahan saat memproses scan.';
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        if (xhr.responseJSON && xhr.responseJSON.message) {
 | 
				
			||||||
 | 
					                            errorMessage = xhr.responseJSON.message;
 | 
				
			||||||
 | 
					                        } else if (xhr.status === 404) {
 | 
				
			||||||
 | 
					                            errorMessage = 'SPJ tidak ditemukan dalam database.';
 | 
				
			||||||
 | 
					                        } else if (xhr.status === 500) {
 | 
				
			||||||
 | 
					                            errorMessage = 'Terjadi kesalahan server. Silakan coba lagi.';
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        this.showModal('error', 'Error', errorMessage, true);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                */
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Mock response untuk testing
 | 
				
			||||||
 | 
					            mockResponse(code) {
 | 
				
			||||||
 | 
					                console.log(`Testing scan untuk kode: ${code}`);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                setTimeout(() => {
 | 
				
			||||||
 | 
					                    const validCodes = [
 | 
				
			||||||
 | 
					                        'SPJ001', 'SPJ002', 'SPJ003', 'SPJ004', 'SPJ005',
 | 
				
			||||||
 | 
					                        'TEST123', 'TEST456', 'TEST789',
 | 
				
			||||||
 | 
					                        '12345', '67890', '11111', '22222',
 | 
				
			||||||
 | 
					                        'ABCDEF', 'GHIJKL', 'MNOPQR'
 | 
				
			||||||
 | 
					                    ];
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    const isValid = validCodes.some(validCode => 
 | 
				
			||||||
 | 
					                        validCode.toLowerCase() === code.toLowerCase()
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if (isValid) {
 | 
				
			||||||
 | 
					                        console.log(`✅ Kode ${code} VALID - menampilkan success`);
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        // Format tanggal Indonesia
 | 
				
			||||||
 | 
					                        const now = new Date();
 | 
				
			||||||
 | 
					                        const tanggal = now.toLocaleDateString('id-ID', {
 | 
				
			||||||
 | 
					                            weekday: 'long',
 | 
				
			||||||
 | 
					                            year: 'numeric',
 | 
				
			||||||
 | 
					                            month: 'long',
 | 
				
			||||||
 | 
					                            day: 'numeric'
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        const waktu = now.toLocaleTimeString('id-ID', {
 | 
				
			||||||
 | 
					                            hour: '2-digit',
 | 
				
			||||||
 | 
					                            minute: '2-digit'
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        this.showSuccessModal(code, tanggal, waktu);
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        setTimeout(() => {
 | 
				
			||||||
 | 
					                            this.hideResult();
 | 
				
			||||||
 | 
					                            this.manualInput.value = '';
 | 
				
			||||||
 | 
					                        }, 3000);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        console.log(`❌ Kode ${code} TIDAK VALID - menampilkan error`);
 | 
				
			||||||
 | 
					                        this.showErrorModal(code);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }, 1000); 
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            async retryScan() {
 | 
					            async retryScan() {
 | 
				
			||||||
                this.hideResult();
 | 
					                this.hideResult();
 | 
				
			||||||
                this.hideError();
 | 
					                this.hideError();
 | 
				
			||||||
| 
						 | 
					@ -389,18 +491,20 @@
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            handleManualSubmit(e) {
 | 
					            handleManualSubmit(e) {
 | 
				
			||||||
 | 
					                e.preventDefault();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
                const code = this.manualInput.value.trim();
 | 
					                const code = this.manualInput.value.trim();
 | 
				
			||||||
                if (!code) {
 | 
					                if (!code) {
 | 
				
			||||||
                    e.preventDefault();
 | 
					                    this.showModal('error', 'Input Kosong', 'Silakan masukkan kode SPJ.', true);
 | 
				
			||||||
                    this.showError('Silakan masukkan kode SPJ.');
 | 
					 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                if (code.length < 5) {
 | 
					                if (code.length < 5) {
 | 
				
			||||||
                    e.preventDefault();
 | 
					                    this.showModal('error', 'Kode Tidak Valid', 'Kode SPJ minimal 5 karakter.', true);
 | 
				
			||||||
                    this.showError('Kode SPJ minimal 5 karakter.');
 | 
					 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                this.processScanCode(code);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            showLoading() {
 | 
					            showLoading() {
 | 
				
			||||||
| 
						 | 
					@ -454,9 +558,185 @@
 | 
				
			||||||
                } catch (e) {
 | 
					                } catch (e) {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            showModal(type, title, message, showCloseButton = true) {
 | 
				
			||||||
 | 
					                const iconHtml = {
 | 
				
			||||||
 | 
					                    'success': '<div class="w-20 h-20 mx-auto bg-green-100 rounded-full flex items-center justify-center shadow-lg"><svg class="w-10 h-10 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg></div>',
 | 
				
			||||||
 | 
					                    'error': '<div class="w-20 h-20 mx-auto bg-red-100 rounded-full flex items-center justify-center shadow-lg"><svg class="w-10 h-10 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg></div>',
 | 
				
			||||||
 | 
					                    'loading': '<div class="w-20 h-20 mx-auto bg-blue-100 rounded-full flex items-center justify-center shadow-lg"><div class="loading-spinner-modal"></div></div>'
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $('#modal-icon').html(iconHtml[type] || iconHtml['error']);
 | 
				
			||||||
 | 
					                $('#modal-title').text(title);
 | 
				
			||||||
 | 
					                $('#modal-message').text(message);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (showCloseButton) {
 | 
				
			||||||
 | 
					                    $('#modal-close').show();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $('#modal-close').hide();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                $('#scan-modal').removeClass('hidden');
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                $('#scan-modal').css('opacity', '0').animate({'opacity': '1'}, 300);
 | 
				
			||||||
 | 
					                $('#scan-modal .bg-white').css('transform', 'scale(0.8)').animate({
 | 
				
			||||||
 | 
					                    'transform': 'scale(1)'
 | 
				
			||||||
 | 
					                }, 300);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (!showCloseButton && type === 'loading') {
 | 
				
			||||||
 | 
					                    setTimeout(() => {
 | 
				
			||||||
 | 
					                        this.hideModal();
 | 
				
			||||||
 | 
					                    }, 3000);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            showSuccessModal(code, tanggal, waktu) {
 | 
				
			||||||
 | 
					                const successIcon = `
 | 
				
			||||||
 | 
					                    <div class="w-28 h-28 mx-auto bg-gradient-to-r from-green-400 to-emerald-500 rounded-full flex items-center justify-center shadow-xl mb-6 animate-pulse">
 | 
				
			||||||
 | 
					                        <svg class="w-16 h-16 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | 
				
			||||||
 | 
					                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"></path>
 | 
				
			||||||
 | 
					                        </svg>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                `;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                const successContent = `
 | 
				
			||||||
 | 
					                    <div class="bg-gradient-to-br from-green-50 via-emerald-50 to-teal-50 rounded-2xl p-6 border border-green-200 shadow-inner">
 | 
				
			||||||
 | 
					                        <div class="flex items-center justify-center mb-4">
 | 
				
			||||||
 | 
					                            <div class="bg-gradient-to-r from-green-500 to-emerald-600 text-white rounded-xl px-4 py-2 shadow-lg">
 | 
				
			||||||
 | 
					                                <span class="font-mono font-bold text-xl tracking-wider">${code}</span>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        <div class="space-y-4">
 | 
				
			||||||
 | 
					                            <div class="bg-white rounded-xl p-4 shadow-sm border border-green-100">
 | 
				
			||||||
 | 
					                                <div class="flex items-center justify-center">
 | 
				
			||||||
 | 
					                                    <div class="flex items-center space-x-3">
 | 
				
			||||||
 | 
					                                        <div class="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div>
 | 
				
			||||||
 | 
					                                        <span class="text-gray-700 font-semibold text-lg">Status Aktif</span>
 | 
				
			||||||
 | 
					                                        <div class="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            <div class="grid grid-cols-2 gap-3">
 | 
				
			||||||
 | 
					                                <div class="bg-white rounded-xl p-4 text-center shadow-sm border border-green-100">
 | 
				
			||||||
 | 
					                                    <div class="text-green-600 font-medium mb-2 text-sm">⏰ Waktu Scan</div>
 | 
				
			||||||
 | 
					                                    <div class="text-gray-800 font-bold text-lg">${waktu}</div>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                
 | 
				
			||||||
 | 
					                                <div class="bg-white rounded-xl p-4 text-center shadow-sm border border-green-100">
 | 
				
			||||||
 | 
					                                    <div class="text-green-600 font-medium mb-2 text-sm">📅 Tanggal</div>
 | 
				
			||||||
 | 
					                                    <div class="text-gray-800 font-bold text-sm leading-tight">${tanggal}</div>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $('#modal-icon').html(successIcon);
 | 
				
			||||||
 | 
					                $('#modal-title').html('<span class="text-2xl font-bold bg-gradient-to-r from-green-600 to-emerald-600 bg-clip-text text-transparent">🎉 Scan Berhasil!</span>');
 | 
				
			||||||
 | 
					                $('#modal-message').html(successContent);
 | 
				
			||||||
 | 
					                $('#modal-close').hide(); // Sembunyikan tombol
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                $('#scan-modal').removeClass('hidden');
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                $('#scan-modal').css('opacity', '0').animate({'opacity': '1'}, 300);
 | 
				
			||||||
 | 
					                $('#scan-modal .bg-white').css('transform', 'scale(0.8)').animate({
 | 
				
			||||||
 | 
					                    'transform': 'scale(1)'
 | 
				
			||||||
 | 
					                }, 300);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                setTimeout(() => {
 | 
				
			||||||
 | 
					                    this.hideModal();
 | 
				
			||||||
 | 
					                }, 2000);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            showErrorModal(code) {
 | 
				
			||||||
 | 
					                const errorIcon = `
 | 
				
			||||||
 | 
					                    <div class="w-28 h-28 mx-auto bg-gradient-to-r from-red-400 to-rose-500 rounded-full flex items-center justify-center shadow-xl mb-6 animate-pulse">
 | 
				
			||||||
 | 
					                        <svg class="w-16 h-16 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | 
				
			||||||
 | 
					                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M6 18L18 6M6 6l12 12"></path>
 | 
				
			||||||
 | 
					                        </svg>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                `;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                const errorContent = `
 | 
				
			||||||
 | 
					                    <div class="bg-gradient-to-br from-red-50 via-rose-50 to-pink-50 rounded-2xl p-6 border border-red-200 shadow-inner">
 | 
				
			||||||
 | 
					                        <div class="flex items-center justify-center mb-4">
 | 
				
			||||||
 | 
					                            <div class="bg-gradient-to-r from-red-500 to-rose-600 text-white rounded-xl px-4 py-2 shadow-lg">
 | 
				
			||||||
 | 
					                                <span class="font-mono font-bold text-xl tracking-wider">${code}</span>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        <div class="space-y-4">
 | 
				
			||||||
 | 
					                            <div class="bg-white rounded-xl p-4 shadow-sm border border-red-100">
 | 
				
			||||||
 | 
					                                <div class="text-center">
 | 
				
			||||||
 | 
					                                    <div class="flex items-center justify-center mb-2">
 | 
				
			||||||
 | 
					                                        <svg class="w-6 h-6 text-red-500 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | 
				
			||||||
 | 
					                                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
 | 
				
			||||||
 | 
					                                        </svg>
 | 
				
			||||||
 | 
					                                        <span class="text-red-700 font-bold text-lg">SPJ Tidak Ditemukan</span>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                    <div class="text-red-600 text-sm opacity-90 leading-relaxed">
 | 
				
			||||||
 | 
					                                        Kode yang Anda masukkan tidak terdaftar dalam sistem.<br>
 | 
				
			||||||
 | 
					                                        Silakan periksa kembali atau hubungi administrator.
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            <div class="bg-yellow-50 rounded-xl p-4 border border-yellow-200">
 | 
				
			||||||
 | 
					                                <div class="flex items-start">
 | 
				
			||||||
 | 
					                                    <svg class="w-5 h-5 text-yellow-600 mr-2 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | 
				
			||||||
 | 
					                                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
 | 
				
			||||||
 | 
					                                    </svg>
 | 
				
			||||||
 | 
					                                    <div class="text-yellow-800 text-sm">
 | 
				
			||||||
 | 
					                                        <div class="font-semibold mb-1">💡 Tips:</div>
 | 
				
			||||||
 | 
					                                        <div class="text-xs leading-relaxed">
 | 
				
			||||||
 | 
					                                            • Pastikan QR Code dalam kondisi baik<br>
 | 
				
			||||||
 | 
					                                            • Coba scan ulang dengan pencahayaan yang cukup<br>
 | 
				
			||||||
 | 
					                                            • Gunakan input manual jika diperlukan
 | 
				
			||||||
 | 
					                                        </div>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $('#modal-icon').html(errorIcon);
 | 
				
			||||||
 | 
					                $('#modal-title').html('<span class="text-xl font-bold bg-gradient-to-r from-red-600 to-rose-600 bg-clip-text text-transparent">❌ Scan Gagal</span>');
 | 
				
			||||||
 | 
					                $('#modal-message').html(errorContent);
 | 
				
			||||||
 | 
					                $('#modal-close').hide(); // Sembunyikan tombol
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                $('#scan-modal').removeClass('hidden');
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                $('#scan-modal').css('opacity', '0').animate({'opacity': '1'}, 300);
 | 
				
			||||||
 | 
					                $('#scan-modal .bg-white').css('transform', 'scale(0.8)').animate({
 | 
				
			||||||
 | 
					                    'transform': 'scale(1)'
 | 
				
			||||||
 | 
					                }, 300);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                setTimeout(() => {
 | 
				
			||||||
 | 
					                    this.hideModal();
 | 
				
			||||||
 | 
					                }, 2000);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            hideModal() {
 | 
				
			||||||
 | 
					                $('#scan-modal').animate({'opacity': '0'}, 200, function() {
 | 
				
			||||||
 | 
					                    $('#scan-modal').addClass('hidden');
 | 
				
			||||||
 | 
					                    $('#scan-modal').css('opacity', '');
 | 
				
			||||||
 | 
					                    $('#scan-modal .bg-white').css('transform', '');
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        document.addEventListener('DOMContentLoaded', function() {
 | 
					        document.addEventListener('DOMContentLoaded', function() {
 | 
				
			||||||
 | 
					            $.ajaxSetup({
 | 
				
			||||||
 | 
					                beforeSend: function(xhr, settings) {
 | 
				
			||||||
 | 
					                    if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
 | 
				
			||||||
 | 
					                        xhr.setRequestHeader("RequestVerificationToken", $('input[name="__RequestVerificationToken"]').val());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            function waitForLibrary() {
 | 
					            function waitForLibrary() {
 | 
				
			||||||
                if (typeof Html5Qrcode !== 'undefined') {
 | 
					                if (typeof Html5Qrcode !== 'undefined') {
 | 
				
			||||||
                    new BarcodeScanner();
 | 
					                    new BarcodeScanner();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,94 @@
 | 
				
			||||||
 | 
					/* Modal animations and enhancements */
 | 
				
			||||||
 | 
					#scan-modal {
 | 
				
			||||||
 | 
					  backdrop-filter: blur(8px);
 | 
				
			||||||
 | 
					  transition: all 0.3s ease;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#scan-modal .bg-white {
 | 
				
			||||||
 | 
					  transform-origin: center center;
 | 
				
			||||||
 | 
					  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Enhanced modal auto-hide animation */
 | 
				
			||||||
 | 
					.modal-auto-hide {
 | 
				
			||||||
 | 
					  animation: modalAutoHide 0.4s ease-in-out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes modalAutoHide {
 | 
				
			||||||
 | 
					  0% {
 | 
				
			||||||
 | 
					    opacity: 1;
 | 
				
			||||||
 | 
					    transform: scale(1);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  80% {
 | 
				
			||||||
 | 
					    opacity: 1;
 | 
				
			||||||
 | 
					    transform: scale(1.02);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  100% {
 | 
				
			||||||
 | 
					    opacity: 0;
 | 
				
			||||||
 | 
					    transform: scale(0.95);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Success modal specific animations */
 | 
				
			||||||
 | 
					.success-gradient-bg {
 | 
				
			||||||
 | 
					  background: linear-gradient(135deg, #10b981, #059669, #047857);
 | 
				
			||||||
 | 
					  animation: gradientShift 2s ease-in-out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes gradientShift {
 | 
				
			||||||
 | 
					  0%,
 | 
				
			||||||
 | 
					  100% {
 | 
				
			||||||
 | 
					    background-position: 0% 50%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  50% {
 | 
				
			||||||
 | 
					    background-position: 100% 50%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Error modal specific animations */
 | 
				
			||||||
 | 
					.error-gradient-bg {
 | 
				
			||||||
 | 
					  background: linear-gradient(135deg, #ef4444, #dc2626, #b91c1c);
 | 
				
			||||||
 | 
					  animation: gradientShift 2s ease-in-out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Enhanced pulse animation for icons */
 | 
				
			||||||
 | 
					.icon-pulse {
 | 
				
			||||||
 | 
					  animation: iconPulse 1.5s ease-in-out infinite;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes iconPulse {
 | 
				
			||||||
 | 
					  0%,
 | 
				
			||||||
 | 
					  100% {
 | 
				
			||||||
 | 
					    transform: scale(1);
 | 
				
			||||||
 | 
					    opacity: 1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  50% {
 | 
				
			||||||
 | 
					    transform: scale(1.1);
 | 
				
			||||||
 | 
					    opacity: 0.8;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Card shadow effects */
 | 
				
			||||||
 | 
					.card-shadow-success {
 | 
				
			||||||
 | 
					  box-shadow: 0 10px 25px rgba(16, 185, 129, 0.2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.card-shadow-error {
 | 
				
			||||||
 | 
					  box-shadow: 0 10px 25px rgba(239, 68, 68, 0.2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Responsive modal improvements */
 | 
				
			||||||
 | 
					@media (max-width: 640px) {
 | 
				
			||||||
 | 
					  #scan-modal .bg-white {
 | 
				
			||||||
 | 
					    margin: 16px;
 | 
				
			||||||
 | 
					    max-width: calc(100vw - 32px);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  #scan-modal .bg-white .space-y-4 > div {
 | 
				
			||||||
 | 
					    margin-bottom: 12px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.scanner-container {
 | 
					.scanner-container {
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  background: #1a1a1a;
 | 
					  background: #1a1a1a;
 | 
				
			||||||
| 
						 | 
					@ -86,6 +177,15 @@
 | 
				
			||||||
  animation: spin 1s linear infinite;
 | 
					  animation: spin 1s linear infinite;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loading-spinner-modal {
 | 
				
			||||||
 | 
					  border: 3px solid rgba(59, 130, 246, 0.3);
 | 
				
			||||||
 | 
					  border-top: 3px solid #3b82f6;
 | 
				
			||||||
 | 
					  border-radius: 50%;
 | 
				
			||||||
 | 
					  width: 24px;
 | 
				
			||||||
 | 
					  height: 24px;
 | 
				
			||||||
 | 
					  animation: spin 1s linear infinite;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@keyframes spin {
 | 
					@keyframes spin {
 | 
				
			||||||
  0% {
 | 
					  0% {
 | 
				
			||||||
    transform: rotate(0deg);
 | 
					    transform: rotate(0deg);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								
		Loading…
	
		Reference in New Issue