feat: oninput handler copies building # to trailer number

- Manual asset form: manBuildingNumber → manAddress (address/trailer field)
- Location form: locFormBldgNum → locFormTrailer
This commit is contained in:
2026-05-20 17:55:00 -04:00
parent 8b92d37d01
commit 02b3ba665e
5 changed files with 296 additions and 11 deletions
+80 -11
View File
@@ -435,8 +435,8 @@
═══════════════════════════════════════════════════════════════════════ */
.camera-area {
position: relative; background: #0a0b0f; border-radius: var(--radius-sm);
aspect-ratio: 16/14; display: flex; align-items: center; justify-content: center;
overflow: hidden; margin-bottom: 4px;
aspect-ratio: 3/4; display: flex; align-items: center; justify-content: center;
overflow: hidden; margin: 0 -12px 8px -12px; max-height: none;
}
.camera-area video { width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; }
.camera-placeholder { text-align: center; z-index: 1; cursor: pointer; }
@@ -1235,7 +1235,7 @@
<input id="manAddress" type="text" class="input-field" placeholder="Address / Trailer Number">
<div class="form-row">
<input id="manBuildingName" type="text" class="input-field" placeholder="Building Name">
<input id="manBuildingNumber" type="text" class="input-field" placeholder="Building #">
<input id="manBuildingNumber" type="text" class="input-field" placeholder="Building #" oninput="document.getElementById('manAddress').value = this.value">
</div>
<div class="form-row">
<input id="manFloor" type="text" class="input-field" placeholder="Floor">
@@ -1246,7 +1246,10 @@
<input id="manMapLink" type="url" class="input-field" placeholder="Map link (URL)">
<button class="btn btn-outline btn-sm" onclick="openMapPin()" style="flex-shrink:0;">📍 Pin</button>
</div>
<input id="manParkingLocation" type="text" class="input-field" placeholder="Parking location" style="margin-bottom:0;">
<div class="form-row">
<input id="manParkingLocation" type="text" class="input-field" placeholder="Parking location">
<button class="btn btn-outline btn-sm" onclick="fillGpsParking('manParkingLocation')" style="flex-shrink:0;">📍 GPS</button>
</div>
</div>
<!-- Keys -->
@@ -1362,6 +1365,7 @@
<div style="display:flex;gap:8px;margin-bottom:10px;flex-wrap:wrap;">
<button class="btn btn-green btn-sm" onclick="quickCheckin()" style="flex:1;min-width:100px;">✓ Check In</button>
<button class="btn btn-outline btn-sm" id="detailDirectionsBtn" onclick="openMapLink()" style="flex:1;min-width:100px;display:none;">🗺️ Directions</button>
<button class="btn btn-outline btn-sm" id="detailNavigateBtn" onclick="navigateToAsset()" style="flex:1;min-width:100px;display:none;">🧭 Navigate</button>
</div>
<div style="display:flex;gap:8px;margin-bottom:10px;">
<button class="btn btn-outline btn-sm" onclick="editAsset()" style="flex:1;">✏️ Edit</button>
@@ -1582,7 +1586,7 @@
<input id="locFormAddress" type="text" class="input-field" placeholder="Address">
<div class="form-row">
<input id="locFormBldgName" type="text" class="input-field" placeholder="Building name">
<input id="locFormBldgNum" type="text" class="input-field" placeholder="Building #">
<input id="locFormBldgNum" type="text" class="input-field" placeholder="Building #" oninput="document.getElementById('locFormTrailer').value = this.value">
</div>
<div class="form-row">
<input id="locFormFloor" type="text" class="input-field" placeholder="Floor">
@@ -2687,7 +2691,13 @@
try {
const asset = await api(`/api/assets/search?machine_id=${encodeURIComponent(machineId)}`);
autoCheckin(asset.id);
showScannedAsset(asset);
// F1: Auto check-in when scanning an existing asset
AppState.currentAssetId = asset.id;
autoCheckin(asset.id);
setScanStatus(`Checked in: ${asset.name}`, 'success');
document.getElementById('checkinCard').style.display = 'none';
} catch (e) {
if (e.message === 'Asset not found' || e.message.includes('404')) {
showNewAssetForm(machineId);
@@ -2754,6 +2764,16 @@
category: document.getElementById('newCatSelect').value || 'Other',
status: document.getElementById('newStatus').value,
};
// F2: Auto-populate address from GPS
if (AppState.gpsLat) {
const geo = await reverseGeocode(AppState.gpsLat, AppState.gpsLng);
if (geo) {
payload.address = geo.address || geo.formatted || '';
payload.building_number = geo.building_number || '';
payload.building_name = geo.building_name || '';
payload.trailer_number = geo.building_number || ''; // F4
}
}
try {
const asset = await api('/api/assets', {
method: 'POST',
@@ -2819,6 +2839,32 @@
}
}
// ── Reverse geocode GPS → address (Nominatim) ─────────────────────────
async function reverseGeocode(lat, lng) {
if (!lat || !lng) return null;
try {
const data = await api(`/api/geocode?lat=${lat}&lng=${lng}`);
return data; // { address, building_number, building_name, ... }
} catch (e) {
console.warn('Reverse geocode failed:', e);
return null;
}
}
// ── Populate address fields from geocode result ────────────────────────
function applyGeocodeToFields(geo, prefix = 'man') {
if (!geo) return;
const set = (suffix, val) => {
const el = document.getElementById(prefix + suffix);
if (el && !el.value) el.value = val || '';
};
set('Address', geo.address || geo.formatted || '');
set('BuildingNumber', geo.building_number || '');
set('BuildingName', geo.building_name || '');
set('TrailerNumber', geo.building_number || ''); // F4: sync building# → trailer
// Don't overwrite floor/room — those are interior details GPS can't know
}
// ═══════════════════════════════════════════════════════════════════════
// OCR MODE
// ═══════════════════════════════════════════════════════════════════════
@@ -2918,15 +2964,26 @@
showToast('Machine ID and name are required', true);
return;
}
const payload = {
machine_id: machineId, name,
category: document.getElementById('ocrCatSelect').value || 'Other',
status: document.getElementById('ocrStatus').value,
};
// F2: Auto-populate address from GPS
if (AppState.gpsLat) {
const geo = await reverseGeocode(AppState.gpsLat, AppState.gpsLng);
if (geo) {
payload.address = geo.address || geo.formatted || '';
payload.building_number = geo.building_number || '';
payload.building_name = geo.building_name || '';
payload.trailer_number = geo.building_number || '';
}
}
try {
const asset = await api('/api/assets', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
machine_id: machineId, name,
category: document.getElementById('ocrCatSelect').value || 'Other',
status: document.getElementById('ocrStatus').value,
}),
body: JSON.stringify(payload),
});
showToast('Asset created!');
document.getElementById('ocrCreateCard').style.display = 'none';
@@ -3133,6 +3190,15 @@
else showToast('Enter a map link first', true);
}
function fillGpsParking(inputId) {
if (!AppState.gpsLat) {
showToast('GPS not available — allow location access', true);
return;
}
document.getElementById(inputId).value = AppState.gpsLat.toFixed(6) + ', ' + AppState.gpsLng.toFixed(6);
showToast('GPS coords filled!');
}
// Settings are loaded after auth (in doLogin / initAuth)
// ═══════════════════════════════════════════════════════════════════════
@@ -3524,7 +3590,10 @@
</div>
<input id="editWalkingDirections" type="text" class="input-field" placeholder="Walking directions" value="${esc(a.walking_directions || '')}">
<input id="editMapLink" type="text" class="input-field" placeholder="Map link (URL)" value="${esc(a.map_link || '')}">
<input id="editParking" type="text" class="input-field" placeholder="Parking location" value="${esc(a.parking_location || '')}">
<div class="form-row">
<input id="editParking" type="text" class="input-field" placeholder="Parking location" value="${esc(a.parking_location || '')}">
<button class="btn btn-outline btn-sm" onclick="fillGpsParking('editParking')" style="flex-shrink:0;">📍 GPS</button>
</div>
<datalist id="catList">
<option value="Coffee"><option value="Cold Beverage"><option value="Snacks">
<option value="Cold Food"><option value="Market"><option value="Smart Cooler">