feat: redesign phone directory UI and add new department-specific JSON data files

This commit is contained in:
Justin 2026-04-14 21:22:01 -05:00
parent 611553efbc
commit 7ab089839e
18 changed files with 1664 additions and 565 deletions

639
admin.html Normal file
View File

@ -0,0 +1,639 @@
<!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>

View File

@ -1,216 +1,398 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Bossier City Phone Directory</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/phone.css" rel="stylesheet">
<meta charset="UTF-8" />
<title>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-color: #eef2f7;
font-family: 'PT Serif', serif;
}
.site-header {
font-size: clamp(1.6rem, 5vw, 2.75rem);
font-weight: 700;
text-align: center;
padding: 1rem 0 0.5rem;
color: #1a1a2e;
}
/* Property tabs */
.property-tabs {
gap: 0.25rem;
}
.property-tabs .nav-link {
font-family: 'PT Serif', serif;
font-size: 1rem;
padding: 0.6rem 1.5rem;
border-radius: 0.5rem 0.5rem 0 0;
background: #d8e4f5;
color: #444;
border: none;
font-weight: 400;
}
.property-tabs .nav-link.active {
background: #aaccfa;
color: #000;
font-weight: 700;
}
/* Logo bar */
.logo-bar {
background: #aaccfa;
border-radius: 0 0.5rem 0.5rem 0.5rem;
padding: 0.9rem 1rem;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
min-height: 80px;
}
.logo-bar img { height: 50px; }
.btbc-logo { filter: invert(1); }
.mville-logo { height: 55px; }
.bt-svg-flip { transform: scaleX(-1); }
.palm-flip { transform: scaleX(-1); }
/* Department section */
.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 {
margin-bottom: 0.75rem;
}
.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, 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;
}
/* Table section */
.table-panel {
background: #fff;
border-radius: 0.5rem;
padding: 1rem 1.25rem;
box-shadow: 0 1px 4px rgba(0,0,0,0.07);
}
.table-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.2rem;
font-weight: 700;
color: #1a1a2e;
margin: 0;
}
#searchInput {
max-width: 260px;
font-family: inherit;
font-size: 0.9rem;
}
#mainTable thead {
background-color: #aaccfa;
}
#mainTable thead th {
font-weight: 700;
font-size: 0.9rem;
border-bottom: none;
}
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: #9ab;
font-size: 1.05rem;
}
@media (max-width: 480px) {
.logo-bar img { height: 36px; }
.dept-btn { font-size: 0.8rem; padding: 0.25rem 0.75rem; }
}
</style>
</head>
<body>
<div class="container text-center">
<div class="header text-center title">
<a class= "homelink" href="index.html"> Bossier City Phone Directory </a>
</div>
<!-- Boomtown Images -->
<div class="text-center images">
<img class="bt-svg bt-svg-left" src="images/bt.svg">
<img class="btbc" src="images/bbc-logo-283x93-white.png">
<img class="bt-svg" src="images/bt.svg">
</div>
<!-- Boomtown Images End -->
<!-- Boomtown Navbar -->
<nav class="navbar navbar-expand-sm navbar-color">
<div class="container buffer">
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav ">
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Casino Operations
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="btbc\casino-operation.html">Casino Operations</a></li>
<li><a class="dropdown-item" href="btbc\security.html">Security</a></li>
<li><a class="dropdown-item" href="btbc\slots.html">Slots</a></li>
<li><a class="dropdown-item" href="btbc\slots-performance.html">Slots Performance</a></li>
<li><a class="dropdown-item" href="btbc\sport.html">Sports Betting</a></li>
<li><a class="dropdown-item" href="btbc\surveillance.html">Surveillance</a></li>
<li><a class="dropdown-item" href="btbc\table.html">Table Games</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Executive
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="btbc\executive.html">Executive</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Finance & Admin
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="btbc\accounting.html">Accounting</a></li>
<li><a class="dropdown-item" href="btbc\cage.html">Cage</a></li>
<li><a class="dropdown-item" href="btbc\compliance.html">Compliance</a></li>
<li><a class="dropdown-item" href="btbc\count.html">Count</a></li>
<li><a class="dropdown-item" href="btbc\purchasing.html">Purchasing</a></li>
<li><a class="dropdown-item" href="btbc\receiving.html">Receiving</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Food & Beverage
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="btbc\food.html">Food & Beverage</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Hotel
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="btbc\hotel.html">Hotel</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Human Resources
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="btbc\human-resources.html">Human Resources</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Sales & Marketing
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="btbc\sales-marketing.html">Sales & Marketing</a></li>
<li><a class="dropdown-item" href="btbc\entertainment.html">Entertainment</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Support Services
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="btbc\facilities.html">Facilities</a></li>
<li><a class="dropdown-item" href="btbc\it.html">Information Technology</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<!-- Boomtown Navbar End -->
<!-- Margaritaville Images -->
<div class="text-center images">
<img class="palm-tree palm-tree-left" src = "images/Palm-tree.svg">
<img class="mville" src="images/margaritaville.svg">
<img class="palm-tree palm-tree-right" src="images/Palm-tree.svg">
</div>
<!-- Margaritaville Images End -->
<!-- Margaritaville Navbar -->
<nav class="navbar navbar-expand-sm navbar-color">
<div class="container buffer">
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Casino Operations
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="mvbc/casino-operation.html">Casino Operations</a></li>
<li><a class="dropdown-item" href="mvbc/security.html">Security</a></li>
<li><a class="dropdown-item" href="mvbc/slots.html">Slots</a></li>
<li><a class="dropdown-item" href="mvbc/slots-performance.html">Slots Performance</a></li>
<li><a class="dropdown-item" href="mvbc/sport.html">Sports Betting</a></li>
<li><a class="dropdown-item" href="mvbc/surveillance.html">Surveillance</a></li>
<li><a class="dropdown-item" href="mvbc/table.html">Table Games</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Executive
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="mvbc/executive.html">Executive</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Finance & Admin
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="mvbc/accounting.html">Accounting</a></li>
<li><a class="dropdown-item" href="mvbc/cage.html">Cage</a></li>
<li><a class="dropdown-item" href="mvbc/compliance.html">Compliance</a></li>
<li><a class="dropdown-item" href="mvbc/count.html">Count</a></li>
<li><a class="dropdown-item" href="mvbc/purchasing.html">Purchasing</a></li>
<li><a class="dropdown-item" href="mvbc/receiving.html">Receiving</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Food & Beverage
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="mvbc/food.html">Food & Beverage</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Hotel
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="mvbc/hotel.html">Hotel</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Human Resources
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="mvbc/human-resources.html">Human Resources</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Sales & Marketing
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="mvbc/sales-marketing.html">Sales & Marketing</a></li>
<li><a class="dropdown-item" href="mvbc/entertainment.html">Entertainment</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-body" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Support Services
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="mvbc/facilities.html">Facilities</a></li>
<li><a class="dropdown-item" href="mvbc/it.html">Information Technology</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<!-- Margaritaville Navbar End -->
<div class="container py-3">
<div class="d-flex justify-content-end">
<a href="admin.html" class="btn btn-sm btn-outline-secondary" style="font-family:'PT Serif',serif;font-size:0.8rem;">Admin</a>
</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>
<h1 class="site-header">Bossier City Phone Directory</h1>
<!-- Property Tabs -->
<ul class="nav property-tabs mt-2 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" id="logoBar">
<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" class="mville-logo" alt="Margaritaville">
<img src="images/Palm-tree.svg" alt="">
</div>
</div>
<!-- Department Navigation -->
<div class="dept-panel mb-3" id="deptNav"></div>
<!-- Table Panel -->
<div class="table-panel">
<div class="table-toolbar">
<h2 class="section-title" id="sectionTitle">Select a department above</h2>
<input type="search" class="form-control" id="searchInput" placeholder="Filter results..." style="display:none;" aria-label="Filter table">
</div>
<div id="tableContainer">
<div class="empty-state">Select a department above to view the phone directory.</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>
const DEPARTMENTS = {
btbc: [
{ category: 'Casino Operations', items: [
{ label: 'Casino Operations', json: 'json/btbc-casino-ops.json' },
{ label: 'Security', json: 'json/btbc-security.json' },
{ label: 'Slots', json: 'json/btbc-slots.json' },
{ label: 'Slots Performance', json: 'json/btbc-slots-performance.json' },
{ label: 'Sports Betting', json: 'json/btbc-sport-betting.json' },
{ label: 'Surveillance', json: 'json/btbc-sureillance.json' },
{ label: 'Table Games', json: 'json/btbc-table-games.json' },
]},
{ category: 'Executive', items: [
{ label: 'Executive', json: 'json/btbc-executive.json' },
]},
{ category: 'Finance & Admin', items: [
{ label: 'Accounting', json: 'json/btbc-accounting.json' },
{ label: 'Cage', json: 'json/btbc-cage.json' },
{ label: 'Compliance', json: 'json/btbc-compliance.json' },
{ label: 'Count', json: 'json/btbc-count.json' },
{ label: 'Purchasing', json: 'json/btbc-purchasing.json' },
{ label: 'Receiving', json: 'json/btbc-receiving.json' },
]},
{ category: 'Food & Beverage', items: [
{ label: 'Food & Beverage', json: 'json/btbc-food-and-beverage.json' },
]},
{ category: 'Hotel', items: [
{ label: 'Hotel', json: 'json/btbc-hotel.json' },
]},
{ category: 'Human Resources', items: [
{ label: 'Human Resources', json: 'json/btbc-human-resources.json' },
]},
{ category: 'Sales & Marketing', items: [
{ label: 'Sales & Marketing', json: 'json/btbc-sales-and-marketing.json' },
{ label: 'Entertainment', json: 'json/btbc-entertainment.json' },
]},
{ category: 'Support Services', items: [
{ label: 'Facilities', json: 'json/btbc-facilties.json' },
{ label: 'Information Technology',json: 'json/btbc-information-technology.json' },
]},
],
mvbc: [
{ category: 'Casino Operations', items: [
{ label: 'Casino Operations', json: 'json/mvbc-casino-ops.json' },
{ label: 'Security', json: 'json/mvbc-security.json' },
{ label: 'Slots', json: 'json/mvbc-slots.json' },
{ label: 'Slots Performance', json: 'json/mvbc-slots-performance.json' },
{ label: 'Sports Betting', json: 'json/mvbc-sport-betting.json' },
{ label: 'Surveillance', json: 'json/mvbc-surveillance.json' },
{ label: 'Table Games', json: 'json/mvbc-table-games.json' },
]},
{ category: 'Executive', items: [
{ label: 'Executive', json: 'json/mvbc-executive.json' },
]},
{ category: 'Finance & Admin', items: [
{ label: 'Accounting', json: 'json/mvbc-accounting.json' },
{ label: 'Cage', json: 'json/mvbc-cage.json' },
{ label: 'Compliance', json: 'json/mvbc-compliance.json' },
{ label: 'Count', json: 'json/mvbc-count.json' },
{ label: 'Purchasing', json: 'json/mvbc-purchasing.json' },
{ label: 'Receiving', json: 'json/mvbc-receiving.json' },
]},
{ category: 'Food & Beverage', items: [
{ label: 'Food & Beverage', json: 'json/mvbc-food-and-beverage.json' },
]},
{ category: 'Hotel', items: [
{ label: 'Hotel', json: 'json/mvbc-hotel.json' },
]},
{ category: 'Human Resources', items: [
{ label: 'Human Resources', json: 'json/mvbc-human-resources.json' },
]},
{ category: 'Sales & Marketing', items: [
{ label: 'Sales & Marketing', json: 'json/mvbc-sales-and-marketing.json' },
{ label: 'Entertainment', json: 'json/mvbc-entertainment.json' },
]},
{ category: 'Support Services', items: [
{ label: 'Facilities', json: 'json/mvbc-facilties.json' },
{ label: 'Information Technology',json: 'json/mvbc-information-technology.json' },
]},
]
};
let currentProperty = 'btbc';
let currentData = [];
let activeBtn = null;
function renderDeptNav(property) {
const nav = document.getElementById('deptNav');
const groups = DEPARTMENTS[property];
let html = '<div class="row g-3">';
groups.forEach(group => {
html += '<div class="col-6 col-sm-4 col-md-3 dept-category">';
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 (activeBtn) activeBtn.classList.remove('active');
btn.classList.add('active');
activeBtn = btn;
loadDepartment(btn.dataset.json, btn.dataset.label);
});
});
}
function loadDepartment(jsonPath, label) {
document.getElementById('sectionTitle').textContent = label;
document.getElementById('searchInput').style.display = '';
document.getElementById('searchInput').value = '';
document.getElementById('tableContainer').innerHTML =
'<div class="text-center py-4"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div></div>';
fetch(jsonPath, { cache: 'no-store' })
.then(r => { if (!r.ok) throw new Error(r.status); return r.json(); })
.then(data => { currentData = data; renderTable(data); })
.catch(() => {
document.getElementById('tableContainer').innerHTML =
'<div class="empty-state">Could not load data for this department.</div>';
});
}
function renderTable(data) {
if (!data || data.length === 0) {
document.getElementById('tableContainer').innerHTML =
'<div class="empty-state">No records found.</div>';
return;
}
let rows = '';
data.forEach(item => {
rows += `<tr>
<td>${item.name || ''}</td>
<td>${item.title || ''}</td>
<td>${item.extension || ''}</td>
<td>${item.exterior || ''}</td>
</tr>`;
});
document.getElementById('tableContainer').innerHTML = `
<div class="table-responsive">
<table class="table table-striped table-hover mb-0" id="mainTable">
<thead><tr><th>Name</th><th>Title</th><th>Extension</th><th>Exterior</th></tr></thead>
<tbody>${rows}</tbody>
</table>
</div>`;
}
function resetTable() {
activeBtn = null;
document.getElementById('sectionTitle').textContent = 'Select a department above';
document.getElementById('searchInput').style.display = 'none';
document.getElementById('tableContainer').innerHTML =
'<div class="empty-state">Select a department above to view the phone directory.</div>';
currentData = [];
}
// Property tab switching
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;
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';
resetTable();
renderDeptNav(prop);
});
// Search / filter
document.getElementById('searchInput').addEventListener('input', function () {
const q = this.value.toLowerCase();
if (!q) { renderTable(currentData); return; }
const filtered = currentData.filter(item =>
(item.name || '').toLowerCase().includes(q) ||
(item.title || '').toLowerCase().includes(q) ||
String(item.extension || '').includes(q) ||
String(item.exterior || '').includes(q)
);
renderTable(filtered);
});
// Init
renderDeptNav('btbc');
</script>
</body>
</html>

