Improve email button UX with loading indicator

- Show ' Email voorbereiden...' immediately when clicked
- Disable button during processing
- Use setTimeout to allow UI update before heavy processing
- Restore button state after mailto opens
main
killercow 2 weeks ago
parent 16f4cc7bc8
commit 46fca0ccdf

@ -612,7 +612,7 @@ input.invalid, select.invalid, textarea.invalid {
<div class="actions">
<button type="button" class="btn btn-primary" onclick="exportCSV()">📥 Exporteer CSV</button>
<button type="button" class="btn btn-success" onclick="sendEmail()">📧 Verstuur per Email</button>
<button type="button" class="btn btn-success" onclick="sendEmail(this)">📧 Verstuur per Email</button>
<button type="button" class="btn btn-danger" onclick="clearForm()">🗑️ Formulier Wissen</button>
</div>
@ -980,50 +980,67 @@ input.invalid, select.invalid, textarea.invalid {
};
// Send email
window.sendEmail = function() {
window.sendEmail = function(btn) {
if (!validateAll()) {
showToast('Vul eerst alle verplichte velden in', 'error');
return;
}
const data = getFormData();
// Build subject
let subject = CONFIG.export.mailto.subject_prefix;
if (CONFIG.export.mailto.include_timestamp) {
subject += ' - ' + formatDateTime(new Date());
// Show loading state immediately
const button = btn || document.querySelector('[onclick*="sendEmail"]');
const originalText = button ? button.innerHTML : '';
if (button) {
button.innerHTML = '⏳ Email voorbereiden...';
button.disabled = true;
}
CONFIG.export.mailto.subject_fields.forEach(fieldId => {
if (data[fieldId]) {
subject += ' - ' + data[fieldId];
}
});
// Build body
let body = 'INVENTARISATIE GEGEVENS\n';
body += '========================\n\n';
body += 'Formulier: ' + CONFIG.name + '\n';
body += 'Versie: ' + CONFIG.version + '\n';
body += 'Datum: ' + formatDateTime(new Date()) + '\n\n';
Object.entries(data).forEach(([key, value]) => {
if (value && value !== '' && !key.includes('foto') && !key.includes('photo')) {
const label = document.querySelector(`label[for="${key}"]`);
const labelText = label ? label.textContent.replace('*', '').trim() : key;
const displayValue = Array.isArray(value) ? value.join(', ') : value;
body += labelText + ': ' + displayValue + '\n';
// Use setTimeout to allow UI to update before heavy processing
setTimeout(function() {
const data = getFormData();
// Build subject
let subject = CONFIG.export.mailto.subject_prefix;
if (CONFIG.export.mailto.include_timestamp) {
subject += ' - ' + formatDateTime(new Date());
}
});
CONFIG.export.mailto.subject_fields.forEach(fieldId => {
if (data[fieldId]) {
subject += ' - ' + data[fieldId];
}
});
body += '\n========================\n';
body += 'Let op: Foto\'s kunnen niet via mailto worden verzonden.\n';
body += 'Gebruik CSV export voor complete data inclusief foto\'s.';
// Build body
let body = 'INVENTARISATIE GEGEVENS\n';
body += '========================\n\n';
body += 'Formulier: ' + CONFIG.name + '\n';
body += 'Versie: ' + CONFIG.version + '\n';
body += 'Datum: ' + formatDateTime(new Date()) + '\n\n';
const mailto = 'mailto:' + encodeURIComponent(CONFIG.export.mailto.to) +
'?subject=' + encodeURIComponent(subject) +
'&body=' + encodeURIComponent(body);
Object.entries(data).forEach(([key, value]) => {
if (value && value !== '' && !key.includes('foto') && !key.includes('photo')) {
const label = document.querySelector(`label[for="${key}"]`);
const labelText = label ? label.textContent.replace('*', '').trim() : key;
const displayValue = Array.isArray(value) ? value.join(', ') : value;
body += labelText + ': ' + displayValue + '\n';
}
});
body += '\n========================\n';
body += 'Let op: Foto\'s kunnen niet via mailto worden verzonden.\n';
body += 'Gebruik CSV export voor complete data inclusief foto\'s.';
const mailto = 'mailto:' + encodeURIComponent(CONFIG.export.mailto.to) +
'?subject=' + encodeURIComponent(subject) +
'&body=' + encodeURIComponent(body);
// Restore button state
if (button) {
button.innerHTML = originalText;
button.disabled = false;
}
window.location.href = mailto;
window.location.href = mailto;
}, 50);
};
// Clear form

@ -643,7 +643,7 @@ select {
<div class="actions">
<button type="button" class="btn btn-primary" onclick="exportCSV()">📥 Exporteer CSV</button>
<button type="button" class="btn btn-success" onclick="sendEmail()">📧 Verstuur per Email</button>
<button type="button" class="btn btn-success" onclick="sendEmail(this)">📧 Verstuur per Email</button>
<button type="button" class="btn btn-danger" onclick="clearForm()">🗑️ Formulier Wissen</button>
</div>
@ -1011,50 +1011,67 @@ select {
};
// Send email
window.sendEmail = function() {
window.sendEmail = function(btn) {
if (!validateAll()) {
showToast('Vul eerst alle verplichte velden in', 'error');
return;
}
const data = getFormData();
// Build subject
let subject = CONFIG.export.mailto.subject_prefix;
if (CONFIG.export.mailto.include_timestamp) {
subject += ' - ' + formatDateTime(new Date());
// Show loading state immediately
const button = btn || document.querySelector('[onclick*="sendEmail"]');
const originalText = button ? button.innerHTML : '';
if (button) {
button.innerHTML = '⏳ Email voorbereiden...';
button.disabled = true;
}
CONFIG.export.mailto.subject_fields.forEach(fieldId => {
if (data[fieldId]) {
subject += ' - ' + data[fieldId];
}
});
// Build body
let body = 'INVENTARISATIE GEGEVENS\n';
body += '========================\n\n';
body += 'Formulier: ' + CONFIG.name + '\n';
body += 'Versie: ' + CONFIG.version + '\n';
body += 'Datum: ' + formatDateTime(new Date()) + '\n\n';
Object.entries(data).forEach(([key, value]) => {
if (value && value !== '' && !key.includes('foto') && !key.includes('photo')) {
const label = document.querySelector(`label[for="${key}"]`);
const labelText = label ? label.textContent.replace('*', '').trim() : key;
const displayValue = Array.isArray(value) ? value.join(', ') : value;
body += labelText + ': ' + displayValue + '\n';
// Use setTimeout to allow UI to update before heavy processing
setTimeout(function() {
const data = getFormData();
// Build subject
let subject = CONFIG.export.mailto.subject_prefix;
if (CONFIG.export.mailto.include_timestamp) {
subject += ' - ' + formatDateTime(new Date());
}
});
CONFIG.export.mailto.subject_fields.forEach(fieldId => {
if (data[fieldId]) {
subject += ' - ' + data[fieldId];
}
});
body += '\n========================\n';
body += 'Let op: Foto\'s kunnen niet via mailto worden verzonden.\n';
body += 'Gebruik CSV export voor complete data inclusief foto\'s.';
// Build body
let body = 'INVENTARISATIE GEGEVENS\n';
body += '========================\n\n';
body += 'Formulier: ' + CONFIG.name + '\n';
body += 'Versie: ' + CONFIG.version + '\n';
body += 'Datum: ' + formatDateTime(new Date()) + '\n\n';
const mailto = 'mailto:' + encodeURIComponent(CONFIG.export.mailto.to) +
'?subject=' + encodeURIComponent(subject) +
'&body=' + encodeURIComponent(body);
Object.entries(data).forEach(([key, value]) => {
if (value && value !== '' && !key.includes('foto') && !key.includes('photo')) {
const label = document.querySelector(`label[for="${key}"]`);
const labelText = label ? label.textContent.replace('*', '').trim() : key;
const displayValue = Array.isArray(value) ? value.join(', ') : value;
body += labelText + ': ' + displayValue + '\n';
}
});
body += '\n========================\n';
body += 'Let op: Foto\'s kunnen niet via mailto worden verzonden.\n';
body += 'Gebruik CSV export voor complete data inclusief foto\'s.';
const mailto = 'mailto:' + encodeURIComponent(CONFIG.export.mailto.to) +
'?subject=' + encodeURIComponent(subject) +
'&body=' + encodeURIComponent(body);
// Restore button state
if (button) {
button.innerHTML = originalText;
button.disabled = false;
}
window.location.href = mailto;
window.location.href = mailto;
}, 50);
};
// Clear form

@ -674,7 +674,7 @@ input.invalid, select.invalid, textarea.invalid {
<div class="actions">
<button type="button" class="btn btn-primary" onclick="exportCSV()">📥 Exporteer CSV</button>
<button type="button" class="btn btn-success" onclick="sendEmail()">📧 Verstuur per Email</button>
<button type="button" class="btn btn-success" onclick="sendEmail(this)">📧 Verstuur per Email</button>
<button type="button" class="btn btn-danger" onclick="clearForm()">🗑️ Formulier Wissen</button>
</div>
@ -1042,50 +1042,67 @@ input.invalid, select.invalid, textarea.invalid {
};
// Send email
window.sendEmail = function() {
window.sendEmail = function(btn) {
if (!validateAll()) {
showToast('Vul eerst alle verplichte velden in', 'error');
return;
}
const data = getFormData();
// Build subject
let subject = CONFIG.export.mailto.subject_prefix;
if (CONFIG.export.mailto.include_timestamp) {
subject += ' - ' + formatDateTime(new Date());
// Show loading state immediately
const button = btn || document.querySelector('[onclick*="sendEmail"]');
const originalText = button ? button.innerHTML : '';
if (button) {
button.innerHTML = '⏳ Email voorbereiden...';
button.disabled = true;
}
CONFIG.export.mailto.subject_fields.forEach(fieldId => {
if (data[fieldId]) {
subject += ' - ' + data[fieldId];
}
});
// Build body
let body = 'INVENTARISATIE GEGEVENS\n';
body += '========================\n\n';
body += 'Formulier: ' + CONFIG.name + '\n';
body += 'Versie: ' + CONFIG.version + '\n';
body += 'Datum: ' + formatDateTime(new Date()) + '\n\n';
Object.entries(data).forEach(([key, value]) => {
if (value && value !== '' && !key.includes('foto') && !key.includes('photo')) {
const label = document.querySelector(`label[for="${key}"]`);
const labelText = label ? label.textContent.replace('*', '').trim() : key;
const displayValue = Array.isArray(value) ? value.join(', ') : value;
body += labelText + ': ' + displayValue + '\n';
// Use setTimeout to allow UI to update before heavy processing
setTimeout(function() {
const data = getFormData();
// Build subject
let subject = CONFIG.export.mailto.subject_prefix;
if (CONFIG.export.mailto.include_timestamp) {
subject += ' - ' + formatDateTime(new Date());
}
});
CONFIG.export.mailto.subject_fields.forEach(fieldId => {
if (data[fieldId]) {
subject += ' - ' + data[fieldId];
}
});
body += '\n========================\n';
body += 'Let op: Foto\'s kunnen niet via mailto worden verzonden.\n';
body += 'Gebruik CSV export voor complete data inclusief foto\'s.';
// Build body
let body = 'INVENTARISATIE GEGEVENS\n';
body += '========================\n\n';
body += 'Formulier: ' + CONFIG.name + '\n';
body += 'Versie: ' + CONFIG.version + '\n';
body += 'Datum: ' + formatDateTime(new Date()) + '\n\n';
const mailto = 'mailto:' + encodeURIComponent(CONFIG.export.mailto.to) +
'?subject=' + encodeURIComponent(subject) +
'&body=' + encodeURIComponent(body);
Object.entries(data).forEach(([key, value]) => {
if (value && value !== '' && !key.includes('foto') && !key.includes('photo')) {
const label = document.querySelector(`label[for="${key}"]`);
const labelText = label ? label.textContent.replace('*', '').trim() : key;
const displayValue = Array.isArray(value) ? value.join(', ') : value;
body += labelText + ': ' + displayValue + '\n';
}
});
body += '\n========================\n';
body += 'Let op: Foto\'s kunnen niet via mailto worden verzonden.\n';
body += 'Gebruik CSV export voor complete data inclusief foto\'s.';
const mailto = 'mailto:' + encodeURIComponent(CONFIG.export.mailto.to) +
'?subject=' + encodeURIComponent(subject) +
'&body=' + encodeURIComponent(body);
// Restore button state
if (button) {
button.innerHTML = originalText;
button.disabled = false;
}
window.location.href = mailto;
window.location.href = mailto;
}, 50);
};
// Clear form

@ -153,7 +153,7 @@ def generate_html(config: InventoryConfig) -> str:
if config.export.csv.enabled:
actions_html += ' <button type="button" class="btn btn-primary" onclick="exportCSV()">📥 Exporteer CSV</button>\n'
if config.export.mailto.enabled:
actions_html += ' <button type="button" class="btn btn-success" onclick="sendEmail()">📧 Verstuur per Email</button>\n'
actions_html += ' <button type="button" class="btn btn-success" onclick="sendEmail(this)">📧 Verstuur per Email</button>\n'
actions_html += ' <button type="button" class="btn btn-danger" onclick="clearForm()">🗑️ Formulier Wissen</button>\n'
actions_html += '</div>\n'

@ -1291,50 +1291,67 @@ JAVASCRIPT = """
};
// Send email
window.sendEmail = function() {
window.sendEmail = function(btn) {
if (!validateAll()) {
showToast('Vul eerst alle verplichte velden in', 'error');
return;
}
const data = getFormData();
// Build subject
let subject = CONFIG.export.mailto.subject_prefix;
if (CONFIG.export.mailto.include_timestamp) {
subject += ' - ' + formatDateTime(new Date());
// Show loading state immediately
const button = btn || document.querySelector('[onclick*="sendEmail"]');
const originalText = button ? button.innerHTML : '';
if (button) {
button.innerHTML = '⏳ Email voorbereiden...';
button.disabled = true;
}
CONFIG.export.mailto.subject_fields.forEach(fieldId => {
if (data[fieldId]) {
subject += ' - ' + data[fieldId];
}
});
// Build body
let body = 'INVENTARISATIE GEGEVENS\\n';
body += '========================\\n\\n';
body += 'Formulier: ' + CONFIG.name + '\\n';
body += 'Versie: ' + CONFIG.version + '\\n';
body += 'Datum: ' + formatDateTime(new Date()) + '\\n\\n';
Object.entries(data).forEach(([key, value]) => {
if (value && value !== '' && !key.includes('foto') && !key.includes('photo')) {
const label = document.querySelector(`label[for="${key}"]`);
const labelText = label ? label.textContent.replace('*', '').trim() : key;
const displayValue = Array.isArray(value) ? value.join(', ') : value;
body += labelText + ': ' + displayValue + '\\n';
// Use setTimeout to allow UI to update before heavy processing
setTimeout(function() {
const data = getFormData();
// Build subject
let subject = CONFIG.export.mailto.subject_prefix;
if (CONFIG.export.mailto.include_timestamp) {
subject += ' - ' + formatDateTime(new Date());
}
});
CONFIG.export.mailto.subject_fields.forEach(fieldId => {
if (data[fieldId]) {
subject += ' - ' + data[fieldId];
}
});
body += '\\n========================\\n';
body += 'Let op: Foto\\'s kunnen niet via mailto worden verzonden.\\n';
body += 'Gebruik CSV export voor complete data inclusief foto\\'s.';
// Build body
let body = 'INVENTARISATIE GEGEVENS\\n';
body += '========================\\n\\n';
body += 'Formulier: ' + CONFIG.name + '\\n';
body += 'Versie: ' + CONFIG.version + '\\n';
body += 'Datum: ' + formatDateTime(new Date()) + '\\n\\n';
const mailto = 'mailto:' + encodeURIComponent(CONFIG.export.mailto.to) +
'?subject=' + encodeURIComponent(subject) +
'&body=' + encodeURIComponent(body);
Object.entries(data).forEach(([key, value]) => {
if (value && value !== '' && !key.includes('foto') && !key.includes('photo')) {
const label = document.querySelector(`label[for="${key}"]`);
const labelText = label ? label.textContent.replace('*', '').trim() : key;
const displayValue = Array.isArray(value) ? value.join(', ') : value;
body += labelText + ': ' + displayValue + '\\n';
}
});
body += '\\n========================\\n';
body += 'Let op: Foto\\'s kunnen niet via mailto worden verzonden.\\n';
body += 'Gebruik CSV export voor complete data inclusief foto\\'s.';
const mailto = 'mailto:' + encodeURIComponent(CONFIG.export.mailto.to) +
'?subject=' + encodeURIComponent(subject) +
'&body=' + encodeURIComponent(body);
// Restore button state
if (button) {
button.innerHTML = originalText;
button.disabled = false;
}
window.location.href = mailto;
window.location.href = mailto;
}, 50);
};
// Clear form

Loading…
Cancel
Save

Powered by TurnKey Linux.