Files
people_database/index.html
2025-05-13 14:56:45 +02:00

1213 lines
42 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>