No notifications
You're all caught up
}
return window.firebase;
}
const firebase = initializeFirebase();
const db = firebase.database();
const auth = firebase.auth();
let companyId = null;
let currentUser = null;
let reservationsCache = {};
let itemsCache = {};
document.addEventListener('DOMContentLoaded', () => {
auth.onAuthStateChanged(async user => {
if (!user) { return; }
currentUser = user;
const userSnap = await db.ref(`users/${user.uid}`).once('value');
const userData = userSnap.val() || {};
companyId = userData.company;
if (!companyId) return;
initTopbar();
await preloadReferenceData();
await loadReservations();
});
document.getElementById('sidebarToggle').addEventListener('click', () => {
document.getElementById('sidebar').classList.toggle('collapsed');
document.getElementById('mainContent').classList.toggle('sidebar-collapsed');
});
document.getElementById('userProfileBtn').addEventListener('click', (e) => {
e.stopPropagation();
document.querySelector('.profile-dropdown').classList.toggle('show');
});
document.addEventListener('click', () => {
document.querySelector('.profile-dropdown').classList.remove('show');
});
document.getElementById('logoutBtn').addEventListener('click', async (e) => { e.preventDefault(); await auth.signOut(); });
document.getElementById('searchBox').addEventListener('input', renderReservations);
document.getElementById('statusFilter').addEventListener('change', renderReservations);
document.getElementById('closeApprovalModal').addEventListener('click', closeApprovalModal);
document.getElementById('approveBtn').addEventListener('click', () => handleDecision('approved'));
document.getElementById('rejectBtn').addEventListener('click', () => handleDecision('rejected'));
});
// Use AuthManager and menu authorization as in project-list.html
function initializeMenuSystem() {
window.authManager = new AuthManager();
initializeMenuAuthorization();
}
async function preloadReferenceData() {
const iSnap = await db.ref(`companies/${companyId}/inventory/items`).once('value');
itemsCache = iSnap.val() || {};
}
async function loadReservations() {
const snap = await db.ref(`companies/${companyId}/inventory/reservations`).once('value');
reservationsCache = snap.val() || {};
renderReservations();
}
function statusBadge(status) {
if (status === 'pending' || !status) return '
Pending';
if (status === 'approved') return '
Approved';
if (status === 'rejected') return '
Rejected';
return `
${status}`;
}
function renderReservations() {
const tbody = document.getElementById('reservationsBody');
const q = (document.getElementById('searchBox').value || '').toLowerCase();
const statusFilter = document.getElementById('statusFilter').value;
let list = Object.entries(reservationsCache).map(([id, r]) => ({ id, ...r }));
if (statusFilter !== 'all') list = list.filter(r => (r.status || 'pending') === statusFilter);
if (q) list = list.filter(r => (r.reservationNumber || '').toLowerCase().includes(q) || (r.requesterName || '').toLowerCase().includes(q));
if (!list.length) { tbody.innerHTML = '
| No reservations found |
'; return; }
list.sort((a,b) => (b.createdAt || 0) - (a.createdAt || 0));
tbody.innerHTML = list.map(r => {
const itemsCount = r.items ? Object.keys(r.items).length : 0;
const date = r.createdAt ? new Date(r.createdAt).toLocaleString() : '-';
const canReview = (r.status || 'pending') === 'pending';
return `
| ${r.reservationNumber || r.id} |
${r.requesterName || r.requester || '—'} |
${itemsCount} |
${(r.priority || 'normal').toString().toUpperCase()} |
${statusBadge(r.status || 'pending')} |
${date} |
${canReview ? `` : '-'} |
`;
}).join('');
}
function openApprovalModal(resId) {
const r = Object.values(reservationsCache).find(x => x.id === resId) || reservationsCache[resId];
if (!r) return;
r.id = r.id || resId;
document.getElementById('modalResNo').textContent = r.reservationNumber || r.id;
const container = document.getElementById('reservationDetails');
const lines = Object.entries(r.items || {}).map(([itemId, line]) => {
const item = itemsCache[itemId] || {};
const qty = Number(line.quantity || line.qty || 0);
const uom = item.uom || 'EA';
return `
${item.name || line.name || ('Item ' + itemId)}SKU: ${item.sku || item.code || itemId}
${qty} ${uom}
`;
}).join('') || '
No items.
';
const meta = `
Requester
${r.requesterName || r.requester || '—'}
Priority
${(r.priority || 'normal').toString().toUpperCase()}
Needed by
${r.neededDate ? new Date(r.neededDate).toLocaleDateString() : '—'}
Submitted
${r.createdAt ? new Date(r.createdAt).toLocaleString() : '—'}
`;
container.innerHTML = meta + `
${lines}
`;
document.getElementById('approvalModal').style.display = 'flex';
document.getElementById('approvalModal').dataset.resId = r.id;
document.getElementById('modalAlertError').style.display = 'none';
document.getElementById('modalAlertOk').style.display = 'none';
}
function closeApprovalModal() {
document.getElementById('approvalModal').style.display = 'none';
document.getElementById('approvalModal').dataset.resId = '';
}
async function handleDecision(decision) {
const resId = document.getElementById('approvalModal').dataset.resId;
if (!resId) return;
const errorBox = document.getElementById('modalAlertError');
const okBox = document.getElementById('modalAlertOk');
errorBox.style.display = 'none';
okBox.style.display = 'none';
try {
const now = Date.now();
const updates = {};
updates[`companies/${companyId}/inventory/reservations/${resId}/status`] = decision;
if (decision === 'approved') {
updates[`companies/${companyId}/inventory/reservations/${resId}/approvedBy`] = { uid: currentUser.uid, email: currentUser.email };
updates[`companies/${companyId}/inventory/reservations/${resId}/approvedAt`] = now;
} else {
updates[`companies/${companyId}/inventory/reservations/${resId}/rejectedBy`] = { uid: currentUser.uid, email: currentUser.email };
updates[`companies/${companyId}/inventory/reservations/${resId}/rejectedAt`] = now;
}
updates[`companies/${companyId}/inventory/reservations/${resId}/approvalTrail/${now}`] = { action: decision, by: { uid: currentUser.uid, email: currentUser.email }, at: now };
await db.ref().update(updates);
// reload
await loadReservations();
okBox.textContent = decision === 'approved' ? 'Reservation approved.' : 'Reservation rejected.';
okBox.style.display = 'block';
setTimeout(() => closeApprovalModal(), 900);
} catch (err) {
console.error(err);
errorBox.textContent = 'Operation failed. Please try again.';
errorBox.style.display = 'block';
}
}