/** * WebWizard Frontend Logic */ const App = { state: { userId: localStorage.getItem('ww_user_id'), currentStep: 1, totalSteps: 8, project: null, categories: [], selection: { category: null, subcategory: null, customDescription: '', style: null, palette: null, logo: null, gallery: [] } }, async init() { console.log('Initializing WebWizard...'); if (!this.state.userId) { await this.initSession(); } await this.loadCategories(); this.bindEvents(); // If we don't have a project, try to load existing or create one if (!this.state.project) { await this.loadLastProject(); } this.showStep(this.state.currentStep); }, async loadLastProject() { try { const response = await this.apiCall('listProjects'); if (response.success && response.data.length > 0) { // Load the most recent project const lastProject = response.data[0]; const statusResponse = await this.apiCall('getProjectStatus', {}, lastProject.project_id); if (statusResponse.success) { this.state.project = statusResponse.data; this.state.currentStep = this.state.project.current_step || 1; console.log('Loaded existing project:', this.state.project.project_id); this.syncSelectionWithProject(); } } else { await this.createProject(); } } catch (error) { console.error('Failed to load projects:', error); await this.createProject(); } }, syncSelectionWithProject() { if (this.state.project && this.state.project.wizard_data) { const wd = this.state.project.wizard_data; // Step 1 if (wd.business_category) { const bc = wd.business_category; this.state.selection.category = bc.group; this.state.selection.subcategory = bc.subcategory; this.state.selection.customDescription = bc.custom_description || ''; if (this.state.selection.category) { this.selectCategory(this.state.selection.category); this.state.selection.subcategory = bc.subcategory; this.renderSubcategories(this.state.selection.category); } } // Step 2 if (wd.identity) { document.getElementById('business-name').value = wd.identity.business_name || ''; document.getElementById('business-tagline').value = wd.identity.tagline || ''; document.getElementById('business-description').value = wd.identity.description || ''; } if (wd.contact) { document.getElementById('contact-email').value = wd.contact.email || ''; document.getElementById('contact-phone').value = wd.contact.phone || ''; document.getElementById('contact-address').value = wd.contact.address || ''; document.getElementById('contact-city').value = wd.contact.city || ''; document.getElementById('contact-facebook').value = wd.contact.socials?.facebook || ''; document.getElementById('contact-instagram').value = wd.contact.socials?.instagram || ''; } // Step 3 if (wd.services && wd.services.items) { const list = document.getElementById('services-list'); list.innerHTML = ''; wd.services.items.forEach(item => this.addServiceItem(item)); document.getElementById('pricing-note').value = wd.services.pricing_note || ''; } else { this.addServiceItem(); } // Step 4 if (wd.visuals) { this.state.selection.style = wd.visuals.style; this.state.selection.palette = wd.visuals.palette; this.updateStyleSelection(); this.updatePaletteSelection(); } if (wd.assets) { this.state.selection.logo = wd.assets.logo; this.state.selection.gallery = wd.assets.gallery || []; if (this.state.selection.logo) this.renderLogoPreview(); this.renderGalleryPreviews(); } // Step 5 if (wd.modules) { const m = wd.modules; if (m.sections) { m.sections.forEach(sec => { const el = document.querySelector(`input[data-module="${sec}"]`); if (el) el.checked = true; }); } if (m.contact_form) { document.getElementById('form-enabled').checked = m.contact_form.enabled; document.getElementById('form-config-container').classList.toggle('hidden', !m.contact_form.enabled); if (m.contact_form.mode) { document.querySelector(`input[name="form-mode"][value="${m.contact_form.mode}"]`).checked = true; document.getElementById('config-local').classList.toggle('hidden', m.contact_form.mode !== 'local'); document.getElementById('config-smtp').classList.toggle('hidden', m.contact_form.mode !== 'smtp'); } if (m.contact_form.smtp) { document.getElementById('smtp-host').value = m.contact_form.smtp.host || ''; document.getElementById('smtp-port').value = m.contact_form.smtp.port || ''; document.getElementById('smtp-user').value = m.contact_form.smtp.user || ''; document.getElementById('smtp-recipient').value = m.contact_form.smtp.recipient || ''; } } } } }, async initSession() { try { const response = await this.apiCall('initSession'); if (response.success) { this.state.userId = response.data.user_id; localStorage.setItem('ww_user_id', this.state.userId); console.log('Session initialized:', this.state.userId); } } catch (error) { console.error('Failed to initialize session:', error); alert('Chyba pri inicializácii session.'); } }, async createProject() { try { const response = await this.apiCall('createProject'); if (response.success) { this.state.project = response.data; console.log('Project created:', this.state.project.project_id); } } catch (error) { console.error('Failed to create project:', error); } }, async loadCategories() { try { const response = await this.apiCall('getCategories'); if (response.success) { this.state.categories = response.data.categories; this.renderCategories(); } } catch (error) { console.error('Failed to load categories:', error); } }, async apiCall(action, payload = {}, projectId = null) { const body = { action, project_id: projectId || (this.state.project ? this.state.project.project_id : null), payload }; const headers = { 'Content-Type': 'application/json' }; if (this.state.userId) { headers['X-User-ID'] = this.state.userId; } const response = await fetch('/ajax.php', { method: 'POST', headers, body: JSON.stringify(body) }); const result = await response.json(); if (!result.success) { throw new Error(result.error.message || 'API Error'); } return result; }, bindEvents() { document.getElementById('btn-next').addEventListener('click', () => this.nextStep()); document.getElementById('btn-prev').addEventListener('click', () => this.prevStep()); document.getElementById('custom-description').addEventListener('input', (e) => { this.state.selection.customDescription = e.target.value; }); // Step 2 validation listeners ['business-name', 'gdpr-consent', 'contact-email', 'contact-phone'].forEach(id => { const el = document.getElementById(id); if (el) { el.addEventListener('input', () => this.updateUI()); el.addEventListener('change', () => this.updateUI()); } }); // Step 3 events document.getElementById('btn-add-service').addEventListener('click', () => this.addServiceItem()); // Step 4 events document.querySelectorAll('#style-grid .category-card').forEach(card => { card.addEventListener('click', () => { this.state.selection.style = card.getAttribute('data-style'); this.updateStyleSelection(); }); }); document.querySelectorAll('#palette-list .palette-card').forEach(card => { card.addEventListener('click', () => { this.state.selection.palette = card.getAttribute('data-palette'); this.updatePaletteSelection(); }); }); document.getElementById('logo-upload-box').addEventListener('click', () => document.getElementById('logo-input').click()); document.getElementById('logo-input').addEventListener('change', (e) => this.handleFileUpload(e, 'logo')); document.getElementById('gallery-add-box').addEventListener('click', () => document.getElementById('gallery-input').click()); document.getElementById('gallery-input').addEventListener('change', (e) => this.handleFileUpload(e, 'gallery')); // Step 5 events document.getElementById('form-enabled').addEventListener('change', (e) => { document.getElementById('form-config-container').classList.toggle('hidden', !e.target.checked); }); document.querySelectorAll('input[name="form-mode"]').forEach(radio => { radio.addEventListener('change', (e) => { document.getElementById('config-local').classList.toggle('hidden', e.target.value !== 'local'); document.getElementById('config-smtp').classList.toggle('hidden', e.target.value !== 'smtp'); }); }); }, renderCategories() { const container = document.getElementById('category-list'); container.innerHTML = ''; const icons = { gastro: '🍕', beauty: '✨', crafts: '🛠️', professional: '💼', other: '❓' }; this.state.categories.forEach(cat => { const card = document.createElement('div'); card.className = 'category-card'; if (this.state.selection.category === cat.id) card.classList.add('selected'); card.innerHTML = `
${icons[cat.id] || '📁'}
${cat.name}
`; card.addEventListener('click', () => this.selectCategory(cat.id)); container.appendChild(card); }); }, selectCategory(categoryId) { this.state.selection.category = categoryId; this.state.selection.subcategory = null; this.renderCategories(); this.renderSubcategories(categoryId); this.renderSmartQuestions(categoryId); document.getElementById('subcategory-container').classList.remove('hidden'); if (categoryId === 'other') { document.getElementById('custom-category-group').classList.remove('hidden'); } else { document.getElementById('custom-category-group').classList.add('hidden'); } }, renderSubcategories(categoryId) { const container = document.getElementById('subcategory-list'); container.innerHTML = ''; const category = this.state.categories.find(c => c.id === categoryId); if (!category) return; category.subcategories.forEach(sub => { const chip = document.createElement('div'); chip.className = 'chip'; if (this.state.selection.subcategory === sub.id) chip.classList.add('selected'); chip.textContent = sub.name; chip.addEventListener('click', () => { this.state.selection.subcategory = sub.id; this.renderSubcategories(categoryId); }); container.appendChild(chip); }); }, addServiceItem(data = {}) { const container = document.getElementById('services-list'); const itemDiv = document.createElement('div'); itemDiv.className = 'service-item'; itemDiv.innerHTML = `
Služba
`; itemDiv.querySelector('.btn-remove-service').addEventListener('click', () => { if (container.querySelectorAll('.service-item').length > 1) { itemDiv.remove(); } else { alert('Zadajte aspoň jednu službu.'); } }); container.appendChild(itemDiv); }, renderSmartQuestions(categoryId) { const container = document.getElementById('smart-questions-list'); container.innerHTML = ''; const category = this.state.categories.find(c => c.id === categoryId); if (!category || !category.smart_questions) { document.getElementById('smart-questions-container').classList.add('hidden'); return; } document.getElementById('smart-questions-container').classList.remove('hidden'); category.smart_questions.forEach(q => { const qDiv = document.createElement('div'); qDiv.className = 'smart-question-item'; if (q.type === 'boolean') { qDiv.innerHTML = `
`; } else { qDiv.innerHTML = `
`; } container.appendChild(qDiv); // Apply existing answer if available if (this.state.project && this.state.project.wizard_data.smart_answers) { const answer = this.state.project.wizard_data.smart_answers[q.id]; if (answer !== undefined) { const input = qDiv.querySelector(`#sq-${q.id}`); if (q.type === 'boolean') { input.checked = !!answer; } else { input.value = answer; } } } }); }, updateStyleSelection() { document.querySelectorAll('#style-grid .category-card').forEach(card => { card.classList.toggle('selected', card.getAttribute('data-style') === this.state.selection.style); }); this.updateUI(); }, updatePaletteSelection() { document.querySelectorAll('#palette-list .palette-card').forEach(card => { card.classList.toggle('selected', card.getAttribute('data-palette') === this.state.selection.palette); }); this.updateUI(); }, async handleFileUpload(event, target) { const files = event.target.files; if (!files.length) return; for (const file of files) { const formData = new FormData(); formData.append('action', 'uploadAsset'); formData.append('project_id', this.state.project.project_id); formData.append('file', file); formData.append('X-User-ID', this.state.userId); try { const response = await fetch('/ajax.php', { method: 'POST', body: formData }); const result = await response.json(); if (result.success) { if (target === 'logo') { this.state.selection.logo = result.data.path; this.renderLogoPreview(); } else { if (this.state.selection.gallery.length < 5) { this.state.selection.gallery.push(result.data.path); this.renderGalleryPreviews(); } else { alert('Maximálne 5 fotografií.'); } } } else { alert('Upload zlyhal: ' + result.error.message); } } catch (error) { console.error('Upload error:', error); alert('Chyba pri nahrávaní.'); } } this.updateUI(); }, renderLogoPreview() { const preview = document.getElementById('logo-preview'); preview.innerHTML = `Logo `; preview.classList.remove('hidden'); document.querySelector('#logo-upload-box .upload-placeholder').classList.add('hidden'); document.getElementById('btn-remove-logo').addEventListener('click', (e) => { e.stopPropagation(); this.removeAsset('logo'); }); }, renderGalleryPreviews() { const container = document.getElementById('gallery-previews'); container.innerHTML = ''; this.state.selection.gallery.forEach((path, index) => { const item = document.createElement('div'); item.className = 'gallery-item'; item.innerHTML = `Gallery ${index+1} `; item.querySelector('.btn-remove-asset').addEventListener('click', (e) => { e.stopPropagation(); this.removeAsset('gallery', index); }); container.appendChild(item); }); }, removeAsset(target, index = null) { if (target === 'logo') { this.state.selection.logo = null; document.getElementById('logo-preview').classList.add('hidden'); document.querySelector('#logo-upload-box .upload-placeholder').classList.remove('hidden'); } else { this.state.selection.gallery.splice(index, 1); this.renderGalleryPreviews(); } this.updateUI(); }, showStep(n) { const steps = document.querySelectorAll('.step'); steps.forEach(step => step.classList.remove('active')); const activeStep = document.querySelector(`.step[data-step="${n}"]`); if (activeStep) { activeStep.classList.add('active'); } this.state.currentStep = n; this.updateUI(); }, async nextStep() { if (this.state.currentStep === 1) { if (!this.state.selection.category || !this.state.selection.subcategory) { alert('Prosím, vyberte kategóriu aj podkategóriu.'); return; } try { await this.apiCall('saveStep', { step: 1, data: { business_category: { group: this.state.selection.category, subcategory: this.state.selection.subcategory, custom_description: this.state.selection.category === 'other' ? this.state.selection.customDescription : null } } }); } catch (error) { console.error('Save step failed:', error); alert('Nepodarilo sa uložiť dáta.'); return; } } else if (this.state.currentStep === 2) { const businessName = document.getElementById('business-name').value; const tagline = document.getElementById('business-tagline').value; const description = document.getElementById('business-description').value; const email = document.getElementById('contact-email').value; const phone = document.getElementById('contact-phone').value; const address = document.getElementById('contact-address').value; const city = document.getElementById('contact-city').value; const gdpr = document.getElementById('gdpr-consent').checked; if (!businessName || !gdpr || (!email && !phone)) { alert('Prosím, vyplňte povinné údaje a zaškrtnite GDPR súhlas.'); return; } try { // 1. Save Consent await this.apiCall('saveConsent', { consent_text: document.querySelector('label[for="gdpr-consent"]').textContent }); // 2. Save Step 2 await this.apiCall('saveStep', { step: 2, data: { identity: { business_name: businessName, tagline: tagline, description: description }, contact: { email: email, phone: phone, address: address, city: city, socials: { facebook: document.getElementById('contact-facebook').value, instagram: document.getElementById('contact-instagram').value } } } }); } catch (error) { console.error('Save step 2 failed:', error); alert('Nepodarilo sa uložiť dáta: ' + error.message); return; } } else if (this.state.currentStep === 3) { const serviceItems = []; document.querySelectorAll('.service-item').forEach(item => { const name = item.querySelector('.service-name').value; if (name) { serviceItems.push({ name: name, price_from: item.querySelector('.service-price').value, description: item.querySelector('.service-desc').value }); } }); const smartAnswers = {}; document.querySelectorAll('.smart-question-item input').forEach(input => { const id = input.getAttribute('data-id'); if (input.type === 'checkbox') { smartAnswers[id] = input.checked; } else { smartAnswers[id] = input.value; } }); try { await this.apiCall('saveStep', { step: 3, data: { services: { items: serviceItems, pricing_note: document.getElementById('pricing-note').value }, smart_answers: smartAnswers } }); } catch (error) { console.error('Save step 3 failed:', error); alert('Nepodarilo sa uložiť dáta: ' + error.message); return; } } else if (this.state.currentStep === 4) { if (!this.state.selection.style || !this.state.selection.palette) { alert('Prosím, vyberte vizuálny štýl a farebnú paletu.'); return; } try { await this.apiCall('saveStep', { step: 4, data: { visuals: { style: this.state.selection.style, palette: this.state.selection.palette }, assets: { logo: this.state.selection.logo, gallery: this.state.selection.gallery } } }); } catch (error) { console.error('Save step 4 failed:', error); alert('Nepodarilo sa uložiť dáta: ' + error.message); return; } } else if (this.state.currentStep === 5) { const sections = []; document.querySelectorAll('input[data-module]:checked').forEach(el => { sections.push(el.getAttribute('data-module')); }); const formEnabled = document.getElementById('form-enabled').checked; const formMode = document.querySelector('input[name="form-mode"]:checked').value; const modulesData = { pages: ['home'], sections: sections, contact_form: { enabled: formEnabled, mode: formMode, smtp: formMode === 'smtp' ? { host: document.getElementById('smtp-host').value, port: document.getElementById('smtp-port').value, user: document.getElementById('smtp-user').value, pass: document.getElementById('smtp-pass').value, recipient: document.getElementById('smtp-recipient').value } : null, local_viewer: formMode === 'local' ? { password: document.getElementById('local-password').value } : null } }; try { await this.apiCall('saveStep', { step: 5, data: { modules: modulesData } }); } catch (error) { console.error('Save step 5 failed:', error); alert('Nepodarilo sa uložiť dáta: ' + error.message); return; } } if (this.state.currentStep < this.state.totalSteps) { this.showStep(this.state.currentStep + 1); } }, prevStep() { if (this.state.currentStep > 1) { this.showStep(this.state.currentStep - 1); } }, updateUI() { const btnPrev = document.getElementById('btn-prev'); const btnNext = document.getElementById('btn-next'); btnPrev.disabled = this.state.currentStep === 1; // Validation for Next button let nextDisabled = false; if (this.state.currentStep === 1) { nextDisabled = !this.state.selection.category || !this.state.selection.subcategory; } else if (this.state.currentStep === 2) { const name = document.getElementById('business-name').value; const email = document.getElementById('contact-email').value; const phone = document.getElementById('contact-phone').value; const gdpr = document.getElementById('gdpr-consent').checked; nextDisabled = !name || !gdpr || (!email && !phone); } else if (this.state.currentStep === 4) { nextDisabled = !this.state.selection.style || !this.state.selection.palette; } else if (this.state.currentStep === 5) { const formEnabled = document.getElementById('form-enabled').checked; if (formEnabled) { const mode = document.querySelector('input[name="form-mode"]:checked').value; if (mode === 'local') { nextDisabled = !document.getElementById('local-password').value; } else { nextDisabled = !document.getElementById('smtp-host').value || !document.getElementById('smtp-recipient').value; } } } btnNext.disabled = nextDisabled; if (this.state.currentStep === this.state.totalSteps) { btnNext.textContent = 'Dokončiť'; } else { btnNext.textContent = 'Pokračovať'; } const progressFill = document.querySelector('.progress-fill'); const percent = ((this.state.currentStep - 1) / (this.state.totalSteps - 1)) * 100; progressFill.style.width = `${percent}%`; } }; document.addEventListener('DOMContentLoaded', () => App.init());