fix: ajustes de toast e msg de erro
All checks were successful
Deploy QR Rapido / test (push) Successful in 33s
Deploy QR Rapido / build-and-push (push) Successful in 6m59s
Deploy QR Rapido / deploy-staging (push) Has been skipped
Deploy QR Rapido / deploy-production (push) Successful in 1m59s

This commit is contained in:
Ricardo Carneiro 2025-08-12 18:23:56 -03:00
parent 83e46a5782
commit 3e84e8d7a6
5 changed files with 230 additions and 23 deletions

View File

@ -172,7 +172,7 @@ function regenerateQR(qrId) {
}) })
.catch(error => { .catch(error => {
console.error('Error regenerating QR:', error); console.error('Error regenerating QR:', error);
alert('@Localizer["ErrorRegeneratingQR"]'); showToast('@Localizer["ErrorRegeneratingQR"]', 'danger');
}); });
} }
@ -236,23 +236,36 @@ function deleteQR(qrId) {
} }
function showToast(message, type = 'info') { function showToast(message, type = 'info') {
// Create toast container if doesn't exist
let toastContainer = document.getElementById('toast-container');
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.id = 'toast-container';
toastContainer.className = 'toast-container position-fixed top-0 start-0 p-3';
toastContainer.style.zIndex = '1060';
toastContainer.style.marginTop = '80px';
document.body.appendChild(toastContainer);
}
// Create toast element // Create toast element
const toast = document.createElement('div'); const toastElement = document.createElement('div');
toast.className = `alert alert-${type === 'success' ? 'success' : 'danger'} alert-dismissible fade show position-fixed`; toastElement.innerHTML = `
toast.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;'; <div class="toast align-items-center text-white bg-${type === 'success' ? 'success' : 'danger'} border-0" role="alert" aria-live="assertive" aria-atomic="true">
toast.innerHTML = ` <div class="d-flex">
${message} <div class="toast-body">${message}</div>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button> <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
</div>
`; `;
document.body.appendChild(toast); toastContainer.appendChild(toastElement);
const toast = new bootstrap.Toast(toastElement.querySelector('.toast'), { delay: 5000 });
toast.show();
// Auto remove after 5 seconds // Remove toast element after it's hidden
setTimeout(() => { toastElement.querySelector('.toast').addEventListener('hidden.bs.toast', function() {
if (toast.parentNode) { toastElement.remove();
toast.remove(); });
}
}, 5000);
} }
// Event listeners // Event listeners

View File

@ -267,6 +267,10 @@
</div> </div>
<div class="form-group" id="website-group" style="display: none;"> <div class="form-group" id="website-group" style="display: none;">
<input type="url" id="vcard-website" class="form-control" placeholder="https://seusite.com"> <input type="url" id="vcard-website" class="form-control" placeholder="https://seusite.com">
<div class="invalid-feedback" id="website-error"></div>
<small class="form-text text-muted">
<i class="fas fa-info-circle"></i> O protocolo https:// será adicionado automaticamente
</small>
</div> </div>
</div> </div>
@ -1337,5 +1341,124 @@ document.addEventListener('DOMContentLoaded', function() {
logoVisualPreview.style.display = 'none'; logoVisualPreview.style.display = 'none';
} }
}); });
// Website field auto-complete functionality
const websiteField = document.getElementById('vcard-website');
if (websiteField) {
// Auto-add https:// on focus if field is empty
websiteField.addEventListener('focus', function() {
if (this.value.trim() === '') {
this.value = 'https://';
// Position cursor after https://
setTimeout(() => {
this.setSelectionRange(8, 8);
}, 10);
}
});
// Validate and auto-correct on blur
websiteField.addEventListener('blur', function() {
const value = this.value.trim();
// If empty, clear the field
if (value === '' || value === 'https://') {
this.value = '';
this.classList.remove('is-invalid');
return;
}
// Check if URL has protocol
if (value && !value.match(/^https?:\/\//i)) {
// Auto-add https:// if missing
this.value = 'https://' + value;
}
// Validate URL format
try {
const url = new URL(this.value);
// Valid URL
this.classList.remove('is-invalid');
this.classList.add('is-valid');
} catch (e) {
// Invalid URL
this.classList.remove('is-valid');
this.classList.add('is-invalid');
document.getElementById('website-error').textContent = 'Por favor, insira uma URL válida (ex: https://seusite.com)';
}
});
// Real-time validation as user types
websiteField.addEventListener('input', function() {
const value = this.value.trim();
// Reset validation if empty
if (value === '' || value === 'https://') {
this.classList.remove('is-invalid', 'is-valid');
return;
}
// Check if it looks like a valid URL structure
if (value.match(/^https?:\/\/.+\..+/i)) {
try {
new URL(value);
this.classList.remove('is-invalid');
this.classList.add('is-valid');
} catch (e) {
this.classList.remove('is-valid');
this.classList.add('is-invalid');
}
}
});
}
}); });
</script> </script>
<style>
/* Toast positioning improvements */
#toast-container {
max-width: 350px;
}
/* Responsive toast positioning */
@@media (max-width: 768px) {
#toast-container {
left: 50% !important;
transform: translateX(-50%);
top: 70px !important;
margin-top: 0 !important;
padding: 0.5rem !important;
max-width: calc(100vw - 1rem);
}
}
@@media (min-width: 769px) {
#toast-container {
margin-top: 85px !important;
}
}
/* Toast style improvements */
.toast {
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
border-radius: 8px;
min-width: 300px;
}
/* Website field validation improvements */
#vcard-website.is-valid {
border-color: #28a745;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='m2.3 6.73.13-.07.44-.44a.34.34 0 0 1 .48 0l.44.44.13.07a.33.33 0 0 1 0 .59.33.33 0 0 1-.59 0l-.44-.44-.44.44a.33.33 0 0 1-.59 0 .33.33 0 0 1 0-.59z'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right calc(0.375em + 0.1875rem) center;
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
#vcard-website.is-invalid {
border-color: #dc3545;
}
/* Smooth transitions for validation states */
#vcard-website {
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
</style>

