@ -3,7 +3,7 @@
< head >
< head >
< meta charset = "UTF-8" >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< meta name = "generator" content = "EasySmartInventory v1.0. 0 ">
< meta name = "generator" content = "EasySmartInventory v1.0. 1 ">
< title > Machine_Inventarisatie< / title >
< title > Machine_Inventarisatie< / title >
< style >
< style >
@ -349,6 +349,43 @@ input.invalid, select.invalid, textarea.invalid {
.photo-container.has-photo .photo-preview { display: block !important; }
.photo-container.has-photo .photo-preview { display: block !important; }
.photo-container.has-photo .photo-buttons { display: flex !important; }
.photo-container.has-photo .photo-buttons { display: flex !important; }
.form-selector-container {
padding: 15px 30px;
background: linear-gradient(to right, #f8f9fa, #e9ecef);
border-bottom: 1px solid #ddd;
display: flex;
align-items: center;
gap: 15px;
flex-wrap: wrap;
}
.form-selector-container label {
font-weight: 600;
margin: 0;
white-space: nowrap;
}
.form-selector-container select {
flex: 1;
min-width: 200px;
max-width: 400px;
}
.form-selector-container .btn {
padding: 8px 16px;
font-size: 0.9em;
}
@media (max-width: 768px) {
.form-selector-container {
flex-direction: column;
align-items: stretch;
}
.form-selector-container select {
max-width: 100%;
}
}
< / style >
< / style >
< / head >
< / head >
< body >
< body >
@ -356,9 +393,17 @@ input.invalid, select.invalid, textarea.invalid {
< header >
< header >
< h1 > Machine_Inventarisatie< / h1 >
< h1 > Machine_Inventarisatie< / h1 >
< div class = "version" > Versie 1.0.0 < / div >
< div class = "version" > Versie 1.0.1 < / div >
< / header >
< / header >
< div class = "form-selector-container" >
< label for = "form-selector" > 📋 Opgeslagen formulieren:< / label >
< select id = "form-selector" >
< option value = "" > -- Selecteer --< / option >
< / select >
< button type = "button" class = "btn btn-danger" onclick = "deleteForm()" > 🗑️ Verwijder< / button >
< / div >
< form id = "inventory-form" onsubmit = "return false;" >
< form id = "inventory-form" onsubmit = "return false;" >
< section class = "section" >
< section class = "section" >
< h2 > Basisinformatie< / h2 >
< h2 > Basisinformatie< / h2 >
@ -675,14 +720,14 @@ input.invalid, select.invalid, textarea.invalid {
< div class = "actions" >
< div class = "actions" >
< button type = "button" class = "btn btn-primary" onclick = "exportCSV()" > 📥 Exporteer CSV< / button >
< button type = "button" class = "btn btn-primary" onclick = "exportCSV()" > 📥 Exporteer CSV< / button >
< button type = "button" class = "btn btn-success" onclick = "sendEmail(this)" > 📧 Verstuur per Email< / button >
< button type = "button" class = "btn btn-success" onclick = "sendEmail(this)" > 📧 Verstuur per Email< / button >
< button type = "button" class = "btn btn- dange r" onclick = "clearForm()" > 🗑️ Formulier Wissen < / button >
< button type = "button" class = "btn btn- secon dary " onclick = "clearForm()" > 🔄 Nieuw Formulier < / button >
< / div >
< / div >
< / form >
< / form >
< div class = "status-bar" >
< div class = "status-bar" >
< span id = "save-indicator" class = "save-indicator" > Gereed< / span >
< span id = "save-indicator" class = "save-indicator" > Gereed< / span >
< span > EasySmartInventory v1.0.0 < / span >
< span > EasySmartInventory v1.0.1 < / span >
< / div >
< / div >
< / div >
< / div >
@ -693,13 +738,15 @@ input.invalid, select.invalid, textarea.invalid {
(function() {
(function() {
'use strict';
'use strict';
const CONFIG = {"name": "Machine_Inventarisatie", "version": "1.0.0", "autosave": {"enabled": true, "interval_seconds": 5, "use_url_hash": true, "use_localstorage": true}, "export": {"csv": {"enabled": true, "include_photo": true}, "mailto": {"enabled": true, "to": "inventaris@bedrijf.nl", "subject_prefix": "Inventarisatie", "subject_fields": ["serienummer", "merk"], "include_timestamp": true}}};
const CONFIG = {"name": "Machine_Inventarisatie", "version": "1.0.1", "autosave": {"enabled": true, "interval_seconds": 5, "use_url_hash": true, "use_localstorage": true}, "export": {"csv": {"enabled": true, "include_photo": true}, "mailto": {"enabled": true, "to": "inventaris@bedrijf.nl", "subject_prefix": "Inventarisatie", "subject_fields": ["serienummer", "merk"], "include_timestamp": true}}, "unique_id_fields": ["serienummer", "asset_id"]};
const STORAGE_KEY = 'inventory_' + CONFIG.name + '_' + CONFIG.version;
const FORMS_INDEX_KEY = 'inventory_' + CONFIG.name + '_forms_index';
let currentFormId = null;
let saveTimeout = null;
let saveTimeout = null;
let hasChanges = false;
let hasChanges = false;
// Initialize
// Initialize
document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('DOMContentLoaded', function() {
setupFormSelector();
loadSavedData();
loadSavedData();
setupAutoSave();
setupAutoSave();
setupValidation();
setupValidation();
@ -707,6 +754,173 @@ input.invalid, select.invalid, textarea.invalid {
updateSaveIndicator('loaded');
updateSaveIndicator('loaded');
});
});
// Get unique form ID based on configured fields
function getFormUniqueId() {
if (!CONFIG.unique_id_fields || CONFIG.unique_id_fields.length === 0) {
return 'default';
}
const parts = [];
CONFIG.unique_id_fields.forEach(fieldId => {
const field = document.getElementById(fieldId);
if (field & & field.value & & field.value.trim()) {
parts.push(field.value.trim());
}
});
return parts.length > 0 ? parts.join('_') : null;
}
// Get storage key for a specific form
function getStorageKey(formId) {
return 'inventory_' + CONFIG.name + '_' + CONFIG.version + '_form_' + formId;
}
// Get all saved forms from index
function getSavedForms() {
try {
const index = localStorage.getItem(FORMS_INDEX_KEY);
return index ? JSON.parse(index) : [];
} catch (e) {
return [];
}
}
// Save form to index
function saveFormToIndex(formId, label) {
const forms = getSavedForms();
const existing = forms.findIndex(f => f.id === formId);
const formInfo = {
id: formId,
label: label || formId,
lastModified: new Date().toISOString()
};
if (existing >= 0) {
forms[existing] = formInfo;
} else {
forms.push(formInfo);
}
localStorage.setItem(FORMS_INDEX_KEY, JSON.stringify(forms));
updateFormSelector();
}
// Remove form from index
function removeFormFromIndex(formId) {
const forms = getSavedForms().filter(f => f.id !== formId);
localStorage.setItem(FORMS_INDEX_KEY, JSON.stringify(forms));
localStorage.removeItem(getStorageKey(formId));
updateFormSelector();
}
// Setup form selector dropdown
function setupFormSelector() {
const selector = document.getElementById('form-selector');
if (!selector) return;
updateFormSelector();
selector.addEventListener('change', function() {
const selectedId = this.value;
if (selectedId === '__new__') {
clearFormData();
currentFormId = null;
showToast('Nieuw formulier gestart', 'success');
} else if (selectedId) {
loadFormById(selectedId);
}
});
// Watch unique ID fields for changes
if (CONFIG.unique_id_fields) {
CONFIG.unique_id_fields.forEach(fieldId => {
const field = document.getElementById(fieldId);
if (field) {
field.addEventListener('blur', function() {
const newId = getFormUniqueId();
if (newId & & newId !== currentFormId) {
// Check if this form already exists
const existing = getSavedForms().find(f => f.id === newId);
if (existing) {
if (confirm('Er bestaat al een formulier met deze ID. Wilt u dit formulier laden?')) {
loadFormById(newId);
}
}
}
});
}
});
}
}
// Update form selector options
function updateFormSelector() {
const selector = document.getElementById('form-selector');
if (!selector) return;
const forms = getSavedForms();
const currentValue = selector.value;
// Clear and rebuild
selector.innerHTML = '< option value = "" > -- Selecteer opgeslagen formulier --< / option > ';
selector.innerHTML += '< option value = "__new__" > ➕ Nieuw formulier< / option > ';
if (forms.length > 0) {
const optgroup = document.createElement('optgroup');
optgroup.label = 'Opgeslagen formulieren (' + forms.length + ')';
forms.sort((a, b) => new Date(b.lastModified) - new Date(a.lastModified));
forms.forEach(form => {
const option = document.createElement('option');
option.value = form.id;
const date = new Date(form.lastModified).toLocaleString('nl-NL');
option.textContent = form.label + ' (' + date + ')';
if (form.id === currentFormId) {
option.selected = true;
}
optgroup.appendChild(option);
});
selector.appendChild(optgroup);
}
}
// Load a specific form by ID
function loadFormById(formId) {
try {
const saved = localStorage.getItem(getStorageKey(formId));
if (saved) {
clearFormData();
const data = JSON.parse(saved);
Object.entries(data).forEach(([key, value]) => {
setFieldValue(key, value);
});
currentFormId = formId;
updateFormSelector();
showToast('Formulier geladen: ' + formId, 'success');
}
} catch (e) {
console.warn('Could not load form:', e);
showToast('Kon formulier niet laden', 'error');
}
}
// Clear form data (without removing from storage)
function clearFormData() {
document.querySelectorAll('input[type="text"], input[type="number"], input[type="date"], select, textarea').forEach(field => {
field.value = '';
field.classList.remove('invalid');
});
document.querySelectorAll('input[type="checkbox"]').forEach(cb => {
cb.checked = false;
});
document.querySelectorAll('.photo-preview').forEach(img => {
img.src = '';
img.style.display = 'none';
img.parentElement.classList.remove('has-photo');
});
history.replaceState(null, '', window.location.pathname);
}
// Load saved data from localStorage or URL
// Load saved data from localStorage or URL
function loadSavedData() {
function loadSavedData() {
// Try URL hash first
// Try URL hash first
@ -717,24 +931,20 @@ input.invalid, select.invalid, textarea.invalid {
params.forEach((value, key) => {
params.forEach((value, key) => {
setFieldValue(key, value);
setFieldValue(key, value);
});
});
currentFormId = getFormUniqueId();
return;
return;
} catch (e) {
} catch (e) {
console.warn('Could not parse URL hash:', e);
console.warn('Could not parse URL hash:', e);
}
}
}
}
// Try localStorage
// Try to load most recent form from localStorage
if (CONFIG.autosave.use_localstorage) {
if (CONFIG.autosave.use_localstorage) {
try {
const forms = getSavedForms();
const saved = localStorage.getItem(STORAGE_KEY);
if (forms.length > 0) {
if (saved) {
// Load most recent
const data = JSON.parse(saved);
forms.sort((a, b) => new Date(b.lastModified) - new Date(a.lastModified));
Object.entries(data).forEach(([key, value]) => {
loadFormById(forms[0].id);
setFieldValue(key, value);
});
}
} catch (e) {
console.warn('Could not load from localStorage:', e);
}
}
}
}
}
}
@ -803,11 +1013,33 @@ input.invalid, select.invalid, textarea.invalid {
// Save data
// Save data
function saveData() {
function saveData() {
const data = getFormData();
const data = getFormData();
const formId = getFormUniqueId();
// Only save if we have a valid form ID
if (!formId) {
updateSaveIndicator('waiting');
return;
}
// Save to localStorage
// Save to localStorage with form-specific key
if (CONFIG.autosave.use_localstorage) {
if (CONFIG.autosave.use_localstorage) {
try {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
localStorage.setItem(getStorageKey(formId), JSON.stringify(data));
// Build label from unique ID fields
const labelParts = [];
if (CONFIG.unique_id_fields) {
CONFIG.unique_id_fields.forEach(fieldId => {
const field = document.getElementById(fieldId);
if (field & & field.value) {
labelParts.push(field.value);
}
});
}
const label = labelParts.join(' - ') || formId;
saveFormToIndex(formId, label);
currentFormId = formId;
} catch (e) {
} catch (e) {
console.warn('Could not save to localStorage:', e);
console.warn('Could not save to localStorage:', e);
}
}
@ -864,7 +1096,8 @@ input.invalid, select.invalid, textarea.invalid {
const statusText = {
const statusText = {
'loaded': 'Gegevens geladen',
'loaded': 'Gegevens geladen',
'saving': 'Opslaan...',
'saving': 'Opslaan...',
'saved': 'Opgeslagen ✓'
'saved': 'Opgeslagen ✓',
'waiting': 'Vul ID-velden in om op te slaan'
};
};
indicator.textContent = statusText[status] || status;
indicator.textContent = statusText[status] || status;
@ -1163,12 +1396,32 @@ input.invalid, select.invalid, textarea.invalid {
img.parentElement.classList.remove('has-photo');
img.parentElement.classList.remove('has-photo');
});
});
localStorage.removeItem(STORAGE_KEY);
history.replaceState(null, '', window.location.pathname);
history.replaceState(null, '', window.location.pathname);
currentFormId = null;
updateFormSelector();
showToast('Formulier gewist', 'success');
showToast('Formulier gewist', 'success');
};
};
// Delete saved form
window.deleteForm = function() {
if (!currentFormId) {
showToast('Geen formulier geselecteerd om te verwijderen', 'error');
return;
}
if (!confirm('Weet u zeker dat u dit opgeslagen formulier wilt verwijderen?\n\nFormulier: ' + currentFormId)) {
return;
}
removeFormFromIndex(currentFormId);
clearFormData();
currentFormId = null;
updateFormSelector();
showToast('Formulier verwijderd', 'success');
};
// Show toast notification
// Show toast notification
function showToast(message, type) {
function showToast(message, type) {
const toast = document.getElementById('toast');
const toast = document.getElementById('toast');