nodebb-plugin-onekite-calendar 2.0.3 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/public/admin.js +163 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Changelog – calendar-onekite
|
|
2
2
|
|
|
3
|
+
## 1.2.11
|
|
4
|
+
- ACP : ajout de la recherche automatique d’adresse (autocomplete Nominatim) dans la modale de validation (unitaire + batch), comme sur le calendrier
|
|
5
|
+
|
|
3
6
|
## 1.2.10
|
|
4
7
|
- HelloAsso : calcul des jours 100% fiable (différence en jours calendaires Y/M/J, sans dépendance aux heures/au fuseau/DST)
|
|
5
8
|
- FullCalendar : endDate traitée comme exclusive partout (UI + checkout)
|
package/package.json
CHANGED
package/plugin.json
CHANGED
package/public/admin.js
CHANGED
|
@@ -187,6 +187,148 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts
|
|
|
187
187
|
return { lat, lon, displayName: hit.display_name || q };
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
// Lightweight address autocomplete (OpenStreetMap Nominatim)
|
|
191
|
+
// Designed to work inside Bootstrap "input-group" without moving DOM nodes.
|
|
192
|
+
async function searchAddresses(query, limit) {
|
|
193
|
+
const q = String(query || '').trim();
|
|
194
|
+
const lim = Math.min(10, Math.max(1, Number(limit) || 6));
|
|
195
|
+
if (q.length < 3) return [];
|
|
196
|
+
const url = `https://nominatim.openstreetmap.org/search?format=jsonv2&addressdetails=1&limit=${lim}&q=${encodeURIComponent(q)}`;
|
|
197
|
+
const res = await fetch(url, {
|
|
198
|
+
method: 'GET',
|
|
199
|
+
headers: {
|
|
200
|
+
'Accept': 'application/json',
|
|
201
|
+
'Accept-Language': 'fr',
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
if (!res.ok) return [];
|
|
205
|
+
const arr = await res.json();
|
|
206
|
+
if (!Array.isArray(arr)) return [];
|
|
207
|
+
return arr.map((hit) => {
|
|
208
|
+
const lat = Number(hit.lat);
|
|
209
|
+
const lon = Number(hit.lon);
|
|
210
|
+
return {
|
|
211
|
+
displayName: hit.display_name || q,
|
|
212
|
+
lat: Number.isFinite(lat) ? lat : null,
|
|
213
|
+
lon: Number.isFinite(lon) ? lon : null,
|
|
214
|
+
};
|
|
215
|
+
}).filter(h => h && h.displayName);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function attachAddressAutocomplete(inputEl, onPick) {
|
|
219
|
+
if (!inputEl) return;
|
|
220
|
+
if (inputEl.getAttribute('data-onekite-autocomplete') === '1') return;
|
|
221
|
+
inputEl.setAttribute('data-onekite-autocomplete', '1');
|
|
222
|
+
|
|
223
|
+
const anchor = inputEl.closest('.input-group') || inputEl.parentNode;
|
|
224
|
+
if (!anchor) return;
|
|
225
|
+
|
|
226
|
+
// Ensure positioning context
|
|
227
|
+
if (getComputedStyle(anchor).position === 'static') {
|
|
228
|
+
anchor.style.position = 'relative';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const menu = document.createElement('div');
|
|
232
|
+
menu.className = 'onekite-autocomplete-menu';
|
|
233
|
+
menu.style.position = 'absolute';
|
|
234
|
+
menu.style.left = '0';
|
|
235
|
+
menu.style.right = '0';
|
|
236
|
+
menu.style.top = '100%';
|
|
237
|
+
menu.style.zIndex = '2000';
|
|
238
|
+
menu.style.background = '#fff';
|
|
239
|
+
menu.style.border = '1px solid rgba(0,0,0,.15)';
|
|
240
|
+
menu.style.borderTop = '0';
|
|
241
|
+
menu.style.maxHeight = '220px';
|
|
242
|
+
menu.style.overflowY = 'auto';
|
|
243
|
+
menu.style.display = 'none';
|
|
244
|
+
menu.style.borderRadius = '0 0 .375rem .375rem';
|
|
245
|
+
menu.style.boxShadow = '0 .25rem .75rem rgba(0,0,0,.08)';
|
|
246
|
+
anchor.appendChild(menu);
|
|
247
|
+
|
|
248
|
+
let timer = null;
|
|
249
|
+
let lastQuery = '';
|
|
250
|
+
let busy = false;
|
|
251
|
+
|
|
252
|
+
function hide() {
|
|
253
|
+
menu.style.display = 'none';
|
|
254
|
+
menu.innerHTML = '';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function show(hits) {
|
|
258
|
+
if (!hits || !hits.length) {
|
|
259
|
+
hide();
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
menu.innerHTML = '';
|
|
263
|
+
hits.forEach((h) => {
|
|
264
|
+
const btn = document.createElement('button');
|
|
265
|
+
btn.type = 'button';
|
|
266
|
+
btn.className = 'onekite-autocomplete-item';
|
|
267
|
+
btn.textContent = h.displayName;
|
|
268
|
+
btn.style.display = 'block';
|
|
269
|
+
btn.style.width = '100%';
|
|
270
|
+
btn.style.textAlign = 'left';
|
|
271
|
+
btn.style.padding = '.35rem .5rem';
|
|
272
|
+
btn.style.border = '0';
|
|
273
|
+
btn.style.background = 'transparent';
|
|
274
|
+
btn.style.cursor = 'pointer';
|
|
275
|
+
btn.addEventListener('click', () => {
|
|
276
|
+
inputEl.value = h.displayName;
|
|
277
|
+
hide();
|
|
278
|
+
try { onPick && onPick(h); } catch (e) {}
|
|
279
|
+
});
|
|
280
|
+
btn.addEventListener('mouseenter', () => { btn.style.background = 'rgba(0,0,0,.05)'; });
|
|
281
|
+
btn.addEventListener('mouseleave', () => { btn.style.background = 'transparent'; });
|
|
282
|
+
menu.appendChild(btn);
|
|
283
|
+
});
|
|
284
|
+
menu.style.display = 'block';
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function run(q) {
|
|
288
|
+
if (busy) return;
|
|
289
|
+
busy = true;
|
|
290
|
+
try {
|
|
291
|
+
const hits = await searchAddresses(q, 6);
|
|
292
|
+
if (String(inputEl.value || '').trim() !== q) return; // ignore stale
|
|
293
|
+
show(hits);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
hide();
|
|
296
|
+
} finally {
|
|
297
|
+
busy = false;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
inputEl.addEventListener('input', () => {
|
|
302
|
+
const q = String(inputEl.value || '').trim();
|
|
303
|
+
lastQuery = q;
|
|
304
|
+
if (timer) clearTimeout(timer);
|
|
305
|
+
if (q.length < 3) {
|
|
306
|
+
hide();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
timer = setTimeout(() => run(lastQuery), 250);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
inputEl.addEventListener('focus', () => {
|
|
313
|
+
const q = String(inputEl.value || '').trim();
|
|
314
|
+
if (q.length >= 3) {
|
|
315
|
+
if (timer) clearTimeout(timer);
|
|
316
|
+
timer = setTimeout(() => run(q), 150);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
inputEl.addEventListener('keydown', (e) => {
|
|
321
|
+
if (e.key === 'Escape') hide();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Close when clicking outside (once per menu)
|
|
325
|
+
document.addEventListener('click', (e) => {
|
|
326
|
+
try {
|
|
327
|
+
if (!anchor.contains(e.target)) hide();
|
|
328
|
+
} catch (err) {}
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
190
332
|
function formToObject(form) {
|
|
191
333
|
const out = {};
|
|
192
334
|
new FormData(form).forEach((v, k) => {
|
|
@@ -698,6 +840,19 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts
|
|
|
698
840
|
} catch (e) {}
|
|
699
841
|
});
|
|
700
842
|
}
|
|
843
|
+
|
|
844
|
+
// Autocomplete like on the calendar validation modal
|
|
845
|
+
const addrInput = document.getElementById('onekite-pickup-address');
|
|
846
|
+
attachAddressAutocomplete(addrInput, (h) => {
|
|
847
|
+
try {
|
|
848
|
+
if (!h || !Number.isFinite(Number(h.lat)) || !Number.isFinite(Number(h.lon))) return;
|
|
849
|
+
const lat = Number(h.lat);
|
|
850
|
+
const lon = Number(h.lon);
|
|
851
|
+
marker.setLatLng([lat, lon]);
|
|
852
|
+
map.setView([lat, lon], 16);
|
|
853
|
+
setLatLon(lat, lon);
|
|
854
|
+
} catch (e) {}
|
|
855
|
+
});
|
|
701
856
|
setTimeout(() => { try { map.invalidateSize(); } catch (e) {} }, 250);
|
|
702
857
|
} catch (e) {}
|
|
703
858
|
});
|
|
@@ -887,6 +1042,14 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts
|
|
|
887
1042
|
}
|
|
888
1043
|
});
|
|
889
1044
|
}
|
|
1045
|
+
|
|
1046
|
+
// Autocomplete like on the calendar validation modal
|
|
1047
|
+
attachAddressAutocomplete(addrInput, (h) => {
|
|
1048
|
+
try {
|
|
1049
|
+
if (!h || !Number.isFinite(Number(h.lat)) || !Number.isFinite(Number(h.lon))) return;
|
|
1050
|
+
setMarker(Number(h.lat), Number(h.lon), 16);
|
|
1051
|
+
} catch (e) {}
|
|
1052
|
+
});
|
|
890
1053
|
} catch (e) {
|
|
891
1054
|
// ignore
|
|
892
1055
|
}
|