View File

@ -101,17 +101,52 @@
if (result.success) { if (result.success) {
window.location.href = result.url; window.location.href = result.url;
} else { } else {
alert('@Localizer["Error"] ' + result.error); showToast('@Localizer["Error"] ' + result.error, 'danger');
this.disabled = false; this.disabled = false;
this.innerHTML = '@Localizer["SubscribeNow"]'; // Reset button text this.innerHTML = '@Localizer["SubscribeNow"]'; // Reset button text
} }
} catch (error) { } catch (error) {
console.error('Checkout error:', error); console.error('Checkout error:', error);
alert('@Localizer["PaymentInitializationError"]'); showToast('@Localizer["PaymentInitializationError"]', 'danger');
this.disabled = false; this.disabled = false;
this.innerHTML = 'Assinar Agora'; // Reset button text this.innerHTML = 'Assinar Agora'; // Reset button text
} }
}); });
}); });
// Toast notification function
function showToast(message, type) {
// Create toast container if doesn't exist
let toastContainer = document.getElementById('toast-container');
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.id = 'toast-container';
toastContainer.className = 'toast-container position-fixed top-0 start-0 p-3';
toastContainer.style.zIndex = '1060';
toastContainer.style.marginTop = '80px';
document.body.appendChild(toastContainer);
}
// Create toast element
const toastId = 'toast-' + Date.now();
const toastElement = document.createElement('div');
toastElement.innerHTML = `
<div class="toast align-items-center text-white bg-${type} border-0" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">${message}</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
</div>
`;
toastContainer.appendChild(toastElement);
const toast = new bootstrap.Toast(toastElement.querySelector('.toast'), { delay: 5000 });
toast.show();
// Remove toast element after it's hidden
toastElement.querySelector('.toast').addEventListener('hidden.bs.toast', function() {
toastElement.remove();
});
}
</script> </script>
} }

View File

@ -286,11 +286,11 @@
window.location.href = result.url; window.location.href = result.url;
} else { } else {
alert('@Localizer["PaymentProcessingError"]' + result.error); showToast('@Localizer["PaymentProcessingError"]' + result.error, 'danger');
} }
} catch (error) { } catch (error) {
console.error('Erro:', error); console.error('Erro:', error);
alert('@Localizer["PaymentErrorTryAgain"]'); showToast('@Localizer["PaymentErrorTryAgain"]', 'danger');
} finally { } finally {
btn.disabled = false; btn.disabled = false;
spinner.classList.add('d-none'); spinner.classList.add('d-none');
@ -304,5 +304,40 @@
'page_location': window.location.href 'page_location': window.location.href
}); });
} }
// Toast notification function
function showToast(message, type) {
// Create toast container if doesn't exist
let toastContainer = document.getElementById('toast-container');
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.id = 'toast-container';
toastContainer.className = 'toast-container position-fixed top-0 start-0 p-3';
toastContainer.style.zIndex = '1060';
toastContainer.style.marginTop = '80px';
document.body.appendChild(toastContainer);
}
// Create toast element
const toastId = 'toast-' + Date.now();
const toastElement = document.createElement('div');
toastElement.innerHTML = `
<div class="toast align-items-center text-white bg-${type} border-0" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">${message}</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
</div>
`;
toastContainer.appendChild(toastElement);
const toast = new bootstrap.Toast(toastElement.querySelector('.toast'), { delay: 5000 });
toast.show();
// Remove toast element after it's hidden
toastElement.querySelector('.toast').addEventListener('hidden.bs.toast', function() {
toastElement.remove();
});
}
</script> </script>
} }

View File

@ -1448,8 +1448,9 @@ class QRRapidoGenerator {
if (!toastContainer) { if (!toastContainer) {
toastContainer = document.createElement('div'); toastContainer = document.createElement('div');
toastContainer.id = 'toast-container'; toastContainer.id = 'toast-container';
toastContainer.className = 'toast-container position-fixed top-0 end-0 p-3'; toastContainer.className = 'toast-container position-fixed top-0 start-0 p-3';
toastContainer.style.zIndex = '1060'; toastContainer.style.zIndex = '1060';
toastContainer.style.marginTop = '80px'; // Avoid covering logo/header
document.body.appendChild(toastContainer); document.body.appendChild(toastContainer);
} }
@ -2452,7 +2453,7 @@ class QRRapidoGenerator {
// Initialize when DOM loads // Initialize when DOM loads
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
window.qrGenerator = new QRRapidoGenerator(); window.qrGenerator = new QRRapidoGenerator();
window.vcardGenerator = new VCardGenerator(); window.vcardGenerator = new VCardGenerator();
window.wifiGenerator = new WiFiQRGenerator(); window.wifiGenerator = new WiFiQRGenerator();
window.smsGenerator = new SMSQRGenerator(); window.smsGenerator = new SMSQRGenerator();