PageSourceSearch

https://siemprecelta.com.ar/js/contact-modal.js

js siemprecelta.com.ar collected 2026-10-10 01:18:04 UTC 17,269 bytes, 579 lines download raw bytes

1/**
2 * SIEMPRE Contact Form Modal
3 * Handles modal display, form validation, and submission
4 */
5
6const ContactModal = (function () {
7    'use strict';
8
9    let modal = null;
10    let overlay = null;
11    let closeBtn = null;
12    let form = null;
13    let submitBtn = null;
14
15    /**
16     * Initialize the modal
17     */
18    function init() {
19        modal = document.getElementById('contact-modal');
20        if (!modal) {
21            console.error('Contact modal not found');
22            return;
23        }
24
25        overlay = modal.querySelector('.modal-overlay');
26        closeBtn = modal.querySelector('.modal-close');
27        form = modal.querySelector('#contact-form');
28        submitBtn = modal.querySelector('.submit-btn');
29
30        // Attach event listeners
31        attachEventListeners();
32    }
33
34    /**
35     * Attach all event listeners
36     */
37    function attachEventListeners() {
38        // Close button
39        if (closeBtn) {
40            closeBtn.addEventListener('click', close);
41        }
42
43        // Overlay click
44        if (overlay) {
45            overlay.addEventListener('click', close);
46        }
47
48        // ESC key
49        document.addEventListener('keydown', (e) => {
50            if (e.key === 'Escape' && !modal.classList.contains('hidden')) {
51                close();
52            }
53        });
54
55        // Form submission
56        if (form) {
57            form.addEventListener('submit', handleSubmit);
58        }
59
60        // Attach to all trigger buttons
61        attachTriggerButtons();
62    }
63
64    /**
65     * Attach click handlers to all contact trigger buttons
66     */
67    function attachTriggerButtons() {
68        const triggers = document.querySelectorAll('[data-contact-trigger]');
69        triggers.forEach(btn => {
70            btn.addEventListener('click', (e) => {
71                e.preventDefault();
72                const queryType = btn.getAttribute('data-query-type') || '';
73                open(queryType);
74            });
75        });
76    }
77
78    /**
79     * Open the modal with fade-in animation
80     */
81    function open(queryType = '') {
82        if (!modal) return;
83
84        // Set query type if provided
85        if (queryType) {
86            const select = form.querySelector('#query-type');
87            if (select) {
88                select.value = queryType;
89            }
90        }
91
92        // Show modal
93        modal.classList.remove('hidden');
94
95        // Trigger animation
96        setTimeout(() => {
97            modal.classList.add('modal-active');
98        }, 10);
99
100        // Prevent body scroll
101        document.body.style.overflow = 'hidden';
102    }
103
104    /**
105     * Close the modal with fade-out animation
106     */
107    function close() {
108        if (!modal) return;
109
110        // Trigger fade-out
111        modal.classList.remove('modal-active');
112
113        // Hide after animation
114        setTimeout(() => {
115            modal.classList.add('hidden');
116            document.body.style.overflow = '';
117            resetForm();
118        }, 300);
119    }
120
121    /**
122     * Reset the form
123     */
124    function resetForm() {
125        if (form) {
126            form.reset();
127            hideMessage();
128        }
129    }
130
131    /**
132     * Handle form submission
133     */
134    async function handleSubmit(e) {
135        e.preventDefault();
136
137        // Validate form
138        if (!validateForm()) {
139            return;
140        }
141
142        // Get form data
143        const formData = new FormData(form);
144
145        // Show loading state
146        setLoading(true);
147
148        try {
149            // Submit to backend
150            const response = await fetch('backend/contact-handler.php', {
151                method: 'POST',
152                body: formData
153            });
154
155            const result = await response.json();
156
157            if (result.success) {
158                showSuccess();
159            } else {
160                showError(result.message || 'Error al enviar el mensaje');
161            }
162        } catch (error) {
163            console.error('Error:', error);
164            showError('Error de conexión. Por favor, intentá nuevamente.');
165        } finally {
166            setLoading(false);
167        }
168    }
169
170    /**
171     * Validate form fields
172     */
173    function validateForm() {
174        const nombre = form.querySelector('#nombre').value.trim();
175        const telefono = form.querySelector('#telefono').value.trim();
176        const queryType = form.querySelector('#query-type').value;
177        const email = form.querySelector('#email').value.trim();
178        const comentarios = form.querySelector('#comentarios').value.trim();
179
180        if (!nombre) {
181            showError('Por favor, ingresá tu nombre');
182            return false;
183        }
184
185        if (!telefono) {
186            showError('Por favor, ingresá tu teléfono');
187            return false;
188        }
189        
190        if (!email) {
191            showError('Por favor, ingresá tu email');
192            return false;
193        }
194
195        if (!queryType) {
196            showError('Por favor, seleccioná el tipo de consulta');
197            return false;
198        }
199        
200        if (!comentarios) {
201            showError('Por favor, ingresá tus comentarios');
202            return false;
203        }
204
205        return true;
206    }
207
208    /**
209     * Set loading state
210     */
211    function setLoading(loading) {
212        if (!submitBtn) return;
213
214        if (loading) {
215            submitBtn.disabled = true;
216            submitBtn.innerHTML = `
217                <svg class="animate-spin w-5 h-5" fill="none" viewBox="0 0 24 24">
218                    <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
219                    <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
220                </svg>
221                <span>Enviando...</span>
222            `;
223        } else {
224            submitBtn.disabled = false;
225            submitBtn.innerHTML = `
226                <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
227                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path>
228                </svg>
229                <span>Enviar Consulta</span>
230            `;
231        }
232    }
233
234    /**
235     * Show error message
236     */
237    function showError(message) {
238        const msgEl = modal.querySelector('.form-message');
239        if (msgEl) {
240            msgEl.className = 'form-message error-message';
241            msgEl.textContent = message;
242            msgEl.classList.remove('hidden');
243        }
244    }
245
246    /**
247     * Show success message with animation
248     */
249    function showSuccess() {
250        const content = modal.querySelector('.modal-content');
251
252        // Fade out form
253        content.style.opacity = '0';
254        content.style.transform = 'scale(0.95)';
255
256        setTimeout(() => {
257            content.innerHTML = `
258                <div class="success-animation text-center py-12">
259                    <div class="success-icon mb-6">
260                        <svg class="w-20 h-20 mx-auto text-brand-cyan" fill="none" stroke="currentColor" viewBox="0 0 24 24">
261                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
262                        </svg>
263                    </div>
264                    <h3 class="text-2xl font-bold text-white mb-3">¡Consulta Enviada!</h3>
265                    <p class="text-brand-neutral">En breve nos estaremos comunicando con vos.</p>
266                </div>
267            `;
268
269            content.style.opacity = '1';
270            content.style.transform = 'scale(1)';
271
272            // Auto-close after 3 seconds
273            setTimeout(() => {
274                close();
275            }, 3000);
276        }, 300);
277    }
278
279    /**
280     * Hide message
281     */
282    function hideMessage() {
283        const msgEl = modal.querySelector('.form-message');
284        if (msgEl) {
285            msgEl.classList.add('hidden');
286        }
287    }
288
289    // Public API
290    return {
291        init,
292        open,
293        close
294    };
295})();
296
297// Initialize on DOM ready
298document.addEventListener('DOMContentLoaded', () => {
299    ContactModal.init();
300    const urlParams = new URLSearchParams(window.location.search);
301    if (urlParams.has('contact')) {
302        ContactModal.open(urlParams.get('contact'));
303    }
304});
305
306/**
307 * Terms & Conditions Modal
308 */
309const TermsModal = {
310    modal: null,
311    overlay: null,
312    closeBtn: null,
313    closeX: null,
314
315    init() {
316        this.modal = document.getElementById('terms-modal');
317        if (!this.modal) return;
318
319        this.overlay = this.modal.querySelector('.modal-overlay');
320        this.closeBtn = this.modal.querySelector('.close-terms-btn');
321        this.closeX = this.modal.querySelector('.modal-close-terms');
322
323        // Attach event listeners
324        if (this.closeBtn) {
325            this.closeBtn.addEventListener('click', () => this.close());
326        }
327        if (this.closeX) {
328            this.closeX.addEventListener('click', () => this.close());
329        }
330        if (this.overlay) {
331            this.overlay.addEventListener('click', () => this.close());
332        }
333
334        // ESC key
335        document.addEventListener('keydown', (e) => {
336            if (e.key === 'Escape' && !this.modal.classList.contains('hidden')) {
337                this.close();
338            }
339        });
340
341        // Attach to triggers
342        document.querySelectorAll('[data-terms-trigger]').forEach(trigger => {
343            trigger.addEventListener('click', (e) => {
344                e.preventDefault();
345                this.open();
346            });
347        });
348    },
349
350    open() {
351        if (!this.modal) return;
352        this.modal.classList.remove('hidden');
353        setTimeout(() => {
354            this.modal.classList.add('modal-active');
355        }, 10);
356        document.body.style.overflow = 'hidden';
357    },
358
359    close() {
360        if (!this.modal) return;
361        this.modal.classList.remove('modal-active');
362        setTimeout(() => {
363            this.modal.classList.add('hidden');
364            document.body.style.overflow = '';
365        }, 300);
366    }
367};
368
369/**
370 * Cancellation Modal
371 */
372const CancellationModal = {
373    modal: null,
374    overlay: null,
375    closeBtn: null,
376    form: null,
377    submitBtn: null,
378
379    init() {
380        this.modal = document.getElementById('cancellation-modal');
381        if (!this.modal) return;
382
383        this.overlay = this.modal.querySelector('.modal-overlay');
384        this.closeBtn = this.modal.querySelector('.modal-close-cancellation');
385        this.form = this.modal.querySelector('#cancellation-form');
386        this.submitBtn = this.modal.querySelector('.submit-cancellation-btn');
387
388        // Attach event listeners
389        if (this.closeBtn) {
390            this.closeBtn.addEventListener('click', () => this.close());
391        }
392        if (this.overlay) {
393            this.overlay.addEventListener('click', () => this.close());
394        }
395
396        // ESC key
397        document.addEventListener('keydown', (e) => {
398            if (e.key === 'Escape' && !this.modal.classList.contains('hidden')) {
399                this.close();
400            }
401        });
402
403        // Form submission
404        if (this.form) {
405            this.form.addEventListener('submit', (e) => this.handleSubmit(e));
406        }
407
408        // Attach to triggers
409        document.querySelectorAll('[data-cancellation-trigger]').forEach(trigger => {
410            trigger.addEventListener('click', (e) => {
411                e.preventDefault();
412                this.open();
413            });
414        });
415    },
416
417    open() {
418        if (!this.modal) return;
419        this.modal.classList.remove('hidden');
420        setTimeout(() => {
421            this.modal.classList.add('modal-active');
422        }, 10);
423        document.body.style.overflow = 'hidden';
424    },
425
426    close() {
427        if (!this.modal) return;
428        this.modal.classList.remove('modal-active');
429        setTimeout(() => {
430            this.modal.classList.add('hidden');
431            document.body.style.overflow = '';
432            this.resetForm();
433        }, 300);
434    },
435
436    resetForm() {
437        if (this.form) {
438            this.form.reset();
439            this.hideMessage();
440        }
441    },
442
443    async handleSubmit(e) {
444        e.preventDefault();
445
446        if (!this.validateForm()) {
447            return;
448        }
449
450        const formData = new FormData(this.form);
451        this.setLoading(true);
452
453        try {
454            const response = await fetch('backend/cancellation-handler.php', {
455                method: 'POST',
456                body: formData
457            });
458
459            const result = await response.json();
460
461            if (result.success) {
462                this.showSuccess();
463            } else {
464                this.showError(result.message || 'Error al enviar la solicitud');
465            }
466        } catch (error) {
467            console.error('Error:', error);
468            this.showError('Error de conexión. Por favor, intentá nuevamente.');
469        } finally {
470            this.setLoading(false);
471        }
472    },
473
474    validateForm() {
475        const nombre = this.form.querySelector('#cancel-nombre').value.trim();
476        const dni = this.form.querySelector('#cancel-dni').value.trim();
477        const domicilio = this.form.querySelector('#cancel-domicilio').value.trim();
478        const telefono = this.form.querySelector('#cancel-telefono').value.trim();
479        const motivo = this.form.querySelector('#cancel-motivo').value;
480
481        if (!nombre) {
482            this.showError('Por favor, ingresá tu nombre');
483            return false;
484        }
485        if (!dni) {
486            this.showError('Por favor, ingresá tu DNI o Número de Suministro');
487            return false;
488        }
489        if (!domicilio) {
490            this.showError('Por favor, ingresá el domicilio de instalación');
491            return false;
492        }
493        if (!telefono) {
494            this.showError('Por favor, ingresá tu teléfono');
495            return false;
496        }
497        if (!motivo) {
498            this.showError('Por favor, seleccioná el motivo de baja');
499            return false;
500        }
501
502        return true;
503    },
504
505    setLoading(loading) {
506        if (!this.submitBtn) return;
507
508        if (loading) {
509            this.submitBtn.disabled = true;
510            this.submitBtn.innerHTML = `
511                <svg class="animate-spin w-5 h-5" fill="none" viewBox="0 0 24 24">
512                    <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
513                    <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
514                </svg>
515                <span>Enviando...</span>
516            `;
517        } else {
518            this.submitBtn.disabled = false;
519            this.submitBtn.innerHTML = `
520                <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
521                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
522                </svg>
523                <span>Solicitar Baja del Servicio</span>
524            `;
525        }
526    },
527
528    showError(message) {
529        const msgEl = this.modal.querySelector('.cancellation-message');
530        if (msgEl) {
531            msgEl.className = 'cancellation-message p-3 rounded-lg text-sm';
532            msgEl.style.background = 'rgba(239, 68, 68, 0.1)';
533            msgEl.style.border = '1px solid rgba(239, 68, 68, 0.3)';
534            msgEl.style.color = '#ef4444';
535            msgEl.textContent = message;
536            msgEl.classList.remove('hidden');
537        }
538    },
539
540    showSuccess() {
541        const content = this.modal.querySelector('.modal-content');
542        content.style.opacity = '0';
543        content.style.transform = 'scale(0.95)';
544
545        setTimeout(() => {
546            content.innerHTML = `
547                <div class="success-animation text-center py-12">
548                    <div class="success-icon mb-6">
549                        <svg class="w-20 h-20 mx-auto text-brand-cyan" fill="none" stroke="currentColor" viewBox="0 0 24 24">
550                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
551                        </svg>
552                    </div>
553                    <h3 class="text-2xl font-bold text-white mb-3">Solicitud Recibida</h3>
554                    <p class="text-brand-neutral">Tu solicitud de baja ha sido enviada. Nos contactaremos a la brevedad.</p>
555                </div>
556            `;
557            
558            content.style.opacity = '1';
559            content.style.transform = 'scale(1)';
560
561            setTimeout(() => {
562                this.close();
563            }, 3000);
564        }, 300);
565    },
566
567    hideMessage() {
568        const msgEl = this.modal.querySelector('.cancellation-message');
569        if (msgEl) {
570            msgEl.classList.add('hidden');
571        }
572    }
573};
574
575// Initialize all modals on DOM ready
576document.addEventListener('DOMContentLoaded', () => {
577    TermsModal.init();
578    CancellationModal.init();
579});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.