Setting the file. One moment.
Campaigns · Wix Headless Templates · wix/skills · Skills Docs
ContentsBack to the top of the page Next
Bundled file Donate
donations/rest/ campaigns.ts
TypeScript · 75 lines · 4 KB
11 import { campaignsQuery, currencyOf, isArchived, rawId, toDetail, toSummary, type Raw } from "../app/wix/donations/donations-core.js" ;
12 import type { CampaignDetail, CampaignSummary } from "../app/wix/donations/types.js" ;
13
14 const BASE = "/donation-campaigns/v2/donation-campaigns" ;
15
16 // A campaign nobody has donated to yet has no metrics entry, and the entity carries no currency code:
17 // the site's currency then comes from the eCommerce settings, read once.
18 // GET /_api/ecommerce-settings/v1/ecommerce-settings?fields=BUSINESS_INFO → { ecommerceSettings: { businessInfo: { currency } } }
19 let siteCurrencyPromise : Promise < string > | null = null ;
20 function siteCurrency () : Promise < string > {
21 siteCurrencyPromise ??= wixRequest < Raw >( "/_api/ecommerce-settings/v1/ecommerce-settings" , { method: "GET" , query: { fields: [ "BUSINESS_INFO" ] } })
22 . then (( res ) => String (res?.ecommerceSettings?.businessInfo?.currency ?? "" ))
23 . catch (() => { siteCurrencyPromise = null ; return "" ; });
24 return siteCurrencyPromise;
25 }
26
27 /** The campaign's currency: the metrics' when a donation exists, else the site's. */
28 async function currencyFor ( metrics : Raw [] | null ) : Promise < string > {
29 return currencyOf (metrics) || ( await siteCurrency ());
30 }
31
32 // GET /donation-campaigns/v2/donation-campaigns/{id}/metrics → { currencyMetricsList: [{ currencyCode, donationCount, totalAmount }] }
33 async function metricsOf ( campaignId : string ) : Promise < Raw [] | null > {
34 try {
35 const res = await wixRequest < Raw >( `${ BASE }/${ campaignId }/metrics` , { method: "GET" });
36 return (res?.currencyMetricsList ?? []) as Raw [];
37 } catch {
38 return null ; // unavailable is not a successfully-read zero total
39 }
40 }
41
42 /**
43 * The non-archived campaigns, oldest first, as card-ready DTOs.
44 * POST /donation-campaigns/v2/donation-campaigns/query { query: { filter: { archived: false }, sort: [{ fieldName: "createdDate", order: "ASC" }], cursorPaging: { limit } } }
45 */
46 export async function fetchCampaigns ({ limit = 100 } = {}) : Promise < CampaignSummary []> {
47 const res = await wixRequest < Raw >( `${ BASE }/query` , { body: { query: campaignsQuery ({ limit }) } });
48 const items = ((res?.donationCampaigns ?? []) as Raw []). filter (( raw ) => ! isArchived (raw));
49 const metrics = await Promise . all (items. map (( raw ) => (raw.campaignGoal ? metricsOf ( rawId (raw)) : Promise . resolve ([] as Raw []))));
50 const currencies = await Promise . all (metrics. map (currencyFor));
51 return items. map (( raw , i ) => toSummary (raw, metrics[i], currencies[i], imgSrc));
52 }
53
54 /**
55 * One campaign by id with its goal progress and form options; null when not found (404) or archived.
56 * GET /donation-campaigns/v2/donation-campaigns/{id} → { donationCampaign }
57 */
58 export async function fetchCampaign ( campaignId : string ) : Promise < CampaignDetail | null > {
59 let raw : Raw | undefined ;
60 try {
61 raw = ( await wixRequest < Raw >( `${ BASE }/${ campaignId }` , { method: "GET" }))?.donationCampaign;
62 } catch (e) {
63 if (e instanceof WixApiError && e.status === 404 ) return null ;
64 throw e;
65 }
66 if ( ! raw || isArchived (raw)) return null ; // an archived campaign is reachable by id but never shows a form
67 const metrics = raw.campaignGoal ? await metricsOf (campaignId) : [];
68 return toDetail (raw, metrics, await currencyFor (metrics), imgSrc);
69 }
70
71 /** The Wix widget's default: the first campaign by creation date. Null when the site has none. */
72 export async function fetchDefaultCampaign () : Promise < CampaignDetail | null > {
73 const [ first ] = await fetchCampaigns ({ limit: 1 });
74 return first ? fetchCampaign (first.id) : null ;
75 }