assets_js_controllers_my-rsvps-controller.js

import MobileControllerBase from "./mobile-controller-base";
import rsvpCacheService from "../services/rsvp-cache-service.js";

/**
 * My RSVPs Controller
 * 
 * Handles RSVP editing using the attendance modal on the My RSVPs page.
 */
class MyRsvpsController extends MobileControllerBase {
    static targets = ["modal", "modalBody", "actionButtons", "upcomingList", "pastList", "pastEmptyState", "upcomingCount", "pastCount"];

    onConnect() {
        this.modal = null;
        this.currentGatheringId = null;
        
        // Initialize RSVP cache service
        rsvpCacheService.init().catch(err => {
            console.warn('[MyRsvps] Failed to init RSVP cache:', err);
        });
        
        // Filter out past events on the client side (safety net for timezone edge cases)
        this.filterPastEvents();
        
        // Update button states based on online status
        this.updateOnlineButtons();
        
        // Set up modal event listener
        if (this.hasModalTarget) {
            this.modalTarget.addEventListener('hidden.bs.modal', () => {
                this.onModalHidden();
            });
        }
    }

    /**
     * Filter out events that have ended based on client's local date.
     * Moves them to the past tab and updates badge counts.
     */
    filterPastEvents() {
        if (!this.hasUpcomingListTarget) return;
        
        const today = new Date();
        today.setHours(0, 0, 0, 0);
        const todayStr = today.toISOString().split('T')[0]; // YYYY-MM-DD
        
        const cards = this.upcomingListTarget.querySelectorAll('.mobile-event-card[data-end-date]');
        let movedCount = 0;
        
        cards.forEach(card => {
            const endDate = card.dataset.endDate;
            if (endDate && endDate < todayStr) {
                // This event has ended - move it to past tab
                movedCount++;
                console.log(`[MyRsvps] Moving past event to Past tab (end: ${endDate}, today: ${todayStr})`);
                
                // Transform the card for past display
                this.transformCardForPast(card);
                
                // Move to past list
                if (this.hasPastListTarget) {
                    // Remove empty state if present
                    if (this.hasPastEmptyStateTarget) {
                        this.pastEmptyStateTarget.remove();
                    }
                    // Insert at beginning of past list (most recent first)
                    this.pastListTarget.insertBefore(card, this.pastListTarget.firstChild);
                } else {
                    // No past list target, just hide it
                    card.style.display = 'none';
                }
            }
        });
        
        if (movedCount > 0) {
            console.log(`[MyRsvps] Moved ${movedCount} events from Upcoming to Past`);
            
            // Update badge counts
            this.updateBadgeCounts();
            
            // Check if all upcoming events are now gone
            const remainingUpcoming = this.upcomingListTarget.querySelectorAll('.mobile-event-card');
            if (remainingUpcoming.length === 0) {
                // Show empty state message
                this.upcomingListTarget.innerHTML = `
                    <div class="card empty-state-card">
                        <div class="card-body text-center py-5">
                            <i class="bi bi-calendar-check d-block fs-1 mb-3" style="color: var(--section-rsvps);"></i>
                            <h3 class="h5 mb-2">No Upcoming RSVPs</h3>
                            <p class="text-muted mb-4">
                                You haven't RSVPed to any upcoming gatherings yet.
                            </p>
                            <a href="/gatherings/mobile-calendar" class="btn btn-primary online-only-btn">
                                <i class="bi bi-calendar me-2"></i>Browse Calendar
                            </a>
                        </div>
                    </div>
                `;
            }
        }
    }

    /**
     * Transform a card from upcoming style to past style
     */
    transformCardForPast(card) {
        // Remove 'attending' class and add 'past' class
        card.classList.remove('attending');
        card.classList.add('past');
        
        // Change the success check icon to muted
        const checkIcon = card.querySelector('.bi-check-circle-fill.text-success');
        if (checkIcon) {
            checkIcon.classList.remove('bi-check-circle-fill', 'text-success');
            checkIcon.classList.add('bi-check-circle', 'text-muted');
        }
        
        // Remove action buttons row
        const actionsRow = card.querySelector('.mobile-event-actions-row');
        if (actionsRow) {
            actionsRow.remove();
        }
    }

