feat: redesign phone directory UI and add new department-specific JSON data files
This commit is contained in:
parent
611553efbc
commit
7ab089839e
|
|
@ -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">↻</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,'&').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
|
||||
// ── 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>
|
||||
594
index.html
594
index.html
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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": ""
|
||||
}
|
||||
]
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -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": ""
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
779
mvbc-files.html
779
mvbc-files.html
|
|
@ -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 — BTBC</div>
|
||||
<div class="prop-tab" data-prop="mvbc">Margaritaville — 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">📞 MVBC Quick Phone Reference</a>
|
||||
<a href="http://10.3.32.10/ucmuser/main#phonesettings">⚙ 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…</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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// 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>
|
||||
Loading…
Reference in New Issue