feat: DO-gated manual batch mode (auto default, office-only switches)
ci / smoke (push) Canceled after 0s
ci / smoke (push) Canceled after 0s
This commit is contained in:
1 parent
735e0c7594
commit
0efcfa45b8
20 files changed
+3128
-209
No files matched your search
+100
-24
@@ -305,14 +305,17 @@
|
||||
<div class="active-batch-card">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;">
|
||||
<h3 class="card-label" style="margin-bottom: 0;">Active Batch</h3>
|
||||
<!-- Mode Switcher -->
|
||||
<!-- Mode Switcher: auto | do_manual | manual -->
|
||||
<div style="display: inline-flex; background: var(--bg-base); border: 1px solid var(--border-color); border-radius: 20px; padding: 2px;">
|
||||
<button id="btnModeManual" class="btn btn-sm" style="padding: 4px 10px; font-size: 11px; border-radius: 18px; border: none; background: var(--accent-blue); color: #fff; font-weight: 600;" onclick="switchBatchMode('manual')">
|
||||
<i class="fa-solid fa-hand"></i> Manual
|
||||
</button>
|
||||
<button id="btnModeAuto" class="btn btn-sm" style="padding: 4px 10px; font-size: 11px; border-radius: 18px; border: none; background: transparent; color: var(--text-secondary);" onclick="switchBatchMode('auto')">
|
||||
<i class="fa-solid fa-robot"></i> Otomatis
|
||||
</button>
|
||||
<button id="btnModeDoManual" class="btn btn-sm" style="padding: 4px 10px; font-size: 11px; border-radius: 18px; border: none; background: transparent; color: var(--text-secondary);" onclick="switchBatchMode('do_manual')">
|
||||
<i class="fa-solid fa-file-lines"></i> DO Manual
|
||||
</button>
|
||||
<button id="btnModeManual" class="btn btn-sm" style="padding: 4px 10px; font-size: 11px; border-radius: 18px; border: none; background: transparent; color: var(--text-secondary);" onclick="switchBatchMode('manual')">
|
||||
<i class="fa-solid fa-hand"></i> Manual
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -347,6 +350,24 @@
|
||||
<span class="meta-key">Pipeline Speed</span>
|
||||
<span class="meta-value"><span id="liveFps">--</span> FPS</span>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<span class="meta-key">OCR Engine</span>
|
||||
<span class="meta-value">
|
||||
<select id="monOcrEngine" style="padding:4px 8px;border-radius:8px;border:1px solid var(--border-color);background:var(--bg-base);color:var(--text-primary);font-size:12px;" onchange="setOcrEngine(this.value)">
|
||||
<option value="tesseract">tesseract</option>
|
||||
<option value="paddle">paddle</option>
|
||||
<option value="none">none</option>
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<span class="meta-key">Require plat</span>
|
||||
<span class="meta-value">
|
||||
<label style="font-weight:400;">
|
||||
<input type="checkbox" id="monRequirePlate" onchange="setRequirePlate(this.checked)"> wajib
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Manual Batch Control Buttons for Office Dashboard (Hidden when in Auto Mode) -->
|
||||
@@ -613,7 +634,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
let currentBatchMode = 'manual';
|
||||
let currentBatchMode = 'auto';
|
||||
|
||||
async function switchBatchMode(mode) {
|
||||
try {
|
||||
@@ -625,6 +646,12 @@
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
updateModeUI(data.mode);
|
||||
} else if (res.status === 409) {
|
||||
alert(data.error || 'Batch sedang berjalan — hentikan dulu.');
|
||||
} else if (res.status === 403) {
|
||||
alert(data.error || 'Hanya dari port kantor.');
|
||||
} else {
|
||||
alert(data.error || 'Gagal ganti mode');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to switch batch mode:', e);
|
||||
@@ -647,46 +674,95 @@
|
||||
currentBatchMode = mode;
|
||||
const btnManual = document.getElementById('btnModeManual');
|
||||
const btnAuto = document.getElementById('btnModeAuto');
|
||||
const btnDo = document.getElementById('btnModeDoManual');
|
||||
const modeText = document.getElementById('liveBatchModeText');
|
||||
const manualSection = document.getElementById('manualControlSection');
|
||||
const autoHint = document.getElementById('autoModeHint');
|
||||
|
||||
if (mode === 'auto') {
|
||||
btnAuto.style.background = 'var(--accent-blue)';
|
||||
btnAuto.style.color = '#fff';
|
||||
btnAuto.style.fontWeight = '600';
|
||||
btnManual.style.background = 'transparent';
|
||||
btnManual.style.color = 'var(--text-secondary)';
|
||||
btnManual.style.fontWeight = 'normal';
|
||||
if (modeText) {
|
||||
const setActive = (el, on) => {
|
||||
if (!el) return;
|
||||
if (on) {
|
||||
el.style.background = 'var(--accent-blue)';
|
||||
el.style.color = '#fff';
|
||||
el.style.fontWeight = '600';
|
||||
} else {
|
||||
el.style.background = 'transparent';
|
||||
el.style.color = 'var(--text-secondary)';
|
||||
el.style.fontWeight = 'normal';
|
||||
}
|
||||
};
|
||||
setActive(btnAuto, mode === 'auto');
|
||||
setActive(btnDo, mode === 'do_manual');
|
||||
setActive(btnManual, mode === 'manual');
|
||||
|
||||
if (modeText) {
|
||||
if (mode === 'auto') {
|
||||
modeText.textContent = 'Otomatis (AI Truk)';
|
||||
modeText.style.color = 'var(--accent-orange)';
|
||||
}
|
||||
if (manualSection) manualSection.style.display = 'none';
|
||||
if (autoHint) autoHint.style.display = 'block';
|
||||
} else {
|
||||
btnManual.style.background = 'var(--accent-blue)';
|
||||
btnManual.style.color = '#fff';
|
||||
btnManual.style.fontWeight = '600';
|
||||
btnAuto.style.background = 'transparent';
|
||||
btnAuto.style.color = 'var(--text-secondary)';
|
||||
btnAuto.style.fontWeight = 'normal';
|
||||
if (modeText) {
|
||||
} else if (mode === 'do_manual') {
|
||||
modeText.textContent = 'DO Manual (Scan Surat)';
|
||||
modeText.style.color = 'var(--accent-blue)';
|
||||
} else {
|
||||
modeText.textContent = 'Manual (Tombol)';
|
||||
modeText.style.color = 'var(--accent-blue)';
|
||||
}
|
||||
}
|
||||
if (mode === 'auto') {
|
||||
if (manualSection) manualSection.style.display = 'none';
|
||||
if (autoHint) autoHint.style.display = 'block';
|
||||
} else {
|
||||
if (manualSection) manualSection.style.display = 'flex';
|
||||
if (autoHint) autoHint.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDoSettings() {
|
||||
try {
|
||||
const res = await fetch('/api/do/settings');
|
||||
const data = await res.json();
|
||||
if (!data.success) return;
|
||||
const sel = document.getElementById('monOcrEngine');
|
||||
if (sel && data.ocr_engine) sel.value = data.ocr_engine;
|
||||
const chk = document.getElementById('monRequirePlate');
|
||||
if (chk) chk.checked = !!data.require_plate;
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
async function setOcrEngine(eng) {
|
||||
try {
|
||||
const res = await fetch('/api/do/settings', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ocr_engine: eng })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!data.success) alert(data.error || 'Gagal set OCR');
|
||||
loadDoSettings();
|
||||
} catch (e) { alert('Gagal terhubung'); loadDoSettings(); }
|
||||
}
|
||||
|
||||
async function setRequirePlate(val) {
|
||||
try {
|
||||
const res = await fetch('/api/do/settings', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ require_plate: val })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!data.success) alert(data.error || 'Gagal set require_plate');
|
||||
loadDoSettings();
|
||||
} catch (e) { alert('Gagal terhubung'); loadDoSettings(); }
|
||||
}
|
||||
|
||||
// Start interval pollings
|
||||
setInterval(pollCurrentBatch, 2000);
|
||||
setInterval(pollFinalizedBatch, 3000);
|
||||
setInterval(checkBatchMode, 5000);
|
||||
setInterval(loadDoSettings, 8000);
|
||||
|
||||
// Initial first load
|
||||
checkBatchMode();
|
||||
loadDoSettings();
|
||||
pollCurrentBatch();
|
||||
pollFinalizedBatch();
|
||||
</script>
|
||||
|
||||
Reference in new issue
Block a user