    /**
     * Update the badge counts for both tabs
     */
    updateBadgeCounts() {
        // Count visible upcoming cards
        if (this.hasUpcomingCountTarget && this.hasUpcomingListTarget) {
            const upcomingCards = this.upcomingListTarget.querySelectorAll('.mobile-event-card:not(.past)');
            const count = upcomingCards.length;
            this.upcomingCountTarget.textContent = count;
            this.upcomingCountTarget.hidden = count === 0;
        }
        
        // Count past cards
        if (this.hasPastCountTarget && this.hasPastListTarget) {
            const pastCards = this.pastListTarget.querySelectorAll('.mobile-event-card');
            const count = pastCards.length;
            this.pastCountTarget.textContent = count;
            this.pastCountTarget.hidden = count === 0;
        }
    }

    /**
     * Called when connection state changes
     */
    onConnectionStateChanged(isOnline) {
        this.updateOnlineButtons();
    }

    /**
     * Update online-only buttons based on connection state
     */
    updateOnlineButtons() {
        const buttons = this.element.querySelectorAll('.online-only-btn');
        buttons.forEach(btn => {
            if (navigator.onLine) {
                btn.classList.remove('disabled');
                btn.style.opacity = '1';
                btn.style.pointerEvents = 'auto';
                btn.removeAttribute('aria-disabled');
            } else {
                btn.classList.add('disabled');
                btn.style.opacity = '0.5';
                btn.style.pointerEvents = 'none';
                btn.setAttribute('aria-disabled', 'true');
            }
        });
    }

    /**
     * Edit RSVP - load attendance modal
     */
    async editRsvp(event) {
        // Don't allow editing when offline
        if (!navigator.onLine) {
            alert('You need to be online to edit RSVPs.');
            return;
        }
        
        const button = event.currentTarget;
        const gatheringId = button.dataset.gatheringId;
        const attendanceId = button.dataset.attendanceId;
        
        this.currentGatheringId = gatheringId;
        
        // Show modal with loading state
        this.modalBodyTarget.innerHTML = `
            <div class="text-center py-4">
                <div class="spinner-border text-primary" role="status">
                    <span class="visually-hidden">Loading...</span>
                </div>
            </div>
        `;
        
        // Initialize and show modal
        if (!this.modal) {
            this.modal = new bootstrap.Modal(this.modalTarget);
        }
        this.modal.show();
        
        try {
            // Load attendance modal content
            const url = `/gatherings/attendance-modal/${gatheringId}?attendance_id=${attendanceId}`;
            const response = await fetch(url, {
                headers: {
                    'X-Requested-With': 'XMLHttpRequest'
                }
            });
            
            if (!response.ok) {
                throw new Error(`Failed to load: ${response.status}`);
            }
            
            const html = await response.text();
            this.modalBodyTarget.innerHTML = html;
            
            // Set up form handlers
            this.setupFormHandlers();
            
        } catch (error) {
            console.error('Error loading RSVP form:', error);
            this.modalBodyTarget.innerHTML = `
                <div class="alert alert-danger">
                    <i class="bi bi-exclamation-triangle me-2"></i>
                    Failed to load RSVP form. Please try again.
                </div>
            `;
        }
    }

    /**
     * Set up form submission handlers
     */
    setupFormHandlers() {
        // Main attendance form
        const form = this.modalBodyTarget.querySelector('#attendanceModalForm');
        if (form) {
            form.addEventListener('submit', (e) => this.handleFormSubmit(e));
        }
        
        // Delete form
        const deleteForm = this.modalBodyTarget.querySelector('form[id^="deleteAttendanceForm_"]');
        if (deleteForm) {
            deleteForm.addEventListener('submit', (e) => this.handleDeleteSubmit(e));
        }
    }

