unit history added

This commit is contained in:
serversdwn
2025-12-16 04:38:06 +00:00
parent 191dceff2b
commit d97999e26f
4 changed files with 351 additions and 1 deletions

View File

@@ -178,6 +178,14 @@
<p id="viewNote" class="mt-1 text-gray-900 dark:text-white whitespace-pre-wrap">--</p>
</div>
<!-- Unit History Timeline -->
<div class="border-t border-gray-200 dark:border-gray-700 pt-6">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Timeline</h3>
<div id="historyTimeline" class="space-y-3">
<p class="text-sm text-gray-500 dark:text-gray-400">Loading history...</p>
</div>
</div>
<!-- Photos -->
<div class="border-t border-gray-200 dark:border-gray-700 pt-6">
<div class="flex justify-between items-start mb-4">
@@ -762,9 +770,130 @@ async function uploadPhoto(file) {
}
}
// Load and display unit history timeline
async function loadUnitHistory() {
try {
const response = await fetch(`/api/roster/history/${unitId}`);
if (!response.ok) {
throw new Error('Failed to load history');
}
const data = await response.json();
const timeline = document.getElementById('historyTimeline');
if (data.history && data.history.length > 0) {
timeline.innerHTML = '';
data.history.forEach(entry => {
const timelineEntry = createTimelineEntry(entry);
timeline.appendChild(timelineEntry);
});
} else {
timeline.innerHTML = '<p class="text-sm text-gray-500 dark:text-gray-400">No history yet. Changes will appear here.</p>';
}
} catch (error) {
console.error('Error loading history:', error);
document.getElementById('historyTimeline').innerHTML = '<p class="text-sm text-red-500">Failed to load history</p>';
}
}
// Create a timeline entry element
function createTimelineEntry(entry) {
const div = document.createElement('div');
div.className = 'flex gap-3 p-3 rounded-lg bg-gray-50 dark:bg-slate-700/50';
// Icon based on change type
const icons = {
'note_change': `<svg class="w-5 h-5 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>`,
'deployed_change': `<svg class="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>`,
'retired_change': `<svg class="w-5 h-5 text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>`
};
const icon = icons[entry.change_type] || `<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>`;
// Format change description
let description = '';
if (entry.change_type === 'note_change') {
description = `<strong>Note changed</strong>`;
if (entry.old_value) {
description += `<br><span class="text-xs text-gray-500 dark:text-gray-400">From: "${entry.old_value}"</span>`;
}
if (entry.new_value) {
description += `<br><span class="text-xs text-gray-600 dark:text-gray-300">To: "${entry.new_value}"</span>`;
}
} else if (entry.change_type === 'deployed_change') {
description = `<strong>Status changed to ${entry.new_value}</strong>`;
} else if (entry.change_type === 'retired_change') {
description = `<strong>Marked as ${entry.new_value}</strong>`;
} else {
description = `<strong>${entry.field_name} changed</strong>`;
if (entry.old_value && entry.new_value) {
description += `<br><span class="text-xs text-gray-500 dark:text-gray-400">${entry.old_value}${entry.new_value}</span>`;
}
}
// Format timestamp
const timestamp = new Date(entry.changed_at).toLocaleString();
div.innerHTML = `
<div class="flex-shrink-0">
${icon}
</div>
<div class="flex-1 min-w-0">
<div class="text-sm text-gray-900 dark:text-white">
${description}
</div>
<div class="text-xs text-gray-500 dark:text-gray-400 mt-1">
${timestamp}
${entry.source !== 'manual' ? `<span class="ml-2 px-2 py-0.5 bg-gray-200 dark:bg-gray-600 rounded text-xs">${entry.source}</span>` : ''}
</div>
</div>
<div class="flex-shrink-0">
<button onclick="deleteHistoryEntry(${entry.id})" class="text-gray-400 hover:text-red-500 transition-colors" title="Delete this history entry">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
</button>
</div>
`;
return div;
}
// Delete a history entry
async function deleteHistoryEntry(historyId) {
if (!confirm('Are you sure you want to delete this history entry?')) {
return;
}
try {
const response = await fetch(`/api/roster/history/${historyId}`, {
method: 'DELETE'
});
if (response.ok) {
// Reload history
await loadUnitHistory();
} else {
const result = await response.json();
alert(`Error: ${result.detail || 'Unknown error'}`);
}
} catch (error) {
alert(`Error: ${error.message}`);
}
}
// Load data when page loads
loadUnitData().then(() => {
loadPhotos();
loadUnitHistory();
});
</script>
{% endblock %}