update: guid tester
parent
46229fccbb
commit
d8684446e6
|
@ -7,6 +7,8 @@ namespace eSPJ.Controllers.SpjAdminController;
|
||||||
[Route("admin")]
|
[Route("admin")]
|
||||||
public class SpjAdminController : Controller
|
public class SpjAdminController : Controller
|
||||||
{
|
{
|
||||||
|
private static readonly Guid DummySpjGuid = new Guid("9f5b8f3a-1c2d-4a5b-9a7c-1234567890ab");
|
||||||
|
private const string DummySpjNumber = "SPJ/08-2025/PKM/000519";
|
||||||
|
|
||||||
[HttpGet("")]
|
[HttpGet("")]
|
||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
|
@ -32,4 +34,58 @@ public class SpjAdminController : Controller
|
||||||
ViewData["Id"] = id;
|
ViewData["Id"] = id;
|
||||||
return View("~/Views/Admin/Transport/SpjAdmin/History/Details.cshtml");
|
return View("~/Views/Admin/Transport/SpjAdmin/History/Details.cshtml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
[HttpPost("scan/process/{id:guid}", Name = "Admin_Scan_Process")]
|
||||||
|
public IActionResult Process(Guid id)
|
||||||
|
{
|
||||||
|
// Dummy rule: hanya GUID dummy yang dianggap valid
|
||||||
|
if (id == DummySpjGuid)
|
||||||
|
{
|
||||||
|
return Json(new { success = true, data = new { id, status = "Valid" } });
|
||||||
|
}
|
||||||
|
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]
|
||||||
|
[HttpPost("scan/resolve", Name = "Admin_Scan_Resolve")]
|
||||||
|
public IActionResult Resolve([FromForm] string code)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(code))
|
||||||
|
{
|
||||||
|
return Json(new { success = false, message = "Kode kosong." });
|
||||||
|
}
|
||||||
|
|
||||||
|
code = code.Trim();
|
||||||
|
|
||||||
|
// Jika sudah GUID, langsung kembalikan (tetap izinkan test langsung GUID dummy)
|
||||||
|
if (Guid.TryParse(code, out var guid))
|
||||||
|
{
|
||||||
|
return Json(new { success = true, id = guid });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pola SPJ
|
||||||
|
var isSpj = System.Text.RegularExpressions.Regex.IsMatch(
|
||||||
|
code,
|
||||||
|
@"^SPJ/\d{2}-\d{4}/[A-Z]+/\d{6}$",
|
||||||
|
System.Text.RegularExpressions.RegexOptions.IgnoreCase
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isSpj)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
if (string.Equals(code, DummySpjNumber, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return Json(new { success = true, id = DummySpjGuid });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(new { success = false, message = "SPJ tidak ditemukan." });
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
|
|
||||||
<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="#" novalidate>
|
||||||
@Html.AntiForgeryToken()
|
@Html.AntiForgeryToken()
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<input type="text"
|
<input type="text"
|
||||||
|
@ -427,95 +427,88 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processScanCode(code) {
|
isGuid(value) {
|
||||||
|
const re = /^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$/;
|
||||||
|
return re.test((value || '').trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
isSpjCode(value) {
|
||||||
|
const re = /^SPJ\/\d{2}-\d{4}\/[A-Z]+\/\d{6}$/i;
|
||||||
|
return re.test((value || '').trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
processUrlFor(id) {
|
||||||
|
const basePath = '@Url.Content("~/admin/scan/process/")';
|
||||||
|
return basePath + encodeURIComponent(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
postGuid(id) {
|
||||||
|
const url = this.processUrlFor(id);
|
||||||
|
return $.ajax({
|
||||||
|
url: url,
|
||||||
|
type: 'POST',
|
||||||
|
headers: { 'RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val() }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveSpj(code) {
|
||||||
|
const url = '@Url.Content("~/admin/scan/resolve")';
|
||||||
|
return $.ajax({
|
||||||
|
url: url,
|
||||||
|
type: 'POST',
|
||||||
|
headers: { 'RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val() },
|
||||||
|
data: { code }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async processScanCode(code) {
|
||||||
this.showModal('loading', 'Memproses...', 'Sedang memverifikasi kode SPJ...', false);
|
this.showModal('loading', 'Memproses...', 'Sedang memverifikasi kode SPJ...', false);
|
||||||
|
|
||||||
// Testing mode - uncomment kalau testing udah selesai
|
try {
|
||||||
this.mockResponse(code);
|
let guid = null;
|
||||||
return;
|
const trimmed = (code || '').trim();
|
||||||
|
|
||||||
// ini bagian ajax yang asli
|
if (this.isGuid(trimmed)) {
|
||||||
/*
|
guid = trimmed;
|
||||||
$.ajax({
|
} else if (this.isSpjCode(trimmed)) {
|
||||||
url: @Url.Action("ProcessScan", "Scan")', // nanti tinggal ganti aja yaa
|
// minta backend resolve SPJ ke GUID
|
||||||
type: 'POST',
|
const rs = await this.resolveSpj(trimmed);
|
||||||
data: {
|
if (!rs || rs.success !== true || !rs.id) {
|
||||||
barcode: code
|
throw new Error((rs && rs.message) ? rs.message : 'Gagal resolve SPJ ke GUID.');
|
||||||
},
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
},
|
guid = rs.id;
|
||||||
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);
|
|
||||||
|
|
||||||
// Scanner tetap aktif, tidak perlu reset tombol
|
|
||||||
setTimeout(() => {
|
|
||||||
this.hideResult();
|
|
||||||
this.manualInput.value = '';
|
|
||||||
}, 3000);
|
|
||||||
} else {
|
} else {
|
||||||
console.log(`❌ Kode ${code} TIDAK VALID - menampilkan error`);
|
throw new Error('Format kode tidak dikenali. Masukkan GUID atau nomor SPJ yang valid.');
|
||||||
this.showErrorModal(code);
|
|
||||||
}
|
}
|
||||||
}, 1000);
|
|
||||||
|
// POST GUID ke process endpoint
|
||||||
|
const resp = await this.postGuid(guid);
|
||||||
|
console.debug('process response', resp);
|
||||||
|
if (resp && resp.success) {
|
||||||
|
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(trimmed, tanggal, waktu);
|
||||||
|
} else {
|
||||||
|
this.showModal('error', 'SPJ Tidak Ditemukan', (resp && resp.message) ? resp.message : 'Kode SPJ tidak ditemukan dalam database.', true);
|
||||||
|
}
|
||||||
|
} catch (xhrOrErr) {
|
||||||
|
let message = '';
|
||||||
|
if (xhrOrErr && xhrOrErr.responseJSON) {
|
||||||
|
message = xhrOrErr.responseJSON.message || 'Terjadi kesalahan saat memproses.';
|
||||||
|
} else if (xhrOrErr && xhrOrErr.responseText) {
|
||||||
|
message = xhrOrErr.responseText;
|
||||||
|
} else if (xhrOrErr instanceof Error) {
|
||||||
|
message = xhrOrErr.message;
|
||||||
|
} else {
|
||||||
|
message = 'Terjadi kesalahan saat memproses scan.';
|
||||||
|
}
|
||||||
|
console.error('processScanCode error', xhrOrErr);
|
||||||
|
this.showModal('error', 'Error', message, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async retryScan() {
|
async retryScan() {
|
||||||
this.hideResult();
|
this.hideResult();
|
||||||
this.hideError();
|
this.hideError();
|
||||||
|
|
|
@ -32,10 +32,12 @@
|
||||||
<div class="upload-area border-2 border-dashed border-gray-300 rounded-xl p-6 text-center hover:border-orange-400 transition-colors cursor-pointer" id="upload-area">
|
<div class="upload-area border-2 border-dashed border-gray-300 rounded-xl p-6 text-center hover:border-orange-400 transition-colors cursor-pointer" id="upload-area">
|
||||||
<input type="file" name="FotoKondisiKendaraan" accept="image/jpeg,image/png" class="hidden" id="foto-upload">
|
<input type="file" name="FotoKondisiKendaraan" accept="image/jpeg,image/png" class="hidden" id="foto-upload">
|
||||||
<label for="foto-upload" class="cursor-pointer w-full block">
|
<label for="foto-upload" class="cursor-pointer w-full block">
|
||||||
<div class="relative w-full h-32 bg-gray-100 rounded-xl flex items-center justify-center mx-auto mb-3 overflow-hidden preview-container" id="preview-container">
|
<div class="relative w-full h-60 bg-center bg-gray-100 rounded-xl flex items-center justify-center mx-auto mb-3 overflow-hidden preview-container" id="preview-container">
|
||||||
<div id="default-state">
|
<div id="default-state">
|
||||||
<div class="upload-icon-container">
|
<div class="upload-icon-container">
|
||||||
<i class="w-8 h-8 text-orange-600" data-lucide="upload-cloud" id="preview-icon"></i>
|
<img src="~/driver/images/trukk.jpg" alt="contoh gambar">
|
||||||
|
<p class="absolute inset-0 flex items-center justify-center text-white">Contoh Foto</p>
|
||||||
|
@* <i class="w-8 h-8 text-orange-600" data-lucide="upload-cloud" id="preview-icon"></i> *@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<img id="preview-image" src="#" alt="Preview" style="display:none;position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;" />
|
<img id="preview-image" src="#" alt="Preview" style="display:none;position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;" />
|
||||||
|
@ -73,6 +75,7 @@
|
||||||
<div class="location-badge rounded-2xl p-4">
|
<div class="location-badge rounded-2xl p-4">
|
||||||
<div class="flex items-center gap-2 mb-2">
|
<div class="flex items-center gap-2 mb-2">
|
||||||
<div class="w-8 h-8 bg-gradient-to-br from-blue-100 to-blue-200 rounded-full flex items-center justify-center">
|
<div class="w-8 h-8 bg-gradient-to-br from-blue-100 to-blue-200 rounded-full flex items-center justify-center">
|
||||||
|
|
||||||
<i class="w-4 h-4 text-blue-600" data-lucide="map-pin"></i>
|
<i class="w-4 h-4 text-blue-600" data-lucide="map-pin"></i>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm font-medium text-gray-700">Lokasi Anda:</span>
|
<span class="text-sm font-medium text-gray-700">Lokasi Anda:</span>
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 640 KiB |
Loading…
Reference in New Issue