update: admin scan select2, convert tanggal
parent
d8684446e6
commit
74ab38ce6b
|
@ -39,17 +39,35 @@ public class SpjAdminController : Controller
|
||||||
[HttpPost("scan/process/{id:guid}", Name = "Admin_Scan_Process")]
|
[HttpPost("scan/process/{id:guid}", Name = "Admin_Scan_Process")]
|
||||||
public IActionResult Process(Guid id)
|
public IActionResult Process(Guid id)
|
||||||
{
|
{
|
||||||
// Dummy rule: hanya GUID dummy yang dianggap valid
|
// GUID yang sudah pernah di scan
|
||||||
if (id == DummySpjGuid)
|
var alreadyScannedGuid = new Guid("550e8400-e29b-41d4-a716-446655440007");
|
||||||
|
|
||||||
|
if (id == alreadyScannedGuid)
|
||||||
|
{
|
||||||
|
return Json(new {
|
||||||
|
success = false,
|
||||||
|
message = "SPJ ini sudah pernah di scan pada 20 Agustus 2024."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var validGuids = new[]
|
||||||
|
{
|
||||||
|
DummySpjGuid, // GUID asli yang sudah ada
|
||||||
|
new Guid("550e8400-e29b-41d4-a716-446655440001"), // Ahmad Supriyadi
|
||||||
|
new Guid("550e8400-e29b-41d4-a716-446655440002"), // Budi Santoso
|
||||||
|
new Guid("550e8400-e29b-41d4-a716-446655440003"), // Candra Wijaya
|
||||||
|
new Guid("550e8400-e29b-41d4-a716-446655440004"), // Dedi Kurniawan
|
||||||
|
new Guid("550e8400-e29b-41d4-a716-446655440006") // Eko Prasetyo gua coba gagalin
|
||||||
|
};
|
||||||
|
|
||||||
|
if (validGuids.Contains(id))
|
||||||
{
|
{
|
||||||
return Json(new { success = true, data = new { id, status = "Valid" } });
|
return Json(new { success = true, data = new { id, status = "Valid" } });
|
||||||
}
|
}
|
||||||
|
|
||||||
return Json(new { success = false, message = "SPJ tidak ditemukan." });
|
return Json(new { success = false, message = "SPJ tidak ditemukan." });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve kode SPJ (contoh: "SPJ/08-2025/PKM/000519") menjadi GUID.
|
|
||||||
// Catatan: Saat ini dummy (development): setiap format SPJ yang valid bakal ke GUID baru.
|
|
||||||
// Integrasi produksi: ganti dengan query DB untuk mencari SPJ berdasarkan nomor, lalu kembalikan Id GUID yang sebenarnya.
|
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
[HttpPost("scan/resolve", Name = "Admin_Scan_Resolve")]
|
[HttpPost("scan/resolve", Name = "Admin_Scan_Resolve")]
|
||||||
public IActionResult Resolve([FromForm] string code)
|
public IActionResult Resolve([FromForm] string code)
|
||||||
|
@ -79,13 +97,141 @@ public class SpjAdminController : Controller
|
||||||
return Json(new { success = false, message = "Format kode tidak dikenali." });
|
return Json(new { success = false, message = "Format kode tidak dikenali." });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dummy mapping: jika persis sesuai nomor di atas, kembalikan GUID tetap; selain itu anggap tidak ditemukan
|
// Mapping SPJ code ke GUID (semua dummy data)
|
||||||
if (string.Equals(code, DummySpjNumber, StringComparison.OrdinalIgnoreCase))
|
var spjMapping = new Dictionary<string, Guid>(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
return Json(new { success = true, id = DummySpjGuid });
|
{ DummySpjNumber, DummySpjGuid },
|
||||||
|
{ "SPJ/01-2024/TRN/240815", new Guid("550e8400-e29b-41d4-a716-446655440001") },
|
||||||
|
{ "SPJ/02-2024/TRN/240816", new Guid("550e8400-e29b-41d4-a716-446655440002") },
|
||||||
|
{ "SPJ/03-2024/TRN/240817", new Guid("550e8400-e29b-41d4-a716-446655440003") },
|
||||||
|
{ "SPJ/04-2024/TRN/240818", new Guid("550e8400-e29b-41d4-a716-446655440004") },
|
||||||
|
{ "SPJ/05-2024/TRN/240819", new Guid("550e8400-e29b-41d4-a716-446655440005") },
|
||||||
|
{ "SPJ/06-2024/TRN/240820", new Guid("550e8400-e29b-41d4-a716-446655440007") } // Sudah pernah di scan
|
||||||
|
};
|
||||||
|
|
||||||
|
if (spjMapping.TryGetValue(code, out var mappedGuid))
|
||||||
|
{
|
||||||
|
return Json(new { success = true, id = mappedGuid });
|
||||||
}
|
}
|
||||||
|
|
||||||
return Json(new { success = false, message = "SPJ tidak ditemukan." });
|
return Json(new { success = false, message = "SPJ tidak ditemukan." });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("search-spj")]
|
||||||
|
public IActionResult SearchSpj(string q, int page = 1, int pageSize = 20)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Dummy data buat testing
|
||||||
|
var dummySpjList = new[]
|
||||||
|
{
|
||||||
|
new {
|
||||||
|
id = "550e8400-e29b-41d4-a716-446655440001",
|
||||||
|
spjCode = "SPJ/01-2024/TRN/240815",
|
||||||
|
driverName = "Ahmad Supriyadi",
|
||||||
|
platNomor = "B 1234 ABC",
|
||||||
|
nomorPintu = "001"
|
||||||
|
},
|
||||||
|
new {
|
||||||
|
id = "550e8400-e29b-41d4-a716-446655440002",
|
||||||
|
spjCode = "SPJ/02-2024/TRN/240816",
|
||||||
|
driverName = "Budi Santoso",
|
||||||
|
platNomor = "B 5678 DEF",
|
||||||
|
nomorPintu = "002"
|
||||||
|
},
|
||||||
|
new {
|
||||||
|
id = "550e8400-e29b-41d4-a716-446655440003",
|
||||||
|
spjCode = "SPJ/03-2024/TRN/240817",
|
||||||
|
driverName = "Candra Wijaya",
|
||||||
|
platNomor = "B 9012 GHI",
|
||||||
|
nomorPintu = "003"
|
||||||
|
},
|
||||||
|
new {
|
||||||
|
id = "550e8400-e29b-41d4-a716-446655440004",
|
||||||
|
spjCode = "SPJ/04-2024/TRN/240818",
|
||||||
|
driverName = "Dedi Kurniawan",
|
||||||
|
platNomor = "B 3456 JKL",
|
||||||
|
nomorPintu = "004"
|
||||||
|
},
|
||||||
|
new {
|
||||||
|
id = "550e8400-e29b-41d4-a716-446655440005",
|
||||||
|
spjCode = "SPJ/05-2024/TRN/240819",
|
||||||
|
driverName = "Eko Prasetyo",
|
||||||
|
platNomor = "B 7890 MNO",
|
||||||
|
nomorPintu = "005"
|
||||||
|
},
|
||||||
|
new {
|
||||||
|
id = DummySpjGuid.ToString(),
|
||||||
|
spjCode = DummySpjNumber,
|
||||||
|
driverName = "Fahmi Rahman",
|
||||||
|
platNomor = "B 2468 PQR",
|
||||||
|
nomorPintu = "006"
|
||||||
|
},
|
||||||
|
new {
|
||||||
|
id = "550e8400-e29b-41d4-a716-446655440007",
|
||||||
|
spjCode = "SPJ/06-2024/TRN/240820",
|
||||||
|
driverName = "Gita Sari",
|
||||||
|
platNomor = "B 1357 STU",
|
||||||
|
nomorPintu = "007"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var filteredData = dummySpjList.AsQueryable();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(q))
|
||||||
|
{
|
||||||
|
q = q.ToLower();
|
||||||
|
filteredData = dummySpjList.Where(s =>
|
||||||
|
s.spjCode.ToLower().Contains(q) ||
|
||||||
|
s.driverName.ToLower().Contains(q) ||
|
||||||
|
s.platNomor.ToLower().Contains(q) ||
|
||||||
|
s.nomorPintu.ToLower().Contains(q)
|
||||||
|
).AsQueryable();
|
||||||
|
}
|
||||||
|
|
||||||
|
var total = filteredData.Count();
|
||||||
|
var items = filteredData
|
||||||
|
.Skip((page - 1) * pageSize)
|
||||||
|
.Take(pageSize)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return Json(new {
|
||||||
|
items = items,
|
||||||
|
hasMore = (page * pageSize) < total
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
// PRODUCTION VERSION - Uncomment untuk production - tambahin sendiri yaa
|
||||||
|
var query = _context.SpjData
|
||||||
|
.Where(s => s.SpjCode.Contains(q) ||
|
||||||
|
s.DriverName.Contains(q) ||
|
||||||
|
s.PlatNomor.Contains(q) ||
|
||||||
|
s.NomorPintu.Contains(q))
|
||||||
|
.OrderBy(s => s.SpjCode);
|
||||||
|
|
||||||
|
var total = await query.CountAsync();
|
||||||
|
var items = await query
|
||||||
|
.Skip((page - 1) * pageSize)
|
||||||
|
.Take(pageSize)
|
||||||
|
.Select(s => new {
|
||||||
|
id = s.Id,
|
||||||
|
spjCode = s.SpjCode,
|
||||||
|
driverName = s.DriverName,
|
||||||
|
platNomor = s.PlatNomor,
|
||||||
|
nomorPintu = s.NomorPintu
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Json(new {
|
||||||
|
items = items,
|
||||||
|
hasMore = (page * pageSize) < total
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Json(new { error = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,109 @@
|
||||||
|
|
||||||
@section Styles {
|
@section Styles {
|
||||||
<link rel="stylesheet" href="@Url.Content("~/driver/css/scanner.css")" asp-append-version="true" />
|
<link rel="stylesheet" href="@Url.Content("~/driver/css/scanner.css")" asp-append-version="true" />
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.select2-container {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single {
|
||||||
|
height: 42px !important;
|
||||||
|
border: 1px solid #d1d5db !important;
|
||||||
|
border-radius: 0.5rem !important;
|
||||||
|
padding: 0 12px !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single:focus {
|
||||||
|
border-color: #f97316 !important;
|
||||||
|
box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.2) !important;
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: #374151 !important;
|
||||||
|
line-height: normal !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__placeholder {
|
||||||
|
color: #9ca3af !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||||
|
height: 40px !important;
|
||||||
|
right: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-dropdown {
|
||||||
|
border: 1px solid #d1d5db !important;
|
||||||
|
border-radius: 0.5rem !important;
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option {
|
||||||
|
padding: 12px 16px !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
border-bottom: 1px solid #f3f4f6 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option:last-child {
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||||
|
background-color: #f97316 !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option[aria-selected=true] {
|
||||||
|
background-color: #fed7aa !important;
|
||||||
|
color: #ea580c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-search--dropdown .select2-search__field {
|
||||||
|
border: 1px solid #d1d5db !important;
|
||||||
|
border-radius: 0.375rem !important;
|
||||||
|
padding: 8px 12px !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-search--dropdown .select2-search__field:focus {
|
||||||
|
border-color: #f97316 !important;
|
||||||
|
box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.2) !important;
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-results__message {
|
||||||
|
padding: 12px 16px !important;
|
||||||
|
color: #6b7280 !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spj-option {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spj-code {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spj-details {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6b7280;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -107,14 +210,19 @@
|
||||||
<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="#" novalidate>
|
<form id="manual-form" method="post" action="#" novalidate>
|
||||||
@Html.AntiForgeryToken()
|
@Html.AntiForgeryToken()
|
||||||
<div class="flex gap-2">
|
<div class="space-y-3">
|
||||||
<input type="text"
|
<div>
|
||||||
id="manual-barcode"
|
<select id="manual-barcode-select"
|
||||||
name="barcode"
|
name="barcode"
|
||||||
placeholder="Masukkan kode SPJ manual"
|
class="w-full"
|
||||||
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-500 focus:border-transparent">
|
style="width: 100%;">
|
||||||
<button type="submit" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg transition-colors">
|
<option value="">Ketik untuk mencari SPJ...</option>
|
||||||
<i class="w-5 h-5" data-lucide="search"></i>
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="w-full bg-blue-500 hover:bg-blue-600 text-white font-medium py-3 px-4 rounded-lg transition-colors">
|
||||||
|
<i class="w-5 h-5 inline mr-2" data-lucide="search"></i>
|
||||||
|
Proses SPJ
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -163,6 +271,7 @@
|
||||||
|
|
||||||
<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://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.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>
|
||||||
|
@ -175,6 +284,86 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
function initializeSelect2() {
|
||||||
|
$('#manual-barcode-select').select2({
|
||||||
|
placeholder: 'Ketik untuk mencari SPJ...',
|
||||||
|
allowClear: true,
|
||||||
|
minimumInputLength: 2,
|
||||||
|
|
||||||
|
ajax: {
|
||||||
|
url: '@Url.Action("SearchSpj", "SpjAdmin")',
|
||||||
|
dataType: 'json',
|
||||||
|
delay: 300,
|
||||||
|
data: function (params) {
|
||||||
|
return {
|
||||||
|
q: params.term,
|
||||||
|
page: params.page || 1,
|
||||||
|
pageSize: 20
|
||||||
|
};
|
||||||
|
},
|
||||||
|
processResults: function (data, params) {
|
||||||
|
params.page = params.page || 1;
|
||||||
|
|
||||||
|
return {
|
||||||
|
results: data.items.map(function(item) {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
text: item.spjCode,
|
||||||
|
driverName: item.driverName,
|
||||||
|
platNomor: item.platNomor,
|
||||||
|
nomorPintu: item.nomorPintu
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
pagination: {
|
||||||
|
more: data.hasMore
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
cache: true
|
||||||
|
},
|
||||||
|
|
||||||
|
templateResult: formatSpjOption,
|
||||||
|
templateSelection: formatSpjSelection,
|
||||||
|
escapeMarkup: function(markup) {
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSpjOption(spj) {
|
||||||
|
if (spj.loading) {
|
||||||
|
return spj.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spj.driverName) {
|
||||||
|
return spj.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $(
|
||||||
|
'<div class="spj-option">' +
|
||||||
|
'<div class="spj-code">' + spj.text + '</div>' +
|
||||||
|
'<div class="spj-details">' +
|
||||||
|
'<span>' + spj.driverName + '</span>' +
|
||||||
|
'<span class="bg-orange-300 rounded-full px-2 py-1 text-xs text-gray-500">' + spj.nomorPintu + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div style="font-size: 11px; color: #9ca3af; font-family: monospace;">' + spj.platNomor + '</div>' +
|
||||||
|
'</div>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSpjSelection(spj) {
|
||||||
|
return spj.text || spj.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateManualFormHandler() {
|
||||||
|
const manualInput = document.getElementById('manual-barcode-select');
|
||||||
|
const scanner = window.barcodeScanner;
|
||||||
|
|
||||||
|
if (manualInput && scanner) {
|
||||||
|
scanner.manualInput = manualInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class BarcodeScanner {
|
class BarcodeScanner {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.isScanning = false;
|
this.isScanning = false;
|
||||||
|
@ -201,7 +390,7 @@
|
||||||
this.confirmBtn = document.getElementById('confirm-scan');
|
this.confirmBtn = document.getElementById('confirm-scan');
|
||||||
this.retryBtn = document.getElementById('retry-scan');
|
this.retryBtn = document.getElementById('retry-scan');
|
||||||
this.manualForm = document.getElementById('manual-form');
|
this.manualForm = document.getElementById('manual-form');
|
||||||
this.manualInput = document.getElementById('manual-barcode');
|
this.manualInput = document.getElementById('manual-barcode-select');
|
||||||
this.permissionInfo = document.getElementById('permission-info');
|
this.permissionInfo = document.getElementById('permission-info');
|
||||||
this.permissionDenied = document.getElementById('permission-denied');
|
this.permissionDenied = document.getElementById('permission-denied');
|
||||||
this.scanningIndicator = document.getElementById('scanning-indicator');
|
this.scanningIndicator = document.getElementById('scanning-indicator');
|
||||||
|
@ -374,6 +563,9 @@
|
||||||
this.detectedCode = code;
|
this.detectedCode = code;
|
||||||
this.isProcessing = true;
|
this.isProcessing = true;
|
||||||
|
|
||||||
|
// Simpan SPJ code asli untuk ditampilkan di modal
|
||||||
|
this.originalSpjCode = code;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.html5QrCode && typeof this.html5QrCode.pause === 'function') {
|
if (this.html5QrCode && typeof this.html5QrCode.pause === 'function') {
|
||||||
this.html5QrCode.pause(true);
|
this.html5QrCode.pause(true);
|
||||||
|
@ -423,6 +615,8 @@
|
||||||
|
|
||||||
confirmScan() {
|
confirmScan() {
|
||||||
if (this.detectedCode) {
|
if (this.detectedCode) {
|
||||||
|
// Simpan SPJ code asli untuk ditampilkan di modal
|
||||||
|
this.originalSpjCode = this.detectedCode;
|
||||||
this.processScanCode(this.detectedCode);
|
this.processScanCode(this.detectedCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -468,10 +662,13 @@
|
||||||
let guid = null;
|
let guid = null;
|
||||||
const trimmed = (code || '').trim();
|
const trimmed = (code || '').trim();
|
||||||
|
|
||||||
|
if (!this.originalSpjCode) {
|
||||||
|
this.originalSpjCode = trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isGuid(trimmed)) {
|
if (this.isGuid(trimmed)) {
|
||||||
guid = trimmed;
|
guid = trimmed;
|
||||||
} else if (this.isSpjCode(trimmed)) {
|
} else if (this.isSpjCode(trimmed)) {
|
||||||
// minta backend resolve SPJ ke GUID
|
|
||||||
const rs = await this.resolveSpj(trimmed);
|
const rs = await this.resolveSpj(trimmed);
|
||||||
if (!rs || rs.success !== true || !rs.id) {
|
if (!rs || rs.success !== true || !rs.id) {
|
||||||
throw new Error((rs && rs.message) ? rs.message : 'Gagal resolve SPJ ke GUID.');
|
throw new Error((rs && rs.message) ? rs.message : 'Gagal resolve SPJ ke GUID.');
|
||||||
|
@ -481,16 +678,20 @@
|
||||||
throw new Error('Format kode tidak dikenali. Masukkan GUID atau nomor SPJ yang valid.');
|
throw new Error('Format kode tidak dikenali. Masukkan GUID atau nomor SPJ yang valid.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST GUID ke process endpoint
|
|
||||||
const resp = await this.postGuid(guid);
|
const resp = await this.postGuid(guid);
|
||||||
console.debug('process response', resp);
|
console.debug('process response', resp);
|
||||||
if (resp && resp.success) {
|
if (resp && resp.success) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const tanggal = now.toLocaleDateString('id-ID', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
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' });
|
const waktu = now.toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit' });
|
||||||
this.showSuccessModal(trimmed, tanggal, waktu);
|
this.showSuccessModal(this.originalSpjCode, tanggal, waktu);
|
||||||
} else {
|
} else {
|
||||||
this.showModal('error', 'SPJ Tidak Ditemukan', (resp && resp.message) ? resp.message : 'Kode SPJ tidak ditemukan dalam database.', true);
|
const errorMessage = (resp && resp.message) ? resp.message : 'Kode SPJ tidak ditemukan dalam database.';
|
||||||
|
if (errorMessage.toLowerCase().includes('sudah pernah di scan')) {
|
||||||
|
this.showAlreadyScannedModal(this.originalSpjCode);
|
||||||
|
} else {
|
||||||
|
this.showModal('error', 'SPJ Tidak Ditemukan', errorMessage, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (xhrOrErr) {
|
} catch (xhrOrErr) {
|
||||||
let message = '';
|
let message = '';
|
||||||
|
@ -505,6 +706,8 @@
|
||||||
}
|
}
|
||||||
console.error('processScanCode error', xhrOrErr);
|
console.error('processScanCode error', xhrOrErr);
|
||||||
this.showModal('error', 'Error', message, true);
|
this.showModal('error', 'Error', message, true);
|
||||||
|
} finally {
|
||||||
|
this.originalSpjCode = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,13 +718,11 @@
|
||||||
this.hidePermissionMessages();
|
this.hidePermissionMessages();
|
||||||
this.detectedCode = null;
|
this.detectedCode = null;
|
||||||
|
|
||||||
// Jika scanner sudah aktif, tidak perlu restart
|
|
||||||
if (this.isScanning) {
|
if (this.isScanning) {
|
||||||
console.log("Scanner sudah aktif, tidak perlu restart");
|
console.log("Scanner sudah aktif, tidak perlu restart");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hanya restart jika scanner tidak aktif
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.startScanner();
|
this.startScanner();
|
||||||
}, 500);
|
}, 500);
|
||||||
|
@ -530,9 +731,11 @@
|
||||||
handleManualSubmit(e) {
|
handleManualSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const code = this.manualInput.value.trim();
|
const code = $('#manual-barcode-select').val();
|
||||||
|
const selectedData = $('#manual-barcode-select').select2('data')[0];
|
||||||
|
|
||||||
if (!code) {
|
if (!code) {
|
||||||
this.showModal('error', 'Input Kosong', 'Silakan masukkan kode SPJ.', true);
|
this.showModal('error', 'Input Kosong', 'Silakan pilih SPJ dari daftar.', true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,6 +744,15 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('Selected SPJ:', {
|
||||||
|
id: selectedData?.id,
|
||||||
|
spjCode: selectedData?.text,
|
||||||
|
driver: selectedData?.driverName,
|
||||||
|
platNomor: selectedData?.platNomor,
|
||||||
|
nomorPintu: selectedData?.nomorPintu
|
||||||
|
});
|
||||||
|
|
||||||
|
this.originalSpjCode = selectedData?.text || code;
|
||||||
this.processScanCode(code);
|
this.processScanCode(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,6 +812,7 @@
|
||||||
const iconHtml = {
|
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>',
|
'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>',
|
'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>',
|
||||||
|
'warning': '<div class="w-20 h-20 mx-auto bg-yellow-100 rounded-full flex items-center justify-center shadow-lg"><svg class="w-10 h-10 text-yellow-600" 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></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>'
|
'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>'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -677,7 +890,59 @@
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.hideModal();
|
this.hideModal();
|
||||||
this.hideResult();
|
this.hideResult();
|
||||||
this.manualInput.value = '';
|
$('#manual-barcode-select').val(null).trigger('change');
|
||||||
|
this.resumeAfterDelay(300);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
showAlreadyScannedModal(code) {
|
||||||
|
const warningIcon = `
|
||||||
|
<div class="w-28 h-28 mx-auto bg-gradient-to-r from-yellow-400 to-amber-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="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>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const warningContent = `
|
||||||
|
<div class="bg-gradient-to-br from-yellow-50 via-amber-50 to-orange-50 rounded-2xl p-6 border border-yellow-200 shadow-inner">
|
||||||
|
<div class="flex items-center justify-center mb-4">
|
||||||
|
<div class="bg-gradient-to-r from-yellow-500 to-amber-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-yellow-100">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="flex flex-col gap-2 items-center justify-center mb-2">
|
||||||
|
<svg class="w-6 h-6 text-yellow-600 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-yellow-700 font-bold text-lg">SPJ SUDAH DISCAN</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-yellow-600 text-sm mt-2">SPJ ini telah diproses sebelumnya</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
$('#modal-icon').html(warningIcon);
|
||||||
|
$('#modal-title').html('<span class="text-xl font-bold bg-gradient-to-r from-yellow-600 to-amber-600 bg-clip-text text-transparent">SPJ Sudah Discan</span>');
|
||||||
|
$('#modal-message').html(warningContent);
|
||||||
|
$('#modal-close').hide();
|
||||||
|
|
||||||
|
$('#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({
|
||||||
|
'transform': 'scale(1)'
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.hideModal();
|
||||||
this.resumeAfterDelay(300);
|
this.resumeAfterDelay(300);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
@ -769,9 +1034,12 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initializeSelect2();
|
||||||
|
|
||||||
function waitForLibrary() {
|
function waitForLibrary() {
|
||||||
if (typeof Html5Qrcode !== 'undefined') {
|
if (typeof Html5Qrcode !== 'undefined') {
|
||||||
new BarcodeScanner();
|
window.barcodeScanner = new BarcodeScanner();
|
||||||
|
updateManualFormHandler();
|
||||||
} else {
|
} else {
|
||||||
setTimeout(waitForLibrary, 500);
|
setTimeout(waitForLibrary, 500);
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,7 +180,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Waktu Masuk dan Keluar -->
|
<!-- Waktu Masuk dan Keluar -->
|
||||||
<div class="grid grid-cols-2 gap-3">
|
<div class="grid grid-cols-1 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label for="WaktuMasuk" class="block text-sm font-medium text-gray-700 mb-1">Masuk</label>
|
<label for="WaktuMasuk" class="block text-sm font-medium text-gray-700 mb-1">Masuk</label>
|
||||||
<input
|
<input
|
||||||
|
@ -188,7 +188,7 @@
|
||||||
id="WaktuMasuk"
|
id="WaktuMasuk"
|
||||||
name="WaktuMasuk"
|
name="WaktuMasuk"
|
||||||
class="mt-1 block w-full rounded-lg border border-orange-300 shadow-sm focus:border-orange-500 focus:ring-2 focus:ring-orange-200 transition-all duration-150 px-4 py-2"
|
class="mt-1 block w-full rounded-lg border border-orange-300 shadow-sm focus:border-orange-500 focus:ring-2 focus:ring-orange-200 transition-all duration-150 px-4 py-2"
|
||||||
placeholder="04 Aug 2025, 08:13:51"
|
placeholder="2025-08-04, 08:13:51"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -198,7 +198,7 @@
|
||||||
id="WaktuKeluar"
|
id="WaktuKeluar"
|
||||||
name="WaktuKeluar"
|
name="WaktuKeluar"
|
||||||
class="mt-1 block w-full rounded-lg border border-orange-300 shadow-sm focus:border-orange-500 focus:ring-2 focus:ring-orange-200 transition-all duration-150 px-4 py-2"
|
class="mt-1 block w-full rounded-lg border border-orange-300 shadow-sm focus:border-orange-500 focus:ring-2 focus:ring-orange-200 transition-all duration-150 px-4 py-2"
|
||||||
placeholder="04 Aug 2025, 14:35:10"
|
placeholder="2025-08-04, 14:35:10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -576,26 +576,23 @@
|
||||||
let weightOut = '';
|
let weightOut = '';
|
||||||
let weightNett = '';
|
let weightNett = '';
|
||||||
|
|
||||||
// Enhanced receipt number detection - prioritize month prefix patterns
|
|
||||||
const receiptPatterns = [
|
const receiptPatterns = [
|
||||||
/(\d{2})_(\d{6,})/gi, // "08_7999566" - underscore pattern (highest priority)
|
/(\d{2})_(\d{6,})/gi,
|
||||||
/(\d{2})\s+(\d{6,})/gi, // "08 7999566" - space pattern
|
/(\d{2})\s+(\d{6,})/gi,
|
||||||
/(?:no.*struk|nomor.*struk|receipt)[\s.:]*(\d{6,})/gi, // Context-based pattern
|
/(?:no.*struk|nomor.*struk|receipt)[\s.:]*(\d{6,})/gi,
|
||||||
/(?:^|\s)(\d{6,10})(?:\s|$)/g, // Standalone number pattern
|
/(?:^|\s)(\d{6,10})(?:\s|$)/g,
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
console.log(`Processing line for receipt: "${line}"`);
|
console.log(`Processing line for receipt: "${line}"`);
|
||||||
|
|
||||||
// Pattern 1: Month_Number format (08_7999566) - HIGHEST PRIORITY
|
|
||||||
const monthUnderscoreMatch = line.match(/(\d{2})_(\d{6,})/);
|
const monthUnderscoreMatch = line.match(/(\d{2})_(\d{6,})/);
|
||||||
if (monthUnderscoreMatch && monthUnderscoreMatch[2]) {
|
if (monthUnderscoreMatch && monthUnderscoreMatch[2]) {
|
||||||
receiptNumber = monthUnderscoreMatch[2]; // Take ONLY the number after underscore
|
receiptNumber = monthUnderscoreMatch[2];
|
||||||
console.log(`Found receipt number: ${receiptNumber} using month-underscore pattern (removed prefix: ${monthUnderscoreMatch[1]}_)`);
|
console.log(`Found receipt number: ${receiptNumber} using month-underscore pattern (removed prefix: ${monthUnderscoreMatch[1]}_)`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pattern 2: Month Number format (08 7999566)
|
|
||||||
const monthSpaceMatch = line.match(/(\d{2})\s+(\d{6,})/);
|
const monthSpaceMatch = line.match(/(\d{2})\s+(\d{6,})/);
|
||||||
if (monthSpaceMatch && monthSpaceMatch[2]) {
|
if (monthSpaceMatch && monthSpaceMatch[2]) {
|
||||||
receiptNumber = monthSpaceMatch[2]; // Take ONLY the number after space
|
receiptNumber = monthSpaceMatch[2]; // Take ONLY the number after space
|
||||||
|
@ -603,7 +600,6 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pattern 3: Context-based patterns (fallback)
|
|
||||||
for (const pattern of receiptPatterns.slice(2)) {
|
for (const pattern of receiptPatterns.slice(2)) {
|
||||||
const matches = [...line.matchAll(pattern)];
|
const matches = [...line.matchAll(pattern)];
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
|
@ -671,10 +667,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!truckNumber) {
|
if (!truckNumber) {
|
||||||
// Ambil semua karakter setelah label "No Truk" tanpa filter khusus
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line.toLowerCase().includes('no truk')) {
|
if (line.toLowerCase().includes('no truk')) {
|
||||||
// Contoh: "No Truk : B 9501 TOQ"
|
|
||||||
const match = line.match(/no\s*truk\s*:?\s*(.+)/i);
|
const match = line.match(/no\s*truk\s*:?\s*(.+)/i);
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
truckNumber = match[1].trim();
|
truckNumber = match[1].trim();
|
||||||
|
@ -683,7 +677,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Jika tidak ditemukan, fallback ke pattern lama (awalan B)
|
|
||||||
if (!truckNumber) {
|
if (!truckNumber) {
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const match = line.match(/\bB\s*\d{3,5}\s*[A-Z]{2,4}\b/i);
|
const match = line.match(/\bB\s*\d{3,5}\s*[A-Z]{2,4}\b/i);
|
||||||
|
@ -697,14 +690,12 @@
|
||||||
|
|
||||||
console.log('Truck detection result:', truckNumber);
|
console.log('Truck detection result:', truckNumber);
|
||||||
|
|
||||||
// Assignment detection - ambil semua karakter setelah label "Penugasan"
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
console.log(`Processing line for assignment: "${line}"`);
|
console.log(`Processing line for assignment: "${line}"`);
|
||||||
|
|
||||||
if (line.toLowerCase().includes('penugasan')) {
|
if (line.toLowerCase().includes('penugasan')) {
|
||||||
console.log('Found penugasan line:', line);
|
console.log('Found penugasan line:', line);
|
||||||
|
|
||||||
// Contoh: "Penugasan : JAKARTA TIMUR" atau "Penugasan: UPST DLH"
|
|
||||||
const match = line.match(/penugasan\s*:?\s*(.+)/i);
|
const match = line.match(/penugasan\s*:?\s*(.+)/i);
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
assignment = match[1].trim();
|
assignment = match[1].trim();
|
||||||
|
@ -716,7 +707,6 @@
|
||||||
|
|
||||||
console.log('Assignment detection result:', assignment);
|
console.log('Assignment detection result:', assignment);
|
||||||
|
|
||||||
// Enhanced time patterns - prioritize YYYY-MM-DD HH:MM:SS format
|
|
||||||
const timePatterns = [
|
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{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
|
||||||
|
@ -725,63 +715,102 @@
|
||||||
/(\d{1,2}-\d{1,2}-\d{4}\s+\d{1,2}:\d{2}:\d{2})/gi, // 04-08-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
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const convertDateFormat = (dateString) => {
|
||||||
|
const monthMap = {
|
||||||
|
'jan': '01', 'january': '01', 'januari': '01',
|
||||||
|
'feb': '02', 'february': '02', 'februari': '02',
|
||||||
|
'mar': '03', 'march': '03', 'maret': '03',
|
||||||
|
'apr': '04', 'april': '04', 'april': '04',
|
||||||
|
'may': '05', 'may': '05', 'mei': '05',
|
||||||
|
'jun': '06', 'june': '06', 'juni': '06',
|
||||||
|
'jul': '07', 'july': '07', 'juli': '07',
|
||||||
|
'aug': '08', 'august': '08', 'agustus': '08',
|
||||||
|
'sep': '09', 'september': '09', 'september': '09',
|
||||||
|
'oct': '10', 'october': '10', 'oktober': '10',
|
||||||
|
'nov': '11', 'november': '11', 'november': '11',
|
||||||
|
'dec': '12', 'december': '12', 'desember': '12'
|
||||||
|
};
|
||||||
|
|
||||||
|
const match = dateString.match(/(\d{1,2})\s+(\w{3,})\s+(\d{4}),?\s*(\d{1,2}:\d{2}:\d{2})/i);
|
||||||
|
if (match) {
|
||||||
|
const day = match[1].padStart(2, '0');
|
||||||
|
const monthName = match[2].toLowerCase();
|
||||||
|
const year = match[3];
|
||||||
|
const time = match[4];
|
||||||
|
|
||||||
|
const month = monthMap[monthName];
|
||||||
|
if (month) {
|
||||||
|
return `${year}-${month}-${day}, ${time}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateString;
|
||||||
|
};
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
console.log(`Processing line for time: "${line}"`);
|
console.log(`Processing line for time: "${line}"`);
|
||||||
|
|
||||||
// Entry time - look for "Masuk :" pattern
|
|
||||||
if (line.toLowerCase().includes('masuk') && line.includes(':')) {
|
if (line.toLowerCase().includes('masuk') && line.includes(':')) {
|
||||||
console.log('Found masuk line:', line);
|
console.log('Found masuk line:', line);
|
||||||
|
|
||||||
// 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);
|
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]) {
|
if (masukMatch && masukMatch[1]) {
|
||||||
entryTime = masukMatch[1].trim();
|
entryTime = masukMatch[1].trim();
|
||||||
console.log(`Found entry time via masuk pattern: ${entryTime} (format: YYYY-MM-DD HH:MM:SS)`);
|
console.log(`Found entry time via masuk pattern: ${entryTime} (format: YYYY-MM-DD HH:MM:SS)`);
|
||||||
} else {
|
} else {
|
||||||
// Fallback to general patterns
|
|
||||||
for (const pattern of timePatterns) {
|
for (const pattern of timePatterns) {
|
||||||
const match = line.match(pattern);
|
const match = line.match(pattern);
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
entryTime = match[1].trim();
|
const rawTime = match[1].trim();
|
||||||
console.log(`Found entry time: ${entryTime}`);
|
entryTime = convertDateFormat(rawTime);
|
||||||
|
console.log(`Found entry time: ${rawTime} -> converted to: ${entryTime}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Also try to extract after "Masuk : "
|
|
||||||
|
if (!entryTime) {
|
||||||
const masukMatch = line.match(/masuk\s*:\s*(.+)/i);
|
const masukMatch = line.match(/masuk\s*:\s*(.+)/i);
|
||||||
if (masukMatch && masukMatch[1] && !entryTime) {
|
if (masukMatch && masukMatch[1]) {
|
||||||
entryTime = masukMatch[1].trim();
|
const rawTime = masukMatch[1].trim();
|
||||||
console.log(`Found entry time via masuk pattern: ${entryTime}`);
|
entryTime = convertDateFormat(rawTime);
|
||||||
|
console.log(`Found entry time via masuk pattern: ${rawTime} -> converted to: ${entryTime}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit time - look for "Keluar :" pattern
|
|
||||||
if (line.toLowerCase().includes('keluar') && line.includes(':')) {
|
if (line.toLowerCase().includes('keluar') && line.includes(':')) {
|
||||||
console.log('Found keluar line:', line);
|
console.log('Found keluar line:', line);
|
||||||
|
|
||||||
// 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);
|
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]) {
|
if (keluarMatch && keluarMatch[1]) {
|
||||||
exitTime = keluarMatch[1].trim();
|
exitTime = keluarMatch[1].trim();
|
||||||
console.log(`Found exit time via keluar pattern: ${exitTime} (format: YYYY-MM-DD HH:MM:SS)`);
|
console.log(`Found exit time via keluar pattern: ${exitTime} (format: YYYY-MM-DD HH:MM:SS)`);
|
||||||
} else {
|
} else {
|
||||||
// Fallback to general patterns
|
|
||||||
for (const pattern of timePatterns) {
|
for (const pattern of timePatterns) {
|
||||||
const match = line.match(pattern);
|
const match = line.match(pattern);
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
exitTime = match[1].trim();
|
const rawTime = match[1].trim();
|
||||||
console.log(`Found exit time: ${exitTime}`);
|
exitTime = convertDateFormat(rawTime);
|
||||||
|
console.log(`Found exit time: ${rawTime} -> converted to: ${exitTime}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!exitTime) {
|
||||||
|
const keluarMatch = line.match(/keluar\s*:\s*(.+)/i);
|
||||||
|
if (keluarMatch && keluarMatch[1]) {
|
||||||
|
const rawTime = keluarMatch[1].trim();
|
||||||
|
exitTime = convertDateFormat(rawTime);
|
||||||
|
console.log(`Found exit time via keluar pattern: ${rawTime} -> converted to: ${exitTime}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Time detection results:', { entryTime, exitTime });
|
console.log('Time detection results:', { entryTime, exitTime });
|
||||||
|
|
||||||
// Enhanced weight patterns - more specific for Indonesian receipts
|
|
||||||
const weightPatterns = [
|
const weightPatterns = [
|
||||||
/berat\s+masuk\s*:\s*(\d+)\s*kg/gi, // Berat Masuk : 23280 kg
|
/berat\s+masuk\s*:\s*(\d+)\s*kg/gi, // Berat Masuk : 23280 kg
|
||||||
/berat\s+keluar\s*:\s*(\d+)\s*kg/gi, // Berat Keluar : 13540 kg
|
/berat\s+keluar\s*:\s*(\d+)\s*kg/gi, // Berat Keluar : 13540 kg
|
||||||
|
@ -820,11 +849,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Berat Keluar - look for exact pattern "Berat Keluar : 13540 kg"
|
|
||||||
if (lowerLine.includes('berat keluar')) {
|
if (lowerLine.includes('berat keluar')) {
|
||||||
console.log('Found berat keluar line:', line);
|
console.log('Found berat keluar line:', line);
|
||||||
|
|
||||||
// Try specific pattern first
|
|
||||||
const specificMatch = line.match(/berat\s+keluar\s*:\s*(\d+)\s*kg/gi);
|
const specificMatch = line.match(/berat\s+keluar\s*:\s*(\d+)\s*kg/gi);
|
||||||
if (specificMatch) {
|
if (specificMatch) {
|
||||||
const numbers = specificMatch[0].match(/(\d+)/);
|
const numbers = specificMatch[0].match(/(\d+)/);
|
||||||
|
@ -833,7 +860,6 @@
|
||||||
console.log(`Found weight out via specific pattern: ${weightOut} (removed kg)`);
|
console.log(`Found weight out via specific pattern: ${weightOut} (removed kg)`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Try general patterns
|
|
||||||
for (const pattern of weightPatterns) {
|
for (const pattern of weightPatterns) {
|
||||||
const match = line.match(pattern);
|
const match = line.match(pattern);
|
||||||
if (match && match[1] && parseInt(match[1]) > 1000) {
|
if (match && match[1] && parseInt(match[1]) > 1000) {
|
||||||
|
|
|
@ -27,13 +27,16 @@
|
||||||
--color-amber-50: oklch(98.7% 0.022 95.277);
|
--color-amber-50: oklch(98.7% 0.022 95.277);
|
||||||
--color-amber-200: oklch(92.4% 0.12 95.746);
|
--color-amber-200: oklch(92.4% 0.12 95.746);
|
||||||
--color-amber-400: oklch(82.8% 0.189 84.429);
|
--color-amber-400: oklch(82.8% 0.189 84.429);
|
||||||
|
--color-amber-500: oklch(76.9% 0.188 70.08);
|
||||||
--color-amber-600: oklch(66.6% 0.179 58.318);
|
--color-amber-600: oklch(66.6% 0.179 58.318);
|
||||||
--color-amber-700: oklch(55.5% 0.163 48.998);
|
--color-amber-700: oklch(55.5% 0.163 48.998);
|
||||||
--color-yellow-50: oklch(98.7% 0.026 102.212);
|
--color-yellow-50: oklch(98.7% 0.026 102.212);
|
||||||
|
--color-yellow-100: oklch(97.3% 0.071 103.193);
|
||||||
--color-yellow-200: oklch(94.5% 0.129 101.54);
|
--color-yellow-200: oklch(94.5% 0.129 101.54);
|
||||||
--color-yellow-400: oklch(85.2% 0.199 91.936);
|
--color-yellow-400: oklch(85.2% 0.199 91.936);
|
||||||
--color-yellow-500: oklch(79.5% 0.184 86.047);
|
--color-yellow-500: oklch(79.5% 0.184 86.047);
|
||||||
--color-yellow-600: oklch(68.1% 0.162 75.834);
|
--color-yellow-600: oklch(68.1% 0.162 75.834);
|
||||||
|
--color-yellow-700: oklch(55.4% 0.135 66.442);
|
||||||
--color-yellow-800: oklch(47.6% 0.114 61.907);
|
--color-yellow-800: oklch(47.6% 0.114 61.907);
|
||||||
--color-green-50: oklch(98.2% 0.018 155.826);
|
--color-green-50: oklch(98.2% 0.018 155.826);
|
||||||
--color-green-100: oklch(96.2% 0.044 156.743);
|
--color-green-100: oklch(96.2% 0.044 156.743);
|
||||||
|
@ -837,6 +840,9 @@
|
||||||
.h-50 {
|
.h-50 {
|
||||||
height: calc(var(--spacing) * 50);
|
height: calc(var(--spacing) * 50);
|
||||||
}
|
}
|
||||||
|
.h-60 {
|
||||||
|
height: calc(var(--spacing) * 60);
|
||||||
|
}
|
||||||
.h-64 {
|
.h-64 {
|
||||||
height: calc(var(--spacing) * 64);
|
height: calc(var(--spacing) * 64);
|
||||||
}
|
}
|
||||||
|
@ -1388,6 +1394,9 @@
|
||||||
border-color: color-mix(in oklab, var(--color-white) 30%, transparent);
|
border-color: color-mix(in oklab, var(--color-white) 30%, transparent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.border-yellow-100 {
|
||||||
|
border-color: var(--color-yellow-100);
|
||||||
|
}
|
||||||
.border-yellow-200 {
|
.border-yellow-200 {
|
||||||
border-color: var(--color-yellow-200);
|
border-color: var(--color-yellow-200);
|
||||||
}
|
}
|
||||||
|
@ -1538,6 +1547,9 @@
|
||||||
.bg-yellow-50 {
|
.bg-yellow-50 {
|
||||||
background-color: var(--color-yellow-50);
|
background-color: var(--color-yellow-50);
|
||||||
}
|
}
|
||||||
|
.bg-yellow-100 {
|
||||||
|
background-color: var(--color-yellow-100);
|
||||||
|
}
|
||||||
.bg-yellow-400 {
|
.bg-yellow-400 {
|
||||||
background-color: var(--color-yellow-400);
|
background-color: var(--color-yellow-400);
|
||||||
}
|
}
|
||||||
|
@ -1632,6 +1644,27 @@
|
||||||
--tw-gradient-from: var(--color-white);
|
--tw-gradient-from: var(--color-white);
|
||||||
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
}
|
}
|
||||||
|
.from-yellow-50 {
|
||||||
|
--tw-gradient-from: var(--color-yellow-50);
|
||||||
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
|
}
|
||||||
|
.from-yellow-400 {
|
||||||
|
--tw-gradient-from: var(--color-yellow-400);
|
||||||
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
|
}
|
||||||
|
.from-yellow-500 {
|
||||||
|
--tw-gradient-from: var(--color-yellow-500);
|
||||||
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
|
}
|
||||||
|
.from-yellow-600 {
|
||||||
|
--tw-gradient-from: var(--color-yellow-600);
|
||||||
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
|
}
|
||||||
|
.via-amber-50 {
|
||||||
|
--tw-gradient-via: var(--color-amber-50);
|
||||||
|
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
|
||||||
|
--tw-gradient-stops: var(--tw-gradient-via-stops);
|
||||||
|
}
|
||||||
.via-blue-50 {
|
.via-blue-50 {
|
||||||
--tw-gradient-via: var(--color-blue-50);
|
--tw-gradient-via: var(--color-blue-50);
|
||||||
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
|
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
|
||||||
|
@ -1662,6 +1695,14 @@
|
||||||
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
|
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
|
||||||
--tw-gradient-stops: var(--tw-gradient-via-stops);
|
--tw-gradient-stops: var(--tw-gradient-via-stops);
|
||||||
}
|
}
|
||||||
|
.to-amber-500 {
|
||||||
|
--tw-gradient-to: var(--color-amber-500);
|
||||||
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
|
}
|
||||||
|
.to-amber-600 {
|
||||||
|
--tw-gradient-to: var(--color-amber-600);
|
||||||
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
|
}
|
||||||
.to-blue-200 {
|
.to-blue-200 {
|
||||||
--tw-gradient-to: var(--color-blue-200);
|
--tw-gradient-to: var(--color-blue-200);
|
||||||
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
|
@ -1761,6 +1802,9 @@
|
||||||
.bg-clip-text {
|
.bg-clip-text {
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
}
|
}
|
||||||
|
.bg-center {
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
.object-contain {
|
.object-contain {
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
@ -2139,6 +2183,9 @@
|
||||||
.text-yellow-600 {
|
.text-yellow-600 {
|
||||||
color: var(--color-yellow-600);
|
color: var(--color-yellow-600);
|
||||||
}
|
}
|
||||||
|
.text-yellow-700 {
|
||||||
|
color: var(--color-yellow-700);
|
||||||
|
}
|
||||||
.text-yellow-800 {
|
.text-yellow-800 {
|
||||||
color: var(--color-yellow-800);
|
color: var(--color-yellow-800);
|
||||||
}
|
}
|
||||||
|
@ -2636,16 +2683,6 @@
|
||||||
outline-style: none;
|
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 {
|
.lg\:max-w-sm {
|
||||||
@media (width >= 64rem) {
|
@media (width >= 64rem) {
|
||||||
max-width: var(--container-sm);
|
max-width: var(--container-sm);
|
||||||
|
|
Loading…
Reference in New Issue