View File

@ -1,38 +1,8 @@
[
{
"name": "Shaun Williams",
"title": "VP of Casino Operations",
"extension": "2158",
"exterior": "318-841-2158"
},
{
"name": "Brook Campbell",
"title": "Casino Ops Manager",
"extension": "2016",
"exterior": "318-841-2016"
},
{
"name": "Stephanie Barnes",
"title": "Casino Ops Manager",
"extension": "2460",
"exterior": "318-841-2460"
},
{
"name": "Mike Lanclos",
"title": "Casino Ops Asst Manager",
"extension": "2460",
"exterior": "318-841-2016"
},
{
"name": "Terri Timmons",
"title": "Casino Ops Asst Manager",
"extension": "2447",
"exterior": "318-841-2447"
},
{
"name": "Justin Morris",
"title": "Casino Operations Coordinator",
"extension": "2431",
"exterior": "318-841-2431"
}
{
"name": "Test",
"title": "Test",
"extension": "3456",
"exterior": ""
}
]

View File

@ -1,33 +1,26 @@
[
{
"name": "Ronnie Coppes",
"title": "Systems Specialist",
"extension": "2037",
"exterior": ""
},
{
"name": "Leighton Warren",
"title": "Applications Administrator",
"extension": "2402",
"exterior": ""
},
{
"name": "Erica Wilkinson",
"title": "Applications Spcialist",
"extension": "2460",
"exterior": ""
},
{
"name": "Justin Frasier",
"title": "I.T Support Analyst",
"extension": "need to add",
"exterior": ""
},
{
"name": "",
"title": "I.T Help Desk",
"extension": "2468",
"exterior": "318-841-2468"
}
]
{
"name": "Ronnie Coppes",
"title": "Systems Specialist",
"extension": "2037",
"exterior": ""
},
{
"name": "Leighton Warren",
"title": "Applications Administrator",
"extension": "2402",
"exterior": ""
},
{
"name": "Erica Wilkinson",
"title": "Applications Spcialist",
"extension": "2460",
"exterior": ""
},
{
"name": "",
"title": "I.T Help Desk",
"extension": "2468",
"exterior": "318-841-2468"
}
]

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1 @@
[]

