1213 lines
42 KiB
HTML
1213 lines
42 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Personnel Database Manager</title>
|
||
<style>
|
||
/* Windows 95 Style */
|
||
body {
|
||
font-family: 'MS Sans Serif', 'Tahoma', sans-serif;
|
||
background-color: #008080;
|
||
margin: 0;
|
||
padding: 0;
|
||
color: #000;
|
||
font-size: 12px;
|
||
cursor: default;
|
||
user-select: none;
|
||
overflow: hidden;
|
||
height: 100vh;
|
||
}
|
||
|
||
.desktop {
|
||
position: relative;
|
||
width: 100%;
|
||
height: calc(100vh - 28px);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.window {
|
||
background-color: #c0c0c0;
|
||
border: 2px solid;
|
||
border-top-color: #dfdfdf;
|
||
border-left-color: #dfdfdf;
|
||
border-right-color: #404040;
|
||
border-bottom-color: #404040;
|
||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||
position: absolute;
|
||
min-width: 200px;
|
||
display: none;
|
||
}
|
||
|
||
.window.active {
|
||
z-index: 10;
|
||
display: block;
|
||
}
|
||
|
||
.title-bar {
|
||
background: linear-gradient(90deg, #000080, #1084d0);
|
||
color: white;
|
||
padding: 2px 3px;
|
||
font-weight: bold;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
cursor: move;
|
||
}
|
||
|
||
.title-bar-text {
|
||
padding-left: 22px;
|
||
background-repeat: no-repeat;
|
||
background-position: left center;
|
||
background-size: 16px 16px;
|
||
}
|
||
|
||
.title-bar-controls {
|
||
display: flex;
|
||
}
|
||
|
||
.title-bar-btn {
|
||
width: 16px;
|
||
height: 14px;
|
||
margin-left: 2px;
|
||
background-color: #c0c0c0;
|
||
border: 1px solid;
|
||
border-top-color: #ffffff;
|
||
border-left-color: #ffffff;
|
||
border-right-color: #404040;
|
||
border-bottom-color: #404040;
|
||
text-align: center;
|
||
line-height: 12px;
|
||
font-weight: bold;
|
||
color: black;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.title-bar-btn:active {
|
||
border-top-color: #404040;
|
||
border-left-color: #404040;
|
||
border-right-color: #ffffff;
|
||
border-bottom-color: #ffffff;
|
||
}
|
||
|
||
.window-body {
|
||
padding: 8px;
|
||
overflow: auto;
|
||
}
|
||
|
||
.btn {
|
||
background-color: #c0c0c0;
|
||
border: 2px solid;
|
||
border-top-color: #ffffff;
|
||
border-left-color: #ffffff;
|
||
border-right-color: #404040;
|
||
border-bottom-color: #404040;
|
||
padding: 3px 12px;
|
||
font-family: 'MS Sans Serif', sans-serif;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
min-width: 75px;
|
||
margin-right: 4px;
|
||
}
|
||
|
||
.btn:active {
|
||
border-top-color: #404040;
|
||
border-left-color: #404040;
|
||
border-right-color: #ffffff;
|
||
border-bottom-color: #ffffff;
|
||
}
|
||
|
||
.btn-primary {
|
||
background-color: #c0c0c0;
|
||
font-weight: bold;
|
||
}
|
||
|
||
input, select, textarea {
|
||
background-color: white;
|
||
border: 2px solid;
|
||
border-top-color: #404040;
|
||
border-left-color: #404040;
|
||
border-right-color: #ffffff;
|
||
border-bottom-color: #ffffff;
|
||
padding: 3px;
|
||
font-family: 'MS Sans Serif', sans-serif;
|
||
font-size: 12px;
|
||
}
|
||
|
||
input:focus, select:focus, textarea:focus {
|
||
outline: 1px dotted black;
|
||
}
|
||
|
||
.tab {
|
||
display: none;
|
||
}
|
||
|
||
.tab.active {
|
||
display: block;
|
||
}
|
||
|
||
.tab-buttons {
|
||
display: flex;
|
||
border-bottom: 2px solid #404040;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.tab-btn {
|
||
padding: 2px 8px;
|
||
background-color: #c0c0c0;
|
||
border: 2px solid;
|
||
border-bottom: none;
|
||
border-top-color: #ffffff;
|
||
border-left-color: #ffffff;
|
||
border-right-color: #404040;
|
||
margin-right: 2px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.tab-btn.active {
|
||
background-color: #ffffff;
|
||
position: relative;
|
||
top: 2px;
|
||
}
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
}
|
||
|
||
th {
|
||
background-color: #000080;
|
||
color: white;
|
||
text-align: left;
|
||
padding: 3px 5px;
|
||
}
|
||
|
||
td {
|
||
padding: 3px 5px;
|
||
border-bottom: 1px solid #404040;
|
||
background-color: white;
|
||
}
|
||
|
||
tr:nth-child(even) td {
|
||
background-color: #e0e0e0;
|
||
}
|
||
|
||
.status-bar {
|
||
background-color: #c0c0c0;
|
||
border-top: 2px solid #ffffff;
|
||
padding: 2px 5px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.modal {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 1000;
|
||
display: none;
|
||
}
|
||
|
||
.scrollable {
|
||
overflow: auto;
|
||
max-height: 300px;
|
||
}
|
||
|
||
.grid {
|
||
display: grid;
|
||
}
|
||
|
||
.grid-cols-2 {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
|
||
.gap-2 {
|
||
gap: 8px;
|
||
}
|
||
|
||
.field-row {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.field-label {
|
||
min-width: 100px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* Taskbar */
|
||
.taskbar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 28px;
|
||
background-color: #c0c0c0;
|
||
border-top: 2px solid white;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0;
|
||
z-index: 100;
|
||
}
|
||
|
||
.start-btn {
|
||
height: 22px;
|
||
padding: 2px 8px 2px 28px;
|
||
margin: 0 2px;
|
||
background-color: #c0c0c0;
|
||
border: 2px solid;
|
||
border-top-color: #ffffff;
|
||
border-left-color: #ffffff;
|
||
border-right-color: #404040;
|
||
border-bottom-color: #404040;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAJ5JREFUOI3tk7ENwjAQRZ+RUqRDlqCgZQSGYAyWoKdmDcZgCTqEUFCmCBR0KVIYLFvEDgUVEv90uvt3/51NQkQwx7KZNUeAJoevUaAH/kxU9SLLpQfqLCClBFADW6BOKfUjYEcMnICNcw61lAA8gK2ZfYGDmR3HLpSoqoC8M7Pg3GHaQQGcgUu2z2Z2ysqvwMo5dzezR1H9Zcn/Qkq5/QaS+Gv9AkIjJAAAAABJRU5ErkJggg==");
|
||
background-repeat: no-repeat;
|
||
background-position: 4px center;
|
||
}
|
||
|
||
.start-btn:active {
|
||
border-top-color: #404040;
|
||
border-left-color: #404040;
|
||
border-right-color: #ffffff;
|
||
border-bottom-color: #ffffff;
|
||
}
|
||
|
||
.task-buttons {
|
||
display: flex;
|
||
flex-grow: 1;
|
||
height: 22px;
|
||
margin: 0 4px;
|
||
}
|
||
|
||
.task-button {
|
||
padding: 0 8px 0 28px;
|
||
margin-right: 2px;
|
||
height: 22px;
|
||
min-width: 150px;
|
||
background-color: #c0c0c0;
|
||
border: 2px solid;
|
||
border-top-color: #ffffff;
|
||
border-left-color: #ffffff;
|
||
border-right-color: #404040;
|
||
border-bottom-color: #404040;
|
||
text-align: left;
|
||
line-height: 20px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
cursor: pointer;
|
||
background-repeat: no-repeat;
|
||
background-position: 4px center;
|
||
background-size: 16px 16px;
|
||
display: none;
|
||
}
|
||
|
||
.task-button.active {
|
||
border-top-color: #404040;
|
||
border-left-color: #404040;
|
||
border-right-color: #ffffff;
|
||
border-bottom-color: #ffffff;
|
||
background-color: #dfdfdf;
|
||
display: block;
|
||
}
|
||
|
||
.clock {
|
||
padding: 2px 6px;
|
||
height: 22px;
|
||
background-color: #c0c0c0;
|
||
border: 2px solid;
|
||
border-top-color: #404040;
|
||
border-left-color: #404040;
|
||
border-right-color: #ffffff;
|
||
border-bottom-color: #ffffff;
|
||
line-height: 20px;
|
||
}
|
||
|
||
/* Desktop Icon */
|
||
.desktop-icon {
|
||
position: absolute;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
width: 80px;
|
||
cursor: pointer;
|
||
padding: 5px;
|
||
}
|
||
|
||
.desktop-icon:hover {
|
||
background-color: rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.desktop-icon.selected {
|
||
background-color: rgba(0, 0, 128, 0.3);
|
||
}
|
||
|
||
.icon-img {
|
||
width: 32px;
|
||
height: 32px;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.icon-text {
|
||
color: white;
|
||
text-align: center;
|
||
text-shadow: 1px 1px 1px black;
|
||
font-size: 12px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
width: 100%;
|
||
}
|
||
|
||
/* Start Menu */
|
||
.start-menu {
|
||
position: absolute;
|
||
bottom: 28px;
|
||
left: 2px;
|
||
width: 200px;
|
||
background-color: #c0c0c0;
|
||
border: 2px solid;
|
||
border-top-color: #ffffff;
|
||
border-left-color: #ffffff;
|
||
border-right-color: #404040;
|
||
border-bottom-color: #404040;
|
||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||
z-index: 1000;
|
||
display: none;
|
||
}
|
||
|
||
.start-menu.active {
|
||
display: block;
|
||
}
|
||
|
||
.start-header {
|
||
background: linear-gradient(90deg, #000080, #1084d0);
|
||
color: white;
|
||
padding: 2px;
|
||
height: 18px;
|
||
width: 28px;
|
||
float: left;
|
||
writing-mode: vertical-rl;
|
||
text-orientation: mixed;
|
||
transform: rotate(180deg);
|
||
font-weight: bold;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.start-items {
|
||
float: right;
|
||
width: calc(100% - 36px);
|
||
}
|
||
|
||
.start-item {
|
||
padding: 4px 5px 4px 28px;
|
||
background-repeat: no-repeat;
|
||
background-position: 5px center;
|
||
background-size: 16px 16px;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.start-item:hover {
|
||
background-color: #000080;
|
||
color: white;
|
||
}
|
||
|
||
.start-divider {
|
||
height: 1px;
|
||
background-color: #808080;
|
||
margin: 2px 0;
|
||
}
|
||
|
||
/* Right-click menu */
|
||
.context-menu {
|
||
position: absolute;
|
||
background-color: #c0c0c0;
|
||
border: 2px solid;
|
||
border-top-color: #ffffff;
|
||
border-left-color: #ffffff;
|
||
border-right-color: #404040;
|
||
border-bottom-color: #404040;
|
||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||
z-index: 1000;
|
||
display: none;
|
||
}
|
||
|
||
.context-menu.active {
|
||
display: block;
|
||
}
|
||
|
||
.context-item {
|
||
padding: 4px 20px 4px 28px;
|
||
background-repeat: no-repeat;
|
||
background-position: 5px center;
|
||
background-size: 16px 16px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.context-item:hover {
|
||
background-color: #000080;
|
||
color: white;
|
||
}
|
||
|
||
.context-divider {
|
||
height: 1px;
|
||
background-color: #808080;
|
||
margin: 2px 0;
|
||
}
|
||
|
||
/* Window with focus vs inactive */
|
||
.window.inactive .title-bar {
|
||
background: #808080;
|
||
color: #c0c0c0;
|
||
}
|
||
|
||
/* Animated wait cursor */
|
||
.wait-cursor {
|
||
cursor: wait !important;
|
||
}
|
||
</style>
|
||
</head>
|
||
<!-- Main Application Window -->
|
||
<div class="window active" id="mainApp" style="top: 80px; left: 100px; width: 600px; height: 400px;">
|
||
<div class="title-bar">
|
||
<div class="title-bar-text">Personnel Database</div>
|
||
<div class="title-bar-controls">
|
||
<div class="title-bar-btn" onclick="document.getElementById('mainApp').style.display='none'">×</div>
|
||
</div>
|
||
</div>
|
||
<div class="window-body">
|
||
<!-- Tab Buttons -->
|
||
<div class="tab-buttons">
|
||
<div class="tab-btn active" onclick="showTab(event, 'listTab')">List View</div>
|
||
<div class="tab-btn" onclick="showTab(event, 'addTab')">Add Personnel</div>
|
||
</div>
|
||
|
||
<!-- Tab Contents -->
|
||
<div id="listTab" class="tab active">
|
||
<div class="scrollable">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Department</th>
|
||
<th>Role</th>
|
||
<th>Email</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="personnelList">
|
||
<!-- JavaScript will populate rows here -->
|
||
<tr>
|
||
<td>Jane Doe</td>
|
||
<td>Engineering</td>
|
||
<td>Software Engineer</td>
|
||
<td>jane.doe@example.com</td>
|
||
</tr>
|
||
<tr>
|
||
<td>John Smith</td>
|
||
<td>HR</td>
|
||
<td>Recruiter</td>
|
||
<td>john.smith@example.com</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="addTab" class="tab">
|
||
<form onsubmit="addPersonnel(event)">
|
||
<div class="grid grid-cols-2 gap-2">
|
||
<div class="field-row">
|
||
<label class="field-label" for="name">Name:</label>
|
||
<input type="text" id="name" name="name" required>
|
||
</div>
|
||
<div class="field-row">
|
||
<label class="field-label" for="department">Department:</label>
|
||
<input type="text" id="department" name="department" required>
|
||
</div>
|
||
<div class="field-row">
|
||
<label class="field-label" for="role">Role:</label>
|
||
<input type="text" id="role" name="role" required>
|
||
</div>
|
||
<div class="field-row">
|
||
<label class="field-label" for="email">Email:</label>
|
||
<input type="email" id="email" name="email" required>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top: 10px;">
|
||
<button class="btn btn-primary" type="submit">Add</button>
|
||
<button class="btn" type="reset">Reset</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script>
|
||
// Database connection
|
||
let db = null;
|
||
let currentDatabase = 'none';
|
||
let database = []; // Fallback for localStorage mode
|
||
let SQL = null; // SQL.js instance
|
||
|
||
// Initialize the application
|
||
function init() {
|
||
updateClock();
|
||
setInterval(updateClock, 1000);
|
||
|
||
// Try to connect to IndexedDB first
|
||
connectIndexedDB();
|
||
}
|
||
|
||
// Update the clock
|
||
function updateClock() {
|
||
const now = new Date();
|
||
let hours = now.getHours();
|
||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||
const ampm = hours >= 12 ? 'PM' : 'AM';
|
||
hours = hours % 12;
|
||
hours = hours ? hours : 12; // the hour '0' should be '12'
|
||
document.getElementById('clock').textContent = `${hours}:${minutes} ${ampm}`;
|
||
}
|
||
|
||
// Switch between tabs
|
||
function switchTab(tabId) {
|
||
document.querySelectorAll('.tab').forEach(tab => {
|
||
tab.classList.remove('active');
|
||
});
|
||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||
btn.classList.remove('active');
|
||
});
|
||
|
||
document.getElementById(tabId).classList.add('active');
|
||
document.querySelector(`.tab-btn[onclick="switchTab('${tabId}')"]`).classList.add('active');
|
||
}
|
||
|
||
// Connect to IndexedDB
|
||
function connectIndexedDB() {
|
||
return new Promise((resolve, reject) => {
|
||
const request = indexedDB.open('PersonnelDatabase', 1);
|
||
|
||
request.onerror = (event) => {
|
||
console.error('IndexedDB error:', event.target.error);
|
||
setStatus('Could not connect to IndexedDB. Using localStorage fallback.', 'warning');
|
||
connectLocalStorage();
|
||
reject(event.target.error);
|
||
};
|
||
|
||
request.onsuccess = (event) => {
|
||
db = event.target.result;
|
||
currentDatabase = 'indexeddb';
|
||
setStatus('Connected to IndexedDB', 'success');
|
||
updateDatabaseStats();
|
||
renderDatabase();
|
||
resolve(db);
|
||
};
|
||
|
||
request.onupgradeneeded = (event) => {
|
||
const db = event.target.result;
|
||
if (!db.objectStoreNames.contains('personnel')) {
|
||
const store = db.createObjectStore('personnel', { keyPath: 'id' });
|
||
store.createIndex('name', 'name', { unique: false });
|
||
store.createIndex('status', 'status', { unique: false });
|
||
}
|
||
};
|
||
});
|
||
}
|
||
|
||
// Fallback to localStorage
|
||
function connectLocalStorage() {
|
||
if (localStorage.getItem('personnelDatabase')) {
|
||
database = JSON.parse(localStorage.getItem('personnelDatabase'));
|
||
} else {
|
||
database = [];
|
||
localStorage.setItem('personnelDatabase', JSON.stringify(database));
|
||
}
|
||
currentDatabase = 'local';
|
||
setStatus('Connected to localStorage', 'success');
|
||
updateDatabaseStats();
|
||
renderDatabase();
|
||
}
|
||
|
||
// Connect to database (user action)
|
||
function connectDatabase() {
|
||
connectIndexedDB().catch(() => {
|
||
connectLocalStorage();
|
||
});
|
||
}
|
||
|
||
// Set status message
|
||
function setStatus(message, type = 'info') {
|
||
const statusElement = document.getElementById('statusMessage');
|
||
statusElement.textContent = message;
|
||
|
||
// Reset classes
|
||
statusElement.className = '';
|
||
|
||
// Add type-specific class
|
||
if (type === 'error') {
|
||
statusElement.style.color = 'red';
|
||
statusElement.style.fontWeight = 'bold';
|
||
} else if (type === 'success') {
|
||
statusElement.style.color = 'green';
|
||
} else if (type === 'warning') {
|
||
statusElement.style.color = 'orange';
|
||
}
|
||
}
|
||
|
||
// Update database statistics
|
||
function updateDatabaseStats() {
|
||
if (currentDatabase === 'none') {
|
||
document.getElementById('recordCount').textContent = '0';
|
||
document.getElementById('dbStatus').textContent = 'Not connected';
|
||
document.getElementById('lastUpdated').textContent = 'Never';
|
||
return;
|
||
}
|
||
|
||
if (currentDatabase === 'indexeddb' && db) {
|
||
const transaction = db.transaction(['personnel'], 'readonly');
|
||
const store = transaction.objectStore('personnel');
|
||
const countRequest = store.count();
|
||
|
||
countRequest.onsuccess = () => {
|
||
document.getElementById('recordCount').textContent = countRequest.result;
|
||
document.getElementById('dbStatus').textContent = 'Connected (IndexedDB)';
|
||
document.getElementById('lastUpdated').textContent = new Date().toLocaleString();
|
||
};
|
||
|
||
countRequest.onerror = () => {
|
||
document.getElementById('recordCount').textContent = 'Error';
|
||
document.getElementById('dbStatus').textContent = 'Error';
|
||
};
|
||
} else if (currentDatabase === 'local') {
|
||
document.getElementById('recordCount').textContent = database.length;
|
||
document.getElementById('dbStatus').textContent = 'Connected (localStorage)';
|
||
document.getElementById('lastUpdated').textContent = new Date().toLocaleString();
|
||
}
|
||
}
|
||
|
||
// Add custom field to form
|
||
function addCustomField() {
|
||
const container = document.getElementById('customFieldsContainer');
|
||
const fieldId = 'custom_' + Date.now();
|
||
|
||
const fieldDiv = document.createElement('div');
|
||
fieldDiv.className = 'field-row';
|
||
fieldDiv.innerHTML = `
|
||
<input type="text" placeholder="Field name" class="custom-field-name" style="width: 100px;">
|
||
<input type="text" placeholder="Value" class="custom-field-value" style="width: 150px;">
|
||
<button class="btn" onclick="this.parentNode.remove()" style="padding: 0 5px; min-width: auto;">×</button>
|
||
`;
|
||
|
||
container.appendChild(fieldDiv);
|
||
}
|
||
|
||
// Save record to database
|
||
function saveRecord() {
|
||
if (currentDatabase === 'none') {
|
||
setStatus('No database connected', 'error');
|
||
return;
|
||
}
|
||
|
||
const id = document.getElementById('id').value;
|
||
const name = document.getElementById('name').value;
|
||
const dob = document.getElementById('dob').value;
|
||
const nationality = document.getElementById('nationality').value;
|
||
const status = document.getElementById('status').value;
|
||
const associates = document.getElementById('associates').value;
|
||
|
||
if (!id || !name) {
|
||
setStatus('ID and Name are required fields', 'error');
|
||
return;
|
||
}
|
||
|
||
// Get custom fields
|
||
const customFields = {};
|
||
document.querySelectorAll('.custom-field-name').forEach((nameInput, index) => {
|
||
const valueInput = document.querySelectorAll('.custom-field-value')[index];
|
||
if (nameInput.value.trim() !== '') {
|
||
customFields[nameInput.value.trim()] = valueInput.value;
|
||
}
|
||
});
|
||
|
||
const record = {
|
||
id,
|
||
name,
|
||
dob,
|
||
nationality,
|
||
status,
|
||
associates,
|
||
customFields: JSON.stringify(customFields),
|
||
lastUpdated: new Date().toISOString()
|
||
};
|
||
|
||
if (currentDatabase === 'indexeddb' && db) {
|
||
const transaction = db.transaction(['personnel'], 'readwrite');
|
||
const store = transaction.objectStore('personnel');
|
||
|
||
// Check if record exists
|
||
const getRequest = store.get(id);
|
||
|
||
getRequest.onsuccess = () => {
|
||
if (getRequest.result) {
|
||
if (confirm(`Record with ID ${id} already exists. Update it?`)) {
|
||
const updateRequest = store.put(record);
|
||
|
||
updateRequest.onsuccess = () => {
|
||
setStatus(`Record ${id} updated successfully`, 'success');
|
||
updateDatabaseStats();
|
||
renderDatabase();
|
||
};
|
||
|
||
updateRequest.onerror = () => {
|
||
setStatus('Error updating record', 'error');
|
||
};
|
||
}
|
||
} else {
|
||
const addRequest = store.add(record);
|
||
|
||
addRequest.onsuccess = () => {
|
||
setStatus(`Record ${id} added successfully`, 'success');
|
||
updateDatabaseStats();
|
||
renderDatabase();
|
||
};
|
||
|
||
addRequest.onerror = () => {
|
||
setStatus('Error adding record', 'error');
|
||
};
|
||
}
|
||
};
|
||
|
||
getRequest.onerror = () => {
|
||
setStatus('Error checking record existence', 'error');
|
||
};
|
||
} else if (currentDatabase === 'local') {
|
||
const existingIndex = database.findIndex(r => r.id === id);
|
||
|
||
if (existingIndex >= 0) {
|
||
if (confirm(`Record with ID ${id} already exists. Update it?`)) {
|
||
database[existingIndex] = record;
|
||
localStorage.setItem('personnelDatabase', JSON.stringify(database));
|
||
setStatus(`Record ${id} updated successfully`, 'success');
|
||
}
|
||
} else {
|
||
database.push(record);
|
||
localStorage.setItem('personnelDatabase', JSON.stringify(database));
|
||
setStatus(`Record ${id} added successfully`, 'success');
|
||
}
|
||
|
||
updateDatabaseStats();
|
||
renderDatabase();
|
||
}
|
||
}
|
||
|
||
// Clear form
|
||
function clearForm() {
|
||
document.getElementById('personForm').reset();
|
||
document.getElementById('customFieldsContainer').innerHTML = '';
|
||
}
|
||
|
||
// Render database table
|
||
function renderDatabase() {
|
||
const tableBody = document.getElementById('databaseTable').querySelector('tbody');
|
||
|
||
if (currentDatabase === 'none') {
|
||
tableBody.innerHTML = '<tr><td colspan="5" style="text-align: center;">No database connection</td></tr>';
|
||
return;
|
||
}
|
||
|
||
if (currentDatabase === 'indexeddb' && db) {
|
||
const transaction = db.transaction(['personnel'], 'readonly');
|
||
const store = transaction.objectStore('personnel');
|
||
const request = store.getAll();
|
||
|
||
request.onsuccess = () => {
|
||
const records = request.result;
|
||
displayRecords(records);
|
||
};
|
||
|
||
request.onerror = () => {
|
||
tableBody.innerHTML = '<tr><td colspan="5" style="text-align: center;">Error loading records</td></tr>';
|
||
};
|
||
} else if (currentDatabase === 'local') {
|
||
displayRecords(database);
|
||
}
|
||
}
|
||
|
||
// Display records in table
|
||
function displayRecords(records) {
|
||
const tableBody = document.getElementById('databaseTable').querySelector('tbody');
|
||
|
||
if (records.length === 0) {
|
||
tableBody.innerHTML = '<tr><td colspan="5" style="text-align: center;">No records found</td></tr>';
|
||
return;
|
||
}
|
||
|
||
tableBody.innerHTML = '';
|
||
|
||
records.forEach(record => {
|
||
const row = document.createElement('tr');
|
||
|
||
row.innerHTML = `
|
||
<td>${record.id}</td>
|
||
<td>${record.name}</td>
|
||
<td>${record.dob || ''}</td>
|
||
<td>${record.status || ''}</td>
|
||
<td>
|
||
<button class="btn" onclick="viewRecord('${record.id}')" style="padding: 0 5px; min-width: auto;">View</button>
|
||
<button class="btn" onclick="deleteRecord('${record.id}')" style="padding: 0 5px; min-width: auto;">Delete</button>
|
||
</td>
|
||
`;
|
||
|
||
tableBody.appendChild(row);
|
||
});
|
||
}
|
||
|
||
// View record details
|
||
function viewRecord(id) {
|
||
if (currentDatabase === 'indexeddb' && db) {
|
||
const transaction = db.transaction(['personnel'], 'readonly');
|
||
const store = transaction.objectStore('personnel');
|
||
const request = store.get(id);
|
||
|
||
request.onsuccess = () => {
|
||
if (request.result) {
|
||
displayRecordDetails(request.result);
|
||
} else {
|
||
setStatus('Record not found', 'error');
|
||
}
|
||
};
|
||
|
||
request.onerror = () => {
|
||
setStatus('Error retrieving record', 'error');
|
||
};
|
||
} else if (currentDatabase === 'local') {
|
||
const record = database.find(r => r.id === id);
|
||
if (record) {
|
||
displayRecordDetails(record);
|
||
} else {
|
||
setStatus('Record not found', 'error');
|
||
}
|
||
}
|
||
}
|
||
|
||
// Display record details in modal
|
||
function displayRecordDetails(record) {
|
||
let customFields = {};
|
||
try {
|
||
if (record.customFields) {
|
||
customFields = JSON.parse(record.customFields);
|
||
}
|
||
} catch (e) {
|
||
console.error('Error parsing custom fields:', e);
|
||
}
|
||
|
||
const modalContent = document.getElementById('modalContent');
|
||
modalContent.innerHTML = `
|
||
<div class="field-row">
|
||
<div class="field-label">ID:</div>
|
||
<div>${record.id}</div>
|
||
</div>
|
||
<div class="field-row">
|
||
<div class="field-label">Name:</div>
|
||
<div>${record.name}</div>
|
||
</div>
|
||
<div class="field-row">
|
||
<div class="field-label">Date of Birth:</div>
|
||
<div>${record.dob || 'N/A'}</div>
|
||
</div>
|
||
<div class="field-row">
|
||
<div class="field-label">Nationality:</div>
|
||
<div>${record.nationality || 'N/A'}</div>
|
||
</div>
|
||
<div class="field-row">
|
||
<div class="field-label">Status:</div>
|
||
<div>${record.status || 'N/A'}</div>
|
||
</div>
|
||
<div class="field-row">
|
||
<div class="field-label">Associates:</div>
|
||
<div>${record.associates || 'N/A'}</div>
|
||
</div>
|
||
<div style="margin-top: 10px;">
|
||
<strong>Custom Fields:</strong>
|
||
${Object.keys(customFields).length > 0 ?
|
||
Object.entries(customFields).map(([key, value]) =>
|
||
`<div class="field-row">
|
||
<div class="field-label">${key}:</div>
|
||
<div>${value}</div>
|
||
</div>`
|
||
).join('') :
|
||
'<div>No custom fields</div>'
|
||
}
|
||
</div>
|
||
`;
|
||
|
||
document.getElementById('recordModal').style.display = 'flex';
|
||
}
|
||
|
||
// Close modal
|
||
function closeModal() {
|
||
document.getElementById('recordModal').style.display = 'none';
|
||
}
|
||
|
||
// Delete record
|
||
function deleteRecord(id) {
|
||
if (!confirm('Are you sure you want to delete this record?')) {
|
||
return;
|
||
}
|
||
|
||
if (currentDatabase === 'indexeddb' && db) {
|
||
const transaction = db.transaction(['personnel'], 'readwrite');
|
||
const store = transaction.objectStore('personnel');
|
||
const request = store.delete(id);
|
||
|
||
request.onsuccess = () => {
|
||
setStatus(`Record ${id} deleted`, 'success');
|
||
updateDatabaseStats();
|
||
renderDatabase();
|
||
};
|
||
|
||
request.onerror = () => {
|
||
setStatus('Error deleting record', 'error');
|
||
};
|
||
} else if (currentDatabase === 'local') {
|
||
const index = database.findIndex(r => r.id === id);
|
||
if (index >= 0) {
|
||
database.splice(index, 1);
|
||
localStorage.setItem('personnelDatabase', JSON.stringify(database));
|
||
setStatus(`Record ${id} deleted`, 'success');
|
||
updateDatabaseStats();
|
||
renderDatabase();
|
||
} else {
|
||
setStatus('Record not found', 'error');
|
||
}
|
||
}
|
||
}
|
||
|
||
// Search records
|
||
function searchRecords() {
|
||
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
||
|
||
if (searchTerm.trim() === '') {
|
||
renderDatabase();
|
||
return;
|
||
}
|
||
|
||
if (currentDatabase === 'indexeddb' && db) {
|
||
const transaction = db.transaction(['personnel'], 'readonly');
|
||
const store = transaction.objectStore('personnel');
|
||
const request = store.getAll();
|
||
|
||
request.onsuccess = () => {
|
||
const records = request.result.filter(record =>
|
||
(record.id && record.id.toLowerCase().includes(searchTerm)) ||
|
||
(record.name && record.name.toLowerCase().includes(searchTerm)) ||
|
||
(record.status && record.status.toLowerCase().includes(searchTerm)) ||
|
||
(record.nationality && record.nationality.toLowerCase().includes(searchTerm)) ||
|
||
(record.associates && record.associates.toLowerCase().includes(searchTerm))
|
||
);
|
||
|
||
displayRecords(records);
|
||
};
|
||
|
||
request.onerror = () => {
|
||
setStatus('Error searching records', 'error');
|
||
};
|
||
} else if (currentDatabase === 'local') {
|
||
const filtered = database.filter(record =>
|
||
(record.id && record.id.toLowerCase().includes(searchTerm)) ||
|
||
(record.name && record.name.toLowerCase().includes(searchTerm)) ||
|
||
(record.status && record.status.toLowerCase().includes(searchTerm)) ||
|
||
(record.nationality && record.nationality.toLowerCase().includes(searchTerm)) ||
|
||
(record.associates && record.associates.toLowerCase().includes(searchTerm))
|
||
);
|
||
|
||
displayRecords(filtered);
|
||
}
|
||
}
|
||
|
||
// Export database
|
||
function exportDatabase() {
|
||
if (currentDatabase === 'none') {
|
||
setStatus('No database connected', 'error');
|
||
return;
|
||
}
|
||
|
||
if (currentDatabase === 'indexeddb' && db) {
|
||
const transaction = db.transaction(['personnel'], 'readonly');
|
||
const store = transaction.objectStore('personnel');
|
||
const request = store.getAll();
|
||
|
||
request.onsuccess = () => {
|
||
const data = request.result;
|
||
const dataStr = JSON.stringify(data, null, 2);
|
||
const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr);
|
||
|
||
const exportName = 'personnel-database-' + new Date().toISOString().slice(0, 10) + '.json';
|
||
|
||
const linkElement = document.createElement('a');
|
||
linkElement.setAttribute('href', dataUri);
|
||
linkElement.setAttribute('download', exportName);
|
||
linkElement.click();
|
||
|
||
setStatus('Database exported as JSON', 'success');
|
||
};
|
||
|
||
request.onerror = () => {
|
||
setStatus('Error exporting database', 'error');
|
||
};
|
||
} else if (currentDatabase === 'local') {
|
||
const dataStr = JSON.stringify(database, null, 2);
|
||
const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr);
|
||
|
||
const exportName = 'personnel-database-' + new Date().toISOString().slice(0, 10) + '.json';
|
||
|
||
const linkElement = document.createElement('a');
|
||
linkElement.setAttribute('href', dataUri);
|
||
linkElement.setAttribute('download', exportName);
|
||
linkElement.click();
|
||
|
||
setStatus('Database exported as JSON', 'success');
|
||
}
|
||
}
|
||
|
||
// Import database
|
||
function importDatabase() {
|
||
if (currentDatabase === 'none') {
|
||
setStatus('No database connected', 'error');
|
||
return;
|
||
}
|
||
|
||
const input = document.createElement('input');
|
||
input.type = 'file';
|
||
input.accept = '.json';
|
||
|
||
input.onchange = e => {
|
||
const file = e.target.files[0];
|
||
const reader = new FileReader();
|
||
|
||
reader.onload = () => {
|
||
try {
|
||
const data = JSON.parse(reader.result);
|
||
|
||
if (!Array.isArray(data)) {
|
||
setStatus('Invalid data format', 'error');
|
||
return;
|
||
}
|
||
|
||
if (!confirm(`Import ${data.length} records? This will overwrite current data.`)) {
|
||
return;
|
||
}
|
||
|
||
if (currentDatabase === 'indexeddb' && db) {
|
||
const transaction = db.transaction(['personnel'], 'readwrite');
|
||
const store = transaction.objectStore('personnel');
|
||
|
||
// Clear existing data
|
||
const clearRequest = store.clear();
|
||
|
||
clearRequest.onsuccess = () => {
|
||
// Add new data
|
||
let successCount = 0;
|
||
let errorCount = 0;
|
||
|
||
data.forEach(record => {
|
||
const addRequest = store.add(record);
|
||
|
||
addRequest.onsuccess = () => {
|
||
successCount++;
|
||
};
|
||
|
||
addRequest.onerror = () => {
|
||
errorCount++;
|
||
};
|
||
});
|
||
|
||
transaction.oncomplete = () => {
|
||
setStatus(`Imported ${successCount} records, ${errorCount} errors`,
|
||
errorCount > 0 ? 'warning' : 'success');
|
||
updateDatabaseStats();
|
||
renderDatabase();
|
||
};
|
||
};
|
||
|
||
clearRequest.onerror = () => {
|
||
setStatus('Error clearing database', 'error');
|
||
};
|
||
} else if (currentDatabase === 'local') {
|
||
database = data;
|
||
localStorage.setItem('personnelDatabase', JSON.stringify(database));
|
||
setStatus(`Imported ${data.length} records`, 'success');
|
||
updateDatabaseStats();
|
||
renderDatabase();
|
||
}
|
||
} catch (err) {
|
||
setStatus('Error parsing JSON file', 'error');
|
||
}
|
||
};
|
||
|
||
reader.onerror = () => {
|
||
setStatus('Error reading file', 'error');
|
||
};
|
||
|
||
reader.readAsText(file);
|
||
};
|
||
|
||
input.click();
|
||
}
|
||
|
||
// Execute SQL query (placeholder - would need SQL.js for full functionality)
|
||
function executeQuery() {
|
||
setStatus('SQL queries not implemented in this version', 'warning');
|
||
}
|
||
|
||
// Clear SQL query
|
||
function clearQuery() {
|
||
document.getElementById('sqlCommand').value = '';
|
||
}
|
||
|
||
// Initialize the application when the page loads
|
||
window.onload = init;
|
||
|
||
// Make windows draggable
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const windows = document.querySelectorAll('.window');
|
||
|
||
windows.forEach(window => {
|
||
const titleBar = window.querySelector('.title-bar');
|
||
let isDragging = false;
|
||
let offsetX, offsetY;
|
||
|
||
titleBar.addEventListener('mousedown', (e) => {
|
||
if (e.target === titleBar || e.target.classList.contains('title-bar-text')) {
|
||
isDragging = true;
|
||
offsetX = e.clientX - window.getBoundingClientRect().left;
|
||
offsetY = e.clientY - window.getBoundingClientRect().top;
|
||
}
|
||
});
|
||
|
||
document.addEventListener('mousemove', (e) => {
|
||
if (isDragging) {
|
||
window.style.left = (e.clientX - offsetX) + 'px';
|
||
window.style.top = (e.clientY - offsetY) + 'px';
|
||
}
|
||
});
|
||
|
||
document.addEventListener('mouseup', () => {
|
||
isDragging = false;
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |