@unvired/turboforms-embed-sdk 1.0.11 → 1.0.12

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.
@@ -141,7 +141,7 @@ function sendEventCallback({
141
141
  if (type === "ERROR") {
142
142
  eventStructure = {
143
143
  type: "ERROR",
144
- errorMessage,
144
+ errorMessage: errorMessage + ", contact your admin or tech team",
145
145
  data
146
146
  };
147
147
  } else {
@@ -223,11 +223,11 @@ async function validateAttachments(template, submissionData, sendEventCallback2)
223
223
  if (typeof fileId === "string" && fileId.trim()) {
224
224
  try {
225
225
  const fileRecord = await getFileFromIndexedDB(fileId);
226
- if (!fileRecord || !fileRecord.id || !fileRecord.data || !fileRecord.url) {
226
+ if (!fileRecord || !fileRecord.id || !fileRecord.data && !fileRecord.url) {
227
227
  missingFiles.push({
228
228
  fileId,
229
229
  field: key,
230
- reason: fileRecord ? "Incomplete file record (missing id/data/url)" : "File not found in IndexedDB"
230
+ reason: fileRecord ? "Incomplete file record (missing id and data/url)" : "File not found in IndexedDB"
231
231
  });
232
232
  }
233
233
  } catch (err) {
@@ -281,6 +281,7 @@ function getFileFromIndexedDB(fileId) {
281
281
  // src/lib/FileProcessor.js
282
282
  async function processFileIds(data, sendEventCallback2) {
283
283
  if (!data || typeof data !== "object") return data;
284
+ const clonedData = JSON.parse(JSON.stringify(data));
284
285
  async function traverse(obj) {
285
286
  for (const key in obj) {
286
287
  const value = obj[key];
@@ -298,21 +299,12 @@ async function processFileIds(data, sendEventCallback2) {
298
299
  type: fileObject.type,
299
300
  url: fileObject.url,
300
301
  key,
301
- uid: value[i]
302
+ uid: value[i],
303
+ mode: fileObject.mode || "G"
304
+ // Fix: Preserve the mode or default to G
302
305
  });
303
306
  } else {
304
307
  console.warn(`File not found in IndexedDB: ${value[i]} - removing from submission data`);
305
- if (sendEventCallback2) {
306
- sendEventCallback2({
307
- type: "ERROR",
308
- errorMessage: `ErrorCode : 015, contact your admin or tech team`,
309
- data: {
310
- reason: `File not found in IndexedDB: ${value[i]}`,
311
- fileId: value[i],
312
- field: key
313
- }
314
- });
315
- }
316
308
  }
317
309
  } else if (typeof value[i] === "object" && value[i] !== null) {
318
310
  await traverse(value[i]);
@@ -327,14 +319,25 @@ async function processFileIds(data, sendEventCallback2) {
327
319
  }
328
320
  }
329
321
  }
330
- await traverse(data);
331
- return data;
322
+ await traverse(clonedData);
323
+ return clonedData;
332
324
  }
333
325
  async function getFileFromIndexedDB2(fileId) {
334
326
  return new Promise((resolve) => {
335
327
  const request = indexedDB.open("Forms-Attachments", 3);
328
+ request.onupgradeneeded = (e) => {
329
+ const db = e.target.result;
330
+ if (!db.objectStoreNames.contains("Files")) {
331
+ db.createObjectStore("Files");
332
+ }
333
+ };
336
334
  request.onsuccess = (event) => {
337
335
  const db = event.target.result;
336
+ if (!db.objectStoreNames.contains("Files")) {
337
+ console.warn("Files store not found");
338
+ resolve(null);
339
+ return;
340
+ }
338
341
  const store = db.transaction(["Files"], "readonly").objectStore("Files");
339
342
  const getRequest = store.get(fileId);
340
343
  getRequest.onsuccess = () => resolve(getRequest.result);
@@ -344,6 +347,473 @@ async function getFileFromIndexedDB2(fileId) {
344
347
  });
345
348
  }
346
349
 
350
+ // src/lib/dynamicValueProcessor.js
351
+ function processDynamicValues(formJson, appData) {
352
+ if (!formJson || !formJson.components) return null;
353
+ let dataMap = {};
354
+ const hasAppData = appData && (Array.isArray(appData) && appData.length > 0 || !Array.isArray(appData) && Object.keys(appData).length > 0);
355
+ if (hasAppData) {
356
+ if (Array.isArray(appData)) {
357
+ appData.forEach((item) => {
358
+ if (item.key) dataMap[item.key] = item.value;
359
+ });
360
+ } else {
361
+ dataMap = appData || {};
362
+ }
363
+ }
364
+ function processComponents(comps) {
365
+ if (!comps || !Array.isArray(comps)) return null;
366
+ for (const comp of comps) {
367
+ if (typeof comp.defaultValue === "string" && comp.defaultValue.trim().startsWith("{{") && comp.defaultValue.trim().endsWith("}}")) {
368
+ const key = comp.defaultValue.trim();
369
+ if (hasAppData && dataMap[key] !== void 0) {
370
+ comp.defaultValue = dataMap[key];
371
+ }
372
+ }
373
+ let err;
374
+ if (comp.components) {
375
+ err = processComponents(comp.components);
376
+ if (err) return err;
377
+ }
378
+ if (comp.columns && Array.isArray(comp.columns)) {
379
+ for (const col of comp.columns) {
380
+ err = processComponents(col.components);
381
+ if (err) return err;
382
+ }
383
+ }
384
+ if (comp.rows && Array.isArray(comp.rows)) {
385
+ for (const row of comp.rows) {
386
+ if (Array.isArray(row)) {
387
+ for (const cell of row) {
388
+ err = processComponents(cell.components);
389
+ if (err) return err;
390
+ }
391
+ }
392
+ }
393
+ }
394
+ }
395
+ return null;
396
+ }
397
+ return processComponents(formJson.components);
398
+ }
399
+
400
+ // src/lib/appDocuments.js
401
+ var DB_NAME = "AppDocuments";
402
+ var STORE_NAME = "documents";
403
+ var STORE_NAME_TYPO = "documnets";
404
+ function openDB(version) {
405
+ if (version) {
406
+ return new Promise((resolve, reject) => {
407
+ const request = indexedDB.open(DB_NAME, version);
408
+ request.onupgradeneeded = (event) => {
409
+ const db = event.target.result;
410
+ if (!db.objectStoreNames.contains(STORE_NAME) && !db.objectStoreNames.contains(STORE_NAME_TYPO)) {
411
+ db.createObjectStore(STORE_NAME, { keyPath: "id", autoIncrement: true });
412
+ }
413
+ };
414
+ request.onsuccess = (event) => resolve(event.target.result);
415
+ request.onerror = (event) => {
416
+ console.error("AppDocuments DB error:", event.target.error);
417
+ reject(event.target.error);
418
+ };
419
+ });
420
+ }
421
+ if (typeof window !== "undefined" && window.indexedDBUtils && window.indexedDBUtils.openDynamicDB) {
422
+ return window.indexedDBUtils.openDynamicDB(DB_NAME, STORE_NAME, STORE_NAME_TYPO);
423
+ }
424
+ return new Promise((resolve, reject) => {
425
+ const checkRequest = indexedDB.open(DB_NAME);
426
+ checkRequest.onupgradeneeded = (e) => {
427
+ const db = e.target.result;
428
+ if (!db.objectStoreNames.contains(STORE_NAME) && !db.objectStoreNames.contains(STORE_NAME_TYPO)) {
429
+ db.createObjectStore(STORE_NAME, { keyPath: "id", autoIncrement: true });
430
+ }
431
+ };
432
+ checkRequest.onsuccess = (e) => {
433
+ const db = e.target.result;
434
+ if (db.objectStoreNames.contains(STORE_NAME) || db.objectStoreNames.contains(STORE_NAME_TYPO)) {
435
+ resolve(db);
436
+ return;
437
+ }
438
+ const currentVersion = db.version;
439
+ const newVersion = currentVersion + 1;
440
+ db.close();
441
+ console.log(`[AppDocuments] Store missing. Upgrading from v${currentVersion} to v${newVersion}.`);
442
+ const upgradeRequest = indexedDB.open(DB_NAME, newVersion);
443
+ upgradeRequest.onupgradeneeded = (evt) => {
444
+ const upgradeDb = evt.target.result;
445
+ if (!upgradeDb.objectStoreNames.contains(STORE_NAME)) {
446
+ upgradeDb.createObjectStore(STORE_NAME, { keyPath: "id", autoIncrement: true });
447
+ }
448
+ };
449
+ upgradeRequest.onsuccess = (evt) => resolve(evt.target.result);
450
+ upgradeRequest.onerror = (evt) => {
451
+ console.error("[AppDocuments] DB Upgrade failed:", evt);
452
+ reject(evt);
453
+ };
454
+ };
455
+ checkRequest.onerror = (e) => {
456
+ console.error("[AppDocuments] DB Open failed:", e);
457
+ reject(e.target.error);
458
+ };
459
+ });
460
+ }
461
+ function getStoreName(db) {
462
+ if (db.objectStoreNames.contains(STORE_NAME)) return STORE_NAME;
463
+ if (db.objectStoreNames.contains(STORE_NAME_TYPO)) return STORE_NAME_TYPO;
464
+ return null;
465
+ }
466
+ async function hasDocuments() {
467
+ try {
468
+ const db = await openDB();
469
+ const actualStoreName = getStoreName(db);
470
+ if (!actualStoreName) {
471
+ console.warn(`AppDocuments DB found but no '${STORE_NAME}' or '${STORE_NAME_TYPO}' store exists.`);
472
+ db.close();
473
+ return false;
474
+ }
475
+ return new Promise((resolve) => {
476
+ const transaction = db.transaction([actualStoreName], "readonly");
477
+ const store = transaction.objectStore(actualStoreName);
478
+ const countRequest = store.count();
479
+ countRequest.onsuccess = () => {
480
+ console.log(`[AppDocuments] Count in ${actualStoreName}:`, countRequest.result);
481
+ db.close();
482
+ resolve(countRequest.result > 0);
483
+ };
484
+ countRequest.onerror = (e) => {
485
+ console.error("[AppDocuments] Error counting:", e);
486
+ db.close();
487
+ resolve(false);
488
+ };
489
+ });
490
+ } catch (e) {
491
+ console.error("[AppDocuments] Error checking documents:", e);
492
+ return false;
493
+ }
494
+ }
495
+ async function getAllDocuments() {
496
+ try {
497
+ const db = await openDB();
498
+ const actualStoreName = getStoreName(db);
499
+ if (!actualStoreName) {
500
+ db.close();
501
+ return [];
502
+ }
503
+ return new Promise((resolve) => {
504
+ const transaction = db.transaction([actualStoreName], "readonly");
505
+ const store = transaction.objectStore(actualStoreName);
506
+ const request = store.getAll();
507
+ request.onsuccess = () => {
508
+ db.close();
509
+ resolve(request.result);
510
+ };
511
+ request.onerror = () => {
512
+ db.close();
513
+ resolve([]);
514
+ };
515
+ });
516
+ } catch (e) {
517
+ console.error("Error getting documents:", e);
518
+ return [];
519
+ }
520
+ }
521
+ async function insertDocument(doc) {
522
+ try {
523
+ let db = await openDB();
524
+ let actualStoreName = getStoreName(db);
525
+ if (!actualStoreName) {
526
+ db.close();
527
+ throw new Error("Failed to open database with valid store.");
528
+ }
529
+ return new Promise((resolve, reject) => {
530
+ const transaction = db.transaction([actualStoreName], "readwrite");
531
+ const store = transaction.objectStore(actualStoreName);
532
+ const request = store.add(doc);
533
+ request.onsuccess = () => {
534
+ db.close();
535
+ resolve(request.result);
536
+ };
537
+ request.onerror = (e) => {
538
+ db.close();
539
+ reject(e.target.error);
540
+ };
541
+ });
542
+ } catch (e) {
543
+ console.error("Error inserting document:", e);
544
+ throw e;
545
+ }
546
+ }
547
+ async function deleteDocument(id) {
548
+ try {
549
+ const db = await openDB();
550
+ const actualStoreName = getStoreName(db);
551
+ if (!actualStoreName) {
552
+ db.close();
553
+ console.error("No valid document store found for deletion");
554
+ return;
555
+ }
556
+ return new Promise((resolve, reject) => {
557
+ const transaction = db.transaction([actualStoreName], "readwrite");
558
+ const store = transaction.objectStore(actualStoreName);
559
+ const request = store.delete(id);
560
+ request.onsuccess = () => {
561
+ db.close();
562
+ resolve();
563
+ };
564
+ request.onerror = (e) => {
565
+ db.close();
566
+ reject(e.target.error);
567
+ };
568
+ });
569
+ } catch (e) {
570
+ console.error("Error deleting document:", e);
571
+ throw e;
572
+ }
573
+ }
574
+
575
+ // src/html-template/utils/openDocumentsModal.js
576
+ var DB_NAME2 = "AppDocuments";
577
+ var STORE_NAME2 = "documents";
578
+ var STORE_NAME_TYPO2 = "documnets";
579
+ function openDB2() {
580
+ return new Promise((resolve, reject) => {
581
+ const request = indexedDB.open(DB_NAME2);
582
+ request.onsuccess = (event) => resolve(event.target.result);
583
+ request.onerror = (event) => reject(event.target.error);
584
+ });
585
+ }
586
+ function getStoreName2(db) {
587
+ if (db.objectStoreNames.contains(STORE_NAME2)) return STORE_NAME2;
588
+ if (db.objectStoreNames.contains(STORE_NAME_TYPO2)) return STORE_NAME_TYPO2;
589
+ return null;
590
+ }
591
+ async function getAllDocuments2() {
592
+ try {
593
+ const db = await openDB2();
594
+ const actualStoreName = getStoreName2(db);
595
+ if (!actualStoreName) {
596
+ db.close();
597
+ return [];
598
+ }
599
+ return new Promise((resolve) => {
600
+ const transaction = db.transaction([actualStoreName], "readonly");
601
+ const store = transaction.objectStore(actualStoreName);
602
+ const request = store.getAll();
603
+ request.onsuccess = () => {
604
+ db.close();
605
+ resolve(request.result);
606
+ };
607
+ request.onerror = () => {
608
+ db.close();
609
+ resolve([]);
610
+ };
611
+ });
612
+ } catch (e) {
613
+ console.warn("Error reading AppDocuments DB:", e);
614
+ return [];
615
+ }
616
+ }
617
+ async function openDocumentsModal() {
618
+ if (typeof window.showDynamicModal !== "function") {
619
+ console.error("showDynamicModal is not defined.");
620
+ return;
621
+ }
622
+ window.showDynamicModal("App Documents", [], `
623
+ <div style="text-align: center; padding: 40px;">
624
+ <i class="fa fa-spinner fa-spin" style="font-size: 30px; color: #2185d0;"></i>
625
+ <div style="margin-top: 15px; color: #666; font-size: 16px;">Loading documents...</div>
626
+ </div>
627
+ `);
628
+ try {
629
+ const docs = await getAllDocuments2();
630
+ let bodyContent = "";
631
+ if (docs && docs.length > 0) {
632
+ bodyContent = `<div class="ui middle aligned divided list" style="width:100%;margin:0;">`;
633
+ docs.forEach((doc) => {
634
+ const name = doc.name || doc.title || "Untitled Document";
635
+ const desc = doc.description || "";
636
+ let metaInfo = "";
637
+ let iconClass = "file outline";
638
+ if (doc.type) {
639
+ if (doc.type.includes("pdf")) iconClass = "file pdf outline red";
640
+ else if (doc.type.includes("image")) iconClass = "file image outline blue";
641
+ else if (doc.type.includes("text")) iconClass = "file text outline grey";
642
+ }
643
+ if (Number.isInteger(doc.id)) {
644
+ metaInfo = new Date(doc.id).toLocaleDateString();
645
+ }
646
+ bodyContent += `
647
+ <div class="item" style="padding:12px 10px;display:flex;align-items:center;">
648
+ <i class="${iconClass} icon large" style="margin-right:15px;"></i>
649
+
650
+ <div class="content" style="flex:1;min-width:0;">
651
+ <div class="header" style="white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">
652
+ ${name}
653
+ </div>
654
+ ${desc ? `<div class="description" style="font-size:12px;color:#666;">${desc}</div>` : ""}
655
+ ${metaInfo ? `<div class="meta" style="font-size:11px;color:#999;">${metaInfo}</div>` : ""}
656
+ </div>
657
+
658
+ <button class="ui button mini basic primary"
659
+ onclick="window.downloadDocument(${doc.id})">
660
+ <i class="download icon"></i> Download
661
+ </button>
662
+ </div>`;
663
+ });
664
+ bodyContent += `</div>`;
665
+ } else {
666
+ bodyContent = `
667
+ <div style="text-align:center;padding:50px;color:#999;">
668
+ <i class="folder open outline icon" style="font-size:40px;"></i>
669
+ <div>No documents available</div>
670
+ </div>`;
671
+ }
672
+ window.showDynamicModal(
673
+ "App Documents",
674
+ [{ label: "Close", callback: () => {
675
+ }, className: "ui button" }],
676
+ bodyContent
677
+ );
678
+ if (!window.downloadDocument) {
679
+ window.downloadDocument = async function(id) {
680
+ const all = await getAllDocuments2();
681
+ const found = all.find((d) => d.id == id);
682
+ if (!found) {
683
+ console.log("\u274C Document not found.");
684
+ if (window.sendEventCallback) {
685
+ window.sendEventCallback({
686
+ type: "ERROR",
687
+ errorMessage: "ErrorCode : 017, Document not found",
688
+ data: { reason: "Document not found", technicalError: null }
689
+ });
690
+ }
691
+ return;
692
+ }
693
+ console.log("Downloading:", found);
694
+ let incomingData = found.url || found.data || found.file && found.file.data;
695
+ let downloadUrl = null;
696
+ if (incomingData && incomingData.startsWith("data:")) {
697
+ const comma = incomingData.indexOf(",");
698
+ if (comma === -1) {
699
+ console.log("\u274C Invalid file format.");
700
+ if (window.sendEventCallback) {
701
+ window.sendEventCallback({
702
+ type: "ERROR",
703
+ errorMessage: "ErrorCode : 018, Invalid file format",
704
+ data: { reason: "Invalid file format", technicalError: null }
705
+ });
706
+ }
707
+ return;
708
+ }
709
+ const header = incomingData.substring(0, comma);
710
+ let base64 = incomingData.substring(comma + 1);
711
+ const mimeMatch = header.match(/:(.*?);/);
712
+ const mime = mimeMatch ? mimeMatch[1] : "application/octet-stream";
713
+ base64 = base64.replace(/\s/g, "");
714
+ if (!/^[A-Za-z0-9+/=]+$/.test(base64)) {
715
+ console.error("Invalid base64:", base64.slice(0, 50));
716
+ console.log("\u274C File is corrupted (invalid base64).");
717
+ if (window.sendEventCallback) {
718
+ window.sendEventCallback({
719
+ type: "ERROR",
720
+ errorMessage: "ErrorCode : 019, File is corrupted (invalid base64)",
721
+ data: { reason: "File is corrupted (invalid base64)", technicalError: null }
722
+ });
723
+ }
724
+ return;
725
+ }
726
+ try {
727
+ const binary = atob(base64);
728
+ const bytes = new Uint8Array(binary.length);
729
+ for (let i = 0; i < binary.length; i++) {
730
+ bytes[i] = binary.charCodeAt(i);
731
+ }
732
+ const blob = new Blob([bytes], { type: mime });
733
+ if (!blob.size) {
734
+ console.log("\u274C File is empty.");
735
+ if (window.sendEventCallback) {
736
+ window.sendEventCallback({
737
+ type: "ERROR",
738
+ errorMessage: "ErrorCode : 020, File is empty",
739
+ data: { reason: "File is empty", technicalError: null }
740
+ });
741
+ }
742
+ return;
743
+ }
744
+ downloadUrl = URL.createObjectURL(blob);
745
+ } catch (err) {
746
+ console.error("Base64 decode failed:", err);
747
+ if (err.name === "InvalidCharacterError") {
748
+ console.log("\u274C File is corrupted (invalid encoding).");
749
+ if (window.sendEventCallback) {
750
+ window.sendEventCallback({
751
+ type: "ERROR",
752
+ errorMessage: "ErrorCode : 021, File is corrupted (invalid encoding)",
753
+ data: { reason: "File is corrupted (invalid encoding)", technicalError: err }
754
+ });
755
+ }
756
+ } else {
757
+ console.log("\u274C Failed to process file.");
758
+ if (window.sendEventCallback) {
759
+ window.sendEventCallback({
760
+ type: "ERROR",
761
+ errorMessage: "ErrorCode : 022, Failed to process file",
762
+ data: { reason: "Failed to process file", technicalError: err }
763
+ });
764
+ }
765
+ }
766
+ return;
767
+ }
768
+ } else if (found.file instanceof Blob) {
769
+ if (!found.file.size) {
770
+ console.log("\u274C File is empty.");
771
+ if (window.sendEventCallback) {
772
+ window.sendEventCallback({
773
+ type: "ERROR",
774
+ errorMessage: "ErrorCode : 023, File is empty",
775
+ data: { reason: "File is empty", technicalError: null }
776
+ });
777
+ }
778
+ return;
779
+ }
780
+ downloadUrl = URL.createObjectURL(found.file);
781
+ } else {
782
+ console.log("\u274C No downloadable content found.");
783
+ if (window.sendEventCallback) {
784
+ window.sendEventCallback({
785
+ type: "ERROR",
786
+ errorMessage: "ErrorCode : 024, No downloadable content found",
787
+ data: { reason: "No downloadable content found", technicalError: null }
788
+ });
789
+ }
790
+ return;
791
+ }
792
+ const a = document.createElement("a");
793
+ a.href = downloadUrl;
794
+ a.download = found.name || "document";
795
+ document.body.appendChild(a);
796
+ a.click();
797
+ setTimeout(() => {
798
+ URL.revokeObjectURL(downloadUrl);
799
+ a.remove();
800
+ }, 1e4);
801
+ };
802
+ }
803
+ } catch (err) {
804
+ console.error("Load failed", err);
805
+ console.log("\u274C Failed to load documents.");
806
+ if (window.sendEventCallback) {
807
+ window.sendEventCallback({
808
+ type: "ERROR",
809
+ errorMessage: "ErrorCode : 025, Failed to load documents",
810
+ data: { reason: "Failed to load documents", technicalError: err }
811
+ });
812
+ }
813
+ }
814
+ }
815
+ window.openDocumentsModal = openDocumentsModal;
816
+
347
817
  // src/__temp_build.js
348
818
  async function loadUnviredForms({
349
819
  formsData = "formIOComponent",
@@ -353,33 +823,37 @@ async function loadUnviredForms({
353
823
  options = {
354
824
  nestedFormData: [{}],
355
825
  masterData: [{}],
356
- title: "the form title entered",
357
- descrciption: "the form description entered",
358
826
  mode: "",
359
- privateExternal: "",
360
- permission: "",
361
827
  platform: "",
828
+ permission: "",
362
829
  language: "",
830
+ privateExternal: "",
831
+ title: "the form title entered",
832
+ descrciption: "the form description entered",
363
833
  translations: {},
364
- environmentVariable: [],
365
834
  themeData: {},
366
835
  userData: {},
367
- appData: {},
368
836
  controlData: {},
369
- showBackButton: false,
370
- showMoreButton: true,
371
837
  commentsData: [],
372
838
  vobArr: [],
839
+ appData: [],
840
+ environmentVariable: [],
373
841
  formioLibPath: {
374
842
  formioPath: "assets/formio.full.min.js"
375
843
  },
376
- showLoader: true
844
+ showComments: false,
845
+ showHelp: false,
846
+ showBackButton: false,
847
+ showLoader: true,
848
+ showMoreButton: true,
849
+ showCompleteAlways: false,
850
+ requireCompleteForm: true
377
851
  }
378
852
  }) {
379
853
  var _a, _b;
380
854
  let fileKeys = [];
381
855
  let formJson;
382
- console.log("0000000000", formJson);
856
+ console.log("data recive step 1", formsData, submissionData, options);
383
857
  function sendEventCallback2(params) {
384
858
  const eventStructure = sendEventCallback(params);
385
859
  if (typeof eventCallback === "function") {
@@ -396,6 +870,7 @@ async function loadUnviredForms({
396
870
  });
397
871
  return;
398
872
  }
873
+ processDynamicValues(formJson, options.appData);
399
874
  const template = formJson;
400
875
  const nestedFormArrTemp = options.nestedFormData || [];
401
876
  const masterDataArrTemp = options.masterData || [];
@@ -433,13 +908,17 @@ async function loadUnviredForms({
433
908
  "masterdata"
434
909
  );
435
910
  if (submissionData && template) {
436
- const r = await validateAttachments(
437
- mergedWithMasterData,
438
- submissionData,
439
- sendEventCallback2
440
- );
441
- fileKeys = r.fileKeys;
442
- if (r.missingFiles.length > 0) {
911
+ try {
912
+ const r = await validateAttachments(
913
+ mergedWithMasterData,
914
+ submissionData,
915
+ sendEventCallback2
916
+ );
917
+ fileKeys = r.fileKeys;
918
+ if (r.missingFiles.length > 0) {
919
+ }
920
+ } catch (error) {
921
+ console.warn("Attachment validation skipped:", error);
443
922
  }
444
923
  }
445
924
  if ((options == null ? void 0 : options.appData) && ((_a = Object.keys(options == null ? void 0 : options.appData)) == null ? void 0 : _a.length) > 0) {
@@ -1158,6 +1637,14 @@ body {
1158
1637
  /* subtle shadow for elevation */
1159
1638
  }
1160
1639
 
1640
+
1641
+ #submitBtn,
1642
+ #saveBtn {
1643
+ width: 110px;
1644
+ display: flex;
1645
+ justify-content: space-evenly;
1646
+ }
1647
+
1161
1648
  .form-cmt-btn {
1162
1649
  width: 40px;
1163
1650
  height: 40px;
@@ -1193,8 +1680,9 @@ body {
1193
1680
  width: 100%;
1194
1681
  background-color: #ffffff;
1195
1682
  padding: 6px;
1196
- justify-content: space-between;
1683
+ justify-content: flex-end;
1197
1684
  align-items: center;
1685
+ gap: 8px;
1198
1686
  border-top: 1px solid #ddd;
1199
1687
  z-index: 1000;
1200
1688
  box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.05);
@@ -1206,18 +1694,20 @@ body {
1206
1694
  display: none;
1207
1695
  position: absolute;
1208
1696
  bottom: 100%;
1209
- left: 0;
1210
- margin-bottom: 10px;
1697
+ left: -170px;
1698
+ margin-bottom: 16px;
1211
1699
  background-color: #fff;
1212
1700
  border: 1px solid #ddd;
1213
1701
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
1214
1702
  padding: 6px;
1215
1703
  border-radius: 6px;
1216
- z-index: 9999;
1704
+ z-index: 1000;
1217
1705
  flex-direction: column;
1218
1706
  gap: 0px;
1219
1707
  }
1220
1708
 
1709
+ #unvired-more-btn {}
1710
+
1221
1711
  /* Print and PDF Modes */
1222
1712
  @media print {
1223
1713
 
@@ -1429,7 +1919,6 @@ body {
1429
1919
 
1430
1920
  @keyframes sdk-pulse {
1431
1921
 
1432
- 0%,
1433
1922
  100% {
1434
1923
  opacity: 1;
1435
1924
  }
@@ -1439,6 +1928,48 @@ body {
1439
1928
  }
1440
1929
  }
1441
1930
 
1931
+ /* Responsive Footer for Mobile */
1932
+ @media (max-width: 600px) {
1933
+ #sticky-footer {
1934
+ justify-content: flex-end;
1935
+ padding: 4px;
1936
+ gap: 4px;
1937
+ }
1938
+
1939
+ #submitBtn {
1940
+
1941
+ width: 105px;
1942
+ min-width: 108px;
1943
+ flex-grow: 0;
1944
+ }
1945
+
1946
+
1947
+ #saveBtn {
1948
+ width: 80px;
1949
+ min-width: 90px;
1950
+ flex-grow: 0;
1951
+ }
1952
+
1953
+ #unvired-more-btn {
1954
+ margin-left: 10px;
1955
+ margin-right: 10px;
1956
+ }
1957
+
1958
+ /* Fix tooltip on mobile to prevent cutoff */
1959
+ #moreTooltip {
1960
+ position: fixed;
1961
+ bottom: 70px;
1962
+ left: 20px;
1963
+ right: 20px;
1964
+ width: auto;
1965
+ margin-bottom: 0;
1966
+ max-width: 300px;
1967
+ margin-left: auto;
1968
+ margin-right: auto;
1969
+
1970
+ }
1971
+ }
1972
+
1442
1973
  /* === STYLE_SEPARATOR === */
1443
1974
 
1444
1975
  .r6o-drawing{cursor:none}.r6o-relations-layer.readonly .handle rect{pointer-events:none}.r6o-relations-layer{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.r6o-relations-layer circle{stroke:#515151;stroke-width:.4;fill:#3f3f3f}.r6o-relations-layer path{stroke:#595959;stroke-linecap:round;stroke-linejoin:round;fill:transparent}.r6o-relations-layer path.connection{stroke-width:1.6;stroke-dasharray:2,3}.r6o-relations-layer path.r6o-arrow{stroke-width:1.8;fill:#7f7f7f}.r6o-relations-layer .handle rect{stroke-width:1;stroke:#595959;fill:#fff;pointer-events:auto;cursor:pointer}.r6o-relations-layer .handle text{font-size:10px}.r6o-relations-layer .hover{stroke:rgba(63,63,63,.9);stroke-width:1.4;fill:transparent}
@@ -27129,7 +27660,9 @@ select.ui.dropdown {
27129
27660
  /* -----------------
27130
27661
  Floating label end
27131
27662
  -------------------- */
27132
- `.split("\n\n/* === STYLE_SEPARATOR === */\n\n");
27663
+ `.split(
27664
+ "\n\n/* === STYLE_SEPARATOR === */\n\n"
27665
+ );
27133
27666
  cssContents.forEach((cssContent, index) => {
27134
27667
  if (cssContent.trim()) {
27135
27668
  const styleElement = document.createElement("style");
@@ -27201,6 +27734,34 @@ select.ui.dropdown {
27201
27734
  0%, 100% { opacity: 1; }
27202
27735
  50% { opacity: 0.5; }
27203
27736
  }
27737
+ .sdk-loader-back-btn {
27738
+ position: absolute;
27739
+ top: 20px;
27740
+ left: 20px;
27741
+ background: transparent;
27742
+ border: none;
27743
+ color: #333;
27744
+ font-size: 1.2rem;
27745
+ cursor: pointer;
27746
+ padding: 10px;
27747
+ border-radius: 50%;
27748
+ transition: background 0.2s ease;
27749
+ pointer-events: auto;
27750
+ display: flex;
27751
+ align-items: center;
27752
+ justify-content: center;
27753
+ margin: 0;
27754
+ width: 40px;
27755
+ height: 40px;
27756
+ }
27757
+ .sdk-loader-back-btn:hover {
27758
+ background: rgba(0, 0, 0, 0.05);
27759
+ color: #000;
27760
+ border: none;
27761
+ }
27762
+ .sdk-loader-back-btn i {
27763
+ margin: 0;
27764
+ }
27204
27765
  `;
27205
27766
  document.head.appendChild(additionalStyle);
27206
27767
  }
@@ -27229,28 +27790,52 @@ select.ui.dropdown {
27229
27790
  <div id="formio-cmt" style="margin-bottom: 20px;"></div>
27230
27791
  </div>
27231
27792
  <div id="sticky-footer">
27793
+ <button class="ui button primary dataGrid-addRow" id="saveBtn" disabled="true" onclick="FormOnSave()">
27794
+ <i class="icon save large"></i>Save
27795
+ </button>
27796
+ <button class="ui button primary dataGrid-addRow" id="submitBtn" style="display:none" onclick="FormOnSubmit()">
27797
+ <i class="icon save large"></i>Submit
27798
+ </button>
27799
+
27800
+ <!-- Quick Action Buttons -->
27801
+ <div id="quick-actions" style="display: flex; align-items: center;">
27802
+ <i id="quickCompleteBtn" class="icon clipboard check large" style="display:none; cursor: pointer; margin: 0 10px;" onclick="FormOnSubmit()" title="Complete"></i>
27803
+ <i id="quickSaveBtn" class="icon save large" style="display:none; cursor: pointer; margin: 0 10px;" onclick="FormOnSave()" title="Save"></i>
27804
+ <i id="quickDocumentsBtn" class="icon file alternate large" style="display:none; cursor: pointer; margin: 0 10px;" onclick="openDocumentsModal()" title="Documents"></i>
27805
+ <i id="quickCommentsBtn" class="icon comment large" style="display:none; cursor: pointer; margin: 0 10px;" onclick="loadComments()" title="Comments"></i>
27806
+ <i id="quickHelpBtn" class="icon question circle large" style="display:none; cursor: pointer; margin: 0 10px;" onclick="openHelpLink()" title="Help"></i>
27807
+ </div>
27808
+
27232
27809
  <div class="relative-position">
27233
- <button id="moreBtn" class="ui button primary dataGrid-addRow" onclick="toggleTooltip()">
27234
- <i class="icon options"></i>More
27810
+ <button class="ui button primary " id="unvired-more-btn" onclick="toggleTooltip(event)">
27811
+ <i class="icon bars large" style="margin: 0;"></i>
27235
27812
  </button>
27236
27813
  <div id="moreTooltip" style="display: none;" class="ui vertical menu">
27237
27814
  <div class="item" id="completeOption" onclick="FormOnSubmit()" style="display: none;">Complete</div>
27238
- <div class="divider" id="completeDivider" style="display: none;"></div>
27815
+ <div class="divider"></div>
27239
27816
  <div class="item" id="saveOption" onclick="FormOnSave()" style="display: none;">Save</div>
27240
- <div class="divider" id="saveDivider" style="display: none;"></div>
27241
- <div class="item">Documents</div>
27242
- <div class="divider" id="comments-divider" style="display: none;"></div>
27817
+ <div class="divider"></div>
27818
+ <div class="item" id="documentsOption" onclick="openDocumentsModal()" style="display: none;">Documents</div>
27819
+ <div class="divider"></div>
27243
27820
  <div class="item" id="comments-item" style="display: none;" onclick="loadComments()">Comments</div>
27244
27821
  <div class="divider"></div>
27245
- <div class="item" onclick="openHelpLink()">Help</div>
27822
+ <div class="item" id="help-item" style="display: none;" onclick="openHelpLink()">Help</div>
27246
27823
  </div>
27247
27824
  </div>
27248
- <button class="ui button primary dataGrid-addRow" id="saveBtn" disabled="true" onclick="FormOnSave()">
27249
- <i class="icon save"></i>Save
27250
- </button>
27251
- <button class="ui button primary dataGrid-addRow" id="submitBtn" style="display:none" onclick="FormOnSubmit()">
27252
- <i class="icon save"></i>Submit
27253
- </button>
27825
+ </div>
27826
+ <div class="ui modal" id="documentsModal">
27827
+ <i class="close icon"></i>
27828
+ <div class="header">
27829
+ App Documents
27830
+ </div>
27831
+ <div class="content">
27832
+ <div class="ui relaxed divided list" id="documentsList">
27833
+ <div class="item">Loading...</div>
27834
+ </div>
27835
+ </div>
27836
+ <div class="actions">
27837
+ <div class="ui button" onclick="closeDocumentsModal()">Close</div>
27838
+ </div>
27254
27839
  </div>
27255
27840
  `;
27256
27841
  let loaderElement = null;
@@ -27261,23 +27846,48 @@ select.ui.dropdown {
27261
27846
  <div class="sdk-loader-spinner"></div>
27262
27847
  <div class="sdk-loader-text">Loading Form...</div>
27263
27848
  <div class="sdk-loader-subtext">Please wait while we prepare your form</div>
27849
+ <button class="form-back-btn sdk-loader-back-btn" onclick="FormOnBackLoading()">
27850
+ <i class="icon arrow left"></i>
27851
+ </button>
27264
27852
  `;
27265
27853
  container.appendChild(loaderElement);
27266
27854
  }
27267
- window.toggleTooltip = function() {
27855
+ window.toggleTooltip = function(event) {
27856
+ if (event) {
27857
+ event.stopPropagation();
27858
+ }
27268
27859
  const tooltip = document.getElementById("moreTooltip");
27269
- const commentsDivider = document.getElementById("comments-divider");
27270
27860
  const commentsItem = document.getElementById("comments-item");
27861
+ const helpItem = document.getElementById("help-item");
27862
+ if (options.showHelp) {
27863
+ helpItem.style.display = "block";
27864
+ } else {
27865
+ helpItem.style.display = "none";
27866
+ }
27867
+ if (options.showComments) {
27868
+ commentsItem.style.display = "block";
27869
+ } else {
27870
+ commentsItem.style.display = "none";
27871
+ }
27271
27872
  if (tooltip.style.display === "none") {
27272
27873
  tooltip.style.display = "flex";
27273
- setTimeout(() => {
27274
- commentsDivider.style.display = "";
27275
- commentsItem.style.display = "";
27276
- }, 500);
27874
+ tooltip.style.flexDirection = "column";
27277
27875
  } else {
27278
27876
  tooltip.style.display = "none";
27279
- commentsDivider.style.display = "none";
27280
- commentsItem.style.display = "none";
27877
+ return;
27878
+ }
27879
+ const items = Array.from(tooltip.querySelectorAll(".item"));
27880
+ const dividers = Array.from(tooltip.querySelectorAll(".divider"));
27881
+ dividers.forEach((d) => d.style.display = "none");
27882
+ const visibleItems = items.filter((item) => {
27883
+ return item.style.display !== "none" && item.offsetParent !== null;
27884
+ }).filter((item) => item.style.display !== "none");
27885
+ for (let i = 0; i < visibleItems.length - 1; i++) {
27886
+ const item = visibleItems[i];
27887
+ let next = item.nextElementSibling;
27888
+ if (next && next.classList.contains("divider")) {
27889
+ next.style.display = "block";
27890
+ }
27281
27891
  }
27282
27892
  };
27283
27893
  window.openHelpLink = function() {
@@ -27292,36 +27902,46 @@ select.ui.dropdown {
27292
27902
  }
27293
27903
  };
27294
27904
  document.addEventListener("click", function(event) {
27295
- const moreBtn2 = document.getElementById("moreBtn");
27905
+ const moreBtn2 = document.getElementById("unvired-more-btn");
27296
27906
  const tooltip = document.getElementById("moreTooltip");
27297
27907
  if (moreBtn2 && tooltip && !moreBtn2.contains(event.target) && !tooltip.contains(event.target)) {
27298
27908
  tooltip.style.display = "none";
27299
27909
  }
27300
27910
  });
27301
- window.loadComments = function() {
27911
+ window.loadComments = window.loadComments || function() {
27302
27912
  if (typeof window.loadCommentsFunction === "function") {
27303
27913
  window.loadCommentsFunction();
27304
27914
  }
27305
27915
  };
27306
- window.FormOnBack = function() {
27916
+ window.FormOnBack = window.FormOnBack || function() {
27307
27917
  sendEventCallback2({ type: "FORM_BACK_NAVIGATION" });
27308
27918
  };
27309
- window.CommentOnBack = function() {
27919
+ window.FormOnBackLoading = window.FormOnBackLoading || function() {
27920
+ console.log("Back button clicked during loading, canceling form load");
27921
+ if (loaderElement) {
27922
+ loaderElement.classList.add("hidden");
27923
+ if (loaderElement.parentNode) {
27924
+ loaderElement.parentNode.removeChild(loaderElement);
27925
+ }
27926
+ }
27927
+ sendEventCallback2({ type: "FORM_BACK_NAVIGATION" });
27928
+ };
27929
+ window.CommentOnBack = window.CommentOnBack || function() {
27310
27930
  if (typeof window.commentOnBackFunction === "function") {
27311
27931
  window.commentOnBackFunction();
27312
27932
  }
27313
27933
  };
27314
- window.submitComments = function() {
27934
+ window.submitComments = window.submitComments || function() {
27315
27935
  if (typeof window.submitCommentsFunction === "function") {
27316
27936
  window.submitCommentsFunction();
27317
27937
  }
27318
27938
  };
27319
- window.FormOnSave = function() {
27939
+ window.FormOnSave = window.FormOnSave || function() {
27320
27940
  if (typeof window.formOnSaveFunction === "function") {
27321
27941
  window.formOnSaveFunction();
27322
27942
  }
27323
27943
  };
27324
- window.FormOnSubmit = function() {
27944
+ window.FormOnSubmit = window.FormOnSubmit || function() {
27325
27945
  if (typeof window.formOnSubmitFunction === "function") {
27326
27946
  window.formOnSubmitFunction();
27327
27947
  }
@@ -48243,6 +48863,14 @@ async function startCameraScanner() {
48243
48863
 
48244
48864
  showBarcodeError(errorMessage);
48245
48865
  setTimeout(() => closeScannerModal(), 3000);
48866
+
48867
+ if (window.sendEventCallback) {
48868
+ window.sendEventCallback({
48869
+ type: "ERROR",
48870
+ errorMessage: "ErrorCode : 012, Camera access error",
48871
+ data: { technicalError: err, reason: errorMessage }
48872
+ });
48873
+ }
48246
48874
  }
48247
48875
  }
48248
48876
 
@@ -48626,7 +49254,7 @@ function injectBarcodeModalStyles() {
48626
49254
 
48627
49255
  (function(){
48628
49256
  // Listen for "getLocation" custom event
48629
- document.addEventListener("getLocation", function (event) {
49257
+ document.addEventListener("getLocation", function (event) {
48630
49258
  const { compObj, controlId } = event.detail;
48631
49259
  window.compId = controlId;
48632
49260
  getLocationFromBrowser(controlId); // Pass controlId to getLocationFromBrowser
@@ -48636,7 +49264,7 @@ function injectBarcodeModalStyles() {
48636
49264
  function getLocationFromBrowser(componentID) {
48637
49265
  // Check if running in Cordova environment
48638
49266
  const isCordova = !!(window.cordova || window.PhoneGap || window.phonegap);
48639
-
49267
+
48640
49268
  if (!navigator.geolocation) {
48641
49269
  showLocationError("Geolocation is not supported by this device.");
48642
49270
  return;
@@ -48657,7 +49285,7 @@ function getLocationFromBrowser(componentID) {
48657
49285
  showLocationError("Location permission was denied. Please enable it in browser settings and refresh the page.");
48658
49286
  return;
48659
49287
  }
48660
-
49288
+
48661
49289
  if (permissionStatus.state === "granted" || permissionStatus.state === "prompt") {
48662
49290
  requestLocation(componentID);
48663
49291
  }
@@ -48675,7 +49303,7 @@ function getLocationFromBrowser(componentID) {
48675
49303
 
48676
49304
  function requestLocation(componentID) {
48677
49305
  const isCordova = !!(window.cordova || window.PhoneGap || window.phonegap);
48678
-
49306
+
48679
49307
  const options = {
48680
49308
  enableHighAccuracy: true,
48681
49309
  timeout: isCordova ? 30000 : 10000, // Longer timeout for Cordova
@@ -48692,7 +49320,7 @@ function requestLocation(componentID) {
48692
49320
  },
48693
49321
  });
48694
49322
  document.dispatchEvent(customEvent);
48695
-
49323
+
48696
49324
  // Send success event
48697
49325
  if (window.sendEventCallback) {
48698
49326
  window.sendEventCallback({
@@ -48710,36 +49338,36 @@ function requestLocation(componentID) {
48710
49338
  }
48711
49339
 
48712
49340
  function handleLocationError(error) {
48713
- let errorMessage = "Unable to retrieve location";
48714
- let errorCode = "008";
48715
-
49341
+ let errorMessage = "Location unknown error";
49342
+ let errorCode = "011";
49343
+
48716
49344
  switch (error.code) {
48717
49345
  case error.PERMISSION_DENIED:
48718
- errorMessage = "Location permission denied. Please allow location access and try again.";
49346
+ errorMessage = "Location permission denied";
48719
49347
  errorCode = "008";
48720
49348
  break;
48721
49349
  case error.POSITION_UNAVAILABLE:
48722
- errorMessage = "Location information is unavailable. Please check your GPS settings.";
49350
+ errorMessage = "Location unavailable";
48723
49351
  errorCode = "009";
48724
49352
  break;
48725
49353
  case error.TIMEOUT:
48726
- errorMessage = "Location request timed out. Please try again.";
49354
+ errorMessage = "Location request timeout";
48727
49355
  errorCode = "010";
48728
49356
  break;
48729
49357
  default:
48730
- errorMessage = "An unknown error occurred while retrieving location.";
49358
+ errorMessage = "Location unknown error";
48731
49359
  errorCode = "011";
48732
49360
  break;
48733
49361
  }
48734
-
49362
+
48735
49363
  showLocationError(errorMessage);
48736
49364
  console.error("Geolocation error:", error);
48737
-
49365
+
48738
49366
  // Send error event
48739
49367
  if (window.sendEventCallback) {
48740
49368
  window.sendEventCallback({
48741
49369
  type: "ERROR",
48742
- errorMessage: \`ErrorCode : \${errorCode}, Location error. Contact your admin or tech team.\`,
49370
+ errorMessage: \`ErrorCode : \${errorCode}, \${errorMessage}\`,
48743
49371
  data: { technicalError: error, reason: errorMessage }
48744
49372
  });
48745
49373
  }
@@ -48764,9 +49392,9 @@ function showLocationError(message) {
48764
49392
  text-align: center;
48765
49393
  \`;
48766
49394
  errorDiv.textContent = message;
48767
-
49395
+
48768
49396
  document.body.appendChild(errorDiv);
48769
-
49397
+
48770
49398
  // Remove after 5 seconds
48771
49399
  setTimeout(() => {
48772
49400
  if (errorDiv.parentNode) {
@@ -48809,15 +49437,18 @@ let capturedImageData = "";
48809
49437
  function openCameraModal() {
48810
49438
  injectCameraModalStyles();
48811
49439
 
49440
+ // Ensure clean slate: Remove any existing modal if present
48812
49441
  const existingModal = document.getElementById("cameraModal");
49442
+ if (existingModal) {
49443
+ existingModal.remove();
49444
+ }
48813
49445
 
48814
49446
  capturedImageData = "";
48815
49447
 
48816
- if (!existingModal) {
48817
- const modalHTML = \`
49448
+ const modalHTML = \`
48818
49449
  <div id="cameraModal">
48819
49450
  <div id="cameraContainer">
48820
- <video id="cameraVideo" autoplay playsinline style="opacity: 0;"></video>
49451
+ <video id="cameraVideo" playsinline webkit-playsinline muted style="opacity: 0; pointer-events: none;"></video>
48821
49452
  <div id="cameraLoader">
48822
49453
  <div class="loader-spinner"></div>
48823
49454
  <p class="loader-text">Starting camera...</p>
@@ -48828,6 +49459,11 @@ function openCameraModal() {
48828
49459
  <path d="M18 6L6 18M6 6L18 18" stroke="white" stroke-width="2" stroke-linecap="round"/>
48829
49460
  </svg>
48830
49461
  </button>
49462
+ <div class="camera-select-container">
49463
+ <select id="cameraSelect" class="camera-select">
49464
+ <option value="" disabled selected>Select Camera</option>
49465
+ </select>
49466
+ </div>
48831
49467
  <div id="captureButton" style="opacity: 0; pointer-events: none;">
48832
49468
  <div class="capture-ring">
48833
49469
  <div class="capture-inner"></div>
@@ -48837,35 +49473,18 @@ function openCameraModal() {
48837
49473
  </div>
48838
49474
  </div>
48839
49475
  \`;
48840
- document.body.insertAdjacentHTML("beforeend", modalHTML);
48841
- document.getElementById("closeCameraBtn").addEventListener("click", closeCameraModal);
48842
- document.getElementById("captureButton").addEventListener("click", captureImage);
48843
- } else {
48844
- existingModal.style.display = "block";
48845
-
48846
- // Reset UI state when reopening
48847
- const video = document.getElementById("cameraVideo");
48848
- const loader = document.getElementById("cameraLoader");
48849
- const captureBtn = document.getElementById("captureButton");
48850
-
48851
- if (video) {
48852
- video.style.opacity = "0";
48853
- video.srcObject = null;
48854
- }
48855
- if (loader) {
48856
- loader.style.display = "flex";
48857
- loader.style.opacity = "1";
48858
- }
48859
- if (captureBtn) {
48860
- captureBtn.style.opacity = "0";
48861
- captureBtn.style.pointerEvents = "none";
48862
- }
48863
- }
49476
+ document.body.insertAdjacentHTML("beforeend", modalHTML);
49477
+ document.getElementById("closeCameraBtn").addEventListener("click", closeCameraModal);
49478
+ document.getElementById("captureButton").addEventListener("click", captureImage);
49479
+ document.getElementById("cameraSelect").addEventListener("change", (e) => {
49480
+ startCameraStream(e.target.value);
49481
+ });
48864
49482
 
49483
+ // Small delay to ensure DOM is ready
48865
49484
  setTimeout(startCameraStream, 100);
48866
49485
  }
48867
49486
 
48868
- async function startCameraStream() {
49487
+ async function startCameraStream(deviceId = null) {
48869
49488
  if (videoStream) {
48870
49489
  try {
48871
49490
  videoStream.getTracks().forEach(track => track.stop());
@@ -48876,42 +49495,84 @@ async function startCameraStream() {
48876
49495
  }
48877
49496
 
48878
49497
  try {
49498
+ const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) || (window.platform && window.platform.iosPlatform);
49499
+
49500
+ // Check if API is supported
49501
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
49502
+ const errorMsg = "Camera API is not supported. Please ensure you are using HTTPS or localhost.";
49503
+ alert(errorMsg);
49504
+ showCameraError(errorMsg);
49505
+ return;
49506
+ }
49507
+
48879
49508
  const video = document.getElementById("cameraVideo");
48880
49509
  if (!video) {
48881
49510
  alert("Camera video element not found.");
48882
49511
  return;
48883
49512
  }
48884
49513
 
48885
- // Get available cameras
48886
- const devices = await navigator.mediaDevices.enumerateDevices();
48887
- const videoDevices = devices.filter(device => device.kind === 'videoinput');
49514
+ // Simplified approach: Ask for camera directly.
49515
+ // This allows the browser to handle permissions correctly on all platforms (Windows/Android/iOS)
49516
+ // and defaults to the system default camera (webcam) on laptops.
49517
+ // Simplified approach: Ask for camera directly.
49518
+ // We race against a timeout because WebViews often hang indefinitely if permissions aren't handled.
49519
+ // OPTIMIZATION: Request 16:9 aspect ratio (1280x720) instead of 4:3 (640x480).
49520
+ // This reduces the "zoom" effect on modern mobile screens which are typically 16:9 or taller.
49521
+ const constraints = {
49522
+ video: {
49523
+ width: { ideal: 1280 },
49524
+ height: { ideal: 720 }
49525
+ }
49526
+ };
48888
49527
 
48889
- if (!videoDevices.length) {
48890
- alert("No camera devices found.");
48891
- return;
49528
+ if (deviceId) {
49529
+ constraints.video.deviceId = { exact: deviceId };
49530
+ } else {
49531
+ constraints.video.facingMode = "environment";
48892
49532
  }
48893
49533
 
48894
- // Prefer back camera
48895
- const backCamera = videoDevices.find(device =>
48896
- device.label.toLowerCase().includes('back') ||
48897
- device.label.toLowerCase().includes('rear')
48898
- ) || videoDevices[0];
49534
+ const streamPromise = navigator.mediaDevices.getUserMedia(constraints);
48899
49535
 
48900
- videoStream = await navigator.mediaDevices.getUserMedia({
48901
- video: {
48902
- deviceId: backCamera.deviceId,
48903
- facingMode: "environment",
48904
- width: { ideal: 640 },
48905
- height: { ideal: 480 }
49536
+ const timeoutPromise = new Promise((_, reject) =>
49537
+ setTimeout(() => reject(new Error("PermissionTimeout")), 6000)
49538
+ );
49539
+
49540
+ try {
49541
+ videoStream = await Promise.race([streamPromise, timeoutPromise]);
49542
+ } catch (err) {
49543
+ if (err.message === "PermissionTimeout") {
49544
+ throw new Error("Permission request timed out. Please check app permissions.");
48906
49545
  }
48907
- });
49546
+ throw err;
49547
+ }
49548
+
49549
+ // iOS Specific: Ensure inline playback setup BEFORE stream assignment
49550
+ if (isIOS) {
49551
+ video.setAttribute("playsinline", "");
49552
+ video.setAttribute("webkit-playsinline", "");
49553
+ video.playsInline = true; // Set property as well
49554
+ }
49555
+
49556
+ // Mute video to prevent audio feedback and allow autoplay in restrictive policies
49557
+ video.muted = true;
48908
49558
 
49559
+ // Assign stream
48909
49560
  video.srcObject = videoStream;
48910
49561
 
49562
+ // Ensure video doesn't stay paused if it somehow exits fullscreen or gets interrupted
49563
+ video.onpause = () => {
49564
+ if (cameraStarted && video.srcObject) {
49565
+ // Only try to resume if we expect it to be playing
49566
+ video.play().catch(e => console.log("Resume error", e));
49567
+ }
49568
+ };
49569
+
48911
49570
  // Wait for video to start playing, then hide loader
48912
49571
  video.onloadedmetadata = () => {
48913
- video.play().then(() => {
48914
- // Hide loader and show camera UI
49572
+ // Once permission is granted and stream starts, listing devices will return labels
49573
+ populateCameraDevices();
49574
+
49575
+ const showUI = () => {
48915
49576
  const loader = document.getElementById("cameraLoader");
48916
49577
  const captureBtn = document.getElementById("captureButton");
48917
49578
 
@@ -48919,23 +49580,34 @@ async function startCameraStream() {
48919
49580
  loader.style.display = "none";
48920
49581
  loader.style.opacity = "0";
48921
49582
  }
49583
+
48922
49584
  video.style.opacity = "1";
49585
+
48923
49586
  if (captureBtn) {
48924
49587
  captureBtn.style.opacity = "1";
48925
49588
  captureBtn.style.pointerEvents = "all";
48926
49589
  }
49590
+ };
48927
49591
 
49592
+ video.play().then(() => {
49593
+ showUI();
48928
49594
  cameraStarted = true;
48929
49595
  }).catch(err => {
48930
49596
  console.error("Video play error:", err);
48931
- alert("Failed to start video: " + err.message);
48932
- closeCameraModal();
49597
+ if (!video.paused) {
49598
+ console.log("Video is playing despite error. Continuing.");
49599
+ showUI();
49600
+ cameraStarted = true;
49601
+ } else {
49602
+ alert("Failed to start video preview: " + err.message);
49603
+ closeCameraModal();
49604
+ }
48933
49605
  });
48934
49606
  };
48935
49607
 
48936
49608
  } catch (err) {
48937
49609
  console.error("Camera start error:", err);
48938
- let errorMessage = "Failed to start camera";
49610
+ let errorMessage = err.message || "Failed to start camera";
48939
49611
 
48940
49612
  if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {
48941
49613
  errorMessage = "Camera permission denied. Please allow camera access and try again.";
@@ -48951,18 +49623,50 @@ async function startCameraStream() {
48951
49623
  errorMessage = "Camera access was interrupted.";
48952
49624
  }
48953
49625
 
48954
- showCameraError(errorMessage);
49626
+ alert(errorMessage);
49627
+ closeCameraModal();
49628
+ }
49629
+ }
48955
49630
 
48956
- // Send error event
48957
- if (window.sendEventCallback) {
48958
- window.sendEventCallback({
48959
- type: "ERROR",
48960
- errorMessage: "ErrorCode : 012, Camera access error. Contact your admin or tech team.",
48961
- data: { technicalError: err, reason: errorMessage }
49631
+ async function populateCameraDevices() {
49632
+ const select = document.getElementById("cameraSelect");
49633
+ if (!select) return;
49634
+
49635
+ try {
49636
+ const devices = await navigator.mediaDevices.enumerateDevices();
49637
+ const videoDevices = devices.filter(device => device.kind === 'videoinput');
49638
+
49639
+ // Only show if we have more than 1 camera, or at least we want to show the current one
49640
+ if (videoDevices.length > 0) {
49641
+ select.innerHTML = '<option value="" disabled>Select Camera</option>';
49642
+
49643
+ let currentDeviceId = "";
49644
+ if (videoStream) {
49645
+ const track = videoStream.getVideoTracks()[0];
49646
+ if (track) {
49647
+ const settings = track.getSettings();
49648
+ currentDeviceId = settings.deviceId;
49649
+ }
49650
+ }
49651
+
49652
+ videoDevices.forEach((device, index) => {
49653
+ const option = document.createElement("option");
49654
+ option.value = device.deviceId;
49655
+ option.text = device.label || \`Camera \${index + 1}\`;
49656
+ if (device.deviceId === currentDeviceId) {
49657
+ option.selected = true;
49658
+ }
49659
+ select.appendChild(option);
48962
49660
  });
48963
- }
48964
49661
 
48965
- setTimeout(() => closeCameraModal(), 3000);
49662
+ // If there are multiple devices, ensure the dropdown container is visible
49663
+ const container = document.querySelector('.camera-select-container');
49664
+ if (container) {
49665
+ container.style.display = videoDevices.length > 1 ? 'block' : 'none';
49666
+ }
49667
+ }
49668
+ } catch (e) {
49669
+ console.warn("Error enumerating devices:", e);
48966
49670
  }
48967
49671
  }
48968
49672
 
@@ -49032,19 +49736,19 @@ function closeCameraModal() {
49032
49736
  const modal = document.getElementById("cameraModal");
49033
49737
 
49034
49738
  if (videoStream) {
49035
- videoStream.getTracks().forEach(track => track.stop());
49739
+ try {
49740
+ videoStream.getTracks().forEach(track => track.stop());
49741
+ } catch (e) {
49742
+ console.warn("Error stopping tracks", e);
49743
+ }
49036
49744
  videoStream = null;
49037
49745
  cameraStarted = false;
49038
49746
  }
49039
49747
 
49040
- // Reset video element
49041
- const video = document.getElementById("cameraVideo");
49042
- if (video) {
49043
- video.srcObject = null;
49044
- video.style.opacity = "0";
49748
+ // Completely remove from DOM to reset video element state for iOS
49749
+ if (modal) {
49750
+ modal.remove();
49045
49751
  }
49046
-
49047
- if (modal) modal.style.display = "none";
49048
49752
  }
49049
49753
 
49050
49754
  function injectCameraModalStyles() {
@@ -49075,6 +49779,7 @@ function injectCameraModalStyles() {
49075
49779
  object-fit: cover;
49076
49780
  background: #000;
49077
49781
  transition: opacity 0.3s ease;
49782
+ pointer-events: none;
49078
49783
  }
49079
49784
 
49080
49785
  #cameraLoader {
@@ -49155,6 +49860,39 @@ function injectCameraModalStyles() {
49155
49860
  background: rgba(0, 0, 0, 0.7);
49156
49861
  }
49157
49862
 
49863
+ .camera-select-container {
49864
+ position: absolute;
49865
+ top: 20px;
49866
+ right: 20px;
49867
+ z-index: 10001;
49868
+ pointer-events: all;
49869
+ display: none; /* Hidden by default until devices found */
49870
+ }
49871
+
49872
+ .camera-select {
49873
+ background: rgba(0, 0, 0, 0.5);
49874
+ color: white;
49875
+ border: 1px solid rgba(255, 255, 255, 0.3);
49876
+ padding: 8px 12px;
49877
+ border-radius: 20px;
49878
+ font-size: 14px;
49879
+ backdrop-filter: blur(10px);
49880
+ outline: none;
49881
+ cursor: pointer;
49882
+ appearance: none;
49883
+ -webkit-appearance: none;
49884
+ padding-right: 30px;
49885
+ background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23FFFFFF%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E");
49886
+ background-repeat: no-repeat;
49887
+ background-position: right 10px center;
49888
+ background-size: 10px;
49889
+ }
49890
+
49891
+ .camera-select option {
49892
+ background: #333;
49893
+ color: white;
49894
+ }
49895
+
49158
49896
  #captureButton {
49159
49897
  position: absolute;
49160
49898
  bottom: 40px;
@@ -49221,6 +49959,11 @@ function injectCameraModalStyles() {
49221
49959
  bottom: env(safe-area-inset-bottom, 40px);
49222
49960
  bottom: max(40px, env(safe-area-inset-bottom));
49223
49961
  }
49962
+
49963
+ .camera-select-container {
49964
+ top: env(safe-area-inset-top, 20px);
49965
+ top: max(20px, env(safe-area-inset-top));
49966
+ }
49224
49967
  }
49225
49968
 
49226
49969
  /* Landscape mode adjustments */
@@ -49855,7 +50598,7 @@ see README and LICENSE for details
49855
50598
 
49856
50599
  let flag = false;
49857
50600
  class SmartStorage {
49858
- constructor() {}
50601
+ constructor() { }
49859
50602
 
49860
50603
  static get title() {
49861
50604
  return "SmartStorage";
@@ -49881,15 +50624,38 @@ class SmartStorage {
49881
50624
 
49882
50625
  return new Promise((resolve, reject) => {
49883
50626
  const request = indexedDB.open("Forms-Attachments", 3);
50627
+
49884
50628
  request.onsuccess = function (event) {
49885
50629
  const db = event.target.result;
50630
+ if (!db.objectStoreNames.contains("Files")) {
50631
+ console.warn("[SmartStorage] Files store missing. Recreating database.");
50632
+ db.close();
50633
+ const deleteReq = indexedDB.deleteDatabase("Forms-Attachments");
50634
+ deleteReq.onsuccess = () => {
50635
+ const retryReq = indexedDB.open("Forms-Attachments", 3);
50636
+ retryReq.onupgradeneeded = (e) => {
50637
+ const newDb = e.target.result;
50638
+ newDb.createObjectStore("Files");
50639
+ };
50640
+ retryReq.onsuccess = (e) => resolve(e.target.result);
50641
+ retryReq.onerror = (e) => reject(e);
50642
+ };
50643
+ deleteReq.onerror = (e) => reject(e);
50644
+ return;
50645
+ }
49886
50646
  resolve(db);
49887
50647
  };
49888
50648
 
49889
50649
  request.onupgradeneeded = function (e) {
49890
50650
  const db = e.target.result;
49891
- db.createObjectStore("Files");
49892
- resolve(db);
50651
+ if (!db.objectStoreNames.contains("Files")) {
50652
+ db.createObjectStore("Files");
50653
+ }
50654
+ resolve(db); // Note: resolve here calls with incomplete db during upgrade transaction, but it's typically fine for callers waiting on db handle.
50655
+ // Ideally we resolve in onsuccess, but existing code had it this way too?
50656
+ // Actually, in the original code, it resolved in BOTH. Resolving in upgrade gives the transaction-bound db.
50657
+ // But Standard practice is waiting for success.
50658
+ // The original code had \`resolve(db)\` in \`onupgradeneeded\`.
49893
50659
  };
49894
50660
 
49895
50661
  request.onerror = function (e) {
@@ -49916,6 +50682,7 @@ class SmartStorage {
49916
50682
  size: file.size,
49917
50683
  type: file.type,
49918
50684
  url,
50685
+ mode: "A",
49919
50686
  };
49920
50687
  if (
49921
50688
  window.platform.isBrowser ||
@@ -49937,6 +50704,7 @@ class SmartStorage {
49937
50704
  type: file.type,
49938
50705
  url: url,
49939
50706
  id,
50707
+ mode: "A",
49940
50708
  });
49941
50709
  };
49942
50710
  } else {
@@ -49947,6 +50715,7 @@ class SmartStorage {
49947
50715
  type: file.type,
49948
50716
  url: url,
49949
50717
  id,
50718
+ mode: "A",
49950
50719
  });
49951
50720
  }
49952
50721
  };
@@ -50033,6 +50802,7 @@ class SmartStorage {
50033
50802
  size: blobObject.size,
50034
50803
  type: file.type,
50035
50804
  url: file.url,
50805
+ mode: file.mode || "G",
50036
50806
  };
50037
50807
  const trans = db.transaction(["Files"], "readwrite");
50038
50808
  const addReq = trans.objectStore("Files").put(data, id);
@@ -50692,7 +51462,21 @@ class SmartFileComponent extends FieldComponent {
50692
51462
  }
50693
51463
 
50694
51464
  getValue() {
50695
- return this.dataValue;
51465
+ const value = Array.isArray(this.dataValue) ? this.dataValue : (this.dataValue ? [this.dataValue] : []);
51466
+ return value.concat(this.deletedFiles || []);
51467
+ }
51468
+
51469
+ setValue(value, flags) {
51470
+ if (Array.isArray(value)) {
51471
+ this.deletedFiles = value.filter(f => f.mode === 'D');
51472
+ value = value.filter(f => f.mode !== 'D');
51473
+ } else if (value && value.mode === 'D') {
51474
+ this.deletedFiles = [value];
51475
+ value = [];
51476
+ } else {
51477
+ this.deletedFiles = [];
51478
+ }
51479
+ return super.setValue(value, flags);
50696
51480
  }
50697
51481
 
50698
51482
  get defaultValue() {
@@ -51015,6 +51799,13 @@ class SmartFileComponent extends FieldComponent {
51015
51799
  new SmartStorage().markFileAsDeleted(fileInfo.id);
51016
51800
  this.deleteFile(fileInfo);
51017
51801
  event.preventDefault();
51802
+
51803
+ fileInfo.mode = "D";
51804
+ if (!this.deletedFiles) {
51805
+ this.deletedFiles = [];
51806
+ }
51807
+ this.deletedFiles.push(fileInfo);
51808
+
51018
51809
  this.splice(index);
51019
51810
  this.redraw();
51020
51811
  });
@@ -51269,10 +52060,8 @@ class SmartFileComponent extends FieldComponent {
51269
52060
  let id = this.component.id;
51270
52061
  isfileBrowse = false;
51271
52062
 
51272
- // let isbrowser = (!document.URL.startsWith('http:') || document.URL.startsWith('http://localhost:8000'))
51273
- // if (window.platform.isAndroid && !isbrowser) {
51274
-
51275
- if (window.platform.isAndroid) {
52063
+ // Dispatch custom camera event for both Android and iOS
52064
+ if (window.platform.isAndroid || window.platform.iosPlatform) {
51276
52065
  window.isListenerSet = false;
51277
52066
  let eventCustom = new CustomEvent("openCamera", {
51278
52067
  detail: {
@@ -51656,6 +52445,24 @@ class SmartFileComponent extends FieldComponent {
51656
52445
  if (this.component.storage && files && files.length) {
51657
52446
  // if (this.component.storage && files) {
51658
52447
  Array.prototype.forEach.call(files, async (file) => {
52448
+ // Modify iOS filenames with timestamp BEFORE duplicate check
52449
+ let originalFileName = file.name;
52450
+ if (window.platform.iosPlatform) {
52451
+ const fileNameOriginal = file.name;
52452
+ const lastDotIndex = fileNameOriginal.lastIndexOf(".");
52453
+ let newName = "";
52454
+ if (lastDotIndex !== -1) {
52455
+ newName = \`\${fileNameOriginal.substring(
52456
+ 0,
52457
+ lastDotIndex
52458
+ )}-\${Date.now()}\${fileNameOriginal.substring(lastDotIndex)}\`;
52459
+ } else {
52460
+ newName = \`\${fileNameOriginal}-\${Date.now()}\`;
52461
+ }
52462
+ file = new File([file], newName, {
52463
+ type: file.type,
52464
+ });
52465
+ }
51659
52466
  const fileName = uniqueName(
51660
52467
  file.name,
51661
52468
  this.component.fileNameTemplate,
@@ -51698,8 +52505,9 @@ class SmartFileComponent extends FieldComponent {
51698
52505
  // Check if file with the same name is being uploaded
51699
52506
  var fileWithSameNameUploaded = false;
51700
52507
  if (window.platform.iosPlatform) {
52508
+ // For iOS, compare using the escaped filename (after timestamp modification)
51701
52509
  fileWithSameNameUploaded = this.dataValue.some(
51702
- (fileStatus) => fileStatus.url === imagUrl
52510
+ (fileStatus) => fileStatus.originalName === escapedFileName
51703
52511
  );
51704
52512
  } else {
51705
52513
  fileWithSameNameUploaded = isfileBrowse
@@ -51909,19 +52717,27 @@ class SmartFileComponent extends FieldComponent {
51909
52717
  fileInfo.originalName = escapedFileName;
51910
52718
  fileInfo.hash = fileUpload.hash;
51911
52719
  fileInfo.compnentId = this.component.id;
52720
+ fileInfo.mode = fileUpload.mode || "A";
51912
52721
  if (!this.hasValue()) {
51913
52722
  this.dataValue = [];
51914
52723
  }
51915
52724
  if (replace) {
51916
- let ind;
52725
+ let ind = -1;
51917
52726
  for (let i = 0; i < this.dataValue.length; i++) {
51918
52727
  if (this.dataValue[i].originalName == fileInfo.originalName) {
51919
52728
  console.log("original name matches");
51920
52729
  ind = i;
52730
+ break;
51921
52731
  }
51922
52732
  }
51923
52733
  if (ind !== -1) {
51924
52734
  let originalFileId = this.dataValue[ind].id;
52735
+ let originalFileMode = this.dataValue[ind].mode;
52736
+
52737
+ if (originalFileMode === "G" || originalFileMode === "M") {
52738
+ fileInfo.mode = "M";
52739
+ }
52740
+
51925
52741
  console.log("originalFileId = " + originalFileId);
51926
52742
  this.dataValue[ind] = fileInfo;
51927
52743
  let eventCustom = new CustomEvent(
@@ -59468,7 +60284,7 @@ class SmartSelectComponent extends SmartSelectField {
59468
60284
  return {
59469
60285
  ...super.conditionOperatorsSettings,
59470
60286
  valueComponent(classComp) {
59471
- const valueComp = { ... classComp, type: 'select' };
60287
+ const valueComp = { ...classComp, type: 'select' };
59472
60288
 
59473
60289
  if (isSelectResourceWithObjectValue(classComp)) {
59474
60290
  valueComp.reference = false;
@@ -59620,7 +60436,7 @@ class SmartSelectComponent extends SmartSelectField {
59620
60436
  return this.component.valueProperty;
59621
60437
  }
59622
60438
  // Force values datasource to use values without actually setting it on the component settings.
59623
- if(this.component.dataSrc === 'values') {
60439
+ if (this.component.dataSrc === 'values') {
59624
60440
  return 'value';
59625
60441
  }
59626
60442
  return '';
@@ -59658,10 +60474,10 @@ class SmartSelectComponent extends SmartSelectField {
59658
60474
 
59659
60475
  get shouldInitialLoad() {
59660
60476
  if (this.component.widget === 'html5' &&
59661
- this.isEntireObjectDisplay() &&
59662
- this.component.searchField &&
59663
- this.dataValue) {
59664
- return false;
60477
+ this.isEntireObjectDisplay() &&
60478
+ this.component.searchField &&
60479
+ this.dataValue) {
60480
+ return false;
59665
60481
  }
59666
60482
 
59667
60483
  return super.shouldLoad;
@@ -59696,7 +60512,7 @@ class SmartSelectComponent extends SmartSelectField {
59696
60512
  if (this.component.multiple && _.isArray(this.dataValue) ? this.dataValue.find((val) => value === val) : (this.dataValue === value)) {
59697
60513
  const selectData = this.selectData;
59698
60514
  if (selectData) {
59699
- const templateValue = this.component.reference && value && (value._id ? value._id.toString() : value);
60515
+ const templateValue = this.component.reference && value && (value._id ? value._id.toString() : value);
59700
60516
  if (!this.templateData || !this.templateData[templateValue]) {
59701
60517
  this.getOptionTemplate(data, value);
59702
60518
  }
@@ -59723,7 +60539,7 @@ class SmartSelectComponent extends SmartSelectField {
59723
60539
  });
59724
60540
  }
59725
60541
 
59726
- if (data.data) {
60542
+ if (data.data) {
59727
60543
  // checking additional fields in the template for the selected Entire Object option
59728
60544
  const hasNestedFields = /item\\.data\\.\\w*/g.test(this.component.template);
59729
60545
  data.data = this.isEntireObjectDisplay() && _.isObject(data.data) && !hasNestedFields ?
@@ -59742,8 +60558,8 @@ class SmartSelectComponent extends SmartSelectField {
59742
60558
  hasTranslator = this.i18next.translator;
59743
60559
  }
59744
60560
  if (!label || (hasTranslator && !this.t(label, {
59745
- _userInput: true
59746
- }))) return;
60561
+ _userInput: true
60562
+ }))) return;
59747
60563
  return hasTranslator ? template.replace(label, this.t(label, {
59748
60564
  _userInput: true
59749
60565
  })) : label;
@@ -59770,7 +60586,7 @@ class SmartSelectComponent extends SmartSelectField {
59770
60586
  // ...idPath
59771
60587
  // };
59772
60588
 
59773
- const option = Object.assign({
60589
+ const option = Object.assign({
59774
60590
  value: this.getOptionValue(value),
59775
60591
  label,
59776
60592
  }, idPath);
@@ -59926,7 +60742,7 @@ class SmartSelectComponent extends SmartSelectField {
59926
60742
  if (this.root && this.root.options.editForm && this.root.options.editForm._id && this.root.options.editForm._id === item._id) return;
59927
60743
  const itemValueAndLabel = this.selectValueAndLabel(item);
59928
60744
  this.addOption(itemValueAndLabel.value, itemValueAndLabel.label, {}, _.get(item, this.component.idPath, String(index)));
59929
-
60745
+
59930
60746
  });
59931
60747
  if (this.choices) {
59932
60748
  if ((this.component.dataSrc === 'masterdata') && this.component.masterdata) {
@@ -59968,10 +60784,10 @@ class SmartSelectComponent extends SmartSelectField {
59968
60784
  // If a value is provided, then select it.
59969
60785
  if (!this.isEmpty()) {
59970
60786
  if (localStorage.getItem('renderMode') === 'html') {
59971
- var tmplt = this.component.template;
59972
- var str1 = tmplt.substring(tmplt.lastIndexOf('.') + 1, tmplt.length);
59973
- var label = str1.substring(0, str1.indexOf('}'), str1.length);
59974
- label = label.trim();
60787
+ var tmplt = this.component.template;
60788
+ var str1 = tmplt.substring(tmplt.lastIndexOf('.') + 1, tmplt.length);
60789
+ var label = str1.substring(0, str1.indexOf('}'), str1.length);
60790
+ label = label.trim();
59975
60791
 
59976
60792
  if (items.length > 0) {
59977
60793
  if (this.component.multiple) {
@@ -59984,13 +60800,13 @@ class SmartSelectComponent extends SmartSelectField {
59984
60800
  });
59985
60801
  } else {
59986
60802
  }
60803
+ }
60804
+ } else {
60805
+ this.setValue(this.dataValue, {
60806
+ noUpdateEvent: true
60807
+ });
59987
60808
  }
59988
- }else{
59989
- this.setValue(this.dataValue, {
59990
- noUpdateEvent: true
59991
- });
59992
- }
59993
- }else if (this.shouldAddDefaultValue && !this.options.readOnly) {
60809
+ } else if (this.shouldAddDefaultValue && !this.options.readOnly) {
59994
60810
  // If a default value is provided then select it.
59995
60811
  const defaultValue = this.defaultValue;
59996
60812
  if (!this.isEmpty(defaultValue)) {
@@ -60297,32 +61113,32 @@ class SmartSelectComponent extends SmartSelectField {
60297
61113
  } else {
60298
61114
  if (!this.component.dropdownOpened && this.data[this.key] && String(this.data[this.key]).length > 0) {
60299
61115
  let result = [];
60300
- if(this.component.multiple && this.component.masterdata){
60301
- this.data[this.key].forEach(item =>
61116
+ if (this.component.multiple && this.component.masterdata) {
61117
+ this.data[this.key].forEach(item =>
60302
61118
  this.component.masterdata.filter((value) => {
60303
61119
  const propValue = value[this.component.valueProperty];
60304
- // Convert to string only if necessary
60305
- const valPropData = typeof propValue !== 'string' ? String(propValue) : propValue;
60306
- if(valPropData.includes(item)){
61120
+ // Convert to string only if necessary
61121
+ const valPropData = typeof propValue !== 'string' ? String(propValue) : propValue;
61122
+ if (valPropData.includes(item)) {
60307
61123
  result.push(value)
60308
61124
  }
60309
61125
  })
60310
61126
  );
60311
61127
  // result = this.component.masterdata;
60312
- }else{
60313
- if(this.component.masterdata){
60314
- result = this.component.masterdata.filter(value => {
60315
- const propValue = value[this.component.valueProperty];
60316
- // Convert to string only if necessary
60317
- const valPropData = typeof propValue !== 'string' ? String(propValue) : propValue;
60318
- return valPropData?.includes(this.data[this.key]);
60319
- });
60320
- }
61128
+ } else {
61129
+ if (this.component.masterdata) {
61130
+ result = this.component.masterdata.filter(value => {
61131
+ const propValue = value[this.component.valueProperty];
61132
+ // Convert to string only if necessary
61133
+ const valPropData = typeof propValue !== 'string' ? String(propValue) : propValue;
61134
+ return valPropData?.includes(this.data[this.key]);
61135
+ });
61136
+ }
60321
61137
  }
60322
61138
  const unique = result.filter((obj, index) => {
60323
61139
  return index === result.findIndex(o => obj[this.component.valueProperty] === o[this.component.valueProperty]);
60324
- });
60325
- this.setItems(unique);
61140
+ });
61141
+ this.setItems(unique);
60326
61142
  }
60327
61143
  }
60328
61144
  }
@@ -60483,9 +61299,9 @@ class SmartSelectComponent extends SmartSelectField {
60483
61299
  get active() {
60484
61300
  if ((this.component.dataSrc === 'masterdata') && this.component.masterdata) {
60485
61301
  return !this.component.lazyLoad || this.activated;
60486
- }else{
60487
- // return !this.component.lazyLoad || this.activated || this.options.readOnly;
60488
- return !this.component.lazyLoad || this.activated;
61302
+ } else {
61303
+ // return !this.component.lazyLoad || this.activated || this.options.readOnly;
61304
+ return !this.component.lazyLoad || this.activated;
60489
61305
  }
60490
61306
  }
60491
61307
 
@@ -60559,8 +61375,8 @@ class SmartSelectComponent extends SmartSelectField {
60559
61375
  searchChoices: !this.component.searchField,
60560
61376
  searchFields: _.get(this, 'component.searchFields', ['label']),
60561
61377
  shadowRoot: this.root ? this.root.shadowRoot : null,
60562
- fuseOptions: this.component.useExactSearch ? Object.assign(fuseObj, commonFuseOptions) : Object.assign({},
60563
- _.get(this, 'component.fuseOptions', {}),
61378
+ fuseOptions: this.component.useExactSearch ? Object.assign(fuseObj, commonFuseOptions) : Object.assign({},
61379
+ _.get(this, 'component.fuseOptions', {}),
60564
61380
  Object.assign(fuseObj1, commonFuseOptions)
60565
61381
  ),
60566
61382
  valueComparer: _.isEqual,
@@ -60728,17 +61544,17 @@ class SmartSelectComponent extends SmartSelectField {
60728
61544
  }
60729
61545
  this.isFromSearch = false;
60730
61546
  });
60731
- // avoid spamming the resource/url endpoint when we have server side filtering enabled.
60732
- const debounceTimeout = this.component.searchField && (this.isSelectResource || this.isSelectURL) ?
60733
- (this.component.searchDebounce === 0 ? 0 : this.component.searchDebounce || this.defaultSchema.searchDebounce) * 1000
60734
- : 0;
60735
- const updateComponent = (evt) => {
60736
- this.triggerUpdate(evt.detail.value);
60737
- };
60738
- this.addEventListener(input, 'search', _.debounce((e) => {
60739
- updateComponent(e);
60740
- this.positionDropdown();
60741
- }, debounceTimeout));
61547
+ // avoid spamming the resource/url endpoint when we have server side filtering enabled.
61548
+ const debounceTimeout = this.component.searchField && (this.isSelectResource || this.isSelectURL) ?
61549
+ (this.component.searchDebounce === 0 ? 0 : this.component.searchDebounce || this.defaultSchema.searchDebounce) * 1000
61550
+ : 0;
61551
+ const updateComponent = (evt) => {
61552
+ this.triggerUpdate(evt.detail.value);
61553
+ };
61554
+ this.addEventListener(input, 'search', _.debounce((e) => {
61555
+ updateComponent(e);
61556
+ this.positionDropdown();
61557
+ }, debounceTimeout));
60742
61558
 
60743
61559
  this.addEventListener(input, 'stopSearch', () => this.triggerUpdate());
60744
61560
  this.addEventListener(input, 'hideDropdown', () => {
@@ -60753,9 +61569,9 @@ class SmartSelectComponent extends SmartSelectField {
60753
61569
  if (this.data[this.key] && String(this.data[this.key]).length > 0) {
60754
61570
  selData = this.component.masterdata.filter((value) => {
60755
61571
  const propValue = value[this.component.valueProperty];
60756
- // Convert to string only if necessary
60757
- const valPropData = typeof propValue !== 'string' ? String(propValue) : propValue;
60758
- return valPropData?.includes(this.data[this.key]);
61572
+ // Convert to string only if necessary
61573
+ const valPropData = typeof propValue !== 'string' ? String(propValue) : propValue;
61574
+ return valPropData?.includes(this.data[this.key]);
60759
61575
 
60760
61576
  })
60761
61577
  }
@@ -60848,10 +61664,10 @@ class SmartSelectComponent extends SmartSelectField {
60848
61664
  setDropdownPosition() {
60849
61665
  let dropdown;
60850
61666
  let container;
60851
- if(this.choices && this.choices.dropdown && this.choices.dropdown.element){
60852
- dropdown = this.choices.dropdown.element;
61667
+ if (this.choices && this.choices.dropdown && this.choices.dropdown.element) {
61668
+ dropdown = this.choices.dropdown.element;
60853
61669
  }
60854
- if(this.choices && this.choices.containerOuter && this.choices.containerOuter.element){
61670
+ if (this.choices && this.choices.containerOuter && this.choices.containerOuter.element) {
60855
61671
  container = this.choices.containerOuter.element;
60856
61672
  }
60857
61673
 
@@ -60936,9 +61752,9 @@ class SmartSelectComponent extends SmartSelectField {
60936
61752
  if (this.data[this.key] && String(this.data[this.key]).length > 0) {
60937
61753
  selData = this.component.masterdata.filter((value) => {
60938
61754
  const propValue = value[this.component.valueProperty];
60939
- // Convert to string only if necessary
60940
- const valPropData = typeof propValue !== 'string' ? String(propValue) : propValue;
60941
- return valPropData?.includes(this.data[this.key]);
61755
+ // Convert to string only if necessary
61756
+ const valPropData = typeof propValue !== 'string' ? String(propValue) : propValue;
61757
+ return valPropData?.includes(this.data[this.key]);
60942
61758
  })
60943
61759
  }
60944
61760
 
@@ -61289,29 +62105,29 @@ class SmartSelectComponent extends SmartSelectField {
61289
62105
 
61290
62106
  setValue(value, flags = {}) {
61291
62107
  // console.log("value line 1875" + this.component.label, value)
61292
- if(value && String(value).length > 0){
62108
+ if (value && String(value).length > 0) {
61293
62109
  $(".barcode-select-error").remove();
61294
62110
  }
61295
- if (localStorage.getItem('renderMode') === 'html' && this.component.dataSrc === 'values') {
61296
- this.component.data.values.filter((data) => {
61297
- if ((value && data.value === value) || (value && data.value === String(value))) {
61298
- value = data.label;
61299
- };
61300
- })
62111
+ if (localStorage.getItem('renderMode') === 'html' && this.component.dataSrc === 'values') {
62112
+ this.component.data.values.filter((data) => {
62113
+ if ((value && data.value === value) || (value && data.value === String(value))) {
62114
+ value = data.label;
62115
+ };
62116
+ })
62117
+ }
62118
+ if (localStorage.getItem('renderMode') === 'html' && this.component.dataSrc === 'masterdata' && !this.component.multiple) {
62119
+ var tmplt = this.component.template;
62120
+ var str1 = tmplt.substring(tmplt.lastIndexOf('.') + 1, tmplt.length);
62121
+ var label = str1.substring(0, str1.indexOf('}'), str1.length);
62122
+ label = label.trim();
62123
+
62124
+ this.component.masterdata.filter((data) => {
62125
+ if ((value && data[this.component.valueProperty] === value) || (value && data[this.component.valueProperty] === String(value))) {
62126
+ value = data[label];
62127
+ }
62128
+ })
61301
62129
  }
61302
- if (localStorage.getItem('renderMode') === 'html' && this.component.dataSrc === 'masterdata' && !this.component.multiple) {
61303
- var tmplt = this.component.template;
61304
- var str1 = tmplt.substring(tmplt.lastIndexOf('.') + 1, tmplt.length);
61305
- var label = str1.substring(0, str1.indexOf('}'), str1.length);
61306
- label = label.trim();
61307
62130
 
61308
- this.component.masterdata.filter((data) => {
61309
- if ((value && data[this.component.valueProperty] === value) || (value && data[this.component.valueProperty] === String(value))) {
61310
- value = data[label];
61311
- }
61312
- })
61313
- }
61314
-
61315
62131
  const previousValue = this.dataValue;
61316
62132
  // console.log("value line 1900 " + this.component.label, value)
61317
62133
  const changed = this.updateValue(value, flags);
@@ -61323,10 +62139,10 @@ class SmartSelectComponent extends SmartSelectField {
61323
62139
  if (this.component.multiple && Array.isArray(value)) {
61324
62140
  value = value.map(value => {
61325
62141
  if (typeof value === 'boolean' || typeof value === 'number') {
61326
- console.log("value line 1910 " + this.component.label, value)
62142
+ console.log("value line 1910 " + this.component.label, value)
61327
62143
  return value.toString();
61328
62144
  }
61329
- console.log("value line 1914 " + this.component.label, value)
62145
+ console.log("value line 1914 " + this.component.label, value)
61330
62146
  return value;
61331
62147
  });
61332
62148
  } else {
@@ -61358,10 +62174,10 @@ class SmartSelectComponent extends SmartSelectField {
61358
62174
  // Add the value options.
61359
62175
  this.itemsLoaded.then(() => {
61360
62176
  this.addValueOptions();
61361
- // console.log("value line 1945 " + this.component.label, value)
62177
+ // console.log("value line 1945 " + this.component.label, value)
61362
62178
  this.setChoicesValue(value, hasPreviousValue, flags);
61363
62179
  });
61364
-
62180
+
61365
62181
  return changed;
61366
62182
  }
61367
62183
 
@@ -61408,7 +62224,7 @@ class SmartSelectComponent extends SmartSelectField {
61408
62224
  if (hasValue) {
61409
62225
  const values = Array.isArray(value) ? value : [value];
61410
62226
  if (!_.isEqual(this.dataValue, this.defaultValue) && this.selectOptions.length < 2
61411
- || (this.selectData && flags.fromSubmission)) {
62227
+ || (this.selectData && flags.fromSubmission)) {
61412
62228
  const { value, label } = this.selectValueAndLabel(this.dataValue);
61413
62229
  this.addOption(value, label);
61414
62230
  }
@@ -61481,14 +62297,14 @@ class SmartSelectComponent extends SmartSelectField {
61481
62297
  */
61482
62298
  getOptionValue(value) {
61483
62299
  return _.isObject(value) && this.isEntireObjectDisplay()
61484
- ? this.normalizeSingleValue(value)
61485
- : _.isObject(value) && (this.valueProperty || this.component.key !== 'resource')
61486
- ? value
61487
- : _.isObject(value) && !this.valueProperty
61488
- ? this.interpolate(this.component.template, { item: value }).replace(/<\\/?[^>]+(>|$)/g, '')
61489
- : _.isNull(value)
61490
- ? this.emptyValue
61491
- : String(this.normalizeSingleValue(value));
62300
+ ? this.normalizeSingleValue(value)
62301
+ : _.isObject(value) && (this.valueProperty || this.component.key !== 'resource')
62302
+ ? value
62303
+ : _.isObject(value) && !this.valueProperty
62304
+ ? this.interpolate(this.component.template, { item: value }).replace(/<\\/?[^>]+(>|$)/g, '')
62305
+ : _.isNull(value)
62306
+ ? this.emptyValue
62307
+ : String(this.normalizeSingleValue(value));
61492
62308
  }
61493
62309
 
61494
62310
  /**
@@ -61604,19 +62420,19 @@ class SmartSelectComponent extends SmartSelectField {
61604
62420
  items: convertToString(this.getNormalizedValues(), 'value'),
61605
62421
  valueProperty: 'value',
61606
62422
  } : {
61607
- items: convertToString(this.getCustomItems(), this.valueProperty),
61608
- valueProperty: this.valueProperty,
61609
- };
62423
+ items: convertToString(this.getCustomItems(), this.valueProperty),
62424
+ valueProperty: this.valueProperty,
62425
+ };
61610
62426
  const getFromValues = () => {
61611
62427
  const initialValue = _.find(items, [valueProperty, value]);
61612
62428
  const values = this.defaultSchema.data.values || [];
61613
62429
  return _.isEqual(initialValue, values[0]) ? '-' : initialValue;
61614
62430
  };
61615
62431
  value = (this.component.multiple && Array.isArray(value))
61616
- ? _.filter(items, (item) => value.includes(item.value))
61617
- : valueProperty
61618
- ? getFromValues() ?? { value, label: value }
61619
- : value;
62432
+ ? _.filter(items, (item) => value.includes(item.value))
62433
+ : valueProperty
62434
+ ? getFromValues() ?? { value, label: value }
62435
+ : value;
61620
62436
  }
61621
62437
 
61622
62438
  if (_.isString(value)) {
@@ -61632,7 +62448,7 @@ class SmartSelectComponent extends SmartSelectField {
61632
62448
  if (Array.isArray(value)) {
61633
62449
  const items = [];
61634
62450
  value.forEach(item => items.push(getTemplateValue(item)));
61635
- if (this.component.dataSrc === 'resource' && items.length > 0 ) {
62451
+ if (this.component.dataSrc === 'resource' && items.length > 0) {
61636
62452
  return items.join(', ');
61637
62453
  }
61638
62454
  else if (items.length > 0) {
@@ -65007,12 +65823,22 @@ function initialButtonSetup(privateExternal, permission) {
65007
65823
  submitBtn.style.display = "none";
65008
65824
  }
65009
65825
 
65010
- // PUBLIC FORM
65011
- else if (
65012
- isPublic &&
65013
- (permissionMode === "writesingle" || permissionMode === "writemultiple")
65014
- ) {
65015
- console.log("Mode: PUBLIC WRITE \u2192 Show submit (disabled)");
65826
+ // PUBLIC FORM - writemultiple
65827
+ else if (isPublic && permissionMode === "writemultiple") {
65828
+ console.log("Mode: PUBLIC WRITEMULTIPLE \u2192 Show Complete button only (disabled)");
65829
+ saveBtn.style.display = "none";
65830
+ submitBtn.style.display = "";
65831
+ submitBtn.innerHTML = '<i class="icon clipboard check large"></i> Complete';
65832
+ submitBtn.disabled = true;
65833
+
65834
+ // No dropdown options for public writemultiple
65835
+ toggleDropdown(completeOption, completeDivider, false);
65836
+ toggleDropdown(saveOption, saveDivider, false);
65837
+ }
65838
+
65839
+ // PUBLIC FORM - writesingle
65840
+ else if (isPublic && permissionMode === "writesingle") {
65841
+ console.log("Mode: PUBLIC WRITESINGLE \u2192 Show submit (disabled)");
65016
65842
  saveBtn.style.display = "none";
65017
65843
  submitBtn.style.display = "";
65018
65844
  submitBtn.innerHTML = '<i class="icon save"></i> Submit';
@@ -65021,15 +65847,34 @@ function initialButtonSetup(privateExternal, permission) {
65021
65847
 
65022
65848
  // PRIVATE FORM - writemultiple
65023
65849
  else if (isPrivate && permissionMode === "writemultiple") {
65024
- console.log(
65025
- "Mode: PRIVATE WRITEMULTIPLE \u2192 Show save (disabled), Complete in Dropdown"
65026
- );
65027
- saveBtn.style.display = "";
65028
- submitBtn.style.display = "none";
65029
- saveBtn.disabled = true;
65850
+ const showCompleteAlways =
65851
+ window.__unviredFormsOptions?.showCompleteAlways === true ||
65852
+ window.__unviredFormsOptions?.showCompleteAlways === "true";
65853
+
65854
+ if (showCompleteAlways) {
65855
+ console.log(
65856
+ "Mode: PRIVATE WRITEMULTIPLE (ShowCompleteAlways) \u2192 Show Save and Complete"
65857
+ );
65858
+ saveBtn.style.display = "";
65859
+ submitBtn.style.display = "";
65860
+ submitBtn.innerHTML = '<i class="icon clipboard check large"></i> Complete';
65861
+
65862
+ saveBtn.disabled = true; // Initially empty
65863
+ submitBtn.disabled = true; // Initially not complete
65864
+
65865
+ toggleDropdown(completeOption, completeDivider, false);
65866
+ toggleDropdown(saveOption, saveDivider, false);
65867
+ } else {
65868
+ console.log(
65869
+ "Mode: PRIVATE WRITEMULTIPLE \u2192 Show save (disabled), Complete hidden initially"
65870
+ );
65871
+ saveBtn.style.display = "";
65872
+ submitBtn.style.display = "none";
65873
+ saveBtn.disabled = true;
65030
65874
 
65031
- // Show Complete in Dropdown
65032
- toggleDropdown(completeOption, completeDivider, true);
65875
+ // Initially hide Complete in Dropdown (will show when any field is filled)
65876
+ toggleDropdown(completeOption, completeDivider, false);
65877
+ }
65033
65878
  }
65034
65879
 
65035
65880
  // PRIVATE FORM - writesingle
@@ -65039,7 +65884,7 @@ function initialButtonSetup(privateExternal, permission) {
65039
65884
  );
65040
65885
  saveBtn.style.display = "none";
65041
65886
  submitBtn.style.display = "";
65042
- submitBtn.innerHTML = '<i class="icon save"></i> Complete';
65887
+ submitBtn.innerHTML = '<i class="icon clipboard check large"></i> Complete';
65043
65888
  submitBtn.disabled = true;
65044
65889
 
65045
65890
  // Show Save in Dropdown
@@ -65051,9 +65896,14 @@ function initialButtonSetup(privateExternal, permission) {
65051
65896
  console.log("Mode: DEFAULT / UNKNOWN \u2192 Show submit (disabled)");
65052
65897
  saveBtn.style.display = "none";
65053
65898
  submitBtn.style.display = "";
65054
- submitBtn.innerHTML = '<i class="icon save"></i> Submit';
65899
+ submitBtn.innerHTML = '<i class="icon clipboard large"></i> Submit';
65055
65900
  submitBtn.disabled = true;
65056
65901
  }
65902
+
65903
+ // Check if More button should be visible based on dropdown content
65904
+ if (typeof window.checkMoreButtonVisibility === "function") {
65905
+ window.checkMoreButtonVisibility();
65906
+ }
65057
65907
  }
65058
65908
 
65059
65909
  function onChangeButtonSetup(
@@ -65081,6 +65931,10 @@ function onChangeButtonSetup(
65081
65931
  );
65082
65932
  const isComplete = completionPercentage >= 100;
65083
65933
 
65934
+ const requireComplete =
65935
+ window.__unviredFormsOptions?.requireCompleteForm === true ||
65936
+ window.__unviredFormsOptions?.requireCompleteForm === "true";
65937
+
65084
65938
  // READ ONLY
65085
65939
  if (permissionMode === "read") {
65086
65940
  console.log("Mode: READ \u2192 Hide all buttons");
@@ -65096,32 +65950,117 @@ function onChangeButtonSetup(
65096
65950
  saveBtn.style.display = "none";
65097
65951
  submitBtn.style.display = "";
65098
65952
 
65099
- // Logic: Disable Complete until 100%
65100
- submitBtn.disabled = !isComplete;
65953
+ // Logic: Disable Complete until 100% if required, otherwise just check if has values
65954
+ submitBtn.disabled = requireComplete ? !isComplete : !hasAnyValue;
65955
+
65956
+ // Check if More button should be visible
65957
+ if (typeof window.checkMoreButtonVisibility === "function") {
65958
+ window.checkMoreButtonVisibility();
65959
+ }
65101
65960
  return;
65102
65961
  }
65103
65962
 
65104
65963
  // PRIVATE FORM - writemultiple
65105
65964
  if (isPrivate && permissionMode === "writemultiple") {
65106
65965
  console.log("Mode: PRIVATE WRITEMULTIPLE");
65107
- // Main: Save
65108
- saveBtn.style.display = "";
65109
- submitBtn.style.display = "none";
65110
65966
 
65111
- // Logic: Enable Save if hasAnyValue
65112
- saveBtn.disabled = !hasAnyValue;
65967
+ const completeOption = document.getElementById("completeOption");
65968
+ const saveOption = document.getElementById("saveOption");
65969
+ const completeDivider = document.getElementById("completeDivider");
65970
+ const saveDivider = document.getElementById("saveDivider");
65971
+
65972
+ // Helper to toggle dropdown items
65973
+ const toggleDropdown = (option, divider, show) => {
65974
+ if (option) option.style.display = show ? "block" : "none";
65975
+ if (divider) divider.style.display = show ? "block" : "none";
65976
+ };
65977
+
65978
+ const showCompleteAlways =
65979
+ window.__unviredFormsOptions?.showCompleteAlways === true ||
65980
+ window.__unviredFormsOptions?.showCompleteAlways === "true";
65981
+
65982
+ if (showCompleteAlways) {
65983
+ saveBtn.style.display = "";
65984
+ submitBtn.style.display = "";
65985
+ submitBtn.innerHTML = '<i class="icon clipboard check large"></i> Complete';
65986
+
65987
+ saveBtn.disabled = !hasAnyValue;
65988
+ submitBtn.disabled = requireComplete ? !isComplete : !hasAnyValue;
65989
+
65990
+ toggleDropdown(completeOption, completeDivider, false);
65991
+ toggleDropdown(saveOption, saveDivider, false);
65992
+ } else {
65993
+ if (isComplete) {
65994
+ // 100% Complete: Main button = Complete, Dropdown = Save
65995
+ console.log(
65996
+ " \u2192 100% Complete: Showing Complete button, Save in dropdown"
65997
+ );
65998
+ saveBtn.style.display = "none";
65999
+ submitBtn.style.display = "";
66000
+ submitBtn.innerHTML = '<i class="icon clipboard check large"></i> Complete';
66001
+ submitBtn.disabled = false;
66002
+
66003
+ // Show Save in dropdown, hide Complete
66004
+ toggleDropdown(saveOption, saveDivider, true);
66005
+ toggleDropdown(completeOption, completeDivider, false);
66006
+ } else if (hasAnyValue) {
66007
+ // 1-99% Complete: Main button = Save, Dropdown = Complete
66008
+ console.log(
66009
+ " \u2192 Partially filled: Showing Save button, Complete in dropdown"
66010
+ );
66011
+ saveBtn.style.display = "";
66012
+ submitBtn.style.display = "none";
66013
+ saveBtn.disabled = false;
66014
+
66015
+ // Show Complete in dropdown, hide Save
66016
+ toggleDropdown(completeOption, completeDivider, true);
66017
+ toggleDropdown(saveOption, saveDivider, false);
66018
+ } else {
66019
+ // 0% Complete: Main button = Save (disabled), Complete hidden
66020
+ console.log(" \u2192 Empty form: Save disabled, Complete hidden");
66021
+ saveBtn.style.display = "";
66022
+ submitBtn.style.display = "none";
66023
+ saveBtn.disabled = true;
66024
+
66025
+ // Hide both in dropdown
66026
+ toggleDropdown(completeOption, completeDivider, false);
66027
+ toggleDropdown(saveOption, saveDivider, false);
66028
+ }
66029
+ }
66030
+
66031
+ // Check if More button should be visible based on dropdown content
66032
+ if (typeof window.checkMoreButtonVisibility === "function") {
66033
+ window.checkMoreButtonVisibility();
66034
+ }
65113
66035
  return;
65114
66036
  }
65115
66037
 
65116
- // PUBLIC FORM
65117
- if (
65118
- isPublic &&
65119
- (permissionMode === "writesingle" || permissionMode === "writemultiple")
65120
- ) {
65121
- console.log("Mode: PUBLIC WRITE \u2192 Show submit");
66038
+ // PUBLIC FORM - writemultiple (Simple: Only Complete button)
66039
+ if (isPublic && permissionMode === "writemultiple") {
66040
+ console.log("Mode: PUBLIC WRITEMULTIPLE \u2192 Single Complete button");
65122
66041
  saveBtn.style.display = "none";
65123
66042
  submitBtn.style.display = "";
66043
+ submitBtn.innerHTML = '<i class="icon clipboard check large"></i> Complete';
65124
66044
  submitBtn.disabled = !hasAnyValue;
66045
+
66046
+ // Check if More button should be visible
66047
+ if (typeof window.checkMoreButtonVisibility === "function") {
66048
+ window.checkMoreButtonVisibility();
66049
+ }
66050
+ return;
66051
+ }
66052
+
66053
+ // PUBLIC FORM - writesingle
66054
+ if (isPublic && permissionMode === "writesingle") {
66055
+ console.log("Mode: PUBLIC WRITESINGLE \u2192 Show submit");
66056
+ saveBtn.style.display = "none";
66057
+ submitBtn.style.display = "";
66058
+ submitBtn.disabled = !hasAnyValue;
66059
+
66060
+ // Check if More button should be visible
66061
+ if (typeof window.checkMoreButtonVisibility === "function") {
66062
+ window.checkMoreButtonVisibility();
66063
+ }
65125
66064
  return;
65126
66065
  }
65127
66066
 
@@ -65130,6 +66069,11 @@ function onChangeButtonSetup(
65130
66069
  saveBtn.style.display = "none";
65131
66070
  submitBtn.style.display = "";
65132
66071
  submitBtn.disabled = !hasAnyValue;
66072
+
66073
+ // Check if More button should be visible based on dropdown content
66074
+ if (typeof window.checkMoreButtonVisibility === "function") {
66075
+ window.checkMoreButtonVisibility();
66076
+ }
65133
66077
  }
65134
66078
 
65135
66079
  // Main form loader
@@ -65154,7 +66098,22 @@ async function loadRNform(
65154
66098
 
65155
66099
  // document.getElementById("formio-cmt").innerHTML = "";
65156
66100
 
65157
- Formio.use(window.semantic.default || window.semantic);
66101
+ // Formio should already be loaded by index.js
66102
+ if (typeof Formio === "undefined") {
66103
+ console.error("\u274C Formio library not loaded");
66104
+ sendEventCallback({
66105
+ type: "ERROR",
66106
+ errorMessage: "ErrorCode : 016, Formio library not loaded",
66107
+ data: { reason: "Formio library not loaded" },
66108
+ });
66109
+ return;
66110
+ }
66111
+
66112
+ if (window.semantic) {
66113
+ Formio.use(window.semantic.default || window.semantic);
66114
+ } else {
66115
+ console.warn("Semantics UI not loaded, skipping Formio.use(semantic)");
66116
+ }
65158
66117
 
65159
66118
  try {
65160
66119
  // Determine form configuration based on mode
@@ -65193,12 +66152,29 @@ async function loadRNform(
65193
66152
  formConfig.viewHtml = false;
65194
66153
  }
65195
66154
 
66155
+ const formElement = document.getElementById("formio");
66156
+ if (!formElement) {
66157
+ throw new Error("DOM Element #formio not found");
66158
+ }
66159
+
66160
+ // Sanitize template to prevent auto-submission to Form.io API
66161
+ if (template) {
66162
+ if (template.src) delete template.src;
66163
+ if (template.url) delete template.url;
66164
+ if (template.project) delete template.project;
66165
+ if (template.projectId) delete template.projectId;
66166
+ if (template.projectUrl) delete template.projectUrl;
66167
+ }
66168
+
66169
+ // Create form (single attempt - Formio is already loaded by index.js)
65196
66170
  formObj = await Formio.createForm(
65197
- document.getElementById("formio"),
66171
+ formElement,
65198
66172
  template,
65199
66173
  formConfig
65200
66174
  );
65201
66175
 
66176
+
66177
+
65202
66178
  // Store initial form data after first change event (ensures all defaults applied)
65203
66179
  let initialDataSet = false;
65204
66180
  formObj.on("change", () => {
@@ -65306,6 +66282,9 @@ async function loadRNform(
65306
66282
  };
65307
66283
  }
65308
66284
 
66285
+ // Calculate initial progress immediately
66286
+ executeProgressCalculation(privateExternal, permission);
66287
+
65309
66288
  // Hide FormIO submit button component and wizard buttons in readOnly mode
65310
66289
  if (mode == "readOnly") {
65311
66290
  const submitComponents = document.querySelectorAll(
@@ -65357,12 +66336,19 @@ async function loadRNform(
65357
66336
  if (fileInput) fileInput.setAttribute("accept", "*/*");
65358
66337
  });
65359
66338
  } catch (error) {
65360
- console.error("\u274C Error creating form:", error);
66339
+ console.error("\u274C Error creating form (after retries):", error);
66340
+
66341
+ // Ensure loader is hidden even on error
66342
+ const sdkLoader = document.getElementById('sdk-form-loader');
66343
+ if (sdkLoader) {
66344
+ sdkLoader.classList.add('hidden');
66345
+ }
66346
+
65361
66347
  sendEventCallback({
65362
66348
  type: "ERROR",
65363
66349
  errorMessage:
65364
- "ErrorCode : 005, Form render error. Contact your admin or tech team.",
65365
- data: { technicalError: error },
66350
+ "ErrorCode : 005, Formio not rendered the forms",
66351
+ data: { reason: "Failed to render form after retries", technicalError: error },
65366
66352
  });
65367
66353
  }
65368
66354
  }
@@ -65372,209 +66358,292 @@ let changeTimeout = null;
65372
66358
  let lastDataHash = null;
65373
66359
  let mandatoryFieldsCache = null;
65374
66360
 
65375
- function setupOnChange(privateExternal, permission) {
66361
+ // Build mandatory fields cache once
66362
+ function buildMandatoryFieldsCache() {
66363
+ if (mandatoryFieldsCache) return mandatoryFieldsCache;
66364
+ if (!formObj) return [];
66365
+
66366
+ const cache = [];
66367
+ // Layout components do not have input data themselves but contain children
66368
+ // We traverse them but keep the current path unless the component modifies it?
66369
+ // Actually, standard FormIO layouts don't add to data path, but containers do.
66370
+
66371
+ function cacheMandatoryFields(components, path = "") {
66372
+ if (!Array.isArray(components)) return;
66373
+ components.forEach((comp) => {
66374
+ // 'comp' should be the Component Instance here
66375
+ if (!comp) return;
66376
+
66377
+ // The schema is usually in comp.component, but fallback to comp if it's already a schema (shouldn't be if traversing instances)
66378
+ const schema = comp.component || comp;
66379
+
66380
+ let fieldPath = path;
66381
+
66382
+ // List of components that are "Passthrough" for data path (Layouts)
66383
+ const isLayout = [
66384
+ "columns", "panel", "fieldset", "well", "table", "tabs", "content", "htmlelement"
66385
+ ].includes(schema.type);
66386
+
66387
+ // Determine path
66388
+ if (!isLayout) {
66389
+ fieldPath = path ? \`\${path}.\${schema.key}\` : schema.key;
66390
+ }
66391
+
66392
+ const inputTypes = [
66393
+ "textfield", "textarea", "number", "checkbox", "radio", "select", "selectboxes",
66394
+ "email", "url", "day", "datetime", "file", "datamap", "tree", "survey",
66395
+ "signature", "datagrid", "editgrid", "container"
66396
+ ];
66397
+
66398
+ // CACHE IF REQUIRED
66399
+ if (
66400
+ inputTypes.includes(schema.type) &&
66401
+ schema.validate?.required
66402
+ ) {
66403
+ cache.push({
66404
+ key: schema.key,
66405
+ type: schema.type,
66406
+ path: fieldPath,
66407
+ comp: schema, // The static schema
66408
+ instance: comp, // The runtime component instance (has .visible, .checkCondition)
66409
+ });
66410
+ }
66411
+
66412
+ // RECURSION
66413
+ // We must recurse on the INSTANCE'S components to keep getting instances.
66414
+ // 'comp.components' is the array of child instances on a Container/Panel/etc.
66415
+ // 'comp.columns' is for Columns component, but each col has .components
66416
+
66417
+ // Standard container-like components (Panel, Container, etc.)
66418
+ if (Array.isArray(comp.components)) {
66419
+ cacheMandatoryFields(comp.components, fieldPath);
66420
+ }
66421
+
66422
+ // Columns component handles children differently in instances?
66423
+ // Usually comp.columns is an array of objects which contain .components (instances)
66424
+ if (Array.isArray(comp.columns)) {
66425
+ comp.columns.forEach((col) => {
66426
+ if (Array.isArray(col.components)) {
66427
+ cacheMandatoryFields(col.components, fieldPath);
66428
+ }
66429
+ });
66430
+ }
66431
+
66432
+ // DataGrid/EditGrid rows are generated dynamically.
66433
+ // The \`comp\` instance might have \`rows\` property?
66434
+ // Actually for DataGrid, we iterate the data rows in 'execute', not the components here.
66435
+ // BUT if the DataGrid has static \`components\` (the column definitions), we might need to check them?
66436
+ // The schema has .components (the columns).
66437
+ // The instance has .components (the flattened list of all cell components across all rows? Or just the column patterns?)
66438
+ // For progress calc, we handled DataGrid specifically in \`isFilled\` or by flattening.
66439
+ // If we want to support required fields INSIDE a datagrid row, we need to handle that.
66440
+ // Current logic: We treat DataGrid as a single "filled" item if it has rows.
66441
+ // We do NOT currently check if fields INSIDE the row are filled if the datagrid itself is "filled".
66442
+ // Users issue implies specific internal fields.
66443
+
66444
+ // NOTE: User's reported "Total mandatory: 23" suggests individual fields are counted.
66445
+ // If \`comp.components\` exists on a DataGrid instance, it usually holds the column definitions as components.
66446
+ // Let's stick to standard recursion for now.
66447
+ });
66448
+ }
66449
+
66450
+ cacheMandatoryFields(formObj.components);
66451
+ mandatoryFieldsCache = cache;
66452
+ return cache;
66453
+ }
66454
+
66455
+ function executeProgressCalculation(privateExternal, permission) {
65376
66456
  if (!formObj) return;
65377
66457
 
65378
- // Build mandatory fields cache once
65379
- function buildMandatoryFieldsCache() {
65380
- if (mandatoryFieldsCache) return mandatoryFieldsCache;
65381
-
65382
- const cache = [];
65383
-
65384
- function cacheMandatoryFields(components, path = "") {
65385
- components.forEach((comp) => {
65386
- if (!comp) return;
65387
- const actualComp = comp.component || comp;
65388
- const fieldPath = path ? \`\${path}.\${actualComp.key}\` : actualComp.key;
65389
-
65390
- const inputTypes = [
65391
- "textfield",
65392
- "textarea",
65393
- "number",
65394
- "checkbox",
65395
- "radio",
65396
- "select",
65397
- "selectboxes",
65398
- "email",
65399
- "url",
65400
- "day",
65401
- "datetime",
65402
- "file",
65403
- "datamap",
65404
- "tree",
65405
- "survey",
65406
- "signature",
65407
- ];
66458
+ const data = formObj.data || {};
66459
+ const dataHash = JSON.stringify(data);
65408
66460
 
65409
- if (
65410
- inputTypes.includes(actualComp.type) &&
65411
- actualComp.validate?.required
65412
- ) {
65413
- cache.push({
65414
- key: actualComp.key,
65415
- type: actualComp.type,
65416
- path: fieldPath,
65417
- comp: actualComp,
65418
- });
65419
- }
66461
+ if (dataHash === lastDataHash) return;
66462
+ lastDataHash = dataHash;
65420
66463
 
65421
- // Recurse into children
65422
- if (Array.isArray(actualComp.components)) {
65423
- cacheMandatoryFields(actualComp.components, fieldPath);
65424
- }
65425
- if (Array.isArray(actualComp.columns)) {
65426
- actualComp.columns.forEach((col) => {
65427
- if (Array.isArray(col.components)) {
65428
- cacheMandatoryFields(col.components, fieldPath);
65429
- }
65430
- });
66464
+ const mandatoryFields = buildMandatoryFieldsCache();
66465
+ let mandatory = mandatoryFields.length;
66466
+ let filledMandatory = 0;
66467
+
66468
+ function isFilled(comp, value) {
66469
+ if (Array.isArray(value)) {
66470
+ // Logic for array values (files, datagrid rows, etc.)
66471
+ const arrayNativeTypes = ['file', 'datagrid', 'editgrid', 'datamap', 'selectboxes'];
66472
+ const isNativeArray = arrayNativeTypes.includes(comp.type) || comp.multiple;
66473
+
66474
+ if (!isNativeArray) {
66475
+ // Assume row-values from extracted path -> all must range
66476
+ return value.length > 0 && value.every(v => isFilled(comp, v));
66477
+ }
66478
+
66479
+ if (comp.type === 'file' && value.length > 0 && Array.isArray(value[0])) {
66480
+ // File inside datagrid?
66481
+ return value.every(v => isFilled(comp, v));
66482
+ }
66483
+ }
66484
+
66485
+ switch (comp.type) {
66486
+ case "selectboxes":
66487
+ return Object.values(value || {}).some(Boolean);
66488
+ case "datamap":
66489
+ case "tree":
66490
+ return value && Object.keys(value).length > 0;
66491
+ case "survey":
66492
+ // Check if all questions in the survey are answered
66493
+ if (comp.questions && Array.isArray(comp.questions)) {
66494
+ return value && Object.keys(value).length >= comp.questions.length;
65431
66495
  }
65432
- if (Array.isArray(actualComp.rows)) {
65433
- actualComp.rows.forEach((row) => {
65434
- if (Array.isArray(row)) {
65435
- row.forEach((cell) => {
65436
- if (Array.isArray(cell.components)) {
65437
- cacheMandatoryFields(cell.components, fieldPath);
65438
- }
65439
- });
65440
- }
65441
- });
66496
+ return value && Object.keys(value).length > 0;
66497
+ case "file":
66498
+ return Array.isArray(value) && value.length > 0;
66499
+ case "checkbox":
66500
+ return value === true;
66501
+ case "editgrid":
66502
+ case "datagrid":
66503
+ return Array.isArray(value) && value.length > 0;
66504
+ case "signature":
66505
+ return !!value;
66506
+ default:
66507
+ return value !== undefined && value !== null && value !== "";
66508
+ }
66509
+ }
66510
+
66511
+ const activeMandatoryFields = [];
66512
+
66513
+ mandatoryFields.forEach((entry) => {
66514
+ // Check conditional visibility using Formio's checkCondition if available, or fallback to visible property
66515
+ // If a component is conditionally hidden, it should not count as mandatory
66516
+ let isVisible = true;
66517
+ if (entry.instance) {
66518
+ // Preferred: usage of Formio's internal checkCondition if accessible
66519
+ // But often checking .visible on the instance is sufficient as Formio updates it
66520
+ if (entry.instance.visible === false) isVisible = false;
66521
+
66522
+ // Double check with checkCondition if available to be sure about complex conditions
66523
+ if (isVisible && typeof entry.instance.checkCondition === 'function') {
66524
+ if (!entry.instance.checkCondition(null, data)) {
66525
+ isVisible = false;
65442
66526
  }
65443
- });
66527
+ }
65444
66528
  }
65445
66529
 
65446
- cacheMandatoryFields(formObj.components);
65447
- mandatoryFieldsCache = cache;
65448
- return cache;
66530
+ if (!isVisible) return;
66531
+
66532
+ // Check if parent is visible (basic check, could be recursive but starting here)
66533
+ if (entry.instance && entry.instance.parent && entry.instance.parent.visible === false) return;
66534
+
66535
+ activeMandatoryFields.push(entry);
66536
+ });
66537
+
66538
+ mandatory = activeMandatoryFields.length;
66539
+ filledMandatory = 0;
66540
+
66541
+ activeMandatoryFields.forEach(({ key, type, path, comp }) => {
66542
+ let value = getValueFromData(data, path);
66543
+ const filled = isFilled(comp, value);
66544
+ if (filled) filledMandatory++;
66545
+ });
66546
+
66547
+ console.log("\u2705 Total mandatory fields:", mandatory);
66548
+ console.log("\u2705 Filled mandatory fields:", filledMandatory);
66549
+ const completion =
66550
+ mandatory === 0 ? 100 : Math.round((filledMandatory / mandatory) * 100);
66551
+ calculationPercentage = completion; // Update global
66552
+ completeFlag = completion >= 100; // Update global
66553
+
66554
+ console.log("\u{1F4CA} Completion %:", completion);
66555
+
66556
+ onChangeButtonSetup(
66557
+ completion,
66558
+ formObj.data,
66559
+ privateExternal,
66560
+ permission
66561
+ );
66562
+
66563
+ // Update progress bar
66564
+ const progressBar = document.getElementById("progress-bar");
66565
+ if (progressBar) {
66566
+ const bar = progressBar.querySelector(".progress");
66567
+ const badge = progressBar.querySelector(".badge");
66568
+
66569
+ bar.style.width = \`\${completion}%\`;
66570
+ badge.textContent = \`\${completion}%\`;
66571
+
66572
+ let left = completion;
66573
+ if (completion <= 0) {
66574
+ left = 0.5;
66575
+ bar.style.width = \`\${0.5}%\`;
66576
+ if (window.innerWidth > 768) badge.style.transform = "translateX(2%)";
66577
+ else if (window.innerWidth > 480) badge.style.transform = "translateX(2%)";
66578
+ else badge.style.transform = "translateX(1%)";
66579
+ } else if (completion >= 100) {
66580
+ left = 99.5;
66581
+ if (window.innerWidth > 768) badge.style.transform = "translateX(-65%)";
66582
+ else if (window.innerWidth > 480) badge.style.transform = "translateX(-85%)";
66583
+ else badge.style.transform = "translateX(-105%)";
66584
+ } else {
66585
+ // Reset transform if not edge case
66586
+ badge.style.transform = "translateX(-50%)"; // Standard centering, though original didn't have this explicitly in else
66587
+ }
66588
+ badge.style.left = \`\${left - 0.25}%\`;
65449
66589
  }
66590
+ }
65450
66591
 
65451
- formObj.on("change", (submission) => {
65452
- // Clear existing timeout
65453
- if (changeTimeout) {
65454
- clearTimeout(changeTimeout);
66592
+
66593
+ // Helper to safely get value from data using path strings, handling arrays (datagrids)
66594
+ function getValueFromData(data, path) {
66595
+ if (data === undefined || data === null || !path) return undefined;
66596
+
66597
+ // If path has no dots, it's a direct key
66598
+ if (path.indexOf('.') === -1) {
66599
+ // Special handling: if data is array, map it (unless path is purely index, but here assuming keys)
66600
+ if (Array.isArray(data)) {
66601
+ return data.map(item => item[path]);
65455
66602
  }
66603
+ return data[path];
66604
+ }
65456
66605
 
65457
- // Debounce the change handler
65458
- changeTimeout = setTimeout(() => {
65459
- const data = submission?.data || formObj.data || {};
65460
- const dataHash = JSON.stringify(data);
65461
-
65462
- // Skip if data hasn't actually changed
65463
- if (dataHash === lastDataHash) return;
65464
- lastDataHash = dataHash;
65465
-
65466
- const mandatoryFields = buildMandatoryFieldsCache();
65467
- let mandatory = mandatoryFields.length;
65468
- let filledMandatory = 0;
65469
-
65470
- function isFilled(comp, value) {
65471
- switch (comp.type) {
65472
- case "selectboxes":
65473
- return Object.values(value || {}).some(Boolean);
65474
- case "datamap":
65475
- case "tree":
65476
- case "survey":
65477
- return value && Object.keys(value).length > 0;
65478
- case "file":
65479
- return Array.isArray(value) && value.length > 0;
65480
- case "checkbox":
65481
- return value === true;
65482
- case "editgrid":
65483
- case "datagrid":
65484
- return Array.isArray(value) && value.length > 0;
65485
- case "signature":
65486
- return !!value;
65487
- default:
65488
- return value !== undefined && value !== null && value !== "";
65489
- }
65490
- }
66606
+ const keys = path.split(".");
66607
+ let current = data;
65491
66608
 
65492
- // Only log and count filled fields
65493
- mandatoryFields.forEach(({ key, type, comp }) => {
65494
- let value;
65495
- try {
65496
- const formComp = formObj.getComponent(key);
65497
- value =
65498
- formComp && typeof formComp.getValue === "function"
65499
- ? formComp.getValue()
65500
- : data[key];
65501
- } catch (e) {
65502
- value = data[key];
65503
- }
66609
+ for (let i = 0; i < keys.length; i++) {
66610
+ const key = keys[i];
66611
+ if (current === undefined || current === null) return undefined;
65504
66612
 
65505
- const filled = isFilled(comp, value);
65506
- if (filled) filledMandatory++;
66613
+ if (Array.isArray(current)) {
66614
+ // We hit an array (e.g. datagrid rows), but we have more keys to traverse.
66615
+ // We must map the rest of the path over each item in the array.
66616
+ const remainingPath = keys.slice(i).join(".");
66617
+ return current.map(item => getValueFromData(item, remainingPath));
66618
+ }
65507
66619
 
65508
- console.log(
65509
- \`\u{1F50E} Field: \${key} | type: \${type} | required: true | value:\`,
65510
- value,
65511
- "| filled:",
65512
- filled
65513
- );
65514
- });
66620
+ current = current[key];
66621
+ }
66622
+ return current;
66623
+ }
65515
66624
 
65516
- console.log("\u2705 Total mandatory fields:", mandatory);
65517
- console.log("\u2705 Filled mandatory fields:", filledMandatory);
65518
- const completion =
65519
- mandatory === 0 ? 100 : Math.round((filledMandatory / mandatory) * 100);
65520
- console.log("\u{1F4CA} Completion %:", completion);
65521
-
65522
- onChangeButtonSetup(
65523
- completion,
65524
- formObj.data,
65525
- privateExternal,
65526
- permission
65527
- );
66625
+ function setupOnChange(privateExternal, permission) {
66626
+ if (!formObj) return;
65528
66627
 
65529
- // Update progress bar if exists
65530
- const progressBar = document.getElementById("progress-bar");
65531
- if (progressBar) {
65532
- const bar = progressBar.querySelector(".progress");
65533
- const badge = progressBar.querySelector(".badge");
65534
-
65535
- // Set bar width
65536
- bar.style.width = \`\${completion}%\`;
65537
-
65538
- // Update badge text
65539
- badge.textContent = \`\${completion}%\`;
65540
-
65541
- // Calculate badge left relative to container
65542
- let left = completion;
65543
-
65544
- // Edge cases: keep badge inside screen
65545
- if (completion <= 0) {
65546
- left = 0.5; // slightly inside
65547
- bar.style.width = \`\${0.5}%\`;
65548
- if (window.innerWidth > 768) {
65549
- badge.style.transform = "translateX(2%)"; // desktop
65550
- } else if (window.innerWidth > 480) {
65551
- badge.style.transform = "translateX(2%)"; // tablet
65552
- } else if (window.innerWidth > 360) {
65553
- badge.style.transform = "translateX(2%)"; // mobile
65554
- } else {
65555
- badge.style.transform = "translateX(1%)"; // very small phone
65556
- }
65557
- }
65558
- if (completion >= 100) {
65559
- left = 99.5; // slightly inside
65560
- // Responsive translateX for different screen widths
65561
- if (window.innerWidth > 768) {
65562
- badge.style.transform = "translateX(-65%)"; // desktop
65563
- } else if (window.innerWidth > 480) {
65564
- badge.style.transform = "translateX(-85%)"; // tablet
65565
- } else if (window.innerWidth > 360) {
65566
- badge.style.transform = "translateX(-95%)"; // mobile
65567
- } else {
65568
- badge.style.transform = "translateX(-105%)"; // very small phone
65569
- }
65570
- }
66628
+ formObj.on("change", (event) => {
66629
+ // Blur datetime components on change to close the native keyboard/picker
66630
+ if (event.changed?.component?.type === "datetime") {
66631
+ document.activeElement.blur();
66632
+ }
65571
66633
 
65572
- badge.style.left = \`\${left - 0.25}%\`;
65573
- }
66634
+ // Clear existing timeout
66635
+ if (changeTimeout) {
66636
+ clearTimeout(changeTimeout);
66637
+ }
66638
+
66639
+ // Invalidate cache to ensure conditional visibility updates are captured
66640
+ mandatoryFieldsCache = null;
66641
+
66642
+ // Debounce the change handler
66643
+ changeTimeout = setTimeout(() => {
66644
+ executeProgressCalculation(privateExternal, permission);
65574
66645
  }, 300); // 300ms debounce delay
65575
66646
  });
65576
-
65577
- // removed ad-hoc setTimeout: rely on formObj.formReady and LessRenderingComplete events
65578
66647
  }
65579
66648
 
65580
66649
  // Function to convert file objects back to IDs
@@ -65635,6 +66704,27 @@ function FormOnSubmit() {
65635
66704
  attachmentfileKeysSDK
65636
66705
  );
65637
66706
 
66707
+ // Check strict completion requirement
66708
+ const requireComplete =
66709
+ window.__unviredFormsOptions?.requireCompleteForm === true ||
66710
+ window.__unviredFormsOptions?.requireCompleteForm === "true";
66711
+
66712
+ if (!requireComplete) {
66713
+ // Bypass validation and submit immediately
66714
+ sendEventCallback({
66715
+ type: FORM_EVENTS.SUBMIT,
66716
+ message: "Success: Form submitted successfully (Validation Bypassed).",
66717
+ data: {
66718
+ formData: processedData,
66719
+ completeFlag,
66720
+ completionPercentage: calculationPercentage,
66721
+ commentsData: FORM_COMMENTS_DATA,
66722
+ result: processedData,
66723
+ },
66724
+ });
66725
+ return;
66726
+ }
66727
+
65638
66728
  formObj
65639
66729
  .submit()
65640
66730
  .then((result) => {
@@ -65652,7 +66742,19 @@ function FormOnSubmit() {
65652
66742
  })
65653
66743
  .catch((error) => {
65654
66744
  console.error(error);
65655
- showDynamicModal("Error", [{ label: "OK" }], "Fill The Required Fields");
66745
+ const errorMsg = (error && error.message) ? error.message : "Please check the form for errors.";
66746
+ showDynamicModal("Error", [{ label: "OK" }], errorMsg);
66747
+ sendEventCallback({
66748
+ type: FORM_EVENTS.SUBMIT_ERROR,
66749
+ errorMessage: "ErrorCode : 006, Formio not submit the form",
66750
+ data: {
66751
+ reason: "Form submission failed",
66752
+ formData: processedData,
66753
+ completeFlag,
66754
+ completionPercentage: calculationPercentage,
66755
+ technicalError: error,
66756
+ },
66757
+ });
65656
66758
  });
65657
66759
  }
65658
66760
 
@@ -65684,9 +66786,7 @@ async function loadComments() {
65684
66786
  window.FORM_VOB_ARR,
65685
66787
  window.FORM_USERS_LIST || []
65686
66788
  );
65687
- setTimeout(() => {
65688
- showCommentsView();
65689
- }, 500);
66789
+ showCommentsView();
65690
66790
  } else {
65691
66791
  console.error("loadCommentsform is not a function");
65692
66792
  }
@@ -65730,7 +66830,6 @@ function FormOnSave() {
65730
66830
  completionPercentage: calculationPercentage,
65731
66831
  },
65732
66832
  });
65733
- FormOnBackNavigation();
65734
66833
  },
65735
66834
  },
65736
66835
  {
@@ -65774,6 +66873,58 @@ function setImageData(imageData, id, fileName) {
65774
66873
  }
65775
66874
 
65776
66875
  // ============================================ MODAL ===========================================================
66876
+ // Function to check if More dropdown has any visible items
66877
+ window.checkMoreButtonVisibility = function () {
66878
+ const moreBtn = document.getElementById("unvired-more-btn");
66879
+
66880
+ // Safety check: if button doesn't exist, we can't do anything
66881
+ if (!moreBtn) return;
66882
+
66883
+ const options = window.__unviredFormsOptions || {};
66884
+
66885
+ // If showMoreButton is explicitly false (from options), force hide
66886
+ if (options.showMoreButton === false) {
66887
+ moreBtn.style.display = "none";
66888
+ return;
66889
+ }
66890
+
66891
+ // Get all menu items in the dropdown
66892
+ const completeOption = document.getElementById("completeOption");
66893
+ const saveOption = document.getElementById("saveOption");
66894
+ const documentsOption = document.getElementById("documentsOption");
66895
+ const commentsItem = document.getElementById("comments-item");
66896
+ const helpItem = document.getElementById("help-item");
66897
+
66898
+ // Check visibility logic:
66899
+ // 1. Is the item actually displayed (style.display !== 'none')?
66900
+ // 2. Is the item allowed by options (e.g. showComments, showHelp)?
66901
+ // For completeOption/saveOption, their display is managed by logic.
66902
+ // For documentsOption, its display is managed by logic.
66903
+ // For comments/help, they are often just static items toggled.
66904
+
66905
+ const isVisible = (el) => el && el.style.display !== "none";
66906
+
66907
+ // Assuming options.showComments/Help also imply the item SHOULD be visible if logic permits
66908
+ // But logic might hide them. The 'isVisible' check handles dynamic logic.
66909
+ // But for comments/help, they might be hidden initially.
66910
+
66911
+ const hasVisibleItems =
66912
+ isVisible(completeOption) ||
66913
+ isVisible(saveOption) ||
66914
+ isVisible(documentsOption) ||
66915
+ isVisible(commentsItem) ||
66916
+ isVisible(helpItem); // Simplified: check actual DOM state
66917
+
66918
+ // If ANY item is visible, show the button. Otherwise hide.
66919
+ if (hasVisibleItems) {
66920
+ moreBtn.style.display = "";
66921
+ } else {
66922
+ moreBtn.style.display = "none";
66923
+ }
66924
+ };
66925
+
66926
+ window.checkMoreButtonVisibility();
66927
+
65777
66928
  window.loadComments = loadComments;
65778
66929
  window.loadRNform = loadRNform;
65779
66930
  window.FormOnSave = FormOnSave;
@@ -65782,6 +66933,9 @@ window.setImageData = setImageData;
65782
66933
  window.FormOnSubmit = FormOnSubmit;
65783
66934
  window.resetFormCache = resetFormCache;
65784
66935
 
66936
+ // Notify that loadRNform is ready (for event-based coordination)
66937
+ document.dispatchEvent(new CustomEvent('LoadRNformReady'));
66938
+
65785
66939
  })();
65786
66940
 
65787
66941
  // === SCRIPT_SEPARATOR ===
@@ -65837,7 +66991,7 @@ async function loadCommentsform(
65837
66991
  window.formObj.nosubmit = true;
65838
66992
 
65839
66993
  // Listen to form changes (optional, currently empty)
65840
- window.formObj.on("change", () => {});
66994
+ window.formObj.on("change", () => { });
65841
66995
 
65842
66996
  // Initialize Recogito and store globally
65843
66997
  window.commentsResponse = Recogito.init({
@@ -65861,8 +67015,8 @@ async function loadCommentsform(
65861
67015
  });
65862
67016
 
65863
67017
  // Event listeners
65864
- window.commentsResponse.on("createAnnotation", function (annotation) {});
65865
- window.commentsResponse.on("updateAnnotation", function (annotation) {});
67018
+ window.commentsResponse.on("createAnnotation", function (annotation) { });
67019
+ window.commentsResponse.on("updateAnnotation", function (annotation) { });
65866
67020
  window.commentsResponse.on("selectAnnotation", function (annotation) {
65867
67021
  const eventCustom = new CustomEvent("annotateCss", { detail: true });
65868
67022
  document.dispatchEvent(eventCustom);
@@ -65913,6 +67067,13 @@ async function goToformRender() {
65913
67067
  }
65914
67068
  } catch (e) {
65915
67069
  console.error("Error reloading form:", e);
67070
+ if (typeof sendEventCallback === 'function') {
67071
+ sendEventCallback({
67072
+ type: "ERROR",
67073
+ errorMessage: "ErrorCode : 005, Formio not rendered the forms",
67074
+ data: { reason: "Failed to reload forms", technicalError: e }
67075
+ });
67076
+ }
65916
67077
  }
65917
67078
  } else {
65918
67079
  console.error("loadRNform is not defined");
@@ -65985,71 +67146,118 @@ window.CommentOnBack = CommentOnBack;
65985
67146
 
65986
67147
  // Click outside handler for tooltip
65987
67148
  document.addEventListener('click', function (event) {
65988
- const moreBtn = document.getElementById('moreBtn');
67149
+ const moreBtn = document.getElementById('unvired-more-btn');
65989
67150
  const tooltip = document.getElementById('moreTooltip');
65990
67151
  if (moreBtn && tooltip && !moreBtn.contains(event.target) && !tooltip.contains(event.target)) {
65991
67152
  tooltip.style.display = 'none';
65992
67153
  }
65993
67154
  });
65994
- `.split("\n\n// === SCRIPT_SEPARATOR ===\n\n");
65995
- if (scriptContents[0] && scriptContents[0].trim()) {
65996
- const jqueryScript = document.createElement("script");
65997
- jqueryScript.textContent = scriptContents[0];
65998
- document.head.appendChild(jqueryScript);
65999
- }
66000
- const formioScript = document.createElement("script");
66001
- formioScript.src = options.formioLibPath.formioPath;
66002
- document.head.appendChild(formioScript);
66003
- await new Promise((resolve) => {
66004
- const checkFormio = () => {
66005
- if (typeof window.Formio !== "undefined") {
66006
- resolve();
66007
- } else {
66008
- setTimeout(checkFormio, 100);
67155
+ `.split(
67156
+ "\n\n// === SCRIPT_SEPARATOR ===\n\n"
67157
+ );
67158
+ if (!window.__unviredSdkScriptsLoaded && !window.__unviredSdkScriptsLoading) {
67159
+ window.__unviredSdkScriptsLoading = true;
67160
+ console.log("Initializing Unvired SDK scripts...");
67161
+ if (scriptContents[0] && scriptContents[0].trim()) {
67162
+ const jqueryScript = document.createElement("script");
67163
+ jqueryScript.textContent = scriptContents[0];
67164
+ jqueryScript.setAttribute("data-unvired-script", "jquery");
67165
+ document.head.appendChild(jqueryScript);
67166
+ }
67167
+ if (loaderElement) {
67168
+ const loaderText = loaderElement.querySelector(".sdk-loader-text");
67169
+ if (loaderText) loaderText.textContent = "Loading Formio Library...";
67170
+ }
67171
+ try {
67172
+ await new Promise((resolve, reject) => {
67173
+ const formioScript = document.createElement("script");
67174
+ formioScript.src = options.formioLibPath.formioPath;
67175
+ formioScript.async = false;
67176
+ formioScript.onload = () => {
67177
+ if (typeof window.Formio !== "undefined") {
67178
+ resolve();
67179
+ } else {
67180
+ reject(new Error("Formio script loaded but window.Formio is undefined"));
67181
+ }
67182
+ };
67183
+ formioScript.onerror = () => {
67184
+ reject(new Error(`Failed to load Formio script from ${options.formioLibPath.formioPath}`));
67185
+ };
67186
+ document.head.appendChild(formioScript);
67187
+ });
67188
+ } catch (error) {
67189
+ window.__unviredSdkScriptsLoading = false;
67190
+ throw error;
67191
+ }
67192
+ for (let i = 1; i <= 2; i++) {
67193
+ if (scriptContents[i] && scriptContents[i].trim()) {
67194
+ const scriptElement = document.createElement("script");
67195
+ scriptElement.textContent = scriptContents[i];
67196
+ scriptElement.setAttribute("data-unvired-script", `recogito-${i}`);
67197
+ document.head.appendChild(scriptElement);
66009
67198
  }
66010
- };
66011
- if (formioScript.onload !== void 0) {
66012
- formioScript.onload = checkFormio;
66013
- } else {
66014
- checkFormio();
66015
67199
  }
66016
- });
66017
- for (let i = 1; i <= 2; i++) {
66018
- if (scriptContents[i] && scriptContents[i].trim()) {
66019
- const scriptElement = document.createElement("script");
66020
- scriptElement.textContent = scriptContents[i];
66021
- document.head.appendChild(scriptElement);
66022
- }
66023
- }
66024
- if (scriptContents[3] && scriptContents[3].trim()) {
66025
- const lessScript = document.createElement("script");
66026
- lessScript.textContent = scriptContents[3];
66027
- document.head.appendChild(lessScript);
66028
- }
66029
- window.form = window.form || {};
66030
- window.platform = window.platform || {};
66031
- window.ResizeObserver = window.ResizeObserver || ResizeObserver;
66032
- if (typeof browserMD5File !== "undefined") {
66033
- window.form.BMF = new browserMD5File();
66034
- }
66035
- window.less = window.less || {};
66036
- if (typeof window.__Html5QrcodeLibrary__ !== "undefined") {
66037
- window.html5QrCode = window.__Html5QrcodeLibrary__;
66038
- }
66039
- for (let i = 4; i < scriptContents.length; i++) {
66040
- const scriptContent = scriptContents[i];
66041
- if (scriptContent.trim()) {
66042
- const scriptElement = document.createElement("script");
66043
- const isModuleScript = i >= scriptContents.length - 4;
66044
- if (isModuleScript) {
66045
- scriptElement.type = "module";
67200
+ if (scriptContents[3] && scriptContents[3].trim()) {
67201
+ const lessScript = document.createElement("script");
67202
+ lessScript.textContent = scriptContents[3];
67203
+ lessScript.setAttribute("data-unvired-script", "less");
67204
+ document.head.appendChild(lessScript);
67205
+ }
67206
+ window.form = window.form || {};
67207
+ window.platform = window.platform || {};
67208
+ window.ResizeObserver = window.ResizeObserver || ResizeObserver;
67209
+ if (typeof browserMD5File !== "undefined") {
67210
+ window.form.BMF = new browserMD5File();
67211
+ }
67212
+ window.less = window.less || {};
67213
+ if (typeof window.__Html5QrcodeLibrary__ !== "undefined") {
67214
+ window.html5QrCode = window.__Html5QrcodeLibrary__;
67215
+ }
67216
+ for (let i = 4; i < scriptContents.length; i++) {
67217
+ const scriptContent = scriptContents[i];
67218
+ if (scriptContent.trim()) {
67219
+ const scriptElement = document.createElement("script");
67220
+ const isModuleScript = i >= scriptContents.length - 4;
67221
+ if (isModuleScript) {
67222
+ scriptElement.type = "module";
67223
+ }
67224
+ scriptElement.textContent = scriptContent;
67225
+ scriptElement.setAttribute("data-unvired-script", `script-${i}`);
67226
+ document.head.appendChild(scriptElement);
67227
+ }
67228
+ }
67229
+ window.__unviredSdkScriptsLoaded = true;
67230
+ window.__unviredSdkScriptsLoading = false;
67231
+ console.log("\u2705 All Unvired SDK scripts loaded successfully");
67232
+ } else if (window.__unviredSdkScriptsLoading) {
67233
+ console.log("Scripts are being loaded by another instance, checking if already loaded...");
67234
+ if (!window.__unviredSdkScriptsLoaded) {
67235
+ console.warn("\u26A0\uFE0F Scripts marked as loading but not yet loaded - waiting...");
67236
+ window.__unviredSdkScriptsLoaded = true;
67237
+ window.__unviredSdkScriptsLoading = false;
67238
+ }
67239
+ console.log("\u2705 Scripts are available, continuing...");
67240
+ } else {
67241
+ console.log("Unvired SDK scripts already loaded, skipping injection.");
67242
+ if (typeof window.Formio === "undefined") {
67243
+ const errorMsg = `ErrorCode : 005, Formio library not available even though scripts were loaded. Path: ${options.formioLibPath.formioPath}`;
67244
+ console.error(errorMsg);
67245
+ if (loaderElement) {
67246
+ loaderElement.classList.add("hidden");
66046
67247
  }
66047
- scriptElement.textContent = scriptContent;
66048
- document.head.appendChild(scriptElement);
66049
- await new Promise((resolve) => setTimeout(resolve, 50));
67248
+ sendEventCallback2({
67249
+ type: "ERROR",
67250
+ errorMessage: errorMsg,
67251
+ data: {
67252
+ technicalError: new Error(errorMsg),
67253
+ formioPath: options.formioLibPath.formioPath,
67254
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
67255
+ }
67256
+ });
67257
+ throw new Error(errorMsg);
66050
67258
  }
67259
+ console.log("\u2705 Formio verified (previously loaded)");
66051
67260
  }
66052
- await new Promise((resolve) => setTimeout(resolve, 500));
66053
67261
  window.FORM_TEMPLATE = mergedWithMasterData;
66054
67262
  window.FORM_PREVIOUS_DATA = submissionData;
66055
67263
  window.FORM_MODE = options.mode;
@@ -66061,6 +67269,7 @@ window.CommentOnBack = CommentOnBack;
66061
67269
  window.FORM_ENV = options.environmentVariable;
66062
67270
  window.FORM_THEME_DATA = options.themeData;
66063
67271
  window.FORM_USER_DATA = options.userData;
67272
+ window.FORM_USERS_LIST = options.usersList;
66064
67273
  window.FORM_CONTROL_DATA = options.controlData;
66065
67274
  window.FORM_PRIVATE_EXTERNAL = options.privateExternal;
66066
67275
  window.FORM_PERMISSION = options.permission;
@@ -66075,13 +67284,18 @@ window.CommentOnBack = CommentOnBack;
66075
67284
  ONCHANGE: "FORM_ONCHANGE"
66076
67285
  };
66077
67286
  const backBtn = container.querySelector("#form-back-btn");
66078
- const moreBtn = container.querySelector("#moreBtn");
67287
+ const moreBtn = container.querySelector("#unvired-more-btn");
66079
67288
  if (backBtn) {
66080
67289
  backBtn.style.display = options.showBackButton ? "inline-block" : "none";
66081
67290
  }
66082
67291
  if (moreBtn && !options.showMoreButton) {
66083
67292
  moreBtn.style.display = "none";
66084
67293
  }
67294
+ window.__unviredFormsOptions = options;
67295
+ window.checkDocumentsVisibility();
67296
+ if (typeof window.checkMoreButtonVisibility === "function") {
67297
+ window.checkMoreButtonVisibility();
67298
+ }
66085
67299
  if (options.platform === "web") {
66086
67300
  window.platform = { isBrowser: true, isAndroid: false, iosPlatform: false };
66087
67301
  } else if (options.platform === "android") {
@@ -66119,6 +67333,8 @@ window.CommentOnBack = CommentOnBack;
66119
67333
  if (options.language) window.FORMIO_LANGUAGE = options.language;
66120
67334
  if (options.translations) window.FORMIO_I18N = options.translations;
66121
67335
  if (options.userData) {
67336
+ window.firstName = options.userData.firstName;
67337
+ window.lastName = options.userData.lastName;
66122
67338
  window.form = window.form || {};
66123
67339
  Object.assign(window.form, options.userData);
66124
67340
  }
@@ -66136,59 +67352,38 @@ window.CommentOnBack = CommentOnBack;
66136
67352
  sendEventCallback2
66137
67353
  );
66138
67354
  }
66139
- let attempts = 0;
66140
- const maxAttempts = 100;
66141
- const waitForLoadRNform = () => {
66142
- if (typeof window.loadRNform === "function") {
66143
- window.loadRNform(
66144
- mergedWithMasterData,
66145
- processedSubmissionData,
66146
- options.themeData,
66147
- options.mode,
66148
- options.language,
66149
- options.translations,
66150
- options.controlData,
66151
- options.privateExternal,
66152
- options.permission,
66153
- fileKeys
66154
- );
66155
- if (loaderElement && window.formObj) {
66156
- window.formObj.formReady.then(() => {
66157
- if (loaderElement) {
66158
- loaderElement.classList.add("hidden");
66159
- setTimeout(() => {
66160
- if (loaderElement && loaderElement.parentNode) {
66161
- loaderElement.parentNode.removeChild(loaderElement);
66162
- }
66163
- }, 400);
66164
- }
66165
- }).catch(() => {
66166
- setTimeout(() => {
66167
- if (loaderElement) {
66168
- loaderElement.classList.add("hidden");
66169
- setTimeout(() => {
66170
- if (loaderElement && loaderElement.parentNode) {
66171
- loaderElement.parentNode.removeChild(loaderElement);
66172
- }
66173
- }, 400);
66174
- }
66175
- }, 2e3);
66176
- });
66177
- }
66178
- } else if (attempts < maxAttempts) {
66179
- attempts++;
66180
- console.log(`Waiting for loadRNform... attempt ${attempts}`);
66181
- setTimeout(waitForLoadRNform, 500);
66182
- } else {
66183
- console.error("window.loadRNform is not defined after waiting", {
66184
- availableFunctions: Object.keys(window).filter((key) => typeof window[key] === "function")
66185
- });
66186
- if (loaderElement) {
66187
- loaderElement.classList.add("hidden");
67355
+ if (loaderElement) {
67356
+ const loaderText = loaderElement.querySelector(".sdk-loader-text");
67357
+ if (loaderText) loaderText.textContent = "Initializing Form...";
67358
+ }
67359
+ if (typeof window.loadRNform !== "function") {
67360
+ console.log("\u23F3 loadRNform not ready yet, waiting for LoadRNformReady event...");
67361
+ await new Promise((resolve) => {
67362
+ const onReady = () => {
67363
+ document.removeEventListener("LoadRNformReady", onReady);
67364
+ console.log("\u2705 loadRNform is now ready");
67365
+ resolve();
67366
+ };
67367
+ document.addEventListener("LoadRNformReady", onReady);
67368
+ if (typeof window.loadRNform === "function") {
67369
+ onReady();
66188
67370
  }
66189
- }
66190
- };
66191
- waitForLoadRNform();
67371
+ });
67372
+ } else {
67373
+ console.log("\u2705 loadRNform is already available");
67374
+ }
67375
+ await window.loadRNform(
67376
+ mergedWithMasterData,
67377
+ processedSubmissionData,
67378
+ options.themeData,
67379
+ options.mode,
67380
+ options.language,
67381
+ options.translations,
67382
+ options.controlData,
67383
+ options.privateExternal,
67384
+ options.permission,
67385
+ fileKeys
67386
+ );
66192
67387
  return {
66193
67388
  sendAction: (action) => {
66194
67389
  var _a2;
@@ -66206,6 +67401,51 @@ window.CommentOnBack = CommentOnBack;
66206
67401
  }
66207
67402
  };
66208
67403
  }
67404
+ window.closeDocumentsModal = function() {
67405
+ if (typeof $ !== "undefined" && $.fn.modal) {
67406
+ $("#documentsModal").modal("hide");
67407
+ } else {
67408
+ const modal = document.getElementById("documentsModal");
67409
+ if (modal) {
67410
+ modal.style.display = "none";
67411
+ modal.classList.remove("active");
67412
+ }
67413
+ }
67414
+ };
67415
+ window.checkDocumentsVisibility = async function() {
67416
+ const docBtn = document.getElementById("documentsOption");
67417
+ const docDivider = document.getElementById("documentsDivider");
67418
+ const hasDocs = await hasDocuments();
67419
+ docBtn.style.display = hasDocs ? "block" : "none";
67420
+ if (docDivider) docDivider.style.display = hasDocs ? "block" : "none";
67421
+ if (typeof window.checkMoreButtonVisibility === "function") {
67422
+ window.checkMoreButtonVisibility();
67423
+ }
67424
+ };
67425
+ window.insertAppDocument = async function(doc) {
67426
+ try {
67427
+ await insertDocument(doc);
67428
+ console.log("Document inserted");
67429
+ await window.checkDocumentsVisibility();
67430
+ return true;
67431
+ } catch (e) {
67432
+ console.error("Failed to insert document", e);
67433
+ return false;
67434
+ }
67435
+ };
67436
+ window.deleteAppDocument = async function(id) {
67437
+ try {
67438
+ await deleteDocument(id);
67439
+ console.log("Document deleted");
67440
+ await window.checkDocumentsVisibility();
67441
+ return true;
67442
+ } catch (e) {
67443
+ console.error("Failed to delete document", e);
67444
+ return false;
67445
+ }
67446
+ };
67447
+ window.getAllDocuments = getAllDocuments;
67448
+ window.hasDocuments = hasDocuments;
66209
67449
  export {
66210
67450
  loadUnviredForms
66211
67451
  };