BossierCityPhoneBooks/admin.html

640 lines
29 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Admin — Bossier City Phone Directory</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/bootstrap.min.css" rel="stylesheet">
<style>
@import url('https://fonts.googleapis.com/css?family=PT+Serif');
body { background: #eef2f7; font-family: 'PT Serif', serif; }
.site-header {
font-size: clamp(1.4rem, 4vw, 2.25rem);
font-weight: 700; text-align: center;
padding: 1rem 0 0.25rem; color: #1a1a2e;
}
.admin-badge {
display: inline-block; background: #e74c3c; color: #fff;
font-size: 0.7rem; font-weight: 700; letter-spacing: 0.08em;
text-transform: uppercase; padding: 0.15rem 0.55rem;
border-radius: 0.25rem; vertical-align: middle;
margin-left: 0.5rem; font-family: sans-serif;
}
.folder-chip {
display: inline-flex; align-items: center; gap: 0.4rem;
font-size: 0.78rem; background: #d4edda; color: #155724;
border: 1px solid #c3e6cb; border-radius: 2rem;
padding: 0.15rem 0.65rem; font-family: sans-serif;
}
.folder-chip button {
background: none; border: none; color: #155724; cursor: pointer;
padding: 0; font-size: 0.75rem; line-height: 1; opacity: 0.6;
}
.folder-chip button:hover { opacity: 1; }
/* Property tabs */
.property-tabs .nav-link {
font-family: 'PT Serif', serif; font-size: 1rem;
padding: 0.5rem 1.5rem; border-radius: 0.5rem 0.5rem 0 0;
background: #d8e4f5; color: #444; border: none;
}
.property-tabs .nav-link.active { background: #aaccfa; color: #000; font-weight: 700; }
.logo-bar {
background: #aaccfa; border-radius: 0 0.5rem 0.5rem 0.5rem;
padding: 0.75rem 1rem;
display: flex; align-items: center; justify-content: center; gap: 1rem;
min-height: 70px;
}
.logo-bar img { height: 44px; }
.btbc-logo { filter: invert(1); }
.bt-svg-flip { transform: scaleX(-1); }
.palm-flip { transform: scaleX(-1); }
/* Department panel */
.dept-panel {
background: #fff; border-radius: 0.5rem;
padding: 1rem 1.25rem 0.75rem;
box-shadow: 0 1px 4px rgba(0,0,0,0.07);
}
.dept-category-label {
font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
letter-spacing: 0.07em; color: #7a8fa8; margin-bottom: 0.35rem;
}
.dept-btn {
display: inline-block; font-family: 'PT Serif', serif; font-size: 0.88rem;
margin: 0.2rem 0.2rem 0.2rem 0; padding: 0.3rem 1rem;
border: 1.5px solid #aaccfa; border-radius: 2rem;
background: #f5f8ff; color: #334; cursor: pointer;
transition: background 0.12s, border-color 0.12s; line-height: 1.4;
}
.dept-btn:hover { background: #d2e6ff; border-color: #7aacf0; }
.dept-btn.active { background: #aaccfa; border-color: #5599e8; color: #000; font-weight: 700; }
/* Edit panel */
.edit-panel {
background: #fff; border-radius: 0.5rem;
padding: 1rem 1.25rem; box-shadow: 0 1px 4px rgba(0,0,0,0.07);
}
.edit-toolbar {
display: flex; flex-wrap: wrap; align-items: center;
justify-content: space-between; gap: 0.5rem; margin-bottom: 0.75rem;
}
.section-title { font-size: 1.15rem; font-weight: 700; color: #1a1a2e; margin: 0; }
#editTable thead { background: #aaccfa; }
#editTable thead th { font-weight: 700; font-size: 0.88rem; white-space: nowrap; }
#editTable tbody tr:hover { background: #f0f6ff; }
#editTable td { vertical-align: middle; padding: 0.35rem 0.5rem; }
.cell-input {
border: 1px solid transparent; background: transparent; width: 100%;
font-family: 'PT Serif', serif; font-size: 0.9rem;
padding: 0.2rem 0.35rem; border-radius: 0.3rem;
transition: border-color 0.1s, background 0.1s; color: #222;
}
.cell-input:focus {
outline: none; border-color: #5599e8; background: #fff;
box-shadow: 0 0 0 2px rgba(85,153,232,0.2);
}
.cell-input.changed { background: #fffbe6; border-color: #f0c040; }
.btn-delete-row {
color: #c0392b; background: none; border: none; cursor: pointer;
padding: 0.2rem 0.4rem; border-radius: 0.25rem; font-size: 1rem; line-height: 1;
}
.btn-delete-row:hover { background: #fde8e8; }
.btn-add-row { font-family: 'PT Serif', serif; }
#statusBar {
min-height: 1.8rem; font-size: 0.88rem;
padding: 0.3rem 0.5rem; border-radius: 0.35rem; display: none;
}
#statusBar.show { display: block; }
#statusBar.success { background: #d4edda; color: #155724; }
#statusBar.error { background: #f8d7da; color: #721c24; }
#statusBar.info { background: #d1ecf1; color: #0c5460; }
.unsaved-dot {
display: inline-block; width: 8px; height: 8px;
background: #e74c3c; border-radius: 50%;
margin-left: 0.4rem; vertical-align: middle;
}
.empty-state { text-align: center; padding: 3rem 1rem; color: #9ab; font-size: 1.05rem; }
/* Access modal */
#accessModal .modal-dialog { max-width: 420px; }
#accessModal .modal-body { padding: 2rem; text-align: center; }
#accessModal .modal-icon { font-size: 2.5rem; margin-bottom: 0.75rem; }
#accessModal h5 { font-weight: 700; margin-bottom: 0.5rem; }
#accessModal p { font-size: 0.92rem; color: #555; margin-bottom: 1.25rem; }
#accessModal code { background: #eef; padding: 0.1rem 0.4rem; border-radius: 0.25rem; font-size: 0.85rem; }
@media (max-width: 480px) {
.dept-btn { font-size: 0.8rem; padding: 0.25rem 0.7rem; }
}
</style>
</head>
<body>
<!-- ── Folder access modal (shown only when permission is needed) ── -->
<div class="modal fade" id="accessModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow">
<div class="modal-body">
<div class="modal-icon">📂</div>
<h5>Select the JSON folder</h5>
<p>
Grant the browser access to the <code>json</code> folder inside your site directory
so entries can be read and saved directly to disk.
</p>
<button class="btn btn-primary w-100 btn-add-row" id="btnModalOpen" onclick="openFolder()">
Open json Folder
</button>
<div id="modalError" class="mt-2 text-danger" style="font-size:0.85rem;display:none;"></div>
</div>
</div>
</div>
</div>
<!-- ── Page ── -->
<div class="container py-3">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-2 mb-1">
<h1 class="site-header mb-0">
Bossier City Phone Directory
<span class="admin-badge">Admin</span>
</h1>
<div class="d-flex align-items-center gap-2 mt-2 flex-wrap">
<span id="folderChip" style="display:none;" class="folder-chip">
<span id="folderChipName"></span>
<button onclick="changeFolder()" title="Change folder">&#8635;</button>
</span>
<a href="index.html" class="btn btn-sm btn-outline-secondary" style="font-family:'PT Serif',serif;">← Back</a>
</div>
</div>
<!-- Property Tabs -->
<ul class="nav property-tabs mt-3 mb-0" id="propertyTabs">
<li class="nav-item"><a class="nav-link active" href="#" data-property="btbc">Boomtown BC</a></li>
<li class="nav-item"><a class="nav-link" href="#" data-property="mvbc">Margaritaville</a></li>
</ul>
<!-- Logo Bar -->
<div class="logo-bar mb-3">
<div id="logo-btbc" class="d-flex align-items-center gap-3">
<img src="images/bt.svg" class="bt-svg-flip" alt="">
<img src="images/bbc-logo-283x93-white.png" class="btbc-logo" alt="Boomtown BC">
<img src="images/bt.svg" alt="">
</div>
<div id="logo-mvbc" class="d-flex align-items-center gap-3" style="display:none!important;">
<img src="images/Palm-tree.svg" class="palm-flip" alt="">
<img src="images/margaritaville.svg" style="height:50px;" alt="Margaritaville">
<img src="images/Palm-tree.svg" alt="">
</div>
</div>
<!-- Department Nav -->
<div class="dept-panel mb-3" id="deptNav"></div>
<!-- Edit Panel -->
<div class="edit-panel">
<div class="edit-toolbar">
<h2 class="section-title" id="sectionTitle">Select a department to edit</h2>
<div class="d-flex gap-2 flex-wrap">
<button class="btn btn-sm btn-success btn-add-row" id="btnAddRow" style="display:none;" onclick="addRow()">+ Add Entry</button>
<button class="btn btn-sm btn-primary btn-add-row" id="btnSave" style="display:none;" onclick="saveData()">Save Changes</button>
</div>
</div>
<div id="statusBar"></div>
<div id="tableContainer">
<div class="empty-state">Select a department above to edit its entries.</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
<script>
// ── Department manifest ───────────────────────────────────────────────────
const DEPARTMENTS = {
btbc: [
{ category: 'Casino Operations', items: [
{ label: 'Casino Operations', json: 'btbc-casino-ops.json' },
{ label: 'Security', json: 'btbc-security.json' },
{ label: 'Slots', json: 'btbc-slots.json' },
{ label: 'Slots Performance', json: 'btbc-slots-performance.json' },
{ label: 'Sports Betting', json: 'btbc-sport-betting.json' },
{ label: 'Surveillance', json: 'btbc-sureillance.json' },
{ label: 'Table Games', json: 'btbc-table-games.json' },
]},
{ category: 'Executive', items: [
{ label: 'Executive', json: 'btbc-executive.json' },
]},
{ category: 'Finance & Admin', items: [
{ label: 'Accounting', json: 'btbc-accounting.json' },
{ label: 'Cage', json: 'btbc-cage.json' },
{ label: 'Compliance', json: 'btbc-compliance.json' },
{ label: 'Count', json: 'btbc-count.json' },
{ label: 'Purchasing', json: 'btbc-purchasing.json' },
{ label: 'Receiving', json: 'btbc-receiving.json' },
]},
{ category: 'Food & Beverage', items: [
{ label: 'Food & Beverage', json: 'btbc-food-and-beverage.json' },
]},
{ category: 'Hotel', items: [
{ label: 'Hotel', json: 'btbc-hotel.json' },
]},
{ category: 'Human Resources', items: [
{ label: 'Human Resources', json: 'btbc-human-resources.json' },
]},
{ category: 'Sales & Marketing', items: [
{ label: 'Sales & Marketing', json: 'btbc-sales-and-marketing.json' },
{ label: 'Entertainment', json: 'btbc-entertainment.json' },
]},
{ category: 'Support Services', items: [
{ label: 'Facilities', json: 'btbc-facilties.json' },
{ label: 'Information Technology',json: 'btbc-information-technology.json' },
]},
],
mvbc: [
{ category: 'Casino Operations', items: [
{ label: 'Casino Operations', json: 'mvbc-casino-ops.json' },
{ label: 'Security', json: 'mvbc-security.json' },
{ label: 'Slots', json: 'mvbc-slots.json' },
{ label: 'Slots Performance', json: 'mvbc-slots-performance.json' },
{ label: 'Sports Betting', json: 'mvbc-sport-betting.json' },
{ label: 'Surveillance', json: 'mvbc-surveillance.json' },
{ label: 'Table Games', json: 'mvbc-table-games.json' },
]},
{ category: 'Executive', items: [
{ label: 'Executive', json: 'mvbc-executive.json' },
]},
{ category: 'Finance & Admin', items: [
{ label: 'Accounting', json: 'mvbc-accounting.json' },
{ label: 'Cage', json: 'mvbc-cage.json' },
{ label: 'Compliance', json: 'mvbc-compliance.json' },
{ label: 'Count', json: 'mvbc-count.json' },
{ label: 'Purchasing', json: 'mvbc-purchasing.json' },
{ label: 'Receiving', json: 'mvbc-receiving.json' },
]},
{ category: 'Food & Beverage', items: [
{ label: 'Food & Beverage', json: 'mvbc-food-and-beverage.json' },
]},
{ category: 'Hotel', items: [
{ label: 'Hotel', json: 'mvbc-hotel.json' },
]},
{ category: 'Human Resources', items: [
{ label: 'Human Resources', json: 'mvbc-human-resources.json' },
]},
{ category: 'Sales & Marketing', items: [
{ label: 'Sales & Marketing', json: 'mvbc-sales-and-marketing.json' },
{ label: 'Entertainment', json: 'mvbc-entertainment.json' },
]},
{ category: 'Support Services', items: [
{ label: 'Facilities', json: 'mvbc-facilties.json' },
{ label: 'Information Technology',json: 'mvbc-information-technology.json' },
]},
]
};
// ── State ─────────────────────────────────────────────────────────────────
let dirHandle = null;
let currentProperty = 'btbc';
let currentJsonFile = null;
let originalData = [];
let activeBtn = null;
let hasUnsaved = false;
const modal = new bootstrap.Modal(document.getElementById('accessModal'));
// ── IndexedDB helpers ─────────────────────────────────────────────────────
const IDB_NAME = 'phonebook-admin', IDB_STORE = 'handles', IDB_KEY = 'jsonDir';
function openIDB() {
return new Promise((res, rej) => {
const r = indexedDB.open(IDB_NAME, 1);
r.onupgradeneeded = e => e.target.result.createObjectStore(IDB_STORE);
r.onsuccess = e => res(e.target.result);
r.onerror = e => rej(e.target.error);
});
}
async function idbGet(key) {
const db = await openIDB();
return new Promise((res, rej) => {
const tx = db.transaction(IDB_STORE, 'readonly');
const r = tx.objectStore(IDB_STORE).get(key);
r.onsuccess = () => res(r.result);
r.onerror = e => rej(e.target.error);
});
}
async function idbSet(key, value) {
const db = await openIDB();
return new Promise((res, rej) => {
const tx = db.transaction(IDB_STORE, 'readwrite');
tx.objectStore(IDB_STORE).put(value, key);
tx.oncomplete = res;
tx.onerror = e => rej(e.target.error);
});
}
async function idbDel(key) {
const db = await openIDB();
return new Promise((res, rej) => {
const tx = db.transaction(IDB_STORE, 'readwrite');
tx.objectStore(IDB_STORE).delete(key);
tx.oncomplete = res;
tx.onerror = e => rej(e.target.error);
});
}
// ── Folder activation ─────────────────────────────────────────────────────
function activateHandle(handle) {
dirHandle = handle;
modal.hide();
document.getElementById('folderChip').style.display = '';
document.getElementById('folderChipName').textContent = handle.name;
renderDeptNav(currentProperty);
}
// Called from the modal button
async function openFolder() {
const errEl = document.getElementById('modalError');
errEl.style.display = 'none';
try {
const handle = await window.showDirectoryPicker({ mode: 'readwrite' });
await idbSet(IDB_KEY, handle);
activateHandle(handle);
} catch (err) {
if (err.name !== 'AbortError') {
errEl.textContent = 'Could not open folder: ' + err.message;
errEl.style.display = '';
}
}
}
// Called from the ↺ chip button — clears stored handle and re-prompts
async function changeFolder() {
if (hasUnsaved && !confirm('You have unsaved changes. Discard them?')) return;
await idbDel(IDB_KEY);
dirHandle = null;
document.getElementById('folderChip').style.display = 'none';
document.getElementById('modalError').style.display = 'none';
modal.show();
}
// On page load: auto-restore saved handle
(async () => {
if (!('showDirectoryPicker' in window)) {
// Unsupported browser — replace modal body
document.querySelector('#accessModal .modal-body').innerHTML =
'<div class="modal-icon">⚠️</div><h5>Browser not supported</h5>' +
'<p>The File System Access API requires <strong>Chrome</strong> or <strong>Edge</strong> 86+.</p>';
modal.show();
return;
}
try {
const saved = await idbGet(IDB_KEY);
if (!saved) { modal.show(); return; }
const perm = await saved.queryPermission({ mode: 'readwrite' });
if (perm === 'granted') {
// Already have permission — activate with no user interaction
activateHandle(saved);
} else if (perm === 'prompt') {
// Need one click to re-grant — show modal with the handle ready
document.getElementById('btnModalOpen').textContent = 'Reconnect "' + saved.name + '"';
document.getElementById('btnModalOpen').onclick = async () => {
const errEl = document.getElementById('modalError');
errEl.style.display = 'none';
try {
const result = await saved.requestPermission({ mode: 'readwrite' });
if (result === 'granted') {
await idbSet(IDB_KEY, saved);
activateHandle(saved);
} else {
errEl.textContent = 'Permission denied. Try selecting the folder again.';
errEl.style.display = '';
document.getElementById('btnModalOpen').textContent = 'Open json Folder';
document.getElementById('btnModalOpen').onclick = openFolder;
}
} catch (err) {
errEl.textContent = err.message;
errEl.style.display = '';
}
};
modal.show();
} else {
// 'denied' — clear and re-prompt
await idbDel(IDB_KEY);
modal.show();
}
} catch {
modal.show();
}
})();
// ── File System read / write ───────────────────────────────────────────────
async function fsReadJson(filename) {
const fh = await dirHandle.getFileHandle(filename);
const file = await fh.getFile();
return JSON.parse(await file.text());
}
async function fsWriteJson(filename, data) {
const fh = await dirHandle.getFileHandle(filename, { create: false });
const w = await fh.createWritable();
await w.write(JSON.stringify(data, null, '\t'));
await w.close();
}
// ── Department nav ────────────────────────────────────────────────────────
function renderDeptNav(property) {
const nav = document.getElementById('deptNav');
let html = '<div class="row g-3">';
DEPARTMENTS[property].forEach(group => {
html += '<div class="col-6 col-sm-4 col-md-3">';
html += `<div class="dept-category-label">${group.category}</div>`;
group.items.forEach(item => {
html += `<button class="dept-btn" data-json="${item.json}" data-label="${item.label}">${item.label}</button>`;
});
html += '</div>';
});
html += '</div>';
nav.innerHTML = html;
nav.querySelectorAll('.dept-btn').forEach(btn => {
btn.addEventListener('click', () => {
if (hasUnsaved && !confirm('You have unsaved changes. Discard them?')) return;
if (activeBtn) activeBtn.classList.remove('active');
btn.classList.add('active');
activeBtn = btn;
loadDepartment(btn.dataset.json, btn.dataset.label);
});
});
}
// ── Load department ───────────────────────────────────────────────────────
async function loadDepartment(jsonFile, label) {
currentJsonFile = jsonFile;
hasUnsaved = false;
setStatus('');
document.getElementById('sectionTitle').innerHTML = label;
document.getElementById('btnAddRow').style.display = '';
document.getElementById('btnSave').style.display = '';
document.getElementById('tableContainer').innerHTML =
'<div class="text-center py-4"><div class="spinner-border text-primary" role="status"></div></div>';
try {
const data = await fsReadJson(jsonFile);
originalData = JSON.parse(JSON.stringify(data));
renderEditTable(data);
} catch (err) {
document.getElementById('tableContainer').innerHTML = '<div class="empty-state">Failed to load file.</div>';
setStatus('Error: ' + err.message, 'error');
}
}
// ── Render editable table ─────────────────────────────────────────────────
function renderEditTable(data) {
let rows = '';
data.forEach((item, i) => { rows += rowHtml(i, item); });
document.getElementById('tableContainer').innerHTML = `
<div class="table-responsive">
<table class="table table-bordered table-sm mb-0" id="editTable">
<thead>
<tr>
<th style="width:2.5rem;">#</th>
<th>Name</th><th>Title</th>
<th style="width:7rem;">Extension</th>
<th style="width:8rem;">Exterior</th>
<th style="width:2.5rem;"></th>
</tr>
</thead>
<tbody id="editBody">${rows}</tbody>
</table>
</div>`;
markUnsaved(false);
}
function rowHtml(i, item) {
const v = f => escHtml(item[f] || '');
return `<tr data-row="${i}">
<td class="text-muted text-center" style="font-size:0.78rem;vertical-align:middle;">${i + 1}</td>
<td><input class="cell-input" data-field="name" value="${v('name')}" placeholder="Name" oninput="onCellChange(this)"></td>
<td><input class="cell-input" data-field="title" value="${v('title')}" placeholder="Title" oninput="onCellChange(this)"></td>
<td><input class="cell-input" data-field="extension" value="${v('extension')}" placeholder="Ext" oninput="onCellChange(this)"></td>
<td><input class="cell-input" data-field="exterior" value="${v('exterior')}" placeholder="Exterior" oninput="onCellChange(this)"></td>
<td class="text-center" style="vertical-align:middle;">
<button class="btn-delete-row" title="Delete row" onclick="deleteRow(this)">✕</button>
</td>
</tr>`;
}
function escHtml(s) {
return String(s).replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
// ── Add / delete rows ─────────────────────────────────────────────────────
function addRow() {
const tbody = document.getElementById('editBody');
if (!tbody) return;
tbody.insertAdjacentHTML('beforeend', rowHtml(tbody.rows.length, {}));
tbody.lastElementChild.querySelector('input').focus();
markUnsaved(true);
}
function deleteRow(btn) {
if (!confirm('Delete this entry?')) return;
btn.closest('tr').remove();
renumberRows();
markUnsaved(true);
}
function renumberRows() {
document.querySelectorAll('#editBody tr').forEach((tr, i) => {
tr.dataset.row = i;
tr.cells[0].textContent = i + 1;
});
}
// ── Unsaved tracking ──────────────────────────────────────────────────────
function onCellChange(input) { input.classList.add('changed'); markUnsaved(true); }
function markUnsaved(state) {
hasUnsaved = state;
const title = document.getElementById('sectionTitle');
const dot = title.querySelector('.unsaved-dot');
if (state && !dot) title.insertAdjacentHTML('beforeend', '<span class="unsaved-dot" title="Unsaved changes"></span>');
if (!state && dot) dot.remove();
if (!state) document.querySelectorAll('.cell-input.changed').forEach(el => el.classList.remove('changed'));
}
// ── Collect data ──────────────────────────────────────────────────────────
function collectData() {
const data = [];
document.querySelectorAll('#editBody tr').forEach(row => {
const entry = {};
row.querySelectorAll('.cell-input').forEach(inp => { entry[inp.dataset.field] = inp.value.trim(); });
data.push(entry);
});
return data;
}
// ── Save ──────────────────────────────────────────────────────────────────
async function saveData() {
if (!currentJsonFile || !dirHandle) return;
const data = collectData();
const btn = document.getElementById('btnSave');
btn.disabled = true; btn.textContent = 'Saving…';
setStatus('Saving…', 'info');
try {
await fsWriteJson(currentJsonFile, data);
originalData = JSON.parse(JSON.stringify(data));
markUnsaved(false);
setStatus(`Saved ${data.length} ${data.length === 1 ? 'entry' : 'entries'} successfully.`, 'success');
} catch (err) {
setStatus('Save failed: ' + err.message, 'error');
} finally {
btn.disabled = false; btn.textContent = 'Save Changes';
}
}
// ── Status bar ────────────────────────────────────────────────────────────
let statusTimer = null;
function setStatus(msg, type) {
const bar = document.getElementById('statusBar');
clearTimeout(statusTimer);
if (!msg) { bar.className = ''; bar.textContent = ''; return; }
bar.textContent = msg;
bar.className = 'show ' + (type || 'info');
if (type === 'success') statusTimer = setTimeout(() => setStatus(''), 4000);
}
// ── Property tabs ─────────────────────────────────────────────────────────
document.getElementById('propertyTabs').addEventListener('click', e => {
const link = e.target.closest('[data-property]');
if (!link) return;
e.preventDefault();
const prop = link.dataset.property;
if (prop === currentProperty) return;
if (hasUnsaved && !confirm('You have unsaved changes. Discard them?')) return;
currentProperty = prop;
document.querySelectorAll('#propertyTabs .nav-link').forEach(l => l.classList.remove('active'));
link.classList.add('active');
document.getElementById('logo-btbc').style.display = prop === 'btbc' ? '' : 'none';
document.getElementById('logo-mvbc').style.display = prop === 'mvbc' ? '' : 'none';
currentJsonFile = null; activeBtn = null; hasUnsaved = false;
setStatus('');
document.getElementById('sectionTitle').textContent = 'Select a department to edit';
document.getElementById('btnAddRow').style.display = 'none';
document.getElementById('btnSave').style.display = 'none';
document.getElementById('tableContainer').innerHTML =
'<div class="empty-state">Select a department above to edit its entries.</div>';
if (dirHandle) renderDeptNav(prop);
});
window.addEventListener('beforeunload', e => {
if (hasUnsaved) { e.preventDefault(); e.returnValue = ''; }
});
</script>
</body>
</html>