nodebb-plugin-onekite-calendar 2.0.73 → 2.0.74
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/lib/api.js +16 -0
- package/library.js +1 -0
- package/package.json +1 -1
- package/public/client.js +105 -0
package/lib/api.js
CHANGED
|
@@ -1465,6 +1465,22 @@ api.createReservation = async function (req, res) {
|
|
|
1465
1465
|
res.json({ ok: true, rid, status: resv.status, autoPaid: !!(isValidatorFree || isRegularization) });
|
|
1466
1466
|
};
|
|
1467
1467
|
|
|
1468
|
+
api.searchUsers = async function (req, res) {
|
|
1469
|
+
const uid = req.uid;
|
|
1470
|
+
if (!uid) return res.status(401).json({ error: 'not-logged-in' });
|
|
1471
|
+
const settings = await getSettings();
|
|
1472
|
+
if (!(await canValidate(uid, settings))) return res.status(403).json({ error: 'not-allowed' });
|
|
1473
|
+
const q = String(req.query.q || '').trim();
|
|
1474
|
+
if (q.length < 2) return res.json([]);
|
|
1475
|
+
try {
|
|
1476
|
+
const result = await user.search({ query: q, searchBy: 'username', resultsPerPage: 8, uid });
|
|
1477
|
+
const list = (result && result.users) ? result.users : [];
|
|
1478
|
+
return res.json(list.map(u => ({ uid: u.uid, username: u.username })));
|
|
1479
|
+
} catch (e) {
|
|
1480
|
+
return res.json([]);
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1468
1484
|
// Validator actions (from calendar popup)
|
|
1469
1485
|
api.approveReservation = async function (req, res) {
|
|
1470
1486
|
const uid = req.uid;
|
package/library.js
CHANGED
|
@@ -65,6 +65,7 @@ Plugin.init = async function (params) {
|
|
|
65
65
|
router.get('/api/v3/plugins/calendar-onekite/events', ...publicExpose, api.getEvents);
|
|
66
66
|
router.get('/api/v3/plugins/calendar-onekite/items', ...publicExpose, api.getItems);
|
|
67
67
|
router.get('/api/v3/plugins/calendar-onekite/capabilities', ...publicExpose, api.getCapabilities);
|
|
68
|
+
router.get('/api/v3/plugins/calendar-onekite/users/search', ...publicExpose, api.searchUsers);
|
|
68
69
|
|
|
69
70
|
// Maintenance / audit (restricted to validator groups)
|
|
70
71
|
router.get('/api/v3/plugins/calendar-onekite/maintenance', ...publicExpose, api.getMaintenance);
|
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -821,6 +821,107 @@ async function searchAddresses(query, limit) {
|
|
|
821
821
|
}).filter(h => h && h.displayName);
|
|
822
822
|
}
|
|
823
823
|
|
|
824
|
+
async function searchUsers(q, limit) {
|
|
825
|
+
try {
|
|
826
|
+
const qs = new URLSearchParams({ q: String(q || ''), limit: String(limit || 8) });
|
|
827
|
+
const arr = await fetchJson(`/api/v3/plugins/calendar-onekite/users/search?${qs}`);
|
|
828
|
+
if (!Array.isArray(arr)) return [];
|
|
829
|
+
return arr;
|
|
830
|
+
} catch (e) {
|
|
831
|
+
return [];
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function attachUserAutocomplete(inputEl) {
|
|
836
|
+
if (!inputEl) return;
|
|
837
|
+
if (inputEl.getAttribute('data-onekite-user-ac') === '1') return;
|
|
838
|
+
inputEl.setAttribute('data-onekite-user-ac', '1');
|
|
839
|
+
|
|
840
|
+
const host = inputEl.parentNode || document.body;
|
|
841
|
+
try {
|
|
842
|
+
const cs = window.getComputedStyle(host);
|
|
843
|
+
if (!cs || cs.position === 'static') host.style.position = 'relative';
|
|
844
|
+
} catch (e) {
|
|
845
|
+
host.style.position = 'relative';
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const menu = document.createElement('div');
|
|
849
|
+
menu.className = 'onekite-autocomplete-menu';
|
|
850
|
+
menu.style.position = 'absolute';
|
|
851
|
+
menu.style.left = '0';
|
|
852
|
+
menu.style.right = '0';
|
|
853
|
+
menu.style.top = '100%';
|
|
854
|
+
menu.style.zIndex = '2000';
|
|
855
|
+
menu.style.background = '#fff';
|
|
856
|
+
menu.style.border = '1px solid rgba(0,0,0,.15)';
|
|
857
|
+
menu.style.borderTop = '0';
|
|
858
|
+
menu.style.maxHeight = '220px';
|
|
859
|
+
menu.style.overflowY = 'auto';
|
|
860
|
+
menu.style.display = 'none';
|
|
861
|
+
menu.style.borderRadius = '0 0 .375rem .375rem';
|
|
862
|
+
host.appendChild(menu);
|
|
863
|
+
|
|
864
|
+
let timer = null;
|
|
865
|
+
let busy = false;
|
|
866
|
+
|
|
867
|
+
function hide() { menu.style.display = 'none'; menu.innerHTML = ''; }
|
|
868
|
+
|
|
869
|
+
function show(hits) {
|
|
870
|
+
if (!hits || !hits.length) { hide(); return; }
|
|
871
|
+
menu.innerHTML = '';
|
|
872
|
+
hits.forEach((h) => {
|
|
873
|
+
const btn = document.createElement('button');
|
|
874
|
+
btn.type = 'button';
|
|
875
|
+
btn.className = 'onekite-autocomplete-item';
|
|
876
|
+
btn.textContent = h.username;
|
|
877
|
+
btn.style.display = 'block';
|
|
878
|
+
btn.style.width = '100%';
|
|
879
|
+
btn.style.textAlign = 'left';
|
|
880
|
+
btn.style.padding = '.35rem .5rem';
|
|
881
|
+
btn.style.border = '0';
|
|
882
|
+
btn.style.background = 'transparent';
|
|
883
|
+
btn.style.cursor = 'pointer';
|
|
884
|
+
btn.addEventListener('click', () => { inputEl.value = h.username; hide(); });
|
|
885
|
+
btn.addEventListener('mouseenter', () => { btn.style.background = 'rgba(0,0,0,.05)'; });
|
|
886
|
+
btn.addEventListener('mouseleave', () => { btn.style.background = 'transparent'; });
|
|
887
|
+
menu.appendChild(btn);
|
|
888
|
+
});
|
|
889
|
+
menu.style.display = 'block';
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
async function run(q) {
|
|
893
|
+
if (busy) return;
|
|
894
|
+
busy = true;
|
|
895
|
+
try {
|
|
896
|
+
const hits = await searchUsers(q, 8);
|
|
897
|
+
if (String(inputEl.value || '').trim() !== q) return;
|
|
898
|
+
show(hits);
|
|
899
|
+
} catch (e) {
|
|
900
|
+
hide();
|
|
901
|
+
} finally {
|
|
902
|
+
busy = false;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
inputEl.addEventListener('input', () => {
|
|
907
|
+
const q = String(inputEl.value || '').trim();
|
|
908
|
+
if (timer) clearTimeout(timer);
|
|
909
|
+
if (q.length < 2) { hide(); return; }
|
|
910
|
+
timer = setTimeout(() => run(q), 250);
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
inputEl.addEventListener('focus', () => {
|
|
914
|
+
const q = String(inputEl.value || '').trim();
|
|
915
|
+
if (q.length >= 2) { if (timer) clearTimeout(timer); timer = setTimeout(() => run(q), 150); }
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
document.addEventListener('click', (e) => {
|
|
919
|
+
try { if (!host.contains(e.target)) hide(); } catch (err) {}
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
inputEl.addEventListener('keydown', (e) => { if (e.key === 'Escape') hide(); });
|
|
923
|
+
}
|
|
924
|
+
|
|
824
925
|
function attachAddressAutocomplete(inputEl, onPick) {
|
|
825
926
|
if (!inputEl) return;
|
|
826
927
|
// Avoid double attach
|
|
@@ -1187,6 +1288,10 @@ function toDatetimeLocalValue(date) {
|
|
|
1187
1288
|
|
|
1188
1289
|
// live total update
|
|
1189
1290
|
setTimeout(() => {
|
|
1291
|
+
if (isValidatorMode) {
|
|
1292
|
+
attachUserAutocomplete(document.getElementById('onekite-target-username'));
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1190
1295
|
const totalEl = document.getElementById('onekite-total');
|
|
1191
1296
|
const periodEl = document.getElementById('onekite-period');
|
|
1192
1297
|
const daysEl = document.getElementById('onekite-days');
|