@ixo/editor 6.31.0 → 6.32.0

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.
@@ -9181,7 +9181,7 @@ var ClaimListItem = ({ claim, onViewClaim }) => {
9181
9181
  };
9182
9182
  fetchUserProfile();
9183
9183
  }, [claim.agentDid]);
9184
- const claimStatus2 = getClaimStatusInfo(claim);
9184
+ const claimStatus = getClaimStatusInfo(claim);
9185
9185
  const displayName = userProfile?.displayname || (claim.agentAddress ? `${claim.agentAddress.slice(0, 12)}...` : "Unknown");
9186
9186
  return /* @__PURE__ */ React79.createElement(
9187
9187
  "div",
@@ -9197,7 +9197,7 @@ var ClaimListItem = ({ claim, onViewClaim }) => {
9197
9197
  tabIndex: 0,
9198
9198
  style: { cursor: "pointer" }
9199
9199
  },
9200
- /* @__PURE__ */ React79.createElement(ListItemContainer, { isChecked: false }, /* @__PURE__ */ React79.createElement(Stack48, { gap: 4, style: { flex: 1 } }, /* @__PURE__ */ React79.createElement(Text48, { size: "sm", fw: 500 }, "Claim #", claim.claimId), /* @__PURE__ */ React79.createElement(Text48, { size: "xs", c: "dimmed" }, "Submitted: ", formatDate(claim.submissionDate || claim.submittedAt)), claim.agentDid && /* @__PURE__ */ React79.createElement(Group22, { gap: 4 }, /* @__PURE__ */ React79.createElement(Text48, { size: "xs", c: "dimmed" }, "Agent: ", loadingProfile ? "Loading..." : displayName), userProfile?.verified && /* @__PURE__ */ React79.createElement(Text48, { size: "xs", c: "blue", fw: 600, title: "Verified user" }, "\u2713"))), /* @__PURE__ */ React79.createElement(Stack48, { gap: 4, align: "flex-end" }, /* @__PURE__ */ React79.createElement(Badge8, { color: claimStatus2.color, size: "sm" }, claimStatus2.label), /* @__PURE__ */ React79.createElement(ActionIcon13, { variant: "subtle", size: "sm" }, /* @__PURE__ */ React79.createElement(IconArrowRight2, { size: 16 }))))
9200
+ /* @__PURE__ */ React79.createElement(ListItemContainer, { isChecked: false }, /* @__PURE__ */ React79.createElement(Stack48, { gap: 4, style: { flex: 1 } }, /* @__PURE__ */ React79.createElement(Text48, { size: "sm", fw: 500 }, "Claim #", claim.claimId), /* @__PURE__ */ React79.createElement(Text48, { size: "xs", c: "dimmed" }, "Submitted: ", formatDate(claim.submissionDate || claim.submittedAt)), claim.agentDid && /* @__PURE__ */ React79.createElement(Group22, { gap: 4 }, /* @__PURE__ */ React79.createElement(Text48, { size: "xs", c: "dimmed" }, "Agent: ", loadingProfile ? "Loading..." : displayName), userProfile?.verified && /* @__PURE__ */ React79.createElement(Text48, { size: "xs", c: "blue", fw: 600, title: "Verified user" }, "\u2713"))), /* @__PURE__ */ React79.createElement(Stack48, { gap: 4, align: "flex-end" }, /* @__PURE__ */ React79.createElement(Badge8, { color: claimStatus.color, size: "sm" }, claimStatus.label), /* @__PURE__ */ React79.createElement(ActionIcon13, { variant: "subtle", size: "sm" }, /* @__PURE__ */ React79.createElement(IconArrowRight2, { size: 16 }))))
9201
9201
  );
9202
9202
  };
9203
9203
 
@@ -9683,8 +9683,8 @@ var ClaimsEvaluation = ({ collectionId, adminAddress, deedId, claim, onRefresh }
9683
9683
  }, [claim, handlers, deedId, collectionId, adminAddress, evaluationResult, onRefresh, editor, buildPaymentCoin]);
9684
9684
  const isClaimAlreadyEvaluated = useMemo39(() => {
9685
9685
  if (!claim) return false;
9686
- const claimStatus2 = getClaimStatus(claim);
9687
- return claimStatus2.status === "Approved" || claimStatus2.status === "Rejected";
9686
+ const claimStatus = getClaimStatus(claim);
9687
+ return claimStatus.status === "Approved" || claimStatus.status === "Rejected";
9688
9688
  }, [claim]);
9689
9689
  if (isClaimAlreadyEvaluated) {
9690
9690
  return /* @__PURE__ */ React86.createElement(Stack53, { style: { flex: 1 } }, /* @__PURE__ */ React86.createElement(BaseAlert, { isError: false, errMsg: "This claim has already been reviewed. No further action is required." }));
@@ -9911,9 +9911,9 @@ var ClaimsList2 = ({ items, isMultiSelect, isItemChecked, onItemCheck }) => {
9911
9911
  }
9912
9912
  const rows = items.map((item) => {
9913
9913
  const isChecked = isItemChecked?.(item.claimId);
9914
- const claimStatus2 = getClaimStatus(item);
9914
+ const claimStatus = getClaimStatus(item);
9915
9915
  const profile = item.agentDid ? profiles[item.agentDid] : null;
9916
- return /* @__PURE__ */ React90.createElement(ListItemContainer, { onClick: () => onItemCheck?.(item.claimId, !isChecked), key: item.claimId, isChecked: !!isChecked }, /* @__PURE__ */ React90.createElement(Flex25, { align: "center", gap: "sm" }, /* @__PURE__ */ React90.createElement(BaseAvatar, { src: profile?.avatarUrl, did: profile?.did, size: 34 }), /* @__PURE__ */ React90.createElement(Stack57, { gap: 0 }, /* @__PURE__ */ React90.createElement(Text57, { size: "sm", fw: 500 }, profile?.displayname || "Unknown User"), /* @__PURE__ */ React90.createElement(Text57, { size: "xs", c: "dimmed", lineClamp: 1 }, shortStr(item.agentAddress, 21, 5) || "No address"))), /* @__PURE__ */ React90.createElement(Flex25, { align: "center", gap: "md" }, /* @__PURE__ */ React90.createElement(Group25, { gap: 4 }, /* @__PURE__ */ React90.createElement(claimStatus2.icon, { color: claimStatus2.color, size: 18 }), /* @__PURE__ */ React90.createElement(Text57, { c: claimStatus2.color, size: "sm" }, claimStatus2.status)), isMultiSelect && /* @__PURE__ */ React90.createElement(ListItemCheckbox, { ariaLabel: `Select claim ${item.claimId}`, checked: isItemChecked?.(item.claimId), onCheck: (checked) => onItemCheck?.(item.claimId, checked) })));
9916
+ return /* @__PURE__ */ React90.createElement(ListItemContainer, { onClick: () => onItemCheck?.(item.claimId, !isChecked), key: item.claimId, isChecked: !!isChecked }, /* @__PURE__ */ React90.createElement(Flex25, { align: "center", gap: "sm" }, /* @__PURE__ */ React90.createElement(BaseAvatar, { src: profile?.avatarUrl, did: profile?.did, size: 34 }), /* @__PURE__ */ React90.createElement(Stack57, { gap: 0 }, /* @__PURE__ */ React90.createElement(Text57, { size: "sm", fw: 500 }, profile?.displayname || "Unknown User"), /* @__PURE__ */ React90.createElement(Text57, { size: "xs", c: "dimmed", lineClamp: 1 }, shortStr(item.agentAddress, 21, 5) || "No address"))), /* @__PURE__ */ React90.createElement(Flex25, { align: "center", gap: "md" }, /* @__PURE__ */ React90.createElement(Group25, { gap: 4 }, /* @__PURE__ */ React90.createElement(claimStatus.icon, { color: claimStatus.color, size: 18 }), /* @__PURE__ */ React90.createElement(Text57, { c: claimStatus.color, size: "sm" }, claimStatus.status)), isMultiSelect && /* @__PURE__ */ React90.createElement(ListItemCheckbox, { ariaLabel: `Select claim ${item.claimId}`, checked: isItemChecked?.(item.claimId), onCheck: (checked) => onItemCheck?.(item.claimId, checked) })));
9917
9917
  });
9918
9918
  return /* @__PURE__ */ React90.createElement(Box27, { flex: 1 }, /* @__PURE__ */ React90.createElement(Stack57, { gap: "xs" }, rows));
9919
9919
  };
@@ -9930,61 +9930,83 @@ var MatrixMembersList = ({ items }) => {
9930
9930
  };
9931
9931
 
9932
9932
  // src/core/utils/files.ts
