|
Server : LiteSpeed System : Linux terra.hostitbro.com 5.14.0-611.54.3.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 7 16:31:24 EDT 2026 x86_64 User : outerorb ( 1091) PHP Version : 8.1.34 Disable Function : mail Directory : /home/outerorb/assetradar.outerorbittech.com/admin/ | |
|
Path: //home/outerorb/assetradar.outerorbittech.com/admin/master_data.php
Size: 22.03 KB
Permissions: 0666
<?php
// admin/master_data.php
// Master Data Panel for Admins
require_once '../inc/auth.php';
define('ADMIN_PAGE', true);
require_once '../inc/header.php';
include __DIR__ . '/admin_menu.php';
// Only allow access for Admin users
if (!is_admin()) {
header('Location: ../index.php');
exit();
}
?>
<style>
/* Page-specific styles (kept minimal — header.php provides Bootstrap) */
.master-panel { max-width: 1100px; margin: 30px auto; background: #fff; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.07); padding: 24px; }
.tab-bar { display: flex; gap: 8px; margin-bottom: 16px; }
.tab { flex: 1; text-align: center; padding: 10px 0; background: #e3f2fd; cursor: pointer; border-radius: 6px 6px 0 0; font-weight: 500; transition: background 0.2s; border: none; outline: none; color: #333; }
.tab.active { background: #4285f4; color: #fff; }
.tab-content { background: #fff; border-radius: 0 0 8px 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.04); padding: 18px; margin-top: -2px; }
table { width: 100%; border-collapse: collapse; margin-top: 12px; background: #fff; border: 1px solid #e9ecef; }
th, td { padding: 8px 10px; border-bottom: 1px solid #e9ecef; text-align: left; }
th { background: #f8f9fa; font-weight: 600; }
/* Ensure table bottom border is visible by styling last-row cells */
tbody tr:last-child td { border-bottom: 1px solid #e9ecef; }
form { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 10px; }
@media (max-width: 700px) { .master-panel { padding: 12px; } table, th, td { font-size: 13px; } }
</style>
<div class="master-panel">
<h2 style="margin-bottom:12px; color:#4285f4;">Master Data Panel</h2>
<div class="tab-bar" id="tabs">
<button class="tab active" data-tab="roles">Roles</button>
<button class="tab" data-tab="teams">Teams</button>
<button class="tab" data-tab="categories">Categories</button>
<button class="tab" data-tab="users">Users</button>
<button class="tab" data-tab="items">Items</button>
<button class="tab" data-tab="vendors">Vendors</button>
</div>
<div id="dynamic-buttons" style="margin-bottom:12px; display:flex; gap:8px;"></div>
<div id="tab-contents">
<div class="tab-content" id="roles-content">Loading...</div>
<div class="tab-content" id="teams-content" style="display:none">Loading...</div>
<div class="tab-content" id="categories-content" style="display:none">Loading...</div>
<div class="tab-content" id="users-content" style="display:none">Loading...</div>
<div class="tab-content" id="items-content" style="display:none;"><div style="overflow-x:auto;">Loading...</div></div>
<div class="tab-content" id="vendors-content" style="display:none">Loading...</div>
</div>
</div>
<script>
// Simple tab switching
const tabs = document.querySelectorAll('.tab');
const contents = document.querySelectorAll('.tab-content');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
contents.forEach(c => c.style.display = 'none');
const tabName = tab.dataset.tab;
document.getElementById(tabName + '-content').style.display = 'block';
loadEntity(tabName);
setDynamicButtons(tabName);
});
});
// Load entity data via AJAX
function loadEntity(entity) {
const contentId = entity + '-content';
document.getElementById(contentId).innerHTML = 'Loading...';
fetch('master_data_api.php?entity=' + entity + '&action=list')
.then(res => res.json())
.then(data => {
let html = '';
// Add form for new entry (now above table)
html += `<h4>Add New</h4><form onsubmit=\"return addRow('${entity}', this)\">`;
if (entity === 'roles') {
html += 'Name: <input name="name" required> Slug: <input name="slug" required> <button type="submit">Add</button>';
} else if (entity === 'teams' || entity === 'categories') {
html += 'Name: <input name="name" required> <button type="submit">Add</button>';
} else if (entity === 'users') {
html += 'Name: <input name="name" required> Email: <input name="email" required> Password: <input name="password" required><br>';
html += '<div style="display:flex;align-items:center;gap:16px;margin-top:8px;margin-bottom:8px;">';
html += '<label for="role-slug-select" style="font-weight:500;">Role</label> <select name="role_slug" required id="role-slug-select" style="width:180px;"><option value="">Loading...</option></select>';
html += '<label for="team-select" style="font-weight:500;">Team</label> <select name="team_id" id="team-select" style="width:180px;"><option value="">Loading...</option></select>';
html += '<button type="submit">Add</button>';
html += '</div>';
} else if (entity === 'items') {
html += 'Asset Tag: <input name="asset_tag" required> Item Name: <input name="item_name" required> Category ID: <input name="category_id" required> Brand/Model: <input name="brand_model"> Serial Number: <input name="serial_number"> Purchase Date: <input name="purchase_date" type="date"> Warranty To: <input name="warranty_to" type="date"> Assigned To (User ID): <input name="assigned_to"> Condition Status: <input name="condition_status"> <button type="submit">Add</button>';
} else if (entity === 'vendors') {
html += 'Name: <input name="name" required> Contact: <input name="contact_info"> Address: <input name="address"> Email: <input name="email"> Phone: <input name="phone"> Notes: <input name="notes"> <button type="submit">Add</button>';
}
html += '</form>';
// Edit form placeholder
html += `<div id=\"edit-form-${entity}\" style=\"display:none;\"></div>`;
// Data table
if (Array.isArray(data)) {
if (entity === 'items') {
html += '<div style="overflow-x:auto">';
}
html += '<table border="1" cellpadding="6"><tr>';
if (data.length > 0) {
if (entity === 'vendors') {
Object.keys(data[0]).forEach(k => {
html += '<th>' + k + '</th>';
});
} else {
// Table headers
if (entity === 'users') {
// Show all columns except team_id, team_name, role_id, role_slug, created_at, deleted, api_token. Add Role and Team columns.
Object.keys(data[0]).forEach(k => {
if (k !== 'team_id' && k !== 'team_name' && k !== 'role_id' && k !== 'role_slug' && k !== 'created_at' && k !== 'deleted' && k !== 'api_token') {
html += '<th>' + k + '</th>';
}
});
html += '<th>Role</th><th>Team</th>';
} else {
Object.keys(data[0]).forEach(k => {
html += '<th>' + k + '</th>';
});
}
}
html += '<th>Actions</th></tr>';
// Table rows
data.forEach(row => {
html += '<tr>';
if (entity === 'users') {
Object.entries(row).forEach(([k, v]) => {
if (k !== 'team_id' && k !== 'team_name' && k !== 'role_id' && k !== 'role_slug' && k !== 'created_at' && k !== 'deleted' && k !== 'api_token') {
html += '<td>' + v + '</td>';
}
});
html += '<td>' + (row.role_name || '') + '</td>';
html += '<td>' + (row.team_name || '') + '</td>';
} else if (entity === 'vendors') {
Object.entries(row).forEach(([k, v]) => {
html += '<td>' + v + '</td>';
});
} else {
Object.entries(row).forEach(([k, v]) => {
html += '<td>' + v + '</td>';
});
}
if (entity === 'roles') {
html += `<td><button title="Edit" onclick=\"editRow('${entity}', ${row.id}, '${escapeQuotes(row.name ?? '')}', '${escapeQuotes(row.slug ?? '')}')\" style="background:none;border:none;padding:4px 8px;font-size:18px;cursor:pointer;">✏️</button> <button title="Delete" onclick=\"deleteRow('${entity}', ${row.id})\" style="background:none;border:none;padding:4px 8px;font-size:18px;cursor:pointer;">🗑️</button></td>`;
} else if (entity === 'teams' || entity === 'categories') {
html += `<td><button title="Edit" onclick=\"editRow('${entity}', ${row.id}, '${escapeQuotes(row.name ?? '')}')\" style="background:none;border:none;padding:4px 8px;font-size:18px;cursor:pointer;">✏️</button> <button title="Delete" onclick=\"deleteRow('${entity}', ${row.id})\" style="background:none;border:none;padding:4px 8px;font-size:18px;cursor:pointer;">🗑️</button></td>`;
} else if (entity === 'users') {
html += `<td><button title="Edit" onclick=\"editRow('${entity}', ${row.id}, '${escapeQuotes(row.name ?? '')}', '${escapeQuotes(row.email ?? '')}', '${escapeQuotes(row.role_slug ?? '')}', ${row.team_id})\" style="background:none;border:none;padding:4px 8px;font-size:18px;cursor:pointer;">✏️</button> <button title="Delete" onclick=\"deleteRow('${entity}', ${row.id})\" style="background:none;border:none;padding:4px 8px;font-size:18px;cursor:pointer;">🗑️</button></td>`;
} else if (entity === 'items') {
html += `<td><button title="Edit" onclick=\"editRow('${entity}', ${row.id}, '${escapeQuotes(row.asset_tag ?? '')}', '${escapeQuotes(row.item_name ?? '')}', ${row.category_id}, '${escapeQuotes(row.brand_model ?? '')}', '${escapeQuotes(row.serial_number ?? '')}', '${row.purchase_date ?? ''}', '${row.warranty_to ?? ''}', ${row.assigned_to ?? 0}, '${escapeQuotes(row.condition_status ?? '')}')\" style="background:none;border:none;padding:4px 8px;font-size:18px;cursor:pointer;">✏️</button> <button title="Delete" onclick=\"deleteRow('${entity}', ${row.id})\" style="background:none;border:none;padding:4px 8px;font-size:18px;cursor:pointer;">🗑️</button></td>`;
} else if (entity === 'vendors') {
html += `<td><button title="Edit" onclick="editRow('vendors', ${row.id}, '${escapeQuotes(row.name ?? '')}', '${escapeQuotes(row.contact_info ?? '')}', '${escapeQuotes(row.address ?? '')}', '${escapeQuotes(row.email ?? '')}', '${escapeQuotes(row.phone ?? '')}', '${escapeQuotes(row.notes ?? '')}')" style="background:none;border:none;padding:4px 8px;font-size:18px;cursor:pointer;">✏️</button> <button title="Delete" onclick="deleteRow('vendors', ${row.id})" style="background:none;border:none;padding:4px 8px;font-size:18px;cursor:pointer;">🗑️</button></td>`;
}
html += '</tr>';
});
} else {
html += '<td>No data found</td></tr>';
}
html += '</table>';
if (entity === 'items') {
html += '</div>';
}
} else {
html += '<span style="color:red">Error loading data</span>';
}
document.getElementById(contentId).innerHTML = html;
})
.catch(() => {
document.getElementById(contentId).innerHTML = '<span style="color:red">Error loading data</span>';
});
}
// Helper to escape quotes in JS
function escapeQuotes(str) {
return String(str).replace(/'/g, "'").replace(/"/g, """);
}
// Add new row
window.addRow = function(entity, form) {
const fd = new FormData(form);
fetch('master_data_api.php?entity=' + entity + '&action=create', {
method: 'POST',
body: fd
}).then(res => res.json()).then(() => {
loadEntity(entity);
});
return false;
}
// Edit row
window.editRow = function(entity, ...args) {
let html = `<h4>Edit</h4><form onsubmit="return updateRow('${entity}', ${args[0]}, this)">`;
if (entity === 'roles') {
html += `Name: <input name="name" value="${args[1]}" required> Slug: <input name="slug" value="${args[2]}" required> <button type="submit">Update</button>`;
} else if (entity === 'teams' || entity === 'categories') {
html += `Name: <input name="name" value="${args[1]}" required> <button type="submit">Update</button>`;
} else if (entity === 'users') {
// Fetch roles and teams for dropdowns
Promise.all([
fetch('master_data_api.php?entity=roles&action=list').then(res => res.json()),
fetch('master_data_api.php?entity=teams&action=list').then(res => res.json())
]).then(([roles, teams]) => {
let roleOptions = '';
roles.forEach(r => {
roleOptions += `<option value="${r.slug}"${r.slug==args[3]?' selected':''}>${r.name}</option>`;
});
let teamOptions = '<option value="">None</option>';
teams.forEach(t => {
teamOptions += `<option value="${t.id}"${t.id==args[4]?' selected':''}>${t.name}</option>`;
});
html += `Name: <input name="name" value="${args[1]}" required> Email: <input name="email" value="${args[2]}" required> Role: <select name="role_slug">${roleOptions}</select> Team: <select name="team_id">${teamOptions}</select> Password: <input name="password" placeholder="Leave blank to keep unchanged"> <button type="submit">Update</button>`;
html += '</form> <button onclick="cancelEdit(\'' + entity + '\')">Cancel</button>';
document.getElementById('edit-form-' + entity).innerHTML = html;
document.getElementById('edit-form-' + entity).style.display = 'block';
});
return;
} else if (entity === 'vendors') {
html += `<h4>Edit</h4><form onsubmit="return updateRow('vendors', ${args[0]}, this)">Name: <input name='name' value='${args[1]}' required> Contact: <input name='contact_info' value='${args[2]}'> Address: <input name='address' value='${args[3]}'> Email: <input name='email' value='${args[4]}'> Phone: <input name='phone' value='${args[5]}'> Notes: <input name='notes' value='${args[6]}'> <button type='submit'>Update</button></form> <button onclick="cancelEdit('vendors')">Cancel</button>`;
}
html += '</form> <button onclick="cancelEdit(\'' + entity + '\')">Cancel</button>';
document.getElementById('edit-form-' + entity).innerHTML = html;
document.getElementById('edit-form-' + entity).style.display = 'block';
}
window.cancelEdit = function(entity) {
document.getElementById('edit-form-' + entity).style.display = 'none';
}
// Update row
window.updateRow = function(entity, id, form) {
const fd = new FormData(form);
fd.append('id', id);
fetch('master_data_api.php?entity=' + entity + '&action=update', {
method: 'POST',
body: fd
}).then(res => res.json()).then(() => {
loadEntity(entity);
});
return false;
}
// Delete row
window.deleteRow = function(entity, id) {
if (!confirm('Are you sure you want to delete this entry?')) return;
const fd = new FormData();
fd.append('id', id);
fetch('master_data_api.php?entity=' + entity + '&action=delete', {
method: 'POST',
body: fd
}).then(res => res.json()).then(() => {
loadEntity(entity);
});
}
// After loadEntity, fetch roles and teams for user form dropdowns
function fetchRolesAndTeams() {
fetch('master_data_api.php?entity=roles&action=list')
.then(res => res.json())
.then(roles => {
let roleOptions = '';
roles.forEach(r => {
roleOptions += `<option value="${r.slug}">${r.name}</option>`;
});
const roleSlugSelect = document.getElementById('role-slug-select');
if (roleSlugSelect) roleSlugSelect.innerHTML = roleOptions;
});
fetch('master_data_api.php?entity=teams&action=list')
.then(res => res.json())
.then(teams => {
let teamOptions = '<option value="">None</option>';
teams.forEach(t => {
teamOptions += `<option value="${t.id}">${t.name}</option>`;
});
const teamSelect = document.getElementById('team-select');
if (teamSelect) teamSelect.innerHTML = teamOptions;
});
}
// Patch loadEntity to call fetchRolesAndTeams for users
const origLoadEntity = loadEntity;
loadEntity = function(entity) {
origLoadEntity(entity);
if (entity === 'users') {
// Use MutationObserver to wait for dropdown to appear, then populate
const observer = new MutationObserver(() => {
const roleSelect = document.getElementById('role-slug-select');
const teamSelect = document.getElementById('team-select');
if (roleSelect && teamSelect) {
fetchRolesAndTeams();
observer.disconnect();
}
});
observer.observe(document.getElementById('users-content'), { childList: true, subtree: true });
}
}
function setDynamicButtons(tabName) {
const btns = document.getElementById('dynamic-buttons');
let label = tabName.charAt(0).toUpperCase() + tabName.slice(1);
btns.innerHTML = `<button onclick=\"exportSingle('${tabName}')\" style=\"background:#43b581; color:#fff; border:none; padding:8px 18px; border-radius:4px; font-weight:500;\">Export ${label}</button> <label style=\"background:#4285f4; color:#fff; border:none; padding:8px 18px; border-radius:4px; font-weight:500; cursor:pointer;\">Import ${label}<input type=\"file\" style=\"display:none;\" onchange=\"importSingle('${tabName}', this.files[0])\"></label>`;
}
function exportSingle(entity) {
window.location = 'master_data_api.php?entity=' + entity + '&action=export';
}
function importSingle(entity, file) {
if (!file) return;
const fd = new FormData();
fd.append('file', file);
fetch('master_data_api.php?entity=' + entity + '&action=import', {
method: 'POST',
body: fd
}).then(res => res.json()).then(data => {
if (data.success) {
alert('Import successful!');
loadEntity(entity);
} else {
alert('Import failed: ' + (data.error || 'Unknown error'));
}
});
}
// Initial load
setDynamicButtons('roles');
loadEntity('roles');
</script>
</body>
</html>
<?php
require_once '../inc/footer.php';
?>