v0.2 fleet overhaul
This commit is contained in:
@@ -69,6 +69,14 @@
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange"
|
||||
placeholder="BE1234">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Device Type *</label>
|
||||
<select name="device_type" id="deviceTypeSelect" onchange="toggleDeviceFields()"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
<option value="seismograph">Seismograph</option>
|
||||
<option value="modem">Modem</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Unit Type</label>
|
||||
<input type="text" name="unit_type" value="series3"
|
||||
@@ -86,9 +94,52 @@
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange"
|
||||
placeholder="San Francisco, CA">
|
||||
</div>
|
||||
|
||||
<!-- Seismograph-specific fields -->
|
||||
<div id="seismographFields" class="space-y-4 border-t border-gray-200 dark:border-gray-700 pt-4">
|
||||
<p class="text-sm font-semibold text-gray-700 dark:text-gray-300">Seismograph Information</p>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Last Calibrated</label>
|
||||
<input type="date" name="last_calibrated"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Next Calibration Due</label>
|
||||
<input type="date" name="next_calibration_due"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Typically 1 year after last calibration</p>
|
||||
</div>
|
||||
<div id="modemPairingField" class="hidden">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Deployed With Modem</label>
|
||||
<input type="text" name="deployed_with_modem_id" placeholder="Modem ID"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Only needed when deployed</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modem-specific fields -->
|
||||
<div id="modemFields" class="hidden space-y-4 border-t border-gray-200 dark:border-gray-700 pt-4">
|
||||
<p class="text-sm font-semibold text-gray-700 dark:text-gray-300">Modem Information</p>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">IP Address</label>
|
||||
<input type="text" name="ip_address" placeholder="192.168.1.100"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Phone Number</label>
|
||||
<input type="text" name="phone_number" placeholder="+1-555-0123"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Hardware Model</label>
|
||||
<input type="text" name="hardware_model" placeholder="e.g., Raven XTV"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" name="deployed" value="true" checked
|
||||
<input type="checkbox" name="deployed" id="deployedCheckbox" value="true" checked onchange="toggleModemPairing()"
|
||||
class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Deployed</span>
|
||||
</label>
|
||||
@@ -111,6 +162,118 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Unit Modal -->
|
||||
<div id="editUnitModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl max-w-lg w-full mx-4 max-h-[90vh] overflow-y-auto">
|
||||
<div class="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">Edit Unit</h2>
|
||||
<button onclick="closeEditUnitModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
||||
<svg class="w-6 h-6" 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>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<form id="editUnitForm" hx-post="" hx-swap="none" class="p-6 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Unit ID</label>
|
||||
<input type="text" name="id" id="editUnitId" readonly
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Device Type *</label>
|
||||
<select name="device_type" id="editDeviceTypeSelect" onchange="toggleEditDeviceFields()"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
<option value="seismograph">Seismograph</option>
|
||||
<option value="modem">Modem</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Unit Type</label>
|
||||
<input type="text" name="unit_type" id="editUnitType"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Project ID</label>
|
||||
<input type="text" name="project_id" id="editProjectId"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Location</label>
|
||||
<input type="text" name="location" id="editLocation"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
|
||||
<!-- Seismograph-specific fields -->
|
||||
<div id="editSeismographFields" class="space-y-4 border-t border-gray-200 dark:border-gray-700 pt-4">
|
||||
<p class="text-sm font-semibold text-gray-700 dark:text-gray-300">Seismograph Information</p>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Last Calibrated</label>
|
||||
<input type="date" name="last_calibrated" id="editLastCalibrated"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Next Calibration Due</label>
|
||||
<input type="date" name="next_calibration_due" id="editNextCalibrationDue"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
<div id="editModemPairingField" class="hidden">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Deployed With Modem</label>
|
||||
<input type="text" name="deployed_with_modem_id" id="editDeployedWithModemId" placeholder="Modem ID"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modem-specific fields -->
|
||||
<div id="editModemFields" class="hidden space-y-4 border-t border-gray-200 dark:border-gray-700 pt-4">
|
||||
<p class="text-sm font-semibold text-gray-700 dark:text-gray-300">Modem Information</p>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">IP Address</label>
|
||||
<input type="text" name="ip_address" id="editIpAddress" placeholder="192.168.1.100"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Phone Number</label>
|
||||
<input type="text" name="phone_number" id="editPhoneNumber" placeholder="+1-555-0123"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Hardware Model</label>
|
||||
<input type="text" name="hardware_model" id="editHardwareModel" placeholder="e.g., Raven XTV"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" name="deployed" id="editDeployedCheckbox" value="true" onchange="toggleEditModemPairing()"
|
||||
class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Deployed</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" name="retired" id="editRetiredCheckbox" value="true"
|
||||
class="w-4 h-4 text-seismo-orange focus:ring-seismo-orange rounded">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Retired</span>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Notes</label>
|
||||
<textarea name="note" id="editNote" rows="3"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-slate-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-seismo-orange"></textarea>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button type="submit" class="flex-1 px-4 py-2 bg-seismo-orange hover:bg-orange-600 text-white rounded-lg transition-colors">
|
||||
Save Changes
|
||||
</button>
|
||||
<button type="button" onclick="closeEditUnitModal()" class="px-4 py-2 bg-gray-300 dark:bg-gray-600 hover:bg-gray-400 dark:hover:bg-gray-500 text-gray-700 dark:text-white rounded-lg transition-colors">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Import CSV Modal -->
|
||||
<div id="importModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div class="bg-white dark:bg-slate-800 rounded-xl shadow-2xl max-w-lg w-full mx-4">
|
||||
@@ -153,6 +316,7 @@
|
||||
// Add Unit Modal
|
||||
function openAddUnitModal() {
|
||||
document.getElementById('addUnitModal').classList.remove('hidden');
|
||||
toggleDeviceFields(); // Initialize field visibility
|
||||
}
|
||||
|
||||
function closeAddUnitModal() {
|
||||
@@ -160,6 +324,35 @@
|
||||
document.getElementById('addUnitForm').reset();
|
||||
}
|
||||
|
||||
// Toggle device-specific fields based on device type selection
|
||||
function toggleDeviceFields() {
|
||||
const deviceType = document.getElementById('deviceTypeSelect').value;
|
||||
const seismoFields = document.getElementById('seismographFields');
|
||||
const modemFields = document.getElementById('modemFields');
|
||||
|
||||
if (deviceType === 'seismograph') {
|
||||
seismoFields.classList.remove('hidden');
|
||||
modemFields.classList.add('hidden');
|
||||
toggleModemPairing(); // Check if modem pairing should be shown
|
||||
} else {
|
||||
seismoFields.classList.add('hidden');
|
||||
modemFields.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle modem pairing field visibility (only for deployed seismographs)
|
||||
function toggleModemPairing() {
|
||||
const deviceType = document.getElementById('deviceTypeSelect').value;
|
||||
const deployedCheckbox = document.getElementById('deployedCheckbox');
|
||||
const modemPairingField = document.getElementById('modemPairingField');
|
||||
|
||||
if (deviceType === 'seismograph' && deployedCheckbox.checked) {
|
||||
modemPairingField.classList.remove('hidden');
|
||||
} else {
|
||||
modemPairingField.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Add unknown unit to roster
|
||||
function addUnknownUnit(unitId) {
|
||||
openAddUnitModal();
|
||||
@@ -167,6 +360,8 @@
|
||||
document.querySelector('#addUnitForm input[name="id"]').value = unitId;
|
||||
// Set deployed to true by default
|
||||
document.querySelector('#addUnitForm input[name="deployed"]').checked = true;
|
||||
// Trigger field visibility updates
|
||||
toggleModemPairing(); // Show modem pairing field for deployed seismographs
|
||||
}
|
||||
|
||||
// Ignore unknown unit
|
||||
@@ -220,6 +415,184 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Edit Unit Modal Functions
|
||||
function openEditUnitModal() {
|
||||
document.getElementById('editUnitModal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
function closeEditUnitModal() {
|
||||
document.getElementById('editUnitModal').classList.add('hidden');
|
||||
document.getElementById('editUnitForm').reset();
|
||||
}
|
||||
|
||||
// Toggle device-specific fields in edit modal
|
||||
function toggleEditDeviceFields() {
|
||||
const deviceType = document.getElementById('editDeviceTypeSelect').value;
|
||||
const seismoFields = document.getElementById('editSeismographFields');
|
||||
const modemFields = document.getElementById('editModemFields');
|
||||
|
||||
if (deviceType === 'seismograph') {
|
||||
seismoFields.classList.remove('hidden');
|
||||
modemFields.classList.add('hidden');
|
||||
toggleEditModemPairing();
|
||||
} else {
|
||||
seismoFields.classList.add('hidden');
|
||||
modemFields.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle modem pairing field in edit modal
|
||||
function toggleEditModemPairing() {
|
||||
const deviceType = document.getElementById('editDeviceTypeSelect').value;
|
||||
const deployedCheckbox = document.getElementById('editDeployedCheckbox');
|
||||
const modemPairingField = document.getElementById('editModemPairingField');
|
||||
|
||||
if (deviceType === 'seismograph' && deployedCheckbox.checked) {
|
||||
modemPairingField.classList.remove('hidden');
|
||||
} else {
|
||||
modemPairingField.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Edit Unit - Fetch data and populate form
|
||||
async function editUnit(unitId) {
|
||||
try {
|
||||
const response = await fetch(`/api/roster/${unitId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch unit data');
|
||||
}
|
||||
|
||||
const unit = await response.json();
|
||||
|
||||
// Populate form fields
|
||||
document.getElementById('editUnitId').value = unit.id;
|
||||
document.getElementById('editDeviceTypeSelect').value = unit.device_type;
|
||||
document.getElementById('editUnitType').value = unit.unit_type;
|
||||
document.getElementById('editProjectId').value = unit.project_id;
|
||||
document.getElementById('editLocation').value = unit.location;
|
||||
document.getElementById('editNote').value = unit.note;
|
||||
|
||||
// Checkboxes
|
||||
document.getElementById('editDeployedCheckbox').checked = unit.deployed;
|
||||
document.getElementById('editRetiredCheckbox').checked = unit.retired;
|
||||
|
||||
// Seismograph fields
|
||||
document.getElementById('editLastCalibrated').value = unit.last_calibrated;
|
||||
document.getElementById('editNextCalibrationDue').value = unit.next_calibration_due;
|
||||
document.getElementById('editDeployedWithModemId').value = unit.deployed_with_modem_id;
|
||||
|
||||
// Modem fields
|
||||
document.getElementById('editIpAddress').value = unit.ip_address;
|
||||
document.getElementById('editPhoneNumber').value = unit.phone_number;
|
||||
document.getElementById('editHardwareModel').value = unit.hardware_model;
|
||||
|
||||
// Set form action
|
||||
document.getElementById('editUnitForm').setAttribute('hx-post', `/api/roster/edit/${unitId}`);
|
||||
|
||||
// Show/hide fields based on device type
|
||||
toggleEditDeviceFields();
|
||||
|
||||
// Open modal
|
||||
openEditUnitModal();
|
||||
} catch (error) {
|
||||
alert(`Error loading unit data: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Edit Unit form submission
|
||||
document.getElementById('editUnitForm').addEventListener('htmx:afterRequest', function(event) {
|
||||
if (event.detail.successful) {
|
||||
closeEditUnitModal();
|
||||
// Trigger roster refresh
|
||||
htmx.trigger(document.querySelector('[hx-get="/partials/roster-table"]'), 'load');
|
||||
alert('Unit updated successfully!');
|
||||
} else {
|
||||
alert('Error updating unit. Please check the form and try again.');
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle Deployed Status
|
||||
async function toggleDeployed(unitId, deployed) {
|
||||
const action = deployed ? 'deploy' : 'bench';
|
||||
if (!confirm(`Are you sure you want to ${action} unit ${unitId}?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('deployed', deployed);
|
||||
|
||||
const response = await fetch(`/api/roster/set-deployed/${unitId}`, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Trigger roster refresh
|
||||
htmx.trigger(document.querySelector('[hx-get="/partials/roster-table"]'), 'load');
|
||||
alert(`Unit ${deployed ? 'deployed' : 'benched'} successfully!`);
|
||||
} else {
|
||||
const result = await response.json();
|
||||
alert(`Error: ${result.detail || 'Unknown error'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Move to Ignore List
|
||||
async function moveToIgnore(unitId) {
|
||||
const reason = prompt(`Why are you ignoring unit ${unitId}?`, '');
|
||||
if (reason === null) {
|
||||
return; // User cancelled
|
||||
}
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('reason', reason);
|
||||
|
||||
const response = await fetch(`/api/roster/ignore/${unitId}`, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Trigger roster refresh
|
||||
htmx.trigger(document.querySelector('[hx-get="/partials/roster-table"]'), 'load');
|
||||
alert(`Unit ${unitId} moved to ignore list`);
|
||||
} else {
|
||||
const result = await response.json();
|
||||
alert(`Error: ${result.detail || 'Unknown error'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Unit
|
||||
async function deleteUnit(unitId) {
|
||||
if (!confirm(`Are you sure you want to PERMANENTLY delete unit ${unitId}?\n\nThis action cannot be undone!`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/roster/${unitId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Trigger roster refresh
|
||||
htmx.trigger(document.querySelector('[hx-get="/partials/roster-table"]'), 'load');
|
||||
alert(`Unit ${unitId} deleted successfully`);
|
||||
} else {
|
||||
const result = await response.json();
|
||||
alert(`Error: ${result.detail || 'Unknown error'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle CSV Import
|
||||
document.getElementById('importForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user