update: css dan regex struk
parent
512eb82743
commit
e9a82f8665
|
@ -3,7 +3,7 @@
|
|||
ViewData["Title"] = "Detail Perjalanan - DLH";
|
||||
}
|
||||
|
||||
<div class="max-w-sm mx-auto bg-gray-50 min-h-screen">
|
||||
<div class="w-full lg:max-w-sm mx-auto bg-gray-50 min-h-screen">
|
||||
<!-- Header -->
|
||||
<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">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
ViewData["Title"] = "History - DLH";
|
||||
}
|
||||
|
||||
<div class="max-w-sm mx-auto bg-gray-50 min-h-screen">
|
||||
<div class="w-full lg: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="flex items-center justify-between">
|
||||
<a href="@Url.Action("Index", "Admin")" class="p-2 hover:bg-white/10 rounded-full transition-all duration-200">
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
ViewData["Title"] = "Admin Dashboard";
|
||||
}
|
||||
|
||||
<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="w-full lg:max-w-sm mx-auto bg-gradient-to-br from-gray-50 to-gray-100 min-h-screen relative overflow-hidden">
|
||||
<div class="relative z-10">
|
||||
<div class="bg-gradient-to-br from-orange-500 via-orange-600 to-red-500 rounded-b-[2rem] shadow-2xl p-6 mb-6">
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
}
|
||||
|
||||
|
||||
<div class="max-w-sm mx-auto bg-white min-h-screen">
|
||||
<div class="w-full lg:max-w-sm mx-auto bg-white min-h-screen">
|
||||
<div class="bg-orange-500 text-white px-3 py-4 rounded-b-2xl relative pb-12">
|
||||
<div class="flex items-center justify-between">
|
||||
<a href="@Url.Action("Index", "Home")" class="p-1 hover:bg-white/10 rounded-full transition-colors">
|
||||
|
@ -49,6 +49,14 @@
|
|||
<p class="text-sm">Memuat scanner...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status indicator ketika scanning aktif -->
|
||||
<div id="scanning-indicator" class="absolute top-2 left-2 bg-green-500 text-white px-3 py-1 rounded-full text-xs font-medium z-20 hidden animate-pulse">
|
||||
<div class="flex items-center">
|
||||
<div class="w-2 h-2 bg-white rounded-full mr-2 animate-ping"></div>
|
||||
Scanner Aktif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -58,7 +66,7 @@
|
|||
Mulai Scan
|
||||
</button>
|
||||
|
||||
<button id="stop-scanner" class="w-full bg-gray-500 hover:bg-gray-600 text-white font-medium py-3 px-4 rounded-lg transition-colors btn-scanner hidden">
|
||||
<button id="stop-scanner" class="w-full bg-red-500 hover:bg-red-600 text-white font-medium py-3 px-4 rounded-lg transition-colors btn-scanner hidden">
|
||||
<i class="w-5 h-5 inline mr-2" data-lucide="camera-off"></i>
|
||||
Hentikan Scan
|
||||
</button>
|
||||
|
@ -139,7 +147,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div id="scan-modal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden flex items-center justify-center">
|
||||
<div id="scan-modal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden 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">
|
||||
|
@ -190,6 +198,7 @@
|
|||
this.manualInput = document.getElementById('manual-barcode');
|
||||
this.permissionInfo = document.getElementById('permission-info');
|
||||
this.permissionDenied = document.getElementById('permission-denied');
|
||||
this.scanningIndicator = document.getElementById('scanning-indicator');
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
|
@ -243,6 +252,7 @@
|
|||
this.isScanning = true;
|
||||
this.startBtn.classList.add('hidden');
|
||||
this.stopBtn.classList.remove('hidden');
|
||||
this.scanningIndicator.classList.remove('hidden');
|
||||
this.hideLoading();
|
||||
|
||||
} catch (error) {
|
||||
|
@ -343,7 +353,7 @@
|
|||
this.vibrate();
|
||||
|
||||
this.detectedCode = decodedText;
|
||||
this.stopScanner();
|
||||
// Jangan stop scanner, biarkan tetap aktif untuk scan berikutnya
|
||||
|
||||
this.processScanCode(decodedText);
|
||||
} else {
|
||||
|
@ -362,6 +372,7 @@
|
|||
|
||||
this.startBtn.classList.remove('hidden');
|
||||
this.stopBtn.classList.add('hidden');
|
||||
this.scanningIndicator.classList.add('hidden');
|
||||
}
|
||||
|
||||
flashSuccess() {
|
||||
|
@ -464,6 +475,7 @@
|
|||
|
||||
this.showSuccessModal(code, tanggal, waktu);
|
||||
|
||||
// Scanner tetap aktif, tidak perlu reset tombol
|
||||
setTimeout(() => {
|
||||
this.hideResult();
|
||||
this.manualInput.value = '';
|
||||
|
@ -481,10 +493,13 @@
|
|||
this.hidePermissionMessages();
|
||||
this.detectedCode = null;
|
||||
|
||||
if (this.isScanning && this.html5QrCode) {
|
||||
await this.stopScanner();
|
||||
// Jika scanner sudah aktif, tidak perlu restart
|
||||
if (this.isScanning) {
|
||||
console.log("Scanner sudah aktif, tidak perlu restart");
|
||||
return;
|
||||
}
|
||||
|
||||
// Hanya restart jika scanner tidak aktif
|
||||
setTimeout(() => {
|
||||
this.startScanner();
|
||||
}, 500);
|
||||
|
@ -577,6 +592,7 @@
|
|||
}
|
||||
|
||||
$('#scan-modal').removeClass('hidden');
|
||||
$('#scan-modal').addClass('flex');
|
||||
|
||||
$('#scan-modal').css('opacity', '0').animate({'opacity': '1'}, 300);
|
||||
$('#scan-modal .bg-white').css('transform', 'scale(0.8)').animate({
|
||||
|
@ -629,6 +645,7 @@
|
|||
$('#modal-close').hide(); // Sembunyikan tombol
|
||||
|
||||
$('#scan-modal').removeClass('hidden');
|
||||
$('#scan-modal').addClass('flex');
|
||||
|
||||
$('#scan-modal').css('opacity', '0').animate({'opacity': '1'}, 300);
|
||||
$('#scan-modal .bg-white').css('transform', 'scale(0.8)').animate({
|
||||
|
@ -637,6 +654,7 @@
|
|||
|
||||
setTimeout(() => {
|
||||
this.hideModal();
|
||||
// Scanner tetap aktif setelah modal ditutup
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
|
@ -678,6 +696,7 @@
|
|||
$('#modal-close').hide(); // Sembunyikan tombol
|
||||
|
||||
$('#scan-modal').removeClass('hidden');
|
||||
$('#scan-modal').addClass('flex');
|
||||
|
||||
$('#scan-modal').css('opacity', '0').animate({'opacity': '1'}, 300);
|
||||
$('#scan-modal .bg-white').css('transform', 'scale(0.8)').animate({
|
||||
|
@ -686,12 +705,14 @@
|
|||
|
||||
setTimeout(() => {
|
||||
this.hideModal();
|
||||
// Scanner tetap aktif setelah modal error ditutup
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
hideModal() {
|
||||
$('#scan-modal').animate({'opacity': '0'}, 200, function() {
|
||||
$('#scan-modal').addClass('hidden');
|
||||
$('#scan-modal').removeClass('flex');
|
||||
$('#scan-modal').css('opacity', '');
|
||||
$('#scan-modal .bg-white').css('transform', '');
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="fixed bottom-0 left-1/2 transform -translate-x-1/2 w-full max-w-sm z-99">
|
||||
<div class="fixed bottom-0 left-1/2 transform -translate-x-1/2 w-full lg:max-w-sm z-99">
|
||||
<div class="relative backdrop-blur-lg border border-gray-200/50 rounded-t-3xl shadow-xl overflow-hidden">
|
||||
<div class="absolute -top-0 left-1/2 transform -translate-x-1/2 w-20 h-10">
|
||||
<div class="w-full h-full bg-transparent relative">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
ViewData["Title"] = "Detail Batal Penjemputan";
|
||||
}
|
||||
|
||||
<div class="max-w-sm mx-auto bg-gray-50 min-h-screen">
|
||||
<div class="w-full lg: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="flex items-center justify-between">
|
||||
<a href="@Url.Action("Index", "Home")" class="p-2 hover:bg-white/10 rounded-full transition-all duration-200">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
ViewData["Title"] = "Detail Penjemputan";
|
||||
}
|
||||
|
||||
<div class="max-w-sm mx-auto min-h-screen">
|
||||
<div class="w-full lg:max-w-sm mx-auto min-h-screen">
|
||||
<div class="bg-orange-500 text-white px-4 py-6 rounded-b-3xl relative pb-12">
|
||||
<div class="flex items-center justify-center relative">
|
||||
<a href="@Url.Action("Index", "Home")" class="absolute left-0 p-1 hover:bg-white/10 rounded-full transition-colors">
|
||||
|
@ -97,7 +97,22 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
const inputAlamat = document.getElementById('input-alamat-jalan');
|
||||
|
||||
function reverseGeocode(lat, lng) {
|
||||
fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`)
|
||||
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
||||
|
||||
const fetchOptions = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'eSPJ-App/1.0'
|
||||
}
|
||||
};
|
||||
|
||||
if (isFirefox) {
|
||||
fetchOptions.cache = 'force-cache';
|
||||
fetchOptions.keepalive = true;
|
||||
}
|
||||
|
||||
fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`, fetchOptions)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const address = data.display_name || `${lat}, ${lng}`;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
ViewData["Title"] = "Detail Perjalanan - DLH";
|
||||
}
|
||||
|
||||
<div class="max-w-sm mx-auto bg-gray-50 min-h-screen">
|
||||
<div class="w-full lg:max-w-sm mx-auto bg-gray-50 min-h-screen">
|
||||
<!-- Header -->
|
||||
<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">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
ViewData["Title"] = "History - DLH";
|
||||
}
|
||||
|
||||
<div class="max-w-sm mx-auto bg-gray-50 min-h-screen">
|
||||
<div class="w-full lg: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="flex items-center justify-between">
|
||||
<a href="@Url.Action("Index", "Home")" class="p-2 hover:bg-white/10 rounded-full transition-all duration-200">
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
@{
|
||||
Layout = "~/Views/Admin/Transport/SpjDriver/Shared/_Layout.cshtml";
|
||||
ViewData["Title"] = "History - DLH";
|
||||
}
|
||||
|
||||
<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="flex items-center justify-between">
|
||||
<a href="@Url.Action("Index", "Home")" class="p-2 hover:bg-white/10 rounded-full transition-all duration-200">
|
||||
<i class="w-5 h-5" data-lucide="chevron-left"></i>
|
||||
</a>
|
||||
<h1 class="text-lg font-bold">Riwayat Perjalanan</h1>
|
||||
<div class="w-9"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@{
|
||||
var spjList = new[]
|
||||
{
|
||||
new {
|
||||
Id = 1,
|
||||
NoSpj = "SPJ/07-2025/PKM/000478",
|
||||
Plat = "B 5678 ABC",
|
||||
Kode = "JRC 007",
|
||||
Tujuan = "Bantar Gebang",
|
||||
Status = "In Progress",
|
||||
Tanggal = "28 Jul 2025",
|
||||
Waktu = "16:45"
|
||||
},
|
||||
new {
|
||||
Id = 2,
|
||||
NoSpj = "SPJ/07-2025/PKM/000476",
|
||||
Plat = "B 9632 TOR",
|
||||
Kode = "JRC 005",
|
||||
Tujuan = "RDF Rorotan",
|
||||
Status = "Completed",
|
||||
Tanggal = "27 Jul 2025",
|
||||
Waktu = "14:30"
|
||||
},
|
||||
new {
|
||||
Id = 3,
|
||||
NoSpj = "SPJ/07-2025/PKM/000477",
|
||||
Plat = "B 1234 XYZ",
|
||||
Kode = "JRC 006",
|
||||
Tujuan = "RDF Pesanggarahan",
|
||||
Status = "Completed",
|
||||
Tanggal = "26 Jul 2025",
|
||||
Waktu = "09:15"
|
||||
},
|
||||
new {
|
||||
Id = 4,
|
||||
NoSpj = "SPJ/07-2025/PKM/000479",
|
||||
Plat = "B 9876 DEF",
|
||||
Kode = "JRC 008",
|
||||
Tujuan = "RDF Sunter",
|
||||
Status = "Completed",
|
||||
Tanggal = "25 Jul 2025",
|
||||
Waktu = "11:20"
|
||||
},
|
||||
new {
|
||||
Id = 5,
|
||||
NoSpj = "SPJ/07-2025/PKM/000480",
|
||||
Plat = "B 4321 GHI",
|
||||
Kode = "JRC 009",
|
||||
Tujuan = "Bantar Gebang",
|
||||
Status = "Completed",
|
||||
Tanggal = "24 Jul 2025",
|
||||
Waktu = "08:45"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
<div class="px-4 py-4 space-y-3">
|
||||
@foreach (var spj in spjList)
|
||||
{
|
||||
<a href="@Url.Action("Details", "History", 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="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-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<div class="w-2 h-2 bg-orange-400 rounded-full"></div>
|
||||
<span class="text-xs font-medium text-gray-500 uppercase tracking-wider">No. SPJ</span>
|
||||
</div>
|
||||
<div class="font-bold text-gray-900 text-sm">@spj.NoSpj</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-end gap-1">
|
||||
@if (spj.Status == "Completed")
|
||||
{
|
||||
<span class="bg-green-100 text-green-700 px-2 py-1 rounded-full text-xs font-semibold flex items-center gap-1">
|
||||
<div class="w-2 h-2 bg-green-500 rounded-full"></div>
|
||||
Selesai
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="bg-blue-100 text-blue-700 px-2 py-1 rounded-full text-xs font-semibold flex items-center gap-1">
|
||||
<div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></div>
|
||||
Berlangsung
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-xl p-3 mb-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 bg-orange-100 rounded-lg flex items-center justify-center">
|
||||
<i class="w-5 h-5 text-orange-600" data-lucide="car"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-bold text-gray-900 text-sm">@spj.Plat</div>
|
||||
<div class="text-xs text-gray-500">@spj.Kode</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-xs text-gray-500">@spj.Tanggal</div>
|
||||
<div class="text-xs font-medium text-gray-700">@spj.Waktu</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 pt-2 border-t border-gray-100">
|
||||
<div class="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center flex-shrink-0">
|
||||
<i class="w-4 h-4 text-green-600" data-lucide="map-pin"></i>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="text-xs text-gray-500 mb-1">Tujuan</div>
|
||||
<div class="font-semibold text-gray-900 text-sm">@spj.Tujuan</div>
|
||||
</div>
|
||||
<div class="p-2 hover:bg-gray-100 rounded-full transition-colors">
|
||||
<i class="w-4 h-4 text-gray-400" data-lucide="chevron-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Bottom Navigation -->
|
||||
<partial name="~/Views/Admin/Transport/SpjDriver/Shared/Components/_Navigation.cshtml" />
|
||||
|
||||
<!-- Kalau butuh tampilan kosong (jika tidak ada data) -->
|
||||
|
||||
@* <div class="flex flex-col items-center justify-center py-16 px-4">
|
||||
<div class="w-24 h-24 bg-gray-100 rounded-full flex items-center justify-center mb-4">
|
||||
<i class="w-12 h-12 text-gray-400" data-lucide="clock"></i>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">Belum Ada Riwayat</h3>
|
||||
<p class="text-gray-500 text-center text-sm">Riwayat perjalanan Anda akan muncul di sini setelah melakukan perjalanan pertama.</p>
|
||||
</div> *@
|
||||
|
||||
</div>
|
|
@ -3,10 +3,10 @@
|
|||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="container max-w-sm mx-auto bg-white min-h-screen">
|
||||
<div class="w-full lg:max-w-sm mx-auto bg-white min-h-screen">
|
||||
|
||||
<!-- Header -->
|
||||
<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="absolute top-0 w-full lg:max-w-sm 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="flex flex-col">
|
||||
<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>
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<partial name="~/Views/Admin/Transport/SpjDriver/Shared/Components/_NavigationAdmin.cshtml" />
|
||||
<partial name="~/Views/Admin/Transport/SpjDriver/Shared/Components/_Navigation.cshtml" />
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="fixed bottom-0 left-1/2 transform -translate-x-1/2 w-full max-w-sm z-99">
|
||||
<div class="fixed bottom-0 left-1/2 transform -translate-x-1/2 w-full lg:max-w-sm z-99">
|
||||
<div class="relative backdrop-blur-lg border border-gray-200/50 rounded-t-3xl shadow-xl overflow-hidden">
|
||||
<div class="absolute -top-0 left-1/2 transform -translate-x-1/2 w-20 h-10">
|
||||
<div class="w-full h-full bg-transparent relative">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
ViewData["Title"] = "Submit Foto Muatan";
|
||||
}
|
||||
|
||||
<div class="max-w-sm mx-auto bg-white min-h-screen">
|
||||
<div class="w-full lg:max-w-sm mx-auto bg-white min-h-screen">
|
||||
<div class="bg-gradient-to-r from-orange-500 to-orange-600 text-white px-3 py-4 rounded-b-2xl relative pb-12">
|
||||
<div class="flex items-center justify-between">
|
||||
<a href="@Url.Action("Index", "Home")" class="p-1 hover:bg-white/10 rounded-full transition-colors">
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
|||
<link rel="stylesheet" href="@Url.Content("~/driver/css/scanner.css")" asp-append-version="true" />
|
||||
}
|
||||
|
||||
<div class="max-w-sm mx-auto bg-white min-h-screen">
|
||||
<div class="w-full lg:max-w-sm mx-auto bg-white min-h-screen">
|
||||
<div class="bg-orange-500 text-white px-3 py-4 rounded-b-2xl relative pb-12">
|
||||
<div class="flex items-center justify-between">
|
||||
<a href="@Url.Action("Index", "Home")" class="p-1 hover:bg-white/10 rounded-full transition-colors">
|
||||
|
@ -253,7 +253,7 @@
|
|||
</div>
|
||||
|
||||
<register-block dynamic-section="scripts" key="jsSubmitStruk">
|
||||
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@4.1.1/dist/tesseract.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@5.1.1/dist/tesseract.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const nomorStrukInput = document.getElementById('NomorStruk');
|
||||
|
@ -576,37 +576,40 @@
|
|||
let weightOut = '';
|
||||
let weightNett = '';
|
||||
|
||||
// Enhanced receipt number detection - prioritize month prefix patterns
|
||||
const receiptPatterns = [
|
||||
/(\d{2})\s+(\d{7,})/gi,
|
||||
/(\d{2})_(\d{7,})/gi,
|
||||
/(?:no.*struk|nomor.*struk|receipt)[\s.:]*(\d{7,})/gi,
|
||||
/(?:^|\s)(\d{7,10})(?:\s|$)/g,
|
||||
/(\d{2})_(\d{6,})/gi, // "08_7999566" - underscore pattern (highest priority)
|
||||
/(\d{2})\s+(\d{6,})/gi, // "08 7999566" - space pattern
|
||||
/(?:no.*struk|nomor.*struk|receipt)[\s.:]*(\d{6,})/gi, // Context-based pattern
|
||||
/(?:^|\s)(\d{6,10})(?:\s|$)/g, // Standalone number pattern
|
||||
];
|
||||
|
||||
for (const line of lines) {
|
||||
console.log(`Processing line for receipt: "${line}"`);
|
||||
|
||||
const monthNumberMatch = line.match(/(\d{2})\s+(\d{7,})/);
|
||||
if (monthNumberMatch && monthNumberMatch[2]) {
|
||||
receiptNumber = monthNumberMatch[2]; // Take the number after space/underscore
|
||||
console.log(`Found receipt number: ${receiptNumber} using month-number pattern`);
|
||||
break;
|
||||
}
|
||||
|
||||
const monthUnderscoreMatch = line.match(/(\d{2})_(\d{7,})/);
|
||||
// Pattern 1: Month_Number format (08_7999566) - HIGHEST PRIORITY
|
||||
const monthUnderscoreMatch = line.match(/(\d{2})_(\d{6,})/);
|
||||
if (monthUnderscoreMatch && monthUnderscoreMatch[2]) {
|
||||
receiptNumber = monthUnderscoreMatch[2]; // Take the number after underscore
|
||||
console.log(`Found receipt number: ${receiptNumber} using month-underscore pattern`);
|
||||
receiptNumber = monthUnderscoreMatch[2]; // Take ONLY the number after underscore
|
||||
console.log(`Found receipt number: ${receiptNumber} using month-underscore pattern (removed prefix: ${monthUnderscoreMatch[1]}_)`);
|
||||
break;
|
||||
}
|
||||
|
||||
// Other patterns
|
||||
for (const pattern of receiptPatterns.slice(2)) { // Skip first 2 patterns already handled above
|
||||
// Pattern 2: Month Number format (08 7999566)
|
||||
const monthSpaceMatch = line.match(/(\d{2})\s+(\d{6,})/);
|
||||
if (monthSpaceMatch && monthSpaceMatch[2]) {
|
||||
receiptNumber = monthSpaceMatch[2]; // Take ONLY the number after space
|
||||
console.log(`Found receipt number: ${receiptNumber} using month-space pattern (removed prefix: ${monthSpaceMatch[1]} )`);
|
||||
break;
|
||||
}
|
||||
|
||||
// Pattern 3: Context-based patterns (fallback)
|
||||
for (const pattern of receiptPatterns.slice(2)) {
|
||||
const matches = [...line.matchAll(pattern)];
|
||||
for (const match of matches) {
|
||||
if (match[1] && match[1].length >= 7) {
|
||||
if (match[1] && match[1].length >= 6) {
|
||||
receiptNumber = match[1];
|
||||
console.log(`Found receipt number: ${receiptNumber} using pattern: ${pattern}`);
|
||||
console.log(`Found receipt number: ${receiptNumber} using context pattern: ${pattern}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -668,106 +671,54 @@
|
|||
}
|
||||
|
||||
if (!truckNumber) {
|
||||
console.log('No truck number found in context lines, trying all lines...');
|
||||
for (const line of lines) {
|
||||
console.log(`Scanning line for truck pattern: "${line}"`);
|
||||
|
||||
if (line.match(/[A-Z]\s*\d+\s*[A-Z]{2,3}/i)) {
|
||||
console.log('Potential truck pattern found:', line);
|
||||
|
||||
for (const pattern of truckPatterns) {
|
||||
const match = line.match(pattern);
|
||||
if (match) {
|
||||
let foundTruck = '';
|
||||
|
||||
if (match.length === 4 && match[1] && match[2] && match[3]) {
|
||||
foundTruck = `${match[1]} ${match[2]} ${match[3]}`;
|
||||
} else if (match[1]) {
|
||||
foundTruck = match[1].trim();
|
||||
}
|
||||
|
||||
if (foundTruck) {
|
||||
foundTruck = foundTruck.replace(/([A-Z])(\d+)(\s+[A-Z]{2,3})/g, '$1 $2$3');
|
||||
foundTruck = foundTruck.replace(/([A-Z]{1,2})(\d{3,4})([A-Z]{2,3})/g, '$1 $2 $3');
|
||||
foundTruck = foundTruck.replace(/\s+/g, ' ').trim();
|
||||
|
||||
if (foundTruck.match(/^[A-Z]{1,2}\s+\d{3,4}\s+[A-Z]{2,3}$/)) {
|
||||
truckNumber = foundTruck;
|
||||
console.log(`Found truck number: "${truckNumber}" using general pattern`);
|
||||
// Ambil semua karakter setelah label "No Truk" tanpa filter khusus
|
||||
for (const line of lines) {
|
||||
if (line.toLowerCase().includes('no truk')) {
|
||||
// Contoh: "No Truk : B 9501 TOQ"
|
||||
const match = line.match(/no\s*truk\s*:?\s*(.+)/i);
|
||||
if (match && match[1]) {
|
||||
truckNumber = match[1].trim();
|
||||
console.log(`Found truck number: ${truckNumber} (ambil semua karakter setelah label No Truk)`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Jika tidak ditemukan, fallback ke pattern lama (awalan B)
|
||||
if (!truckNumber) {
|
||||
for (const line of lines) {
|
||||
const match = line.match(/\bB\s*\d{3,5}\s*[A-Z]{2,4}\b/i);
|
||||
if (match && match[0]) {
|
||||
truckNumber = match[0].trim();
|
||||
console.log(`Fallback truck number: ${truckNumber}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (truckNumber) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!truckNumber) {
|
||||
console.log('Still no truck number, trying loose patterns...');
|
||||
for (const line of lines) {
|
||||
const looseMatch = line.match(/([A-Z]{1,2})\s*(\d{3,4})\s*([A-Z]{2,3})/i);
|
||||
if (looseMatch && looseMatch[1] && looseMatch[2] && looseMatch[3]) {
|
||||
const candidate = `${looseMatch[1].toUpperCase()} ${looseMatch[2]} ${looseMatch[3].toUpperCase()}`;
|
||||
console.log(`Found potential truck number with loose pattern: "${candidate}"`);
|
||||
|
||||
if (looseMatch[1].length <= 2 &&
|
||||
looseMatch[2].length >= 3 && looseMatch[2].length <= 4 &&
|
||||
looseMatch[3].length >= 2 && looseMatch[3].length <= 3) {
|
||||
truckNumber = candidate;
|
||||
console.log(`Accepted truck number: "${truckNumber}"`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Truck detection results:', { truckNumber });
|
||||
|
||||
console.log('Truck detection result:', truckNumber);
|
||||
|
||||
const assignmentPatterns = [
|
||||
/(?:penugasan|assignment)[\s.:]*([A-Z\s]+?)(?:\n|$)/gi,
|
||||
/(JAKARTA\s+\w+)/gi, // Specific pattern for Jakarta areas
|
||||
/(BANDUNG|SURABAYA|MEDAN|SEMARANG|PALEMBANG|MAKASSAR)[\s\w]*/gi,
|
||||
];
|
||||
|
||||
// Assignment detection - ambil semua karakter setelah label "Penugasan"
|
||||
for (const line of lines) {
|
||||
console.log(`Processing line for assignment: "${line}"`);
|
||||
|
||||
if (line.toLowerCase().includes('penugasan')) {
|
||||
console.log('Found penugasan line:', line);
|
||||
|
||||
const assignmentMatch = line.match(/penugasan\s*:\s*(.+)/i);
|
||||
if (assignmentMatch && assignmentMatch[1]) {
|
||||
assignment = assignmentMatch[1].trim();
|
||||
console.log(`Found assignment: ${assignment}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (line.toUpperCase().includes('JAKARTA') && line.toUpperCase().includes('BARAT')) {
|
||||
assignment = 'JAKARTA BARAT';
|
||||
console.log(`Found assignment: ${assignment} from Jakarta Barat line`);
|
||||
break;
|
||||
}
|
||||
|
||||
for (const pattern of assignmentPatterns) {
|
||||
const match = line.match(pattern);
|
||||
// Contoh: "Penugasan : JAKARTA TIMUR" atau "Penugasan: UPST DLH"
|
||||
const match = line.match(/penugasan\s*:?\s*(.+)/i);
|
||||
if (match && match[1]) {
|
||||
assignment = match[1].trim();
|
||||
console.log(`Found assignment: ${assignment} using pattern: ${pattern}`);
|
||||
console.log(`Found assignment: ${assignment} (ambil semua karakter setelah label Penugasan)`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (assignment) break;
|
||||
}
|
||||
|
||||
console.log('Assignment detection result:', assignment);
|
||||
|
||||
// Enhanced time patterns
|
||||
// Enhanced time patterns - prioritize YYYY-MM-DD HH:MM:SS format
|
||||
const timePatterns = [
|
||||
/(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/gi, // 2025-08-02 12:35:34 (highest priority)
|
||||
/(\d{1,2}\s+\w{3}\s+\d{4},\s*\d{1,2}:\d{2}:\d{2})/gi, // 04 Aug 2025, 08:13:51
|
||||
/(\d{1,2}\s+\w{3}\s+\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04 Aug 2025 08:13:51
|
||||
/(\d{1,2}\/\d{1,2}\/\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04/08/2025 08:13:51
|
||||
|
@ -780,12 +731,21 @@
|
|||
// Entry time - look for "Masuk :" pattern
|
||||
if (line.toLowerCase().includes('masuk') && line.includes(':')) {
|
||||
console.log('Found masuk line:', line);
|
||||
for (const pattern of timePatterns) {
|
||||
const match = line.match(pattern);
|
||||
if (match && match[1]) {
|
||||
entryTime = match[1].trim();
|
||||
console.log(`Found entry time: ${entryTime}`);
|
||||
break;
|
||||
|
||||
// First try to find exact YYYY-MM-DD HH:MM:SS format after "Masuk :"
|
||||
const masukMatch = line.match(/masuk\s*:\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/i);
|
||||
if (masukMatch && masukMatch[1]) {
|
||||
entryTime = masukMatch[1].trim();
|
||||
console.log(`Found entry time via masuk pattern: ${entryTime} (format: YYYY-MM-DD HH:MM:SS)`);
|
||||
} else {
|
||||
// Fallback to general patterns
|
||||
for (const pattern of timePatterns) {
|
||||
const match = line.match(pattern);
|
||||
if (match && match[1]) {
|
||||
entryTime = match[1].trim();
|
||||
console.log(`Found entry time: ${entryTime}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Also try to extract after "Masuk : "
|
||||
|
@ -799,19 +759,22 @@
|
|||
// Exit time - look for "Keluar :" pattern
|
||||
if (line.toLowerCase().includes('keluar') && line.includes(':')) {
|
||||
console.log('Found keluar line:', line);
|
||||
for (const pattern of timePatterns) {
|
||||
const match = line.match(pattern);
|
||||
if (match && match[1]) {
|
||||
exitTime = match[1].trim();
|
||||
console.log(`Found exit time: ${exitTime}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Also try to extract after "Keluar : "
|
||||
const keluarMatch = line.match(/keluar\s*:\s*(.+)/i);
|
||||
if (keluarMatch && keluarMatch[1] && !exitTime) {
|
||||
|
||||
// First try to find exact YYYY-MM-DD HH:MM:SS format after "Keluar :"
|
||||
const keluarMatch = line.match(/keluar\s*:\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/i);
|
||||
if (keluarMatch && keluarMatch[1]) {
|
||||
exitTime = keluarMatch[1].trim();
|
||||
console.log(`Found exit time via keluar pattern: ${exitTime}`);
|
||||
console.log(`Found exit time via keluar pattern: ${exitTime} (format: YYYY-MM-DD HH:MM:SS)`);
|
||||
} else {
|
||||
// Fallback to general patterns
|
||||
for (const pattern of timePatterns) {
|
||||
const match = line.match(pattern);
|
||||
if (match && match[1]) {
|
||||
exitTime = match[1].trim();
|
||||
console.log(`Found exit time: ${exitTime}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -841,16 +804,16 @@
|
|||
if (specificMatch) {
|
||||
const numbers = specificMatch[0].match(/(\d+)/);
|
||||
if (numbers && numbers[1]) {
|
||||
weightIn = numbers[1];
|
||||
console.log(`Found weight in via specific pattern: ${weightIn} kg`);
|
||||
weightIn = numbers[1]; // Hanya angka, tanpa "kg"
|
||||
console.log(`Found weight in via specific pattern: ${weightIn} (removed kg)`);
|
||||
}
|
||||
} else {
|
||||
// Try general patterns
|
||||
for (const pattern of weightPatterns) {
|
||||
const match = line.match(pattern);
|
||||
if (match && match[1] && parseInt(match[1]) > 1000) {
|
||||
weightIn = match[1];
|
||||
console.log(`Found weight in via general pattern: ${weightIn} kg`);
|
||||
weightIn = match[1]; // Hanya angka
|
||||
console.log(`Found weight in via general pattern: ${weightIn} (removed kg)`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -866,16 +829,16 @@
|
|||
if (specificMatch) {
|
||||
const numbers = specificMatch[0].match(/(\d+)/);
|
||||
if (numbers && numbers[1]) {
|
||||
weightOut = numbers[1];
|
||||
console.log(`Found weight out via specific pattern: ${weightOut} kg`);
|
||||
weightOut = numbers[1]; // Hanya angka, tanpa "kg"
|
||||
console.log(`Found weight out via specific pattern: ${weightOut} (removed kg)`);
|
||||
}
|
||||
} else {
|
||||
// Try general patterns
|
||||
for (const pattern of weightPatterns) {
|
||||
const match = line.match(pattern);
|
||||
if (match && match[1] && parseInt(match[1]) > 1000) {
|
||||
weightOut = match[1];
|
||||
console.log(`Found weight out via general pattern: ${weightOut} kg`);
|
||||
weightOut = match[1]; // Hanya angka
|
||||
console.log(`Found weight out via general pattern: ${weightOut} (removed kg)`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -891,16 +854,16 @@
|
|||
if (specificMatch) {
|
||||
const numbers = specificMatch[0].match(/(\d+)/);
|
||||
if (numbers && numbers[1]) {
|
||||
weightNett = numbers[1];
|
||||
console.log(`Found weight nett via specific pattern: ${weightNett} kg`);
|
||||
weightNett = numbers[1]; // Hanya angka, tanpa "kg"
|
||||
console.log(`Found weight nett via specific pattern: ${weightNett} (removed kg)`);
|
||||
}
|
||||
} else {
|
||||
// Try general patterns
|
||||
for (const pattern of weightPatterns) {
|
||||
const match = line.match(pattern);
|
||||
if (match && match[1] && parseInt(match[1]) > 100) {
|
||||
weightNett = match[1];
|
||||
console.log(`Found weight nett via general pattern: ${weightNett} kg`);
|
||||
weightNett = match[1]; // Hanya angka
|
||||
console.log(`Found weight nett via general pattern: ${weightNett} (removed kg)`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -908,6 +871,24 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Validasi Berat Nett = Berat Masuk - Berat Keluar
|
||||
if (weightIn && weightOut && weightNett) {
|
||||
const calculatedNett = parseInt(weightIn) - parseInt(weightOut);
|
||||
const detectedNett = parseInt(weightNett);
|
||||
|
||||
console.log(`Weight validation: ${weightIn} - ${weightOut} = ${calculatedNett}, detected: ${detectedNett}`);
|
||||
|
||||
if (calculatedNett !== detectedNett) {
|
||||
console.warn(`WARNING: Berat Nett tidak sesuai! Perhitungan: ${calculatedNett}, Terdeteksi: ${detectedNett}`);
|
||||
// Gunakan hasil perhitungan yang benar
|
||||
weightNett = calculatedNett.toString();
|
||||
console.log(`Using calculated weight nett: ${weightNett}`);
|
||||
|
||||
// Alert akan ditampilkan di frontend saat apply data
|
||||
this.weightValidationError = `Berat Nett tidak sesuai! Perhitungan: ${calculatedNett} kg, Terdeteksi: ${detectedNett} kg. Menggunakan hasil perhitungan.`;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Weight detection results:', { weightIn, weightOut, weightNett });
|
||||
|
||||
console.log('Final Detected Data before assignment:', {
|
||||
|
@ -960,9 +941,15 @@
|
|||
}
|
||||
|
||||
applyDetectedDataDirectly() {
|
||||
console.log('=== APPLYING DATA DIRECTLY TO FORM ===');
|
||||
console.log(`=== APPLYING DATA DIRECTLY TO FORM ===`);
|
||||
console.log('Data to apply:', this.detectedData);
|
||||
|
||||
// Tampilkan alert jika ada error validasi berat nett
|
||||
if (this.weightValidationError) {
|
||||
alert(this.weightValidationError);
|
||||
this.weightValidationError = null; // Reset error
|
||||
}
|
||||
|
||||
// Get input elements by ID directly
|
||||
const nomorStrukInput = document.getElementById('NomorStruk');
|
||||
const nomorPolisiInput = document.getElementById('NomorPolisi');
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
--color-white: #fff;
|
||||
--spacing: 0.25rem;
|
||||
--container-sm: 24rem;
|
||||
--container-md: 28rem;
|
||||
--text-xs: 0.75rem;
|
||||
--text-xs--line-height: calc(1 / 0.75);
|
||||
--text-sm: 0.875rem;
|
||||
|
@ -113,7 +114,6 @@
|
|||
--radius-xl: 0.75rem;
|
||||
--radius-2xl: 1rem;
|
||||
--radius-3xl: 1.5rem;
|
||||
--radius-4xl: 2rem;
|
||||
--drop-shadow-lg: 0 4px 4px rgb(0 0 0 / 0.15);
|
||||
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
|
@ -1213,9 +1213,6 @@
|
|||
.rounded-3xl {
|
||||
border-radius: var(--radius-3xl);
|
||||
}
|
||||
.rounded-4xl {
|
||||
border-radius: var(--radius-4xl);
|
||||
}
|
||||
.rounded-full {
|
||||
border-radius: calc(infinity * 1px);
|
||||
}
|
||||
|
@ -2639,6 +2636,21 @@
|
|||
outline-style: none;
|
||||
}
|
||||
}
|
||||
.md\:max-w-md {
|
||||
@media (width >= 48rem) {
|
||||
max-width: var(--container-md);
|
||||
}
|
||||
}
|
||||
.md\:max-w-sm {
|
||||
@media (width >= 48rem) {
|
||||
max-width: var(--container-sm);
|
||||
}
|
||||
}
|
||||
.lg\:max-w-sm {
|
||||
@media (width >= 64rem) {
|
||||
max-width: var(--container-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
@property --tw-translate-x {
|
||||
syntax: "*";
|
||||
|
|
Loading…
Reference in New Issue