874 lines
31 KiB
JavaScript
874 lines
31 KiB
JavaScript
// QR Rapido Speed Generator
|
|
class QRRapidoGenerator {
|
|
constructor() {
|
|
this.startTime = 0;
|
|
this.currentQR = null;
|
|
this.timerInterval = null;
|
|
this.languageStrings = {
|
|
'pt-BR': {
|
|
tagline: 'Gere QR codes em segundos!',
|
|
generating: 'Gerando...',
|
|
generated: 'Gerado em',
|
|
seconds: 's',
|
|
ultraFast: 'Geração ultra rápida!',
|
|
fast: 'Geração rápida!',
|
|
normal: 'Geração normal',
|
|
error: 'Erro na geração. Tente novamente.',
|
|
success: 'QR Code salvo no histórico!'
|
|
},
|
|
'es': {
|
|
tagline: '¡Genera códigos QR en segundos!',
|
|
generating: 'Generando...',
|
|
generated: 'Generado en',
|
|
seconds: 's',
|
|
ultraFast: '¡Generación ultra rápida!',
|
|
fast: '¡Generación rápida!',
|
|
normal: 'Generación normal',
|
|
error: 'Error en la generación. Inténtalo de nuevo.',
|
|
success: '¡Código QR guardado en el historial!'
|
|
},
|
|
'en': {
|
|
tagline: 'Generate QR codes in seconds!',
|
|
generating: 'Generating...',
|
|
generated: 'Generated in',
|
|
seconds: 's',
|
|
ultraFast: 'Ultra fast generation!',
|
|
fast: 'Fast generation!',
|
|
normal: 'Normal generation',
|
|
error: 'Generation error. Please try again.',
|
|
success: 'QR Code saved to history!'
|
|
}
|
|
};
|
|
this.currentLang = localStorage.getItem('qrrapido-lang') || 'pt-BR';
|
|
|
|
this.initializeEvents();
|
|
this.checkAdFreeStatus();
|
|
this.updateLanguage();
|
|
this.updateStatsCounters();
|
|
}
|
|
|
|
initializeEvents() {
|
|
// Form submission with timer
|
|
const form = document.getElementById('qr-speed-form');
|
|
if (form) {
|
|
form.addEventListener('submit', this.generateQRWithTimer.bind(this));
|
|
}
|
|
|
|
// Quick style selection
|
|
document.querySelectorAll('input[name="quick-style"]').forEach(radio => {
|
|
radio.addEventListener('change', this.applyQuickStyle.bind(this));
|
|
});
|
|
|
|
// QR type change with hints
|
|
const qrType = document.getElementById('qr-type');
|
|
if (qrType) {
|
|
qrType.addEventListener('change', this.updateContentHints.bind(this));
|
|
}
|
|
|
|
// Language selector
|
|
document.querySelectorAll('[data-lang]').forEach(link => {
|
|
link.addEventListener('click', this.changeLanguage.bind(this));
|
|
});
|
|
|
|
// Real-time preview for premium users
|
|
if (this.isPremiumUser()) {
|
|
this.setupRealTimePreview();
|
|
}
|
|
|
|
// Download buttons
|
|
this.setupDownloadButtons();
|
|
|
|
// Share functionality
|
|
this.setupShareButtons();
|
|
|
|
// Save to history
|
|
const saveBtn = document.getElementById('save-to-history');
|
|
if (saveBtn) {
|
|
saveBtn.addEventListener('click', this.saveToHistory.bind(this));
|
|
}
|
|
}
|
|
|
|
setupDownloadButtons() {
|
|
const pngBtn = document.getElementById('download-png');
|
|
const svgBtn = document.getElementById('download-svg');
|
|
const pdfBtn = document.getElementById('download-pdf');
|
|
|
|
if (pngBtn) pngBtn.addEventListener('click', () => this.downloadQR('png'));
|
|
if (svgBtn) svgBtn.addEventListener('click', () => this.downloadQR('svg'));
|
|
if (pdfBtn) pdfBtn.addEventListener('click', () => this.downloadQR('pdf'));
|
|
}
|
|
|
|
setupShareButtons() {
|
|
// Check if Web Share API is supported and show/hide native share option
|
|
if (navigator.share && this.isMobileDevice()) {
|
|
const nativeShareOption = document.getElementById('native-share-option');
|
|
if (nativeShareOption) {
|
|
nativeShareOption.classList.remove('d-none');
|
|
}
|
|
}
|
|
|
|
// Show save to gallery option on mobile
|
|
if (this.isMobileDevice()) {
|
|
const saveGalleryOption = document.getElementById('save-gallery-option');
|
|
if (saveGalleryOption) {
|
|
saveGalleryOption.classList.remove('d-none');
|
|
}
|
|
}
|
|
|
|
// Add event listeners to share buttons
|
|
const shareButtons = {
|
|
'native-share': () => this.shareNative(),
|
|
'share-whatsapp': () => this.shareWhatsApp(),
|
|
'share-telegram': () => this.shareTelegram(),
|
|
'share-email': () => this.shareEmail(),
|
|
'copy-qr-link': () => this.copyToClipboard(),
|
|
'save-to-gallery': () => this.saveToGallery()
|
|
};
|
|
|
|
Object.entries(shareButtons).forEach(([id, handler]) => {
|
|
const button = document.getElementById(id);
|
|
if (button) {
|
|
button.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
handler();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
isMobileDevice() {
|
|
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||
|
|
(navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform));
|
|
}
|
|
|
|
async shareNative() {
|
|
if (!this.currentQR || !navigator.share) return;
|
|
|
|
try {
|
|
// Create a blob from the base64 image
|
|
const base64Response = await fetch(`data:image/png;base64,${this.currentQR.base64}`);
|
|
const blob = await base64Response.blob();
|
|
const file = new File([blob], 'qrcode.png', { type: 'image/png' });
|
|
|
|
const shareData = {
|
|
title: 'QR Code - QR Rapido',
|
|
text: 'QR Code gerado com QR Rapido - o gerador mais rápido do Brasil!',
|
|
url: window.location.origin,
|
|
files: [file]
|
|
};
|
|
|
|
// Check if files can be shared
|
|
if (navigator.canShare && navigator.canShare(shareData)) {
|
|
await navigator.share(shareData);
|
|
} else {
|
|
// Fallback without files
|
|
await navigator.share({
|
|
title: shareData.title,
|
|
text: shareData.text,
|
|
url: shareData.url
|
|
});
|
|
}
|
|
|
|
this.trackShareEvent('native');
|
|
} catch (error) {
|
|
console.error('Error sharing:', error);
|
|
if (error.name !== 'AbortError') {
|
|
this.showError('Erro ao compartilhar. Tente outro método.');
|
|
}
|
|
}
|
|
}
|
|
|
|
shareWhatsApp() {
|
|
if (!this.currentQR) return;
|
|
|
|
const text = encodeURIComponent('QR Code gerado com QR Rapido - o gerador mais rápido do Brasil! ' + window.location.origin);
|
|
const url = `https://wa.me/?text=${text}`;
|
|
|
|
if (this.isMobileDevice()) {
|
|
window.open(url, '_blank');
|
|
} else {
|
|
window.open(`https://web.whatsapp.com/send?text=${text}`, '_blank');
|
|
}
|
|
|
|
this.trackShareEvent('whatsapp');
|
|
}
|
|
|
|
shareTelegram() {
|
|
if (!this.currentQR) return;
|
|
|
|
const text = encodeURIComponent('QR Code gerado com QR Rapido - o gerador mais rápido do Brasil!');
|
|
const url = encodeURIComponent(window.location.origin);
|
|
const telegramUrl = `https://t.me/share/url?url=${url}&text=${text}`;
|
|
|
|
window.open(telegramUrl, '_blank');
|
|
this.trackShareEvent('telegram');
|
|
}
|
|
|
|
shareEmail() {
|
|
if (!this.currentQR) return;
|
|
|
|
const subject = encodeURIComponent('QR Code - QR Rapido');
|
|
const body = encodeURIComponent(`Olá!\n\nCompartilho com você este QR Code gerado no QR Rapido, o gerador mais rápido do Brasil!\n\nAcesse: ${window.location.origin}\n\nAbraços!`);
|
|
const mailtoUrl = `mailto:?subject=${subject}&body=${body}`;
|
|
|
|
window.location.href = mailtoUrl;
|
|
this.trackShareEvent('email');
|
|
}
|
|
|
|
async copyToClipboard() {
|
|
if (!this.currentQR) return;
|
|
|
|
try {
|
|
const shareText = `QR Code gerado com QR Rapido - ${window.location.origin}`;
|
|
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
await navigator.clipboard.writeText(shareText);
|
|
} else {
|
|
// Fallback for older browsers
|
|
const textArea = document.createElement('textarea');
|
|
textArea.value = shareText;
|
|
textArea.style.position = 'fixed';
|
|
textArea.style.left = '-999999px';
|
|
textArea.style.top = '-999999px';
|
|
document.body.appendChild(textArea);
|
|
textArea.focus();
|
|
textArea.select();
|
|
document.execCommand('copy');
|
|
textArea.remove();
|
|
}
|
|
|
|
this.showSuccess('Link copiado para a área de transferência!');
|
|
this.trackShareEvent('copy');
|
|
} catch (error) {
|
|
console.error('Error copying to clipboard:', error);
|
|
this.showError('Erro ao copiar link. Tente novamente.');
|
|
}
|
|
}
|
|
|
|
async saveToGallery() {
|
|
if (!this.currentQR) return;
|
|
|
|
try {
|
|
// Create a blob from the base64 image
|
|
const base64Response = await fetch(`data:image/png;base64,${this.currentQR.base64}`);
|
|
const blob = await base64Response.blob();
|
|
|
|
// Create download link
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `qrrapido-${new Date().toISOString().slice(0,10)}-${Date.now()}.png`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
this.showSuccess('QR Code baixado! Verifique sua galeria/downloads.');
|
|
this.trackShareEvent('gallery');
|
|
} catch (error) {
|
|
console.error('Error saving to gallery:', error);
|
|
this.showError('Erro ao salvar na galeria. Tente novamente.');
|
|
}
|
|
}
|
|
|
|
trackShareEvent(method) {
|
|
// Google Analytics
|
|
if (typeof gtag !== 'undefined') {
|
|
gtag('event', 'qr_shared', {
|
|
'share_method': method,
|
|
'user_type': this.isPremiumUser() ? 'premium' : 'free',
|
|
'language': this.currentLang
|
|
});
|
|
}
|
|
|
|
// Internal tracking
|
|
console.log(`QR Code shared via ${method}`);
|
|
}
|
|
|
|
async generateQRWithTimer(e) {
|
|
e.preventDefault();
|
|
|
|
// Validation
|
|
if (!this.validateForm()) return;
|
|
|
|
// Start timer
|
|
this.startTime = performance.now();
|
|
this.showGenerationStarted();
|
|
|
|
const formData = this.collectFormData();
|
|
|
|
try {
|
|
const response = await fetch('/api/QR/GenerateRapid', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(formData)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 429) {
|
|
this.showUpgradeModal('Limite de QR codes atingido! Upgrade para QR Rapido Premium e gere códigos ilimitados.');
|
|
return;
|
|
}
|
|
throw new Error('Erro na geração');
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || 'Erro desconhecido');
|
|
}
|
|
|
|
const generationTime = ((performance.now() - this.startTime) / 1000).toFixed(1);
|
|
|
|
this.displayQRResult(result, generationTime);
|
|
this.updateSpeedStats(generationTime);
|
|
this.trackGenerationEvent(formData.type, generationTime);
|
|
|
|
} catch (error) {
|
|
console.error('Erro ao gerar QR:', error);
|
|
this.showError(this.languageStrings[this.currentLang].error);
|
|
} finally {
|
|
this.hideGenerationLoading();
|
|
}
|
|
}
|
|
|
|
validateForm() {
|
|
const qrType = document.getElementById('qr-type').value;
|
|
const qrContent = document.getElementById('qr-content').value.trim();
|
|
|
|
if (!qrType) {
|
|
this.showError('Selecione o tipo de QR code');
|
|
return false;
|
|
}
|
|
|
|
if (!qrContent) {
|
|
this.showError('Digite o conteúdo do QR code');
|
|
return false;
|
|
}
|
|
|
|
if (qrContent.length > 4000) {
|
|
this.showError('Conteúdo muito longo. Máximo 4000 caracteres.');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
collectFormData() {
|
|
const quickStyle = document.querySelector('input[name="quick-style"]:checked')?.value || 'classic';
|
|
const styleSettings = this.getStyleSettings(quickStyle);
|
|
|
|
return {
|
|
type: document.getElementById('qr-type').value,
|
|
content: document.getElementById('qr-content').value,
|
|
quickStyle: quickStyle,
|
|
primaryColor: document.getElementById('primary-color').value,
|
|
backgroundColor: document.getElementById('bg-color').value,
|
|
size: parseInt(document.getElementById('qr-size').value),
|
|
margin: parseInt(document.getElementById('qr-margin').value),
|
|
cornerStyle: document.getElementById('corner-style')?.value || 'square',
|
|
optimizeForSpeed: true,
|
|
language: this.currentLang,
|
|
...styleSettings
|
|
};
|
|
}
|
|
|
|
getStyleSettings(style) {
|
|
const styles = {
|
|
classic: { primaryColor: '#000000', backgroundColor: '#FFFFFF' },
|
|
modern: { primaryColor: '#007BFF', backgroundColor: '#F8F9FA' },
|
|
colorful: { primaryColor: '#FF6B35', backgroundColor: '#FFF3E0' }
|
|
};
|
|
return styles[style] || styles.classic;
|
|
}
|
|
|
|
displayQRResult(result, generationTime) {
|
|
const previewDiv = document.getElementById('qr-preview');
|
|
if (!previewDiv) return;
|
|
|
|
previewDiv.innerHTML = `
|
|
<img src="data:image/png;base64,${result.qrCodeBase64}"
|
|
class="img-fluid border rounded shadow-sm"
|
|
alt="QR Code gerado em ${generationTime}s">
|
|
`;
|
|
|
|
// Show generation statistics
|
|
this.showGenerationStats(generationTime);
|
|
|
|
// Show download buttons
|
|
const downloadSection = document.getElementById('download-section');
|
|
if (downloadSection) {
|
|
downloadSection.style.display = 'block';
|
|
}
|
|
|
|
// Save current data
|
|
this.currentQR = {
|
|
base64: result.qrCodeBase64,
|
|
id: result.qrId,
|
|
generationTime: generationTime
|
|
};
|
|
|
|
// Update counter for free users
|
|
if (result.remainingQRs !== undefined) {
|
|
this.updateRemainingCounter(result.remainingQRs);
|
|
}
|
|
}
|
|
|
|
showGenerationStarted() {
|
|
const button = document.getElementById('generate-btn');
|
|
const spinner = button?.querySelector('.spinner-border');
|
|
const timer = document.querySelector('.generation-timer');
|
|
|
|
if (button) button.disabled = true;
|
|
if (spinner) spinner.classList.remove('d-none');
|
|
if (timer) timer.classList.remove('d-none');
|
|
|
|
// Update timer in real time
|
|
this.timerInterval = setInterval(() => {
|
|
const elapsed = ((performance.now() - this.startTime) / 1000).toFixed(1);
|
|
const timerSpan = timer?.querySelector('span');
|
|
if (timerSpan) {
|
|
timerSpan.textContent = `${elapsed}s`;
|
|
}
|
|
}, 100);
|
|
|
|
// Preview loading
|
|
const preview = document.getElementById('qr-preview');
|
|
if (preview) {
|
|
preview.innerHTML = `
|
|
<div class="text-center p-4">
|
|
<div class="spinner-border text-primary mb-3" role="status"></div>
|
|
<p class="text-muted">${this.languageStrings[this.currentLang].generating}</p>
|
|
<div class="progress">
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated"
|
|
style="width: 100%"></div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
showGenerationStats(generationTime) {
|
|
const statsDiv = document.querySelector('.generation-stats');
|
|
const speedBadge = document.querySelector('.speed-badge');
|
|
|
|
if (statsDiv) {
|
|
statsDiv.classList.remove('d-none');
|
|
const timeSpan = statsDiv.querySelector('.generation-time');
|
|
if (timeSpan) {
|
|
timeSpan.textContent = `${generationTime}s`;
|
|
}
|
|
}
|
|
|
|
// Show speed badge
|
|
if (speedBadge) {
|
|
const strings = this.languageStrings[this.currentLang];
|
|
let badgeText = strings.normal;
|
|
let badgeClass = 'bg-secondary';
|
|
|
|
if (generationTime < 1.0) {
|
|
badgeText = strings.ultraFast;
|
|
badgeClass = 'bg-success';
|
|
} else if (generationTime < 2.0) {
|
|
badgeText = strings.fast;
|
|
badgeClass = 'bg-primary';
|
|
}
|
|
|
|
speedBadge.innerHTML = `
|
|
<span class="badge ${badgeClass}">
|
|
<i class="fas fa-bolt"></i> ${badgeText}
|
|
</span>
|
|
`;
|
|
speedBadge.classList.remove('d-none');
|
|
}
|
|
}
|
|
|
|
hideGenerationLoading() {
|
|
const button = document.getElementById('generate-btn');
|
|
const spinner = button?.querySelector('.spinner-border');
|
|
|
|
if (button) button.disabled = false;
|
|
if (spinner) spinner.classList.add('d-none');
|
|
|
|
if (this.timerInterval) {
|
|
clearInterval(this.timerInterval);
|
|
this.timerInterval = null;
|
|
}
|
|
}
|
|
|
|
updateContentHints() {
|
|
const type = document.getElementById('qr-type')?.value;
|
|
const hintsElement = document.getElementById('content-hints');
|
|
if (!hintsElement || !type) return;
|
|
|
|
const hints = {
|
|
'pt-BR': {
|
|
'url': 'Ex: https://www.exemplo.com.br',
|
|
'text': 'Digite qualquer texto que desejar',
|
|
'wifi': 'Nome da rede;Senha;Tipo de segurança (WPA/WEP)',
|
|
'vcard': 'Nome;Telefone;Email;Empresa',
|
|
'sms': 'Número;Mensagem',
|
|
'email': 'email@exemplo.com;Assunto;Mensagem'
|
|
},
|
|
'es': {
|
|
'url': 'Ej: https://www.ejemplo.com',
|
|
'text': 'Escribe cualquier texto que desees',
|
|
'wifi': 'Nombre de red;Contraseña;Tipo de seguridad (WPA/WEP)',
|
|
'vcard': 'Nombre;Teléfono;Email;Empresa',
|
|
'sms': 'Número;Mensaje',
|
|
'email': 'email@ejemplo.com;Asunto;Mensaje'
|
|
}
|
|
};
|
|
|
|
const langHints = hints[this.currentLang] || hints['pt-BR'];
|
|
hintsElement.textContent = langHints[type] || 'Digite o conteúdo apropriado para o tipo selecionado';
|
|
}
|
|
|
|
changeLanguage(e) {
|
|
e.preventDefault();
|
|
this.currentLang = e.target.dataset.lang;
|
|
this.updateLanguage();
|
|
this.updateContentHints();
|
|
|
|
// Save preference
|
|
localStorage.setItem('qrrapido-lang', this.currentLang);
|
|
|
|
// Track language change
|
|
window.trackLanguageChange && window.trackLanguageChange('pt-BR', this.currentLang);
|
|
}
|
|
|
|
updateLanguage() {
|
|
const strings = this.languageStrings[this.currentLang];
|
|
|
|
// Update tagline
|
|
const tagline = document.getElementById('tagline');
|
|
if (tagline) {
|
|
tagline.textContent = strings.tagline;
|
|
}
|
|
|
|
// Update language selector
|
|
const langMap = { 'pt-BR': 'PT', 'es': 'ES', 'en': 'EN' };
|
|
const currentLang = document.getElementById('current-lang');
|
|
if (currentLang) {
|
|
currentLang.textContent = langMap[this.currentLang];
|
|
}
|
|
|
|
// Update hints if type already selected
|
|
const qrType = document.getElementById('qr-type');
|
|
if (qrType?.value) {
|
|
this.updateContentHints();
|
|
}
|
|
}
|
|
|
|
applyQuickStyle(e) {
|
|
const style = e.target.value;
|
|
const settings = this.getStyleSettings(style);
|
|
|
|
const primaryColor = document.getElementById('primary-color');
|
|
const bgColor = document.getElementById('bg-color');
|
|
|
|
if (primaryColor) primaryColor.value = settings.primaryColor;
|
|
if (bgColor) bgColor.value = settings.backgroundColor;
|
|
}
|
|
|
|
updateStatsCounters() {
|
|
// Simulate real-time counters
|
|
setInterval(() => {
|
|
const totalElement = document.getElementById('total-qrs');
|
|
if (totalElement) {
|
|
const current = parseFloat(totalElement.textContent.replace('K', '')) || 10.5;
|
|
const newValue = (current + Math.random() * 0.1).toFixed(1);
|
|
totalElement.textContent = `${newValue}K`;
|
|
}
|
|
|
|
// Update average time based on real performance
|
|
const avgElement = document.getElementById('avg-generation-time');
|
|
if (avgElement && window.qrRapidoStats) {
|
|
const avg = window.qrRapidoStats.getAverageTime();
|
|
avgElement.textContent = `${avg}s`;
|
|
}
|
|
}, 30000); // Update every 30 seconds
|
|
}
|
|
|
|
trackGenerationEvent(type, time) {
|
|
// Google Analytics
|
|
if (typeof gtag !== 'undefined') {
|
|
gtag('event', 'qr_generated', {
|
|
'qr_type': type,
|
|
'generation_time': parseFloat(time),
|
|
'user_type': this.isPremiumUser() ? 'premium' : 'free',
|
|
'language': this.currentLang
|
|
});
|
|
}
|
|
|
|
// Internal statistics
|
|
if (!window.qrRapidoStats) {
|
|
window.qrRapidoStats = {
|
|
times: [],
|
|
getAverageTime: function() {
|
|
if (this.times.length === 0) return '1.2';
|
|
const avg = this.times.reduce((a, b) => a + b) / this.times.length;
|
|
return avg.toFixed(1);
|
|
}
|
|
};
|
|
}
|
|
window.qrRapidoStats.times.push(parseFloat(time));
|
|
}
|
|
|
|
isPremiumUser() {
|
|
return document.querySelector('.text-success')?.textContent.includes('Premium Ativo') || false;
|
|
}
|
|
|
|
async downloadQR(format) {
|
|
if (!this.currentQR) return;
|
|
|
|
try {
|
|
const response = await fetch(`/api/QR/Download/${this.currentQR.id}?format=${format}`);
|
|
if (!response.ok) throw new Error('Download failed');
|
|
|
|
const blob = await response.blob();
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `qrrapido-${new Date().toISOString().slice(0,10)}.${format}`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
} catch (error) {
|
|
console.error('Download error:', error);
|
|
this.showError('Erro ao fazer download. Tente novamente.');
|
|
}
|
|
}
|
|
|
|
async saveToHistory() {
|
|
if (!this.currentQR) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/QR/SaveToHistory', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
qrId: this.currentQR.id
|
|
})
|
|
});
|
|
|
|
if (response.ok) {
|
|
this.showSuccess(this.languageStrings[this.currentLang].success);
|
|
} else {
|
|
throw new Error('Failed to save');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Save error:', error);
|
|
this.showError('Erro ao salvar no histórico.');
|
|
}
|
|
}
|
|
|
|
async checkAdFreeStatus() {
|
|
try {
|
|
const response = await fetch('/Account/AdFreeStatus');
|
|
const status = await response.json();
|
|
|
|
if (status.isAdFree) {
|
|
this.hideAllAds();
|
|
this.showAdFreeMessage(status.timeRemaining);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Error checking ad-free status:', error);
|
|
}
|
|
}
|
|
|
|
hideAllAds() {
|
|
document.querySelectorAll('.ad-container').forEach(ad => {
|
|
ad.style.display = 'none';
|
|
});
|
|
}
|
|
|
|
showAdFreeMessage(timeRemaining) {
|
|
if (timeRemaining <= 0) return;
|
|
|
|
const existing = document.querySelector('.ad-free-notice');
|
|
if (existing) return; // Already shown
|
|
|
|
const message = document.createElement('div');
|
|
message.className = 'alert alert-success text-center mb-3 ad-free-notice';
|
|
message.innerHTML = `
|
|
<i class="fas fa-crown text-warning"></i>
|
|
<strong>Sessão sem anúncios ativa!</strong>
|
|
Tempo restante: <span class="ad-free-countdown">${this.formatTime(timeRemaining)}</span>
|
|
<a href="/Premium/Upgrade" class="btn btn-sm btn-warning ms-2">Tornar Permanente</a>
|
|
`;
|
|
|
|
const container = document.querySelector('.container');
|
|
const row = container?.querySelector('.row');
|
|
if (container && row) {
|
|
container.insertBefore(message, row);
|
|
}
|
|
}
|
|
|
|
formatTime(minutes) {
|
|
if (minutes === 0) return '0m';
|
|
|
|
const days = Math.floor(minutes / 1440);
|
|
const hours = Math.floor((minutes % 1440) / 60);
|
|
const mins = minutes % 60;
|
|
|
|
if (days > 0) return `${days}d ${hours}h ${mins}m`;
|
|
if (hours > 0) return `${hours}h ${mins}m`;
|
|
return `${mins}m`;
|
|
}
|
|
|
|
showUpgradeModal(message) {
|
|
const modal = document.createElement('div');
|
|
modal.className = 'modal fade';
|
|
modal.innerHTML = `
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header bg-warning">
|
|
<h5 class="modal-title">
|
|
<i class="fas fa-crown"></i> Upgrade para Premium
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>${message}</p>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>Plano Atual (Free)</h6>
|
|
<ul class="list-unstyled">
|
|
<li>❌ Limite de 10 QR/dia</li>
|
|
<li>❌ Anúncios</li>
|
|
<li>✅ QR básicos</li>
|
|
</ul>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>Premium (R$ 19,90/mês)</h6>
|
|
<ul class="list-unstyled">
|
|
<li>✅ QR ilimitados</li>
|
|
<li>✅ Sem anúncios</li>
|
|
<li>✅ QR dinâmicos</li>
|
|
<li>✅ Analytics</li>
|
|
<li>✅ Suporte prioritário</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
|
<a href="/Premium/Upgrade" class="btn btn-warning">
|
|
<i class="fas fa-crown"></i> Fazer Upgrade
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(modal);
|
|
const bsModal = new bootstrap.Modal(modal);
|
|
bsModal.show();
|
|
|
|
// Remove modal when closed
|
|
modal.addEventListener('hidden.bs.modal', () => {
|
|
document.body.removeChild(modal);
|
|
});
|
|
}
|
|
|
|
updateRemainingCounter(remaining) {
|
|
const counterElement = document.querySelector('.qr-counter');
|
|
if (counterElement) {
|
|
counterElement.textContent = `${remaining} QR codes restantes hoje`;
|
|
|
|
if (remaining <= 3) {
|
|
counterElement.className = 'badge bg-warning qr-counter';
|
|
}
|
|
if (remaining === 0) {
|
|
counterElement.className = 'badge bg-danger qr-counter';
|
|
}
|
|
}
|
|
}
|
|
|
|
showError(message) {
|
|
this.showAlert(message, 'danger');
|
|
}
|
|
|
|
showSuccess(message) {
|
|
this.showAlert(message, 'success');
|
|
}
|
|
|
|
showAlert(message, type) {
|
|
const alert = document.createElement('div');
|
|
alert.className = `alert alert-${type} alert-dismissible fade show`;
|
|
alert.innerHTML = `
|
|
${message}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
`;
|
|
|
|
const container = document.querySelector('.container');
|
|
const row = container?.querySelector('.row');
|
|
if (container && row) {
|
|
container.insertBefore(alert, row);
|
|
}
|
|
|
|
// Auto-remove after delay
|
|
setTimeout(() => {
|
|
if (alert.parentNode) {
|
|
alert.parentNode.removeChild(alert);
|
|
}
|
|
}, type === 'success' ? 3000 : 5000);
|
|
}
|
|
|
|
setupRealTimePreview() {
|
|
const contentField = document.getElementById('qr-content');
|
|
const typeField = document.getElementById('qr-type');
|
|
|
|
if (contentField && typeField) {
|
|
let previewTimeout;
|
|
const updatePreview = () => {
|
|
clearTimeout(previewTimeout);
|
|
previewTimeout = setTimeout(() => {
|
|
if (contentField.value.trim() && typeField.value) {
|
|
// Could implement real-time preview for premium users
|
|
console.log('Real-time preview update');
|
|
}
|
|
}, 500);
|
|
};
|
|
|
|
contentField.addEventListener('input', updatePreview);
|
|
typeField.addEventListener('change', updatePreview);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize when DOM loads
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
window.qrGenerator = new QRRapidoGenerator();
|
|
|
|
// Initialize AdSense if necessary
|
|
if (window.adsbygoogle && document.querySelector('.adsbygoogle')) {
|
|
(adsbygoogle = window.adsbygoogle || []).push({});
|
|
}
|
|
});
|
|
|
|
// Global functions for ad control
|
|
window.QRApp = {
|
|
refreshAds: function() {
|
|
if (window.adsbygoogle) {
|
|
document.querySelectorAll('.adsbygoogle').forEach(ad => {
|
|
(adsbygoogle = window.adsbygoogle || []).push({});
|
|
});
|
|
}
|
|
},
|
|
|
|
hideAds: function() {
|
|
document.querySelectorAll('.ad-container').forEach(ad => {
|
|
ad.style.display = 'none';
|
|
});
|
|
}
|
|
}; |