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.
- package/library.js +55 -18
- package/package.json +1 -1
- 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
|
-
|
|
220
|
+
|
|
221
|
+
// Cache 10 minutes
|
|
222
|
+
const cached = await db.getObject(cacheKey);
|
|
218
223
|
const now = Date.now();
|
|
219
|
-
if (
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
|
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
|
-
//
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
|
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.
|
|
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