Compare commits

..
Author SHA1 Message Date
Amol d705a4314f Autogenerate API sidebar 2025-07-04 13:24:20 +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
476 changed files with 86121 additions and 53811 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
+11 -9
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)
@@ -40,17 +42,17 @@ DO_SECRET_ACCESS_KEY=
# Digital ocean spaces region or AWS s3 region
DO_REGION=us-west
# local storage
USE_LOCAL=true
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 *********************************************************************************************************************
+3 -6
View File
@@ -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
+6 -1
View File
@@ -24,4 +24,9 @@ yarn-debug.log*
yarn-error.log*
apps/OpenSign/public/mfbuild/*
microfrontends/SignDocuments/build/*
apps/OpenSignServer/files/files/*
apps/OpenSignServer/files/files/*
# ignore generated API documentation
/docs/docs/API-docs/*
!/docs/docs/API-docs/opensign.yaml
!/docs/docs/API-docs/intro.md
/docs/docs/API-docs-1.1/
+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><a href="#security-Zathiel" title="Security">🛡️</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"]
}
}
};
+4278 -7454
View File
File diff suppressed because it is too large Load Diff
+36 -50
View File
@@ -3,58 +3,48 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@formkit/auto-animate": "^0.8.2",
"@radix-ui/themes": "^3.0.5",
"@react-pdf/renderer": "^3.4.4",
"@reduxjs/toolkit": "^2.2.5",
"axios": "^1.7.2",
"@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",
"nth-check": "^2.1.1",
"parse": "^5.1.0",
"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-confetti": "^6.1.0",
"react-cookie": "^7.1.4",
"react-datepicker": "^6.9.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-pdf": "^9.0.0",
"react-quill": "^2.0.0",
"react-redux": "^9.1.2",
"react-rnd": "^10.4.11",
"react-router-dom": "^6.22.3",
"react-modal": "^3.16.1",
"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.4",
"react-web-share": "^2.0.2",
"reactour": "^1.19.4",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"regex-parser": "^2.3.0",
"serve": "^14.2.3",
"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": "^4.0.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",
@@ -84,20 +74,16 @@
}
},
"devDependencies": {
"@babel/runtime-corejs2": "^7.24.7",
"autoprefixer": "^10.4.19",
"@babel/runtime-corejs2": "^7.23.2",
"autoprefixer": "^10.4.16",
"commitizen": "^4.3.0",
"daisyui": "^4.12.2",
"eslint": "^8.57.0",
"eslint": "^8.51.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.34.1",
"lint-staged": "^15.2.5",
"postcss": "^8.4.38",
"prettier": "^2.8.8",
"pretty-quick": "^3.3.1",
"tailwindcss": "^3.4.4"
},
"overrides": {
"nth-check": "$nth-check"
"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"
}
}
+4 -4
View File
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en" data-theme="opensigncss">
<html lang="en">
<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.
@@ -50,4 +50,4 @@
-->
</body>
</html>
</html>
Binary file not shown.
+111 -144
View File
@@ -1,45 +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";
import SSOVerify from "./pages/SSOVerify";
import Loader from "./primitives/Loader";
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"));
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";
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
const AppLoader = () => {
return (
<div className="flex justify-center items-center h-[100vh]">
<Loader />
</div>
);
};
function App() {
const [isloading, setIsLoading] = useState(true);
useEffect(() => {
@@ -47,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) {
@@ -65,114 +37,109 @@ function App() {
};
return (
<div className="bg-base-200">
<div className="bg-[#eef1f5]">
{isloading ? (
<AppLoader />
<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 path="/sso" element={<SSOVerify />} />
<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>
);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

-310
View File
@@ -1,310 +0,0 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import axios from "axios";
import Loader from "../primitives/Loader";
const AddSigner = (props) => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
const [addYourself, setAddYourself] = useState(false);
const [isLoader, setIsLoader] = useState(false);
const [isUserExist, setIsUserExist] = useState(false);
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
useEffect(() => {
checkUserExist();
}, []);
// Load user details from localStorage when the component mounts
useEffect(() => {
const savedUserDetails = JSON.parse(
localStorage.getItem("UserInformation")
);
if (savedUserDetails && addYourself) {
setName(savedUserDetails.name);
setPhone(savedUserDetails?.phone || "");
setEmail(savedUserDetails.email);
}
}, [addYourself]);
const checkUserExist = async () => {
const user = Parse.User.current();
try {
const query = new Parse.Query("contracts_Contactbook");
query.equalTo("CreatedBy", user);
query.notEqualTo("IsDeleted", true);
query.equalTo("Email", user.getEmail());
const res = await query.first();
// console.log(res);
if (!res) {
setIsUserExist(true);
}
} catch (err) {
console.log("err", err);
}
};
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
if (localStorage.getItem("TenantId")) {
try {
const user = Parse.User.current();
const query = new Parse.Query("contracts_Contactbook");
query.equalTo("CreatedBy", user);
query.notEqualTo("IsDeleted", true);
query.equalTo("Email", email);
const res = await query.first();
// console.log(res);
if (!res) {
const contactQuery = new Parse.Object("contracts_Contactbook");
contactQuery.set("Name", name);
if (phone) {
contactQuery.set("Phone", phone);
}
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
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("password", email);
if (phone) {
_user.set("phone", 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_Guest",
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);
// Reset the form fields
setAddYourself(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_Guest",
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);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
}
} else {
alert("Contact already exist!");
setIsLoader(false);
}
} catch (err) {
console.log("err in fetch contact", err);
setIsLoader(false);
alert("something went wrong, please try again later");
}
} else {
setIsLoader(false);
alert("something went wrong, please try again later");
}
};
// Define a function to handle the "add yourself" checkbox
const handleAddYourselfChange = () => {
if (addYourself) {
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
} else {
setAddYourself(true);
}
};
const handleReset = () => {
setAddYourself(false);
};
return (
<div className="h-full p-[20px]">
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30">
<Loader />
</div>
)}
<div className="w-full mx-auto p-2 text-base-content">
{isUserExist && (
<div className="mb-3 flex items-center">
<input
type="checkbox"
id="addYourself"
checked={addYourself}
onChange={handleAddYourselfChange}
className="op-checkbox op-checkbox-sm"
/>
<label htmlFor="addYourself" className="ml-2 mb-0">
Add Yourself
</label>
</div>
)}
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label htmlFor="name" className="block text-xs font-semibold">
Name
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
required
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label htmlFor="email" className="block text-xs font-semibold">
Email
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
required
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label htmlFor="phone" className="block text-xs font-semibold">
Phone
</label>
<input
type="text"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mt-4 flex gap-x-2 justify-start">
<button type="submit" className="op-btn op-btn-primary">
Submit
</button>
<button
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
>
Reset
</button>
</div>
</form>
</div>
</div>
);
};
export default AddSigner;
-278
View File
@@ -1,278 +0,0 @@
import React, { useState } from "react";
import Parse from "parse";
import axios from "axios";
import Title from "./Title";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
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("password", email);
if (phone) {
_user.set("phone", 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">
<Loader />
</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 className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
Email
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Phone
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full 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 "../styles/AddUser.css";
import Loader from "./Loader";
const AddContact = (props) => {
const AppendFormInForm = (props) => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
@@ -12,6 +11,7 @@ const AddContact = (props) => {
const [isUserExist, setIsUserExist] = useState(false);
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
useEffect(() => {
checkUserExist();
}, []);
@@ -22,7 +22,7 @@ const AddContact = (props) => {
);
if (savedUserDetails && addYourself) {
setName(savedUserDetails.name);
setPhone(savedUserDetails?.phone || "");
setPhone(savedUserDetails.phone);
setEmail(savedUserDetails.email);
}
}, [addYourself]);
@@ -46,22 +46,21 @@ const AddContact = (props) => {
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
try {
const contactQuery = new Parse.Object("contracts_Contactbook");
contactQuery.set("Name", name);
if (phone) {
contactQuery.set("Phone", phone);
}
contactQuery.set("Phone", phone);
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
if (localStorage.getItem("TenantId")) {
if (localStorage.getItem("TenetId")) {
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
objectId: localStorage.getItem("TenetId")
});
}
@@ -71,10 +70,8 @@ const AddContact = (props) => {
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("password", email);
if (phone) {
_user.set("phone", phone);
}
_user.set("phone", phone);
_user.set("password", phone);
const user = await _user.save();
if (user) {
@@ -108,7 +105,10 @@ const AddContact = (props) => {
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
props.details(parseData);
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
if (props.closePopup) {
props.closePopup();
}
@@ -158,10 +158,10 @@ const AddContact = (props) => {
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details(parseData);
}
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
if (props.closePopup) {
props.closePopup();
}
@@ -196,36 +196,41 @@ const AddContact = (props) => {
};
return (
<div className="h-full px-[20px] py-[10px]">
<div className="h-full p-[20px]">
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30">
<Loader />
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
)}
<div className="w-full mx-auto p-[8px]">
<div className="text-[14px] font-[700]">Add User</div>
<div className="w-full mx-auto p-2">
{isUserExist && (
<div className="mb-[0.75rem] flex items-center mt-1">
<div className="mb-3">
<input
type="checkbox"
id="addYourself"
checked={addYourself}
onChange={handleAddYourselfChange}
className="op-checkbox op-checkbox-sm"
className="form-checkbox"
/>
<label
htmlFor="addYourself"
className="ml-[0.5rem] text-base-content mb-0"
>
<label htmlFor="addYourself" className="ml-2 text-gray-700">
Add Yourself
</label>
</div>
)}
<form className="text-base-content" onSubmit={handleSubmit}>
<div className="mb-[0.75rem]">
<label htmlFor="name" className="text-[13px]">
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
Name
<span className="text-[13px] text-[red]"> *</span>
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
@@ -234,46 +239,57 @@ const AddContact = (props) => {
onChange={(e) => setName(e.target.value)}
required
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mb-[0.75rem]">
<label htmlFor="email" className="text-[13px]">
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
Email
<span className="text-[13px] text-[red]"> *</span>
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
onChange={(e) => setEmail(e.target.value)}
required
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs lowercase"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mb-[0.75rem]">
<label htmlFor="phone" className="text-[13px]">
<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
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mt-6 flex justify-start gap-2">
<button type="submit" className="op-btn op-btn-primary">
<div className="mt-4 flex justify-start">
<button
type="submit"
className="bg-[#1ab6ce] text-sm text-white px-4 py-2 rounded shadow focus:outline-none"
>
Submit
</button>
<button
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
className="bg-[#188ae2] text-sm text-white px-4 py-2 rounded ml-2 shadow focus:outline-none"
>
Reset
</button>
@@ -284,4 +300,4 @@ const AddContact = (props) => {
);
};
export default AddContact;
export default AppendFormInForm;
-256
View File
@@ -1,256 +0,0 @@
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import SuggestionInput from "./shared/fields/SuggestionInput";
import Loader from "../primitives/Loader";
const BulkSendUi = (props) => {
const [forms, setForms] = useState([]);
const [formId, setFormId] = useState(2);
const formRef = useRef(null);
const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false);
const [isSubmit, setIsSubmit] = useState(false);
const [allowedForm, setAllowedForm] = useState(0);
const [isSignatureExist, setIsSignatureExist] = useState();
const allowedSigners = 50;
useEffect(() => {
signatureExist();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//function to check atleast one signature field exist
const signatureExist = () => {
const getPlaceholder = props.item?.Placeholders;
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
)
);
setIsSignatureExist(checkIsSignatureExistt);
};
useEffect(() => {
if (scrollOnNextUpdate && formRef.current) {
formRef.current.scrollIntoView({
behavior: "smooth",
block: "end",
inline: "nearest"
});
setScrollOnNextUpdate(false);
}
}, [forms, scrollOnNextUpdate]);
useEffect(() => {
(() => {
if (props?.Placeholders?.length > 0) {
let users = [];
props?.Placeholders?.forEach((element) => {
if (!element.signerObjId) {
users = [
...users,
{
fieldId: element.Id,
email: "",
label: element.Role,
signer: {}
}
];
}
});
setForms((prevForms) => [...prevForms, { Id: 1, fields: users }]);
const totalForms = Math.floor(allowedSigners / users?.length);
setAllowedForm(totalForms);
}
})();
// eslint-disable-next-line
}, []);
const handleInputChange = (index, signer, fieldIndex) => {
const newForms = [...forms];
newForms[index].fields[fieldIndex].email = signer?.Email
? signer?.Email
: signer || "";
newForms[index].fields[fieldIndex].signer = signer?.objectId ? signer : "";
setForms(newForms);
};
const handleAddForm = (e) => {
e.preventDefault();
// Check if the quick send limit has been reached
if (forms?.length < allowedForm) {
if (props?.Placeholders.length > 0) {
let newForm = [];
props?.Placeholders?.forEach((element) => {
if (!element.signerObjId) {
newForm = [
...newForm,
{
fieldId: element.Id,
email: "",
label: element.Role,
signer: {}
}
];
}
});
setForms([...forms, { Id: formId, fields: newForm }]);
}
setFormId(formId + 1);
setScrollOnNextUpdate(true);
} else {
// If the limit has been reached, throw an error with the appropriate message
alert("Quick send reached limit.");
}
};
const handleRemoveForm = (index) => {
const updatedForms = forms.filter((_, i) => i !== index);
setForms(updatedForms);
};
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsSubmit(true);
// Create a copy of Placeholders array from props.item
let Placeholders = [...props.item.Placeholders];
// Initialize an empty array to store updated documents
let Documents = [];
// Loop through each form
forms.forEach((form) => {
// Map through the copied Placeholders array to update email values
const updatedPlaceholders = Placeholders.map((placeholder) => {
// Find the field in the current form that matches the placeholder Id
const field = form.fields.find(
(element) => parseInt(element.fieldId) === placeholder.Id
);
// If a matching field is found, update the email value in the placeholder
const signer = field?.signer?.objectId ? field.signer : {};
if (field) {
return {
...placeholder,
email: field.email,
signerObjId: field?.signer?.objectId || "",
signerPtr: signer
};
}
// If no matching field is found, keep the placeholder as is
return placeholder;
});
// Push a new document object with updated Placeholders into the Documents array
Documents.push({ ...props.item, Placeholders: updatedPlaceholders });
});
// console.log("Documents ", Documents);
await batchQuery(Documents);
};
const batchQuery = async (Documents) => {
const serverUrl = localStorage.getItem("baseUrl");
const functionsUrl = `${serverUrl}functions/batchdocuments`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
};
const params = {
Documents: JSON.stringify(Documents)
};
try {
const res = await axios.post(functionsUrl, params, { headers: headers });
// console.log("res ", res);
if (res.data && res.data.result) {
props.handleClose("success", Documents?.length);
}
} catch (err) {
console.log("Err ", err);
props.handleClose("error", 0);
} finally {
setIsSubmit(false);
}
};
return (
<>
{isSubmit && (
<div className="absolute z-[999] h-full w-full flex justify-center items-center bg-black bg-opacity-30">
<Loader />
</div>
)}
{props.Placeholders?.length > 0 ? (
isSignatureExist ? (
<>
{props.Placeholders?.some((x) => !x.signerObjId) ? (
<form onSubmit={handleSubmit}>
<div className="min-h-max max-h-[250px] overflow-y-auto">
{forms?.map((form, index) => (
<div
key={form.Id}
className="p-3 op-card border-[1px] border-gray-400 mt-3 mx-4 mb-4 bg-base-200 text-base-content grid grid-cols-1 md:grid-cols-2 gap-2 relative"
>
{form?.fields?.map((field, fieldIndex) => (
<div className="flex flex-col" key={field.fieldId}>
<label>{field.label}</label>
<SuggestionInput
required
type="email"
value={field.value}
index={fieldIndex}
onChange={(signer) =>
handleInputChange(index, signer, fieldIndex)
}
/>
</div>
))}
{forms?.length > 1 && (
<button
onClick={() => handleRemoveForm(index)}
className="absolute right-3 top-1 text-[red] border-[1px] border-[red] rounded-lg w-[1.7rem] h-[1.7rem]"
>
<i className="fa-solid fa-trash"></i>
</button>
)}
<div ref={formRef}></div>
</div>
))}
</div>
<div className="flex flex-col mx-4 mb-4 gap-3">
<button
onClick={handleAddForm}
className="op-btn op-btn-primary focus:outline-none"
>
<i className="fa-solid fa-plus"></i> <span>Add new</span>
</button>
<button
type="submit"
className="op-btn op-btn-secondary focus:outline-none"
>
<i className="fa-solid fa-paper-plane"></i>{" "}
<span>Send</span>
</button>
</div>
</form>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
All roles in this document are currently linked to contacts. To
&apos;quick send&apos; copies of this template to multiple
signers, please ensure that at least one role is not linked to
any contact.
</div>
)}
</>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
Please ensure there&#39;s at least one signature widget added for
all recipients.
</div>
)
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
Please add at least one role to this template in order to &apos;quick
send&apos; copies of it to multiple signers.
</div>
)}
</>
);
};
export default BulkSendUi;
@@ -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;
+5 -16
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,23 +37,13 @@ const Footer = () => {
}, []);
const appName = "OpenSign™";
const openUrl = () => {
openInNewTab(
"https://github.com/OpenSignLabs/OpenSign/releases/tag/" + version
);
};
return (
<>
<footer className="op-footer op-footer-center py-3 bg-base-300 text-base-content text-center text-[13px]">
<aside>
<p>
All Rights Reserved &copy; {new Date().getFullYear()} &nbsp;
<span onClick={openUrl} className="hover:underline cursor-pointer">
{appName} ( version: {version ? version : `${Package.version} `})
</span>
</p>
</aside>
</footer>
<div className="bg-[#222c3c] text-[#98a6ba] text-center text-[13px] py-3">
All Rights Reserved &copy; {new Date().getFullYear()} &nbsp;
{appName} ( version: {version ? version : `${Package.version} `})
</div>
<button
className={`${
showButton ? "block" : "hidden"
@@ -67,7 +67,7 @@ const FullScreenButton = () => {
<div className="flex items-center">
<button
onClick={toggleFullScreen}
className="text-base-content p-2 text-sm focus:outline-none"
className="text-black p-2 text-sm focus:outline-none"
>
{isFullScreen ? (
<i className="fa-solid fa-compress"></i>
+57 -118
View File
@@ -4,53 +4,26 @@ 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);
if (Parse?.User?.current()) {
try {
Parse.User.logOut();
} catch (err) {
console.log("Err", err);
}
}
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
Parse.User.logOut();
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
let appName = localStorage.getItem("appName");
@@ -58,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();
@@ -70,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
@@ -93,16 +66,14 @@ const Header = ({ showSidebar }) => {
}, [isOpen]);
return (
<div className="op-navbar bg-base-100 shadow">
<div className="flex-none">
<button
className="op-btn op-btn-square op-btn-ghost focus:outline-none"
onClick={showSidebar}
>
<i className="fa-solid fa-bars text-xl text-base-content"></i>
</button>
</div>
<div className="flex-1 ml-2">
<div className="flex flex-row justify-between items-center gap-x-3 md:gap-x-4 bg-white md:px-4 px-2 shadow h-[50px] md:w-full ">
<button
className="cursor-pointer focus:outline-none"
onClick={showSidebar}
>
<i className={`fa-solid fa-bars text-xl `}></i>
</button>
<div className="flex-1">
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
<img
className="object-contain h-full w-auto"
@@ -111,22 +82,10 @@ const Header = ({ showSidebar }) => {
/>
</div>
</div>
<div id="profile-menu" className="flex-none gap-2">
{!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] bg-white rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
PRO
</div>
)}
<div
id="profile-menu"
className="flex justify-between items-center gap-x-3"
>
<div>
<FullScreenButton />
</div>
@@ -143,66 +102,46 @@ const Header = ({ showSidebar }) => {
</div>
)}
{width >= 768 && (
<div
onClick={toggleDropdown}
className="cursor-pointer text-base-content text-sm"
>
<div onClick={toggleDropdown} className="cursor-pointer text-sm">
{username && username}
</div>
)}
<div className="op-dropdown op-dropdown-end" id="profile-menu">
<div tabIndex={0} role="button" className="op-btn op-btn-ghost">
<i
tabIndex={0}
role="button"
onClick={toggleDropdown}
className="fa-solid fa-angle-down text-base-content"
></i>
<div className="relative">
<div className="cursor-pointer">
<i onClick={toggleDropdown} className="fa-solid fa-angle-down"></i>
</div>
<ul
tabIndex={0}
className="mt-3 z-[1] p-2 shadow op-menu op-menu-sm op-dropdown-content text-base-content bg-base-100 rounded-box w-52"
<div
className={`dropdown absolute text-sm text-gray-700 font-light right-0 mt-4 p-1 w-48 bg-white border rounded-lg shadow-lg z-10 ${
isOpen ? "block" : "hidden"
}`}
>
<li onClick={() => openInNewTab("https://docs.opensignlabs.com")}>
<span>
<i className="fa-solid fa-book"></i> Docs
</span>
</li>
<li
onClick={() => {
setIsOpen(false);
navigate("/profile");
}}
>
<span>
<ul>
<li
className="hover:bg-gray-100 rounded-t-lg py-1 px-2 cursor-pointer font-normal"
onClick={() => {
setIsOpen(false);
navigation("/profile");
}}
>
<i className="fa-regular fa-user"></i> Profile
</span>
</li>
<li
onClick={() => {
setIsOpen(false);
navigate("/changepassword");
}}
>
<span>
</li>
<li
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
onClick={() => {
setIsOpen(false);
navigation("/changepassword");
}}
>
<i className="fa-solid fa-lock"></i> Change Password
</span>
</li>
<li
onClick={() => {
window.open("https://console.opensignlabs.com/");
}}
>
<span>
<i className="fa-regular fa-id-card"></i> Console
</span>
</li>
<li onClick={closeDropdown}>
<span>
</li>
<li
className="hover:bg-gray-100 rounded-b-lg py-1 px-2 cursor-pointer font-normal"
onClick={closeDropdown}
>
<i className="fa-solid fa-arrow-right-from-bracket"></i> Log Out
</span>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
+48 -17
View File
@@ -1,6 +1,5 @@
import React, { useState } from "react";
import Parse from "parse";
import Loader from "../primitives/Loader";
// import FacebookLogin from "react-facebook-login";
const LoginFacebook = ({
@@ -50,7 +49,7 @@ const LoginFacebook = ({
const extRes = await Parse.Cloud.run("getUserDetails", params);
// console.log("extRes ", extRes);
if (extRes) {
const params = { ...details, Phone: extRes?.get("Phone") || "" };
const params = { ...details, Phone: extRes.get("Phone") };
const payload = await Parse.Cloud.run("facebooksign", params);
// console.log("payload ", payload);
if (payload && payload.sessiontoken) {
@@ -60,7 +59,7 @@ const LoginFacebook = ({
const LocalUserDetails = {
name: details.Name,
email: details.Email,
phone: extRes?.get("Phone") || "",
phone: extRes.get("Phone"),
company: extRes.get("Company")
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
@@ -76,7 +75,7 @@ const LoginFacebook = ({
const handleSubmitbtn = async () => {
if (userDetails.Phone && userDetails.Company) {
setThirdpartyLoader(true);
const params = { ...fBDetails, Phone: userDetails?.Phone || "" };
const params = { ...fBDetails, Phone: userDetails.Phone };
const payload = await Parse.Cloud.run("facebooksign", params);
// console.log("payload ", payload);
@@ -86,8 +85,8 @@ const LoginFacebook = ({
name: userDetails.Name,
email: userDetails.Email,
// "passsword":userDetails.Phone,
phone: userDetails?.Phone || "",
role: "contracts_User",
phone: userDetails.Phone,
role: "contracts_Admin",
company: userDetails.Company
}
};
@@ -97,7 +96,7 @@ const LoginFacebook = ({
const LocalUserDetails = {
name: userDetails.Name,
email: userDetails.Email,
phone: userDetails?.Phone || "",
phone: userDetails.Phone,
company: userDetails.Company
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
@@ -113,10 +112,29 @@ const LoginFacebook = ({
}
};
return (
<div className="relative">
<div style={{ position: "relative" }}>
{thirdpartyLoader && (
<div className="fixed flex justify-center items-center inset-0 bg-black bg-opacity-25 z-20 ">
<Loader />
<div
style={{
position: "fixed",
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0,0,0,0.2)",
top: 0,
left: 0,
zIndex: 2
}}
>
<div
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
</div>
)}
{/* <FacebookLogin
@@ -128,10 +146,11 @@ const LoginFacebook = ({
/> */}
{isModal && (
<div
className="modal fade show block z-[1]"
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">
@@ -144,8 +163,13 @@ const LoginFacebook = ({
<div className="modal-body">
<form>
<div className="form-group">
<label htmlFor="Phone" className="col-form-label">
Phone <span className="text-[red] text-[13px]">*</span>
<label
htmlFor="Phone"
style={{ display: "flex" }}
className="col-form-label"
>
Phone{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="tel"
@@ -162,8 +186,13 @@ const LoginFacebook = ({
/>
</div>
<div className="form-group">
<label htmlFor="Company" className="col-form-label">
Company <span className="text-[red] text-[13px]">*</span>
<label
htmlFor="Company"
style={{ display: "flex" }}
className="col-form-label"
>
Company{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
@@ -183,14 +212,16 @@ const LoginFacebook = ({
<button
type="button"
onClick={() => handleSubmitbtn()}
className="btn btn-info mr-[10px]"
className="btn btn-info"
style={{ marginRight: 10 }}
>
Sign up
</button>
<button
type="button"
className="btn btn-secondary w-[90px]"
className="btn btn-secondary"
onClick={() => setIsModal(false)}
style={{ width: 90 }}
>
Cancel
</button>
+151 -96
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 Loader from "../primitives/Loader";
/*
* `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);
@@ -110,22 +108,22 @@ const GoogleSignInBtn = ({
const extRes = await Parse.Cloud.run("getUserDetails", params);
// console.log("extRes ", extRes);
if (extRes) {
const params = { ...details, Phone: extRes?.get("Phone") || "" };
const params = { ...details, Phone: extRes.get("Phone") };
const payload = await Parse.Cloud.run("googlesign", params);
// 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,
email: details.Gmail,
phone: extRes?.get("Phone") || "",
phone: extRes.get("Phone"),
company: extRes.get("Company")
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
thirdpartyLoginfn(payload.sessiontoken);
thirdpartyLoginfn(payload.sessiontoken, billingDate);
}
return { msg: "exist" };
} else {
@@ -139,7 +137,7 @@ const GoogleSignInBtn = ({
setThirdpartyLoader(true);
// e.preventDefault()
// console.log("handelSubmit", userDetails);
const params = { ...googleDetails, Phone: userDetails?.Phone };
const params = { ...googleDetails, Phone: userDetails.Phone };
const payload = await Parse.Cloud.run("googlesign", params);
// console.log("payload ", payload);
@@ -149,8 +147,8 @@ const GoogleSignInBtn = ({
name: userDetails.Name,
email: userDetails.Email,
// "passsword":userDetails.Phone,
phone: userDetails?.Phone || "",
role: "contracts_User",
phone: userDetails.Phone,
role: "contracts_Admin",
company: userDetails.Company,
jobTitle: userDetails.Destination
}
@@ -161,7 +159,7 @@ const GoogleSignInBtn = ({
const LocalUserDetails = {
name: userDetails.Name,
email: userDetails.Email,
phone: userDetails?.Phone || "",
phone: userDetails.Phone,
company: userDetails.Company
// jobTitle: userDetails.JobTitle
};
@@ -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,91 +201,148 @@ 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 className="relative">
<div style={{ position: "relative" }}>
{thirdpartyLoader && (
<div className="fixed flex justify-center items-center inset-0 bg-black bg-opacity-25 z-20 ">
<Loader />
<div
style={{
position: "fixed",
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0,0,0,0.2)",
top: 0,
left: 0,
zIndex: 2
}}
>
<div
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
</div>
)}
<div ref={googleBtn} className="text-sm"></div>
<ModalUi showClose={false} isOpen={isModal} title="Sign up form">
<form className="px-4 py-3 text-base-content">
<div className="mb-3">
<label htmlFor="Phone" className="block text-xs font-semibold">
Phone <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="tel"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full 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" className="block text-xs font-semibold">
Company <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
required
/>
</div>
<div className="mb-3">
<label htmlFor="JobTitle" className="block text-xs font-semibold">
Job Title <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
required
/>
</div>
<div className="flex flex-row gap-2">
<button
type="button"
className="op-btn op-btn-primary"
onClick={() => handleSubmitbtn()}
>
Sign up
</button>
<button
type="button"
className="op-btn op-btn-ghost"
onClick={handleCloseModal}
>
Cancel
</button>
</div>
</form>
</ModalUi>
</div>
)}
</div>
);
};
@@ -1,69 +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 className="sticky top-0 p-[10px] z-10 bg-white border-[1px] border-[gray] my-[5px]">
{`Co-ordinates: X - ${props.hoverCoordinates.x}, Y - ${props.hoverCoordinates.y}`}
</div>
<div
className="relative flex-1 cursor-crosshair border-[1px] border-[gray] overflow-auto"
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}
className="absolute top-0 right-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 shadow-md px-3 py-2 op-card bg-base-100`}
>
<div className="flex flex-row items-center">
<span className="rounded-full bg-base-content 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="op-card-title text-lg ml-3 text-base-content">
{props.Label}
</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,30 +342,23 @@ 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`}
>
<div className="flex items-center justify-start gap-5 text-white">
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
<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="font-medium">
<div className="">
<div className="text-base lg:text-lg"> {props.Label}</div>
<div className="text-2xl font-light">
{loading ? <div className="loader-01"></div> : setFormat(response)}
</div>
</div>
</div>
<div className="text-xs absolute top-3 right-2">
<Tooltip
id={props.Label}
iconColor={"white"}
message={props?.Data?.tourMessage}
/>
</div>
</div>
);
};
@@ -3,7 +3,6 @@ import Parse from "parse";
import ReportTable from "../../primitives/GetReportDisplay";
import reportJson from "../../json/ReportJson";
import axios from "axios";
import Loader from "../../primitives/Loader";
function DashboardReport(props) {
// console.log("props ", props)
const [List, setList] = useState([]);
@@ -46,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
@@ -98,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) {
@@ -113,7 +112,6 @@ function DashboardReport(props) {
console.log("err ", err);
setIsLoader(false);
}
setIsLoader(false);
}
} else {
setIsLoader(false);
@@ -122,8 +120,21 @@ function DashboardReport(props) {
return (
<>
{isLoader ? (
<div className="h-[250px] flex justify-center items-center">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<>
@@ -1,52 +1,28 @@
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 }) => (
<DashboardButton
Icon={icon}
Label={label}
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
/>
);
const renderSwitchWithTour = (col) => {
switch (col.widget.type) {
case "Card":
return (
<div
className={`${
col?.widget?.bgColor ? col.widget.bgColor : "bg-[#2ed8b6]"
} op-card w-full h-[140px] px-3 pt-4 mb-3 shadow-md"`}
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 className="h-[150px] w-full flex justify-center items-center">
loading...
</div>
}
>
<Suspense fallback={<div>please wait</div>}>
<DashboardCard
Icon={col.widget.icon}
Label={col.widget.label}
Format={col.widget.format && col.widget.format}
Data={col.widget.data}
FilterData={col.widget.filter}
DefaultQuery={props.DefaultQuery}
/>
</Suspense>
</div>
@@ -55,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>
@@ -63,7 +39,7 @@ const GetDashboard = (props) => {
);
}
default:
return <></>;
return null;
}
};
const renderSwitch = (col) => {
@@ -71,9 +47,8 @@ const GetDashboard = (props) => {
case "Card":
return (
<div
className={`${
col?.widget?.bgColor ? col.widget.bgColor : "bg-[#2ed8b6]"
} op-card w-full h-[140px] px-3 pt-4 mb-3 shadow-md"`}
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"}
style={{ background: col.widget.bgColor }}
>
<Suspense fallback={<div>please wait</div>}>
<DashboardCard
@@ -82,6 +57,7 @@ const GetDashboard = (props) => {
Format={col.widget.format && col.widget.format}
Data={col.widget.data}
FilterData={col.widget.filter}
DefaultQuery={props.DefaultQuery}
/>
</Suspense>
</div>
@@ -89,48 +65,36 @@ 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>
);
}
default:
return <></>;
return null;
}
};
return (
<div>
<div className="mb-3">
<div
data-tut={"tourbutton"}
className="flex flex-col md:flex-row gap-4"
>
{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>
<div className="grid grid-cols-12 w-full gap-x-4">
{props?.dashboard?.columns?.map((col, i) =>
col.widget.data && col.widget.data.tourSection ? (
<div key={i} className={col?.colsize}>
{renderSwitchWithTour(col)}
</div>
) : (
<div key={i} className={col?.colsize}>
{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,595 +0,0 @@
import React, { useState, useEffect, useRef } from "react";
import folder from "../../assets/images/folder.png";
import "../../styles/opensigndrive.css";
import pdfLogo from "../../assets/images/pdf3.png";
import axios from "axios";
import * as ContextMenu from "@radix-ui/react-context-menu";
import { saveAs } from "file-saver";
import { useNavigate } from "react-router-dom";
import Table from "react-bootstrap/Table";
import * as HoverCard from "@radix-ui/react-hover-card";
import ModalUi from "../../primitives/ModalUi";
import FolderModal from "../shared/fields/FolderModal";
function DriveBody(props) {
const [rename, setRename] = useState("");
const [renameValue, setRenameValue] = useState("");
const inputRef = useRef(null);
const [isOpenMoveModal, setIsOpenMoveModal] = useState(false);
const [selectDoc, setSelectDoc] = useState();
const [isDeleteDoc, setIsDeleteDoc] = useState(false);
const contextMenu = [
{ type: "Download", icon: "fa-solid fa-arrow-down" },
{ type: "Rename", icon: "fa-solid fa-font" },
{ type: "Move", icon: "fa-solid fa-file-export" },
{ type: "Delete", icon: "fa-solid fa-trash" }
];
const navigate = useNavigate();
//to focus input box on press rename to change doc name
useEffect(() => {
if (rename && inputRef.current) {
setTimeout(() => {
inputRef.current.focus();
}, 10);
}
}, [rename]);
//function to handle folder component
const handleOnclikFolder = (data) => {
const folderData = {
name: data.Name,
objectId: data.objectId
};
props.setFolderName((prev) => [...prev, folderData]);
props.setIsLoading({
isLoad: true,
message: "This might take some time"
});
props.setDocId(data.objectId);
props.setPdfData([]);
props.setSkip(0);
};
//function for change doc name and update doc name in _document class
const handledRenameDoc = async (data) => {
setRename("");
const trimmedValue = renameValue.trim();
if (trimmedValue.length > 0) {
const updateName = {
Name: renameValue
};
const docId = data.objectId;
const docData = props.pdfData;
const updatedData = docData.map((item) => {
if (item.objectId === docId) {
// If the item's ID matches the target ID, update the name
return { ...item, Name: renameValue };
}
// If the item's ID doesn't match, keep it unchanged
return item;
});
props.setPdfData(updatedData);
props.sortingData(null, null, updatedData);
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document/${docId}`,
updateName,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
)
.then(() => {
// const res = result.data;
// console.log("res", res);
})
.catch((err) => {
console.log("Err ", err);
props.setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
});
}
};
//function for navigate user to microapp-signature component
const checkPdfStatus = async (data) => {
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}`);
}
};
const handleMenuItemClick = (selectType, data) => {
switch (selectType) {
case "Download": {
const pdfName = data && data.Name;
const pdfUrl = data && data.SignedUrl ? data.SignedUrl : data.URL;
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
break;
}
case "Rename": {
setRenameValue(data.Name);
setRename(data.objectId);
break;
}
case "Delete": {
setIsDeleteDoc(true);
setSelectDoc(data);
break;
}
case "Move": {
handleMoveDocument(data);
break;
}
default:
null;
}
};
//function for delete document
const handleDeleteDocument = async (docData) => {
setIsDeleteDoc(false);
const docId = docData.objectId;
const data = {
IsArchive: true
};
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document/${docId}`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
)
.then((result) => {
const res = result.data;
if (res) {
const updatedData = props.pdfData.filter((x) => x.objectId !== docId);
props.setPdfData(updatedData);
}
})
.catch((err) => {
console.log("Err ", err);
props.setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
});
};
const handleMoveDocument = async (docData) => {
setIsOpenMoveModal(true);
setSelectDoc(docData);
};
//function for move document from one folder to another folder
const handleMoveFolder = async (selectFolderData) => {
const selecFolderId = selectDoc?.Folder?.objectId;
const moveFolderId = selectFolderData?.ObjectId;
let updateDocId = selectDoc?.objectId;
let updateData;
const checkExist = moveFolderId
? selecFolderId === moveFolderId
? true
: false
: selecFolderId
? false
: true;
if (!checkExist) {
if (moveFolderId) {
updateData = {
Folder: {
__type: "Pointer",
className: `${localStorage.getItem("_appName")}_Document`,
objectId: moveFolderId
}
};
} else {
updateData = {
Folder: { __op: "Delete" }
};
}
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document/${updateDocId}`,
updateData,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
)
.then((Listdata) => {
// console.log("Listdata ", Listdata);
const res = Listdata.data;
if (res) {
const updatedData = props.pdfData.filter(
(x) => x.objectId !== updateDocId
);
props.setPdfData(updatedData);
}
})
.catch((err) => {
console.log("err", err);
});
setIsOpenMoveModal(false);
} else {
alert("folder already exist!");
setIsOpenMoveModal(false);
}
};
const sanitizeFileName = (pdfName) => {
// Replace spaces with underscore
return pdfName.replace(/ /g, "_");
};
const handleEnterPress = (e, data) => {
if (e.key === "Enter") {
handledRenameDoc(data);
}
};
//component to handle type of document and render according to type
const handleFolderData = (data, ind, listType) => {
let createddate, status, isDecline, signerExist, isComplete;
if (data.Type !== "Folder") {
const expireDate = data.ExpiryDate && data.ExpiryDate.iso;
const createdDate = data.createdAt && data.createdAt;
createddate = new Date(createdDate).toLocaleDateString();
isComplete = data.IsCompleted && data.IsCompleted ? true : false;
isDecline = data.IsDeclined && data.IsDeclined;
signerExist = data.Signers && data.Signers;
const signedUrl = data.SignedUrl;
const expireUpdateDate = new Date(expireDate).getTime();
const currDate = new Date().getTime();
let isExpire = false;
if (currDate > expireUpdateDate) {
isExpire = true;
}
if (isComplete) {
status = "Completed";
} else if (isDecline) {
status = "Declined";
} else if (!signedUrl) {
status = "Draft";
} else if (isExpire) {
status = "Expired";
} else {
status = "In Progress";
}
}
const signersName = () => {
const getSignersName = signerExist.map((data) => data.Name);
const signerName = getSignersName.join(", ");
return (
<span className="statusSpan w-[90%] break-words">{signerName}</span>
);
};
return listType === "table" ? (
data.Type === "Folder" ? (
<tr onClick={() => handleOnclikFolder(data)}>
<td className="cursor-pointer flex items-center">
<i
className="fa fa-folder mr-[8px] text-[26px] text-[#f0ad26]"
aria-hidden="true"
></i>
<span className="text-[12px] font-medium">{data.Name}</span>
</td>
<td>_</td>
<td>Folder</td>
<td>_</td>
<td>_</td>
</tr>
) : (
<tr onClick={() => checkPdfStatus(data)}>
<td className="cursor-pointer flex items-center">
<i className="fa fa-file-pdf mr-[8px] text-[26px] text-[#ed4d0e]"></i>
<span className="text-[12px] font-medium">{data.Name}</span>
</td>
<td>{createddate}</td>
<td>Pdf</td>
<td>{status}</td>
<td>
<i
onClick={(e) => {
e.stopPropagation();
handleMenuItemClick("Download", data);
}}
className="fa fa-download mr-[8px] text-[#ed280e]"
aria-hidden="true"
></i>
</td>
</tr>
)
) : listType === "list" && data.Type === "Folder" ? (
<div key={ind} className="icon-container">
<ContextMenu.Root>
<ContextMenu.Trigger className="ContextMenuTrigger">
<div data-tut={props.dataTutSeventh}>
<img
alt="folder"
onClick={() => handleOnclikFolder(data)}
src={folder}
className="w-full h-full"
/>
{rename === data.objectId ? (
<input
onFocus={() => {
inputRef.current.setSelectionRange(0, 0);
const input = inputRef.current;
if (input) {
input.select();
}
}}
autoFocus={true}
type="text"
onBlur={() => handledRenameDoc(data)}
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
/>
) : (
<span className="foldName">{data.Name}</span>
)}
</div>
</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content
className="ContextMenuContent"
sideOffset={5}
align="end"
>
<ContextMenu.Item
onClick={() => handleMenuItemClick("Rename", data)}
className="ContextMenuItem"
>
<i className="fa-solid fa-font mr-[8px]"></i>
<span>Rename</span>
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
</div>
) : (
<HoverCard.Root openDelay={0} closeDelay={100}>
<HoverCard.Trigger asChild>
<div>
<ContextMenu.Root>
<div className="icon-container">
<ContextMenu.Trigger className="ContextMenuTrigger">
<img
alt="PDF"
className="w-full h-full"
src={pdfLogo}
onClick={() => checkPdfStatus(data)}
data-tut={props.dataTutSixth}
/>
{rename === data.objectId ? (
<input
autoFocus={true}
type="text"
onFocus={() => {
inputRef.current.setSelectionRange(0, 0);
const input = inputRef.current;
if (input) {
input.select();
}
}}
onBlur={() => handledRenameDoc(data)}
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
/>
) : (
<span className="fileName">{data.Name}</span>
)}
</ContextMenu.Trigger>
{status === "Completed" ? (
<div className="status-badge completed">
<i className="fas fa-check-circle"></i>
</div>
) : status === "Declined" ? (
<div className="status-badge declined">
<i className="fa fa-thumbs-down"></i>
</div>
) : status === "Expired" ? (
<div className="status-badge expired">
<i className="fa fa-hourglass-end"></i>
</div>
) : status === "Draft" ? (
<div className="status-badge draft">
<i className="fa fa-file"></i>
</div>
) : (
status === "In Progress" && (
<div className="status-badge in-progress">
<i className="fa fa-paper-plane"></i>
</div>
)
)}
</div>
<ContextMenu.Portal>
<ContextMenu.Content
className="ContextMenuContent"
sideOffset={5}
align="end"
>
{contextMenu.map((menu, ind) => {
return (
<ContextMenu.Item
key={ind}
onClick={() => handleMenuItemClick(menu.type, data)}
className="ContextMenuItem"
>
<i className={menu.icon}></i>
<span className="ml-[8px]">{menu.type}</span>
</ContextMenu.Item>
);
})}
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
</div>
</HoverCard.Trigger>
<HoverCard.Portal>
<HoverCard.Content className="HoverCardContent" sideOffset={5}>
<strong className="text-[13px]">Title: </strong>
<span className="statusSpan" style={{ marginBottom: "0px" }}>
{" "}
{data.Name}
</span>
<br />
<strong className="text-[13px]">Status: </strong>
<span className="statusSpan"> {status}</span>
<br />
<strong className="text-[13px]">Created Date: </strong>
<span className="statusSpan">{createddate}</span>
<br />
{signerExist && (
<>
<strong className="text-[13px]">Signers: </strong>
{/* <span className="statusSpan">kjefjjnejkfnkbjs bbfjkdsbjbfjkbjk kscbjkbjkb</span> */}
{signersName()}
</>
)}
<HoverCard.Arrow className="HoverCardArrow" />
</HoverCard.Content>
</HoverCard.Portal>
</HoverCard.Root>
);
};
//component to handle type of document and render according to type
return (
<>
{props.isList ? (
<div className="container" style={{ overflowX: "auto" }}>
<Table striped bordered hover>
<thead>
<tr>
<th>Name</th>
<th>Created Date</th>
<th>Type</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{props.pdfData.map((data, ind) => {
return (
<React.Fragment key={ind}>
{handleFolderData(data, ind, "table")}
</React.Fragment>
);
})}
</tbody>
</Table>
</div>
) : (
<div className="flex flex-row flex-wrap items-center mt-1 pb-[20px] mx-[5px]">
{props.pdfData.map((data, ind) => {
return <div key={ind}>{handleFolderData(data, ind, "list")}</div>;
})}
</div>
)}
{isOpenMoveModal && (
<FolderModal
onSuccess={handleMoveFolder}
isOpenModal={isOpenMoveModal}
folderCls={"contracts_Document"}
setIsOpenMoveModal={setIsOpenMoveModal}
setPdfData={props.setPdfData}
/>
)}
<ModalUi
isOpen={isDeleteDoc}
title={"Delete Document"}
handleClose={() => setIsDeleteDoc(false)}
>
<div className="h-full p-[20px] text-base-content">
<p>Are you sure you want to delete this document?</p>
<div className="h-[1px] w-full bg-[#9f9f9f] my-[15px]"></div>
<button
onClick={() => handleDeleteDocument(selectDoc)}
type="button"
className="op-btn op-btn-primary mr-2"
>
Yes
</button>
<button
onClick={() => setIsDeleteDoc(false)}
type="button"
className="op-btn op-btn-neutral"
>
No
</button>
</div>
</ModalUi>
</>
);
}
export default DriveBody;
@@ -1,45 +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="h-full py-[10px] px-[20px]">
<form className="flex flex-col" 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="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs mt-1"
/>
<p className="text-[gray] text-[11px] mt-[5px] mb-[10px] ml-[10px]">
e.g: Customer, Hr, Director, Manager, Student, etc...
</p>
<div>
<div className="h-[1px] w-full bg-[#9f9f9f] mb-[10px]"></div>
<button type="submit" className="op-btn op-btn-primary">
Add
</button>
<button
onClick={props.handleCloseRoleModal}
type="button"
className="op-btn op-btn-ghost ml-2"
>
Close
</button>
</div>
</form>
</div>
</ModalUi>
);
};
export default AddRoleModal;
@@ -1,44 +0,0 @@
import React from "react";
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 data-tut="reactourThird">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
Signature
</div>
<div className="flex flex-col items-center mt-[10px] font-semibold">
<p className="text-base-content">Your Signature</p>
<div className="op-card shadow-md h-[111px] w-[90%] p-2">
<img
alt="signature"
className="w-full h-full object-contain"
src={defaultSignImg}
/>
</div>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mt-[10px]"
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 LoaderWithMsg 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 ? (
<LoaderWithMsg 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,389 +0,0 @@
import React, { useEffect, useState } from "react";
import { isEnableSubscription } 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] text-base-content">
<form
onSubmit={(e) => {
e.preventDefault();
handleSaveOption();
}}
>
<div>
<label className="text-[13px] font-semibold">
Name<span className="text-[red] text-[13px]"> *</span>
</label>
<input
required
defaultValue={dropdownName}
value={dropdownName}
onChange={(e) => setDropdownName(e.target.value)}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
<label className="text-[13px] font-semibold mt-[5px]">
Options
</label>
<div className="flex flex-col">
{dropdownOptionList.map((option, index) => (
<div
key={index}
className="flex flex-row mb-[5px] items-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);
}
}}
className="op-checkbox focus:outline-none hover:border-base-content mr-[5px]"
/>
)}
<input
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
type="text"
value={option}
onChange={(e) => handleInputChange(index, e.target.value)}
/>
<i
className="fa-solid fa-rectangle-xmark text-[25px] ml-[10px] text-error"
onClick={() => handleDeleteInput(index)}
></i>
</div>
))}
<i
onClick={handleAddInput}
className="fa-solid fa-square-plus text-[25px] ml-[10px] text-primary"
></i>
<div>
{props.type === "checkbox" && !props.isSignYourself && (
<>
<label
className={`${
!props.isSubscribe ? "text-[gray]" : ""
} text-[13px] font-semibold`}
>
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
? ""
: "pointer-events-none bg-opacity-50"
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
/>
<label
className={`${
!props.isSubscribe ? "text-[gray]" : ""
} text-[13px] font-semibold`}
>
Maximum check
</label>
<input
required
defaultValue={0}
value={maxCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMaxCount(count);
}}
className={`${
props.isSubscribe || !isEnableSubscription
? ""
: "pointer-events-none bg-opacity-50"
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
/>
</>
)}
</div>
</div>
{["dropdown", radioButtonWidget].includes(props.type) && (
<>
<label className="text-[13px] font-semibold mt-[5px]">
Default value
</label>
<select
onChange={(e) => setDefaultValue(e.target.value)}
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
name="defaultvalue"
value={defaultValue}
placeholder="select default value"
>
<option value="" disabled hidden className="text-[13px]">
Select...
</option>
{dropdownOptionList.map((data, ind) => {
return (
<option className="text-[13px]" key={ind} value={data}>
{data}
</option>
);
})}
</select>
</>
)}
{props.type !== "checkbox" && props.type !== radioButtonWidget && (
<>
<div className="flex flex-row gap-[10px] mt-[0.5rem]">
{statusArr.map((data, ind) => {
return (
<div
key={ind}
className="flex flex-row gap-[5px] items-center"
>
<input
className="op-radio op-radio-xs my-1"
type="radio"
name="status"
onChange={() => setStatus(data.toLowerCase())}
checked={status.toLowerCase() === data.toLowerCase()}
/>
<div className="text-[13px] font-500">{data}</div>
</div>
);
})}
</div>
</>
)}
{["checkbox", radioButtonWidget].includes(props.type) && (
<div className="flex flex-row gap-5 my-2 items-center text-center">
{!props.isSignYourself && (
<div className="flex items-center">
<input
id="isreadonly"
type="checkbox"
checked={isReadOnly}
className="op-checkbox op-checkbox-sm"
onChange={(e) => setIsReadOnly(e.target.checked)}
/>
<label className="ml-1 mb-0" htmlFor="isreadonly">
Is read only
</label>
</div>
)}
<div className="flex items-center">
<input
id="ishidelabel"
type="checkbox"
checked={isHideLabel}
className="op-checkbox op-checkbox-sm"
onChange={(e) => setIsHideLabel(e.target.checked)}
/>
<label className="ml-1 mb-0" 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}
type="submit"
className="op-btn op-btn-primary"
>
Save
</button>
{props.currWidgetsDetails?.options?.values?.length > 0 && (
<button
type="submit"
className="op-btn op-btn-ghost ml-1"
onClick={() => {
props.handleClose && props.handleClose();
resetState();
}}
>
Cancel
</button>
)}
</form>
</div>
</ModalUi>
);
}
export default DropdownWidgetOption;
@@ -1,197 +0,0 @@
import React, { useState, useEffect } from "react";
import { checkIsSubscribed, getFileName } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const";
// 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",
AutomaticReminders: template?.AutomaticReminders || false,
RemindOnceInEvery: template?.RemindOnceInEvery || 5
});
const [isSubscribe, setIsSubscribe] = useState(false);
useEffect(() => {
fetchSubscription();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const fetchSubscription = async () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
}
};
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 AutoReminder = formData?.AutomaticReminders || false;
let reminderDate = {};
if (AutoReminder) {
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
const ReminderDate = new Date(template?.createdAt);
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
reminderDate = { NextReminderDate: ReminderDate };
}
const data = { ...formData, SendinOrder: isChecked, ...reminderDate };
onSuccess(data);
};
const handleAutoReminder = () => {
setFormData((prev) => ({
...prev,
AutomaticReminders: !formData.AutomaticReminders
}));
};
return (
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
<div className="text-base-content">
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name" className="text-[13px]">
File
</label>
<div className="op-input op-input-bordered op-input-sm focus:outline-none py-2 font-semibold w-full text-xs">
{getFileName(template.URL)}
</div>
</div>
<div className="form-section">
<label htmlFor="name" className="text-[13px]">
Name
<span className="text-[13px] text-[red]"> *</span>
</label>
<input
type="text"
name="Name"
value={formData.Name}
onChange={(e) => handleStrInput(e)}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="form-section">
<label htmlFor="Note" className="text-[13px]">
Note
</label>
<input
type="text"
name="Note"
id="Note"
value={formData.Note}
onChange={(e) => handleStrInput(e)}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="form-section">
<label htmlFor="Description" className="text-[13px]">
Description
</label>
<input
type="text"
name="Description"
id="Description"
value={formData.Description}
onChange={(e) => handleStrInput(e)}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="form-section">
<label className="text-[13px]">Send In Order</label>
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
<input
type="radio"
value={"true"}
className="op-radio op-radio-xs"
name="SendinOrder"
checked={formData.SendinOrder === "true"}
onChange={handleStrInput}
/>
<div className="text-[12px]">Yes</div>
</div>
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
<input
type="radio"
value={"false"}
name="SendinOrder"
className="op-radio op-radio-xs"
checked={formData.SendinOrder === "false"}
onChange={handleStrInput}
/>
<div className="text-[12px]">No</div>
</div>
</div>
<div className="text-xs mt-2">
{!isEnableSubscription && (
<PremiumAlertHeader
message={
"Disable Auto reminder is free in beta, this feature will incur a fee later."
}
/>
)}
<span
className={
isSubscribe || !isEnableSubscription
? "font-semibold"
: "font-semibold text-gray-300"
}
>
Auto reminder{" "}
{!isSubscribe && isEnableSubscription && <Upgrade />}
</span>
<label
className={`${
isSubscribe || !isEnableSubscription
? "cursor-pointer "
: "pointer-events-none opacity-50"
} relative block items-center mb-0`}
>
<input
checked={formData.AutomaticReminders}
onChange={handleAutoReminder}
type="checkbox"
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white mt-2"
/>
</label>
</div>
{isSubscribe && formData?.AutomaticReminders === true && (
<div className="text-xs mt-2">
<label className="block">
Remind once in every (Days)
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
type="number"
value={formData.RemindOnceInEvery}
name="RemindOnceInEvery"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
onChange={handleStrInput}
required
/>
</div>
)}
<div className="mt-[1rem] flex justify-start">
<button type="submit" className="op-btn op-btn-primary">
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,42 +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 (
<form className="flex flex-col text-base-content text-lg font-normal">
<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="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full 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>
</form>
);
}
@@ -1,318 +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 "../../primitives/Loader";
function EmailComponent({
isEmail,
pdfUrl,
setIsEmail,
setSuccessEmail,
pdfName,
sender,
setIsAlert,
extUserId,
activeMailAdapter
}) {
const [emailList, setEmailList] = useState([]);
const [emailValue, setEmailValue] = useState();
const [isLoading, setIsLoading] = useState(false);
const [isEmailCelebration, setIsEmailCelebration] = 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 });
setIsEmailCelebration(true);
setTimeout(() => {
setIsEmailCelebration(false);
}, 3000);
} 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 className="absolute w-full h-full flex flex-col justify-center items-center z-[20] bg-[#e6f2f2] bg-opacity-80">
<Loader />
<span className="text-[12px] font-bold">
This might take some time
</span>
</div>
)}
<div style={{ background: "#32a3ac" }} className="modalheader">
<span className="text-white">Successfully signed!</span>
<div className="flex flex-row">
<div></div>
{!isAndroid && (
<button
onClick={handleToPrint}
className="op-btn op-btn-neutral op-btn-sm text-[15px]"
>
<i className="fa fa-print" aria-hidden="true"></i>
Print
</button>
)}
<button
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
onClick={() => handleDownloadPdf()}
>
<i className="fa fa-download" aria-hidden="true"></i>
Download
</button>
</div>
</div>
<div className="h-full p-[20px]">
{isEmailCelebration && (
<div className="absolute w-[100%] flex justify-center items-center">
<img alt="celeb" width={300} height={250} src={celebration} />
</div>
)}
<p className="font-medium text-[15px] mb-[5px] text-base-content align-baseline">
Recipients added here will get a copy of the signed document.
</p>
{emailList.length > 0 ? (
<>
<div className="addEmail">
<div className="flex flex-row flex-wrap">
{emailList.map((data, ind) => {
return (
<div
className="flex flex-row items-center bg-[#47a3ad] m-[4px] rounded-md py-[5px] px-[10px]"
key={ind}
>
<span className="text-white text-[13px]">
{data}
</span>
<span
className="text-white text-[13px] font-semibold ml-[7px] cursor-pointer"
onClick={() => removeChip(ind)}
>
<i className="fa-solid fa-xmark"></i>
</span>
</div>
);
})}
</div>
{emailList.length <= 9 && (
<input
type="text"
value={emailValue}
className="p-[10px] pb-[20px] rounded w-full text-[15px] bg-white outline-none"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
onBlur={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
required
/>
)}
</div>
</>
) : (
<div>
<input
type="text"
value={emailValue}
className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-white border-2 border-[#47a3ad]"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
placeholder="Add the email addresses"
onBlur={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
required
/>
</div>
)}
<button
className={`${
emailValue ? "cursor-pointer" : "cursor-default"
} op-btn op-btn-primary op-btn-sm m-2 shadow-md`}
onClick={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
>
<i className="fa fa-plus" aria-hidden="true"></i>
</button>
<div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded">
<span className="font-bold">Note: </span>
<span className="text-[15px]">
You can only send to ten recipients at a time.
</span>
</div>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
type="button"
className="op-btn op-btn-primary"
onClick={() => emailList.length > 0 && sendEmail()}
>
Send
</button>
<button
type="button"
className="op-btn op-btn-ghost ml-2"
onClick={() => {
setIsEmail(false);
setEmailValue("");
setEmailList([]);
}}
>
Close
</button>
</div>
</div>
</div>
)}
</div>
);
}
export default EmailComponent;
@@ -1,488 +0,0 @@
import React, { useState } from "react";
import PrevNext from "./PrevNext";
import {
handleDownloadCertificate,
handleDownloadPdf,
handleToPrint
} from "../../constant/Utils";
import "../../styles/signature.css";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { useNavigate } from "react-router-dom";
import ModalUi from "../../primitives/ModalUi";
import Loader from "../../primitives/Loader";
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 [isDownloading, setIsDownloading] = useState("");
const isGuestSigner = localStorage.getItem("isGuestSigner");
//function for show decline alert
const handleDeclinePdfAlert = async () => {
const currentDecline = {
currnt: "Sure",
isDeclined: true
};
setIsDecline(currentDecline);
};
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="flex justify-between items-center py-[5px] px-[10px] ">
<div onClick={() => navigate(-1)}>
<i
className="fa fa-arrow-left text-base-content"
aria-hidden="true"
></i>
</div>
<PrevNext
pageNumber={pageNumber}
allPages={allPages}
changePage={changePage}
/>
{pdfUrl && alreadySign ? (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div className="op-link op-link-primary no-underline text-[16px] font-semibold pr-[3px] pl-[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(pdfDetails, pdfUrl, setIsDownloading)
}
>
<div className="flex flex-row">
<i
className="fa fa-arrow-down mr-[2px]"
aria-hidden="true"
></i>
Download
</div>
</DropdownMenu.Item>
{isCompleted && (
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() =>
handleDownloadCertificate(
pdfDetails,
setIsDownloading
)
}
>
<div className="border-none bg-[#fff]">
<i
className="fa-solid fa-award mr-[2px]"
aria-hidden="true"
></i>
Certificate
</div>
</DropdownMenu.Item>
)}
{isSignYourself && (
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => setIsEmail(true)}
>
<div className="flex flex-row">
<i
className="fa fa-envelope mr-[2px]"
aria-hidden="true"
></i>
Mail
</div>
</DropdownMenu.Item>
)}
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading)
}
>
<div className="flex flex-row">
<i
className="fa fa-print mr-[2px]"
aria-hidden="true"
></i>
Print
</div>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
) : (
<div className="flex justify-around items-center">
{/* 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 && (
<div className="flex items-center" data-tut="reactourFifth">
{decline && (
<div
onClick={() => handleDeclinePdfAlert()}
className="text-[red] border-none font-[650] text-[14px] mr-2"
>
Decline
</div>
)}
{isPlaceholder ? (
<div
onClick={() => {
if (!isMailSend) {
alertSendEmail();
}
}}
className={`${
isMailSend ? "" : "op-link-primary"
} op-link no-underline font-[650] text-[14px]`}
data-tut={dataTut4}
>
{completeBtnTitle ? completeBtnTitle : "Send"}
</div>
) : (
<div
data-tut="reactourThird"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
} else if (isPdfRequestFiles) {
embedWidgetsData();
}
}}
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
>
Finish
</div>
)}
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div className="font-[650] text-[18px] px-2 ml-[4px] op-link op-link-info no-underline">
<i
className="fa fa-ellipsis-v"
aria-hidden="true"
></i>
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="bg-white shadow-md rounded px-2 py-1"
sideOffset={5}
>
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() =>
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
>
<i
className="fa fa-arrow-down mr-[5px]"
aria-hidden="true"
></i>
<span className="font-[500]">Download</span>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
</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 className="text-[13px] text-[#f5405e]">
Add {signersdata.length - filterPrefill.length}{" "}
recipients signature
</span>
) : (
<span className="text-[13px] text-[#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="op-btn op-btn-ghost op-btn-sm mr-[5px]"
>
Back
</button>
<button
disabled={isMailSend && true}
data-tut="reactourFour"
className="op-btn op-btn-primary op-btn-sm mr-[5px]"
onClick={() => alertSendEmail()}
>
{completeBtnTitle
? completeBtnTitle
: isMailSend
? "Sent"
: "Send"}
</button>
</div>
</>
) : isPdfRequestFiles ? (
alreadySign ? (
<div className="flex flex-row">
{isCompleted && (
<button
type="button"
onClick={() =>
handleDownloadCertificate(pdfDetails, setIsDownloading)
}
className="op-btn op-btn-secondary op-btn-sm mr-[5px] shadow"
>
<i
className="fa-solid fa-award py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block">Certificate</span>
</button>
)}
<button
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
type="button"
className="op-btn op-btn-neutral op-btn-sm mr-[5px] shadow"
>
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block">Print</span>
</button>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mr-[5px] shadow"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
>
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block">Download</span>
</button>
</div>
) : (
<div className="flex" data-tut="reactourFifth">
<button
onClick={() => navigate(-1)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
>
Back
</button>
{currentSigner && (
<>
<button
className="op-btn op-btn-secondary op-btn-sm mr-[5px] shadow"
onClick={() => handleDeclinePdfAlert()}
>
Decline
</button>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mr-[5px] shadow"
onClick={() => embedWidgetsData()}
>
Finish
</button>
<div className="op-dropdown op-dropdown-end">
<div
tabIndex={0}
role="button"
className="op-btn op-btn-info op-btn-sm shadow"
>
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
</div>
<ul
tabIndex={0}
className="op-dropdown-content z-[1] op-menu op-menu-sm p-1 shadow bg-base-100 rounded-box mt-1"
>
<li
onClick={() =>
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
>
<span className="font-semibold text-[12px]">
<i
className="fa fa-arrow-down"
aria-hidden="true"
></i>{" "}
Download
</span>
</li>
</ul>
</div>
</>
)}
</div>
)
) : isCompleted ? (
<div className="flex flex-row">
{isCompleted && (
<button
type="button"
onClick={() =>
handleDownloadCertificate(pdfDetails, setIsDownloading)
}
className="op-btn op-btn-secondary op-btn-sm font-medium text-[13px] mr-[5px] shadow"
>
<i className="fa-solid fa-award" aria-hidden="true"></i>
<span className="hidden lg:block">Certificate</span>
</button>
)}
<button
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
type="button"
className="op-btn op-btn-neutral op-btn-sm font-medium text-[13px] mr-[5px] shadow"
>
<i className="fa fa-print" aria-hidden="true"></i>
<span className="hidden lg:block">Print</span>
</button>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm font-medium text-[13px] mr-[5px] shadow"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
>
<i className="fa fa-download" aria-hidden="true"></i>
<span className="hidden lg:block">Download</span>
</button>
<button
type="button"
className="op-btn op-btn-info op-btn-sm font-medium text-[13px] mr-[5px] shadow"
onClick={() => setIsEmail(true)}
>
<i className="fa fa-envelope" aria-hidden="true"></i>
<span className="hidden lg:block">Mail</span>
</button>
</div>
) : (
<div className="flex">
<button
onClick={() => navigate(-1)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
>
Back
</button>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mr-[5px]"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
}
}}
>
Finish
</button>
</div>
)}
</div>
)}
{isDownloading === "pdf" && (
<div className="fixed z-[200] inset-0 flex justify-center items-center bg-black bg-opacity-30">
<Loader />
</div>
)}
<ModalUi
isOpen={isDownloading === "certificate"}
title={
isDownloading === "certificate"
? "Generating certificate"
: "PDF Download"
}
handleClose={() => setIsDownloading("")}
>
<div className="p-3 md:p-5 text-[13px] md:text-base text-center text-base-content">
{isDownloading === "certificate"}{" "}
<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,240 +0,0 @@
import React, { useState } from "react";
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 className="h-full p-[20px] text-base-content">
{copyType.map((data, key) => {
return (
<div key={key} className="flex flex-col">
<label className="text-[16px] font-medium">
<input
className="mr-[8px] op-radio op-radio-xs"
type="radio"
value={data}
onChange={() => setSelectCopyType(data)}
checked={selectCopyType === data}
/>
{data}
</label>
</div>
);
})}
<div className="flex bg-[#9f9f9f] w-full my-[15px]"></div>
<button
onClick={() => {
handleApplyCopy();
handleUniqueId();
}}
type="button"
disabled={!selectCopyType}
className="op-btn op-btn-primary"
>
Apply
</button>
<button
type="button"
className="op-btn op-btn-ghost"
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
className={`${
ind === 0 ? "mt-0" : "mt-[5px]"
} flex justify-center op-checkbox op-checkbox-xs rounded-[0.350rem]`}
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", color: "initial" }}
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", color: "initial" }}
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", color: "initial" }}
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", color: "initial" }}
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", color: "initial" }}
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", color: "initial" }}
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
className={`${ind === 0 ? "mt-0" : "mt-[5px]"} ${
props?.pos?.Width
? `w-[${props?.pos?.Width}px] h-[${props?.pos?.Width}px]`
: "w-[15px] h-[15px]"
} flex justify-center op-radio`}
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",
color: "initial"
}}
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,42 +0,0 @@
import React from "react";
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 className="flex items-center">
<button
className="op-btn op-btn-secondary op-btn-xs md:op-btn-sm font-semibold text-xs"
disabled={pageNumber <= 1}
onClick={previousPage}
>
<span className="block lg:hidden">
<i className="fa fa-backward" aria-hidden="true"></i>
</span>
<span className="lg:block hidden">Prev</span>
</button>
<span className="text-xs text-base-content font-medium mx-2">
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<button
className="op-btn op-btn-secondary op-btn-xs md:op-btn-sm font-semibold text-xs"
disabled={pageNumber >= allPages}
onClick={nextPage}
>
<span className="block lg:hidden">
<i className="fa fa-forward" aria-hidden="true"></i>
</span>
<span className="lg:block hidden">Next</span>
</button>
</div>
);
}
export default PrevNext;
@@ -1,265 +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 cursor =
"cursor-[url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAASElEQVR4nGNgwAMkJSUbpKSkOvCpIaT5PxSTbogUQjMYMwxeIIXmVFIxA8UGDDyQGg0DnIDi6JKUlCxHMqCeZAOghjSAMD5FAKfeaURdUFxCAAAAAElFTkSuQmCC'),_pointer]";
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 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
? "pointer-events-none bg-opacity-80"
: `text-[12px] font-bold ${cursor}`
: props.isMailSend && "pointer-events-none bg-opacity-80"
} flex flex-row rounded-xl px-2 py-[10px] mt-1 mx-1 items-center last:mb-0.5`}
style={{
background:
(!isMobile && isHover === ind) || props.isSelectListId === ind
? obj?.blockColor
? obj?.blockColor
: color[ind % color.length]
: "transparent"
}}
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 className="flex flex-row items-center w-full">
<div
className="flex w-[30px] h-[30px] rounded-full items-center justify-center mr-2"
style={{
background: obj?.blockColor
? darkenColor(obj?.blockColor, 0.4)
: nameColor[ind % nameColor.length]
}}
>
<span className="text-white uppercase font-bold text-center text-[12px]">
{isWidgetExist(obj.Id) ? (
<i className="fa-solid fa-check"></i>
) : (
<>
{obj.Name
? getFirstLetter(obj.Name)
: getFirstLetter(obj.Role)}
</>
)}
</span>
</div>
<div
className={`${
obj?.Name ? "flex-col" : "flex-row"
} flex items-center`}
>
{obj.Name ? (
<span
className={`${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
>
{obj.Name}
</span>
) : (
<span
className={`${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer`}
onClick={() => {
setIsEdit({ [obj.Id]: true });
props.setRoleName(obj.Role);
}}
>
{isEdit?.[obj.Id] && props.handleRoleChange ? (
<input
ref={inputRef}
className="bg-transparent p-[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 className="p-[3px]">{obj.Role}</span>
)}
</span>
)}
{obj.Name && (
<span
className={` ${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[10px] font-medium w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
>
{obj?.Role || obj?.Email}
</span>
)}
</div>
</div>
{isMobile && props.sendInOrder && (
<div className="flex flex-row items-center gap-[5px]">
<div
onClick={(e) => {
if (ind !== 0) {
e.stopPropagation();
handleChangeSequence(e, ind, "up");
}
}}
className={ind === 0 ? "text-[gray]" : "text-black"}
>
</div>
<div
onClick={(e) => {
if (ind !== props.signersdata.length - 1) {
e.stopPropagation();
handleChangeSequence(e, ind, null, "down");
}
}}
className={
ind === props.signersdata.length - 1
? "text-[gray]"
: "text-black"
}
>
</div>
</div>
)}
{props.handleDeleteUser && (
<div
onClick={(e) => {
e.stopPropagation();
props.handleDeleteUser(obj.Id);
}}
className={`${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} cursor-pointer ml-[5px]`}
>
<i className="fa-regular fa-trash-can"></i>
</div>
)}
<hr />
</div>
);
})}
</>
);
};
export default RecipientList;
@@ -1,112 +0,0 @@
import React, { useState } from "react";
import RSC from "react-scrollbars-custom";
import { Document, Page } from "react-pdf";
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 className="absolute z-20 top-[1px] -right-[13px] -translate-x-1/2 -translate-y-1/2">
<i
style={{ color: bookmarkColor || "red" }}
className="fa-solid fa-bookmark"
></i>
</div>
)
);
};
return (
<div>
<div className=" hidden md:flex flex-row bg-base-100 h-full">
<div className="w-[140px]">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
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}
className={`${
pageNumber - 1 === index
? "border-[red]"
: "border-[#878787]"
} border-2 w-[100px] m-[10px] flex justify-center items-center relative`}
onClick={() => {
setPageNumber(index + 1);
if (setSignBtnPosition) {
setSignBtnPosition([]);
}
}}
>
{signerPos && addSignatureBookmark(index)}
<div className="relative z-[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,675 +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) => {
let checkSign = [];
//condition to handle quick send flow and using normal request sign flow
checkSign = signedSigners.filter(
(sign) => sign?.Id === data?.Id || sign?.objectId === data?.signerObjId
);
if (data.signerObjId === signerObjectId) {
setCurrentSigner(true);
}
const handleAllUserName = (Id, Role, type) => {
return (
<React.Fragment>
<div className="text-black text-[8px] font-medium">
{
pdfDetails[0].Signers?.find(
(signer) => signer.objectId === data.signerObjId
)?.Name
}
</div>
{type && <div className="text-[11px] font-bold">{type}</div>}
{Role && (
<div className="text-black text-[8px] font-medium">
{`(${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 className="text-black text-[8px] font-medium">
{checkSign?.Name}
</div>
{type && <div className="text-[11px] font-bold">{type}</div>}
{Role && (
<div className="text-black text-[8px] font-medium">
{`(${Role})`}
</div>
)}
</>
);
} else {
return (
<>
{type && <div className="text-[11px] font-bold">{type}</div>}
<div className="text-black text-[8px] font-medium">{Role}</div>
</>
);
}
} else {
return (
<>
{type && <div className="text-[11px] font-bold">{type}</div>}
<div className="text-black text-[8px] font-medium">{Role}</div>
</>
);
}
};
return (
<>
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
{isMobile && scale ? (
<div
data-tut="reactourForth"
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 className="flex items-center justify-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
data-tut="reactourForth"
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;
-599
View File
@@ -1,599 +0,0 @@
import React, { useRef, useState, useEffect } from "react";
import SignatureCanvas from "react-signature-canvas";
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>
{(isTab === "draw" || isTab === "uploadImage") && (
<button
type="button"
className="op-btn op-btn-ghost mr-1 mt-[2px]"
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);
}}
type="button"
className={`${
isSignImg || image || isDefaultSign || textWidth
? ""
: "pointer-events-none"
} op-btn op-btn-primary shadow-lg`}
>
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 className="flex flex-row items-start">
{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="op-modal op-modal-open">
<div className="op-modal-box px-[13px] pt-2 pb-0">
<div className="flex justify-between text-base-content items-center">
<div className="text-[1.2rem]">
<div className="flex flex-row justify-between mt-[3px]">
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
{isStamp ? (
<span className="op-link-primary op-link">
{widgetType === "image"
? "Upload image"
: "Upload stamp image"}
</span>
) : (
<>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(false);
setIsTab("draw");
setImage();
}}
className={`${
isTab === "draw"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
Draw
</span>
</div>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(true);
setIsTab("uploadImage");
setSignatureType("");
}}
className={`${
isTab === "uploadImage"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
Upload Image
</span>
</div>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(false);
setIsTab("type");
setSignatureType("");
setImage();
}}
className={`${
isTab === "type"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
Type
</span>
</div>
{!isInitial && defaultSign ? (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
setImage();
}}
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
My Signature
</span>
</div>
) : (
isInitial &&
myInitial && (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
setImage();
}}
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
My Initials
</span>
</div>
)
)}
</>
)}
</div>
</div>
</div>
<div
className="text-[1.5rem] cursor-pointer"
onClick={() => {
setPenColor("blue");
setIsSignPad(false);
setIsInitial(false);
setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
setIsTab("draw");
setSignatureType("draw");
setSignValue("");
setIsStamp(false);
}}
>
&times;
</div>
</div>
<div className="p-[20px] h-full">
{isDefaultSign ? (
<>
<div className="flex justify-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"
className="w-full h-full object-contain bg-white"
draggable="false"
src={isInitial ? myInitial : defaultSign}
/>
</div>
</div>
<div className="flex justify-end">
<SaveBtn />
</div>
</>
) : isImageSelect || isStamp ? (
!image ? (
<div className="flex justify-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 className="flex justify-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"
className=" object-contain h-full w-full"
/>
</div>
</div>
<div className="flex justify-end">
<SaveBtn />
</div>
</>
)
) : isTab === "type" ? (
<div>
<div className="flex justify-between items-center">
<span className="signatureText">
{isInitial ? "Initials" : "Signature"}:
</span>
<input
maxLength={isInitial ? 3 : 30}
style={{ fontFamily: fontSelect, color: penColor }}
type="text"
className="ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
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 className="flex flex-row justify-between mt-[10px]">
<PenColorComponent convertToImg={convertToImg} />
<SaveBtn />
</div>
</div>
) : (
<>
<div className="flex justify-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 className="flex flex-row justify-between mt-[10px]">
<PenColorComponent />
<SaveBtn />
</div>
</>
)}
</div>
</div>
</div>
)}
</div>
);
}
export default SignPad;
@@ -1,31 +0,0 @@
import React from "react";
import "../../styles/signature.css";
function Signedby({ pdfDetails }) {
const getFirstLetter = (name) => {
const firstLetter = name.charAt(0);
return firstLetter;
};
return (
<div className="hidden md:block w-[180px] h-full bg-base-100">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
Signed By
</div>
<div className="mt-[2px] bg-base-100">
<div className="bg-[#93a3db] rounded-xl mx-1 flex flex-row items-center py-[10px]">
<div className="bg-[#576081] flex w-[30px] h-[30px] rounded-full justify-center items-center mx-1">
<span className="text-[12px] text-center font-bold text-white uppercase">
{getFirstLetter(pdfDetails.ExtUserPtr.Name)}
</span>
</div>
<div className="flex flex-col">
<span className="userName">{pdfDetails.ExtUserPtr.Name}</span>
<span className="useEmail">{pdfDetails.ExtUserPtr.Email}</span>
</div>
</div>
</div>
</div>
);
}
export default Signedby;
@@ -1,86 +0,0 @@
import React from "react";
import RecipientList from "./RecipientList";
import { Tooltip } from "react-tooltip";
function SignerListPlace(props) {
return (
<div>
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
<span>
{props.title ? props.title : "Recipients"}
<span className="text-xs mt-1 ml-0.5">
{props?.title === "Roles" && (
<>
<a data-tooltip-id="my-tooltip">
<sup>
<i className="fa-solid fa-question rounded-full border-[1.5px] border-base-content text-[11px] py-[1px] px-[3px]"></i>
</sup>
</a>
<Tooltip id="my-tooltip" className="z-[100]">
<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="overflow-auto hide-scrollbar max-h-[180px]">
<RecipientList {...props} />
</div>
<div className="mx-1">
{props.handleAddSigner ? (
<div
role="button"
data-tut="reactourAddbtn"
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
disabled={props?.isMailSend ? true : false}
onClick={() => props.handleAddSigner()}
>
<i className="fa-solid fa-plus"></i> Add role
</div>
) : (
<div
role="button"
data-tut="reactourAddbtn"
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
disabled={props?.isMailSend ? true : false}
onClick={() => props.setIsAddSigner(true)}
>
<i className="fa-solid fa-plus"></i> Add recipients
</div>
)}
</div>
</div>
);
}
export default SignerListPlace;
@@ -1,67 +0,0 @@
import React from "react";
import Loader from "../../primitives/Loader";
function VerifyEmail(props) {
return (
<dialog className="op-modal op-modal-open absolute z-[1999]">
<div className="md:w-[40%] w-[80%] op-modal-box p-0 overflow-y-auto hide-scrollbar text-sm">
<h3 className="font-bold text-lg pt-[15px] px-[20px] text-base-content">
OTP verification
</h3>
{props.isVerifyModal ? (
<form
onSubmit={(e) => {
props.setIsVerifyModal(false);
props.handleVerifyEmail(e);
}}
>
<div className="px-6 py-3 text-base-content">
<label className="mb-2">Enter OTP</label>
<input
required
type="tel"
pattern="[0-9]{4}"
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
placeholder="Enter OTP received over email"
value={props.otp}
onChange={(e) => props.setOtp(e.target.value)}
/>
</div>
<div className="px-6 my-3">
<button type="submit" className="op-btn op-btn-primary">
Verify
</button>
<button
className="op-btn op-btn-secondary ml-2"
onClick={(e) => props.handleResend(e)}
>
Resend
</button>
</div>
</form>
) : props.otpLoader ? (
<div className="h-[150px] flex items-center justify-center">
<Loader />
</div>
) : (
<div className="px-6 py-3 text-base-content">
<p className="mb-2 text-sm">Please verify your email !</p>
<div className="px-0 mt-3">
<button
className="op-btn op-btn-primary"
type="submit"
onClick={() => {
props.handleVerifyBtn();
}}
>
Send OTP
</button>
</div>
</div>
)}
</div>
</dialog>
);
}
export default VerifyEmail;
@@ -1,404 +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";
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" }}
>
<div className="mx-1">
{isSigners && (
<div
data-tut={dataTut}
className="py-[10px] flex justify-center items-center op-card"
style={{
background: blockColor
? blockColor
: isSelectListId
? color[isSelectListId % color.length]
: color[0]
}}
onClick={() => handleModal()}
>
<span className="text-[13px] font-bold flex items-center">
{title ? title : "Recipient"}
{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 className="ml-[6px] text-[16px]">
<i className="fa-solid fa-angle-down"></i>
</div>
</span>
</div>
)}
</div>
<div className="mx-1">
{handleAddSigner ? (
<div
data-tut="reactourAddbtn"
className="op-btn op-btn-accent w-full my-[2px]"
onClick={() => handleAddSigner()}
>
<i className="fa-solid fa-plus"></i>
<span>Add role</span>
</div>
) : (
setIsAddSigner && (
<div
data-tut="reactourAddbtn"
className="op-btn op-btn-accent w-full my-[2px]"
onClick={() => setIsAddSigner(true)}
>
<i className="fa-solid fa-plus"></i>
<span>Add recipients</span>
</div>
)
)}
</div>
<div
ref={scrollContainerRef}
className="bg-base-100 border-[2px] border-t-primary"
>
<div className="flex whitespace-nowrap overflow-x-scroll pt-[10px] pb-[5px] pr-[5px]">
<WidgetList
updateWidgets={updateWidgets}
handleDivClick={handleDivClick}
handleMouseLeave={handleMouseLeave}
signRef={signRef}
marginLeft={5}
addPositionOfSignature={addPositionOfSignature}
isMobile={isMobile}
/>
</div>
</div>
</div>
)
) : (
<div
data-tut={dataTut}
className={`${
isMailSend ? "bg-opacity-50 pointer-events-none" : ""
} hidden md:block w-[180px] h-full bg-base-100`}
>
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
<span>Fields</span>
</div>
<div className="flex flex-col items-center my-3">
<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 className="max-h-[600px] overflow-auto pb-1">
<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 className=" p-[20px] text-[15px] font-medium text-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} className="mb-[5px]">
<div
className="select-none"
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,278 +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";
import Tooltip from "../../primitives/Tooltip";
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] text-base-content`}
>
<div className="mb-[0.75rem] text-[13px]">
<label htmlFor="name">
Name
<span className="text-[red]"> *</span>
</label>
<input
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
required
/>
</div>
{(props.defaultdata?.type === textInputWidget ||
props.widgetName === textInputWidget) && (
<>
<div className="mb-[0.75rem]">
<div className="flex items-center gap-1">
<label
htmlFor="textvalidate"
className={`${
!props.isSubscribe && isEnableSubscription
? "bg-opacity-50 pointer-events-none"
: ""
} text-[13px]`}
>
Validation
</label>
<Tooltip
url={"https://www.w3schools.com/jsref/jsref_obj_regexp.asp"}
/>
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
</div>
<div
className={`${
!props.isSubscribe && isEnableSubscription
? "bg-opacity-50 pointer-events-none"
: ""
} flex flex-row gap-[10px] mb-[0.5rem]`}
>
<div className="w-full relative group">
<input
className="z-20 relative op-input op-input-bordered rounded-r-none op-input-sm focus:outline-none group-hover:border-base-content w-[87%] md:w-[92%] text-xs"
name="textvalidate"
placeholder="Enter custom regular expression"
value={formdata.textvalidate}
onChange={(e) => handleChange(e)}
// onBlur={() => handleBlurRegex()}
/>
<select
className="validationlist op-input op-input-bordered op-input-sm focus:outline-none group-hover:border-base-content w-full text-xs"
name="textvalidate"
value={formdata.textvalidate}
onChange={(e) => handleChange(e)}
onBlur={() => handleBlurRegex()}
>
<option
disabled={formdata?.textvalidate}
className="text-[13px]"
>
Select...
</option>
{inputOpt.map((data, ind) => {
return (
<option className="text-[13px]" key={ind} value={data}>
{data}
</option>
);
})}
</select>
</div>
</div>
</div>
<div className="mb-[0.75rem]">
<label htmlFor="name" className="text-[13px]">
Default value
</label>
<input
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
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 text-[15px]"
style={{ color: "#fab005" }}
></i>
invalid default value
</div>
)}
</div>
</>
)}
<div className="mb-[0.75rem]">
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
{statusArr.map((data, ind) => {
return (
<div key={ind} className="flex flex-row gap-[5px] items-center">
<input
className="mr-[2px] op-radio op-radio-xs"
type="radio"
name="status"
onChange={() =>
setFormdata({ ...formdata, status: data.toLowerCase() })
}
checked={
formdata.status.toLowerCase() === data.toLowerCase()
}
/>
<div className="text-[13px] font-medium">{data}</div>
</div>
);
})}
</div>
</div>
{(props.defaultdata?.type === textInputWidget ||
props?.widgetName === textInputWidget) && (
<div className="mb-[0.75rem]">
<label htmlFor="hint" className="text-[13px]">
Hint
</label>
<input
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="hint"
value={formdata.hint}
onChange={(e) => handleChange(e)}
/>
</div>
)}
<div className="h-[1px] w-full mb-[16px] bg-[#b7b3b3]"></div>
<button type="submit" className="op-btn op-btn-primary">
Save
</button>
</form>
</ModalUi>
);
};
export default WidgetNameModal;
@@ -1,164 +0,0 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "../../../primitives/Alert";
import Loader from "../../../primitives/Loader";
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");
FolderQuery.notEqualTo("IsArchive", true);
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
}
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");
exsitQuery.notEqualTo("IsArchive", true);
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">
<Loader />
</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,236 +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");
FolderQuery.notEqualTo("IsArchive", true);
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
}
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,306 +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");
FolderQuery.notEqualTo("IsArchive", true);
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
}
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-base-100 border-[1px] border-base-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,118 +0,0 @@
import React, { useState } from "react";
import Parse from "parse";
import "../../../styles/AddUser.css";
import AsyncSelect from "react-select/async";
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="h-full px-[20px] py-[10px] text-base-content">
<div className="w-full mx-auto p-[8px]">
<div className="mb-0">
<label className="text-[14px] font-bold">Choose from contacts</label>
<AsyncSelect
cacheOptions
defaultOptions
value={selected}
loadingMessage={() => "Loading..."}
noOptionsMessage={() => "Contact not found"}
loadOptions={loadOptions}
onChange={handleOptions}
unstyled
classNames={{
control: () =>
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
valueContainer: () =>
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
multiValue: () => "op-badge op-badge-primary h-full text-[11px]",
multiValueLabel: () => "mb-[2px]",
menu: () =>
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content",
menuList: () => "shadow-md rounded-lg overflow-hidden",
option: () =>
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
noOptionsMessage: () => "p-2 bg-base-200 rounded-lg m-1 p-2"
}}
/>
</div>
<p
className={`${
isError ? "text-[red]" : "text-transparent"
} text-[11px] ml-[6px] my-[2px]`}
>
Please select signer
</p>
<div>
<button className="op-btn op-btn-primary" onClick={() => handleAdd()}>
Submit
</button>
</div>
</div>
</div>
);
};
export default SelectSigners;
@@ -1,196 +0,0 @@
import React, { useState, useEffect } from "react";
import Select from "react-select";
import AddSigner from "../../AddSigner";
import Parse from "parse";
import Tooltip from "../../../primitives/Tooltip";
import { createPortal } from "react-dom";
function arrayMove(array, from, to) {
array = array.slice();
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
return array;
}
const AddSignerModal = ({ isOpen, children }) => {
if (!isOpen) {
return null;
}
return createPortal(
<div className="op-modal op-modal-open">
<div className="op-modal-box p-0 min-w-[90%] md:min-w-[500px] max-h-90 overflow-y-auto hide-scrollbar text-sm">
{children}
</div>
</div>,
document.body
);
};
/**
* 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) => {
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-30">
<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 className="flex gap-x-[5px]">
<div className="w-full">
<Select
onSortEnd={onSortEnd}
distance={4}
isMulti
options={state || []}
value={selected}
onChange={onChange}
closeMenuOnSelect={false}
required={props.required}
noOptionsMessage={() => "Contact not found"}
unstyled
classNames={{
control: () =>
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
valueContainer: () =>
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
multiValue: () => "op-badge op-badge-primary h-full text-[11px]",
multiValueLabel: () => "mb-[2px]",
menu: () =>
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content",
menuList: () => "shadow-md rounded-lg overflow-hidden",
option: () =>
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
noOptionsMessage: () => "p-2 bg-base-200 rounded-lg m-1 p-2"
}}
/>
</div>
<div
onClick={() => {
setIsModel(true);
openModal();
}}
className="cursor-pointer op-input op-input-bordered focus:outline-none hover:border-base-content max-h-[38px] min-w-[48px] flex justify-center items-center"
>
<i className="fas fa-plus"></i>
</div>
<AddSignerModal isOpen={modalIsOpen}>
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
Add Signer
</h3>
<button
onClick={handleModalCloseClick}
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2"
>
</button>
{isModal && (
<AddSigner
valueKey={"objectId"}
displayKey={"Name"}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</AddSignerModal>
</div>
</div>
);
};
export default SignersInput;
@@ -1,92 +0,0 @@
import React, { useState, useEffect, useRef } from "react";
import { findContact } from "../../../constant/Utils";
const SuggestionInput = (props) => {
const [inputValue, setInputValue] = useState(props?.value || "");
const [suggestions, setSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const ref = useRef(null);
useEffect(() => {
document.addEventListener("mousedown", Clickout);
return () => {
document.removeEventListener("mousedown", Clickout);
};
}, []);
const Clickout = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
setShowSuggestions(false);
}
};
// create debounce to avoid unnecessay api calls
useEffect(() => {
let timer;
if (inputValue) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
(async () => {
const res = await findContact(inputValue);
if (res?.length > 0) {
setSuggestions(res);
setShowSuggestions(true);
} else {
setSuggestions(res);
setShowSuggestions(false);
}
})();
}, 1000);
}
return () => clearTimeout(timer);
}, [inputValue]);
const handleInputChange = async (e) => {
const value = e.target.value;
setInputValue(value?.toLowerCase());
if (props.onChange) {
props.onChange(value);
}
if (value.trim() === "") {
setSuggestions([]);
setShowSuggestions(false);
return;
}
};
const handleSuggestionClick = (suggestion) => {
setInputValue(suggestion.Email);
setSuggestions([]);
setShowSuggestions(false);
if (props.onChange) {
props.onChange(suggestion);
}
};
return (
<div className="flex flex-col items-center relative text-base-content">
<input
type={props?.type || "text"}
value={inputValue}
onChange={handleInputChange}
placeholder="Enter Email..."
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs lowercase"
required={props.required}
/>
{showSuggestions && (
<ul
ref={ref}
className="absolute z-50 left-0 top-[2.55rem] w-full max-h-[100px] overflow-auto bg-base-200 border border-gray-300 rounded shadow-md"
>
{suggestions.map((suggestion, index) => (
<li
key={index}
className="py-2 px-2 w-full text-sm cursor-pointer hover:bg-base-300"
onClick={() => handleSuggestionClick(suggestion)}
>
{suggestion.Email}
</li>
))}
</ul>
)}
</div>
);
};
export default SuggestionInput;
+16 -24
View File
@@ -2,28 +2,20 @@ import React from "react";
import { NavLink } from "react-router-dom";
const Menu = ({ item, isOpen, closeSidebar }) => {
return (
<li key={item.title} role="none" className="my-0.5">
<NavLink
to={
item.pageType
? `/${item.pageType}/${item.objectId}`
: `/${item.objectId}`
}
className={({ isActive }) =>
`${
isActive ? " bg-base-300 text-base-content" : ""
} flex items-center text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
}
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;
+21 -49
View File
@@ -1,15 +1,14 @@
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";
const Sidebar = ({ isOpen, closeSidebar }) => {
const [menuList, setmenuList] = useState([]);
const [submenuOpen, setSubmenuOpen] = useState(false);
let username = localStorage.getItem("username");
const image = localStorage.getItem("profileImg") || dp;
const tenantname = localStorage.getItem("TenantName");
@@ -21,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;
@@ -32,70 +37,37 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
} catch (e) {
console.error("Problem", e);
}
};
const toggleSubmenu = (title) => {
setSubmenuOpen({ [title]: !submenuOpen[title] });
};
const handleMenuItem = () => {
closeSidebar();
setSubmenuOpen({});
};
return (
<aside
className={`absolute md:relative bg-base-100 h-screen overflow-y-auto transition-all z-[100] hide-scrollbar
${isOpen ? "w-full md:w-[300px]" : "w-0"}`}
className={`bg-white text-[#444] h-screen w-full md:w-[300px] overflow-y-auto transform transition-transform z-40 md:translate-x-0
${isOpen ? "translate-x-0 block" : "-translate-x-full hidden"}`}
>
<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 text-base-content">{username}</p>
<p
className={`text-[12px] text-base-content ${
tenantname ? "mt-2" : ""
}`}
>
<p className="text-[14px] font-bold">{username}</p>
<p className={`text-[12px] ${tenantname ? "mt-2" : ""}`}>
{tenantname}
</p>
</div>
</div>
<nav
className="op-menu op-menu-sm"
aria-label="OpenSign Sidebar Navigation"
>
<ul
className="text-sm"
role="menubar"
aria-label="OpenSign Sidebar Navigation"
>
{menuList.map((item) =>
<nav aria-label="OpenSign Sidebar Navigation">
<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={handleMenuItem}
/>
<Menu key={item.title} item={item} isOpen={isOpen} closeSidebar={closeSidebar} />
) : (
<Submenu
key={item.title}
item={item}
closeSidebar={closeSidebar}
toggleSubmenu={toggleSubmenu}
submenuOpen={submenuOpen}
/>
<Submenu key={item.title} item={item} closeSidebar={closeSidebar} />
)
)}
))}
</ul>
</nav>
<footer className="mt-4 flex justify-center items-center text-[25px] text-base-content gap-3">
<footer className="mt-4 flex justify-center items-center text-[25px] text-black gap-3">
<SocialMedia />
</footer>
</aside>
+18 -26
View File
@@ -1,13 +1,19 @@
import React from "react";
import React, { useState } from "react";
import { NavLink } from "react-router-dom";
const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
const Submenu = ({ item, closeSidebar }) => {
const [submenuOpen, setSubmenuOpen] = useState(false);
const { title, icon, children } = item;
const toggleSubmenu = () => {
setSubmenuOpen(!submenuOpen);
};
return (
<li role="none" className="my-0.5">
<li role="none">
<button
onClick={() => toggleSubmenu(item.title)}
className="flex items-center text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none "
onClick={toggleSubmenu}
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}`}
@@ -16,38 +22,24 @@ const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
<div className="flex justify-between items-center w-full">
<span className="ml-3 lg:ml-4">{title}</span>
<i
className={`${
submenuOpen[item.title]
? "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>
</button>
{submenuOpen[item.title] && (
{submenuOpen && (
<ul id={`submenu-${title}`} role="menu" aria-label={`${title} submenu`}>
{children.map((childItem) => (
<li key={childItem.title} role="none" className="my-0.5">
<li key={childItem.title} role="none">
<NavLink
to={
childItem.pageType
? `/${childItem.pageType}/${childItem.objectId}`
: `/${childItem.objectId}`
}
className={({ isActive }) =>
`${
isActive ? "bg-base-300 text-base-content" : ""
} flex items-center text-left pl-6 md:pl-8 py-2 text-sm cursor-pointer text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
}
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}`
-6
View File
@@ -1,6 +0,0 @@
export const contactCls = "contracts_Contactbook";
export const templateCls = "contracts_Template";
export const documentCls = "contracts_Document";
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);
}
};
+3 -13
View File
@@ -2,16 +2,6 @@
@tailwind components;
@tailwind utilities;
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 */
}
body{
background-color: white;
}
+1 -75
View File
@@ -1,75 +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";
import './polyfills'
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;
}
};
+64 -271
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 templateReport = ["Sr.No", "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,24 +10,14 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "1231",
hoverLabel: "Edit",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-pen",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "2142",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
btnLabel: "sign",
btnColor: "#4bd396",
textColor: "white",
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":
@@ -38,17 +26,14 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "4536",
btnLabel: "sign",
hoverLabel: "Sign",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-signature",
redirectUrl: "pdfRequestFiles",
action: "redirect"
btnColor: "#3ac9d6",
textColor: "white",
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":
@@ -57,58 +42,14 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "8901",
hoverLabel: "Share",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-share",
redirectUrl: "",
action: "share"
},
{
btnId: "1588",
hoverLabel: "View",
btnColor: "op-btn-secondary",
btnLabel: "View",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles",
action: "redirect"
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
textColor: "black",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "1630",
btnLabel: "Resend",
hoverLabel: "Resend",
btnIcon: "fa-solid fa-envelope",
redirectUrl: "",
action: "resend"
},
{
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":
@@ -117,24 +58,14 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "1378",
hoverLabel: "Edit",
btnColor: "op-btn-primary",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1278",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
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":
@@ -143,24 +74,14 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "1458",
hoverLabel: "View",
btnColor: "op-btn-primary",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1358",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
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":
@@ -169,80 +90,28 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "1898",
hoverLabel: "View",
btnColor: "op-btn-primary",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1998",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
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",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-share",
redirectUrl: "",
action: "share"
},
{
btnId: "1999",
hoverLabel: "View",
btnColor: "op-btn-secondary",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles",
action: "redirect"
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
textColor: "black",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "1631",
btnLabel: "Resend",
hoverLabel: "Resend",
btnIcon: "fa-solid fa-envelope",
redirectUrl: "",
action: "resend"
},
{
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"
}
]
};
@@ -250,16 +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: "op-btn-primary",
btnIcon: "fa-solid fa-signature",
redirectUrl: "pdfRequestFiles",
action: "redirect"
btnLabel: "Sign",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
}
]
};
@@ -267,23 +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",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-pen",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "2004",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
btnLabel: "sign",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
}
]
};
@@ -294,79 +154,12 @@ export default function reportJson(id) {
heading: contactbook,
actions: [
{
btnId: "2204",
hoverLabel: "Delete",
btnColor: "op-btn-outline op-btn-error",
btnIcon: "fa-solid fa-trash",
action: "delete"
btnLabel: "",
btnColor: "#f55a42",
textColor: "white",
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: templateReport,
actions: [
{
btnId: "2234",
btnLabel: "Use",
hoverLabel: "Use",
btnColor: "op-btn-primary",
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: "1631",
btnLabel: "Quick send",
hoverLabel: "Quick send",
btnColor: "op-btn-secondary",
btnIcon: "fa-solid fa-envelope",
redirectUrl: "",
action: "bulksend",
selector: "tourbulksend",
message:
"To quickly create and send multiple documents using an existing template, click the 'Quick Send' button."
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
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;
-90
View File
@@ -1,90 +0,0 @@
const dashboardJson = [
{
id: "35KBoSgoAK",
columns: [
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "Card",
icon: "fas fa-signature",
bgColor: "bg-[#f0058e]",
label: "Need your Signature",
description: null,
data: {
id: "e04ee9dc-932d-6b80-0202-703fd154eb74",
queryType: "",
class: "contracts_Document",
query:
'where={"$and":[{"AuditTrail.UserPtr":{"$ne":{"__type":"Pointer","className": "contracts_Users","objectId":"#objectId#"}}},{"AuditTrail.Activity":{"$ne":"Signed"}}],"Signers":{"$in":[{"__type":"Pointer","className":"contracts_Users","objectId":"#objectId#"}]},"IsDeclined":{"$ne":true},"IsCompleted":{"$ne":true},"Type":{"$ne":"Folder"},"Placeholders":{"$ne":null},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
key: "count",
Redirect_type: "Report",
Redirect_id: "4Hhwbp482K",
tourSection: "tourcard1",
tourMessage:
"Clicking on this card will take you to the list of documents awaiting your review."
}
}
},
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "Card",
icon: "fas fa-sign-out-alt",
bgColor: "bg-[#2ed8b6]",
label: "Out for signatures",
description: null,
data: {
id: "c1935cc0-28d4-3d73-b72a-8c1e3d18a17f",
queryType: "",
class: "contracts_Document",
query:
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*ne":null},"IsCompleted":{"#*ne":true},"IsDeclined":{"#*ne":true},"IsArchive":{"#*ne":true},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
key: "count",
Redirect_type: "Report",
Redirect_id: "1MwEuxLEkF",
tourSection: "tourcard2",
tourMessage:
"Clicking on this card will take you to a list of documents awaiting signature."
}
}
},
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "report",
reportId: "5Go51Q7T8r",
data: {
tourSection: "tourreport1",
tourMessage:
"This is a list of documents that are waiting for your signature."
}
}
},
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "report",
reportId: "d9k3UfYHBc",
data: {
tourSection: "tourreport2",
tourMessage:
"This is a list of documents you've sent to other parties for signature."
}
}
},
{
colsize: "col-span-12 md:col-span-12 lg:col-span-12",
widget: {
type: "report",
reportId: "kC5mfynCi4",
data: {
tourSection: "tourreport3",
tourMessage:
"This are documents you have started but have not finalized for sending."
}
}
}
]
}
];
export default dashboardJson;
-79
View File
@@ -1,79 +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",
"Auto reminders"
]
},
{
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"
]
}
]
+30 -146
View File
@@ -7,97 +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";
import Loader from "../primitives/Loader";
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) {
@@ -106,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
@@ -145,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" },
}
@@ -167,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;
@@ -235,66 +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-[101]">
{!isLoader && <Header showSidebar={showSidebar} />}
<div className="sticky top-0 z-50">
<Header showSidebar={showSidebar} />
</div>
{isUserValid ? (
<>
{isLoader ? (
<div className="flex h-[100vh] justify-center items-center">
<Loader />
</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-base-200 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 showHeader={false} 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-medium">Your session has expired.</p>
<button onClick={handleLoginBtn} className="op-btn op-btn-neutral">
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

-71
View File
@@ -1,71 +0,0 @@
import React, { useState, useEffect } from "react";
import "../styles/loader.css";
import GetDashboard from "../components/dashboard/GetDashboard";
import { useNavigate, useParams } from "react-router-dom";
import Title from "../components/Title";
import { useDispatch } from "react-redux";
import { saveTourSteps } from "../redux/reducers/TourStepsReducer";
import dashboardJson from "../json/dashboardJson";
import Loader from "../primitives/Loader";
const Dashboard = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const { id } = useParams();
const [dashboard, setdashboard] = useState({});
const [loading, setloading] = useState(true);
useEffect(() => {
if (localStorage.getItem("accesstoken")) {
if (id !== undefined) {
getDashboard(id);
} else {
getDashboard(localStorage.getItem("PageLanding"));
}
} else {
navigate("/", { replace: true, state: { from: "" } });
}
// eslint-disable-next-line
}, [id]);
const getDashboard = async (id) => {
try {
const dashboard = dashboardJson.find((x) => x.id === id);
setdashboard(dashboard);
const dashboardTour = dashboard.columns
.filter((col) => {
if (col.widget.data && col.widget.data.tourSection) {
return col;
}
})
.map((col) => {
return {
selector: `[data-tut=${col.widget.data.tourSection}]`,
content: col.widget.data.tourMessage,
position: "top"
// style: { backgroundColor: "#abd4d2" },
};
});
dispatch(saveTourSteps(dashboardTour));
setloading(false);
} catch (e) {
console.error("Problem", e);
setloading(false);
}
};
return (
<React.Fragment>
<Title title="Dashboard" />
{loading ? (
<div className="h-[300px] w-full bg-white flex justify-center items-center rounded-md">
<Loader />
</div>
) : (
<GetDashboard dashboard={dashboard} />
)}
</React.Fragment>
);
};
export default Dashboard;

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