View File

@ -1,44 +1,26 @@
[
{
"name": "Jeff Taylor",
"title": "Infrastructure Specialist",
"extension": "3888",
"exterior": ""
},
{
"name": "Greg Benjamin",
"title": "Applications Specialist",
"extension": "7112",
"exterior": ""
},
{
"name": "Dean Lehmann",
"title": "Applications Administrator",
"extension": "7111",
"exterior": ""
},
{
"name": "Jarnell Mack",
"title": "Infrastructure Administrator",
"extension": "3804",
"exterior": ""
},
{
"name": "Joktan Lynch",
"title": "Systems Administrator",
"extension": "3852",
"exterior": ""
},
{
"name": "Justin Frasier",
"title": "I.T Support Analyst",
"extension": "7199",
"exterior": ""
}
{
"name": "Greg Benjamin",
"title": "Applications Specialist",
"extension": "7112",
"exterior": ""
},
{
"name": "Jarnell Mack",
"title": "Infrastructure Administrator",
"extension": "3804",
"exterior": ""
},
{
"name": "Joktan Lynch",
"title": "Systems Administrator",
"extension": "3852",
"exterior": ""
},
{
"name": "Justin Frasier",
"title": "I.T Support Analyst",
"extension": "7199",
"exterior": ""
}
]

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1 @@
[]