    /**
     * Handle main form submission
     */
    async handleFormSubmit(event) {
        event.preventDefault();
        
        const form = event.target;
        const formData = new FormData(form);
        const submitButton = form.querySelector('button[type="submit"]');
        
        // Disable submit button
        if (submitButton) {
            submitButton.disabled = true;
            submitButton.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Saving...';
        }
        
        try {
            const response = await fetch(form.action, {
                method: 'POST',
                body: formData,
                headers: {
                    'X-Requested-With': 'XMLHttpRequest'
                }
            });
            
            if (response.ok || response.redirected) {
                // Update RSVP cache
                if (this.currentGatheringId) {
                    rsvpCacheService.updateCachedRsvp(this.currentGatheringId, {
                        share_with_kingdom: formData.get('share_with_kingdom') === '1',
                        share_with_hosting_group: formData.get('share_with_hosting_group') === '1',
                        share_with_crown: formData.get('share_with_crown') === '1',
                        public_note: formData.get('public_note') || ''
                    }).catch(err => console.warn('[MyRsvps] Failed to update RSVP cache:', err));
                }
                
                // Success - close modal and reload page
                this.modal.hide();
                window.location.reload();
            } else {
                throw new Error(`Request failed: ${response.status}`);
            }
        } catch (error) {
            console.error('Error submitting form:', error);
            
            // Re-enable submit button
            if (submitButton) {
                submitButton.disabled = false;
                submitButton.innerHTML = '<i class="bi bi-check-circle me-2"></i>Save Changes';
            }
            
            // Show error
            const alertDiv = document.createElement('div');
            alertDiv.className = 'alert alert-danger mt-3';
            alertDiv.innerHTML = '<i class="bi bi-exclamation-triangle me-2"></i>Failed to save. Please try again.';
            form.appendChild(alertDiv);
        }
    }

    /**
     * Handle delete form submission
     */
    async handleDeleteSubmit(event) {
        event.preventDefault();
        
        if (!confirm('Are you sure you want to cancel your RSVP?')) {
            return;
        }
        
        const form = event.target;
        const formData = new FormData(form);
        const submitButton = form.querySelector('button[type="submit"]');
        
        // Disable submit button
        if (submitButton) {
            submitButton.disabled = true;
            submitButton.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Cancelling...';
        }
        
        try {
            const response = await fetch(form.action, {
                method: 'POST',
                body: formData,
                headers: {
                    'X-Requested-With': 'XMLHttpRequest'
                }
            });
            
            if (response.ok || response.redirected) {
                // Remove from cache
                if (this.currentGatheringId) {
                    rsvpCacheService.removeCachedRsvp(this.currentGatheringId)
                        .catch(err => console.warn('[MyRsvps] Failed to remove from RSVP cache:', err));
                }
                
                // Success - close modal and reload page
                this.modal.hide();
                window.location.reload();
            } else {
                throw new Error(`Request failed: ${response.status}`);
            }
        } catch (error) {
            console.error('Error deleting attendance:', error);
            
            // Re-enable submit button
            if (submitButton) {
                submitButton.disabled = false;
                submitButton.innerHTML = '<i class="bi bi-trash me-2"></i>Cancel RSVP';
            }
            
            alert('Failed to cancel RSVP. Please try again.');
        }
    }

    /**
     * Handle modal hidden event
     */
    onModalHidden() {
        // Reset modal content
        if (this.hasModalBodyTarget) {
            this.modalBodyTarget.innerHTML = `
                <div class="text-center py-4">
                    <div class="spinner-border text-primary" role="status">
                        <span class="visually-hidden">Loading...</span>
                    </div>
                </div>
            `;
        }
        this.currentGatheringId = null;
    }

    onDisconnect() {
        if (this.modal) {
            this.modal.dispose();
            this.modal = null;
        }
    }
}

// Register controller
if (!window.Controllers) {
    window.Controllers = {};
}
window.Controllers["my-rsvps"] = MyRsvpsController;