9933
- function downloadArrayAsCsv(rows, options) {
9934
- const safeString = (value) => {
9935
- if (value === null || value === void 0) return "";
9936
- if (typeof value === "string") return value;
9937
- if (typeof value === "number" || typeof value === "boolean") return String(value);
9938
- try {
9939
- return JSON.stringify(value);
9940
- } catch {
9941
- return String(value);
9942
- }
9943
- };
9944
- const csvEscape = (field) => {
9945
- const needsQuotes = /[",\n\r]/.test(field) || field.includes(",");
9946
- const out = field.replace(/"/g, '""');
9947
- return needsQuotes ? `"${out}"` : out;
9948
- };
9949
- const isObjectRow = (r) => r !== null && typeof r === "object" && !Array.isArray(r);
9950
- let headers = [];
9951
- if (rows.length > 0 && isObjectRow(rows[0])) {
9952
- const keySet = /* @__PURE__ */ new Set();
9953
- for (const r of rows) {
9954
- if (isObjectRow(r)) {
9955
- for (const k of Object.keys(r)) keySet.add(k);
9956
- }
9933
+ var safeString = (value) => {
9934
+ if (value === null || value === void 0) return "";
9935
+ if (typeof value === "string") return value;
9936
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
9937
+ try {
9938
+ return JSON.stringify(value);
9939
+ } catch {
9940
+ return String(value);
9941
+ }
9942
+ };
9943
+ var csvEscape = (field) => {
9944
+ const needsQuotes = /[",\n\r]/.test(field);
9945
+ const out = field.replace(/"/g, '""');
9946
+ return needsQuotes ? `"${out}"` : out;
9947
+ };
9948
+ var isObjectRow = (r) => r !== null && typeof r === "object" && !Array.isArray(r);
9949
+ function unionHeaders(rows) {
9950
+ const keySet = /* @__PURE__ */ new Set();
9951
+ for (const r of rows) {
9952
+ if (isObjectRow(r)) {
9953
+ for (const k of Object.keys(r)) keySet.add(k);
9957
9954
  }
9958
- headers = Array.from(keySet);
9959
- } else {
9960
- headers = ["value"];
9961
9955
  }
9956
+ return Array.from(keySet);
9957
+ }
9958
+ function rowsToCsv(rows) {
9959
+ const headers = rows.length > 0 && isObjectRow(rows[0]) ? unionHeaders(rows) : ["value"];
9962
9960
  const csvLines = [];
9963
9961
  csvLines.push(headers.map((h) => csvEscape(h)).join(","));
9964
9962
  for (const row of rows) {
9965
9963
  if (isObjectRow(row)) {
9966
9964
  const obj = row;
9967
- const line = headers.map((h) => csvEscape(safeString(Object.prototype.hasOwnProperty.call(obj, h) ? obj[h] : ""))).join(",");
9968
- csvLines.push(line);
9965
+ csvLines.push(headers.map((h) => csvEscape(safeString(Object.prototype.hasOwnProperty.call(obj, h) ? obj[h] : ""))).join(","));
9969
9966
  } else {
9970
9967
  csvLines.push(csvEscape(safeString(row)));
9971
9968
  }
9972
9969
  }
9973
- const csvContent = csvLines.join("\r\n");
9974
- const bom = "\uFEFF";
9975
- const blob = new Blob([bom + csvContent], { type: "text/csv;charset=utf-8;" });
9970
+ return csvLines.join("\r\n");
9971
+ }
9972
+ function sectionsToCsv(sections) {
9973
+ const blocks = [];
9974
+ sections.forEach((section, index) => {
9975
+ const lines = [];
9976
+ if (index > 0) lines.push("");
9977
+ lines.push(csvEscape(section.title));
9978
+ if (section.rows.length === 0) {
9979
+ lines.push(csvEscape(section.emptyNote || "No records"));
9980
+ } else {
9981
+ lines.push(rowsToCsv(section.rows));
9982
+ }
9983
+ blocks.push(lines.join("\r\n"));
9984
+ });
9985
+ return blocks.join("\r\n");
9986
+ }
9987
+ var UTF8_BOM = "\uFEFF";
9988
+ function timestampSuffix(d = /* @__PURE__ */ new Date()) {
9989
+ const pad = (n) => String(n).padStart(2, "0");
9990
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
9991
+ }
9992
+ function downloadBlob(blob, filename) {
9976
9993
  const url = URL.createObjectURL(blob);
9977
9994
  const a = document.createElement("a");
9978
- const pad = (n) => String(n).padStart(2, "0");
9979
- const d = /* @__PURE__ */ new Date();
9980
- const defaultName = `list-export-${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}.csv`;
9981
9995
  a.href = url;
9982
- a.download = options?.filename || defaultName;
9996
+ a.download = filename;
9983
9997
  document.body.appendChild(a);
9984
9998
  a.click();
9985
9999
  document.body.removeChild(a);
9986
10000
  URL.revokeObjectURL(url);
9987
10001
  }
10002
+ function downloadArrayAsCsv(rows, options) {
10003
+ const blob = new Blob([UTF8_BOM + rowsToCsv(rows)], { type: "text/csv;charset=utf-8;" });
10004
+ downloadBlob(blob, options?.filename || `list-export-${timestampSuffix()}.csv`);
10005
+ }
10006
+ function downloadSectionedCsv(sections, options) {
10007
+ const blob = new Blob([UTF8_BOM + sectionsToCsv(sections)], { type: "text/csv;charset=utf-8;" });
10008
+ downloadBlob(blob, options?.filename || `list-export-${timestampSuffix()}.csv`);
10009
+ }
9988
10010
 
9989
10011
  // src/mantine/blocks/list/flow/ListFlowView.tsx
9990
10012
  import { useDisclosure as useDisclosure3 } from "@mantine/hooks";
@@ -31388,41 +31410,119 @@ function CollapsibleSection({ title, defaultOpen = false, badge, children }) {
31388
31410
  // src/mantine/blocks/action/actionTypes/_shared/dataExport/DownloadCsvButton.tsx
31389
31411
  import React311, { useCallback as useCallback105, useState as useState162 } from "react";
31390
31412
  import { ActionIcon as ActionIcon44 } from "@mantine/core";
31391
- import { IconDownload as IconDownload4 } from "@tabler/icons-react";
31392
- var DownloadCsvButton = ({ onDownload, disabled, label = "Download CSV" }) => {
31413
+ import { IconDownload as IconDownload4, IconFileZip } from "@tabler/icons-react";
31414
+ var DownloadIconButton = ({ onDownload, disabled, label, Icon, onError }) => {
31393
31415
  const [busy, setBusy] = useState162(false);
31394
31416
  const handleClick = useCallback105(async () => {
31395
31417
  setBusy(true);
31396
31418
  try {
31397
31419
  await onDownload();
31420
+ } catch (err) {
31421
+ const message = err instanceof Error ? err.message : "The download failed.";
31422
+ console.error(`[dataExport] ${label} failed`, err);
31423
+ onError?.(message);
31398
31424
  } finally {
31399
31425
  setBusy(false);
31400
31426
  }
31401
- }, [onDownload]);
31402
- return /* @__PURE__ */ React311.createElement(ActionIcon44, { variant: "subtle", color: "gray", size: "sm", "aria-label": label, title: label, loading: busy, disabled, onClick: handleClick }, /* @__PURE__ */ React311.createElement(IconDownload4, { size: 16 }));
31427
+ }, [onDownload, onError, label]);
31428
+ return /* @__PURE__ */ React311.createElement(ActionIcon44, { variant: "subtle", color: "gray", size: "sm", "aria-label": label, title: label, loading: busy, disabled, onClick: handleClick }, /* @__PURE__ */ React311.createElement(Icon, { size: 16 }));
31429
+ };
31430
+ var DownloadCsvButton = ({ onDownload, disabled, label = "Download CSV", onError }) => /* @__PURE__ */ React311.createElement(DownloadIconButton, { onDownload, disabled, label, Icon: IconDownload4, onError });
31431
+ var DownloadZipButton = ({ onDownload, disabled, label = "Download CSV + media (ZIP)", onError }) => /* @__PURE__ */ React311.createElement(DownloadIconButton, { onDownload, disabled, label, Icon: IconFileZip, onError });
31432
+
31433
+ // src/mantine/utils/bidStatus.ts
31434
+ var STATUS_META2 = {
31435
+ approved: { label: "Approved", color: "green", isTerminal: true },
31436
+ rejected: { label: "Rejected", color: "red", isTerminal: true },
31437
+ pending: { label: "Pending", color: "yellow", isTerminal: false }
31403
31438
  };
31439
+ function parseStatusKey(value) {
31440
+ const normalized = String(value ?? "").trim().toLowerCase();
31441
+ if (normalized === "approved" || normalized === "approve") return "approved";
31442
+ if (normalized === "rejected" || normalized === "reject") return "rejected";
31443
+ if (normalized === "pending") return "pending";
31444
+ return null;
31445
+ }
31446
+ function granteeRoleForBid(bid) {
31447
+ const role = String(bid?.role || "").toLowerCase();
31448
+ return role === "evaluation_agent" || role === "ea" ? "evaluate" : "submit";
31449
+ }
31450
+ function getBidStatusInfo(bid, context = {}) {
31451
+ const asInfo = (key, source) => ({ key, ...STATUS_META2[key], source });
31452
+ const session = context.evaluatedByBidId?.[bid?.id ?? ""];
31453
+ if (session) return asInfo(session, "session");
31454
+ const direct = parseStatusKey(bid?.status);
31455
+ if (direct) return asInfo(direct, "payload");
31456
+ try {
31457
+ const data = typeof bid?.data === "string" ? JSON.parse(bid.data) : bid?.data;
31458
+ const embedded = parseStatusKey(data?.status);
31459
+ if (embedded) return asInfo(embedded, "payload");
31460
+ } catch {
31461
+ }
31462
+ const grantees = context.grantees;
31463
+ if (grantees?.length) {
31464
+ const wanted = granteeRoleForBid(bid);
31465
+ if (grantees.some((grantee) => grantee.address === bid?.address && grantee.role === wanted)) {
31466
+ return asInfo("approved", "grant");
31467
+ }
31468
+ }
31469
+ return asInfo("pending", "default");
31470
+ }
31471
+ function describeGrantState(address, grantees) {
31472
+ const grants = grantees.filter((grantee) => grantee.address === address);
31473
+ if (grants.length === 0) return "none";
31474
+ return grants.map((grant) => `${grant.role} (quota ${grant.agentQuota ?? "\u2014"})`).join("; ");
31475
+ }
31476
+ var BID_GRANT_STATE_LABELS = {
31477
+ open: "Open \u2014 awaiting decision",
31478
+ granted: "Granted \u2014 active",
31479
+ granted_other_role: "Granted \u2014 different role",
31480
+ lapsed: "No active grant"
31481
+ };
31482
+ function classifyBidGrantState(bid, grantees, context = {}) {
31483
+ const decided = getBidStatusInfo(bid, { ...context, grantees: void 0 }).key !== "pending" || !!bid.decided;
31484
+ if (!decided) return "open";
31485
+ const wanted = granteeRoleForBid(bid);
31486
+ const onCollection = grantees.filter((grantee) => grantee.address === bid.address);
31487
+ if (onCollection.some((grantee) => grantee.role === wanted)) return "granted";
31488
+ if (onCollection.length > 0) return "granted_other_role";
31489
+ return "lapsed";
31490
+ }
31404
31491
 
31405
31492
  // src/mantine/blocks/action/actionTypes/_shared/dataExport/buildBidRows.ts
31406
- function buildBidRows(bids, grantees) {
31407
- return bids.map((bid) => {
31408
- const grants = grantees.filter((grantee) => grantee.address === bid.address);
31409
- const grantState = grants.length === 0 ? "none" : grants.map((grant) => `${grant.role} (quota ${grant.agentQuota ?? "\u2014"})`).join("; ");
31410
- let answers = {};
31411
- try {
31412
- const parsed = typeof bid.data === "string" ? JSON.parse(bid.data) : bid.data;
31413
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
31414
- answers = parsed;
31415
- }
31416
- } catch {
31417
- answers = { data: bid.data };
31493
+ function bidAnswers(bid) {
31494
+ try {
31495
+ const parsed = typeof bid.data === "string" ? JSON.parse(bid.data) : bid.data;
31496
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
31497
+ return parsed;
31418
31498
  }
31499
+ } catch {
31500
+ return { data: bid.data };
31501
+ }
31502
+ return {};
31503
+ }
31504
+ function buildBidRows(bids, grantees, options = {}) {
31505
+ const statusContext = { grantees, evaluatedByBidId: options.evaluatedByBidId };
31506
+ return bids.map((bid) => {
31507
+ const answers = options.answersByBidId?.[bid.id] ?? bidAnswers(bid);
31508
+ const status = getBidStatusInfo(bid, statusContext);
31509
+ const grantState = classifyBidGrantState(bid, grantees, statusContext);
31419
31510
  return {
31420
31511
  bidId: bid.id,
31512
+ name: options.namesByDid?.[bid.did] || "",
31421
31513
  did: bid.did,
31422
31514
  address: bid.address,
31423
31515
  role: bid.role,
31424
31516
  created: bid.created,
31425
- grantState,
31517
+ status: status.key,
31518
+ statusSource: status.source,
31519
+ // The column that answers "did anything come of this bid?". `lapsed`
31520
+ // means the account submitted, was decided, and holds no grant today —
31521
+ // a population that appears in neither the bids list nor the grant list.
31522
+ bidState: grantState,
31523
+ bidStateNote: BID_GRANT_STATE_LABELS[grantState],
31524
+ activeGrant: grantState === "granted" || grantState === "granted_other_role" ? "yes" : "no",
31525
+ grantDetail: describeGrantState(bid.address, grantees),
31426
31526
  ...answers
31427
31527
  };
31428
31528
  });
@@ -31444,30 +31544,489 @@ function buildGranteeRows(grantees, namesByDid = {}) {
31444
31544
  }
31445
31545
 
31446
31546
  // src/mantine/blocks/action/actionTypes/_shared/dataExport/buildClaimRows.ts
31447
- function claimStatus(claim) {
31448
- if (claim.approved) return "approved";
31449
- if (claim.rejected) return "rejected";
31450
- if (claim.disputed) return "disputed";
31451
- return "pending";
31547
+ function claimAnswers(contents) {
31548
+ const raw = contents;
31549
+ const subject = raw?.credentialSubject ?? raw;
31550
+ return subject && typeof subject === "object" && !Array.isArray(subject) ? subject : {};
31452
31551
  }
31453
- function buildClaimRows(claims, contentsByClaimId) {
31552
+ function buildClaimRows(claims, contentsByClaimId, options = {}) {
31454
31553
  return claims.map((claim) => {
31455
- const raw = contentsByClaimId[claim.claimId];
31456
- const subject = raw?.credentialSubject ?? raw;
31457
- const answers = subject && typeof subject === "object" && !Array.isArray(subject) ? subject : {};
31554
+ const answers = options.answersByClaimId?.[claim.claimId] ?? claimAnswers(contentsByClaimId[claim.claimId]);
31555
+ const status = getClaimStatusInfo(claim);
31458
31556
  return {
31459
31557
  claimId: claim.claimId,
31460
31558
  collectionId: claim.collectionId,
31461
31559
  agentDid: claim.agentDid,
31462
31560
  agentAddress: claim.agentAddress,
31463
31561
  submissionDate: claim.submissionDate,
31464
- status: claimStatus(claim),
31562
+ status: status.key,
31563
+ // A terminal decision outranks a dispute in `status`, but the dispute
31564
+ // record stays on chain — keep it visible rather than losing it.
31565
+ hasDispute: !!claim.disputeBySubjectId,
31465
31566
  schemaType: claim.schemaType ?? "",
31466
31567
  ...answers
31467
31568
  };
31468
31569
  });
31469
31570
  }
31470
31571
 
31572
+ // src/mantine/blocks/action/actionTypes/_shared/dataExport/buildCollectionMembersSections.ts
31573
+ var OPEN_ONLY_NOTE = "Open bids only \u2014 the bids service drops a bid from its query response once it is approved or rejected, and this host cannot read decided bids from the collection room.";
31574
+ var FULL_HISTORY_NOTE = "All bids, open and decided. See bidState: open = awaiting decision, granted = decided with a live grant, lapsed = decided with NO active grant.";
31575
+ function buildCollectionMembersSections(params) {
31576
+ const { grantees, bids, namesByDid = {}, collectionId, bidsTruncated, decidedBidsIncluded } = params;
31577
+ const label = collectionId ? ` (collection ${collectionId})` : "";
31578
+ const scopeNote = decidedBidsIncluded ? FULL_HISTORY_NOTE : OPEN_ONLY_NOTE;
31579
+ const truncationNote = bidsTruncated ? " WARNING: paging stopped early \u2014 this list is incomplete." : "";
31580
+ return [
31581
+ {
31582
+ title: `PART 1 \u2014 ACTIVE GRANTS${label}`,
31583
+ rows: buildGranteeRows(grantees, namesByDid),
31584
+ emptyNote: "No active grants on this collection."
31585
+ },
31586
+ {
31587
+ title: `PART 2 \u2014 ${decidedBidsIncluded ? "BIDS" : "OPEN BIDS"}${label} \u2014 ${scopeNote}${truncationNote}`,
31588
+ rows: buildBidRows(bids, grantees, { namesByDid, ...params.bidOptions }),
31589
+ emptyNote: decidedBidsIncluded ? "No bids have been submitted to this collection." : "No open bids on this collection."
31590
+ }
31591
+ ];
31592
+ }
31593
+
31594
+ // src/core/utils/zip.ts
31595
+ var MAX_ENTRIES = 65535;
31596
+ var MAX_ARCHIVE_BYTES = 4294967295;
31597
+ var CRC_TABLE = (() => {
31598
+ const table = new Uint32Array(256);
31599
+ for (let i = 0; i < 256; i += 1) {
31600
+ let c = i;
31601
+ for (let k = 0; k < 8; k += 1) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
31602
+ table[i] = c >>> 0;
31603
+ }
31604
+ return table;
31605
+ })();
31606
+ function crc32(bytes) {
31607
+ let c = 4294967295;
31608
+ for (let i = 0; i < bytes.length; i += 1) c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
31609
+ return (c ^ 4294967295) >>> 0;
31610
+ }
31611
+ function dosDateTime(date) {
31612
+ const year = Math.max(1980, date.getFullYear());
31613
+ return {
31614
+ time: (date.getHours() & 31) << 11 | (date.getMinutes() & 63) << 5 | date.getSeconds() >> 1 & 31,
31615
+ date: (year - 1980 & 127) << 9 | (date.getMonth() + 1 & 15) << 5 | date.getDate() & 31
31616
+ };
31617
+ }
31618
+ function toBytes(data, encoder) {
31619
+ return typeof data === "string" ? encoder.encode(data) : data;
31620
+ }
31621
+ function createZipBlob(entries, options) {
31622
+ if (entries.length > MAX_ENTRIES) {
31623
+ throw new Error(`ZIP archives are limited to ${MAX_ENTRIES} entries (got ${entries.length}).`);
31624
+ }
31625
+ const encoder = new TextEncoder();
31626
+ const { time, date } = dosDateTime(options?.modifiedAt ?? /* @__PURE__ */ new Date());
31627
+ const seen = /* @__PURE__ */ new Set();
31628
+ const prepared = entries.map((entry) => {
31629
+ const path = entry.path.replace(/^\/+/, "");
31630
+ if (seen.has(path)) throw new Error(`Duplicate ZIP entry path: ${path}`);
31631
+ seen.add(path);
31632
+ const nameBytes = encoder.encode(path);
31633
+ const dataBytes = toBytes(entry.data, encoder);
31634
+ return { nameBytes, dataBytes, crc: crc32(dataBytes), offset: 0 };
31635
+ });
31636
+ const localSize = prepared.reduce((sum, e) => sum + 30 + e.nameBytes.length + e.dataBytes.length, 0);
31637
+ const centralSize = prepared.reduce((sum, e) => sum + 46 + e.nameBytes.length, 0);
31638
+ const total = localSize + centralSize + 22;
31639
+ if (total > MAX_ARCHIVE_BYTES) {
31640
+ throw new Error(`Archive would be ${Math.round(total / 1024 / 1024)} MB, over the 4 GB ZIP limit. Export a smaller selection.`);
31641
+ }
31642
+ const buffer = new Uint8Array(total);
31643
+ const view = new DataView(buffer.buffer);
31644
+ let cursor = 0;
31645
+ const u16 = (value) => {
31646
+ view.setUint16(cursor, value, true);
31647
+ cursor += 2;
31648
+ };
31649
+ const u32 = (value) => {
31650
+ view.setUint32(cursor, value >>> 0, true);
31651
+ cursor += 4;
31652
+ };
31653
+ const raw = (bytes) => {
31654
+ buffer.set(bytes, cursor);
31655
+ cursor += bytes.length;
31656
+ };
31657
+ for (const entry of prepared) {
31658
+ entry.offset = cursor;
31659
+ u32(67324752);
31660
+ u16(20);
31661
+ u16(2048);
31662
+ u16(0);
31663
+ u16(time);
31664
+ u16(date);
31665
+ u32(entry.crc);
31666
+ u32(entry.dataBytes.length);
31667
+ u32(entry.dataBytes.length);
31668
+ u16(entry.nameBytes.length);
31669
+ u16(0);
31670
+ raw(entry.nameBytes);
31671
+ raw(entry.dataBytes);
31672
+ }
31673
+ const centralStart = cursor;
31674
+ for (const entry of prepared) {
31675
+ u32(33639248);
31676
+ u16(20);
31677
+ u16(20);
31678
+ u16(2048);
31679
+ u16(0);
31680
+ u16(time);
31681
+ u16(date);
31682
+ u32(entry.crc);
31683
+ u32(entry.dataBytes.length);
31684
+ u32(entry.dataBytes.length);
31685
+ u16(entry.nameBytes.length);
31686
+ u16(0);
31687
+ u16(0);
31688
+ u16(0);
31689
+ u16(0);
31690
+ u32(0);
31691
+ u32(entry.offset);
31692
+ raw(entry.nameBytes);
31693
+ }
31694
+ const centralEnd = cursor;
31695
+ u32(101010256);
31696
+ u16(0);
31697
+ u16(0);
31698
+ u16(prepared.length);
31699
+ u16(prepared.length);
31700
+ u32(centralEnd - centralStart);
31701
+ u32(centralStart);
31702
+ u16(0);
31703
+ return new Blob([buffer], { type: "application/zip" });
31704
+ }
31705
+
31706
+ // src/mantine/blocks/action/actionTypes/_shared/media/resolveClaimFileRef.ts
31707
+ var IPFS_GATEWAY = "https://ipfs.gateway.ixo.world/ipfs/";
31708
+ var CID_REGEX = /(baf[a-z0-9]{20,})/i;
31709
+ function extractCid(source) {
31710
+ const m = source.match(CID_REGEX);
31711
+ return m ? m[1] : null;
31712
+ }
31713
+ function resolveClaimFileRef(raw) {
31714
+ if (raw == null) return null;
31715
+ if (typeof raw === "string") {
31716
+ const trimmed = raw.trim();
31717
+ if (!trimmed) return null;
31718
+ if (/^https?:/i.test(trimmed)) return trimmed;
31719
+ if (trimmed.startsWith("data:")) return trimmed;
31720
+ if (/^ipfs:/i.test(trimmed)) {
31721
+ const cid = extractCid(trimmed.replace(/^ipfs:\/*/i, ""));
31722
+ return cid ? `${IPFS_GATEWAY}${cid}` : null;
31723
+ }
31724
+ if (trimmed.startsWith("{")) {
31725
+ try {
31726
+ return resolveClaimFileRef(JSON.parse(trimmed));
31727
+ } catch {
31728
+ return null;
31729
+ }
31730
+ }
31731
+ if (/^did:/i.test(trimmed)) {
31732
+ const cid = extractCid(trimmed);
31733
+ return cid ? `${IPFS_GATEWAY}${cid}` : null;
31734
+ }
31735
+ if (/^baf[a-z0-9]/i.test(trimmed) && trimmed.length < 200 && !trimmed.includes(" ")) {
31736
+ const cid = extractCid(trimmed);
31737
+ return cid ? `${IPFS_GATEWAY}${cid}` : null;
31738
+ }
31739
+ return null;
31740
+ }
31741
+ if (typeof raw === "object") {
31742
+ const obj = raw;
31743
+ if (typeof obj.serviceEndpoint === "string" && /^https?:/i.test(obj.serviceEndpoint)) {
31744
+ return obj.serviceEndpoint;
31745
+ }
31746
+ if (typeof obj.proof === "string") {
31747
+ const cid = extractCid(obj.proof);
31748
+ if (cid) return `${IPFS_GATEWAY}${cid}`;
31749
+ }
31750
+ if (typeof obj.id === "string") {
31751
+ const hashIdx = obj.id.indexOf("#");
31752
+ const tail = hashIdx >= 0 ? obj.id.slice(hashIdx + 1) : obj.id;
31753
+ const cid = extractCid(tail);
31754
+ if (cid) return `${IPFS_GATEWAY}${cid}`;
31755
+ }
31756
+ if (typeof obj.content === "string") {
31757
+ return resolveClaimFileRef(obj.content);
31758
+ }
31759
+ if (typeof obj.url === "string" && /^https?:/i.test(obj.url)) {
31760
+ return obj.url;
31761
+ }
31762
+ }
31763
+ return null;
31764
+ }
31765
+ function isFileLike(value) {
31766
+ if (Array.isArray(value)) {
31767
+ return value.length > 0 && value.every((v) => v && typeof v === "object" && typeof v.content === "string");
31768
+ }
31769
+ return !!value && typeof value === "object" && typeof value.content === "string";
31770
+ }
31771
+ function resolveOneFileEntry(entry) {
31772
+ const resolved = resolveClaimFileRef(entry.content);
31773
+ if (resolved && resolved !== entry.content) {
31774
+ return { ...entry, content: resolved };
31775
+ }
31776
+ return entry;
31777
+ }
31778
+ function resolveFilesInCredentialSubject(subject) {
31779
+ if (!subject || typeof subject !== "object") return subject;
31780
+ const out = Array.isArray(subject) ? [...subject] : { ...subject };
31781
+ for (const [key, value] of Object.entries(subject)) {
31782
+ if (!isFileLike(value)) continue;
31783
+ if (Array.isArray(value)) {
31784
+ out[key] = value.map((v) => resolveOneFileEntry(v));
31785
+ } else {
31786
+ out[key] = resolveOneFileEntry(value);
31787
+ }
31788
+ }
31789
+ return out;
31790
+ }
31791
+
31792
+ // src/mantine/blocks/action/actionTypes/_shared/media/mediaNaming.ts
31793
+ var MEDIA_DIR = "media";
31794
+ var MEDIA_SEPARATOR = "__";
31795
+ var SEQ_DIGITS = 4;
31796
+ var REF_MAX = 14;
31797
+ var FIELD_MAX = 32;
31798
+ var NAME_MAX = 48;
31799
+ var WINDOWS_RESERVED = /^(con|prn|aux|nul|com\d|lpt\d)$/i;
31800
+ function sanitizeSegment(value, maxLength) {
31801
+ const out = String(value ?? "").replace(/[^A-Za-z0-9.-]+/g, "-").replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, maxLength).replace(/[-.]+$/g, "");
31802
+ return out;
31803
+ }
31804
+ function splitExtension(fileName) {
31805
+ const dot = fileName.lastIndexOf(".");
31806
+ if (dot <= 0 || dot === fileName.length - 1) return { stem: fileName, ext: "" };
31807
+ const ext = fileName.slice(dot + 1);
31808
+ if (!/^[A-Za-z0-9]{1,5}$/.test(ext)) return { stem: fileName, ext: "" };
31809
+ return { stem: fileName.slice(0, dot), ext };
31810
+ }
31811
+ var MIME_EXTENSIONS = {
31812
+ "image/jpeg": "jpg",
31813
+ "image/jpg": "jpg",
31814
+ "image/png": "png",
31815
+ "image/gif": "gif",
31816
+ "image/webp": "webp",
31817
+ "image/svg+xml": "svg",
31818
+ "image/bmp": "bmp",
31819
+ "image/heic": "heic",
31820
+ "application/pdf": "pdf",
31821
+ "application/msword": "doc",
31822
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
31823
+ "application/vnd.ms-excel": "xls",
31824
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
31825
+ "text/plain": "txt",
31826
+ "text/csv": "csv",
31827
+ "video/mp4": "mp4",
31828
+ "video/webm": "webm",
31829
+ "video/quicktime": "mov",
31830
+ "audio/mpeg": "mp3",
31831
+ "audio/wav": "wav",
31832
+ "audio/ogg": "ogg"
31833
+ };
31834
+ function resolveExtension(originalName, mimeType) {
31835
+ const key = String(mimeType || "").split(";")[0].trim().toLowerCase();
31836
+ const fromMime = MIME_EXTENSIONS[key];
31837
+ if (fromMime) return fromMime;
31838
+ const fromName = splitExtension(originalName).ext;
31839
+ return fromName ? fromName.toLowerCase() : "bin";
31840
+ }
31841
+ function buildMediaFileName(parts) {
31842
+ const seq = String(Math.max(0, Math.trunc(parts.seq))).padStart(SEQ_DIGITS, "0");
31843
+ const ref = sanitizeSegment(parts.recordId, REF_MAX) || "record";
31844
+ const field = sanitizeSegment(parts.fieldName, FIELD_MAX) || "field";
31845
+ const idx = String(Math.max(1, Math.trunc(parts.fileIndex))).padStart(2, "0");
31846
+ const original = String(parts.originalName || "").trim();
31847
+ const { stem } = splitExtension(original);
31848
+ let name = sanitizeSegment(stem, NAME_MAX);
31849
+ if (!name || WINDOWS_RESERVED.test(name)) name = "file";
31850
+ const ext = resolveExtension(original, parts.mimeType);
31851
+ return [seq, ref, field, idx, name].join(MEDIA_SEPARATOR) + `.${ext}`;
31852
+ }
31853
+ function buildMediaPath(parts) {
31854
+ return `${MEDIA_DIR}/${buildMediaFileName(parts)}`;
31855
+ }
31856
+ function dedupeMediaPath(path, taken) {
31857
+ if (!taken.has(path)) {
31858
+ taken.add(path);
31859
+ return path;
31860
+ }
31861
+ const dot = path.lastIndexOf(".");
31862
+ const stem = dot > 0 ? path.slice(0, dot) : path;
31863
+ const ext = dot > 0 ? path.slice(dot) : "";
31864
+ for (let n = 2; n < 1e3; n += 1) {
31865
+ const candidate = `${stem}-${n}${ext}`;
31866
+ if (!taken.has(candidate)) {
31867
+ taken.add(candidate);
31868
+ return candidate;
31869
+ }
31870
+ }
31871
+ throw new Error(`Unable to find a unique archive path for ${path}`);
31872
+ }
31873
+
31874
+ // src/mantine/blocks/action/actionTypes/_shared/media/collectMedia.ts
31875
+ function isFileEntry(value) {
31876
+ return !!value && typeof value === "object" && typeof value.content === "string";
31877
+ }
31878
+ function fileEntriesOf(value) {
31879
+ if (Array.isArray(value)) return value.filter(isFileEntry);
31880
+ return isFileEntry(value) ? [value] : [];
31881
+ }
31882
+ function collectRecordMedia(answers, record, options = {}) {
31883
+ if (!answers || typeof answers !== "object") return [];
31884
+ const taken = options.takenPaths ?? /* @__PURE__ */ new Set();
31885
+ const titles = options.titlesByFieldName ?? {};
31886
+ const refs = [];
31887
+ for (const [fieldName, value] of Object.entries(answers)) {
31888
+ const entries = fileEntriesOf(value);
31889
+ if (entries.length === 0) continue;
31890
+ entries.forEach((entry, index) => {
31891
+ const sourceUrl = resolveClaimFileRef(entry.content);
31892
+ if (!sourceUrl) return;
31893
+ const originalName = typeof entry.name === "string" ? entry.name : "";
31894
+ const mimeType = typeof entry.type === "string" ? entry.type : "";
31895
+ const fileIndex = index + 1;
31896
+ const archivePath = dedupeMediaPath(buildMediaPath({ seq: record.seq, recordId: record.recordId, fieldName, fileIndex, originalName, mimeType }), taken);
31897
+ refs.push({
31898
+ seq: record.seq,
31899
+ recordId: record.recordId,
31900
+ fieldName,
31901
+ fieldTitle: titles[fieldName] || fieldName,
31902
+ fileIndex,
31903
+ originalName,
31904
+ mimeType,
31905
+ sourceUrl,
31906
+ archivePath
31907
+ });
31908
+ });
31909
+ }
31910
+ return refs;
31911
+ }
31912
+ function replaceFileValuesWithPaths(answers, refs, failedPaths) {
31913
+ if (refs.length === 0) return answers;
31914
+ const byField = /* @__PURE__ */ new Map();
31915
+ for (const ref of refs) {
31916
+ const list = byField.get(ref.fieldName) || [];
31917
+ list.push(failedPaths?.has(ref.archivePath) ? `${ref.originalName || ref.archivePath} (download failed \u2014 not in archive)` : ref.archivePath);
31918
+ byField.set(ref.fieldName, list);
31919
+ }
31920
+ const out = { ...answers };
31921
+ for (const [fieldName, paths] of byField) out[fieldName] = paths.join("; ");
31922
+ return out;
31923
+ }
31924
+
31925
+ // src/mantine/blocks/action/actionTypes/_shared/dataExport/buildExportBundle.ts
31926
+ var DEFAULT_CONCURRENCY = 6;
31927
+ async function mapWithLimit(items, limit, worker) {
31928
+ const results = new Array(items.length);
31929
+ let cursor = 0;
31930
+ const runners = Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, async () => {
31931
+ for (; ; ) {
31932
+ const index = cursor;
31933
+ cursor += 1;
31934
+ if (index >= items.length) return;
31935
+ results[index] = await worker(items[index], index);
31936
+ }
31937
+ });
31938
+ await Promise.all(runners);
31939
+ return results;
31940
+ }
31941
+ async function downloadMedia(ref, fetchMedia) {
31942
+ try {
31943
+ let blob;
31944
+ if (fetchMedia) {
31945
+ blob = await fetchMedia(ref.sourceUrl);
31946
+ } else {
31947
+ const response = await fetch(ref.sourceUrl);
31948
+ if (!response.ok) throw new Error(`gateway returned ${response.status} ${response.statusText}`.trim());
31949
+ blob = await response.blob();
31950
+ }
31951
+ return { bytes: new Uint8Array(await blob.arrayBuffer()), reason: "" };
31952
+ } catch (err) {
31953
+ return { bytes: null, reason: err instanceof Error ? err.message : "download failed" };
31954
+ }
31955
+ }
31956
+ async function buildExportBundle(params) {
31957
+ const takenPaths = /* @__PURE__ */ new Set();
31958
+ const refs = [];
31959
+ const refsByRecordId = /* @__PURE__ */ new Map();
31960
+ params.records.forEach((record, index) => {
31961
+ const recordRefs = collectRecordMedia(record.answers, { seq: index + 1, recordId: record.recordId }, { titlesByFieldName: params.titlesByFieldName, takenPaths });
31962
+ refs.push(...recordRefs);
31963
+ refsByRecordId.set(record.recordId, recordRefs);
31964
+ });
31965
+ const manifestByRecordId = new Map(params.records.map((record) => [record.recordId, record.manifest ?? {}]));
31966
+ const downloads = await mapWithLimit(refs, params.concurrency ?? DEFAULT_CONCURRENCY, (ref) => downloadMedia(ref, params.fetchMedia));
31967
+ const failedPaths = new Set(refs.filter((_, index) => !downloads[index].bytes).map((ref) => ref.archivePath));
31968
+ const answersByRecordId = {};
31969
+ for (const record of params.records) {
31970
+ answersByRecordId[record.recordId] = replaceFileValuesWithPaths(record.answers, refsByRecordId.get(record.recordId) ?? [], failedPaths);
31971
+ }
31972
+ const entries = [];
31973
+ const manifestRows = [];
31974
+ const failures = [];
31975
+ refs.forEach((ref, index) => {
31976
+ const { bytes, reason } = downloads[index];
31977
+ if (bytes) {
31978
+ entries.push({ path: ref.archivePath, data: bytes });
31979
+ } else {
31980
+ failures.push({ archivePath: ref.archivePath, sourceUrl: ref.sourceUrl, reason });
31981
+ }
31982
+ manifestRows.push({
31983
+ archivePath: bytes ? ref.archivePath : "",
31984
+ ...manifestByRecordId.get(ref.recordId),
31985
+ recordId: ref.recordId,
31986
+ fieldName: ref.fieldName,
31987
+ fieldTitle: ref.fieldTitle,
31988
+ fileIndex: ref.fileIndex,
31989
+ originalName: ref.originalName,
31990
+ mimeType: ref.mimeType,
31991
+ sizeBytes: bytes ? bytes.length : "",
31992
+ sourceUrl: ref.sourceUrl,
31993
+ downloadStatus: bytes ? "ok" : "failed",
31994
+ downloadError: reason
31995
+ });
31996
+ });
31997
+ entries.unshift({ path: params.csvName, data: UTF8_BOM + params.renderCsv(answersByRecordId) });
31998
+ entries.splice(1, 0, {
31999
+ path: params.manifestName || "media-manifest.csv",
32000
+ data: UTF8_BOM + (manifestRows.length > 0 ? rowsToCsv(manifestRows) : "note\r\nNo media was submitted for these records.")
32001
+ });
32002
+ return { blob: createZipBlob(entries), mediaTotal: refs.length, mediaFailed: failures.length, failures };
32003
+ }
32004
+
32005
+ // src/mantine/blocks/action/actionTypes/_shared/dataExport/exportFileNames.ts
32006
+ var slug = (value, fallback) => value ? String(value).replace(/[^A-Za-z0-9._-]+/g, "-") : fallback;
32007
+ var exportFileNames = {
32008
+ claims(collectionId) {
32009
+ const id = slug(collectionId, "collection");
32010
+ return { csv: `claims-${id}.csv`, manifest: `media-manifest-${id}.csv`, zip: `claims-${id}-${timestampSuffix()}.zip` };
32011
+ },
32012
+ claim(claimId) {
32013
+ const id = slug(claimId, "claim").slice(0, 20);
32014
+ return { csv: `claim-${id}.csv`, manifest: `media-manifest-${id}.csv`, zip: `claim-${id}-${timestampSuffix()}.zip` };
32015
+ },
32016
+ bids(collectionId) {
32017
+ const id = slug(collectionId, "collection");
32018
+ return { csv: `bids-${id}.csv`, manifest: `media-manifest-${id}.csv`, zip: `bids-${id}-${timestampSuffix()}.zip` };
32019
+ },
32020
+ bid(bidId) {
32021
+ const id = slug(bidId, "bid").slice(0, 20);
32022
+ return { csv: `bid-${id}.csv`, manifest: `media-manifest-${id}.csv`, zip: `bid-${id}-${timestampSuffix()}.zip` };
32023
+ },
32024
+ collectionMembers(collectionId) {
32025
+ const id = slug(collectionId, "collection");
32026
+ return { csv: `collection-members-${id}.csv`, manifest: `media-manifest-${id}.csv`, zip: `collection-members-${id}-${timestampSuffix()}.zip` };
32027
+ }
32028
+ };
32029
+
31471
32030
  // src/mantine/blocks/action/actionTypes/_shared/ReadableKeyValues.tsx
31472
32031
  import React313, { useState as useState163 } from "react";
31473
32032
  import { Group as Group110, Stack as Stack204, Text as Text192, UnstyledButton as UnstyledButton3 } from "@mantine/core";
@@ -31618,6 +32177,34 @@ var ReadableKeyValues = ({ data }) => {
31618
32177
  return /* @__PURE__ */ React313.createElement(React313.Fragment, null, /* @__PURE__ */ React313.createElement(Stack204, { gap: "xs" }, Object.entries(data).map(([key, value]) => /* @__PURE__ */ React313.createElement(Group110, { key, justify: "space-between", align: "flex-start", wrap: "nowrap", gap: "md" }, /* @__PURE__ */ React313.createElement(Text192, { size: "xs", c: "dimmed", style: { flexShrink: 0 } }, humanizeKey(key)), renderValue(value, setActiveFile)))), /* @__PURE__ */ React313.createElement(MediaPreviewModal, { file: activeFile, onClose: () => setActiveFile(null) }));
31619
32178
  };
31620
32179
 
32180
+ // src/mantine/blocks/action/actionTypes/_shared/bids/fetchAllBids.ts
32181
+ var MAX_PAGES = 200;
32182
+ async function fetchAllBids(queryBids, params, options) {
32183
+ const maxPages = options?.maxPages ?? MAX_PAGES;
32184
+ const bids = [];
32185
+ const seenTokens = /* @__PURE__ */ new Set();
32186
+ let nextPageToken;
32187
+ let pages = 0;
32188
+ for (; ; ) {
32189
+ const response = await queryBids({ ...params, ...nextPageToken ? { pagination: { nextPageToken } } : {} });
32190
+ pages += 1;
32191
+ if (Array.isArray(response?.data)) bids.push(...response.data);
32192
+ nextPageToken = response?.nextPageToken;
32193
+ if (!nextPageToken) return { bids, truncated: false, pages };
32194
+ if (seenTokens.has(nextPageToken)) return { bids, truncated: true, pages };
32195
+ seenTokens.add(nextPageToken);
32196
+ if (pages >= maxPages) return { bids, truncated: true, pages };
32197
+ }
32198
+ }
32199
+ function dedupeBids(bids) {
32200
+ const seen = /* @__PURE__ */ new Set();
32201
+ return bids.filter((bid) => {
32202
+ if (seen.has(bid.id)) return false;
32203
+ seen.add(bid.id);
32204
+ return true;
32205
+ });
32206
+ }
32207
+
31621
32208
  // src/mantine/blocks/action/actionTypes/evaluateBid/EvaluateBidFlowDetail.tsx
31622
32209
  function getRoleColor2(role) {
31623
32210
  const r = String(role || "").toLowerCase();
@@ -31665,14 +32252,14 @@ function truncateAddress2(address) {
31665
32252
  if (address.length <= 14) return address;
31666
32253
  return `${address.slice(0, 8)}...${address.slice(-3)}`;
31667
32254
  }
31668
- function getBidStatus(bid, translate) {
31669
- try {
31670
- const data = typeof bid.data === "string" ? JSON.parse(bid.data) : bid.data;
31671
- if (data?.status === "approved") return { label: translate("actionTypes.evaluateBid.flow.status.approved", { defaultValue: "Approved" }), key: "approved", color: "green" };
31672
- if (data?.status === "rejected") return { label: translate("actionTypes.evaluateBid.flow.status.rejected", { defaultValue: "Rejected" }), key: "rejected", color: "red" };
31673
- } catch {
31674
- }
31675
- return { label: translate("actionTypes.evaluateBid.flow.status.pending", { defaultValue: "Pending" }), key: "pending", color: "yellow" };
32255
+ function getBidStatus(bid, translate, context) {
32256
+ const info = getBidStatusInfo(bid, context);
32257
+ const labels = {
32258
+ approved: translate("actionTypes.evaluateBid.flow.status.approved", { defaultValue: "Approved" }),
32259
+ rejected: translate("actionTypes.evaluateBid.flow.status.rejected", { defaultValue: "Rejected" }),
32260
+ pending: translate("actionTypes.evaluateBid.flow.status.pending", { defaultValue: "Pending" })
32261
+ };
32262
+ return { label: labels[info.key], key: info.key, color: info.color };
31676
32263
  }
31677
32264
  var EvaluateBidFlowDetail = ({
31678
32265
  inputs,
@@ -31709,33 +32296,56 @@ var EvaluateBidFlowDetail = ({
31709
32296
  const [activeFilter, setActiveFilter] = useState164("pending");
31710
32297
  const [paymentRows, setPaymentRows] = useState164([createPaymentRow()]);
31711
32298
  const [profilesByDid, setProfilesByDid] = useState164({});
32299
+ const [grantees, setGrantees] = useState164([]);
31712
32300
  const selectedBid = useMemo114(() => bids.find((bid) => bid.id === selectedBidId) || null, [bids, selectedBidId]);
32301
+ const statusContext = useMemo114(() => ({ grantees }), [grantees]);
32302
+ const statusCounts = useMemo114(() => {
32303
+ const counts = { approved: 0, rejected: 0, pending: 0 };
32304
+ for (const bid of bids) counts[getBidStatusInfo(bid, statusContext).key] += 1;
32305
+ return counts;
32306
+ }, [bids, statusContext]);
31713
32307
  const filteredBids = useMemo114(() => {
31714
32308
  if (activeFilter === "all") return bids;
31715
- return bids.filter((bid) => getBidStatus(bid, t).key === activeFilter);
31716
- }, [bids, activeFilter]);
32309
+ return bids.filter((bid) => getBidStatus(bid, t, statusContext).key === activeFilter);
32310
+ }, [bids, activeFilter, t, statusContext]);
32311
+ const fetchGrantees = useCallback106(
32312
+ async (adminAddressForList) => {
32313
+ const listGrantees = handlersRef.current.collectionUsers?.list;
32314
+ if (typeof listGrantees !== "function" || !adminAddressForList) return [];
32315
+ try {
32316
+ const result = await listGrantees({ granterAdminAddress: adminAddressForList, collectionId });
32317
+ return Array.isArray(result?.grantees) ? result.grantees : [];
32318
+ } catch {
32319
+ return [];
32320
+ }
32321
+ },
32322
+ [collectionId]
32323
+ );
31717
32324
  const refreshBids = useCallback106(async () => {
31718
32325
  if (!deedDid || !collectionId) return;
31719
32326
  const handlers2 = handlersRef.current;
31720
32327
  setLoadingBids(true);
31721
32328
  setError(null);
31722
32329
  try {
31723
- const [bidsResponse, collectionsResponse] = await Promise.all([handlers2.queryBids({ collectionId, entityDid: deedDid }), handlers2.getClaimCollections(deedDid)]);
31724
- const fetchedBids = Array.isArray(bidsResponse?.data) ? bidsResponse.data : [];
32330
+ const [bidsResult, collectionsResponse] = await Promise.all([fetchAllBids(handlers2.queryBids, { collectionId, entityDid: deedDid }), handlers2.getClaimCollections(deedDid)]);
32331
+ const fetchedBids = dedupeBids(bidsResult.bids);
31725
32332
  setBids(fetchedBids);
32333
+ if (bidsResult.truncated) setError("Bid paging stopped early \u2014 this list may be incomplete.");
31726
32334
  if (selectedBidId && !fetchedBids.some((bid) => bid.id === selectedBidId)) {
31727
32335
  setSelectedBidId("");
31728
32336
  }
31729
32337
  const resolvedAdmin = collectionsResponse?.collections?.find?.((c) => c?.id === collectionId)?.admin || collectionsResponse?.adminAddress || "";
31730
32338
  setAdminAddress(resolvedAdmin);
32339
+ setGrantees(await fetchGrantees(resolvedAdmin));
31731
32340
  } catch (err) {
31732
32341
  setBids([]);
31733
32342
  setAdminAddress("");
32343
+ setGrantees([]);
31734
32344
  setError(err instanceof Error ? err.message : t("actionTypes.evaluateBid.flow.errors.loadBids", { defaultValue: "Failed to load bids" }));
31735
32345
  } finally {
31736
32346
  setLoadingBids(false);
31737
32347
  }
31738
- }, [deedDid, collectionId, selectedBidId]);
32348
+ }, [deedDid, collectionId, selectedBidId, fetchGrantees]);
31739
32349
  useEffect109(() => {
31740
32350
  setSelectedBidId("");
31741
32351
  setRejectReason("");
@@ -31750,23 +32360,52 @@ var EvaluateBidFlowDetail = ({
31750
32360
  if (!deedDid || !collectionId) return;
31751
32361
  refreshBids();
31752
32362
  }, [deedDid, collectionId, refreshBids]);
32363
+ const bidRowOptions = useCallback106(
32364
+ (freshGrantees) => ({
32365
+ namesByDid: Object.fromEntries(Object.entries(profilesByDid).map(([did, profile]) => [did, profile?.displayname || ""])),
32366
+ grantees: freshGrantees
32367
+ }),
32368
+ [profilesByDid]
32369
+ );
31753
32370
  const downloadBidsCsv = useCallback106(
31754
32371
  async (subset, filename) => {
31755
- let grantees = [];
31756
- const listGrantees = handlersRef.current.collectionUsers?.list;
31757
- if (typeof listGrantees === "function" && adminAddress) {
31758
- try {
31759
- const result = await listGrantees({ granterAdminAddress: adminAddress, collectionId });
31760
- grantees = Array.isArray(result?.grantees) ? result.grantees : [];
31761
- } catch {
31762
- grantees = [];
31763
- }
32372
+ const freshGrantees = await fetchGrantees(adminAddress) || grantees;
32373
+ downloadArrayAsCsv(buildBidRows(subset, freshGrantees, bidRowOptions(freshGrantees)), { filename });
32374
+ },
32375
+ [adminAddress, fetchGrantees, grantees, bidRowOptions]
32376
+ );
32377
+ const downloadBidsZip = useCallback106(
32378
+ async (subset, names) => {
32379
+ const handlers2 = handlersRef.current;
32380
+ const freshGrantees = await fetchGrantees(adminAddress) || grantees;
32381
+ const fetchMedia = handlers2.fetchClaimMedia ? (url) => handlers2.fetchClaimMedia(url, { entityDid: deedDid }) : void 0;
32382
+ const { blob, mediaTotal, mediaFailed } = await buildExportBundle({
32383
+ records: subset.map((bid) => ({
32384
+ recordId: bid.id,
32385
+ answers: bidAnswers(bid),
32386
+ manifest: {
32387
+ bidId: bid.id,
32388
+ did: bid.did,
32389
+ address: bid.address,
32390
+ role: bid.role,
32391
+ created: bid.created,
32392
+ status: getBidStatusInfo(bid, { grantees: freshGrantees }).key
32393
+ }
32394
+ })),
32395
+ fetchMedia,
32396
+ renderCsv: (answersByBidId) => rowsToCsv(buildBidRows(subset, freshGrantees, { ...bidRowOptions(freshGrantees), answersByBidId })),
32397
+ csvName: names.csv,
32398
+ manifestName: names.manifest
32399
+ });
32400
+ downloadBlob(blob, names.zip);
32401
+ if (mediaFailed > 0) {
32402
+ setError(`${mediaFailed} of ${mediaTotal} media files could not be downloaded \u2014 see downloadStatus in ${names.manifest}.`);
31764
32403
  }
31765
- downloadArrayAsCsv(buildBidRows(subset, grantees), { filename });
31766
32404
  },
31767
- [adminAddress, collectionId]
32405
+ [adminAddress, fetchGrantees, grantees, bidRowOptions, deedDid]
31768
32406
  );
31769
- const handleDownloadBidsCsv = useCallback106(() => downloadBidsCsv(bids, `bids-${collectionId || "collection"}.csv`), [downloadBidsCsv, bids, collectionId]);
32407
+ const handleDownloadBidsCsv = useCallback106(() => downloadBidsCsv(bids, exportFileNames.bids(collectionId).csv), [downloadBidsCsv, bids, collectionId]);
32408
+ const handleDownloadBidsZip = useCallback106(() => downloadBidsZip(bids, exportFileNames.bids(collectionId)), [downloadBidsZip, bids, collectionId]);
31770
32409
  useEffect109(() => {
31771
32410
  let mounted = true;
31772
32411
  const dids = Array.from(new Set(bids.map((bid) => bid.did).filter(Boolean)));
@@ -31896,7 +32535,7 @@ var EvaluateBidFlowDetail = ({
31896
32535
  });
31897
32536
  }, [provideSigningHandler, selectedBid, decision, adminAddress, rejectReason, collectionId, deedDid, buildMaxAmounts2, refreshBids, executeAction]);
31898
32537
  if (selectedBid) {
31899
- const bidStatus = getBidStatus(selectedBid, t);
32538
+ const bidStatus = getBidStatus(selectedBid, t, statusContext);
31900
32539
  const bidRole = String(selectedBid.role || "").toLowerCase();
31901
32540
  const bidIsEvaluator = bidRole === "evaluation_agent" || bidRole === "ea";
31902
32541
  const selectedBidProfile = profilesByDid[selectedBid.did];
@@ -31907,7 +32546,7 @@ var EvaluateBidFlowDetail = ({
31907
32546
  bidData = typeof selectedBid.data === "string" ? JSON.parse(selectedBid.data) : selectedBid.data;
31908
32547
  } catch {
31909
32548
  }
31910
- return /* @__PURE__ */ React314.createElement(Stack205, { gap: "md" }, /* @__PURE__ */ React314.createElement(Group111, { gap: "xs", align: "center" }, /* @__PURE__ */ React314.createElement(ActionIcon45, { variant: "subtle", color: "gray", size: "sm", onClick: () => setSelectedBidId("") }, /* @__PURE__ */ React314.createElement(IconArrowLeft8, { size: 16 })), /* @__PURE__ */ React314.createElement(Text193, { fw: 500, size: "sm", truncate: true, style: { flex: 1, minWidth: 0 } }, t("actionTypes.evaluateBid.flow.bidNumber", { id: selectedBid.id, defaultValue: `Bid #${selectedBid.id}` })), /* @__PURE__ */ React314.createElement(DownloadCsvButton, { onDownload: () => downloadBidsCsv([selectedBid], `bid-${selectedBid.id}.csv`), label: "Download bid CSV" })), /* @__PURE__ */ React314.createElement(Group111, { gap: 16, align: "center", style: { width: "100%" } }, /* @__PURE__ */ React314.createElement(
32549
+ return /* @__PURE__ */ React314.createElement(Stack205, { gap: "md" }, /* @__PURE__ */ React314.createElement(Group111, { gap: "xs", align: "center" }, /* @__PURE__ */ React314.createElement(ActionIcon45, { variant: "subtle", color: "gray", size: "sm", onClick: () => setSelectedBidId("") }, /* @__PURE__ */ React314.createElement(IconArrowLeft8, { size: 16 })), /* @__PURE__ */ React314.createElement(Text193, { fw: 500, size: "sm", truncate: true, style: { flex: 1, minWidth: 0 } }, t("actionTypes.evaluateBid.flow.bidNumber", { id: selectedBid.id, defaultValue: `Bid #${selectedBid.id}` })), /* @__PURE__ */ React314.createElement(DownloadCsvButton, { onDownload: () => downloadBidsCsv([selectedBid], exportFileNames.bid(selectedBid.id).csv), label: "Download bid CSV", onError: setError }), /* @__PURE__ */ React314.createElement(DownloadZipButton, { onDownload: () => downloadBidsZip([selectedBid], exportFileNames.bid(selectedBid.id)), label: "Download bid + media (ZIP)", onError: setError })), /* @__PURE__ */ React314.createElement(Group111, { gap: 16, align: "center", style: { width: "100%" } }, /* @__PURE__ */ React314.createElement(
31911
32550
  Box57,
31912
32551
  {
31913
32552
  style: {
@@ -31982,7 +32621,12 @@ var EvaluateBidFlowDetail = ({
31982
32621
  }
31983
32622
  ))), /* @__PURE__ */ React314.createElement(Button62, { variant: "light", size: "xs", onClick: addPaymentRow, disabled: isDisabled || submitting }, t("actionTypes.evaluateClaim.flow.payment.addPayment", { defaultValue: "Add Payment" }))))), error && /* @__PURE__ */ React314.createElement(DismissibleAlert, { color: "red", styles: actionAlertStyles }, error));
31984
32623
  }
31985
- const filterTabs = [{ value: "pending", label: t("actionTypes.evaluateBid.flow.status.pending", { defaultValue: "Pending" }) }];
32624
+ const filterTabs = [
32625
+ { value: "pending", label: t("actionTypes.evaluateBid.flow.status.pending", { defaultValue: "Pending" }) },
32626
+ ...statusCounts.approved > 0 ? [{ value: "approved", label: t("actionTypes.evaluateBid.flow.status.approved", { defaultValue: "Approved" }) }] : [],
32627
+ ...statusCounts.rejected > 0 ? [{ value: "rejected", label: t("actionTypes.evaluateBid.flow.status.rejected", { defaultValue: "Rejected" }) }] : [],
32628
+ ...statusCounts.approved > 0 || statusCounts.rejected > 0 ? [{ value: "all", label: t("actionTypes.evaluateBid.flow.status.all", { defaultValue: "All" }) }] : []
32629
+ ];
31986
32630
  return /* @__PURE__ */ React314.createElement(Stack205, { gap: "md" }, /* @__PURE__ */ React314.createElement(GroupProposalStatus, { editor, block, runtime }), runtime?.state !== "awaiting_readback" && /* @__PURE__ */ React314.createElement(ActAsGroupSection, { value: actAs, onChange: setActAs, isDisabled, actionLabel: "decide this application" }), !deedDid || !collectionId ? /* @__PURE__ */ React314.createElement(DismissibleAlert, { color: "yellow", styles: actionAlertStyles }, t("actionTypes.shared.errors.configRequired", {
31987
32631
  defaultValue: "Configure {{did}} and {{claimCollection}} in template mode before running this action.",
31988
32632
  did: "DID",
@@ -32001,8 +32645,8 @@ var EvaluateBidFlowDetail = ({
32001
32645
  }
32002
32646
  },
32003
32647
  /* @__PURE__ */ React314.createElement(Text193, { size: "sm", fw: 500, c: activeFilter === tab.value ? "var(--mantine-color-text)" : "dimmed" }, tab.label)
32004
- ))), /* @__PURE__ */ React314.createElement(Group111, { gap: 4 }, /* @__PURE__ */ React314.createElement(DownloadCsvButton, { onDownload: handleDownloadBidsCsv, disabled: loadingBids || bids.length === 0, label: "Download bids CSV" }), /* @__PURE__ */ React314.createElement(ActionIcon45, { variant: "subtle", color: "gray", size: "sm" }, /* @__PURE__ */ React314.createElement(IconFilter, { size: 16 })))), loadingBids && /* @__PURE__ */ React314.createElement(Group111, { gap: "xs", justify: "center", py: "md" }, /* @__PURE__ */ React314.createElement(Loader55, { size: "xs" }), /* @__PURE__ */ React314.createElement(Text193, { size: "xs", c: "dimmed" }, t("actionTypes.evaluateBid.flow.loadingBids", { defaultValue: "Loading bids..." }))), !loadingBids && filteredBids.length === 0 && /* @__PURE__ */ React314.createElement(Text193, { size: "sm", c: "dimmed", ta: "center", py: "md" }, bids.length === 0 ? t("actionTypes.evaluateBid.flow.empty.allCollections", { defaultValue: "No bids available for this collection." }) : t("actionTypes.evaluateBid.flow.empty.filtered", { filter: activeFilter, defaultValue: `No ${activeFilter} bids found.` })), filteredBids.length > 0 && /* @__PURE__ */ React314.createElement(Stack205, { gap: 12 }, filteredBids.map((bid) => {
32005
- const status = getBidStatus(bid, t);
32648
+ ))), /* @__PURE__ */ React314.createElement(Group111, { gap: 4 }, /* @__PURE__ */ React314.createElement(DownloadCsvButton, { onDownload: handleDownloadBidsCsv, disabled: loadingBids || bids.length === 0, label: "Download bids CSV", onError: setError }), /* @__PURE__ */ React314.createElement(DownloadZipButton, { onDownload: handleDownloadBidsZip, disabled: loadingBids || bids.length === 0, label: "Download bids + media (ZIP)", onError: setError }), /* @__PURE__ */ React314.createElement(ActionIcon45, { variant: "subtle", color: "gray", size: "sm" }, /* @__PURE__ */ React314.createElement(IconFilter, { size: 16 })))), loadingBids && /* @__PURE__ */ React314.createElement(Group111, { gap: "xs", justify: "center", py: "md" }, /* @__PURE__ */ React314.createElement(Loader55, { size: "xs" }), /* @__PURE__ */ React314.createElement(Text193, { size: "xs", c: "dimmed" }, t("actionTypes.evaluateBid.flow.loadingBids", { defaultValue: "Loading bids..." }))), !loadingBids && filteredBids.length === 0 && /* @__PURE__ */ React314.createElement(Text193, { size: "sm", c: "dimmed", ta: "center", py: "md" }, bids.length === 0 ? t("actionTypes.evaluateBid.flow.empty.allCollections", { defaultValue: "No bids available for this collection." }) : t("actionTypes.evaluateBid.flow.empty.filtered", { filter: activeFilter, defaultValue: `No ${activeFilter} bids found.` })), filteredBids.length > 0 && /* @__PURE__ */ React314.createElement(Stack205, { gap: 12 }, filteredBids.map((bid) => {
32649
+ const status = getBidStatus(bid, t, statusContext);
32006
32650
  const profile = profilesByDid[bid.did];
32007
32651
  const displayName = profile?.displayname || bid.did || bid.address;
32008
32652
  const avatarLabel = (profile?.displayname || bid.did || bid.address || "?")[0]?.toUpperCase();
@@ -32228,94 +32872,6 @@ import React316, { useCallback as useCallback108, useEffect as useEffect111, use
32228
32872
  import { ActionIcon as ActionIcon46, Box as Box58, Collapse as Collapse9, Group as Group112, Loader as Loader57, Stack as Stack207, Text as Text195, UnstyledButton as UnstyledButton5 } from "@mantine/core";
32229
32873
  import { IconArrowLeft as IconArrowLeft9, IconCheck as IconCheck22, IconChevronRight as IconChevronRight12, IconPlayerPlay as IconPlayerPlay6 } from "@tabler/icons-react";
32230
32874
  import { SurveyModel as SurveyModel10 } from "@ixo/surveys";
32231
-
32232
- // src/mantine/blocks/action/actionTypes/evaluateClaim/resolveClaimFileRef.ts
32233
- var IPFS_GATEWAY = "https://ipfs.gateway.ixo.world/ipfs/";
32234
- var CID_REGEX = /(baf[a-z0-9]{20,})/i;
32235
- function extractCid(source) {
32236
- const m = source.match(CID_REGEX);
32237
- return m ? m[1] : null;
32238
- }
32239
- function resolveClaimFileRef(raw) {
32240
- if (raw == null) return null;
32241
- if (typeof raw === "string") {
32242
- const trimmed = raw.trim();
32243
- if (!trimmed) return null;
32244
- if (/^https?:/i.test(trimmed)) return trimmed;
32245
- if (trimmed.startsWith("data:")) return trimmed;
32246
- if (/^ipfs:/i.test(trimmed)) {
32247
- const cid = extractCid(trimmed.replace(/^ipfs:\/*/i, ""));
32248
- return cid ? `${IPFS_GATEWAY}${cid}` : null;
32249
- }
32250
- if (trimmed.startsWith("{")) {
32251
- try {
32252
- return resolveClaimFileRef(JSON.parse(trimmed));
32253
- } catch {
32254
- return null;
32255
- }
32256
- }
32257
- if (/^did:/i.test(trimmed)) {
32258
- const cid = extractCid(trimmed);
32259
- return cid ? `${IPFS_GATEWAY}${cid}` : null;
32260
- }
32261
- if (/^baf[a-z0-9]/i.test(trimmed) && trimmed.length < 200 && !trimmed.includes(" ")) {
32262
- const cid = extractCid(trimmed);
32263
- return cid ? `${IPFS_GATEWAY}${cid}` : null;
32264
- }
32265
- return null;
32266
- }
32267
- if (typeof raw === "object") {
32268
- const obj = raw;
32269
- if (typeof obj.serviceEndpoint === "string" && /^https?:/i.test(obj.serviceEndpoint)) {
32270
- return obj.serviceEndpoint;
32271
- }
32272
- if (typeof obj.proof === "string") {
32273
- const cid = extractCid(obj.proof);
32274
- if (cid) return `${IPFS_GATEWAY}${cid}`;
32275
- }
32276
- if (typeof obj.id === "string") {
32277
- const hashIdx = obj.id.indexOf("#");
32278
- const tail = hashIdx >= 0 ? obj.id.slice(hashIdx + 1) : obj.id;
32279
- const cid = extractCid(tail);
32280
- if (cid) return `${IPFS_GATEWAY}${cid}`;
32281
- }
32282
- if (typeof obj.content === "string") {
32283
- return resolveClaimFileRef(obj.content);
32284
- }
32285
- if (typeof obj.url === "string" && /^https?:/i.test(obj.url)) {
32286
- return obj.url;
32287
- }
32288
- }
32289
- return null;
32290
- }
32291
- function isFileLike(value) {
32292
- if (Array.isArray(value)) {
32293
- return value.length > 0 && value.every((v) => v && typeof v === "object" && typeof v.content === "string");
32294
- }
32295
- return !!value && typeof value === "object" && typeof value.content === "string";
32296
- }
32297
- function resolveOneFileEntry(entry) {
32298
- const resolved = resolveClaimFileRef(entry.content);
32299
- if (resolved && resolved !== entry.content) {
32300
- return { ...entry, content: resolved };
32301
- }
32302
- return entry;
32303
- }
32304
- function resolveFilesInCredentialSubject(subject) {
32305
- if (!subject || typeof subject !== "object") return subject;
32306
- const out = Array.isArray(subject) ? [...subject] : { ...subject };
32307
- for (const [key, value] of Object.entries(subject)) {
32308
- if (!isFileLike(value)) continue;
32309
- if (Array.isArray(value)) {
32310
- out[key] = value.map((v) => resolveOneFileEntry(v));
32311
- } else {
32312
- out[key] = resolveOneFileEntry(value);
32313
- }
32314
- }
32315
- return out;
32316
- }
32317
-
32318
- // src/mantine/blocks/action/actionTypes/claim/ClaimFlowDetail.tsx
32319
32875
  function getTimeAgo2(dateString) {
32320
32876
  if (!dateString) return "";
32321
32877
  try {
@@ -33965,6 +34521,19 @@ function parseAccountList(text) {
33965
34521
  };
33966
34522
  }
33967
34523
 
34524
+ // src/mantine/blocks/action/actionTypes/_shared/bids/decidedBids.ts
34525
+ async function fetchDecidedBids(handlers, params) {
34526
+ const queryDecided = handlers?.bids?.queryDecided;
34527
+ if (typeof queryDecided !== "function") return { bids: [], supported: false };
34528
+ try {
34529
+ const result = await queryDecided(params);
34530
+ const list = Array.isArray(result) ? result : result?.data ?? [];
34531
+ return { bids: list.map((bid) => ({ ...bid, decided: true })), supported: true };
34532
+ } catch {
34533
+ return { bids: [], supported: false };
34534
+ }
34535
+ }
34536
+
33968
34537
  // src/mantine/blocks/action/actionTypes/collectionUsers/CollectionUsersFlowDetail.tsx
33969
34538
  var LOG2 = "[collection.users]";
33970
34539
  var MAX_LISTED_REJECTIONS = 8;
@@ -34356,13 +34925,85 @@ var CollectionUsersFlowDetail = ({
34356
34925
  const handleBidEvaluated = useCallback112((bidId, outcome) => {
34357
34926
  setEvaluatedBids((prev) => ({ ...prev, [bidId]: outcome }));
34358
34927
  }, []);
34359
- const handleDownloadGranteesCsv = useCallback112(() => {
34928
+ const granteeNamesByDid = useMemo120(() => {
34360
34929
  const namesByDid = {};
34361
34930
  for (const [did, profile] of Object.entries(granteeProfilesByDid)) {
34362
34931
  if (profile?.displayname) namesByDid[did] = profile.displayname;
34363
34932
  }
34364
- downloadArrayAsCsv(buildGranteeRows(listGrantees, namesByDid), { filename: `collection-members-${collectionId || "collection"}.csv` });
34365
- }, [listGrantees, granteeProfilesByDid, collectionId]);
34933
+ return namesByDid;
34934
+ }, [granteeProfilesByDid]);
34935
+ const fetchCollectionBids = useCallback112(async () => {
34936
+ if (!deedDid || !collectionId) return { bids: [], truncated: false, decidedIncluded: false };
34937
+ const handlers2 = handlersRef.current;
34938
+ let open = [];
34939
+ let truncated = false;
34940
+ try {
34941
+ const result = await fetchAllBids(handlers2.queryBids, { collectionId, entityDid: deedDid });
34942
+ open = result.bids;
34943
+ truncated = result.truncated;
34944
+ } catch {
34945
+ open = [];
34946
+ }
34947
+ const decided = await fetchDecidedBids(handlers2, { collectionId, entityDid: deedDid });
34948
+ return { bids: dedupeBids([...open, ...decided.bids]), truncated, decidedIncluded: decided.supported };
34949
+ }, [deedDid, collectionId]);
34950
+ const [membersExportError, setMembersExportError] = useState170(null);
34951
+ const handleDownloadGranteesCsv = useCallback112(async () => {
34952
+ const { bids, truncated, decidedIncluded } = await fetchCollectionBids();
34953
+ downloadSectionedCsv(
34954
+ buildCollectionMembersSections({
34955
+ grantees: listGrantees,
34956
+ bids,
34957
+ namesByDid: granteeNamesByDid,
34958
+ bidOptions: { evaluatedByBidId: evaluatedBids },
34959
+ collectionId,
34960
+ bidsTruncated: truncated,
34961
+ decidedBidsIncluded: decidedIncluded
34962
+ }),
34963
+ { filename: exportFileNames.collectionMembers(collectionId).csv }
34964
+ );
34965
+ setMembersExportError(truncated ? "Bid paging stopped early \u2014 part 2 of the export is incomplete." : null);
34966
+ }, [fetchCollectionBids, listGrantees, granteeNamesByDid, evaluatedBids, collectionId]);
34967
+ const handleDownloadGranteesZip = useCallback112(async () => {
34968
+ const { bids, truncated, decidedIncluded } = await fetchCollectionBids();
34969
+ const names = exportFileNames.collectionMembers(collectionId);
34970
+ const handlers2 = handlersRef.current;
34971
+ const fetchMedia = handlers2.fetchClaimMedia ? (url) => handlers2.fetchClaimMedia(url, { entityDid: deedDid }) : void 0;
34972
+ const { blob, mediaTotal, mediaFailed } = await buildExportBundle({
34973
+ records: bids.map((bid) => ({
34974
+ recordId: bid.id,
34975
+ answers: bidAnswers(bid),
34976
+ manifest: {
34977
+ bidId: bid.id,
34978
+ did: bid.did,
34979
+ address: bid.address,
34980
+ role: bid.role,
34981
+ created: bid.created,
34982
+ status: getBidStatusInfo(bid, { grantees: listGrantees, evaluatedByBidId: evaluatedBids }).key
34983
+ }
34984
+ })),
34985
+ fetchMedia,
34986
+ renderCsv: (answersByBidId) => sectionsToCsv(
34987
+ buildCollectionMembersSections({
34988
+ grantees: listGrantees,
34989
+ bids,
34990
+ namesByDid: granteeNamesByDid,
34991
+ bidOptions: { evaluatedByBidId: evaluatedBids, answersByBidId },
34992
+ collectionId,
34993
+ bidsTruncated: truncated,
34994
+ decidedBidsIncluded: decidedIncluded
34995
+ })
34996
+ ),
34997
+ csvName: names.csv,
34998
+ manifestName: names.manifest
34999
+ });
35000
+ downloadBlob(blob, names.zip);
35001
+ const notes = [
35002
+ truncated ? "Bid paging stopped early \u2014 part 2 of the export is incomplete." : "",
35003
+ mediaFailed > 0 ? `${mediaFailed} of ${mediaTotal} bid media files could not be downloaded \u2014 see downloadStatus in ${names.manifest}.` : ""
35004
+ ].filter(Boolean);
35005
+ setMembersExportError(notes.length > 0 ? notes.join(" ") : null);
35006
+ }, [fetchCollectionBids, listGrantees, granteeNamesByDid, evaluatedBids, collectionId, deedDid]);
34366
35007
  useEffect115(() => {
34367
35008
  let mounted = true;
34368
35009
  const dids = Array.from(new Set(listGrantees.map(getGranteeDid).filter(Boolean)));
@@ -34581,7 +35222,7 @@ var CollectionUsersFlowDetail = ({
34581
35222
  { label: "Bids", value: "bids" }
34582
35223
  ]
34583
35224
  }
34584
- ), !configMissing && section === "list" && /* @__PURE__ */ React320.createElement(Stack211, { gap: "sm" }, /* @__PURE__ */ React320.createElement(Group116, { justify: "space-between", align: "center" }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm", fw: 600 }, "Grantees"), /* @__PURE__ */ React320.createElement(Group116, { gap: 4 }, /* @__PURE__ */ React320.createElement(DownloadCsvButton, { onDownload: handleDownloadGranteesCsv, disabled: isRunning || listGrantees.length === 0, label: "Download members CSV" }), /* @__PURE__ */ React320.createElement(ActionIcon47, { variant: "subtle", color: "gray", size: "sm", onClick: handleManualRefreshList, disabled: isRunning || !adminAddress }, /* @__PURE__ */ React320.createElement(IconRefresh9, { size: 16 })))), isRunning && lastOp !== "add" && lastOp !== "revoke" && /* @__PURE__ */ React320.createElement(Group116, { gap: "xs", justify: "center", py: "sm" }, /* @__PURE__ */ React320.createElement(Loader61, { size: "xs" }), /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "Loading grantees\u2026")), !isRunning && listGrantees.length === 0 && /* @__PURE__ */ React320.createElement(Text199, { size: "sm", c: "dimmed", ta: "center", py: "sm" }, "No grantees hold a constraint for this collection yet."), listGrantees.map((grantee) => {
35225
+ ), !configMissing && section === "list" && /* @__PURE__ */ React320.createElement(Stack211, { gap: "sm" }, /* @__PURE__ */ React320.createElement(Group116, { justify: "space-between", align: "center" }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm", fw: 600 }, "Grantees"), /* @__PURE__ */ React320.createElement(Group116, { gap: 4 }, /* @__PURE__ */ React320.createElement(DownloadCsvButton, { onDownload: handleDownloadGranteesCsv, disabled: isRunning, label: "Download members CSV (grants + bids)", onError: setMembersExportError }), /* @__PURE__ */ React320.createElement(DownloadZipButton, { onDownload: handleDownloadGranteesZip, disabled: isRunning, label: "Download members + bid media (ZIP)", onError: setMembersExportError }), /* @__PURE__ */ React320.createElement(ActionIcon47, { variant: "subtle", color: "gray", size: "sm", onClick: handleManualRefreshList, disabled: isRunning || !adminAddress }, /* @__PURE__ */ React320.createElement(IconRefresh9, { size: 16 })))), membersExportError && /* @__PURE__ */ React320.createElement(DismissibleAlert, { color: "yellow", styles: actionAlertStyles, onClose: () => setMembersExportError(null) }, membersExportError), isRunning && lastOp !== "add" && lastOp !== "revoke" && /* @__PURE__ */ React320.createElement(Group116, { gap: "xs", justify: "center", py: "sm" }, /* @__PURE__ */ React320.createElement(Loader61, { size: "xs" }), /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "Loading grantees\u2026")), !isRunning && listGrantees.length === 0 && /* @__PURE__ */ React320.createElement(Text199, { size: "sm", c: "dimmed", ta: "center", py: "sm" }, "No grantees hold a constraint for this collection yet."), listGrantees.map((grantee) => {
34585
35226
  const isTarget = revokeTarget?.address === grantee.address && revokeTarget?.role === grantee.role;
34586
35227
  const granteeDid = getGranteeDid(grantee);
34587
35228
  const profile = granteeProfilesByDid[granteeDid];
@@ -34726,31 +35367,64 @@ var BidInboxSection = ({
34726
35367
  const role = String(selectedBid?.role || "").toLowerCase();
34727
35368
  return role === "evaluation_agent" || role === "ea";
34728
35369
  }, [selectedBid]);
35370
+ const bidStatusContext = useMemo120(() => ({ grantees, evaluatedByBidId }), [grantees, evaluatedByBidId]);
34729
35371
  const bidEvaluatedStatus = useCallback112(
34730
35372
  (bid) => {
34731
- const local = evaluatedByBidId[bid.id];
34732
- if (local) return local;
34733
- const role = String(bid.role || "").toLowerCase();
34734
- const grantRole = role === "evaluation_agent" || role === "ea" ? "evaluate" : "submit";
34735
- return grantees.some((g) => g.address === bid.address && g.role === grantRole) ? "approved" : null;
35373
+ const info = getBidStatusInfo(bid, bidStatusContext);
35374
+ return info.key === "pending" ? null : info.key;
34736
35375
  },
34737
- [evaluatedByBidId, grantees]
35376
+ [bidStatusContext]
35377
+ );
35378
+ const bidRowOptions = useMemo120(
35379
+ () => ({
35380
+ evaluatedByBidId,
35381
+ namesByDid: Object.fromEntries(Object.entries(profilesByDid).map(([did, profile]) => [did, profile?.displayname || ""]))
35382
+ }),
35383
+ [evaluatedByBidId, profilesByDid]
34738
35384
  );
34739
35385
  const downloadBidsCsv = useCallback112(
34740
35386
  (subset, filename) => {
34741
- downloadArrayAsCsv(buildBidRows(subset, grantees), { filename });
35387
+ downloadArrayAsCsv(buildBidRows(subset, grantees, bidRowOptions), { filename });
35388
+ },
35389
+ [grantees, bidRowOptions]
35390
+ );
35391
+ const downloadBidsZip = useCallback112(
35392
+ async (subset, names) => {
35393
+ const handlers = handlersRef.current;
35394
+ const fetchMedia = handlers.fetchClaimMedia ? (url) => handlers.fetchClaimMedia(url, { entityDid: deedDid }) : void 0;
35395
+ const { blob, mediaTotal, mediaFailed } = await buildExportBundle({
35396
+ records: subset.map((bid) => ({
35397
+ recordId: bid.id,
35398
+ answers: bidAnswers(bid),
35399
+ manifest: {
35400
+ bidId: bid.id,
35401
+ did: bid.did,
35402
+ address: bid.address,
35403
+ role: bid.role,
35404
+ created: bid.created,
35405
+ status: getBidStatusInfo(bid, bidStatusContext).key
35406
+ }
35407
+ })),
35408
+ fetchMedia,
35409
+ renderCsv: (answersByBidId) => rowsToCsv(buildBidRows(subset, grantees, { ...bidRowOptions, answersByBidId })),
35410
+ csvName: names.csv,
35411
+ manifestName: names.manifest
35412
+ });
35413
+ downloadBlob(blob, names.zip);
35414
+ setError(mediaFailed > 0 ? `${mediaFailed} of ${mediaTotal} media files could not be downloaded \u2014 see downloadStatus in ${names.manifest}.` : null);
34742
35415
  },
34743
- [grantees]
35416
+ [grantees, bidRowOptions, bidStatusContext, deedDid, handlersRef]
34744
35417
  );
34745
- const handleDownloadBidsCsv = useCallback112(() => downloadBidsCsv(bids, `bids-${collectionId || "collection"}.csv`), [downloadBidsCsv, bids, collectionId]);
35418
+ const handleDownloadBidsCsv = useCallback112(() => downloadBidsCsv(bids, exportFileNames.bids(collectionId).csv), [downloadBidsCsv, bids, collectionId]);
35419
+ const handleDownloadBidsZip = useCallback112(() => downloadBidsZip(bids, exportFileNames.bids(collectionId)), [downloadBidsZip, bids, collectionId]);
34746
35420
  const refreshBids = useCallback112(async () => {
34747
35421
  if (!deedDid || !collectionId) return;
34748
35422
  setLoading(true);
34749
35423
  setError(null);
34750
35424
  try {
34751
- const response = await handlersRef.current.queryBids({ collectionId, entityDid: deedDid });
34752
- const fetched = Array.isArray(response?.data) ? response.data : [];
34753
- setBids(fetched);
35425
+ const result = await fetchAllBids(handlersRef.current.queryBids, { collectionId, entityDid: deedDid });
35426
+ setBids(dedupeBids(result.bids));
35427
+ setError(result.truncated ? "Bid paging stopped early \u2014 this list may be incomplete." : null);
34754
35428
  } catch {
34755
35429
  setBids([]);
34756
35430
  setError(null);
@@ -34936,7 +35610,7 @@ var BidInboxSection = ({
34936
35610
  }
34937
35611
  ] : []
34938
35612
  ];
34939
- return /* @__PURE__ */ React320.createElement(Stack211, { gap: "md" }, /* @__PURE__ */ React320.createElement(Group116, { gap: "xs", align: "center" }, /* @__PURE__ */ React320.createElement(ActionIcon47, { variant: "subtle", color: "gray", size: "sm", onClick: () => setSelectedBidId("") }, /* @__PURE__ */ React320.createElement(IconArrowLeft10, { size: 16 })), /* @__PURE__ */ React320.createElement(Text199, { fw: 500, size: "sm", truncate: true, style: { flex: 1, minWidth: 0 } }, "Bid #", selectedBid.id), /* @__PURE__ */ React320.createElement(DownloadCsvButton, { onDownload: () => downloadBidsCsv([selectedBid], `bid-${selectedBid.id}.csv`), label: "Download bid CSV" })), /* @__PURE__ */ React320.createElement(Stack211, { gap: 4 }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm", fw: 600, truncate: true }, displayName), /* @__PURE__ */ React320.createElement(Group116, { gap: "xs" }, /* @__PURE__ */ React320.createElement(Badge52, { size: "xs", variant: "light", color: getRoleColor3(selectedBid.role) }, getBidRoleLabel(selectedBid.role)), /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, truncateAddress4(selectedBid.address)))), /* @__PURE__ */ React320.createElement(CollapsibleSection, { title: "Inputs" }, bidData && typeof bidData === "object" && Object.keys(bidData).length > 0 ? /* @__PURE__ */ React320.createElement(ReadableKeyValues, { data: bidData }) : /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "No input data available.")), evaluatedStatus ? /* @__PURE__ */ React320.createElement(
35613
+ return /* @__PURE__ */ React320.createElement(Stack211, { gap: "md" }, /* @__PURE__ */ React320.createElement(Group116, { gap: "xs", align: "center" }, /* @__PURE__ */ React320.createElement(ActionIcon47, { variant: "subtle", color: "gray", size: "sm", onClick: () => setSelectedBidId("") }, /* @__PURE__ */ React320.createElement(IconArrowLeft10, { size: 16 })), /* @__PURE__ */ React320.createElement(Text199, { fw: 500, size: "sm", truncate: true, style: { flex: 1, minWidth: 0 } }, "Bid #", selectedBid.id), /* @__PURE__ */ React320.createElement(DownloadCsvButton, { onDownload: () => downloadBidsCsv([selectedBid], exportFileNames.bid(selectedBid.id).csv), label: "Download bid CSV", onError: setError }), /* @__PURE__ */ React320.createElement(DownloadZipButton, { onDownload: () => downloadBidsZip([selectedBid], exportFileNames.bid(selectedBid.id)), label: "Download bid + media (ZIP)", onError: setError })), /* @__PURE__ */ React320.createElement(Stack211, { gap: 4 }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm", fw: 600, truncate: true }, displayName), /* @__PURE__ */ React320.createElement(Group116, { gap: "xs" }, /* @__PURE__ */ React320.createElement(Badge52, { size: "xs", variant: "light", color: getRoleColor3(selectedBid.role) }, getBidRoleLabel(selectedBid.role)), /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, truncateAddress4(selectedBid.address)))), /* @__PURE__ */ React320.createElement(CollapsibleSection, { title: "Inputs" }, bidData && typeof bidData === "object" && Object.keys(bidData).length > 0 ? /* @__PURE__ */ React320.createElement(ReadableKeyValues, { data: bidData }) : /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "No input data available.")), evaluatedStatus ? /* @__PURE__ */ React320.createElement(
34940
35614
  MantineAlert,
34941
35615
  {
34942
35616
  color: evaluatedStatus === "approved" ? "green" : "red",
@@ -35000,7 +35674,7 @@ var BidInboxSection = ({
35000
35674
  styles: actionAlertStyles
35001
35675
  },
35002
35676
  lastDecision.kind === "proposed" ? `Proposal #${lastDecision.proposalId} asks the POD to grant ${lastDecision.roleLabel} access to ${lastDecision.name}. Nothing is granted until the vote passes \u2014 the bid stays pending until then.` : lastDecision.kind === "approved" ? `${lastDecision.roleLabel} access granted to ${lastDecision.name}. They can now ${lastDecision.roleLabel === "Evaluator" ? "evaluate" : "submit"} claims in this collection.` : `${lastDecision.name}'s application was declined.`
35003
- ), /* @__PURE__ */ React320.createElement(Group116, { justify: "space-between", align: "center" }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm", fw: 600 }, "Incoming bids"), /* @__PURE__ */ React320.createElement(Group116, { gap: 4 }, /* @__PURE__ */ React320.createElement(DownloadCsvButton, { onDownload: handleDownloadBidsCsv, disabled: loading || bids.length === 0, label: "Download bids CSV" }), /* @__PURE__ */ React320.createElement(ActionIcon47, { variant: "subtle", color: "gray", size: "sm", onClick: refreshBids, disabled: loading }, /* @__PURE__ */ React320.createElement(IconRefresh9, { size: 16 })))), /* @__PURE__ */ React320.createElement(Divider22, { color: "color-mix(in srgb, var(--mantine-color-text) 6%, transparent)" }), loading && /* @__PURE__ */ React320.createElement(Group116, { gap: "xs", justify: "center", py: "sm" }, /* @__PURE__ */ React320.createElement(Loader61, { size: "xs" }), /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "Loading bids\u2026")), !loading && bids.length === 0 && /* @__PURE__ */ React320.createElement(Text199, { size: "sm", c: "dimmed", ta: "center", py: "sm" }, "No bids available for this collection."), bids.map((bid) => {
35677
+ ), /* @__PURE__ */ React320.createElement(Group116, { justify: "space-between", align: "center" }, /* @__PURE__ */ React320.createElement(Text199, { size: "sm", fw: 600 }, "Incoming bids"), /* @__PURE__ */ React320.createElement(Group116, { gap: 4 }, /* @__PURE__ */ React320.createElement(DownloadCsvButton, { onDownload: handleDownloadBidsCsv, disabled: loading || bids.length === 0, label: "Download bids CSV", onError: setError }), /* @__PURE__ */ React320.createElement(DownloadZipButton, { onDownload: handleDownloadBidsZip, disabled: loading || bids.length === 0, label: "Download bids + media (ZIP)", onError: setError }), /* @__PURE__ */ React320.createElement(ActionIcon47, { variant: "subtle", color: "gray", size: "sm", onClick: refreshBids, disabled: loading }, /* @__PURE__ */ React320.createElement(IconRefresh9, { size: 16 })))), /* @__PURE__ */ React320.createElement(Divider22, { color: "color-mix(in srgb, var(--mantine-color-text) 6%, transparent)" }), loading && /* @__PURE__ */ React320.createElement(Group116, { gap: "xs", justify: "center", py: "sm" }, /* @__PURE__ */ React320.createElement(Loader61, { size: "xs" }), /* @__PURE__ */ React320.createElement(Text199, { size: "xs", c: "dimmed" }, "Loading bids\u2026")), !loading && bids.length === 0 && /* @__PURE__ */ React320.createElement(Text199, { size: "sm", c: "dimmed", ta: "center", py: "sm" }, "No bids available for this collection."), bids.map((bid) => {
35004
35678
  const profile = profilesByDid[bid.did];
35005
35679
  const displayName = profile?.displayname || bid.did || bid.address;
35006
35680
  const status = bidEvaluatedStatus(bid) || "pending";
@@ -41376,8 +42050,8 @@ var EvaluateClaimFlowDetail = ({
41376
42050
  if (!deedDid || !collectionId) return;
41377
42051
  void refreshClaims();
41378
42052
  }, [deedDid, collectionId, refreshClaims]);
41379
- const downloadClaimsCsv = useCallback119(
41380
- async (subset, filename) => {
42053
+ const fetchClaimContents = useCallback119(
42054
+ async (subset) => {
41381
42055
  const handlers2 = handlersRef.current;
41382
42056
  const contentsByClaimId = {};
41383
42057
  await Promise.all(
@@ -41389,11 +42063,60 @@ var EvaluateClaimFlowDetail = ({
41389
42063
  }
41390
42064
  })
41391
42065
  );
41392
- downloadArrayAsCsv(buildClaimRows(subset, contentsByClaimId), { filename });
42066
+ return contentsByClaimId;
41393
42067
  },
41394
42068
  [collectionId, deedDid]
41395
42069
  );
41396
- const handleDownloadClaimsCsv = useCallback119(() => downloadClaimsCsv(claims, `claims-${collectionId || "collection"}.csv`), [downloadClaimsCsv, claims, collectionId]);
42070
+ const downloadClaimsCsv = useCallback119(
42071
+ async (subset, filename) => {
42072
+ downloadArrayAsCsv(buildClaimRows(subset, await fetchClaimContents(subset)), { filename });
42073
+ },
42074
+ [fetchClaimContents]
42075
+ );
42076
+ const titlesByFieldName = useMemo132(() => {
42077
+ const titles = {};
42078
+ if (!surveyJson) return titles;
42079
+ try {
42080
+ for (const question of new SurveyModel11(surveyJson).getAllQuestions()) {
42081
+ if (question.name) titles[question.name] = question.title || question.name;
42082
+ }
42083
+ } catch {
42084
+ return titles;
42085
+ }
42086
+ return titles;
42087
+ }, [surveyJson]);
42088
+ const downloadClaimsZip = useCallback119(
42089
+ async (subset, names) => {
42090
+ const handlers2 = handlersRef.current;
42091
+ const contentsByClaimId = await fetchClaimContents(subset);
42092
+ const fetchMedia = handlers2.fetchClaimMedia ? (url) => handlers2.fetchClaimMedia(url, { entityDid: deedDid }) : void 0;
42093
+ const { blob, mediaTotal, mediaFailed } = await buildExportBundle({
42094
+ records: subset.map((claim) => ({
42095
+ recordId: claim.claimId,
42096
+ answers: claimAnswers(contentsByClaimId[claim.claimId]),
42097
+ manifest: {
42098
+ claimId: claim.claimId,
42099
+ collectionId: claim.collectionId,
42100
+ agentAddress: claim.agentAddress,
42101
+ submissionDate: claim.submissionDate,
42102
+ status: getClaimStatusInfo(claim).key
42103
+ }
42104
+ })),
42105
+ titlesByFieldName,
42106
+ fetchMedia,
42107
+ renderCsv: (answersByClaimId) => rowsToCsv(buildClaimRows(subset, contentsByClaimId, { answersByClaimId })),
42108
+ csvName: names.csv,
42109
+ manifestName: names.manifest
42110
+ });
42111
+ downloadBlob(blob, names.zip);
42112
+ if (mediaFailed > 0) {
42113
+ setError(`${mediaFailed} of ${mediaTotal} media files could not be downloaded \u2014 see downloadStatus in ${names.manifest}.`);
42114
+ }
42115
+ },
42116
+ [fetchClaimContents, deedDid, titlesByFieldName]
42117
+ );
42118
+ const handleDownloadClaimsCsv = useCallback119(() => downloadClaimsCsv(claims, exportFileNames.claims(collectionId).csv), [downloadClaimsCsv, claims, collectionId]);
42119
+ const handleDownloadClaimsZip = useCallback119(() => downloadClaimsZip(claims, exportFileNames.claims(collectionId)), [downloadClaimsZip, claims, collectionId]);
41397
42120
  useEffect127(() => {
41398
42121
  let mounted = true;
41399
42122
  const dids = Array.from(new Set(claims.map((claim) => claim.agentDid).filter(Boolean)));
@@ -41727,11 +42450,25 @@ var EvaluateClaimFlowDetail = ({
41727
42450
  ]);
41728
42451
  const flowManagerContext = /* @__PURE__ */ React347.createElement(FlowManagerContextSection, { record: nodeContext, onOverride: setNodeContextOverride, disabled: isDisabled });
41729
42452
  if (selectedClaim) {
41730
- const claimStatus2 = getClaimStatusInfo(selectedClaim);
42453
+ const claimStatus = getClaimStatusInfo(selectedClaim);
41731
42454
  const selectedClaimProfile = profilesByDid[selectedClaim.agentDid];
41732
42455
  const selectedDisplayName = selectedClaimProfile?.displayname || selectedClaim.agentDid || selectedClaim.agentAddress;
41733
42456
  const selectedAvatarLabel = (selectedClaimProfile?.displayname || selectedClaim.agentDid || selectedClaim.agentAddress || "?")[0]?.toUpperCase();
41734
- return /* @__PURE__ */ React347.createElement(Stack236, { gap: "md" }, /* @__PURE__ */ React347.createElement(Group142, { gap: "xs", align: "center" }, /* @__PURE__ */ React347.createElement(ActionIcon57, { variant: "subtle", color: "gray", size: "sm", onClick: () => setSelectedClaimId("") }, /* @__PURE__ */ React347.createElement(IconArrowLeft11, { size: 16 })), /* @__PURE__ */ React347.createElement(Text224, { fw: 500, size: "sm", title: selectedClaim.claimId, truncate: true, style: { flex: 1, minWidth: 0 } }, t("actionTypes.claim.flow.claimNumber", { id: truncateId2(selectedClaim.claimId), defaultValue: `Claim #${truncateId2(selectedClaim.claimId)}` })), /* @__PURE__ */ React347.createElement(DownloadCsvButton, { onDownload: () => downloadClaimsCsv([selectedClaim], `claim-${truncateId2(selectedClaim.claimId)}.csv`), label: "Download claim CSV" })), /* @__PURE__ */ React347.createElement(Group142, { gap: 16, align: "center", style: { width: "100%" } }, /* @__PURE__ */ React347.createElement(
42457
+ return /* @__PURE__ */ React347.createElement(Stack236, { gap: "md" }, /* @__PURE__ */ React347.createElement(Group142, { gap: "xs", align: "center" }, /* @__PURE__ */ React347.createElement(ActionIcon57, { variant: "subtle", color: "gray", size: "sm", onClick: () => setSelectedClaimId("") }, /* @__PURE__ */ React347.createElement(IconArrowLeft11, { size: 16 })), /* @__PURE__ */ React347.createElement(Text224, { fw: 500, size: "sm", title: selectedClaim.claimId, truncate: true, style: { flex: 1, minWidth: 0 } }, t("actionTypes.claim.flow.claimNumber", { id: truncateId2(selectedClaim.claimId), defaultValue: `Claim #${truncateId2(selectedClaim.claimId)}` })), /* @__PURE__ */ React347.createElement(
42458
+ DownloadCsvButton,
42459
+ {
42460
+ onDownload: () => downloadClaimsCsv([selectedClaim], exportFileNames.claim(selectedClaim.claimId).csv),
42461
+ label: "Download claim CSV",
42462
+ onError: setError
42463
+ }
42464
+ ), /* @__PURE__ */ React347.createElement(
42465
+ DownloadZipButton,
42466
+ {
42467
+ onDownload: () => downloadClaimsZip([selectedClaim], exportFileNames.claim(selectedClaim.claimId)),
42468
+ label: "Download claim + media (ZIP)",
42469
+ onError: setError
42470
+ }
42471
+ )), /* @__PURE__ */ React347.createElement(Group142, { gap: 16, align: "center", style: { width: "100%" } }, /* @__PURE__ */ React347.createElement(
41735
42472
  Box69,
41736
42473
  {
41737
42474
  style: {
@@ -41752,7 +42489,7 @@ var EvaluateClaimFlowDetail = ({
41752
42489
  }
41753
42490
  },
41754
42491
  selectedClaimProfile?.avatarUrl ? null : selectedAvatarLabel
41755
- ), /* @__PURE__ */ React347.createElement(Stack236, { gap: 0, style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React347.createElement(Text224, { fw: 500, size: "md", truncate: true }, selectedDisplayName), /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed", truncate: true }, truncateAddress5(selectedClaim.agentAddress))), /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed", style: { flexShrink: 0 } }, getTimeAgo4(selectedClaim.submissionDate || ""))), claimStatus2.key === "disputed" && /* @__PURE__ */ React347.createElement(DismissibleAlert, { color: "orange", title: t("actionTypes.claim.flow.disputed.title", { defaultValue: "Claim disputed" }), styles: actionAlertStyles }, loadingDispute ? /* @__PURE__ */ React347.createElement(Group142, { gap: "xs" }, /* @__PURE__ */ React347.createElement(Loader69, { size: "xs" }), /* @__PURE__ */ React347.createElement(Text224, { size: "xs" }, t("actionTypes.claim.flow.disputed.loadingDetails", { defaultValue: "Loading dispute details\u2026" }))) : disputeDetails ? /* @__PURE__ */ React347.createElement(Stack236, { gap: 6 }, disputeDetails.reason ? /* @__PURE__ */ React347.createElement(Text224, { size: "sm" }, disputeDetails.reason) : /* @__PURE__ */ React347.createElement(Text224, { size: "sm", c: "dimmed" }, t("actionTypes.evaluateClaim.flow.disputed.noMessageAttached", { defaultValue: "A dispute was recorded but no message was attached." })), (disputeDetails.evaluatorDid || disputeDetails.disputedAt) && /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, disputeDetails.evaluatorDid ? t("actionTypes.claim.flow.disputed.byEvaluator", {
42492
+ ), /* @__PURE__ */ React347.createElement(Stack236, { gap: 0, style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React347.createElement(Text224, { fw: 500, size: "md", truncate: true }, selectedDisplayName), /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed", truncate: true }, truncateAddress5(selectedClaim.agentAddress))), /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed", style: { flexShrink: 0 } }, getTimeAgo4(selectedClaim.submissionDate || ""))), claimStatus.key === "disputed" && /* @__PURE__ */ React347.createElement(DismissibleAlert, { color: "orange", title: t("actionTypes.claim.flow.disputed.title", { defaultValue: "Claim disputed" }), styles: actionAlertStyles }, loadingDispute ? /* @__PURE__ */ React347.createElement(Group142, { gap: "xs" }, /* @__PURE__ */ React347.createElement(Loader69, { size: "xs" }), /* @__PURE__ */ React347.createElement(Text224, { size: "xs" }, t("actionTypes.claim.flow.disputed.loadingDetails", { defaultValue: "Loading dispute details\u2026" }))) : disputeDetails ? /* @__PURE__ */ React347.createElement(Stack236, { gap: 6 }, disputeDetails.reason ? /* @__PURE__ */ React347.createElement(Text224, { size: "sm" }, disputeDetails.reason) : /* @__PURE__ */ React347.createElement(Text224, { size: "sm", c: "dimmed" }, t("actionTypes.evaluateClaim.flow.disputed.noMessageAttached", { defaultValue: "A dispute was recorded but no message was attached." })), (disputeDetails.evaluatorDid || disputeDetails.disputedAt) && /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, disputeDetails.evaluatorDid ? t("actionTypes.claim.flow.disputed.byEvaluator", {
41756
42493
  address: truncateAddress5(disputeDetails.evaluatorDid),
41757
42494
  defaultValue: `By ${truncateAddress5(disputeDetails.evaluatorDid)}`
41758
42495
  }) : "", disputeDetails.evaluatorDid && disputeDetails.disputedAt ? " \xB7 " : "", disputeDetails.disputedAt ? new Date(disputeDetails.disputedAt).toLocaleString() : ""), /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, t("actionTypes.evaluateClaim.flow.disputed.amendNeeded", { defaultValue: "The submitter needs to amend and resubmit before this claim can be evaluated." }))) : typeof handlers.getClaimDisputeDetails !== "function" ? /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, t("actionTypes.claim.flow.disputed.unavailable", { defaultValue: "Dispute details unavailable in this environment." })) : /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, t("actionTypes.claim.flow.disputed.noRecord", { defaultValue: "No dispute record found on-chain for this claim." }))), engine.supported && (engine.forbidden ? /* @__PURE__ */ React347.createElement(DismissibleAlert, { color: "gray", styles: actionAlertStyles }, "You need evaluator access on this collection to see the engine's results.") : engine.loading && !engine.record ? /* @__PURE__ */ React347.createElement(Group142, { gap: "xs" }, /* @__PURE__ */ React347.createElement(Loader69, { size: "xs" }), /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, "Checking what the engine knows about this claim\u2026")) : /* @__PURE__ */ React347.createElement(React347.Fragment, null, /* @__PURE__ */ React347.createElement(
@@ -41857,7 +42594,7 @@ var EvaluateClaimFlowDetail = ({
41857
42594
  checked: createUdid,
41858
42595
  onChange: (event) => setCreateUdid(event.currentTarget.checked)
41859
42596
  }
41860
- ))), engine.supported && !engine.forbidden && engine.record && /* @__PURE__ */ React347.createElement(CollapsibleSection, { title: "Technical details" }, /* @__PURE__ */ React347.createElement(TechnicalDetails, { record: engine.record, onChain: onChainDisplay })), outcomeNotReady && !isClaimAlreadyEvaluated && /* @__PURE__ */ React347.createElement(DismissibleAlert, { color: "yellow", styles: actionAlertStyles }, t("actionTypes.evaluateClaim.flow.outcome.completeBeforeAction", { defaultValue: "Complete the Outcome evaluation template before approving or rejecting." })), isClaimAlreadyEvaluated && claimStatus2.key !== "disputed" && /* @__PURE__ */ React347.createElement(DismissibleAlert, { color: "yellow", styles: actionAlertStyles }, /* @__PURE__ */ React347.createElement(Stack236, { gap: 4 }, /* @__PURE__ */ React347.createElement(Text224, { size: "sm" }, t("actionTypes.evaluateClaim.flow.alreadyEvaluated", { defaultValue: "This claim is already evaluated." })), evaluationAmount && evaluationAmount.length > 0 && /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, t("actionTypes.evaluateClaim.flow.transferredOnChain", {
42597
+ ))), engine.supported && !engine.forbidden && engine.record && /* @__PURE__ */ React347.createElement(CollapsibleSection, { title: "Technical details" }, /* @__PURE__ */ React347.createElement(TechnicalDetails, { record: engine.record, onChain: onChainDisplay })), outcomeNotReady && !isClaimAlreadyEvaluated && /* @__PURE__ */ React347.createElement(DismissibleAlert, { color: "yellow", styles: actionAlertStyles }, t("actionTypes.evaluateClaim.flow.outcome.completeBeforeAction", { defaultValue: "Complete the Outcome evaluation template before approving or rejecting." })), isClaimAlreadyEvaluated && claimStatus.key !== "disputed" && /* @__PURE__ */ React347.createElement(DismissibleAlert, { color: "yellow", styles: actionAlertStyles }, /* @__PURE__ */ React347.createElement(Stack236, { gap: 4 }, /* @__PURE__ */ React347.createElement(Text224, { size: "sm" }, t("actionTypes.evaluateClaim.flow.alreadyEvaluated", { defaultValue: "This claim is already evaluated." })), evaluationAmount && evaluationAmount.length > 0 && /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, t("actionTypes.evaluateClaim.flow.transferredOnChain", {
41861
42598
  amount: evaluationAmount.map(formatCoin).join(", "),
41862
42599
  defaultValue: `Transferred on-chain: ${evaluationAmount.map(formatCoin).join(", ")}`
41863
42600
  })))), !authChecking && authError && /* @__PURE__ */ React347.createElement(DismissibleAlert, { color: "orange", styles: actionAlertStyles }, /* @__PURE__ */ React347.createElement(Stack236, { gap: "xs" }, /* @__PURE__ */ React347.createElement(Text224, { size: "sm" }, t("actionTypes.claim.flow.authVerifyFailed", { defaultValue: "Couldn't verify your authorization \u2014 the chain RPC is unreachable." })), /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, authError), /* @__PURE__ */ React347.createElement(Button80, { size: "xs", variant: "light", onClick: () => setAuthRetryKey((k) => k + 1) }, t("actionTypes.claim.flow.retry", { defaultValue: "Retry" })))), !authChecking && !authError && !isEvaluatorAuthorized && /* @__PURE__ */ React347.createElement(DismissibleAlert, { color: "yellow", styles: actionAlertStyles }, t("actionTypes.evaluateClaim.flow.notAuthorized", { defaultValue: "You need evaluator authorization for this collection to approve or reject claims." })), !isClaimAlreadyEvaluated && /* @__PURE__ */ React347.createElement(
@@ -41923,7 +42660,7 @@ var EvaluateClaimFlowDetail = ({
41923
42660
  }
41924
42661
  },
41925
42662
  /* @__PURE__ */ React347.createElement(IconRefresh11, { size: 16 })
41926
- ), /* @__PURE__ */ React347.createElement(DownloadCsvButton, { onDownload: handleDownloadClaimsCsv, disabled: loadingClaims || claims.length === 0, label: "Download claims CSV" }), /* @__PURE__ */ React347.createElement(ActionIcon57, { variant: "subtle", color: "gray", size: "sm" }, /* @__PURE__ */ React347.createElement(IconFilter2, { size: 16 })))), engineSummaries.supported && engineSummaries.notEnrolled && /* @__PURE__ */ React347.createElement(EnginePromo, null), loadingClaims && /* @__PURE__ */ React347.createElement(Group142, { gap: "xs", justify: "center", py: "md" }, /* @__PURE__ */ React347.createElement(Loader69, { size: "xs" }), /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, t("actionTypes.evaluateClaim.flow.loadingClaims", { defaultValue: "Loading claims..." }))), !loadingClaims && filteredClaims.length === 0 && /* @__PURE__ */ React347.createElement(Text224, { size: "sm", c: "dimmed", ta: "center", py: "md" }, claims.length === 0 ? t("actionTypes.evaluateClaim.flow.empty.allCollections", { defaultValue: "No claims available for this collection." }) : activeFilter === "owed" ? "No claims are waiting on you right now." : t("actionTypes.evaluateClaim.flow.empty.filtered", { filter: activeFilter, defaultValue: `No ${activeFilter} claims found.` })), filteredClaims.length > 0 && /* @__PURE__ */ React347.createElement(Stack236, { gap: 12 }, filteredClaims.map((claim) => {
42663
+ ), /* @__PURE__ */ React347.createElement(DownloadCsvButton, { onDownload: handleDownloadClaimsCsv, disabled: loadingClaims || claims.length === 0, label: "Download claims CSV", onError: setError }), /* @__PURE__ */ React347.createElement(DownloadZipButton, { onDownload: handleDownloadClaimsZip, disabled: loadingClaims || claims.length === 0, label: "Download claims + media (ZIP)", onError: setError }), /* @__PURE__ */ React347.createElement(ActionIcon57, { variant: "subtle", color: "gray", size: "sm" }, /* @__PURE__ */ React347.createElement(IconFilter2, { size: 16 })))), engineSummaries.supported && engineSummaries.notEnrolled && /* @__PURE__ */ React347.createElement(EnginePromo, null), loadingClaims && /* @__PURE__ */ React347.createElement(Group142, { gap: "xs", justify: "center", py: "md" }, /* @__PURE__ */ React347.createElement(Loader69, { size: "xs" }), /* @__PURE__ */ React347.createElement(Text224, { size: "xs", c: "dimmed" }, t("actionTypes.evaluateClaim.flow.loadingClaims", { defaultValue: "Loading claims..." }))), !loadingClaims && filteredClaims.length === 0 && /* @__PURE__ */ React347.createElement(Text224, { size: "sm", c: "dimmed", ta: "center", py: "md" }, claims.length === 0 ? t("actionTypes.evaluateClaim.flow.empty.allCollections", { defaultValue: "No claims available for this collection." }) : activeFilter === "owed" ? "No claims are waiting on you right now." : t("actionTypes.evaluateClaim.flow.empty.filtered", { filter: activeFilter, defaultValue: `No ${activeFilter} claims found.` })), filteredClaims.length > 0 && /* @__PURE__ */ React347.createElement(Stack236, { gap: 12 }, filteredClaims.map((claim) => {
41927
42664
  const row = engineSummaries.byClaimId[claim.claimId];
41928
42665
  const view = rowViewByClaimId.get(claim.claimId) ?? { tone: "neutral", headline: "", owed: false, action: null, chain: null };
41929
42666
  const profile = profilesByDid[claim.agentDid];
@@ -58189,4 +58926,4 @@ export {
58189
58926
  ixoGraphQLClient,
58190
58927
  getEntity
58191
58928
  };
58192
- //# sourceMappingURL=chunk-NYQBGIZ2.js.map
58929
+ //# sourceMappingURL=chunk-37OKRZEK.js.map