Compare commits

..
628 Commits
Author SHA1 Message Date
prafull-opensignlabsandGitHub 1641217c43 Update docusaurus.config.js 2025-08-28 16:21:15 +05:30
prafull-opensignlabsandGitHub 49a63eeb50 Update docusaurus.config.js 2025-08-28 16:12:53 +05:30
prafull-opensignlabsandGitHub b73b0c6b2d Update introduction of opensign v1.1 2025-08-28 11:42:52 +05:30
prafull-opensignlabsandGitHub c9fe580e44 add prefill widget json 2025-08-28 11:34:53 +05:30
prafull-opensignlabsandGitHub b0556310dd Delete docs/docs/API-docs/v2 directory 2025-08-28 10:38:23 +05:30
prafull-opensignlabsandGitHub 9da13a23cc Create intro.md v1.1 2025-08-28 10:38:01 +05:30
prafull-opensignlabsandGitHub d727e16ef9 Create opensign.yaml 2025-08-28 10:37:24 +05:30
prafull-opensignlabsandGitHub 1508c63d0e Delete docs/docs/API-docs/v1.1 2025-08-28 10:36:54 +05:30
prafull-opensignlabsandGitHub 8a198c1543 Create v1.1 2025-08-28 10:36:27 +05:30
prafull-opensignlabsandGitHub da0652d8ed Create opensign.yaml v2 2025-08-28 10:35:39 +05:30
prafull-opensignlabsandGitHub 59f1453176 Rename opensign.yml to opensign.yaml 2025-08-28 10:33:31 +05:30
prafull-opensignlabsandGitHub ad748fbd03 Update intro.md v2 2025-08-28 10:32:41 +05:30
prafull-opensignlabsandGitHub 2a1afca818 Create intro.md 2025-08-28 10:32:29 +05:30
prafull-opensignlabsandGitHub 8e1cfd1ce5 Update intro.md v1 2025-08-28 10:31:56 +05:30
prafull-opensignlabsandGitHub 6236353316 Create opensign.yml v1 2025-08-28 10:31:03 +05:30
prafull-opensignlabsandGitHub b5271017dc Create intro.md 2025-08-28 10:29:14 +05:30
prafull-opensignlabsandGitHub 4b6b955fe5 Merge pull request #1905 from pravinOpenSign/patch-21
Patch 21
2025-08-22 13:17:24 +05:30
pravinandGitHub a2b3761d5e Update create-template.md
Update the content on the create template page.
2025-08-22 12:53:46 +05:30
pravinandGitHub 2c5e311c7f Update embed-template.md 2025-08-22 12:46:31 +05:30
pravinandGitHub 98facb2f6d Update publicTemplate.md
update the menu sequence
2025-08-22 12:46:11 +05:30
pravinandGitHub 77618e57ea Update quick-send.md
update the Menu sequence.
2025-08-22 12:45:38 +05:30
pravinandGitHub 0bb8aa880d Update use-template.md
Update the menu sequence
2025-08-22 12:44:57 +05:30
pravinandGitHub f97f2c1253 Create manage-templates.md
A new page has been added: Manage Templates.
2025-08-22 12:42:30 +05:30
prafull-opensignlabsandGitHub b76add3b2b Merge pull request #1891 from pravinOpenSign/patch-31
Update APIToken.md
2025-08-06 13:27:03 +05:30
prafull-opensignlabsandGitHub 07b14a0a47 Merge pull request #1890 from pravinOpenSign/patch-33
Update Preferences.md
2025-08-06 13:25:30 +05:30
pravinandGitHub aa7326a941 Update Preferences.md
Update the page content on the preferences page
2025-08-06 13:24:23 +05:30
pravinandGitHub 336d3424fe Update APIToken.md
update the page content and images on the api token page
2025-08-06 12:24:32 +05:30
prafull-opensignlabsandGitHub 73ae7b1c06 Merge pull request #1879 from pravinOpenSign/patch-28
Update quick-send.md
2025-08-01 21:04:24 +05:30
pravinandGitHub ccfd7450d5 Update quick-send.md
update the bullet list
2025-08-01 18:36:23 +05:30
prafull-opensignlabsandGitHub e23c04cf06 Merge pull request #1878 from pravinOpenSign/patch-21
Patch 21
2025-08-01 18:25:17 +05:30
pravinandGitHub 7bf661d584 Update quick-send.md
update the page content and images for bulk send.
2025-08-01 18:11:45 +05:30
pravinandGitHub 98c18357cd Update contactbook.md
updated the image and details on the contact page.
2025-08-01 17:24:37 +05:30
pravinandGitHub 8b7079bd8b Update in-progress.md
Update the image on the in progress document page
2025-08-01 16:47:16 +05:30
prafull-opensignlabsandGitHub 9f0963d8a3 Merge pull request #1876 from pravinOpenSign/patch-29
Update need-your-sign.md
2025-08-01 15:45:48 +05:30
prafull-opensignlabsandGitHub ebc79ddec7 Merge pull request #1874 from pravinOpenSign/patch-30
Update in-progress.md
2025-08-01 15:45:04 +05:30
pravinandGitHub 24c6e9f5c0 Update declined.md
Update the content and images on the declined documents report.
2025-08-01 14:10:54 +05:30
pravinandGitHub 475b2e09af Update expired.md
update the text and images on the expired documents report.
2025-08-01 14:06:10 +05:30
pravinandGitHub 05a922446a Update drafts.md
removed the extra text
2025-08-01 14:00:42 +05:30
pravinandGitHub 81a426b087 Update drafts.md
update the image and content on the draft document report.
2025-08-01 13:59:23 +05:30
pravinandGitHub 743b751b5a Update completed.md
update the images and the text on the complete report
2025-08-01 13:56:56 +05:30
pravinandGitHub 3d6e83b4af Update in-progress.md
update the content and images on the page.
2025-08-01 13:36:01 +05:30
pravinandGitHub 4f3e8ab95a Update need-your-sign.md
update the images
2025-08-01 12:58:39 +05:30
prafull-opensignlabsandGitHub cc00b35ac8 Merge pull request #1873 from pravinOpenSign/patch-28
Patch 28
2025-07-31 19:38:16 +05:30
pravinandGitHub 1c86caac1f Update rename-document.md
update the images on rename the folder opensign draive.
2025-07-31 19:34:36 +05:30
pravinandGitHub 20dd3c90d6 Update organize-documents.md
update the images on the OpenSign drive page
2025-07-31 19:31:59 +05:30
pravinandGitHub 8b87a90e78 Update create-document.md
Update the page content and images in OpenSign drive page.
2025-07-31 19:21:42 +05:30
pravinandGitHub bd7937deb2 Update request-signatures.md
update the bullate points
2025-07-31 18:39:16 +05:30
pravinandGitHub 7b0dc4cf9a Update edit-document-before-sending.md
update the content for edit document before send.
2025-07-31 18:34:35 +05:30
prafull-opensignlabsandGitHub 81c47190bf Merge pull request #1872 from pravinOpenSign/patch-20
Patch 20
2025-07-31 17:39:53 +05:30
pravinandGitHub cecf22bb85 Update create-template.md
update the bullet points
2025-07-31 17:19:23 +05:30
pravinandGitHub 17eacfe86b Update request-signatures.md
update the bullet points
2025-07-31 17:16:26 +05:30
pravinandGitHub 8f5c9d1b40 Update request-signatures.md
Add the description about new functionality and updated the images.
2025-07-31 17:13:03 +05:30
pravinandGitHub f34e277657 Update create-template.md
corrected the spelling mistakes.
2025-07-31 16:34:19 +05:30
pravinandGitHub 7cc7e36f64 Update request-signatures.md
Add the description about the widgets.
2025-07-31 16:29:32 +05:30
pravinandGitHub b340463ea7 Update create-template.md
Update the list and sub list bullet points.
2025-07-31 16:16:46 +05:30
prafull-opensignlabsandGitHub 9364bb7859 Merge pull request #1870 from pravinOpenSign/patch-20
Update create-template.md
2025-07-31 16:06:02 +05:30
pravinandGitHub 9f676c12e7 Update create-template.md
Fixed spelling errors and added missing descriptions for the widgets.
2025-07-31 16:03:49 +05:30
prafull-opensignlabsandGitHub 0077b71388 Merge pull request #1869 from pravinOpenSign/patch-19
Patch 19
2025-07-31 13:14:52 +05:30
pravinandGitHub 50af669bd2 Update create-template.md
update the template help text and images.
2025-07-31 12:35:10 +05:30
pravinandGitHub 45055b567f Update create-template.md
update the create template help document.
2025-07-30 19:37:04 +05:30
pravinandGitHub 4a7fd118bb Update create-template.md
Update the help document for manage template.
2025-07-30 17:41:21 +05:30
pravinandGitHub d27e8a43dc Update sign-yourself.md
update the help text for cells widget.
2025-07-30 17:01:23 +05:30
pravinandGitHub 4d3635a5bd Update request-signatures.md
update the help text foe input field
2025-07-30 16:59:22 +05:30
pravinandGitHub aeef6ca567 Update create-template.md
update the cells widget info
2025-07-30 16:49:07 +05:30
pravinandGitHub cc68dd109f Update create-template.md
Updated the images and content on the create template page.
2025-07-30 16:44:22 +05:30
pravinandGitHub 7943f1444f Update request-signatures.md
updated the content and images on the request signature help doc.
2025-07-30 14:10:35 +05:30
pravinandGitHub 876e04d815 Update sign-yourself.md
updated the sign yourself help text and screenshots.
2025-07-30 13:35:22 +05:30
prafull-opensignlabsandGitHub 43dbe71c99 update example of email_subject and email_body in docs 2025-07-08 15:44:40 +05:30
prafull-opensignlabsandGitHub 73b36fafd7 feat: add date format in doc APIs and title in request parameter of create document from template API 2025-07-04 18:34:21 +05:30
AmolandGitHub 095040713e Merge pull request #1797 from OpenSignLabs/feature/correct-spelling-mistakes-in-documentation
Fix docs typos
2025-06-16 03:09:19 +05:30
Amol ac884185b4 fix: correct spelling in docs 2025-06-16 02:34:45 +05:30
prafull-opensignlabsandGitHub eede170d16 update response of get document, get document list from status, get template and get templatelist 2025-06-06 10:57:05 +05:30
prafull-opensignlabsandGitHub c4d6d8e442 update GET APIs of document and template response 2025-06-04 21:21:27 +05:30
prafull-opensignlabsandGitHub 14a2f6f78a Merge pull request #1783 from pravinOpenSign/patch-17
Update SecurityManagement.md
2025-06-03 10:49:02 +05:30
pravinandGitHub dd28cefac8 Update SecurityManagement.md
added the react player to play the video
2025-06-02 19:28:36 +05:30
prafull-opensignlabsandGitHub 00179f5415 Merge pull request #1780 from pravinOpenSign/patch-17
Patch 17
2025-06-02 19:06:13 +05:30
pravinandGitHub 7b465687ef Update SecurityManagement.md
updated the navigation labels
2025-06-02 19:04:59 +05:30
pravinandGitHub e67fdfb134 Update Custom-email-templates.md
update the sidebar titles
2025-06-02 19:04:09 +05:30
prafull-opensignlabsandGitHub ceee0e1089 Merge pull request #1779 from pravinOpenSign/patch-17
Patch 17
2025-06-02 17:20:56 +05:30
pravinandGitHub 1d184a69b0 Update SecurityManagement.md
sidebar removed
2025-06-02 17:19:21 +05:30
pravinandGitHub 8e09d7235b Update Custom-email-templates.md
sidebar removed
2025-06-02 17:19:00 +05:30
prafull-opensignlabsandGitHub a39c01ef12 Merge pull request #1775 from pravinOpenSign/patch-17
Patch 17
2025-06-02 16:46:04 +05:30
pravinandGitHub d96504bb30 Update Preferences.md
updated the Preferences page content.
2025-06-02 16:40:31 +05:30
pravinandGitHub 0b8c4eac6e Update Preferences.md
updated the preferences page content.
2025-06-02 14:45:40 +05:30
pravinandGitHub 98f7de5e0c Update Custom-email-templates.md
Updated the custom email template page content
2025-06-02 14:20:47 +05:30
pravinandGitHub 998a50412a Update Custom-email-templates.md
Updated the custom email template text
2025-06-02 14:15:07 +05:30
pravinandGitHub e16c5e08a8 Update SecurityManagement.md
Updated the security management page.
2025-06-02 13:55:24 +05:30
pravinandGitHub 72a61bc621 Create SecurityManagement.md
Added the security management tab
2025-06-02 13:40:11 +05:30
pravinandGitHub f095629134 Create Custom-email-templates.md
added the page for custom email template
2025-06-02 12:15:08 +05:30
pravinandGitHub a0c1e08091 Update Preferences.md
updated the preferences page.
2025-06-02 11:52:01 +05:30
prafull-opensignlabsandGitHub 3583d7a549 add regular expression's help url 2025-06-02 11:33:36 +05:30
prafull-opensignlabsandGitHub 69e679982a update introduction, add redirect_url and auto_reminder parameter in document and template APIs 2025-05-27 18:11:03 +05:30
AmolandGitHub 3303fcb895 Update request-signatures.md 2025-05-22 15:02:42 +05:30
pravinandGitHub 6b5815f52a Update disabledocumentid.md
updated the menu option preference for disabled docuemnt id tab
2025-05-20 13:08:36 +05:30
pravinandGitHub a01e361d14 Update Preferences.md
Updated the preferences page content
2025-05-20 13:07:41 +05:30
pravinandGitHub c059e95fa9 Update Preferences.md
updated Preferences page contents.
2025-05-20 11:26:49 +05:30
pravinandGitHub 70abddec3a Create Preferences.md
Preferences page added
2025-05-19 19:17:25 +05:30
RaktimaandGitHub ca8eaa492c Merge pull request #1747 from pravinOpenSign/patch-32
Update manage-users.md
2025-05-19 13:15:35 +05:30
pravinandGitHub 94806c895f Update manage-users.md
Fixed the br tag closing issue on manage user form.
2025-05-19 13:05:00 +05:30
RaktimaandGitHub 8b08995e7d Merge pull request #1743 from pravinOpenSign/patch-31
Patch 31
2025-05-19 12:00:58 +05:30
pravinandGitHub de9c58e5fd Update manage-users.md
added fequently ask questions on the manage users page.
2025-05-19 10:48:42 +05:30
pravinandGitHub 6d914829e9 Update Teams.md
updated image alter tags
2025-05-16 19:32:45 +05:30
pravinandGitHub 0193e4946f Update manage-users.md
updated the image alter tags
2025-05-16 19:32:00 +05:30
pravinandGitHub 14c6b14c27 Update manage-users.md
updated the manage user page content.
2025-05-16 19:30:40 +05:30
pravinandGitHub 794365f6dc Create Teams.md
New help document added to manage teams page
2025-05-16 17:56:57 +05:30
pravinandGitHub d0a0a28dec Update manage-users.md
changed teh navigation sequence
2025-05-16 16:41:16 +05:30
pravinandGitHub 1385f12543 Update disabledocumentid.md
change the navigation sequence
2025-05-16 16:40:43 +05:30
pravinandGitHub 0d81ffe677 Update Webhook.md
Updated the webhook help text
2025-05-16 16:39:48 +05:30
pravinandGitHub 9d7376aedb Update APIToken.md
Updated the api token help text
2025-05-16 16:09:54 +05:30
pravinandGitHub 5eb7d8ce13 Create Webhook.md
Add the help docuemnt. how to setup webhook in OpenSign.
2025-05-16 15:34:37 +05:30
prafull-opensignlabsandGitHub 4609bba4c1 feat: add generate api token url 2025-05-16 12:49:10 +05:30
prafull-opensignlabsandGitHub de0de1973e Update APIToken.md 2025-05-16 12:33:28 +05:30
prafull-opensignlabsandGitHub 16c48e9d23 feat: update position of api token in settings menu 2025-05-16 12:32:32 +05:30
prafull-opensignlabsandGitHub 344ccadfb1 Merge pull request #1739 from pravinOpenSign/patch-30
Create APIToken.md
2025-05-16 12:27:50 +05:30
pravinandGitHub eb1086e5b1 Create APIToken.md
added new file how to genarate api token
2025-05-16 12:26:42 +05:30
AmolandGitHub ae568a89d5 Update _category_.json 2025-05-12 01:16:11 +05:30
AmolandGitHub daa348c2e3 Update expired.md 2025-05-12 01:15:11 +05:30
AmolandGitHub 78532731da Update drafts.md 2025-05-12 01:14:46 +05:30
AmolandGitHub 2e26d5687e Update declined.md 2025-05-12 01:13:54 +05:30
AmolandGitHub 4b8b7ed8d9 Update contactbook.md 2025-05-12 01:12:57 +05:30
AmolandGitHub ac986af20f Update completed.md 2025-05-12 01:12:30 +05:30
AmolandGitHub a96333bba5 Update in-progress.md 2025-05-12 01:12:05 +05:30
AmolandGitHub 78ca0d1fdb Update need-your-sign.md 2025-05-12 01:11:36 +05:30
AmolandGitHub 415760ba52 Update navigate-between-pages.md 2025-05-12 01:03:01 +05:30
AmolandGitHub 67bd246ef9 Create edit-document-before-sending.md 2025-05-12 00:58:43 +05:30
AmolandGitHub 4963a7ad7f Update navigate-between-pages.md 2025-05-12 00:47:03 +05:30
AmolandGitHub 94b02105f3 Update sign-yourself.md 2025-05-08 14:32:31 +05:30
AmolandGitHub 6aaeeeeaf5 Create azure-vm-setup.md 2025-04-21 04:40:00 +05:30
prafull-opensignlabsandGitHub 8039f5a744 add file parameter in update template, create document from template APIs 2025-04-17 12:25:55 +05:30
prafull-opensignlabsandGitHub a5ebd67967 Update template_id in response of getdocument and getdocumentlist 2025-04-15 19:57:33 +05:30
prafull-opensignlabsandGitHub 266a4c5654 update {{note}} variable in documentation 2025-04-05 11:44:14 +05:30
prafull-opensignlabsandGitHub 406a29054e Update opensign.yaml 2025-03-28 13:28:53 +05:30
prafull-opensignlabsandGitHub 57f8ba8a72 Merge pull request #1665 from pravinOpenSign/patch-28
Update upgrade-deployment.md
2025-03-28 12:31:56 +05:30
pravinandGitHub f1a70eac5a Update upgrade-deployment.md
removed the description for docker compose up command.
2025-03-28 12:31:17 +05:30
AmolandGitHub f228ae5ba8 Update opensign.yaml 2025-03-26 20:16:20 +05:30
prafull-opensignlabsandGitHub 2c1239dcf0 Update upgrade-deployment.md 2025-03-24 14:11:29 +05:30
prafull-opensignlabsandGitHub 6ca9cbab16 changes in upgrade docker command to support one click deployment 2025-03-24 14:07:20 +05:30
prafull-opensignlabsandGitHub 9922f22ef7 Update opensign.yaml 2025-03-20 16:51:34 +05:30
prafull-opensignlabsandGitHub 84cc1d65fe Update date format 2025-03-20 16:49:51 +05:30
prafull-opensignlabsandGitHub 7a5db1ca40 deprecated env updated 2025-03-20 11:21:07 +05:30
prafull-opensignlabsandGitHub de79c9a4b3 Update deprecated env 2025-03-20 11:20:02 +05:30
prafull-opensignlabsandGitHub 7a7e1b506a Update opensign.yaml 2025-03-19 18:49:07 +05:30
prafull-opensignlabsandGitHub fa2e66914d Update opensign.yaml 2025-03-19 10:37:02 +05:30
prafull-opensignlabsandGitHub b0c65fe5f1 Merge pull request #1642 from OpenSignLabs/revoke-api
docs: add revoke api and update desc of create document from template
2025-03-18 22:11:15 +05:30
prafull-opensignlabsandGitHub b0440876cd docs: add revoke api and update desc of create document from template 2025-03-17 17:06:19 +05:30
AmolandGitHub 6359067839 Update publicTemplate.md 2025-03-07 03:18:13 +05:30
prafull-opensignlabsandGitHub 71f36ff383 Update opensign.yaml 2025-03-06 01:15:56 +05:30
prafull-opensignlabsandGitHub beab4bdfc9 Update opensign.yaml 2025-03-05 18:06:54 +05:30
prafull-opensignlabsandGitHub c9e6750724 Update opensign.yaml 2025-03-05 17:42:46 +05:30
prafull-opensignlabsandGitHub 06c7355efd Update env-variables.md 2025-02-27 17:01:32 +05:30
prafull-opensignlabsandGitHub 73f7d1b32f Update run-locally.md 2025-02-27 17:00:12 +05:30
prafull-opensignlabsandGitHub fecc87d8c3 Update run-locally.md 2025-02-27 16:55:24 +05:30
prafull-opensignlabsandGitHub 57c89bf601 commented unused envs 2025-02-27 16:53:25 +05:30
prafull-opensignlabsandGitHub b10b2617b8 appName commented 2025-02-27 15:59:05 +05:30
prafull-opensignlabsandGitHub eaed35cefd reverted description parameter in API 2025-02-18 19:02:00 +05:30
prafull-opensignlabsandGitHub 44e92344fc Update draft template API description 2025-02-18 13:36:51 +05:30
prafull-opensignlabsandGitHub 9aebbde627 Update selfsign API 2025-02-18 13:11:31 +05:30
prafull-opensignlabsandGitHub 8ece6ef046 Update draft doc API 2025-02-18 12:52:35 +05:30
prafull-opensignlabsandGitHub 7d46e26365 update webhook API 2025-02-18 12:26:35 +05:30
prafull-opensignlabsandGitHub e2646bd49f Update draft and create template api 2025-02-18 11:51:00 +05:30
prafull-opensignlabsandGitHub 6158ac7bc4 Update create doc api 2025-02-18 11:31:43 +05:30
prafull-opensignlabsandGitHub 596340bea9 Update draft doc 2025-02-18 11:23:45 +05:30
nxglabs 73d1cdee00 version updates 2025-02-17 03:53:31 +05:30
prafull-opensignlabsandGitHub f88f2c681b Update webhook doc 2025-02-16 14:25:05 +05:30
prafull-opensignlabsandGitHub 25ecf7221d update create document and create document from template response 2025-02-16 12:41:58 +05:30
prafull-opensignlabsandGitHub bb18feaacf docs: make phone field optional 2025-02-10 16:41:47 +05:30
prafull-opensignlabsandGitHub 19324ad0d8 docs: update sender_name and sender_email in selfsign API 2025-02-10 11:56:57 +05:30
prafull-opensignlabsandGitHub 8a0f5c460f Update opensign.yaml 2025-02-07 22:40:56 +05:30
AmolandGitHub 625689b203 Update s3.md 2025-02-06 03:54:31 +05:30
prafull-opensignlabsandGitHub d919e8514b remove description parameter from selfsign and draft document API 2025-02-05 18:35:12 +05:30
prafull-opensignlabsandGitHub 18f183f48b removed swagger url 2025-02-05 12:23:36 +05:30
prafull-opensignlabsandGitHub 69135afe6b docs: removed description parameter from create and update document API 2025-02-05 12:23:07 +05:30
AmolandGitHub 474a20aa41 Update opensign.yaml 2025-01-31 18:23:37 +05:30
AmolandGitHub 19478536b3 Update opensign.yaml 2025-01-31 15:17:52 +05:30
prafull-opensignlabsandGitHub dc29d7a23b Update opensign.yaml 2025-01-20 13:29:50 +05:30
prafull-opensignlabsandGitHub f5137f54b0 Merge pull request #1538 from OpenSignLabs/draft_doc_api
Draft doc api
2025-01-18 00:34:55 +05:30
prafull-opensignlabsandGitHub 42f2122070 Update draft document api documentation 2025-01-18 00:33:34 +05:30
prafull-opensignlabsandGitHub e4ca29c2c3 Update opensign.yaml 2025-01-18 00:30:03 +05:30
AmolandGitHub b8a039bc20 Update env-variables.md 2025-01-17 01:42:03 +05:30
prafull-opensignlabsandGitHub 331b2d5a40 Merge pull request #1527 from pravinOpenSign/patch-27
Update upgrade-deployment.md
2025-01-02 20:46:25 +05:30
pravinandGitHub 46e390d3c5 Update upgrade-deployment.md
updated the self host upgrade document
2025-01-02 20:34:37 +05:30
prafull-opensignlabsandGitHub 0b8fb87a29 add certificate parameter in completed webhook event 2024-12-23 17:54:39 +05:30
prafull-opensignlabsandGitHub e0249211d2 update webhook event for draft template and readonly for dropdown, textbox widget 2024-12-19 19:11:55 +05:30
prafull-opensignlabsandGitHub a244c1ba3d docs: update info of drodown, radio button, checkbox 2024-12-13 20:49:53 +05:30
prafull-opensignlabsandGitHub 0a79807cb4 feat: add api documentation for selfsign 2024-12-11 19:10:11 +05:30
prafull-opensignlabsandGitHub b70c473002 docs: change parameter from size to fontsize in api docs 2024-11-12 15:28:15 +05:30
prafull-opensignlabsandGitHub 937390fca0 docs: update font size in api documentation 2024-11-12 12:25:12 +05:30
prafull-opensignlabsandGitHub 1140318719 Update info 2024-11-11 17:09:24 +05:30
prafull-opensignlabsandGitHub 655371c6d9 added draft template and color for widget cotent 2024-11-11 16:23:11 +05:30
AmolandGitHub f07fc78eff Update enforce-email-verification.md 2024-11-07 00:53:23 +05:30
AmolandGitHub 60034a4ad8 Create enforce-email-verification.md 2024-11-07 00:49:49 +05:30
RaktimaandGitHub ead7bf8841 Merge pull request #1421 from pravinOpenSign/patch-26
Update run-locally.md
2024-11-06 17:25:31 +05:30
pravinandGitHub 15d2798aab Update run-locally.md 2024-11-06 17:22:44 +05:30
prafull-opensignlabsandGitHub 58a0af40e6 Merge pull request #1397 from pravinOpenSign/patch-22
Update embed-template.md
2024-10-29 16:21:49 +05:30
prafull-opensignlabsandGitHub 1b79b45b1f Update embed-template.md 2024-10-29 16:21:29 +05:30
pravinandGitHub f831ff3b7a Update embed-template.md
Updated the react player settings, default playing set to False,  playing ={false}
2024-10-29 16:17:16 +05:30
prafull-opensignlabsandGitHub f1b6859e9c remove import statement from publictemplate doc 2024-10-29 14:11:16 +05:30
ZathielandGitHub 2148f5196c Merge pull request #1395 from pravinOpenSign/patch-15 2024-10-29 09:15:28 +01:00
pravinandGitHub 5495e569df Update publicTemplate.md
Added dependency import ReactPlayer from 'react-player';
2024-10-29 13:36:51 +05:30
pravinandGitHub e121df48fe Update embed-template.md
added the dependency import ReactPlayer from 'react-player'
2024-10-29 13:33:05 +05:30
prafull-opensignlabsandGitHub 39c05b1eb3 Merge pull request #1394 from pravinOpenSign/patch-12
Patch 12
2024-10-29 12:56:29 +05:30
pravinandGitHub 78892a497d Update embed-template.md
Updated the content for embeding the template
2024-10-29 12:19:22 +05:30
pravinandGitHub ccb08949ff Update sign-yourself.md
Added the details about My signature
2024-10-28 17:37:28 +05:30
pravinandGitHub 74b091b1eb Create embed-template.md
Added a new help document for embedding the template.
2024-10-24 19:13:22 +05:30
AmolandGitHub 264214babd Update opensign.yaml 2024-10-24 17:30:37 +05:30
pravinandGitHub 38930133f7 Update publicTemplate.md
Updated the steps
2024-10-24 16:29:58 +05:30
pravinandGitHub 758cfc8e8d Update publicTemplate.md 2024-10-24 16:15:37 +05:30
pravinandGitHub 2a9ae74c33 Update publicTemplate.md
Updated the page content
2024-10-24 16:14:48 +05:30
pravinandGitHub 2f73ba974c Update publicTemplate.md
updated the content
2024-10-24 16:08:25 +05:30
pravinandGitHub 7c0b744c40 Update publicTemplate.md
Update the header font
2024-10-24 15:07:08 +05:30
pravinandGitHub 010d2dba60 Update publicTemplate.md
Updated the content
2024-10-24 15:04:58 +05:30
pravinandGitHub c0b45e97ea Create publicTemplate.md
help document for public template
2024-10-23 17:04:02 +05:30
RaktimaandGitHub 7316fcd18a Merge pull request #1370 from pravinOpenSign/patch-25
Patch 25
2024-10-22 10:21:25 +05:30
pravinandGitHub 08a2d8a1a4 Update create-template.md
Correct the spelling
2024-10-21 18:24:54 +05:30
pravinandGitHub dbd53ba0d2 Update create-template.md
Updated the document content and Images.
2024-10-21 17:54:53 +05:30
pravinandGitHub 8b0c73c58e Update need-your-sign.md 2024-10-21 14:28:45 +05:30
pravinandGitHub a292687a8c Update expired.md 2024-10-21 14:28:16 +05:30
pravinandGitHub 8659c1f4dc Update contactbook.md
update the label actions button to Action buttons
2024-10-21 14:27:45 +05:30
pravinandGitHub 8ed73ba07b Create contactbook.md
Added the new contact book report
2024-10-21 14:26:20 +05:30
pravinandGitHub a41a31176d Update expired.md
Update the report name from Expired Documents Report to Expired documents report
2024-10-21 13:40:13 +05:30
pravinandGitHub d6740efaf9 Update declined.md
Updated the report name "Declined Documents Report"  to "Declined documents report"
2024-10-21 13:37:23 +05:30
pravinandGitHub aac4ea8e2a Update drafts.md
update the report name from "Draft Documents Report" to Draft documents report"
2024-10-21 13:34:51 +05:30
pravinandGitHub 3fe26cfb07 Update need-your-sign.md
Updated the header from # Need your sign report in OpenSign  to # Need your sign report to maintain the consistency in all report.
2024-10-21 13:30:23 +05:30
pravinandGitHub c837df6540 Update completed.md
Updated the word from 
"Completed Documents Report" to ""Completed documents report"
2024-10-21 13:28:43 +05:30
pravinandGitHub 03b6b567fc Update in-progress.md
Updated the sentence "In progress document Interface  " to  "In progress document report"
2024-10-21 13:22:58 +05:30
pravinandGitHub 09fbd442be Update need-your-sign.md
Updated the sentence "Understanding the Need your sign Interface  " to  "Understanding the Need your sign report"
2024-10-21 13:22:15 +05:30
pravinandGitHub 5f32ca0140 Update in-progress.md
Updated the Document and The latest Images in in-progress report.
2024-10-21 13:12:12 +05:30
pravinandGitHub 46e5aa2815 Update run-locally.md
Updated the sentence 
"Create a Docker Hub account if you don't already have one."
2024-10-21 12:31:15 +05:30
pravinandGitHub db635f80f8 Update need-your-sign.md
Update the Word "Need Your Signature" to "Need your signature.
2024-10-21 12:21:19 +05:30
prafull-opensignlabsandGitHub c3a101bc18 Merge pull request #1352 from pravinOpenSign/patch-23
Update upgrade-deployment.md
2024-10-18 19:47:26 +05:30
pravinandGitHub 1af4bd430f Update upgrade-deployment.md
Update the command to pull upgrade the opensign deployment
2024-10-18 17:18:13 +05:30
AmolandGitHub 0459c404f5 Update completed.md 2024-10-18 16:16:51 +05:30
prafull-opensignlabsandGitHub 492f242e4d update local command 2024-10-16 13:21:44 +05:30
prafull-opensignlabsandGitHub e06d6d5427 update command of local 2024-10-16 13:16:44 +05:30
prafull-opensignlabsandGitHub 747c7c549c Merge pull request #1344 from OpenSignLabs/update-doc
feat: update doc add enableTour variable
2024-10-15 11:51:03 +05:30
RaktimaNXG e91d615e68 feat: update doc add enableTour variable 2024-10-15 11:14:10 +05:30
prafull-opensignlabsandGitHub 2d527d59b0 Merge pull request #1310 from OpenSignLabs/revert-1303-update-doc
Revert "feat: update doc add enableTour variable"
2024-10-09 10:08:00 +05:30
prafull-opensignlabsandGitHub 340947a36d Revert "feat: update doc add enableTour variable" 2024-10-09 10:07:44 +05:30
prafull-opensignlabsandGitHub 91d098ef65 Merge pull request #1303 from OpenSignLabs/update-doc
feat: update doc add enableTour variable
2024-10-09 10:04:27 +05:30
RaktimaNXG d9f8ea8aca refactor code 2024-10-07 10:23:28 +05:30
AndrewandGitHub 33bcb55124 Update s3.md 2024-10-03 12:59:28 +05:30
AndrewandGitHub 5a93207dc2 Update s3.md 2024-10-03 12:55:03 +05:30
AndrewandGitHub fa7c36dbe2 fix syntax issue 2024-10-03 12:26:13 +05:30
prafull-opensignlabsandGitHub faba8b8201 Merge pull request #1286 from OpenSignLabs/docs_s3
update s3 doc
2024-10-03 10:52:42 +05:30
prafull-opensignlabsandGitHub 3422bc6f9e Update s3.md 2024-10-03 10:51:19 +05:30
prafull-opensignlabsandGitHub a195b758e6 Update s3.md 2024-10-03 10:49:26 +05:30
ZathielandGitHub 083cd60f2d Merge pull request #1284 from pravinOpenSign/patch-15 2024-10-01 10:19:21 +02:00
pravinandGitHub f4b7449262 Update run-locally.md 2024-10-01 13:19:00 +05:30
prafull-opensignlabsandGitHub fb68931538 Merge pull request #1281 from OpenSignLabs/docs_s3
Docs: Update s3 documentation
2024-09-30 21:28:55 +05:30
prafull-opensignlabsandGitHub be07fcec24 Update s3 documentation 2024-09-30 21:24:58 +05:30
RaktimaNXG f6245cfada feat: update doc add enableTourvariable 2024-09-30 16:47:08 +05:30
prafull-opensignlabsandGitHub 77cf6de722 Update run-locally.md 2024-09-26 10:11:25 +05:30
prafull-opensignlabsandGitHub 2b17c26722 Merge pull request #1177 from EDIflyer/feature/ediflyer-docs
docs: update install guidance
2024-09-25 20:07:19 +05:30
RaktimaNXG 9862921297 Merge branch 'feat-docs' of https://github.com/OpenSignLabs/OpenSign into feat-docs 2024-09-19 14:56:55 +05:30
RaktimaNXG 685b2ceabd update opensign-demo video 2024-09-19 14:55:20 +05:30
prafull-opensignlabsandGitHub 786b674db3 Update opensign.yaml 2024-09-18 19:11:20 +05:30
prafull-opensignlabsandGitHub 0b9074611a Update opensign.yaml 2024-09-18 14:56:23 +05:30
prafull-opensignlabsandGitHub 6ebfa038b3 Update opensign.yaml 2024-09-18 14:53:47 +05:30
prafull-opensignlabsandGitHub 4c809bb8d3 Merge pull request #1214 from pravinOpenSign/patch-62
Create upgrade-deployment.md
2024-09-18 14:33:56 +05:30
pravinandGitHub 2dcb7eb7a2 Create upgrade-deployment.md
Added deployment upgrade help document.
2024-09-18 11:56:53 +05:30
prafull-opensignlabsandGitHub 743e89038c webhook response schema updated 2024-09-09 20:19:36 +05:30
AndrewandGitHub f9f25dc0c3 Update run-locally.md 2024-09-09 02:45:33 +05:30
EDIflyerandEDIflyer ca75698328 better differentiate between custom domain and local 2024-09-08 16:26:01 +01:00
EDIflyerandEDIflyer f117aeb7c4 Add comment re CPU architecture
As per Discord discussions
2024-09-08 16:25:54 +01:00
EDIflyerandEDIflyer e36557e844 Update run-locally.md
Remove reference to NodeJS and Git as these aren't required for Docker as are included within it.
2024-09-08 16:25:39 +01:00
prafull-opensignlabsandGitHub e73b8ed5a5 Update opensign.yaml 2024-09-06 21:01:18 +05:30
AmolandGitHub 0aaeb28eb7 change branches in command 2024-09-03 22:32:08 +05:30
prafull-opensignlabsandGitHub 305a5f6fda added staging url 2024-09-02 11:06:33 +05:30
prafull-opensignlabsandGitHub 045961be02 update testing url 2024-09-02 10:28:14 +05:30
RaktimaNXG c612dd3e70 feat: replace opensign video to gif 2024-08-30 19:02:29 +05:30
RaktimaNXG ea4527e2dd feat: add opensign-react npm demo video 2024-08-30 18:49:55 +05:30
prafull-opensignlabsandGitHub aada33f23f Update run-locally.md 2024-08-30 11:02:17 +05:30
prafull-opensignlabsandGitHub 1f44582658 Update run-locally.md 2024-08-30 10:52:14 +05:30
prafull-opensignlabsandGitHub 198a58e524 Update run-locally.md 2024-08-29 20:01:58 +05:30
prafull-opensignlabsandGitHub 0d78aef414 Update run-locally.md 2024-08-29 20:01:31 +05:30
prafull-opensignlabsandGitHub 8f99cb0514 Update run-locally.md 2024-08-29 19:59:30 +05:30
prafull-opensignlabsandGitHub 9a734d9b3c Merge pull request #1132 from pravinOpenSign/patch-57
Update need-your-sign.md
2024-08-29 19:15:54 +05:30
prafull-opensignlabsandGitHub 7a2c93a3e9 Merge pull request #1133 from pravinOpenSign/patch-58
Update drafts.md
2024-08-29 19:13:23 +05:30
pravinandGitHub a5e1768f7e Update drafts.md
update the title Complete documents report to "draft documents report"
2024-08-29 18:20:49 +05:30
pravinandGitHub 30e27062bd Update need-your-sign.md
Corrected the spelling mistake Spelling mistake Action button  to Actions buttons
2024-08-29 18:19:05 +05:30
prafull-opensignlabsandGitHub 97d28a1b48 Merge pull request #1123 from pravinOpenSign/patch-52
Update declined.md
2024-08-29 10:43:36 +05:30
prafull-opensignlabsandGitHub aade153f1c Merge pull request #1125 from pravinOpenSign/patch-54
Update completed.md
2024-08-29 10:42:38 +05:30
prafull-opensignlabsandGitHub df6f5d4b33 Merge pull request #1126 from pravinOpenSign/patch-55
Update in-progress.md
2024-08-29 10:41:51 +05:30
prafull-opensignlabsandGitHub ab14628769 Merge pull request #1127 from pravinOpenSign/patch-56
Update drafts.md
2024-08-29 10:41:33 +05:30
RaktimaNXG 69effb5af0 feat: replace demo-video 2024-08-28 18:41:17 +05:30
RaktimaNXG 6e9078d64b feat: upload public demo video 2024-08-28 18:22:17 +05:30
pravinandGitHub 9c73b7818f Update drafts.md
title updated
2024-08-28 18:04:23 +05:30
pravinandGitHub c5246d7a83 Update in-progress.md
content updated for shared document popup
2024-08-28 18:03:04 +05:30
pravinandGitHub 35511e08ac Update completed.md
Title updated
2024-08-28 17:57:59 +05:30
pravinandGitHub 39dc8b226a Update declined.md
page title updated
2024-08-28 17:53:52 +05:30
prafull-opensignlabsandGitHub 7cb531b183 Merge pull request #1118 from pravinOpenSign/patch-48
Update drafts.md
2024-08-28 17:42:44 +05:30
prafull-opensignlabsandGitHub 58422b17e5 Merge pull request #1119 from pravinOpenSign/patch-49
Update in-progress.md
2024-08-28 17:42:14 +05:30
prafull-opensignlabsandGitHub 0b2191f97c Merge pull request #1121 from pravinOpenSign/patch-50
Update expired.md
2024-08-28 17:41:40 +05:30
prafull-opensignlabsandGitHub a3e672cde0 Merge pull request #1122 from pravinOpenSign/patch-51
Update declined.md
2024-08-28 17:41:16 +05:30
pravinandGitHub 5a0b10bdff Update declined.md
Declined Document report
2024-08-28 17:18:37 +05:30
pravinandGitHub dfbf4d8a8f Update expired.md
Expired documents report help document completed
2024-08-28 17:15:51 +05:30
pravinandGitHub 4727d622db Update in-progress.md
Updated the help document for in-progress report.
2024-08-28 17:02:43 +05:30
pravinandGitHub e18e62754f Update drafts.md
Draft document report help document updated
2024-08-28 15:28:15 +05:30
prafull-opensignlabsandGitHub bec7af5142 Merge pull request #1117 from pravinOpenSign/patch-47
Update completed.md
2024-08-28 12:58:03 +05:30
pravinandGitHub 0db937eeeb Update completed.md
content updated
2024-08-28 12:57:20 +05:30
prafull-opensignlabsandGitHub c8d26a5ea9 Merge pull request #1114 from pravinOpenSign/patch-46
Update create-document.md
2024-08-28 12:45:54 +05:30
pravinandGitHub 1659ebd2ac Update create-document.md
fixed the end tag <img> issue
2024-08-27 19:34:35 +05:30
prafull-opensignlabsandGitHub 7877d13776 Merge pull request #1113 from pravinOpenSign/patch-45
Update create-document.md
2024-08-27 19:16:39 +05:30
pravinandGitHub 2f7cacb933 Update create-document.md
Added the Missing <img> tag
2024-08-27 16:47:17 +05:30
prafull-opensignlabsandGitHub 16fcbf8a54 Update create-document.md 2024-08-27 16:37:29 +05:30
prafull-opensignlabsandGitHub 719a23c95b Update create-document.md 2024-08-27 16:30:52 +05:30
prafull-opensignlabsandGitHub f4db8af201 Update create-document.md 2024-08-27 16:23:01 +05:30
prafull-opensignlabsandGitHub 3549c63cc0 Merge pull request #1109 from pravinOpenSign/patch-42
Update declined.md
2024-08-27 16:17:25 +05:30
prafull-opensignlabsandGitHub b9ed7e5293 Merge pull request #1100 from pravinOpenSign/patch-34
Update completed.md
2024-08-27 16:16:30 +05:30
prafull-opensignlabsandGitHub e34e2c4386 Merge pull request #1110 from pravinOpenSign/patch-43
Update create-template.md
2024-08-27 16:16:09 +05:30
prafull-opensignlabsandGitHub a09b2e186f Merge pull request #1099 from pravinOpenSign/patch-33
Update list-grid-view.md
2024-08-27 16:15:52 +05:30
prafull-opensignlabsandGitHub cc273261c3 Merge pull request #1111 from pravinOpenSign/patch-44
Update need-your-sign.md
2024-08-27 16:15:17 +05:30
prafull-opensignlabsandGitHub 26c5c951b6 Merge pull request #1098 from pravinOpenSign/patch-32
Update create-document.md
2024-08-27 16:14:59 +05:30
prafull-opensignlabsandGitHub e63178b899 Merge pull request #1105 from pravinOpenSign/patch-39
Update my-signature.md
2024-08-27 16:14:36 +05:30
pravinandGitHub eb42f08640 Update need-your-sign.md
updated the signers description paragraph.
2024-08-27 15:30:36 +05:30
pravinandGitHub 2843bfd5c9 Update create-template.md
Updated the quick send paragraph description
2024-08-27 15:25:10 +05:30
pravinandGitHub b2a838dc4e Update declined.md
Update the signers description
2024-08-27 15:02:01 +05:30
pravinandGitHub c682a6d8eb Update my-signature.md
Updated the Images on my signature page
2024-08-27 13:19:16 +05:30
prafull-opensignlabsandGitHub c4a23b4f1b Merge pull request #1104 from pravinOpenSign/patch-38
Update declined.md
2024-08-27 11:21:50 +05:30
pravinandGitHub 12e74533c7 Update declined.md
updated the heading "Understanding the Completed Documents Report"
2024-08-27 11:20:14 +05:30
prafull-opensignlabsandGitHub 24c8f6012f Merge pull request #1102 from pravinOpenSign/patch-36
Update declined.md
2024-08-27 10:24:08 +05:30
prafull-opensignlabsandGitHub b20fb9ea3f Merge pull request #1103 from pravinOpenSign/patch-37
Update need-your-sign.md
2024-08-27 10:22:24 +05:30
prafull-opensignlabsandGitHub 079e0defc4 Merge pull request #1097 from pravinOpenSign/patch-31
Update my-signature.md
2024-08-27 10:08:02 +05:30
prafull-opensignlabsandGitHub 00ad406e1b Merge pull request #1090 from pravinOpenSign/patch-30
Update quick-send.md
2024-08-27 10:03:59 +05:30
pravinandGitHub 5c7a9e1d08 Update need-your-sign.md
Need your signature report help document completed.
2024-08-26 19:02:23 +05:30
pravinandGitHub ea2c981a7f Update declined.md
help document for decline document reports is completed.
2024-08-26 18:45:08 +05:30
pravinandGitHub ee18d1b317 Update completed.md
Help document added for completed document report
2024-08-26 18:18:23 +05:30
pravinandGitHub 7f6771e56d Update list-grid-view.md
Images updated
2024-08-26 17:21:37 +05:30
pravinandGitHub 1308b143c4 Update create-document.md
Help document for creating and managing folders in OpenSign Drive has been completed
2024-08-26 17:07:42 +05:30
pravinandGitHub 7be2d1f06a Update my-signature.md
Update the signature page image
2024-08-26 14:34:51 +05:30
pravinandGitHub 62021e8870 Update quick-send.md
Help documents finalized for signature requests using Quick Send.
2024-08-23 19:16:54 +05:30
prafull-opensignlabsandGitHub ea5f407786 Merge pull request #1083 from pravinOpenSign/patch-29
Update use-template.md
2024-08-22 13:07:26 +05:30
pravinandGitHub 2ecd1823e1 Update use-template.md
completed help document: create the document from template
2024-08-21 19:02:26 +05:30
prafull-opensignlabsandGitHub 1e2eee63df Update create-template.md 2024-08-19 16:03:45 +05:30
prafull-opensignlabsandGitHub 8fda979870 Update create-template.md 2024-08-19 15:58:24 +05:30
prafull-opensignlabsandGitHub c59002c375 Update create-template.md 2024-08-19 15:55:50 +05:30
prafull-opensignlabsandGitHub 1fe05b402b Update create-template.md 2024-08-19 15:51:48 +05:30
prafull-opensignlabsandGitHub c7d5bef886 Merge pull request #1075 from pravinOpenSign/patch-26
Update create-template.md
2024-08-19 15:45:20 +05:30
pravinandGitHub ca2fc2bd5c Update create-template.md
Create template help document updated.
2024-08-19 15:43:27 +05:30
prafull-opensignlabsandGitHub 8bf9fb6299 Delete docs/docs/self-host/docker/run-locally-windows.md 2024-08-19 10:42:11 +05:30
prafull-opensignlabsandGitHub 031f24180a Merge pull request #1061 from pravinOpenSign/patch-25
Update sign-yourself.md
2024-08-14 21:05:21 +05:30
prafull-opensignlabsandGitHub 8663777097 Update run-locally.md 2024-08-14 21:04:16 +05:30
prafull-opensignlabsandGitHub 342876cd0b Update run-locally.md 2024-08-14 21:02:17 +05:30
prafull-opensignlabsandGitHub beaa256e3f Update run-locally.md 2024-08-14 20:59:06 +05:30
pravinandGitHub 27ae27187d Update sign-yourself.md
updated the Side bar position.
2024-08-14 19:43:38 +05:30
prafull-opensignlabsandGitHub e8d692a707 Merge pull request #1059 from pravinOpenSign/patch-24
Update sign-yourself.md
2024-08-14 17:07:15 +05:30
prafull-opensignlabsandGitHub 4f71d1bd03 Merge pull request #1057 from pravinOpenSign/patch-23
Update request-signatures.md
2024-08-14 13:42:43 +05:30
prafull-opensignlabsandGitHub 8d33d4c44e Update run-locally.md 2024-08-13 20:21:56 +05:30
prafull-opensignlabsandGitHub 8be55a30c1 Update run-locally.md 2024-08-13 20:18:26 +05:30
prafull-opensignlabsandGitHub 46a0cfa1eb Update run-locally.md 2024-08-13 20:16:29 +05:30
prafull-opensignlabsandGitHub 6988bca7f4 Update run-locally.md 2024-08-13 20:13:33 +05:30
prafull-opensignlabsandGitHub 1a2c0ecbfc Update run-locally.md 2024-08-13 20:11:10 +05:30
prafull-opensignlabsandGitHub f2281505a4 Update run-locally.md 2024-08-13 20:10:52 +05:30
prafull-opensignlabsandGitHub 3ff790c57b Update run-locally.md 2024-08-13 20:02:04 +05:30
pravinandGitHub d5b0fc4e3b Update sign-yourself.md
Updated the title and removed some text
2024-08-13 18:52:25 +05:30
pravinandGitHub 69129c2d47 Update request-signatures.md
Image Location Changed and updated the support line add the bottom
2024-08-13 18:46:38 +05:30
prafull-opensignlabsandGitHub 1486d66b9b Update run-locally.md 2024-08-13 18:33:20 +05:30
prafull-opensignlabsandGitHub 67323d01f5 Update run-locally.md 2024-08-13 18:18:17 +05:30
prafull-opensignlabsandGitHub d0566e6645 Update run-locally.md 2024-08-13 18:16:57 +05:30
prafull-opensignlabsandGitHub a3e1fa866e Update request-signatures.md 2024-08-13 17:45:58 +05:30
prafull-opensignlabsandGitHub 6d6b70a888 Update run-locally.md 2024-08-13 17:31:33 +05:30
prafull-opensignlabsandGitHub c152d3bf35 Update sign-yourself.md 2024-08-13 17:24:25 +05:30
prafull-opensignlabsandGitHub 56a0211b03 Update request-signatures.md 2024-08-13 17:20:43 +05:30
prafull-opensignlabsandGitHub 87fb943ddb Update request-signatures.md 2024-08-13 17:12:40 +05:30
prafull-opensignlabsandGitHub dacc9bb27e Update request-signatures.md 2024-08-13 17:07:02 +05:30
prafull-opensignlabsandGitHub 43a9fa6820 Update run-locally.md 2024-08-13 16:52:11 +05:30
prafull-opensignlabsandGitHub 523d7a0896 Update request-signatures.md 2024-08-13 16:06:50 +05:30
prafull-opensignlabsandGitHub df53e243a5 Update request-signatures.md 2024-08-13 15:51:03 +05:30
prafull-opensignlabsandGitHub b30d7e3337 Update request-signatures.md 2024-08-13 15:45:36 +05:30
AmolandGitHub 7a8e2bcd02 Update run-locally.md 2024-08-13 15:35:17 +05:30
AmolandGitHub 7aa505f1a3 Update run-locally.md 2024-08-13 15:34:34 +05:30
AmolandGitHub beecd2d26f Merge pull request #1008 from pravinOpenSign/patch-20
Update request-signatures.md
2024-08-02 17:49:43 +05:30
pravinandGitHub 96ecbe97f1 Update request-signatures.md
The issue with the image closing tag has been fixed.
2024-08-02 17:47:22 +05:30
AndrewandGitHub 7161da4a2f Merge pull request #1004 from pravinOpenSign/patch-17 2024-08-02 17:21:48 +05:30
AndrewandGitHub 462fdfe1be Merge pull request #1005 from pravinOpenSign/patch-18 2024-08-02 17:21:41 +05:30
AndrewandGitHub dc25133b10 Merge pull request #1006 from pravinOpenSign/patch-19 2024-08-02 17:21:21 +05:30
pravinandGitHub bb7552a7bf Update sign-yourself.md
Added padding between images and the subsequent line
2024-08-02 16:58:28 +05:30
pravinandGitHub 79b4ca053a Update sign-yourself.md
The images in the "Sign Yourself" help document have been updated.
2024-08-02 16:44:24 +05:30
pravinandGitHub 8d91bd218b Update request-signatures.md
The request signature flow help document has been updated.
2024-08-02 16:36:50 +05:30
AmolandGitHub 8e0c96589a Update manage-users.md 2024-07-28 12:41:29 +05:30
AmolandGitHub 3ac66ea4c5 Update manage-users.md 2024-07-28 12:39:43 +05:30
AndrewandGitHub acbff61d9e Update run-locally.md 2024-07-25 02:07:01 +05:30
prafull-opensignlabsandGitHub 83a1da04d1 Update run-locally-windows.md 2024-07-25 01:37:30 +05:30
AmolandGitHub b86155de49 Merge pull request #951 from pravinOpenSign/patch-11
Update sign-yourself.md
2024-07-24 23:50:43 +05:30
prafull-opensignlabsandGitHub 2108ce52ca Update how-to-generate-self-signed-document-signing-certificate.md 2024-07-24 22:56:51 +05:30
AmolandGitHub 4af96da515 Create upgrade-to-v2.1.0.md 2024-07-24 15:31:57 +05:30
pravinandGitHub eda2085d4b Update sign-yourself.md
Replace the duplicate step 4 to step 5
2024-07-19 11:49:14 +05:30
AmolandGitHub 6cdcf66e81 Merge pull request #947 from pravinOpenSign/patch-9
Update sign-yourself.md
2024-07-18 18:20:45 +05:30
pravinandGitHub a701522dca Update sign-yourself.md
Completed Sign yourself help document.
2024-07-18 18:19:05 +05:30
AmolandGitHub ddeb31e530 Update opensign.yaml 2024-07-11 08:58:16 +05:30
AmolandGitHub f827ead0b6 Update opensign.yaml 2024-07-11 08:32:19 +05:30
AmolandGitHub ae87703a34 Update index.js 2024-07-11 08:07:54 +05:30
AmolandGitHub 250c2d768b Update DigitalOcean.md 2024-06-14 23:29:24 +05:30
AmolandGitHub e5e6587490 Create env-variables.md 2024-06-12 03:15:47 +05:30
AmolandGitHub 4921152336 Update expired.md 2024-06-10 22:50:29 +05:30
AmolandGitHub 0c7da91fab Update expired.md 2024-06-10 22:49:17 +05:30
AmolandGitHub add07026bc Update Ensure this field is accurately filled and meets all requirements.md 2024-06-06 12:37:37 +05:30
AmolandGitHub 7f64418a6c Create Ensure this field is accurately filled and meets all requirements.md 2024-06-06 12:23:07 +05:30
AmolandGitHub e83ceaa815 Update sign-yourself.md 2024-06-05 00:05:58 +05:30
AmolandGitHub e3dcb99438 Update FAQs.md 2024-06-04 18:48:29 +05:30
AmolandGitHub 78821cb5e4 Update _category_.json 2024-06-04 18:47:55 +05:30
AmolandGitHub 548441dded Create FAQs.md 2024-06-04 18:43:50 +05:30
AmolandGitHub 7c53ae421c Create _category_.json 2024-06-04 18:12:25 +05:30
AmolandGitHub a71a867aea Update opensign.yaml 2024-05-31 20:50:13 +05:30
AmolandGitHub 4d0d2e9a58 Update my-signature.md 2024-05-23 10:37:06 +05:30
AmolandGitHub fcda1e325e Update manage-users.md 2024-05-22 02:59:26 +05:30
AmolandGitHub b41fed0d12 Update my-signature.md 2024-05-22 02:58:14 +05:30
AmolandGitHub ac533d92f6 Create _category_.json 2024-05-22 02:37:55 +05:30
AmolandGitHub 792b48b75a Create how-to-generate-self-signed-document-signing-certificate.md 2024-05-22 02:35:09 +05:30
AmolandGitHub 175eb921c1 Update run-locally-windows.md 2024-05-22 02:27:08 +05:30
prafull-opensignlabsandGitHub 4cd959b34b stop automatic playing 2024-05-16 10:06:42 +05:30
AmolandGitHub 0083efb7dc Update run-locally-windows.md 2024-05-16 00:41:25 +05:30
AmolandGitHub 18738a56c0 Add files via upload 2024-05-15 22:41:11 +05:30
AmolandGitHub 3254fc8199 Rename favicon.ico to favicon-old.ico 2024-05-15 22:40:29 +05:30
prafull-opensignlabsandGitHub 4a52299d35 Update opensign.yaml 2024-05-15 10:28:34 +05:30
prafull-opensignlabsandGitHub 7a1247051c Update opensign.yaml 2024-05-14 10:44:12 +05:30
prafull-opensignlabsandGitHub 71d34dd3d3 Update opensign.yaml 2024-05-13 19:43:09 +05:30
prafull-opensignlabsandGitHub 1971ffd221 Update opensign.yaml 2024-05-13 19:35:17 +05:30
prafull-opensignlabsandGitHub a0af2145f3 Update opensign.yaml 2024-05-13 19:10:31 +05:30
prafull-opensignlabsandGitHub 64bd5cef35 Update opensign.yaml 2024-05-13 19:06:46 +05:30
prafull-opensignlabsandGitHub 4eb511e76e Update opensign.yaml 2024-05-13 19:01:23 +05:30
prafull-opensignlabsandGitHub 6e7fa2a7f8 add documentation for folder api 2024-05-13 18:51:48 +05:30
prafull-opensignlabsandGitHub 1e3765990a Update opensign.yaml 2024-05-13 14:11:53 +05:30
prafull-opensignlabsandGitHub 2ef608fa33 add resend mail api 2024-05-13 11:50:04 +05:30
AmolandGitHub 479878a59d Update primary color 2024-05-07 16:28:09 +05:30
AmolandGitHub 475eba8b7e Update index.js 2024-04-24 18:06:24 +05:30
AmolandGitHub c0ddec8896 Update index.js 2024-04-24 18:02:41 +05:30
prafull-opensignlabsandGitHub 38cd32b8c8 Update opensign.yaml 2024-04-22 19:49:00 +05:30
prafull-opensignlabsandGitHub 9a6ee0397b Update opensign.yaml 2024-04-22 19:29:13 +05:30
prafull-opensignlabsandGitHub 57f7bf6375 add all widgets json 2024-04-22 19:11:46 +05:30
AmolandGitHub 93f3cd7868 Update docusaurus.config.js 2024-04-21 16:11:20 +05:30
AmolandGitHub 157b5a891e Update run-locally.md 2024-04-20 22:12:36 +05:30
AmolandGitHub fcd10703a8 Update sign-yourself.md 2024-04-15 12:15:03 +05:30
AmolandGitHub d17a8524c5 Update sign-yourself.md 2024-04-15 12:12:57 +05:30
AmolandGitHub 2d42013fed Update index.js 2024-04-14 17:23:56 +05:30
AmolandGitHub 5288fe1512 Update index.js 2024-04-14 17:18:40 +05:30
AmolandGitHub 3988361471 Update run-locally-windows.md 2024-04-14 16:45:57 +05:30
AmolandGitHub 62204bda9d Update list-grid-view.md 2024-04-14 16:37:37 +05:30
AmolandGitHub 1ac4418d41 Create run-locally-windows.md 2024-04-14 16:35:09 +05:30
AmolandGitHub 23914274b5 Update run-locally.md 2024-04-14 16:30:27 +05:30
AmolandGitHub a668aa11db Update home page 2024-04-14 16:20:24 +05:30
AmolandGitHub 0aefb792dc Update list-grid-view.md 2024-04-12 17:19:11 +05:30
AmolandGitHub 54de1c1f5a Create navigate-between-pages.md 2024-04-12 17:08:32 +05:30
prafull-opensignlabsandGitHub f53f125e3e Update debugUI url 2024-04-03 09:37:00 +05:30
AmolandGitHub 0269647d75 Update disabledocumentid.md 2024-03-26 19:18:26 +05:30
AmolandGitHub 5fc05e7faa Rename profile.md to disabledocumentid.md 2024-03-26 16:59:44 +05:30
AmolandGitHub 8c3caed1fb Update profile.md 2024-03-26 16:59:15 +05:30
AmolandGitHub fbcb1fc47a fix: spelling mistakes 2024-03-26 16:46:16 +05:30
AmolandGitHub fdd6e64060 Update profile.md 2024-03-26 16:36:27 +05:30
AmolandGitHub 3dd88f2453 Update profile.md 2024-03-26 12:41:38 +05:30
prafull-opensignlabsandGitHub d06bfefc40 Update run-locally.md 2024-03-23 17:16:43 +05:30
prafull-opensignlabsandGitHub 30236206bc remove CI env variables 2024-03-23 15:52:51 +05:30
prafull-opensignlabsandGitHub 24b0231495 Update opensign.yaml 2024-03-19 13:16:14 +05:30
prafull-opensignlabsandGitHub 57946fea6d Update run-locally.md 2024-03-18 17:39:42 +05:30
prafull-opensignlabsandGitHub 187990ce05 Update run-locally.md 2024-03-18 17:29:58 +05:30
AmolandGitHub 8cfcecf440 Update s3.md 2024-03-12 12:35:29 +05:30
AmolandGitHub 041d290f0f Create _category_.json 2024-03-12 12:28:43 +05:30
AmolandGitHub 291791cb08 Update docusaurus.config.js 2024-03-12 12:24:58 +05:30
AmolandGitHub 3d081cb7a0 Create s3.md 2024-03-12 11:58:03 +05:30
AmolandGitHub 0201159b31 Update INSTALLATION.md 2024-03-12 11:55:47 +05:30
AndrewandGitHub 4caaa203bb Update docusaurus.config.js 2024-03-11 10:48:51 +05:30
AndrewandGitHub 783bd63da0 Update docusaurus.config.js 2024-03-11 10:46:06 +05:30
AndrewandGitHub 82a187fe46 Add sitemap 2024-03-11 10:43:09 +05:30
prafull-opensignlabsandGitHub 8ebf96f1e1 Update run-locally.md 2024-03-02 14:44:55 +05:30
prafull-opensignlabsandGitHub b25152a5e8 Update run-locally.md 2024-02-29 19:11:34 +05:30
prafull-opensignlabsandGitHub b1b488a8a0 Update INSTALLATION.md 2024-02-29 19:08:47 +05:30
prafull-opensignlabsandGitHub 8a76b92b5f Update run-locally.md 2024-02-29 15:31:01 +05:30
prafull-opensignlabsandGitHub 724f3a9724 Update run-locally.md 2024-02-29 15:29:40 +05:30
prafull-opensignlabsandGitHub 388d9cb29e Update run-locally.md 2024-02-29 15:22:55 +05:30
prafull-opensignlabsandGitHub 6b463c6f1a Update run-locally.md 2024-02-29 15:15:10 +05:30
prafull-opensignlabsandGitHub 4afca77d1e Update run-locally.md 2024-02-29 15:14:20 +05:30
prafull-opensignlabsandGitHub 53d8e60ec3 Update run-locally.md 2024-02-23 20:43:58 +05:30
prafull-opensignlabsandGitHub 23c22b79d8 Update run-locally.md 2024-02-23 20:40:39 +05:30
prafull-opensignlabsandGitHub 4943517145 Update run-locally.md 2024-02-23 20:12:42 +05:30
prafull-opensignlabsandGitHub c71398c6e3 Update run-locally.md 2024-02-23 20:09:39 +05:30
prafull-opensignlabsandGitHub dfaa278636 Update run-locally.md 2024-02-22 17:24:42 +05:30
prafull-opensignlabsandGitHub 139c3b42f5 Update run-locally.md 2024-02-22 17:18:16 +05:30
prafull-opensignlabsandGitHub d89c28334e Update run-locally.md 2024-02-22 17:15:31 +05:30
prafull-opensignlabsandGitHub 7638bcad02 Update run-locally.md 2024-02-22 17:15:13 +05:30
prafull-opensignlabsandGitHub 63090b82e3 Update run-locally.md 2024-02-22 17:11:07 +05:30
prafull-opensignlabsandGitHub 63b7cb8188 update info of env 2024-02-22 16:58:04 +05:30
prafull-opensignlabsandGitHub ad9dda27ba Update INSTALLATION.md 2024-02-22 16:39:14 +05:30
prafull-opensignlabsandGitHub 3f3137874a change sidebar position of installation instructions 2024-02-22 16:37:00 +05:30
prafull-opensignlabsandGitHub 440eaaeb26 Update title of installation instructiosn 2024-02-22 16:29:39 +05:30
prafull-opensignlabsandGitHub 2d6d8ce2be update run command 2024-02-22 16:25:45 +05:30
prafull-opensignlabsandGitHub 229d0731b8 Update INSTALLATION.md 2024-02-22 16:23:59 +05:30
prafull-opensignlabsandGitHub 418cd92f80 Update opensign.yaml 2024-02-16 19:56:49 +05:30
prafull-opensignlabsandGitHub 6e4ed4bc09 Update opensign.yaml 2024-02-16 16:43:42 +05:30
prafull-opensignlabsandGitHub 51b417aa7e Update opensign.yaml 2024-02-16 16:38:00 +05:30
prafull-opensignlabsandGitHub 0fc895ad17 Update opensign.yaml 2024-02-15 19:11:15 +05:30
prafull-opensignlabsandGitHub 2dea2172bc send in order 2024-02-15 19:04:20 +05:30
prafull-opensignlabsandGitHub fcd3c15ec7 Update opensign.yaml 2024-02-15 15:49:21 +05:30
prafull-opensignlabsandGitHub 4cd9b08698 Update opensign.yaml 2024-02-14 21:26:07 +05:30
prafull-opensignlabsandGitHub b21b641e2a Update opensign.yaml 2024-02-14 21:18:45 +05:30
prafull-opensignlabsandGitHub af9c8d71c6 removed binary api from document and template 2024-02-12 18:01:04 +05:30
prafull-opensignlabsandGitHub 8bd1891fcc Update opensign.yaml 2024-02-12 17:39:09 +05:30
prafull-opensignlabsandGitHub e5a62f17a5 Update opensign.yaml 2024-02-12 17:16:12 +05:30
prafull-opensignlabsandGitHub db383d0e88 Update opensign.yaml 2024-02-12 17:06:21 +05:30
prafull-opensignlabsandGitHub e519d86cad Update opensign.yaml 2024-02-09 17:31:54 +05:30
prafull-opensignlabsandGitHub bf68656d05 Update opensign.yaml 2024-02-09 13:56:43 +05:30
prafull-opensignlabsandGitHub 69913151b6 Update opensign.yaml 2024-02-09 13:47:27 +05:30
prafull-opensignlabsandGitHub 46312eb138 Update opensign.yaml 2024-02-09 12:43:17 +05:30
prafull-opensignlabsandGitHub 1a13936681 Update opensign.yaml 2024-02-09 12:35:33 +05:30
prafull-opensignlabsandGitHub 251ea22f5f Update opensign.yaml 2024-02-09 09:49:48 +05:30
prafull-opensignlabsandGitHub 8f8808ce1c Update opensign.yaml 2024-02-09 09:41:31 +05:30
prafull-opensignlabsandGitHub bb207b953c Update opensign.yaml 2024-02-09 09:35:34 +05:30
prafull-opensignlabsandGitHub 2984d6876d add event description in webhook 2024-02-09 09:25:56 +05:30
prafull-opensignlabsandGitHub 5d2b131c99 remove webhook description 2024-02-09 09:15:08 +05:30
prafull-opensignlabsandGitHub 43c9b4db9c Update opensign.yaml 2024-02-08 10:39:43 +05:30
prafull-opensignlabsandGitHub ce9e27e116 Update opensign.yaml 2024-02-08 10:29:28 +05:30
prafull-opensignlabsandGitHub e25b7deaf3 update description of webhook , create document, create template 2024-02-07 20:51:13 +05:30
prafull-opensignlabsandGitHub 81078073cb Update opensign.yaml 2024-02-02 18:48:59 +05:30
prafull-opensignlabsandGitHub e6e5db95ba Update opensign.yaml 2024-02-02 18:41:50 +05:30
prafull-opensignlabsandGitHub 2cf3af242b Update opensign.yaml 2024-02-02 12:30:49 +05:30
prafull-opensignlabsandGitHub ed664359a6 Update opensign.yaml 2024-02-01 19:46:46 +05:30
prafull-opensignlabsandGitHub 4a5e855b76 Update opensign.yaml 2024-01-29 18:40:43 +05:30
prafull-opensignlabsandGitHub 1e60863a40 Update opensign.yaml 2024-01-29 18:21:44 +05:30
prafull-opensignlabsandGitHub 10ad3ee5bd Update opensign.yaml 2024-01-29 18:20:02 +05:30
prafull-opensignlabsandGitHub 32c5a0a77a Update opensign.yaml 2024-01-29 15:16:37 +05:30
prafull-opensignlabsandGitHub b6651f12e2 Update opensign.yaml 2024-01-25 23:46:25 +05:30
prafull-opensignlabsandGitHub 15ad35bf76 fix: all body parameter must be in lowercase 2024-01-25 18:29:32 +05:30
prafull-opensignlabsandGitHub 9a986dd000 Update title of create template api 2024-01-24 23:40:13 +05:30
prafull-opensignlabsandGitHub 714146f616 update create template API 2024-01-24 23:34:25 +05:30
prafull-opensignlabsandGitHub 96d2b4e9fd Update createdocument api 2024-01-24 18:31:54 +05:30
prafull-opensignlabsandGitHub 122c42153e add webhook api 2024-01-24 12:37:58 +05:30
AmolandGitHub 32c4ee16a0 Create rename-document.md 2024-01-19 15:47:57 +05:30
AmolandGitHub 53cdb411b7 Update organize-documents.md 2024-01-19 15:36:10 +05:30
AmolandGitHub 0708e0ae1c Update intro.md 2024-01-19 15:27:29 +05:30
AmolandGitHub 95db24f8ed Delete docs/docs/help/New-Document/video-tutes.mdx 2024-01-19 15:26:58 +05:30
AmolandGitHub bcdd2e7feb Update REPORTING_GUIDELINES.md 2024-01-19 15:24:10 +05:30
AmolandGitHub 4adfa9f830 Update REPORTING_GUIDELINES.md 2024-01-19 15:23:30 +05:30
AmolandGitHub dcc8f99991 update video link 2024-01-19 14:57:01 +05:30
AmolandGitHub 3bdb56462d Update video-tutes.mdx 2024-01-19 14:47:50 +05:30
AmolandGitHub b8bffe97b5 Update video-tutes.mdx 2024-01-19 14:28:09 +05:30
AmolandGitHub af817b6bc3 Update video-tutes.mdx 2024-01-18 21:18:25 +05:30
nxglabs 8e1cbc65d7 update package lock 2024-01-18 21:10:41 +05:30
nxglabs e38a1f9c1a add help docs 2024-01-18 21:09:17 +05:30
nxglabs 50c3133dae docs updates 2024-01-18 21:09:17 +05:30
AmolandGitHub b1e2a74ad5 Create video-tutes.mdx 2024-01-18 20:40:56 +05:30
AmolandGitHub 7e9dc8cd65 Update package.json 2024-01-18 20:38:19 +05:30
AmolandGitHub 7d87cc27fc Update run-locally.md 2024-01-18 20:30:57 +05:30
AmolandGitHub 25714cde90 Update DigitalOcean.md 2024-01-18 20:28:23 +05:30
AmolandGitHub 8241482c19 Update run-locally.md 2024-01-18 20:23:11 +05:30
AmolandGitHub 3044e010f6 Update run-locally.md 2024-01-18 20:22:19 +05:30
AmolandGitHub 9743b933da Update DigitalOcean.md 2024-01-18 20:19:32 +05:30
AmolandGitHub 1ce8ec4758 Create run-locally.md 2024-01-18 20:16:38 +05:30
AmolandGitHub cb99b7646c Update intro.md 2024-01-18 20:14:56 +05:30
AmolandGitHub 5af7279c67 Delete docs/docs/self-host/tutorial-extras directory 2024-01-18 20:06:23 +05:30
AmolandGitHub 224d9f6787 Rename _category_.json to _category_.json 2024-01-18 20:06:11 +05:30
AmolandGitHub ed7df13f9b Update _category_.json 2024-01-18 20:05:55 +05:30
AmolandGitHub 2c5f6631d7 Delete docs/docs/self-host/tutorial-basics directory 2024-01-18 20:04:56 +05:30
AmolandGitHub a32b88f6b0 Rename docs/docs/self-host/oneclick-install_category_.json to docs/docs/self-host/One-click-Install/oneclick-install_category_.json 2024-01-18 20:02:49 +05:30
AmolandGitHub 88cb271332 Rename DigitalOcean.md to DigitalOcean.md 2024-01-18 20:02:15 +05:30
AmolandGitHub e18d1a7a16 Update DigitalOcean.md 2024-01-18 20:00:59 +05:30
AmolandGitHub b2dfc6f32c Update and rename create-a-page.md to DigitalOcean.md 2024-01-18 19:56:00 +05:30
AmolandGitHub a64e30a94c Update intro.md 2024-01-18 19:52:13 +05:30
AmolandGitHub 6d5b28f75f Update and rename docs/docs/self-host/tutorial-basics/_category_.json to docs/docs/self-host/oneclick-install_category_.json 2024-01-18 19:49:27 +05:30
AmolandGitHub c7decaec02 Update INSTALLATION.md 2024-01-18 19:45:33 +05:30
AmolandGitHub 8600437c5a Update COMMUNITY.md 2024-01-18 19:41:55 +05:30
AmolandGitHub ece3f42ab3 Update COMMUNITY.md 2024-01-18 17:20:06 +05:30
prafull-opensignlabsandGitHub 32be8dfe3a Update opensign.yaml 2024-01-17 17:01:26 +05:30
prafull-opensignlabsandGitHub 2aebceda24 remove duplicate header 2024-01-17 11:55:41 +05:30
prafull-opensignlabsandGitHub 85b463f0e0 Update description of apis 2024-01-17 11:23:43 +05:30
prafull-opensignlabsandGitHub f6c8d4f51a change request body for create document and template 2024-01-17 10:04:49 +05:30
AmolandGitHub b84c1c8821 Update server url 2024-01-16 18:14:10 +05:30
prafull-opensignlabsandGitHub a3334fdf2d remove code from response 2024-01-15 22:17:51 +05:30
prafull-opensignlabsandGitHub d6b12b75b0 Update create template template response 2024-01-15 16:09:15 +05:30
prafull-opensignlabsandGitHub a9e623391b Update response of create contact 2024-01-15 15:35:36 +05:30
prafull-opensignlabsandGitHub fd34ba778c Update opensign.yaml 2024-01-15 13:17:55 +05:30
prafull-opensignlabsandGitHub d00d31eaf2 Update response syntax 2024-01-15 13:03:23 +05:30
prafull-opensignlabsandGitHub 016a0f62bd Update opensign.yaml 2024-01-15 12:11:54 +05:30
prafull-opensignlabsandGitHub cf52292f7c Update documentlist 2024-01-15 11:59:17 +05:30
prafull-opensignlabsandGitHub 8b26c94ada Update document list api 2024-01-12 18:19:02 +05:30
prafull-opensignlabsandGitHub 582b10b5ee Update opensign.yaml 2024-01-12 13:23:34 +05:30
prafull-opensignlabsandGitHub b51e152d10 Update opensign.yaml 2024-01-12 12:35:01 +05:30
prafull-opensignlabsandGitHub 5f197f5f9f Update opensign.yaml 2024-01-12 11:03:13 +05:30
AmolandGitHub 5a3ee25dfd Add clean API docs step 2024-01-12 00:57:03 +05:30
AmolandGitHub 7050138e5e Add build api-docs step 2024-01-12 00:49:06 +05:30
prafull-opensignlabsandGitHub 049d268314 Update template section 2024-01-11 19:59:07 +05:30
prafull-opensignlabsandGitHub 6f71afb418 Update template section name 2024-01-11 17:08:54 +05:30
AndrewandGitHub 7e65692b5a Update CODE_OF_CONDUCT.md 2024-01-01 01:26:47 +05:30
AndrewandGitHub bd1f44479b Delete docs/docs/contribute/tutorial-extras directory 2024-01-01 01:22:47 +05:30
AndrewandGitHub c0935001ce Delete docs/docs/contribute/tutorial-basics directory 2024-01-01 01:22:24 +05:30
AndrewandGitHub f65ab5dfea Update CODE_OF_CONDUCT.md 2024-01-01 01:19:42 +05:30
AndrewandGitHub dbb7fd576f Create REPORTING_GUIDELINES.md 2024-01-01 01:17:56 +05:30
AndrewandGitHub be5cc6126e Update CODE_OF_CONDUCT.md 2024-01-01 01:16:33 +05:30
AndrewandGitHub 6a4eb24630 Update CODE_OF_CONDUCT.md 2024-01-01 01:14:59 +05:30
AndrewandGitHub 61a8bc519f Update sign-yourself.md 2024-01-01 01:10:45 +05:30
AmolandGitHub b15f3c0333 Update request-signatures.md 2023-12-30 00:44:56 +05:30
AmolandGitHub 5830fda578 Update request-signatures.md 2023-12-30 00:44:19 +05:30
AmolandGitHub 102f837263 Update intro.md 2023-12-28 01:30:14 +05:30
AmolandGitHub 5cee42a76e Update intro.md 2023-12-28 01:29:32 +05:30
AmolandGitHub 8fc7cc924a Update sign-yourself.md 2023-12-28 01:20:25 +05:30
AmolandGitHub 80170b814f Create COMMUNITY.md 2023-12-28 01:17:24 +05:30
AmolandGitHub 91fcb71bc3 Update intro.md 2023-12-28 01:13:45 +05:30
AmolandGitHub eb13d91bbb Create INSTALLATION.md 2023-12-28 01:11:46 +05:30
AmolandGitHub a464437b3a Create CODE_OF_CONDUCT.md 2023-12-28 01:10:31 +05:30
AmolandGitHub 3a67dc9b08 Update intro.md 2023-12-28 01:07:49 +05:30
AmolandGitHub a6ff20a657 Update intro.md 2023-12-28 01:00:24 +05:30
AmolandGitHub 4bf380edf8 Update intro.md 2023-12-28 00:58:03 +05:30
AmolandGitHub ab3148b705 Update intro.md 2023-12-28 00:55:20 +05:30
AmolandGitHub 2ffd1ebf2a Update sign-yourself.md 2023-12-28 00:54:17 +05:30
AmolandGitHub d1155bc8ef Update intro.md 2023-12-28 00:44:42 +05:30
AmolandGitHub 33ff706803 docs: Update dropbox icon documentation 2023-12-27 16:46:47 +05:30
nxglabs 7fdd819722 docs: update docs 2023-12-27 02:35:12 +05:30
AmolandGitHub aeaea23098 docs: fix build issue due to no closing img tag 2023-12-27 02:28:55 +05:30
AmolandGitHub ff7f45f055 docs: update screenshot 2023-12-27 02:26:04 +05:30
nxglabs 40665684a8 docs: update intro 2023-12-27 02:23:40 +05:30
nxglabs 861f822151 docs: update docs 2023-12-27 02:19:58 +05:30
nxglabs 62541f1450 docs: fix broken link 2023-12-25 03:30:46 +05:30
nxglabs a356e77f54 docs: fix broken link 2023-12-25 03:29:29 +05:30
nxglabs 74abeff8d9 docs: OpenAPI setup 2023-12-25 03:22:18 +05:30
AmolandGitHub 78dfaccd38 docs: fix broken link 2023-12-24 18:39:33 +05:30
AmolandGitHub 757b72161a docs: add closing img tag 2023-12-24 18:20:26 +05:30
AmolandGitHub c2065bf752 docs: move to other directory 2023-12-24 18:09:30 +05:30
AmolandGitHub 5608dae6e3 docs: add sign yourself documentation 2023-12-24 18:05:03 +05:30
AmolandGitHub 2288880eba docs: update intro 2023-12-24 17:25:32 +05:30
nxglabs ba7d0a9723 chore: add CNAME file for setting up custom domain 2023-12-18 23:09:19 +05:30
nxglabs 1ac8800b40 docs: setup help docs 2023-12-18 23:00:04 +05:30
nxglabs bb7827b762 chore: change the build directory 2023-12-18 20:56:52 +05:30
nxglabs 8caa0275ab chore: change working dir 2023-12-18 20:49:35 +05:30
nxglabs 7e1d0f8a49 chore: add debig code 2023-12-18 20:45:33 +05:30
nxglabs f7f5c2de37 chore: fix syntax issue 2023-12-18 20:44:09 +05:30
nxglabs f75710c836 fix syntax 2023-12-18 20:42:40 +05:30
nxglabs a02cc89e5d chore: change dir 2023-12-18 20:41:37 +05:30
nxglabs f54a51c546 chore: change yarn to npm 2023-12-18 20:38:07 +05:30
nxglabs b12eb87b38 chore: setup github pages action to publish docs 2023-12-18 20:34:20 +05:30
nxglabs 93f7408847 docs: add docs verson 1 2023-12-18 20:21:12 +05:30
429 changed files with 89960 additions and 45004 deletions
-27
View File
@@ -205,33 +205,6 @@
"contributions": [
"code"
]
},
{
"login": "ugoconsonni",
"name": "ugoconsonni",
"avatar_url": "https://avatars.githubusercontent.com/u/13661702?v=4",
"profile": "https://github.com/ugoconsonni",
"contributions": [
"code"
]
},
{
"login": "daniel-mutwiri",
"name": "Daniel Mutwiri",
"avatar_url": "https://avatars.githubusercontent.com/u/8936960?v=4",
"profile": "https://github.com/daniel-mutwiri",
"contributions": [
"code"
]
},
{
"login": "Zathiel",
"name": "Zathiel",
"avatar_url": "https://avatars.githubusercontent.com/u/26553418?v=4",
"profile": "https://github.com/Zathiel",
"contributions": [
"code"
]
}
]
}
+4 -3
View File
@@ -1,5 +1,6 @@
# Frontend config ************************************************************************************************************************************************************************************************
# Set CI to false while running the app locally
CI=false
# Set it to the URL form where the app home page will be accessed
PUBLIC_URL=https://app.opensignlabs.com/
# Set it to true if you want to generate the Sourcemap for debugging
@@ -7,13 +8,13 @@ GENERATE_SOURCEMAP=false
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
REACT_APP_SERVERURL=https://app.opensignlabs.com/api/app
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
REACT_APP_APPID=opensign
REACT_APP_APPID=aDsADsqweqwe
# Google tag manager container id for configuring GTM on all pages of the app
REACT_APP_GTM=GTM-123N7LD
# Backend ExpressJS config ****************************************************************************************************************************************************************************************
# A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App.
APP_ID=opensign
APP_ID=aDsADsqweqwe
# Name of the app. It will be visible in the verification emails sent out.
appName=open_sign_server
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
+3 -1
View File
@@ -2,6 +2,8 @@
# When used as it is, this env config will connect to our hosted UAT backend
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
# Set CI to false while running the app locally
CI=false
# Set it to the URL form where the app home page will be accessed
PUBLIC_URL=http://localhost:3000
# Set it to true if you want to generate the Sourcemap for debugging
@@ -9,4 +11,4 @@ GENERATE_SOURCEMAP=false
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
REACT_APP_SERVERURL=https://staging-app.opensignlabs.com/api/app
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
REACT_APP_APPID=opensign
REACT_APP_APPID=opensignstgn
+10 -8
View File
@@ -2,6 +2,8 @@
# When used as it is, this env config will connect to our hosted UAT backend
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
# Set CI to false while running the app locally
CI=false
# Set it to the URL form where the app home page will be accessed
PUBLIC_URL=http://localhost:3000
# Set it to true if you want to generate the Sourcemap for debugging
@@ -9,18 +11,18 @@ GENERATE_SOURCEMAP=false
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
REACT_APP_SERVERURL=http://localhost:8080/app
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
REACT_APP_APPID=opensign
REACT_APP_APPID=opensignstgn
# Backend ExpressJS config ****************************************************************************************************************************************************************************************
# A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App.
APP_ID=opensign
APP_ID=opensignstgn
# Name of the app. It will be visible in the verification emails sent out.
appName=open_sign_server
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
MASTER_KEY=XnAadwKxxByMr
# Mongodb URI to connect to
MONGODB_URI=mongodb://mongo:27017/OpenSignDB
MONGODB_URI=mongodb://host.docker.internal:27017/OpenSignDB
# Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions.
PARSE_MOUNT=/app
# Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead)
@@ -43,14 +45,14 @@ DO_REGION=us-west
USE_LOCAL=TRUE
# Email mailgun or set SMTP_ENABLE true to use SMTP config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
MAILGUN_API_KEY=
MAILGUN_API_KEY=XXXXX
MAILGUN_DOMAIN=mail.yourdomain.com
MAILGUN_SENDER=postmaster@mail.yourdomain.com
SMTP_ENABLE=
SMTP_HOST=smtp.yourhost.com
SMTP_PORT=443
SMTP_USER_EMAIL=mailer@yourdomain.com
SMTP_PASS=password # if your password includes spaces then write password in single quotes ('asdf pasd asdf bgds').
SMTP_HOST=
SMTP_PORT=
SMTP_USER_EMAIL=
SMTP_PASS=
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
+4 -7
View File
@@ -2,7 +2,7 @@ name: ci
on:
push:
branches:
- 'staging'
- 'main'
jobs:
docker:
@@ -11,9 +11,9 @@ jobs:
matrix:
include:
- image: amolshejole/OpenSign
dockerfile: apps/OpenSign/Dockerhubfile
dockerfile: apps/OpenSign/Dockerfile
- image: amolshejole/OpenSignServer
dockerfile: apps/OpenSignServer/Dockerhubfile
dockerfile: apps/OpenSignServer/Dockerfile
steps:
-
name: Checkout
@@ -31,11 +31,8 @@ jobs:
with:
images: ${{ matrix.image }}
-
name: Debug - List files opensign
name: Debug - List files
run: ls -R apps/OpenSign/ # Adjust the path as needed
-
name: Debug - List files opensignserver
run: ls -R apps/OpenSignServer/ # Adjust the path as needed
-
name: Build and push
uses: docker/build-push-action@v4
+50
View File
@@ -0,0 +1,50 @@
name: Deploy to GitHub Pages
on:
push:
branches:
- feat-docs
# Review gh actions docs if you want to further define triggers, paths, etc
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on
permissions:
contents: write
jobs:
deploy:
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: npm
- name: Install dependencies
working-directory: ./docs
run: npm ci
- name: Clean api-docs
working-directory: ./docs
run: npm run docusaurus clean-api-docs all
- name: Build api-docs
working-directory: ./docs
run: npm run docusaurus gen-api-docs all
- name: Build website
working-directory: ./docs
run: npm run build
# Popular action to deploy to GitHub Pages:
# Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Build output to publish to the `gh-pages` branch:
publish_dir: ./docs/build
# The following lines assign commit authorship to the official
# GH-Actions bot for deploys to `gh-pages` branch:
# https://github.com/actions/checkout/issues/13#issuecomment-724415212
# The GH actions bot is used by default if you didn't specify the two fields.
# You can swap them out with your own user credentials.
user_name: github-actions[bot]
user_email: 41898282+github-actions[bot]@users.noreply.github.com
+136 -16
View File
@@ -1,29 +1,149 @@
# INSTALLATON INSTRUCTIONS HAS A NEW HOME
# INSTALLATON INSTRUCTIONS
We've updated our installation instructions and moved them to our dedicated documentation portal to provide you with a better experience and updated guidance. Whether you're looking to self-host OpenSign™ or contribute to its development, you can find detailed instructions tailored to your needs at [docs.opensignlabs.com](https://docs.opensignlabs.com).
You can use our app as a cloud version from [OpenSignLabs](https://www.opensignlabs.com)
## Self Hosting
or follow below instructions to install it on your own infrastructure.
If you're interested in self-hosting OpenSign™, our new documentation portal provides comprehensive, step-by-step instructions to help you set up OpenSign™ in your own environment. This section is designed for system administrators and those looking to deploy OpenSign™ within their organization or for personal use.
- Localhost(Frontend only)
- Digital Ocean
- Localhost(Docker)
- **Access Self Hosting Instructions:** [Self Hosting Guide](https://docs.opensignlabs.com/docs/category/docker)
## Localhost(Frontend only)
## Contributors
This is the easiest way to run the frontend application for development or testing purpose without the hassle of installing backend & DB. All the features including document upload, signing, email notifications will work seamlessly from staging backend.
For developers and contributors who are looking to build upon or contribute to OpenSign™, we've prepared a separate section that covers the setup, development environment configuration, and guidelines for contributing to the OpenSign™ project.
**Warning:** All data, including user accounts and documents, is stored in the staging backend. This data will be erased every time there is a merge to the main branch. As a result, you may need to recreate user accounts repeatedly. If you find that login attempts are failing, it is likely because the data has been cleared due to a recent merge.
- **Access Contributor Instructions:** [Contributors Guide](https://docs.opensignlabs.com/docs/contribute/INSTALLATION)
Below are the steps to follow -
- [Clone the repository](https://help.github.com/articles/cloning-a-repository/) to your local machine using below command -
```
git clone https://github.com/OpenSignLabs/OpenSign.git
```
- Copy the .env.frontend_dev file to apps/OpenSign/.env using below command(on mac & linux). For windows use COPY command instead.
```
cp .env.frontend_dev apps/OpenSign/.env
```
- CD to /apps/OpenSign directory
- Install NPM packages using
```
npm install
```
- Run the project locally using
```
npm run start
```
You should be able to access the application from http://localhost:3000 after this.
Create an account by signing-up and start contributing.
## Additional Resources
Should you need further assistance or have any questions, please feel free to reach out to us through our support channels:
## Digital Ocean
- **Twitter:** [OpenSignHQ on Twitter](https://twitter.com/OpenSignHQ)
- **Facebook:** [OpenSign on Facebook](https://www.facebook.com/profile.php?id=61551030403669)
- **LinkedIn:** [OpenSign™ on LinkedIn](https://www.linkedin.com/company/opensign%E2%84%A2/)
- **Discord:** [Join our Discord Community](https://discord.com/invite/opensign)
- **YouTube:** [OpenSignHQ on YouTube](https://www.youtube.com/@opensignhq)
OpenSign application consistes of 3 components -
- ReactJS frontend
- NodeJS API
- MongoDB database
We are committed to providing you with the support you need to successfully install and use OpenSign™. Visit our website at [www.opensignlabs.com](https://www.opensignlabs.com) for more information about our project and its features.
You can install all 3 components on digital ocean using the button below -
[![Deploy on DigitalOcean](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/OpenSignLabs/Deploy-OpenSign-to-Digital-Ocean/tree/main&refcode=30db1c901ab0)
## You will need to create an AWS S3 bucket or digital ocean space in order to store your uploaded documents
### AWS S3 -
- Step 1 : Create a S3 bucket
- Login to AWS console
- Navigate to S3 under services
- Hit "Create Bucket" button on upper right corner
- Remove the check from "block all public access" checkbox(we need this in order to provide access to not-logged in users after OTP verification)
- Set bucket versioning and tags as per your requirements
- Hit "Create bucket" button
- Step 2 : Create IAM user and provide access to AWS bucket
- Search for "IAM" on the search bar in AWS console
- On IAM dashboard, click the number of users(count) under IAM resources table
- Hit "create user" button on the upper right corner of the page
- Enter the user name & click next
- Click create policy, search for S3 and provide the Read, Write & list permissions
- Click next and click "Create user"
- Step 3 : Generate Credentials
- Go to IAM/Users in AWS console
- Hit the hyperlink for the user created in the previous step
- Click the "Security credentials" tab
- Scroll down to "Access keys" and hit "Create access key"
- In the next step select "Application running outside AWS"
- Add a description tag if needed & hit "Create access key"
- In the next step you will see "Access key" and "Secret Access key". Copy both the values.
- Set the value of "Access key" to "DO_ACCESS_KEY_ID" environment variable
- Set the value of "Secret Access key" to "DO_SECRET_ACCESS_KEY" environment variable
- Step 3 : Copy bucket credentials
- Visit "Amazon S3 -> Buckets" in aws console
- Click the bucket created in previous steps & visit the properties tab
- Under "Bucket overview" you will find the value of AWS region(for ex. ap-south-1). Set that value to env variable "DO_REGION"
- You can create the value for "DO_ENDPOINT" env variable by appending the region value to amazonaws.com (for ex. s3.ap-south-1.amazonaws.com)
- You can create the value for "DO_BASEURL" by adding the bucketname in front of the endpoint value(for ex. https://bucketname.s3.ap-south-1.amazonaws.com)
Visit below link if you face any issues while following the above instructions -
- https://repost.aws/knowledge-center/create-access-key
## Localhost(Docker)
For local Setup we need to need following prerequisite:
Environment Varaibles:
| Environment Varibale | Value | Description |
| ------------- | ------------- | ------------- |
| CI | false | Set CI to false while running the app locally |
| PUBLIC_URL | http://localhost:3000 | Set it to the URL form where the app home page will be accessed |
| GENERATE_SOURCEMAP | false | Set it to true if you want to generate the Sourcemap for debugging |
| REACT_APP_SERVERURL | http://localhost:8080/app | Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead) |
| REACT_APP_APPID | opensignstgn | A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API. |
| APP_ID | opensignstgn | A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App. |
| appName | open_sign_server | Name of the app. It will be visible in the verification emails sent out. |
| MASTER_KEY | XnAadwKxxByMr | A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database. |
| MONGODB_URI | mongodb://host.docker.internal:27017/OpenSignDB | Mongodb URI to connect to |
| PARSE_MOUNT |/app | Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions. |
| SERVER_URL | http://127.0.0.1:8080/app | Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead) |
| DO_SPACE | DOSPACENAME | Digital ocean space name or AWS S3 bucket name for uploading documents |
| DO_ENDPOINT | ams3.digitaloceanspaces.com | Digital ocean spaces endpoint or AWS S3 endpoint for uploading documents |
| DO_BASEURL |https://DOSPACENAME.ams3.digitaloceanspaces.com | Digital ocean baseurl or AWS S3 base URL |
| DO_ACCESS_KEY_ID | YOUR_S3_ACCESS_ID | Digital ocean spaces access key ID or AWS s3 Access key ID for uploading the docs |
| DO_SECRET_ACCESS_KEY | YOUR_S3_ACCESS_KEY | Digital ocean spaces secret access key or AWS s3 secret access key for uploading the docs |
| DO_REGION | YOUR_S3_REGION | Digital ocean spaces region or AWS s3 region |
| USE_LOCAL | FALSE | To use local file storage to save file |
| MAILGUN_API_KEY | YOUR_MAILGUNAPI_KEY | Mailgun API Key |
| MAILGUN_DOMAIN | YOUR_MAILGUNAPI_DOMAIN | Mailgun API Domain |
| MAILGUN_SENDER | - | Mailgun Sender Mail ID |
| PFX_BASE64 | - | Base64 encoded PFX or p12 document signing certificate file. You can generate base64 encoded self sign certificate using the passphrase `emudhra` |
# Steps to Generate Self Sign Certificate
```
# execute below command and use passphrase emudhra
openssl genrsa -des3 -out ./cert/local_dev.key 2048
openssl req -key ./cert/local_dev.key -new -x509 -days 365 -out ./cert/local_dev.crt
openssl pkcs12 -inkey ./cert/local_dev.key -in ./cert/local_dev.crt -export -out ./cert/local_dev.pfx
openssl base64 -in ./cert/local_dev.pfx -out ./cert/base64_pfx
```
# CORS Configuration
As document storage is delegated to S3-compatible services that reside in a different host than the OpenSign one, document operations (loading, storing, deleting) are subject to [Cross-Origin Resource Sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) restriction policies; as a consequence, OpenSign app may fail with (browser console) errors like the following:
```
Access to fetch at 'https://foo.nyc3.digitaloceanspaces.com/exported_file_4627_0000-00-00T00%3A45%3A43.344Z.pdf'
from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header
is present on the requested resource. If an opaque response serves your needs, set the request's mode to
'no-cors' to fetch the resource with CORS disabled.
```
In order to address this, your document storage system must be instructed to accept requests from other hosts; below the relevant documentation links:
- [How to Configure CORS on DigitalOcean Spaces](https://docs.digitalocean.com/products/spaces/how-to/configure-cors/)
- [Configuring cross-origin resource sharing on AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html)
# Build Local Environment
Command to build project -
- Execute `make build`
Command to run project -
- Execute `make run`
+2 -1
View File
@@ -1,6 +1,7 @@
build:
cp .env.local_dev .env
cd apps/OpenSign && cp ../../.env.local_dev .env && npm install && npm run build
rm -rf apps/OpenSign/public/mfbuild
cd microfrontends/SignDocuments && npm install && npm run build
docker compose up --build --force-recreate
run:
+11 -15
View File
@@ -38,28 +38,27 @@
7. [Acknowledgments](#acknowledgments)
---
Please star ⭐ the repo to support us! 😀
### Introduction
Welcome to OpenSign, the premier open source docusign alternative - document e-signing solution designed to provide a secure, reliable and free alternative to commercial esign platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
Welcome to OpenSign, an open-source document e-signing solution designed to provide a secure, reliable, and free alternative to commercial platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Developed under the OpenSignLabs organization, our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
---
### Features
- **Secure PDF E-Signing:** With the help of robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility.
- **Annotate Documents:** OpenSign™ allows you to annotate PDF documents with an advanced signing pad that allows hand drawn signatures, uploaded images, typed signatures & saved signatures for the simplest signing experience ever.
- **User-Friendly Interface:** OpenSign™ was built while keeping Intuitive design in mind for ease of use. Features like "Sign yourself", "Templates", "One click signatures" and "OpenSign Drive" makes it stand out of the crowd and even makes it better than a lot of so-called industry leaders.
- **Multi-signer Support:** OpenSign's ability to invite multiple signers for signing along with the ability to invite witnesses & being able to enforce signing in a sequence makes it the only open source solution that is fully loaded and allows it to compete head-to-head with established players in e-signature space.
- **Secure PDF E-Signing:** With the help of Robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility.
- **Annotate Documents:** OpenSign™ allows you to annotate PDF documents with an advanced signing pad that comes with hand drawn signatures support as well as uploaded images & saved signatures for the simplest signing experience.
- **User-Friendly Interface:** OpenSign™ was built while keeping Intuitive design in mind for ease of use. Features like "Sign yourself", "One click signatures" and "OpenSign Drive" makes it stand out of the crowd and even makes it better than a lot of so-called industry leaders.
- **Multi-signer Support:** OpenSign's ability to invite multiple signers for signing along with the ability to invite witnesses & being able to enforce signing in a sequence makes it the only open source solution that is fully loaded and allows it to compete head-to-head with established players.
- **Email Unique Code(OTP) verification support for guest signers:** With OpenSign™, your documents are fully secure even when being signed by guest users. Guest signers can only sign the document after entering a unique code sent to their email address. 
- **"Expiring Docs" & "Rejection":** You can set documents to expire after certain number of days after which nobody will be able to sign. Not just this, OpenSign also allows signers to reject signing a document.
- **"Expiring Docs" & "Rejection":** You can set documents to expire after certain number of days after which nobody will be able to sign it. Not just this, OpenSign also allows signers to reject signing a document.
- **Beautiful email templates:** All document signing invitations, completion notifications & reminders are formatted using great looking email templates.
- **PDF Template Creation:** OpenSign™ allows you to create and store PDF document templates for repeated use thereby saving you a lot of time & collect e-signatures seamlessly.
- **OpenSign™ Drive:** It is a centralised secure vault for your digital documents that makes storing, signing, organizing, sharing & achieving your docs a breeze.
- **PDF Template Creation(coming soon):** OpenSign™ allows you to create and store PDF document templates for repeated use thereby saving you a lot of time.
- **OpenSign™ Drive:** It is a centralised secure vault for your signed documents that makes storing, signing, organizing, sharing & achieving your docs a breeze.
- **Audit Trails & completion certificate:** Being a security focused solution, OpenSign™ makes it a top priority to save detailed logs for tracking document activities along with time-stamps, IP addresses, email IDs & phone numbers. A completion certificate is generated as soon as document is completed which contains all the document related logs for added safety.
- **API Support:** OpenSign™ API allows seamless integration into existing systems and software. You can generate an API key from the app and refer the [docs](https://docs.opensignlabs.com) to start integrating it in your existing applications.
- **Integrations:** Seamless integrations with various Cloud storage systems, CRMs & enterprise platforms is available. We also have a Zapier integration that allows you to integrate it with virtually any application.
- **API Support(coming soon):** OpenSign™ API allows seamless integration into existing systems and software. APIs will soon be available as a cloud hosted solution.
- **Integrations:** Seamless integrations with various Cloud storage systems, CRMs & enterprise platforms is coming soon.
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/b37ff443-7099-4273-9aeb-21c46d7154cf height='200'>
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/86db91b7-6c2f-4885-a33c-58f4fd35ec89 height='200'>
@@ -76,7 +75,7 @@ Please refer to the [Installation Guide](INSTALLATION.md) for detailed instructi
### Usage
For comprehensive guidelines on how to use OpenSign, please consult our [User Manual](USAGE.md).
For comprehensive guidelines on how to use OpenSign, please consult our [User Manual](USAGE.md).
---
@@ -134,9 +133,6 @@ We would like to thank all our contributors and users for their support and feed
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ugoconsonni"><img src="https://avatars.githubusercontent.com/u/13661702?v=4?s=100" width="100px;" alt="ugoconsonni"/><br /><sub><b>ugoconsonni</b></sub></a><br /><a href="#code-ugoconsonni" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/daniel-mutwiri"><img src="https://avatars.githubusercontent.com/u/8936960?v=4?s=100" width="100px;" alt="Daniel Mutwiri"/><br /><sub><b>Daniel Mutwiri</b></sub></a><br /><a href="#code-daniel-mutwiri" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a></td>
</tr>
</tbody>
</table>
+2 -2
View File
@@ -1,5 +1,5 @@
# Use an official Node runtime as the base image
FROM node:18
FROM node:16
# Set the working directory inside the container
WORKDIR /usr/src/app
@@ -21,5 +21,5 @@ EXPOSE 3000
# ENV NODE_ENV production
# Run the application
ENTRYPOINT npm run start
ENTRYPOINT npm run start-dev
-27
View File
@@ -1,27 +0,0 @@
# Use an official Node runtime as the base image
FROM node:18
# Set the working directory inside the container
WORKDIR /usr/src/app
# Copy package.json and package-lock.json first to leverage Docker cache
COPY apps/OpenSign/package*.json ./
# Install application dependencies
RUN npm install
# Copy the current directory contents into the container
COPY apps/OpenSign/ .
COPY apps/OpenSign/.husky .
# build
RUN npm run build
# Make port 3000 available to the world outside this container
EXPOSE 3000
# Define environment variables if needed
# ENV NODE_ENV production
# Run the application
CMD ["npm", "start"]
+29
View File
@@ -0,0 +1,29 @@
const { dependencies } = require("./package.json");
module.exports = {
name: "shell_app", // change me
filename: "remoteEntry.js",
exposes: {},
remotes: {},
shared: {
...dependencies,
react: {
singleton: true,
import: "react", // fallback is also react
shareScope: "default",
requiredVersion: dependencies.react
},
"react-dom": {
singleton: true,
requiredVersion: dependencies["react-dom"]
},
"react-router-dom": {
singleton: true,
requiredVersion: dependencies["react-router-dom"]
},
"rjsf-conditionals": {
singleton: true,
requiredVersion: dependencies["rjsf-conditionals"]
}
}
};
+1188 -3814
View File
File diff suppressed because it is too large Load Diff
+33 -42
View File
@@ -3,57 +3,48 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@formkit/auto-animate": "^0.8.2",
"@radix-ui/themes": "^2.0.3",
"@react-pdf/renderer": "^3.4.4",
"@reduxjs/toolkit": "^2.2.1",
"axios": "^1.6.8",
"@reduxjs/toolkit": "^1.9.7",
"@rjsf/core": "^5.13.0",
"@rjsf/utils": "^5.13.0",
"@rjsf/validator-ajv8": "^5.13.0",
"axios": "^1.5.1",
"daisyui": "^3.9.2",
"file-saver": "^2.0.5",
"jwt-decode": "^4.0.0",
"moment": "^2.30.1",
"parse": "^4.3.1",
"pdf-lib": "^1.17.1",
"print-js": "^1.6.0",
"radix-ui": "^1.0.1",
"html-react-parser": "^4.2.2",
"json-rules-engine-simplified": "^0.1.17",
"jwt-decode": "^3.1.2",
"mf-cra": "^0.0.5",
"moment": "^2.29.4",
"parse": "^4.2.0",
"react": "^18.2.0",
"react-bootstrap": "^2.10.2",
"react-cookie": "^7.1.4",
"react-datepicker": "^6.4.0",
"react-bootstrap": "^2.9.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^8.0.3",
"react-dnd-touch-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-drag-listview": "^2.0.0",
"react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0",
"react-konva": "^18.2.10",
"react-modal": "^3.16.1",
"react-pdf": "^7.7.3",
"react-quill": "^2.0.0",
"react-redux": "^9.1.0",
"react-rnd": "^10.4.10",
"react-router-dom": "^6.22.3",
"react-redux": "^8.1.3",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"react-scrollbars-custom": "^4.1.1",
"react-select": "^5.8.0",
"react-signature-canvas": "^1.0.6",
"react-tooltip": "^5.26.3",
"react-web-share": "^2.0.2",
"reactour": "^1.19.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"regex-parser": "^2.3.0",
"react-select": "^5.7.7",
"reactour": "^1.19.2",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"rjsf-conditionals": "github:prafullnavkar-nxg/rjsf-conditionals",
"serve": "^14.2.1",
"styled-components": "^4.4.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "serve -s build",
"start-dev": "react-scripts start",
"start-dev": "mf-cra start",
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
"build": "npm run version && react-scripts build",
"build-win": "npm run version-win && react-scripts build",
"build": "npm run version && rm -rf public/mfbuild && npm run micro && CI=false && mf-cra build",
"micro": "cd ../../microfrontends/SignDocuments && npm install && npm run build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"release": "standard-version",
@@ -83,16 +74,16 @@
}
},
"devDependencies": {
"@babel/runtime-corejs2": "^7.24.5",
"autoprefixer": "^10.4.19",
"@babel/runtime-corejs2": "^7.23.2",
"autoprefixer": "^10.4.16",
"commitizen": "^4.3.0",
"eslint": "^8.57.0",
"eslint": "^8.51.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.34.1",
"lint-staged": "^15.2.2",
"postcss": "^8.4.38",
"prettier": "^2.8.8",
"pretty-quick": "^3.3.1",
"tailwindcss": "^3.4.1"
"eslint-plugin-react": "^7.33.2",
"lint-staged": "^15.0.1",
"postcss": "^8.4.31",
"prettier": "^2.8.0",
"pretty-quick": "^3.1.3",
"tailwindcss": "^3.3.3"
}
}
+2 -2
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- <link rel="icon" type="image/png"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC"
sizes="40x40" /> -->
@@ -15,7 +15,7 @@
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="/manifest.json" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Binary file not shown.
+110 -152
View File
@@ -1,55 +1,21 @@
import React, { useState, useEffect, lazy } from "react";
import React, { useState, useEffect } from "react";
import { Routes, Route, BrowserRouter } from "react-router-dom";
import { pdfjs } from "react-pdf";
import Login from "./pages/Login";
import Form from "./pages/Form";
import Report from "./pages/Report";
import Dashboard from "./pages/Dashboard";
import Pgsignup from "./routes/Pgsignup";
import Login from "./routes/Login";
import Microapp from "./routes/RemoteApp";
import Signup from "./routes/Signup";
import Form from "./routes/Form";
import Report from "./routes/Report";
import Dashboard from "./routes/Dashboard";
import PlanSubscriptions from "./routes/PlanSubscriptions";
import HomeLayout from "./layout/HomeLayout";
import PageNotFound from "./pages/PageNotFound";
import ValidateRoute from "./primitives/ValidateRoute";
import Validate from "./primitives/Validate";
import TemplatePlaceholder from "./pages/TemplatePlaceholder";
import SignYourSelf from "./pages/SignyourselfPdf";
import DraftDocument from "./components/pdf/DraftDocument";
import PlaceHolderSign from "./pages/PlaceHolderSign";
import PdfRequestFiles from "./pages/PdfRequestFiles";
import LazyPage from "./primitives/LazyPage";
import { isEnableSubscription } from "./constant/const";
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
const Pgsignup = lazy(() => import("./pages/Pgsignup"));
const Subscriptions = lazy(() => import("./pages/PlanSubscriptions"));
const ChangePassword = lazy(() => import("./pages/ChangePassword"));
const UserProfile = lazy(() => import("./pages/UserProfile"));
const Signup = lazy(() => import("./pages/Signup"));
const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
const ManageSign = lazy(() => import("./pages/Managesign"));
const GenerateToken = lazy(() => import("./pages/GenerateToken"));
const Webhook = lazy(() => import("./pages/Webhook"));
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
import UserProfile from "./routes/UserProfile";
import PageNotFound from "./routes/PageNotFound";
import ForgetPassword from "./routes/ForgetPassword";
import ChangePassword from "./routes/ChangePassword";
import ReportMicroapp from "./components/ReportMicroapp";
import LoadMf from "./routes/LoadMf";
const Loader = () => {
return (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
);
};
function App() {
const [isloading, setIsLoading] = useState(true);
useEffect(() => {
@@ -57,16 +23,12 @@ function App() {
}, []);
const handleCredentials = () => {
const appId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
const baseurl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
const appId = process.env.REACT_APP_APPID;
const baseurl = process.env.REACT_APP_SERVERURL;
const appName = "contracts";
try {
localStorage.setItem("baseUrl", `${baseurl}/`);
localStorage.setItem("parseAppId", appId);
localStorage.setItem("BaseUrl12", `${baseurl}/`);
localStorage.setItem("AppID12", appId);
localStorage.setItem("domain", appName);
setIsLoading(false);
} catch (error) {
@@ -77,111 +39,107 @@ function App() {
return (
<div className="bg-[#eef1f5]">
{isloading ? (
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<BrowserRouter>
<Routes>
<Route element={<ValidateRoute />}>
<Route exact path="/" element={<Login />} />
<Route path="/signup" element={<LazyPage Page={Signup} />} />
</Route>
<Route element={<Validate />}>
<Route
path="/load/template/:templateId"
element={<TemplatePlaceholder />}
/>
<Route
exact
path="/load/placeholdersign/:docId"
element={<PlaceHolderSign />}
/>
<Route
exact
path="/load/recipientSignPdf/:docId/:contactBookId"
element={<PdfRequestFiles />}
/>
</Route>
<Route
path="/loadmf/signmicroapp/login/:id/:userMail/:contactBookId/:serverUrl"
element={<LazyPage Page={GuestLogin} />}
/>
<Route
path="/login/:id/:userMail/:contactBookId/:serverUrl"
element={<LazyPage Page={GuestLogin} />}
/>
<Route
path="/login/:base64url"
element={<LazyPage Page={GuestLogin} />}
/>
<Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} />
<Route
path="/forgetpassword"
element={<LazyPage Page={ForgetPassword} />}
/>
{isEnableSubscription && (
<Route exact path="/" element={<Login />} />
<Route exact path="/signup" element={<Signup />} />
<Route exact path="/loadmf/:remoteApp/*" element={<LoadMf />} />
<Route exact path="/forgetpassword" element={<ForgetPassword />} />
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
<>
<Route exact path="/pgsignup" element={<Pgsignup />} />
<Route
path="/pgsignup"
element={<LazyPage Page={Pgsignup} />}
/>
<Route
exact
path="/subscription"
element={<LazyPage Page={Subscriptions} />}
element={<PlanSubscriptions />}
/>
</>
)}
<Route element={<HomeLayout />}>
<Route
path="/changepassword"
element={<LazyPage Page={ChangePassword} />}
/>
<Route path="/form/:id" element={<Form />} />
<Route path="/report/:id" element={<Report />} />
<Route path="/dashboard/:id" element={<Dashboard />} />
<Route
path="/profile"
element={<LazyPage Page={UserProfile} />}
/>
<Route
path="/opensigndrive"
element={<LazyPage Page={Opensigndrive} />}
/>
<Route
path="/managesign"
element={<LazyPage Page={ManageSign} />}
/>
<Route
path="/generatetoken"
element={<LazyPage Page={GenerateToken} />}
/>
<Route path="/webhook" element={<LazyPage Page={Webhook} />} />
<Route
path="/template/:templateId"
element={<TemplatePlaceholder />}
/>
{/* signyouself route with no rowlevel data using docId from url */}
<Route path="/signaturePdf/:docId" element={<SignYourSelf />} />
{/* draft document route to handle and navigate route page accordiing to document status */}
<Route path="/draftDocument" element={<DraftDocument />} />
{/* recipient placeholder set route with no rowlevel data using docId from url*/}
<Route
path="/placeHolderSign/:docId"
element={<PlaceHolderSign />}
/>
{/* for user signature (need your sign route) with row level data */}
<Route path="/pdfRequestFiles" element={<PdfRequestFiles />} />
{/* for user signature (need your sign route) with no row level data */}
<Route
path="/pdfRequestFiles/:docId"
element={<PdfRequestFiles />}
/>
{/* recipient signature route with no rowlevel data using docId from url */}
<Route
path="/recipientSignPdf/:docId/:contactBookId"
element={<PdfRequestFiles />}
/>
</Route>
<Route
exact
path="/changepassword"
element={
<HomeLayout>
<ChangePassword />
</HomeLayout>
}
/>
<Route
path="/mf/:remoteApp/*"
element={
<HomeLayout>
<Microapp />
</HomeLayout>
}
/>
<Route
path="/asmf/:remoteApp/*"
element={
<HomeLayout>
<Microapp />
</HomeLayout>
}
/>
<Route
path="/rpmf/:remoteApp/*"
element={
<HomeLayout>
<ReportMicroapp />
</HomeLayout>
}
/>
<Route
path="/form/:id"
element={
<HomeLayout>
<Form />
</HomeLayout>
}
/>
<Route
path="/report/:id"
element={
<HomeLayout>
<Report />
</HomeLayout>
}
/>
<Route
path="/dashboard/:id"
element={
<HomeLayout>
<Dashboard />
</HomeLayout>
}
/>
<Route
path="/profile"
element={
<HomeLayout>
<UserProfile />
</HomeLayout>
}
/>
<Route path="*" element={<PageNotFound />} />
{/* <Route exact path="/ForgotPassword" element={<ForgotPassword />} /> */}
</Routes>
</BrowserRouter>
)}
+62
View File
@@ -0,0 +1,62 @@
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import { store } from "./redux/store";
import { HTML5Backend } from "react-dnd-html5-backend";
import { TouchBackend } from "react-dnd-touch-backend";
import {
DndProvider,
TouchTransition,
MouseTransition,
Preview
} from "react-dnd-multi-backend";
import DragElement from "./microappComponent/DragElement";
import TagManager from "react-gtm-module";
const HTML5toTouch = {
backends: [
{
id: "html5",
backend: HTML5Backend,
transition: MouseTransition
},
{
id: "touch",
backend: TouchBackend,
options: { enableMouseEvents: true },
preview: true,
transition: TouchTransition
}
]
};
const generatePreview = (props) => {
const { item, style } = props;
const newStyle = {
...style
};
return (
<div style={newStyle}>
<DragElement {...item} />
</div>
);
};
if (process.env.REACT_APP_GTM) {
const tagManagerArgs = {
gtmId: process.env.REACT_APP_GTM
};
TagManager.initialize(tagManagerArgs);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<DndProvider options={HTML5toTouch}>
<Preview>{generatePreview}</Preview>
<App />
</DndProvider>
</Provider>
);
-281
View File
@@ -1,281 +0,0 @@
import React, { useState } from "react";
import Parse from "parse";
import axios from "axios";
import Title from "./Title";
import Alert from "../primitives/Alert";
const AddUser = (props) => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
const [isLoader, setIsLoader] = useState(false);
const [isUserExist, setIsUserExist] = useState(false);
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
const checkUserExist = async () => {
try {
const res = await Parse.Cloud.run("getUserDetails", {
email: email,
userId: Parse.User.current().id
});
if (res) {
return true;
} else {
return false;
}
} catch (err) {
console.log("err", err);
}
};
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
const res = await checkUserExist();
if (res) {
setIsUserExist(true);
setIsLoader(false);
setTimeout(() => {
setIsUserExist(false);
}, 1000);
} else {
try {
const contactQuery = new Parse.Object("contracts_Users");
contactQuery.set("Name", name);
contactQuery.set("Phone", phone);
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_User");
if (localStorage.getItem("TenantId")) {
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
});
}
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("phone", phone);
_user.set("password", phone);
const user = await _user.save();
if (user) {
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_User",
userId: user.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
setName("");
setPhone("");
setEmail("");
}
} catch (err) {
console.log("err ", err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run("getUserId", params);
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_User",
userId: userRes.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: userRes.id
});
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
setName("");
setPhone("");
setEmail("");
}
}
} catch (err) {
// console.log("err", err);
setIsLoader(false);
alert("something went wrong!");
}
}
};
// Define a function to handle the "add yourself" checkbox
const handleReset = () => {
setName("");
setPhone("");
setEmail("");
};
return (
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
<Title title={"Add User"} />
{isUserExist && <Alert type="danger">User already exists!</Alert>}
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50">
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
)}
<div className="w-full mx-auto">
<form onSubmit={handleSubmit}>
<h1 className="text-[20px] font-semibold mb-4">Add User</h1>
<div className="mb-3">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
Name
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
required
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
Email
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Phone
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="flex items-center mt-3 gap-2 text-white">
<button
type="submit"
className="bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none"
>
Submit
</button>
<div
type="button"
onClick={() => handleReset()}
className="bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
>
Reset
</div>
</div>
</form>
</div>
</div>
);
};
export default AddUser;
@@ -1,9 +1,8 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import axios from "axios";
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
const AddSigner = (props) => {
const AppendFormInForm = (props) => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
@@ -47,9 +46,9 @@ const AddSigner = (props) => {
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
if (localStorage.getItem("TenantId")) {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
try {
const contactQuery = new Parse.Object("contracts_Contactbook");
contactQuery.set("Name", name);
@@ -57,11 +56,13 @@ const AddSigner = (props) => {
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
if (localStorage.getItem("TenetId")) {
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
objectId: localStorage.getItem("TenetId")
});
}
try {
const _users = Parse.Object.extend("User");
@@ -104,18 +105,13 @@ const AddSigner = (props) => {
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
@@ -162,18 +158,13 @@ const AddSigner = (props) => {
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
@@ -187,9 +178,6 @@ const AddSigner = (props) => {
setIsLoader(false);
alert("something went wrong!");
}
}else{
alert('something went wrong, please try again later')
}
};
// Define a function to handle the "add yourself" checkbox
@@ -294,16 +282,14 @@ const AddSigner = (props) => {
<div className="mt-4 flex justify-start">
<button
type="submit"
style={{ backgroundColor: modalSubmitBtnColor }}
className="mr-2 px-[20px] py-1.5 text-white rounded shadow-md text-center focus:outline-none "
className="bg-[#1ab6ce] text-sm text-white px-4 py-2 rounded shadow focus:outline-none"
>
Submit
</button>
<button
type="button"
onClick={() => handleReset()}
style={{ backgroundColor: modalCancelBtnColor }}
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
className="bg-[#188ae2] text-sm text-white px-4 py-2 rounded ml-2 shadow focus:outline-none"
>
Reset
</button>
@@ -314,4 +300,4 @@ const AddSigner = (props) => {
);
};
export default AddSigner;
export default AppendFormInForm;
@@ -0,0 +1,49 @@
import React, { Component } from "react";
export class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return {
hasError: true
};
}
componentDidCatch(error, info) {
console.log(error);
console.log(info);
}
render() {
if (this.state.hasError) {
return (
<div className="p-2">
<div
style={{
height: "100vh",
display: "flex",
flexFlow: "column"
}}
>
<h1
style={{
fontSize: "2em",
fontFamily: "sans-serif",
textShadow: "0 0 20px black"
}}
>
Something went wrong.
</h1>
</div>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
+2 -9
View File
@@ -1,7 +1,6 @@
import React, { useEffect, useState } from "react";
import Package from "../../package.json";
import axios from "axios";
import { openInNewTab } from "../constant/Utils";
const Footer = () => {
const [showButton, setShowButton] = useState(false);
const [version, setVersion] = useState("");
@@ -38,18 +37,12 @@ const Footer = () => {
}, []);
const appName = "OpenSign™";
const openUrl = () => {
openInNewTab(
"https://github.com/OpenSignLabs/OpenSign/releases/tag/" + version
);
};
return (
<>
<div className="bg-[#222c3c] text-[#98a6ba] text-center text-[13px] py-3">
All Rights Reserved &copy; {new Date().getFullYear()} &nbsp;
<span onClick={openUrl} className="hover:underline cursor-pointer">
{appName} ( version: {version ? version : `${Package.version} `})
</span>
{appName} ( version: {version ? version : `${Package.version} `})
</div>
<button
className={`${
+14 -64
View File
@@ -4,46 +4,25 @@ import FullScreenButton from "./FullScreenButton";
import { useNavigate } from "react-router-dom";
import Parse from "parse";
import { useWindowSize } from "../hook/useWindowSize";
import { checkIsSubscribed, getAppLogo, openInNewTab } from "../constant/Utils";
import { isEnableSubscription } from "../constant/const";
const Header = ({ showSidebar }) => {
const navigate = useNavigate();
const navigation = useNavigate();
const { width } = useWindowSize();
let applogo = localStorage.getItem("appLogo") || "";
let username = localStorage.getItem("username");
const image = localStorage.getItem("profileImg") || dp;
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [isOpen, setIsOpen] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(true);
const [isPro, setIsPro] = useState(false);
const [applogo, setAppLogo] = useState(
localStorage.getItem("appLogo") || " "
);
const toggleDropdown = () => {
setIsOpen(!isOpen);
};
useEffect(() => {
checkSubscription();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function checkSubscription() {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
if (getIsSubscribe) {
const applogo = await getAppLogo();
if (applogo) {
setAppLogo(applogo);
} else {
setAppLogo(localStorage.getItem("appLogo") || "");
}
}
setIsPro(getIsSubscribe);
setIsSubscribe(getIsSubscribe);
}
}
const closeDropdown = () => {
setIsOpen(false);
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
Parse.User.logOut();
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
@@ -52,8 +31,8 @@ const Header = ({ showSidebar }) => {
let PageLanding = localStorage.getItem("PageLanding");
let domain = localStorage.getItem("domain");
let _appName = localStorage.getItem("_appName");
let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId");
let baseUrl = localStorage.getItem("BaseUrl12");
let appid = localStorage.getItem("AppID12");
localStorage.clear();
@@ -64,10 +43,10 @@ const Header = ({ showSidebar }) => {
localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("domain", domain);
localStorage.setItem("userSettings", appdata);
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
localStorage.setItem("BaseUrl12", baseUrl);
localStorage.setItem("AppID12", appid);
navigate("/");
navigation("/");
};
//handle to close profile drop down menu onclick screen
@@ -107,21 +86,6 @@ const Header = ({ showSidebar }) => {
id="profile-menu"
className="flex justify-between items-center gap-x-3"
>
{!isSubscribe && (
<div>
<button
className="text-xs bg-[#002864] p-2 text-white rounded shadow"
onClick={() => navigate("/subscription")}
>
Upgrade Now
</button>
</div>
)}
{isPro && (
<div className="w-[35px] h-[35px] rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
PRO
</div>
)}
<div>
<FullScreenButton />
</div>
@@ -154,15 +118,9 @@ const Header = ({ showSidebar }) => {
<ul>
<li
className="hover:bg-gray-100 rounded-t-lg py-1 px-2 cursor-pointer font-normal"
onClick={() => openInNewTab("https://docs.opensignlabs.com")}
>
<i className="fa-solid fa-book"></i> Docs
</li>
<li
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
onClick={() => {
setIsOpen(false);
navigate("/profile");
navigation("/profile");
}}
>
<i className="fa-regular fa-user"></i> Profile
@@ -171,19 +129,11 @@ const Header = ({ showSidebar }) => {
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
onClick={() => {
setIsOpen(false);
navigate("/changepassword");
navigation("/changepassword");
}}
>
<i className="fa-solid fa-lock"></i> Change Password
</li>
<li
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
onClick={() => {
window.open("https://console.opensignlabs.com/");
}}
>
<i className="fa-regular fa-id-card"></i> Console
</li>
<li
className="hover:bg-gray-100 rounded-b-lg py-1 px-2 cursor-pointer font-normal"
onClick={closeDropdown}
@@ -86,7 +86,7 @@ const LoginFacebook = ({
email: userDetails.Email,
// "passsword":userDetails.Phone,
phone: userDetails.Phone,
role: "contracts_User",
role: "contracts_Admin",
company: userDetails.Company
}
};
+124 -102
View File
@@ -1,9 +1,7 @@
import React, { useState, useRef } from "react";
import Parse from "parse";
import { jwtDecode } from "jwt-decode";
import jwtDecode from "jwt-decode";
import { useScript } from "../hook/useScript";
import ModalUi from "../primitives/ModalUi";
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
/*
* `GoogleSignInBtn`as it's name indicates it render google sign in button
@@ -48,8 +46,8 @@ const GoogleSignInBtn = ({
if (Parse.User.current()) {
await Parse.User.logOut();
}
let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId");
let baseUrl = localStorage.getItem("BaseUrl12");
let appid = localStorage.getItem("AppID12");
let applogo = localStorage.getItem("appLogo");
let domain = localStorage.getItem("domain");
let appversion = localStorage.getItem("appVersion");
@@ -63,8 +61,8 @@ const GoogleSignInBtn = ({
localStorage.clear();
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
localStorage.setItem("BaseUrl12", baseUrl);
localStorage.setItem("AppID12", appid);
localStorage.setItem("appLogo", applogo);
localStorage.setItem("domain", domain);
localStorage.setItem("appversion", appversion);
@@ -115,8 +113,8 @@ const GoogleSignInBtn = ({
// console.log("payload ", payload);
if (payload && payload.sessiontoken) {
// setThirdpartyLoader(true);
// const billingDate =
// extRes.get("Next_billing_date") && extRes.get("Next_billing_date");
const billingDate =
extRes.get("Next_billing_date") && extRes.get("Next_billing_date");
// console.log("billingDate expired", billingDate > new Date());
const LocalUserDetails = {
name: details.Name,
@@ -125,7 +123,7 @@ const GoogleSignInBtn = ({
company: extRes.get("Company")
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
thirdpartyLoginfn(payload.sessiontoken);
thirdpartyLoginfn(payload.sessiontoken, billingDate);
}
return { msg: "exist" };
} else {
@@ -150,7 +148,7 @@ const GoogleSignInBtn = ({
email: userDetails.Email,
// "passsword":userDetails.Phone,
phone: userDetails.Phone,
role: "contracts_User",
role: "contracts_Admin",
company: userDetails.Company,
jobTitle: userDetails.Destination
}
@@ -191,8 +189,8 @@ const GoogleSignInBtn = ({
let PageLanding = localStorage.getItem("PageLanding");
let domain = localStorage.getItem("domain");
let _appName = localStorage.getItem("_appName");
let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId");
let baseUrl = localStorage.getItem("BaseUrl12");
let appid = localStorage.getItem("AppID12");
localStorage.clear();
@@ -203,8 +201,8 @@ const GoogleSignInBtn = ({
localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("domain", domain);
localStorage.setItem("userSettings", appdata);
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
localStorage.setItem("BaseUrl12", baseUrl);
localStorage.setItem("AppID12", appid);
};
return (
<div style={{ position: "relative" }}>
@@ -233,94 +231,118 @@ const GoogleSignInBtn = ({
</div>
)}
<div ref={googleBtn} className="text-sm"></div>
<ModalUi showClose={false} isOpen={isModal} title="Sign up form">
<form className="px-4 py-3">
<div className="mb-3">
<label
htmlFor="Phone"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Phone <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="tel"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="Phone"
value={userDetails.Phone}
onChange={(e) =>
setUserDetails({
...userDetails,
Phone: e.target.value
})
}
required
/>
{isModal && (
<div
className="modal fade show"
id="exampleModal"
tabIndex="-1"
role="dialog"
style={{ display: "block", zIndex: 1 }}
>
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Sign up form</h5>
<span>
<span></span>
</span>
</div>
<div className="modal-body">
<form>
<div className="form-group">
<label
htmlFor="Phone"
style={{ display: "flex" }}
className="col-form-label"
>
Phone{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="tel"
className="form-control"
id="Phone"
value={userDetails.Phone}
onChange={(e) =>
setUserDetails({
...userDetails,
Phone: e.target.value
})
}
required
/>
</div>
<div className="form-group">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="col-form-label"
>
Company{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="form-control"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
required
/>
</div>
<div className="form-group">
<label
htmlFor="JobTitle"
style={{ display: "flex" }}
className="col-form-label"
>
Job Title{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="form-control"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
required
/>
</div>
<div>
<button
type="button"
className="bg-[#17a2b8] p-2 text-white rounded"
onClick={() => handleSubmitbtn()}
style={{ marginRight: 10 }}
>
Sign up
</button>
<button
type="button"
className="bg-[#6c757d] p-2 text-white rounded"
onClick={handleCloseModal}
style={{ width: 90 }}
>
Cancel
</button>
</div>
</form>
</div>
</div>
</div>
<div className="mb-3">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Company <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
required
/>
</div>
<div className="mb-3">
<label
htmlFor="JobTitle"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Job Title <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
required
/>
</div>
<div>
<button
type="button"
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
onClick={() => handleSubmitbtn()}
style={{ marginRight: 10, backgroundColor: modalSubmitBtnColor }}
>
Sign up
</button>
<button
type="button"
className="p-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
onClick={handleCloseModal}
style={{ width: 90, backgroundColor: modalCancelBtnColor }}
>
Cancel
</button>
</div>
</form>
</ModalUi>
</div>
)}
</div>
);
};
@@ -1,85 +0,0 @@
import React from "react";
import { Document, Page } from "react-pdf";
import { Stage, Layer, Rect } from "react-konva";
const RenderDebugPdf = (props) => {
return (
<div>
<div
style={{
position: "sticky",
top: 0,
padding: 10,
zIndex: 1,
backgroundColor: "white",
border: "1px solid grey",
margin: "5px 0"
}}
>
{`Co-ordinates: X - ${props.hoverCoordinates.x}, Y - ${props.hoverCoordinates.y}`}
</div>
<div
style={{
flex: 1,
position: "relative",
border: "1px solid grey",
overflow: "auto",
cursor: "crosshair"
}}
onMouseMove={props.handleMouseMoveDiv}
>
<Document
onLoadError={() => {
const load = {
status: false,
type: "failed"
};
props.setPdfLoadFail(load);
}}
loading={"Loading Document.."}
onLoadSuccess={props.pageDetails}
ref={props.pdfRef}
file={props.pdfUrl}
>
<Page
onLoadSuccess={props.handlePageLoadSuccess}
pageNumber={props.pageNumber}
renderAnnotationLayer={false}
renderTextLayer={false}
onGetAnnotationsError={(error) => {
console.log("annotation error", error);
}}
/>
</Document>
<Stage
onMouseMove={props.handleMouseMove}
onMouseDown={props.handleMouseDown}
onMouseUp={props.handleMouseUp}
width={props.pdfDimension.width}
height={props.pdfDimension.height}
style={{ position: "absolute", top: 0, left: 0 }}
>
<Layer>
{props.annotations
.filter((value) => value.page === props.pageNumber)
.map((value) => {
return (
<Rect
key={value.key}
x={value.x}
y={value.y}
width={value.width}
height={value.height}
fill="transparent"
stroke="black"
/>
);
})}
</Layer>
</Stage>
</div>
</div>
);
};
export default RenderDebugPdf;
@@ -0,0 +1,66 @@
import useFederatedComponent from "mf-cra";
import React, { Suspense, useEffect } from "react";
import { useParams } from "react-router-dom";
const MicroappModal = () => {
const { remoteApp } = useParams();
useEffect(() => {
return () => localStorage.removeItem("rowlevel");
}, []);
let paramString = remoteApp;
let queryString = new URLSearchParams(paramString);
let obj = {};
for (const [key, value] of queryString.entries()) {
// console.log("value ", key + ":" + value);
if (key === "remoteUrl") {
const url = window.location.origin + "/mfbuild/remoteEntry.js";
// console.log("atob(value) ", atob(value))
obj = { ...obj, [key]: url };
} else {
if (key === "moduleToLoad") {
obj = { ...obj, [key]: "./" + value };
} else {
obj = { ...obj, [key]: value };
}
}
}
// console.log("app ", remoteApp);
const { Component: RemoteComponent } = useFederatedComponent(obj);
// console.log("RemoteComponent ", RemoteComponent);
return (
<div className="w-full h-full flex items-center justify-center ">
<div className="w-full">
<Suspense
fallback={
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
}
>
<div
style={{
backgroundColor: "#ffffff",
width: "100%",
overflow: "hidden",
minHeight: "50vh"
}}
>
{RemoteComponent && <RemoteComponent />}
</div>
</Suspense>
</div>
</div>
);
};
export default MicroappModal;
+13 -3
View File
@@ -1,11 +1,21 @@
import React from "react";
import { Helmet } from "react-helmet";
function Title({ title, drive }) {
function Title(props) {
return (
<Helmet>
<title>{drive ? title : `${title} - OpenSign™`}</title>
<meta name="description" content={`${title} - OpenSign™`} />
<title>{`${props.title} - OpenSign™`}</title>
{/* <title>
{`${localStorage.getItem("appTitle") ? localStorage.getItem("appTitle") : ""} - ${props.title}`}
</title> */}
<meta
name="description"
content={`${props.title} - OpenSign™`}
/>
{/* <meta
name="description"
content={`${localStorage.getItem("appTitle")} - ${props.title}`}
/> */}
<link
rel="icon"
type="image/png"
@@ -0,0 +1,391 @@
import React, { useState, useEffect } from "react";
import Form from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
import Parse from "parse";
import "../styles/loader.css";
import LayoutField from "./fields/Rjsf-layout";
import TimeWidget from "./fields/TimeWidget";
import Level1Dropdown from "./fields/Level1Dropdown";
import HiddenField from "./fields/HiddenField";
import MultiSelectField from "./fields/MultiSelectField";
import TreeWidget from "../components/TreeWidget";
import { formJson } from "../json/FormJson";
const TreeEditForm = (props) => {
const widget = {
TimeWidget: TimeWidget
};
const fields = {
layout: LayoutField,
Level1Dropdown: Level1Dropdown,
HiddenField: HiddenField,
MultiSelectField: MultiSelectField,
FolderComponent: TreeWidget
};
const [schema, setschema] = useState({});
const [ui_schema, setui_schema] = useState({});
const [title, settitle] = useState("");
const [schemaState, setschemaState] = useState({});
const [formData, setformData] = useState({});
const [active, setactive] = useState(true);
const [loading, setloading] = useState(false);
const [_validate, set_validate] = useState(null);
const [noValidate, setnoValidate] = useState(false);
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const getForm = async (id) => {
setloading(true);
try {
//get json form data (schema ,uischema)
const results = formJson(id);
try {
let _record = Parse.Object.extend(results.class);
var query1 = new Parse.Query(_record);
await query1.get(props.objectId).then(
(x) => {
try {
if (x) {
let result = x.toJSON();
let new_result = result;
for (let [key, value] of Object.entries(result)) {
if (value["__type"] === "Date") {
let todayTime1 = new Date(value.iso);
var month = String(todayTime1.getMonth() + 1);
var day = String(todayTime1.getDate());
var year = String(todayTime1.getFullYear());
if (month.length < 2) month = "0" + month;
if (day.length < 2) day = "0" + day;
let date1 = year + "-" + month + "-" + day;
let bindVar = date1;
if (!results.jsonSchema.properties[key].format) {
bindVar = todayTime1.toISOString();
}
new_result[key] = bindVar;
}
}
setschemaState(results.jsonSchema);
setformData(new_result);
}
} catch (error) {
//alert(error.message);
setloading(false);
}
},
(error) => {
console.log("err", error);
setloading(false);
}
);
setschema(results.jsonSchema);
setui_schema(results.uiSchema);
set_validate(results.validFunction);
setnoValidate(results.noValidate);
settitle(results.class);
setloading(false);
setactive(true);
} catch (error) {
setloading(false);
}
} catch (e) {
setloading(false);
console.error("Problem", e);
}
};
const wrap = (s) => "{ return " + s + " };";
const dynamicValidate = (formData, errors) => {
try {
let body = atob(_validate);
let res = new Function(wrap(body))
.call(null)
.call(null, formData, errors);
return res;
} catch (error) {
console.log(error);
}
};
const handleSubmit = async ({ formData }) => {
setactive(false);
setloading(true);
let RowData = formData;
RowData &&
Object.entries(RowData).forEach(([key, value]) => {
if (typeof value === "string") {
RowData[key] = value.trim();
}
});
let _scanData = schemaState;
if (_scanData.dependencies) {
Object.keys(_scanData.dependencies).forEach((key) => {
if (_scanData.dependencies[key].oneOf) {
_scanData.dependencies[key].oneOf.forEach((val) => {
Object.keys(val.properties).forEach((k) => {
if (typeof val.properties[k] === "object") {
if (val.properties[k].format === "date") {
if (RowData[k]) {
let newdate = new Date(RowData[k]);
RowData[k] = newdate;
}
}
if (val.properties[k].component === "HtmlEditor") {
if (RowData[k]) {
let newHtml = RowData[k]
.replace(/<p[^>]*>/g, "")
.replace(/<\/p>/g, " ");
RowData[k] = newHtml;
}
}
if (val.properties[k].component === "DateTime") {
if (RowData[k]) {
let newDate11 = new Date(RowData[k]);
RowData[k] = newDate11;
}
}
if (val.properties[k].component === "CurrencyInput") {
if (val.properties[k].currencyColumn) {
RowData[`${val.properties[k].currencyColumn}`] =
val.properties[k].defaultcurrency;
}
}
if (val.properties[k].type === "string") {
if (typeof RowData[k] === "string")
RowData[k] = RowData[k].trim();
}
if (val.properties[k].data !== undefined) {
if (val.properties[k].data.isPointer) {
let pointer = undefined;
if (val.properties[k].data.class) {
if (RowData[k]) {
pointer = {
__type: "Pointer",
className: val.properties[k].data.class,
objectId: RowData[k]
};
}
} else {
if (RowData[k]) {
pointer = {
__type: "Pointer",
className: localStorage.getItem("extended_class"),
objectId: RowData[k]
};
}
}
RowData[k] = pointer;
}
if (val.properties[k].data.FolderTypeValue) {
if (RowData[k]) {
let obj = {
__type: "Pointer",
className: val.properties[k].data.ClassName,
objectId: RowData[k]
};
RowData[k] = obj;
}
}
}
}
});
});
}
});
}
let temp = [];
let _dd = _scanData.properties;
let allowed = [];
if (_dd) {
Object.keys(_dd).forEach(function (k) {
allowed.push(k);
if (RowData[k]) {
let pointer = {
__type: "Pointer",
className: _dd[k].class,
objectId: RowData[k]
};
RowData[k] = pointer;
}
if (_dd[k].format === "date") {
if (RowData[k]) {
let newdate = new Date(RowData[k]);
if (!isNaN(newdate.getTime())) {
RowData[k] = newdate;
}
}
}
if (_dd[k].component === "HtmlEditor") {
if (RowData[k]) {
let newHtml = RowData[k]
.replace(/<p[^>]*>/g, "")
.replace(/<\/p>/g, " ");
RowData[k] = newHtml;
}
}
if (_dd[k].component === "DateTime") {
if (RowData[k]) {
let newdate = new Date(RowData[k]);
if (!isNaN(newdate.getTime())) {
RowData[k] = newdate;
}
}
}
if (_dd[k].format === "date-time") {
let newdate = new Date(RowData[k]);
RowData[k] = newdate;
}
if (_dd[k].component === "CurrencyInput") {
if (_dd[k].currencyColumn) {
RowData[`${_dd[k].currencyColumn}`] = _dd[k].defaultcurrency;
}
}
if (_dd[k].data !== undefined) {
if (_dd[k].data.isPointer) {
let pointer = undefined;
if (typeof RowData[k] === "object") {
if (RowData[k]) {
if (RowData[k].objectId !== "Select") {
let obj = {
__type: "Pointer",
className: RowData[k].className,
objectId: RowData[k].objectId
};
pointer = obj;
}
}
} else if (_dd[k].data.class) {
if (RowData[k]) {
pointer = {
__type: "Pointer",
className: _dd[k].data.class,
objectId: RowData[k]
};
}
} else {
if (RowData[k]) {
pointer = {
__type: "Pointer",
className: localStorage.getItem("extended_class"),
objectId: RowData[k]
};
}
}
RowData[k] = pointer;
}
if (_dd[k].data.FolderTypeValue) {
if (RowData[k]) {
let obj = {
__type: "Pointer",
className: _dd[k].data.ClassName,
objectId: RowData[k]
};
RowData[k] = obj;
}
}
}
if (_dd[k].type === "string") {
if (typeof RowData[k] === "string") RowData[k] = RowData[k].trim();
}
});
}
const filtered = Object.keys(RowData)
.filter((key) => allowed.includes(key))
.reduce((obj, key) => {
obj[key] = RowData[key];
return obj;
}, {});
try {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
if (temp.length !== 0) {
let userPointer = {
__type: "Pointer",
className: "_User",
objectId: currentUser.id
};
filtered[[temp[0]]] = userPointer;
}
var data = Parse.Object.extend(title);
var query = new Parse.Query(data);
query.get(props.objectId).then(async (object) => {
object.save(filtered).then(
() => {
try {
alert("Record updated successfully");
props.HideView(false);
setloading(false);
setactive(true);
} catch (error) {
console.log("err", error);
}
},
(error) => {
console.log("err", error);
setloading(false);
setactive(true);
}
);
});
} catch (error) {
console.log("err", error);
alert(error.message);
setloading(false);
setactive(true);
}
};
useEffect(() => {
if (props.FormId) {
getForm(props.FormId);
}
// eslint-disable-next-line
}, [props.FormId]);
if (loading) {
return (
<div
className="loader-01"
style={{
marginTop: "50px",
marginLeft: "50%",
color: "rgb(0,28,28)",
fontSize: "35px"
}}
></div>
);
}
return (
<Form
schema={schema}
uiSchema={ui_schema}
formData={formData}
showErrorList={false}
widgets={widget}
fields={fields}
validate={noValidate && dynamicValidate}
onSubmit={handleSubmit}
validator={validator}
>
<div>
{active ? (
<button className="bg-[#1ab6ce] px-3 py-2 rounded text-white text-sm" type="submit">
Update
</button>
) : (
<button className="bg-[#1ab6ce] px-3 py-2 rounded text-white text-sm" type="submit" disabled>
Update
</button>
)}
</div>
</Form>
);
};
export default TreeEditForm;
@@ -0,0 +1,237 @@
import React, { useEffect, useState } from "react";
import Form from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
import LayoutField from "./fields/Rjsf-layout";
import Level1Dropdown from "./fields/Level1Dropdown";
import HiddenField from "./fields/HiddenField";
import MultiSelectField from "./fields/MultiSelectField";
import "../styles/loader.css";
import "../styles/form.css";
import FileUpload from "./fields/FileUpload";
import parse from "html-react-parser";
import TimeWidget from "./fields/TimeWidget";
import { formJson } from "../json/FormJson";
function TreeFormComponent(props) {
const widget = {
TimeWidget: TimeWidget
};
const fields = {
layout: LayoutField,
FileUpload: FileUpload,
Level1Dropdown: Level1Dropdown,
HiddenField: HiddenField,
MultiSelectField: MultiSelectField
};
const [link, setLink] = useState("");
const [help, setHelp] = useState("");
const toastDescription = "";
const toastColor = "#5cb85c";
const [loading, setLoding] = useState(true);
const [schemaState, setSchemaState] = useState({});
const [formData, setFormData] = useState({});
const [ui_schema, setUi_schema] = useState({});
const active = true;
console.log("new form", props);
const getForm = async (id) => {
setLoding(true);
try {
// get json from data(jsonschema, uischema)
const results = formJson(id);
// console.log("result", results);
if (results) {
let parentObject = {},
childObject = {};
const resultjson = results;
for (let [key, value] of Object.entries(
resultjson.jsonSchema.properties
)) {
if (key === props.ParentField) {
if (props.ParentValue[key]) {
parentObject = { [key]: props.ParentValue[key] };
}
} else {
if (props.ParentValue && typeof props.ParentValue === "object") {
Object.entries(props.ParentValue).forEach(([k, value]) => {
if (k === key) {
let newO = { [k]: value };
parentObject = { ...parentObject, ...newO };
}
});
}
}
if (props.ChildField) {
Object.entries(props.ChildField).forEach(([k, value]) => {
if (k === key) {
let newO = { [k]: value };
childObject = { ...childObject, ...newO };
}
});
}
if (typeof value === "object") {
for (let [k, v] of Object.entries(value)) {
if (k === "format" && v === "date") {
let today = new Date();
let date =
today.getFullYear() +
"-" +
("0" + (today.getMonth() + 1)).slice(-2) +
"-" +
("0" + today.getDate()).slice(-2);
value.default = date;
}
}
}
}
if (props.ParentField) {
setFormData({ ...parentObject, ...childObject });
}
let txt, link;
if (resultjson.help) {
if (resultjson.help.htmlbody) {
txt = resultjson.help.htmlbody;
}
if (resultjson.help.link) {
link = resultjson.help.link;
}
}
setHelp(txt);
setLink(link);
props.ClassName(resultjson.class);
props.SchemaState(resultjson.jsonSchema);
setSchemaState(resultjson.jsonSchema);
setUi_schema(resultjson.uiSchema);
setLoding(false);
} else {
alert("form not found");
setLoding(false);
}
} catch (e) {
console.error("Problem", e);
setLoding(false);
}
};
useEffect(() => {
if (!props.IsEdit) {
getForm(props.Id);
}
// console.log("props tree", props);
// eslint-disable-next-line
}, []);
let formView = (
<React.Fragment>
<Form
schema={schemaState}
uiSchema={ui_schema}
showErrorList={false}
widgets={widget}
fields={fields}
formData={formData}
onSubmit={props.handleSubmit}
validator={validator}
>
{active ? (
<button className="btn btn-info submiBtn" type="submit">
{props.IsEdit ? "update folder" : "create folder"}
</button>
) : (
<button className="btn btn-info submiBtn " type="submit" disabled>
submitting...
</button>
)}
</Form>
</React.Fragment>
);
if (loading) {
formView = (
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
);
}
return (
<React.Fragment>
<div className="row">
<div className="col-md-12">
<div className="card">
<div className="card-body no-padding height-9">
{help ? (
<div className="dropdown" style={{ marginTop: "-30px" }}>
<i
className="far fa-question-circle dropdown-toggle hovereffect"
aria-hidden="true"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
style={{
fontSize: "18px",
color: "purple",
cursor: "pointer !important",
position: "relative",
top: "40px",
left: "98%"
}}
></i>
<div
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
style={{
marginleft: "-121px",
margintop: "-14px",
position: "absolute",
padding: "10px",
width: "300px",
top: "102px!important"
}}
>
{parse(`
${help}
`)}
<br />
{link ? (
<a
onClick={(e) => {
e.preventDefault();
window.location.href = link;
}}
target="_blank"
className="btn btn-xs btn-primary"
>
Read more..
</a>
) : null}
</div>
</div>
) : null}
<div style={{ fontSize: "13px" }}>{formView}</div>
</div>
</div>
</div>
</div>
<div id="snackbar" style={{ backgroundColor: toastColor }}>
{toastDescription}
</div>
</React.Fragment>
);
}
export default TreeFormComponent;
+895
View File
@@ -0,0 +1,895 @@
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import LabelField from "./fields/LabelField";
import Parse from "parse";
import axios from "axios";
import "../styles/spinner.css";
import TreeFormComponent from "./TreeFormComponent";
import TreeEditForm from "./TreeEditForm";
import "../styles/modal.css";
import Modal from "react-modal";
const TreeWidget = (props) => {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [isAddField, setIsAddField] = useState(false);
const [folderList, setFolderList] = useState([]);
const [tabList, setTabList] = useState([]);
const [appId, setAppId] = useState(undefined);
const [selectedFolder, setSelectedFolder] = useState(undefined);
const [loader, setLoader] = useState(false);
const [className, setClassName] = useState("");
const [schemaState, setSchemaState] = useState({});
const [TabURL, setTabURL] = useState("");
const [editable, setEditable] = useState(false);
const [editId, setEditId] = useState("");
const [defaultState, setDefaultState] = useState(false);
const [isShowModal, setIsShowModal] = useState(false);
const selectFolderHandle = async () => {
setIsShowModal(true);
setIsAddField(false);
setDefaultState(false);
setFolderList([]);
setLoader(true);
if (tabList.length > 0) {
let len = tabList.length - 1;
selectedItemList(tabList[len]);
} else {
try {
var test;
if (props.schema.parent) {
try {
if (props.parent[props.schema.parent]) {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
let res;
if (localStorage.getItem("Extand_Class")) {
let data = JSON.parse(localStorage.getItem("Extand_Class"));
res = data[0];
} else {
res = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
if (res) res = res.toJSON();
}
if (res) {
let json = res;
setAppId(json.AppId);
}
// eslint-disable-next-line
let reg = /(\#.*?\#)/gi;
let str = props.schema.data.Query;
if (str.includes("#")) {
test = str.replace(reg, props.parent[props.schema.parent]);
} else {
test = str;
}
} else {
if (props.child[props.schema.parent]) {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
let res;
if (localStorage.getItem("Extand_Class")) {
let data = JSON.parse(localStorage.getItem("Extand_Class"));
res = data[0];
} else {
res = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
if (res) res = res.toJSON();
}
if (res) {
let json = res;
setAppId(json.AppId);
}
// eslint-disable-next-line
let reg = /(\#.*?\#)/gi;
let str = props.schema.data.Query;
if (str.includes("#")) {
test = str.replace(reg, props.child[props.schema.parent]);
} else {
test = str;
}
} else {
alert(`Please select ${props.schema.parent}`);
setLoader(false);
return;
}
}
} catch (error) {
console.log("error ", error);
}
} else {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
// eslint-disable-next-line
let reg = /(\#.*?\#)/gi;
let str = props.schema.data.Query;
let res;
if (localStorage.getItem("Extand_Class")) {
let data = JSON.parse(localStorage.getItem("Extand_Class"));
res = data[0];
} else {
// emp = Parse.Object.extend(localStorage.getItem("extended_class"));
// q = new Parse.Query(emp);
// q.equalTo("UserId", currentUser);
// res = await q.first();
// if (res) res = res.toJSON();
const currentUser = Parse.User.current();
res = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
if (res) res = res.toJSON();
}
if (res) {
let json = res;
setAppId(json.AppId);
}
if (str.includes("#")) {
if (res) {
let json = res.toJSON();
let output = str.match(reg);
if (output.length === 1) {
output = output.join();
output = output.substring(1, output.length - 1);
output = output.split(".");
if (output.length === 1) {
let out = output[0];
if (json[out]) {
if (typeof json[out] === "object") {
test = str.replace(reg, JSON.stringify(json[out]));
} else {
test = str.replace(reg, json[out]);
}
} else {
test = str.replace(reg, currentUser.id);
}
} else if (output.length === 2) {
let out1 = json[output[0]][output[1]];
if (out1) {
test = str.replace(reg, out1);
}
}
}
}
} else {
test = str;
}
}
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?${test}`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
await axios
.get(url, { headers: headers })
.then((res) => {
if (res) {
setFolderList(res.data.results);
setLoader(false);
}
})
.catch((err) => {
setLoader(false);
console.log(err);
});
} catch (error) {
setLoader(false);
}
}
};
const selectedItemList = async (folder) => {
setLoader(true);
setFolderList([]);
try {
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?where={"${props.schema.data.ParentFolderField}":{"__type":"Pointer","className":"${props.schema.data.ClassName}","objectId":"${folder.objectId}"}}`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
await axios.get(url, { headers: headers }).then((result) => {
if (result) {
setFolderList(result.data.results);
setLoader(false);
}
});
} catch (error) {
setLoader(false);
}
};
const removeTabListItem = async (e, i) => {
e.preventDefault();
setEditable(false);
setLoader(true);
setFolderList([]);
let list = tabList.filter((itm, j) => {
if (j <= i) {
return itm;
}
});
setTabList(list);
setIsAddField(false);
try {
let _len = list.length - 1;
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?where={"${props.schema.data.ParentFolderField}":{"__type":"Pointer","className":"${props.schema.data.ClassName}","objectId":"${list[_len].objectId}"}}`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
await axios.get(url, { headers: headers }).then((result) => {
if (result) {
setFolderList(result.data.results);
setLoader(false);
}
setLoader(false);
});
} catch (error) {
setLoader(false);
}
};
const onDeleteFolder = async (e) => {
e.preventDefault();
if (folderList.length > 0) {
alert(
"Folder should not delete. To delete first delete sub items in it."
);
} else if (window.confirm(`Are you sure you want to delete this record`)) {
try {
setLoader(true);
let len = tabList.length - 1;
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
var deleted = Parse.Object.extend(props.schema.data.ClassName);
var query = new Parse.Query(deleted);
await query.get(tabList[len].objectId).then(
(del) => {
del.destroy().then(
(y) => {
if (y) {
setLoader(false);
let list = tabList.filter((itm, j) => {
if (j !== len) return itm;
});
setTabList(list);
if (list.length > 0) {
let len = list.length - 1;
selectedItemList(list[len]);
} else {
selectFolderHandle();
}
alert("Folder deleted successfully.");
}
},
(error) => {
alert(error.message);
setLoader(false);
}
);
},
(error) => {
console.log("error ", error);
// The object was not retrieved successfully.
setLoader(false);
}
);
} catch (error) {
setLoader(false);
console.log(error);
}
}
};
const onSubmitResult = async (e) => {
setIsShowModal(false);
e.preventDefault();
//console.log("tabList ", tabList);
if (tabList.length > 0) {
try {
let len = tabList.length - 1;
setSelectedFolder({
Topic: tabList[len][`${props.schema.data.FolderNameField}`],
description: tabList[len][`${props.schema.data.FolderDescription}`]
});
props.onChange(tabList[len]["objectId"]);
let url = "Folders";
tabList.forEach((t) => {
url = url + " / " + t[`${props.schema.data.FolderNameField}`];
});
setTabURL(url);
setIsAddField(false);
if (!props.formData) {
setTabList([]);
}
} catch (error) {
setIsAddField(false);
alert(error.message);
}
} else {
setIsAddField(false);
}
};
const editFolderDisplay = async (formData) => {
try {
let objectId = formData.objectId;
let className = formData.className;
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const fldr = Parse.Object.extend(className);
let query = new Parse.Query(fldr);
query.equalTo("objectId", objectId);
const result = await query.first();
let resultJson = result.toJSON();
let CustomTabList = [];
if (resultJson) {
CustomTabList.push(resultJson);
if (resultJson[`${props.schema.data.ParentFolderField}`]) {
let tabsRes = resultJson[`${props.schema.data.ParentFolderField}`];
let looping = false;
do {
let fldttr = Parse.Object.extend(tabsRes.className);
let query2 = new Parse.Query(fldttr);
query2.equalTo("objectId", tabsRes.objectId);
let custome_res = await query2.first();
if (custome_res) {
let tabsData = custome_res.toJSON();
CustomTabList.push(tabsData);
if (tabsData[`${props.schema.data.ParentFolderField}`]) {
tabsRes = tabsData[`${props.schema.data.ParentFolderField}`];
looping = true;
} else {
looping = false;
}
}
} while (looping);
}
let url = "Folders";
CustomTabList.length > 0 &&
CustomTabList.reverse().forEach(
(t) => (url = url + " / " + t[props.schema.data.FolderNameField])
);
setTabURL(url);
setSelectedFolder({
Topic: resultJson[`${props.schema.data.FolderNameField}`],
description: resultJson[`${props.schema.data.FolderDescription}`]
});
setTabList(CustomTabList);
props.onChange(resultJson.objectId);
}
} catch (error) {
console.log(error);
}
};
const handleSubmit = async ({ formData }) => {
setLoader(true);
try {
let RowData = formData;
let _scanData = schemaState;
Object.keys(_scanData).forEach(function (key) {
let _dd = _scanData[key];
Object.keys(_dd).forEach(function (k) {
if (_dd[k].component === "AutoSuggest" && _dd[k].isPointer) {
let pointer = {
__type: "Pointer",
className: _dd[k].class,
objectId: RowData[k]
};
RowData[k] = pointer;
}
if (_dd[k].format === "date") {
let newdate = new Date(RowData[k]);
RowData[k] = newdate;
}
if (_dd[k].component === "DateTime") {
let newDate11;
if (!RowData[k]) {
newDate11 = new Date();
} else {
newDate11 = new Date(RowData[k]);
}
RowData[k] = newDate11;
}
if (_dd[k].component === "CurrencyInput") {
if (_dd[k].currencyColumn) {
RowData[`${_dd[k].currencyColumn}`] = _dd[k].defaultcurrency;
}
}
if (_dd[k].data !== undefined) {
if (_dd[k].data.isPointer) {
let pointer = {};
if (RowData[k] && RowData[k] !== "Select") {
if (_dd[k].data.class) {
if (RowData[k]) {
pointer = {
__type: "Pointer",
className: _dd[k].data.class,
objectId: RowData[k]
};
}
} else {
pointer = {
__type: "Pointer",
className: localStorage.getItem("extended_class"),
objectId: RowData[k]
};
}
RowData[k] = pointer;
}
}
if (_dd[k].data.FolderTypeValue) {
let obj = {
__type: "Pointer",
className: _dd[k].data.ClassName,
objectId: RowData[k]
};
RowData[k] = obj;
}
}
if (_dd[k].type === "string") {
let d = RowData[k];
if (typeof d === "string") {
RowData[k] = d.trim();
}
}
});
});
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
var forms = Parse.Object.extend(className);
var folder = new forms();
folder.set(
props.schema.data.FolderNameField,
formData[props.schema.data.FolderNameField]
);
if (props.schema.data.FolderDescription) {
folder.set(
props.schema.data.FolderDescription,
formData[props.schema.data.FolderDescription]
);
}
folder.set("AppId", appId);
folder.set(
props.schema.data.FolderTypeField,
props.schema.data.FolderTypeValue
);
const currentUser = Parse.User.current();
folder.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
if (tabList.length > 0) {
let len = tabList.length - 1;
folder.set(props.schema.data.ParentFolderField, {
__type: "Pointer",
className: props.schema.data.ClassName,
objectId: tabList[len]["objectId"]
});
}
folder.save(RowData).then(
(result) => {
RowData = {};
if (result) {
setIsAddField(false);
setLoader(false);
if (tabList.length > 0) {
let len = tabList.length - 1;
selectedItemList(tabList[len]);
} else {
selectFolderHandle();
}
}
},
(error) => {
console.log("error ", error);
setLoader(false);
}
);
} catch (error) {
setLoader(false);
}
};
useEffect(() => {
if (props.formData) {
editFolderDisplay(props.formData);
}
// eslint-disable-next-line
}, [props.formData]);
useEffect(() => {
if (defaultState) {
selectFolderHandle();
}
// eslint-disable-next-line
}, [defaultState]);
const customStyles = {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
backgroundColor: "white",
padding: 0
},
overlay: {
width: "100%",
backgroundColor: "rgba(0, 0, 0, 0.75)",
zIndex: 50
}
};
return (
<React.Fragment>
<LabelField
Title={props.schema.title}
Name={props.name}
Required={props.required}
HelpBody={props.schema.helpbody}
HelpLink={props.schema.helplink}
/>
<br />
<div className="block max-w-sm bg-white border border-gray-200 shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
<div
style={{
display: "flex",
color: "black",
fontWeight: "bold",
fontSize: "13px",
padding: "2%",
alignItems: "center"
}}
>
<div style={{ width: "20%" }}>
<div
className="pull-left"
title="Select Folder"
style={{
color: "#33bbff",
margin: "auto",
padding: "10px 10px",
textAlign: "center"
}}
>
<i
className="far fa-folder-open"
style={{ fontSize: "40px" }}
aria-hidden="true"
></i>
</div>
</div>
<div style={{ margin: "0px 10px 0 20px", width: "80%" }}>
<div>
{selectedFolder && selectedFolder.Topic
? selectedFolder.Topic
: "Root"}
<i
className="fa fa-pencil"
title="Select Folder"
style={{ fontSize: 17, cursor: "pointer", marginLeft: 12 }}
onClick={selectFolderHandle}
></i>
{selectedFolder && (
<a
className="pull-right"
onClick={() => {
setSelectedFolder(undefined);
setTabURL(undefined);
props.onChange(undefined);
}}
>
<i
className="fa fa-times-circle-o"
aria-hidden="true"
style={{ fontSize: 17 }}
></i>
</a>
)}
</div>
{TabURL && (
<div
style={{ fontSize: "10px", color: "gray", fontWeight: "bold" }}
>
({TabURL})
</div>
)}
</div>
</div>
</div>
{/* save document in folder and add new folder modal */}
<Modal
isOpen={isShowModal}
onRequestClose={() => setIsShowModal(false)}
shouldCloseOnOverlayClick={false}
contentLabel="Modal"
style={customStyles}
>
<div className="w-full min-w-[300px] md:min-w-[500px]">
{/* header */}
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
backgroundColor: "rgba(203, 200, 200, 0.09)"
}}
>
<div style={{ padding: "10px" }}>
<h4 className=" text-black">Select Folder</h4>
</div>
<div style={{ padding: "10px" }}>
<button
className="btn btn-sm pull-right"
data-dismiss="modal"
title="Select Folder"
style={{
width: "auto",
backgroundColor: "white",
border: "1px solid gray",
boxshadow: "rgba(100, 100, 111, 0.2) 0px 7px 29px 0px"
}}
onClick={(e) => {
e.preventDefault();
setIsAddField(false);
setFolderList([]);
setEditable(false);
if (!props.formData) {
setTabList([]);
}
setIsShowModal(false);
}}
>
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
<hr />
{/* body */}
<div className="p-3 space-y-6">
<div
style={{
color: "#ac4848",
cursor: "pointer",
fontSize: "14px",
fontWeight: "500"
}}
>
<a
onClick={() => {
setTabList([]);
setIsAddField(false);
setDefaultState(true);
setEditable(false);
}}
>
Folders
</a>
{" / "}
{tabList &&
tabList.map((tab, i) => (
<React.Fragment key={`${tab.objectId}-${i}`}>
<a
title={tab[`${props.schema.data.FolderNameField}`]}
onClick={(e) => removeTabListItem(e, i)}
>
{tab[`${props.schema.data.FolderNameField}`]}
</a>
{" / "}
</React.Fragment>
))}
<hr />
</div>
{/* {editable && (
<TreeEditForm
FormId={props.schema.data.FormId}
objectId={editId}
HideView={(cl) => {
setEditable(cl);
selectFolderHandle();
}}
/>
)} */}
{isAddField && !loader && !editable && (
<TreeFormComponent
Id={props.schema.data.FormId}
handleSubmit={handleSubmit}
ParentField={props.schema.parent && props.schema.parent}
ParentValue={props.schema.parent && props.parent}
ChildField={props.child && props.child}
ClassName={(cls) => setClassName(cls)}
SchemaState={(cls) => setSchemaState(cls)}
/>
)}
{loader && (
<div
className="loader-01"
style={{
marginTop: "50px",
marginLeft: "50%",
color: "rgb(0,28,28)",
fontSize: "35px"
}}
></div>
)}
{!isAddField && !editable && (
<div
style={{
maxHeight: "280px",
overflowY: "auto",
marginTop: "5px"
}}
>
<ul>
{folderList &&
folderList.map(
(fldr) =>
fldr[props.schema.data.FolderTypeField] ===
props.schema.data.FolderTypeValue && (
<li
key={`${fldr.objectId}`}
style={{
listStyle: "none",
padding: "10px 20px",
textTransform: "capitalize",
fontWeight: 300,
border: "1px solid gray",
borderRadius: "1px",
marginTop: "8px",
transition: "all 0.75s ease",
WebkitTransition: "all 0.5s ease",
MozTransition: "all 0.5s ease",
MsTransition: "all 0.5s ease",
OTransition: "all 0.5 ease",
display: "flex",
alignItems: "center",
justifyContent: "space-between"
}}
>
<div
style={{
display: "flex",
alignItems: "center"
}}
>
<i
className={"fa fa-folder"}
style={{
fontSize: "1.4rem",
color: "skyblue"
}}
aria-hidden="true"
></i>
<a
style={{
fontWeight: "bold",
fontSize: "12px",
marginLeft: "4px",
cursor: "pointer"
}}
onClick={() => {
setTabList((tabs) => tabs.concat(fldr));
selectedItemList(fldr);
}}
>
{fldr[`${props.schema.data.FolderNameField}`]}
</a>
</div>
{/* {fldr[props.schema.data.FolderTypeField] ===
props.schema.data.FolderTypeValue && (
<a
className="float-right"
onClick={() => {
setEditable(true);
setEditId(fldr.objectId);
}}
>
<i
className={"fa fa-pencil"}
title={"Edit Folder"}
style={{
fontSize: "0.9rem",
color: "purple",
cursor: "pointer"
}}
aria-hidden="true"
></i>
</a>
)} */}
</li>
)
)}
</ul>
</div>
)}
</div>
{/* footer */}
<div className="flex items-center p-3 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600 justify-between">
<button
className="btn btn-sm float-left createFolder"
title="Create New Folder"
onClick={(e) => {
e.preventDefault();
setIsAddField(true);
}}
>
{" "}
<i
className="fa fa-plus"
style={{ fontSize: "1.5rem" }}
aria-hidden="true"
></i>
</button>
{!isAddField && !editable && (
<div className="flex">
{tabList.length > 0 && (
<button
className="btn btn-sm float-right deleteFolder"
title="Delete Folder"
onClick={onDeleteFolder}
>
{" "}
<i
className="fa fa-trash"
style={{ fontSize: "1rem" }}
aria-hidden="true"
></i>
</button>
)}
<button
className="btn btn-sm float-right saveFolder"
title="Save Here"
onClick={onSubmitResult}
>
{" "}
{/* <i
className="fa fa-floppy-o"
style={{ fontSize: "2rem" }}
aria-hidden="true"
></i> */}
<i
className="fas fa-save"
aria-hidden="true"
style={{ fontSize: "20px" }}
></i>
</button>
</div>
)}
</div>
</div>
</Modal>
</React.Fragment>
);
};
const mapStateToProps = (state) => {
return {
parent: state.Level1_Dropdown,
child: state.Level2_Dropdown
};
};
export default connect(mapStateToProps)(TreeWidget);
@@ -1,47 +0,0 @@
import React from "react";
import "../../styles/loader.css";
import { useNavigate } from "react-router-dom";
import { openInNewTab } from "../../constant/Utils";
const DashboardButton = (props) => {
const navigate = useNavigate();
function openReport() {
if (props.Data && props.Data.Redirect_type) {
const Redirect_type = props.Data.Redirect_type;
const id = props.Data.Redirect_id;
if (Redirect_type === "Form") {
navigate(`/form/${id}`);
} else if (Redirect_type === "Report") {
navigate(`/report/${id}`);
} else if (Redirect_type === "Url") {
openInNewTab(id);
}
}
}
return (
<div
onClick={() => openReport()}
className={`${
props.Data && props.Data.Redirect_type
? "cursor-pointer"
: "cursor-default"
} w-full px-3 py-2 flex text-white rounded-md shadow overflow-hidden`}
>
<div className="flex items-center justify-start gap-5">
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
<i
className={`${
props.Icon ? props.Icon : "fa fa-solid fa-info"
} text-[25px] lg:text-[30px]`}
></i>
</span>
<div className="">
<div className="text-base lg:text-lg text-black"> {props.Label}</div>
</div>
</div>
</div>
);
};
export default DashboardButton;
@@ -4,7 +4,6 @@ import Parse from "parse";
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
import "../../styles/loader.css";
import { useNavigate } from "react-router-dom";
import Tooltip from "../../primitives/Tooltip";
const DashboardCard = (props) => {
const navigate = useNavigate();
@@ -25,6 +24,9 @@ const DashboardCard = (props) => {
};
let body = {};
let currentUser;
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
let currentUser1 = Parse.User.current();
currentUser = currentUser1.id;
let res;
@@ -80,6 +82,8 @@ const DashboardCard = (props) => {
} else {
setLoading(true);
try {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
let reg1 = /(\#.*?\#)/gi; // eslint-disable-line
let _query = props.Data.query;
@@ -149,7 +153,7 @@ const DashboardCard = (props) => {
}
})
.then((res) => {
const listData = res.data?.result?.filter(
const listData = res.data?.result.filter(
(x) => x.Signers.length > 0
);
let arr = [];
@@ -219,6 +223,8 @@ const DashboardCard = (props) => {
if (restr.includes("#")) {
try {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
let res;
if (localStorage.getItem("Extand_Class")) {
@@ -336,7 +342,7 @@ const DashboardCard = (props) => {
props.Data && props.Data.Redirect_type
? "cursor-pointer"
: "cursor-default"
} w-full h-[140px] px-3 pt-4 pb-10 text-white rounded-md shadow overflow-hidden relative`}
} w-full h-[140px] px-3 pt-4 pb-10 text-white rounded-md shadow overflow-hidden`}
>
<div className="flex items-center justify-start gap-5">
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
@@ -353,9 +359,6 @@ const DashboardCard = (props) => {
</div>
</div>
</div>
<div className="text-xs absolute top-2 right-1">
<Tooltip id={props.Label} iconColor={"white"} message={props?.Data?.tourMessage} />
</div>
</div>
);
};
@@ -45,16 +45,18 @@ function DashboardReport(props) {
setActions(json.actions);
setReportName(json.reportName);
setHeading(json.heading);
Parse.serverURL = localStorage.getItem("BaseUrl12");
Parse.initialize(localStorage.getItem("AppID12"));
const currentUser = Parse.User.current().id;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Application-Id": localStorage.getItem("AppID12"),
sessiontoken: localStorage.getItem("accesstoken")
};
try {
const params = { reportId: id, skip: skipUserRecord, limit: limit };
const url = `${localStorage.getItem("baseUrl")}/functions/getReport`;
const url = `${localStorage.getItem("BaseUrl12")}/functions/getReport`;
const res = await axios.post(url, params, {
headers: headers,
signal: abortController.signal // is used to cancel fetch query
@@ -97,13 +99,11 @@ function DashboardReport(props) {
setIsMoreDocs(false);
}
setIsNextRecord(false);
if (!res.data.result.error) {
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.result]
: res.data.result
);
}
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.result]
: res.data.result
);
}
setIsLoader(false);
} catch (err) {
@@ -112,7 +112,6 @@ function DashboardReport(props) {
console.log("err ", err);
setIsLoader(false);
}
setIsLoader(false);
}
} else {
setIsLoader(false);
@@ -1,71 +1,21 @@
import React, { Suspense, lazy } from "react";
const DashboardButton = lazy(() => import("./DashboardButton"));
import ErrorBoundary from "../ErrorBoundary";
const DashboardCard = lazy(() => import("./DashboardCard"));
const DashboardReport = lazy(() => import("./DashboardReport"));
const buttonList = [
{
label: "Sign yourself",
redirectId: "sHAnZphf69",
redirectType: "Form",
icon: "fas fa-pen-nib"
},
{
label: "Request signature",
redirectId: "8mZzFxbG1z",
redirectType: "Form",
icon: "fa-solid fa-paper-plane"
}
];
const GetDashboard = (props) => {
const Button = ({ label, redirectId, redirectType, icon }) => (
<div className={"bg-white rounded-md shadow w-full"}>
<Suspense
fallback={
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
}
>
<DashboardButton
Icon={icon}
Label={label}
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
/>
</Suspense>
</div>
);
const renderSwitchWithTour = (col) => {
switch (col.widget.type) {
case "Card":
return (
<div
className={"bg-[#2ed8b6] rounded-md shadow mb-3 md:mb-0"}
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"}
data-tut={col.widget.data.tourSection}
style={{ background: col.widget.bgColor }}
>
<Suspense
fallback={
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
}
>
<Suspense fallback={<div>please wait</div>}>
<DashboardCard
Icon={col.widget.icon}
Label={col.widget.label}
@@ -81,7 +31,7 @@ const GetDashboard = (props) => {
return (
<div data-tut={col.widget.data.tourSection}>
<Suspense fallback={<div>please wait</div>}>
<div className="mb-3 md:mb-0">
<div className="mb-4 md:mb-0">
<DashboardReport Record={col.widget} />
</div>
</Suspense>
@@ -97,7 +47,7 @@ const GetDashboard = (props) => {
case "Card":
return (
<div
className={"bg-[#2ed8b6] rounded-md shadow mb-3 md:mb-0"}
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"}
style={{ background: col.widget.bgColor }}
>
<Suspense fallback={<div>please wait</div>}>
@@ -115,7 +65,7 @@ const GetDashboard = (props) => {
case "report": {
return (
<Suspense fallback={<div>please wait</div>}>
<div className="mb-3 md:mb-0">
<div className="mb-4 md:mb-0">
<DashboardReport Record={col.widget} />
</div>
</Suspense>
@@ -126,39 +76,25 @@ const GetDashboard = (props) => {
}
};
return (
<div>
<div className="mb-3">
<div
data-tut={"tourbutton"}
className="flex flex-col md:flex-row gap-6 md:gap-8"
>
{buttonList.map((btn) => (
<Button
key={btn.label}
label={btn.label}
redirectType={btn.redirectType}
redirectId={btn.redirectId}
icon={btn.icon}
/>
))}
</div>
<ErrorBoundary>
<div>
{props.dashboard.map((val, key) => (
<div key={"a" + key} className="row">
{val.columns.map((col, i) =>
col.widget.data && col.widget.data.tourSection ? (
<div key={i} className={props.classnameArray[key][i]}>
{renderSwitchWithTour(col)}
</div>
) : (
<div key={i} className={props.classnameArray[key][i]}>
{renderSwitch(col)}
</div>
)
)}
</div>
))}
</div>
{props.dashboard.map((val, key) => (
<div key={"a" + key} className="row">
{val.columns.map((col, i) =>
col.widget.data && col.widget.data.tourSection ? (
<div key={i} className={props.classnameArray[key][i]}>
{renderSwitchWithTour(col)}
</div>
) : (
<div key={i} className={props.classnameArray[key][i]}>
{renderSwitch(col)}
</div>
)
)}
</div>
))}
</div>
</ErrorBoundary>
);
};
@@ -1,5 +1,5 @@
import React, { useCallback, useMemo } from "react";
import { useDropScript } from "../../../hook/useScript";
import { useDropScript } from "../../hook/useScript";
const DROPBOX_APP_KEY = process.env.REACT_APP_DROPBOX_API_KEY; // App key
const DROPBOX_SDK_URL = "https://www.dropbox.com/static/api/2/dropins.js";
@@ -41,15 +41,11 @@ export default function DropboxChooser({ children, onSuccess, onCancel }) {
[onSuccess, onCancel]
);
const handleChoose = useCallback(
(e) => {
e.preventDefault();
if (window.Dropbox) {
window.Dropbox.choose(options);
}
},
[options]
);
const handleChoose = useCallback(() => {
if (window.Dropbox) {
window.Dropbox.choose(options);
}
}, [options]);
return (
<div onClick={handleChoose}>
@@ -0,0 +1,308 @@
import React, { useState, useEffect } from "react";
import { SaveFileSize } from "../../constant/saveFileSize";
import Parse from "parse";
import sanitizeFileName from "../../primitives/sanitizeFileName";
import DropboxChooser from "./DropboxChoose";
const FileUpload = (props) => {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [_fileupload, setFileUpload] = useState("");
const [fileload, setfileload] = useState(false);
const [Message] = useState(false);
const [percentage, setpercentage] = useState(0);
const REQUIRED_FIELD_SYMBOL = "*";
useEffect(() => {
if (!props.formData) {
setFileUpload([]);
props.onChange(undefined);
}
// eslint-disable-next-line
}, [props.formData]);
const onChange = (e) => {
try {
let files = e.target.files;
if (typeof files[0] !== "undefined") {
if (props.schema.filetypes && props.schema.filetypes.length > 0) {
var fileName = files[0].name;
var fileNameExt = fileName
.substr(fileName.lastIndexOf(".") + 1)
.toLowerCase();
let Extensions = props.schema.filetypes.map((x) => x.toLowerCase());
let arr = Extensions.filter((x) => x === fileNameExt);
if (arr.length > 0) {
console.log("multiple type");
} else {
alert(
"Only these file types are accepted : " + Extensions.join(", ")
);
}
}
if (props.schema.maxfilesizeKB && props.schema.maxfilesizeKB !== "") {
// console.log(Math.round(files[0].size / 1024));
if (
Math.round(Number(files[0].size) / 1024) >=
props.schema.maxfilesizeKB
) {
alert(
`The selected file size is too large. Please select a file less than ${Math.round(
props.schema.maxfilesizeKB / 1024
)} MB`
);
return;
}
}
handleFileUpload(files[0]);
} else {
alert("Please select file.");
return false;
}
} catch (error) {
alert(error.message);
return false;
}
};
const handleFileUpload = async (file) => {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
setfileload(true);
const size = file.size;
const fileName = file.name;
const name = sanitizeFileName(fileName);
const pdfFile = file;
const parseFile = new Parse.File(name, pdfFile);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
// console.log("percentCompleted ", percentCompleted);
setpercentage(percentCompleted);
}
}
});
setFileUpload(response.url());
props.onChange(response.url());
setfileload(false);
// The response object will contain information about the uploaded file
// console.log("File uploaded:", response);
// You can access the URL of the uploaded file using response.url()
// console.log("File URL:", response.url());
if (response.url()) {
SaveFileSize(size, response.url());
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
};
const dropboxSuccess = async (files) => {
// console.log("file ", files);
setfileload(true);
const file = files[0];
const url = file.link;
const size = file.bytes;
const mb = Math.round(file.bytes / Math.pow(1024, 2));
if (mb > 10) {
setTimeout(() => {
alert(
`The selected file size is too large. Please select a file less than 10 MB`
);
}, 500);
return;
} else {
const name = sanitizeFileName(file.name);
const parseFile = new Parse.File(name, { uri: url });
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
// console.log("percentCompleted ", percentCompleted);
setpercentage(percentCompleted);
}
}
});
// console.log("response.url() ", response.url());
setFileUpload(response.url());
props.onChange(response.url());
setfileload(false);
if (response.url()) {
SaveFileSize(size, response.url());
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
}
};
const dropboxCancel = async () => {
console.log("cancel clicked ");
};
let fileView =
props.formData &&
props.schema.uploadtype === "s3viajw" ? null : props.formData &&
props.formData ? (
<React.Fragment>
<a
href={props.formData}
title={props.formData}
style={{ paddingBottom: "10px", color: "blue" }}
>
Download
</a>
</React.Fragment>
) : fileload === false ? null : (
<React.Fragment>
<a
href={_fileupload}
title={_fileupload}
style={{ paddingBottom: "10px" }}
>
Download
</a>
<br />
</React.Fragment>
);
return (
<React.Fragment>
<div style={{ display: "inline-block" }}>
<label htmlFor={props.name}>
{props.schema.title}
{props.required && (
<span className="required">{REQUIRED_FIELD_SYMBOL}</span>
)}
{fileload ? (
<div className="flex items-center gap-x-2">
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
<div
className="h-2 rounded-full bg-blue-500"
style={{ width: `${percentage}%` }}
></div>
</div>
<span className="text-black text-sm">{percentage}%</span>
</div>
) : (
Message && (
<span style={{ fontSize: "9px", marginLeft: "4px" }}>
Processing will take 5-10 mins....
</span>
)
)}
{props.schema.helpbody ? (
<div className="dropdown pull-right">
<i
className="far fa-question-circle dropdown-toggle hovereffect"
aria-hidden="true"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
style={{
fontSize: "12px",
color: "purple",
cursor: "pointer !important",
position: "relative",
bottom: "0px",
left: "0px",
paddingBottom: "4px",
paddingLeft: "4px"
}}
></i>
<div
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
style={{
marginleft: "-121px",
margintop: "-14px",
position: "absolute",
padding: "10px",
top: "102px!important"
}}
>
<br />
{props.schema.helplink ? (
<a
href={props.schema.helplink}
target="_blank"
rel="noreferrer"
className="btn btn-xs btn-primary"
>
Read more..
</a>
) : null}
</div>
</div>
) : null}
<span style={{ marginLeft: "10px" }}>{fileView}</span>
</label>
</div>
<>
{props.formData ? (
<div className="flex gap-2 justify-center items-center">
<div className="flex justify-between items-center px-2 py-[3px] w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
<div className="break-all">
file selected : {props.formData.split("/")[3]}
</div>
<div
onClick={() => {
console.log("clicked");
setFileUpload([]);
props.onChange(undefined);
}}
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
>
<i className="fa-solid fa-xmark"></i>
</div>
</div>
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
</div>
) : (
<div className="flex gap-2 justify-center items-center">
<input
type="file"
id="hashfile"
style={{
border: "1px solid #ccc",
color: "gray",
backgroundColor: "white",
padding: "5px 10px",
borderRadius: "4px",
fontSize: "13px",
width: "100%",
fontWeight: "bold"
}}
accept="application/pdf,application/vnd.ms-excel"
onChange={onChange}
/>
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
</div>
)}
</>
</React.Fragment>
);
};
export default FileUpload;
@@ -0,0 +1,98 @@
import React, { Component } from "react";
import Parse from "parse";
// import axios from "axios";
export class HiddenField extends Component {
state = {
baseUrl: localStorage.getItem("baseUrl"),
parseAppId: localStorage.getItem("parseAppId"),
_fields: ""
};
loadData = async () => {
if (this.props.schema.default) {
this.setState({ _fields: this.props.schema.default });
} else {
try {
Parse.serverURL = this.state.baseUrl;
Parse.initialize(this.state.parseAppId);
let currentUser = await Parse.User.current();
if (localStorage.getItem("Extand_Class")) {
let _rdata = JSON.parse(localStorage.getItem("Extand_Class"));
let custome_Filed = "";
let splitArray = this.props.schema.data.valueKey.split(".");
if (splitArray.length > 1) {
custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
} else {
let _Filed = _rdata[0][splitArray[0]];
if (typeof _Filed === "object") {
custome_Filed = {
__type: "Pointer",
className: _Filed["className"],
objectId: _Filed["objectId"]
};
} else {
custome_Filed = _rdata[0][splitArray[0]];
}
}
this.setState({ _fields: custome_Filed });
} else {
if (currentUser) {
// let url = `${this.state.baseUrl}classes/${localStorage.getItem(
// "extended_class"
// )}?where={"UserId":{"__type":"Pointer","className":"_User","objectId":"${
// currentUser.id
// }"}}&keys=${this.props.schema.data.valueKey}`;
// const headers = {
// "Content-Type": "application/json",
// "X-Parse-Application-Id": this.state.parseAppId
// };
// await axios.get(url, { headers: headers }).then((res) => {
// let custome_Filed = "";
// let _rdata = res.data.results;
// let splitArray = this.props.schema.data.valueKey.split(".");
// if (splitArray.length > 1) {
// custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
// } else {
// console.log(_rdata[0][splitArray[0]]);
// custome_Filed = _rdata[0][splitArray[0]];
// }
// this.setState({ _fields: custome_Filed });
// });
const res = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
let custome_Filed = "";
const result = res.toJSON();
let _rdata = [result];
let splitArray = this.props.schema.data.valueKey.split(".");
if (splitArray.length > 1) {
custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
} else {
console.log(_rdata[0][splitArray[0]]);
custome_Filed = _rdata[0][splitArray[0]];
}
this.setState({ _fields: custome_Filed });
}
}
} catch (error) {
console.log("err", error);
}
}
};
componentDidMount() {
this.loadData();
}
render() {
return (
<input
type="hidden"
value={this.state._fields}
onChange={this.props.onChange(this.state._fields)}
/>
);
}
}
export default HiddenField;
@@ -0,0 +1,70 @@
import React from "react";
import parse from "html-react-parser";
import PropTypes from "prop-types";
const LabelField = (props) => {
const { Title, Name, Required, HelpBody, HelpLink } = props;
const REQUIRED_FIELD_SYMBOL = "*";
return (
<div style={{ display: "inline-block" }}>
<label htmlFor={Name}>
{Title}
{Required && <span className="required">{REQUIRED_FIELD_SYMBOL}</span>}
{HelpBody ? (
<div className="dropdown pull-right">
<i
className="far fa-question-circle dropdown-toggle hovereffect pull-right"
aria-hidden="true"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
style={{
fontSize: "12px",
color: "purple",
cursor: "pointer !important",
position: "relative",
bottom: "0px",
left: "0px",
paddingBottom: "4px",
paddingLeft: "4px"
}}
></i>
<div
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
style={{
marginleft: "-121px",
margintop: "-14px",
position: "absolute",
padding: "10px",
top: "102px!important"
}}
>
{parse(`
${HelpBody}
`)}
<br />
{HelpLink ? (
<a
href={HelpLink}
target="_blank"
rel="noreferrer"
className="btn btn-xs btn-primary"
>
Read more..
</a>
) : null}
</div>
</div>
) : null}
</label>
</div>
);
};
LabelField.propTypes = {
Name: PropTypes.string.isRequired,
Title: PropTypes.string.isRequired
};
export default LabelField;
@@ -0,0 +1,482 @@
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { onChangeLevel1Dropdown } from "../../redux/actions/index";
import axios from "axios";
import Parse from "parse";
import parse from "html-react-parser";
const Level1Dropdown = (props) => {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [Level1_DD, setLevel1_DD] = useState([]);
const [toastColor] = useState("#d9534f");
const [toastDescription, setToastDescription] = useState("");
const [active, setActive] = useState(false);
const getParams = (offset) => {
const params = {
skip: offset
};
return params;
};
const Level1DropdownData = async () => {
try {
if (
props.formData &&
props.uiSchema["ui:disabled"] &&
props.formData !== "Select"
) {
let url = `${parseBaseUrl}classes/${props.schema.data.class}/${props.formData}`;
if (typeof props.formData === "object") {
url = `${parseBaseUrl}classes/${props.schema.data.class}/${props.formData.objectId}`;
}
let headers1 = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
let data = [];
let response = [];
await axios.get(url, { headers: headers1 }).then((res) => {
if (res) {
data.push(res.data);
}
});
if (data.length > 0) {
let temp = [];
data.forEach((x) => {
let opt = "";
if (Array.isArray(x[props.schema.data.valueKey])) {
x[props.schema.data.valueKey].forEach((tt) => {
if (typeof tt === "object") {
opt = {
objectId: tt[props.schema.data.displayKey],
option: tt[props.schema.data.displayKey]
};
} else {
opt = {
objectId: tt,
option: tt
};
}
temp.push(opt);
});
} else if (props.schema.data.isPointer) {
if (props.schema.data.valueKey.includes(".")) {
let newArr = props.schema.data.valueKey.split(".");
if (newArr.length === 2) {
opt = {
objectId: x[newArr[0]][newArr[1]],
option: x[props.schema.data.displayKey]
};
} else {
opt = {
objectId: x[newArr[0]],
option: x[props.schema.data.displayKey]
};
}
} else {
opt = {
objectId: x[props.schema.data.valueKey],
option: x[props.schema.data.displayKey]
};
}
temp.push(opt);
} else {
opt = {
objectId: x[props.schema.data.valueKey],
option: x[props.schema.data.displayKey]
};
temp.push(opt);
}
});
response = temp;
setLevel1_DD(response);
setActive(false);
if (response.length === 1) {
props.onChangeLevel1Dropdown(response[0].objectId, props.name);
props.onChange(response[0].objectId);
} else if (response.length === 0) {
props.onChangeLevel1Dropdown("", props.name);
props.onChange("");
}
}
} else {
let response = [];
setActive(true);
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
// eslint-disable-next-line
var reg = /(\#.*?\#)/gi;
let str = props.schema.data.query;
var test;
if (str.includes("#")) {
let res;
if (localStorage.getItem("Extand_Class")) {
let data = JSON.parse(localStorage.getItem("Extand_Class"));
res = data[0];
} else {
const currentUser = Parse.User.current();
res = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
if (res) res = res.toJSON();
}
if (res) {
let json = res;
let output = str.match(reg);
if (output.length === 1) {
output = output.join();
output = output.substring(1, output.length - 1);
output = output.split(".");
if (output.length === 1) {
let out = output[0];
if (json[out]) {
if (typeof json[out] === "object") {
test = str.replace(reg, JSON.stringify(json[out]));
} else {
test = str.replace(reg, json[out]);
}
} else {
test = str.replace(reg, currentUser.id);
}
} else if (output.length === 2) {
let out1 = json[output[0]][output[1]];
if (out1) {
test = str.replace(reg, out1);
}
}
} else if (output.length === 2) {
let output1 = output[0];
output1 = output1.substring(1, output1.length - 1);
output1 = output1.split(".");
if (output1.length === 1) {
let out = output1[0];
if (json[out]) {
if (typeof json[out] === "object") {
str = str.replace(output[0], JSON.stringify(json[out]));
} else {
str = str.replace(output[0], json[out]);
}
} else {
str = str.replace(output[0], currentUser.id);
}
} else if (output1.length === 2) {
let out1 = json[output1[0]][output1[1]];
if (out1) {
str = str.replace(output[0], out1);
}
}
let output2 = output[1];
output2 = output2.substring(1, output2.length - 1);
output2 = output2.split(".");
if (output2.length === 1) {
let out = output2[0];
if (json[out]) {
if (typeof json[out] === "object") {
str = str.replace(output[1], JSON.stringify(json[out]));
} else {
str = str.replace(output[1], json[out]);
}
} else {
str = str.replace(output[1], currentUser.id);
}
} else if (output2.length === 2) {
let out1 = json[output2[0]][output2[1]];
if (out1) {
str = str.replace(output[1], out1);
}
}
test = str;
}
} else {
setToastDescription(
`User not found in ${localStorage.getItem("extended_class")}`
);
setActive(false);
var x = document.getElementById("Level_1");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 5000);
return;
}
} else {
test = str;
}
let url = `${parseBaseUrl}classes/${props.schema.data.class}?${test}`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
let fetchCount = 0;
let offset = 0;
let data = [];
/* eslint-disable no-constant-condition */
while (true) {
const params = getParams(offset);
await axios
.get(url, { params: params, headers: headers })
.then((response) => {
fetchCount = response.data.results.length;
offset += response.data.results.length;
if (fetchCount > 0) {
response.data.results.forEach((x) => {
data.push(x);
});
}
})
.catch((err) => {
console.error("Problem", err.response);
setToastDescription(err.response.data.message);
setActive(false);
var x = document.getElementById("Level_1");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 5000);
});
if (fetchCount === 0 || fetchCount < 100) {
break;
}
}
if (data.length > 0) {
let temp = [];
data.forEach((x) => {
let opt = "";
if (Array.isArray(x[props.schema.data.valueKey])) {
x[props.schema.data.valueKey].forEach((tt) => {
if (typeof tt === "object") {
if (props.schema.data.isPointer) {
opt = {
objectId: tt["objectId"],
option: tt[props.schema.data.displayKey]
};
} else {
opt = {
objectId: tt[props.schema.data.displayKey],
option: tt[props.schema.data.displayKey]
};
}
} else {
opt = {
objectId: tt,
option: tt
};
}
temp.push(opt);
});
} else if (props.schema.data.isPointer) {
if (props.schema.data.valueKey.includes(".")) {
let newArr = props.schema.data.valueKey.split(".");
if (newArr.length === 2) {
let _dis = props.schema.data.displayKey.split(".");
if (_dis.length === 2) {
opt = {
objectId: x[newArr[0]][newArr[1]],
option: x[_dis[0]][_dis[1]]
};
} else {
opt = {
objectId: x[newArr[0]][newArr[1]],
option: x[props.schema.data.displayKey]
};
}
} else {
opt = {
objectId: x[newArr[0]],
option: x[props.schema.data.displayKey]
};
}
} else {
opt = {
objectId: x[props.schema.data.valueKey],
option: x[props.schema.data.displayKey]
};
}
temp.push(opt);
} else {
opt = {
objectId: x[props.schema.data.valueKey],
option: x[props.schema.data.displayKey]
};
temp.push(opt);
}
});
response = temp;
setLevel1_DD(response);
setActive(false);
if (!props.formData) {
props.onChangeLevel1Dropdown("", props.name);
}
if (response.length === 1) {
props.onChangeLevel1Dropdown(response[0].objectId, props.name);
props.onChange(response[0].objectId);
} else if (response.length === 0) {
props.onChangeLevel1Dropdown("", props.name);
props.onChange("");
}
} else {
setActive(false);
}
}
} catch (e) {
try {
setToastDescription(e.message);
setActive(false);
x = document.getElementById("Level_1");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 5000);
} catch (error) {
console.log("Err", error);
}
}
};
useEffect(() => {
Level1DropdownData();
// eslint-disable-next-line
}, []);
useEffect(() => {
if (props.formData && props.formData[`objectId`]) {
props.onChangeLevel1Dropdown(props.formData.objectId, props.name);
} else if (props.formData) {
props.onChangeLevel1Dropdown(props.formData, props.name);
}
// eslint-disable-next-line
}, [props.formData]);
const selectDefault = () => {
props.onChange(Level1_DD[0].objectId);
props.onChangeLevel1Dropdown(Level1_DD[0].objectId, props.name);
};
const REQUIRED_FIELD_SYMBOL = "*";
return (
<React.Fragment>
<div style={{ display: "inline-block" }}>
<label htmlFor={props.name}>
{props.schema.title}
{props.required && (
<span className="required">{REQUIRED_FIELD_SYMBOL}</span>
)}
{props.schema.data.helpbody ? (
<div className="dropdown pull-right">
<i
className="far fa-question-circle dropdown-toggle hovereffect pull-right"
aria-hidden="true"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
style={{
fontSize: "12px",
color: "purple",
cursor: "pointer !important",
position: "relative",
bottom: "0px",
left: "0px",
paddingBottom: "4px",
paddingLeft: "4px"
}}
></i>
<div
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
style={{
marginleft: "-121px",
margintop: "-14px",
position: "absolute",
padding: "10px",
top: "102px!important"
}}
>
{parse(`
${props.schema.data.helpbody}
`)}
<br />
{props.schema.data.helplink ? (
<a
href={props.schema.data.helplink}
target="_blank"
rel="noreferrer"
className="btn btn-xs btn-primary"
>
Read more..
</a>
) : null}
</div>
</div>
) : null}
</label>
</div>
{Level1_DD && (
<select
className="form-control"
name={props.name}
value={props.value}
required={props.required}
disabled={props.uiSchema["ui:disabled"]}
onChange={(e) => {
if (!e.target.value) {
if (props.schema.data.isPointer) {
localStorage.setItem("Level1", undefined);
props.onChangeLevel1Dropdown(undefined, props.name);
return props.onChange(undefined);
} else {
localStorage.setItem("Level1", e.target.value);
props.onChangeLevel1Dropdown(e.target.value, props.name);
return props.onChange(e.target.value);
}
} else {
localStorage.setItem("Level1", e.target.value);
props.onChangeLevel1Dropdown(e.target.value, props.name);
return props.onChange(e.target.value);
}
}}
readOnly={props.uiSchema["ui:disabled"] ? true : false}
defaultValue={Level1_DD.length === 1 && selectDefault}
>
<option value="">{!active ? "Select" : "loading..."}</option>
{Level1_DD &&
Level1_DD.map((x, i) => (
<React.Fragment key={i}>
{props.formData && props.formData.objectId ? (
props.formData.objectId === x.objectId ? (
<option value={x.objectId} selected>
{x.option}
</option>
) : (
<option value={x.objectId}>{x.option}</option>
)
) : props.formData && props.formData === x.objectId ? (
<option value={x.objectId} selected>
{x.option}
</option>
) : (
<option value={x.objectId}>{x.option}</option>
)}
</React.Fragment>
))}
</select>
)}
<div id="Level_1" style={{ backgroundColor: toastColor }}>
{toastDescription}
</div>
</React.Fragment>
);
};
export default connect(null, { onChangeLevel1Dropdown })(Level1Dropdown);
@@ -0,0 +1,709 @@
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import Select from "react-select";
import axios from "axios";
import LabelField from "./LabelField";
import ReactDragListView from "react-drag-listview";
import AppendFormInForm from "../AppendFormInForm";
import Modal from "react-modal";
function arrayMove(array, from, to) {
array = array.slice();
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
return array;
}
/**
* react-sortable-hoc is depcreated not usable from react 18.x.x
* need to replace it with @dnd-kit
* code changes required
*/
const MultiSelectField = (props) => {
Modal.setAppElement("body");
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [state, setState] = useState(undefined);
// const [editFormData, setEditFormData] = useState([]);
const [selected, setSelected] = React.useState([]);
const [isModal, setIsModel] = useState(false);
const onChange = (selectedOptions) => setSelected(selectedOptions);
const [modalIsOpen, setModalIsOpen] = useState(false);
const onSortEnd = ({ oldIndex, newIndex }) => {
const newValue = arrayMove(selected, oldIndex, newIndex);
setSelected(newValue);
};
// console.log("props",props)
const GetSelectListData = async () => {
try {
let response = [];
let str = props.schema.data.query;
let _query;
/* eslint-disable no-useless-escape */
let reg = /(\#.*?\#)/gi;
let extclass =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"));
let extRow = extclass.length && extclass[0];
if (str.includes("#")) {
let output = str.match(reg);
output = output.join();
output = output.substring(1, output.length - 1);
output = output.split(".");
if (output.length === 2) {
let out1 = extRow[output[0]][output[1]];
_query = str.replace(reg, out1 && out1);
} else {
let out1 = extRow[output[0]];
_query = str.replace(reg, out1 && out1);
}
} else {
_query = str;
}
let url = `${parseBaseUrl}classes/${props.schema.data.class}?${_query}`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
await axios.get(url, { headers: headers }).then((res) => {
let temp = [];
let formArray = [];
let _selected = [];
if (props.schema.uiLayout === "MultiDropdownList") {
if (props.formData) {
props.formData.forEach((x) => {
if (typeof x === "object") {
formArray.push(x.objectId);
} else {
formArray.push(x);
}
});
}
res.data.results.forEach((x) => {
let obj = {};
if (formArray.includes(x[props.schema.data.valueKey])) {
obj = {
name: x[props.schema.data.displayKey],
value: x[props.schema.data.valueKey],
isChecked: true
};
} else {
if (props.schema.selectAll) {
obj = {
name: x[props.schema.data.displayKey],
value: x[props.schema.data.valueKey],
isChecked: true
};
} else {
obj = {
name: x[props.schema.data.displayKey],
value: x[props.schema.data.valueKey],
isChecked: false
};
}
}
temp.push(obj);
});
response = temp;
if (props.formData) {
let checkData = [];
response.forEach((x) => {
if (x.isChecked) {
checkData.push(x.value);
}
});
setSelected(checkData);
}
setState({ [`${props.name}_DD`]: response });
} else {
res.data.results.forEach((x) => {
let obj = {};
if (props.formData) {
props.formData.forEach((x) => {
if (typeof x === "object") {
formArray.push(x.objectId);
} else {
formArray.push(x);
}
});
}
obj = {
label: x[props.schema.data.displayKey],
value: x[props.schema.data.valueKey]
};
if (formArray.includes(x[props.schema.data.valueKey])) {
_selected.push(obj);
}
if (props.schema.selectAll) {
_selected.push(obj);
}
temp.push(obj);
});
response = temp;
setState({ [`${props.name}_DD`]: response });
setSelected(_selected);
}
});
} catch (error) {
console.log("err", error);
}
};
const handleCheckChieldElement = (event) => {
let SelectLists = state[`${props.name}_DD`];
let List = [];
SelectLists.forEach((select) => {
if (select.value === event.target.value) {
select.isChecked = event.target.checked;
}
if (select.isChecked) {
List.push(select.value);
}
});
setSelected(List);
setState({ [`${props.name}_DD`]: SelectLists });
};
// const Level1CheckList = async (id) => {
// try {
// let response = [];
// // eslint-disable-next-line
// let reg = /(\#.*?\#)/gi;
// let _query = props.schema.data.query;
// let output = _query.match(reg);
// if (output.length === 2) {
// let res;
// if (localStorage.getItem("Extand_Class")) {
// let data = JSON.parse(localStorage.getItem("Extand_Class"));
// res = data[0];
// }
// output = output.filter((x) => x !== "#queryString#");
// if (output.length === 1) {
// _query = _query.replace("#queryString#", id);
// output = output.join();
// output = output.substring(1, output.length - 1);
// output = output.split(".");
// if (output.length > 0) {
// _query = _query.replace(reg, res[output[0]][output[1]]);
// } else {
// _query = _query.replace(reg, res[output[0]]);
// }
// }
// } else {
// _query = props.schema.data.query.replace(reg, id);
// }
// let url = `${parseBaseUrl}classes/${props.schema.data.class}?${_query}`;
// const headers = {
// "Content-Type": "application/json",
// "X-Parse-Application-Id": parseAppId,
// "X-Parse-Session-Token": localStorage.getItem("accesstoken")
// };
// await axios.get(url, { headers: headers }).then(async (res) => {
// let temp = [];
// let formArray = [];
// let _selected = [];
// if (editFormData.length > 0) {
// editFormData.forEach((x) => {
// if (typeof x === "object") {
// formArray.push(x.objectId);
// } else {
// formArray.push(x);
// }
// });
// } else if (
// props.schema.selectedData &&
// Object.keys(props.schema.selectedData).length !== 0 &&
// props.schema.selectedData.constructor === Object
// ) {
// try {
// let selectedDataQuery = props.schema.selectedData.query.replace(
// reg,
// id
// );
// let selectedDataUrl = `${parseBaseUrl}classes/${props.schema.selectedData.class}?${selectedDataQuery}`;
// await axios.get(selectedDataUrl, { headers }).then((sltres) => {
// let sltData = sltres.data.results;
// sltData.forEach((x) => {
// if (props.schema.selectedData.valueKey.includes(".")) {
// let sltArr = props.schema.selectedData.valueKey.split(".");
// if (Array.isArray(x[sltArr[0]])) {
// x[sltArr[0]].forEach((l) => {
// formArray.push(l[sltArr[1]]);
// });
// } else {
// formArray.push(x[sltArr[0][sltArr[1]]]);
// }
// } else {
// formArray.push(x[props.schema.selectedData.valueKey]);
// }
// });
// });
// } catch (error) {}
// }
// res.data.results.forEach((x) => {
// let obj = {};
// if (props.schema.data.valueKey.includes(".")) {
// let newArr = props.schema.data.valueKey.split(".");
// if (Array.isArray(x[newArr[0]])) {
// if (props.schema.data.displayKey.includes(".")) {
// let _dis = props.schema.data.displayKey.split(".");
// x[newArr[0]].forEach((l) => {
// if (formArray.includes(l[newArr[1]])) {
// obj = {
// label: l[_dis[1]],
// value: l[newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: l[_dis[1]],
// value: l[newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: l[_dis[1]],
// value: l[newArr[1]],
// isChecked: false
// };
// }
// }
// temp.push(obj);
// });
// } else {
// x[newArr[0]].forEach((l) => {
// if (formArray.includes(l[newArr[1]])) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: l[newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: l[newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: x[props.schema.data.displayKey],
// value: l[newArr[1]],
// isChecked: false
// };
// }
// }
// temp.push(obj);
// });
// }
// } else {
// if (props.schema.data.displayKey.includes(".")) {
// let disArr = props.schema.data.displayKey.split(".");
// if (formArray.includes(x[newArr[0]][newArr[1]])) {
// obj = {
// label: x[disArr[0]][disArr[1]],
// value: x[newArr[0]][newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: x[disArr[0]][disArr[1]],
// value: x[newArr[0]][newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: x[disArr[0]][disArr[1]],
// value: x[newArr[0]][newArr[1]],
// isChecked: false
// };
// }
// }
// } else {
// if (formArray.includes(x[newArr[0]][newArr[1]])) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[newArr[0]][newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[newArr[0]][newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[newArr[0]][newArr[1]],
// isChecked: false
// };
// }
// }
// }
// }
// } else {
// if (Array.isArray(x[props.schema.data.valueKey])) {
// x[props.schema.data.valueKey].forEach((t) => {
// if (formArray.includes(t)) {
// obj = {
// label: t,
// value: t,
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: t,
// value: t,
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: t,
// value: t,
// isChecked: false
// };
// }
// }
// temp.push(obj);
// });
// } else if (formArray.includes(x[props.schema.data.valueKey])) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[props.schema.data.valueKey],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[props.schema.data.valueKey],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[props.schema.data.valueKey],
// isChecked: false
// };
// }
// }
// temp.push(obj);
// }
// });
// response = temp;
// if (props.schema.uiLayout === "MultiDropdownList") {
// if (editFormData) {
// let checkData = [];
// response.forEach((x) => {
// if (x.isChecked) {
// checkData.push(x.value);
// }
// });
// setSelected(checkData);
// }
// setState({ [`${props.name}_DD`]: response });
// } else {
// setState({ [`${props.name}_DD`]: response });
// setSelected(_selected);
// }
// });
// } catch (error) {}
// };
const dragProps = {
onDragEnd(fromIndex, toIndex) {
const data = [...state[`${props.name}_DD`]];
const item = data.splice(fromIndex, 1)[0];
data.splice(toIndex, 0, item);
let NewList = [];
data.forEach((x) => {
if (x.isChecked) {
NewList.push(x.value);
}
});
setState({ [`${props.name}_DD`]: data });
setSelected(NewList);
},
nodeSelector: "li",
handleSelector: "a"
};
useEffect(() => {
if (!props.schema.parent) GetSelectListData();
// eslint-disable-next-line
}, []);
useEffect(() => {
if (props.schema.uiLayout === "MultiDropdownList") {
props.onChange(selected);
} else {
if (selected && selected.length) {
let newData = [];
selected.forEach((x) => {
newData.push(x.value);
});
props.onChange(newData);
}
}
// eslint-disable-next-line
}, [selected]);
// useState(() => {
// if (props.formData) {
// if (props.formData === "Select") {
// } else {
// setEditFormData(props.formData);
// }
// }
// // eslint-disable-next-line
// }, [props.formData]);
const handleModalCloseClick = () => {
setIsModel(false);
setModalIsOpen(false);
};
const openModal = () => {
setModalIsOpen(true);
};
// `handleNewDetails` is used to set just save from quick form to selected option in dropdown
const handleNewDetails = (data) => {
setState({ [`${props.name}_DD`]: [...state[`${props.name}_DD`], data] });
if (selected.length > 0) {
setSelected([...selected, data]);
} else {
setSelected([data]);
}
};
if (props.schema.uiLayout === "MultiDropdownList") {
return (
<React.Fragment>
<LabelField
Title={props.schema.title}
Name={props.name}
Required={props.required}
HelpBody={props.schema.helpbody}
HelpLink={props.schema.helplink}
/>
<ReactDragListView {...dragProps}>
<ul
style={{
listStyleType: "square",
listStylePosition: "outside",
maxHeight: "250px",
overflow: "auto",
paddingTop: "10px",
paddingBottom: "10px",
border: "0px solid #b3b3b3",
borderRadius: "5px"
}}
>
{state && state[`${props.name}_DD`].length > 0 ? (
state[`${props.name}_DD`].map((x, i) => {
return (
<li key={i} className="multiSelectList">
<input
key={x.value}
onClick={handleCheckChieldElement}
type="checkbox"
checked={x.isChecked}
style={{ marginTop: "7px" }}
value={x.value}
disabled={props.disabled}
/>{" "}
<span style={{ marginTop: "7px" }}>{x.name}</span>
{props.schema.isSortable && !props.disabled && (
<a
onClick={(e) => e.preventDefault()}
className="pull-right"
title="Sort List"
style={{ textDecoration: "none" }}
>
<i
className="fa fa-bars"
style={{
fontSize: "30px",
color: "skyblue",
paddingTop: "11px"
}}
></i>
</a>
)}
</li>
);
})
) : (
<li className="multiSelectList">No Record Found</li>
)}
</ul>
</ReactDragListView>
</React.Fragment>
);
}
return (
<React.Fragment>
<LabelField
Title={props.schema.title}
Name={props.name}
Required={props.required}
HelpBody={props.schema.helpbody}
HelpLink={props.schema.helplink}
/>
<div style={{ display: "flex", gap: 5 }}>
<div style={{ flexWrap: "wrap", width: "100%" }}>
<Select
onSortEnd={onSortEnd}
distance={4}
isMulti
options={(state && state[`${props.name}_DD`]) || []}
value={selected}
onChange={onChange}
closeMenuOnSelect={false}
/>
</div>
{props.schema.data.quickAddFormId && (
<div
onClick={() => {
setIsModel(true);
openModal();
}}
style={{
cursor: "pointer",
borderRadius: 4,
border: "1px solid #ccc",
minHeight: 38,
minWidth: 48,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<i className="fas fa-plus"></i>
</div>
)}
{/* {props.schema.data.quickAddFormId && (
<div
className="modal fade"
id={"multiSelectModal" + props.schema.data.quickAddFormId}
tabIndex="-1"
role="dialog"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div className="modal-dialog modal-lg" role="document">
<div className="modal-content">
<div className="modal-header">
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
onClick={handleModalCloseClick}
>
<i className="fa fa-times-circle" aria-hidden="true"></i>
</button>
</div>
<div className="modal-body">
{isModal && (
<AppendFormInForm
id={props.schema.data.quickAddFormId}
valueKey={props.schema.data.valueKey}
displayKey={props.schema.data.displayKey}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</div>
</div>
</div>
</div>
)} */}
<Modal
isOpen={modalIsOpen}
onRequestClose={handleModalCloseClick}
shouldCloseOnOverlayClick={false}
contentLabel="Modal"
style={{
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
transform: "translate(-50%, -50%)",
padding: 0
},
overlay: {
width: "100%",
backgroundColor: "rgba(0, 0, 0, 0.75)",
zIndex: 50
}
}}
>
<div className="min-w-full md:min-w-[500px]">
<div
type="button"
className="flex justify-between items-center p-3 border-b-[1px] border-gray-300"
>
<div className=" text-black text-xl font-semibold pl-3">
Add Signer
</div>
<button onClick={handleModalCloseClick}>
<i
style={{ fontSize: 25 }}
className="fa fa-times-circle"
aria-hidden="true"
></i>
</button>
</div>
{isModal && (
<AppendFormInForm
id={props.schema.data.quickAddFormId}
valueKey={props.schema.data.valueKey}
displayKey={props.schema.data.displayKey}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</div>
</Modal>
</div>
</React.Fragment>
);
};
const mapStateToProps = (state) => {
return {
Level1_Dropdown: state.Level1_Dropdown,
Level2_Dropdown: state.Level2_Dropdown,
Level3_Dropdown: state.Level3_Dropdown
};
};
export default connect(mapStateToProps, null)(MultiSelectField);
@@ -0,0 +1,92 @@
import React from "react";
import ObjectField from "@rjsf/core/lib/components/fields/ObjectField";
import validator from "@rjsf/validator-ajv8";
import { Col } from "react-bootstrap";
import { retrieveSchema } from "@rjsf/utils";
class GridField extends ObjectField {
state = {};
render() {
const {
uiSchema,
errorSchema,
idSchema,
disabled,
readonly,
onBlur,
formData
} = this.props;
const { fields, rootSchema } = this.props.registry;
const { SchemaField } = fields;
const schema = retrieveSchema(validator, this.props.schema, rootSchema);
const title = schema.title === undefined ? "" : schema.title;
const description =
schema && schema.description === undefined ? "" : schema.description;
// console.log('this.props.schema ', this.props.schema)
// console.log("schema ", schema);
const layout = uiSchema["ui:layout"];
return (
<fieldset>
{title ? <h4>{title}</h4> : null}
{description ? <h6>{description}</h6> : null}
{layout.map((row, index) => {
return (
<div className="row" key={index}>
{Object.keys(row).map((name, index) => {
const { doShow, ...rowProps } = row[name];
let style = {};
if (doShow && !doShow({ formData })) {
style = { display: "none" };
}
if (schema.properties[name]) {
return (
<Col {...rowProps} key={index} style={style}>
<SchemaField
name={name}
required={this.isRequired(name)}
schema={schema.properties[name]}
uiSchema={uiSchema[name]}
errorSchema={errorSchema[name]}
idSchema={idSchema[name]}
formData={formData[name]}
onChange={this.onPropertyChange(name)}
onBlur={onBlur}
registry={this.props.registry}
disabled={disabled}
readonly={readonly}
/>
</Col>
);
} else {
const { render, ...rowProps } = row[name];
let UIComponent = () => null;
if (render) {
UIComponent = render;
}
return (
<Col {...rowProps} key={index} style={style}>
<UIComponent
name={name}
formData={formData}
errorSchema={errorSchema}
uiSchema={uiSchema}
schema={schema}
registry={this.props.registry}
/>
</Col>
);
}
})}
</div>
);
})}
</fieldset>
);
}
}
export default GridField;
@@ -0,0 +1,26 @@
import React from "react";
import PropTypes from "prop-types";
function TimeWidget(props) {
const {
onChange,
registry: {
widgets: { BaseInput }
}
} = props;
return (
<BaseInput
type="time"
{...props}
onChange={(value) => onChange(value || undefined)}
/>
);
}
if (process.env.NODE_ENV !== "production") {
TimeWidget.propTypes = {
value: PropTypes.string
};
}
export default TimeWidget;
@@ -1,67 +0,0 @@
import React from "react";
import ModalUi from "../../primitives/ModalUi";
const AddRoleModal = (props) => {
return (
<ModalUi
title={"Add Role"}
isOpen={props.isModalRole}
handleClose={props.handleCloseRoleModal}
>
<div className="addusercontainer">
<form
style={{ display: "flex", flexDirection: "column" }}
onSubmit={props.handleAddRole}
>
<input
value={props.roleName}
onChange={(e) => props.setRoleName(e.target.value)}
placeholder={
props.signersdata.length > 0
? "User " + (props.signersdata.length + 1)
: "User 1"
}
className="addUserInput"
/>
<p
style={{
color: "grey",
fontSize: 11,
margin: "10px 0 10px 5px"
}}
>
e.g: Customer, Hr, Director, Manager, Student, etc...
</p>
<div>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginBottom: "15px"
}}
></div>
<button
type="submit"
style={{
background: "#00a2b7"
}}
className="finishBtn"
>
Add
</button>
<button
onClick={props.handleCloseRoleModal}
type="button"
className="finishBtn cancelBtn"
>
Close
</button>
</div>
</form>
</div>
</ModalUi>
);
};
export default AddRoleModal;
@@ -1,74 +0,0 @@
import React from "react";
import { themeColor } from "../../constant/const";
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
const confirmToaddDefaultSign = () => {
if (xyPostion.length > 0) {
setDefaultSignAlert({
isShow: true,
alertMessage:
"Are you sure you want to auto sign at requested all locations?"
});
} else {
setDefaultSignAlert({
isShow: true,
alertMessage: "please select position!"
});
}
};
return (
<div>
<div
style={{
background: themeColor,
color: "white",
padding: "5px",
fontFamily: "sans-serif",
marginTop: "5px"
}}
>
Signature
</div>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
marginTop: "10px",
fontWeight: "600"
}}
>
<>
<p>Your Signature</p>
<div className="defaultSignBox">
<img
alt="default img"
style={{
width: "100%",
height: "100%",
objectFit: "contain"
}}
src={defaultSignImg}
/>
</div>
<button
style={{
background: themeColor,
color: "white",
marginTop: "20px",
cursor: "pointer",
marginBottom: "10px"
}}
type="button"
className="finishBtn finishnHover"
onClick={() => confirmToaddDefaultSign()}
>
Auto Sign All
</button>
</>
</div>
</div>
);
}
export default DefaultSignature;
@@ -1,97 +0,0 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import Loader from "../../primitives/LoaderWithMsg";
import { contractDocument } from "../../constant/Utils";
import HandleError from "../../primitives/HandleError";
function DraftDocument() {
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState({
isLoader: true,
message: "This might take some time"
});
const rowLevel =
localStorage.getItem("rowlevel") &&
JSON.parse(localStorage.getItem("rowlevel"));
const docId =
rowLevel && rowLevel?.id
? rowLevel.id
: rowLevel?.objectId && rowLevel.objectId;
useEffect(() => {
getDocumentDetails();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//get document details
const getDocumentDetails = async () => {
//getting document details
const documentData = await contractDocument(docId);
if (documentData && documentData.length > 0) {
handleDraftDoc(documentData);
} else if (
documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error)
) {
setIsLoading({
isLoader: false,
message: "Error: Something went wrong!"
});
} else {
setIsLoading({
isLoader: false,
message: "No data found!"
});
}
};
//check document type and render on signyour self and placeholder route
const handleDraftDoc = (documentData) => {
const data = documentData[0];
const signerExist = data.Signers && data.Signers;
const isDecline = data.IsDeclined && data.IsDeclined;
const isPlaceholder = data.Placeholders && data.Placeholders;
const signedUrl = data.SignedUrl;
//checking if document has completed and request signature flow
if (data?.IsCompleted && signerExist?.length > 0) {
navigate(`/pdfRequestFiles/${data.objectId}`);
}
//checking if document has completed and signyour-self flow
else if (!signerExist && !isPlaceholder) {
navigate(`/signaturePdf/${data.objectId}`);
}
//checking if document has declined by someone
else if (isDecline) {
navigate(`/pdfRequestFiles/${data.objectId}`);
//checking draft type document
} else if (
signerExist?.length > 0 &&
isPlaceholder?.length > 0 &&
!signedUrl
) {
navigate(`/placeHolderSign/${data.objectId}`);
}
//Inprogress document
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
navigate(`/pdfRequestFiles/${data.objectId}`);
} //placeholder draft document
else if (
(signerExist?.length > 0 &&
(!isPlaceholder || isPlaceholder?.length === 0)) ||
((!signerExist || signerExist?.length === 0) && isPlaceholder?.length > 0)
) {
navigate(`/placeHolderSign/${data.objectId}`);
}
};
return (
<div>
{isLoading.isLoader ? (
<Loader isLoading={isLoading} />
) : (
<HandleError handleError={isLoading.message} />
)}
</div>
);
}
export default DraftDocument;
@@ -1,13 +0,0 @@
import React from "react";
import { getWidgetType, widgets } from "../../constant/Utils";
function DragElement(item) {
const getWidgets = widgets;
const filterWidgetPreview = getWidgets.filter(
(data) => data.type === item?.text
);
return <div>{getWidgetType(filterWidgetPreview[0])}</div>;
}
export default DragElement;
@@ -1,451 +0,0 @@
import React, { useEffect, useState } from "react";
import { isEnableSubscription, themeColor } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi";
import { radioButtonWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade";
function DropdownWidgetOption(props) {
const [dropdownOptionList, setDropdownOptionList] = useState([
"option-1",
"option-2"
]);
const [minCount, setMinCount] = useState(0);
const [maxCount, setMaxCount] = useState(0);
const [dropdownName, setDropdownName] = useState(props.type);
const [isReadOnly, setIsReadOnly] = useState(false);
const [isHideLabel, setIsHideLabel] = useState(false);
const [status, setStatus] = useState("required");
const [defaultValue, setDefaultValue] = useState("");
const statusArr = ["required", "optional"];
const [defaultCheckbox, setDefaultCheckbox] = useState([]);
const resetState = () => {
setDropdownOptionList(["option-1", "option-2"]);
setDropdownName(props.type);
setIsReadOnly(false);
setIsHideLabel(false);
setMinCount(0);
setMaxCount(0);
setDefaultCheckbox([]);
setDefaultValue("");
};
useEffect(() => {
if (
props.currWidgetsDetails?.options?.name &&
props.currWidgetsDetails?.options?.values
) {
setDropdownName(props.currWidgetsDetails?.options?.name);
setDropdownOptionList(props.currWidgetsDetails?.options?.values);
setMinCount(
props.currWidgetsDetails?.options?.validation?.minRequiredCount
);
setMaxCount(
props.currWidgetsDetails?.options?.validation?.maxRequiredCount
);
setIsReadOnly(props.currWidgetsDetails?.options?.isReadOnly);
setIsHideLabel(props.currWidgetsDetails?.options?.isHideLabel);
setStatus(props.currWidgetsDetails?.options?.status || "required");
setDefaultValue(props.currWidgetsDetails?.options?.defaultValue || "");
setDefaultCheckbox(props.currWidgetsDetails?.options?.defaultValue || []);
} else {
setStatus("required");
resetState();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.currWidgetsDetails]);
const handleInputChange = (index, value) => {
setDropdownOptionList((prevInputs) => {
const newInputs = [...prevInputs];
newInputs[index] = value;
return newInputs;
});
};
//function add add checkbox option and add width of checkbox
const handleAddInput = () => {
const deleteOption = false;
const addOption = true;
setDropdownOptionList((prevInputs) => [...prevInputs, ""]);
props.handleSaveWidgetsOptions(
null,
null,
null,
null,
null,
addOption,
deleteOption
);
};
//function add add checkbox option and delete width of checkbox
const handleDeleteInput = (ind) => {
const deleteOption = true;
const addOption = false;
const getUpdatedOptions = dropdownOptionList.filter(
(data, index) => index !== ind
);
setDropdownOptionList(getUpdatedOptions);
props.handleSaveWidgetsOptions(
null,
null,
null,
null,
null,
addOption,
deleteOption
);
};
const handleSaveOption = () => {
const defaultData =
defaultCheckbox && defaultCheckbox.length > 0
? defaultCheckbox
: defaultValue;
props.handleSaveWidgetsOptions(
dropdownName,
dropdownOptionList,
minCount,
maxCount,
isReadOnly,
null,
null,
status,
defaultData,
isHideLabel
);
// props.setShowDropdown(false);
setDropdownOptionList(["option-1", "option-2"]);
setDropdownName(props.type);
// props.setCurrWidgetsDetails({});
setIsReadOnly(false);
setIsHideLabel(false);
setMinCount(0);
setMaxCount(0);
setDefaultCheckbox([]);
setDefaultValue("");
};
const handleSetMinMax = (e) => {
const minValue = e.target.value;
if (minValue > dropdownOptionList.length) {
return "";
} else {
return minValue;
}
};
const handleDefaultCheck = (index) => {
const getDefaultCheck = defaultCheckbox.includes(index);
if (getDefaultCheck) {
return true;
} else {
return false;
}
};
return (
<ModalUi isOpen={props.showDropdown} title={props.title} showClose={false}>
<div className="h-full p-[15px]">
<form
onSubmit={(e) => {
e.preventDefault();
handleSaveOption();
}}
>
<div>
<label style={{ fontSize: "13px", fontWeight: "600" }}>
Name<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
required
defaultValue={dropdownName}
value={dropdownName}
onChange={(e) => setDropdownName(e.target.value)}
className="drodown-input"
/>
<label
style={{ fontSize: "13px", fontWeight: "600", marginTop: "5px" }}
>
Options
</label>
<div
style={{
display: "flex",
flexDirection: "column"
}}
>
{dropdownOptionList.map((option, index) => (
<div
key={index}
style={{
display: "flex",
flexDirection: "row",
marginBottom: "5px",
alignItems: "center"
}}
>
{props.type === "checkbox" && !props.isSignYourself && (
<input
type="checkbox"
checked={handleDefaultCheck(index)}
onChange={(e) => {
if (e.target.checked) {
const getDefaultCheck =
defaultCheckbox?.includes(index);
if (!getDefaultCheck) {
setDefaultCheckbox((prev) => [...prev, index]);
}
} else {
const removeOption = defaultCheckbox.filter(
(data) => data !== index
);
setDefaultCheckbox(removeOption);
}
}}
style={{
width: "23px",
height: "19px",
marginRight: "5px"
}}
/>
)}
<input
required
className="drodown-input"
type="text"
value={option}
onChange={(e) => handleInputChange(index, e.target.value)}
/>
<i
className="fa-solid fa-rectangle-xmark"
onClick={() => handleDeleteInput(index)}
style={{
color: "red",
fontSize: "25px",
marginLeft: "10px"
}}
></i>
</div>
))}
<i
onClick={handleAddInput}
style={{
cursor: "pointer",
color: themeColor,
fontSize: "25px",
marginLeft: "10px"
}}
className="fa-solid fa-square-plus"
></i>
<div>
{props.type === "checkbox" && !props.isSignYourself && (
<>
<label
style={{
fontSize: "13px",
fontWeight: "600",
color: !props.isSubscribe && "gray"
}}
>
Minimun check
</label>
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
<input
required
defaultValue={0}
value={minCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMinCount(count);
}}
className={
props.isSubscribe || !isEnableSubscription
? "drodown-input"
: "disabled drodown-input"
}
/>
<label
style={{
fontSize: "13px",
fontWeight: "600",
color: !props.isSubscribe && "gray"
}}
>
Maximum check
</label>
<input
required
defaultValue={0}
value={maxCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMaxCount(count);
}}
className={
props.isSubscribe || !isEnableSubscription
? "drodown-input"
: "disabled drodown-input"
}
/>
</>
)}
</div>
</div>
{["dropdown", radioButtonWidget].includes(props.type) && (
<>
<label
style={{
fontSize: "13px",
fontWeight: "600",
marginTop: "5px"
}}
>
Default value
</label>
<select
onChange={(e) => setDefaultValue(e.target.value)}
className="drodown-input"
name="defaultvalue"
value={defaultValue}
placeholder="select default value"
>
<option value="" disabled hidden style={{ fontSize: "13px" }}>
Select...
</option>
{dropdownOptionList.map((data, ind) => {
return (
<option
style={{ fontSize: "13px" }}
key={ind}
value={data}
>
{data}
</option>
);
})}
</select>
</>
)}
{props.type !== "checkbox" && props.type !== radioButtonWidget && (
<>
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
marginTop: "0.5rem"
}}
>
{statusArr.map((data, ind) => {
return (
<div
key={ind}
style={{
display: "flex",
flexDirection: "row",
gap: 5,
alignItems: "center"
}}
>
<input
style={{ accentColor: "red" }}
type="radio"
name="status"
onChange={() => setStatus(data.toLowerCase())}
checked={status.toLowerCase() === data.toLowerCase()}
/>
<div style={{ fontSize: "13px", fontWeight: "500" }}>
{data}
</div>
</div>
);
})}
</div>
</>
)}
{["checkbox", radioButtonWidget].includes(props.type) && (
<div className="flex flex-row gap-5 mt-2 items-center text-center">
{!props.isSignYourself && (
<div>
<input
id="isreadonly"
type="checkbox"
checked={isReadOnly}
onChange={(e) => {
setIsReadOnly(e.target.checked);
}}
/>
<label className="ml-1" htmlFor="isreadonly">
Is read only
</label>
</div>
)}
<div>
<input
id="ishidelabel"
type="checkbox"
checked={isHideLabel}
onChange={(e) => {
setIsHideLabel(e.target.checked);
}}
/>
<label className="ml-1" htmlFor="ishidelabel">
Hide labels
</label>
</div>
</div>
)}
</div>
{props.type === "checkbox" &&
!props.isSignYourself &&
!isEnableSubscription && (
<PremiumAlertHeader
message={
"Field validations are free in beta, this feature will incur a fee later."
}
/>
)}
<div
className={`${
props.type === "checkbox" && !props.isSignYourself
? "mb-[15px]"
: "my-[15px]"
} w-full h-[1px] bg-[#9f9f9f]`}
></div>
<button
disabled={dropdownOptionList.length === 0 && true}
style={{
background: themeColor,
color: "white"
}}
type="submit"
className={"finishBtn"}
>
Save
</button>
{props.currWidgetsDetails?.options?.values?.length > 0 && (
<button
type="submit"
className="finishBtn cancelBtn"
onClick={() => {
props.handleClose && props.handleClose();
resetState();
}}
>
Cancel
</button>
)}
</form>
</div>
</ModalUi>
);
}
export default DropdownWidgetOption;
@@ -1,143 +0,0 @@
import React, { useState } from "react";
import "../../styles/AddUser.css";
import { getFileName } from "../../constant/Utils";
// import SelectFolder from "../../premitives/SelectFolder";
const EditTemplate = ({ template, onSuccess }) => {
// const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
const [formData, setFormData] = useState({
Name: template?.Name || "",
Note: template?.Note || "",
Description: template?.Description || "",
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false"
});
const handleStrInput = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
// const handleFolder = (data) => {
// console.log("handleFolder ", data)
// setFolder(data);
// };
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
const isChecked = formData.SendinOrder === "true" ? true : false;
const data = { ...formData, SendinOrder: isChecked };
onSuccess(data);
};
return (
<div className="addusercontainer">
<div className="form-wrapper">
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name" style={{ fontSize: 13 }}>
File
</label>
<div
style={{
padding: "0.5rem 0.75rem",
border: "1px solid #d1d5db",
borderRadius: "0.375rem",
fontSize: "0.75rem",
fontWeight: "700"
}}
>
{getFileName(template.URL)}
</div>
</div>
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
Name
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
name="Name"
value={formData.Name}
onChange={(e) => handleStrInput(e)}
required
className="addUserInput"
/>
</div>
<div className="form-section">
<label htmlFor="Note" style={{ fontSize: 13 }}>
Note
</label>
<input
type="text"
name="Note"
id="Note"
value={formData.Note}
onChange={(e) => handleStrInput(e)}
className="addUserInput"
/>
</div>
<div className="form-section">
<label htmlFor="Description" style={{ fontSize: 13 }}>
Description
</label>
<input
type="text"
name="Description"
id="Description"
value={formData.Description}
onChange={(e) => handleStrInput(e)}
className="addUserInput"
/>
</div>
<div className="form-section">
<label style={{ fontSize: 13 }}>Send In Order</label>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginLeft: 8,
marginBottom: 5
}}
>
<input
type="radio"
value={"true"}
name="SendinOrder"
checked={formData.SendinOrder === "true"}
onChange={handleStrInput}
/>
<div style={{ fontSize: 12 }}>Yes</div>
</div>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginLeft: 8,
marginBottom: 5
}}
>
<input
type="radio"
value={"false"}
name="SendinOrder"
checked={formData.SendinOrder === "false"}
onChange={handleStrInput}
/>
<div style={{ fontSize: 12 }}>No</div>
</div>
</div>
{/* <SelectFolder onSuccess={handleFolder} folderCls={"contracts_Template"} /> */}
<div className="buttoncontainer">
<button type="submit" className="submitbutton">
Submit
</button>
</div>
</form>
</div>
</div>
);
};
export default EditTemplate;
@@ -1,157 +0,0 @@
import React from "react";
import { Quill } from "react-quill";
// Custom Undo button icon component for Quill editor. You can import it directly
// from 'quill/assets/icons/undo.svg' but I found that a number of loaders do not
// handle them correctly
const CustomUndo = () => (
<svg viewBox="0 0 18 18">
<polygon className="ql-fill ql-stroke" points="6 10 4 12 2 10 6 10" />
<path
className="ql-stroke"
d="M8.09,13.91A4.6,4.6,0,0,0,9,14,5,5,0,1,0,4,9"
/>
</svg>
);
// Redo button icon component for Quill editor
const CustomRedo = () => (
<svg viewBox="0 0 18 18">
<polygon className="ql-fill ql-stroke" points="12 10 14 12 16 10 12 10" />
<path
className="ql-stroke"
d="M9.91,13.91A4.6,4.6,0,0,1,9,14a5,5,0,1,1,5-5"
/>
</svg>
);
// Undo and redo functions for Custom Toolbar
function undoChange() {
this.quill.history.undo();
}
function redoChange() {
this.quill.history.redo();
}
// Add sizes to whitelist and register them
const Size = Quill.import("formats/size");
Size.whitelist = ["extra-small", "small", "medium", "large"];
Quill.register(Size, true);
// Add fonts to whitelist and register them
const Font = Quill.import("formats/font");
Font.whitelist = [
"arial",
"comic-sans",
"courier-new",
"georgia",
"helvetica",
"lucida"
];
Quill.register(Font, true);
// Modules object for setting up the Quill editor
export const module1 = {
toolbar: {
container: "#toolbar1",
handlers: {
undo: undoChange,
redo: redoChange
}
},
history: {
delay: 500,
maxStack: 100,
userOnly: true
}
};
// Formats objects for setting up the Quill editor
export const formats = [
"header",
"font",
"size",
"bold",
"italic",
"underline",
"align",
"strike",
"script",
"blockquote",
"background",
"list",
"bullet",
"indent",
"link",
"image",
"color",
"code-block"
];
// Quill Toolbar component
export const QuillToolbar = ({ containerId }) => (
<div id={containerId}>
<span className="ql-formats">
<select className="ql-font" defaultValue="arial">
<option value="arial">Arial</option>
<option value="comic-sans">Comic Sans</option>
<option value="courier-new">Courier New</option>
<option value="georgia">Georgia</option>
<option value="helvetica">Helvetica</option>
<option value="lucida">Lucida</option>
</select>
<select className="ql-size" defaultValue="medium">
<option value="extra-small">Size 1</option>
<option value="small">Size 2</option>
<option value="medium">Size 3</option>
<option value="large">Size 4</option>
</select>
<select className="ql-header" defaultValue="3">
<option value="1">Heading</option>
<option value="2">Subheading</option>
<option value="3">Normal</option>
</select>
</span>
<span className="ql-formats">
<button className="ql-bold" />
<button className="ql-italic" />
<button className="ql-underline" />
<button className="ql-strike" />
</span>
<span className="ql-formats">
<button className="ql-list" value="ordered" />
<button className="ql-list" value="bullet" />
<button className="ql-indent" value="-1" />
<button className="ql-indent" value="+1" />
</span>
<span className="ql-formats">
<button className="ql-script" value="super" />
<button className="ql-script" value="sub" />
<button className="ql-blockquote" />
<button className="ql-direction" />
</span>
<span className="ql-formats">
<select className="ql-align" />
<select className="ql-color" />
<select className="ql-background" />
</span>
<span className="ql-formats">
<button className="ql-link" />
<button className="ql-image" />
<button className="ql-video" />
</span>
<span className="ql-formats">
<button className="ql-formula" />
<button className="ql-code-block" />
<button className="ql-clean" />
</span>
<span className="ql-formats">
<button className="ql-undo">
<CustomUndo />
</button>
<button className="ql-redo">
<CustomRedo />
</button>
</span>
</div>
);
export default QuillToolbar;
@@ -1,52 +0,0 @@
import React from "react";
import Tooltip from "../../primitives/Tooltip";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
import EditorToolbar, { module1, formats } from "./EditorToolbar";
export function EmailBody(props) {
return (
<React.Fragment>
<div className={`flex justify-center py-1 p-2`}>
<div className=" flex flex-col">
<form>
<div className="text-lg font-normal text-black">
<div className=" m-2 md:m-10 p-3 md:p-10 shadow-md hover:shadow-lg border-[1px] border-indigo-800 rounded-md">
<label className="text-sm ml-2">
Subject <Tooltip message={"email subject"} />
</label>
<input
required
value={props.requestSubject}
onChange={(e) => props.setRequestSubject(e.target.value)}
placeholder='${senderName} has requested you to sign "${documentName}"'
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
<label className="text-sm ml-2 mt-3">
Body <Tooltip message={"email body"} />
</label>
<div className="px-3 py-2 w-full focus:outline-none text-xs">
<EditorToolbar containerId="toolbar1" />
<ReactQuill
theme="snow"
value={props.requestBody}
// value={typedText}
placeholder="add body of email "
// readOnly={loading}
// onChangeSelection={handleTextSelection} // Listen for text selection
ref={props.editorRef}
modules={module1}
formats={formats}
onChange={props.handleOnchangeRequest}
/>
</div>
</div>
</div>
</form>
</div>
</div>
</React.Fragment>
);
}
@@ -1,434 +0,0 @@
import React, { useState } from "react";
import { saveAs } from "file-saver";
import celebration from "../../assets/images/newCeleb.gif";
import axios from "axios";
import { getBase64FromUrl } from "../../constant/Utils";
import { themeColor } from "../../constant/const";
import printModule from "print-js";
import loader from "../../assets/images/loader2.gif";
function EmailComponent({
isEmail,
pdfUrl,
isCeleb,
setIsEmail,
setSuccessEmail,
pdfName,
sender,
setIsAlert,
extUserId,
activeMailAdapter
}) {
const [emailList, setEmailList] = useState([]);
const [emailValue, setEmailValue] = useState();
const [isLoading, setIsLoading] = useState(false);
//function for send email
const sendEmail = async () => {
setIsLoading(true);
let sendMail;
for (let i = 0; i < emailList.length; i++) {
try {
const imgPng =
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3/`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
};
const openSignUrl = "https://www.opensignlabs.com/contact-us";
const themeBGcolor = themeColor;
let params = {
mailProvider: activeMailAdapter,
extUserId: extUserId,
pdfName: pdfName,
url: pdfUrl,
recipient: emailList[i],
subject: `${sender.name} has signed the doc - ${pdfName}`,
from: sender.email,
html:
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=" +
imgPng +
" height='50' style='padding:20px,width:170px,height:40px'/> </div><div style='padding:2px;font-family:system-ui; background-color:" +
themeBGcolor +
";'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document Copy</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document <strong>" +
pdfName +
" </strong>is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender.email +
" directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
openSignUrl +
" target=_blank>here</a> </p></div></div></body></html>"
};
sendMail = await axios.post(url, params, { headers: headers });
} catch (error) {
console.log("error", error);
setIsLoading(false);
setIsEmail(false);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
}
if (sendMail && sendMail.data.result.status === "success") {
setSuccessEmail(true);
setTimeout(() => {
setSuccessEmail(false);
setIsEmail(false);
setEmailValue("");
setEmailList([]);
}, 1500);
setIsLoading(false);
} else if (sendMail && sendMail.data.result.status === "error") {
setIsLoading(false);
setIsEmail(false);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
setEmailValue("");
setEmailList([]);
} else {
setIsLoading(false);
setIsEmail(false);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
setEmailValue("");
setEmailList([]);
}
};
//function for remove email
const removeChip = (index) => {
const updateEmailCount = emailList.filter((data, key) => key !== index);
setEmailList(updateEmailCount);
};
//function for get email value
const handleEmailValue = (e) => {
const value = e.target.value;
setEmailValue(value);
};
//function for save email in array after press enter
const handleEnterPress = (e) => {
if (e.key === "Enter" && emailValue) {
setEmailList((prev) => [...prev, emailValue]);
setEmailValue("");
} else if (e === "add" && emailValue) {
setEmailList((prev) => [...prev, emailValue]);
setEmailValue("");
}
};
// function for print signed pdf
const handleToPrint = async (event) => {
event.preventDefault();
const pdf = await getBase64FromUrl(pdfUrl);
const isAndroidDevice = navigator.userAgent.match(/Android/i);
const isAppleDevice =
(/iPad|iPhone|iPod/.test(navigator.platform) ||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
!window.MSStream;
if (isAndroidDevice || isAppleDevice) {
const byteArray = Uint8Array.from(
atob(pdf)
.split("")
.map((char) => char.charCodeAt(0))
);
const blob = new Blob([byteArray], { type: "application/pdf" });
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, "_blank");
} else {
printModule({ printable: pdf, type: "pdf", base64: true });
}
};
//handle download signed pdf
const handleDownloadPdf = () => {
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
};
const sanitizeFileName = (pdfName) => {
// Replace spaces with underscore
return pdfName.replace(/ /g, "_");
};
const isAndroid = /Android/i.test(navigator.userAgent);
return (
<div>
{/* isEmail */}
{isEmail && (
<div className="modaloverlay">
<div className="modalcontainer">
{isLoading && (
<div
style={{
position: "absolute",
height: "100%",
width: "100%",
display: "flex",
justifyContent: "center",
flexDirection: "column",
alignItems: "center",
zIndex: "20",
backgroundColor: "#e6f2f2",
opacity: 0.8
}}
>
<img
alt="no img"
src={loader}
style={{ width: "70px", height: "70px" }}
/>
<span style={{ fontSize: "12px", fontWeight: "bold" }}>
This might take some time
</span>
</div>
)}
<div style={{ background: "#32a3ac" }} className="modalheader">
<span style={{ color: "white" }}>Successfully signed!</span>
<div style={{ display: "flex", flexDirection: "row" }}>
<div></div>
{!isAndroid && (
<button
onClick={handleToPrint}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="btn btn-primary btn-sm"
>
<i
className="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white"
}}
aria-hidden="true"
></i>
Print
</button>
)}
<button
className="btn btn-danger btn-sm"
onClick={() => handleDownloadPdf()}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginLeft: "10px"
}}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Download
</button>
</div>
</div>
<div style={{ height: "100%", padding: 20 }}>
{isCeleb && (
<div
style={{
position: "absolute",
marginLeft: "50px"
}}
>
<img
alt="print img"
width={300}
height={250}
// style={styles.gifCeleb}
src={celebration}
/>
</div>
)}
<p
style={{
fontFamily: "system-ui",
verticalAlign: "baseline",
fontWeight: "500",
color: "#403f3e",
fontSize: "15px",
marginBottom: "5px"
}}
>
Recipients added here will get a copy of the signed document.
</p>
{emailList.length > 0 ? (
<>
<div className="addEmail">
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap"
}}
>
{emailList.map((data, ind) => {
return (
<div
className="emailChip"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
key={ind}
>
<span
style={{
color: "white",
fontSize: "13px"
}}
>
{data}
</span>
<span
style={{
color: "white",
fontSize: 13,
fontWeight: 600,
marginLeft: 7,
cursor: "pointer"
}}
onClick={() => removeChip(ind)}
>
<i className="fa-solid fa-xmark"></i>
</span>
</div>
);
})}
</div>
{emailList.length <= 9 && (
<input
type="text"
value={emailValue}
className="addEmailInput"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
onBlur={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
required
/>
)}
</div>
</>
) : (
<div>
<input
type="text"
value={emailValue}
className="emailInput"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
placeholder="Add the email addresses"
onBlur={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
required
/>
</div>
)}
<span
style={{
cursor: emailValue && "pointer"
}}
onClick={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
>
<i
style={{
backgroundColor: themeColor,
padding: "5px 7px",
marginTop: "10px",
color: "white",
borderRadius: "2px"
}}
className="fa fa-plus"
aria-hidden="true"
></i>
</span>
<div
style={{
background: "#e3e2e1",
marginTop: "10px",
padding: "5px",
borderRadius: "3px"
}}
>
<span style={{ fontWeight: "700" }}>Note:</span>
<span style={{ fontSize: "15px" }}>
{" "}
You can only send to ten recipients at a time.
</span>
</div>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<button
disabled={emailList.length === 0 && true}
style={{
background: themeColor,
color: "white"
}}
type="button"
className={emailList.length === 0 ? "defaultBtn" : "finishBtn"}
onClick={() => sendEmail()}
>
Send
</button>
<button
type="button"
className="finishBtn cancelBtn"
onClick={() => {
setIsEmail(false);
setEmailValue("");
setEmailList([]);
}}
>
Close
</button>
</div>
</div>
</div>
)}
</div>
);
}
export default EmailComponent;
@@ -1,617 +0,0 @@
import React, { useState } from "react";
import PrevNext from "./PrevNext";
import printModule from "print-js";
import { getBase64FromUrl } from "../../constant/Utils";
import { saveAs } from "file-saver";
import "../../styles/signature.css";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { useNavigate } from "react-router-dom";
import { themeColor } from "../../constant/const";
import axios from "axios";
import ModalUi from "../../primitives/ModalUi";
import { appInfo } from "../../constant/appinfo";
function Header({
isPdfRequestFiles,
isPlaceholder,
setIsDecline,
pageNumber,
allPages,
changePage,
pdfUrl,
embedWidgetsData,
pdfDetails,
signerPos,
signersdata,
isMailSend,
alertSendEmail,
isCompleted,
isShowHeader,
decline,
currentSigner,
dataTut4,
alreadySign,
isSignYourself,
setIsEmail,
completeBtnTitle,
setIsEditTemplate
}) {
const filterPrefill =
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
const isMobile = window.innerWidth < 767;
const navigate = useNavigate();
const [isCertificate, setIsCertificate] = useState(false);
const isGuestSigner = localStorage.getItem("isGuestSigner");
//for go to previous page
function previousPage() {
changePage(-1);
}
//for go to next page
function nextPage() {
changePage(1);
}
//function for show decline alert
const handleDeclinePdfAlert = async () => {
const currentDecline = {
currnt: "Sure",
isDeclined: true
};
setIsDecline(currentDecline);
};
//function for print digital sign pdf
const handleToPrint = async (event) => {
event.preventDefault();
try {
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
const axiosRes = await axios.post(
`${appInfo.baseUrl}/functions/getsignedurl`,
{ url: pdfUrl },
{
headers: {
"content-type": "Application/json",
"X-Parse-Application-Id": appInfo.appId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
);
const url = axiosRes.data.result;
const pdf = await getBase64FromUrl(url);
const isAndroidDevice = navigator.userAgent.match(/Android/i);
const isAppleDevice =
(/iPad|iPhone|iPod/.test(navigator.platform) ||
(navigator.platform === "MacIntel" &&
navigator.maxTouchPoints > 1)) &&
!window.MSStream;
if (isAndroidDevice || isAppleDevice) {
const byteArray = Uint8Array.from(
atob(pdf)
.split("")
.map((char) => char.charCodeAt(0))
);
const blob = new Blob([byteArray], { type: "application/pdf" });
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, "_blank");
} else {
printModule({ printable: pdf, type: "pdf", base64: true });
}
} catch (err) {
console.log("err in getsignedurl", err);
alert("something went wrong, please try again later.");
}
};
//handle download signed pdf
const handleDownloadPdf = async () => {
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
try {
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
const axiosRes = await axios.post(
`${appInfo.baseUrl}/functions/getsignedurl`,
{ url: pdfUrl },
{
headers: {
"content-type": "Application/json",
"X-Parse-Application-Id": appInfo.appId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
);
const url = axiosRes.data.result;
saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
} catch (err) {
console.log("err in getsignedurl", err);
alert("something went wrong, please try again later.");
}
};
const sanitizeFileName = (pdfName) => {
// Replace spaces with underscore
return pdfName.replace(/ /g, "_");
};
//handle download signed pdf
const handleDownloadCertificate = async () => {
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
try {
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
} catch (err) {
console.log("err in download in certificate", err);
}
} else {
setIsCertificate(true);
try {
const data = {
docId: pdfDetails[0]?.objectId
};
const docDetails = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getDocument`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
}
}
);
if (docDetails.data && docDetails.data.result) {
const doc = docDetails.data.result;
if (doc?.CertificateUrl) {
await fetch(doc?.CertificateUrl);
const certificateUrl = doc?.CertificateUrl;
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
setIsCertificate(false);
} else {
setIsCertificate(true);
}
}
} catch (err) {
console.log("err in download in certificate", err);
alert("something went wrong, please try again later.");
}
}
};
return (
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
{isMobile && isShowHeader ? (
<div
id="navbar"
className={isGuestSigner ? "stickySignerHead" : "stickyHead"}
style={{
width: isGuestSigner
? window.innerWidth
: window.innerWidth - 30 + "px"
}}
>
<div className="preBtn2">
<div onClick={() => navigate(-1)}>
<i
className="fa fa-arrow-left"
aria-hidden="true"
style={{ color: themeColor, cursor: "pointer" }}
></i>
</div>
<div>
<i
onClick={() => {
if (pageNumber > 1) {
previousPage();
}
}}
className="fa fa-backward"
style={{ color: "gray", cursor: "pointer" }}
></i>
<span
style={{
fontSize: "13px",
fontWeight: "600",
marginLeft: "10px",
marginRight: "10px"
}}
>
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<i
onClick={() => {
if (pageNumber < allPages) {
nextPage();
}
}}
className="fa fa-forward"
style={{ color: "gray", cursor: "pointer" }}
></i>
</div>
{pdfUrl && alreadySign ? (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div
style={{
color: themeColor,
border: "none",
fontWeight: "650",
fontSize: "16px",
padding: "0px 3px 0px 5px"
}}
>
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="DropdownMenuContent"
sideOffset={5}
>
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => handleDownloadPdf()}
>
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<i
className="fa fa-arrow-down"
aria-hidden="true"
style={{ marginRight: "2px" }}
></i>
Download
</div>
</DropdownMenu.Item>
{isCompleted && (
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => handleDownloadCertificate()}
>
<div
style={{
border: "none",
backgroundColor: "#fff"
}}
>
<i
className="fa-solid fa-award"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Certificate
</div>
</DropdownMenu.Item>
)}
{isSignYourself && (
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => setIsEmail(true)}
>
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<i
className="fa fa-envelope"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Mail
</div>
</DropdownMenu.Item>
)}
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={handleToPrint}
>
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<i
className="fa fa-print"
aria-hidden="true"
style={{ marginRight: "2px" }}
></i>
Print
</div>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
) : (
<div style={{ display: "flex", justifyContent: "space-around" }}>
{/* current signer is checking user send request and check status of pdf sign than if current
user exist than show finish button else no
*/}
{currentSigner && (
<>
{decline && (
<div
onClick={() => {
handleDeclinePdfAlert();
}}
style={{
color: "red",
border: "none",
fontWeight: "650",
fontSize: "16px",
marginRight: "10px"
}}
>
Decline
</div>
)}
{isPlaceholder ? (
<div
onClick={() => {
if (!isMailSend) {
alertSendEmail();
}
}}
style={{
color: isMailSend ? "gray" : themeColor,
border: "none",
fontWeight: "650",
fontSize: "16px"
}}
data-tut={dataTut4}
>
{completeBtnTitle ? completeBtnTitle : "Send"}
</div>
) : (
<div
data-tut="reactourThird"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
} else if (isPdfRequestFiles) {
embedWidgetsData();
}
}}
style={{
color: themeColor,
border: "none",
fontWeight: "650",
fontSize: "16px"
}}
>
Finish
</div>
)}
</>
)}
</div>
)}
</div>
</div>
) : (
<div className="flex flex-wrap justify-between items-center mt-[5px]">
<PrevNext
pageNumber={pageNumber}
allPages={allPages}
changePage={changePage}
/>
{isPlaceholder ? (
<>
<div className="flex mx-[100px] lg:mx-0 order-last lg:order-none">
{!isMailSend &&
signersdata.length > 0 &&
signersdata.length !== filterPrefill.length && (
<div>
{filterPrefill.length === 0 ? (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.length - filterPrefill.length}{" "}
recipients signature
</span>
) : (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.length - filterPrefill.length} more
recipients signature
</span>
)}
</div>
)}
</div>
<div className="flex">
{setIsEditTemplate && (
<button
onClick={() => setIsEditTemplate(true)}
className="outline-none border-none text-center mr-[5px]"
>
<i className="fa-solid fa-gear fa-lg"></i>
</button>
)}
<button
onClick={() => navigate(-1)}
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
>
Back
</button>
<button
disabled={isMailSend && true}
data-tut="reactourFour"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] font-[500] text-sm mr-[5px]"
style={{
background: isMailSend ? "rgb(203, 203, 203)" : "#188ae2",
color: isMailSend ? "rgb(77, 75, 75)" : "white"
}}
onClick={() => {
alertSendEmail();
}}
>
{completeBtnTitle
? completeBtnTitle
: isMailSend
? "Sent"
: "Send"}
</button>
</div>
</>
) : isPdfRequestFiles ? (
alreadySign ? (
<div style={{ display: "flex", flexDirection: "row" }}>
{isCompleted && (
<button
type="button"
onClick={() => handleDownloadCertificate()}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
>
<i
className="fa-solid fa-award py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block ml-1">Certificate</span>
</button>
)}
<button
onClick={handleToPrint}
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
>
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Print</span>
</button>
<button
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
onClick={() => handleDownloadPdf()}
>
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Download</span>
</button>
</div>
) : (
<div className="flex">
<button
onClick={() => navigate(-1)}
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
>
Back
</button>
{currentSigner && (
<>
<button
style={{ background: "#de4337" }}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
onClick={() => {
handleDeclinePdfAlert();
}}
>
Decline
</button>
<button
style={{
background: "#188ae2"
}}
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
onClick={() => {
embedWidgetsData();
}}
>
Finish
</button>
</>
)}
</div>
)
) : isCompleted ? (
<div style={{ display: "flex", flexDirection: "row" }}>
{isCompleted && (
<button
type="button"
onClick={() => handleDownloadCertificate()}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
>
<i
className="fa-solid fa-award py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block ml-1">Certificate</span>
</button>
)}
<button
onClick={handleToPrint}
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
>
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Print</span>
</button>
<button
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
onClick={() => handleDownloadPdf()}
>
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Download</span>
</button>
<button
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#3ba7e5]"
onClick={() => setIsEmail(true)}
>
<i className="fa fa-envelope py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Mail</span>
</button>
</div>
) : (
<div className="flex">
<button
onClick={() => {
navigate(-1);
}}
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
>
Back
</button>
<button
style={{
background: "#188ae2"
}}
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
}
}}
>
Finish
</button>
</div>
)}
</div>
)}
<ModalUi
isOpen={isCertificate}
title={"Generating certificate"}
handleClose={() => setIsCertificate(false)}
>
<div className="p-3 md:p-5 text-[13px] md:text-base text-center">
<p>
Your completion certificate is being generated. Please wait
momentarily.
</p>
<p>
If the download doesn&apos;t start shortly, click the button again.
</p>
</div>
</ModalUi>
</div>
);
}
export default Header;
@@ -1,759 +0,0 @@
import React, { useState, useEffect } from "react";
import BorderResize from "./BorderResize";
import PlaceholderBorder from "./PlaceholderBorder";
import { Rnd } from "react-rnd";
import {
defaultWidthHeight,
handleCopyNextToWidget,
isMobile,
onChangeInput,
radioButtonWidget,
textInputWidget,
textWidget
} from "../../constant/Utils";
import PlaceholderType from "./PlaceholderType";
import moment from "moment";
import "../../styles/opensigndrive.css";
const selectFormat = (data) => {
switch (data) {
case "L":
return "MM/dd/yyyy";
case "DD-MM-YYYY":
return "dd-MM-yyyy";
case "DD/MM/YYYY":
return "dd/MM/yyyy";
case "LL":
return "MMMM dd, yyyy";
case "DD MMM, YYYY":
return "dd MMM, yyyy";
case "YYYY-MM-DD":
return "yyyy-MM-dd";
case "MM-DD-YYYY":
return "MM-dd-yyyy";
case "MM.DD.YYYY":
return "MM.dd.yyyy";
case "MMM DD, YYYY":
return "MMM dd, yyyy";
case "DD MMMM, YYYY":
return "dd MMMM, yyyy";
default:
return "MM/dd/yyyy";
}
};
const changeDateToMomentFormat = (format) => {
switch (format) {
case "MM/dd/yyyy":
return "L";
case "dd-MM-yyyy":
return "DD-MM-YYYY";
case "dd/MM/yyyy":
return "DD/MM/YYYY";
case "MMMM dd, yyyy":
return "LL";
case "dd MMM, yyyy":
return "DD MMM, YYYY";
case "yyyy-MM-dd":
return "YYYY-MM-DD";
case "MM-dd-yyyy":
return "MM-DD-YYYY";
case "MM.dd.yyyy":
return "MM.DD.YYYY";
case "MMM dd, yyyy":
return "MMM DD, YYYY";
case "dd MMMM, yyyy":
return "DD MMMM, YYYY";
default:
return "L";
}
};
//function to get default date
const getDefaultdate = (selectedDate, format = "dd-MM-yyyy") => {
let date;
if (format && format === "dd-MM-yyyy") {
const newdate = selectedDate
? selectedDate
: moment(new Date()).format(changeDateToMomentFormat(format));
const [day, month, year] = newdate.split("-");
date = new Date(`${year}-${month}-${day}`);
} else {
date = new Date(selectedDate);
}
const value = date;
return value;
};
//function to get default format
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
function Placeholder(props) {
const [isDraggingEnabled, setDraggingEnabled] = useState(true);
const [isShowDateFormat, setIsShowDateFormat] = useState(false);
const [selectDate, setSelectDate] = useState({
date:
props.pos.type === "date"
? moment(
getDefaultdate(
props?.pos?.options?.response,
props.pos?.options?.validation?.format
).getTime()
).format(
changeDateToMomentFormat(props.pos?.options?.validation?.format)
)
: "",
format:
props.pos.type === "date"
? getDefaultFormat(props.pos?.options?.validation?.format)
: ""
});
const [dateFormat, setDateFormat] = useState([]);
const [startDate, setStartDate] = useState(
props.pos.type === "date" &&
getDefaultdate(
props?.pos?.options?.response,
props.pos?.options?.validation?.format
)
);
const [getCheckboxRenderWidth, setGetCheckboxRenderWidth] = useState({
width: null,
height: null
});
const dateFormatArr = [
"L",
"DD-MM-YYYY",
"YYYY-MM-DD",
"MM.DD.YYYY",
"MM-DD-YYYY",
"MMM DD, YYYY",
"LL",
"DD MMM, YYYY",
"DD MMMM, YYYY"
];
useEffect(() => {
const updateWidth = () => {
const rndElement = document.getElementById(props.pos.key);
if (rndElement) {
const { width, height } = rndElement.getBoundingClientRect();
setGetCheckboxRenderWidth({ width: width, height: height });
}
};
// Delay to ensure rendering is complete
const timer = setTimeout(updateWidth, 0);
return () => clearTimeout(timer);
}, [props.pos]);
useEffect(() => {
const closeMenuOnOutsideClick = (e) => {
if (!isDraggingEnabled && !e.target.closest("#changeIsDragging")) {
setDraggingEnabled(true);
}
};
document.addEventListener("click", closeMenuOnOutsideClick);
return () => {
// Cleanup the event listener when the component unmounts
document.removeEventListener("click", closeMenuOnOutsideClick);
};
}, [isDraggingEnabled]);
//function change format array list with selected date and format
const changeDateFormat = () => {
const updateDate = [];
dateFormatArr.map((data) => {
let date;
if (selectDate && selectDate.format === "dd-MM-yyyy") {
const [day, month, year] = selectDate.date.split("-");
date = new Date(`${year}-${month}-${day}`);
} else {
date = new Date(selectDate?.date);
}
const milliseconds = date.getTime();
const newDate = moment(milliseconds).format(data);
const dateObj = {
date: newDate,
format: selectFormat(data)
};
updateDate.push(dateObj);
});
setDateFormat(updateDate);
};
useEffect(() => {
if (props.isPlaceholder || props.isSignYourself) {
selectDate && changeDateFormat();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectDate]);
//it detect outside click of date dropdown menu
useEffect(() => {
const closeMenuOnOutsideClick = (e) => {
if (isShowDateFormat && !e.target.closest("#menu-container")) {
setIsShowDateFormat(!isShowDateFormat);
}
};
document.addEventListener("click", closeMenuOnOutsideClick);
return () => {
// Cleanup the event listener when the component unmounts
document.removeEventListener("click", closeMenuOnOutsideClick);
};
}, [isShowDateFormat]);
//`handleWidgetIdandPopup` is used to set current widget id and open relative popup
const handleWidgetIdandPopup = () => {
if (props.setSelectWidgetId) {
props.setSelectWidgetId(props.pos.key);
}
const widgetTypeExist = [
textInputWidget,
"checkbox",
"name",
"company",
"job title",
"date",
"email",
textWidget
].includes(props.pos.type);
if (widgetTypeExist) {
setDraggingEnabled(false);
}
if ((props.isNeedSign || props.isSignYourself) && !props.isDragging) {
if (props.pos.type) {
if (
props.pos.type === "signature" ||
props.pos.type === "stamp" ||
props.pos.type === "image"
) {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
} else if (props.pos.type === "initials") {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
props.setIsInitial(true);
}
} else {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos?.isStamp ? props.pos.isStamp : false);
}
} else if (
props.isPlaceholder &&
!props.isDragging &&
props.pos.type !== textWidget
) {
if (props.pos.key === props.selectWidgetId) {
props.handleLinkUser(props.data.Id);
props.setUniqueId(props.data.Id);
}
} else if (!props.pos.type) {
if (
!props.pos.type &&
props.isNeedSign &&
props.data.signerObjId === props.signerObjId
) {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
} else if (
(props.isNeedSign && props.pos.type === "signature") ||
props.pos.type === "stamp"
) {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
} else if (props.isNeedSign && props.pos.type === "dropdown") {
props.setSignKey(props.pos.key);
}
}
};
const handleOnClickPlaceholder = () => {
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
if (!props.isNeedSign) {
props.setWidgetType(props.pos.type);
}
if (props.isNeedSign && props.data?.signerObjId === props.signerObjId) {
handleWidgetIdandPopup();
} else if (props.isPlaceholder || props.isSignYourself) {
handleWidgetIdandPopup();
}
};
//`handleOnClickSettingIcon` is used set current widget details and open setting of it
const handleOnClickSettingIcon = () => {
if (props.pos.type === radioButtonWidget) {
props.setIsRadio(true);
} else if (props.pos.type === "dropdown") {
props?.setShowDropdown(true);
} else if (props.pos.type === "checkbox") {
props?.setIsCheckbox(true);
} else {
props?.handleNameModal(true);
}
if (props.isPlaceholder && props.type !== textWidget) {
props.setUniqueId(props.data.Id);
}
props.setSignKey(props.pos.key);
props.setWidgetType(props.pos.type);
props.setCurrWidgetsDetails(props.pos);
};
//function to set required state value onclick on widget's copy icon
const handleCopyPlaceholder = (e) => {
e.stopPropagation();
//condition to handle text widget signer obj id and unique id
//when user click on copy icon of text widget in that condition text widget does not have any signerObjId
//in that case i have to save in tempSignerId as a unique id of previous select signer's unique id
//and on save or cancel button of copy all page popup i have set this temp signer Id in unique id
if (props.data && props?.pos?.type !== textWidget) {
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
} else if (props.data && props.pos.type === textWidget) {
props.setTempSignerId(props.uniqueId);
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
}
//checking widget's type and open widget copy modal for required widgets
if (
["signature", textWidget, "stamp", "initial"].includes(props.pos.type)
) {
props.setIsPageCopy(true);
props.setSignKey(props.pos.key);
} else {
//function to create new widget next to just widget
handleCopyNextToWidget(
props.pos,
props.pos.type,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id
);
}
};
//function to save date and format on local array onchange date and onclick format
const handleSaveDate = (data, isDateChange) => {
let updateDate = data.date;
//check if date change by user
if (isDateChange) {
//`changeDateToMomentFormat` is used to convert date as per required to moment package
updateDate = moment(data.date).format(
changeDateToMomentFormat(data.format)
);
}
//using moment package is used to change date as per the format provided in selectDate obj e.g. - MM/dd/yyyy -> 03/12/2024
//`getDefaultdate` is used to convert update date in new Date() format
const date = moment(
getDefaultdate(updateDate, data?.format).getTime()
).format(changeDateToMomentFormat(data?.format));
//`onChangeInput` is used to save data related to date in a placeholder field
onChangeInput(
date,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data.Id,
false,
data?.format
);
setSelectDate({ date: date, format: data?.format });
};
const PlaceholderIcon = () => {
return (
props.isShowBorder && (
<>
{(props.isPlaceholder || props.isSignYourself) && (
<>
{props.pos.type === "checkbox" && props.isSignYourself ? (
<i
onClick={(e) => {
e.stopPropagation();
handleOnClickSettingIcon();
}}
onTouchEnd={(e) => {
e.stopPropagation();
handleOnClickSettingIcon();
}}
className="fa-solid fa-gear settingIcon"
style={{
color: "#188ae2",
right: "29px",
top: "-19px"
}}
></i>
) : (
((!props?.pos?.type && props.pos.isStamp) ||
(props?.pos?.type &&
!["date", textWidget, "signature"].includes(
props.pos.type
) &&
!props.isSignYourself)) && (
<i
onClick={(e) => {
e.stopPropagation();
handleOnClickSettingIcon();
}}
onTouchEnd={(e) => {
e.stopPropagation();
handleOnClickSettingIcon();
}}
className="fa-solid fa-gear settingIcon"
style={{ color: "#188ae2", right: "47px", top: "-19px" }}
></i>
)
)}
{props.pos.type !== textWidget && !props.isSignYourself && (
<i
data-tut="assignSigner"
className="fa-regular fa-user signUserIcon"
onClick={(e) => {
e.stopPropagation();
props.handleLinkUser(props.data.Id);
props.setUniqueId(props.data.Id);
}}
onTouchEnd={(e) => {
e.stopPropagation();
props.handleLinkUser(props.data.Id);
props.setUniqueId(props.data.Id);
}}
style={{ color: "#188ae2", right: "32px", top: "-18px" }}
></i>
)}
</>
)}
{props.pos.type === "date" && selectDate && (
<div
id="menu-container"
style={{
zIndex: "99",
top: "-19px",
right: props.isPlaceholder ? "60px" : "44px"
}}
className={
isShowDateFormat
? "dropdown signUserIcon show"
: "dropdown signUserIcon"
}
onClick={(e) => {
setIsShowDateFormat(!isShowDateFormat);
e.stopPropagation();
if (props.data) {
props.setSignKey(props.pos.key);
props.setUniqueId(props.data.Id);
}
}}
onTouchEnd={(e) => {
e.stopPropagation();
setIsShowDateFormat(!isShowDateFormat);
if (props.data) {
props.setSignKey(props.pos.key);
props.setUniqueId(props.data.Id);
}
}}
>
<i
className="fa-solid fa-gear settingIcon"
style={{
color: "#188ae2",
fontSize: "14px"
}}
></i>
<div
className={
isShowDateFormat ? "dropdown-menu show" : "dropdown-menu"
}
aria-labelledby="dropdownMenuButton"
aria-expanded={isShowDateFormat ? "true" : "false"}
>
{dateFormat.map((data, ind) => {
return (
<span
key={ind}
onTouchEnd={(e) => {
e.stopPropagation();
setIsShowDateFormat(!isShowDateFormat);
setSelectDate(data);
handleSaveDate(data);
}}
onClick={() => {
setIsShowDateFormat(!isShowDateFormat);
setSelectDate(data);
handleSaveDate(data);
}}
className="dropdown-item itemColor"
style={{ fontSize: "12px" }}
>
{data?.date ? data?.date : "nodata"}
</span>
);
})}
</div>
</div>
)}
<i
className="fa-regular fa-copy signCopy"
onClick={(e) => handleCopyPlaceholder(e)}
onTouchEnd={(e) => handleCopyPlaceholder(e)}
style={{ color: "#188ae2", right: "12px", top: "-18px" }}
></i>
<i
className="fa-regular fa-circle-xmark signCloseBtn"
onClick={(e) => {
e.stopPropagation();
if (props.data) {
props.handleDeleteSign(props.pos.key, props.data.Id);
} else {
props.handleDeleteSign(props.pos.key);
props.setIsStamp(false);
}
}}
onTouchEnd={(e) => {
e.stopPropagation();
if (props.data) {
props.handleDeleteSign(props.pos.key, props.data?.Id);
} else {
props.handleDeleteSign(props.pos.key);
props.setIsStamp(false);
}
}}
style={{ color: "#188ae2", right: "-8px", top: "-18px" }}
></i>
</>
)
);
};
return (
<Rnd
id={props.pos.key}
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
key={props.pos.key}
lockAspectRatio={
// props.pos.type !== textWidget
![
textWidget,
"email",
"name",
"company",
"job title",
textInputWidget
].includes(props.pos.type) &&
(props.pos.Width
? props.pos.Width / props.pos.Height
: defaultWidthHeight(props.pos.type).width /
defaultWidthHeight(props.pos.type).height)
}
enableResizing={{
top: false,
right: false,
bottom: false,
left: false,
topRight: false,
bottomRight:
props.data && props.isNeedSign
? props.data?.signerObjId === props.signerObjId &&
props.pos.type !== "checkbox" &&
props.pos.type !== radioButtonWidget
? true
: false
: props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId &&
true,
bottomLeft: false,
topLeft: false
}}
bounds="parent"
className="signYourselfBlock"
style={{
border: "1px solid #007bff",
borderRadius: "2px",
textAlign:
props.pos.type !== "name" &&
props.pos.type !== "company" &&
props.pos.type !== "job title" &&
"center",
cursor:
props.data && props.isNeedSign
? props.data?.signerObjId === props.signerObjId
? "pointer"
: "not-allowed"
: "all-scroll",
zIndex:
props.pos.type === "date"
? props.pos.key === props.selectWidgetId
? 99 + 1
: 99
: props?.pos?.zIndex
? props.pos.zIndex
: "5",
background: props.data ? props.data.blockColor : "rgb(203 233 237)"
}}
onDrag={() => {
setDraggingEnabled(true);
props.handleTabDrag && props.handleTabDrag(props.pos.key);
}}
size={{
width:
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
? "auto"
: props.posWidth(props.pos, props.isSignYourself),
height:
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
? "auto"
: props.posHeight(props.pos, props.isSignYourself)
}}
onResizeStart={() => {
setDraggingEnabled(true);
props.setIsResize && props.setIsResize(true);
}}
onResizeStop={() => {
props.setIsResize && props.setIsResize(false);
}}
disableDragging={
props.isNeedSign
? true
: props.isPlaceholder && ![textWidget].includes(props.pos.type)
? false
: !isDraggingEnabled
}
onDragStop={(event, dragElement) =>
props.handleStop &&
props.handleStop(event, dragElement, props.data?.Id, props.pos?.key)
}
default={{
x: props.xPos(props.pos, props.isSignYourself),
y: props.yPos(props.pos, props.isSignYourself)
}}
onResize={(e, direction, ref) => {
props.handleSignYourselfImageResize &&
props.handleSignYourselfImageResize(
ref,
props.pos.key,
props.xyPostion,
props.setXyPostion,
props.index,
props.data && props.data.Id,
false
);
}}
onClick={() => handleOnClickPlaceholder()}
>
{props.isShowBorder &&
props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId ? (
<BorderResize right={-12} top={-11} />
) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? (
props.data?.signerObjId === props.signerObjId &&
props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" ? (
<BorderResize />
) : (
<></>
)
) : (
props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId && <BorderResize />
)}
{props.isShowBorder && props.pos.key === props.selectWidgetId && (
<PlaceholderBorder
setDraggingEnabled={setDraggingEnabled}
pos={props.pos}
isPlaceholder={props.isPlaceholder}
getCheckboxRenderWidth={getCheckboxRenderWidth}
/>
)}
{isMobile ? (
<div
style={{
left: props.xPos(props.pos, props.isSignYourself),
top: props.yPos(props.pos, props.isSignYourself),
width:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posWidth(props.pos, props.isSignYourself),
// "auto", //props.posWidth(props.pos, props.isSignYourself),
// height: props.posHeight(props.pos, props.isSignYourself),
height:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posHeight(props.pos, props.isSignYourself),
zIndex: "10"
}}
onTouchEnd={() => handleOnClickPlaceholder()}
>
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
<PlaceholderType
pos={props.pos}
xyPostion={props.xyPostion}
index={props.index}
setXyPostion={props.setXyPostion}
data={props.data}
setSignKey={props.setSignKey}
isShowDropdown={props?.isShowDropdown}
isPlaceholder={props.isPlaceholder}
isSignYourself={props.isSignYourself}
signerObjId={props.signerObjId}
handleUserName={props.handleUserName}
setDraggingEnabled={setDraggingEnabled}
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
isNeedSign={props.isNeedSign}
setSelectDate={setSelectDate}
selectDate={selectDate}
setValidateAlert={props.setValidateAlert}
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
/>
</div>
) : (
<>
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
<PlaceholderType
pos={props.pos}
xyPostion={props.xyPostion}
index={props.index}
setXyPostion={props.setXyPostion}
data={props.data}
setSignKey={props.setSignKey}
isShowDropdown={props?.isShowDropdown}
isPlaceholder={props.isPlaceholder}
isSignYourself={props.isSignYourself}
signerObjId={props.signerObjId}
handleUserName={props.handleUserName}
setDraggingEnabled={setDraggingEnabled}
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
isNeedSign={props.isNeedSign}
setSelectDate={setSelectDate}
selectDate={selectDate}
setValidateAlert={props.setValidateAlert}
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
/>
</>
)}
</Rnd>
);
}
export default Placeholder;
@@ -1,52 +0,0 @@
import React from "react";
import { themeColor } from "../../constant/const";
import {
defaultWidthHeight,
isMobile,
radioButtonWidget,
resizeBorderExtraWidth,
textWidget
} from "../../constant/Utils";
function PlaceholderBorder(props) {
const getResizeBorderExtraWidth = resizeBorderExtraWidth();
const defaultWidth = defaultWidthHeight(props.pos.type).width;
const defaultHeight = defaultWidthHeight(props.pos.type).height;
const handleMinWidth = () => {
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
return props.getCheckboxRenderWidth.width + getResizeBorderExtraWidth;
} else {
return props.pos.Width
? props.pos.Width + getResizeBorderExtraWidth
: defaultWidth + getResizeBorderExtraWidth;
}
};
const handleMinHeight = () => {
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
return props.getCheckboxRenderWidth.height + getResizeBorderExtraWidth;
} else {
return props.pos.Height
? props.pos.Height + getResizeBorderExtraWidth
: defaultHeight + getResizeBorderExtraWidth;
}
};
return (
<div
onMouseEnter={() => !isMobile && props?.setDraggingEnabled(true)}
onTouchEnd={() =>
props.pos.type === textWidget && props?.setDraggingEnabled(true)
}
className="borderResize"
style={{
borderColor: themeColor,
borderStyle: "dashed",
minWidth: handleMinWidth(),
minHeight: handleMinHeight(),
borderWidth: "0.2px",
overflow: "hidden"
}}
></div>
);
}
export default PlaceholderBorder;
@@ -1,252 +0,0 @@
import React, { useState } from "react";
import { themeColor } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi";
import { randomId, textWidget } from "../../constant/Utils";
function PlaceholderCopy(props) {
const copyType = ["All pages", "All pages but last", "All pages but first"];
const [selectCopyType, setSelectCopyType] = useState("");
//function for get copy placeholder position
const getCopyPlaceholderPosition = (
type,
currentPlaceholder,
newPageNumber,
existPlaceholderPosition
) => {
const filterPosition =
existPlaceholderPosition &&
existPlaceholderPosition.filter(
(data) => data.xPosition !== currentPlaceholder.xPosition
);
//copy all page placeholder at requested position except first page
if (newPageNumber === 1 && type === "first") {
if (filterPosition && filterPosition.length > 0) {
return {
pageNumber: newPageNumber,
pos: [...filterPosition]
};
}
}
//copy all page placeholder at requested position except last page
else if (newPageNumber === props.allPages && type === "last") {
if (existPlaceholderPosition) {
return {
pageNumber: newPageNumber,
pos: [...filterPosition]
};
}
}
//copy all page placeholder at requested position with existing placeholder position
else if (existPlaceholderPosition) {
return {
pageNumber: newPageNumber,
pos: [...filterPosition, currentPlaceholder]
};
}
//copy all page placeholder at requested position
else {
return {
pageNumber: newPageNumber,
pos: [currentPlaceholder]
};
}
};
//function for copy placeholder as per select copy type
const copyPlaceholder = (type) => {
let newPlaceholderPosition = [];
let newPageNumber = 1;
const signerPosition = props.xyPostion;
const signerId = props.signerObjId ? props.signerObjId : props.Id;
//handle placeholder array and copy for multiple signers placeholder at requested location
if (signerId) {
//get current signers data
let filterSignerPosition;
if (props?.signerObjId) {
filterSignerPosition = signerPosition.filter(
(data) => data.signerObjId === signerId
);
} else {
filterSignerPosition = signerPosition.filter(
(item) => item.Id === signerId
);
}
//get current pagenumber's all placeholder position data
const placeholderPosition = filterSignerPosition[0].placeHolder.filter(
(data) => data.pageNumber === props.pageNumber
);
//get current placeholder position data which user want to copy
const currentPlaceholder = placeholderPosition[0].pos.find(
(position) => position.key === props.signKey
);
for (let i = 0; i < props.allPages; i++) {
const newId = randomId();
currentPlaceholder.key = newId;
//get exist placeholder position for particular page
const existPlaceholder = filterSignerPosition[0].placeHolder.filter(
(data) => data.pageNumber === newPageNumber
);
const existPlaceholderPosition =
existPlaceholder[0] && existPlaceholder[0].pos;
//function for get copy to requested location of placeholder position
const getPlaceholderObj = getCopyPlaceholderPosition(
type,
currentPlaceholder,
newPageNumber,
existPlaceholderPosition
);
if (getPlaceholderObj) {
newPlaceholderPosition.push(getPlaceholderObj);
}
newPageNumber++;
}
let updatedSignerPlaceholder;
if (props?.signerObjId) {
updatedSignerPlaceholder = signerPosition.map((signersData) => {
if (signersData.signerObjId === props.signerObjId) {
return {
...signersData,
placeHolder: newPlaceholderPosition
};
}
return signersData;
});
} else {
updatedSignerPlaceholder = signerPosition.map((signersData) => {
if (signersData.Id === props.Id) {
return {
...signersData,
placeHolder: newPlaceholderPosition
};
}
return signersData;
});
}
const signersData = signerPosition;
signersData.splice(0, signerPosition.length, ...updatedSignerPlaceholder);
props.setXyPostion(signersData);
}
//handle signyourself array and copy for single signers placeholder at requested location
else {
const xyPostion = props.xyPostion;
const placeholderPosition = xyPostion.filter(
(data) => data.pageNumber === props.pageNumber
);
//get current placeholder position data which user want to copy
const currentPlaceholder = placeholderPosition[0].pos.find(
(pos) => pos.key === props.signKey
);
for (let i = 0; i < props.allPages; i++) {
//get exist placeholder position for particular page
const existPlaceholder = xyPostion.filter(
(data) => data.pageNumber === newPageNumber
);
const existPlaceholderPosition =
existPlaceholder[0] && existPlaceholder[0].pos;
const newId = randomId();
currentPlaceholder.key = newId;
//function for get copy to requested location of placeholder position
const getPlaceholderObj = getCopyPlaceholderPosition(
type,
currentPlaceholder,
newPageNumber,
existPlaceholderPosition
);
if (getPlaceholderObj) {
newPlaceholderPosition.push(getPlaceholderObj);
}
newPageNumber++;
}
props.setXyPostion(newPlaceholderPosition);
}
};
//function for getting selected type placeholder copy
const handleApplyCopy = () => {
if (selectCopyType === "All pages") {
copyPlaceholder("all");
} else if (selectCopyType === "All pages but last") {
copyPlaceholder("last");
} else if (selectCopyType === "All pages but first") {
copyPlaceholder("first");
}
};
const handleUniqueId = () => {
if (props.widgetType === textWidget) {
props.setUniqueId(props?.tempSignerId);
props.setTempSignerId("");
}
props.setIsPageCopy(false);
};
return (
<ModalUi
isOpen={props.isPageCopy}
title={"Copy to all pages"}
handleClose={() => {
props.setIsPageCopy(false);
}}
>
<div style={{ height: "100%", padding: 20 }}>
{copyType.map((data, key) => {
return (
<div key={key} style={{ display: "flex", flexDirection: "column" }}>
<label key={key} style={{ fontSize: "16px", fontWeight: "500" }}>
<input
style={{ accentColor: "red", marginRight: "10px" }}
type="radio"
value={data}
onChange={() => setSelectCopyType(data)}
checked={selectCopyType === data}
/>
{data}
</label>
</div>
);
})}
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<button
onClick={() => {
handleApplyCopy();
handleUniqueId();
}}
style={{ background: themeColor }}
type="button"
disabled={!selectCopyType}
className="finishBtn"
>
Apply
</button>
<button
type="button"
className="finishBtn cancelBtn"
onClick={() => {
handleUniqueId();
}}
>
Cancel
</button>
</div>
</ModalUi>
);
}
export default PlaceholderCopy;
@@ -1,837 +0,0 @@
import React, { useEffect, useState, forwardRef, useRef } from "react";
import {
getMonth,
getYear,
isMobile,
onChangeHeightOfTextArea,
onChangeInput,
radioButtonWidget,
range,
textInputWidget,
textWidget
} from "../../constant/Utils";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import "../../styles/signature.css";
import RegexParser from "regex-parser";
function PlaceholderType(props) {
const type = props?.pos?.type;
const [selectOption, setSelectOption] = useState("");
const [validatePlaceholder, setValidatePlaceholder] = useState("");
const inputRef = useRef(null);
const [textValue, setTextValue] = useState();
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
const years = range(1990, getYear(new Date()) + 16, 1);
const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
];
const validateExpression = (regexValidation) => {
if (textValue && regexValidation) {
let regexObject = regexValidation;
if (props.pos?.options?.validation?.type === "regex") {
regexObject = RegexParser(regexValidation);
}
// new RegExp(regexValidation);
let isValidate = regexObject.test(textValue);
if (!isValidate) {
props?.setValidateAlert(true);
inputRef.current.focus();
}
}
};
const handleInputBlur = () => {
props.setDraggingEnabled(true);
const validateType = props.pos?.options?.validation?.type;
let regexValidation;
if (validateType && validateType !== "text") {
switch (validateType) {
case "email":
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
validateExpression(regexValidation);
break;
case "number":
regexValidation = /^[0-9\s]*$/;
validateExpression(regexValidation);
break;
default:
regexValidation = props.pos?.options?.validation?.pattern || "";
validateExpression(regexValidation);
}
}
};
const handleTextValid = (e) => {
const textInput = e.target.value;
setTextValue(textInput);
};
function checkRegularExpress(validateType) {
switch (validateType) {
case "email":
setValidatePlaceholder("demo@gmail.com");
break;
case "number":
setValidatePlaceholder("12345");
break;
case "text":
setValidatePlaceholder("please enter text");
break;
default:
setValidatePlaceholder("please enter value");
}
}
useEffect(() => {
if (type && type === "checkbox" && props.isNeedSign) {
const isDefaultValue = props.pos.options?.defaultValue;
if (isDefaultValue) {
setSelectedCheckbox(isDefaultValue);
}
} else if (props.pos?.options?.hint) {
setValidatePlaceholder(props.pos?.options.hint);
} else if (props.pos?.options?.validation?.type) {
checkRegularExpress(props.pos?.options?.validation?.type);
}
setTextValue(
props.pos?.options?.response
? props.pos?.options?.response
: props.pos?.options?.defaultValue
? props.pos?.options?.defaultValue
: ""
);
setSelectOption(
props.pos?.options?.response
? props.pos?.options?.response
: props.pos?.options?.defaultValue
? props.pos?.options?.defaultValue
: ""
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (
["name", "email", "job title", "company"].includes(props.pos?.type) &&
props.isNeedSign &&
props.data?.signerObjId === props?.signerObjId
) {
const defaultData = props.pos?.options?.defaultValue;
if (defaultData) {
setTextValue(defaultData);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.pos?.options?.defaultValue]);
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
<div
className="inputPlaceholder"
style={{ overflow: "hidden", fontSize: calculateFontSize() }}
onClick={onClick}
ref={ref}
>
{value}
<i className="fa-regular fa-calendar" style={{ marginLeft: "5px" }}></i>
</div>
));
ExampleCustomInput.displayName = "ExampleCustomInput";
useEffect(() => {
if (
["name", "email", "job title", "company"].includes(type) &&
props.isNeedSign &&
props.data?.signerObjId === props.signerObjId
) {
const isDefault = true;
const senderUser = localStorage.getItem(`Extand_Class`);
const jsonSender = JSON.parse(senderUser);
onChangeInput(
jsonSender && jsonSender[0],
null,
props.xyPostion,
null,
props.setXyPostion,
props.data.Id,
isDefault
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [type]);
const calculateFontSize = () => {
const fontSize = 10 + Math.min(props.pos.Width, props.pos.Height) * 0.1;
const size = fontSize ? fontSize : 12;
return size + "px";
};
//function for show checked checkbox
const selectCheckbox = (ind) => {
const res = props.pos.options?.response;
const defaultCheck = props.pos.options?.defaultValue;
if (res && res?.length > 0) {
const isSelectIndex = res.indexOf(ind);
if (isSelectIndex > -1) {
return true;
} else {
return false;
}
// }
} else if (defaultCheck) {
const isSelectIndex = defaultCheck.indexOf(ind);
if (isSelectIndex > -1) {
return true;
} else {
return false;
}
} else {
return false;
}
};
const handleRadioCheck = (data) => {
const defaultData = props.pos.options?.defaultValue;
if (textValue === data) {
return true;
} else if (defaultData === data) {
return true;
} else {
return false;
}
};
//function for set checked and unchecked value of checkbox
const handleCheckboxValue = (isChecked, ind) => {
let updateSelectedCheckbox = [],
checkedList;
let isDefaultValue, isDefaultEmpty;
if (type === "checkbox") {
updateSelectedCheckbox = selectedCheckbox ? selectedCheckbox : [];
if (isChecked) {
updateSelectedCheckbox.push(ind);
setSelectedCheckbox(updateSelectedCheckbox);
} else {
checkedList = selectedCheckbox.filter((data) => data !== ind);
setSelectedCheckbox(checkedList);
}
if (props.isNeedSign) {
isDefaultValue = props.pos.options?.defaultValue;
}
if (isDefaultValue && isDefaultValue.length > 0) {
isDefaultEmpty = true;
}
onChangeInput(
checkedList ? checkedList : updateSelectedCheckbox,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data.Id,
false,
null,
isDefaultEmpty
);
}
};
//function to handle select radio widget and set value seletced by user
const handleCheckRadio = (isChecked, data) => {
let isDefaultValue,
isDefaultEmpty,
isRadio = true;
if (props.isNeedSign) {
isDefaultValue = props.pos.options?.defaultValue;
}
if (isDefaultValue) {
isDefaultEmpty = true;
}
if (isChecked) {
setTextValue(data);
} else {
setTextValue("");
}
onChangeInput(
data,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data.Id,
false,
null,
isDefaultEmpty,
isRadio
);
};
//function to set onchange date
const handleOnDateChange = (date) => {
props.setStartDate(date);
const isDateChange = true;
const dateObj = {
date: date,
format: props.selectDate.format
};
props.handleSaveDate(dateObj, isDateChange);
};
//handle height on enter press in text area
const handleEnterPress = (e) => {
const height = 18;
if (e.key === "Enter") {
//function to save height of text area
onChangeHeightOfTextArea(
height,
props.pos.type,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id
);
}
};
switch (type) {
case "signature":
return props.pos.SignUrl ? (
<img
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
style={{
width: "99%",
height: "100%",
objectFit: "contain"
}}
/>
) : (
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
{props?.handleUserName &&
props?.handleUserName(
props?.data?.Id,
props?.data?.Role,
props?.pos?.type
)}
</div>
);
case "stamp":
return props.pos.SignUrl ? (
<img
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
style={{
width: "99%",
height: "100%",
objectFit: "contain"
}}
/>
) : (
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
{props?.handleUserName &&
props?.handleUserName(
props?.data?.Id,
props?.data?.Role,
props?.pos?.type
)}
</div>
);
case "checkbox":
return (
<div style={{ zIndex: props.isSignYourself && "99" }}>
{props.pos.options?.values?.map((data, ind) => {
return (
<div key={ind} className="flex items-center text-center gap-0.5">
<input
style={{
// width: props.pos.Width,
display: "flex",
justifyContent: "center",
marginTop: ind === 0 ? 0 : "5px"
}}
onBlur={handleInputBlur}
disabled={
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId)
}
type="checkbox"
checked={selectCheckbox(ind)}
onChange={(e) => {
if (e.target.checked) {
if (!props.isPlaceholder) {
const maxRequired =
props.pos.options?.validation?.maxRequiredCount;
const maxCountInt =
maxRequired && parseInt(maxRequired);
if (maxCountInt > 0) {
if (
selectedCheckbox &&
selectedCheckbox?.length <= maxCountInt - 1
) {
handleCheckboxValue(e.target.checked, ind);
}
} else {
handleCheckboxValue(e.target.checked, ind);
}
}
} else {
handleCheckboxValue(e.target.checked, ind);
}
}}
/>
{!props.pos.options?.isHideLabel && (
<label className="text-xs mb-0 text-center">{data}</label>
)}
</div>
);
})}
</div>
);
case textInputWidget:
return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<textarea
ref={inputRef}
placeholder={validatePlaceholder || "please enter value"}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
setTextValue(e.target.value);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id,
false
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }}
cols="50"
/>
) : (
<div
style={{
color: "black",
fontSize: calculateFontSize()
}}
>
<span>{type}</span>
</div>
);
case "dropdown":
return props.data?.signerObjId === props.signerObjId ? (
<select
className="inputPlaceholder"
id="myDropdown"
style={{ fontSize: "12px" }}
value={selectOption}
onChange={(e) => {
setSelectOption(e.target.value);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id,
false
);
}}
>
{/* Default/Title option */}
<option value="" disabled hidden>
{props?.pos?.options?.name}
</option>
{props.pos?.options?.values.map((data, ind) => {
return (
<option key={ind} value={data}>
{data}
</option>
);
})}
</select>
) : (
<div
className="inputPlaceholder"
style={{ fontSize: calculateFontSize() }}
>
{props.pos?.options?.name ? props.pos.options.name : type}
</div>
);
case "initials":
return props.pos.SignUrl ? (
<img
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
style={{
width: "99%",
height: "100%",
objectFit: "contain"
}}
/>
) : (
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
{props?.handleUserName &&
props?.handleUserName(
props?.data?.Id,
props?.data?.Role,
props?.pos?.type
)}
</div>
);
case "name":
return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<textarea
ref={inputRef}
placeholder={"name"}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onChange={(e) => {
const isDefault = false;
handleTextValid(e);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id,
isDefault
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }}
cols="50"
/>
) : (
<div
style={{
color: "black",
fontSize: calculateFontSize()
}}
>
<span>{type}</span>
</div>
);
case "company":
return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<textarea
ref={inputRef}
placeholder="Enter text"
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onChange={(e) => {
handleTextValid(e);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id,
false
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }}
cols="50"
/>
) : (
<div
style={{
color: "black",
fontSize: calculateFontSize()
}}
>
<span>{type}</span>
</div>
);
case "job title":
return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<textarea
ref={inputRef}
placeholder={"job title"}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onChange={(e) => {
handleTextValid(e);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id,
false
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }}
cols="50"
/>
) : (
<div
style={{
color: "black",
fontSize: calculateFontSize()
}}
>
<span>{type}</span>
</div>
);
case "date":
return (
<div>
<DatePicker
renderCustomHeader={({ date, changeYear, changeMonth }) => (
<div className="flex justify-start ml-2 ">
<select
className="bg-transparent outline-none"
value={months[getMonth(date)]}
onChange={({ target: { value } }) =>
changeMonth(months.indexOf(value))
}
>
{months.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<select
className="bg-transparent outline-none"
value={getYear(date)}
onChange={({ target: { value } }) => changeYear(value)}
>
{years.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
</div>
)}
disabled={
props.isPlaceholder ||
(props.isNeedSign &&
props.data?.signerObjId !== props.signerObjId)
}
onBlur={handleInputBlur}
closeOnScroll={true}
className="inputPlaceholder"
style={{ outlineColor: "#007bff" }}
selected={
props?.startDate
? props?.startDate
: props.pos.options?.response &&
new Date(props.pos.options.response)
}
onChange={(date) => handleOnDateChange(date)}
popperPlacement="top-end"
customInput={<ExampleCustomInput />}
dateFormat={
props.selectDate
? props.selectDate?.format
: props.pos?.options?.validation?.format
? props.pos?.options?.validation?.format
: "MM/dd/yyyy"
}
/>
</div>
);
case "image":
return props.pos.SignUrl ? (
<img
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
style={{
width: "99%",
height: "100%",
objectFit: "contain"
}}
/>
) : (
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
{props?.handleUserName &&
props?.handleUserName(
props?.data?.Id,
props?.data?.Role,
props?.pos?.type
)}
</div>
);
case "email":
return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<textarea
ref={inputRef}
placeholder={"enter email"}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
handleTextValid(e);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id,
false
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }}
cols="50"
/>
) : (
<div
style={{
color: "black",
fontSize: calculateFontSize()
}}
>
<span>{type}</span>
</div>
);
case radioButtonWidget:
return (
<div>
{props.pos.options?.values.map((data, ind) => {
return (
<div key={ind} className="flex items-center text-center gap-0.5">
<input
style={{
width: props.pos.Width,
display: "flex",
justifyContent: "center",
marginTop: ind === 0 ? 0 : "5px"
}}
type="radio"
disabled={
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId)
}
checked={handleRadioCheck(data)}
onChange={(e) => {
if (!props.isPlaceholder) {
handleCheckRadio(e.target.checked, data);
}
}}
/>
{!props.pos.options?.isHideLabel && (
<label className="text-xs mb-0">{data}</label>
)}
</div>
);
})}
</div>
);
case textWidget:
return (
<textarea
placeholder="Enter text"
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
setTextValue(e.target.value);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id,
false
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap", overflow: "hidden" }}
cols="50"
/>
);
default:
return props.pos.SignUrl ? (
<div style={{ pointerEvents: "none" }}>
<img
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
style={{
width: "99%",
height: "100%",
objectFit: "contain"
}}
/>
</div>
) : (
<div
style={{
fontSize: "10px",
color: "black",
justifyContent: "center"
}}
>
{props.pos.isStamp ? <div>stamp</div> : <div>signature</div>}
{props?.handleUserName &&
props?.handleUserName(props?.data?.Id, props?.data?.Role)}
</div>
);
}
}
export default PlaceholderType;
@@ -1,53 +0,0 @@
import React from "react";
import { themeColor } from "../../constant/const";
function PrevNext({ pageNumber, allPages, changePage }) {
//for go to previous page
function previousPage() {
changePage(-1);
}
//for go to next page
function nextPage() {
changePage(1);
}
return (
<div>
<div className="preBtn1">
<button
className="py-[3px] px-[10px] text-xs bg-[#d3edeb] font-[600] mr-[5px]"
disabled={pageNumber <= 1}
onClick={previousPage}
>
<span className="block lg:hidden">
<i
className="fa fa-backward"
aria-hidden="true"
style={{ color: themeColor, cursor: "pointer" }}
></i>
</span>
<span className="lg:block hidden">Prev</span>
</button>
<span className="text-xs font-[500]">
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<button
className="py-[3px] px-[10px] text-xs bg-[#d3edeb] font-[600] ml-[5px]"
disabled={pageNumber >= allPages}
onClick={nextPage}
>
<span className="block lg:hidden">
<i
className="fa fa-forward"
aria-hidden="true"
style={{ color: themeColor, cursor: "pointer" }}
></i>
</span>
<span className="lg:block hidden">Next</span>
</button>
</div>
</div>
);
}
export default PrevNext;
@@ -1,295 +0,0 @@
import React, { useRef, useState } from "react";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import {
color,
darkenColor,
getFirstLetter,
isMobile,
nameColor
} from "../../constant/Utils";
const RecipientList = (props) => {
const [animationParent] = useAutoAnimate();
const [isHover, setIsHover] = useState();
const [isEdit, setIsEdit] = useState(false);
//function for onhover signer name change background color
const inputRef = useRef(null);
const onHoverStyle = (ind, blockColor) => {
const style = {
background: blockColor ? blockColor : color[ind % color.length],
padding: "10px",
marginTop: "2px",
display: "flex",
flexDirection: "row",
borderBottom: "1px solid #e3e1e1",
alignItems: "center"
};
return style;
};
//function for onhover signer name remove background color
const nonHoverStyle = () => {
const style = {
padding: "10px",
marginTop: "2px",
display: "flex",
flexDirection: "row",
borderBottom: "1px solid #e3e1e1",
alignItems: "center"
};
return style;
};
const isWidgetExist = (Id) => {
return props.signerPos.some((x) => x.Id === Id);
};
//handle drag start
const handleDragStart = (e, id) => {
// `e.dataTransfer.setData('text/plain')`is used to set the data to be transferred during a drag operation.
// The first argument specifies the type of data being set, and the second argument is the actual data you want to transfer.
e.dataTransfer.setData("text/plain", id);
};
//handleDragOver prevents the default behavior of the dragover event, which is necessary for the drop event to be triggered.
const handleDragOver = (e) => {
e.preventDefault();
};
//handle draggable element drop and also used in mobile view on up and down key to chnage sequence of recipient's list
const handleChangeSequence = (e, ind, isUp, isDown) => {
e.preventDefault();
let draggedItemId;
let index = ind;
//if isUp true then set item's id and index in mobile view
if (isUp) {
draggedItemId = index;
index = index - 1;
}
//if isDown true then set item's id and index in mobile view
else if (isDown) {
draggedItemId = index;
index = index + 1;
} //else set id on drag element in desktop viiew
else {
//`e.dataTransfer.getData('text/plain')` is used to get data that you have saved.
draggedItemId = e.dataTransfer.getData("text/plain");
}
//convert string to number
const intDragId = parseInt(draggedItemId);
//get that item to change position
const draggedItem = props.signersdata.filter((_, ind) => ind === intDragId);
const remainingItems = props.signersdata.filter(
(_, ind) => ind !== intDragId
);
//splice method is used to replace or add new value in array at specific index
remainingItems.splice(index, 0, ...draggedItem);
//set current draggable recipient details,objectId,index,contract_className ... after replace recipient list
props.setSignersData(remainingItems);
props.setSignerObjId(remainingItems[index]?.objectId || "");
props.setIsSelectId(index);
props.setContractName(remainingItems[index]?.className || "");
props.setUniqueId(remainingItems[index]?.Id);
props.setRoleName(remainingItems[index]?.Role);
props.setBlockColor(remainingItems[index]?.blockColor);
};
return (
<>
{props.signersdata.length > 0 &&
props.signersdata.map((obj, ind) => {
return (
<div
ref={animationParent}
key={ind}
draggable={props.sendInOrder && !isMobile ? true : false}
onDragStart={(e) =>
props.sendInOrder && !isMobile && handleDragStart(e, ind)
}
onDragOver={(e) =>
props.sendInOrder && !isMobile && handleDragOver(e)
}
onDrop={(e) =>
props.sendInOrder && !isMobile && handleChangeSequence(e, ind)
}
data-tut="reactourFirst"
onMouseEnter={() => setIsHover(ind)}
onMouseLeave={() => setIsHover(null)}
className={
props.sendInOrder
? props.isMailSend
? "disabled"
: "dragCursor"
: props.isMailSend && "disabled"
}
style={
(!isMobile && isHover === ind) || props.isSelectListId === ind
? onHoverStyle(ind, obj?.blockColor)
: nonHoverStyle(ind)
}
onClick={(e) => {
e.preventDefault();
props.setSignerObjId(obj?.objectId || "");
props.setIsSelectId(ind);
props.setContractName(obj?.className || "");
props.setUniqueId(obj.Id);
props.setRoleName(obj.Role);
props.setBlockColor(obj?.blockColor);
if (props.handleModal) {
props.handleModal();
}
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
width: "100%"
}}
>
<div
style={{
background: obj?.blockColor
? darkenColor(obj?.blockColor, 0.4)
: nameColor[ind % nameColor.length],
width: 30,
height: 30,
display: "flex",
borderRadius: 30 / 2,
justifyContent: "center",
alignItems: "center",
marginRight: "12px"
}}
>
<span
className={props.sendInOrder && "dragCursor"}
style={{
fontSize: "12px",
textAlign: "center",
fontWeight: "bold",
color: "white",
textTransform: "uppercase"
}}
>
{isWidgetExist(obj.Id) ? (
<i className="fa-solid fa-check"></i>
) : (
<>
{obj.Name
? getFirstLetter(obj.Name)
: getFirstLetter(obj.Role)}
</>
)}
</span>
</div>
<div
style={{
display: "flex",
flexDirection: obj.Name ? "column" : "row",
alignItems: "center"
}}
>
{obj.Name ? (
<span className={"userName"}>{obj.Name}</span>
) : (
<>
<span
className="userName"
style={{ cursor: "pointer" }}
onClick={() => {
setIsEdit({ [obj.Id]: true });
props.setRoleName(obj.Role);
}}
>
{isEdit?.[obj.Id] && props.handleRoleChange ? (
<input
ref={inputRef}
style={{
backgroundColor: "transparent",
width: "inherit",
padding: "3px"
}}
value={obj.Role}
onChange={(e) => props.handleRoleChange(e, obj.Id)}
onBlur={() => {
setIsEdit({});
props.handleOnBlur(obj.Role, obj.Id);
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
inputRef.current.blur();
}
}}
/>
) : (
<span style={{ padding: "3px" }}>{obj.Role}</span>
)}
</span>
</>
)}
{obj.Name && (
<span className={"useEmail"}>
{obj?.Role || obj?.Email}
</span>
)}
</div>
</div>
{isMobile && props.sendInOrder && (
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 5
}}
>
<div
onClick={(e) => {
if (ind !== 0) {
e.stopPropagation();
handleChangeSequence(e, ind, "up");
}
}}
style={{ color: ind === 0 ? "gray" : "black" }}
>
</div>
<div
onClick={(e) => {
if (ind !== props.signersdata.length - 1) {
e.stopPropagation();
handleChangeSequence(e, ind, null, "down");
}
}}
style={{
color:
ind === props.signersdata.length - 1 ? "gray" : "black"
}}
>
</div>
</div>
)}
{props.handleDeleteUser && (
<div
onClick={(e) => {
e.stopPropagation();
props.handleDeleteUser(obj.Id);
}}
style={{ cursor: "pointer", marginLeft: "5px" }}
>
<i className="fa-regular fa-trash-can"></i>
</div>
)}
<hr />
</div>
);
})}
</>
);
};
export default RecipientList;
@@ -1,151 +0,0 @@
import React, { useState } from "react";
import RSC from "react-scrollbars-custom";
import { Document, Page } from "react-pdf";
import { themeColor } from "../../constant/const";
function RenderAllPdfPage({
signPdfUrl,
allPages,
setAllPages,
setPageNumber,
setSignBtnPosition,
pageNumber,
signerPos,
signerObjectId
}) {
const [signPageNumber, setSignPageNumber] = useState([]);
const [bookmarkColor, setBookmarkColor] = useState("");
//set all number of pages after load pdf
function onDocumentLoad({ numPages }) {
setAllPages(numPages);
//check if signerPos array exist then save page number exist in signerPos array to show bookmark icon
if (signerPos) {
const checkUser = signerPos.filter(
(data) => data.signerObjId === signerObjectId
);
setBookmarkColor(checkUser[0]?.blockColor);
let pageNumberArr = [];
if (checkUser?.length > 0) {
checkUser[0]?.placeHolder?.map((data) => {
pageNumberArr.push(data?.pageNumber);
});
setSignPageNumber(pageNumberArr);
}
}
}
//'function `addSignatureBookmark` is used to display the page where the user's signature is located.
const addSignatureBookmark = (index) => {
const ispageNumber = signPageNumber.includes(index + 1);
return (
ispageNumber && (
<div
style={{
position: "absolute",
zIndex: 2,
top: -12,
right: -7,
transform: "translate(50% -50%)"
}}
>
<i
style={{ color: bookmarkColor || "red" }}
className="fa-solid fa-bookmark"
></i>
</div>
)
);
};
return (
<div className="showPages">
<div
style={{
display: "flex",
flexDirection: "row",
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
backgroundColor: "white",
height: "100%"
}}
>
<div style={{ width: "140px" }}>
<div
style={{
background: themeColor
// color:"white"
}}
className="signedStyle"
>
Pages
</div>
<div>
<RSC
id="RSC-Example"
style={{
width: "135px",
height: window.innerHeight - 130 + "px"
}}
>
<Document
loading={"Loading Document.."}
onLoadSuccess={onDocumentLoad}
file={signPdfUrl}
>
{Array.from(new Array(allPages), (el, index) => (
<div
key={index}
style={{
width: "100px",
border:
pageNumber - 1 === index
? "2px solid red"
: "2px solid #878787",
// padding: "5px",
margin: "10px",
display: "flex",
justifyContent: "center",
alignItems: "center",
position: "relative"
}}
onClick={() => {
setPageNumber(index + 1);
if (setSignBtnPosition) {
setSignBtnPosition([]);
}
}}
>
{signerPos && addSignatureBookmark(index)}
<div
style={{
position: "relative",
zIndex: 1,
overflow: "hidden"
}}
>
<Page
key={`page_${index + 1}`}
pageNumber={index + 1}
width={100}
height={100}
scale={1}
renderAnnotationLayer={false}
renderTextLayer={false}
/>
</div>
</div>
))}
</Document>
</RSC>
</div>
<hr />
</div>
</div>
</div>
);
}
export default RenderAllPdfPage;
@@ -1,696 +0,0 @@
import React from "react";
import RSC from "react-scrollbars-custom";
import { Document, Page } from "react-pdf";
import {
defaultWidthHeight,
handleImageResize,
handleSignYourselfImageResize
} from "../../constant/Utils";
import Placeholder from "./Placeholder";
import Alert from "../../primitives/Alert";
function RenderPdf({
pageNumber,
pdfOriginalWidth,
pdfNewWidth,
drop,
signerPos,
successEmail,
handleTabDrag,
handleStop,
isDragging,
setIsSignPad,
setIsStamp,
handleDeleteSign,
setSignKey,
pdfDetails,
xyPostion,
pdfUrl,
numPages,
pageDetails,
pdfRequest,
setCurrentSigner,
signerObjectId,
signedSigners,
setPdfLoadFail,
placeholder,
pdfLoadFail,
setSignerPos,
setXyPostion,
index,
containerWH,
setIsResize,
handleLinkUser,
setUniqueId,
signersdata,
setIsPageCopy,
setSignerObjId,
setShowDropdown,
setIsInitial,
setIsValidate,
setWidgetType,
setValidateAlert,
setIsRadio,
setCurrWidgetsDetails,
setSelectWidgetId,
selectWidgetId,
unSignedWidgetId,
setIsCheckbox,
handleNameModal,
setTempSignerId,
uniqueId
}) {
const isMobile = window.innerWidth < 767;
const newWidth = containerWH.width;
const scale = isMobile ? pdfOriginalWidth / newWidth : 1;
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
const isGuestSigner = localStorage.getItem("isGuestSigner");
// handle signature block width and height according to screen
const posWidth = (pos, signYourself) => {
const defaultWidth = defaultWidthHeight(pos.type).width;
const posWidth = pos.Width ? pos.Width : defaultWidth;
let width;
if (signYourself) {
width = posWidth;
return width;
} else {
if (isMobile) {
if (!pos.isMobile) {
if (pos.IsResize) {
width = posWidth ? posWidth : defaultWidth;
return width;
} else {
width = (posWidth || defaultWidth) / scale;
return width;
}
} else {
width = posWidth;
return width;
}
} else {
if (pos.isMobile) {
if (pos.IsResize) {
width = posWidth ? posWidth : defaultWidth;
return width;
} else {
width = (posWidth || defaultWidth) * pos.scale;
return width;
}
} else {
width = posWidth ? posWidth : defaultWidth;
return width;
}
}
}
};
const posHeight = (pos, signYourself) => {
let height;
const posHeight = pos.Height;
const defaultHeight = defaultWidthHeight(pos.type).height;
if (signYourself) {
height = posHeight ? posHeight : defaultHeight;
return height;
} else {
if (isMobile) {
if (!pos.isMobile) {
if (pos.IsResize) {
height = posHeight ? posHeight : defaultHeight;
return height;
} else {
height = (posHeight || defaultHeight) / scale;
return height;
}
} else {
height = posHeight ? posHeight : defaultHeight;
return height;
}
} else {
if (pos.isMobile) {
if (pos.IsResize) {
height = posHeight ? posHeight : defaultHeight;
return height;
} else {
height = (posHeight || defaultHeight) * pos.scale;
return height;
}
} else {
height = posHeight ? posHeight : defaultHeight;
return height;
}
}
}
};
const xPos = (pos, signYourself) => {
const resizePos = pos.xPosition;
if (signYourself) {
return resizePos;
} else {
//checking both condition mobile and desktop view
if (isMobile) {
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
if (!pos.isMobile) {
return resizePos / scale;
}
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
else {
return resizePos * (pos.scale / scale);
}
} else {
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
if (pos.isMobile) {
return pos.scale && resizePos * pos.scale;
}
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
else {
return resizePos;
}
}
}
};
const yPos = (pos, signYourself) => {
const resizePos = pos.yPosition;
if (signYourself) {
return resizePos;
} else {
//checking both condition mobile and desktop view
if (isMobile) {
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
if (!pos.isMobile) {
return resizePos / scale;
}
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
else {
return resizePos * (pos.scale / scale);
}
} else {
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
if (pos.isMobile) {
return pos.scale && resizePos * pos.scale;
}
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
else {
return resizePos;
}
}
}
};
//function for render placeholder block over pdf document
const checkSignedSignes = (data) => {
const checkSign = signedSigners.filter(
(sign) => sign.objectId === data.signerObjId
);
if (data.signerObjId === signerObjectId) {
setCurrentSigner(true);
}
const handleAllUserName = (Id, Role, type) => {
return pdfDetails[0].Signers.map((signerData, key) => {
return (
signerData.objectId === data.signerObjId && (
<React.Fragment key={key}>
{signerData?.Name && (
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{signerData.Name}
</div>
)}
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
{Role && (
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{`(${Role})`}
</div>
)}
</React.Fragment>
)
);
});
};
return (
checkSign.length === 0 &&
data.placeHolder.map((placeData, key) => {
return (
<React.Fragment key={key}>
{placeData.pageNumber === pageNumber &&
placeData.pos.map((pos) => {
return (
pos && (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setSignKey={setSignKey}
setIsSignPad={setIsSignPad}
setIsStamp={setIsStamp}
handleSignYourselfImageResize={handleImageResize}
index={pageNumber}
xyPostion={signerPos}
setXyPostion={setSignerPos}
data={data}
setIsResize={setIsResize}
isShowBorder={false}
signerObjId={signerObjectId}
isShowDropdown={true}
isNeedSign={true}
isSignYourself={false}
xPos={xPos}
yPos={yPos}
posWidth={posWidth}
posHeight={posHeight}
handleUserName={handleAllUserName}
isDragging={false}
pdfDetails={pdfDetails}
setIsInitial={setIsInitial}
setValidateAlert={setValidateAlert}
unSignedWidgetId={unSignedWidgetId}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
setCurrWidgetsDetails={setCurrWidgetsDetails}
/>
</React.Fragment>
)
);
})}
</React.Fragment>
);
})
);
};
const handleUserName = (Id, Role, type) => {
if (Id) {
const checkSign = signersdata.find((sign) => sign.Id === Id);
if (checkSign?.Name) {
return (
<>
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{checkSign?.Name}
</div>
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
{Role && (
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{`(${Role})`}
</div>
)}
</>
);
} else {
return (
<>
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{Role}
</div>
</>
);
}
} else {
return (
<>
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{Role}
</div>
</>
);
}
};
return (
<>
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
{isMobile && scale ? (
<div
style={{
border: "0.1px solid #ebe8e8",
marginTop: isGuestSigner && "30px"
}}
ref={drop}
id="container"
>
{pdfLoadFail.status &&
(pdfRequest
? signerPos.map((data, key) => {
return (
<React.Fragment key={key}>
{checkSignedSignes(data)}
</React.Fragment>
);
})
: placeholder // placeholder mobile
? signerPos.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data.placeHolder.map((placeData, index) => {
return (
<React.Fragment key={index}>
{placeData.pageNumber === pageNumber &&
placeData.pos.map((pos) => {
return (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleImageResize
}
index={pageNumber}
xyPostion={signerPos}
setXyPostion={setSignerPos}
setSignerObjId={setSignerObjId}
data={data}
setIsResize={setIsResize}
setShowDropdown={setShowDropdown}
isShowBorder={true}
isPlaceholder={true}
setUniqueId={setUniqueId}
handleLinkUser={handleLinkUser}
handleUserName={handleUserName}
isSignYourself={false}
xPos={xPos}
yPos={yPos}
posWidth={posWidth}
posHeight={posHeight}
isDragging={isDragging}
setIsValidate={setIsValidate}
setWidgetType={setWidgetType}
setIsRadio={setIsRadio}
setIsCheckbox={setIsCheckbox}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
/>
</React.Fragment>
);
})}
</React.Fragment>
);
})}
</React.Fragment>
);
})
: xyPostion.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data.pageNumber === pageNumber &&
data.pos.map((pos) => {
return (
pos && (
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleSignYourselfImageResize
}
index={index}
xyPostion={xyPostion}
setXyPostion={setXyPostion}
pdfOriginalWidth={pdfOriginalWidth}
containerWH={containerWH}
setIsSignPad={setIsSignPad}
isShowBorder={true}
isSignYourself={true}
xPos={xPos}
yPos={yPos}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={pdfDetails[0]}
isDragging={isDragging}
setIsInitial={setIsInitial}
setWidgetType={setWidgetType}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleUserName={handleUserName}
setIsCheckbox={setIsCheckbox}
setValidateAlert={setValidateAlert}
setCurrWidgetsDetails={setCurrWidgetsDetails}
/>
)
);
})}
</React.Fragment>
);
}))}
{/* this component for render pdf document is in middle of the component */}
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<Document
onLoadError={() => {
setPdfLoadFail(true);
}}
loading={"Loading Document.."}
onLoadSuccess={pageDetails}
// ref={pdfRef}'
onClick={() => {
if (setSelectWidgetId) {
setSelectWidgetId("");
}
}}
file={
pdfUrl
? pdfUrl
: pdfDetails[0] && pdfDetails[0].SignedUrl
? pdfDetails[0].SignedUrl
: pdfDetails[0].URL
}
>
{Array.from(new Array(numPages), (el, index) => (
<Page
key={index}
pageNumber={pageNumber}
width={containerWH.width}
height={containerWH.height}
renderAnnotationLayer={false}
renderTextLayer={false}
onGetAnnotationsError={(error) => {
console.log("annotation error", error);
}}
/>
))}
</Document>
</div>
</div>
) : (
<RSC
style={{
position: "relative",
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
width:
pdfOriginalWidth > pdfNewWidth ? pdfNewWidth : pdfOriginalWidth,
height: window.innerHeight - 110 + "px"
}}
noScrollY={false}
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
>
<div
style={{
border: "0.1px solid #ebe8e8",
width: pdfOriginalWidth
}}
ref={drop}
id="container"
>
{pdfLoadFail.status &&
(pdfRequest
? signerPos.map((data, key) => {
return (
<React.Fragment key={key}>
{checkSignedSignes(data)}
</React.Fragment>
);
})
: placeholder
? signerPos.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data.placeHolder.map((placeData, index) => {
return (
<React.Fragment key={index}>
{placeData.pageNumber === pageNumber &&
placeData.pos.map((pos) => {
return (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleImageResize
}
index={pageNumber}
xyPostion={signerPos}
setXyPostion={setSignerPos}
setSignerObjId={setSignerObjId}
data={data}
setIsResize={setIsResize}
setShowDropdown={setShowDropdown}
isShowBorder={true}
isPlaceholder={true}
setUniqueId={setUniqueId}
handleLinkUser={handleLinkUser}
handleUserName={handleUserName}
isSignYourself={false}
xPos={xPos}
yPos={yPos}
posWidth={posWidth}
posHeight={posHeight}
isDragging={isDragging}
setIsValidate={setIsValidate}
setWidgetType={setWidgetType}
setIsRadio={setIsRadio}
setIsCheckbox={setIsCheckbox}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
/>
</React.Fragment>
);
})}
</React.Fragment>
);
})}
</React.Fragment>
);
})
: xyPostion.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data.pageNumber === pageNumber &&
data.pos.map((pos) => {
return (
pos && (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={(event, dragElement) =>
handleStop(event, dragElement, pos.type)
}
handleSignYourselfImageResize={
handleSignYourselfImageResize
}
index={index}
xyPostion={xyPostion}
setXyPostion={setXyPostion}
pdfOriginalWidth={pdfOriginalWidth}
containerWH={containerWH}
setIsSignPad={setIsSignPad}
isShowBorder={true}
isSignYourself={true}
xPos={xPos}
yPos={yPos}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={pdfDetails[0]}
isDragging={isDragging}
setIsInitial={setIsInitial}
setWidgetType={setWidgetType}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleUserName={handleUserName}
setIsCheckbox={setIsCheckbox}
setValidateAlert={setValidateAlert}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
/>
</React.Fragment>
)
);
})}
</React.Fragment>
);
}))}
{/* this component for render pdf document is in middle of the component */}
<Document
onLoadError={() => {
const load = {
status: false,
type: "failed"
};
setPdfLoadFail(load);
}}
loading={"Loading Document.."}
onLoadSuccess={pageDetails}
onClick={() => {
if (setSelectWidgetId) {
setSelectWidgetId("");
}
}}
// ref={pdfRef}
file={
pdfUrl
? pdfUrl
: pdfDetails[0] && pdfDetails[0].SignedUrl
? pdfDetails[0].SignedUrl
: pdfDetails[0].URL
}
>
{Array.from(new Array(numPages), (el, index) => (
<Page
key={index}
pageNumber={pageNumber}
renderAnnotationLayer={false}
renderTextLayer={false}
onGetAnnotationsError={(error) => {
console.log("annotation error", error);
}}
/>
))}
</Document>
</div>
</RSC>
)}
</>
);
}
export default RenderPdf;
-711
View File
@@ -1,711 +0,0 @@
import React, { useRef, useState, useEffect } from "react";
import SignatureCanvas from "react-signature-canvas";
import { themeColor } from "../../constant/const";
function SignPad({
isSignPad,
isStamp,
setIsImageSelect,
onSaveSign,
setIsSignPad,
setImage,
isImageSelect,
imageRef,
onImageChange,
onSaveImage,
image,
defaultSign,
setSignature,
myInitial,
isInitial,
setIsInitial,
setIsStamp,
widgetType,
currWidgetsDetails,
setCurrWidgetsDetails
}) {
const [penColor, setPenColor] = useState("blue");
const allColor = ["blue", "red", "black"];
const canvasRef = useRef(null);
const [isDefaultSign, setIsDefaultSign] = useState(false);
const [isTab, setIsTab] = useState("draw");
const [isSignImg, setIsSignImg] = useState("");
const [signValue, setSignValue] = useState("");
const [textWidth, setTextWidth] = useState(null);
const [textHeight, setTextHeight] = useState(null);
const [signatureType, setSignatureType] = useState("draw");
const fontOptions = [
{ value: "Fasthand" },
{ value: "Dancing Script" },
{ value: "Cedarville Cursive" },
{ value: "Delicious Handrawn" }
// Add more font options as needed
];
const [fontSelect, setFontSelect] = useState(fontOptions[0].value);
const senderUser =
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
) &&
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
const jsonSender = JSON.parse(senderUser);
const currentUserName = jsonSender && jsonSender.name;
//function for clear signature image
const handleClear = () => {
setCurrWidgetsDetails({});
if (isTab === "draw") {
if (canvasRef.current) {
canvasRef.current.clear();
} else if (isStamp) {
setImage("");
}
setIsSignImg("");
} else if (isTab === "uploadImage") {
setImage("");
}
// setIsInitial(false);
};
//function for set signature url
const handleSignatureChange = () => {
setSignature(canvasRef.current.toDataURL());
setIsSignImg(canvasRef.current.toDataURL());
};
//save button component
const SaveBtn = () => {
return (
<div style={{ marginTop: "2px" }}>
{(isTab === "draw" || isTab === "uploadImage") && (
<button
style={{
color: "black",
border: "1px solid #ccc"
}}
type="button"
className="finishBtn saveBtn"
onClick={() => handleClear()}
>
Clear
</button>
)}
<button
onClick={() => {
setCurrWidgetsDetails({});
if (!image) {
if (isTab === "mysignature") {
setIsSignImg("");
if (isInitial) {
onSaveSign(signatureType, "initials");
} else {
onSaveSign(null, "default");
}
} else {
if (isTab === "type") {
setIsSignImg("");
onSaveSign(null, false, textWidth, textHeight);
} else {
onSaveSign(signatureType);
}
}
setPenColor("blue");
} else {
setIsSignImg("");
onSaveImage(signatureType);
}
setIsSignPad(false);
setIsInitial(false);
setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
setIsTab("draw");
setSignValue("");
setIsStamp(false);
}}
style={{
background: themeColor,
color: "white"
}}
disabled={
isSignImg || image || isDefaultSign || textWidth ? false : true
}
type="button"
className={
isSignImg || image || isDefaultSign || textWidth
? "finishBtn saveBtn"
: "disabledFinish saveBtn"
}
>
Save
</button>
</div>
);
};
//useEffect for set already draw or save signature url/text url of signature text type and draw type for initial type and signature type widgets
useEffect(() => {
if (currWidgetsDetails && canvasRef.current) {
const isWidgetType = currWidgetsDetails?.type;
const signatureType = currWidgetsDetails?.signatureType;
const url = currWidgetsDetails?.SignUrl;
//checking widget type and draw type signature url
if (isInitial) {
if (isWidgetType === "initials" && signatureType === "draw" && url) {
canvasRef.current.fromDataURL(url);
}
} else if (
isWidgetType === "signature" &&
signatureType === "draw" &&
url
) {
canvasRef.current.fromDataURL(url);
}
}
const trimmedName = currentUserName.trim();
const firstCharacter = trimmedName.charAt(0);
const userName = isInitial ? firstCharacter : currentUserName;
setSignValue(userName);
setFontSelect("Fasthand");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isSignPad]);
useEffect(() => {
const loadFont = async () => {
try {
await document.fonts.load(`20px ${fontSelect}`);
const selectFontSTyle = fontOptions.find(
(font) => font.value === fontSelect
);
setFontSelect(selectFontSTyle?.value || fontOptions[0].value);
} catch (error) {
console.error("Error loading font:", error);
}
};
loadFont();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fontSelect]);
useEffect(() => {
// Load the default signature after the component mounts
if (canvasRef.current) {
canvasRef.current.fromDataURL(isSignImg);
}
if (isTab === "type") {
const trimmedName = signValue ? signValue.trim() : currentUserName.trim();
const firstCharacter = trimmedName.charAt(0);
const userName = isInitial ? firstCharacter : signValue;
setSignValue(userName);
convertToImg(fontSelect, userName);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isTab]);
//function for convert input text value in image
const convertToImg = async (fontStyle, text, color) => {
//get text content to convert in image
const textContent = text;
const fontfamily = fontStyle
? fontStyle
: fontSelect
? fontSelect
: "Fasthand";
//creating span for getting text content width
const span = document.createElement("span");
span.textContent = textContent;
span.style.font = `20px ${fontfamily}`; // here put your text size and font family
span.style.color = color ? color : penColor;
span.style.display = "hidden";
document.body.appendChild(span); // Replace 'container' with the ID of the container element
//create canvas to render text in canvas and convert in image
const canvasElement = document.createElement("canvas");
// Draw the text content on the canvas
const ctx = canvasElement.getContext("2d");
const pixelRatio = window.devicePixelRatio || 1;
const width = span.offsetWidth;
const height = span.offsetHeight;
setTextWidth(width);
setTextHeight(height);
const font = span.style["font"];
// Set the canvas dimensions to match the span
canvasElement.width = width * pixelRatio;
canvasElement.height = height * pixelRatio;
// You can customize text styles if needed
ctx.font = font;
ctx.fillStyle = color ? color : penColor; // Set the text color
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.scale(pixelRatio, pixelRatio);
// Draw the content of the span onto the canvas
ctx.fillText(span.textContent, width / 2, height / 2); // Adjust the x,y-coordinate as needed
//remove span tag
document.body.removeChild(span);
// Convert the canvas to image data
const dataUrl = canvasElement.toDataURL("image/png");
setSignature(dataUrl);
};
const PenColorComponent = (props) => {
return (
<div style={{ display: "flex", flexDirection: "row" }}>
{allColor.map((data, key) => {
return (
<i
style={{
margin: "5px",
color: data,
borderBottom:
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
props?.convertToImg &&
props?.convertToImg(fontSelect, signValue, data);
if (key === 0) {
setPenColor("blue");
} else if (key === 1) {
setPenColor("red");
} else if (key === 2) {
setPenColor("black");
}
}}
key={key}
className="fa solid fa-pen-nib"
width={20}
height={20}
></i>
);
})}
</div>
);
};
return (
<div>
{isSignPad && (
<div className="modaloverlay">
<div className="modalcontainer">
<div
className="modalheader"
style={{ padding: "0 13px", marginTop: "5px" }}
>
<div className="modaltitle">
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
background: "white",
marginTop: "3px"
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around",
alignItems: "end",
gap: 10
}}
>
{isStamp ? (
<span style={{ color: themeColor }} className="signTab">
{widgetType === "image"
? "Upload image"
: "Upload stamp image"}
</span>
) : (
<>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(false);
setIsTab("draw");
setImage();
}}
style={{
color: isTab === "draw" ? themeColor : "#515252",
marginLeft: "2px"
}}
className="signTab"
>
Draw
</span>
<div
style={{
border:
isTab === "draw"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(true);
setIsTab("uploadImage");
setSignatureType("");
}}
style={{
color:
isTab === "uploadImage" ? themeColor : "#515252"
}}
className="signTab"
>
Upload Image
</span>
<div
style={{
border:
isTab === "uploadImage"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(false);
setIsTab("type");
setSignatureType("");
setImage();
}}
style={{
color: isTab === "type" ? themeColor : "#515252",
marginLeft: "2px"
}}
className="signTab"
>
Type
</span>
<div
style={{
border:
isTab === "type"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
{!isInitial && defaultSign ? (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
setImage();
}}
style={{
color:
isTab === "mysignature"
? themeColor
: "#515252"
}}
className="signTab"
>
My Signature
</span>
<div
style={{
border:
isTab === "mysignature"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
) : (
isInitial &&
myInitial && (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
setImage();
}}
style={{
color:
isTab === "mysignature"
? themeColor
: "#515252"
}}
className="signTab"
>
My Initials
</span>
<div
style={{
border:
isTab === "mysignature"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
)
)}
</>
)}
</div>
</div>
</div>
<div
className="closebtn"
style={{ color: "black" }}
onClick={() => {
setPenColor("blue");
setIsSignPad(false);
setIsInitial(false);
setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
setIsTab("draw");
setSignatureType("draw");
setSignValue("");
setIsStamp(false);
}}
>
&times;
</div>
</div>
<div style={{ height: "100%", padding: 20 }}>
{isDefaultSign ? (
<>
<div style={{ display: "flex", justifyContent: "center" }}>
<div
style={{
border: "1.3px solid #007bff",
borderRadius: "2px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginBottom: 6,
cursor: "pointer"
}}
className={
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
}
>
<img
alt="stamp img"
style={{
width: "100%",
height: "100%",
background: "rgb(255, 255, 255)",
objectFit: "contain"
}}
draggable="false"
src={isInitial ? myInitial : defaultSign}
/>
</div>
</div>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<SaveBtn />
</div>
</>
) : isImageSelect || isStamp ? (
!image ? (
<div style={{ display: "flex", justifyContent: "center" }}>
<div
style={{
border: "1.3px solid #007bff",
borderRadius: "2px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginBottom: 6,
cursor: "pointer"
}}
className={
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
}
onClick={() => imageRef.current.click()}
>
<input
type="file"
onChange={onImageChange}
className="filetype"
accept="image/png,image/jpeg"
ref={imageRef}
hidden
/>
<i className="fas fa-cloud-upload-alt uploadImgLogo"></i>
<div className="uploadImg">Upload</div>
</div>
</div>
) : (
<>
<div style={{ display: "flex", justifyContent: "center" }}>
<div
style={{
border: "1.3px solid #007bff",
borderRadius: "2px",
marginBottom: 6,
overflow: "hidden"
}}
className={
isInitial
? "intialSignatureCanvas"
: "signatureCanvas"
}
>
<img
alt="print img"
ref={imageRef}
src={image.src}
draggable="false"
style={{
objectFit: "contain",
height: "100%",
width: "100%"
}}
/>
</div>
</div>
<div
style={{ display: "flex", justifyContent: "flex-end" }}
>
<SaveBtn />
</div>
</>
)
) : isTab === "type" ? (
<div>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}}
>
<span className="signatureText">
{isInitial ? "Initials" : "Signature"}:
</span>
<input
maxLength={isInitial ? 3 : 30}
style={{ fontFamily: fontSelect, color: penColor }}
type="text"
className="signatureInput"
placeholder="Your signature"
value={signValue}
onChange={(e) => {
setSignValue(e.target.value);
convertToImg(fontSelect, e.target.value);
}}
/>
</div>
<div className="fontOptionContainer">
{fontOptions.map((font, ind) => {
return (
<div
key={ind}
style={{
cursor: "pointer",
fontFamily: font.value,
backgroundColor:
fontSelect === font.value && "rgb(206 225 247)"
}}
onClick={() => {
setFontSelect(font.value);
convertToImg(font.value, signValue);
}}
>
<div
style={{
padding: "5px 10px 5px 10px",
fontSize: "20px",
color: penColor
}}
>
{signValue ? signValue : "Your signature"}
</div>
</div>
);
})}
</div>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
marginTop: "10px"
}}
>
<PenColorComponent convertToImg={convertToImg} />
<SaveBtn />
</div>
</div>
) : (
<>
<div style={{ display: "flex", justifyContent: "center" }}>
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
canvasProps={{
className: isInitial
? "intialSignatureCanvas"
: "signatureCanvas",
style: {
border: "1.6px solid #007bff",
borderRadius: "2px"
}
}}
backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
handleSignatureChange(canvasRef.current.toDataURL())
}
dotSize={1}
/>
</div>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
marginTop: "10px"
}}
>
<PenColorComponent />
<SaveBtn />
</div>
</>
)}
</div>
</div>
</div>
)}
</div>
);
}
export default SignPad;
@@ -1,64 +0,0 @@
import React from "react";
import { themeColor } from "../../constant/const";
import "../../styles/signature.css";
function Signedby({ pdfDetails }) {
const getFirstLetter = (name) => {
const firstLetter = name.charAt(0);
return firstLetter;
};
return (
<div className="signerComponent">
<div
style={{
background: themeColor,
color: "white"
}}
className="signedStyle"
>
Signed By
</div>
<div style={{ marginTop: "2px", background: "white" }}>
<div
style={{
display: "flex",
flexDirection: "row",
padding: "10px",
background: "#93a3db"
}}
>
<div
className="signerStyle"
style={{
background: "#abd1d0",
width: 20,
height: 20,
display: "flex",
borderRadius: 30 / 2,
justifyContent: "center",
alignItems: "center",
marginRight: "20px",
marginTop: "5px"
}}
>
<span
style={{
fontSize: "8px",
textAlign: "center",
fontWeight: "bold"
}}
>
{getFirstLetter(pdfDetails.ExtUserPtr.Name)}
</span>
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<span className="userName">{pdfDetails.ExtUserPtr.Name}</span>
<span className="useEmail">{pdfDetails.ExtUserPtr.Email}</span>
</div>
</div>
</div>
</div>
);
}
export default Signedby;
@@ -1,105 +0,0 @@
import React from "react";
import { themeColor } from "../../constant/const";
import RecipientList from "./RecipientList";
import { Tooltip } from "react-tooltip";
function SignerListPlace(props) {
return (
<div>
<div
style={{
background: themeColor,
padding: "5px"
}}
>
<span className="signedStyle">
{props.title ? props.title : "Recipients"}
<span className="absolute text-xs z-[30] mt-1 ml-0.5">
{props?.title === "Roles" && (
<>
<a data-tooltip-id="my-tooltip">
<sup>
<i
className="fa-solid fa-question rounded-full"
style={{
borderColor: "white",
color: "white",
fontSize: 11,
borderWidth: 1.5,
padding: "1px 3px"
}}
></i>
</sup>
</a>
<Tooltip id="my-tooltip">
<div className="max-w-[450px]">
<p className="font-bold">What are template roles?</p>
<p>
Begin by specifying each role needed for the completion of
the document. Think about the parties involved in the
signing process and what their responsibilities are.
Common roles include HR for internal documents, Customer
for agreements or Vendor for business agreements.{" "}
</p>
<p className="font-bold">
Why pre-attach users to some roles?
</p>
<p>
For roles that consistently involve the same individual
(e.g., the CEO&apos;s signature on employee offer
letters), you can pre-attach a user to a role within the
template. This step is optional but recommended for
efficiency and consistency across documents.
</p>
<p className="font-bold">
When do i specify the user attached to each role?
</p>
<p>
When you create a document from your template, you&apos;ll
be prompted to attach users to each defined role. If a
role already has a user attached, this will be pre-filled,
but you can modify it as needed before sending out the
document.
</p>
</div>
</Tooltip>
</>
)}
</span>
</span>
</div>
<div className="signerList">
<RecipientList {...props} />
</div>
{props.handleAddSigner ? (
<div
data-tut="reactourAddbtn"
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
onClick={() => props.handleAddSigner()}
style={{
opacity: props.isMailSend && "0.5",
pointerEvents: props.isMailSend && "none"
}}
>
<i className="fa-solid fa-plus"></i>
<span style={{ marginLeft: 2 }}>Add role</span>
</div>
) : (
<div
data-tut="reactourAddbtn"
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
onClick={() => props.setIsAddSigner(true)}
style={{
opacity: props.isMailSend && "0.5",
pointerEvents: props.isMailSend && "none"
}}
>
<i className="fa-solid fa-plus"></i>
<span style={{ marginLeft: 2 }}>Add recipients</span>
</div>
)}
</div>
);
}
export default SignerListPlace;
@@ -1,85 +0,0 @@
import React from "react";
import { rejectBtn, submitBtn, themeColor } from "../../constant/const";
function VerifyEmail(props) {
return (
<div className="bg-black bg-opacity-[75%] absolute z-[999] flex flex-col items-center justify-center w-full h-full rounded">
<div className="bg-white rounded outline-none md:w-[40%] w-[80%]">
<div
style={{ backgroundColor: themeColor }}
className=" text-white p-[10px] rounded-t"
>
OTP verification
</div>
{props.isVerifyModal ? (
<form
onSubmit={(e) => {
props.setIsVerifyModal(false);
props.handleVerifyEmail(e);
}}
>
<div className="px-6 py-3">
<label className="mb-2">Enter OTP</label>
<input
required
type="tel"
pattern="[0-9]{4}"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
placeholder="Enter OTP received over email"
value={props.otp}
onChange={(e) => props.setOtp(e.target.value)}
/>
</div>
<hr />
<div className="px-6 my-3">
<button type="submit" className={submitBtn}>
Verify
</button>
<button
className={`${rejectBtn} ml-2`}
onClick={(e) => props.handleResend(e)}
>
Resend
</button>
</div>
</form>
) : props.otpLoader ? (
<div
style={{
height: "150px",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<div className="px-6 py-3">
<p className="mb-2">Please verify your email !</p>
<hr />
<div className="px-0 mt-3">
<button
className={submitBtn}
type="submit"
onClick={() => {
props.handleVerifyBtn();
}}
>
Send OTP
</button>
</div>
</div>
)}
</div>
</div>
);
}
export default VerifyEmail;
@@ -1,453 +0,0 @@
import React, { useState, useRef, useEffect } from "react";
import ModalUi from "../../primitives/ModalUi";
import RecipientList from "./RecipientList";
import { useDrag } from "react-dnd";
import WidgetList from "./WidgetList";
import {
radioButtonWidget,
textInputWidget,
textWidget,
widgets
} from "../../constant/Utils";
import { themeColor } from "../../constant/const";
function WidgetComponent({
dragSignature,
signRef,
handleDivClick,
handleMouseLeave,
dragStamp,
isSignYourself,
addPositionOfSignature,
signersdata,
isSelectListId,
setSignerObjId,
setIsSelectId,
setContractName,
isSigners,
dataTut,
isMailSend,
handleAddSigner,
setUniqueId,
setRoleName,
handleDeleteUser,
signerPos,
handleRoleChange,
handleOnBlur,
title,
setSignersData,
sendInOrder,
isTemplateFlow,
setBlockColor,
blockColor,
setIsAddSigner
}) {
const [isSignersModal, setIsSignersModal] = useState(false);
const [, dropdown] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 5,
text: "dropdown"
},
collect: (monitor) => ({
isDragDropdown: !!monitor.isDragging()
})
});
const [, checkbox] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 6,
text: "checkbox"
},
collect: (monitor) => ({
isDragCheck: !!monitor.isDragging()
})
});
const [, textInput] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 7,
text: textInputWidget
},
collect: (monitor) => ({
isDragTextInput: !!monitor.isDragging()
})
});
const [, initials] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 8,
text: "initials"
},
collect: (monitor) => ({
isDragInitial: !!monitor.isDragging()
})
});
const [, name] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 9,
text: "name"
},
collect: (monitor) => ({
isDragName: !!monitor.isDragging()
})
});
const [, company] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 10,
text: "company"
},
collect: (monitor) => ({
isDragCompany: !!monitor.isDragging()
})
});
const [, jobTitle] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 11,
text: "job title"
},
collect: (monitor) => ({
isDragJobtitle: !!monitor.isDragging()
})
});
const [, date] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 12,
text: "date"
},
collect: (monitor) => ({
isDragDate: !!monitor.isDragging()
})
});
const [, image] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 13,
text: "image"
},
collect: (monitor) => ({
isDragImage: !!monitor.isDragging()
})
});
const [, email] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 14,
text: "email"
},
collect: (monitor) => ({
isDragEmail: !!monitor.isDragging()
})
});
const [, radioButton] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 15,
text: radioButtonWidget
},
collect: (monitor) => ({
isDragRadiotton: !!monitor.isDragging()
})
});
const [, text] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 16,
text: textWidget
},
collect: (monitor) => ({
isDragText: !!monitor.isDragging()
})
});
const isMobile = window.innerWidth < 767;
const scrollContainerRef = useRef(null);
const [widget, setWidget] = useState([]);
const color = [
"#93a3db",
"#e6c3db",
"#c0e3bc",
"#bce3db",
"#b8ccdb",
"#ceb8db",
"#ffccff",
"#99ffcc",
"#cc99ff",
"#ffcc99",
"#66ccff",
"#ffffcc"
];
const handleModal = () => {
setIsSignersModal(!isSignersModal);
};
useEffect(() => {
const widgetRef = [
dragSignature,
dragStamp,
initials,
name,
jobTitle,
company,
date,
text,
textInput,
checkbox,
dropdown,
radioButton,
image,
email
];
const getWidgetArray = widgets;
const newUpdateSigner = getWidgetArray.map((obj, ind) => {
return { ...obj, ref: widgetRef[ind] };
});
setWidget(newUpdateSigner);
// eslint-disable-next-line
}, []);
const filterWidgets = widget.filter(
(data) =>
data.type !== "dropdown" &&
data.type !== radioButtonWidget &&
data.type !== textInputWidget
);
const textWidgetData = widget.filter((data) => data.type !== textWidget);
const updateWidgets = isSignYourself
? filterWidgets
: isTemplateFlow
? textWidgetData
: widget;
return (
<>
{isMobile ? (
!isMailSend && (
<div
data-tut={dataTut}
id="navbar"
className="stickyfooter"
style={{ width: window.innerWidth + "px" }}
>
{isSigners && (
<div
data-tut={dataTut}
style={{
background: blockColor
? blockColor
: isSelectListId
? color[isSelectListId % color.length]
: color[0],
padding: "10px 20px",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
onClick={() => handleModal()}
>
<span
style={{
fontSize: "13px",
fontWeight: "700",
textAlign: "center"
}}
>
{title ? title : "Recipient"}
</span>
<span
style={{
fontSize: "13px",
fontWeight: "700",
display: "flex",
alignItems: "center"
}}
>
{signersdata[isSelectListId]?.Role && (
<div>
{signersdata[isSelectListId]?.Name ? (
<>
:{" "}
{signersdata[isSelectListId]?.Name?.length > 12
? `${signersdata[isSelectListId].Name.slice(
0,
12
)}...`
: signersdata[isSelectListId]?.Name}
</>
) : (
<>
:{" "}
{signersdata[isSelectListId]?.Role?.length > 12
? `${signersdata[isSelectListId].Role.slice(
0,
12
)}...`
: signersdata[isSelectListId]?.Role}
</>
)}
</div>
)}
<div style={{ marginLeft: 6, fontSize: 16 }}>
<i className="fa-solid fa-angle-down"></i>
</div>
</span>
</div>
)}
{handleAddSigner ? (
<div
data-tut="reactourAddbtn"
style={{
margin: "5px 0 5px 0",
backgroundColor: themeColor,
color: "white"
}}
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
onClick={() => handleAddSigner()}
>
<i className="fa-solid fa-plus"></i>
<span style={{ marginLeft: 2 }}>Add role</span>
</div>
) : (
setIsAddSigner && (
<div
data-tut="reactourAddbtn"
style={{
margin: "5px 0 5px 0",
backgroundColor: themeColor,
color: "white"
}}
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
onClick={() => setIsAddSigner(true)}
>
<i className="fa-solid fa-plus"></i>
<span style={{ marginLeft: 2 }}>Add recipients</span>
</div>
)
)}
<div
ref={scrollContainerRef}
style={{
background: "white",
borderTop: `2px solid ${themeColor}`
}}
>
<div
style={{
display: "flex",
overflowX: "scroll",
whiteSpace: "nowrap",
padding: "10px 5px 10px 1px"
}}
>
<WidgetList
updateWidgets={updateWidgets}
handleDivClick={handleDivClick}
handleMouseLeave={handleMouseLeave}
signRef={signRef}
marginLeft={5}
addPositionOfSignature={addPositionOfSignature}
isMobile={isMobile}
/>
</div>
</div>
</div>
)
) : (
<div
data-tut={dataTut}
className={
isMailSend ? "disabled signerComponent " : "signerComponent"
}
>
<div
style={{
background: themeColor,
padding: "5px"
}}
>
<span className="signedStyle">Fields</span>
</div>
<div className="signLayoutContainer1">
<WidgetList
updateWidgets={updateWidgets}
handleDivClick={handleDivClick}
handleMouseLeave={handleMouseLeave}
signRef={signRef}
/>
</div>
</div>
)}
{isSignersModal && (
<ModalUi
title={title ? title : "Recipients"}
isOpen={isSignersModal}
handleClose={handleModal}
>
{signersdata.length > 0 ? (
<div style={{ maxHeight: "600px", overflow: "auto" }}>
<RecipientList
signerPos={signerPos}
signersdata={signersdata}
isSelectListId={isSelectListId}
setSignerObjId={setSignerObjId}
setIsSelectId={setIsSelectId}
setContractName={setContractName}
setUniqueId={setUniqueId}
setRoleName={setRoleName}
handleDeleteUser={handleDeleteUser}
handleRoleChange={handleRoleChange}
handleOnBlur={handleOnBlur}
handleModal={handleModal}
sendInOrder={sendInOrder}
setSignersData={setSignersData}
setBlockColor={setBlockColor}
/>
</div>
) : (
<div
style={{
padding: 20,
fontSize: 15,
fontWeight: "500",
textAlign: "center"
}}
>
Please add a {title ? title : "recipients"}
</div>
)}
</ModalUi>
)}
</>
);
}
export default WidgetComponent;
@@ -1,36 +0,0 @@
import React from "react";
import { getWidgetType } from "../../constant/Utils";
function WidgetList(props) {
return props.updateWidgets.map((item, ind) => {
return (
<div key={ind} style={{ marginBottom: "5px" }}>
<div
className="widgets"
onClick={() => {
props.addPositionOfSignature &&
props.addPositionOfSignature("onclick", item);
}}
ref={(element) => {
if (!props.isMobile) {
item.ref(element);
if (element) {
if (props?.signRef) {
props.signRef.current = element;
}
}
}
}}
onMouseMove={props?.handleDivClick}
onMouseDown={() => {
props?.handleMouseLeave();
}}
>
{item.ref && getWidgetType(item, props?.marginLeft)}
</div>
</div>
);
});
}
export default WidgetList;
@@ -1,311 +0,0 @@
import React, { useEffect, useState } from "react";
import ModalUi from "../../primitives/ModalUi";
import "../../styles/AddUser.css";
import RegexParser from "regex-parser";
import { textInputWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const";
const WidgetNameModal = (props) => {
const [formdata, setFormdata] = useState({
name: "",
defaultValue: "",
status: "required",
hint: "",
textvalidate: ""
});
const [isValid, setIsValid] = useState(true);
const statusArr = ["Required", "Optional"];
const inputOpt = ["text", "email", "number"];
useEffect(() => {
if (props.defaultdata) {
setFormdata({
name: props.defaultdata?.options?.name || props?.widgetName || "",
defaultValue: props.defaultdata?.options?.defaultValue || "",
status: props.defaultdata?.options?.status || "required",
hint: props.defaultdata?.options?.hint || "",
textvalidate:
props.defaultdata?.options?.validation?.type === "regex"
? props.defaultdata?.options?.validation?.pattern
: props.defaultdata?.options?.validation?.type || ""
});
} else {
setFormdata({
...formdata,
name: props.defaultdata?.options?.name || props?.widgetName || ""
});
}
// eslint-disable-next-line
}, [props.defaultdata, props.widgetName]);
const handleSubmit = (e) => {
e.preventDefault();
if (props.handleData) {
props.handleData(formdata);
setFormdata({
name: "",
defaultValue: "",
status: "required",
hint: "",
textvalidate: ""
});
}
};
const handleChange = (e) => {
if (e) {
setFormdata({ ...formdata, [e.target.name]: e.target.value });
} else {
setFormdata({ ...formdata, textvalidate: "" });
}
};
const handledefaultChange = (e) => {
if (formdata.textvalidate) {
const regexObject = RegexParser(handleValidation(formdata.textvalidate));
const isValidate = regexObject?.test(e.target.value);
setIsValid(isValidate);
} else {
setIsValid(true);
}
setFormdata({ ...formdata, [e.target.name]: e.target.value });
};
function handleValidation(type) {
switch (type) {
case "email":
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
case "number":
return "/^\\d+$/";
case "text":
return "/^[a-zA-Zs]+$/";
default:
return type;
}
}
function handleBlurRegex() {
if (!formdata.textvalidate) {
setFormdata({ ...formdata, textvalidate: "" });
} else {
if (formdata.defaultValue) {
const regexObject = RegexParser(
handleValidation(formdata.textvalidate)
);
const isValidate = regexObject?.test(formdata.defaultValue);
if (isValidate === false) {
setFormdata({ ...formdata, defaultValue: "" });
}
}
}
}
return (
<ModalUi
isOpen={props.isOpen}
handleClose={props.handleClose && props.handleClose}
title={"Widget info"}
>
{(props.defaultdata?.type === textInputWidget ||
props.widgetName === textInputWidget) &&
!isEnableSubscription && (
<PremiumAlertHeader
message={
"Field validations are free in beta, this feature will incur a fee later."
}
/>
)}
<form
onSubmit={handleSubmit}
className={`${
props.defaultdata?.type === textInputWidget ||
props.widgetName === textInputWidget
? "pt-0"
: ""
} p-[20px]`}
>
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
Name
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
className="addUserInput"
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
required
/>
</div>
{(props.defaultdata?.type === textInputWidget ||
props.widgetName === textInputWidget) && (
<>
<div className="form-section">
<label
htmlFor="textvalidate"
className={
!props.isSubscribe && isEnableSubscription && "disabled"
}
style={{ fontSize: 13 }}
>
Validation
</label>
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
<div
className={
!props.isSubscribe && isEnableSubscription && "disabled"
}
style={{
display: "flex",
flexDirection: "row",
gap: 10,
marginBottom: "0.5rem"
}}
>
<div style={{ width: "100%", position: "relative" }}>
<input
className="relative z-20 w-[87%] md:w-[92%] p-2.5 border-[1px] border-[#d1d5db] rounded-l-md outline-none text-xs"
name="textvalidate"
placeholder="Enter custom expression"
value={formdata.textvalidate}
onChange={(e) => handleChange(e)}
// onBlur={() => handleBlurRegex()}
/>
<select
className="validationlist addUserInput"
name="textvalidate"
value={formdata.textvalidate}
onChange={(e) => handleChange(e)}
onBlur={() => handleBlurRegex()}
>
<option
disabled={formdata?.textvalidate}
style={{ fontSize: "13px" }}
>
Select...
</option>
{inputOpt.map((data, ind) => {
return (
<option
style={{ fontSize: "13px" }}
key={ind}
value={data}
>
{data}
</option>
);
})}
</select>
</div>
</div>
</div>
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
Default value
</label>
<input
className="addUserInput"
name="defaultValue"
value={formdata.defaultValue}
onChange={(e) => handledefaultChange(e)}
autoComplete="off"
onBlur={() => {
if (isValid === false) {
setFormdata({ ...formdata, defaultValue: "" });
setIsValid(true);
}
}}
/>
{isValid === false && (
<div
className="warning defaultvalueWarning"
style={{ fontSize: 12 }}
>
<i
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
invalid default value
</div>
)}
</div>
</>
)}
<div className="form-section">
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
marginBottom: "0.5rem"
}}
>
{statusArr.map((data, ind) => {
return (
<div
key={ind}
style={{
display: "flex",
flexDirection: "row",
gap: 5,
alignItems: "center"
}}
>
<input
style={{ accentColor: "red", marginRight: "10px" }}
type="radio"
name="status"
onChange={() =>
setFormdata({ ...formdata, status: data.toLowerCase() })
}
checked={
formdata.status.toLowerCase() === data.toLowerCase()
}
/>
<div style={{ fontSize: "13px", fontWeight: "500" }}>
{data}
</div>
</div>
);
})}
</div>
</div>
{(props.defaultdata?.type === textInputWidget ||
props?.widgetName === textInputWidget) && (
<div className="form-section">
<label htmlFor="hint" style={{ fontSize: 13 }}>
Hint
</label>
<input
className="addUserInput"
name="hint"
value={formdata.hint}
onChange={(e) => handleChange(e)}
/>
</div>
)}
<div
style={{
height: 1,
backgroundColor: "#b7b3b3",
width: "100%",
marginBottom: "16px"
}}
></div>
<button
style={{
color: "white",
padding: "5px 20px",
backgroundColor: "#32a3ac"
}}
type="submit"
className="finishBtn"
>
Save
</button>
</form>
</ModalUi>
);
};
export default WidgetNameModal;
@@ -1,163 +0,0 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "../../../primitives/Alert";
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
const folderPtr = {
__type: "Pointer",
className: folderCls,
objectId: parentFolderId
};
const [name, setName] = useState("");
const [folderList, setFolderList] = useState([]);
const [isAlert, setIsAlert] = useState(false);
const [isLoader, setIsLoader] = useState(false);
const [selectedParent, setSelectedParent] = useState();
const [alert, setAlert] = useState({ type: "info", message: "" });
useEffect(() => {
fetchFolder();
// eslint-disable-next-line
}, []);
const fetchFolder = async () => {
try {
const FolderQuery = new Parse.Query(folderCls);
if (parentFolderId) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
}
const res = await FolderQuery.find();
if (res) {
const result = JSON.parse(JSON.stringify(res));
if (result) {
setFolderList(result);
}
}
} catch (error) {
console.log("Err ", error);
}
};
const handleCreateFolder = async (event) => {
event.preventDefault();
handleLoader(true);
if (name) {
const currentUser = Parse.User.current();
const exsitQuery = new Parse.Query(folderCls);
exsitQuery.equalTo("Name", name);
exsitQuery.equalTo("Type", "Folder");
if (parentFolderId) {
exsitQuery.equalTo("Folder", folderPtr);
}
const templExist = await exsitQuery.first();
if (templExist) {
setAlert({ type: "danger", message: "Folder already exist!" });
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
} else {
const template = new Parse.Object(folderCls);
template.set("Name", name);
template.set("Type", "Folder");
if (selectedParent) {
template.set("Folder", {
__type: "Pointer",
className: folderCls,
objectId: selectedParent
});
} else if (parentFolderId) {
template.set("Folder", folderPtr);
}
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
const res = await template.save();
if (res) {
handleLoader(false);
setAlert({
type: "success",
message: "Folder created successfully!"
});
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
if (onSuccess) {
onSuccess(res);
}
}
}
} else {
handleLoader(false);
setAlert({ type: "info", message: "Please fill folder name" });
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
}
};
const handleOptions = (e) => {
setSelectedParent(e.target.value);
};
const handleLoader = (status) => {
setIsLoader(status);
};
return (
<div>
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
<div id="createFolder" className="relative">
{isLoader && (
<div className="absolute h-full w-full flex justify-center items-center ">
<div
style={{ fontSize: "45px", color: "#3dd3e0" }}
className="loader-37"
></div>
</div>
)}
<h1 className="text-base font-semibold">Create Folder</h1>
<div className="text-xs mt-2">
<label className="block">
Name<span className="text-red-500 text-[13px]">*</span>
</label>
<input
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
</div>
<div className="text-xs mt-2">
<label className="block">Parent Folder</label>
<select
value={selectedParent}
onChange={handleOptions}
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
>
<option>select</option>
{folderList.length > 0 &&
folderList.map((x) => (
<option key={x.objectId} value={x.objectId}>
{x.Name}
</option>
))}
</select>
</div>
<div>
<button
onClick={handleCreateFolder}
disabled={isLoader}
className="flex items-center rounded p-2 bg-[#32a3ac] text-white mt-3"
>
<i className="fa-solid fa-plus mr-1"></i>
<span>Create</span>
</button>
</div>
</div>
</div>
);
};
export default CreateFolder;
@@ -1,234 +0,0 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import CreateFolder from "./CreateFolder";
import ModalUi from "../../../primitives/ModalUi";
const FolderModal = (props) => {
const [clickFolder, setClickFolder] = useState("");
const [folderList, setFolderList] = useState([]);
const [tabList, setTabList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
const [isAdd, setIsAdd] = useState(false);
// below useEffect is called when user open popup
useEffect(() => {
if (props.isOpenModal) {
fetchFolder();
}
// eslint-disable-next-line
}, [props.isOpenModal]);
// `fetchFolder` is used to fetch of folder list created by user on basis of folderPtr or without folderPtr
const fetchFolder = async (folderPtr) => {
setIsLoader(true);
try {
const FolderQuery = new Parse.Query(props.folderCls);
if (folderPtr) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
}
const res = await FolderQuery.find();
if (res) {
const result = JSON.parse(JSON.stringify(res));
if (result) {
setFolderList(result);
setIsLoader(false);
}
setIsLoader(false);
}
} catch (error) {
setIsLoader(false);
}
};
// `handleSelect` is used to save pointer of folder selected by user and it's path in state
const handleSelect = (item) => {
setFolderList([]);
setClickFolder({ ObjectId: item.objectId, Name: item.Name });
if (tabList.length > 0) {
const tab = tabList.some((x) => x.objectId === item.objectId);
if (!tab) {
setTabList((tabs) => [...tabs, item]);
const folderPtr = {
__type: "Pointer",
className: props.folderCls,
objectId: item.objectId
};
fetchFolder(folderPtr);
}
} else {
setTabList((tabs) => [...tabs, item]);
const folderPtr = {
__type: "Pointer",
className: props.folderCls,
objectId: item.objectId
};
fetchFolder(folderPtr);
}
};
// `handleSubmit` is used to pass folderPtr to parent component
const handleSubmit = () => {
let url = "OpenSign™ Drive";
tabList.forEach((t) => {
url = url + " / " + t.Name;
});
if (props.onSuccess) {
props.onSuccess(clickFolder);
}
// SetIsOpen(false);
props.setIsOpenMoveModal(false);
};
// `handleCancel` is used to clear list of folder, close popup and folderUrl
const handleCancel = () => {
// SetIsOpen(false);
props.setIsOpenMoveModal(false);
setClickFolder({});
setFolderList([]);
setTabList([]);
};
// `handleCancel` is call when user click on folder name from path/tab in popup
const removeTabListItem = async (e, i) => {
e.preventDefault();
// setEditable(false);
if (!isAdd) {
setIsLoader(true);
let folderPtr;
if (i) {
setFolderList([]);
let list = tabList.filter((itm, j) => {
if (j <= i) {
return itm;
}
});
let _len = list.length - 1;
folderPtr = {
__type: "Pointer",
className: props.folderCls,
objectId: list[_len].objectId
};
setTabList(list);
} else {
setClickFolder({});
setFolderList([]);
setTabList([]);
}
fetchFolder(folderPtr);
}
};
// `handleCreate` is used to open folder creation form in popup
const handleCreate = () => {
setIsAdd(!isAdd);
};
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
const handleAddFolder = (newFolder) => {
props.setPdfData((prev) => [...prev, newFolder?.toJSON()]);
if (clickFolder && clickFolder.ObjectId) {
fetchFolder({
__type: "Pointer",
className: props.folderCls,
objectId: clickFolder.ObjectId
});
} else {
fetchFolder();
}
handleCreate();
};
return (
<div className="text-xs mt-2 ">
<ModalUi
title={"Select Folder"}
isOpen={props.isOpenModal}
handleClose={handleCancel}
>
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
<span
className="cursor-pointer"
title="OpenSign™ Drive"
onClick={(e) => removeTabListItem(e)}
>
OpenSign Drive /{" "}
</span>
{tabList &&
tabList.map((tab, i) => (
<React.Fragment key={`${tab.objectId}-${i}`}>
<span
className="cursor-pointer"
title={tab.Name}
onClick={(e) => removeTabListItem(e, i)}
>
{tab.Name}
</span>
{" / "}
</React.Fragment>
))}
<hr />
</div>
<div className="mt-2 mb-3">
<div className="max-h-[210px] overflow-auto">
{!isAdd &&
folderList.length > 0 &&
folderList.map((folder) => (
<div
key={folder.Name}
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
onClick={() => handleSelect(folder)}
>
<div className="flex items-center gap-2">
<i
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
aria-hidden="true"
></i>
<span className="font-semibold">{folder.Name}</span>
</div>
</div>
))}
</div>
{isAdd && (
<CreateFolder
parentFolderId={clickFolder && clickFolder.ObjectId}
folderCls={props.folderCls}
onSuccess={handleAddFolder}
/>
)}
{isLoader && (
<div className="flex justify-center">
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
</div>
)}
</div>
</div>
<hr />
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
<div
className="text-[30px] cursor-pointer text-[#32a3ac]"
title="Save Here"
onClick={handleCreate}
>
{isAdd ? (
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
) : (
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
)}
</div>
<div
className="text-[30px] cursor-pointer"
title="Save Here"
onClick={handleSubmit}
>
<i className="fas fa-save" aria-hidden="true"></i>
</div>
</div>
</ModalUi>
</div>
);
};
export default FolderModal;
@@ -1,304 +0,0 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import CreateFolder from "./CreateFolder";
import ModalUi from "../../../primitives/ModalUi";
import Tooltip from "../../../primitives/Tooltip";
const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
const [isOpen, SetIsOpen] = useState(false);
const [clickFolder, setClickFolder] = useState("");
const [selectFolder, setSelectedFolder] = useState({});
const [folderList, setFolderList] = useState([]);
const [tabList, setTabList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
const [folderPath, setFolderPath] = useState("");
const [isAdd, setIsAdd] = useState(false);
useEffect(() => {
if (isOpen) {
setIsAdd(false);
setClickFolder({});
setFolderList([]);
setTabList([]);
fetchFolder();
}
// eslint-disable-next-line
}, [isOpen]);
// below useEffect is used to reset folder selection if user pass isReset = true from parent component
useEffect(() => {
if (isReset == true) {
handleReset();
}
}, [isReset]);
const handleReset = () => {
setFolderPath({});
setSelectedFolder("");
setClickFolder("");
setIsAdd(false);
setFolderList([]);
setTabList([]);
};
// `fetchFolder` is used to fetch of folder list created by user on basis of folderPtr or without folderPtr
const fetchFolder = async (folderPtr) => {
setIsLoader(true);
try {
const FolderQuery = new Parse.Query(folderCls);
if (folderPtr) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
}
const res = await FolderQuery.find();
if (res) {
const result = JSON.parse(JSON.stringify(res));
if (result) {
setFolderList(result);
setIsLoader(false);
}
setIsLoader(false);
}
} catch (error) {
setIsLoader(false);
}
};
// `handleSelect` is used to save pointer of folder selected by user and it's path in state
const handleSelect = (item) => {
setFolderList([]);
setClickFolder({ ObjectId: item.objectId, Name: item.Name });
if (tabList.length > 0) {
const tab = tabList.some((x) => x.objectId === item.objectId);
if (!tab) {
setTabList((tabs) => [...tabs, item]);
const folderPtr = {
__type: "Pointer",
className: folderCls,
objectId: item.objectId
};
fetchFolder(folderPtr);
}
} else {
setTabList((tabs) => [...tabs, item]);
const folderPtr = {
__type: "Pointer",
className: folderCls,
objectId: item.objectId
};
fetchFolder(folderPtr);
}
};
// `handleSubmit` is used to pass folderPtr to parent component
const handleSubmit = () => {
let url = "OpenSign™ Drive";
tabList.forEach((t) => {
url = url + " / " + t.Name;
});
setFolderPath(url);
setSelectedFolder(clickFolder);
if (onSuccess) {
onSuccess(clickFolder);
}
SetIsOpen(false);
};
// `handleCancel` is used to clear list of folder, close popup and folderUrl
const handleCancel = () => {
SetIsOpen(false);
setClickFolder({});
setFolderList([]);
setTabList([]);
};
// `handleCancel` is call when user click on folder name from path/tab in popup
const removeTabListItem = async (e, i) => {
e.preventDefault();
// setEditable(false);
if (!isAdd) {
setIsLoader(true);
let folderPtr;
if (i) {
setFolderList([]);
let list = tabList.filter((itm, j) => {
if (j <= i) {
return itm;
}
});
let _len = list.length - 1;
folderPtr = {
__type: "Pointer",
className: folderCls,
objectId: list[_len].objectId
};
setTabList(list);
} else {
setClickFolder({});
setSelectedFolder({});
setFolderList([]);
setTabList([]);
}
fetchFolder(folderPtr);
}
};
// `handleCreate` is used to open folder creation form in popup
const handleCreate = () => {
setIsAdd(!isAdd);
};
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
const handleAddFolder = () => {
setFolderList([]);
if (clickFolder && clickFolder.ObjectId) {
fetchFolder({
__type: "Pointer",
className: folderCls,
objectId: clickFolder.ObjectId
});
} else {
fetchFolder();
}
handleCreate();
};
return (
<div className="text-xs mt-2 ">
<div>
<label className="block">
Select Folder
{required && <span className="text-red-500 text-[13px]">*</span>}
</label>
</div>
<div className="relative max-w-sm">
<div
onClick={() => SetIsOpen(true)}
className=" cursor-pointer rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center"
>
<div>
<i
className="far fa-folder-open text-[40px] text-[#33bbff]"
style={{ fontSize: "40px" }}
aria-hidden="true"
></i>
</div>
<div className="font-semibold ">
<div className="flex items-center gap-2">
<p>
{selectFolder && selectFolder.Name
? selectFolder.Name
: "OpenSign™ Drive"}
</p>
<div
className="text-black text-sm"
// onClick={() => SetIsOpen(true)}
>
<i
className="fa fa-pencil cursor-pointer"
title="Select Folder"
aria-hidden="true"
></i>
</div>
</div>
<p className="text-[10px] text-gray-400">
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
</p>
</div>
</div>
<div className="absolute top-2 right-1 cursor-pointer">
<Tooltip
message={
"If you do not select a folder, your signed document will be saved in the Main OpenSign drive folder."
}
/>
</div>
</div>
<ModalUi
title={"Select Folder"}
isOpen={isOpen}
handleClose={handleCancel}
>
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
<span
className="cursor-pointer"
title="OpenSign™ Drive"
onClick={(e) => removeTabListItem(e)}
>
OpenSign Drive /{" "}
</span>
{tabList &&
tabList.map((tab, i) => (
<React.Fragment key={`${tab.objectId}-${i}`}>
<span
className="cursor-pointer"
title={tab.Name}
onClick={(e) => removeTabListItem(e, i)}
>
{tab.Name}
</span>
{" / "}
</React.Fragment>
))}
<hr />
</div>
<div className="mt-2 mb-3">
<div className="max-h-[210px] overflow-auto">
{!isAdd &&
folderList.length > 0 &&
folderList.map((folder) => (
<div
key={folder.Name}
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
onClick={() => handleSelect(folder)}
>
<div className="flex items-center gap-2">
<i
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
aria-hidden="true"
></i>
<span className="font-semibold">{folder.Name}</span>
</div>
</div>
))}
</div>
{isAdd && (
<CreateFolder
parentFolderId={clickFolder && clickFolder.ObjectId}
folderCls={folderCls}
onSuccess={handleAddFolder}
/>
)}
{isLoader && (
<div className="flex justify-center">
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
</div>
)}
</div>
</div>
<hr />
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
<div
className="text-[30px] cursor-pointer text-[#32a3ac]"
title="Save Here"
onClick={handleCreate}
>
{isAdd ? (
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
) : (
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
)}
</div>
<div
className="text-[30px] cursor-pointer"
title="Save Here"
onClick={handleSubmit}
>
<i className="fas fa-save" aria-hidden="true"></i>
</div>
</div>
</ModalUi>
</div>
);
};
export default SelectFolder;
@@ -1,116 +0,0 @@
import React, { useState } from "react";
import Parse from "parse";
import "../../../styles/AddUser.css";
import AsyncSelect from "react-select/async";
const customStyles = {
control: (provided) => ({
...provided,
fontSize: "13px" // Font size for the control
}),
option: (provided) => ({
...provided,
fontSize: "13px" // Font size for the options
})
};
const SelectSigners = (props) => {
const [userList, setUserList] = useState([]);
const [selected, setSelected] = useState();
const [userData, setUserData] = useState({});
const [isError, setIsError] = useState(false);
// `handleOptions` is used to set just save from quick form to selected option in dropdown
const handleOptions = (item) => {
setSelected(item);
const userData = userList.find((x) => x.objectId === item.value);
if (userData) {
setUserData(userData);
}
};
const handleAdd = () => {
if (userData && userData.objectId) {
props.details(userData);
if (props.closePopup) {
props.closePopup();
}
} else {
setIsError(true);
setTimeout(() => setIsError(false), 1000);
}
};
const loadOptions = async (inputValue) => {
try {
const currentUser = Parse.User.current();
const contactbook = new Parse.Query("contracts_Contactbook");
contactbook.equalTo(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
if (inputValue.length > 1) {
contactbook.matches("Name", new RegExp(inputValue, "i"));
}
contactbook.notEqualTo("IsDeleted", true);
const contactRes = await contactbook.find();
if (contactRes) {
const res = JSON.parse(JSON.stringify(contactRes));
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
//and filter signers from total signer's list which already present in document's signers list
const compareArrays = (res, signerObj) => {
return res.filter(
(item1) =>
!signerObj.find((item2) => item2.objectId === item1.objectId)
);
};
//get update signer's List if signersdata is present
const updateSignersList =
props?.signersData && compareArrays(res, props?.signersData);
const result = updateSignersList ? updateSignersList : res;
setUserList(result);
return await result.map((item) => ({
label: item.Email,
value: item.objectId
}));
}
} catch (error) {
console.log("err", error);
}
};
return (
<div className="addusercontainer">
<div className="form-wrapper">
<div className="form-section" style={{ marginBottom: 0 }}>
<label style={{ fontSize: 14, fontWeight: "700" }}>Choose User</label>
<AsyncSelect
cacheOptions
defaultOptions
value={selected}
loadingMessage={() => "Loading..."}
noOptionsMessage={() => "User not Found"}
loadOptions={loadOptions}
onChange={handleOptions}
styles={customStyles}
/>
</div>
{isError ? (
<p style={{ color: "red", fontSize: "12px", margin: "5px" }}>
Please select signer
</p>
) : (
<p style={{ color: "transparent", fontSize: "12px", margin: "5px" }}>
.
</p>
)}
<div>
<button className="submitbutton" onClick={() => handleAdd()}>
Submit
</button>
</div>
</div>
</div>
);
};
export default SelectSigners;
@@ -1,201 +0,0 @@
import React, { useState, useEffect } from "react";
import Select from "react-select";
import AddSigner from "../../AddSigner";
import Modal from "react-modal";
import Parse from "parse";
import Tooltip from "../../../primitives/Tooltip";
function arrayMove(array, from, to) {
array = array.slice();
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
return array;
}
/**
* react-sortable-hoc is depcreated not usable from react 18.x.x
* need to replace it with @dnd-kit
* code changes required
*/
const SignersInput = (props) => {
Modal.setAppElement("body");
const [state, setState] = useState(undefined);
// const [editFormData, setEditFormData] = useState([]);
const [selected, setSelected] = useState([]);
const [isModal, setIsModel] = useState(false);
const onChange = (selectedOptions) => setSelected(selectedOptions);
const [modalIsOpen, setModalIsOpen] = useState(false);
const onSortEnd = ({ oldIndex, newIndex }) => {
const newValue = arrayMove(selected, oldIndex, newIndex);
setSelected(newValue);
};
const GetSelectListData = async () => {
try {
const currentUser = Parse.User.current();
const contactbook = new Parse.Query("contracts_Contactbook");
contactbook.equalTo(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactbook.notEqualTo("IsDeleted", true);
const contactRes = await contactbook.find();
if (contactRes) {
const res = JSON.parse(JSON.stringify(contactRes));
let list = [];
// let _selected = [];
res.forEach((x) => {
let obj = {
label: x.Name,
value: x.objectId,
isChecked: true
};
list.push(obj);
});
setState(list);
}
} catch (error) {
console.log("err", error);
}
};
useEffect(() => {
GetSelectListData();
}, []);
useEffect(() => {
if (props.isReset && props.isReset === true) {
setSelected([]);
}
}, [props.isReset]);
useEffect(() => {
if (selected && selected.length) {
let newData = [];
selected.forEach((x) => {
newData.push(x.value);
});
if (props.onChange) {
props.onChange(newData);
}
}
// eslint-disable-next-line
}, [selected]);
const handleModalCloseClick = () => {
setIsModel(false);
setModalIsOpen(false);
};
const openModal = () => {
setModalIsOpen(true);
};
// `handleNewDetails` is used to set just save from quick form to selected option in dropdown
const handleNewDetails = (data) => {
setState([...state, data]);
if (selected.length > 0) {
setSelected([...selected, data]);
} else {
setSelected([data]);
}
};
return (
<div className="text-xs mt-2 ">
<label className="block relative">
Signers
{props.required && <span className="text-red-500 text-[13px]">*</span>}
<span className="absolute ml-1 text-xs z-50">
<Tooltip
id={"signer-tooltip"}
message={"Begin typing a contact's name to see suggested signers from your saved contacts or add new ones. Arrange the signing order by adding signers in the desired sequence. Use the '+' button to include signers and the 'x' to remove them. Each signer will receive an email prompt to sign the document in the order listed."}
/>
</span>
</label>
<div style={{ display: "flex", gap: 5 }}>
<div style={{ flexWrap: "wrap", width: "100%" }}>
<Select
onSortEnd={onSortEnd}
distance={4}
isMulti
options={state || []}
value={selected}
onChange={onChange}
closeMenuOnSelect={false}
required={props.required}
/>
</div>
<div
onClick={() => {
setIsModel(true);
openModal();
}}
style={{
cursor: "pointer",
borderRadius: 4,
border: "1px solid #ccc",
minHeight: 38,
minWidth: 48,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<i className="fas fa-plus"></i>
</div>
<Modal
isOpen={modalIsOpen}
onRequestClose={handleModalCloseClick}
shouldCloseOnOverlayClick={false}
id="modal1"
contentLabel="Modal"
style={{
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
transform: "translate(-50%, -50%)",
border: "1px transparent",
padding: 0
},
overlay: {
width: "100%",
backgroundColor: "rgba(0, 0, 0, 0.75)",
zIndex: 50
}
}}
>
<div className="min-w-full md:min-w-[500px]">
<div
className="flex justify-between rounded-t items-center py-[10px] px-[20px] text-white"
style={{ background: "#32a3ac" }}
>
<div className="text-[1.2rem] font-normal">Add Signer</div>
<button
onClick={handleModalCloseClick}
className="text-[1.5rem] cursor-pointer focus:outline-none"
>
&times;
</button>
</div>
{isModal && (
<AddSigner
valueKey={"objectId"}
displayKey={"Name"}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</div>
</Modal>
</div>
</div>
);
};
export default SignersInput;
+16 -20
View File
@@ -2,24 +2,20 @@ import React from "react";
import { NavLink } from "react-router-dom";
const Menu = ({ item, isOpen, closeSidebar }) => {
return (
<li key={item.title} role="none">
<NavLink
to={
item.pageType
? `/${item.pageType}/${item.objectId}`
: `/${item.objectId}`
}
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 cursor-pointer focus:text-[#0056b3] focus:bg-[#eef1f5]"
onClick={closeSidebar}
tabIndex={isOpen ? 0 : -1}
role="menuitem"
>
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
<span className="ml-3 lg:ml-4">{item.title}</span>
</NavLink>
</li>
);
};
return (
<li key={item.title} role="none">
<NavLink
to={`/${item.pageType}/${item.objectId}`}
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4"
onClick={closeSidebar}
tabIndex={isOpen ? 0 : -1}
role="menuitem"
>
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
<span className="ml-3 lg:ml-4">{item.title}</span>
</NavLink>
</li>
)
}
export default Menu;
export default Menu;
+15 -25
View File
@@ -1,8 +1,8 @@
import React, { useState, useEffect } from "react";
import Menu from "./Menu";
import Menu from './Menu';
import Submenu from "./SubMenu";
import SocialMedia from "./SocialMedia";
import SocialMedia from './SocialMedia';
import Parse from "parse";
import dp from "../../assets/images/dp.png";
@@ -20,10 +20,16 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
}, []);
const menuItem = async () => {
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
try {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
var sideMenu = Parse.Object.extend("w_menu");
var query = new Parse.Query(sideMenu);
query.equalTo("objectId", localStorage.getItem("defaultmenuid"));
const results = await query.first();
const resultjson = results.toJSON();
let result = resultjson;
@@ -31,6 +37,7 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
} catch (e) {
console.error("Problem", e);
}
};
return (
@@ -40,11 +47,7 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
>
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
<div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden">
<img
className="w-full h-full object-contain"
src={image}
alt="Profile"
/>
<img className="w-full h-full object-cover" src={image} alt="Profile" />
</div>
<div>
<p className="text-[14px] font-bold">{username}</p>
@@ -54,27 +57,14 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
</div>
</div>
<nav aria-label="OpenSign Sidebar Navigation">
<ul
className="text-sm"
role="menubar"
aria-label="OpenSign Sidebar Navigation"
>
{menuList.map((item) =>
<ul className="text-sm" role="menubar" aria-label="OpenSign Sidebar Navigation">
{menuList.map((item) => (
!item.children ? (
<Menu
key={item.title}
item={item}
isOpen={isOpen}
closeSidebar={closeSidebar}
/>
<Menu key={item.title} item={item} isOpen={isOpen} closeSidebar={closeSidebar} />
) : (
<Submenu
key={item.title}
item={item}
closeSidebar={closeSidebar}
/>
<Submenu key={item.title} item={item} closeSidebar={closeSidebar} />
)
)}
))}
</ul>
</nav>
<footer className="mt-4 flex justify-center items-center text-[25px] text-black gap-3">
@@ -13,7 +13,7 @@ const Submenu = ({ item, closeSidebar }) => {
<li role="none">
<button
onClick={toggleSubmenu}
className="w-full flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:outline-none focus:text-[#0056b3] focus:bg-[#eef1f5]"
className="w-full flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:ring-2 focus:ring-blue-600"
aria-expanded={submenuOpen}
aria-haspopup="true"
aria-controls={`submenu-${title}`}
@@ -22,9 +22,8 @@ const Submenu = ({ item, closeSidebar }) => {
<div className="flex justify-between items-center w-full">
<span className="ml-3 lg:ml-4">{title}</span>
<i
className={`${
submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
}`}
className={`${submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
}`}
aria-hidden="true"
></i>
</div>
@@ -34,20 +33,13 @@ const Submenu = ({ item, closeSidebar }) => {
{children.map((childItem) => (
<li key={childItem.title} role="none">
<NavLink
to={
childItem.pageType
? `/${childItem.pageType}/${childItem.objectId}`
: `/${childItem.objectId}`
}
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white focus:text-[#0056b3] focus:bg-[#eef1f5] cursor-pointer"
to={`/${childItem.pageType}/${childItem.objectId}`}
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white"
onClick={closeSidebar}
role="menuitem"
tabIndex={submenuOpen ? 0 : -1}
>
<i
className={`${childItem.icon} text-[18px]`}
aria-hidden="true"
></i>
<i className={`${childItem.icon} text-[18px]`} aria-hidden="true"></i>
<span className="ml-3 lg:ml-4">{childItem.title}</span>
</NavLink>
</li>
File diff suppressed because it is too large Load Diff
+1 -4
View File
@@ -3,10 +3,7 @@ export const appInfo = {
appTitle: "contracts",
applogo: logo,
appname: "contracts",
appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign",
baseUrl: process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app",
baseurl: process.env.REACT_APP_SERVERURL,
defaultRole: "contracts_User",
fbAppId: process.env.REACT_APP_FBAPPID
? `${process.env.REACT_APP_FBAPPID}`
-12
View File
@@ -1,12 +0,0 @@
export const contactCls = "contracts_Contactbook";
export const templateCls = "contracts_Template";
export const documentCls = "contracts_Document";
export const rejectBtn =
"bg-[#ffffff] rounded-sm shadow-md text-[12px] font-semibold uppercase text-black py-1.5 px-4 focus:outline-none text-center border-[1px] border-[#b4b4b4]";
export const submitBtn =
"bg-[#32a3ac] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-4 focus:outline-none text-center";
export const modalSubmitBtnColor = "#17a2b8";
export const modalCancelBtnColor = "white";
export const themeColor = "#47a3ad";
export const iconColor = "#686968";
export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION;
+65 -44
View File
@@ -1,30 +1,30 @@
import axios from "axios";
const parseAppId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
const serverUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
import Parse from "parse";
const parseAppId = localStorage.getItem("AppID12");
const serverUrl = localStorage.getItem("BaseUrl12");
Parse.serverURL = serverUrl;
Parse.initialize(parseAppId);
export const SaveFileSize = async (size, imageUrl, tenantId) => {
export const SaveFileSize = async (size, imageUrl) => {
//checking server url and save file's size
const tenantPtr = {
__type: "Pointer",
className: "partners_Tenant",
objectId: tenantId
};
const _tenantPtr = JSON.stringify(tenantPtr);
try {
const res = await axios.get(
`${serverUrl}/classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`,
{
const response = await axios
.get(`${serverUrl}classes/partners_TenantCredits`, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
}
);
const response = res.data.results;
})
.then((result) => {
const res = result.data;
// console.log("res", res);
return res.results;
})
.catch((err) => {
console.log("axois err ", err);
});
let data;
// console.log("response", response);
if (response && response.length > 0) {
@@ -33,48 +33,69 @@ export const SaveFileSize = async (size, imageUrl, tenantId) => {
? response[0].usedStorage + size
: size
};
await axios.put(
`${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`,
data,
{
await axios
.put(
`${serverUrl}classes/partners_TenantCredits/${response[0].objectId}`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
}
)
.then(() => {
// const res = result.data;
// console.log("save res", res);
})
.catch((err) => {
console.log("axois err ", err);
});
} else {
data = {
usedStorage: size
};
await axios
.post(`${serverUrl}classes/partners_TenantCredits`, data, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
}
);
} else {
data = { usedStorage: size, PartnersTenant: tenantPtr };
await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
});
})
.then(() => {
// const res = result.data;
// console.log("res", res);
})
.catch((err) => {
console.log("axois err ", err);
});
}
} catch (err) {
console.log("err in save usage", err);
} catch (e) {
console.log("org app error", e);
}
saveDataFile(size, imageUrl, tenantPtr);
saveDataFile(size, imageUrl);
};
//function for save fileUrl and file size in particular client db class partners_DataFiles
const saveDataFile = async (size, imageUrl, tenantPtr) => {
const saveDataFile = async (size, imageUrl) => {
const data = {
FileUrl: imageUrl,
FileSize: size,
TenantPtr: tenantPtr
FileSize: size
};
// console.log("data save",file, data)
try {
await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, {
await axios
.post(`${serverUrl}classes/partners_DataFiles`, data, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
})
.then(() => {
// const res = result.data;
// console.log("res", res);
})
.catch((err) => {
console.log("axois err ", err);
});
} catch (err) {
console.log("err in save usage ", err);
}
};
+1 -12
View File
@@ -4,15 +4,4 @@
body{
background-color: white;
}
/* Custom CSS to hide scrollbar */
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.hide-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
}
+1 -74
View File
@@ -1,74 +1 @@
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import { store } from "./redux/store";
import { CookiesProvider } from "react-cookie";
import { HTML5Backend } from "react-dnd-html5-backend";
import { TouchBackend } from "react-dnd-touch-backend";
import {
DndProvider,
TouchTransition,
MouseTransition,
Preview
} from "react-dnd-multi-backend";
import DragElement from "./components/pdf/DragElement";
import TagManager from "react-gtm-module";
import Parse from "parse";
const appId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
const serverUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
Parse.initialize(appId);
Parse.serverURL = serverUrl;
const HTML5toTouch = {
backends: [
{
id: "html5",
backend: HTML5Backend,
transition: MouseTransition
},
{
id: "touch",
backend: TouchBackend,
options: { enableMouseEvents: true },
preview: true,
transition: TouchTransition
}
]
};
const generatePreview = (props) => {
const { item, style } = props;
const newStyle = {
...style
};
return (
<div style={newStyle}>
<DragElement {...item} />
</div>
);
};
if (process.env.REACT_APP_GTM) {
const tagManagerArgs = {
gtmId: process.env.REACT_APP_GTM
};
TagManager.initialize(tagManagerArgs);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<CookiesProvider defaultSetOptions={{ path: "/" }}>
<Provider store={store}>
<DndProvider options={HTML5toTouch}>
<Preview>{generatePreview}</Preview>
<App />
</DndProvider>
</Provider>
</CookiesProvider>
);
import("./Bootstrap");
+580 -22
View File
@@ -1,26 +1,584 @@
import { documentCls, templateCls } from "../constant/const";
export const formJson = {
import Parse from "parse";
export const formJson = (id) => {
let formData;
//json form for signYourself
sHAnZphf69: {
title: "Sign Yourself",
redirectRoute: "signaturePdf",
msgVar: "Document",
Cls: documentCls
},
if (id === "sHAnZphf69") {
formData = {
jsonSchema: {
title: "Sign Yourself",
description: "",
type: "object",
required: ["URL", "Name"],
properties: {
URL: {
type: "string",
title: "Select Document",
filetypes: [],
maxfilesizeKB: "10000",
uploadtype: "regular",
helpbody: "",
helplink: ""
},
Name: {
type: "string",
title: "Title",
maxLength: 50
},
Description: {
type: "string",
title: "Description",
maxLength: 500
},
Folder: {
title: "Folder",
parent: null,
data: {
ClassName: "contracts_Document",
FolderNameField: "Name",
FolderTypeField: "Type",
FolderTypeValue: "Folder",
FolderDescription: "Description",
ParentFolderField: "Folder",
FormId: "YjIB7W7Xs6",
Query: 'where={"Folder":{"$exists":false},"Type":"Folder"}'
}
},
ExtUserPtr: {
data: {
valueKey: "objectId",
isPointer: true
}
}
}
},
uiSchema: {
URL: {
"ui:field": "FileUpload"
},
Description: {
"ui:widget": "textarea",
"ui:options": {
rows: 2
}
},
Note: {
"ui:widget": "textarea",
"ui:options": {
rows: 2
}
},
Folder: {
"ui:field": "FolderComponent"
},
ExtUserPtr: {
"ui:field": "HiddenField"
}
},
userSchema: {},
rules: null,
noValidate: false,
liveValidate: false,
isRegisterForm: false,
help: { htmlbody: "" },
description: "",
class: "contracts_Document",
buttons: {
add: {
resetText: "Reset",
submitText: "Submit"
},
edit: { submitText: "Update", cancelText: "Cancel" }
},
formACL: {
"#currentUser#": { read: true, write: true },
"*": { read: true, write: true }
},
redirect_id:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/signaturePdf",
success_message: "Success!",
success_redirect: "Microapp",
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
//json form for request signatures
"8mZzFxbG1z": {
title: "Request Signatures",
msgVar: "Document",
redirectRoute: "placeHolderSign",
Cls: documentCls,
signers: true
},
//json form for template
template: {
title: "New Template",
redirectRoute: "template",
msgVar: "Template",
Cls: templateCls
return formData;
}
//json form for request signature
else if (id === "8mZzFxbG1z") {
const user = Parse.User.current();
const userPtr = JSON.stringify({
__type: "Pointer",
className: "_User",
objectId: user.id
});
formData = {
jsonSchema: {
title: "New Document",
description: "",
type: "object",
required: ["URL", "Name", "Note", "TimeToCompleteDays", "Signers"],
properties: {
URL: {
type: "string",
title: "Select Document",
filetypes: [],
maxfilesizeKB: "5000",
uploadtype: "regular",
helpbody: "",
helplink: ""
},
Name: {
type: "string",
title: "Title",
maxLength: 100
},
Description: {
type: "string",
title: "Description",
maxLength: 500
},
Signers: {
type: "array",
title: "Signers",
isSortable: false,
selectAll: false,
selectedData: null,
data: {
class: "contracts_Contactbook",
displayKey: "Name",
valueKey: "objectId",
query: `where={"CreatedBy":${userPtr},"IsDeleted":{"$ne":true}}&keys=Name`,
isPointer: true,
helpbody: "",
helplink: "",
quickAddFormId: "qUGywSWd8e"
}
},
Note: {
type: "string",
title: "Note",
default: "Please review and sign this document",
maxLength: 500
},
Folder: {
title: "Folder",
data: {
ClassName: "contracts_Document",
FolderNameField: "Name",
FolderTypeField: "Type",
FolderTypeValue: "Folder",
FolderDescription: "Description",
ParentFolderField: "Folder",
FormId: "YjIB7W7Xs6",
Query: 'where={"Folder":{"$exists":false},"Type":"Folder"}'
}
},
TimeToCompleteDays: {
type: "number",
title: "Time To Complete (Days)",
default: 15,
maxLength: 5000
},
ExtUserPtr: {
data: {
valueKey: "objectId",
isPointer: true
}
}
}
},
uiSchema: {
URL: {
"ui:field": "FileUpload"
},
Description: {
"ui:widget": "textarea",
"ui:options": {
rows: 2
}
},
Note: {
"ui:widget": "textarea",
"ui:options": {
rows: 2
}
},
Signers: {
"ui:field": "MultiSelectField"
},
Folder: {
"ui:field": "FolderComponent"
},
TimeToCompleteDays: {
"ui:placeholder": "No. of days",
"ui:widget": "updown"
},
ExtUserPtr: {
"ui:field": "HiddenField"
}
},
userSchema: {},
rules: null,
noValidate: false,
liveValidate: false,
isRegisterForm: false,
help: { htmlbody: "" },
description: "",
class: "contracts_Document",
buttons: {
add: {
resetText: "Reset",
submitText: "Submit"
},
edit: { submitText: "Update", cancelText: "Cancel" }
},
formACL: {
"#currentUser#": { read: true, write: true },
"*": { read: true, write: true }
},
redirect_id:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/placeHolderSign",
success_message: "Success!",
success_redirect: "Microapp",
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
return formData;
}
//json form for create new folder
else if (id === "YjIB7W7Xs6") {
formData = {
jsonSchema: {
title: "New Folder",
description: "",
type: "object",
required: ["Name"],
properties: {
Name: {
type: "string",
title: "Name",
maxLength: 50
},
Type: {
type: "string",
data: {
valueKey: "Type",
isPointer: false
},
default: "Folder"
},
Folder: {
title: "Parent Folder",
data: {
class: "contracts_Document",
displayKey: "Name",
valueKey: "objectId",
query: 'where={"Type":"Folder"}&keys=Name',
isPointer: true,
savePointerClass: "",
helpbody: "",
helplink: ""
}
}
}
},
uiSchema: {
Type: {
"ui:field": "HiddenField"
},
Folder: {
"ui:field": "Level1Dropdown"
}
},
userSchema: {},
rules: null,
noValidate: false,
liveValidate: false,
isRegisterForm: false,
help: { htmlbody: "" },
description: "",
class: "contracts_Document",
buttons: {
add: {
resetText: "Reset",
submitText: "Submit"
},
edit: { submitText: "Update", cancelText: "Cancel" }
},
formACL: {
"#currentUser#": { read: true, write: true },
"*": { read: true, write: true }
},
redirect_id: "",
success_message: "Success!",
success_redirect: "",
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
return formData;
}
//json form for add signers
else if (id === "fICciRuUcB") {
formData = {
jsonSchema: {
title: "Add Signer",
description: "",
type: "object",
required: ["Name", "Email", "Phone", "Company"],
properties: {
Name: {
type: "string",
title: "Name",
maxLength: 50
},
Email: {
type: "string",
title: "Email",
maxLength: 50
},
Phone: {
type: "string",
title: "Phone",
maxLength: 50
},
Company: {
type: "string",
title: "Company",
maxLength: 50
},
JobTitle: {
type: "string",
title: "JobTitle",
maxLength: 50
},
IsContactEntry: {
type: "boolean",
data: {
valueKey: "IsContactEntry",
isPointer: false
},
default: true
}
}
},
uiSchema: {
Email: {
"ui:widget": "email"
},
IsContactEntry: {
"ui:field": "HiddenField"
}
},
userSchema: {
email: "$Email",
name: "$Name",
password: "$Phone",
phone: "$Phone",
role: "contracts_User",
username: "$Email"
},
rules: null,
noValidate: false,
liveValidate: false,
isRegisterForm: true,
help: {},
description: "",
class: "contracts_Users",
buttons: {
add: {
resetText: "Reset",
submitText: "Submit"
},
edit: { submitText: "Update", cancelText: "Cancel" }
},
formACL: {
"#currentUser#": { read: true, write: true },
"*": { read: true, write: true }
},
redirect_id: "",
success_message: "Success!",
success_redirect: "",
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
return formData;
}
//json form for add users
else if (id === "lM0xRnM3iE") {
formData = {
jsonSchema: {
title: "Add User",
description: "",
type: "object",
required: ["Name", "Email", "Phone", "JobTitle"],
properties: {
Name: {
type: "string",
title: "Name",
maxLength: 50
},
Email: {
type: "string",
title: "Email",
maxLength: 50
},
Phone: {
type: "string",
title: "Phone",
maxLength: 50
},
JobTitle: {
type: "string",
title: "Job Title",
maxLength: 50
},
Company: {
type: "string",
title: "Company",
maxLength: 50
}
}
},
uiSchema: {
Email: {
"ui:widget": "email"
},
Phone: {
"ui:options": {
inputType: "tel"
}
}
},
userSchema: {
email: "$Email",
name: "$Name",
password: "$Phone",
phone: "$Phone",
role: "contracts_User",
username: "$Email"
},
rules: null,
noValidate: false,
liveValidate: false,
isRegisterForm: true,
help: {},
description: "",
class: "contracts_Users",
buttons: {
add: {
resetText: "Reset",
submitText: "Submit"
},
edit: { submitText: "Update", cancelText: "Cancel" }
},
formACL: {
"#currentUser#": { read: true, write: true },
"*": { read: true, write: true }
},
redirect_id: "",
success_message: "Success!",
success_redirect: "",
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
return formData;
} else if (id === "qUGywSWd8e") {
formData = {
jsonSchema: {
title: "Add Contact",
description: "",
type: "object",
required: ["Name", "Email", "Phone"],
properties: {
Name: {
type: "string",
title: "Name",
maxLength: 50
},
Email: {
type: "string",
title: "Email",
maxLength: 50
},
Phone: {
type: "string",
title: "Phone",
maxLength: 50
}
}
},
uiSchema: {
Email: {
"ui:widget": "email"
},
Phone: {
"ui:options": {
inputType: "tel"
}
}
},
userSchema: {
name: "$Name",
username: "$Email",
email: "$Email",
password: "$Phone",
phone: "$Phone",
role: "contracts_Guest"
},
restrict_form_entry: {
enable_captcha: false,
allow_one_entry_per_ip_address: false,
allow_one_entry_per_user: false,
enable_geo_fence: false
},
restrict_form_access: {
entryCount: ""
},
help: {},
email_template: {
from: "",
to: "",
subject: "",
message: ""
},
buttons: {
add: {
submitText: "Submit",
resetText: "Reset"
},
edit: {
submitText: "Update",
cancelText: "Cancel"
}
},
appId: {
__type: "Pointer",
className: "w_appinfo",
objectId: "aIPmIvMzGM"
},
formACL: {
"*": {
read: true,
write: true
},
"#currentUser#": {
read: true,
write: true
}
},
class: "contracts_Contactbook",
description: "",
success_message: "Success!",
form_permission: "private",
success_redirect: "",
redirect_id: "",
isRegisterForm: true,
liveValidate: false,
noValidate: false,
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
return formData;
}
};
+45 -242
View File
@@ -1,9 +1,7 @@
export default function reportJson(id) {
// console.log("json ", json);
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
const head = ["Sr.No", "Name", "Note", "Folder", "File", "Owner", "Signers"];
const contactbook = ["Sr.No", "Name", "Email", "Phone"];
switch (id) {
// draft documents report
case "ByHuevtCFY":
@@ -12,26 +10,14 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "1231",
hoverLabel: "Edit",
btnLabel: "sign",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa-solid fa-pen",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "2142",
hoverLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
btnIcon: "fa fa-plus",
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
}
],
helpMsg:
"These are documents you have started but have not finalized for sending."
]
};
// Need your sign report
case "4Hhwbp482K":
@@ -40,18 +26,14 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "4536",
btnLabel: "sign",
hoverLabel: "Sign",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa-solid fa-signature",
redirectUrl: "pdfRequestFiles",
action: "redirect"
btnIcon: "fa fa-eye",
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
}
],
helpMsg:
"This is a list of documents that are waiting for your signature"
]
};
// In progess report
case "1MwEuxLEkF":
@@ -60,52 +42,14 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "8901",
hoverLabel: "Share",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa-solid fa-share",
redirectUrl: "",
action: "share"
},
{
btnId: "1588",
hoverLabel: "View",
btnLabel: "View",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles",
action: "redirect"
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "transparent",
textColor: "black",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "1688",
btnLabel: "Revoke",
hoverLabel: "Revoke",
btnIcon: "fa-solid fa-file-circle-xmark",
redirectUrl: "",
action: "revoke"
},
{
btnId: "1488",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
}
],
helpMsg:
"This is a list of documents you've sent to other parties for signature."
]
};
// completed documents report
case "kQUoW4hUXz":
@@ -114,26 +58,14 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "1378",
hoverLabel: "Edit",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1278",
hoverLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
}
],
helpMsg:
"This is a list of documents that have been signed by all parties."
]
};
// declined documents report
case "UPr2Fm5WY3":
@@ -142,26 +74,14 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "1458",
hoverLabel: "View",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1358",
hoverLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
}
],
helpMsg:
"This is a list of documents that have been declined by one of the signers."
]
};
// Expired Documents report
case "zNqBHXHsYH":
@@ -170,76 +90,28 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "1898",
hoverLabel: "View",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1998",
hoverLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
}
],
helpMsg:
"This is a list of documents that have reached their expiration date."
]
};
// Recently sent for signatures report show on dashboard
case "d9k3UfYHBc":
return {
reportName: "Recently sent for signatures",
heading: dashboardReportHead,
heading: head,
actions: [
{
btnId: "1999",
hoverLabel: "Share",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa-solid fa-share",
redirectUrl: "",
action: "share"
},
{
btnId: "1999",
hoverLabel: "View",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles",
action: "redirect"
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "transparent",
textColor: "black",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "6788",
btnLabel: "Revoke",
hoverLabel: "Revoke",
btnIcon: "fa-solid fa-file-circle-xmark",
redirectUrl: "",
action: "revoke"
},
{
btnId: "2000",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
}
]
};
@@ -247,17 +119,15 @@ export default function reportJson(id) {
case "5Go51Q7T8r":
return {
reportName: "Recent signature requests",
heading: dashboardReportHead,
heading: head,
actions: [
{
btnId: "2001",
btnLabel: "sign",
hoverLabel: "Sign",
btnColor: "#3ac9d6",
btnLabel: "Sign",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa-solid fa-signature",
redirectUrl: "pdfRequestFiles",
action: "redirect"
btnIcon: "fa fa-eye",
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
}
]
};
@@ -265,25 +135,15 @@ export default function reportJson(id) {
case "kC5mfynCi4":
return {
reportName: "Drafts",
heading: ["Title", "Note", "Folder", "File", "Owner", "Signers"],
heading: head,
actions: [
{
btnId: "2003",
hoverLabel: "Edit",
btnLabel: "sign",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa-solid fa-pen",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "2004",
hoverLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
btnIcon: "fa fa-plus",
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
}
]
};
@@ -294,69 +154,12 @@ export default function reportJson(id) {
heading: contactbook,
actions: [
{
btnId: "2204",
hoverLabel: "Delete",
btnLabel: "",
btnColor: "#f55a42",
textColor: "white",
btnIcon: "fa-solid fa-trash",
action: "delete"
btnIcon: "fa-solid fa-trash"
}
],
form: "ContactBook",
helpMsg:
"This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document."
};
// template report
case "6TeaPr321t":
return {
reportName: "Templates",
heading: head,
actions: [
{
btnId: "2234",
btnLabel: "Use",
hoverLabel: "Use",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl: "placeHolderSign",
action: "redirect",
selector: "reactourSecond",
message:
"Click the 'Use' button to create a new document from an existing template. "
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "transparent",
textColor: "black",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
selector: "reactourThird",
message:
"This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but wont affect existing documents.Use the Delete button you can delete template. ",
subaction: [
{
btnId: "2434",
btnLabel: "Edit",
hoverLabel: "Edit",
btnIcon: "fa-solid fa-pen",
redirectUrl: "template",
action: "redirect"
},
{
btnId: "1834",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
}
],
helpMsg:
"This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step."
]
};
default:
return null;
-78
View File
@@ -1,78 +0,0 @@
const plans = [
{
planName: "OPENSIGN™ FREE",
currency: "",
monthlyPrice: "Free",
yearlyPrice: "Free",
subtitle: "Free Unlimited E-signatures, Forever.",
btnText: "Subscribe",
url: "",
target: "_blank",
benefits: [
"Unlimited envelopes",
"Sign documents yourself",
"Request signatures from others",
"14 field types",
"Automatic e-signatures",
"Completion certificates",
"Send in order",
"Document templates",
"Import from Dropbox",
"Contact book",
"Document expiry support",
"Decline document support",
"Email notifications",
"Recipient authentication using OTP",
"And much more"
]
},
{
planName: "OPENSIGN™ PROFESSIONAL",
currency: "$",
monthlyPrice: "29.99",
yearlyPrice: "329.99",
subtitle: "Exclusive Access to advanced features.",
btnText: "Subscribe",
url:
window.location.origin === "https://staging-app.opensignlabs.com"
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
yearlyUrl:
window.location.origin === "https://staging-app.opensignlabs.com"
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
target: "_self",
benefits: [
"Everything in OpenSign™ free",
"Field validations",
"Regular expression validations",
"Organize docs in OpenSign™ Drive",
"Webhooks",
"Zapier integration",
"API Access",
"100 API signatures included",
"DocumentId removal from signed docs",
"Custom email templates"
]
},
{
planName: "OPENSIGN™ ENTERPRISE",
currency: "",
monthlyPrice: "Request Price",
yearlyPrice: "Request Price",
subtitle: "Scalable Features with priority support.",
btnText: "Contact us",
url: "https://www.opensignlabs.com/contact-us",
target: "_blank",
benefits: [
"All features",
"Custom domain",
"Custom branding",
"Uptime SLA",
"SSO support",
"Priority support"
]
}
];
export default plans;
+65
View File
@@ -0,0 +1,65 @@
[
{
"planName": "SELF HOSTED",
"currency": "",
"price": "Free",
"subtitle": "Free for life One click install.",
"btnText": "Visit github",
"url": "https://github.com/opensignlabs/opensign",
"target": "_blank",
"benefits": [
"Sign Unlimited Documents",
"S3 Doc Storage",
"Unlimited Guest Signers",
"Unlimited completion certificates",
"Unique Code(OTP) verification for guest signers",
"Expiring docs & reject signature support",
"PDF Template Creation(coming soon)",
"Audit Trails",
"API support",
"Add-ons support(coming soon)",
"Self hosted cloud infrastructure",
"Free for lifetime",
"Community support"
]
},
{
"planName": "THANKS PLAN",
"currency": "$",
"price": "29.99",
"subtitle": "Use code 'FREEBETA' to get this for free(First 1000 users)",
"btnText": "Subscribe",
"url": "https://subscriptions.zoho.in/subscribe/ef798486e6a0a11ea65f2bae8f2af901dbadf3175d495584fd25b9af5d2f2b9e/thanks",
"target": "_self",
"benefits": [
"Sign Unlimited Documents",
"Unlimited Secure Doc Storage with OpenSignDrive",
"Unlimited Guest Signers",
"Unlimited completion certificates",
"Unique Code(OTP) verification for guest signers",
"Expiring docs & reject signature support",
"PDF Template Creation(coming soon)",
"Audit Trails",
"API support",
"Add-ons support(coming soon)",
"Self hosted cloud infrastructure"
]
},
{
"planName": "ENTERPRISE",
"currency": "",
"price": "Request Price",
"subtitle": "Customization available Priority support.",
"btnText": "Contact us",
"url": "https://www.opensignlabs.com/contact-us",
"target": "_blank",
"benefits": [
"All features",
"Custom domain",
"Whitelabeling",
"Uptime SLA",
"SSO support",
"24/7 support"
]
}
]
+29 -160
View File
@@ -7,96 +7,19 @@ import Tour from "reactour";
import axios from "axios";
import { useSelector } from "react-redux";
import Parse from "parse";
import ModalUi from "../primitives/ModalUi";
import { useNavigate, useLocation, Outlet } from "react-router-dom";
import { isEnableSubscription } from "../constant/const";
import { useCookies } from "react-cookie";
import { fetchSubscription } from "../constant/Utils";
const HomeLayout = () => {
const navigate = useNavigate();
const location = useLocation();
const HomeLayout = ({ children }) => {
const { width } = useWindowSize();
const [isOpen, setIsOpen] = useState(true);
const arr = useSelector((state) => state.TourSteps);
const [isUserValid, setIsUserValid] = useState(true);
const [isLoader, setIsLoader] = useState(true);
// reactour state
const [isCloseBtn, setIsCloseBtn] = useState(true);
const [isTour, setIsTour] = useState(false);
const [tourStatusArr, setTourStatusArr] = useState([]);
const [tourConfigs, setTourConfigs] = useState([]);
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
const tenantId = localStorage.getItem("TenantId");
useEffect(() => {
if (!tenantId) {
setIsUserValid(false);
} else {
(async () => {
try {
// Use the session token to validate the user
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(Parse.User.current().id, {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
checkIsSubscribed();
} else {
setIsUserValid(false);
}
} catch (error) {
// Session token is invalid or there was an error
setIsUserValid(false);
}
})();
saveCookies();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tenantId]);
//function to use save data in cookies storage
const saveCookies = () => {
const main_Domain = window.location.origin;
const domainName = window.location.hostname; //app.opensignlabs.com
// Find the index of the first dot in the string
const indexOfFirstDot = domainName.indexOf(".");
// Remove the first dot and get the substring starting from the next character
const updateDomain = domainName.substring(indexOfFirstDot); //.opensignlabs.com
setCookie("accesstoken", localStorage.getItem("accesstoken"), {
secure: true,
domain: updateDomain
});
setCookie("main_Domain", main_Domain, {
secure: true,
domain: updateDomain
});
};
async function checkIsSubscribed() {
if (isEnableSubscription) {
const res = await fetchSubscription();
if (res.plan === "freeplan") {
setIsUserValid(true);
setIsLoader(false);
} else if (res.billingDate) {
if (new Date(res.billingDate) > new Date()) {
setIsUserValid(true);
setIsLoader(false);
} else {
navigate(`/subscription`);
}
} else {
navigate(`/subscription`);
}
} else {
setIsUserValid(true);
setIsLoader(false);
}
}
const showSidebar = () => {
setIsOpen((value) => !value);
setIsOpen(value => !value);
};
useEffect(() => {
if (width && width <= 768) {
@@ -105,7 +28,9 @@ const HomeLayout = () => {
}, [width]);
useEffect(() => {
if (
if (localStorage.getItem("domain") === "sign" && arr && arr.length > 0) {
handleDynamicSteps();
} else if (
localStorage.getItem("domain") === "contracts" &&
arr &&
arr.length > 0
@@ -144,16 +69,10 @@ const HomeLayout = () => {
position: "top"
// style: { backgroundColor: "#abd4d2" },
},
{
selector: '[data-tut="tourbutton"]',
content: `To upload documents for self-signing or to request others signatures, simply select the respective buttons.`,
position: "top"
// style: { backgroundColor: "#abd4d2" },
},
...resArr,
{
selector: '[data-tut="reactourLast"]',
content: `You are ready to start using OpenSign! If you need support feel free to contact us.`,
content: `You are good to go`,
position: "top"
// style: { backgroundColor: "#abd4d2" },
}
@@ -166,7 +85,7 @@ const HomeLayout = () => {
// console.log("closeTour");
setIsTour(false);
const serverUrl = localStorage.getItem("baseUrl");
const appId = localStorage.getItem("parseAppId");
const appId = localStorage.getItem("AppID12");
const extUserClass = localStorage.getItem("extended_class");
const json = JSON.parse(localStorage.getItem("Extand_Class"));
const extUserId = json && json.length > 0 && json[0].objectId;
@@ -234,82 +153,32 @@ const HomeLayout = () => {
setIsOpen(false);
}
};
const handleLoginBtn = () => {
try {
Parse.User.logOut();
} catch (err) {
console.log("err ", err);
} finally {
localStorage.removeItem("accesstoken");
navigate("/", { replace: true, state: { from: location } });
}
};
return (
<div>
<div className="sticky top-0 z-50">
{!isLoader && <Header showSidebar={showSidebar} />}
<Header showSidebar={showSidebar} />
</div>
{isUserValid ? (
<>
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<>
<div className="flex md:flex-row flex-col z-50">
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
<div
id="renderList"
className="relative h-screen flex flex-col justify-between w-full overflow-y-auto"
>
<div className="bg-[#eef1f5] p-3">{<Outlet />}</div>
<div className="z-30">
<Footer />
</div>
</div>
</div>
<Tour
onRequestClose={closeTour}
steps={tourConfigs}
isOpen={isTour}
closeWithMask={false}
disableKeyboardNavigation={["esc"]}
// disableInteraction={true}
scrollOffset={-100}
rounded={5}
showCloseButton={isCloseBtn}
/>
</>
)}
</>
) : (
<ModalUi title={"Session Expired"} isOpen={true} showClose={false}>
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
<p className="text-xl font-normal">Your session has expired.</p>
<button
onClick={handleLoginBtn}
className="text-base px-3 py-1.5 rounded shadow-md text-white bg-[#1ab6ce]"
>
Login
</button>
<div className="flex md:flex-row flex-col z-50">
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
<div className="relative h-screen flex flex-col justify-between w-full overflow-y-auto">
<div className="bg-[#eef1f5] p-3">{children}</div>
<div className="z-30">
<Footer />
</div>
</ModalUi>
)}
</div>
</div>
<Tour
onRequestClose={closeTour}
steps={tourConfigs}
isOpen={isTour}
closeWithMask={false}
disableKeyboardNavigation={["esc"]}
// disableInteraction={true}
scrollOffset={-100}
rounded={5}
showCloseButton={isCloseBtn}
/>
</div>
);
};
@@ -0,0 +1,58 @@
import React from "react";
import stamp from "./stamp2.png";
import sign from "./sign3.png";
import { themeColor } from "./IconColor.js";
function DragElement({ id }) {
const selectedId = id;
return (
<div>
{selectedId === 3 ? (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
userSelect: "none"
}}
>
<img
alt="sign img"
style={{
width: "25px",
height: "23px",
background: themeColor()
}}
src={sign}
/>
<span style={{ color: themeColor(), fontSize: "12px" }}>
Signature
</span>
</div>
) : (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
userSelect: "none"
}}
>
<img
alt="stamp img"
style={{
width: "25px",
height: "23px",
background: themeColor()
}}
src={stamp}
/>
<span style={{ color: themeColor(), fontSize: "12px" }}>Stamp</span>
</div>
)}
</div>
);
}
export default DragElement;
@@ -0,0 +1,3 @@
export const themeColor = () => {
return "#47a3ad";
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

-429
View File
@@ -1,429 +0,0 @@
import React, { useEffect, useState } from "react";
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
import "../styles/signature.css";
import "../styles/AddUser.css";
import Title from "../components/Title";
import RenderDebugPdf from "../components/RenderDebugPdf";
import { pdfjs } from "react-pdf";
import ModalUi from "../primitives/ModalUi";
import Alert from "../primitives/Alert";
import HandleError from "../primitives/HandleError";
import { useWindowSize } from "../hook/useWindowSize";
function processDimensions(x, y, width, height) {
if (width < 0) {
x -= Math.abs(width);
width = Math.abs(width);
}
if (height < 0) {
y -= Math.abs(height);
height = Math.abs(height);
}
return {
x: Math.floor(x),
y: Math.floor(y),
width: Math.floor(width),
height: Math.floor(height)
};
}
const DebugPdf = () => {
const { width } = useWindowSize();
const [pdf, setPdf] = useState("");
const [isModal, setIsModal] = useState(true);
const [allPages, setAllPages] = useState(null);
const [pageNumber, setPageNumber] = useState(1);
const [pdfLoadFail, setPdfLoadFail] = useState({
status: false,
type: "load"
});
const [pdfDetails, setPdfDetails] = useState({
name: "",
pdftype: "",
totalPages: "",
currentPage: 1,
x: 0,
y: 0,
base64: ""
});
const [hoverCoordinates, setHoverCoordinates] = useState({ x: 0, y: 0 });
const [pdfDimension, setPdfDimension] = useState({ width: 0, height: 0 });
const [annotations, setAnnotations] = useState([]);
const [newAnnotation, setNewAnnotation] = useState([]);
const [copied, setCopied] = useState(false);
useEffect(() => {
if (pdf && pdf.name) {
const fetchPdfMetadata = async () => {
try {
const pdfDataURL = URL.createObjectURL(pdf); // Convert File to data URL
console.log("pdfDataURL ", pdfDataURL);
const pdfInfo = await pdfjs.getDocument({ url: pdfDataURL }).promise;
const pdfType = await inferPdfType(pdfInfo);
setPdfDetails((prevDetails) => ({
...prevDetails,
pdftype: pdfType
}));
} catch (error) {
console.error("Error fetching PDF metadata:", error);
}
};
fetchPdfMetadata();
}
}, [pdf]);
const inferPdfType = async (pdf) => {
try {
const firstPage = await pdf.getPage(1);
const scale = 1;
const { width, height } = firstPage.getViewport({ scale });
setPdfDimension({ width: width, height: height });
// Assuming a standard DPI of 72, you can adjust this value if needed
const dpi = 72;
const widthInInches = width / dpi;
const heightInInches = height / dpi;
const isA1 = widthInInches > 23.39 && heightInInches > 16.54;
const isA2 = widthInInches > 16.54 && heightInInches > 11.69;
const isA3 = widthInInches > 11.69 && heightInInches > 8.27;
const isA4 = widthInInches > 8.27 && heightInInches > 5.83;
const isLegal = widthInInches > 8.5 && heightInInches > 14;
const isLetter = widthInInches > 8.5 && heightInInches > 11;
const isLedger = widthInInches > 11 && heightInInches > 17;
if (isA1) return "A1";
if (isA2) return "A2";
if (isA3) return "A3";
if (isA4) return "A4";
if (isLegal) return "Legal";
if (isLetter) return "Letter";
if (isLedger) return "Ledger";
return "Unknown";
} catch (error) {
console.error("Error getting page dimensions:", error);
return "Unknown";
}
};
const handleSubmit = (e) => {
e.preventDefault();
setIsModal(false);
setPdfDetails((prevData) => ({ ...prevData, name: pdf?.name }));
};
//function for get pdf page details
const pageDetails = async ({ numPages }) => {
const load = {
status: true
};
setPdfDetails((prevDetails) => ({ ...prevDetails, totalPages: numPages }));
setPdfLoadFail(load);
};
const handleMouseMoveDiv = (event) => {
setHoverCoordinates({
x: event.nativeEvent.offsetX,
y: event.nativeEvent.offsetY
});
};
const handleMouseDown = (event) => {
if (newAnnotation.length === 0) {
const { x, y } = event.target.getStage().getPointerPosition();
setNewAnnotation([
{ x, y, width: 0, height: 0, key: "0", page: pageNumber }
]);
}
};
const handleMouseUp = (event) => {
if (newAnnotation.length === 1) {
const sx = newAnnotation[0].x;
const sy = newAnnotation[0].y;
const { x, y } = event.target.getStage().getPointerPosition();
const result = processDimensions(sx, sy, x - sx, y - sy);
const annotationToAdd = {
...result,
key: annotations.length + 1,
page: pageNumber
};
annotations.push(annotationToAdd);
setNewAnnotation([]);
setAnnotations(annotations);
setPdfDetails((prevDetails) => ({
...prevDetails,
x: result.x,
y: result.y
}));
}
};
const handleMouseMove = (event) => {
if (newAnnotation.length === 1) {
const sx = newAnnotation[0].x;
const sy = newAnnotation[0].y;
const { x, y } = event.target.getStage().getPointerPosition();
setNewAnnotation([
{
x: sx,
y: sy,
width: x - sx,
height: y - sy,
page: pageNumber,
key: "0"
}
]);
}
};
const annotationsToDraw = [...annotations, ...newAnnotation];
const handlePageLoadSuccess = (page) => {
setPdfDetails((prevDetails) => ({
...prevDetails,
currentPage: page.pageNumber
}));
};
const copytoclipboard = (text) => {
navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => {
setCopied(false);
}, 1500); // Reset copied state after 1.5 seconds
};
const handleFileChange = (files) => {
const file = files[0];
setPdf(file);
if (file && file.type === "application/pdf") {
const reader = new FileReader();
reader.onloadend = () => {
const base64String = reader.result.split(",")[1];
setPdfDetails((prevdata) => ({ ...prevdata, base64: base64String }));
};
reader.readAsDataURL(file);
}
};
const handleDelete = (key) => {
const updateAnnotations = annotations.filter((x) => x.key !== key);
setAnnotations(updateAnnotations);
};
return (
<div>
{copied && <Alert type="success">Copied</Alert>}
<Title title={"Debug Pdf"} />
{width < 800 ? (
<HandleError handleError={"Debug PDF only availble for PC"} />
) : (
<>
{!isModal && (
<div className="signatureContainer">
{/* this component used to render all pdf pages in left side */}
<RenderAllPdfPage
signPdfUrl={pdf}
allPages={allPages}
setAllPages={setAllPages}
setPageNumber={setPageNumber}
pageNumber={pageNumber}
/>
{/* pdf render view */}
<div>
<div data-tut="reactourThird">
<RenderDebugPdf
pdfUrl={pdf}
pageDetails={pageDetails}
pageNumber={pageNumber}
setPdfLoadFail={setPdfLoadFail}
pdfLoadFail={pdfLoadFail}
handlePageLoadSuccess={handlePageLoadSuccess}
handleMouseMove={handleMouseMove}
handleMouseUp={handleMouseUp}
handleMouseDown={handleMouseDown}
hoverCoordinates={hoverCoordinates}
annotations={annotationsToDraw}
pdfDimension={pdfDimension}
handleMouseMoveDiv={handleMouseMoveDiv}
/>
</div>
</div>
<div style={{ backgroundColor: "white", width: 220 }}>
<div
style={{
fontSize: 18,
fontWeight: 500,
padding: "10px 12px",
borderBottom: "1px solid grey"
}}
>
PDF details
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Name: {pdfDetails?.name}
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Pdf type: {pdfDetails?.pdftype}
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Total Pages: {pdfDetails?.totalPages}
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Current Page: {pdfDetails?.currentPage}
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Base64 : {pdfDetails?.base64.slice(0, 10)}...
<span
style={{
borderRadius: 4,
padding: "3px 5px",
border: "1px solid gray",
fontSize: 12,
margin: 2,
cursor: "pointer"
}}
onClick={() => copytoclipboard(pdfDetails?.base64)}
>
<i className="fa-solid fa-copy"></i>
</span>
</div>
<div
style={{
fontSize: 18,
fontWeight: 500,
padding: "10px 12px",
borderBottom: "1px solid grey"
}}
>
Last click
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
x co-ordinate: {pdfDetails?.x}
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
y co-ordinate: {pdfDetails?.y}
</div>
<div
style={{
fontSize: 18,
fontWeight: 500,
padding: "10px 12px",
borderBottom: "1px solid grey",
borderTop: "1px solid grey"
}}
>
Annotation
</div>
<ul
style={{
listStyle: "none",
padding: 10,
height: 500,
overflowY: "auto",
scrollbarWidth: 5
}}
>
{annotations.map((coord, index) => (
<li key={index}>
<span style={{ fontSize: 13, fontWeight: 500 }}>{`Box ${
index + 1
}:`}</span>
<code
style={{
fontSize: 12,
color: "black",
cursor: "pointer"
}}
>
{` ["page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}]`}
</code>
<div>
<span
style={{
borderRadius: 4,
padding: "3px 5px",
border: "1px solid gray",
fontSize: 12,
margin: 2,
cursor: "pointer"
}}
onClick={() =>
copytoclipboard(
`"page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}`
)
}
>
<i className="fa-solid fa-copy"></i>
</span>
<span
style={{
borderRadius: 4,
padding: "3px 5px",
border: "1px solid gray",
fontSize: 12,
margin: 2,
cursor: "pointer"
}}
onClick={() => handleDelete(coord.key)}
>
<i className="fa-solid fa-trash-can"></i>
</span>
</div>
</li>
))}
</ul>
</div>
</div>
)}
<ModalUi title={"Select PDF"} isOpen={isModal}>
<form onSubmit={handleSubmit} style={{ margin: "10px" }}>
<input
type="file"
onChange={(e) => handleFileChange(e.target.files)}
style={{
width: "100%",
border: "1px solid grey",
borderRadius: "4px",
padding: "5px",
fontSize: 12
}}
accept=".pdf"
required
/>
<div
style={{ borderTop: "1px solid grey", margin: "10px 0" }}
></div>
<button
style={{
background: "#32a3ac",
borderRadius: "2px",
boxShadow:
"0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18)",
border: "none",
textTransform: "uppercase",
fontSize: "13px",
fontWeight: "600",
padding: "0.375rem 0.75rem",
textAlign: "center",
color: "#ffffff",
outline: "none"
}}
type="submit"
>
Submit
</button>
</form>
</ModalUi>
</>
)}
</div>
);
};
export default DebugPdf;
-842
View File
@@ -1,842 +0,0 @@
import React, { useEffect, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { formJson } from "../json/FormJson";
import AddUser from "../components/AddUser";
import sanitizeFileName from "../primitives/sanitizeFileName";
import Parse from "parse";
import DropboxChooser from "../components/shared/fields/DropboxChoose";
import Alert from "../primitives/Alert";
import SelectFolder from "../components/shared/fields/SelectFolder";
import SignersInput from "../components/shared/fields/SignersInput";
import Title from "../components/Title";
import PageNotFound from "./PageNotFound";
import { SaveFileSize } from "../constant/saveFileSize";
import { getFileName, toDataUrl } from "../constant/Utils";
import { PDFDocument } from "pdf-lib";
import axios from "axios";
import { isEnableSubscription, submitBtn } from "../constant/const";
import ModalUi from "../primitives/ModalUi";
import { Tooltip } from "react-tooltip";
// `Form` render all type of Form on this basis of their provided in path
function Form() {
const { id } = useParams();
if (id === "lM0xRnM3iE") {
return <AddUser />;
} else {
const config = formJson[id];
if (config) {
return <Forms {...config} />;
} else {
return <PageNotFound prefix={"Form"} />;
}
}
}
const Forms = (props) => {
const maxFileSize = 20;
const abortController = new AbortController();
const inputFileRef = useRef(null);
const navigate = useNavigate();
const [signers, setSigners] = useState([]);
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
const [formData, setFormData] = useState({
Name: "",
Description: "",
Note: "",
TimeToCompleteDays: 15,
SendinOrder: "false",
password: "",
file: ""
});
const [fileupload, setFileUpload] = useState("");
const [fileload, setfileload] = useState(false);
const [percentage, setpercentage] = useState(0);
const [isReset, setIsReset] = useState(false);
const [isAlert, setIsAlert] = useState(false);
const [isSubmit, setIsSubmit] = useState(false);
const [isErr, setIsErr] = useState("");
const [isPassword, setIsPassword] = useState(false);
const [isDecrypting, setIsDecrypting] = useState(false);
const [isCorrectPass, setIsCorrectPass] = useState(true);
const handleStrInput = (e) => {
setIsCorrectPass(true);
setFormData({ ...formData, [e.target.name]: e.target.value });
};
useEffect(() => {
handleReset();
return () => abortController.abort();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.title]);
function getFileAsArrayBuffer(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
resolve(e.target.result);
};
reader.onerror = (e) => {
reject(e.target.error);
};
reader.readAsArrayBuffer(file);
});
}
const handleFileInput = async (e) => {
setpercentage(0);
try {
let files = e.target.files;
setFormData((prev) => ({ ...prev, file: e.target.files[0] }));
if (typeof files[0] !== "undefined") {
const mb = Math.round(files[0].size / Math.pow(1024, 2));
if (mb > maxFileSize) {
alert(
`The selected file size is too large. Please select a file less than ${maxFileSize} MB`
);
setFileUpload("");
e.target.value = "";
return;
} else {
if (files?.[0]?.type === "application/pdf") {
try {
const res = await getFileAsArrayBuffer(files[0]);
await PDFDocument.load(res);
handleFileUpload(files[0]);
} catch (err) {
if (err?.message?.includes("is encrypted")) {
try {
await Parse.Cloud.run("encryptedpdf", {
email: Parse.User.current().getEmail()
});
} catch (err) {
console.log("err in sending posthog encryptedpdf", err);
}
// console.log("err ", err);
try {
setIsDecrypting(true);
const fileName = files?.[0].name;
const size = files?.[0].size;
const name = sanitizeFileName(fileName);
const url = "https://ai.nxglabs.in/decryptpdf"; //"https://ai.nxglabs.in/decryptpdf"; //
let formData = new FormData();
formData.append("file", files[0]);
formData.append("password", "");
const config = {
headers: {
"content-type": "multipart/form-data"
// sessiontoken: Parse.User.current().getSessionToken()
},
responseType: "blob"
};
const response = await axios.post(url, formData, config);
const pdfBlob = new Blob([response.data], {
type: "application/pdf"
});
const pdfFile = new File([pdfBlob], name, {
type: "application/pdf"
});
setIsDecrypting(false);
setfileload(true);
// Upload the file to Parse Server
const parseFile = new Parse.File(
name,
pdfFile,
"application/pdf"
);
await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round(
(loaded * 100) / total
);
setpercentage(percentCompleted);
}
}
});
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
setFileUpload(parseFile.url());
setfileload(false);
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, parseFile.url(), tenantId);
return parseFile.url();
}
} catch (err) {
setfileload(false);
setpercentage(0);
if (err?.response?.status === 401) {
setIsPassword(true);
} else {
console.log("Error uploading file: ", err?.response);
setIsDecrypting(false);
e.target.value = "";
}
}
} else {
console.log("err ", err);
setFileUpload("");
e.target.value = "";
}
}
} else {
const isImage = files?.[0]?.type.includes("image/");
if (isImage) {
const image = await toDataUrl(files[0]);
const pdfDoc = await PDFDocument.create();
let embedImg;
if (files?.[0]?.type === "image/png") {
embedImg = await pdfDoc.embedPng(image);
} else {
embedImg = await pdfDoc.embedJpg(image);
}
// Get image dimensions
const imageWidth = embedImg.width;
const imageHeight = embedImg.height;
const page = pdfDoc.addPage([imageWidth, imageHeight]);
page.drawImage(embedImg, {
x: 0,
y: 0,
width: imageWidth,
height: imageHeight
});
const getFile = await pdfDoc.save({
useObjectStreams: false
});
setfileload(true);
const fileName = files[0].name;
const size = files[0].size;
const name = sanitizeFileName(fileName);
const pdfName = `${name?.split(".")[0]}.pdf`;
const parseFile = new Parse.File(
pdfName,
[...getFile],
"application/pdf"
);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round(
(loaded * 100) / total
);
setpercentage(percentCompleted);
}
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, response.url(), tenantId);
return response.url();
}
} catch (error) {
e.target.value = "";
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
} else {
if (isEnableSubscription) {
try {
setfileload(true);
const url = "https://ai.nxglabs.in/docxtopdf";
let formData = new FormData();
formData.append("file", files[0]);
const config = {
headers: {
"content-type": "multipart/form-data",
sessiontoken: Parse.User.current().getSessionToken()
},
signal: abortController.signal
};
const res = await axios.post(url, formData, config);
if (res.data) {
setFileUpload(res.data.url);
setfileload(false);
}
} catch (err) {
e.target.value = "";
setfileload(false);
setpercentage(0);
console.log("err in libreconverter ", err);
}
}
}
}
}
} else {
alert("Please select file.");
return false;
}
} catch (error) {
alert(error.message);
return false;
}
};
const handleFileUpload = async (file) => {
setfileload(true);
const fileName = file.name;
const size = file.size;
const name = sanitizeFileName(fileName);
const pdfFile = file;
const parseFile = new Parse.File(name, pdfFile);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, response.url(), tenantId);
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
};
const dropboxSuccess = async (files) => {
setfileload(true);
const file = files[0];
const size = file.bytes;
const url = file.link;
const mb = Math.round(file.bytes / Math.pow(1024, 2));
if (mb > maxFileSize) {
setTimeout(() => {
alert(
`The selected file size is too large. Please select a file less than ${maxFileSize} MB`
);
}, 500);
return;
} else {
const name = sanitizeFileName(file.name);
const parseFile = new Parse.File(name, { uri: url });
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, response.url(), tenantId);
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
}
};
const dropboxCancel = async () => {};
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
if (fileupload) {
setIsSubmit(true);
try {
const currentUser = Parse.User.current();
const object = new Parse.Object(props.Cls);
object.set("Name", formData?.Name);
object.set("Description", formData?.Description);
object.set("Note", formData?.Note);
if (props.title === "Request Signatures") {
object.set(
"TimeToCompleteDays",
parseInt(formData?.TimeToCompleteDays)
);
}
if (props.title !== "Sign Yourself") {
const isChecked = formData.SendinOrder === "false" ? false : true;
object.set("SendinOrder", isChecked);
}
object.set("URL", fileupload);
object.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
if (folder && folder.ObjectId) {
object.set("Folder", {
__type: "Pointer",
className: props.Cls,
objectId: folder.ObjectId
});
}
if (signers && signers.length > 0) {
object.set("Signers", signers);
}
const ExtCls = JSON.parse(localStorage.getItem("Extand_Class"));
object.set("ExtUserPtr", {
__type: "Pointer",
className: "contracts_Users",
objectId: ExtCls[0].objectId
});
const res = await object.save();
if (res) {
setSigners([]);
setFolder({ ObjectId: "", Name: "" });
setFormData({
Name: "",
Description: "",
Note: ""
});
setFileUpload("");
setpercentage(0);
navigate(`/${props?.redirectRoute}/${res.id}`);
}
} catch (err) {
console.log("err ", err);
setIsErr(true);
} finally {
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
setIsSubmit(false);
}
} else {
alert("Please wait until the PDF has been uploading.");
}
};
const handleFolder = (data) => {
setFolder(data);
};
const handleSigners = (data) => {
if (data && data.length > 0) {
const updateSigners = data.map((x) => ({
__type: "Pointer",
className: "contracts_Contactbook",
objectId: x
}));
setSigners(updateSigners);
}
};
const handleReset = () => {
setIsReset(true);
setSigners([]);
setFolder({ ObjectId: "", Name: "" });
setFormData({
Name: "",
Description: "",
Note:
props.title === "Sign Yourself"
? "Note to myself"
: "Please review and sign this document",
TimeToCompleteDays: 15,
SendinOrder: "true"
});
setFileUpload("");
setpercentage(0);
setTimeout(() => setIsReset(false), 50);
};
const handlePasswordSubmit = async (e) => {
e.preventDefault();
setIsPassword(false);
setfileload(true);
try {
const fileName = formData?.file?.name;
const size = formData?.file?.size;
const name = sanitizeFileName(fileName);
const url = "https://ai.nxglabs.in/decryptpdf"; //
let Data = new FormData();
Data.append("file", formData?.file);
Data.append("password", formData.password);
const config = {
headers: {
"content-type": "multipart/form-data"
// sessiontoken: Parse.User.current().getSessionToken()
},
responseType: "blob"
};
const response = await axios.post(url, Data, config);
const pdfBlob = new Blob([response.data], {
type: "application/pdf"
});
const pdfFile = new File([pdfBlob], name, {
type: "application/pdf"
});
setIsDecrypting(false);
// Upload the file to Parse Server
const parseFile = new Parse.File(name, pdfFile, "application/pdf");
await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
setFormData((prev) => ({ ...prev, password: "" }));
setFileUpload(parseFile.url());
setfileload(false);
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, parseFile.url(), tenantId);
return parseFile.url();
}
} catch (err) {
setfileload(false);
setpercentage(0);
if (err?.response?.status === 401) {
setIsPassword(true);
setIsCorrectPass(false);
} else {
console.log("Error uploading file: ", err?.response);
setFormData((prev) => ({ ...prev, password: "" }));
setIsDecrypting(false);
if (inputFileRef.current) {
inputFileRef.current.value = ""; // Set file input value to empty string
}
}
}
};
const handeCloseModal = () => {
setIsPassword(false);
setFormData((prev) => ({ ...prev, file: "", password: "" }));
setIsDecrypting(false);
setfileload(false);
if (inputFileRef.current) {
inputFileRef.current.value = ""; // Set file input value to empty string
}
};
return (
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
<Title title={props?.title} />
{isAlert && (
<Alert type={isErr ? "danger" : "success"}>
{isErr
? "Something went wrong please try again!"
: `${props.msgVar} created successfully!`}
</Alert>
)}
{isSubmit ? (
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<>
<ModalUi
isOpen={isPassword}
handleClose={() => handeCloseModal()}
title={"Enter Pdf Password"}
>
<form onSubmit={handlePasswordSubmit}>
<div className="px-6 pt-3 pb-2">
<label className="mb-2 text-xs">Password</label>
<input
type="text"
name="password"
value={formData.password}
onChange={(e) => handleStrInput(e)}
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
placeholder="Enter pdf password"
required
/>
{!isCorrectPass ? (
<p className="ml-2 text-[11px] text-red-600">
Please provide correct password
</p>
) : (
<p className="ml-2 text-[11px] text-transparent text-red-600">
.
</p>
)}
</div>
<hr />
<div className="px-6 my-3">
<button type="submit" className={submitBtn}>
Submit
</button>
</div>
</form>
</ModalUi>
<form onSubmit={handleSubmit}>
<h1 className="text-[20px] font-semibold mb-4">{props?.title}</h1>
{fileload && (
<div className="flex items-center gap-x-2">
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
<div
className="h-2 rounded-full bg-blue-500"
style={{ width: `${percentage}%` }}
></div>
</div>
<span className="text-black text-sm">{percentage}%</span>
</div>
)}
{isDecrypting && (
<div className="flex items-center gap-x-2">
<span className="text-black text-sm">
Decrypting pdf please wait...
</span>
</div>
)}
<div className="text-xs">
<label className="block">
{`File (pdf, png, jpg, jpeg${
isEnableSubscription ? ", docx)" : ")"
}`}
<span className="text-red-500 text-[13px]">*</span>
</label>
{fileupload.length > 0 ? (
<div className="flex gap-2 justify-center items-center">
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
<div className="break-all">
file selected : {getFileName(fileupload)}
</div>
<div
onClick={() => setFileUpload("")}
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
>
<i className="fa-solid fa-xmark"></i>
</div>
</div>
{process.env.REACT_APP_DROPBOX_API_KEY && (
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
)}
</div>
) : (
<div className="flex gap-2 justify-center items-center">
<input
type="file"
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
onChange={(e) => handleFileInput(e)}
ref={inputFileRef}
accept={
isEnableSubscription
? "application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg"
: "application/pdf,image/png,image/jpeg"
}
required
/>
{process.env.REACT_APP_DROPBOX_API_KEY && (
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
)}
</div>
)}
</div>
<div className="text-xs mt-2">
<label className="block">
{props.title === "New Template"
? "Template Title"
: "Document Title"}
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
name="Name"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Name}
onChange={(e) => handleStrInput(e)}
required
/>
</div>
<div className="text-xs mt-2">
<label className="block">Description</label>
<input
name="Description"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Description}
onChange={(e) => handleStrInput(e)}
/>
</div>
{props.signers && (
<SignersInput
onChange={handleSigners}
isReset={isReset}
required
/>
)}
<div className="text-xs mt-2">
<label className="block">
Note<span className="text-red-500 text-[13px]">*</span>
</label>
<input
name="Note"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Note}
onChange={(e) => handleStrInput(e)}
required
/>
</div>
{props.title !== "New Template" && (
<SelectFolder
onSuccess={handleFolder}
folderCls={props.Cls}
isReset={isReset}
/>
)}
{props.title === "Request Signatures" && (
<div className="text-xs mt-2">
<label className="block">
Time To Complete (Days)
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
type="number"
name="TimeToCompleteDays"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.TimeToCompleteDays}
onChange={(e) => handleStrInput(e)}
required
/>
</div>
)}
{props.title !== "Sign Yourself" && (
<div className="text-xs mt-2">
<label className="block">
Send In Order
<a data-tooltip-id="sendInOrder-tooltip" className="ml-1">
<sup>
<i
className="fa-solid fa-question rounded-full"
style={{
borderColor: "#33bbff",
color: "#33bbff",
fontSize: 13,
borderWidth: 1.5,
padding: "1.5px 4px"
}}
></i>
</sup>
</a>
<Tooltip id="sendInOrder-tooltip" className="z-50">
<div className="max-w-[200px] md:max-w-[450px]">
<p className="font-bold">Send in Order</p>
<p>
Choose how you want the signing requests to be sent to
the document signers:
</p>
<p className="p-[5px]">
<ol className="list-disc">
<li>
<span className="font-bold">Yes:</span>
<span>
{" "}
Selecting this option will send the signing
request to the first signer initially. Once the
first signer completes their part, the next signer
in the sequence will receive the request. This
process continues until all signers have signed
the document. This method ensures that the
document is signed in a specific order.
</span>
</li>
<li>
<span className="font-bold">No: </span>
<span>
Selecting this option will send the signing links
to all signers simultaneously. Every signer can
sign the document at their convenience, regardless
of whether other signers have completed their
signatures. This method is faster but does not
enforce any signing order among the participants.
</span>
</li>
</ol>
</p>
<p>
Select the option that best suits the needs of your
document processing.
</p>
</div>
</Tooltip>
</label>
<div className="flex items-center gap-2 ml-2 mb-1">
<input
type="radio"
value={"true"}
name="SendinOrder"
checked={formData.SendinOrder === "true"}
className=""
onChange={handleStrInput}
/>
<div className="text-center">Yes</div>
</div>
<div className="flex items-center gap-2 ml-2 mb-1">
<input
type="radio"
value={"false"}
name="SendinOrder"
checked={formData.SendinOrder === "false"}
onChange={handleStrInput}
/>
<div className="text-center">No</div>
</div>
</div>
)}
<div className="flex items-center mt-3 gap-2 text-white">
<button
className={`${
isSubmit && "cursor-progress"
} bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none`}
type="submit"
disabled={isSubmit}
>
Next
</button>
<div
className="cursor-pointer bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
onClick={() => handleReset()}
>
Reset
</div>
</div>
</form>
</>
)}
</div>
);
};
export default Form;
-243
View File
@@ -1,243 +0,0 @@
import React, { useEffect, useState } from "react";
import Title from "../components/Title";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import Alert from "../primitives/Alert";
import ModalUi from "../primitives/ModalUi";
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
import { checkIsSubscribed, copytoData, openInNewTab } from "../constant/Utils";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
function GenerateToken() {
const navigation = useNavigate();
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [apiToken, SetApiToken] = useState("");
const [isLoader, setIsLoader] = useState(true);
const [copied, setCopied] = useState(false);
const [isGenerate, setIsGenerate] = useState(false);
const [isErr, setIsErr] = useState(false);
const [isModal, setIsModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
useEffect(() => {
fetchToken();
// eslint-disable-next-line
}, []);
const fetchToken = async () => {
try {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
}
const url = parseBaseUrl + "functions/getapitoken";
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
};
const res = await axios.post(url, {}, { headers: headers });
if (res) {
SetApiToken(res.data.result.result);
}
setIsLoader(false);
} catch (err) {
SetApiToken();
setIsLoader(false);
console.log("Err", err);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoader(true);
setIsModal(false);
try {
const url = parseBaseUrl + "functions/generateapitoken";
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
};
await axios.post(url, {}, { headers: headers }).then((res) => {
if (res) {
SetApiToken(res.data.result.token);
// localStorage.setItem("apiToken", res.data.result.token);
setIsGenerate(true);
setTimeout(() => {
setIsGenerate(false);
}, 1500);
setIsLoader(false);
} else {
console.error("Error while generating Token");
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
}
});
} catch (error) {
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
console.log("err", error);
}
};
const copytoclipboard = (text) => {
copytoData(text);
setCopied(true);
setTimeout(() => {
setCopied(false);
}, 1500); // Reset copied state after 1.5 seconds
};
const handleModal = () => setIsModal(!isModal);
return (
<React.Fragment>
<Title title={"API Token"} />
{isGenerate && (
<Alert type="success">Token generated successfully!</Alert>
)}
{copied && <Alert type="success">Copied</Alert>}
{isErr && <Alert type="danger">Something went wrong!</Alert>}
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<div className="bg-white flex flex-col justify-center shadow rounded">
{!isEnableSubscription && <PremiumAlertHeader />}
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign API{" "}
<Tooltip
url={
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
}
isSubscribe={true}
/>
</h1>
<ul
className={
isSubscribe || !isEnableSubscription
? "w-full flex flex-col p-2 text-sm "
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
}
>
<li
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<div className="w-[70%] flex-col md:flex-row flex items-center gap-5 ">
<span className="">Api Token:</span>{" "}
<span
id="token"
className=" md:text-end cursor-pointer"
onClick={() => copytoclipboard(apiToken)}
>
{apiToken ? apiToken : "_____"}
</span>
</div>
<button
type="button"
onClick={apiToken ? handleModal : handleSubmit}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
>
{apiToken ? "Regenerate Token" : "Generate Token"}
</button>
</li>
</ul>
<div className="flex items-center justify-center ">
<button
type="button"
onClick={() =>
openInNewTab(
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
)
}
className="rounded hover:bg-[#15b4e9] border-[1px] my-2 border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
>
View Docs
</button>
</div>
{!isSubscribe && isEnableSubscription && (
<>
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
Upgrade to PRO Plan
</h1>
<ul className={"w-full flex flex-col p-2 text-sm "}>
<li
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<div className="w-[70%] flex-col md:flex-row flex items-center gap-3 ">
<span className="">$29.99/month:</span>{" "}
<span id="token" className=" md:text-end cursor-pointer">
Sign 100 docs using API. Only 0.15/docs after that.
</span>
</div>
{!isSubscribe && isEnableSubscription && (
<button
type="button"
onClick={() => navigation("/subscription")}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
>
Upgrade Now
</button>
)}
</li>
</ul>
</>
)}
<ModalUi
isOpen={isModal}
title={"Regenerate Token"}
handleClose={handleModal}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to regenerate token it will expire old
token?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={handleSubmit}
className={submitBtn + "ml-[2px]"}
>
Yes
</button>
<button onClick={handleModal} className={rejectBtn}>
No
</button>
</div>
</div>
</ModalUi>
</div>
)}
</React.Fragment>
);
}
export default GenerateToken;
-956
View File
@@ -1,956 +0,0 @@
import React, { useEffect, useState, useRef } from "react";
import "../styles/opensigndrive.css";
import loader from "../assets/images/loader2.gif";
import { themeColor, iconColor } from "../constant/const";
import { getDrive } from "../constant/Utils";
import { useNavigate } from "react-router-dom";
import Title from "../components/Title";
import Parse from "parse";
import ModalUi from "../primitives/ModalUi";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
import Tour from "reactour";
import axios from "axios";
const DriveBody = React.lazy(
() => import("../components/opensigndrive/DriveBody")
);
const Loader = () => {
return (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
);
};
function Opensigndrive() {
const navigate = useNavigate();
const scrollRef = useRef(null);
const [isList, setIsList] = useState(false);
const [selectedSort, setSelectedSort] = useState("Date");
const [sortingOrder, setSortingOrder] = useState("Descending");
const [pdfData, setPdfData] = useState([]);
const [isFolder, setIsFolder] = useState(false);
const [newFolderName, setNewFolderName] = useState();
const [error, setError] = useState();
const [folderLoader, setIsFolderLoader] = useState(false);
const [isShowSort, setIsShowSort] = useState(false);
const [isTour, setIsTour] = useState(false);
const [tourStatusArr, setTourStatusArr] = useState([]);
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: "This might take some time"
});
const [docId, setDocId] = useState();
const [handleError, setHandleError] = useState("");
const [folderName, setFolderName] = useState([]);
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
const [isNewFol, setIsNewFol] = useState(false);
const [skip, setSkip] = useState(0);
const limit = 100;
const [loading, setLoading] = useState(false);
const sortOrder = ["Ascending", "Descending"];
const sortingValue = ["Name", "Date"];
const [isDontShow, setIsDontShow] = useState(false);
const [tourData, setTourData] = useState();
const [showTourFirstTIme, setShowTourFirstTime] = useState(true);
const orderName = {
Ascending: "Ascending",
Descending: "Descending",
Name: "Name",
Date: "Date"
};
const currentUser =
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
) &&
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
const jsonCurrentUser = JSON.parse(currentUser);
useEffect(() => {
getPdfDocumentList();
// eslint-disable-next-line
}, [docId]);
const tourConfigs = [
{
selector: '[data-tut="reactourFirst"]',
content: () => (
<TourContentWithBtn
message={`Click on the breadcrumb links to easily navigate through the folder hierarchy and view the documents within each folder.`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourSecond"]',
content: () => (
<TourContentWithBtn
message={`Click the add button to create a new folder or document.`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourThird"]',
content: () => (
<TourContentWithBtn
message={`Sort your documents by Date or Name using this menu.`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourForth"]',
content: () => (
<TourContentWithBtn
message={`Click on this menu to display the documents in list view.`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
}
];
//function for get all pdf document list
const getPdfDocumentList = async (disbaleLoading) => {
setLoading(true);
if (showTourFirstTIme) {
checkTourStatus();
}
if (!disbaleLoading) {
setIsLoading({ isLoad: true, message: "This might take some time" });
}
try {
const driveDetails = await getDrive(docId, skip, limit);
if (driveDetails && driveDetails === "Error: Something went wrong!") {
setHandleError("Error: Something went wrong!");
} else if (driveDetails && driveDetails.length > 0) {
const addMoreTour = [
{
selector: '[data-tut="reactourFifth"]',
content: () => (
<TourContentWithBtn
message={`The document list is displayed according to the selected sorting option. Icons next to each document indicate its current status.`}
isChecked={handleDontShow}
/>
),
position: "bottom",
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourSixth"]',
content: () => (
<TourContentWithBtn
message={`Right-click on a document to see options such as Download, Rename, Move, and Delete. Click on the document to open it.`}
isChecked={handleDontShow}
/>
),
position: "bottom",
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourSeventh"]',
content: () => (
<TourContentWithBtn
message={`Right-click on any folder to see options. Choose Rename to change the folders name or click on the folder to navigate through its contents.`}
isChecked={handleDontShow}
/>
),
position: "bottom",
style: { fontSize: "13px" }
}
];
let newTour = [...tourConfigs, ...addMoreTour];
const isFolderExist = driveDetails.some(
(data) => data.Type === "Folder"
);
if (!isFolderExist) {
newTour = newTour.filter(
(data) => data.selector !== '[data-tut="reactourSeventh"]'
);
}
const isDocumentExist = driveDetails.some((data) => data.URL);
if (!isDocumentExist) {
newTour = newTour.filter(
(data) => data.selector !== '[data-tut="reactourSixth"]'
);
}
setTourData(newTour);
setSkip((prevSkip) => prevSkip + limit);
sortingData(null, null, driveDetails, true);
} else {
setTourData(tourConfigs);
}
if (!docId) {
setFolderName([{ name: "OpenSign™ Drive", objectId: "" }]);
}
} catch (e) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
} finally {
setLoading(false);
setIsLoading({
isLoad: false
});
}
};
//function to fetch drive details list on scroll bottom
const handleScroll = () => {
//get document of render openSign-drive component using id
const documentList = document.getElementById("renderList");
//documentList.clientHeight property returns the height of an element's content area, including padding but not including borders, margins, or scrollbars.
//documentList.scrollHeight property returns the entire height of an element,including the parts that are not visible due to overflow..
// documentList.scrollTop property show height of element, how much the content has been scrolled from the top.
// When the sum of scrollTop and clientHeight is equal to scrollHeight, it means that the user has scrolled to the bottom of the div.
if (
documentList &&
documentList.scrollTop + documentList.clientHeight >=
documentList.scrollHeight
) {
//disableLoading is used disable initial loader
const disableLoading = true;
// If the fetched data length is less than the limit, it means there's no more data to fetch
if (!loading && pdfData.length % 100 === 0) {
getPdfDocumentList(disableLoading);
}
}
};
//useEffect is used to call handleScroll function on scrolling event
useEffect(() => {
const documentList = document.getElementById("renderList");
if (documentList) {
documentList.addEventListener("scroll", handleScroll);
return () => {
documentList.removeEventListener("scroll", handleScroll);
};
}
// eslint-disable-next-line
}, [loading, sortingOrder, selectedSort]); // Add/remove scroll event listener when loading changes
//function for handle folder name path
const handleRoute = (index, folderData) => {
setSkip(0);
// after onclick on route filter route from that index
const updateFolderName = folderName.filter((x, i) => {
if (i <= index) {
return x;
}
});
setFolderName(updateFolderName);
//get route details after onclick path of folder name
const getCurrentId = folderData[index];
const getLastId = updateFolderName[updateFolderName.length - 1];
//below condition is used to check if user click on same route which already open then don't change any thing
if (docId !== getCurrentId.objectId) {
setPdfData([]);
setDocId(getLastId.objectId);
}
};
//function for add new folder name
const handleFolderName = (e) => {
setError();
const value = e.target.value;
setNewFolderName(value);
};
//function for create folder
const handleAddFolder = async () => {
if (newFolderName) {
setIsFolderLoader(true);
const getParentObjId = folderName[folderName.length - 1];
const parentId = getParentObjId && getParentObjId.objectId;
const foldercls = `${localStorage.getItem("_appName")}_Document`;
const folderPtr = {
__type: "Pointer",
className: foldercls,
objectId: parentId
};
const CreatedBy = {
__type: "Pointer",
className: "_User",
objectId: jsonCurrentUser.objectId
};
try {
const exsitQuery = new Parse.Query(foldercls);
exsitQuery.equalTo("Name", newFolderName);
exsitQuery.equalTo("Type", "Folder");
if (parentId) {
exsitQuery.equalTo("Folder", folderPtr);
}
const templExist = await exsitQuery.first();
if (templExist) {
setError("Folder already exist!");
setIsFolderLoader(false);
} else {
const template = new Parse.Object(foldercls);
template.set("Name", newFolderName);
template.set("Type", "Folder");
if (parentId) {
template.set("Folder", folderPtr);
}
template.set("CreatedBy", CreatedBy);
const res = await template.save();
if (res) {
const result = JSON.parse(JSON.stringify(res));
setPdfData((prev) => [...prev, result]);
sortingData(null, null, [result], true);
setNewFolderName();
setIsFolderLoader(false);
setIsFolder(false);
}
}
} catch (e) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong!"
});
}
} else {
setError("Please fill out this field");
}
};
//function to use sorting document list according to type and order
const sortedBy = (appInfo, type, order) => {
if (type === orderName.Name) {
if (order === orderName.Ascending) {
return appInfo.sort((a, b) =>
a.Name.toLowerCase() < b.Name.toLowerCase() ? -1 : 1
);
} else if (order === orderName.Descending) {
return appInfo.sort((a, b) =>
a.Name.toLowerCase() < b.Name.toLowerCase() ? 1 : -1
);
}
} else if (type === orderName.Date) {
if (order === orderName.Ascending) {
return appInfo.sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1));
} else if (order === orderName.Descending) {
return appInfo.sort((a, b) => (a.createdAt > b.createdAt ? -1 : 1));
}
}
};
//function to use get sorting type, order and document list to sort
const sortingData = (type, order, driveDetails, isInitial) => {
const selectedSortType = type ? type : selectedSort;
const sortOrder = order ? order : sortingOrder;
//check isInitial true it means sort previous 20 and get on scrolling 20 = 40 document list
const allPdfData = isInitial ? [...pdfData, ...driveDetails] : driveDetails;
//call sortedBy function according to selected Type and order
if (selectedSortType === orderName.Name) {
sortedBy(allPdfData, orderName.Name, sortOrder);
} else if (selectedSortType === orderName.Date) {
sortedBy(allPdfData, orderName.Date, sortOrder);
}
setPdfData(allPdfData);
};
//function for handle auto scroll on folder path
const handleMouseEnter = (e) => {
let side = "";
const container = scrollRef.current;
const containerRect = container.getBoundingClientRect();
const cursorX = e.clientX;
const containerX = containerRect.left;
const containerWidth = containerRect.width;
// Define a threshold (e.g., 10 pixels) for the start and end points
const threshold = 10;
if (cursorX - containerX <= threshold) {
side = "start";
} else if (containerX + containerWidth - cursorX <= threshold) {
side = "end";
} else {
side = "";
}
const scrollSpeed = 10;
let scrollAmount = 0;
const scroll = () => {
if (side === "start" && container.scrollLeft > 0) {
container.scrollLeft -= scrollAmount;
requestAnimationFrame(scroll);
} else if (
side === "end" &&
container.scrollLeft < container.scrollWidth - container.clientWidth
) {
container.scrollLeft += scrollAmount;
requestAnimationFrame(scroll);
}
};
container.addEventListener("mousemove", (e) => {
const rect = container.getBoundingClientRect();
const x = e.clientX - rect.left;
const containerWidth = rect.width;
// Calculate the scroll amount based on the cursor's position
scrollAmount = (x / containerWidth) * scrollSpeed;
});
scroll();
};
//handle to close drop down menu onclick screen
useEffect(() => {
const closeMenuOnOutsideClick = (e) => {
if (isShowSort && !e.target.closest("#menu-container")) {
setIsShowSort(!isShowSort);
} else if (isNewFol && !e.target.closest("#folder-menu")) {
setIsNewFol(!isNewFol);
}
};
document.addEventListener("click", closeMenuOnOutsideClick);
return () => {
// Cleanup the event listener when the component unmounts
document.removeEventListener("click", closeMenuOnOutsideClick);
};
// eslint-disable-next-line
}, [isShowSort, isNewFol]);
const handleFolderTab = (folderData) => {
return folderData.map((data, id) => {
return (
<React.Fragment key={id}>
<span
onClick={() => handleRoute(id, folderData)}
style={{
color: "#a64b4e",
fontWeight: "400",
cursor: "pointer"
}}
>
{data.name}
<span
style={{
color: "#a64b4e",
fontWeight: "200",
cursor: "pointer",
margin: "0 4px"
}}
>
&gt;
</span>
</span>
</React.Fragment>
);
});
};
const oncloseFolder = () => {
setIsFolder(false);
setNewFolderName("");
setError("");
};
const handleDontShow = (isChecked) => {
setIsDontShow(isChecked);
};
const closeTour = async () => {
setIsTour(false);
setShowTourFirstTime(false);
if (isDontShow) {
const serverUrl = localStorage.getItem("baseUrl");
const appId = localStorage.getItem("parseAppId");
const extUserClass = localStorage.getItem("extended_class");
const json = JSON.parse(localStorage.getItem("Extand_Class"));
const extUserId = json && json.length > 0 && json[0].objectId;
let updatedTourStatus = [];
if (tourStatusArr.length > 0) {
updatedTourStatus = [...tourStatusArr];
const driveTourIndex = tourStatusArr.findIndex(
(obj) => obj["driveTour"] === false || obj["driveTour"] === true
);
if (driveTourIndex !== -1) {
updatedTourStatus[driveTourIndex] = { driveTour: true };
} else {
updatedTourStatus.push({ driveTour: true });
}
} else {
updatedTourStatus = [{ driveTour: true }];
}
await axios.put(
serverUrl + "classes/" + extUserClass + "/" + extUserId,
{
TourStatus: updatedTourStatus
},
{
headers: {
"X-Parse-Application-Id": appId
}
}
);
}
};
//function to use check tour status of open sign drive
async function checkTourStatus() {
const currentUser = Parse.User.current();
const cloudRes = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
const res = { data: cloudRes.toJSON() };
if (res.data && res.data.TourStatus && res.data.TourStatus.length > 0) {
const tourStatus = res.data.TourStatus;
setTourStatusArr(tourStatus);
const filteredtourStatus = tourStatus.filter((obj) => obj["driveTour"]);
if (filteredtourStatus.length > 0) {
const driveTour = filteredtourStatus[0]["driveTour"];
// console.log("loginTour", loginTour);
if (driveTour) {
setIsTour(false);
} else {
setIsTour(true);
}
} else {
setIsTour(true);
}
} else {
setIsTour(true);
}
}
return (
<div style={{ backgroundColor: "white" }} className="folderComponent">
<Title title={"OpenSign™ Drive"} drive={true} />
<div>
<ModalUi
headerColor={"#dc3545"}
isOpen={isAlert.isShow}
title={"Alert"}
handleClose={() => {
setIsAlert({
isShow: false,
alertMessage: ""
});
}}
>
<div style={{ height: "100%", padding: 20 }}>
<p>{isAlert.alertMessage}</p>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<button
onClick={() =>
setIsAlert({
isShow: false,
alertMessage: ""
})
}
type="button"
className="finishBtn cancelBtn"
>
Close
</button>
</div>
</ModalUi>
<ModalUi
isOpen={isFolder}
title={"Add New Folder"}
handleClose={oncloseFolder}
>
<div style={{ height: "100%", padding: 20 }}>
{folderLoader ? (
<div
style={{
height: "200px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}}
>
<img
alt="loader img"
src={loader}
style={{ width: "50px", height: "50px" }}
/>
<span style={{ fontSize: "13px", color: "gray" }}>
Loading...
</span>
</div>
) : (
<form
onSubmit={(e) => {
e.preventDefault();
handleAddFolder();
}}
style={{ display: "flex", flexDirection: "column" }}
>
<label
style={{
margin: "10px 0px 10px 0px",
fontSize: "15px",
fontWeight: "400"
}}
>
Name
<span style={{ color: "red" }}>*</span>
</label>
<input
required
className="form-control inputStyle"
type="text"
value={newFolderName}
onChange={(e) => handleFolderName(e)}
// className="addFolderInput"
/>
<span
style={{ color: "red", fontSize: "12px", marginTop: "6px" }}
>
{error}
</span>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<div style={{ display: "flex", flexDirection: "row" }}>
<button
style={{ background: themeColor }}
type="submit"
className="finishBtn"
>
Add
</button>
<button
type="button"
className="finishBtn cancelBtn"
onClick={oncloseFolder}
>
Close
</button>
</div>
</form>
)}
</div>
</ModalUi>
{isLoading.isLoad ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
width: "100%",
flexDirection: "column"
}}
>
<img
alt="loader img"
src={loader}
style={{ width: "80px", height: "80px" }}
/>
<span style={{ fontSize: "13px", color: "gray" }}>
{isLoading.message}
</span>
</div>
) : handleError ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
width: "100%"
}}
>
<span style={{ fontSize: "20px", color: "gray" }}>
{handleError}
</span>
</div>
) : (
<>
<div className="folderContainer">
{tourData && (
<Tour
onRequestClose={closeTour}
steps={tourData}
isOpen={isTour}
closeWithMask={false}
scrollOffset={-100}
rounded={5}
/>
)}
<div
data-tut="reactourFirst"
onMouseEnter={(e) => handleMouseEnter(e)}
ref={scrollRef}
style={{
width: "100%"
}}
className="folderPath"
>
{handleFolderTab(folderName)}
</div>
<div className="dropMenuBD">
<div
id="folder-menu"
className={
isNewFol ? "dropdown show dropDownStyle" : "dropdown"
}
onClick={() => setIsNewFol(!isNewFol)}
>
<div className="sort" data-tut="reactourSecond">
<i
className="fa fa-plus-square"
aria-hidden="true"
style={{ fontSize: "25px", color: `${iconColor}` }}
></i>
</div>
<div
className={
isNewFol ? "dropdown-menu show" : "dropdown-menu"
}
aria-labelledby="dropdownMenuButton"
aria-expanded={isNewFol ? "true" : "false"}
>
{" "}
<div
style={{
display: "flex",
flexDirection: "column"
}}
>
<span
className="dropdown-item itemColor"
onClick={() => setIsFolder(true)}
>
<i
style={{ marginRight: "5px" }}
className="fa fa-plus"
aria-hidden="true"
></i>
Create folder
</span>
<span
className="dropdown-item itemColor"
onClick={() => navigate("/form/sHAnZphf69")}
>
<i
style={{ marginRight: "5px" }}
className="fas fa-pen-nib"
></i>
Sign Yourself
</span>
<span
className="dropdown-item itemColor"
onClick={() => navigate("/form/8mZzFxbG1z")}
>
{" "}
<i
style={{ marginRight: "5px" }}
className="fa fa-file-signature"
></i>
Request Signatures{" "}
</span>
</div>
</div>
</div>
<div
id="menu-container"
className={isShowSort ? "dropdown show" : "dropdown"}
onClick={() => setIsShowSort(!isShowSort)}
>
<div
data-tut="reactourThird"
className=" sort "
data-toggle="dropdown"
>
<i
className="fa fa-sort-amount-asc"
aria-hidden="true"
style={{
marginRight: "5px",
fontSize: "14px",
color: `${iconColor}`
}}
></i>
<span
style={{
fontSize: "15px",
color: `${iconColor}`
}}
>
{selectedSort}
</span>
</div>
<div
className={
isShowSort ? "dropdown-menu show" : "dropdown-menu"
}
aria-labelledby="dropdownMenuButton"
aria-expanded={isShowSort ? "true" : "false"}
>
{sortingValue.map((value, ind) => {
return (
<span
key={ind}
onClick={() => {
setSelectedSort(value);
sortingData(value, null, pdfData);
}}
className="dropdown-item itemColor"
style={{
paddingLeft: selectedSort !== value && "33px"
}}
>
{selectedSort === value && (
<i
className="fa fa-check"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
)}
{value}
</span>
);
})}
<hr className="hrStyle" />
{sortOrder.map((order, ind) => {
return (
<span
key={ind}
onClick={() => {
setSortingOrder(order);
sortingData(null, order, pdfData);
}}
className="dropdown-item itemColor"
style={{
paddingLeft: sortingOrder !== order && "33px"
}}
>
{sortingOrder === order && (
<i
className="fa fa-check"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
)}
{order}
</span>
);
})}
</div>
</div>
<div>
{isList ? (
<div className="sort" onClick={() => setIsList(!isList)}>
<i
onClick={() => setIsList(!isList)}
className="fa fa-th-large"
style={{ fontSize: "24px", color: `${iconColor}` }}
aria-hidden="true"
></i>
</div>
) : (
<div
data-tut="reactourForth"
className="sort"
onClick={() => setIsList(!isList)}
>
<i
className="fa fa-list"
aria-hidden="true"
style={{ fontSize: "23px", color: `${iconColor}` }}
></i>
</div>
)}
</div>
</div>
</div>
{pdfData && pdfData.length === 0 ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "50vh",
width: "100%"
}}
>
<span style={{ fontWeight: "bold" }}>No Data Found!</span>
</div>
) : (
<div data-tut="reactourFifth">
<React.Suspense fallback={<Loader />}>
<DriveBody
dataTutSixth="reactourSixth"
dataTutSeventh="reactourSeventh"
pdfData={pdfData}
setFolderName={setFolderName}
setIsLoading={setIsLoading}
setDocId={setDocId}
getPdfDocumentList={getPdfDocumentList}
isDocId={docId}
setPdfData={setPdfData}
isList={isList}
setIsAlert={setIsAlert}
setSkip={setSkip}
sortingData={sortingData}
/>
{loading && (
<div style={{ textAlign: "center" }}>Loading...</div>
)}
</React.Suspense>
</div>
)}
</>
)}
</div>
</div>
);
}
export default Opensigndrive;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,234 +0,0 @@
import React, { useState, useEffect } from "react";
import { NavLink, useNavigate } from "react-router-dom";
import checkmark from "../assets/images/checkmark.png";
import plansArr from "../json/plansArr";
import Title from "../components/Title";
import Parse from "parse";
const listItemStyle = {
paddingLeft: "20px", // Add padding to create space for the image
backgroundImage: `url(${checkmark})`, // Set your image as the list style image
backgroundPosition: "left",
backgroundRepeat: "no-repeat",
backgroundSize: "16px 16px" // Adjust the size of the image
};
const PlanSubscriptions = () => {
const navigate = useNavigate();
const [yearlyVisible, setYearlyVisible] = useState(false);
const [isLoader, setIsLoader] = useState(true);
const userDetails = JSON.parse(localStorage.getItem("userDetails"));
const name =
userDetails && userDetails.name
? "first_name=" + encodeURIComponent(userDetails.name)
: "";
const email =
userDetails && userDetails.email
? "&email=" + encodeURIComponent(userDetails.email)
: "";
const company =
userDetails && userDetails.company
? "&company_name=" + encodeURIComponent(userDetails.company)
: "";
const phone =
userDetails && userDetails.phone
? "&mobile=" + encodeURIComponent(userDetails.phone)
: "";
const details =
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
name +
email +
company +
phone;
useEffect(() => {
// if (localStorage.getItem("accesstoken")) {
setIsLoader(false);
// } else {
// navigate("/", { replace: true });
// }
// eslint-disable-next-line
}, []);
const handleFreePlan = async () => {
setIsLoader(true);
try {
const params = { userId: Parse.User.current().id };
const res = await Parse.Cloud.run("freesubscription", params);
if (res.status === "success" && res.result === "already subscribed!") {
setIsLoader(false);
alert("You have already subscribed to plan!");
} else if (res.status === "success") {
setIsLoader(false);
navigate("/");
} else if (res.status === "error") {
setIsLoader(false);
alert(res.result);
}
} catch (err) {
setIsLoader(false);
console.log("err in free subscribe", err.message);
alert("Somenthing went wrong, please try again later!");
}
};
return (
<>
<Title title={"Subscriptions"} />
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<div
style={{
backgroundColor: "white",
overflowY: "auto",
maxHeight: "100%"
}}
>
<div className="block my-2">
<div className="flex flex-col justify-center items-center w-full">
<div className="mb-6 mt-2 flex flex-row border-[1px] p-2 border-gray-300 rounded text-sm">
<span
onClick={() => setYearlyVisible(false)}
className={`${
!yearlyVisible
? "bg-[#002862] text-white"
: "bg-white text-black"
} px-4 py-1 rounded cursor-pointer`}
>
Monthly
</span>
<span
onClick={() => setYearlyVisible(true)}
className={`${
yearlyVisible
? "bg-[#002862] text-white"
: "bg-white text-black"
} px-4 py-1 rounded cursor-pointer`}
>
Yearly (10% off)
</span>
</div>
<ul className="flex flex-col md:flex-row h-full bg-white justify-center">
{plansArr.map((item) => (
<li
className="flex flex-col text-center border-collapse border-[1px] border-gray-300 max-w-[250px]"
key={item.planName}
>
<div className="p-2 flex flex-col justify-center items-center max-h-[310px]">
<h3 className="text-[#002862] uppercase">
{item.planName}
</h3>
<div className="w-[150px] h-[150px]">
<img
className="icon-basic mx-auto"
src="https://js.zohostatic.com/books/zfwidgets/assets/images/plan.png"
alt="freeimg"
/>
</div>
<div>
<span className="text-3xl">
{item.currency && <small>{item.currency}</small>}
{yearlyVisible
? item?.yearlyPrice
: item.monthlyPrice}
</span>
<p className="font-semibold pt-2 text-sm">
{yearlyVisible ? "Billed Yearly" : "Billed Monthly"}
</p>
<div className="max-w-[250px] h-[40px] text-center text-sm my-2">
<div
style={{
textAlign: "center",
backgroundColor: item.subtitlecolor
? item.subtitlecolor
: "white"
}}
>
{item.subtitle.includes("<") ? (
<div
dangerouslySetInnerHTML={{
__html: item.subtitle
}}
/>
) : (
<span>{item.subtitle}</span>
)}
</div>
</div>
</div>
{item.url ? (
<NavLink
to={
item.btnText === "Subscribe"
? yearlyVisible
? item.yearlyUrl + details
: item.url + details
: item.url
}
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer"
target={item.target}
>
{item.btnText}
</NavLink>
) : (
<button
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:text-white cursor-pointer"
onClick={() => handleFreePlan()}
>
{item.btnText}
</button>
)}
</div>
<hr className="w-full bg-gray-300 h-[0.5px]" />
<ul className="mx-1 p-3 text-left break-words text-sm list-none">
{item.benefits.map((subitem, index) => (
<li style={listItemStyle} key={index} className="m-1">
<span style={{ position: "relative" }}>
{subitem}
</span>
</li>
))}
</ul>
</li>
))}
</ul>
</div>
<div className="text-sm flex justify-center items-center">
<hr className="border-[1px] border-gray-300 w-[20%]" />
<span className="px-2 text-gray-500 cursor-default">or</span>
<hr className="border-[1px] border-gray-300 w-[20%]" />
</div>
<div className="flex flex-col justify-center w-full items-center">
<h3 className="text-[#002862] mt-1 mb-2">
Host it yourself for free
</h3>
<NavLink
to={"https://github.com/OpenSignLabs/OpenSign"}
className="bg-[#002862] w-[200px] text-center text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer"
target={"_blank"}
>
Visit Github
</NavLink>
</div>
</div>
</div>
)}
</>
);
};
export default PlanSubscriptions;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More