@unlev/exeq 0.1.6 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -235,9 +235,10 @@ function FieldOverlayItem({
235
235
  const overlayRef = useRef(null);
236
236
  const dragStartRef = useRef(null);
237
237
  const resizeStartRef = useRef(null);
238
- const color = getSignerColor(field.assignee);
239
238
  const isRedact = field.type === "blackout" || field.type === "whiteout";
240
239
  const isEditable = mode === "designer" || mode === "signer" && field.assignee === currentSigner;
240
+ const isInactiveSigner = mode === "signer" && !isRedact && field.assignee !== currentSigner;
241
+ const color = isInactiveSigner ? "#cccccc" : getSignerColor(field.assignee);
241
242
  const isPreFilled = !isEditable && !!field.value;
242
243
  const handleMouseDown = useCallback((e) => {
243
244
  if (mode !== "designer" || !onMove) return;
@@ -302,7 +303,7 @@ function FieldOverlayItem({
302
303
  "div",
303
304
  {
304
305
  ref: overlayRef,
305
- className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""} ${isRedact ? "redact" : ""}`,
306
+ className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""} ${isRedact ? "redact" : ""} ${isInactiveSigner ? "inactive-signer" : ""}`,
306
307
  style: {
307
308
  left: `${field.x}%`,
308
309
  top: `${field.y}%`,
@@ -310,9 +311,9 @@ function FieldOverlayItem({
310
311
  height: `${field.height}%`,
311
312
  borderColor: isRedact ? field.type === "blackout" ? "#666" : "#bbb" : color,
312
313
  borderStyle: isRedact ? "dashed" : "solid",
313
- backgroundColor: isRedact ? field.type === "blackout" ? "#000000" : "#ffffff" : isSelected ? `${color}22` : `${color}11`,
314
- cursor: mode === "designer" ? "move" : "default",
315
- pointerEvents: mode === "signer" && isRedact ? "none" : void 0
314
+ backgroundColor: isRedact ? field.type === "blackout" ? "#000000" : "#ffffff" : isInactiveSigner ? "#f5f5f5" : isSelected ? `${color}22` : `${color}11`,
315
+ cursor: mode === "designer" ? "move" : isInactiveSigner ? "default" : "default",
316
+ pointerEvents: mode === "signer" && (isRedact || isInactiveSigner) ? "none" : void 0
316
317
  },
317
318
  onClick: (e) => {
318
319
  e.stopPropagation();
@@ -999,6 +1000,10 @@ function DesignerView({
999
1000
  pages.length > 0 && /* @__PURE__ */ jsxs4(Fragment, { children: [
1000
1001
  /* @__PURE__ */ jsx4("div", { className: "panel-resize-handle", onMouseDown: handleResizeStart }),
1001
1002
  /* @__PURE__ */ jsxs4("div", { className: "designer-panel", style: { width: panelWidth }, children: [
1003
+ /* @__PURE__ */ jsxs4("div", { className: "signer-role-indicator", children: [
1004
+ /* @__PURE__ */ jsx4("span", { className: "signer-role-indicator-label", children: "Editing as" }),
1005
+ /* @__PURE__ */ jsx4("strong", { children: activeRole })
1006
+ ] }),
1002
1007
  /* @__PURE__ */ jsxs4("div", { className: "panel-tabs", children: [
1003
1008
  /* @__PURE__ */ jsx4(
1004
1009
  "button",
@@ -1287,7 +1292,8 @@ function SignerView({
1287
1292
  callbackUrl: initialCallbackUrl,
1288
1293
  onComplete,
1289
1294
  initialValues,
1290
- submitLabel
1295
+ submitLabel,
1296
+ signerOrder: signerOrderProp
1291
1297
  } = {}) {
1292
1298
  if (!isValidApiKey(apiKey)) {
1293
1299
  return /* @__PURE__ */ jsx6("div", { className: "signer-layout", children: /* @__PURE__ */ jsxs6("div", { className: "empty-state", children: [
@@ -1302,15 +1308,28 @@ function SignerView({
1302
1308
  const [pages, setPages] = useState3([]);
1303
1309
  const [fields, setFields] = useState3([]);
1304
1310
  const [selectedFieldId, setSelectedFieldId] = useState3(null);
1305
- const [signer, setSigner] = useState3(initialSigner || "Signer 1");
1311
+ const [signerRoles, setSignerRoles] = useState3([]);
1312
+ const [currentSignerIndex, setCurrentSignerIndex] = useState3(() => {
1313
+ if (initialSigner && signerOrderProp) {
1314
+ const idx = signerOrderProp.indexOf(initialSigner);
1315
+ return idx >= 0 ? idx : 0;
1316
+ }
1317
+ return 0;
1318
+ });
1319
+ const initializedRef = useRef4(false);
1306
1320
  const [loading, setLoading] = useState3(false);
1307
1321
  const [submitting, setSubmitting] = useState3(false);
1308
1322
  const [pdfSource, setPdfSource] = useState3(null);
1309
1323
  const [callbackUrl, setCallbackUrl] = useState3(initialCallbackUrl || "");
1310
1324
  const containerRef = useRef4(null);
1325
+ const signerOrder = signerOrderProp || (signerRoles.length > 0 ? [...signerRoles.filter((r) => r !== "Sender"), ...signerRoles.filter((r) => r === "Sender")] : [initialSigner || "Signer 1"]);
1326
+ const signer = signerOrder[currentSignerIndex] || signerOrder[0] || "Signer 1";
1327
+ const isLastSigner = currentSignerIndex >= signerOrder.length - 1;
1328
+ const isMultiSigner = signerOrder.length > 1;
1311
1329
  useEffect3(() => {
1312
1330
  if (initialTemplate) {
1313
1331
  let templateFields = initialTemplate.fields;
1332
+ if (initialTemplate.signerRoles) setSignerRoles(initialTemplate.signerRoles);
1314
1333
  if (initialValues) {
1315
1334
  templateFields = templateFields.map((f) => {
1316
1335
  const byLabel = Object.entries(initialValues).find(
@@ -1321,26 +1340,56 @@ function SignerView({
1321
1340
  return value !== void 0 ? { ...f, value } : f;
1322
1341
  });
1323
1342
  }
1324
- setFields(templateFields);
1343
+ if (initializedRef.current) {
1344
+ setFields((prev) => {
1345
+ const valueMap = new Map(prev.filter((f) => f.value).map((f) => [f.id, f.value]));
1346
+ return templateFields.map((f) => valueMap.has(f.id) ? { ...f, value: valueMap.get(f.id) } : f);
1347
+ });
1348
+ } else {
1349
+ setFields(templateFields);
1350
+ initializedRef.current = true;
1351
+ }
1325
1352
  if (initialPdfUrl) loadPdf(initialPdfUrl);
1326
1353
  return;
1327
1354
  }
1328
1355
  const params = new URLSearchParams(window.location.search);
1329
1356
  const pdfUrl = params.get("pdf");
1330
1357
  const fieldsUrl = params.get("fields");
1331
- const signerParam = params.get("signer");
1332
1358
  const cbUrl = params.get("callbackUrl");
1333
- if (signerParam) setSigner(signerParam);
1334
1359
  if (cbUrl) setCallbackUrl(cbUrl);
1335
1360
  if (pdfUrl) loadPdf(pdfUrl);
1336
1361
  if (fieldsUrl) {
1337
- fetch(fieldsUrl).then((r) => r.json()).then((template) => setFields(template.fields)).catch((err) => console.error("Failed to load fields:", err));
1362
+ fetch(fieldsUrl).then((r) => r.json()).then((template) => {
1363
+ if (template.signerRoles) setSignerRoles(template.signerRoles);
1364
+ if (initializedRef.current) {
1365
+ setFields((prev) => {
1366
+ const valueMap = new Map(prev.filter((f) => f.value).map((f) => [f.id, f.value]));
1367
+ return template.fields.map((f) => valueMap.has(f.id) ? { ...f, value: valueMap.get(f.id) } : f);
1368
+ });
1369
+ } else {
1370
+ setFields(template.fields);
1371
+ initializedRef.current = true;
1372
+ }
1373
+ }).catch((err) => console.error("Failed to load fields:", err));
1338
1374
  }
1339
1375
  const handleMessage = (e) => {
1340
1376
  if (e.data?.type === "load-signing") {
1341
- if (e.data.fields) setFields(e.data.fields);
1377
+ if (e.data.fields) {
1378
+ if (initializedRef.current) {
1379
+ setFields((prev) => {
1380
+ const valueMap = new Map(prev.filter((f) => f.value).map((f) => [f.id, f.value]));
1381
+ return e.data.fields.map((f) => valueMap.has(f.id) ? { ...f, value: valueMap.get(f.id) } : f);
1382
+ });
1383
+ } else {
1384
+ setFields(e.data.fields);
1385
+ initializedRef.current = true;
1386
+ }
1387
+ }
1342
1388
  if (e.data.pdfUrl) loadPdf(e.data.pdfUrl);
1343
- if (e.data.signer) setSigner(e.data.signer);
1389
+ if (e.data.signer) {
1390
+ const idx = signerOrder.indexOf(e.data.signer);
1391
+ if (idx >= 0) setCurrentSignerIndex(idx);
1392
+ }
1344
1393
  if (e.data.callbackUrl) setCallbackUrl(e.data.callbackUrl);
1345
1394
  }
1346
1395
  };
@@ -1385,8 +1434,18 @@ function SignerView({
1385
1434
  if (f.type === "checkbox") return true;
1386
1435
  return !!f.value;
1387
1436
  });
1388
- const handleSubmit = useCallback4(async () => {
1389
- if (!pdfSource || !allRequiredFilled) return;
1437
+ const handleAdvanceOrSubmit = useCallback4(async () => {
1438
+ if (!allRequiredFilled) return;
1439
+ if (!isLastSigner) {
1440
+ setCurrentSignerIndex((prev) => prev + 1);
1441
+ setSelectedFieldId(null);
1442
+ const pdfArea = containerRef.current?.querySelector(".signer-pdf-area");
1443
+ if (pdfArea) {
1444
+ pdfArea.scrollTo({ top: 0, behavior: "smooth" });
1445
+ }
1446
+ return;
1447
+ }
1448
+ if (!pdfSource) return;
1390
1449
  setSubmitting(true);
1391
1450
  try {
1392
1451
  const pdfBytes = await generateFilledPdf(pdfSource, fields);
@@ -1406,7 +1465,7 @@ function SignerView({
1406
1465
  } finally {
1407
1466
  setSubmitting(false);
1408
1467
  }
1409
- }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete]);
1468
+ }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete, isLastSigner]);
1410
1469
  const renderFieldContent = useCallback4((field) => {
1411
1470
  if (field.type === "blackout" || field.type === "whiteout") {
1412
1471
  return null;
@@ -1469,8 +1528,20 @@ function SignerView({
1469
1528
  }));
1470
1529
  }
1471
1530
  }, [fields.filter((f) => f.type === "signature" && f.value).length]);
1531
+ const stepLabel = isLastSigner ? submitLabel || "Complete" : `Next: ${signerOrder[currentSignerIndex + 1]}`;
1472
1532
  return /* @__PURE__ */ jsxs6("div", { className: "signer-layout", ref: containerRef, children: [
1473
1533
  loading && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Loading document..." }),
1534
+ isMultiSigner && /* @__PURE__ */ jsx6("div", { className: "signer-progress-bar", children: signerOrder.map((role, i) => /* @__PURE__ */ jsxs6(
1535
+ "div",
1536
+ {
1537
+ className: `signer-progress-step ${i < currentSignerIndex ? "completed" : ""} ${i === currentSignerIndex ? "active" : ""} ${i > currentSignerIndex ? "upcoming" : ""}`,
1538
+ children: [
1539
+ /* @__PURE__ */ jsx6("div", { className: "signer-progress-dot", children: i < currentSignerIndex ? "\u2713" : i + 1 }),
1540
+ /* @__PURE__ */ jsx6("span", { className: "signer-progress-label", children: role })
1541
+ ]
1542
+ },
1543
+ role
1544
+ )) }),
1474
1545
  /* @__PURE__ */ jsxs6("div", { className: "signer-content", children: [
1475
1546
  /* @__PURE__ */ jsx6("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ jsx6(
1476
1547
  PdfViewer,
@@ -1485,6 +1556,10 @@ function SignerView({
1485
1556
  }
1486
1557
  ) }),
1487
1558
  /* @__PURE__ */ jsxs6("div", { className: "signer-panel", children: [
1559
+ /* @__PURE__ */ jsxs6("div", { className: "signer-role-indicator", children: [
1560
+ /* @__PURE__ */ jsx6("span", { className: "signer-role-indicator-label", children: "Signing as" }),
1561
+ /* @__PURE__ */ jsx6("strong", { children: signer })
1562
+ ] }),
1488
1563
  selectedField && isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-input", children: [
1489
1564
  /* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
1490
1565
  selectedField.required && /* @__PURE__ */ jsx6("span", { className: "required-badge", children: "Required" }),
@@ -1532,16 +1607,16 @@ function SignerView({
1532
1607
  currentFieldId: selectedFieldId,
1533
1608
  onNavigate: handleNavigate,
1534
1609
  allRequiredFilled,
1535
- onSubmit: handleSubmit,
1536
- submitLabel
1610
+ onSubmit: handleAdvanceOrSubmit,
1611
+ submitLabel: stepLabel
1537
1612
  }
1538
1613
  ),
1539
- submitting && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Generating PDF..." })
1614
+ submitting && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Generating PDF..." }),
1615
+ /* @__PURE__ */ jsxs6("div", { className: "powered-by", children: [
1616
+ "Powered by ",
1617
+ /* @__PURE__ */ jsx6("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1618
+ ] })
1540
1619
  ] })
1541
- ] }),
1542
- /* @__PURE__ */ jsxs6("div", { className: "powered-by", children: [
1543
- "Powered by ",
1544
- /* @__PURE__ */ jsx6("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1545
1620
  ] })
1546
1621
  ] });
1547
1622
  }