PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.6
Yatra – Travel Booking & Tour Operator Software v3.0.2.6
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
yatra / assets / js / listing-wishlist.js

listing-wishlist.js in Yatra – Travel Booking & Tour Operator Software 3.0.2.6, at assets/js/listing-wishlist.js

298 lines 10.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // Wishlist functionality for trip listing page (Yatra Pro + setting)
2 class TripListingWishlist {
3 constructor() {
4 var cfg = window.yatraWishlistConfig || {};
5 var base = (cfg.restUrl || '').replace(/\/$/, '');
6 this.apiUrl = base || (window.location && window.location.origin ? window.location.origin + '/wp-json/yatra/v1' : '/wp-json/yatra/v1');
7 this.nonce = cfg.nonce || '';
8 this.isLoggedIn = !!cfg.isLoggedIn;
9
10 // Debug logging
11
12
13 this.init();
14 }
15
16 init() {
17 // Handle wishlist button clicks
18 document.addEventListener('click', (e) => {
19 const btn = e.target.closest('.yatra-favorite-btn, .yatra-similar-favorite-btn');
20 if (btn) {
21 e.preventDefault();
22 e.stopPropagation();
23 this.handleWishlistClick(btn);
24 }
25 });
26
27 // Check saved status for all trips if logged in
28 if (this.isLoggedIn) {
29 this.checkAllSavedStatus();
30 }
31 }
32
33 async checkAllSavedStatus() {
34 const buttons = document.querySelectorAll('.yatra-favorite-btn[data-trip-id], .yatra-similar-favorite-btn[data-trip-id]');
35 buttons.forEach(async (btn) => {
36 const tripId = btn.getAttribute('data-trip-id');
37 if (tripId) {
38 try {
39 const response = await fetch(
40 `${this.apiUrl}/saved-trips/check/${tripId}`,
41 {
42 method: 'GET',
43 headers: {
44 'X-WP-Nonce': this.nonce
45 },
46 credentials: 'same-origin'
47 }
48 );
49
50 const data = await response.json();
51 if (data.success && data.data.is_saved) {
52 this.updateButtonState(btn, true);
53 }
54 } catch (error) {
55 console.error('Error checking wishlist status:', error);
56 }
57 }
58 });
59 }
60
61 handleWishlistClick(btn) {
62 if (!this.isLoggedIn) {
63 this.showLoginPopup();
64 return;
65 }
66
67 const tripId = btn.getAttribute('data-trip-id');
68 if (!tripId) {
69 console.error('Trip ID not found');
70 return;
71 }
72
73 const isSaved = btn.classList.contains('saved');
74 if (isSaved) {
75 this.removeFromWishlist(btn, tripId);
76 } else {
77 this.addToWishlist(btn, tripId);
78 }
79 }
80
81 async addToWishlist(btn, tripId) {
82 btn.disabled = true;
83 btn.classList.add('loading');
84
85 try {
86 const params = new URLSearchParams();
87 params.set('trip_id', String(parseInt(tripId, 10)));
88 const response = await fetch(`${this.apiUrl}/saved-trips`, {
89 method: 'POST',
90 headers: {
91 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
92 'X-WP-Nonce': this.nonce
93 },
94 credentials: 'same-origin',
95 body: params.toString()
96 });
97
98 const data = await response.json();
99 if (data.success) {
100 this.updateButtonState(btn, true);
101 this.showMessage(data.message || 'Trip saved to wishlist', 'success', btn);
102 } else {
103 this.showMessage(data.message || 'Failed to save trip', 'error', btn);
104 }
105 } catch (error) {
106 console.error('Error saving trip:', error);
107 this.showMessage('An error occurred. Please try again.', 'error', btn);
108 } finally {
109 btn.disabled = false;
110 btn.classList.remove('loading');
111 }
112 }
113
114 async removeFromWishlist(btn, tripId) {
115 btn.disabled = true;
116 btn.classList.add('loading');
117
118 try {
119 const response = await fetch(`${this.apiUrl}/saved-trips/${tripId}`, {
120 method: 'DELETE',
121 headers: {
122 'X-WP-Nonce': this.nonce
123 },
124 credentials: 'same-origin'
125 });
126
127 const data = await response.json();
128 if (data.success) {
129 this.updateButtonState(btn, false);
130 this.showMessage(data.message || 'Trip removed from wishlist', 'success', btn);
131 } else {
132 this.showMessage(data.message || 'Failed to remove trip', 'error', btn);
133 }
134 } catch (error) {
135 console.error('Error removing trip:', error);
136 this.showMessage('An error occurred. Please try again.', 'error', btn);
137 } finally {
138 btn.disabled = false;
139 btn.classList.remove('loading');
140 }
141 }
142
143 updateButtonState(btn, isSaved) {
144 if (isSaved) {
145 btn.classList.add('saved', 'is-saved');
146 btn.setAttribute('aria-label', 'Remove from favorites');
147 btn.setAttribute('title', 'Remove from favorites');
148 // Fill the heart icon and change color
149 const svg = btn.querySelector('svg');
150 if (svg) {
151 svg.style.fill = 'currentColor';
152 svg.style.stroke = 'currentColor';
153 btn.style.color = '#ef4444';
154 }
155 } else {
156 btn.classList.remove('saved', 'is-saved');
157 btn.setAttribute('aria-label', 'Add to favorites');
158 btn.setAttribute('title', 'Add to favorites');
159 // Unfill the heart icon
160 const svg = btn.querySelector('svg');
161 if (svg) {
162 svg.style.fill = 'none';
163 svg.style.stroke = 'currentColor';
164 btn.style.color = '';
165 }
166 }
167 }
168
169 showLoginPopup() {
170 // Create a simple modal for login prompt
171 const modal = document.createElement('div');
172 modal.className = 'yatra-login-modal';
173 modal.innerHTML = `
174 <div class="yatra-login-modal-overlay"></div>
175 <div class="yatra-login-modal-content">
176 <div class="yatra-login-prompt">
177 <h3>Login Required</h3>
178 <p>Please login to save trips to your wishlist.</p>
179 <div class="yatra-login-buttons">
180 <a href="${window.yatraAdmin?.login_url || '/wp-login.php'}" class="yatra-btn yatra-btn-primary">Login</a>
181 <button type="button" class="yatra-btn yatra-btn-secondary yatra-close-modal">Cancel</button>
182 </div>
183 </div>
184 </div>
185 `;
186
187 // Add styles
188 modal.style.cssText = `
189 position: fixed;
190 top: 0;
191 left: 0;
192 width: 100%;
193 height: 100%;
194 z-index: 10000;
195 display: flex;
196 align-items: center;
197 justify-content: center;
198 `;
199
200 const overlay = modal.querySelector('.yatra-login-modal-overlay');
201 overlay.style.cssText = `
202 position: absolute;
203 top: 0;
204 left: 0;
205 width: 100%;
206 height: 100%;
207 background: rgba(0, 0, 0, 0.5);
208 `;
209
210 const content = modal.querySelector('.yatra-login-modal-content');
211 content.style.cssText = `
212 position: relative;
213 background: white;
214 border-radius: 8px;
215 padding: 40px;
216 max-width: 400px;
217 width: 90%;
218 text-align: center;
219 box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
220 `;
221
222 document.body.appendChild(modal);
223 document.body.style.overflow = 'hidden';
224
225 // Handle close
226 const closeModal = () => {
227 modal.remove();
228 document.body.style.overflow = '';
229 };
230
231 overlay.addEventListener('click', closeModal);
232 modal.querySelector('.yatra-close-modal').addEventListener('click', closeModal);
233 }
234
235 showMessage(message, type, button) {
236 // Remove existing toast if any
237 const existingToast = document.getElementById('yatra-wishlist-toast');
238 if (existingToast) {
239 existingToast.remove();
240 }
241
242 // Create toast near the button
243 const toast = document.createElement('div');
244 toast.id = 'yatra-wishlist-toast';
245 toast.textContent = message;
246
247 // Position relative to button
248 const rect = button.getBoundingClientRect();
249 const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
250 const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
251
252 toast.style.cssText = `
253 position: absolute;
254 top: ${rect.bottom + scrollTop + 10}px;
255 left: ${rect.left + scrollLeft}px;
256 padding: 10px 16px;
257 border-radius: 8px;
258 z-index: 10001;
259 box-shadow: 0 4px 12px rgba(0,0,0,0.2);
260 transition: all 0.3s ease;
261 white-space: nowrap;
262 font-size: 14px;
263 pointer-events: none;
264 background-color: ${type === 'success' ? '#10b981' : '#ef4444'};
265 color: #ffffff;
266 opacity: 0;
267 transform: translateY(-10px);
268 `;
269
270 document.body.appendChild(toast);
271
272 // Animate in
273 requestAnimationFrame(() => {
274 toast.style.opacity = '1';
275 toast.style.transform = 'translateY(0)';
276 });
277
278 // Remove after delay
279 setTimeout(() => {
280 toast.style.opacity = '0';
281 toast.style.transform = 'translateY(-10px)';
282 setTimeout(() => {
283 if (toast.parentNode) {
284 toast.remove();
285 }
286 }, 300);
287 }, 3000);
288 }
289 }
290
291 document.addEventListener('DOMContentLoaded', function() {
292 var cfg = window.yatraWishlistConfig || {};
293 if (!cfg.enabled) {
294 return;
295 }
296 window.tripListingWishlist = new TripListingWishlist();
297 });
298