@unlev/exeq 0.1.7 → 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,22 +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
+ });
1306
1319
  const initializedRef = useRef4(false);
1307
1320
  const [loading, setLoading] = useState3(false);
1308
1321
  const [submitting, setSubmitting] = useState3(false);
1309
1322
  const [pdfSource, setPdfSource] = useState3(null);
1310
1323
  const [callbackUrl, setCallbackUrl] = useState3(initialCallbackUrl || "");
1311
1324
  const containerRef = useRef4(null);
1312
- useEffect3(() => {
1313
- if (initialSigner) {
1314
- setSigner(initialSigner);
1315
- setSelectedFieldId(null);
1316
- }
1317
- }, [initialSigner]);
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;
1318
1329
  useEffect3(() => {
1319
1330
  if (initialTemplate) {
1320
1331
  let templateFields = initialTemplate.fields;
1332
+ if (initialTemplate.signerRoles) setSignerRoles(initialTemplate.signerRoles);
1321
1333
  if (initialValues) {
1322
1334
  templateFields = templateFields.map((f) => {
1323
1335
  const byLabel = Object.entries(initialValues).find(
@@ -1343,13 +1355,12 @@ function SignerView({
1343
1355
  const params = new URLSearchParams(window.location.search);
1344
1356
  const pdfUrl = params.get("pdf");
1345
1357
  const fieldsUrl = params.get("fields");
1346
- const signerParam = params.get("signer");
1347
1358
  const cbUrl = params.get("callbackUrl");
1348
- if (signerParam) setSigner(signerParam);
1349
1359
  if (cbUrl) setCallbackUrl(cbUrl);
1350
1360
  if (pdfUrl) loadPdf(pdfUrl);
1351
1361
  if (fieldsUrl) {
1352
1362
  fetch(fieldsUrl).then((r) => r.json()).then((template) => {
1363
+ if (template.signerRoles) setSignerRoles(template.signerRoles);
1353
1364
  if (initializedRef.current) {
1354
1365
  setFields((prev) => {
1355
1366
  const valueMap = new Map(prev.filter((f) => f.value).map((f) => [f.id, f.value]));
@@ -1375,7 +1386,10 @@ function SignerView({
1375
1386
  }
1376
1387
  }
1377
1388
  if (e.data.pdfUrl) loadPdf(e.data.pdfUrl);
1378
- 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
+ }
1379
1393
  if (e.data.callbackUrl) setCallbackUrl(e.data.callbackUrl);
1380
1394
  }
1381
1395
  };
@@ -1420,8 +1434,18 @@ function SignerView({
1420
1434
  if (f.type === "checkbox") return true;
1421
1435
  return !!f.value;
1422
1436
  });
1423
- const handleSubmit = useCallback4(async () => {
1424
- 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;
1425
1449
  setSubmitting(true);
1426
1450
  try {
1427
1451
  const pdfBytes = await generateFilledPdf(pdfSource, fields);
@@ -1441,7 +1465,7 @@ function SignerView({
1441
1465
  } finally {
1442
1466
  setSubmitting(false);
1443
1467
  }
1444
- }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete]);
1468
+ }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete, isLastSigner]);
1445
1469
  const renderFieldContent = useCallback4((field) => {
1446
1470
  if (field.type === "blackout" || field.type === "whiteout") {
1447
1471
  return null;
@@ -1504,8 +1528,20 @@ function SignerView({
1504
1528
  }));
1505
1529
  }
1506
1530
  }, [fields.filter((f) => f.type === "signature" && f.value).length]);
1531
+ const stepLabel = isLastSigner ? submitLabel || "Complete" : `Next: ${signerOrder[currentSignerIndex + 1]}`;
1507
1532
  return /* @__PURE__ */ jsxs6("div", { className: "signer-layout", ref: containerRef, children: [
1508
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
+ )) }),
1509
1545
  /* @__PURE__ */ jsxs6("div", { className: "signer-content", children: [
1510
1546
  /* @__PURE__ */ jsx6("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ jsx6(
1511
1547
  PdfViewer,
@@ -1520,6 +1556,10 @@ function SignerView({
1520
1556
  }
1521
1557
  ) }),
1522
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
+ ] }),
1523
1563
  selectedField && isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-input", children: [
1524
1564
  /* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
1525
1565
  selectedField.required && /* @__PURE__ */ jsx6("span", { className: "required-badge", children: "Required" }),
@@ -1567,16 +1607,16 @@ function SignerView({
1567
1607
  currentFieldId: selectedFieldId,
1568
1608
  onNavigate: handleNavigate,
1569
1609
  allRequiredFilled,
1570
- onSubmit: handleSubmit,
1571
- submitLabel
1610
+ onSubmit: handleAdvanceOrSubmit,
1611
+ submitLabel: stepLabel
1572
1612
  }
1573
1613
  ),
1574
- 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
+ ] })
1575
1619
  ] })
1576
- ] }),
1577
- /* @__PURE__ */ jsxs6("div", { className: "powered-by", children: [
1578
- "Powered by ",
1579
- /* @__PURE__ */ jsx6("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1580
1620
  ] })
1581
1621
  ] });
1582
1622
  }