nodebb-plugin-equipment-calendar 0.8.3 → 0.8.6

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.
Files changed (3) hide show
  1. package/library.js +55 -18
  2. package/package.json +1 -1
  3. package/plugin.json +1 -1
package/library.js CHANGED
@@ -207,38 +207,75 @@ function isCheckoutPaid(checkout) {
207
207
  return false;
208
208
  }
209
209
 
210
+
210
211
  async function fetchHelloAssoItems(settings) {
211
212
  const org = String(settings.ha_organizationSlug || '').trim();
212
213
  const formType = String(settings.ha_itemsFormType || '').trim();
213
214
  const formSlug = String(settings.ha_itemsFormSlug || '').trim();
214
215
  if (!org || !formType || !formSlug) return [];
215
216
 
217
+ const token = await getHelloAssoAccessToken(settings);
218
+ const base = (String(settings.ha_apiBaseUrl || '').trim() || 'https://api.helloasso.com').replace(/\/$/, '');
216
219
  const cacheKey = `equipmentCalendar:ha:items:${org}:${formType}:${formSlug}`;
217
- const cache = await db.getObject(cacheKey);
220
+
221
+ // Cache 10 minutes
222
+ const cached = await db.getObject(cacheKey);
218
223
  const now = Date.now();
219
- if (cache && cache.payload && cache.expiresAt && now < parseInt(cache.expiresAt, 10)) {
220
- try {
221
- return JSON.parse(cache.payload);
222
- } catch (e) {}
224
+ if (cached && cached.itemsJson && cached.expMs && now < parseInt(cached.expMs, 10)) {
225
+ try { return JSON.parse(cached.itemsJson); } catch (e) {}
223
226
  }
224
227
 
225
- const token = await getHelloAssoAccessToken(settings);
226
- const url = `https://api.helloasso.com/v5/organizations/${encodeURIComponent(org)}/forms/${encodeURIComponent(formType)}/${encodeURIComponent(formSlug)}/items`;
227
- const resp = await fetchFn(url, { headers: { authorization: `Bearer ${token}` } });
228
+ // IMPORTANT:
229
+ // - /forms/.../items = "articles vendus" (liés aux commandes) => peut renvoyer 0 si aucune commande
230
+ // - /forms/.../public = données publiques détaillées => contient la structure (tiers / products) du formulaire
231
+ const publicUrl = `${base}/v5/organizations/${encodeURIComponent(org)}/forms/${encodeURIComponent(formType)}/${encodeURIComponent(formSlug)}/public`;
232
+ const resp = await fetchFn(publicUrl, { headers: { authorization: `Bearer ${token}`, accept: 'application/json' } });
228
233
  if (!resp.ok) {
229
234
  const t = await resp.text();
230
- throw new Error(`HelloAsso items error: ${resp.status} ${t}`);
235
+ throw new Error(`HelloAsso public form error: ${resp.status} ${t}`);
236
+ }
237
+ const data = await resp.json();
238
+
239
+ // Extract catalog items:
240
+ // structure differs by formType; common pattern: data.tiers[] or data.tiers[].products[] / items[]
241
+ const out = [];
242
+ const tiers = (data && (data.tiers || data.tiersList || data.prices || data.priceCategories)) || [];
243
+ const tierArr = Array.isArray(tiers) ? tiers : [];
244
+
245
+ function pushItem(it, tierName) {
246
+ if (!it) return;
247
+ const id = String(it.id || it.itemId || it.reference || it.slug || it.code || it.name || '').trim();
248
+ const name = String(it.name || it.label || it.title || '').trim() || (tierName ? String(tierName) : id);
249
+ if (!id || !name) return;
250
+ const amount = it.amount || it.price || it.unitPrice || it.totalAmount || it.initialAmount;
251
+ const priceCents = (typeof amount === 'number' ? amount : parseInt(amount, 10)) || 0;
252
+ out.push({ id, name, priceCents: String(priceCents), location: '' });
231
253
  }
232
- const json = await resp.json();
233
- // API responses are usually { data: [...] } but keep it flexible
234
- const list = Array.isArray(json) ? json : (Array.isArray(json.data) ? json.data : []);
235
254
 
236
- // cache 15 minutes
237
- try {
238
- await db.setObject(cacheKey, { payload: JSON.stringify(list), expiresAt: String(now + 15 * 60 * 1000) });
239
- } catch (e) {}
255
+ // Try a few known layouts
256
+ for (const t of tierArr) {
257
+ const tierName = t && (t.name || t.label || t.title);
258
+ const products = (t && (t.items || t.products || t.prices || t.options)) || [];
259
+ const arr = Array.isArray(products) ? products : [];
260
+ if (arr.length) {
261
+ arr.forEach(p => pushItem(p, tierName));
262
+ } else {
263
+ // sometimes tier itself is the product
264
+ pushItem(t, tierName);
265
+ }
266
+ }
267
+
268
+ // Fallback: some forms expose items directly
269
+ if (!out.length && data && Array.isArray(data.items)) {
270
+ data.items.forEach(p => pushItem(p, ''));
271
+ }
272
+
273
+ await db.setObject(cacheKey, {
274
+ itemsJson: JSON.stringify(out),
275
+ expMs: String(Date.now() + 10 * 60 * 1000),
276
+ });
240
277
 
241
- return list;
278
+ return out;
242
279
  }
243
280
 
244
281
  function parseItems(itemsJson) {
@@ -816,7 +853,7 @@ async function handleHelloAssoTest(req, res) {
816
853
  }));
817
854
  hasSampleItems = sampleItems && sampleItems.length > 0;
818
855
  ok = true;
819
- message = `OK: token valide. Items récupérés: ${count}.`;
856
+ message = `OK: token valide. Catalogue récupéré via /public : ${count} item(s).`;
820
857
  } catch (e) {
821
858
  ok = false;
822
859
  message = (e && e.message) ? e.message : String(e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-equipment-calendar",
3
- "version": "0.8.3",
3
+ "version": "0.8.6",
4
4
  "description": "Equipment reservation calendar for NodeBB (FullCalendar, approvals, HelloAsso payments)",
5
5
  "main": "library.js",
6
6
  "scripts": {
package/plugin.json CHANGED
@@ -25,6 +25,6 @@
25
25
  "scripts": [
26
26
  "public/js/client.js"
27
27
  ],
28
- "version": "0.4.9",
28
+ "version": "0.5.0",
29
29
  "minver": "4.7.1"
30
30
  }