Files
core/internal/api/ui_lots.tmpl

208 lines
7.3 KiB
Cheetah

{{define "lots"}}
<!DOCTYPE html>
<html lang="en">
{{template "head" .}}
<body>
{{template "topbar" .}}
{{template "breadcrumbs" .}}
<main class="container">
<section class="card">
<h2>Create or Update Mapping</h2>
<div class="grid cols-3">
<div>
<label for="modelInput">Model</label>
<input id="modelInput" class="input" type="text" placeholder="e.g. ST12000NM0008" />
</div>
<div>
<label for="lotSelect">LOT (existing)</label>
<select id="lotSelect" class="input">
<option value="">Select LOT</option>
{{range .Lots}}
<option value="{{.Code}}">{{.Code}}</option>
{{end}}
</select>
</div>
<div>
<label for="lotCodeInput">LOT (manual code)</label>
<input id="lotCodeInput" class="input" type="text" placeholder="e.g. LOT-HDD-12TB" />
</div>
</div>
<div class="controls" style="margin-top: 12px;">
<button id="saveMapping" class="button" type="button">Save Mapping</button>
</div>
<div id="statusMessage" class="meta" style="margin-top: 12px;"></div>
</section>
<section class="card">
<h2>Current Mappings</h2>
{{if .LotMappings}}
<table class="table">
<thead>
<tr>
<th>Model</th>
<th>LOT code</th>
<th>Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{range .LotMappings}}
<tr>
<td><button class="button ghost" type="button" onclick='openEditModal({{printf "%q" .Model}}, {{printf "%q" .LotCode}})'>{{.Model}}</button></td>
<td><button class="button ghost" type="button" onclick='openEditModal({{printf "%q" .Model}}, {{printf "%q" .LotCode}})'>{{.LotCode}}</button></td>
<td title="{{formatTimeFull .UpdatedAt}}">{{formatTime .UpdatedAt}}</td>
<td>
<button class="button ghost" type="button" onclick='openEditModal({{printf "%q" .Model}}, {{printf "%q" .LotCode}})'>Edit</button>
<button class="button ghost" type="button" onclick='deleteMapping({{printf "%q" .Model}})'>Delete</button>
</td>
</tr>
{{end}}
</tbody>
</table>
{{else}}
<div class="meta">No mappings yet.</div>
{{end}}
</section>
</main>
<div id="editModal" style="display:none; position:fixed; inset:0; background:rgba(0,0,0,0.35); z-index:1000;">
<div class="card" style="max-width:980px; margin:60px auto; position:relative;">
<h2>Edit Mapping</h2>
<div class="grid cols-2">
<div>
<label for="modalModelInput">Model</label>
<input id="modalModelInput" class="input" type="text" style="width:100%; min-height:52px; font-size:28px; line-height:1.2;" />
</div>
<div>
<label for="modalLotCodeInput">LOT code</label>
<input id="modalLotCodeInput" class="input" type="text" style="width:100%; min-height:52px; font-size:28px; line-height:1.2;" />
</div>
</div>
<div class="controls" style="margin-top:12px;">
<button class="button" type="button" onclick="saveModalMapping()">Save</button>
<button class="button ghost" type="button" onclick="closeEditModal()">Cancel</button>
</div>
</div>
</div>
<script>
let editingOriginalModel = "";
function normalizeValue(value) {
const trimmed = (value || "").trim();
if (trimmed.length >= 2) {
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
return trimmed.slice(1, -1).trim();
}
}
return trimmed;
}
function mappingModel() {
return normalizeValue(document.getElementById("modelInput").value);
}
function mappingLotCode() {
const manual = normalizeValue(document.getElementById("lotCodeInput").value);
if (manual !== "") return manual;
return normalizeValue(document.getElementById("lotSelect").value);
}
function setStatus(text, isError) {
const el = document.getElementById("statusMessage");
el.textContent = text;
el.style.color = isError ? "#c81d25" : "#4f772d";
}
async function saveMapping() {
const model = mappingModel();
const lotCode = mappingLotCode();
if (!model) {
setStatus("Model is required.", true);
return;
}
if (!lotCode) {
setStatus("LOT code is required.", true);
return;
}
const response = await fetch("/lot-mappings/" + encodeURIComponent(model), {
method: "PUT",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({lot_code: lotCode})
});
const payload = await response.json().catch(() => ({}));
if (!response.ok) {
setStatus(payload.error || "Failed to save mapping.", true);
return;
}
setStatus("Saved. Updated parts: " + (payload.affected_parts_count || 0), false);
window.location.reload();
}
function openEditModal(model, lotCode) {
editingOriginalModel = normalizeValue(model);
document.getElementById("modalModelInput").value = normalizeValue(model);
document.getElementById("modalLotCodeInput").value = normalizeValue(lotCode);
document.getElementById("editModal").style.display = "block";
}
function closeEditModal() {
document.getElementById("editModal").style.display = "none";
editingOriginalModel = "";
}
async function saveModalMapping() {
const nextModel = normalizeValue(document.getElementById("modalModelInput").value);
const nextLotCode = normalizeValue(document.getElementById("modalLotCodeInput").value);
if (!nextModel || !nextLotCode) {
alert("Model and LOT code are required.");
return;
}
const putResp = await fetch("/lot-mappings/" + encodeURIComponent(nextModel), {
method: "PUT",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({lot_code: nextLotCode})
});
const putPayload = await putResp.json().catch(() => ({}));
if (!putResp.ok) {
alert(putPayload.error || "Failed to save mapping.");
return;
}
if (editingOriginalModel && editingOriginalModel !== nextModel) {
const delResp = await fetch("/lot-mappings/" + encodeURIComponent(editingOriginalModel), {
method: "DELETE"
});
if (!delResp.ok) {
const delPayload = await delResp.json().catch(() => ({}));
alert(delPayload.error || "Failed to replace old mapping.");
return;
}
}
closeEditModal();
window.location.reload();
}
async function deleteMapping(model) {
if (!confirm("Delete mapping for model '" + model + "'?")) {
return;
}
const response = await fetch("/lot-mappings/" + encodeURIComponent(model), {method: "DELETE"});
const payload = await response.json().catch(() => ({}));
if (!response.ok) {
setStatus(payload.error || "Failed to delete mapping.", true);
return;
}
setStatus("Deleted. Updated parts: " + (payload.affected_parts_count || 0), false);
window.location.reload();
}
document.getElementById("saveMapping").addEventListener("click", saveMapping);
</script>
</body>
</html>
{{end}}