PageSourceSearch

https://guess-the-jumpshot.com/magic-feedback.js

js guess-the-jumpshot.com collected 2026-10-04 00:21:15 UTC 13,430 bytes, 357 lines download raw bytes

1// Magic Feedback Component
2class MagicFeedback {
3    constructor() {
4        this.overlay = null;
5        this.modal = null;
6        this.selectedType = '';
7        this.rating = 0;
8        this.init();
9    }
10
11    init() {
12        this.createFeedbackModal();
13        this.setupEventListeners();
14    }
15
16    createFeedbackModal() {
17        this.overlay = document.createElement('div');
18        this.overlay.className = 'magic-feedback-overlay';
19        this.overlay.innerHTML = `
20            <div class="magic-feedback-modal">
21                <div class="magic-feedback-header">
22                    <button class="magic-feedback-close" id="feedbackClose">
23                        <i data-lucide="x" size="20"></i>
24                    </button>
25                    <h2 class="magic-feedback-title">Share Your Feedback</h2>
26                    <p class="magic-feedback-subtitle">Help us improve Guess the Jumpshot! Your feedback is valuable to us.</p>
27                </div>
28                
29                <div class="magic-feedback-body">
30                    <!-- Feedback Type Selection -->
31                    <div class="magic-feedback-form-group">
32                        <label class="magic-feedback-label">What's your feedback about?</label>
33                        <div class="magic-feedback-types">
34                            <div class="magic-feedback-type" data-type="bug">
35                                <div class="magic-feedback-type-icon">🐛</div>
36                                <div class="magic-feedback-type-label">Bug Report</div>
37                            </div>
38                            <div class="magic-feedback-type" data-type="feature">
39                                <div class="magic-feedback-type-icon">💡</div>
40                                <div class="magic-feedback-type-label">Feature Request</div>
41                            </div>
42                            <div class="magic-feedback-type" data-type="improvement">
43                                <div class="magic-feedback-type-icon">✨</div>
44                                <div class="magic-feedback-type-label">Improvement</div>
45                            </div>
46                            <div class="magic-feedback-type" data-type="other">
47                                <div class="magic-feedback-type-icon">💬</div>
48                                <div class="magic-feedback-type-label">Other</div>
49                            </div>
50                        </div>
51                    </div>
52                    
53                    <!-- Rating -->
54                    <div class="magic-feedback-form-group magic-feedback-rating">
55                        <label class="magic-feedback-label">How would you rate your experience?</label>
56                        <div class="magic-rating-stars">
57                            <span class="magic-rating-star" data-rating="1">⭐</span>
58                            <span class="magic-rating-star" data-rating="2">⭐</span>
59                            <span class="magic-rating-star" data-rating="3">⭐</span>
60                            <span class="magic-rating-star" data-rating="4">⭐</span>
61                            <span class="magic-rating-star" data-rating="5">⭐</span>
62                        </div>
63                    </div>
64                    
65                    <!-- Email -->
66                    <div class="magic-feedback-form-group">
67                        <label class="magic-feedback-label">Your Email (optional)</label>
68                        <input 
69                            type="email" 
70                            id="feedbackEmail" 
71                            class="magic-feedback-input" 
72                            placeholder="[email protected]"
73                        >
74                    </div>
75                    
76                    <!-- Message -->
77                    <div class="magic-feedback-form-group">
78                        <label class="magic-feedback-label">Your Feedback</label>
79                        <textarea 
80                            id="feedbackMessage" 
81                            class="magic-feedback-textarea" 
82                            placeholder="Tell us what you think..."
83                            maxlength="1000"
84                        ></textarea>
85                        <span class="magic-feedback-char-limit">
86                            <span id="feedbackCharCount">0</span>/1000
87                        </span>
88                    </div>
89                </div>
90                
91                <div class="magic-feedback-footer">
92                    <button class="magic-feedback-cancel" id="feedbackCancel">
93                        Cancel
94                    </button>
95                    <button class="magic-feedback-submit" id="feedbackSubmit">
96                        <i data-lucide="send" size="18"></i>
97                        Send Feedback
98                    </button>
99                </div>
100            </div>
101        `;
102        
103        document.body.appendChild(this.overlay);
104        this.modal = this.overlay.querySelector('.magic-feedback-modal');
105        
106        // Initialize Lucide icons
107        if (window.lucide) {
108            window.lucide.createIcons();
109        }
110    }
111
112    setupEventListeners() {
113        // Close modal
114        document.getElementById('feedbackClose').addEventListener('click', () => {
115            this.close();
116        });
117        
118        document.getElementById('feedbackCancel').addEventListener('click', () => {
119            this.close();
120        });
121        
122        // Click outside to close
123        this.overlay.addEventListener('click', (e) => {
124            if (e.target === this.overlay) {
125                this.close();
126            }
127        });
128        
129        // ESC key to close
130        document.addEventListener('keydown', (e) => {
131            if (e.key === 'Escape' && this.overlay.classList.contains('active')) {
132                this.close();
133            }
134        });
135        
136        // Feedback type selection
137        document.querySelectorAll('.magic-feedback-type').forEach(type => {
138            type.addEventListener('click', () => {
139                document.querySelectorAll('.magic-feedback-type').forEach(t => {
140                    t.classList.remove('active');
141                });
142                type.classList.add('active');
143                this.selectedType = type.dataset.type;
144            });
145        });
146        
147        // Rating stars
148        document.querySelectorAll('.magic-rating-star').forEach(star => {
149            star.addEventListener('click', () => {
150                this.rating = parseInt(star.dataset.rating);
151                this.updateRatingStars();
152            });
153            
154            star.addEventListener('mouseenter', () => {
155                const hoverRating = parseInt(star.dataset.rating);
156                document.querySelectorAll('.magic-rating-star').forEach((s, index) => {
157                    if (index < hoverRating) {
158                        s.classList.add('filled');
159                    } else {
160                        s.classList.remove('filled');
161                    }
162                });
163            });
164        });
165        
166        // Reset stars on mouse leave
167        document.querySelector('.magic-rating-stars').addEventListener('mouseleave', () => {
168            this.updateRatingStars();
169        });
170        
171        // Character counter
172        const textarea = document.getElementById('feedbackMessage');
173        const charCount = document.getElementById('feedbackCharCount');
174        textarea.addEventListener('input', () => {
175            charCount.textContent = textarea.value.length;
176        });
177        
178        // Submit feedback
179        document.getElementById('feedbackSubmit').addEventListener('click', () => {
180            this.submitFeedback();
181        });
182    }
183
184    updateRatingStars() {
185        document.querySelectorAll('.magic-rating-star').forEach((star, index) => {
186            if (index < this.rating) {
187                star.classList.add('filled');
188            } else {
189                star.classList.remove('filled');
190            }
191        });
192    }
193
194    open() {
195        this.overlay.classList.add('active');
196        // Reset form
197        this.reset();
198    }
199
200    close() {
201        this.overlay.classList.remove('active');
202    }
203
204    reset() {
205        this.selectedType = '';
206        this.rating = 0;
207        document.querySelectorAll('.magic-feedback-type').forEach(type => {
208            type.classList.remove('active');
209        });
210        this.updateRatingStars();
211        document.getElementById('feedbackEmail').value = '';
212        document.getElementById('feedbackMessage').value = '';
213        document.getElementById('feedbackCharCount').textContent = '0';
214    }
215
216    async submitFeedback() {
217        const email = document.getElementById('feedbackEmail').value.trim();
218        const message = document.getElementById('feedbackMessage').value.trim();
219        const submitBtn = document.getElementById('feedbackSubmit');
220        
221        // Validation
222        if (!this.selectedType) {
223            this.showError('Please select a feedback type');
224            return;
225        }
226        
227        if (this.rating === 0) {
228            this.showError('Please rate your experience');
229            return;
230        }
231        
232        if (!message) {
233            this.showError('Please enter your feedback message');
234            return;
235        }
236        
237        // Disable submit button
238        submitBtn.disabled = true;
239        submitBtn.innerHTML = '<i data-lucide="loader" size="18"></i> Sending...';
240        if (window.lucide) {
241            window.lucide.createIcons();
242        }
243        
244        try {
245            // Prepare feedback data
246            const feedbackData = {
247                type: this.selectedType,
248                rating: this.rating,
249                email: email || null,
250                message: message,
251                user_agent: navigator.userAgent,
252                screen_resolution: `${window.screen.width}x${window.screen.height}`,
253                created_at: new Date().toISOString()
254            };
255            
256            // Save to Supabase
257            if (window.GameDB) {
258                await window.GameDB.loadSupabase();
259                const { data, error } = await window.supabase
260                    .from('player_feedback')
261                    .insert(feedbackData)
262                    .select();
263                
264                if (error) throw error;
265            }
266            
267            // Show success state
268            this.showSuccess();
269            
270        } catch (error) {
271            console.error('Error submitting feedback:', error);
272            alert('Failed to submit feedback. Please try again.');
273        } finally {
274            submitBtn.disabled = false;
275            submitBtn.innerHTML = '<i data-lucide="send" size="18"></i> Send Feedback';
276            if (window.lucide) {
277                window.lucide.createIcons();
278            }
279        }
280    }
281
282    showError(message) {
283        // Create a temporary error message
284        const errorDiv = document.createElement('div');
285        errorDiv.style.cssText = `
286            position: fixed;
287            top: 20px;
288            left: 50%;
289            transform: translateX(-50%);
290            background: linear-gradient(135deg, #EF4444, #DC2626);
291            color: white;
292            padding: 1rem 2rem;
293            border-radius: 12px;
294            box-shadow: 0 10px 30px rgba(239, 68, 68, 0.3);
295            z-index: 10002;
296            animation: slideDown 0.3s ease;
297        `;
298        errorDiv.textContent = message;
299        
300        document.body.appendChild(errorDiv);
301        
302        setTimeout(() => {
303            errorDiv.remove();
304        }, 3000);
305    }
306
307    showSuccess() {
308        const modalContent = this.modal.querySelector('.magic-feedback-body');
309        const footer = this.modal.querySelector('.magic-feedback-footer');
310        
311        modalContent.innerHTML = `
312            <div class="magic-feedback-success">
313                <div class="magic-feedback-success-icon">✓</div>
314                <h3 class="magic-feedback-success-title">Thank You!</h3>
315                <p class="magic-feedback-success-message">
316                    Your feedback has been received. We appreciate your input and will use it to improve the game!
317                </p>
318            </div>
319        `;
320        
321        footer.innerHTML = `
322            <button class="magic-feedback-submit" style="width: 100%;" onclick="magicFeedback.close()">
323                Close
324            </button>
325        `;
326        
327        // Auto close after 3 seconds
328        setTimeout(() => {
329            this.close();
330            // Recreate the modal for next use
331            setTimeout(() => {
332                this.overlay.remove();
333                this.createFeedbackModal();
334                this.setupEventListeners();
335            }, 300);
336        }, 3000);
337    }
338}
339
340// Add animation styles
341const feedbackStyles = document.createElement('style');
342feedbackStyles.textContent = `
343    @keyframes slideDown {
344        from {
345            opacity: 0;
346            transform: translateX(-50%) translateY(-20px);
347        }
348        to {
349            opacity: 1;
350            transform: translateX(-50%) translateY(0);
351        }
352    }
353`;
354document.head.appendChild(feedbackStyles);
355
356// Initialize feedback component
357window.magicFeedback = new MagicFeedback();

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.