View File

@ -1,266 +1,587 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Bossier City Phone Directory</title>
<meta name="viewport" content="width=divice-width,initial-scale=1" />
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Bossier City Casino Phone Directory" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<link rel="icon" href="favicon.png">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
background-color: #A99ABD; /* Background color of page */
:root {
--gold: #d4af37;
--gold-dim: #b8962e;
--navy-deep: #0f1b2d;
--navy-mid: #16263e;
--navy-lite: #1e3353;
--surface: rgba(255,255,255,0.05);
--border: rgba(255,255,255,0.10);
--text: #e2e8f0;
--muted: #94a3b8;
--sidebar-w: 240px;
}
table.banner {
padding-top: 50px;
text-align: center;
font-size: xxx-large;
width:100%;
color:#CCCCCC; /* Color of title font */
}
html, body { height: 100%; }
table.mainTable {
margin-left:auto;
margin-right:auto;
padding-left:10px;
padding-top: 50px;
}
body {
font-family: 'Segoe UI', Arial, sans-serif;
background: var(--navy-deep);
color: var(--text);
display: flex;
flex-direction: column;
min-height: 100vh;
}
td[colspan]:not([colspan="1"]) {
text-align: center;
}
/* ══ TOP HEADER ══ */
.top-header {
background: linear-gradient(90deg, var(--navy-deep) 0%, var(--navy-mid) 100%);
border-bottom: 2px solid rgba(212,175,55,0.4);
padding: 14px 24px;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
position: sticky;
top: 0;
z-index: 100;
}
.titleBar td {
.top-header h1 {
font-size: clamp(1rem, 2.5vw, 1.45rem);
font-weight: 700;
letter-spacing: 0.06em;
color: var(--gold);
text-shadow: 0 2px 10px rgba(212,175,55,0.3);
white-space: nowrap;
}
/* search */
.search-wrap {
position: relative;
flex: 0 1 280px;
}
.search-wrap input {
width: 100%;
padding: 8px 12px 8px 36px;
border-radius: 8px;
border: 1px solid var(--border);
background: rgba(255,255,255,0.07);
color: var(--text);
font-size: 0.875rem;
outline: none;
transition: border-color 0.2s;
}
.search-wrap input::placeholder { color: var(--muted); }
.search-wrap input:focus { border-color: rgba(212,175,55,0.55); }
.search-wrap svg {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
color: var(--muted);
pointer-events: none;
}
/* ══ PROPERTY TABS ══ */
.prop-tabs {
display: flex;
background: var(--navy-mid);
border-bottom: 1px solid var(--border);
padding: 0 24px;
}
.prop-tab {
padding: 10px 28px;
font-size: 0.85rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
cursor: pointer;
border-bottom: 3px solid transparent;
transition: color 0.15s, border-color 0.15s;
user-select: none;
}
.prop-tab:hover { color: var(--text); }
.prop-tab.active {
color: var(--gold);
border-bottom-color: var(--gold);
}
/* ══ BODY LAYOUT ══ */
.layout {
display: flex;
flex: 1;
overflow: hidden;
}
/* ══ SIDEBAR ══ */
.sidebar {
width: var(--sidebar-w);
min-width: var(--sidebar-w);
background: var(--navy-mid);
border-right: 1px solid var(--border);
overflow-y: auto;
padding: 12px 0 24px;
}
.sidebar-group { margin-bottom: 4px; }
.sidebar-group-btn {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 9px 16px 9px 20px;
background: none;
border: none;
background-color: #C0D8C0; /* Background color of title bar */
text-align: center;
font-size: large;
}
color: var(--gold);
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
cursor: pointer;
text-align: left;
transition: background 0.15s;
}
.sidebar-group-btn:hover { background: rgba(255,255,255,0.04); }
.header td {
.sidebar-group-btn .chevron {
transition: transform 0.2s;
flex-shrink: 0;
}
.sidebar-group-btn.open .chevron { transform: rotate(90deg); }
.sidebar-items {
display: none;
flex-direction: column;
}
.sidebar-items.open { display: flex; }
.dept-btn {
padding: 8px 16px 8px 32px;
background: none;
border: none;
background-color: #E1EFCA; /* Background color of column header bar */
text-align: center;
font-size: x-large;
}
color: var(--muted);
font-size: 0.83rem;
text-align: left;
cursor: pointer;
border-left: 3px solid transparent;
transition: color 0.12s, background 0.12s, border-color 0.12s;
}
.dept-btn:hover {
color: var(--text);
background: rgba(255,255,255,0.05);
}
.dept-btn.active {
color: var(--gold);
border-left-color: var(--gold);
background: rgba(212,175,55,0.08);
}
table.mainTable td {
height: 25px;
/* ══ MAIN CONTENT ══ */
.main {
flex: 1;
overflow-y: auto;
padding: 28px 32px 48px;
}
.dept-heading {
font-size: 1.3rem;
font-weight: 700;
color: var(--gold);
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(212,175,55,0.25);
display: flex;
align-items: center;
gap: 10px;
}
.dept-heading span.prop-badge {
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 3px 8px;
border-radius: 4px;
background: rgba(212,175,55,0.18);
color: var(--gold);
border: 1px solid rgba(212,175,55,0.3);
}
/* table */
.table-wrap {
overflow-x: auto;
border-radius: 10px;
border: 1px solid var(--border);
}
table.dir-table {
width: 100%;
border-collapse: collapse;
font-size: 0.875rem;
}
table.dir-table thead tr {
background: rgba(212,175,55,0.12);
border-bottom: 1px solid rgba(212,175,55,0.3);
}
table.dir-table thead th {
padding: 11px 16px;
text-align: left;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--gold);
white-space: nowrap;
}
table.dir-table tbody tr {
border-bottom: 1px solid var(--border);
transition: background 0.1s;
}
table.dir-table tbody tr:last-child { border-bottom: none; }
table.dir-table tbody tr:hover { background: rgba(255,255,255,0.04); }
table.dir-table tbody tr.hidden { display: none; }
table.dir-table td {
padding: 10px 16px;
color: var(--text);
white-space: nowrap;
}
table.dir-table td:first-child { font-weight: 600; }
table.dir-table td.ext { font-family: 'Courier New', monospace; color: var(--gold); }
table.dir-table td.ext-empty { color: var(--muted); font-style: italic; }
.no-results {
text-align: center;
vertical-align: middle;
padding-left:10px;
padding-right:10px;
white-space: nowrap;
}
padding: 48px 16px;
color: var(--muted);
font-size: 0.9rem;
}
table.mainTable td.first {
.loading {
text-align: center;
}
padding: 48px 16px;
color: var(--muted);
font-size: 0.9rem;
}
table.mainTable td.second {
text-align: center;
}
table.mainTable td.third {
text-align: center;
}
table.mainTable td.forth {
text-align: center;
}
</style>
/* ══ QUICK LINKS FOOTER ══ */
.quick-links {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
padding: 18px 24px;
background: rgba(0,0,0,0.3);
border-top: 1px solid rgba(212,175,55,0.2);
}
.quick-links a {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 10px 22px;
border-radius: 7px;
font-size: 0.82rem;
font-weight: 700;
letter-spacing: 0.05em;
color: var(--navy-deep);
background: linear-gradient(135deg, var(--gold), var(--gold-dim));
text-decoration: none;
transition: opacity 0.15s, transform 0.15s;
box-shadow: 0 3px 12px rgba(212,175,55,0.3);
}
.quick-links a:hover { opacity: 0.85; transform: translateY(-1px); }
/* ══ RESPONSIVE ══ */
@media (max-width: 700px) {
.sidebar { width: 200px; min-width: 200px; }
.main { padding: 20px 16px 40px; }
}
@media (max-width: 520px) {
.layout { flex-direction: column; }
.sidebar { width: 100%; min-width: 0; border-right: none; border-bottom: 1px solid var(--border); max-height: 220px; }
}
</style>
</head>
<body>
<table class=banner>
<tr>
<td>Bossier City Phone Directory</td>
<tr>
</table>
<!-- ══ TOP HEADER ══ -->
<header class="top-header">
<h1>Bossier City Phone Directory</h1>
<div class="search-wrap">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
<input type="search" id="searchInput" placeholder="Search name, title, extension…" autocomplete="off" />
</div>
</header>
<table class=mainTable>
<tr class=titleBar>
<td>Casino Operations</td>
<td>Executive</td>
<td>Finance and Admin</td>
<td>Food and Beverage</td>
<td>Hotel</td>
<td>Human Resources</td>
<td>Sales and Marketing</td>
<td>Support Operations</td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/btbc-casino_operations.html';">BTBC Casino Operations</button></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-executive.html';">BTBC Executive</button></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-accounting.html';">BTBC Accounting</button></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-food_and_beverage.html';">BTBC Food and Beverage</button></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-hotel.html';">BTBC Hotel</button></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-human_resources.html';">BTBC Human Resources</button></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-sales_and_marketing.html';">BTBC Sales and Marketing</button></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-facilities.html';">BTBC Facilities</button></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/btbc-security.html';">BTBC Security</button></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-cage.html';">BTBC Cage</button></td>
<td></td>
<td></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-entertainment.html';">BTBC Entertainment</button></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-information_technology.html';">BTBC Information Technology</button></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/btbc-slot_performance.html';">BTBC Slot Performance</button></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-compliance.html';">BTBC Compliance</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/btbc-slots.html';">BTBC Slots</button></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-count.html';">BTBC Count</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/btbc-sports_betting.html';">BTBC Sports Betting</button></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-purchasing.html';">BTBC Purchasing</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/btbc-surveillance.html';">BTBC Surveillance</button></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/btbc-receiving.html';">BTBC Receiving</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/btbc-table_games.html';">BTBC Table Games</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<!-- ══ PROPERTY TABS ══ -->
<div class="prop-tabs">
<div class="prop-tab active" data-prop="btbc">Boomtown &mdash; BTBC</div>
<div class="prop-tab" data-prop="mvbc">Margaritaville &mdash; MVBC</div>
</div>
<table class=mainTable>
<tr class=titleBar>
<td>Casino Operations</td>
<td>Executive</td>
<td>Finance and Admin</td>
<td>Food and Beverage</td>
<td>Hotel</td>
<td>Human Resources</td>
<td>Sales and Marketing</td>
<td>Support Operations</td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-casino_operations.html';">MVBC Casino Operations</button></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-executive.html';">MVBC Executive</button></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-accounting.html';">MVBC Accounting</button></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-food_and_beverage.html';">MVBC Food and Beverage</button></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-hotel.html';">MVBC Hotel</button></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-human_resources.html';">MVBC Human Resources</button></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-sales_and_marketing.html';">MVBC Sales and Marketing</button></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-facilities.html';">MVBC Facilities</button></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-security.html';">MVBC Security</button></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-cage.html';">MVBC Cage</button></td>
<td></td>
<td></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-entertainment.html';">MVBC Entertainment</button></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-information_technology.html';">MVBC Information Technology</button></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-slot_performance.html';">MVBC Slot Performance</button></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-compliance.html';">MVBC Compliance</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-slots.html';">MVBC Slots</button></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-count.html';">MVBC Count</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-sports_betting.html';">MVBC Sports Betting</button></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-purchasing.html';">MVBC Purchasing</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-surveillance.html';">MVBC Surveillance</button></td>
<td></td>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-receiving.html';">MVBC Receiving</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/mvbc-table_games.html';">MVBC Table Games</button></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<!-- ══ LAYOUT ══ -->
<div class="layout">
<!-- Sidebar -->
<nav class="sidebar" id="sidebar">
<!-- rendered by JS -->
</nav>
<table class=mainTable>
<tr>
<td><button onclick="window.location.href='http://mvbc-files/Phone User Guide.pdf';">MVBC Quick Phone Reference</button></td>
<td><button onclick="window.location.href='http://10.3.32.10/ucmuser/main#phonesettings';">MVBC Self Care Portal</button></td>
</tr>
</table>
<!-- Main panel -->
<main class="main" id="mainPanel">
<div class="loading">Select a department from the sidebar.</div>
</main>
</div>
<!-- ══ QUICK LINKS ══ -->
<footer class="quick-links">
<a href="http://mvbc-files/Phone User Guide.pdf">&#128222; MVBC Quick Phone Reference</a>
<a href="http://10.3.32.10/ucmuser/main#phonesettings">&#9881; MVBC Self Care Portal</a>
</footer>
<script>
// ─────────────────────────────────────────────
// DATA MAP dept label → JSON file path
// ─────────────────────────────────────────────
const DEPTS = [
{
group: 'Casino Operations',
items: [
{ label: 'Casino Operations', btbc: 'json/btbc-casino-ops.json', mvbc: 'json/mvbc-casino-ops.json' },
{ label: 'Security', btbc: 'json/btbc-security.json', mvbc: 'json/mvbc-security.json' },
{ label: 'Slots', btbc: 'json/btbc-slots.json', mvbc: 'json/mvbc-slots.json' },
{ label: 'Slots Performance', btbc: 'json/btbc-slots-performance.json', mvbc: 'json/mvbc-slots-performance.json' },
{ label: 'Sports Betting', btbc: 'json/btbc-sport-betting.json', mvbc: 'json/mvbc-sport-betting.json' },
{ label: 'Surveillance', btbc: 'json/btbc-sureillance.json', mvbc: 'json/mvbc-surveillance.json' },
{ label: 'Table Games', btbc: 'json/btbc-table-games.json', mvbc: 'json/mvbc-table-games.json' },
]
},
{
group: 'Executive',
items: [
{ label: 'Executive', btbc: 'json/btbc-executive.json', mvbc: 'json/mvbc-executive.json' },
]
},
{
group: 'Finance & Admin',
items: [
{ label: 'Accounting', btbc: 'json/btbc-accounting.json', mvbc: 'json/mvbc-accounting.json' },
{ label: 'Cage', btbc: 'json/btbc-cage.json', mvbc: 'json/mvbc-cage.json' },
{ label: 'Compliance', btbc: 'json/btbc-compliance.json', mvbc: 'json/mvbc-compliance.json' },
{ label: 'Count', btbc: 'json/btbc-count.json', mvbc: 'json/mvbc-count.json' },
{ label: 'Purchasing', btbc: 'json/btbc-purchasing.json', mvbc: 'json/mvbc-purchasing.json' },
{ label: 'Receiving', btbc: 'json/btbc-receiving.json', mvbc: 'json/mvbc-receiving.json' },
]
},
{
group: 'Food & Beverage',
items: [
{ label: 'Food & Beverage', btbc: 'json/btbc-food-and-beverage.json', mvbc: 'json/mvbc-food-and-beverage.json' },
]
},
{
group: 'Hotel',
items: [
{ label: 'Hotel', btbc: 'json/btbc-hotel.json', mvbc: 'json/mvbc-hotel.json' },
]
},
{
group: 'Human Resources',
items: [
{ label: 'Human Resources', btbc: 'json/btbc-human-resources.json', mvbc: 'json/mvbc-human-resources.json' },
]
},
{
group: 'Sales & Marketing',
items: [
{ label: 'Sales & Marketing', btbc: 'json/btbc-sales-and-marketing.json', mvbc: 'json/mvbc-sales-and-marketing.json' },
{ label: 'Entertainment', btbc: 'json/btbc-entertainment.json', mvbc: 'json/mvbc-entertainment.json' },
]
},
{
group: 'Support Services',
items: [
{ label: 'Facilities', btbc: 'json/btbc-facilties.json', mvbc: 'json/mvbc-facilties.json' },
{ label: 'Information Technology', btbc: 'json/btbc-information-technology.json', mvbc: 'json/mvbc-information-technology.json' },
]
},
];
// ─────────────────────────────────────────────
// STATE
// ─────────────────────────────────────────────
let currentProp = 'btbc';
let currentDept = null; // { label, btbc, mvbc }
let currentData = [];
const sidebar = document.getElementById('sidebar');
const mainPanel = document.getElementById('mainPanel');
const searchInput = document.getElementById('searchInput');
// ─────────────────────────────────────────────
// RENDER SIDEBAR
// ─────────────────────────────────────────────
function renderSidebar() {
sidebar.innerHTML = '';
DEPTS.forEach((grp, gi) => {
const groupEl = document.createElement('div');
groupEl.className = 'sidebar-group';
const btn = document.createElement('button');
btn.className = 'sidebar-group-btn';
const hasActive = grp.items.some(it => it === currentDept);
if (hasActive) btn.classList.add('open');
btn.innerHTML = `${grp.group} <svg class="chevron" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>`;
const itemsEl = document.createElement('div');
itemsEl.className = 'sidebar-items' + (hasActive ? ' open' : '');
grp.items.forEach(dept => {
const db = document.createElement('button');
db.className = 'dept-btn' + (dept === currentDept ? ' active' : '');
db.textContent = dept.label;
db.addEventListener('click', () => selectDept(dept));
itemsEl.appendChild(db);
});
btn.addEventListener('click', () => {
btn.classList.toggle('open');
itemsEl.classList.toggle('open');
});
groupEl.appendChild(btn);
groupEl.appendChild(itemsEl);
sidebar.appendChild(groupEl);
});
}
// ─────────────────────────────────────────────
// SELECT DEPARTMENT
// ─────────────────────────────────────────────
function selectDept(dept) {
currentDept = dept;
searchInput.value = '';
renderSidebar();
loadDept();
}
// ─────────────────────────────────────────────
// LOAD + DISPLAY
// ─────────────────────────────────────────────
function loadDept() {
if (!currentDept) return;
const jsonPath = currentDept[currentProp];
mainPanel.innerHTML = '<div class="loading">Loading&hellip;</div>';
fetch(jsonPath)
.then(r => {
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.json();
})
.then(data => {
currentData = data;
renderTable(data);
})
.catch(() => {
mainPanel.innerHTML = '<div class="no-results">Could not load data for this department.</div>';
});
}
function propLabel(prop) {
return prop === 'btbc' ? 'BTBC' : 'MVBC';
}
function renderTable(data) {
const q = searchInput.value.trim().toLowerCase();
const filtered = q
? data.filter(r =>
(r.name || '').toLowerCase().includes(q) ||
(r.title || '').toLowerCase().includes(q) ||
(r.extension || '').toLowerCase().includes(q) ||
(r.exterior || '').toLowerCase().includes(q)
)
: data;
let html = `
<div class="dept-heading">
${escHtml(currentDept.label)}
<span class="prop-badge">${propLabel(currentProp)}</span>
</div>`;
if (filtered.length === 0) {
html += `<div class="no-results">No results${q ? ' matching "<strong>' + escHtml(q) + '</strong>"' : ''}.</div>`;
} else {
html += `
<div class="table-wrap">
<table class="dir-table">
<thead>
<tr>
<th>Name</th>
<th>Title</th>
<th>Extension</th>
<th>Exterior</th>
</tr>
</thead>
<tbody>`;
filtered.forEach(r => {
const ext = (r.extension || '').trim();
const exr = (r.exterior || '').trim();
html += `<tr>
<td>${escHtml(r.name || '')}</td>
<td>${escHtml(r.title || '')}</td>
<td class="${ext ? 'ext' : 'ext-empty'}">${ext ? escHtml(ext) : '—'}</td>
<td class="${exr ? 'ext' : 'ext-empty'}">${exr ? escHtml(exr) : '—'}</td>
</tr>`;
});
html += `</tbody></table></div>`;
}
mainPanel.innerHTML = html;
}
function escHtml(s) {
return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
// ─────────────────────────────────────────────
// PROPERTY TABS
// ─────────────────────────────────────────────
document.querySelectorAll('.prop-tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.prop-tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
currentProp = tab.dataset.prop;
if (currentDept) loadDept();
});
});
// ─────────────────────────────────────────────
// SEARCH
// ─────────────────────────────────────────────
searchInput.addEventListener('input', () => {
if (currentDept && currentData.length) {
renderTable(currentData);
}
});
// ─────────────────────────────────────────────
// INIT open Casino Operations, load first dept
// ─────────────────────────────────────────────
currentDept = DEPTS[0].items[0];
renderSidebar();
loadDept();
</script>
</body>
</html>