@axcode/axcloud-sdk 0.1.5 → 0.1.7-dev.3a3be6d

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/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, useContext, useMemo, useCallback, useRef } from 'react';
1
+ import { createContext, useContext, useMemo, useCallback, useState, useRef } from 'react';
2
2
  import { Auth0Provider, useAuth0 } from '@auth0/auth0-react';
3
3
  import { QueryClient, QueryClientProvider, useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
4
4
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
@@ -122,7 +122,7 @@ var AxCloudClient = class {
122
122
  */
123
123
  shouldAttachApiKey(path) {
124
124
  const normalized = path.startsWith("/") ? path : `/${path}`;
125
- return normalized.startsWith("/public/") || normalized.startsWith("/api/public/") || normalized.startsWith("/content/public/");
125
+ return normalized.startsWith("/public/") || normalized.startsWith("/api/public/") || normalized.startsWith("/content/public/") || normalized.startsWith("/media/public/") || normalized.startsWith("/documents/public/") || normalized.startsWith("/barnsport/public/") || normalized.startsWith("/api/barnsport/public/");
126
126
  }
127
127
  /**
128
128
  * Make an authenticated request
@@ -236,6 +236,88 @@ function getClient(key) {
236
236
  throw new Error("AxCloud client not found. Make sure AxCloudProvider is mounted and use useAxCloudContext() to get the client.");
237
237
  }
238
238
 
239
+ // src/core/contentSlots.ts
240
+ var SLOT_ATTRIBUTE_KEYS = ["slot", "field", "key", "name"];
241
+ function getContentSlotMap(content) {
242
+ const fields = isRecord(content?.fields) ? { ...content?.fields } : {};
243
+ for (const block of content?.blocks || []) {
244
+ const slot = getBlockSlot(block);
245
+ if (!slot) continue;
246
+ fields[slot] = blockToSlotValue(block);
247
+ }
248
+ return fields;
249
+ }
250
+ function createContentSlotReader(content) {
251
+ const fields = getContentSlotMap(content);
252
+ const value = (key, fallback) => {
253
+ return key in fields ? fields[key] : fallback;
254
+ };
255
+ return {
256
+ fields,
257
+ value,
258
+ text: (key, fallback = "") => stringifySlotValue(value(key, fallback)),
259
+ html: (key, fallback = "") => stringifySlotValue(value(key, fallback)),
260
+ list: (key, fallback = []) => slotValueToList(value(key, fallback)),
261
+ image: (key, fallback) => slotValueToImage(value(key, fallback))
262
+ };
263
+ }
264
+ function getBlockSlot(block) {
265
+ const attributes = block.attributes || {};
266
+ for (const key of SLOT_ATTRIBUTE_KEYS) {
267
+ const value = attributes[key];
268
+ if (typeof value === "string" && value.trim()) return value.trim();
269
+ }
270
+ return void 0;
271
+ }
272
+ function blockToSlotValue(block) {
273
+ if (block.attributes && "value" in block.attributes) {
274
+ return block.attributes.value;
275
+ }
276
+ if (block.type === "list") {
277
+ return Array.isArray(block.items) ? block.items : [];
278
+ }
279
+ if (block.type === "image" && block.url) {
280
+ return {
281
+ url: block.url,
282
+ alt: block.alt,
283
+ caption: block.caption
284
+ };
285
+ }
286
+ return block.content ?? "";
287
+ }
288
+ function stringifySlotValue(value) {
289
+ if (value === void 0 || value === null) return "";
290
+ if (typeof value === "string") return value;
291
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
292
+ if (Array.isArray(value)) return value.map((item) => stringifySlotValue(item)).filter(Boolean).join("\n");
293
+ return "";
294
+ }
295
+ function slotValueToList(value) {
296
+ if (Array.isArray(value)) {
297
+ return value.map((item) => stringifySlotValue(item)).filter(Boolean);
298
+ }
299
+ if (typeof value === "string") {
300
+ return value.split(/\r?\n/).map((item) => item.trim()).filter(Boolean);
301
+ }
302
+ return [];
303
+ }
304
+ function slotValueToImage(value) {
305
+ if (isRecord(value) && typeof value.url === "string") {
306
+ return {
307
+ url: value.url,
308
+ alt: typeof value.alt === "string" ? value.alt : void 0,
309
+ caption: typeof value.caption === "string" ? value.caption : void 0
310
+ };
311
+ }
312
+ if (typeof value === "string" && value) {
313
+ return { url: value };
314
+ }
315
+ return void 0;
316
+ }
317
+ function isRecord(value) {
318
+ return !!value && typeof value === "object" && !Array.isArray(value);
319
+ }
320
+
239
321
  // src/core/types.ts
240
322
  function userHasPermission(user, permission) {
241
323
  if (user.permissions && user.permissions.length > 0) {
@@ -349,6 +431,98 @@ function createFormHelper(config) {
349
431
  })
350
432
  };
351
433
  }
434
+
435
+ // src/core/voting.ts
436
+ function baseOf(votingApiUrl) {
437
+ return votingApiUrl.replace(/\/$/, "");
438
+ }
439
+ async function readJson(response) {
440
+ const json = await response.json().catch(() => ({}));
441
+ if (json && typeof json === "object" && "success" in json && "data" in json) {
442
+ return json.data;
443
+ }
444
+ return json;
445
+ }
446
+ async function ensureOk(response) {
447
+ if (!response.ok) {
448
+ const body = await response.json().catch(() => ({}));
449
+ const message = body && (body.error || body.message) || `Request failed with status ${response.status}`;
450
+ throw new Error(message);
451
+ }
452
+ }
453
+ async function checkVotingCode(votingApiUrl, params) {
454
+ const response = await fetch(
455
+ `${baseOf(votingApiUrl)}/public/organizations/${params.orgId}/polls/${params.pollId}/eligibility`,
456
+ {
457
+ method: "POST",
458
+ headers: { "Content-Type": "application/json" },
459
+ body: JSON.stringify({ code: params.code })
460
+ }
461
+ );
462
+ await ensureOk(response);
463
+ return readJson(response);
464
+ }
465
+ async function castVoteWithCode(votingApiUrl, params) {
466
+ const response = await fetch(
467
+ `${baseOf(votingApiUrl)}/public/organizations/${params.orgId}/polls/${params.pollId}/vote`,
468
+ {
469
+ method: "POST",
470
+ headers: { "Content-Type": "application/json" },
471
+ body: JSON.stringify({ code: params.code, selected_options: params.selectedOptions, rankings: params.rankings })
472
+ }
473
+ );
474
+ await ensureOk(response);
475
+ return readJson(response);
476
+ }
477
+ async function getVotingReceipt(votingApiUrl, params) {
478
+ const response = await fetch(
479
+ `${baseOf(votingApiUrl)}/public/organizations/${params.orgId}/polls/${params.pollId}/receipt`,
480
+ {
481
+ method: "POST",
482
+ headers: { "Content-Type": "application/json" },
483
+ body: JSON.stringify({ code: params.code })
484
+ }
485
+ );
486
+ await ensureOk(response);
487
+ return readJson(response);
488
+ }
489
+ function authHeaders(accessToken) {
490
+ return { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}` };
491
+ }
492
+ async function getBallot(votingApiUrl, params) {
493
+ const base = `${baseOf(votingApiUrl)}/organizations/${params.orgId}/polls/${params.pollId}`;
494
+ const pollResp = await fetch(base, { headers: authHeaders(params.accessToken) });
495
+ await ensureOk(pollResp);
496
+ const poll = await readJson(pollResp);
497
+ let candidates = [];
498
+ if (poll.poll_type === "election") {
499
+ const candResp = await fetch(`${base}/candidates`, { headers: authHeaders(params.accessToken) });
500
+ await ensureOk(candResp);
501
+ const data = await readJson(candResp);
502
+ candidates = data.candidates || [];
503
+ }
504
+ return { ...poll, candidates };
505
+ }
506
+ async function checkEligibility(votingApiUrl, params) {
507
+ const response = await fetch(
508
+ `${baseOf(votingApiUrl)}/organizations/${params.orgId}/polls/${params.pollId}/eligibility`,
509
+ { headers: authHeaders(params.accessToken) }
510
+ );
511
+ await ensureOk(response);
512
+ return readJson(response);
513
+ }
514
+ async function castVote(votingApiUrl, params) {
515
+ const response = await fetch(
516
+ `${baseOf(votingApiUrl)}/organizations/${params.orgId}/polls/${params.pollId}/vote`,
517
+ {
518
+ method: "POST",
519
+ headers: authHeaders(params.accessToken),
520
+ body: JSON.stringify({ selected_options: params.selectedOptions, rankings: params.rankings })
521
+ }
522
+ );
523
+ await ensureOk(response);
524
+ return readJson(response);
525
+ }
352
526
  var AxCloudContext = createContext(null);
353
527
  function useAxCloudContext() {
354
528
  const context = useContext(AxCloudContext);
@@ -659,6 +833,7 @@ function useNews(options = {}) {
659
833
  search,
660
834
  sortBy = "publishedAt",
661
835
  sortOrder = "desc",
836
+ lang,
662
837
  publicOnly = false
663
838
  } = options;
664
839
  const usePublicEndpoint = isPublicMode || publicOnly;
@@ -675,7 +850,8 @@ function useNews(options = {}) {
675
850
  sectionId,
676
851
  search,
677
852
  sortBy,
678
- sortOrder
853
+ sortOrder,
854
+ lang
679
855
  ],
680
856
  queryFn: async () => {
681
857
  const endpoint = usePublicEndpoint ? `/content/public/${orgId}/news` : `/content/members/${orgId}/news`;
@@ -687,7 +863,8 @@ function useNews(options = {}) {
687
863
  sectionId,
688
864
  search,
689
865
  sortBy,
690
- sortOrder
866
+ sortOrder,
867
+ lang
691
868
  });
692
869
  },
693
870
  staleTime: 1e3 * 60 * 5
@@ -789,6 +966,15 @@ function useContentItem(contentType, idOrSlug, options = {}) {
789
966
  refetch: query.refetch
790
967
  };
791
968
  }
969
+ function usePageContent(slug, options = {}) {
970
+ const state = useContentItem("pages", slug, options);
971
+ const reader = useMemo(() => createContentSlotReader(state.data), [state.data]);
972
+ return {
973
+ ...state,
974
+ page: state.data,
975
+ ...reader
976
+ };
977
+ }
792
978
  function useMySection(options = {}) {
793
979
  const { includeMembers = true } = options;
794
980
  const { isAuthenticated } = useAuth0();
@@ -907,6 +1093,76 @@ function useUpdateFormSubmission() {
907
1093
  }
908
1094
  });
909
1095
  }
1096
+ function useCodeBallot({ pollId, votingApiUrl }) {
1097
+ const { orgId, config } = useAxCloudContext();
1098
+ const baseUrl = votingApiUrl || `${config.apiUrl.replace(/\/$/, "")}/voting`;
1099
+ const [stage, setStage] = useState("enter_code");
1100
+ const [code, setCode] = useState("");
1101
+ const [ballot, setBallot] = useState(null);
1102
+ const [receipt, setReceipt] = useState(null);
1103
+ const [reason, setReason] = useState(void 0);
1104
+ const [loading, setLoading] = useState(false);
1105
+ const [error, setError] = useState(null);
1106
+ const validateCode = useCallback(
1107
+ async (inputCode) => {
1108
+ setLoading(true);
1109
+ setError(null);
1110
+ try {
1111
+ const result = await checkVotingCode(baseUrl, { orgId, pollId, code: inputCode });
1112
+ setCode(inputCode);
1113
+ setReason(result.reason);
1114
+ if (result.has_voted) {
1115
+ const r = await getVotingReceipt(baseUrl, { orgId, pollId, code: inputCode }).catch(() => null);
1116
+ setReceipt(r);
1117
+ setStage("already_voted");
1118
+ } else if (result.eligible && result.ballot) {
1119
+ setBallot(result.ballot);
1120
+ setStage("ballot");
1121
+ } else {
1122
+ setError(result.reason || "This code cannot be used to vote right now.");
1123
+ }
1124
+ return result;
1125
+ } catch (e) {
1126
+ setError(e instanceof Error ? e.message : "Failed to validate code");
1127
+ return null;
1128
+ } finally {
1129
+ setLoading(false);
1130
+ }
1131
+ },
1132
+ [baseUrl, orgId, pollId]
1133
+ );
1134
+ const submitVote = useCallback(
1135
+ async (selectedOptions, rankings) => {
1136
+ setLoading(true);
1137
+ setError(null);
1138
+ try {
1139
+ await castVoteWithCode(baseUrl, { orgId, pollId, code, selectedOptions, rankings });
1140
+ const r = await getVotingReceipt(baseUrl, { orgId, pollId, code }).catch(() => null);
1141
+ setReceipt(r);
1142
+ setStage("submitted");
1143
+ return true;
1144
+ } catch (e) {
1145
+ setError(e instanceof Error ? e.message : "Failed to submit vote");
1146
+ return false;
1147
+ } finally {
1148
+ setLoading(false);
1149
+ }
1150
+ },
1151
+ [baseUrl, orgId, pollId, code]
1152
+ );
1153
+ const reset = useCallback(() => {
1154
+ setStage("enter_code");
1155
+ setCode("");
1156
+ setBallot(null);
1157
+ setReceipt(null);
1158
+ setReason(void 0);
1159
+ setError(null);
1160
+ }, []);
1161
+ return useMemo(
1162
+ () => ({ stage, code, ballot, receipt, reason, loading, error, validateCode, submitVote, reset }),
1163
+ [stage, code, ballot, receipt, reason, loading, error, validateCode, submitVote, reset]
1164
+ );
1165
+ }
910
1166
  var LINKED_ACCOUNTS_QUERY_KEY = ["axcloud", "linked-accounts"];
911
1167
  function useLinkedAccounts() {
912
1168
  const { client, stage } = useAxCloudContext();
@@ -1065,6 +1321,160 @@ function useMedia(mediaId) {
1065
1321
  refetch: query.refetch
1066
1322
  };
1067
1323
  }
1324
+ function buildDocumentParams(options) {
1325
+ return {
1326
+ search: options?.search,
1327
+ tags: options?.tags?.join(","),
1328
+ limit: options?.limit
1329
+ };
1330
+ }
1331
+ function useDocuments(options) {
1332
+ const { client, orgId } = useAxCloudContext();
1333
+ const params = buildDocumentParams(options);
1334
+ const query = useQuery({
1335
+ queryKey: ["axcloud", "documents", "list", orgId, options],
1336
+ queryFn: async () => {
1337
+ return client.get(`/documents/public/${orgId}/documents`, params);
1338
+ },
1339
+ staleTime: 1e3 * 60 * 5
1340
+ });
1341
+ return {
1342
+ documents: query.data?.items ?? [],
1343
+ items: query.data?.items ?? [],
1344
+ count: query.data?.count ?? 0,
1345
+ isLoading: query.isLoading,
1346
+ isError: query.isError,
1347
+ error: query.error,
1348
+ refetch: query.refetch
1349
+ };
1350
+ }
1351
+ function useDocumentDownload(documentId) {
1352
+ const { client, orgId } = useAxCloudContext();
1353
+ const query = useQuery({
1354
+ queryKey: ["axcloud", "documents", "download", orgId, documentId],
1355
+ queryFn: async () => {
1356
+ return client.get(
1357
+ `/documents/public/${orgId}/documents/${encodeURIComponent(documentId || "")}/download`
1358
+ );
1359
+ },
1360
+ enabled: !!documentId,
1361
+ staleTime: 1e3 * 60 * 10,
1362
+ gcTime: 1e3 * 60 * 14
1363
+ });
1364
+ return {
1365
+ url: query.data?.url ?? null,
1366
+ documentUrl: query.data?.url ?? null,
1367
+ filename: query.data?.filename ?? null,
1368
+ mimeType: query.data?.mimeType ?? null,
1369
+ expiresIn: query.data?.expiresIn ?? null,
1370
+ isLoading: query.isLoading,
1371
+ isError: query.isError,
1372
+ error: query.error,
1373
+ refetch: query.refetch
1374
+ };
1375
+ }
1376
+ function useBarnsportMunicipalities() {
1377
+ const { client, orgId } = useAxCloudContext();
1378
+ const query = useQuery({
1379
+ queryKey: ["axcloud", "barnsport", "municipalities", orgId],
1380
+ queryFn: () => client.get(
1381
+ `/barnsport/public/${orgId}/municipalities`
1382
+ ),
1383
+ staleTime: 1e3 * 60 * 30
1384
+ });
1385
+ return {
1386
+ municipalities: query.data?.items ?? [],
1387
+ count: query.data?.count ?? 0,
1388
+ isLoading: query.isLoading,
1389
+ isError: query.isError,
1390
+ error: query.error,
1391
+ refetch: query.refetch
1392
+ };
1393
+ }
1394
+ function useBarnsportSports() {
1395
+ const { client, orgId } = useAxCloudContext();
1396
+ const query = useQuery({
1397
+ queryKey: ["axcloud", "barnsport", "sports", orgId],
1398
+ queryFn: () => client.get(
1399
+ `/barnsport/public/${orgId}/sports`
1400
+ ),
1401
+ staleTime: 1e3 * 60 * 30
1402
+ });
1403
+ return {
1404
+ sports: query.data?.items ?? [],
1405
+ count: query.data?.count ?? 0,
1406
+ isLoading: query.isLoading,
1407
+ isError: query.isError,
1408
+ error: query.error,
1409
+ refetch: query.refetch
1410
+ };
1411
+ }
1412
+ function useBarnsportActivities(filters = {}) {
1413
+ const { client, orgId } = useAxCloudContext();
1414
+ const params = { ...filters };
1415
+ const query = useQuery({
1416
+ queryKey: ["axcloud", "barnsport", "activities", orgId, filters],
1417
+ queryFn: () => client.get(
1418
+ `/barnsport/public/${orgId}/activities`,
1419
+ params
1420
+ ),
1421
+ staleTime: 1e3 * 60 * 5
1422
+ });
1423
+ return {
1424
+ activities: query.data?.items ?? [],
1425
+ count: query.data?.count ?? 0,
1426
+ nextCursor: query.data?.nextCursor ?? null,
1427
+ isLoading: query.isLoading,
1428
+ isError: query.isError,
1429
+ error: query.error,
1430
+ refetch: query.refetch
1431
+ };
1432
+ }
1433
+ function useBarnsportActivity(slug) {
1434
+ const { client, orgId } = useAxCloudContext();
1435
+ const query = useQuery({
1436
+ queryKey: ["axcloud", "barnsport", "activity", orgId, slug],
1437
+ queryFn: () => client.get(
1438
+ `/barnsport/public/${orgId}/activities/${slug || ""}`
1439
+ ),
1440
+ enabled: Boolean(slug),
1441
+ staleTime: 1e3 * 60 * 5
1442
+ });
1443
+ return {
1444
+ activity: query.data?.activity ?? null,
1445
+ isLoading: query.isLoading,
1446
+ isError: query.isError,
1447
+ error: query.error,
1448
+ refetch: query.refetch
1449
+ };
1450
+ }
1451
+ function useBarnsportSitemap(baseUrl) {
1452
+ const { client, orgId } = useAxCloudContext();
1453
+ const query = useQuery({
1454
+ queryKey: ["axcloud", "barnsport", "sitemap", orgId, baseUrl],
1455
+ queryFn: () => client.get(`/barnsport/public/${orgId}/sitemap`, {
1456
+ baseUrl
1457
+ }),
1458
+ staleTime: 1e3 * 60 * 15
1459
+ });
1460
+ return {
1461
+ sitemap: query.data ?? null,
1462
+ urls: query.data?.urls ?? [],
1463
+ isLoading: query.isLoading,
1464
+ isError: query.isError,
1465
+ error: query.error,
1466
+ refetch: query.refetch
1467
+ };
1468
+ }
1469
+ function useSubmitBarnsportInterest() {
1470
+ const { client, orgId } = useAxCloudContext();
1471
+ return useMutation({
1472
+ mutationFn: (submission) => client.post(
1473
+ `/barnsport/public/${orgId}/interest`,
1474
+ submission
1475
+ )
1476
+ });
1477
+ }
1068
1478
  function LoginButton({
1069
1479
  children = "Log in",
1070
1480
  returnTo,
@@ -1308,6 +1718,6 @@ function LinkedAccountsList({
1308
1718
  ] });
1309
1719
  }
1310
1720
 
1311
- export { AxCloudClient, AxCloudError, AxCloudProvider, LinkedAccountsList, LoginButton, LogoutButton, ModuleGate, ProtectedRoute, createClient, createFormHelper, getClient, isAxCloudError, submitForm, useAuth, useAvatar, useAxCloudContext, useBranding, useContent, useContentItem, useFormStats, useForms, useLinkedAccounts, useLogo, useMedia, useMediaList, useModuleAccess, useMySection, useNews, useOrg, useSections, useUpdateFormSubmission, useUser, userHasAllPermissions, userHasAnyPermission, userHasPermission };
1721
+ export { AxCloudClient, AxCloudError, AxCloudProvider, LinkedAccountsList, LoginButton, LogoutButton, ModuleGate, ProtectedRoute, castVote, castVoteWithCode, checkEligibility, checkVotingCode, createClient, createContentSlotReader, createFormHelper, getBallot, getClient, getContentSlotMap, getVotingReceipt, isAxCloudError, submitForm, useAuth, useAvatar, useAxCloudContext, useBarnsportActivities, useBarnsportActivity, useBarnsportMunicipalities, useBarnsportSitemap, useBarnsportSports, useBranding, useCodeBallot, useContent, useContentItem, useDocumentDownload, useDocuments, useFormStats, useForms, useLinkedAccounts, useLogo, useMedia, useMediaList, useModuleAccess, useMySection, useNews, useOrg, usePageContent, useSections, useSubmitBarnsportInterest, useUpdateFormSubmission, useUser, userHasAllPermissions, userHasAnyPermission, userHasPermission };
1312
1722
  //# sourceMappingURL=index.mjs.map
1313
1723
  //# sourceMappingURL=index.mjs.map