Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4315b5e0a | ||
|
|
ccecec5e59 |
@@ -178,69 +178,6 @@
|
||||
"contributions": [
|
||||
"a11y"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Govinda04",
|
||||
"name": "Govinda Kocharekar",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/50038172?v=4",
|
||||
"profile": "https://github.com/Govinda04",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "crediblebilal",
|
||||
"name": "Bilal Ahmad Bhat",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/55330484?v=4",
|
||||
"profile": "https://bilal.cc",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "VikramNagwal",
|
||||
"name": "Vikram",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/123088024?v=4",
|
||||
"profile": "https://github.com/VikramNagwal",
|
||||
"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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "1024mb",
|
||||
"name": "1024mb",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9301204?v=4",
|
||||
"profile": "https://github.com/1024mb",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -38,18 +39,11 @@ DO_ACCESS_KEY_ID=
|
||||
DO_SECRET_ACCESS_KEY=
|
||||
# Digital ocean spaces region or AWS s3 region
|
||||
DO_REGION=us-west
|
||||
# Set this to true id you are using local storage instead of s3 compatible storage
|
||||
# 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=XXXXX
|
||||
# Email mailgun config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
|
||||
MAILGUN_API_KEY=
|
||||
MAILGUN_DOMAIN=mail.yourdomain.com
|
||||
MAILGUN_SENDER=postmaster@mail.yourdomain.com
|
||||
SMTP_ENABLE=
|
||||
SMTP_HOST=
|
||||
SMTP_PORT=
|
||||
SMTP_USER_EMAIL=
|
||||
SMTP_PASS=
|
||||
|
||||
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
|
||||
PFX_BASE64=
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Clone the repo and rename this file to .env if you want to run the frontend react app locally for some quick contributions to the project.
|
||||
# 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
|
||||
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=opensignstgn
|
||||
@@ -2,23 +2,27 @@
|
||||
# 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=https://localhost:3001
|
||||
PUBLIC_URL=http://localhost:3000
|
||||
# Set it to true if you want to generate the Sourcemap for debugging
|
||||
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
|
||||
# (DEPRECATED) This should not be changed if provided; it should be 'opensign'.
|
||||
REACT_APP_APPID=opensign
|
||||
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=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=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-container: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)
|
||||
@@ -38,77 +42,69 @@ DO_SECRET_ACCESS_KEY=
|
||||
# Digital ocean spaces region or AWS s3 region
|
||||
DO_REGION=us-west
|
||||
# local storage
|
||||
USE_LOCAL=true
|
||||
USE_LOCAL=FALSE
|
||||
|
||||
# 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=
|
||||
# Email mailgun config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
|
||||
MAILGUN_API_KEY=XXXXX
|
||||
MAILGUN_DOMAIN=mail.yourdomain.com
|
||||
MAILGUN_SENDER=postmaster@mail.yourdomain.com
|
||||
SMTP_ENABLE=true
|
||||
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').
|
||||
|
||||
|
||||
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
|
||||
PFX_BASE64='MIIKCQIBAzCCCc8GCSqGSIb3DQEHAaCCCcAEggm8MIIJuDCCBG8GCSqGSIb3DQEH
|
||||
BqCCBGAwggRcAgEAMIIEVQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIpumu
|
||||
bFabBWACAggAgIIEKDqHS7Icd5MzIBk1dBokGK2s+5a2fA4308WA1QzEWmczqVYI
|
||||
z6lYmW8qsiZIw4PFkRzdIx1+zCmP8jgWiiqh5RKnbHYmh1JPNyx9SqmUDILDXmjg
|
||||
KxO9agRw9LXge4hgRL7YW0TxYZYw4EeXV9Yr7kcOq9DoO2MoRikJ+2Tuvvq/hfuV
|
||||
9zFHsz4jpf7W95k1IeH6bZ92klz5R1e/EK8JuHy592i/u+BxxW1vTkiPpTaHD6s9
|
||||
1lDSkT1j0LIDU6l9gVvJvFBV8j88vet/Z4QkTbHPMYvBnKNRstKuOvDasEJE4RkA
|
||||
PV4TfBwFF6uvREZI1vBboM/18pzeyjoRX+mlJNfcH3kh1tkck+Jg+M2bLXAH1EdD
|
||||
GRwENvnTNW1CFIn37VVja7SFBQNXA+E6rejmqtUmZ5XktSINDBUyjow9XuYTUtI/
|
||||
sgcRSC0aB0EKKpphbFPp1niKCLm1ef33e+bWyWbtR2L2Kc3ETmhjNADpyapAGmzc
|
||||
LP2BafGaKj18KiLAG4GL6+kXk0GUz7Fw5q8H8RY1T33o+xjWvC0+aJgvr65qWdKw
|
||||
+ug6wuu4Cr2kFEuXxPKsnYqmE5NT/x+lfunlZ5iEQN3w2XO5J2u2eMXVRUsPH2T6
|
||||
0sJKomjY0RB1JsqZBnyleWm+tYo0RpVAyJ3NGBp9dVb7a3Qw6jNZBG/xMW+gvtUJ
|
||||
q6hfK/cD3cagD0NQFVD5STMOuGOSqq/bbwAKjZ/lrPmCCYATKwkjAFybYfHJVrrW
|
||||
Y2tQgCQ/zrZLIxQjp7F17hU0+gT54x0MpF6AoqIM/qqNnHrQ0fi50ECmLvx3SqYa
|
||||
nvjvt/HZhMZfqRXiXjeicIwpnNoG7+uQknySk+7cc9GNhHZR7cfPfkTpgvuuebrQ
|
||||
l5YW+k9tG+xrX8g72nvtFMnammlBixlFwECJaEszEQk5tc2ti8uG2z+kGnFoDoBV
|
||||
M6ZVMpKDJzXPxLdzT6ChIPyatjIkqOKe6vDpnmzcoEhbBhmtkVjWFA2dfX846ugV
|
||||
y1QiecWgk8pMNWhh3IDd05v/wzbFwNcq+Gi+1dZsPmFH+egIcrQKZu2r3jCJtGHh
|
||||
DNG/nyf/anqvKXF90bi4hRT3vdDXXuWgY7EUwfn9h5jYabCO6HsQxQOBjFYNmBrh
|
||||
MfaiBZe72E8fzNAfIlwDcJ4AWY1SOM7hi8bqfRWw1NtNxuMUiRFoR31O6XkQauye
|
||||
9hAmpdlZHZXtJ6LM0QaWkPJmG3058GMtIn/qS60V9nINh+zXhwza/1pnFB+e9MWc
|
||||
hAPgo0nn55FusM//g1nM31PKe+nOm3Jyp0nEQ4m8aDtJS63o2fvp3e2KJbPWQMHE
|
||||
a83W975diWg4NTdevV9lFpNllHclVoPoavUHIzHd7HF9TcPpjbXJSUZ7cITHHefr
|
||||
kQTa/2G9cWuRhN9GKIHBdr13nfEvMIIFQQYJKoZIhvcNAQcBoIIFMgSCBS4wggUq
|
||||
MIIFJgYLKoZIhvcNAQwKAQKgggTuMIIE6jAcBgoqhkiG9w0BDAEDMA4ECDgnjfOf
|
||||
AjArAgIIAASCBMgkbVuptroBdyMY5gl/eBQj5+iAV02YSaF969ihStLSWI4x5FAf
|
||||
HuDgqCyEMSl4RHG8ZvNaO9JoNfIpMK/TPQBoQRCzE2Vjf66VTERaQtU6h7tZrbQt
|
||||
K96n9eqvohm/vXbXe08fsodYp2s79kAvKgpuGG5iX77qYVzJHBPed87cvczo4ToP
|
||||
CpurDpByt8fGEmjtcikal8o+H9uoHjcFwqMigX1Q7IEjuKXrb5e6wTvxMRob8yfv
|
||||
LVl6ahteKSkWCMb6rLZ5f12jDoCGX/YXRrSsU9t/lXe0Nxv5i7c8flb2EsNtwygD
|
||||
fexVvn5u6ble6RDYNpuQkPgF41HyZh+JvGAF6i1r4tJPL9Pf4HnjEB8Y8IHgon+w
|
||||
T5I+8LTgcrWyhptzblXMjKySTEp9OEa2cCVwZkl4z43PWJ5oIAQ2IDTOQ603/5o/
|
||||
d9KA76/zMPQ5O0gv17OA7kwFv98v6sTcR4vgkQZqnyDpJSEPHyNxhw2MSBqS3jq3
|
||||
YPhRz+3Ei1oFp9uYFhzn39f9gzNi++X9pOjSrDb8v3mcDXZKNrxQLuoRMSf0eQNI
|
||||
dgJRqj/fuqNZ5ac3kd4P5BALCsnA1tM27zYrgUdpOnrc2D9FBthEjpRlQKMMzhQf
|
||||
cNMFWmoNjakDilvvLcsQeQ3P6cXbN1ODoNnjgK3VzcmeKyzW2PrTXEPHARUM3j4V
|
||||
sZzGaUOVKLqaaDrXWQKC0vrTvxP2WMfjQ1dTdXQP/kSZTXRC5rtB9tky38flBor0
|
||||
N9rhUDIGMs5qBvyrkV2hKXiW39G69p1KWY+Cw28AzY+CNSjePRi60TsnUxgcOS7i
|
||||
0AT25Gx3A4YqxmTY38MPB4wPcBPcw4hjNoQamaC9mqs+KVHcP5YHUlqykMeH0YVi
|
||||
J+ehytJE9xuDcfBOjxjadxp3/q/Lku4rky89gdJGFQWuuz75VulDywPRZRwmQTel
|
||||
sjEGMHR1zTOlPJdTDWhqaKmWvL4GhbCgEPIKwmqd1QKRTl/3Baa1nYllJeYVwiTT
|
||||
qLCkbA6qXrLGj1ENZxrKn9yqr8HwOoApjwhARu4LF/BRMgdkzelK2kOXHnZ26sZ2
|
||||
NY/MMtVmq4c59y6iQwmsbHr9tWGz3ahyXcZZufjNXU/AVnO1c92g7umAWZF4RohL
|
||||
gMmh57cAhN9PEbEv6j/6BT3XXp8jB0ywcRriYxfxwidviCKH/76Q4sytYJQaDFI1
|
||||
fnzmv0aDotbG2BdpWSiYPDOPm+3cmfjlULsn1FYWL4NDCX1f+C4lNtKVX9LU8c5M
|
||||
m8dA1DUlDfnuNO5/BShuoGXG2z4O/XwxIaRFizMxWS2sWurbAEFJ6oNw3kC7WSah
|
||||
NIe8aUC1umpc1Gk3X2f4Ytzj9OEqn0y55qoqLINJuejXMffF/gjopxHWadLVaVaj
|
||||
Q1SewrECuEXdSbBR/a10po24wmRtkmlvRJXJl7sG95xE5ZCqp+m6sRPWGdf1yKbV
|
||||
vyWNUe4Qvkxe4a0VAJpTyIGr980CKz/jkvtNQMobGl7AfhxKJ450wg454WcexMct
|
||||
BYXvZMSdUDCgMLlh0nHJHl5btrFWqE0Z/fqWmIknZL2jZ4J+2hdVl/xB/sUt6kcu
|
||||
txw+RfL+vNE8PxhTNaOdJFwD8yeN1mJ3yMUA8HHYpw9yljgxJTAjBgkqhkiG9w0B
|
||||
CRUxFgQUDYlgGVxSxuOknhQc256x3++7BDwwMTAhMAkGBSsOAwIaBQAEFFjASdYl
|
||||
3pXAXxZuvVvv9tsb4bdrBAhyb+KCIjp/gAICCAA='
|
||||
PFX_BASE64='MIIKLwIBAzCCCeUGCSqGSIb3DQEHAaCCCdYEggnSMIIJzjCCBEIGCSqGSIb3DQEH
|
||||
BqCCBDMwggQvAgEAMIIEKAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG
|
||||
SIb3DQEFDDAcBAh6cQqVpHfTzAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQME
|
||||
ASoEEFKEIGJTTRo3voO/FvsXs7yAggPAI7rMCboTFFBfdMuuV3Y9/xfphAgdGD7P
|
||||
0xAN10CcmXlMtedc8cRxOaD9n3Q7zJx5iIbqEGDTc0dgp5EtdWsGl7MP76iHkSfB
|
||||
ZUPc13nbnRU4Nq4hNFcSj1m6NVa0lT9UobbTsG+wUkJn8aLfijzZ1OY82KLlKUoJ
|
||||
lqLsQ1yZkeLiLKI0aEew/2vzfiLUX9A99qnCRoryo8ZbLiWl0Vu/FEKM5mqIRFEp
|
||||
BKeSUsRxBfJCMZ2Ks5t4/MTCV+yrcsuhLPF/AJpzKfIinEFDhm7MxP4RXtk2LWNe
|
||||
+EO2GuFRjZCfsMO0oNWybFmDeNDMCHZj5TeoqL1JwAV5e1lEeqR38v4Wcr4OVefY
|
||||
DfdrJ2xdvm7XbBZ8Lgx8L4fwugw7oDyTpR2jJqHMlpOsQrxipN6UKkN6EVYeCC5K
|
||||
4dF5LHdCcIa7L/OI/feD5g/zz8SGCTc91husVN7zi+CVO0lQLRkAj02+VbiXPPDD
|
||||
eQm/QBieG2yVe5IRfcnIs2aszulbQbgHdguSYSQa/7DEvN/9/uxUIRFbItwrreRI
|
||||
VAUUNS02L680Zok9xzDed1I/WJoPr2+5n2eV1jc5zoH7mAni0+Z1d5qLoYV6pNaN
|
||||
P9okvBf75LLNoJKsb/eWlkk73rmlH6/Qz+hudgnlkxUJuU/b79AMQpd0EWEWhcim
|
||||
ZeB4d9lPLLwyvY/FN4aCGWzsoAW69RK9E1DyGZAZRYRwCKHSWXmCaenR1ilxcPFx
|
||||
TDavpybObIISPtylv5Z/IGBjE19Lw0UHc6Waln/jx0YXXGGDWs1F/MrdJ/8mkVXf
|
||||
tHB/5ds4u5md+CmXpaCITO3Cg9GltGSWyDoEJsz1u/Wzq4X4FYmolXiqQvMPQrxu
|
||||
iThTnPDGbz9VqFSyNKPKswIQ8CUbvZ209A5vjERPzE0Lp1pO39K51fnHD9oD968t
|
||||
tGbGmNiQ0++Orze79cH0UVwDa6NLGCmnE0oBgke1JDPlaU3kkx7+R9SAS0Zytsmj
|
||||
aLxW5Z1cOFaqqBLjwgHUxOqRO+cl6P9ORox34wJjiwZKy1L+0ppw22Na/unwi2Rk
|
||||
a8jvK7+ycOLaiSmI77T0l7VCWgKsRsWLvBA3cjX5XAGtatFXrY/7Imoxl0nWGLjG
|
||||
n5Sv5XuDek4e4QAPeJmhpURMnzLDsSXAYOSYZvS/kgENYhYSnQLQp50TxrzquA2c
|
||||
lAeFIEqqAJy9tAMfp5UAqMeOC60Ttn6ySsdOle003F2NrRilhPynNDZ3h1ZQknur
|
||||
Y9zWUm9qBmXCjhPzdmzHz3Ozj2Bi6kd4MIIFhAYJKoZIhvcNAQcBoIIFdQSCBXEw
|
||||
ggVtMIIFaQYLKoZIhvcNAQwKAQKgggUxMIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkq
|
||||
hkiG9w0BBQwwHAQIZenzippqfSQCAggAMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUD
|
||||
BAEqBBCddSIj/wfxowZAO1j3W9FwBIIE0C/guzeZtV/vPsaIDAtWEkmepCvhm9Rg
|
||||
aUgOoR66wjyYx7upeuZJ+pYJdsEqUmTgGYN+ClBpsU0bqCM7PI0mepvIT9kLD6ZL
|
||||
UkprtNAYP87NBA/e5dpXlGAf9ibDRT0F2Y+lCSF83rCIYKG8n7h5834F6Saf+dU7
|
||||
6y5bgorbf2Fnxy123XfUSg5qdCraZZM+DwE1gpQdBGrFyq8cwlQ8hJpKHDWup7WA
|
||||
f2HcT/X0Wjb24L6JWjC1FvTbTNdS/lzTOcqLYUc31iuU9rF6O4qtX9PK/yGWc+YE
|
||||
qakuV+HrsF4je/YdVZRYHtkbbcYXW9PjtMv+4D1ut5X2SPeHwpj3WmGAb7Zno6SD
|
||||
SfohMihXvDbIjVSMDBpW97bhgk0h7iS4ISIYoQJUGJv3QQ3XJq91LoIP3zpiO/Cq
|
||||
ZuuHuGvZmO6lb0PGShRT7mFg3k3rswTfFyKdtY76zAVJIR2bz/6bRf4uQI48SOLR
|
||||
NX1Z6lavUNiE4nPNlnfBp480C+YANKs/bdev0wkRVmFxPMyb5FFLXwLSqcfdSQag
|
||||
RayrKd8xPOm6lor3cTvrkO3XH2Hf9TgFHjeIAQ3VZ1Cncb9jfra3V4zvIocek4dC
|
||||
GREirPbEyL/js5O0aAa0mg9EontMWKRqncomKhUOckheP2dYiQOazDFDFP9OQCYV
|
||||
MBb0RDwm3+hiMU0lPeO+NAjAv2SfFK7G4maMb2SL7inT7An7ZPPpnDNXLDt+nOgo
|
||||
IKuaJpy8JX/OKB4ntYsdnQG4E0J3vpHWjYPKif7j1njIYFd76bIgNVlqWuQFArBy
|
||||
U9BuPyF+eYouJc+TpX9R5ybPvabRR4NUjnrttr2E5AzQfIgNGrSFiewjvYZHQOKd
|
||||
WyVR9YV0cUv591xyUDdXMoHh+J0v0xK1WCHe/9enY5y/C8YohL8h5tEvInAQconj
|
||||
lJ2r9qX8iBmIupJij5M5Ebza8HQ4aVMdHu/s05Vj9BC6K0ok3h+DFTj2gUBD24sp
|
||||
APHic0DmNnciUQuHuuoW/1SVb6I1HJswsgcC8CekvoXF26ygwWhJPyqw+5djip2w
|
||||
I/l3uOnPPUWZQHKhnBH4noZydrzBBJN1po9dkxb8LKuKZDEcnKo+tQrTnZJaFRXK
|
||||
pQDsAjQ0HcYi8ZqN/GPezXOqzRgvObevXeDYTJrNejr46B1HqjPw8doai+AEf//1
|
||||
iiM9EK4y3bGbX837oebTByRi112AOctISHTWbF0zdFMiBO9X9PML4n3i14ToqLH1
|
||||
EUp0WtCdKLJmrRnO9PKJUMpce3yNu6oRSBzB4EkgUMOTVx9tM66h/gcR3wr+qKJP
|
||||
1EABMbbCsJeYYLujhQHVh0JluqLBXYuCmr5n6o8qmonj5tEfTn9zs2gi0OvKTliH
|
||||
wRXWsar004alt8D8o4M/Oqu3qyVV1O8BlVEofUC1CfAMIh/T1NJBeKG/FPfAFoEz
|
||||
9liLLkoDvGvFVJ4Ezy4BaPtg+G3yU/SeiruwsCB3unHRD7G6VvS4jY2B8vEYl0yM
|
||||
Z1mLFbKK8CIEvg8+v/vbc0noYUOzd6bkwh6Y6cziR/u78y2nALYZpQK+3XiVVH/v
|
||||
SaVoDckZgUwAX+dMd1P4U3a487MPXBZZmLVFMHTuv3NvW6NIQCMd4l35ViRd7YcS
|
||||
V2QDKTnUtPN2MSUwIwYJKoZIhvcNAQkVMRYEFMSz7PlBO5VzTDqC/foidPWrMjkw
|
||||
MEEwMTANBglghkgBZQMEAgEFAAQgPlLuG+67SxPOEPbgFuEnVgewvY6LUzFmrprx
|
||||
c6yryFgECIld0S5kDk7JAgIIAA=='
|
||||
|
||||
# Provide Pass pharse of above PFX or p12 document
|
||||
PASS_PHRASE=opensign
|
||||
|
||||
# (DEPRECATED) This should not be changed if provided; it should be 'opensign'.
|
||||
APP_ID=opensign
|
||||
PASS_PHRASE=
|
||||
@@ -1,73 +0,0 @@
|
||||
name: "Bug Report"
|
||||
labels: ["bug","up-for-grabs"]
|
||||
title: "[Bug]: "
|
||||
assignees:
|
||||
- andrew-opensignlabs
|
||||
description: Create a bug report to help us make OpenSign™ the world's best document e-signature platform
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Issue Description
|
||||
description: Please provide a clear & concise description of the problem.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: Describe what you expected to happen.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: Describe what is happening instead.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Please provide step-by-step instructions to reproduce the issue. Include code snippets, URLs, error messages and any other relevant information.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Screenshots of the issue(optional)
|
||||
description: Add screenshots to better explain the issue.
|
||||
- type: input
|
||||
attributes:
|
||||
label: Operating System [e.g. MacOS Sonoma 14.1, Windows 11]
|
||||
- type: dropdown
|
||||
id: browsers
|
||||
attributes:
|
||||
label: What browsers are you seeing the problem on?
|
||||
multiple: true
|
||||
options:
|
||||
- Chrome
|
||||
- Firefox
|
||||
- Safari
|
||||
- Microsoft Edge
|
||||
default: 0
|
||||
- type: input
|
||||
attributes:
|
||||
label: What version of OpenSign™ are you seeing this issue on? [e.g. 1.0.6]
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: environment
|
||||
attributes:
|
||||
label: What environment are you seeing the problem on?
|
||||
multiple: true
|
||||
options:
|
||||
- Production (app.opensignlabs.com)
|
||||
- Staging (staging-app.opensignlabs.com)
|
||||
- Hosted (app.yourdomain.com)
|
||||
- Dev (localhost or vercel)
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Please check the boxes that apply to this issue report.
|
||||
options:
|
||||
- label: I have searched the existing issues & discussions to make sure that this is not a duplicate.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://www.opensignlabs.com)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
- label: I have searched the existing issues & discussions to make sure that this is not a duplicate.
|
||||
required: true
|
||||
@@ -1,70 +0,0 @@
|
||||
name: Feature Request
|
||||
description: Suggest an idea for OpenSign™
|
||||
title: "Feature Request: [Short Description]"
|
||||
labels: [feature request, needs triage]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Thanks for taking the time to fill out this feature request!
|
||||
|
||||
- type: input
|
||||
id: title
|
||||
attributes:
|
||||
label: Feature Title
|
||||
description: What would you call this feature?
|
||||
placeholder: e.g., Advanced Signature Verification
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: Feature Description
|
||||
description: |
|
||||
Please provide a detailed description of the feature you're proposing.
|
||||
placeholder: |
|
||||
e.g., I'm proposing an advanced signature verification feature that...
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: featuretype
|
||||
attributes:
|
||||
label: What type of feature are you requesting?
|
||||
multiple: true
|
||||
options:
|
||||
- UI/UX Improvement
|
||||
- Security / Compliance
|
||||
- Performance / Optimization
|
||||
- 3rd party integration
|
||||
- Other
|
||||
- type: dropdown
|
||||
id: importance
|
||||
attributes:
|
||||
label: Importance
|
||||
description: How important is this feature to you?
|
||||
options:
|
||||
- Critical
|
||||
- High
|
||||
- Medium
|
||||
- Low
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Any other context or screenshots about the feature request here.
|
||||
placeholder: Additional details, examples, or screenshots...
|
||||
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://www.opensignlabs.com)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
- label: I have searched the existing issues & discussions to make sure that this is not a duplicate.
|
||||
required: true
|
||||
@@ -1,22 +0,0 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# maintain dependencies for frontend
|
||||
- package-ecosystem: "npm" # See documentation for possible values
|
||||
directory: "/apps/OpenSign" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
# maintain dependencies for server
|
||||
- package-ecosystem: "npm" # See documentation for possible values
|
||||
directory: "/apps/OpenSignServer" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
# maintain dependencies for microfrontends
|
||||
- package-ecosystem: "npm" # See documentation for possible values
|
||||
directory: "microfrontends/SignDocuments" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -1,15 +1,8 @@
|
||||
name: ci
|
||||
|
||||
# 👇 add this block
|
||||
permissions:
|
||||
contents: read # allow checkout & metadata-action to read repo
|
||||
id-token: write # needed by docker/metadata-action v4
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'staging'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
@@ -17,10 +10,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- image: opensign/opensign
|
||||
dockerfile: apps/OpenSign/Dockerhubfile
|
||||
- image: opensign/opensignserver
|
||||
dockerfile: apps/OpenSignServer/Dockerhubfile
|
||||
- image: amolshejole/OpenSign
|
||||
dockerfile: apps/OpenSign/Dockerfile
|
||||
- image: amolshejole/OpenSignServer
|
||||
dockerfile: apps/OpenSignServer/Dockerfile
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
@@ -38,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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run lint-staged-changes
|
||||
npx eslint '**/*.{js,jsx}'
|
||||
npx pretty-quick --staged '**/*.{js,jsx}'
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"*.js": "prettier --write"
|
||||
}
|
||||
@@ -77,6 +77,6 @@ Engage with the community on our [Gitter Channel](https://gitter.im/OpenSignLabs
|
||||
|
||||
## License
|
||||
|
||||
By contributing, you agree that your contributions will be licensed under its [AGPL-V3 License](LICENSE).
|
||||
By contributing, you agree that your contributions will be licensed under its [MIT License](LICENSE).
|
||||
|
||||
---
|
||||
---
|
||||
@@ -1,7 +0,0 @@
|
||||
{$HOST_URL} {
|
||||
reverse_proxy client:3000
|
||||
handle_path /api/* {
|
||||
reverse_proxy server:8080
|
||||
rewrite * {uri}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,150 @@
|
||||
|
||||
# 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 -
|
||||
|
||||
[](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 | http://localhost:3000 | Set it to the URL form where the app home page will be accessed |
|
||||
| PUBLIC_URL | 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`
|
||||
@@ -1,9 +1,4 @@
|
||||
* All content that resides under the "apps/OpenSignServer/cloud/customRoute" directory of this repository, if that directory exists, is licensed under the license defined in "apps/OpenSignServer/cloud/customRoute".
|
||||
* All third party components incorporated into the OpenSign Software are licensed under the original license provided by the owner of the applicable component.
|
||||
* Content outside of the above mentioned directories or restrictions above is available under the "GNU AFFERO GENERAL PUBLIC LICENSE" as defined below -
|
||||
|
||||
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
build:
|
||||
@echo "Building with HOST_URL=${HOST_URL}"
|
||||
cp .env.local_dev .env
|
||||
cd apps/OpenSign && cp ../../.env.local_dev .env && npm install && npm run build
|
||||
HOST_URL=${HOST_URL} docker compose up --build --force-recreate
|
||||
rm -rf apps/OpenSign/public/mfbuild
|
||||
cd microfrontends/SignDocuments && npm install && npm run build
|
||||
docker compose up -d
|
||||
|
||||
run:
|
||||
@echo "Building with HOST_URL=${HOST_URL}"
|
||||
cp .env.local_dev .env
|
||||
docker compose up -d
|
||||
docker compose up -d
|
||||
@@ -1,7 +1,8 @@
|
||||
<h1 align="center"><a href='https://www.opensignlabs.com'><img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/e518cc9c-5de3-47da-950b-f93336b9f14e></a>
|
||||
</h1><div align="center">
|
||||
<h1 align="center"><a href='https://www.opensignlabs.com'>OpenSign™</a></h1>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[The free and open source alternative to DocuSign](https://www.opensignlabs.com)
|
||||
The free and open source alternative to DocuSign
|
||||
|
||||
[](http://isitmaintained.com/project/opensignlabs/opensign "Average time to resolve an issue")
|
||||
[](#contributors)
|
||||
@@ -11,9 +12,7 @@
|
||||
|
||||
<a href="https://www.opensignlabs.com/">Website</a>
|
||||
<span> • </span>
|
||||
<a href="https://docs.opensignlabs.com">Help Docs</a>
|
||||
<span> • </span>
|
||||
<a href="https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1">API Docs</a>
|
||||
<a href="https://www.opensignlabs.com">Docs</a>
|
||||
<span> • </span>
|
||||
<a href="https://www.opensignlabs.com/blog">Blog</a>
|
||||
<span> • </span>
|
||||
@@ -24,7 +23,7 @@
|
||||
<a href="https://www.linkedin.com/company/opensign%E2%84%A2/about/">LinkedIn</a>
|
||||
|
||||
|
||||
## The premier open source document signing solution(DocuSign alternative)
|
||||
## An open-source document e-signing solution
|
||||
|
||||
---
|
||||
</div>
|
||||
@@ -40,54 +39,40 @@
|
||||
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 document 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. Now sign unlimited documents even on the [cloud hosted free version of OpenSign](https://www.opensignlabs.com/).
|
||||
- **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 open source document 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. OpenSign intends to provide the best document signing experience in the open source ecosystem.
|
||||
- **Multi-signer Support:** OpenSign's ability to invite multiple signers for signing along with the ability to invite by sharing signing links & 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.
|
||||
- **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 with a reason that will be promptly shared with the sender.
|
||||
- **Beautiful email templates:** All document signing invitations, completion notifications & reminders are formatted using great looking email templates. Not just this, you are even allowed to customise the email templates making your free document signing invitations look the way you always wanted them to be.
|
||||
- **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 & archieving 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 [official API docs](https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1) to start integrating it in your existing applications.
|
||||
- **Integrations:** The open source document signing experience becomes even more seamless because of integrations with various Cloud storage systems, CRMs & enterprise platforms. We also have a Zapier integration that allows you to integrate it with virtually any application.
|
||||
<img alt="Login page" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/8016edb6-7d6e-4185-ab72-82d2c4a16032" height='200' >
|
||||
<img alt="Dash_board" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/6e7b96f7-3154-4c25-bc1e-caf33925cc47" height='200'>
|
||||
<img alt="Widgets" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/7edb65bd-c265-4b3d-9758-38e79766c5c2" height='200'>
|
||||
<img alt="Request Signature" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/1697ae46-dcac-41c9-a081-aded25693846" height='200'>
|
||||
<img alt="Create template" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/c2060fda-50f1-4116-a532-51b94b48463a" height='200'>
|
||||
<img alt="Manage_Template" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/90310c74-8271-49bd-8c5f-89daa5a623c4" height='200'>
|
||||
<img alt="Folders" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/a52207e7-3b0e-497c-b5f6-747bc4e67918" height='200'>
|
||||
<img alt="OpenSign_Confetti" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/8f4c6a6e-4a73-439c-9f34-f426044d0a7f" height='200'>
|
||||
- **Secure Signing**: Utilizes state-of-the-art cryptographic algorithms to ensure the security & integrity of your documents.
|
||||
- **User-Friendly Interface**: Designed with usability in mind, making it easy for both technical and non-technical users.
|
||||
- **Multi-Platform Support**: Compatible with various browsers and devices.
|
||||
- **Invite & collaborate users**: Bring multiple people from your team into the signing process, all within your own infrastructure.
|
||||
- **Secure**: Allows for the easy, secure and seamless organization of your documents using 'OpenSigDrive'.
|
||||
- **Audit Trails**: Keeps a detailed log of all activities related to the document signing process including IP addresses and access timings.
|
||||
- **Completion Certificate**: Generate secure completion certificate as soon as a document is signed by all participants.
|
||||
- **API Support**: Provides a robust API for integration into other software and services.
|
||||
|
||||
<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'>
|
||||
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/56835efd-c40e-42de-a206-20202c293876 height='200'>
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Installation
|
||||
|
||||
The simplest way to install OpenSign on your own server is using official docker images by running the following command -
|
||||
```
|
||||
export HOST_URL=https://opensign.yourdomain.com && curl --remote-name-all https://raw.githubusercontent.com/OpenSignLabs/OpenSign/main/docker-compose.yml https://raw.githubusercontent.com/OpenSignLabs/OpenSign/main/Caddyfile https://raw.githubusercontent.com/OpenSignLabs/OpenSign/main/.env.local_dev && mv .env.local_dev .env.prod && docker compose up --force-recreate
|
||||
```
|
||||
Make sure that you have `Docker` and `git` installed before you run this command -
|
||||
|
||||
Please refer to the [Installation Guide](https://docs.opensignlabs.com/docs/self-host/docker/run-locally/) for detailed instructions on how to install OpenSign on your system.
|
||||
Please refer to the [Installation Guide](INSTALLATION.md) for detailed instructions on how to install OpenSign on your system.
|
||||
|
||||
---
|
||||
|
||||
### 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).
|
||||
|
||||
---
|
||||
|
||||
@@ -99,13 +84,13 @@ We welcome contributions from the open-source community. For more information on
|
||||
|
||||
### License
|
||||
|
||||
OpenSign is licensed under the AGPL-3 License. For more details, see the [LICENSE](LICENSE) file.
|
||||
OpenSign is licensed under the AGPL License. For more details, see the [LICENSE](LICENSE) file.
|
||||
|
||||
---
|
||||
|
||||
### Acknowledgments
|
||||
|
||||
We would like to thank all our contributors and users for their support and feedback. Special thanks to [OpenSignLabs](https://www.opensignlabs.com) for spearheading this initiative.
|
||||
We would like to thank all our contributors and users for their support and feedback. Special thanks to [OpenSignLabs](https://github.com/OpenSignLabs) for spearheading this initiative.
|
||||
|
||||
---
|
||||
|
||||
@@ -140,15 +125,6 @@ We would like to thank all our contributors and users for their support and feed
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rishabjasrotia"><img src="https://avatars.githubusercontent.com/u/33950743?v=4?s=100" width="100px;" alt="Rishab"/><br /><sub><b>Rishab</b></sub></a><br /><a href="#code-rishabjasrotia" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://session.it"><img src="https://avatars.githubusercontent.com/u/327285?v=4?s=100" width="100px;" alt="Maurizio Pillitu"/><br /><sub><b>Maurizio Pillitu</b></sub></a><br /><a href="#bug-maoo" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://luisparra.dev"><img src="https://avatars.githubusercontent.com/u/16653744?v=4?s=100" width="100px;" alt="Luis Parra"/><br /><sub><b>Luis Parra</b></sub></a><br /><a href="#a11y-lsprr" title="Accessibility">️️️️♿️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Govinda04"><img src="https://avatars.githubusercontent.com/u/50038172?v=4?s=100" width="100px;" alt="Govinda Kocharekar"/><br /><sub><b>Govinda Kocharekar</b></sub></a><br /><a href="#code-Govinda04" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://bilal.cc"><img src="https://avatars.githubusercontent.com/u/55330484?v=4?s=100" width="100px;" alt="Bilal Ahmad Bhat"/><br /><sub><b>Bilal Ahmad Bhat</b></sub></a><br /><a href="#code-crediblebilal" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ugoconsonni"><img src="https://avatars.githubusercontent.com/u/13661702?v=4?s=100" width="100px;" alt="ugoconsonni"/><br /><sub><b>ugoconsonni</b></sub></a><br /><a href="#code-ugoconsonni" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/daniel-mutwiri"><img src="https://avatars.githubusercontent.com/u/8936960?v=4?s=100" width="100px;" alt="Daniel Mutwiri"/><br /><sub><b>Daniel Mutwiri</b></sub></a><br /><a href="#code-daniel-mutwiri" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/1024mb"><img src="https://avatars.githubusercontent.com/u/9301204?v=4?s=100" width="100px;" alt="1024mb"/><br /><sub><b>1024mb</b></sub></a><br /><a href="#translation-1024mb" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -158,5 +134,3 @@ We would like to thank all our contributors and users for their support and feed
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project is tested with BrowserStack.
|
||||
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
Please visit the official documentation [here](https://docs.opensignlabs.com).
|
||||
The community is working hard to put together a great usage guide.
|
||||
You will soon see one of the best usage guides here.
|
||||
|
||||
Till then you can view the quick promo video by clicking the link below -
|
||||
|
||||
[<img src="https://i.ytimg.com/vi/szYaBZhXZNo/maxresdefault.jpg" width="50%">](https://www.youtube.com/watch?v=szYaBZhXZNo "OpenSign promo")
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"]
|
||||
}
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
# production
|
||||
/build
|
||||
/mfbuild
|
||||
|
||||
# misc
|
||||
.env
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Use an official Node runtime as the base image
|
||||
FROM node:16
|
||||
|
||||
# Set the working directory inside the container
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package.json and package-lock.json first to leverage Docker cache
|
||||
COPY ./package*.json ./
|
||||
|
||||
# Install application dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy the current directory contents into the container
|
||||
COPY ./ .
|
||||
COPY ./.husky .
|
||||
|
||||
# Make port 3000 available to the world outside this container
|
||||
EXPOSE 3000
|
||||
|
||||
# Define environment variables if needed
|
||||
# ENV NODE_ENV production
|
||||
|
||||
# Run the application
|
||||
ENTRYPOINT npm run start-dev
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# Use an official Node runtime as the base image
|
||||
FROM node:22.14.0
|
||||
|
||||
# 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 .
|
||||
COPY apps/OpenSign/entrypoint.sh .
|
||||
|
||||
# make the entrypoint.sh file executable
|
||||
RUN chmod +x entrypoint.sh
|
||||
|
||||
# Define environment variables if needed
|
||||
ENV NODE_ENV=production
|
||||
ENV GENERATE_SOURCEMAP=false
|
||||
# build
|
||||
RUN npm run build
|
||||
|
||||
# Inject env.js loader into index.html
|
||||
RUN sed -i '/<head>/a\<script src="/env.js"></script>' build/index.html
|
||||
|
||||
# Make port 3000 available to the world outside this container
|
||||
EXPOSE 3000
|
||||
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
|
||||
# Run the application
|
||||
CMD ["npm", "start"]
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
presets: ["@babel/preset-env", "@babel/preset-react"]
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
ENV_FILE=./build/env.js
|
||||
DOTENV_FILE=./.env.prod # ✅ use .env.prod
|
||||
|
||||
echo "Generating runtime env file at $ENV_FILE..."
|
||||
|
||||
echo "window.RUNTIME_ENV = {" > $ENV_FILE
|
||||
|
||||
# List of keys to include
|
||||
RUNTIME_KEYS="REACT_APP_SERVERURL"
|
||||
|
||||
for key in $RUNTIME_KEYS; do
|
||||
# First check docker env (-e), fallback to .env file
|
||||
value=$(printenv "$key")
|
||||
|
||||
if [ -z "$value" ] && [ -f "$DOTENV_FILE" ]; then
|
||||
# fallback: read from .env
|
||||
value=$(grep "^$key=" "$DOTENV_FILE" | cut -d '=' -f2- | tr -d '\r\n' | sed 's/"/\\"/g')
|
||||
else
|
||||
value=$(echo "$value" | sed 's/"/\\"/g')
|
||||
fi
|
||||
|
||||
echo " $key: \"$value\"," >> $ENV_FILE
|
||||
done
|
||||
|
||||
echo "};" >> $ENV_FILE
|
||||
|
||||
exec "$@"
|
||||
@@ -1,22 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="opensigncss">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="The fastest way to sign PDFs & request signatures from others" />
|
||||
<!-- <link rel="apple-touch-icon" href="/logo192.png" /> -->
|
||||
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdn.opensignlabs.com/fonts.css" />
|
||||
<title>...Loading</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="touch-action:pan-x pan-y;"></div>
|
||||
<script type="module" src="/src/index.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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"]
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,71 +1,51 @@
|
||||
{
|
||||
"name": "open_sign",
|
||||
"version": "2.21.1",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@lottiefiles/dotlottie-react": "^0.13.5",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@radix-ui/themes": "^3.2.1",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@imgly/background-removal": "^1.6.0",
|
||||
"axios": "^1.9.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"@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",
|
||||
"i18next": "^23.16.8",
|
||||
"i18next-browser-languagedetector": "^8.1.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"parse": "^6.1.1",
|
||||
"pkijs": "^3.0.8",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"print-js": "^1.6.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"radix-ui": "^1.4.2",
|
||||
"react": "^18.3.1",
|
||||
"react-bootstrap": "^2.10.10",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-datepicker": "^8.3.0",
|
||||
"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.9.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^9.0.0",
|
||||
"react-dnd-multi-backend": "^8.0.3",
|
||||
"react-dnd-touch-backend": "^16.0.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-drag-listview": "^2.0.0",
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^15.5.1",
|
||||
"react-konva": "^18.2.10",
|
||||
"react-pdf": "^9.2.1",
|
||||
"react-quill-new": "^3.4.6",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-rnd": "^10.5.2",
|
||||
"react-router": "^7.6.0",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.10.1",
|
||||
"react-signature-canvas": "^1.1.0-alpha.2",
|
||||
"react-timezone-select": "^3.2.8",
|
||||
"react-tooltip": "^5.28.1",
|
||||
"reactour": "^1.19.4",
|
||||
"redux": "^5.0.1",
|
||||
"regex-parser": "^2.3.1",
|
||||
"serve": "^14.2.4",
|
||||
"styled-components": "^5.3.11",
|
||||
"web-vitals": "^5.0.1",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
||||
"react-modal": "^3.16.1",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-router-dom": "^6.16.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-select": "^5.7.7",
|
||||
"reactour": "^1.19.2",
|
||||
"redux": "^4.2.1",
|
||||
"redux-thunk": "^2.4.2",
|
||||
"rjsf-conditionals": "github:prafullnavkar-nxg/rjsf-conditionals",
|
||||
"serve": "^14.2.1",
|
||||
"styled-components": "^4.4.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run version && NODE_OPTIONS=\"--max-old-space-size=8192\" vite build",
|
||||
"start-dev": "vite",
|
||||
"dev": "vite",
|
||||
"preview": "vite preview",
|
||||
"start": "serve -s build",
|
||||
"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-win": "npm run version-win && vite build",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"start-dev": "mf-cra start",
|
||||
"build": "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",
|
||||
"commit": "cz"
|
||||
},
|
||||
@@ -93,38 +73,16 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.27.1",
|
||||
"@babel/preset-env": "^7.27.2",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@babel/runtime-corejs2": "^7.27.1",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/react": "^18.3.22",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"@vitejs/plugin-react-swc": "^3.9.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"babel-loader": "^10.0.0",
|
||||
"commitizen": "^4.3.1",
|
||||
"concurrently": "^9.1.2",
|
||||
"css-loader": "^7.1.2",
|
||||
"daisyui": "^4.12.24",
|
||||
"dotenv": "^16.5.0",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-plugin-prettier": "^5.4.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"lint-staged": "^16.0.0",
|
||||
"postcss": "^8.5.3",
|
||||
"prettier": "^3.5.3",
|
||||
"pretty-quick": "^4.1.1",
|
||||
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^3.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || 22"
|
||||
"@babel/runtime-corejs2": "^7.23.2",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"commitizen": "^4.3.0",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"lint-staged": "^15.0.1",
|
||||
"postcss": "^8.4.31",
|
||||
"prettier": "^2.8.0",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"tailwindcss": "^3.3.3"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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" /> -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="The fastest way to sign PDFs & request signatures from others" />
|
||||
<!-- <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> -->
|
||||
<!--
|
||||
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="%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.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"
|
||||
integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
|
||||
|
||||
<title>Open sign</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 86 KiB |
@@ -1 +0,0 @@
|
||||
import "@testing-library/jest-dom";
|
||||
@@ -0,0 +1,150 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Routes, Route, BrowserRouter } from "react-router-dom";
|
||||
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 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";
|
||||
|
||||
function App() {
|
||||
const [isloading, setIsLoading] = useState(true);
|
||||
useEffect(() => {
|
||||
handleCredentials();
|
||||
}, []);
|
||||
|
||||
const handleCredentials = () => {
|
||||
const appId = process.env.REACT_APP_APPID;
|
||||
const baseurl = process.env.REACT_APP_SERVERURL;
|
||||
const appName = "contracts";
|
||||
try {
|
||||
localStorage.setItem("BaseUrl12", `${baseurl}/`);
|
||||
localStorage.setItem("AppID12", appId);
|
||||
localStorage.setItem("domain", appName);
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-[#eef1f5]">
|
||||
{isloading ? (
|
||||
<div
|
||||
style={{
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<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
|
||||
exact
|
||||
path="/subscription"
|
||||
element={<PlanSubscriptions />}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<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>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,183 +0,0 @@
|
||||
import { useState, useEffect, lazy } from "react";
|
||||
import { Routes, Route, BrowserRouter } from "react-router";
|
||||
import { pdfjs } from "react-pdf";
|
||||
import Form from "./pages/Form";
|
||||
import Report from "./pages/Report";
|
||||
import Dashboard from "./pages/Dashboard";
|
||||
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 Loader from "./primitives/Loader";
|
||||
import UserList from "./pages/UserList";
|
||||
import { serverUrl_fn } from "./constant/appinfo";
|
||||
import DocSuccessPage from "./pages/DocSuccessPage";
|
||||
import ValidateSession from "./primitives/ValidateSession";
|
||||
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
|
||||
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
|
||||
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
|
||||
const ChangePassword = lazy(() => import("./pages/ChangePassword"));
|
||||
const UserProfile = lazy(() => import("./pages/UserProfile"));
|
||||
const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
|
||||
const ManageSign = lazy(() => import("./pages/Managesign"));
|
||||
const AddAdmin = lazy(() => import("./pages/AddAdmin"));
|
||||
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
|
||||
const Preferences = lazy(() => import("./pages/Preferences"));
|
||||
const Login = lazy(() => import("./pages/Login"));
|
||||
|
||||
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(() => {
|
||||
handleCredentials();
|
||||
}, []);
|
||||
|
||||
const handleCredentials = () => {
|
||||
const appId = process.env.REACT_APP_APPID
|
||||
? process.env.REACT_APP_APPID
|
||||
: "opensign";
|
||||
const baseurl = serverUrl_fn();
|
||||
try {
|
||||
localStorage.setItem("baseUrl", `${baseurl}/`);
|
||||
localStorage.setItem("parseAppId", appId);
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-base-200">
|
||||
{isloading ? (
|
||||
<AppLoader />
|
||||
) : (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route element={<ValidateRoute />}>
|
||||
<Route exact path="/" element={<LazyPage Page={Login} />} />
|
||||
<Route
|
||||
path="/addadmin"
|
||||
element={<LazyPage Page={AddAdmin} />}
|
||||
/>
|
||||
<Route
|
||||
path="/upgrade-2.1"
|
||||
element={<LazyPage Page={UpdateExistUserAdmin} />}
|
||||
/>
|
||||
</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} />}
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<ValidateSession>
|
||||
<HomeLayout />
|
||||
</ValidateSession>
|
||||
}
|
||||
>
|
||||
<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="/drive"
|
||||
element={<LazyPage Page={Opensigndrive} />}
|
||||
/>
|
||||
<Route
|
||||
path="/managesign"
|
||||
element={<LazyPage Page={ManageSign} />}
|
||||
/>
|
||||
<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
|
||||
path="/recipientSignPdf/:docId"
|
||||
element={<PdfRequestFiles />}
|
||||
/>
|
||||
<Route path="/users" element={<UserList />} />
|
||||
<Route
|
||||
path="/preferences"
|
||||
element={<LazyPage Page={Preferences} />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/success" element={<DocSuccessPage />} />
|
||||
<Route path="*" element={<PageNotFound />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -12,17 +12,8 @@ import {
|
||||
MouseTransition,
|
||||
Preview
|
||||
} from "react-dnd-multi-backend";
|
||||
import DragElement from "./components/pdf/DragElement.jsx";
|
||||
import Parse from "parse";
|
||||
import "./polyfills";
|
||||
import { serverUrl_fn } from "./constant/appinfo";
|
||||
import "./i18n";
|
||||
|
||||
const appId =
|
||||
import.meta.env.VITE_APPID || process.env.REACT_APP_APPID || "opensign";
|
||||
const serverUrl = serverUrl_fn();
|
||||
Parse.initialize(appId);
|
||||
Parse.serverURL = serverUrl;
|
||||
import DragElement from "./microappComponent/DragElement";
|
||||
import TagManager from "react-gtm-module";
|
||||
|
||||
const HTML5toTouch = {
|
||||
backends: [
|
||||
@@ -53,6 +44,12 @@ const generatePreview = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
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(
|
||||
|
After Width: | Height: | Size: 805 B |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 5.2 KiB |
@@ -1,184 +1,184 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="691.365" height="586.346" viewBox="0 0 691.365 586.346">
|
||||
<defs>
|
||||
<linearGradient id="linear-gradient" x1="0.5" y1="1" x2="0.5" y2="-0.001" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0" stop-color="gray" stop-opacity="0.251"/>
|
||||
<stop offset="0.54" stop-color="gray" stop-opacity="0.122"/>
|
||||
<stop offset="1" stop-color="gray" stop-opacity="0.102"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Group_644" data-name="Group 644" transform="translate(-588.635 -80)">
|
||||
<g id="Group_642" data-name="Group 642" transform="translate(22538.635 6999)">
|
||||
<path id="Path_472" data-name="Path 472" d="M482.764,637.675c51.612-29.22,67.336-29.693,124.954-17.169,81.646,17.758,88.43-49.93,108.306-186.933,33.946-233.98-108.306-295.737-108.306-295.737s-94.6-8.1-150.536-38.963C377.726,55.027,286.9,49.443,207.046,86.542c-51.069,23.7-93.125,56.714-100.31,102.034-20.09,126.746,12.9,196.58-48.819,237.954S48.944,581.5,96.263,577.564c34.968-2.906,80.842,52.415,130.687,60.111,17.382,2.684,88.7-13.043,99.578,0C341.327,655.4,464.763,647.87,482.764,637.675Z" transform="translate(-21979.895 -6980.982)" fill="#00ddf1" opacity="0.1"/>
|
||||
<g id="Group_641" data-name="Group 641" transform="translate(-21545.08 -6816.88)">
|
||||
<rect id="Rectangle_263" data-name="Rectangle 263" width="217.867" height="15.073" transform="translate(0 39.052)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_264" data-name="Rectangle 264" width="217.867" height="15.073" transform="translate(0 104.137)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_265" data-name="Rectangle 265" width="217.867" height="15.073" transform="translate(0 171.964)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_266" data-name="Rectangle 266" width="13.702" height="39.052" transform="translate(19.183)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_267" data-name="Rectangle 267" width="13.702" height="2.74" transform="translate(19.183 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_268" data-name="Rectangle 268" width="13.702" height="2.74" transform="translate(19.183 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_269" data-name="Rectangle 269" width="13.702" height="39.052" transform="translate(39.737)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_270" data-name="Rectangle 270" width="13.702" height="2.74" transform="translate(39.737 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_271" data-name="Rectangle 271" width="13.702" height="2.74" transform="translate(39.737 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_272" data-name="Rectangle 272" width="13.702" height="39.052" transform="translate(60.29)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_273" data-name="Rectangle 273" width="13.702" height="2.74" transform="translate(60.29 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_274" data-name="Rectangle 274" width="13.702" height="2.74" transform="translate(60.29 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_275" data-name="Rectangle 275" width="13.702" height="39.052" transform="translate(80.844)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_276" data-name="Rectangle 276" width="13.702" height="2.74" transform="translate(80.844 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_277" data-name="Rectangle 277" width="13.702" height="2.74" transform="translate(80.844 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_278" data-name="Rectangle 278" width="13.702" height="39.052" transform="translate(101.397)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_279" data-name="Rectangle 279" width="13.702" height="2.74" transform="translate(101.397 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_280" data-name="Rectangle 280" width="13.702" height="2.74" transform="translate(101.397 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_281" data-name="Rectangle 281" width="13.702" height="39.052" transform="translate(121.95)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_282" data-name="Rectangle 282" width="13.702" height="2.74" transform="translate(121.95 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_283" data-name="Rectangle 283" width="13.702" height="2.74" transform="translate(121.95 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_284" data-name="Rectangle 284" width="13.702" height="39.052" transform="translate(142.504)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_285" data-name="Rectangle 285" width="13.702" height="2.74" transform="translate(142.504 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_286" data-name="Rectangle 286" width="13.702" height="2.74" transform="translate(142.504 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_287" data-name="Rectangle 287" width="13.702" height="39.052" transform="matrix(0.894, -0.448, 0.448, 0.894, 135.847, 138.025)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_288" data-name="Rectangle 288" width="13.702" height="2.74" transform="matrix(0.894, -0.448, 0.448, 0.894, 138.614, 143.541)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_289" data-name="Rectangle 289" width="13.702" height="2.74" transform="matrix(0.894, -0.448, 0.448, 0.894, 148.128, 162.529)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_290" data-name="Rectangle 290" width="13.702" height="39.052" transform="translate(19.183 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_291" data-name="Rectangle 291" width="13.702" height="2.74" transform="translate(19.183 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_292" data-name="Rectangle 292" width="13.702" height="2.74" transform="translate(19.183 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_293" data-name="Rectangle 293" width="13.702" height="39.052" transform="translate(39.737 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_294" data-name="Rectangle 294" width="13.702" height="2.74" transform="translate(39.737 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_295" data-name="Rectangle 295" width="13.702" height="2.74" transform="translate(39.737 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_296" data-name="Rectangle 296" width="13.702" height="39.052" transform="translate(60.29 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_297" data-name="Rectangle 297" width="13.702" height="2.74" transform="translate(60.29 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_298" data-name="Rectangle 298" width="13.702" height="2.74" transform="translate(60.29 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_299" data-name="Rectangle 299" width="13.702" height="39.052" transform="translate(145.93 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_300" data-name="Rectangle 300" width="13.702" height="2.74" transform="translate(145.93 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_301" data-name="Rectangle 301" width="13.702" height="2.74" transform="translate(145.93 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_302" data-name="Rectangle 302" width="13.702" height="39.052" transform="translate(166.483 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_303" data-name="Rectangle 303" width="13.702" height="2.74" transform="translate(166.483 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_304" data-name="Rectangle 304" width="13.702" height="2.74" transform="translate(166.483 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_305" data-name="Rectangle 305" width="13.702" height="39.052" transform="translate(187.036 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_306" data-name="Rectangle 306" width="13.702" height="2.74" transform="translate(187.036 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_307" data-name="Rectangle 307" width="13.702" height="2.74" transform="translate(187.036 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_308" data-name="Rectangle 308" width="13.702" height="39.052" transform="translate(19.183 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_309" data-name="Rectangle 309" width="13.702" height="2.74" transform="translate(19.183 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_310" data-name="Rectangle 310" width="13.702" height="2.74" transform="translate(19.183 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_311" data-name="Rectangle 311" width="13.702" height="39.052" transform="translate(39.737 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_312" data-name="Rectangle 312" width="13.702" height="2.74" transform="translate(39.737 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_313" data-name="Rectangle 313" width="13.702" height="2.74" transform="translate(39.737 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_314" data-name="Rectangle 314" width="13.702" height="39.052" transform="translate(60.29 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_315" data-name="Rectangle 315" width="13.702" height="2.74" transform="translate(60.29 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_316" data-name="Rectangle 316" width="13.702" height="2.74" transform="translate(60.29 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_317" data-name="Rectangle 317" width="13.702" height="39.052" transform="translate(80.844 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_318" data-name="Rectangle 318" width="13.702" height="2.74" transform="translate(80.844 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_319" data-name="Rectangle 319" width="13.702" height="2.74" transform="translate(80.844 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_320" data-name="Rectangle 320" width="13.702" height="39.052" transform="translate(101.397 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_321" data-name="Rectangle 321" width="13.702" height="2.74" transform="translate(101.397 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_322" data-name="Rectangle 322" width="13.702" height="2.74" transform="translate(101.397 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_323" data-name="Rectangle 323" width="13.702" height="39.052" transform="translate(121.95 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_324" data-name="Rectangle 324" width="13.702" height="2.74" transform="translate(121.95 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_325" data-name="Rectangle 325" width="13.702" height="2.74" transform="translate(121.95 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_473" data-name="Path 473" d="M797.862,159.09v35.571h-13.7V156.48Q791.039,157.693,797.862,159.09Z" transform="translate(-621.103 -155.61)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_326" data-name="Rectangle 326" width="13.702" height="2.74" transform="translate(163.057 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_327" data-name="Rectangle 327" width="13.702" height="2.74" transform="translate(163.057 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_474" data-name="Path 474" d="M827.862,165.719v30.83h-13.7V162.43c1.781.391,3.556.8,5.33,1.22Q823.7,164.636,827.862,165.719Z" transform="translate(-630.549 -157.483)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_475" data-name="Path 475" d="M827.862,166.285v.685h-13.7v-2.74h5.33Q823.7,165.2,827.862,166.285Z" transform="translate(-630.549 -158.05)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_328" data-name="Rectangle 328" width="13.702" height="2.74" transform="translate(183.611 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_476" data-name="Path 476" d="M691.358,287.276s-4.152-1.411-6.166,2.254a6.68,6.68,0,0,0,.089,6.426,9.4,9.4,0,0,0,9.146,5.179c4.35-.233,5.8-3.549,6.166-6.639a6.851,6.851,0,0,0-9.057-7.3Z" transform="translate(-589.693 -196.649)" fill="#00ddf1"/>
|
||||
<path id="Path_477" data-name="Path 477" d="M686.2,296.239a6.68,6.68,0,0,1-.089-6.426c2-3.659,6.166-2.254,6.166-2.254l.212-.075a6.762,6.762,0,0,1,4.885.11,6.762,6.762,0,0,0-5.762-.548l-.212.075s-4.152-1.411-6.166,2.254a6.68,6.68,0,0,0,.089,6.426,9.982,9.982,0,0,0,4.7,4.446A10.277,10.277,0,0,1,686.2,296.239Z" transform="translate(-589.706 -196.595)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_478" data-name="Path 478" d="M691.42,290.06s1.542,1.213,5.282,0" transform="translate(-591.9 -197.672)" fill="#00ddf1" opacity="0.2"/>
|
||||
<path id="Path_479" data-name="Path 479" d="M690.93,277.21s2.638,7.153,2.418,9.023l2.09-.219s-3.521-6.269-2.638-8.8Z" transform="translate(-591.746 -193.626)" fill="#00ddf1"/>
|
||||
<path id="Path_480" data-name="Path 480" d="M690.93,277.21s2.638,7.153,2.418,9.023l2.09-.219s-3.521-6.269-2.638-8.8Z" transform="translate(-591.746 -193.626)" fill="#00ddf1" opacity="0.2"/>
|
||||
<path id="Path_481" data-name="Path 481" d="M702.92,277.6s-4.159-2.124-6.015-.37-2.144,6.241-2.144,6.241S701.536,284.391,702.92,277.6Z" transform="translate(-592.952 -193.414)" fill="#00ddf1"/>
|
||||
<path id="Path_482" data-name="Path 482" d="M702.92,277.6s-4.159-2.124-6.015-.37-2.144,6.241-2.144,6.241S701.536,284.391,702.92,277.6Z" transform="translate(-592.952 -193.414)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_483" data-name="Path 483" d="M694.75,283.958s1.089-5.207,8.16-5.878" transform="translate(-592.949 -193.9)" fill="#00ddf1"/>
|
||||
<path id="Path_484" data-name="Path 484" d="M819.358,386.276s-4.152-1.411-6.166,2.254a6.68,6.68,0,0,0,.089,6.426,9.4,9.4,0,0,0,9.146,5.18c4.35-.233,5.8-3.549,6.166-6.639a6.851,6.851,0,0,0-9.057-7.3Z" transform="translate(-629.999 -227.823)" fill="#00ddf1"/>
|
||||
<path id="Path_485" data-name="Path 485" d="M814.2,395.239a6.68,6.68,0,0,1-.089-6.426c2-3.659,6.166-2.254,6.166-2.254l.212-.075a6.762,6.762,0,0,1,4.885.11,6.762,6.762,0,0,0-5.762-.548l-.212.075s-4.152-1.411-6.166,2.254a6.68,6.68,0,0,0,.089,6.426,9.982,9.982,0,0,0,4.7,4.446A10.276,10.276,0,0,1,814.2,395.239Z" transform="translate(-630.011 -227.769)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_486" data-name="Path 486" d="M819.42,389.06s1.542,1.213,5.282,0" transform="translate(-632.206 -228.846)" fill="#00ddf1" opacity="0.2"/>
|
||||
<path id="Path_487" data-name="Path 487" d="M818.93,376.21s2.638,7.153,2.419,9.023l2.09-.219s-3.522-6.269-2.638-8.8Z" transform="translate(-632.051 -224.799)" fill="#00ddf1"/>
|
||||
<path id="Path_488" data-name="Path 488" d="M818.93,376.21s2.638,7.153,2.419,9.023l2.09-.219s-3.522-6.269-2.638-8.8Z" transform="translate(-632.051 -224.799)" fill="#00ddf1" opacity="0.2"/>
|
||||
<path id="Path_489" data-name="Path 489" d="M830.92,376.6s-4.159-2.124-6.015-.37-2.144,6.241-2.144,6.241S829.536,383.391,830.92,376.6Z" transform="translate(-633.257 -224.588)" fill="#00ddf1"/>
|
||||
<path id="Path_490" data-name="Path 490" d="M830.92,376.6s-4.159-2.124-6.015-.37-2.144,6.241-2.144,6.241S829.536,383.391,830.92,376.6Z" transform="translate(-633.257 -224.588)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_491" data-name="Path 491" d="M822.75,382.958s1.089-5.207,8.16-5.878" transform="translate(-633.254 -225.073)" fill="#00ddf1"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group_643" data-name="Group 643" transform="translate(7 -63)">
|
||||
<g id="Group_639" data-name="Group 639" transform="translate(21233 8372)">
|
||||
<ellipse id="Ellipse_73" data-name="Ellipse 73" cx="49.851" cy="8.007" rx="49.851" ry="8.007" transform="translate(-20118.443 -7772.555)" fill="#00abf6" opacity="0.1"/>
|
||||
<rect id="Rectangle_182" data-name="Rectangle 182" width="3.208" height="72.224" transform="translate(-20070.199 -7917.934)" fill="#535461"/>
|
||||
<path id="Path_268" data-name="Path 268" d="M1025.906,587.251l-.2,3.254-.287,4.6-.118,1.913-.281,4.6-.123,1.913-.286,4.6-3.233,52.22a8.851,8.851,0,0,1-8.83,8.268H995.731a8.841,8.841,0,0,1-8.82-8.3l-3.244-52.225-.281-4.6-.117-1.913-.292-4.6-.117-1.913-.281-4.6-.2-3.254a4.6,4.6,0,0,1,4.574-4.865h34.391a4.6,4.6,0,0,1,4.569,4.9Z" transform="translate(-21072.734 -8433.181)" fill="#565661"/>
|
||||
<path id="Path_269" data-name="Path 269" d="M990.949,517.36l-.287,4.589H948.106l-.286-4.589Z" transform="translate(-21037.977 -8360.036)" fill="#9d9cb5"/>
|
||||
<path id="Path_270" data-name="Path 270" d="M990.93,530.08l-.281,4.594H948.9l-.286-4.594Z" transform="translate(-21038.363 -8366.248)" fill="#9d9cb5"/>
|
||||
<path id="Path_271" data-name="Path 271" d="M990.912,542.8l-.287,4.589H949.686L949.4,542.8Z" transform="translate(-21038.748 -8372.46)" fill="#9d9cb5"/>
|
||||
<path id="Path_272" data-name="Path 272" d="M933.875,490.464c18.684,13.517,26.466,34.656,26.466,34.656s-22.511-.773-41.2-14.289-26.46-34.651-26.46-34.651S915.2,476.953,933.875,490.464Z" transform="translate(-21028.934 -8381.328)" fill="#00abf6"/>
|
||||
<path id="Path_273" data-name="Path 273" d="M892.68,476.18s23.365,12.969,30.865,23.708,36.8,25.233,36.8,25.233" transform="translate(-21028.934 -8381.328)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<path id="Path_274" data-name="Path 274" d="M975.393,443.457c10.171,7.352,14.407,18.863,14.407,18.863s-12.279-.425-22.424-7.776S953,435.68,953,435.68,965.227,436.1,975.393,443.457Z" transform="translate(-21058.393 -8361.549)" fill="#00abf6"/>
|
||||
<path id="Path_275" data-name="Path 275" d="M953,435.68s12.719,7.06,16.8,12.9,20.03,13.737,20.03,13.737" transform="translate(-21058.393 -8361.549)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<path id="Path_276" data-name="Path 276" d="M1040.3,470.483c-11.68,12.836-14.151,29.612-14.151,29.612s16.469-4.042,28.139-16.883,14.151-29.612,14.151-29.612S1051.981,457.647,1040.3,470.483Z" transform="translate(-21094.119 -8370.301)" fill="#00abf6"/>
|
||||
<path id="Path_277" data-name="Path 277" d="M1068.45,453.61s-15.215,13.159-19.083,22.224S1026.15,500.1,1026.15,500.1" transform="translate(-21094.119 -8370.306)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/>
|
||||
</g>
|
||||
<g id="Group_638" data-name="Group 638" transform="translate(-40 36)">
|
||||
<rect id="Rectangle_254" data-name="Rectangle 254" width="12.343" height="37.459" transform="translate(741.459 392.675)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_255" data-name="Rectangle 255" width="12.343" height="39.629" transform="translate(1073.647 390.726)" fill="#3f3d56"/>
|
||||
<path id="Path_418" data-name="Path 418" d="M597.889,523.665v8.446l-98.7.909-1.052.02-37.511.344-.9.013-11.4.1h-.338l-69.5.643L264.408,535.2h-.1l-29.007.273-.312.007L106.1,536.658v-9.745l30.306-18.19,60.808-.507,1.3-.007.429-.007,1.3-.019,29.2-.24,1.306-.007.416-.007,1.3-.006,9.362-.078.247-.006,23.1-.195h.188l180.214-1.52,4.411-.032h.442l3.365-.033,24.459-.2,1.3-.02,1.2-.013h.624l7.607-.058,1.507-.02,2.02-.013,6.334-.058,10.05-.084,1.3-.013,2.261-.013h.422l48.536-.416Z" transform="translate(566.82 43)" fill="#65617d"/>
|
||||
<path id="Path_419" data-name="Path 419" d="M597.889,523.665v8.446l-98.7.909-1.052.02-37.511.344-.9.013-11.4.1h-.338l-69.5.643L264.408,535.2h-.1l-29.007.273-.312.007L106.1,536.658v-9.745l30.306-18.19,60.808-.507,1.3-.007.429-.007,1.3-.019,29.2-.24,1.306-.007.416-.007,1.3-.006,9.362-.078.247-.006,23.1-.195h.188l180.214-1.52,4.411-.032h.442l3.365-.033,24.459-.2,1.3-.02,1.2-.013h.624l7.607-.058,1.507-.02,2.02-.013,6.334-.058,10.05-.084,1.3-.013,2.261-.013h.422l48.536-.416Z" transform="translate(566.82 43)" opacity="0.2"/>
|
||||
<path id="Path_420" data-name="Path 420" d="M358.914,656.57v15.91a3.982,3.982,0,0,1-2.274,3.6,3.9,3.9,0,0,1-1.624.39l-22.673.481a3.9,3.9,0,0,1-1.754-.37,3.976,3.976,0,0,1-2.319-3.619V656.57Z" transform="translate(437.441 -122.381)" fill="#3f3d56"/>
|
||||
<path id="Path_421" data-name="Path 421" d="M358.914,656.57v15.91a3.982,3.982,0,0,1-2.274,3.6,3.9,3.9,0,0,1-1.624.39l-22.673.481a3.9,3.9,0,0,1-1.754-.37,3.976,3.976,0,0,1-2.319-3.619V656.57Z" transform="translate(437.441 -122.381)" opacity="0.1"/>
|
||||
<path id="Path_422" data-name="Path 422" d="M360.914,656.57v15.91a3.982,3.982,0,0,1-2.274,3.6,3.9,3.9,0,0,1-1.624.39l-22.673.481a3.9,3.9,0,0,1-1.754-.37,3.976,3.976,0,0,1-2.319-3.619V656.57Z" transform="translate(436.74 -122.381)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_256" data-name="Rectangle 256" width="30.644" height="20.464" transform="translate(1046.362 534.189)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_257" data-name="Rectangle 257" width="30.644" height="20.464" transform="translate(1046.362 534.189)" opacity="0.1"/>
|
||||
<rect id="Rectangle_258" data-name="Rectangle 258" width="30.644" height="20.464" transform="translate(1045.062 534.189)" fill="#3f3d56"/>
|
||||
<path id="Path_423" data-name="Path 423" d="M223.943,265.82v3.723l-12.343-.318v-3.4Z" transform="translate(529.859 126.848)" opacity="0.1"/>
|
||||
<path id="Path_424" data-name="Path 424" d="M358.741,271.246,343.909,377.789a2.794,2.794,0,0,1-2.839,2.41L222.314,377.12l-2.254-.058a2.794,2.794,0,0,1-2.7-3.164l16.651-111.533a2.794,2.794,0,0,1,2.949-2.358l119.186,8.069a2.787,2.787,0,0,1,2.6,3.17Z" transform="translate(476.307 16.556)" fill="#65617d"/>
|
||||
<path id="Path_425" data-name="Path 425" d="M169.158,99.16,156.6,197.472l109.142,3.463,13.422-94.85Z" transform="translate(549.128 185.237)" fill="#00ddf1"/>
|
||||
<rect id="Rectangle_259" data-name="Rectangle 259" width="132.965" height="112.605" rx="5.33" transform="translate(853.849 282.877)" fill="#65617d"/>
|
||||
<rect id="Rectangle_260" data-name="Rectangle 260" width="120.836" height="93.765" transform="translate(860.125 290.458)" fill="#00ddf1"/>
|
||||
<path id="Path_426" data-name="Path 426" d="M735.273,262.82v2.462l-12.343.253V262.82Z" transform="translate(350.717 127.899)" opacity="0.1"/>
|
||||
<path id="Path_427" data-name="Path 427" d="M833.626,371a2,2,0,0,1-1.949,2.131l-121.615,2.514a2,2,0,0,1-2.033-1.813L697.575,265.156a2,2,0,0,1,1.9-2.189l120.693-6,1.3-.065a1.994,1.994,0,0,1,2.092,1.819Z" transform="translate(308.06 17.643)" fill="#65617d"/>
|
||||
<path id="Path_428" data-name="Path 428" d="M628.93,100.238,739.157,94.82l9.31,98.748-109.356,2.6Z" transform="translate(383.65 186.757)" fill="#00ddf1"/>
|
||||
<path id="Path_429" data-name="Path 429" d="M899.741,371a2,2,0,0,1-1.949,2.131L886.28,256.963l1.3-.065a2,2,0,0,1,2.092,1.819Z" transform="translate(241.945 17.643)" opacity="0.1"/>
|
||||
<rect id="Rectangle_261" data-name="Rectangle 261" width="12.343" height="11.096" transform="translate(741.459 419.038)" opacity="0.1"/>
|
||||
<path id="Path_431" data-name="Path 431" d="M281.574,483.65v3.229l-30.644.819V483.65Z" transform="translate(516.08 50.533)" opacity="0.1"/>
|
||||
<path id="Path_432" data-name="Path 432" d="M390.8,483.886V593.158a2.6,2.6,0,0,1-2.5,2.566l-124.506,3.313h-.033a2.6,2.6,0,0,1-2.566-2.566V483.886a2.6,2.6,0,0,1,2.566-2.566H388.23A2.6,2.6,0,0,1,390.8,483.886Z" transform="translate(460.942 -60.983)" fill="#65617d"/>
|
||||
<path id="Path_434" data-name="Path 434" d="M194.6,319.15H309.914v96.311L194.6,418.06Z" transform="translate(535.815 108.164)" fill="#d4dfec"/>
|
||||
<rect id="Rectangle_262" data-name="Rectangle 262" width="12.343" height="11.317" transform="translate(1073.647 419.038)" opacity="0.1"/>
|
||||
<path id="Path_435" data-name="Path 435" d="M709.574,483.65v4.164L678.93,487V483.65Z" transform="translate(366.132 50.533)" opacity="0.1"/>
|
||||
<path id="Path_436" data-name="Path 436" d="M797.782,483.886V596.471a2.6,2.6,0,0,1-2.6,2.566h0l-124.532-3.313a2.6,2.6,0,0,1-2.5-2.566V483.886a2.6,2.6,0,0,1,2.566-2.566H795.19a2.6,2.6,0,0,1,2.592,2.566Z" transform="translate(318.365 -60.983)" fill="#65617d"/>
|
||||
<path id="Path_437" data-name="Path 437" d="M866.289,483.886V596.471a2.6,2.6,0,0,1-2.6,2.566h0V481.32h0a2.6,2.6,0,0,1,2.6,2.566Z" transform="translate(249.859 -60.983)" opacity="0.1"/>
|
||||
<path id="Path_438" data-name="Path 438" d="M713.414,319.15H598.1v96.311l115.314,2.6Z" transform="translate(394.451 108.164)" fill="#d4dfec"/>
|
||||
<path id="Path_439" data-name="Path 439" d="M593.3,483.886V593.158a2.6,2.6,0,0,1-2.5,2.566l-124.506,3.313h0a2.6,2.6,0,0,1-2.6-2.566V483.886a2.6,2.6,0,0,1,2.566-2.566H590.73A2.6,2.6,0,0,1,593.3,483.886Z" transform="translate(389.997 -60.983)" fill="#65617d"/>
|
||||
<path id="Path_440" data-name="Path 440" d="M397.1,319.15H512.414v96.311L397.1,418.06Z" transform="translate(464.87 108.164)" fill="#d4dfec"/>
|
||||
<path id="Path_441" data-name="Path 441" d="M597.889,533.65V542.1l-98.7.909-1.052.02-37.511.344-.9.013-11.4.1h-.338l-69.5.643-114.086,1.059h-.1l-29.007.273-.312.007L106.1,546.643V536.9l131.562-.864h.312l26.629-.182h.123l183.9-1.215h.188l.11-.006h.305l3.112-.019h.948l48.393-.325,2.858-.013.637-.007Z" transform="translate(566.82 33.015)" opacity="0.1"/>
|
||||
<circle id="Ellipse_99" data-name="Ellipse 99" cx="33.347" cy="33.347" r="33.347" transform="translate(887.196 363.44)" fill="#fbbebe"/>
|
||||
<path id="Path_442" data-name="Path 442" d="M383.74,592.973a.806.806,0,0,1,.065-.227c.65-2.332,1.806-6.276,2.67-9.427.11-.39.214-.773.312-1.137.955-3.554,2.813-11.876,3.846-17.066.364-1.826.65-3.248.708-3.963.13-1.1,2.085-5.795,4.3-11.194,1.553-3.794,3.248-7.939,4.489-11.434a39.876,39.876,0,0,0,1.9-6.4,3.313,3.313,0,0,0,0-1.384,9.543,9.543,0,0,1-.312-2.274h0c-.149-4.028,1.052-10.394,2.91-13.065,2.274-3.248,3.9-15.371,3.9-15.371s3.365.487,3.9-2.293a4,4,0,0,0,.065-.91,4.815,4.815,0,0,0-.058-.721c-.578-4.054,9.563-8.368,15.267-9.3a12.769,12.769,0,0,1,1.949-.182,17.694,17.694,0,0,0,4.06-.935q.909-.279,1.949-.65c2.391-.806,5.249-1.871,8.283-3.04,11.408-4.424,25.22-10.394,25.22-10.394.1-.461.208-.9.331-1.338a18,18,0,0,1,10.622-12.3h0v-1.3c0-2.241-.1-4.548-.195-6.847,0-.448-.039-.9-.065-1.345v-.734c0-.455-.046-.91-.071-1.358l-.117-.091a10.66,10.66,0,0,1-2.534-2.729,9.916,9.916,0,0,1-.923-1.949c-.812-2.2-1-4.658-1.546-6.984l-.117-.481a54.579,54.579,0,0,0-2.6-6.873c-1.436-3.521-2.54-7.146-3.638-10.817q-1-3.333-2.02-6.678l-1.026-3.4c-.195-.65-.39-1.3-.539-1.949h0a8.627,8.627,0,0,1-.169-.91h0a6.5,6.5,0,0,1,.156-2.995,20.854,20.854,0,0,1,.858-1.949,5.113,5.113,0,0,0,.325-.9h0a1.947,1.947,0,0,0,.052-.208,3.667,3.667,0,0,0,.071-.7,11.993,11.993,0,0,0-.338-2.527.045.045,0,0,1,0-.032,1.661,1.661,0,0,0-.032-.175,16.564,16.564,0,0,1-.234-1.949,18.483,18.483,0,0,1,2.254-10.115l.123-.234a16.383,16.383,0,0,1,1.2-1.806h0a9.122,9.122,0,0,1,.65-.793,2.594,2.594,0,0,1-3.606-.936,2.475,2.475,0,0,1-.149-.292c-.033-.078-.059-.162-.085-.247-.455-1.592.338-3.287,1.377-4.58a12.012,12.012,0,0,1,3.729-3.053h0a2.51,2.51,0,0,1,.26-.136,11.125,11.125,0,0,1,1.715-.689h0a9.781,9.781,0,0,1,1.059-.286l-2.41-.221-.974-.091A9.414,9.414,0,0,1,479,361.527h.7a7.625,7.625,0,0,1,1.02.084A2.885,2.885,0,0,1,479,358.8a9.362,9.362,0,0,0,5.97,1.988h.247a19.494,19.494,0,0,1-.221-2.124h0a21.545,21.545,0,0,1,0-2.9,5.483,5.483,0,0,0,4.4,1.468l-1.351-1.884a6.165,6.165,0,0,0,4.262,1.332l.312-.033a4.547,4.547,0,0,0,.773-.149h.058c-.123-.156-.234-.318-.344-.487l-.039-.078a6.282,6.282,0,0,1-.65-1.358,4.284,4.284,0,0,1-.156-.565h0a5.684,5.684,0,0,1,.292-3.541c2.1,1.754,4.4,4.151,7.146,2.339s2.839-5.139,7.146-4.8a6.626,6.626,0,0,1,5.464,3.976,4.385,4.385,0,0,1,4.983-1.137,4.45,4.45,0,0,1,2.521,4.476c1.786-.448,3.71-.89,5.425-.214s2.82,2.865,1.845,4.327a.931.931,0,0,1-.091.13c-.8,1.033-2.566,1.6-2.378,2.9l.273.195.845.611q4.97,3.586,9.68,7.536c1.611,1.351,3.326,2.975,3.437,5.087a5.617,5.617,0,0,1-.156,1.54h0c-.1.507-.247,1.013-.357,1.52a12.676,12.676,0,0,0-.221,3.93h0c.045.383.1.76.175,1.143.994,5.022,5.009,9.167,5.405,14.292v.364a11.546,11.546,0,0,1-.045,1.644c-.3,3.118-1.741,6.113-2.91,9.1-.072.182-.143.364-.208.546a90.5,90.5,0,0,0-4.548,18.294,65.14,65.14,0,0,1-1.3,6.555,17.365,17.365,0,0,1-3.307,6.776,10.536,10.536,0,0,1-2.183,1.949,12.132,12.132,0,0,1-1.722.916v.039c.071.4.13.793.2,1.189.039.208.071.416.11.65h0a.089.089,0,0,0,0,.039c.546,3.073,1.169,6.081,1.826,8.939l.331,1.423c8.186,2.956,9.154,9.011,9.212,11.33a6.259,6.259,0,0,1,0,.929s12.343.325,17.216,3.9a13.21,13.21,0,0,0,2.93,1.494,53.917,53.917,0,0,0,6.009,1.877c1.046.273,2.137.539,3.248.8,1.949.448,4.015.884,6.009,1.3,5.321,1.072,10.33,1.949,12.87,2.6,6.061,1.533,19.925,14.825,19.925,14.825s1.624,13.409,3.248,15.364,4.548,8.829,4.548,8.829l10.745,19.106,4.548,8.036h2.923a4.97,4.97,0,0,1,2.085,1.364c.773.689,1.631,1.546,2.384,2.326l1.247,1.332.78.864s4.723.117,6,3.852c3.846-.11,8.569.143,11.876,1.624a9.187,9.187,0,0,1,1.3.7,5.639,5.639,0,0,1,2.683,3.677,5.422,5.422,0,0,1,.117.935c0,.37,0,.741,0,1.1-.234,4.918-3.437,9.011-6.38,11.765l.208.37s-.1.234-.286.65c-.052.123-.117.26-.188.416h0c-.383.819-.994,2.046-1.793,3.482a41.136,41.136,0,0,1-6.217,8.556l-.188.195-37.511.344-.11-.13-7.276-8.679-3.619-4.314-.11-.136-.1,1.033-.351,3.443-.845,8.322-.045.416v.11l-.1.961.071-.961h-.188l-69.513.65-114.092,1.26v.546h-.091v-.546l-29.072.312Z" transform="translate(418.007 -14.269)" fill="url(#linear-gradient)"/>
|
||||
<path id="Path_443" data-name="Path 443" d="M586.185,511.522s-64.641,7.8-58.469,0c2.235-2.82,2.852-11.174,2.729-20.691-.039-2.891-.143-5.886-.292-8.868-.715-14.292-2.436-28.26-2.436-28.26s56.52-26.636,50.023-5.522c-2.6,8.53-1.748,20.51.227,31.755.578,3.281,1.247,6.5,1.949,9.55a223.892,223.892,0,0,0,6.269,22.036Z" transform="translate(367.711 -46.521)" fill="#fbbebe"/>
|
||||
<path id="Path_444" data-name="Path 444" d="M586.185,537.354s-64.641,7.8-58.469,0c2.235-2.82,2.852-11.174,2.729-20.692,9.855-4.21,22.394-1.072,22.394-1.072,13.2-2.631,21.666-2.118,27.078-.273A223.888,223.888,0,0,0,586.185,537.354Z" transform="translate(367.711 -72.353)" opacity="0.1"/>
|
||||
<path id="Path_445" data-name="Path 445" d="M413.346,651.973v-.546l114.092-1.241,69.513-.65h.195l-.071.961.1-.961h.123v-.11l.89-8.738.338-3.307.1-1.033.091-.864,4.015-13.389,4.015-13.37,3.378-11.258,5.158-17.2s-2.923-6.821-4.548-8.77-3.248-15.267-3.248-15.267-13.864-13.208-19.925-14.728c-2.54-.65-7.555-1.514-12.883-2.6-1.994-.4-4.041-.832-6.009-1.3-1.111-.253-2.2-.52-3.248-.786a57.086,57.086,0,0,1-6.009-1.858,13.448,13.448,0,0,1-2.924-1.488c-4.775-3.5-16.742-3.9-17.2-3.9h0a.269.269,0,0,1,0-.045v-.058a6.66,6.66,0,0,0,0-.819,5.208,5.208,0,0,0-.059-.91.82.82,0,0,0-.045-.325c-.032-.234-.071-.481-.13-.741a3.226,3.226,0,0,0-.078-.344,11.181,11.181,0,0,0-.812-2.267c-.045-.1-.1-.214-.156-.318a11.861,11.861,0,0,0-1.1-1.709c-.084-.11-.175-.227-.273-.338a12.721,12.721,0,0,0-1.767-1.715c-.175-.136-.357-.279-.546-.409a3.026,3.026,0,0,0-.331-.234l-.578-.377-.539-.312c-.474-.26-.974-.513-1.52-.754-.169-.078-.351-.149-.533-.221s-.468-.182-.708-.273h-.039l-.487-.195c-.37-.123-.747-.234-1.143-.344l-.6-.156-.65-.149-.65-.136c-.65-.13-1.345-.247-2.072-.338l-.741-.091-.76-.078-.786-.065-1.117-.065-1.3-.045a81.017,81.017,0,0,0-17.054,1.814,46.946,46.946,0,0,0-11.855-1.093h-.565l-.65.039h-.416l-.838.078a6.224,6.224,0,0,0-.845.1c-.279.039-.572.071-.851.117l-.429.071c-.422.071-.851.149-1.3.24q-.214.039-.429.1c-.565.123-1.13.273-1.689.442-.279.078-.559.169-.838.266-.143.039-.279.091-.416.143l-.416.149-.409.162-.487.195c-.247.1-.487.214-.728.331-.136.058-.273.123-.4.195s-.266.13-.39.2a18.03,18.03,0,0,0-4.034,2.846l-.331.318-.325.331c-.11.11-.214.227-.318.344a15.473,15.473,0,0,0-1.182,1.462c-.1.136-.2.273-.3.416a17.335,17.335,0,0,0-1.117,1.9c-.084.156-.162.318-.24.474a21.243,21.243,0,0,0-1.3,3.411q-.182.65-.331,1.3s-13.753,6.068-25.161,10.46c-3.034,1.169-5.892,2.228-8.283,3.027-.7.234-1.345.448-1.949.65a17.347,17.347,0,0,1-4.06.935,12.778,12.778,0,0,0-1.949.182c-5.243.851-14.227,4.548-15.208,8.257a2.4,2.4,0,0,0-.065.91v.071c.65,4.548-3.9,3.9-3.9,3.9s-1.624,12.019-3.9,15.267c-1.644,2.339-2.768,7.555-2.91,11.492h0a12.741,12.741,0,0,0,.286,3.658v.091c.26.871-.364,3.248-1.423,6.3-1.15,3.346-2.826,7.5-4.437,11.427-2.43,5.925-4.716,11.3-4.859,12.486-.058.526-.227,1.468-.461,2.683-.948,4.95-3.053,14.377-4.093,18.19-.773,2.845-1.9,6.743-2.67,9.42a.91.91,0,0,1-.065.227l29.124-.286v.546h.091Z" transform="translate(417.856 -73.042)" fill="#00ddf1"/>
|
||||
<path id="Path_446" data-name="Path 446" d="M445.44,607.57s37.03,19.165,50.348,16.891Z" transform="translate(396.391 -105.214)" opacity="0.1"/>
|
||||
<path id="Path_447" data-name="Path 447" d="M561.44,595.57s36.056,11.694,48.074,13.318S561.44,595.57,561.44,595.57Z" transform="translate(355.751 -101.01)" opacity="0.1"/>
|
||||
<path id="Path_448" data-name="Path 448" d="M585.443,530.686c-18.84,3.469-54.311,2.722-69.643,2.2,5.62-20.139,33.022-13.292,33.022-13.292C582.74,512.821,585.339,526.866,585.443,530.686Z" transform="translate(371.74 -73.754)" opacity="0.1"/>
|
||||
<path id="Path_449" data-name="Path 449" d="M585.443,528.686c-18.84,3.469-54.311,2.722-69.643,2.2,5.62-20.139,33.022-13.292,33.022-13.292C582.74,510.821,585.339,524.866,585.443,528.686Z" transform="translate(371.74 -73.053)" fill="#00ddf1"/>
|
||||
<path id="Path_450" data-name="Path 450" d="M776.94,672.008s22.192-3.411,22.628,6.334S788.25,694.9,788.25,694.9Z" transform="translate(280.251 -127.587)" fill="#fbbebe"/>
|
||||
<path id="Path_451" data-name="Path 451" d="M578.17,479.936a30.558,30.558,0,0,1-2.923,1.13c-7,2.417-14.065,4.853-21.439,5.736s-15.111.1-21.543-3.573a18.685,18.685,0,0,1-1.949-1.267c-.715-14.292-2.436-28.26-2.436-28.26s56.52-26.636,50.023-5.522C575.318,456.711,576.195,468.69,578.17,479.936Z" transform="translate(367.508 -46.521)" opacity="0.1"/>
|
||||
<path id="Path_452" data-name="Path 452" d="M536.9,360.29a6.153,6.153,0,0,1-5.405-1.137l1.351,1.878a5.5,5.5,0,0,1-4.4-1.462,22.146,22.146,0,0,0,.208,5.035,9.433,9.433,0,0,1-6.211-1.949,2.872,2.872,0,0,0,1.715,2.8,9.316,9.316,0,0,0-10.135,6.958l3.391.312a12,12,0,0,0-6.782,4.138c-1.039,1.3-1.832,2.962-1.377,4.548s2.651,2.6,3.846,1.468a18.151,18.151,0,0,0-3.963,14.806,9.4,9.4,0,0,1,.3,3.417,28.993,28.993,0,0,1-1.228,3.047c-.65,1.9-.032,3.956.546,5.847l3.047,10.018c1.1,3.619,2.2,7.244,3.645,10.745a53.328,53.328,0,0,1,2.6,6.828c.819,3.144.851,6.588,2.6,9.336a12.948,12.948,0,0,0,4.665,4.112c6.425,3.677,14.188,4.457,21.542,3.573s14.409-3.32,21.406-5.736a20.141,20.141,0,0,0,4.444-1.949c4.84-3.17,5.918-9.654,6.821-15.364a87.242,87.242,0,0,1,4.736-18.71c1.416-3.567,3.248-7.185,2.936-11.005-.4-5.074-4.411-9.186-5.405-14.175a12.344,12.344,0,0,1,.045-5.048,11.473,11.473,0,0,0,.513-3.053c-.11-2.092-1.826-3.7-3.437-5.048q-5.243-4.379-10.823-8.277c-.188-1.3,1.579-1.858,2.378-2.884,1.13-1.449-.039-3.748-1.754-4.424s-3.638-.234-5.425.214a4.281,4.281,0,0,0-7.5-3.32,6.627,6.627,0,0,0-5.47-3.95c-4.307-.338-4.392,2.956-7.146,4.768s-5.028-.578-7.146-2.319A6.029,6.029,0,0,0,536.9,360.29Z" transform="translate(374.5 -15.625)" fill="#3f3d56"/>
|
||||
<path id="Path_453" data-name="Path 453" d="M768.171,669.128s-.578,1.377-1.644,3.372a44.83,44.83,0,0,1-5.847,8.556l-.175.188h-1.052l-37.511.344h-.9l-.11-.13-7.276-8.634-2.741-3.255-.11-.136-.234-.273-.13-.156-.169-.2-5.334-6.328,1.949-8.121,3.32-13.682.084-.331.65-2.6.461-1.949,1.546-6.354,5.3-21.861,9.42,4.872,9.933,17.541,5.334,9.42h2.923c.552,0,1.819,1.065,3.092,2.293.455.435.91.9,1.3,1.3,1.156,1.2,2.072,2.222,2.072,2.222s4.736.1,6.009,3.833a4.537,4.537,0,0,1,.162.552,26.191,26.191,0,0,0,1.644,4.262c.838,1.826,1.891,3.9,2.956,5.97,1.981,3.8,4,7.419,4.736,8.731l.091.162Z" transform="translate(305.476 -105.217)" fill="#00ddf1"/>
|
||||
<path id="Path_454" data-name="Path 454" d="M615.681,653.093l-.065.929v.065l-.11,1.494-.864,12.025L613.6,682.093l-.3,4.08-.65,8.731v.11l-69.513.65L429.061,696.9v.546h-.091v-.728l.292-9.329.559-18.229L430.3,653.7l.045-1.54a6.841,6.841,0,0,1,7.419-6.607c3.066.26,7.049.578,11.811.916l1.949.143c27.5,1.91,78.322,4.379,129.606,1.3,1.078-.059,2.163-.123,3.248-.2,7.952-.5,15.9-1.143,23.764-1.949a6.841,6.841,0,0,1,7.536,7.3Z" transform="translate(402.161 -118.514)" opacity="0.1"/>
|
||||
<path id="Path_455" data-name="Path 455" d="M429.08,698.857v-.546l114.092-1.241,69.513-.65h.195v-.11l.65-8.731.279-3.9,1.065-14.689.838-11.629v-.286a6.893,6.893,0,0,0-.071-1.6.2.2,0,0,0,0-.065,6.751,6.751,0,0,0-1.3-3.014,6.847,6.847,0,0,0-6.12-2.6c-7.867.806-15.813,1.449-23.764,1.949-1.085.078-2.17.143-3.248.2-51.284,3.047-102.106.578-129.606-1.3l-1.949-.143c-4.762-.338-8.744-.65-11.811-.916a6.847,6.847,0,0,0-7.3,5.542,7.146,7.146,0,0,0-.117,1.065l-.442,14.4-.6,18.236-.286,9.329v.728Z" transform="translate(402.122 -119.926)" fill="#3f3d56"/>
|
||||
<path id="Path_456" data-name="Path 456" d="M615.925,661.692l-.8,9.959L613.937,686.5l-.3,3.716-.65,8.322-.039.416v.11h0l-.1.961.071-.961h-.188l-69.513.65L429.11,700.944v-.182l.318-9.329.65-18.229.416-12.116a4.132,4.132,0,0,1,1.988-3.411,4.061,4.061,0,0,1,2.456-.585c1.364.11,3.04.24,5,.383,3.177.234,7.094.507,11.655.793,8.445.533,19.041,1.1,31.183,1.572,33.782,1.3,79.388,1.735,122.707-2.059,1.949-.169,3.963-.357,5.931-.546a4.066,4.066,0,0,1,2.261.429,3.346,3.346,0,0,1,.559.351,3.989,3.989,0,0,1,1.3,1.6,2.315,2.315,0,0,1,.162.4A4.08,4.08,0,0,1,615.925,661.692Z" transform="translate(402.112 -122.559)" opacity="0.1"/>
|
||||
<path id="Path_457" data-name="Path 457" d="M360.914,677.87v2.072a3.982,3.982,0,0,1-2.274,3.6l-26.051.5a3.976,3.976,0,0,1-2.319-3.619v-1.949Z" transform="translate(436.74 -129.843)" opacity="0.1"/>
|
||||
<path id="Path_458" data-name="Path 458" d="M280.294,512.532l-33.944.65V507.55l33.944-.65Z" transform="translate(517.685 42.387)" fill="#3f3d56"/>
|
||||
<path id="Path_459" data-name="Path 459" d="M280.294,512.532l-33.944.65V507.55l33.944-.65Z" transform="translate(517.685 42.387)" opacity="0.1"/>
|
||||
<path id="Path_460" data-name="Path 460" d="M282.294,512.532l-33.944.65V507.55l33.944-.65Z" transform="translate(516.984 42.387)" fill="#3f3d56"/>
|
||||
<path id="Path_461" data-name="Path 461" d="M552.228,360.039A6.334,6.334,0,0,1,551,357.22a6.165,6.165,0,0,0,.273,2.988A5.847,5.847,0,0,0,552.228,360.039Z" transform="translate(359.434 -17.505)"/>
|
||||
<path id="Path_462" data-name="Path 462" d="M540.1,366.9a22.83,22.83,0,0,1-.24-2.4c0,.8,0,1.6,0,2.41Z" transform="translate(363.311 -20.056)"/>
|
||||
<path id="Path_463" data-name="Path 463" d="M621.706,398.627a11.912,11.912,0,0,1,.234-1.722,11.7,11.7,0,0,0,.52-3.047,4.008,4.008,0,0,0-.1-.689c-.11.6-.286,1.2-.416,1.787a12.433,12.433,0,0,0-.234,3.671Z" transform="translate(334.652 -30.1)"/>
|
||||
<path id="Path_464" data-name="Path 464" d="M600.922,368.894l.708.513a12.8,12.8,0,0,0,1.67-1.442,2.384,2.384,0,0,0,.247-2.365,1.95,1.95,0,0,1-.247.416C602.5,367.042,600.734,367.607,600.922,368.894Z" transform="translate(341.923 -20.441)"/>
|
||||
<path id="Path_465" data-name="Path 465" d="M518.455,380.29a8,8,0,0,0-.565,1.481,11.8,11.8,0,0,1,3.248-1.228Z" transform="translate(371.008 -25.587)"/>
|
||||
<path id="Path_466" data-name="Path 466" d="M531.888,368.979a9.637,9.637,0,0,1-1.221-.8,2.758,2.758,0,0,0,.058.767,7.7,7.7,0,0,1,1.163.033Z" transform="translate(366.534 -21.345)"/>
|
||||
<path id="Path_467" data-name="Path 467" d="M587.579,428.33c-.266,3.151-1.735,6.185-2.923,9.173a87.2,87.2,0,0,0-4.736,18.717c-.9,5.71-1.949,12.194-6.821,15.358a19.488,19.488,0,0,1-4.437,1.949c-7,2.423-14.065,4.853-21.439,5.736s-15.111.11-21.543-3.567a12.993,12.993,0,0,1-4.664-4.112c-1.735-2.748-1.767-6.191-2.6-9.336a53.424,53.424,0,0,0-2.6-6.834c-1.436-3.5-2.54-7.146-3.638-10.739q-1.527-5.009-3.047-10.024a18.716,18.716,0,0,1-.741-3.086,11.644,11.644,0,0,0,.741,5.035q1.52,5.022,3.047,10.024c1.1,3.619,2.2,7.244,3.638,10.739a53.417,53.417,0,0,1,2.6,6.834c.819,3.144.851,6.587,2.6,9.336a12.993,12.993,0,0,0,4.664,4.112c6.432,3.677,14.188,4.45,21.543,3.567s14.409-3.313,21.439-5.736a19.487,19.487,0,0,0,4.437-1.949c4.84-3.164,5.918-9.647,6.821-15.358a87.2,87.2,0,0,1,4.736-18.717c1.416-3.56,3.248-7.179,2.936-11.005C587.592,428.408,587.579,428.369,587.579,428.33Z" transform="translate(374.359 -42.418)"/>
|
||||
<path id="Path_468" data-name="Path 468" d="M510.318,421.419v-.071a9.1,9.1,0,0,0-.3-3.411,17.272,17.272,0,0,1-.253-2.157,17.769,17.769,0,0,0,.253,4.106C510.123,420.393,510.24,420.906,510.318,421.419Z" transform="translate(373.864 -38.021)"/>
|
||||
<path id="Path_469" data-name="Path 469" d="M512.452,397.929a18.02,18.02,0,0,1,1.7-2.384c-1.189,1.137-3.385.123-3.846-1.455a3.788,3.788,0,0,0,0,1.949,2.852,2.852,0,0,0,2.15,1.89Z" transform="translate(373.712 -30.422)"/>
|
||||
<path id="Path_470" data-name="Path 470" d="M762.946,656.57s2.059,7.471,0,13.968S762.946,656.57,762.946,656.57Z" transform="translate(285.475 -122.381)" opacity="0.1"/>
|
||||
<path id="Path_471" data-name="Path 471" d="M776,668.3s.864,9.485-1.085,11.168S776,668.3,776,668.3Z" transform="translate(281.189 -126.49)" opacity="0.1"/>
|
||||
<line id="Line_88" data-name="Line 88" y1="0.65" x2="66.011" transform="translate(733.267 442.068)" fill="none" stroke="#00ddf1" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<line id="Line_89" data-name="Line 89" y1="0.643" x2="66.011" transform="translate(733.267 450.176)" fill="none" stroke="#00ddf1" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<line id="Line_90" data-name="Line 90" y1="0.65" x2="66.011" transform="translate(733.267 458.277)" fill="none" stroke="#00ddf1" stroke-miterlimit="10" stroke-width="2"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="691.365" height="586.346" viewBox="0 0 691.365 586.346">
|
||||
<defs>
|
||||
<linearGradient id="linear-gradient" x1="0.5" y1="1" x2="0.5" y2="-0.001" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0" stop-color="gray" stop-opacity="0.251"/>
|
||||
<stop offset="0.54" stop-color="gray" stop-opacity="0.122"/>
|
||||
<stop offset="1" stop-color="gray" stop-opacity="0.102"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Group_644" data-name="Group 644" transform="translate(-588.635 -80)">
|
||||
<g id="Group_642" data-name="Group 642" transform="translate(22538.635 6999)">
|
||||
<path id="Path_472" data-name="Path 472" d="M482.764,637.675c51.612-29.22,67.336-29.693,124.954-17.169,81.646,17.758,88.43-49.93,108.306-186.933,33.946-233.98-108.306-295.737-108.306-295.737s-94.6-8.1-150.536-38.963C377.726,55.027,286.9,49.443,207.046,86.542c-51.069,23.7-93.125,56.714-100.31,102.034-20.09,126.746,12.9,196.58-48.819,237.954S48.944,581.5,96.263,577.564c34.968-2.906,80.842,52.415,130.687,60.111,17.382,2.684,88.7-13.043,99.578,0C341.327,655.4,464.763,647.87,482.764,637.675Z" transform="translate(-21979.895 -6980.982)" fill="#00ddf1" opacity="0.1"/>
|
||||
<g id="Group_641" data-name="Group 641" transform="translate(-21545.08 -6816.88)">
|
||||
<rect id="Rectangle_263" data-name="Rectangle 263" width="217.867" height="15.073" transform="translate(0 39.052)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_264" data-name="Rectangle 264" width="217.867" height="15.073" transform="translate(0 104.137)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_265" data-name="Rectangle 265" width="217.867" height="15.073" transform="translate(0 171.964)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_266" data-name="Rectangle 266" width="13.702" height="39.052" transform="translate(19.183)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_267" data-name="Rectangle 267" width="13.702" height="2.74" transform="translate(19.183 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_268" data-name="Rectangle 268" width="13.702" height="2.74" transform="translate(19.183 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_269" data-name="Rectangle 269" width="13.702" height="39.052" transform="translate(39.737)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_270" data-name="Rectangle 270" width="13.702" height="2.74" transform="translate(39.737 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_271" data-name="Rectangle 271" width="13.702" height="2.74" transform="translate(39.737 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_272" data-name="Rectangle 272" width="13.702" height="39.052" transform="translate(60.29)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_273" data-name="Rectangle 273" width="13.702" height="2.74" transform="translate(60.29 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_274" data-name="Rectangle 274" width="13.702" height="2.74" transform="translate(60.29 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_275" data-name="Rectangle 275" width="13.702" height="39.052" transform="translate(80.844)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_276" data-name="Rectangle 276" width="13.702" height="2.74" transform="translate(80.844 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_277" data-name="Rectangle 277" width="13.702" height="2.74" transform="translate(80.844 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_278" data-name="Rectangle 278" width="13.702" height="39.052" transform="translate(101.397)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_279" data-name="Rectangle 279" width="13.702" height="2.74" transform="translate(101.397 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_280" data-name="Rectangle 280" width="13.702" height="2.74" transform="translate(101.397 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_281" data-name="Rectangle 281" width="13.702" height="39.052" transform="translate(121.95)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_282" data-name="Rectangle 282" width="13.702" height="2.74" transform="translate(121.95 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_283" data-name="Rectangle 283" width="13.702" height="2.74" transform="translate(121.95 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_284" data-name="Rectangle 284" width="13.702" height="39.052" transform="translate(142.504)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_285" data-name="Rectangle 285" width="13.702" height="2.74" transform="translate(142.504 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_286" data-name="Rectangle 286" width="13.702" height="2.74" transform="translate(142.504 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_287" data-name="Rectangle 287" width="13.702" height="39.052" transform="matrix(0.894, -0.448, 0.448, 0.894, 135.847, 138.025)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_288" data-name="Rectangle 288" width="13.702" height="2.74" transform="matrix(0.894, -0.448, 0.448, 0.894, 138.614, 143.541)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_289" data-name="Rectangle 289" width="13.702" height="2.74" transform="matrix(0.894, -0.448, 0.448, 0.894, 148.128, 162.529)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_290" data-name="Rectangle 290" width="13.702" height="39.052" transform="translate(19.183 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_291" data-name="Rectangle 291" width="13.702" height="2.74" transform="translate(19.183 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_292" data-name="Rectangle 292" width="13.702" height="2.74" transform="translate(19.183 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_293" data-name="Rectangle 293" width="13.702" height="39.052" transform="translate(39.737 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_294" data-name="Rectangle 294" width="13.702" height="2.74" transform="translate(39.737 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_295" data-name="Rectangle 295" width="13.702" height="2.74" transform="translate(39.737 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_296" data-name="Rectangle 296" width="13.702" height="39.052" transform="translate(60.29 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_297" data-name="Rectangle 297" width="13.702" height="2.74" transform="translate(60.29 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_298" data-name="Rectangle 298" width="13.702" height="2.74" transform="translate(60.29 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_299" data-name="Rectangle 299" width="13.702" height="39.052" transform="translate(145.93 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_300" data-name="Rectangle 300" width="13.702" height="2.74" transform="translate(145.93 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_301" data-name="Rectangle 301" width="13.702" height="2.74" transform="translate(145.93 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_302" data-name="Rectangle 302" width="13.702" height="39.052" transform="translate(166.483 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_303" data-name="Rectangle 303" width="13.702" height="2.74" transform="translate(166.483 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_304" data-name="Rectangle 304" width="13.702" height="2.74" transform="translate(166.483 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_305" data-name="Rectangle 305" width="13.702" height="39.052" transform="translate(187.036 65.771)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_306" data-name="Rectangle 306" width="13.702" height="2.74" transform="translate(187.036 71.937)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_307" data-name="Rectangle 307" width="13.702" height="2.74" transform="translate(187.036 93.176)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_308" data-name="Rectangle 308" width="13.702" height="39.052" transform="translate(19.183 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_309" data-name="Rectangle 309" width="13.702" height="2.74" transform="translate(19.183 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_310" data-name="Rectangle 310" width="13.702" height="2.74" transform="translate(19.183 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_311" data-name="Rectangle 311" width="13.702" height="39.052" transform="translate(39.737 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_312" data-name="Rectangle 312" width="13.702" height="2.74" transform="translate(39.737 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_313" data-name="Rectangle 313" width="13.702" height="2.74" transform="translate(39.737 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_314" data-name="Rectangle 314" width="13.702" height="39.052" transform="translate(60.29 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_315" data-name="Rectangle 315" width="13.702" height="2.74" transform="translate(60.29 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_316" data-name="Rectangle 316" width="13.702" height="2.74" transform="translate(60.29 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_317" data-name="Rectangle 317" width="13.702" height="39.052" transform="translate(80.844 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_318" data-name="Rectangle 318" width="13.702" height="2.74" transform="translate(80.844 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_319" data-name="Rectangle 319" width="13.702" height="2.74" transform="translate(80.844 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_320" data-name="Rectangle 320" width="13.702" height="39.052" transform="translate(101.397 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_321" data-name="Rectangle 321" width="13.702" height="2.74" transform="translate(101.397 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_322" data-name="Rectangle 322" width="13.702" height="2.74" transform="translate(101.397 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_323" data-name="Rectangle 323" width="13.702" height="39.052" transform="translate(121.95 132.912)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_324" data-name="Rectangle 324" width="13.702" height="2.74" transform="translate(121.95 139.078)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_325" data-name="Rectangle 325" width="13.702" height="2.74" transform="translate(121.95 160.317)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_473" data-name="Path 473" d="M797.862,159.09v35.571h-13.7V156.48Q791.039,157.693,797.862,159.09Z" transform="translate(-621.103 -155.61)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_326" data-name="Rectangle 326" width="13.702" height="2.74" transform="translate(163.057 6.166)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_327" data-name="Rectangle 327" width="13.702" height="2.74" transform="translate(163.057 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_474" data-name="Path 474" d="M827.862,165.719v30.83h-13.7V162.43c1.781.391,3.556.8,5.33,1.22Q823.7,164.636,827.862,165.719Z" transform="translate(-630.549 -157.483)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_475" data-name="Path 475" d="M827.862,166.285v.685h-13.7v-2.74h5.33Q823.7,165.2,827.862,166.285Z" transform="translate(-630.549 -158.05)" fill="#00ddf1" opacity="0.1"/>
|
||||
<rect id="Rectangle_328" data-name="Rectangle 328" width="13.702" height="2.74" transform="translate(183.611 27.405)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_476" data-name="Path 476" d="M691.358,287.276s-4.152-1.411-6.166,2.254a6.68,6.68,0,0,0,.089,6.426,9.4,9.4,0,0,0,9.146,5.179c4.35-.233,5.8-3.549,6.166-6.639a6.851,6.851,0,0,0-9.057-7.3Z" transform="translate(-589.693 -196.649)" fill="#00ddf1"/>
|
||||
<path id="Path_477" data-name="Path 477" d="M686.2,296.239a6.68,6.68,0,0,1-.089-6.426c2-3.659,6.166-2.254,6.166-2.254l.212-.075a6.762,6.762,0,0,1,4.885.11,6.762,6.762,0,0,0-5.762-.548l-.212.075s-4.152-1.411-6.166,2.254a6.68,6.68,0,0,0,.089,6.426,9.982,9.982,0,0,0,4.7,4.446A10.277,10.277,0,0,1,686.2,296.239Z" transform="translate(-589.706 -196.595)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_478" data-name="Path 478" d="M691.42,290.06s1.542,1.213,5.282,0" transform="translate(-591.9 -197.672)" fill="#00ddf1" opacity="0.2"/>
|
||||
<path id="Path_479" data-name="Path 479" d="M690.93,277.21s2.638,7.153,2.418,9.023l2.09-.219s-3.521-6.269-2.638-8.8Z" transform="translate(-591.746 -193.626)" fill="#00ddf1"/>
|
||||
<path id="Path_480" data-name="Path 480" d="M690.93,277.21s2.638,7.153,2.418,9.023l2.09-.219s-3.521-6.269-2.638-8.8Z" transform="translate(-591.746 -193.626)" fill="#00ddf1" opacity="0.2"/>
|
||||
<path id="Path_481" data-name="Path 481" d="M702.92,277.6s-4.159-2.124-6.015-.37-2.144,6.241-2.144,6.241S701.536,284.391,702.92,277.6Z" transform="translate(-592.952 -193.414)" fill="#00ddf1"/>
|
||||
<path id="Path_482" data-name="Path 482" d="M702.92,277.6s-4.159-2.124-6.015-.37-2.144,6.241-2.144,6.241S701.536,284.391,702.92,277.6Z" transform="translate(-592.952 -193.414)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_483" data-name="Path 483" d="M694.75,283.958s1.089-5.207,8.16-5.878" transform="translate(-592.949 -193.9)" fill="#00ddf1"/>
|
||||
<path id="Path_484" data-name="Path 484" d="M819.358,386.276s-4.152-1.411-6.166,2.254a6.68,6.68,0,0,0,.089,6.426,9.4,9.4,0,0,0,9.146,5.18c4.35-.233,5.8-3.549,6.166-6.639a6.851,6.851,0,0,0-9.057-7.3Z" transform="translate(-629.999 -227.823)" fill="#00ddf1"/>
|
||||
<path id="Path_485" data-name="Path 485" d="M814.2,395.239a6.68,6.68,0,0,1-.089-6.426c2-3.659,6.166-2.254,6.166-2.254l.212-.075a6.762,6.762,0,0,1,4.885.11,6.762,6.762,0,0,0-5.762-.548l-.212.075s-4.152-1.411-6.166,2.254a6.68,6.68,0,0,0,.089,6.426,9.982,9.982,0,0,0,4.7,4.446A10.276,10.276,0,0,1,814.2,395.239Z" transform="translate(-630.011 -227.769)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_486" data-name="Path 486" d="M819.42,389.06s1.542,1.213,5.282,0" transform="translate(-632.206 -228.846)" fill="#00ddf1" opacity="0.2"/>
|
||||
<path id="Path_487" data-name="Path 487" d="M818.93,376.21s2.638,7.153,2.419,9.023l2.09-.219s-3.522-6.269-2.638-8.8Z" transform="translate(-632.051 -224.799)" fill="#00ddf1"/>
|
||||
<path id="Path_488" data-name="Path 488" d="M818.93,376.21s2.638,7.153,2.419,9.023l2.09-.219s-3.522-6.269-2.638-8.8Z" transform="translate(-632.051 -224.799)" fill="#00ddf1" opacity="0.2"/>
|
||||
<path id="Path_489" data-name="Path 489" d="M830.92,376.6s-4.159-2.124-6.015-.37-2.144,6.241-2.144,6.241S829.536,383.391,830.92,376.6Z" transform="translate(-633.257 -224.588)" fill="#00ddf1"/>
|
||||
<path id="Path_490" data-name="Path 490" d="M830.92,376.6s-4.159-2.124-6.015-.37-2.144,6.241-2.144,6.241S829.536,383.391,830.92,376.6Z" transform="translate(-633.257 -224.588)" fill="#00ddf1" opacity="0.1"/>
|
||||
<path id="Path_491" data-name="Path 491" d="M822.75,382.958s1.089-5.207,8.16-5.878" transform="translate(-633.254 -225.073)" fill="#00ddf1"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group_643" data-name="Group 643" transform="translate(7 -63)">
|
||||
<g id="Group_639" data-name="Group 639" transform="translate(21233 8372)">
|
||||
<ellipse id="Ellipse_73" data-name="Ellipse 73" cx="49.851" cy="8.007" rx="49.851" ry="8.007" transform="translate(-20118.443 -7772.555)" fill="#00abf6" opacity="0.1"/>
|
||||
<rect id="Rectangle_182" data-name="Rectangle 182" width="3.208" height="72.224" transform="translate(-20070.199 -7917.934)" fill="#535461"/>
|
||||
<path id="Path_268" data-name="Path 268" d="M1025.906,587.251l-.2,3.254-.287,4.6-.118,1.913-.281,4.6-.123,1.913-.286,4.6-3.233,52.22a8.851,8.851,0,0,1-8.83,8.268H995.731a8.841,8.841,0,0,1-8.82-8.3l-3.244-52.225-.281-4.6-.117-1.913-.292-4.6-.117-1.913-.281-4.6-.2-3.254a4.6,4.6,0,0,1,4.574-4.865h34.391a4.6,4.6,0,0,1,4.569,4.9Z" transform="translate(-21072.734 -8433.181)" fill="#565661"/>
|
||||
<path id="Path_269" data-name="Path 269" d="M990.949,517.36l-.287,4.589H948.106l-.286-4.589Z" transform="translate(-21037.977 -8360.036)" fill="#9d9cb5"/>
|
||||
<path id="Path_270" data-name="Path 270" d="M990.93,530.08l-.281,4.594H948.9l-.286-4.594Z" transform="translate(-21038.363 -8366.248)" fill="#9d9cb5"/>
|
||||
<path id="Path_271" data-name="Path 271" d="M990.912,542.8l-.287,4.589H949.686L949.4,542.8Z" transform="translate(-21038.748 -8372.46)" fill="#9d9cb5"/>
|
||||
<path id="Path_272" data-name="Path 272" d="M933.875,490.464c18.684,13.517,26.466,34.656,26.466,34.656s-22.511-.773-41.2-14.289-26.46-34.651-26.46-34.651S915.2,476.953,933.875,490.464Z" transform="translate(-21028.934 -8381.328)" fill="#00abf6"/>
|
||||
<path id="Path_273" data-name="Path 273" d="M892.68,476.18s23.365,12.969,30.865,23.708,36.8,25.233,36.8,25.233" transform="translate(-21028.934 -8381.328)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<path id="Path_274" data-name="Path 274" d="M975.393,443.457c10.171,7.352,14.407,18.863,14.407,18.863s-12.279-.425-22.424-7.776S953,435.68,953,435.68,965.227,436.1,975.393,443.457Z" transform="translate(-21058.393 -8361.549)" fill="#00abf6"/>
|
||||
<path id="Path_275" data-name="Path 275" d="M953,435.68s12.719,7.06,16.8,12.9,20.03,13.737,20.03,13.737" transform="translate(-21058.393 -8361.549)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<path id="Path_276" data-name="Path 276" d="M1040.3,470.483c-11.68,12.836-14.151,29.612-14.151,29.612s16.469-4.042,28.139-16.883,14.151-29.612,14.151-29.612S1051.981,457.647,1040.3,470.483Z" transform="translate(-21094.119 -8370.301)" fill="#00abf6"/>
|
||||
<path id="Path_277" data-name="Path 277" d="M1068.45,453.61s-15.215,13.159-19.083,22.224S1026.15,500.1,1026.15,500.1" transform="translate(-21094.119 -8370.306)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="2"/>
|
||||
</g>
|
||||
<g id="Group_638" data-name="Group 638" transform="translate(-40 36)">
|
||||
<rect id="Rectangle_254" data-name="Rectangle 254" width="12.343" height="37.459" transform="translate(741.459 392.675)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_255" data-name="Rectangle 255" width="12.343" height="39.629" transform="translate(1073.647 390.726)" fill="#3f3d56"/>
|
||||
<path id="Path_418" data-name="Path 418" d="M597.889,523.665v8.446l-98.7.909-1.052.02-37.511.344-.9.013-11.4.1h-.338l-69.5.643L264.408,535.2h-.1l-29.007.273-.312.007L106.1,536.658v-9.745l30.306-18.19,60.808-.507,1.3-.007.429-.007,1.3-.019,29.2-.24,1.306-.007.416-.007,1.3-.006,9.362-.078.247-.006,23.1-.195h.188l180.214-1.52,4.411-.032h.442l3.365-.033,24.459-.2,1.3-.02,1.2-.013h.624l7.607-.058,1.507-.02,2.02-.013,6.334-.058,10.05-.084,1.3-.013,2.261-.013h.422l48.536-.416Z" transform="translate(566.82 43)" fill="#65617d"/>
|
||||
<path id="Path_419" data-name="Path 419" d="M597.889,523.665v8.446l-98.7.909-1.052.02-37.511.344-.9.013-11.4.1h-.338l-69.5.643L264.408,535.2h-.1l-29.007.273-.312.007L106.1,536.658v-9.745l30.306-18.19,60.808-.507,1.3-.007.429-.007,1.3-.019,29.2-.24,1.306-.007.416-.007,1.3-.006,9.362-.078.247-.006,23.1-.195h.188l180.214-1.52,4.411-.032h.442l3.365-.033,24.459-.2,1.3-.02,1.2-.013h.624l7.607-.058,1.507-.02,2.02-.013,6.334-.058,10.05-.084,1.3-.013,2.261-.013h.422l48.536-.416Z" transform="translate(566.82 43)" opacity="0.2"/>
|
||||
<path id="Path_420" data-name="Path 420" d="M358.914,656.57v15.91a3.982,3.982,0,0,1-2.274,3.6,3.9,3.9,0,0,1-1.624.39l-22.673.481a3.9,3.9,0,0,1-1.754-.37,3.976,3.976,0,0,1-2.319-3.619V656.57Z" transform="translate(437.441 -122.381)" fill="#3f3d56"/>
|
||||
<path id="Path_421" data-name="Path 421" d="M358.914,656.57v15.91a3.982,3.982,0,0,1-2.274,3.6,3.9,3.9,0,0,1-1.624.39l-22.673.481a3.9,3.9,0,0,1-1.754-.37,3.976,3.976,0,0,1-2.319-3.619V656.57Z" transform="translate(437.441 -122.381)" opacity="0.1"/>
|
||||
<path id="Path_422" data-name="Path 422" d="M360.914,656.57v15.91a3.982,3.982,0,0,1-2.274,3.6,3.9,3.9,0,0,1-1.624.39l-22.673.481a3.9,3.9,0,0,1-1.754-.37,3.976,3.976,0,0,1-2.319-3.619V656.57Z" transform="translate(436.74 -122.381)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_256" data-name="Rectangle 256" width="30.644" height="20.464" transform="translate(1046.362 534.189)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_257" data-name="Rectangle 257" width="30.644" height="20.464" transform="translate(1046.362 534.189)" opacity="0.1"/>
|
||||
<rect id="Rectangle_258" data-name="Rectangle 258" width="30.644" height="20.464" transform="translate(1045.062 534.189)" fill="#3f3d56"/>
|
||||
<path id="Path_423" data-name="Path 423" d="M223.943,265.82v3.723l-12.343-.318v-3.4Z" transform="translate(529.859 126.848)" opacity="0.1"/>
|
||||
<path id="Path_424" data-name="Path 424" d="M358.741,271.246,343.909,377.789a2.794,2.794,0,0,1-2.839,2.41L222.314,377.12l-2.254-.058a2.794,2.794,0,0,1-2.7-3.164l16.651-111.533a2.794,2.794,0,0,1,2.949-2.358l119.186,8.069a2.787,2.787,0,0,1,2.6,3.17Z" transform="translate(476.307 16.556)" fill="#65617d"/>
|
||||
<path id="Path_425" data-name="Path 425" d="M169.158,99.16,156.6,197.472l109.142,3.463,13.422-94.85Z" transform="translate(549.128 185.237)" fill="#00ddf1"/>
|
||||
<rect id="Rectangle_259" data-name="Rectangle 259" width="132.965" height="112.605" rx="5.33" transform="translate(853.849 282.877)" fill="#65617d"/>
|
||||
<rect id="Rectangle_260" data-name="Rectangle 260" width="120.836" height="93.765" transform="translate(860.125 290.458)" fill="#00ddf1"/>
|
||||
<path id="Path_426" data-name="Path 426" d="M735.273,262.82v2.462l-12.343.253V262.82Z" transform="translate(350.717 127.899)" opacity="0.1"/>
|
||||
<path id="Path_427" data-name="Path 427" d="M833.626,371a2,2,0,0,1-1.949,2.131l-121.615,2.514a2,2,0,0,1-2.033-1.813L697.575,265.156a2,2,0,0,1,1.9-2.189l120.693-6,1.3-.065a1.994,1.994,0,0,1,2.092,1.819Z" transform="translate(308.06 17.643)" fill="#65617d"/>
|
||||
<path id="Path_428" data-name="Path 428" d="M628.93,100.238,739.157,94.82l9.31,98.748-109.356,2.6Z" transform="translate(383.65 186.757)" fill="#00ddf1"/>
|
||||
<path id="Path_429" data-name="Path 429" d="M899.741,371a2,2,0,0,1-1.949,2.131L886.28,256.963l1.3-.065a2,2,0,0,1,2.092,1.819Z" transform="translate(241.945 17.643)" opacity="0.1"/>
|
||||
<rect id="Rectangle_261" data-name="Rectangle 261" width="12.343" height="11.096" transform="translate(741.459 419.038)" opacity="0.1"/>
|
||||
<path id="Path_431" data-name="Path 431" d="M281.574,483.65v3.229l-30.644.819V483.65Z" transform="translate(516.08 50.533)" opacity="0.1"/>
|
||||
<path id="Path_432" data-name="Path 432" d="M390.8,483.886V593.158a2.6,2.6,0,0,1-2.5,2.566l-124.506,3.313h-.033a2.6,2.6,0,0,1-2.566-2.566V483.886a2.6,2.6,0,0,1,2.566-2.566H388.23A2.6,2.6,0,0,1,390.8,483.886Z" transform="translate(460.942 -60.983)" fill="#65617d"/>
|
||||
<path id="Path_434" data-name="Path 434" d="M194.6,319.15H309.914v96.311L194.6,418.06Z" transform="translate(535.815 108.164)" fill="#d4dfec"/>
|
||||
<rect id="Rectangle_262" data-name="Rectangle 262" width="12.343" height="11.317" transform="translate(1073.647 419.038)" opacity="0.1"/>
|
||||
<path id="Path_435" data-name="Path 435" d="M709.574,483.65v4.164L678.93,487V483.65Z" transform="translate(366.132 50.533)" opacity="0.1"/>
|
||||
<path id="Path_436" data-name="Path 436" d="M797.782,483.886V596.471a2.6,2.6,0,0,1-2.6,2.566h0l-124.532-3.313a2.6,2.6,0,0,1-2.5-2.566V483.886a2.6,2.6,0,0,1,2.566-2.566H795.19a2.6,2.6,0,0,1,2.592,2.566Z" transform="translate(318.365 -60.983)" fill="#65617d"/>
|
||||
<path id="Path_437" data-name="Path 437" d="M866.289,483.886V596.471a2.6,2.6,0,0,1-2.6,2.566h0V481.32h0a2.6,2.6,0,0,1,2.6,2.566Z" transform="translate(249.859 -60.983)" opacity="0.1"/>
|
||||
<path id="Path_438" data-name="Path 438" d="M713.414,319.15H598.1v96.311l115.314,2.6Z" transform="translate(394.451 108.164)" fill="#d4dfec"/>
|
||||
<path id="Path_439" data-name="Path 439" d="M593.3,483.886V593.158a2.6,2.6,0,0,1-2.5,2.566l-124.506,3.313h0a2.6,2.6,0,0,1-2.6-2.566V483.886a2.6,2.6,0,0,1,2.566-2.566H590.73A2.6,2.6,0,0,1,593.3,483.886Z" transform="translate(389.997 -60.983)" fill="#65617d"/>
|
||||
<path id="Path_440" data-name="Path 440" d="M397.1,319.15H512.414v96.311L397.1,418.06Z" transform="translate(464.87 108.164)" fill="#d4dfec"/>
|
||||
<path id="Path_441" data-name="Path 441" d="M597.889,533.65V542.1l-98.7.909-1.052.02-37.511.344-.9.013-11.4.1h-.338l-69.5.643-114.086,1.059h-.1l-29.007.273-.312.007L106.1,546.643V536.9l131.562-.864h.312l26.629-.182h.123l183.9-1.215h.188l.11-.006h.305l3.112-.019h.948l48.393-.325,2.858-.013.637-.007Z" transform="translate(566.82 33.015)" opacity="0.1"/>
|
||||
<circle id="Ellipse_99" data-name="Ellipse 99" cx="33.347" cy="33.347" r="33.347" transform="translate(887.196 363.44)" fill="#fbbebe"/>
|
||||
<path id="Path_442" data-name="Path 442" d="M383.74,592.973a.806.806,0,0,1,.065-.227c.65-2.332,1.806-6.276,2.67-9.427.11-.39.214-.773.312-1.137.955-3.554,2.813-11.876,3.846-17.066.364-1.826.65-3.248.708-3.963.13-1.1,2.085-5.795,4.3-11.194,1.553-3.794,3.248-7.939,4.489-11.434a39.876,39.876,0,0,0,1.9-6.4,3.313,3.313,0,0,0,0-1.384,9.543,9.543,0,0,1-.312-2.274h0c-.149-4.028,1.052-10.394,2.91-13.065,2.274-3.248,3.9-15.371,3.9-15.371s3.365.487,3.9-2.293a4,4,0,0,0,.065-.91,4.815,4.815,0,0,0-.058-.721c-.578-4.054,9.563-8.368,15.267-9.3a12.769,12.769,0,0,1,1.949-.182,17.694,17.694,0,0,0,4.06-.935q.909-.279,1.949-.65c2.391-.806,5.249-1.871,8.283-3.04,11.408-4.424,25.22-10.394,25.22-10.394.1-.461.208-.9.331-1.338a18,18,0,0,1,10.622-12.3h0v-1.3c0-2.241-.1-4.548-.195-6.847,0-.448-.039-.9-.065-1.345v-.734c0-.455-.046-.91-.071-1.358l-.117-.091a10.66,10.66,0,0,1-2.534-2.729,9.916,9.916,0,0,1-.923-1.949c-.812-2.2-1-4.658-1.546-6.984l-.117-.481a54.579,54.579,0,0,0-2.6-6.873c-1.436-3.521-2.54-7.146-3.638-10.817q-1-3.333-2.02-6.678l-1.026-3.4c-.195-.65-.39-1.3-.539-1.949h0a8.627,8.627,0,0,1-.169-.91h0a6.5,6.5,0,0,1,.156-2.995,20.854,20.854,0,0,1,.858-1.949,5.113,5.113,0,0,0,.325-.9h0a1.947,1.947,0,0,0,.052-.208,3.667,3.667,0,0,0,.071-.7,11.993,11.993,0,0,0-.338-2.527.045.045,0,0,1,0-.032,1.661,1.661,0,0,0-.032-.175,16.564,16.564,0,0,1-.234-1.949,18.483,18.483,0,0,1,2.254-10.115l.123-.234a16.383,16.383,0,0,1,1.2-1.806h0a9.122,9.122,0,0,1,.65-.793,2.594,2.594,0,0,1-3.606-.936,2.475,2.475,0,0,1-.149-.292c-.033-.078-.059-.162-.085-.247-.455-1.592.338-3.287,1.377-4.58a12.012,12.012,0,0,1,3.729-3.053h0a2.51,2.51,0,0,1,.26-.136,11.125,11.125,0,0,1,1.715-.689h0a9.781,9.781,0,0,1,1.059-.286l-2.41-.221-.974-.091A9.414,9.414,0,0,1,479,361.527h.7a7.625,7.625,0,0,1,1.02.084A2.885,2.885,0,0,1,479,358.8a9.362,9.362,0,0,0,5.97,1.988h.247a19.494,19.494,0,0,1-.221-2.124h0a21.545,21.545,0,0,1,0-2.9,5.483,5.483,0,0,0,4.4,1.468l-1.351-1.884a6.165,6.165,0,0,0,4.262,1.332l.312-.033a4.547,4.547,0,0,0,.773-.149h.058c-.123-.156-.234-.318-.344-.487l-.039-.078a6.282,6.282,0,0,1-.65-1.358,4.284,4.284,0,0,1-.156-.565h0a5.684,5.684,0,0,1,.292-3.541c2.1,1.754,4.4,4.151,7.146,2.339s2.839-5.139,7.146-4.8a6.626,6.626,0,0,1,5.464,3.976,4.385,4.385,0,0,1,4.983-1.137,4.45,4.45,0,0,1,2.521,4.476c1.786-.448,3.71-.89,5.425-.214s2.82,2.865,1.845,4.327a.931.931,0,0,1-.091.13c-.8,1.033-2.566,1.6-2.378,2.9l.273.195.845.611q4.97,3.586,9.68,7.536c1.611,1.351,3.326,2.975,3.437,5.087a5.617,5.617,0,0,1-.156,1.54h0c-.1.507-.247,1.013-.357,1.52a12.676,12.676,0,0,0-.221,3.93h0c.045.383.1.76.175,1.143.994,5.022,5.009,9.167,5.405,14.292v.364a11.546,11.546,0,0,1-.045,1.644c-.3,3.118-1.741,6.113-2.91,9.1-.072.182-.143.364-.208.546a90.5,90.5,0,0,0-4.548,18.294,65.14,65.14,0,0,1-1.3,6.555,17.365,17.365,0,0,1-3.307,6.776,10.536,10.536,0,0,1-2.183,1.949,12.132,12.132,0,0,1-1.722.916v.039c.071.4.13.793.2,1.189.039.208.071.416.11.65h0a.089.089,0,0,0,0,.039c.546,3.073,1.169,6.081,1.826,8.939l.331,1.423c8.186,2.956,9.154,9.011,9.212,11.33a6.259,6.259,0,0,1,0,.929s12.343.325,17.216,3.9a13.21,13.21,0,0,0,2.93,1.494,53.917,53.917,0,0,0,6.009,1.877c1.046.273,2.137.539,3.248.8,1.949.448,4.015.884,6.009,1.3,5.321,1.072,10.33,1.949,12.87,2.6,6.061,1.533,19.925,14.825,19.925,14.825s1.624,13.409,3.248,15.364,4.548,8.829,4.548,8.829l10.745,19.106,4.548,8.036h2.923a4.97,4.97,0,0,1,2.085,1.364c.773.689,1.631,1.546,2.384,2.326l1.247,1.332.78.864s4.723.117,6,3.852c3.846-.11,8.569.143,11.876,1.624a9.187,9.187,0,0,1,1.3.7,5.639,5.639,0,0,1,2.683,3.677,5.422,5.422,0,0,1,.117.935c0,.37,0,.741,0,1.1-.234,4.918-3.437,9.011-6.38,11.765l.208.37s-.1.234-.286.65c-.052.123-.117.26-.188.416h0c-.383.819-.994,2.046-1.793,3.482a41.136,41.136,0,0,1-6.217,8.556l-.188.195-37.511.344-.11-.13-7.276-8.679-3.619-4.314-.11-.136-.1,1.033-.351,3.443-.845,8.322-.045.416v.11l-.1.961.071-.961h-.188l-69.513.65-114.092,1.26v.546h-.091v-.546l-29.072.312Z" transform="translate(418.007 -14.269)" fill="url(#linear-gradient)"/>
|
||||
<path id="Path_443" data-name="Path 443" d="M586.185,511.522s-64.641,7.8-58.469,0c2.235-2.82,2.852-11.174,2.729-20.691-.039-2.891-.143-5.886-.292-8.868-.715-14.292-2.436-28.26-2.436-28.26s56.52-26.636,50.023-5.522c-2.6,8.53-1.748,20.51.227,31.755.578,3.281,1.247,6.5,1.949,9.55a223.892,223.892,0,0,0,6.269,22.036Z" transform="translate(367.711 -46.521)" fill="#fbbebe"/>
|
||||
<path id="Path_444" data-name="Path 444" d="M586.185,537.354s-64.641,7.8-58.469,0c2.235-2.82,2.852-11.174,2.729-20.692,9.855-4.21,22.394-1.072,22.394-1.072,13.2-2.631,21.666-2.118,27.078-.273A223.888,223.888,0,0,0,586.185,537.354Z" transform="translate(367.711 -72.353)" opacity="0.1"/>
|
||||
<path id="Path_445" data-name="Path 445" d="M413.346,651.973v-.546l114.092-1.241,69.513-.65h.195l-.071.961.1-.961h.123v-.11l.89-8.738.338-3.307.1-1.033.091-.864,4.015-13.389,4.015-13.37,3.378-11.258,5.158-17.2s-2.923-6.821-4.548-8.77-3.248-15.267-3.248-15.267-13.864-13.208-19.925-14.728c-2.54-.65-7.555-1.514-12.883-2.6-1.994-.4-4.041-.832-6.009-1.3-1.111-.253-2.2-.52-3.248-.786a57.086,57.086,0,0,1-6.009-1.858,13.448,13.448,0,0,1-2.924-1.488c-4.775-3.5-16.742-3.9-17.2-3.9h0a.269.269,0,0,1,0-.045v-.058a6.66,6.66,0,0,0,0-.819,5.208,5.208,0,0,0-.059-.91.82.82,0,0,0-.045-.325c-.032-.234-.071-.481-.13-.741a3.226,3.226,0,0,0-.078-.344,11.181,11.181,0,0,0-.812-2.267c-.045-.1-.1-.214-.156-.318a11.861,11.861,0,0,0-1.1-1.709c-.084-.11-.175-.227-.273-.338a12.721,12.721,0,0,0-1.767-1.715c-.175-.136-.357-.279-.546-.409a3.026,3.026,0,0,0-.331-.234l-.578-.377-.539-.312c-.474-.26-.974-.513-1.52-.754-.169-.078-.351-.149-.533-.221s-.468-.182-.708-.273h-.039l-.487-.195c-.37-.123-.747-.234-1.143-.344l-.6-.156-.65-.149-.65-.136c-.65-.13-1.345-.247-2.072-.338l-.741-.091-.76-.078-.786-.065-1.117-.065-1.3-.045a81.017,81.017,0,0,0-17.054,1.814,46.946,46.946,0,0,0-11.855-1.093h-.565l-.65.039h-.416l-.838.078a6.224,6.224,0,0,0-.845.1c-.279.039-.572.071-.851.117l-.429.071c-.422.071-.851.149-1.3.24q-.214.039-.429.1c-.565.123-1.13.273-1.689.442-.279.078-.559.169-.838.266-.143.039-.279.091-.416.143l-.416.149-.409.162-.487.195c-.247.1-.487.214-.728.331-.136.058-.273.123-.4.195s-.266.13-.39.2a18.03,18.03,0,0,0-4.034,2.846l-.331.318-.325.331c-.11.11-.214.227-.318.344a15.473,15.473,0,0,0-1.182,1.462c-.1.136-.2.273-.3.416a17.335,17.335,0,0,0-1.117,1.9c-.084.156-.162.318-.24.474a21.243,21.243,0,0,0-1.3,3.411q-.182.65-.331,1.3s-13.753,6.068-25.161,10.46c-3.034,1.169-5.892,2.228-8.283,3.027-.7.234-1.345.448-1.949.65a17.347,17.347,0,0,1-4.06.935,12.778,12.778,0,0,0-1.949.182c-5.243.851-14.227,4.548-15.208,8.257a2.4,2.4,0,0,0-.065.91v.071c.65,4.548-3.9,3.9-3.9,3.9s-1.624,12.019-3.9,15.267c-1.644,2.339-2.768,7.555-2.91,11.492h0a12.741,12.741,0,0,0,.286,3.658v.091c.26.871-.364,3.248-1.423,6.3-1.15,3.346-2.826,7.5-4.437,11.427-2.43,5.925-4.716,11.3-4.859,12.486-.058.526-.227,1.468-.461,2.683-.948,4.95-3.053,14.377-4.093,18.19-.773,2.845-1.9,6.743-2.67,9.42a.91.91,0,0,1-.065.227l29.124-.286v.546h.091Z" transform="translate(417.856 -73.042)" fill="#00ddf1"/>
|
||||
<path id="Path_446" data-name="Path 446" d="M445.44,607.57s37.03,19.165,50.348,16.891Z" transform="translate(396.391 -105.214)" opacity="0.1"/>
|
||||
<path id="Path_447" data-name="Path 447" d="M561.44,595.57s36.056,11.694,48.074,13.318S561.44,595.57,561.44,595.57Z" transform="translate(355.751 -101.01)" opacity="0.1"/>
|
||||
<path id="Path_448" data-name="Path 448" d="M585.443,530.686c-18.84,3.469-54.311,2.722-69.643,2.2,5.62-20.139,33.022-13.292,33.022-13.292C582.74,512.821,585.339,526.866,585.443,530.686Z" transform="translate(371.74 -73.754)" opacity="0.1"/>
|
||||
<path id="Path_449" data-name="Path 449" d="M585.443,528.686c-18.84,3.469-54.311,2.722-69.643,2.2,5.62-20.139,33.022-13.292,33.022-13.292C582.74,510.821,585.339,524.866,585.443,528.686Z" transform="translate(371.74 -73.053)" fill="#00ddf1"/>
|
||||
<path id="Path_450" data-name="Path 450" d="M776.94,672.008s22.192-3.411,22.628,6.334S788.25,694.9,788.25,694.9Z" transform="translate(280.251 -127.587)" fill="#fbbebe"/>
|
||||
<path id="Path_451" data-name="Path 451" d="M578.17,479.936a30.558,30.558,0,0,1-2.923,1.13c-7,2.417-14.065,4.853-21.439,5.736s-15.111.1-21.543-3.573a18.685,18.685,0,0,1-1.949-1.267c-.715-14.292-2.436-28.26-2.436-28.26s56.52-26.636,50.023-5.522C575.318,456.711,576.195,468.69,578.17,479.936Z" transform="translate(367.508 -46.521)" opacity="0.1"/>
|
||||
<path id="Path_452" data-name="Path 452" d="M536.9,360.29a6.153,6.153,0,0,1-5.405-1.137l1.351,1.878a5.5,5.5,0,0,1-4.4-1.462,22.146,22.146,0,0,0,.208,5.035,9.433,9.433,0,0,1-6.211-1.949,2.872,2.872,0,0,0,1.715,2.8,9.316,9.316,0,0,0-10.135,6.958l3.391.312a12,12,0,0,0-6.782,4.138c-1.039,1.3-1.832,2.962-1.377,4.548s2.651,2.6,3.846,1.468a18.151,18.151,0,0,0-3.963,14.806,9.4,9.4,0,0,1,.3,3.417,28.993,28.993,0,0,1-1.228,3.047c-.65,1.9-.032,3.956.546,5.847l3.047,10.018c1.1,3.619,2.2,7.244,3.645,10.745a53.328,53.328,0,0,1,2.6,6.828c.819,3.144.851,6.588,2.6,9.336a12.948,12.948,0,0,0,4.665,4.112c6.425,3.677,14.188,4.457,21.542,3.573s14.409-3.32,21.406-5.736a20.141,20.141,0,0,0,4.444-1.949c4.84-3.17,5.918-9.654,6.821-15.364a87.242,87.242,0,0,1,4.736-18.71c1.416-3.567,3.248-7.185,2.936-11.005-.4-5.074-4.411-9.186-5.405-14.175a12.344,12.344,0,0,1,.045-5.048,11.473,11.473,0,0,0,.513-3.053c-.11-2.092-1.826-3.7-3.437-5.048q-5.243-4.379-10.823-8.277c-.188-1.3,1.579-1.858,2.378-2.884,1.13-1.449-.039-3.748-1.754-4.424s-3.638-.234-5.425.214a4.281,4.281,0,0,0-7.5-3.32,6.627,6.627,0,0,0-5.47-3.95c-4.307-.338-4.392,2.956-7.146,4.768s-5.028-.578-7.146-2.319A6.029,6.029,0,0,0,536.9,360.29Z" transform="translate(374.5 -15.625)" fill="#3f3d56"/>
|
||||
<path id="Path_453" data-name="Path 453" d="M768.171,669.128s-.578,1.377-1.644,3.372a44.83,44.83,0,0,1-5.847,8.556l-.175.188h-1.052l-37.511.344h-.9l-.11-.13-7.276-8.634-2.741-3.255-.11-.136-.234-.273-.13-.156-.169-.2-5.334-6.328,1.949-8.121,3.32-13.682.084-.331.65-2.6.461-1.949,1.546-6.354,5.3-21.861,9.42,4.872,9.933,17.541,5.334,9.42h2.923c.552,0,1.819,1.065,3.092,2.293.455.435.91.9,1.3,1.3,1.156,1.2,2.072,2.222,2.072,2.222s4.736.1,6.009,3.833a4.537,4.537,0,0,1,.162.552,26.191,26.191,0,0,0,1.644,4.262c.838,1.826,1.891,3.9,2.956,5.97,1.981,3.8,4,7.419,4.736,8.731l.091.162Z" transform="translate(305.476 -105.217)" fill="#00ddf1"/>
|
||||
<path id="Path_454" data-name="Path 454" d="M615.681,653.093l-.065.929v.065l-.11,1.494-.864,12.025L613.6,682.093l-.3,4.08-.65,8.731v.11l-69.513.65L429.061,696.9v.546h-.091v-.728l.292-9.329.559-18.229L430.3,653.7l.045-1.54a6.841,6.841,0,0,1,7.419-6.607c3.066.26,7.049.578,11.811.916l1.949.143c27.5,1.91,78.322,4.379,129.606,1.3,1.078-.059,2.163-.123,3.248-.2,7.952-.5,15.9-1.143,23.764-1.949a6.841,6.841,0,0,1,7.536,7.3Z" transform="translate(402.161 -118.514)" opacity="0.1"/>
|
||||
<path id="Path_455" data-name="Path 455" d="M429.08,698.857v-.546l114.092-1.241,69.513-.65h.195v-.11l.65-8.731.279-3.9,1.065-14.689.838-11.629v-.286a6.893,6.893,0,0,0-.071-1.6.2.2,0,0,0,0-.065,6.751,6.751,0,0,0-1.3-3.014,6.847,6.847,0,0,0-6.12-2.6c-7.867.806-15.813,1.449-23.764,1.949-1.085.078-2.17.143-3.248.2-51.284,3.047-102.106.578-129.606-1.3l-1.949-.143c-4.762-.338-8.744-.65-11.811-.916a6.847,6.847,0,0,0-7.3,5.542,7.146,7.146,0,0,0-.117,1.065l-.442,14.4-.6,18.236-.286,9.329v.728Z" transform="translate(402.122 -119.926)" fill="#3f3d56"/>
|
||||
<path id="Path_456" data-name="Path 456" d="M615.925,661.692l-.8,9.959L613.937,686.5l-.3,3.716-.65,8.322-.039.416v.11h0l-.1.961.071-.961h-.188l-69.513.65L429.11,700.944v-.182l.318-9.329.65-18.229.416-12.116a4.132,4.132,0,0,1,1.988-3.411,4.061,4.061,0,0,1,2.456-.585c1.364.11,3.04.24,5,.383,3.177.234,7.094.507,11.655.793,8.445.533,19.041,1.1,31.183,1.572,33.782,1.3,79.388,1.735,122.707-2.059,1.949-.169,3.963-.357,5.931-.546a4.066,4.066,0,0,1,2.261.429,3.346,3.346,0,0,1,.559.351,3.989,3.989,0,0,1,1.3,1.6,2.315,2.315,0,0,1,.162.4A4.08,4.08,0,0,1,615.925,661.692Z" transform="translate(402.112 -122.559)" opacity="0.1"/>
|
||||
<path id="Path_457" data-name="Path 457" d="M360.914,677.87v2.072a3.982,3.982,0,0,1-2.274,3.6l-26.051.5a3.976,3.976,0,0,1-2.319-3.619v-1.949Z" transform="translate(436.74 -129.843)" opacity="0.1"/>
|
||||
<path id="Path_458" data-name="Path 458" d="M280.294,512.532l-33.944.65V507.55l33.944-.65Z" transform="translate(517.685 42.387)" fill="#3f3d56"/>
|
||||
<path id="Path_459" data-name="Path 459" d="M280.294,512.532l-33.944.65V507.55l33.944-.65Z" transform="translate(517.685 42.387)" opacity="0.1"/>
|
||||
<path id="Path_460" data-name="Path 460" d="M282.294,512.532l-33.944.65V507.55l33.944-.65Z" transform="translate(516.984 42.387)" fill="#3f3d56"/>
|
||||
<path id="Path_461" data-name="Path 461" d="M552.228,360.039A6.334,6.334,0,0,1,551,357.22a6.165,6.165,0,0,0,.273,2.988A5.847,5.847,0,0,0,552.228,360.039Z" transform="translate(359.434 -17.505)"/>
|
||||
<path id="Path_462" data-name="Path 462" d="M540.1,366.9a22.83,22.83,0,0,1-.24-2.4c0,.8,0,1.6,0,2.41Z" transform="translate(363.311 -20.056)"/>
|
||||
<path id="Path_463" data-name="Path 463" d="M621.706,398.627a11.912,11.912,0,0,1,.234-1.722,11.7,11.7,0,0,0,.52-3.047,4.008,4.008,0,0,0-.1-.689c-.11.6-.286,1.2-.416,1.787a12.433,12.433,0,0,0-.234,3.671Z" transform="translate(334.652 -30.1)"/>
|
||||
<path id="Path_464" data-name="Path 464" d="M600.922,368.894l.708.513a12.8,12.8,0,0,0,1.67-1.442,2.384,2.384,0,0,0,.247-2.365,1.95,1.95,0,0,1-.247.416C602.5,367.042,600.734,367.607,600.922,368.894Z" transform="translate(341.923 -20.441)"/>
|
||||
<path id="Path_465" data-name="Path 465" d="M518.455,380.29a8,8,0,0,0-.565,1.481,11.8,11.8,0,0,1,3.248-1.228Z" transform="translate(371.008 -25.587)"/>
|
||||
<path id="Path_466" data-name="Path 466" d="M531.888,368.979a9.637,9.637,0,0,1-1.221-.8,2.758,2.758,0,0,0,.058.767,7.7,7.7,0,0,1,1.163.033Z" transform="translate(366.534 -21.345)"/>
|
||||
<path id="Path_467" data-name="Path 467" d="M587.579,428.33c-.266,3.151-1.735,6.185-2.923,9.173a87.2,87.2,0,0,0-4.736,18.717c-.9,5.71-1.949,12.194-6.821,15.358a19.488,19.488,0,0,1-4.437,1.949c-7,2.423-14.065,4.853-21.439,5.736s-15.111.11-21.543-3.567a12.993,12.993,0,0,1-4.664-4.112c-1.735-2.748-1.767-6.191-2.6-9.336a53.424,53.424,0,0,0-2.6-6.834c-1.436-3.5-2.54-7.146-3.638-10.739q-1.527-5.009-3.047-10.024a18.716,18.716,0,0,1-.741-3.086,11.644,11.644,0,0,0,.741,5.035q1.52,5.022,3.047,10.024c1.1,3.619,2.2,7.244,3.638,10.739a53.417,53.417,0,0,1,2.6,6.834c.819,3.144.851,6.587,2.6,9.336a12.993,12.993,0,0,0,4.664,4.112c6.432,3.677,14.188,4.45,21.543,3.567s14.409-3.313,21.439-5.736a19.487,19.487,0,0,0,4.437-1.949c4.84-3.164,5.918-9.647,6.821-15.358a87.2,87.2,0,0,1,4.736-18.717c1.416-3.56,3.248-7.179,2.936-11.005C587.592,428.408,587.579,428.369,587.579,428.33Z" transform="translate(374.359 -42.418)"/>
|
||||
<path id="Path_468" data-name="Path 468" d="M510.318,421.419v-.071a9.1,9.1,0,0,0-.3-3.411,17.272,17.272,0,0,1-.253-2.157,17.769,17.769,0,0,0,.253,4.106C510.123,420.393,510.24,420.906,510.318,421.419Z" transform="translate(373.864 -38.021)"/>
|
||||
<path id="Path_469" data-name="Path 469" d="M512.452,397.929a18.02,18.02,0,0,1,1.7-2.384c-1.189,1.137-3.385.123-3.846-1.455a3.788,3.788,0,0,0,0,1.949,2.852,2.852,0,0,0,2.15,1.89Z" transform="translate(373.712 -30.422)"/>
|
||||
<path id="Path_470" data-name="Path 470" d="M762.946,656.57s2.059,7.471,0,13.968S762.946,656.57,762.946,656.57Z" transform="translate(285.475 -122.381)" opacity="0.1"/>
|
||||
<path id="Path_471" data-name="Path 471" d="M776,668.3s.864,9.485-1.085,11.168S776,668.3,776,668.3Z" transform="translate(281.189 -126.49)" opacity="0.1"/>
|
||||
<line id="Line_88" data-name="Line 88" y1="0.65" x2="66.011" transform="translate(733.267 442.068)" fill="none" stroke="#00ddf1" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<line id="Line_89" data-name="Line 89" y1="0.643" x2="66.011" transform="translate(733.267 450.176)" fill="none" stroke="#00ddf1" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<line id="Line_90" data-name="Line 90" y1="0.65" x2="66.011" transform="translate(733.267 458.277)" fill="none" stroke="#00ddf1" stroke-miterlimit="10" stroke-width="2"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 41 KiB |
@@ -1,300 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { emailRegex } from "../constant/const";
|
||||
|
||||
const AddSigner = (props) => {
|
||||
const { t } = useTranslation();
|
||||
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);
|
||||
|
||||
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?.toLowerCase()?.replace(/\s/g, ""));
|
||||
}
|
||||
}, [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();
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
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 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],
|
||||
email: parseData?.Email
|
||||
});
|
||||
}
|
||||
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 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],
|
||||
email: parseData?.Email
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert(t("add-signer-alert"));
|
||||
setIsLoader(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in fetch contact", err);
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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">
|
||||
<p className="mb-3 text-xs text-gray-500">{t("add-signer-note")}</p>
|
||||
{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">
|
||||
{t("add-yourself")}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="name" className="block text-xs font-semibold">
|
||||
{t("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}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
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">
|
||||
{t("email")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={(e) =>
|
||||
setEmail(e.target.value?.toLowerCase()?.replace(/\s/g, ""))
|
||||
}
|
||||
required
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
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">
|
||||
{t("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"
|
||||
placeholder={t("phone-optional")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 flex gap-x-2 justify-start">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("reset")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddSigner;
|
||||
@@ -1,288 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import Title from "./Title";
|
||||
import Loader from "../primitives/Loader";
|
||||
import {
|
||||
copytoData,
|
||||
usertimezone
|
||||
} from "../constant/Utils";
|
||||
import {
|
||||
emailRegex,
|
||||
} from "../constant/const";
|
||||
import {
|
||||
useTranslation
|
||||
} from "react-i18next";
|
||||
function generatePassword(length) {
|
||||
const characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let result = "";
|
||||
const charactersLength = characters.length;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const AddUser = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const [formdata, setFormdata] = useState({
|
||||
name: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
team: "",
|
||||
password: "",
|
||||
role: ""
|
||||
});
|
||||
const [isFormLoader, setIsFormLoader] = useState(false);
|
||||
const [teamList, setTeamList] = useState([]);
|
||||
const role = ["OrgAdmin", "Editor", "User"];
|
||||
useEffect(() => {
|
||||
getTeamList();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const getTeamList = async () => {
|
||||
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
|
||||
const teamRes = await Parse.Cloud.run("getteams", { active: true });
|
||||
if (teamRes.length > 0) {
|
||||
const _teamRes = JSON.parse(JSON.stringify(teamRes));
|
||||
setTeamList(_teamRes);
|
||||
const allUserId =
|
||||
_teamRes.find((x) => x.Name === "All Users")?.objectId || "";
|
||||
setFormdata((prev) => ({ ...prev, team: allUserId }));
|
||||
}
|
||||
};
|
||||
const checkUserExist = async () => {
|
||||
try {
|
||||
const res = await Parse.Cloud.run("getUserDetails", {
|
||||
email: formdata.email
|
||||
});
|
||||
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();
|
||||
if (!emailRegex.test(formdata.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
setIsFormLoader(true);
|
||||
const res = await checkUserExist();
|
||||
if (res) {
|
||||
props.showAlert("danger", t("user-already-exist"));
|
||||
setIsFormLoader(false);
|
||||
} else {
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
const timezone = usertimezone;
|
||||
try {
|
||||
const params = {
|
||||
name: formdata.name,
|
||||
email: formdata.email,
|
||||
phone: formdata.phone,
|
||||
password: formdata.password,
|
||||
role: formdata.role,
|
||||
team: formdata.team,
|
||||
timezone: timezone,
|
||||
tenantId: localStorage.getItem("TenantId"),
|
||||
organization: {
|
||||
objectId: localUser?.OrganizationId?.objectId,
|
||||
company: localUser?.Company
|
||||
},
|
||||
};
|
||||
const res = await Parse.Cloud.run("adduser", params);
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
console.log("parseData ", parseData);
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find((x) => x.objectId === formdata.team);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsFormLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
props.showAlert("success", t("user-created-successfully"));
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
setIsFormLoader(false);
|
||||
props.showAlert("danger", t("something-went-wrong-mssg"));
|
||||
}
|
||||
} else {
|
||||
props.showAlert("danger", t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define a function to handle the "add yourself" checkbox
|
||||
const handleReset = () => {
|
||||
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
};
|
||||
const handleChange = (event) => {
|
||||
let { name, value } = event.target;
|
||||
if (name === "email") {
|
||||
value = value?.toLowerCase()?.replace(/\s/g, "");
|
||||
}
|
||||
setFormdata((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
copytoData(text);
|
||||
props.showAlert("success", t("copied"));
|
||||
};
|
||||
return (
|
||||
<div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative">
|
||||
<Title title={t("add-user")} />
|
||||
{isFormLoader && (
|
||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mx-auto">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("name")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
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"
|
||||
>
|
||||
{t("email")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formdata.email}
|
||||
onChange={(e) => handleChange(e)}
|
||||
required
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
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 className="block text-xs text-gray-700 font-semibold">
|
||||
{t("password")}
|
||||
</label>
|
||||
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
|
||||
<div className="break-all">{formdata?.password}</div>
|
||||
<i
|
||||
onClick={() => copytoclipboard(formdata?.password)}
|
||||
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
|
||||
></i>
|
||||
</div>
|
||||
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
|
||||
{t("password-generateed")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("phone")}
|
||||
{/* <span className="text-[red] text-[13px]"> *</span> */}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="phone"
|
||||
placeholder={t("phone-optional")}
|
||||
value={formdata.phone}
|
||||
onChange={(e) => handleChange(e)}
|
||||
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"
|
||||
>
|
||||
{t("Role")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<select
|
||||
value={formdata.role}
|
||||
onChange={(e) => handleChange(e)}
|
||||
name="role"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
>
|
||||
<option defaultValue={""} value={""}>
|
||||
{t("Select")}
|
||||
</option>
|
||||
{role.length > 0 &&
|
||||
role.map((x) => (
|
||||
<option key={x} value={x}>
|
||||
{x}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
</button>
|
||||
<div
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("cancel")}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddUser;
|
||||
@@ -0,0 +1,304 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
|
||||
const AppendFormInForm = (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.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();
|
||||
setIsLoader(true);
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
try {
|
||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||
contactQuery.set("Name", name);
|
||||
contactQuery.set("Phone", phone);
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
if (localStorage.getItem("TenetId")) {
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenetId")
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", name);
|
||||
_user.set("username", email);
|
||||
_user.set("email", email);
|
||||
_user.set("phone", phone);
|
||||
_user.set("password", phone);
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const body = {
|
||||
appName: localStorage.getItem("_appName"),
|
||||
roleName: "contracts_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));
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
|
||||
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));
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(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 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">
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mx-auto p-2">
|
||||
{isUserExist && (
|
||||
<div className="mb-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="addYourself"
|
||||
checked={addYourself}
|
||||
onChange={handleAddYourselfChange}
|
||||
className="form-checkbox"
|
||||
/>
|
||||
<label htmlFor="addYourself" className="ml-2 text-gray-700">
|
||||
Add Yourself
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Name
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
disabled={addYourself}
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Phone
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="phone"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
required
|
||||
disabled={addYourself}
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Email
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
disabled={addYourself}
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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="bg-[#188ae2] text-sm text-white px-4 py-2 rounded ml-2 shadow focus:outline-none"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppendFormInForm;
|
||||
@@ -1,289 +0,0 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import axios from "axios";
|
||||
import SuggestionInput from "./shared/fields/SuggestionInput";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
emailRegex,
|
||||
} from "../constant/const";
|
||||
const BulkSendUi = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const [forms, setForms] = useState([]);
|
||||
const formRef = useRef(null);
|
||||
const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false);
|
||||
const [isSubmit, setIsSubmit] = useState(false);
|
||||
const [isSignatureExist, setIsSignatureExist] = useState();
|
||||
const [isDisableBulkSend, setIsDisableBulkSend] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
useEffect(() => {
|
||||
signatureExist();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
//function to check atleast one signature field exist
|
||||
const signatureExist = async () => {
|
||||
setIsDisableBulkSend(false);
|
||||
const getPlaceholder = props?.Placeholders;
|
||||
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
|
||||
placeholderObj?.placeHolder?.some((holder) =>
|
||||
holder?.pos?.some((posItem) => posItem?.type === "signature")
|
||||
)
|
||||
);
|
||||
setIsSignatureExist(checkIsSignatureExistt);
|
||||
setIsLoader(false);
|
||||
};
|
||||
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 }]);
|
||||
}
|
||||
})();
|
||||
// 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 handleRemoveForm = (index) => {
|
||||
const updatedForms = forms.filter((_, i) => i !== index);
|
||||
setForms(updatedForms);
|
||||
};
|
||||
|
||||
function validateEmails(data) {
|
||||
for (const item of data) {
|
||||
for (const field of item.fields) {
|
||||
if (!emailRegex.test(field.email)) {
|
||||
alert(`Invalid email found: ${field.email}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsSubmit(true);
|
||||
if (validateEmails(forms)) {
|
||||
// Create a copy of Placeholders array from props.item
|
||||
let Placeholders = [...props.Placeholders];
|
||||
// Initialize an empty array to store updated documents
|
||||
let Documents = [];
|
||||
// Loop through each form
|
||||
forms.forEach((form) => {
|
||||
//checking if user enter email which already exist as a signer then add user in a signers array
|
||||
let existSigner = [];
|
||||
form.fields.map((data) => {
|
||||
if (data.signer) {
|
||||
existSigner.push(data.signer);
|
||||
}
|
||||
});
|
||||
// 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) {
|
||||
if (signer) {
|
||||
return {
|
||||
...placeholder,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
} else {
|
||||
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
|
||||
if (existSigner?.length > 0) {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders,
|
||||
Signers: props.item.Signers
|
||||
? [...props.item.Signers, ...existSigner]
|
||||
: [...existSigner]
|
||||
});
|
||||
} else {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders,
|
||||
SignatureType: props.signatureType
|
||||
});
|
||||
}
|
||||
});
|
||||
await batchQuery(Documents);
|
||||
} else {
|
||||
setIsSubmit(false);
|
||||
}
|
||||
};
|
||||
|
||||
const batchQuery = async (Documents) => {
|
||||
const token =
|
||||
{ "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const functionsUrl = `${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}functions/batchdocuments`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token,
|
||||
};
|
||||
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 (
|
||||
<>
|
||||
{isLoader ? (
|
||||
<div className="w-full h-[100px] flex justify-center items-center z-[999]">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{isSubmit && (
|
||||
<div className="absolute z-[999] h-full w-full flex justify-center items-center bg-black bg-opacity-30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{!isDisableBulkSend ? (
|
||||
<>
|
||||
{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 className="block text-xs font-semibold">
|
||||
{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-light fa-trash"></i>
|
||||
</button>
|
||||
)}
|
||||
<div ref={formRef}></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col mx-4 mb-4 gap-3">
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-accent focus:outline-none"
|
||||
>
|
||||
<i className="fa-light fa-paper-plane"></i>{" "}
|
||||
<span>{t("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">
|
||||
{t("quick-send-alert-1")}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||
{t("quick-send-alert-2")}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||
{t("quick-send-alert-3")}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BulkSendUi;
|
||||
@@ -1,126 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Parse from "parse";
|
||||
const EditContactForm = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [formData, setFormData] = useState({ Name: "", Email: "", Phone: "" });
|
||||
useEffect(() => {
|
||||
if (props.contact?.Email) {
|
||||
setFormData((prev) => ({ ...prev, ...props.contact }));
|
||||
}
|
||||
}, [props.contact]);
|
||||
const handleChange = (e) => {
|
||||
if (e.target.name === "Email") {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[e.target.name]: e.target.value?.toLowerCase()?.replace(/\s/g, "")
|
||||
}));
|
||||
} else {
|
||||
setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (props.handleEditContact) {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
const params = {
|
||||
contactId: props.contact.objectId,
|
||||
name: formData.Name,
|
||||
email: formData.Email,
|
||||
phone: formData.Phone,
|
||||
tenantId: localStorage.getItem("TenantId")
|
||||
};
|
||||
const res = await Parse.Cloud.run("editcontact", params);
|
||||
const updateContact = {
|
||||
...res,
|
||||
Name: formData.Name,
|
||||
Email: formData.Email,
|
||||
Phone: formData.Phone
|
||||
};
|
||||
props.handleEditContact(updateContact);
|
||||
} catch (err) {
|
||||
console.log("err in edit contact ", err);
|
||||
if (err.code === 137) {
|
||||
alert(t("contact-already-exists"));
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
props.handleClose && props.handleClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
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">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="name" className="block text-xs font-semibold">
|
||||
{t("name")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="Name"
|
||||
value={formData.Name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="email" className="block text-xs font-semibold">
|
||||
{t("email")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="Email"
|
||||
value={formData.Email}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="phone" className="block text-xs font-semibold">
|
||||
{t("phone")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="phone"
|
||||
name="Phone"
|
||||
value={formData.Phone}
|
||||
onChange={(e) => handleChange(e)}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
placeholder={t("phone-optional")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 flex gap-x-2 justify-start">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditContactForm;
|
||||
@@ -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;
|
||||
@@ -0,0 +1,51 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Package from "../../package.json";
|
||||
|
||||
const Footer = () => {
|
||||
const [showButton, setShowButton] = useState(false);
|
||||
const handleScroll = () => {
|
||||
if (window.pageYOffset >= 50) {
|
||||
setShowButton(true);
|
||||
} else {
|
||||
setShowButton(false);
|
||||
}
|
||||
};
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo(0, 0);
|
||||
setShowButton(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const appName = "OpenSign™";
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="bg-[#222c3c] text-[#98a6ba] text-center text-[13px] py-3">
|
||||
All Rights Reserved © {new Date().getFullYear()}
|
||||
{appName}{" "}
|
||||
(version:{" "}
|
||||
{localStorage.getItem("appVersion") &&
|
||||
`${Package.version}.${localStorage.getItem("appVersion")}`}
|
||||
)
|
||||
</div>
|
||||
<button
|
||||
className={`${
|
||||
showButton ? "block" : "hidden"
|
||||
} fixed bottom-4 right-4 px-3 p-2 text-xl bg-blue-500 text-white rounded focus:outline-none`}
|
||||
onClick={scrollToTop}
|
||||
>
|
||||
<i className="fa-solid fa-angle-up"></i>
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -1,73 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Package from "../../package.json";
|
||||
import axios from "axios";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const Footer = () => {
|
||||
const appName = "OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
const [showButton, setShowButton] = useState(false);
|
||||
const [version, setVersion] = useState("");
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get("/version.txt")
|
||||
.then((response) => {
|
||||
setVersion(response.data); // Set the retrieved data to the state variable
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error reading the file:", error);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleScroll = () => {
|
||||
if (window.pageYOffset >= 50) {
|
||||
setShowButton(true);
|
||||
} else {
|
||||
setShowButton(false);
|
||||
}
|
||||
};
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo(0, 0);
|
||||
setShowButton(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
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>
|
||||
{t("all-right")} © {new Date().getFullYear()}
|
||||
<span onClick={openUrl} className="hover:underline cursor-pointer">
|
||||
{appName} ( {t("version")}:{" "}
|
||||
{version ? version : `${Package.version} `})
|
||||
</span>
|
||||
</p>
|
||||
</aside>
|
||||
</footer>
|
||||
<button
|
||||
className={`${
|
||||
showButton ? "block" : "hidden"
|
||||
} fixed bottom-4 right-4 px-3 p-2 text-xl op-bg-secondary text-white rounded focus:outline-none`}
|
||||
onClick={scrollToTop}
|
||||
>
|
||||
<i className="fa-light fa-angle-up"></i>
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -67,12 +67,12 @@ 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-light fa-compress"></i>
|
||||
<i className="fa-solid fa-compress"></i>
|
||||
) : (
|
||||
<i className="fa-light fa-maximize"></i>
|
||||
<i className="fa fa-maximize"></i>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
@@ -0,0 +1,151 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import dp from "../assets/images/dp.png";
|
||||
import FullScreenButton from "./FullScreenButton";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Parse from "parse";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
const Header = ({ showSidebar }) => {
|
||||
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 toggleDropdown = () => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
const closeDropdown = () => {
|
||||
setIsOpen(false);
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
Parse.User.logOut();
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
let appName = localStorage.getItem("appName");
|
||||
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let domain = localStorage.getItem("domain");
|
||||
let _appName = localStorage.getItem("_appName");
|
||||
let baseUrl = localStorage.getItem("BaseUrl12");
|
||||
let appid = localStorage.getItem("AppID12");
|
||||
|
||||
localStorage.clear();
|
||||
|
||||
localStorage.setItem("appLogo", applogo);
|
||||
localStorage.setItem("appName", appName);
|
||||
localStorage.setItem("_appName", _appName);
|
||||
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("domain", domain);
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("BaseUrl12", baseUrl);
|
||||
localStorage.setItem("AppID12", appid);
|
||||
|
||||
navigation("/");
|
||||
};
|
||||
|
||||
//handle to close profile drop down menu onclick screen
|
||||
useEffect(() => {
|
||||
const closeMenuOnOutsideClick = (e) => {
|
||||
if (isOpen && !e.target.closest("#profile-menu")) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("click", closeMenuOnOutsideClick);
|
||||
|
||||
return () => {
|
||||
// Cleanup the event listener when the component unmounts
|
||||
document.removeEventListener("click", closeMenuOnOutsideClick);
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
return (
|
||||
<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"
|
||||
src={applogo}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="profile-menu"
|
||||
className="flex justify-between items-center gap-x-3"
|
||||
>
|
||||
<div>
|
||||
<FullScreenButton />
|
||||
</div>
|
||||
{width >= 768 && (
|
||||
<div
|
||||
onClick={toggleDropdown}
|
||||
className="cursor-pointer w-[35px] h-[35px] rounded-full ring-[1px] ring-offset-2 ring-gray-400 overflow-hidden"
|
||||
>
|
||||
<img
|
||||
className="w-[35px] h-[35px] object-contain"
|
||||
src={image}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{width >= 768 && (
|
||||
<div onClick={toggleDropdown} className="cursor-pointer text-sm">
|
||||
{username && username}
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
<div className="cursor-pointer">
|
||||
<i onClick={toggleDropdown} className="fa-solid fa-angle-down"></i>
|
||||
</div>
|
||||
<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"
|
||||
}`}
|
||||
>
|
||||
<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
|
||||
</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
|
||||
</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
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@@ -1,202 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import dp from "../assets/images/dp.png";
|
||||
import FullScreenButton from "./FullScreenButton";
|
||||
import { useNavigate } from "react-router";
|
||||
import Parse from "parse";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import {
|
||||
getAppLogo,
|
||||
openInNewTab,
|
||||
saveLanguageInLocal
|
||||
} from "../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
|
||||
const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { width } = useWindowSize();
|
||||
const username = localStorage.getItem("username") || "";
|
||||
const image = localStorage.getItem("profileImg") || dp;
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [applogo, setAppLogo] = useState("");
|
||||
|
||||
const toggleDropdown = () => {
|
||||
setIsOpen(!isOpen);
|
||||
if (width <= 768) {
|
||||
setIsMenu(false);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
initializeHead();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
|
||||
async function initializeHead() {
|
||||
const applogo = await getAppLogo();
|
||||
if (applogo?.logo) {
|
||||
setAppLogo(applogo?.logo);
|
||||
} else {
|
||||
const logo = localStorage.getItem("appLogo") || appInfo.applogo;
|
||||
setAppLogo(logo);
|
||||
}
|
||||
}
|
||||
|
||||
const closeDropdown = async () => {
|
||||
setIsOpen(false);
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
|
||||
localStorage.clear();
|
||||
saveLanguageInLocal(i18n);
|
||||
localStorage.setItem("appLogo", applogo);
|
||||
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
|
||||
navigate("/");
|
||||
};
|
||||
|
||||
//handle to close profile drop down menu onclick screen
|
||||
useEffect(() => {
|
||||
const closeMenuOnOutsideClick = (e) => {
|
||||
if (isOpen && !e.target.closest("#profile-menu")) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("click", closeMenuOnOutsideClick);
|
||||
|
||||
return () => {
|
||||
// Cleanup the event listener when the component unmounts
|
||||
document.removeEventListener("click", closeMenuOnOutsideClick);
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<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 hover:bg-transparent op-btn-sm no-animation"
|
||||
onClick={showSidebar}
|
||||
>
|
||||
<i className="fa-light fa-bars text-xl text-base-content"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 ml-2">
|
||||
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
|
||||
{applogo && (
|
||||
<img
|
||||
className="object-contain h-full w-auto"
|
||||
src={applogo}
|
||||
alt="logo"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div id="profile-menu" className="flex-none gap-2">
|
||||
<div>
|
||||
<FullScreenButton />
|
||||
</div>
|
||||
{width >= 768 && (
|
||||
<div
|
||||
onClick={toggleDropdown}
|
||||
className="cursor-pointer w-[35px] h-[35px] rounded-full ring-[1px] ring-offset-2 ring-gray-400 overflow-hidden"
|
||||
>
|
||||
<img
|
||||
className="w-[35px] h-[35px] object-contain"
|
||||
src={image}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{width >= 768 && (
|
||||
<div
|
||||
onClick={toggleDropdown}
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
className="cursor-pointer text-base-content text-sm"
|
||||
>
|
||||
{username && username}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="op-dropdown op-dropdown-open op-dropdown-end"
|
||||
id="profile-menu"
|
||||
>
|
||||
<div
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onClick={toggleDropdown}
|
||||
className="op-btn op-btn-ghost op-btn-xs w-[10px] h-[20px] hover:bg-transparent"
|
||||
>
|
||||
<i className="fa-light fa-angle-down text-base-content"></i>
|
||||
</div>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
className={`mt-3 z-[1] p-2 shadow op-dropdown-open op-menu op-menu-sm op-dropdown-content text-base-content bg-base-100 rounded-box w-52 ${
|
||||
isOpen ? "" : "hidden"
|
||||
}`}
|
||||
>
|
||||
{!isConsole && (
|
||||
<>
|
||||
<li
|
||||
onClick={() =>
|
||||
openInNewTab("https://docs.opensignlabs.com")
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-light fa-book"></i> {t("docs")}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
navigate("/profile");
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-light fa-user"></i> {t("profile")}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
navigate("/changepassword");
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-light fa-lock"></i>{" "}
|
||||
{t("change-password")}
|
||||
</span>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
<li onClick={closeDropdown}>
|
||||
<span>
|
||||
<i className="fa-light fa-arrow-right-from-bracket"></i>{" "}
|
||||
{t("log-out")}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@@ -0,0 +1,236 @@
|
||||
import React, { useState } from "react";
|
||||
import Parse from "parse";
|
||||
// import FacebookLogin from "react-facebook-login";
|
||||
|
||||
const LoginFacebook = ({
|
||||
FBCred,
|
||||
thirdpartyLoginfn,
|
||||
thirdpartyLoader,
|
||||
setThirdpartyLoader
|
||||
}) => {
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [userDetails, setUserDetails] = useState({
|
||||
Phone: "",
|
||||
Company: "",
|
||||
Name: "",
|
||||
Email: ""
|
||||
});
|
||||
const [fBDetails, setFBDetails] = useState({
|
||||
Id: "",
|
||||
AccessToken: "",
|
||||
Name: "",
|
||||
Email: ""
|
||||
});
|
||||
const responseFacebook = async (response) => {
|
||||
// console.log("response ", response);
|
||||
if (response.userID) {
|
||||
setThirdpartyLoader(true);
|
||||
const details = {
|
||||
Email: response.email,
|
||||
Name: response.name,
|
||||
};
|
||||
setUserDetails({ ...userDetails, ...details });
|
||||
const fDetails = {
|
||||
Id: response.userID,
|
||||
AccessToken: response.accessToken,
|
||||
Name: response.name,
|
||||
Email: response.email,
|
||||
};
|
||||
setFBDetails({ ...fBDetails, ...fDetails });
|
||||
const res = await checkExtUser(fDetails);
|
||||
}
|
||||
};
|
||||
|
||||
const checkExtUser = async (details) => {
|
||||
const extUser = new Parse.Query("contracts_Users");
|
||||
extUser.equalTo("Email", details.Email);
|
||||
const extRes = await extUser.first();
|
||||
// console.log("extRes ", extRes);
|
||||
if (extRes) {
|
||||
const params = { ...details, Phone: extRes.get("Phone") };
|
||||
const payload = await Parse.Cloud.run("facebooksign", params);
|
||||
// console.log("payload ", payload);
|
||||
if (payload && payload.sessiontoken) {
|
||||
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.Email,
|
||||
phone: extRes.get("Phone"),
|
||||
company: extRes.get("Company")
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||
thirdpartyLoginfn(payload.sessiontoken, billingDate);
|
||||
}
|
||||
return { msg: "exist" };
|
||||
} else {
|
||||
setIsModal(true);
|
||||
setThirdpartyLoader(false);
|
||||
return { msg: "notexist" };
|
||||
}
|
||||
};
|
||||
const handleSubmitbtn = async () => {
|
||||
if (userDetails.Phone && userDetails.Company) {
|
||||
setThirdpartyLoader(true);
|
||||
const params = { ...fBDetails, Phone: userDetails.Phone };
|
||||
const payload = await Parse.Cloud.run("facebooksign", params);
|
||||
// console.log("payload ", payload);
|
||||
|
||||
if (payload && payload.sessiontoken) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
name: userDetails.Name,
|
||||
email: userDetails.Email,
|
||||
// "passsword":userDetails.Phone,
|
||||
phone: userDetails.Phone,
|
||||
role: "contracts_Admin",
|
||||
company: userDetails.Company
|
||||
}
|
||||
};
|
||||
const userSignUp = await Parse.Cloud.run("usersignup", params);
|
||||
// console.log("userSignUp ", userSignUp);
|
||||
if (userSignUp && userSignUp.sessionToken) {
|
||||
const LocalUserDetails = {
|
||||
name: userDetails.Name,
|
||||
email: userDetails.Email,
|
||||
phone: userDetails.Phone,
|
||||
company: userDetails.Company
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||
thirdpartyLoginfn(userSignUp.sessionToken);
|
||||
} else {
|
||||
alert(userSignUp.message);
|
||||
}
|
||||
} else {
|
||||
alert("Internal server error !");
|
||||
}
|
||||
} else {
|
||||
alert("Please fill required details!");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div style={{ position: "relative" }}>
|
||||
{thirdpartyLoader && (
|
||||
<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
|
||||
appId={FBCred}
|
||||
fields="name,email,picture"
|
||||
callback={responseFacebook}
|
||||
cssClass="btn btn-info btn-md btn-fb"
|
||||
icon="fa fa-facebook-f"
|
||||
/> */}
|
||||
{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>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
onClick={() => setIsModal(false)}
|
||||
style={{ marginRight: 10, width: 90 }}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleSubmitbtn()}
|
||||
className="btn btn-info"
|
||||
>
|
||||
Sign up
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginFacebook;
|
||||
@@ -0,0 +1,312 @@
|
||||
import React, { useState, useRef } from "react";
|
||||
import Parse from "parse";
|
||||
import jwtDecode from "jwt-decode";
|
||||
import { useScript } from "../hook/useScript";
|
||||
|
||||
/*
|
||||
* `GoogleSignInBtn`as it's name indicates it render google sign in button
|
||||
* and in this `useScript` in which we have created for generate google sign button
|
||||
* and when user click on sign in with google it will be verify on server side
|
||||
* and then generate token
|
||||
*/
|
||||
const GoogleSignInBtn = ({
|
||||
GoogleCred,
|
||||
thirdpartyLoginfn,
|
||||
thirdpartyLoader,
|
||||
setThirdpartyLoader
|
||||
}) => {
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const googleBtn = useRef();
|
||||
const [userDetails, setUserDetails] = useState({
|
||||
Phone: "",
|
||||
Company: "",
|
||||
Name: "",
|
||||
Email: "",
|
||||
Destination: ""
|
||||
});
|
||||
const [googleDetails, setGoogleDetails] = useState({
|
||||
Id: "",
|
||||
TokenId: "",
|
||||
Gmail: "",
|
||||
Name: ""
|
||||
});
|
||||
useScript("https://accounts.google.com/gsi/client", () => {
|
||||
window.google.accounts.id.initialize({
|
||||
client_id: GoogleCred,
|
||||
callback: responseGoogle,
|
||||
auto_select: false
|
||||
});
|
||||
window.google.accounts.id.renderButton(googleBtn.current, {
|
||||
theme: "outline",
|
||||
size: "large",
|
||||
width: "187px"
|
||||
});
|
||||
});
|
||||
const responseGoogle = async (response) => {
|
||||
setThirdpartyLoader(true);
|
||||
// console.log("response ", response);
|
||||
if (response.credential) {
|
||||
const data = jwtDecode(response.credential);
|
||||
// console.log("data ", data);
|
||||
if (data.sub && data.email) {
|
||||
const details = {
|
||||
Email: data.email,
|
||||
Name: data.name
|
||||
};
|
||||
setUserDetails({ ...userDetails, ...details });
|
||||
const Gdetails = {
|
||||
Id: data.sub,
|
||||
TokenId: response.credential,
|
||||
Gmail: data.email,
|
||||
Name: data.name
|
||||
};
|
||||
setGoogleDetails({ ...googleDetails, ...Gdetails });
|
||||
await checkExtUser(Gdetails);
|
||||
}
|
||||
}
|
||||
};
|
||||
const checkExtUser = async (details) => {
|
||||
const extUser = new Parse.Query("contracts_Users");
|
||||
extUser.equalTo("Email", details.Gmail);
|
||||
const extRes = await extUser.first();
|
||||
// console.log("extRes ", extRes);
|
||||
if (extRes) {
|
||||
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");
|
||||
// console.log("billingDate expired", billingDate > new Date());
|
||||
const LocalUserDetails = {
|
||||
name: details.Name,
|
||||
email: details.Gmail,
|
||||
phone: extRes.get("Phone"),
|
||||
company: extRes.get("Company")
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||
thirdpartyLoginfn(payload.sessiontoken, billingDate);
|
||||
}
|
||||
return { msg: "exist" };
|
||||
} else {
|
||||
setIsModal(true);
|
||||
setThirdpartyLoader(false);
|
||||
return { msg: "notexist" };
|
||||
}
|
||||
};
|
||||
const handleSubmitbtn = async () => {
|
||||
if (userDetails.Phone && userDetails.Company) {
|
||||
setThirdpartyLoader(true);
|
||||
// e.preventDefault()
|
||||
// console.log("handelSubmit", userDetails);
|
||||
const params = { ...googleDetails, Phone: userDetails.Phone };
|
||||
const payload = await Parse.Cloud.run("googlesign", params);
|
||||
|
||||
// console.log("payload ", payload);
|
||||
if (payload && payload.sessiontoken) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
name: userDetails.Name,
|
||||
email: userDetails.Email,
|
||||
// "passsword":userDetails.Phone,
|
||||
phone: userDetails.Phone,
|
||||
role: "contracts_Admin",
|
||||
company: userDetails.Company,
|
||||
jobTitle: userDetails.Destination
|
||||
}
|
||||
};
|
||||
const userSignUp = await Parse.Cloud.run("usersignup", params);
|
||||
// console.log("userSignUp ", userSignUp);
|
||||
if (userSignUp && userSignUp.sessionToken) {
|
||||
const LocalUserDetails = {
|
||||
name: userDetails.Name,
|
||||
email: userDetails.Email,
|
||||
phone: userDetails.Phone,
|
||||
company: userDetails.Company
|
||||
// jobTitle: userDetails.JobTitle
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||
thirdpartyLoginfn(userSignUp.sessionToken);
|
||||
} else {
|
||||
alert(userSignUp.message);
|
||||
}
|
||||
} else if (
|
||||
payload &&
|
||||
payload.message.replace(/ /g, "_") === "Internal_server_err"
|
||||
) {
|
||||
alert("Internal server error !");
|
||||
}
|
||||
} else {
|
||||
alert("Please fill required details!");
|
||||
}
|
||||
};
|
||||
const handleCloseModal = () => {
|
||||
setIsModal(false);
|
||||
Parse.User.logOut();
|
||||
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
let appName = localStorage.getItem("appName");
|
||||
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let domain = localStorage.getItem("domain");
|
||||
let _appName = localStorage.getItem("_appName");
|
||||
let baseUrl = localStorage.getItem("BaseUrl12");
|
||||
let appid = localStorage.getItem("AppID12");
|
||||
|
||||
localStorage.clear();
|
||||
|
||||
localStorage.setItem("appLogo", applogo);
|
||||
localStorage.setItem("appName", appName);
|
||||
localStorage.setItem("_appName", _appName);
|
||||
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("domain", domain);
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("BaseUrl12", baseUrl);
|
||||
localStorage.setItem("AppID12", appid);
|
||||
};
|
||||
return (
|
||||
<div style={{ position: "relative" }}>
|
||||
{thirdpartyLoader && (
|
||||
<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>
|
||||
|
||||
{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="Destination"
|
||||
style={{ display: "flex" }}
|
||||
className="col-form-label"
|
||||
>
|
||||
Destination{" "}
|
||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="Destination"
|
||||
value={userDetails.Destination}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Destination: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-[#6c757d] p-2 text-white rounded"
|
||||
onClick={handleCloseModal}
|
||||
style={{ marginRight: 10, width: 90 }}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-[#17a2b8] p-2 text-white rounded"
|
||||
onClick={() => handleSubmitbtn()}
|
||||
>
|
||||
Sign up
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GoogleSignInBtn;
|
||||
@@ -1,80 +0,0 @@
|
||||
import React from "react";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import { Stage, Layer, Rect, Text } from "react-konva";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const RenderDebugPdf = (props) => {
|
||||
const { t } = useTranslation();
|
||||
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={() => props.setPdfLoadFail(false)}
|
||||
loading={t("loading-doc")}
|
||||
error={<p className="mx-2">{t("failed-to-load-refresh-page")}</p>}
|
||||
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) => {
|
||||
const textX = value.x + value.width / 3;
|
||||
const textY = value.y + value.height / 2.5;
|
||||
return (
|
||||
<React.Fragment key={value.key}>
|
||||
<Rect
|
||||
x={value.x}
|
||||
y={value.y}
|
||||
width={value.width}
|
||||
height={value.height}
|
||||
fill="#cbe9ed"
|
||||
stroke="black"
|
||||
/>
|
||||
<Text
|
||||
x={textX}
|
||||
y={textY}
|
||||
text={
|
||||
value?.key > 0 ? "box " + value.key.toString() : "box"
|
||||
}
|
||||
fontSize={16}
|
||||
fill="black"
|
||||
align="center"
|
||||
verticalAlign="middle"
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</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;
|
||||
@@ -1,39 +0,0 @@
|
||||
import React from "react";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function RotateAlert(props) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.showRotateAlert}
|
||||
title={t("Rotation-alert")}
|
||||
handleClose={() => props.setShowRotateAlert({ status: false, degree: 0 })}
|
||||
>
|
||||
<div className="p-[20px] h-full">
|
||||
<p>{t("rotate-alert-mssg")}</p>
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
onClick={() => props.handleRemoveWidgets()}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
props.setShowRotateAlert({ status: false, degree: 0 })
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost shadow-md"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default RotateAlert;
|
||||
@@ -1,54 +0,0 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { NavLink } from "react-router";
|
||||
|
||||
const SocialMedia = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<NavLink
|
||||
to="https://github.com/opensignlabs/opensign"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i aria-hidden="true" className="fa-brands fa-github"></i>
|
||||
<span className="fa-sr-only">
|
||||
OpenSign's {t("social-media.github")}
|
||||
</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="https://www.linkedin.com/company/opensign%E2%84%A2/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i aria-hidden="true" className="fa-brands fa-linkedin"></i>
|
||||
<span className="fa-sr-only">
|
||||
OpenSign's {t("social-media.linked-in")}
|
||||
</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="https://www.twitter.com/opensignlabs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i aria-hidden="true" className="fa-brands fa-square-x-twitter"></i>
|
||||
<span className="fa-sr-only">
|
||||
OpenSign's {t("social-media.twitter")}
|
||||
</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="https://discord.com/invite/xe9TDuyAyj"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i aria-hidden="true" className="fa-brands fa-discord"></i>
|
||||
<span className="fa-sr-only">
|
||||
OpenSign's {t("social-media.discord")}
|
||||
</span>
|
||||
</NavLink>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default SocialMedia;
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
function Title(props) {
|
||||
return (
|
||||
<Helmet>
|
||||
<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"
|
||||
href={localStorage.getItem("fev_Icon")}
|
||||
sizes="40x40"
|
||||
/>
|
||||
</Helmet>
|
||||
);
|
||||
}
|
||||
|
||||
export default Title;
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
function Title({ title, drive }) {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
return (
|
||||
<Helmet>
|
||||
<title>{drive ? title : `${title} - ${appName}`}</title>
|
||||
<meta name="description" content={`${title} - ${appName}`} />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href={localStorage.getItem("fev_Icon")}
|
||||
sizes="40x40"
|
||||
/>
|
||||
</Helmet>
|
||||
);
|
||||
}
|
||||
|
||||
export default Title;
|
||||
@@ -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;
|
||||
@@ -0,0 +1,894 @@
|
||||
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 {
|
||||
var emp = Parse.Object.extend(
|
||||
localStorage.getItem("extended_class")
|
||||
);
|
||||
var q = new Parse.Query(emp);
|
||||
q.equalTo("UserId", currentUser);
|
||||
res = await q.first();
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
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">×</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,60 +0,0 @@
|
||||
import React from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { openInNewTab } from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const DashboardButton = (props) => {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
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">
|
||||
<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-light fa-info"
|
||||
} text-[25px] lg:text-[30px]`}
|
||||
></i>
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-lg ml-3 text-base-content">
|
||||
{t(`sidebar.${props.Label}`)}
|
||||
{props.Label === "Sign yourself" && (
|
||||
<div className="text-gray-500 text-xs mt-1">
|
||||
{t("signyour-self-button")}
|
||||
</div>
|
||||
)}
|
||||
{props.Label === "Request signatures" && (
|
||||
<div className="text-gray-500 text-xs mt-1">
|
||||
{t("requestsign-button")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardButton;
|
||||
@@ -1,14 +1,13 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import Parse from "parse";
|
||||
import onSearchFilter from "../../constant/searchQuery";
|
||||
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
|
||||
import { useNavigate } from "react-router";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import "../../styles/loader.css";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const DashboardCard = (props) => {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [response, setresponse] = useState("");
|
||||
@@ -25,12 +24,25 @@ const DashboardCard = (props) => {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
let body = {};
|
||||
let currentUser;
|
||||
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
let currentUser1 = Parse.User.current();
|
||||
currentUser = currentUser1.id;
|
||||
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");
|
||||
var emp = Parse.Object.extend(localStorage.getItem("extended_class"));
|
||||
var q = new Parse.Query(emp);
|
||||
q.equalTo("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: currentUser
|
||||
});
|
||||
res = await q.first();
|
||||
if (res) res = res.toJSON();
|
||||
}
|
||||
if (res) {
|
||||
@@ -38,7 +50,12 @@ const DashboardCard = (props) => {
|
||||
var reg = /(\#.*?\#)/gi; // eslint-disable-line
|
||||
var str = props.Data.query;
|
||||
var test = "";
|
||||
|
||||
if (str.includes("#filterCondition#")) {
|
||||
str = str.replace(
|
||||
"#filterCondition#",
|
||||
onSearchFilter(props.DefaultQuery)
|
||||
);
|
||||
}
|
||||
if (props.Data.extendkey) {
|
||||
let a = props.Data.extendkey.split(".");
|
||||
if (a.length > 0) {
|
||||
@@ -76,6 +93,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;
|
||||
@@ -91,8 +110,13 @@ const DashboardCard = (props) => {
|
||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
resr = data[0];
|
||||
} else {
|
||||
resr = await Parse.Cloud.run("getUserDetails");
|
||||
if (resr) resr = resr.toJSON();
|
||||
let emp = Parse.Object.extend(
|
||||
localStorage.getItem("extended_class")
|
||||
);
|
||||
let q = new Parse.Query(emp);
|
||||
q.equalTo("UserId", currentUser);
|
||||
let t = await q.first();
|
||||
resr = t.toJSON();
|
||||
}
|
||||
|
||||
let json = resr;
|
||||
@@ -126,65 +150,47 @@ const DashboardCard = (props) => {
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
// handle need your sign report count
|
||||
if (props.Data.Redirect_id === "4Hhwbp482K") {
|
||||
const params = {
|
||||
reportId: props.Data.Redirect_id,
|
||||
skip: 0,
|
||||
limit: 200
|
||||
};
|
||||
const url = `${parseBaseUrl}functions/getReport`;
|
||||
await axios
|
||||
.post(url, params, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
const listData = res.data?.result?.filter(
|
||||
(x) => x.Signers.length > 0
|
||||
);
|
||||
let arr = [];
|
||||
for (const obj of listData) {
|
||||
const isSigner = obj.Signers.some(
|
||||
(item) => item.UserId.objectId === currentUser.id
|
||||
await axios.get(url, { headers: headers }).then((res) => {
|
||||
if (res.data.results.length > 0) {
|
||||
setLoading(false);
|
||||
if (props.Data.key !== "count") {
|
||||
setresponse(res.data.results[0][props.Data.key]);
|
||||
} else {
|
||||
if (props.Label === "Need your Signature") {
|
||||
const listData = res.data?.results.filter(
|
||||
(x) => x.Signers.length > 0
|
||||
);
|
||||
if (isSigner) {
|
||||
let isRecord;
|
||||
if (obj?.AuditTrail && obj?.AuditTrail.length > 0) {
|
||||
isRecord = obj?.AuditTrail.some(
|
||||
(item) =>
|
||||
item?.UserPtr?.UserId?.objectId === currentUser.id &&
|
||||
item.Activity === "Signed"
|
||||
);
|
||||
} else {
|
||||
isRecord = false;
|
||||
}
|
||||
if (isRecord === false) {
|
||||
arr.push(obj);
|
||||
let arr = [];
|
||||
for (const obj of listData) {
|
||||
const isSigner = obj.Signers.some(
|
||||
(item) => item.UserId.objectId === currentUser.id
|
||||
);
|
||||
if (isSigner) {
|
||||
let isRecord;
|
||||
if (obj?.AuditTrail && obj?.AuditTrail.length > 0) {
|
||||
isRecord = obj?.AuditTrail.some(
|
||||
(item) =>
|
||||
item?.UserPtr?.UserId?.objectId === currentUser.id &&
|
||||
item.Activity === "Signed"
|
||||
);
|
||||
} else {
|
||||
isRecord = false;
|
||||
}
|
||||
if (isRecord === false) {
|
||||
arr.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setresponse(arr.length);
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
await axios.get(url, { headers: headers }).then((res) => {
|
||||
if (res.data.results.length > 0) {
|
||||
setLoading(false);
|
||||
if (props.Data.key !== "count") {
|
||||
setresponse(res.data.results[0][props.Data.key]);
|
||||
setresponse(arr.length);
|
||||
} else {
|
||||
setresponse(res.data[props.Data.key]);
|
||||
}
|
||||
} else {
|
||||
setresponse(0);
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setresponse(0);
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Problem", e);
|
||||
setLoading(false);
|
||||
@@ -213,13 +219,21 @@ 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")) {
|
||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
res = data[0];
|
||||
} else {
|
||||
let resr = await Parse.Cloud.run("getUserDetails");
|
||||
if (res) res = resr.toJSON();
|
||||
var emp = Parse.Object.extend(
|
||||
localStorage.getItem("extended_class")
|
||||
);
|
||||
var q = new Parse.Query(emp);
|
||||
q.equalTo("UserId", currentUser);
|
||||
let resr = await q.first();
|
||||
res = resr.toJSON();
|
||||
}
|
||||
|
||||
let json = res;
|
||||
@@ -253,14 +267,21 @@ const DashboardCard = (props) => {
|
||||
}
|
||||
}
|
||||
body = str;
|
||||
const response = await axios.post(url, body, { headers: headers });
|
||||
if (response.data.result.length > 0) {
|
||||
setresponse(response.data.result[0][props.FilterData.key]);
|
||||
setLoading(false);
|
||||
} else {
|
||||
setresponse("0");
|
||||
setLoading(false);
|
||||
}
|
||||
await axios
|
||||
.post(url, body, { headers: headers })
|
||||
.then((response) => {
|
||||
try {
|
||||
if (response.data.result.length > 0) {
|
||||
setresponse(response.data.result[0][props.FilterData.key]);
|
||||
setLoading(false);
|
||||
} else {
|
||||
setresponse("0");
|
||||
setLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -313,7 +334,6 @@ const DashboardCard = (props) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => openReport()}
|
||||
@@ -321,33 +341,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-base-300 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-light fa-info"
|
||||
props.Icon ? props.Icon : "fa fa-solid fa-info"
|
||||
} text-[25px] lg:text-[30px]`}
|
||||
></i>
|
||||
</span>
|
||||
|
||||
<div className="font-medium">
|
||||
<div className="text-base lg:text-lg">
|
||||
{t(`dashboard-card.${props.Label}`)}
|
||||
</div>
|
||||
<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={t(`tour-mssg.${props.Label}`)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import ReportTable from "../../primitives/GetReportDisplay";
|
||||
import reportJson from "../../json/ReportJson";
|
||||
import axios from "axios";
|
||||
function DashboardReport(props) {
|
||||
// console.log("props ", props)
|
||||
const [List, setList] = useState([]);
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
const [reportName, setReportName] = useState("");
|
||||
const [actions, setActions] = useState([]);
|
||||
useEffect(() => {
|
||||
setReportName("");
|
||||
getReportData(props.Record.reportId);
|
||||
}, [props.Record.reportId]);
|
||||
|
||||
const getReportData = async (id) => {
|
||||
setIsLoader(true);
|
||||
const json = reportJson(id);
|
||||
if (json) {
|
||||
setActions(json.actions);
|
||||
setReportName(json.reportName);
|
||||
const { className, params, keys, orderBy } = json;
|
||||
Parse.serverURL = localStorage.getItem("BaseUrl12");
|
||||
Parse.initialize(localStorage.getItem("AppID12"));
|
||||
const serverURL =
|
||||
localStorage.getItem("BaseUrl12") + "classes/" + className;
|
||||
|
||||
const strParams = JSON.stringify(params);
|
||||
const strKeys = keys.join();
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("AppID12"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
try {
|
||||
const url = `${serverURL}?where=${strParams}&keys=${strKeys}&order=${orderBy}&include=AuditTrail.UserPtr`;
|
||||
const res = await axios.get(url, { headers: headers });
|
||||
// console.log("res ", res.data?.results);
|
||||
if (id === "5Go51Q7T8r") {
|
||||
const currentUser = Parse.User.current().id;
|
||||
const listData = res.data?.results.filter(
|
||||
(x) => x.Signers.length > 0
|
||||
);
|
||||
let arr = [];
|
||||
for (const obj of listData) {
|
||||
const isSigner = obj.Signers.some(
|
||||
(item) => item.UserId.objectId === currentUser
|
||||
);
|
||||
if (isSigner) {
|
||||
let isRecord;
|
||||
if (obj?.AuditTrail && obj?.AuditTrail.length > 0) {
|
||||
isRecord = obj?.AuditTrail.some(
|
||||
(item) =>
|
||||
item?.UserPtr?.UserId?.objectId === currentUser &&
|
||||
item.Activity === "Signed"
|
||||
);
|
||||
} else {
|
||||
isRecord = false;
|
||||
}
|
||||
if (isRecord === false) {
|
||||
arr.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
setList(arr);
|
||||
} else {
|
||||
setList(res.data?.results);
|
||||
}
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
setIsLoader(false);
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{isLoader ? (
|
||||
<div
|
||||
style={{
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{reportName ? (
|
||||
<ReportTable
|
||||
ReportName={reportName}
|
||||
List={List}
|
||||
act={"Sign"}
|
||||
actions={actions}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-[100px] w-full bg-white rounded">
|
||||
<div className="text-center">
|
||||
<p className="text-xl text-black">Report Not Found</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default DashboardReport;
|
||||
@@ -0,0 +1,431 @@
|
||||
import React, { useState, Suspense, lazy } from "react";
|
||||
import moment from "moment";
|
||||
import ErrorBoundary from "../ErrorBoundary";
|
||||
|
||||
const DashboardCard = lazy(() => import("./DashboardCard"));
|
||||
|
||||
const DashboardReport = lazy(() => import("./DashboardReport"));
|
||||
|
||||
const GetDashboard = (props) => {
|
||||
const [filter, setfilter] = useState("");
|
||||
const [option, setoption] = useState("");
|
||||
|
||||
const onSearchFilter = (e) => {
|
||||
let _search = e.target.value;
|
||||
localStorage.setItem("DashboardDefaultFilter", _search);
|
||||
switch (_search) {
|
||||
case "Today": {
|
||||
let d = new Date();
|
||||
let formDate = new Date(
|
||||
Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0)
|
||||
).toISOString();
|
||||
let Todate = new Date(
|
||||
Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59)
|
||||
).toISOString();
|
||||
let Query = `'$gte':'${formDate}','$lte':'${Todate}'`;
|
||||
setfilter(Query);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "Yesterday": {
|
||||
let d1 = new Date();
|
||||
d1.setDate(d1.getDate() - 1);
|
||||
let yesFrDate = new Date(
|
||||
Date.UTC(d1.getFullYear(), d1.getMonth(), d1.getDate(), 0, 0, 0)
|
||||
).toISOString();
|
||||
|
||||
let YesTodate = new Date(
|
||||
Date.UTC(d1.getFullYear(), d1.getMonth(), d1.getDate(), 23, 59, 59)
|
||||
).toISOString();
|
||||
let Query1 = `'$gte':'${yesFrDate}','$lte':'${YesTodate}'`;
|
||||
setfilter(Query1);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "This week": {
|
||||
let oneDay = 24 * 60 * 60 * 1000;
|
||||
let curr = new Date(); // get current date
|
||||
let first = curr.getDate() - curr.getDay(); // First day is the day of the month - the day of the week
|
||||
|
||||
let firstday = new Date(
|
||||
new Date(
|
||||
Date.UTC(
|
||||
curr.getFullYear(),
|
||||
curr.getMonth(),
|
||||
curr.getDate(),
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
).setDate(first)
|
||||
).toISOString();
|
||||
let diffDays = Math.round(
|
||||
Math.abs(
|
||||
(new Date(
|
||||
Date.UTC(
|
||||
curr.getFullYear(),
|
||||
curr.getMonth(),
|
||||
curr.getDate(),
|
||||
23,
|
||||
59,
|
||||
59
|
||||
)
|
||||
) -
|
||||
new Date(firstday)) /
|
||||
oneDay
|
||||
)
|
||||
); //Days since last week
|
||||
let last = first + diffDays;
|
||||
let lastday = new Date(
|
||||
new Date(
|
||||
Date.UTC(
|
||||
curr.getFullYear(),
|
||||
curr.getMonth(),
|
||||
curr.getDate(),
|
||||
23,
|
||||
59,
|
||||
59
|
||||
)
|
||||
).setDate(last)
|
||||
).toISOString();
|
||||
let Query2 = `'$gte':'${firstday}','$lte':'${lastday}'`;
|
||||
setfilter(Query2);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "Last 7 days": {
|
||||
let d7 = new Date();
|
||||
let d7l = new Date();
|
||||
d7.setDate(d7.getDate() - 7);
|
||||
let l7t = new Date(
|
||||
Date.UTC(d7l.getFullYear(), d7l.getMonth(), d7l.getDate(), 23, 59, 59)
|
||||
).toISOString();
|
||||
let date7 = new Date(
|
||||
Date.UTC(d7.getFullYear(), d7.getMonth(), d7.getDate(), 0, 0, 0)
|
||||
).toISOString();
|
||||
let Query7 = `'$gte':'${date7}','$lte':'${l7t}'`;
|
||||
setfilter(Query7);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "Last 14 days": {
|
||||
let d14 = new Date();
|
||||
let d14l = new Date();
|
||||
let l14t = new Date(
|
||||
Date.UTC(
|
||||
d14l.getFullYear(),
|
||||
d14l.getMonth(),
|
||||
d14l.getDate(),
|
||||
23,
|
||||
59,
|
||||
59
|
||||
)
|
||||
).toISOString();
|
||||
d14.setDate(d14.getDate() - 14);
|
||||
let date14 = new Date(
|
||||
Date.UTC(d14.getFullYear(), d14.getMonth(), d14.getDate(), 0, 0, 0)
|
||||
).toISOString();
|
||||
|
||||
let Query14 = `'$gte':'${date14}','$lte':'${l14t}'`;
|
||||
setfilter(Query14);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "Last 30 days": {
|
||||
let d30 = new Date();
|
||||
let d30l = new Date();
|
||||
let l30t = new Date(
|
||||
Date.UTC(
|
||||
d30l.getFullYear(),
|
||||
d30l.getMonth(),
|
||||
d30l.getDate(),
|
||||
23,
|
||||
59,
|
||||
59
|
||||
)
|
||||
).toISOString();
|
||||
d30.setDate(d30.getDate() - 30);
|
||||
let date30 = new Date(
|
||||
Date.UTC(d30.getFullYear(), d30.getMonth(), d30.getDate(), 0, 0, 0)
|
||||
).toISOString();
|
||||
let Query30 = `'$gte':'${date30}','$lte':'${l30t}'`;
|
||||
setfilter(Query30);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "Last week": {
|
||||
const from_date = moment().startOf("week").subtract(7, "days");
|
||||
const to_date = moment().endOf("week").subtract(7, "days");
|
||||
let Queryl = `'$gte':'${from_date.toISOString()}','$lte':'${to_date.toISOString()}'`;
|
||||
setfilter(Queryl);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "This month": {
|
||||
const tdate = new Date();
|
||||
let tfirstDay = new Date(
|
||||
tdate.getFullYear(),
|
||||
tdate.getMonth(),
|
||||
1
|
||||
).toISOString();
|
||||
let tlastDay = new Date(
|
||||
tdate.getFullYear(),
|
||||
tdate.getMonth() + 1,
|
||||
0
|
||||
).toISOString();
|
||||
let Querylt = `'$gte':'${tfirstDay}','$lte':'${tlastDay}'`;
|
||||
setfilter(Querylt);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "Last month": {
|
||||
const ldate = new Date();
|
||||
var lfirstDay = new Date(
|
||||
ldate.getFullYear(),
|
||||
ldate.getMonth() - 1,
|
||||
2
|
||||
).toISOString();
|
||||
var llastDay = new Date(
|
||||
ldate.getFullYear(),
|
||||
ldate.getMonth(),
|
||||
1
|
||||
).toISOString();
|
||||
let Queryltl = `'$gte':'${lfirstDay}','$lte':'${llastDay}'`;
|
||||
setfilter(Queryltl);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "All": {
|
||||
let fall = new Date(
|
||||
new Date().getFullYear() - 100,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
).toISOString();
|
||||
let fall_year = `'$gte':'${fall}'`;
|
||||
setfilter(fall_year);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "This year": {
|
||||
let fcy = new Date(
|
||||
new Date().getFullYear(),
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
).toISOString();
|
||||
let lcy = new Date(
|
||||
new Date().getFullYear(),
|
||||
11,
|
||||
31,
|
||||
23,
|
||||
59,
|
||||
59
|
||||
).toISOString();
|
||||
let cur_year = `'$gte':'${fcy}','$lte':'${lcy}'`;
|
||||
setfilter(cur_year);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
case "Last year": {
|
||||
let fly = new Date(
|
||||
new Date().getFullYear() - 1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
).toISOString();
|
||||
let lly = new Date(
|
||||
new Date().getFullYear() - 1,
|
||||
11,
|
||||
31,
|
||||
23,
|
||||
59,
|
||||
59
|
||||
).toISOString();
|
||||
let last_year = `'$gte':'${fly}','$lte':'${lly}'`;
|
||||
setfilter(last_year);
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
setoption(_search);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
const renderSwitchWithTour = (col) => {
|
||||
switch (col.widget.type) {
|
||||
case "Card":
|
||||
return (
|
||||
<div
|
||||
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>please wait</div>}>
|
||||
<DashboardCard
|
||||
Icon={col.widget.icon}
|
||||
Label={col.widget.label}
|
||||
Filter={filter}
|
||||
Format={col.widget.format && col.widget.format}
|
||||
Data={col.widget.data}
|
||||
FilterData={col.widget.filter}
|
||||
DefaultQuery={props.DefaultQuery}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
case "customeHtml":
|
||||
return (
|
||||
<div data-tut={col.widget.data.tourSection}>
|
||||
<select
|
||||
id="searchFiltr"
|
||||
className="form-control"
|
||||
value={
|
||||
option ||
|
||||
localStorage.getItem("DashboardDefaultFilter") ||
|
||||
col.widget.data[0].default
|
||||
}
|
||||
onChange={onSearchFilter}
|
||||
>
|
||||
{col.widget.data[0].options.map((opt) => (
|
||||
<option key={opt} value={opt}>
|
||||
{opt}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<br />
|
||||
</div>
|
||||
);
|
||||
|
||||
case "customeTag":
|
||||
return (
|
||||
<h3
|
||||
data-tut={col.widget.data.tourSection}
|
||||
style={{
|
||||
marginBottom: "4px",
|
||||
color: "currentColor",
|
||||
marginLeft: "4px"
|
||||
}}
|
||||
>
|
||||
{col.widget.label}
|
||||
</h3>
|
||||
);
|
||||
case "report": {
|
||||
let _filter = `${col.widget.filterKey}:${filter}`;
|
||||
return (
|
||||
<div data-tut={col.widget.data.tourSection}>
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
<div className="mb-4 md:mb-0">
|
||||
<DashboardReport Filter={_filter} Record={col.widget} />
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const renderSwitch = (col) => {
|
||||
switch (col.widget.type) {
|
||||
case "Card":
|
||||
return (
|
||||
<div
|
||||
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"}
|
||||
style={{ background: col.widget.bgColor }}
|
||||
>
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
<DashboardCard
|
||||
Icon={col.widget.icon}
|
||||
Label={col.widget.label}
|
||||
Filter={filter}
|
||||
Format={col.widget.format && col.widget.format}
|
||||
Data={col.widget.data}
|
||||
FilterData={col.widget.filter}
|
||||
DefaultQuery={props.DefaultQuery}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
case "customeHtml":
|
||||
return (
|
||||
<div>
|
||||
<select
|
||||
id="searchFiltr"
|
||||
className="form-control"
|
||||
value={
|
||||
option ||
|
||||
localStorage.getItem("DashboardDefaultFilter") ||
|
||||
col.widget.data[0].default
|
||||
}
|
||||
onChange={onSearchFilter}
|
||||
>
|
||||
{col.widget.data[0].options.map((opt) => (
|
||||
<option key={opt} value={opt}>
|
||||
{opt}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<br />
|
||||
</div>
|
||||
);
|
||||
|
||||
case "customeTag":
|
||||
return (
|
||||
<h3
|
||||
style={{
|
||||
marginBottom: "4px",
|
||||
color: "currentColor",
|
||||
marginLeft: "4px"
|
||||
}}
|
||||
>
|
||||
{col.widget.label}
|
||||
</h3>
|
||||
);
|
||||
case "report": {
|
||||
let _filter = `${col.widget.filterKey}:${filter}`;
|
||||
return (
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
<div className="mb-4 md:mb-0">
|
||||
<DashboardReport Filter={_filter} Record={col.widget} />
|
||||
</div>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return (
|
||||
<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>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default GetDashboard;
|
||||
@@ -1,139 +0,0 @@
|
||||
import React, { Suspense, lazy } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const DashboardButton = lazy(() => import("./DashboardButton"));
|
||||
const DashboardCard = lazy(() => import("./DashboardCard"));
|
||||
const DashboardReport = lazy(() => import("./DashboardReport"));
|
||||
const buttonList = [
|
||||
{
|
||||
label: "Sign yourself",
|
||||
redirectId: "sHAnZphf69",
|
||||
redirectType: "Form",
|
||||
icon: "fa-light fa-pen-nib"
|
||||
},
|
||||
{
|
||||
label: "Request signatures",
|
||||
redirectId: "8mZzFxbG1z",
|
||||
redirectType: "Form",
|
||||
icon: "fa-light fa-paper-plane"
|
||||
}
|
||||
];
|
||||
const GetDashboard = (props) => {
|
||||
const { t } = useTranslation();
|
||||
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`}
|
||||
data-tut={col.widget.data.tourSection}
|
||||
>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="h-[150px] w-full flex justify-center items-center">
|
||||
{t("loading")}
|
||||
</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}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
case "report": {
|
||||
return (
|
||||
<div data-tut={col.widget.data.tourSection}>
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
<div className="mb-3 md:mb-0">
|
||||
<DashboardReport Record={col.widget} />
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
const renderSwitch = (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"`}
|
||||
>
|
||||
<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}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
case "report": {
|
||||
return (
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
<div className="mb-3 md:mb-0">
|
||||
<DashboardReport Record={col.widget} />
|
||||
</div>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
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>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
||||
export default GetDashboard;
|
||||
@@ -0,0 +1,265 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { SaveFileSize } from "../../constant/saveFileSize";
|
||||
import Parse from "parse";
|
||||
import sanitizeFileName from "../../primitives/sanitizeFileName";
|
||||
|
||||
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 [localValue, setLocalValue] = useState("");
|
||||
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;
|
||||
setLocalValue(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);
|
||||
}
|
||||
};
|
||||
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>
|
||||
|
||||
<>
|
||||
{localValue ? (
|
||||
<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}
|
||||
/>
|
||||
) : props.formData ? (
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid #ccc",
|
||||
color: "gray",
|
||||
backgroundColor: "white",
|
||||
padding: "5px 10px",
|
||||
borderRadius: "4px",
|
||||
fontSize: "13px",
|
||||
width: "100%",
|
||||
fontWeight: "bold"
|
||||
}}
|
||||
>
|
||||
file selected : {props.formData.split("/")[3]}
|
||||
</div>
|
||||
) : (
|
||||
<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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileUpload;
|
||||
@@ -0,0 +1,83 @@
|
||||
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 });
|
||||
});
|
||||
}
|
||||
}
|
||||
} 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,484 @@
|
||||
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 {
|
||||
var emp = Parse.Object.extend(
|
||||
localStorage.getItem("extended_class")
|
||||
);
|
||||
var q = new Parse.Query(emp);
|
||||
q.equalTo("UserId", currentUser);
|
||||
res = await q.first();
|
||||
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,697 +0,0 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import "../../styles/opensigndrive.css";
|
||||
import axios from "axios";
|
||||
import { ContextMenu } from "radix-ui";
|
||||
import { useNavigate } from "react-router";
|
||||
import Table from "react-bootstrap/Table";
|
||||
import { HoverCard } from "radix-ui";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import FolderModal from "../shared/fields/FolderModal";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { handleDownloadPdf, isMobile } from "../../constant/Utils";
|
||||
import Parse from "parse";
|
||||
|
||||
function DriveBody(props) {
|
||||
const { t } = useTranslation();
|
||||
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({});
|
||||
const contextMenu = [
|
||||
{ type: "Download", icon: "fa-light fa-arrow-down" },
|
||||
{ type: "Rename", icon: "fa-light fa-font" },
|
||||
{ type: "Move", icon: "fa-light fa-file-export" },
|
||||
{ type: "Delete", icon: "fa-light 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: t("loading-mssg")
|
||||
});
|
||||
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/contracts_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: t("something-went-wrong-mssg")
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//function for navigate user to microapp-signature component
|
||||
const checkPdfStatus = async (data) => {
|
||||
const signerExist = data?.Signers;
|
||||
const isDecline = data?.IsDeclined;
|
||||
const isPlaceholder = data?.Placeholders;
|
||||
const signedUrl = data?.SignedUrl;
|
||||
const isSignYourself = data?.IsSignyourself;
|
||||
//checking if document has completed and request signature flow
|
||||
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||
navigate(`/recipientSignPdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if ((!signerExist && !isPlaceholder) || isSignYourself) {
|
||||
navigate(`/signaturePdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
else if (isDecline) {
|
||||
navigate(`/recipientSignPdf/${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 && signedUrl) {
|
||||
navigate(`/recipientSignPdf/${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 = async (selectType, data, deleteType) => {
|
||||
switch (selectType) {
|
||||
case "Download": {
|
||||
await handleDownloadPdf([data]);
|
||||
break;
|
||||
}
|
||||
case "Rename": {
|
||||
setRenameValue(data.Name);
|
||||
setRename(data.objectId);
|
||||
break;
|
||||
}
|
||||
case "Delete": {
|
||||
setIsDeleteDoc({ status: true, deleteType });
|
||||
setSelectDoc(data);
|
||||
break;
|
||||
}
|
||||
case "Move": {
|
||||
handleMoveDocument(data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
null;
|
||||
}
|
||||
};
|
||||
//function for delete document
|
||||
const handleDeleteDocument = async (docData) => {
|
||||
setIsDeleteDoc({});
|
||||
const docId = docData.objectId;
|
||||
const data = {
|
||||
IsArchive: true
|
||||
};
|
||||
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/contracts_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: t("something-went-wrong-mssg")
|
||||
});
|
||||
});
|
||||
};
|
||||
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: "contracts_Document",
|
||||
objectId: moveFolderId
|
||||
}
|
||||
};
|
||||
} else {
|
||||
updateData = {
|
||||
Folder: { __op: "Delete" }
|
||||
};
|
||||
}
|
||||
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_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(t("folder-already-exist!"));
|
||||
setIsOpenMoveModal(false);
|
||||
}
|
||||
};
|
||||
const handleEnterPress = (e, data) => {
|
||||
if (e.key === "Enter") {
|
||||
handledRenameDoc(data);
|
||||
}
|
||||
};
|
||||
|
||||
const checkFolderEmpty = async (docData) => {
|
||||
let isEmptyFolder = true;
|
||||
const query = new Parse.Query("contracts_Document");
|
||||
query.equalTo("Folder", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Document",
|
||||
objectId: docData.objectId
|
||||
});
|
||||
query.notEqualTo("IsArchive", true);
|
||||
const res = await query.find();
|
||||
const jsonRes = JSON.parse(JSON.stringify(res));
|
||||
if (jsonRes && jsonRes.length > 0) {
|
||||
isEmptyFolder = false;
|
||||
return isEmptyFolder;
|
||||
} else {
|
||||
return isEmptyFolder;
|
||||
}
|
||||
};
|
||||
const handleDeleteFolder = async (docData) => {
|
||||
setIsDeleteDoc({});
|
||||
const isEmptyFolder = await checkFolderEmpty(docData);
|
||||
if (isEmptyFolder) {
|
||||
const docId = docData?.objectId;
|
||||
try {
|
||||
const updateQuery = new Parse.Query("contracts_Document");
|
||||
const updateObj = await updateQuery.get(docId);
|
||||
updateObj.set("IsArchive", true);
|
||||
const res = await updateObj.save();
|
||||
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: t("something-went-wrong-mssg")
|
||||
});
|
||||
}
|
||||
} else {
|
||||
alert(t("delete-folder-alert-1"));
|
||||
}
|
||||
};
|
||||
|
||||
//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?.length > 0 && signerExist?.map((data) => data?.Name || "");
|
||||
const signerName =
|
||||
getSignersName?.length > 0 ? getSignersName?.join(", ") : "";
|
||||
|
||||
return (
|
||||
<span className="text-[12px] font-medium w-[90%] break-words">
|
||||
{signerName && signerName}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
return listType === "table" ? (
|
||||
data.Type === "Folder" ? (
|
||||
<tr onClick={() => handleOnclikFolder(data)}>
|
||||
<td className="cursor-pointer flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[26px] h-[26px] fill-current op-text-secondary"
|
||||
>
|
||||
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
||||
</svg>
|
||||
<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 gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 384 512"
|
||||
className="w-[26px] h-[26px] fill-current op-text-primary"
|
||||
>
|
||||
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
|
||||
</svg>
|
||||
<span className="text-[12px] font-medium">{data.Name}</span>
|
||||
</td>
|
||||
<td>{createddate}</td>
|
||||
<td>Pdf</td>
|
||||
<td>{t(`drive-document-status.${status}`)}</td>
|
||||
<td>
|
||||
<i
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleMenuItemClick("Download", data);
|
||||
}}
|
||||
className="fa-light fa-download mr-[8px] op-text-primary cursor-pointer"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
) : listType === "list" && data.Type === "Folder" ? (
|
||||
<div key={ind} className="relative w-[100px] h-[100px] mx-2 my-3">
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger className="flex flex-col justify-center items-center select-none-cls">
|
||||
{/* folder */}
|
||||
<div
|
||||
data-tut={props.dataTutSeventh}
|
||||
onClick={() => {
|
||||
if (!rename) {
|
||||
handleOnclikFolder(data);
|
||||
}
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[100px] h-[100px] fill-current op-text-secondary"
|
||||
>
|
||||
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
||||
</svg>
|
||||
{rename === data.objectId ? (
|
||||
<input
|
||||
onFocus={() => {
|
||||
const input = inputRef.current;
|
||||
if (input) {
|
||||
input.select();
|
||||
}
|
||||
}}
|
||||
autoFocus={true}
|
||||
type="text"
|
||||
onBlur={() => handledRenameDoc(data)}
|
||||
onKeyDown={(e) => handleEnterPress(e, data)}
|
||||
ref={inputRef}
|
||||
defaultValue={renameValue}
|
||||
onChange={(e) => setRenameValue(e.target.value)}
|
||||
className="op-input op-input-bordered op-input-xs w-[100px] focus:outline-none hover:border-base-content text-[10px]"
|
||||
/>
|
||||
) : (
|
||||
<span className="fileName select-none-cls">{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-light fa-font mr-[8px]"></i>
|
||||
<span>Rename</span>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item
|
||||
onClick={() => handleMenuItemClick("Delete", data, data.Type)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
<i className="fa-light fa-trash mr-[8px]"></i>
|
||||
<span>Delete</span>
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Portal>
|
||||
</ContextMenu.Root>
|
||||
</div>
|
||||
) : (
|
||||
<HoverCard.Root
|
||||
open={rename || isMobile ? false : undefined}
|
||||
openDelay={0}
|
||||
closeDelay={100}
|
||||
>
|
||||
<HoverCard.Trigger asChild>
|
||||
<div>
|
||||
<ContextMenu.Root>
|
||||
<div className="relative w-[100px] h-[100px] mx-2 my-3">
|
||||
<ContextMenu.Trigger
|
||||
asChild
|
||||
className="flex flex-col justify-center items-center select-none-cls"
|
||||
>
|
||||
{/* pdf */}
|
||||
<div
|
||||
data-tut={props.dataTutSixth}
|
||||
onClick={() => {
|
||||
if (!rename) {
|
||||
checkPdfStatus(data);
|
||||
}
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 384 512"
|
||||
className="w-[100px] h-[100px] fill-current op-text-primary"
|
||||
>
|
||||
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
|
||||
</svg>
|
||||
{rename === data.objectId ? (
|
||||
<input
|
||||
autoFocus={true}
|
||||
type="text"
|
||||
onFocus={() => {
|
||||
const input = inputRef.current;
|
||||
if (input) {
|
||||
input.select();
|
||||
}
|
||||
}}
|
||||
onBlur={() => handledRenameDoc(data)}
|
||||
onKeyDown={(e) => handleEnterPress(e, data, data.Type)}
|
||||
ref={inputRef}
|
||||
defaultValue={renameValue}
|
||||
onChange={(e) => setRenameValue(e.target.value)}
|
||||
className="op-input op-input-bordered op-input-xs w-[100px] focus:outline-none hover:border-base-content text-[10px]"
|
||||
/>
|
||||
) : (
|
||||
<span className="fileName select-none-cls">
|
||||
{data.Name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
{status === "Completed" ? (
|
||||
<div className="status-badge completed">
|
||||
<i className="fa-light fa-check-circle"></i>
|
||||
</div>
|
||||
) : status === "Declined" ? (
|
||||
<div className="status-badge declined">
|
||||
<i className="fa-light fa-thumbs-down"></i>
|
||||
</div>
|
||||
) : status === "Expired" ? (
|
||||
<div className="status-badge expired">
|
||||
<i className="fa-light fa-hourglass-end"></i>
|
||||
</div>
|
||||
) : status === "Draft" ? (
|
||||
<div className="status-badge draft">
|
||||
<i className="fa-light fa-file"></i>
|
||||
</div>
|
||||
) : (
|
||||
status === "In Progress" && (
|
||||
<div className="status-badge in-progress">
|
||||
<i className="fa-light 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]">
|
||||
{t(`context-menu.${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]">
|
||||
{t("report-heading.Title")}:{" "}
|
||||
</strong>
|
||||
<span className="text-[12px] font-medium mb-0"> {data.Name}</span>
|
||||
<br />
|
||||
<strong className="text-[13px]">
|
||||
{t("report-heading.Status")}:{" "}
|
||||
</strong>
|
||||
<span className="text-[12px] font-medium">
|
||||
{t(`drive-document-status.${status}`)}
|
||||
</span>
|
||||
<br />
|
||||
<strong className="text-[13px]">
|
||||
{t("report-heading.created-date")}:{" "}
|
||||
</strong>
|
||||
<span className="text-[12px] font-medium">{createddate}</span>
|
||||
<br />
|
||||
{signerExist && (
|
||||
<>
|
||||
<strong className="text-[13px]">
|
||||
{t("report-heading.Signers")}:{" "}
|
||||
</strong>
|
||||
{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>{t("report-heading.Name")}</th>
|
||||
<th>{t("report-heading.created-date")}</th>
|
||||
<th>{t("report-heading.Type")}</th>
|
||||
<th>{t("report-heading.Status")}</th>
|
||||
<th>{t("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.status}
|
||||
title={t("delete-document")}
|
||||
handleClose={() => setIsDeleteDoc({})}
|
||||
>
|
||||
<div className="h-full p-[20px] text-base-content">
|
||||
{isDeleteDoc.deleteType ? (
|
||||
<p>{t("delete-folder-alert")}</p>
|
||||
) : (
|
||||
<p>{t("delete-document-alert")}</p>
|
||||
)}
|
||||
|
||||
<div className="h-[1px] w-full bg-[#9f9f9f] my-[15px]"></div>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (isDeleteDoc.deleteType) {
|
||||
handleDeleteFolder(selectDoc);
|
||||
} else {
|
||||
handleDeleteDocument(selectDoc);
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary mr-2"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsDeleteDoc({})}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default DriveBody;
|
||||
@@ -1,47 +0,0 @@
|
||||
import React from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const AddRoleModal = (props) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<ModalUi
|
||||
title={t("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
|
||||
? "Role " + (props.signersdata.length + 1)
|
||||
: "Role 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]">
|
||||
{t("role-ex")}..
|
||||
</p>
|
||||
<div>
|
||||
<div className="h-[1px] w-full bg-[#9f9f9f] mb-[10px]"></div>
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("add")}
|
||||
</button>
|
||||
<button
|
||||
onClick={props.handleCloseRoleModal}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-2"
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddRoleModal;
|
||||
@@ -1,134 +0,0 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
|
||||
function AgreementContent(props) {
|
||||
const { t } = useTranslation();
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const h2Style = "text-base-content font-medium text-lg";
|
||||
const ulStyle = "list-disc px-4 py-3";
|
||||
const handleOnclick = () => {
|
||||
props.setIsAgreeTour(false);
|
||||
props.setIsAgree(true);
|
||||
props.setIsShowAgreeTerms(false);
|
||||
props.showFirstWidget();
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<ModalUi
|
||||
isOpen={true}
|
||||
title={t("term-cond-title")}
|
||||
handleClose={() => props.setIsShowAgreeTerms(false)}
|
||||
>
|
||||
<div className="h-[100%] p-[20px]">
|
||||
<h2 className={h2Style}>{t("term-cond-h")}</h2>
|
||||
<span className="mt-2">
|
||||
{t("term-cond-p1", { appName: appName })}
|
||||
</span>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<h2 className={h2Style}>{t("term-cond-h1")}</h2>
|
||||
<span className="mt-2">
|
||||
{t("term-cond-p2", { appName: appName })}
|
||||
</span>
|
||||
<ul className={ulStyle}>
|
||||
<li>{t("term-cond-p3", { appName: appName })}</li>
|
||||
<li>{t("term-cond-p4")}</li>
|
||||
</ul>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<h2 className={h2Style}>{t("term-cond-h2")}</h2>
|
||||
<span className="mt-2">{t("term-cond-p5")}</span>
|
||||
<ul className={ulStyle}>
|
||||
<li>{t("term-cond-p6", { appName: appName })}</li>
|
||||
<li>{t("term-cond-p7", { appName: appName })}</li>
|
||||
<li>{t("term-cond-p8")}</li>
|
||||
</ul>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<h2 className={h2Style}>{t("term-cond-h3")}</h2>
|
||||
<span className="mt-2">{t("term-cond-p9")}</span>
|
||||
<ul className={ulStyle}>
|
||||
<li>{t("term-cond-p10")}</li>
|
||||
<li>{t("term-cond-p11")}</li>
|
||||
</ul>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<h2 className={h2Style}>{t("term-cond-h4")}</h2>
|
||||
<span className="mt-2">
|
||||
{t("term-cond-p12", { appName: appName })}
|
||||
</span>
|
||||
<ul className={ulStyle}>
|
||||
<li>{t("term-cond-p13")}</li>
|
||||
<li>{t("term-cond-p14")}</li>
|
||||
<li>{t("term-cond-p15")}</li>
|
||||
</ul>
|
||||
<span>{t("term-cond-p16", { appName: appName })}</span>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<h2 className={h2Style}>{t("term-cond-h5")}</h2>
|
||||
<p>{t("term-cond-p17", { appName: appName })}</p>
|
||||
<p className="mt-2">{t("term-cond-p18")}</p>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<h2 className={h2Style}>{t("term-cond-h6")}</h2>
|
||||
<span className="mt-2">{t("term-cond-p19")}</span>
|
||||
<ul className={ulStyle}>
|
||||
<li>{t("term-cond-p20")}</li>
|
||||
<li>{t("term-cond-p21")}</li>
|
||||
<li>{t("term-cond-p22", { appName: appName })}</li>
|
||||
</ul>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<h2 className={h2Style}>{t("term-cond-h7")}</h2>
|
||||
<span className="mt-2">
|
||||
{t("term-cond-p23", { appName: appName })}
|
||||
</span>
|
||||
<ul className={ulStyle}>
|
||||
<li>{t("term-cond-p24")}</li>
|
||||
<li>{t("term-cond-p25")}</li>
|
||||
</ul>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<h2 className={h2Style}>{t("term-cond-h8")}</h2>
|
||||
<span className="mt-2">
|
||||
{t("term-cond-p26", { appName: appName })}
|
||||
</span>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<h2 className={h2Style}>{t("term-cond-h9")}</h2>
|
||||
<span className="mt-2">
|
||||
{t("term-cond-p27", { appName: appName })}
|
||||
</span>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<span className="mt-2 font-medium">
|
||||
{t("term-cond-p28", { appName: appName })}
|
||||
</span>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<span className="mt-2">
|
||||
{t("term-cond-p29", { appName: appName })}
|
||||
</span>
|
||||
<a
|
||||
href="www.opensignlabs.com"
|
||||
target="_blank"
|
||||
className="text-blue-700 cursor-pointer"
|
||||
>
|
||||
www.opensignlabs.com
|
||||
</a>
|
||||
|
||||
<span>{t("term-cond-p30")}</span>
|
||||
<span className="font-medium"> support@opensignlabs.com </span>
|
||||
<hr className="bg-[#9f9f9f] w-full my-[15px]" />
|
||||
<div className="mt-6 flex justify-start gap-2">
|
||||
<button
|
||||
onClick={() => handleOnclick()}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("agrre-button")}
|
||||
</button>
|
||||
<button
|
||||
className="op-btn op-btn-ghost"
|
||||
onClick={() => props.setIsShowAgreeTerms(false)}
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AgreementContent;
|
||||
@@ -1,74 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import AgreementContent from "./AgreementContent";
|
||||
|
||||
function AgreementSign(props) {
|
||||
const { t } = useTranslation();
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
const [isShowAgreeTerms, setIsShowAgreeTerms] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="op-modal op-modal-open absolute z-[448]">
|
||||
<div className="w-[95%] md:w-[60%] lg:w-[40%] op-modal-box overflow-y-auto hide-scrollbar text-sm p-4">
|
||||
<div className="flex flex-row items-center">
|
||||
<input
|
||||
data-tut="IsAgree"
|
||||
className="mr-3 op-checkbox op-checkbox-m"
|
||||
type="checkbox"
|
||||
value={isChecked}
|
||||
onChange={(e) => {
|
||||
setIsChecked(e.target.checked);
|
||||
if (e.target.checked) {
|
||||
props.setIsAgreeTour(false);
|
||||
}
|
||||
props.showFirstWidget();
|
||||
}}
|
||||
/>
|
||||
<div className="text-[11px] md:text-base">
|
||||
<span>{t("agree-p1")}</span>
|
||||
<span
|
||||
className="font-bold text-blue-600 cursor-pointer"
|
||||
onClick={() => {
|
||||
setIsShowAgreeTerms(true);
|
||||
props.setIsAgreeTour(false);
|
||||
}}
|
||||
>
|
||||
{t("agree-p2")}
|
||||
</span>
|
||||
<span> {t("agree-p3")}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex ml-[35px] mt-3">
|
||||
<button
|
||||
onClick={() => {
|
||||
if (isChecked) {
|
||||
props.setIsAgreeTour(false);
|
||||
props.setIsAgree(true);
|
||||
} else {
|
||||
props.setIsAgreeTour(true);
|
||||
}
|
||||
}}
|
||||
className="op-btn op-btn-primary op-btn-sm w-full md:w-auto"
|
||||
>
|
||||
{t("agrre-button")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<span className="text-[11px]">{t("agreement-note")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isShowAgreeTerms && (
|
||||
<AgreementContent
|
||||
setIsAgreeTour={props.setIsAgreeTour}
|
||||
setIsAgree={props.setIsAgree}
|
||||
setIsShowAgreeTerms={setIsShowAgreeTerms}
|
||||
showFirstWidget={props.showFirstWidget}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default AgreementSign;
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
function BorderResize(props) {
|
||||
const getHeight = () => {
|
||||
const height = props.posHeight(props.pos, props.isSignYourself);
|
||||
if (height > 14) {
|
||||
return "14px";
|
||||
} else {
|
||||
return `${height}px`;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: getHeight() || "14px",
|
||||
height: getHeight() || "14px"
|
||||
}}
|
||||
className={`${props.right ? `-right-[12px]` : "-right-[2px]"} ${
|
||||
props.top ? `-bottom-[12px]` : "-bottom-[2px] "
|
||||
} absolute inline-block hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BorderResize;
|
||||
@@ -1,122 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
function DefaultSignature(props) {
|
||||
const { t } = useTranslation();
|
||||
const defaultSignImg = useSelector((state) => state.widget.defaultSignImg);
|
||||
const myInitial = useSelector((state) => state.widget.myInitial)
|
||||
const tabName = ["my-signature", "my-initials"];
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
const confirmToaddDefaultSign = (type) => {
|
||||
if (!props.isAgree) {
|
||||
props.setIsAgreeTour(true);
|
||||
} else {
|
||||
if (props?.xyPosition.length > 0) {
|
||||
//check signature or initial widgets exist or not for auto signing
|
||||
const getCurrentSignerXY = props?.xyPosition.filter(
|
||||
(data) => data.Id === props.uniqueId
|
||||
);
|
||||
const checkIsSignInitialExist = getCurrentSignerXY?.every(
|
||||
(placeholderObj) =>
|
||||
placeholderObj?.placeHolder?.some((placeholder) =>
|
||||
placeholder?.pos?.some((posItem) => posItem?.type === type)
|
||||
)
|
||||
);
|
||||
if (checkIsSignInitialExist) {
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("default-sign-alert", { widgetsType: type }),
|
||||
type: type
|
||||
});
|
||||
} else {
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("defaultSign-alert", { widgetsType: type })
|
||||
});
|
||||
}
|
||||
} else {
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("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">
|
||||
<p className="text-base-content">{t("signature")}</p>
|
||||
</div>
|
||||
<div className="flex justify-center items-center mt-2">
|
||||
<div role="tablist" className="op-tabs op-tabs-bordered">
|
||||
{tabName.map((tabData, ind) => (
|
||||
<div
|
||||
onClick={() => setActiveTab(ind)}
|
||||
key={ind}
|
||||
role="tab"
|
||||
className={`${
|
||||
activeTab === ind ? "op-tab-active" : ""
|
||||
} op-tab flex items-center pb-10 md:pb-0`}
|
||||
>
|
||||
<span className="ml-1 text-[7px] font-medium md:font-normal md:text-[12px]">
|
||||
{t(`${tabData}`)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center mt-[10px] font-semibold relative">
|
||||
<div className="op-card shadow-md h-[111px] w-[90%] p-2">
|
||||
{activeTab === 0 ? (
|
||||
<img
|
||||
alt="signature"
|
||||
className="w-full h-full object-contain"
|
||||
src={defaultSignImg}
|
||||
/>
|
||||
) : (
|
||||
activeTab === 1 &&
|
||||
(myInitial ? (
|
||||
<img
|
||||
alt="signature"
|
||||
className="w-full h-full object-contain"
|
||||
src={myInitial}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<span>{t("initial-alert")}</span>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mt-[10px]"
|
||||
onClick={() =>
|
||||
confirmToaddDefaultSign(
|
||||
activeTab === 0 ? "signature" : activeTab === 1 && "initials"
|
||||
)
|
||||
}
|
||||
disabled={
|
||||
activeTab === 0 && !props?.isDefault
|
||||
? true
|
||||
: activeTab === 1 && !myInitial
|
||||
? true
|
||||
: false
|
||||
}
|
||||
>
|
||||
{t("auto-sign-all")}
|
||||
</button>
|
||||
{!props.isDefault && (
|
||||
<div className="absolute bg-black/70 text-white w-full h-full flex items-center justify-center text-[11px] cursor-default">
|
||||
<span className="-rotate-45">
|
||||
Document creator has disabled this option
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DefaultSignature;
|
||||
@@ -1,99 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import LoaderWithMsg from "../../primitives/LoaderWithMsg";
|
||||
import { contractDocument } from "../../constant/Utils";
|
||||
import HandleError from "../../primitives/HandleError";
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
import { useTranslation } from "react-i18next";
|
||||
function useQuery() {
|
||||
return new URLSearchParams(useLocation().search);
|
||||
}
|
||||
function DraftDocument() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const query = useQuery();
|
||||
const docId = query.get("docId");
|
||||
const [isLoading, setIsLoading] = useState({
|
||||
isLoader: true,
|
||||
message: t("loading-mssg")
|
||||
});
|
||||
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)
|
||||
) {
|
||||
if (documentData?.result?.error?.includes("deleted")) {
|
||||
setIsLoading({
|
||||
isLoader: false,
|
||||
message: t("document-deleted")
|
||||
});
|
||||
} else {
|
||||
setIsLoading({
|
||||
isLoader: false,
|
||||
message: t("something-went-wrong-mssg")
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setIsLoading({ isLoader: false, message: t("no-data") });
|
||||
}
|
||||
};
|
||||
|
||||
//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(`/recipientSignPdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if ((!signerExist && !isPlaceholder) || data?.IsSignyourself) {
|
||||
navigate(`/signaturePdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
else if (isDecline) {
|
||||
navigate(`/recipientSignPdf/${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 && signedUrl) {
|
||||
navigate(`/recipientSignPdf/${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,373 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { radioButtonWidget } from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
|
||||
|
||||
function DropdownWidgetOption(props) {
|
||||
const { t } = useTranslation();
|
||||
const [dropdownOptionList, setDropdownOptionList] = useState([
|
||||
"option-1",
|
||||
"option-2"
|
||||
]);
|
||||
const [minCount, setMinCount] = useState(0);
|
||||
const [maxCount, setMaxCount] = useState(0);
|
||||
const [dropdownName, setDropdownName] = useState();
|
||||
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.currWidgetsDetails?.options?.name || props.type);
|
||||
setIsReadOnly(false);
|
||||
setIsHideLabel(false);
|
||||
setMinCount(0);
|
||||
setMaxCount(0);
|
||||
setDefaultCheckbox([]);
|
||||
setDefaultValue("");
|
||||
};
|
||||
useEffect(() => {
|
||||
if (
|
||||
props.currWidgetsDetails?.options?.name &&
|
||||
props.currWidgetsDetails?.options?.values?.length > 0
|
||||
) {
|
||||
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
|
||||
);
|
||||
resetState();
|
||||
};
|
||||
|
||||
|
||||
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">
|
||||
{t("name")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
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]">
|
||||
{t("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.isShowAdvanceFeature && (
|
||||
<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
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
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-light fa-rectangle-xmark text-[25px] ml-[10px] text-accent cursor-pointer"
|
||||
onClick={() => handleDeleteInput(index)}
|
||||
></i>
|
||||
</div>
|
||||
))}
|
||||
<i
|
||||
onClick={handleAddInput}
|
||||
className="fa-light fa-square-plus text-[25px] ml-[10px] op-text-primary cursor-pointer"
|
||||
></i>
|
||||
</div>
|
||||
{["dropdown", radioButtonWidget].includes(props.type) && (
|
||||
<>
|
||||
<label className="text-[13px] font-semibold mt-[5px]">
|
||||
{t("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]">
|
||||
{t("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>
|
||||
</>
|
||||
)}
|
||||
<div className="flex items-center mt-3 mb-3">
|
||||
<span>{t("font-size")} :</span>
|
||||
<select
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
value={
|
||||
props.fontSize ||
|
||||
props.currWidgetsDetails?.options?.fontSize ||
|
||||
12
|
||||
}
|
||||
onChange={(e) => props.setFontSize(parseInt(e.target.value))}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
<option className="text-[13px]" value={size} key={ind}>
|
||||
{size}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<div className="flex flex-row gap-1 items-center ml-4 ">
|
||||
<span>{t("color")} : </span>
|
||||
<select
|
||||
value={
|
||||
props.fontColor ||
|
||||
props.currWidgetsDetails?.options?.fontColor ||
|
||||
"black"
|
||||
}
|
||||
onChange={(e) => props.setFontColor(e.target.value)}
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
>
|
||||
{fontColorArr.map((color, ind) => {
|
||||
return (
|
||||
<option value={color} key={ind}>
|
||||
{t(`color-type.${color}`)}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<span
|
||||
style={{
|
||||
background:
|
||||
props.fontColor ||
|
||||
props.currWidgetsDetails?.options?.fontColor ||
|
||||
"black"
|
||||
}}
|
||||
className="w-5 h-[19px] ml-1"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
{["checkbox", radioButtonWidget, "dropdown"].includes(
|
||||
props.type
|
||||
) && (
|
||||
<div className="flex flex-row gap-5 my-2 items-center text-center">
|
||||
{props.isShowAdvanceFeature && (
|
||||
<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">
|
||||
{t("read-only")}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{props.type !== "dropdown" && (
|
||||
<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">
|
||||
{t("hide-labels")}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`${
|
||||
props.type === "checkbox" && props.isShowAdvanceFeature
|
||||
? "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"
|
||||
>
|
||||
{t("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();
|
||||
}}
|
||||
>
|
||||
{t("cancel")}
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default DropdownWidgetOption;
|
||||
@@ -1,538 +0,0 @@
|
||||
import {
|
||||
useState,
|
||||
useRef,
|
||||
} from "react";
|
||||
import {
|
||||
base64ToArrayBuffer,
|
||||
convertBase64ToFile,
|
||||
generatePdfName,
|
||||
getFileName
|
||||
} from "../../constant/Utils";
|
||||
import {
|
||||
maxDescriptionLength,
|
||||
maxNoteLength,
|
||||
maxTitleLength
|
||||
} from "../../constant/const";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import SignersInput from "../shared/fields/SignersInput";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { SaveFileSize } from "../../constant/saveFileSize";
|
||||
|
||||
const EditTemplate = ({
|
||||
title,
|
||||
handleClose,
|
||||
pdfbase64,
|
||||
template,
|
||||
onSuccess,
|
||||
setPdfArrayBuffer,
|
||||
setPdfBase64Url,
|
||||
}) => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
const inputFileRef = useRef(null);
|
||||
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,
|
||||
IsEnableOTP: template?.IsEnableOTP ? `${template?.IsEnableOTP}` : "false",
|
||||
IsTourEnabled: template?.IsTourEnabled
|
||||
? `${template?.IsTourEnabled}`
|
||||
: "false",
|
||||
NotifyOnSignatures:
|
||||
template?.NotifyOnSignatures !== undefined
|
||||
? template?.NotifyOnSignatures
|
||||
: false,
|
||||
Bcc: template?.Bcc,
|
||||
RedirectUrl: template?.RedirectUrl || "",
|
||||
AllowModifications: template?.AllowModifications || false,
|
||||
TimeToCompleteDays: template?.TimeToCompleteDays || 15
|
||||
});
|
||||
const [isUpdate, setIsUpdate] = useState(false);
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
const [uploadPdf, setUploadPdf] = useState({
|
||||
name: "",
|
||||
base64: "",
|
||||
url: ""
|
||||
});
|
||||
const handleDrop = (e) => {
|
||||
e.preventDefault();
|
||||
const file = e.dataTransfer.files[0];
|
||||
handleFile(file);
|
||||
};
|
||||
|
||||
const handleFile = (file) => {
|
||||
if (file && file.type === "application/pdf") {
|
||||
handleReplaceFileValdition(file);
|
||||
// You can handle the file here
|
||||
} else {
|
||||
alert("Only pdf files are allowed.");
|
||||
if (inputFileRef.current) inputFileRef.current.value = "";
|
||||
}
|
||||
};
|
||||
const handleDragOver = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// `isValidURL` is used to check valid webhook url
|
||||
function isValidURL(value) {
|
||||
try {
|
||||
const url = new URL(value);
|
||||
return url.protocol === "https:" || url.protocol === "http:";
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const handleStrInput = (e) => {
|
||||
setIsUpdate(true);
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
};
|
||||
const getPdfMetadataHash = async (pdfBytes) => {
|
||||
const pdfDoc = await PDFDocument.load(pdfBytes);
|
||||
const pages = pdfDoc.getPages();
|
||||
const metaString = pages
|
||||
.map((page, index) => {
|
||||
const { width, height } = page.getSize();
|
||||
return `${index + 1}:${Math.round(width)}x${Math.round(height)}`;
|
||||
})
|
||||
.join("|");
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(metaString);
|
||||
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
||||
return Array.from(new Uint8Array(hashBuffer))
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
};
|
||||
|
||||
const handleFileInput = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
handleReplaceFileValdition(file);
|
||||
};
|
||||
const handleReplaceFileValdition = async (file) => {
|
||||
try {
|
||||
const basePdfBytes = base64ToArrayBuffer(pdfbase64);
|
||||
const expectedHash = await getPdfMetadataHash(basePdfBytes);
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = async (event) => {
|
||||
const uploadedPdfBytes = event.target.result;
|
||||
const uploadedHash = await getPdfMetadataHash(uploadedPdfBytes);
|
||||
|
||||
if (expectedHash === uploadedHash) {
|
||||
const arrayBuffer = uploadedPdfBytes;
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
const binaryString = Array.from(uint8Array)
|
||||
.map((b) => String.fromCharCode(b))
|
||||
.join("");
|
||||
const base64 = btoa(binaryString);
|
||||
const pdfName = generatePdfName(16);
|
||||
setIsUpdate(true);
|
||||
setUploadPdf((prev) => ({ ...prev, name: pdfName, base64: base64 }));
|
||||
// alert("✅ PDFs match (based on page number, width, height)");
|
||||
} else {
|
||||
alert("❌ PDF do NOT match based on page number, width, height");
|
||||
if (inputFileRef.current) inputFileRef.current.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
} catch (err) {
|
||||
alert("Error: " + err.message);
|
||||
if (inputFileRef.current) inputFileRef.current.value = "";
|
||||
}
|
||||
};
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (formData.RedirectUrl && !isValidURL(formData?.RedirectUrl)) {
|
||||
alert(t("invalid-redirect-url"));
|
||||
return;
|
||||
}
|
||||
if (formData?.Name?.length > maxTitleLength) {
|
||||
alert(t("title-length-alert"));
|
||||
return;
|
||||
}
|
||||
if (formData?.Note?.length > maxNoteLength) {
|
||||
alert(t("note-length-alert"));
|
||||
return;
|
||||
}
|
||||
if (formData?.Description?.length > maxDescriptionLength) {
|
||||
alert(t("description-length-alert"));
|
||||
return;
|
||||
}
|
||||
let pdfUrl;
|
||||
if (uploadPdf?.base64) {
|
||||
pdfUrl = await convertBase64ToFile(
|
||||
uploadPdf.name,
|
||||
uploadPdf.base64,
|
||||
);
|
||||
setUploadPdf((prev) => ({ ...prev, url: pdfUrl }));
|
||||
const pdfBuffer = base64ToArrayBuffer(uploadPdf.base64);
|
||||
setPdfArrayBuffer && setPdfArrayBuffer(pdfBuffer);
|
||||
setPdfBase64Url && setPdfBase64Url(uploadPdf.base64);
|
||||
const tenantId =
|
||||
localStorage.getItem("TenantId") ||
|
||||
template?.ExtUserPtr?.TenantId?.objectId;
|
||||
const buffer = atob(uploadPdf.base64);
|
||||
SaveFileSize(buffer.length, pdfUrl, tenantId);
|
||||
}
|
||||
const isChecked = formData.SendinOrder === "true" ? true : false;
|
||||
const isTourEnabled = formData?.IsTourEnabled === "false" ? false : true;
|
||||
const AutoReminder = formData?.AutomaticReminders || false;
|
||||
const IsEnableOTP = formData.IsEnableOTP === "true" ? true : false;
|
||||
const allowModify = formData?.AllowModifications || false;
|
||||
let reminderDate = {};
|
||||
const remindOnceInEvery = formData?.RemindOnceInEvery;
|
||||
const TimeToCompleteDays = parseInt(formData?.TimeToCompleteDays);
|
||||
const reminderCount = TimeToCompleteDays / remindOnceInEvery;
|
||||
if (AutoReminder && reminderCount > 15) {
|
||||
alert(t("only-15-reminder-allowed"));
|
||||
return;
|
||||
}
|
||||
if (AutoReminder) {
|
||||
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
|
||||
const ReminderDate = new Date(template?.createdAt);
|
||||
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||
reminderDate = { NextReminderDate: ReminderDate };
|
||||
}
|
||||
const data = {
|
||||
...formData,
|
||||
...(pdfUrl ? { URL: pdfUrl } : {}),
|
||||
SendinOrder: isChecked,
|
||||
IsEnableOTP: IsEnableOTP,
|
||||
IsTourEnabled: isTourEnabled,
|
||||
AllowModifications: allowModify,
|
||||
...reminderDate
|
||||
};
|
||||
onSuccess(data);
|
||||
};
|
||||
|
||||
// `handleNotifySignChange` is trigger when user change radio of notify on signatures
|
||||
const handleNotifySignChange = (value) => {
|
||||
setIsUpdate(true);
|
||||
setFormData((obj) => ({ ...obj, NotifyOnSignatures: value }));
|
||||
};
|
||||
const handleBcc = (data) => {
|
||||
if (data && data.length > 0) {
|
||||
const trimEmail = data.map((item) => ({
|
||||
objectId: item?.value,
|
||||
Name: item?.label,
|
||||
Email: item?.email
|
||||
}));
|
||||
setIsUpdate(true);
|
||||
setFormData((prev) => ({ ...prev, Bcc: trimEmail }));
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditTemplateClose = () => {
|
||||
if (isUpdate) {
|
||||
setShowConfirm(true);
|
||||
} else {
|
||||
handleClose();
|
||||
}
|
||||
};
|
||||
const discardChanges = () => {
|
||||
setShowConfirm(false);
|
||||
handleClose();
|
||||
};
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={isUpdate ? `${title} (unsaved)` : title}
|
||||
handleClose={handleEditTemplateClose}
|
||||
>
|
||||
<ModalUi isOpen={showConfirm} showClose={false}>
|
||||
<div className="p-[20px]">
|
||||
<p className="text-base font-normal text-base-content py-[5px] md:py-[6px] px-[5px]">
|
||||
{t("unsaved-changes-discard-them?")}
|
||||
</p>
|
||||
<div className="flex items-center mt-2.5 gap-2 md:gap-3 text-white">
|
||||
<button
|
||||
className="op-btn op-btn-primary px-6"
|
||||
onClick={discardChanges}
|
||||
>
|
||||
{t("yes-discard")}
|
||||
</button>
|
||||
<button
|
||||
className="op-btn op-btn-secondary px-4 md:px-6"
|
||||
onClick={() => setShowConfirm(false)}
|
||||
>
|
||||
{t("cancel")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
|
||||
<div className="text-base-content">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-[0.35rem]">
|
||||
<label htmlFor="name" className="text-[13px]">
|
||||
{t("report-heading.File")}
|
||||
</label>
|
||||
<div
|
||||
className="border-[1.5px] border-dashed border-gray-300 rounded-lg px-4 py-6 text-center text-gray-500 bg-white cursor-pointer hover:border-base-content transition"
|
||||
onDrop={handleDrop}
|
||||
onDragOver={handleDragOver}
|
||||
onClick={() => inputFileRef?.current?.click()}
|
||||
>
|
||||
<label
|
||||
htmlFor="fileUpload"
|
||||
className="cursor-pointer text-center mb-0"
|
||||
>
|
||||
Browse or drag & drop a new file to replace the existng one
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
ref={inputFileRef}
|
||||
type="file"
|
||||
className="hidden"
|
||||
accept="application/pdf"
|
||||
onChange={(e) => handleFileInput(e)}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
/>
|
||||
{uploadPdf?.name && (
|
||||
<div
|
||||
onClick={() => inputFileRef?.current?.click()}
|
||||
className="mt-2 cursor-pointer op-input op-input-bordered op-input-sm focus:outline-none py-2 font-semibold w-full text-xs"
|
||||
>
|
||||
selected:{" "}
|
||||
{uploadPdf?.url
|
||||
? `${uploadPdf?.name}.pdf`
|
||||
: getFileName(template.URL)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-[0.35rem]">
|
||||
<label htmlFor="name" className="text-[13px]">
|
||||
{t("Title")}
|
||||
<span className="text-[13px] text-[red]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="Name"
|
||||
value={formData.Name}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
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-[0.35rem]">
|
||||
<label htmlFor="Note" className="text-[13px]">
|
||||
{t("report-heading.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="mb-[0.35rem]">
|
||||
<label htmlFor="Description" className="text-[13px]">
|
||||
{t("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="mb-[0.35rem]">
|
||||
<label className="text-[13px]">{t("send-in-order")}</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<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]">{t("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]">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs mt-3">
|
||||
<label className="block">
|
||||
<span>
|
||||
{t("enable-tour")}
|
||||
<a data-tooltip-id="istourenabled-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>{" "}
|
||||
</span>
|
||||
<Tooltip id="istourenabled-tooltip" className="z-50">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("enable-tour")}</p>
|
||||
<div className="p-[5px]">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">{t("yes")}: </span>
|
||||
<span>{t("istourenabled-help.p1")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">{t("no")}: </span>
|
||||
<span>{t("istourenabled-help.p2")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<p>{t("istourenabled-help.p3", { appName: appName })}</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
className="op-radio op-radio-xs"
|
||||
name="IsTourEnabled"
|
||||
checked={formData.IsTourEnabled === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="IsTourEnabled"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={formData.IsTourEnabled === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs mt-3">
|
||||
<label>
|
||||
{t("notify-on-signatures")}
|
||||
<a data-tooltip-id="nos-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>{" "}
|
||||
<Tooltip id="nos-tooltip" className="z-[999]">
|
||||
<div className="max-w-[200px] md:max-w-[450px] text-[11px]">
|
||||
<p className="font-bold">{t("notify-on-signatures")}</p>
|
||||
<p>{t("notify-on-signatures-help.p1")}</p>
|
||||
<p>{t("notify-on-signatures-help.note")}</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div
|
||||
className={
|
||||
`flex items-center gap-2 ml-2 mb-1`
|
||||
}
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
onChange={() => handleNotifySignChange(true)}
|
||||
checked={formData.NotifyOnSignatures === true}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
`flex items-center gap-2 ml-2 mb-1`
|
||||
}
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
onChange={() => handleNotifySignChange(false)}
|
||||
checked={formData.NotifyOnSignatures === false}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs mt-3">
|
||||
<SignersInput
|
||||
label={t("Bcc")}
|
||||
initialData={template?.Bcc}
|
||||
onChange={handleBcc}
|
||||
helptextZindex={50}
|
||||
helpText={t("bcc-help")}
|
||||
isCaptureAllData
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Redirect Url</label>
|
||||
<input
|
||||
name="RedirectUrl"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={formData.RedirectUrl}
|
||||
onChange={handleStrInput}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
{t("time-to-complete")}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="TimeToCompleteDays"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={formData.TimeToCompleteDays}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-[1rem] flex justify-start">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditTemplate;
|
||||
@@ -1,159 +0,0 @@
|
||||
import React from "react";
|
||||
import { Quill } from "react-quill-new";
|
||||
|
||||
// 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 }
|
||||
};
|
||||
|
||||
// Modules object for setting up the Quill editor
|
||||
export const module2 = {
|
||||
toolbar: {
|
||||
container: "#toolbar2",
|
||||
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",
|
||||
"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,43 +0,0 @@
|
||||
import React from "react";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
import ReactQuill from "react-quill-new";
|
||||
import "../../styles/quill.css";
|
||||
import EditorToolbar, { module1, formats } from "./EditorToolbar";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export function EmailBody(props) {
|
||||
const { t } = useTranslation();
|
||||
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">
|
||||
{t("subject")} <Tooltip message={t("email-subject")} />
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
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">
|
||||
{t("body")} <Tooltip message={t("email-body")} />
|
||||
</label>
|
||||
<div className="px-1 py-2 w-full focus:outline-none text-xs">
|
||||
<EditorToolbar containerId="toolbar1" />
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={props.requestBody}
|
||||
placeholder={t("email-placeholder")}
|
||||
ref={props.editorRef}
|
||||
modules={module1}
|
||||
formats={formats}
|
||||
onChange={props.handleOnchangeRequest}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
handleToPrint,
|
||||
} from "../../constant/Utils";
|
||||
import {
|
||||
emailRegex,
|
||||
} from "../../constant/const";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Parse from "parse";
|
||||
|
||||
function EmailComponent({
|
||||
isEmail,
|
||||
setIsEmail,
|
||||
setSuccessEmail,
|
||||
pdfDetails,
|
||||
setIsAlert,
|
||||
setIsDownloadModal
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [emailList, setEmailList] = useState([]);
|
||||
const [emailValue, setEmailValue] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [emailErr, setEmailErr] = useState(false);
|
||||
const [isDownloading, setIsDownloading] = useState("");
|
||||
const isAndroid = /Android/i.test(navigator.userAgent);
|
||||
|
||||
//function for send email
|
||||
const sendEmail = async () => {
|
||||
setIsLoading(true);
|
||||
const params = { docId: pdfDetails?.[0]?.objectId, recipients: emailList };
|
||||
const sendmail = await Parse.Cloud.run("forwarddoc", params);
|
||||
console.log("sendmail ", sendmail);
|
||||
if (sendmail?.status === "success") {
|
||||
setSuccessEmail(true);
|
||||
setIsEmail(false);
|
||||
setTimeout(() => {
|
||||
setSuccessEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}, 1500);
|
||||
setIsLoading(false);
|
||||
}
|
||||
else {
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
});
|
||||
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?.toLowerCase()?.replace(/\s/g, "");
|
||||
setEmailErr(false);
|
||||
setEmailValue(value);
|
||||
};
|
||||
|
||||
//function for save email in array after press enter
|
||||
const handleEnterPress = (e) => {
|
||||
const pattern = emailRegex;
|
||||
const validate = emailValue?.match(pattern);
|
||||
if (e.key === "Enter" && emailValue) {
|
||||
if (validate) {
|
||||
const emailLowerCase = emailValue?.toLowerCase();
|
||||
setEmailList((prev) => [...prev, emailLowerCase]);
|
||||
setEmailValue("");
|
||||
} else {
|
||||
setEmailErr(true);
|
||||
}
|
||||
} else if (e === "add" && emailValue) {
|
||||
if (validate) {
|
||||
const emailLowerCase = emailValue?.toLowerCase();
|
||||
setEmailList((prev) => [...prev, emailLowerCase]);
|
||||
setEmailValue("");
|
||||
} else {
|
||||
setEmailErr(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleClose = () => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{/* isEmail */}
|
||||
{isEmail && (
|
||||
<ModalUi isOpen showHeader={false}>
|
||||
{isLoading && (
|
||||
<div className="absolute w-full h-full flex flex-col justify-center items-center z-[20] bg-[#e6f2f2]/70">
|
||||
<Loader />
|
||||
<span className="text-[12px] text-base-content">
|
||||
{t("loader")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{isDownloading === "pdf" && (
|
||||
<div className="fixed z-[200] inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between items-center py-[10px] px-[20px] border-b-[1px] border-base-content">
|
||||
<span className="text-base-content font-bold text-sm md:text-lg">
|
||||
{t("successfully-signed")}
|
||||
</span>
|
||||
<div className="flex flex-row">
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, setIsDownloading, pdfDetails)
|
||||
}
|
||||
className="op-btn op-btn-neutral op-btn-sm text-xs md:text-[15px]"
|
||||
>
|
||||
<i className="fa-light fa-print" aria-hidden="true"></i>
|
||||
{t("print")}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="op-btn op-btn-primary op-btn-sm text-xs md:text-[15px] ml-2"
|
||||
onClick={() => {
|
||||
handleClose();
|
||||
setIsDownloadModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
{t("download")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-full p-[20px]">
|
||||
<p className="font-medium text-[15px] mb-[5px] text-base-content align-baseline">
|
||||
{t("email-mssg")}
|
||||
</p>
|
||||
{emailList.length > 0 ? (
|
||||
<div className="p-0 border-[1px] op-border-primary w-full rounded-md text-[15px] overflow-hidden">
|
||||
<div className="flex flex-row flex-wrap">
|
||||
{emailList.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
className="flex flex-row items-center op-bg-primary mx-[2px] mt-[2px] rounded-md py-[5px] px-[10px]"
|
||||
key={ind}
|
||||
>
|
||||
<span className="text-base-100 text-[13px]">
|
||||
{data}
|
||||
</span>
|
||||
<span
|
||||
className="text-base-100 text-[13px] font-semibold ml-[7px] cursor-pointer"
|
||||
onClick={() => removeChip(ind)}
|
||||
>
|
||||
<i className="fa-light fa-xmark"></i>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{emailList.length <= 9 && (
|
||||
<input
|
||||
type="email"
|
||||
value={emailValue}
|
||||
className="p-[10px] rounded-md w-full text-[15px] bg-transparent outline-none"
|
||||
onChange={handleEmailValue}
|
||||
onKeyDown={handleEnterPress}
|
||||
onBlur={() => emailValue && handleEnterPress("add")}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<input
|
||||
type="email"
|
||||
value={emailValue}
|
||||
className="p-[10px] pb-[20px] rounded-md w-full text-[15px] outline-none bg-transparent border-[1px] op-border-primary"
|
||||
onChange={handleEmailValue}
|
||||
onKeyDown={handleEnterPress}
|
||||
placeholder={t("enter-email-plaholder")}
|
||||
onBlur={() => emailValue && handleEnterPress("add")}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{emailErr && (
|
||||
<p className="text-xs text-[red] ml-1.5 mt-0.5">
|
||||
{t("email-error-1")}
|
||||
</p>
|
||||
)}
|
||||
{/* <button
|
||||
className={`${
|
||||
emailValue ? "cursor-pointer" : "cursor-default"
|
||||
} op-btn op-btn-primary op-btn-sm m-2 shadow-md`}
|
||||
onClick={() => emailValue && handleEnterPress("add")}
|
||||
>
|
||||
<i className="fa-light fa-plus" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded">
|
||||
<span className="font-bold">{t("report-heading.Note")}: </span>
|
||||
<span className="text-[15px]">{t("email-error-2")}</span>
|
||||
</div>
|
||||
<hr className="w-full my-[15px] bg-base-content" /> */}
|
||||
<div className="mt-2">
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-secondary"
|
||||
onClick={() => emailList.length > 0 && sendEmail()}
|
||||
>
|
||||
{t("send")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-2"
|
||||
onClick={() => handleClose()}
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EmailComponent;
|
||||
@@ -1,703 +0,0 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import PrevNext from "./PrevNext";
|
||||
import {
|
||||
base64ToArrayBuffer,
|
||||
deletePdfPage,
|
||||
handleDownloadCertificate,
|
||||
handleDownloadPdf,
|
||||
handleRemoveWidgets,
|
||||
handleToPrint
|
||||
} from "../../constant/Utils";
|
||||
import "../../styles/signature.css";
|
||||
import { DropdownMenu } from "radix-ui";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import { maxFileSize } from "../../constant/const";
|
||||
|
||||
function Header(props) {
|
||||
const { t } = useTranslation();
|
||||
const filterPrefill =
|
||||
props?.signerPos &&
|
||||
props?.signerPos?.filter((data) => data.Role !== "prefill");
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const [isDownloading, setIsDownloading] = useState("");
|
||||
const [isDeletePage, setIsDeletePage] = useState(false);
|
||||
const mergePdfInputRef = useRef(null);
|
||||
const enabledBackBtn = props?.disabledBackBtn === true ? false : true;
|
||||
//function for show decline alert
|
||||
const handleDeclinePdfAlert = async () => {
|
||||
if (props?.handleDecline) {
|
||||
props.handleDecline();
|
||||
} else {
|
||||
const currentDecline = { currnt: "Sure", isDeclined: true };
|
||||
props?.setIsDecline(currentDecline);
|
||||
}
|
||||
};
|
||||
const handleDetelePage = async () => {
|
||||
props?.setIsUploadPdf && props?.setIsUploadPdf(true);
|
||||
const pdfupdatedData = await deletePdfPage(
|
||||
props?.pdfArrayBuffer,
|
||||
props?.pageNumber
|
||||
);
|
||||
if (pdfupdatedData?.totalPages === 1) {
|
||||
alert(t("delete-alert"));
|
||||
} else {
|
||||
props?.setPdfBase64Url(pdfupdatedData.base64);
|
||||
props?.setPdfArrayBuffer(pdfupdatedData.arrayBuffer);
|
||||
setIsDeletePage(false);
|
||||
handleRemoveWidgets(
|
||||
props?.setSignerPos,
|
||||
props?.signerPos,
|
||||
props?.pageNumber
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// `removeFile` is used to remove file if exists
|
||||
const removeFile = (e) => {
|
||||
if (e) {
|
||||
e.target.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileUpload = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
alert("Please upload a valid PDF file.");
|
||||
return;
|
||||
}
|
||||
if (!file.type.includes("pdf")) {
|
||||
alert("Only PDF files are allowed.");
|
||||
return;
|
||||
}
|
||||
|
||||
const mb = Math.round(file?.size / Math.pow(1024, 2));
|
||||
if (mb > maxFileSize) {
|
||||
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
|
||||
removeFile(e);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const uploadedPdfBytes = await file.arrayBuffer();
|
||||
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
|
||||
ignoreEncryption: true
|
||||
});
|
||||
const basePdfDoc = await PDFDocument.load(props.pdfArrayBuffer);
|
||||
|
||||
// Copy pages from the uploaded PDF to the base PDF
|
||||
const uploadedPdfPages = await basePdfDoc.copyPages(
|
||||
uploadedPdfDoc,
|
||||
uploadedPdfDoc.getPageIndices()
|
||||
);
|
||||
uploadedPdfPages.forEach((page) => basePdfDoc.addPage(page));
|
||||
// Save the updated PDF
|
||||
const pdfBase64 = await basePdfDoc.saveAsBase64({
|
||||
useObjectStreams: false
|
||||
});
|
||||
const pdfBuffer = base64ToArrayBuffer(pdfBase64);
|
||||
props.setPdfArrayBuffer(pdfBuffer);
|
||||
props.setPdfBase64Url(pdfBase64);
|
||||
props.setIsUploadPdf && props.setIsUploadPdf(true);
|
||||
mergePdfInputRef.current.value = "";
|
||||
} catch (error) {
|
||||
mergePdfInputRef.current.value = "";
|
||||
console.error("Error merging PDF:", error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="flex py-[5px]">
|
||||
{isMobile && props?.isShowHeader ? (
|
||||
<div
|
||||
id="navbar"
|
||||
className="stickyHead"
|
||||
style={{
|
||||
width: window.innerWidth + "px"
|
||||
}}
|
||||
>
|
||||
<div className="flex justify-between items-center py-[5px] pl-[10px] ">
|
||||
<div onClick={() => window.history.go(-2)}>
|
||||
<i
|
||||
className="fa-light fa-arrow-left text-base-content"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
<PrevNext
|
||||
pageNumber={props?.pageNumber}
|
||||
allPages={props?.allPages}
|
||||
changePage={props?.changePage}
|
||||
/>
|
||||
{props?.isCompleted || props?.alreadySign ? (
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<div className="op-link op-link-primary no-underline text-[16px] font-semibold px-3">
|
||||
<i
|
||||
className="fa-light fa-ellipsis-v"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
className="DropdownMenuContent"
|
||||
sideOffset={5}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => {
|
||||
if (props?.isCompleted) {
|
||||
props?.setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa-light fa-arrow-down mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{t("download")}
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
{props?.isCompleted && (
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="border-none bg-[#fff]">
|
||||
<i
|
||||
className="fa-light fa-award mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{t("certificate")}
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
)}
|
||||
{props?.isSignYourself && (
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => props?.setIsEmail(true)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa-light fa-envelope mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{t("mail")}
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
)}
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, setIsDownloading, props?.pdfDetails)
|
||||
}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa-light fa-print mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{t("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
|
||||
*/}
|
||||
{props?.currentSigner && (
|
||||
<div className="flex items-center" data-tut="reactourFifth">
|
||||
{props?.decline && (
|
||||
<div
|
||||
onClick={() => handleDeclinePdfAlert()}
|
||||
className="text-[red] border-none font-[650] text-[14px] mr-2"
|
||||
>
|
||||
{t("decline")}
|
||||
</div>
|
||||
)}
|
||||
{props?.isPlaceholder ? (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (!props?.isMailSend) {
|
||||
props?.alertSendEmail();
|
||||
}
|
||||
}}
|
||||
className={`${
|
||||
props?.isMailSend ? "" : "op-link-primary"
|
||||
} op-link no-underline font-[650] text-[14px]`}
|
||||
data-tut="headerArea"
|
||||
>
|
||||
{props?.completeBtnTitle
|
||||
? props?.completeBtnTitle
|
||||
: t("send")}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
data-tut="reactourThird"
|
||||
onClick={() => props?.embedWidgetsData()}
|
||||
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
|
||||
>
|
||||
{t("finish")}
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
type="file"
|
||||
className="hidden"
|
||||
accept="application/pdf"
|
||||
ref={mergePdfInputRef}
|
||||
onChange={handleFileUpload}
|
||||
/>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<div className="font-[650] text-[18px] px-3 text-base-content no-underline">
|
||||
<i
|
||||
className="fa-light fa-ellipsis-v"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
className="bg-white shadow-md rounded-md px-3 py-2"
|
||||
sideOffset={5}
|
||||
>
|
||||
{props?.setIsEditTemplate && (
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => props?.setIsEditTemplate(true)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa-light fa-gear mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-[500]">{t("Edit")}</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
)}
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa-light fa-arrow-down mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-[500]">
|
||||
{t("download")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
{!props?.isDisablePdfEditTools && (
|
||||
<>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => mergePdfInputRef.current.click()}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-plus text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("add-pages")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => setIsDeletePage(true)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-trash text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("delete-page")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => props?.handleRotationFun(90)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("rotate-right")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => props?.handleRotationFun(-90)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("rotate-left")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => props?.clickOnZoomIn()}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">{t("zoom-in")}</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => props?.clickOnZoomOut()}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("zoom-out")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
)}
|
||||
{props?.isPublicTemplate && (
|
||||
<div
|
||||
data-tut="reactourThird"
|
||||
onClick={() => props?.embedWidgetsData()}
|
||||
className="border-none font-[650] text-[14px] pr-2 op-link op-link-primary no-underline"
|
||||
>
|
||||
{t("sign-now")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-wrap justify-between items-center w-full gap-y-1 ml-1">
|
||||
<PrevNext
|
||||
pageNumber={props?.pageNumber}
|
||||
allPages={props?.allPages}
|
||||
changePage={props?.changePage}
|
||||
/>
|
||||
{props?.isPlaceholder ? (
|
||||
<>
|
||||
<div className="flex mx-[100px] lg:mx-0 order-last lg:order-none">
|
||||
{!props?.isMailSend &&
|
||||
props?.signersdata.length > 0 &&
|
||||
props?.signersdata.length !== filterPrefill.length && (
|
||||
<div>
|
||||
{filterPrefill.length === 0 ? (
|
||||
<span className="text-[13px] text-[#f5405e]">
|
||||
{t("add")}{" "}
|
||||
{props?.signersdata.length - filterPrefill.length}{" "}
|
||||
{t("recipients")} {t("widgets-name.signature")}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-[13px] text-[#f5405e]">
|
||||
{t("add")}{" "}
|
||||
{props?.signersdata.length - filterPrefill.length}{" "}
|
||||
{t("more")}
|
||||
{t("recipients")} {t("widgets-name.signature")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex">
|
||||
{props?.setIsEditTemplate && (
|
||||
<button
|
||||
onClick={() => props?.setIsEditTemplate(true)}
|
||||
className="outline-none border-none text-center mr-[3px]"
|
||||
>
|
||||
<i className="fa-light fa-gear fa-lg"></i>
|
||||
</button>
|
||||
)}
|
||||
{enabledBackBtn && (
|
||||
<button
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
{t("back")}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
disabled={props?.isMailSend && true}
|
||||
data-tut="headerArea"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px]"
|
||||
onClick={() => props?.alertSendEmail()}
|
||||
>
|
||||
{props?.completeBtnTitle
|
||||
? props?.completeBtnTitle
|
||||
: props?.isMailSend
|
||||
? t("sent")
|
||||
: t("send")}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : props?.isPdfRequestFiles || props?.isSelfSign ? (
|
||||
props?.alreadySign || (props?.isSelfSign && props?.isCompleted) ? (
|
||||
<div className="flex flex-row">
|
||||
<button
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, setIsDownloading, props?.pdfDetails)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-print py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block">{t("print")}</span>
|
||||
</button>
|
||||
{props?.isCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-award py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block">{t("certificate")}</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() => {
|
||||
if (props?.isCompleted) {
|
||||
props?.setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-download py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block">{t("download")}</span>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex" data-tut="reactourFifth">
|
||||
{props?.currentSigner && (
|
||||
<>
|
||||
{props?.templateId && (
|
||||
<button
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
<span className="hidden lg:block">{t("download")}</span>
|
||||
</button>
|
||||
)}
|
||||
{!props?.isSelfSign && (
|
||||
<button
|
||||
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() => handleDeclinePdfAlert()}
|
||||
>
|
||||
{t("decline")}
|
||||
</button>
|
||||
)}
|
||||
{!props?.templateId && (
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
)
|
||||
}
|
||||
>
|
||||
<i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
|
||||
<span className="hidden lg:block">{t("download")}</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() => props?.embedWidgetsData()}
|
||||
>
|
||||
{t("finish")}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
) : props?.isCompleted ? (
|
||||
<div className="flex flex-row">
|
||||
{props?.isCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
>
|
||||
<i className="fa-light fa-award" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">
|
||||
{t("certificate")}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, setIsDownloading, props?.pdfDetails)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
>
|
||||
<i className="fa-light fa-print" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">{t("print")}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
onClick={() => props?.setIsDownloadModal(true)}
|
||||
>
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">{t("download")}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-info op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
onClick={() => props?.setIsEmail(true)}
|
||||
>
|
||||
<i className="fa-light fa-envelope" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">{t("mail")}</span>
|
||||
</button>
|
||||
</div>
|
||||
) : props?.isPublicTemplate ? (
|
||||
<div className="flex">
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm shadow"
|
||||
onClick={() => props?.embedWidgetsData()}
|
||||
>
|
||||
{t("sign-now")}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex">
|
||||
<button
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
{t("back")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px]"
|
||||
onClick={() => props?.embedWidgetsData()}
|
||||
>
|
||||
{t("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" || isDownloading === "certificate_err"
|
||||
}
|
||||
title={
|
||||
isDownloading === "certificate" || isDownloading === "certificate_err"
|
||||
? t("generating-certificate")
|
||||
: t("pdf-download")
|
||||
}
|
||||
handleClose={() => setIsDownloading("")}
|
||||
>
|
||||
<div className="p-3 md:p-5 text-[13px] md:text-base text-center text-base-content">
|
||||
{isDownloading === "certificate" ? (
|
||||
<p>{t("generate-certificate-alert")}</p>
|
||||
) : (
|
||||
<p>{t("generate-certificate-err")}</p>
|
||||
)}
|
||||
</div>
|
||||
</ModalUi>
|
||||
<ModalUi
|
||||
isOpen={isDeletePage}
|
||||
title={t("delete-page")}
|
||||
handleClose={() => setIsDeletePage(false)}
|
||||
>
|
||||
<div className="h-[100%] p-[20px]">
|
||||
<p className="font-medium">{t("delete-alert-2")}</p>
|
||||
<p className="pt-3">{t("delete-note")}</p>
|
||||
<div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div>
|
||||
<button
|
||||
onClick={() => handleDetelePage()}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsDeletePage(false)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
@@ -1,192 +0,0 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
base64ToArrayBuffer,
|
||||
deletePdfPage,
|
||||
handleRemoveWidgets
|
||||
} from "../../constant/Utils";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import { maxFileSize } from "../../constant/const";
|
||||
|
||||
function PdfZoom(props) {
|
||||
const { t } = useTranslation();
|
||||
const mergePdfInputRef = useRef(null);
|
||||
const [isDeletePage, setIsDeletePage] = useState(false);
|
||||
const handleDetelePage = async () => {
|
||||
props.setIsUploadPdf && props.setIsUploadPdf(true);
|
||||
try {
|
||||
const pdfupdatedData = await deletePdfPage(
|
||||
props.pdfArrayBuffer,
|
||||
props.pageNumber
|
||||
);
|
||||
if (pdfupdatedData?.totalPages === 1) {
|
||||
alert(t("delete-alert"));
|
||||
} else {
|
||||
props.setPdfBase64Url(pdfupdatedData.base64);
|
||||
props.setPdfArrayBuffer(pdfupdatedData.arrayBuffer);
|
||||
setIsDeletePage(false);
|
||||
handleRemoveWidgets(
|
||||
props.setSignerPos,
|
||||
props.signerPos,
|
||||
props.pageNumber
|
||||
);
|
||||
props.setAllPages(pdfupdatedData.remainingPages || 1);
|
||||
if (props.allPages === props.pageNumber) {
|
||||
props.setPageNumber(props.pageNumber - 1);
|
||||
} else if (props.allPages > 2) {
|
||||
props.setPageNumber(props.pageNumber);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error in delete pdf page", e);
|
||||
}
|
||||
};
|
||||
|
||||
// `removeFile` is used to remove file if exists
|
||||
const removeFile = (e) => {
|
||||
if (e) {
|
||||
e.target.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileUpload = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
alert("Please upload a valid PDF file.");
|
||||
return;
|
||||
}
|
||||
if (!file.type.includes("pdf")) {
|
||||
alert("Only PDF files are allowed.");
|
||||
return;
|
||||
}
|
||||
const mb = Math.round(file?.size / Math.pow(1024, 2));
|
||||
if (mb > maxFileSize) {
|
||||
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
|
||||
removeFile(e);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const uploadedPdfBytes = await file.arrayBuffer();
|
||||
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
|
||||
ignoreEncryption: true
|
||||
});
|
||||
const basePdfDoc = await PDFDocument.load(props.pdfArrayBuffer);
|
||||
|
||||
// Copy pages from the uploaded PDF to the base PDF
|
||||
const uploadedPdfPages = await basePdfDoc.copyPages(
|
||||
uploadedPdfDoc,
|
||||
uploadedPdfDoc.getPageIndices()
|
||||
);
|
||||
uploadedPdfPages.forEach((page) => basePdfDoc.addPage(page));
|
||||
// Save the updated PDF
|
||||
const pdfBase64 = await basePdfDoc.saveAsBase64({
|
||||
useObjectStreams: false
|
||||
});
|
||||
const pdfBuffer = base64ToArrayBuffer(pdfBase64);
|
||||
props.setPdfArrayBuffer(pdfBuffer);
|
||||
props.setPdfBase64Url(pdfBase64);
|
||||
props.setIsUploadPdf && props.setIsUploadPdf(true);
|
||||
mergePdfInputRef.current.value = "";
|
||||
} catch (error) {
|
||||
mergePdfInputRef.current.value = "";
|
||||
console.error("Error merging PDF:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="hidden md:flex flex-col gap-1 text-center md:w-[5%] mt-[42px]">
|
||||
{!props.isDisableEditTools && (
|
||||
<>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={() => mergePdfInputRef.current.click()}
|
||||
title={t("add-pages")}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
className="hidden"
|
||||
accept="application/pdf"
|
||||
ref={mergePdfInputRef}
|
||||
onChange={handleFileUpload}
|
||||
/>
|
||||
<i className="fa-light fa-plus text-gray-500 2xl:text-[25px]"></i>
|
||||
</span>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={() => setIsDeletePage(true)}
|
||||
title={t("delete-page")}
|
||||
>
|
||||
<i className="fa-light fa-trash text-gray-500 2xl:text-[25px]"></i>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={() => props.clickOnZoomIn()}
|
||||
title={t("zoom-in")}
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[25px]"></i>
|
||||
</span>
|
||||
|
||||
{!props.isDisableEditTools && (
|
||||
<>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={() => props.handleRotationFun(90)}
|
||||
title={t("rotate-right")}
|
||||
>
|
||||
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[25px]"></i>
|
||||
</span>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
title={t("rotate-left")}
|
||||
onClick={() => props.handleRotationFun(-90)}
|
||||
>
|
||||
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[25px]"></i>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px]"
|
||||
onClick={() => props.clickOnZoomOut()}
|
||||
style={{
|
||||
cursor: props.zoomPercent > 0 ? "pointer" : "default"
|
||||
}}
|
||||
title={t("zoom-out")}
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px]"></i>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<ModalUi
|
||||
isOpen={isDeletePage}
|
||||
title={t("delete-page")}
|
||||
handleClose={() => setIsDeletePage(false)}
|
||||
>
|
||||
<div className="h-[100%] p-[20px]">
|
||||
<p className="font-medium">{t("delete-alert-2")}</p>
|
||||
<p className="pt-3">{t("delete-note")}</p>
|
||||
<div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div>
|
||||
<button
|
||||
onClick={() => handleDetelePage()}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsDeletePage(false)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-1"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default PdfZoom;
|
||||
@@ -1,319 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import {
|
||||
handleCopyNextToWidget,
|
||||
randomId,
|
||||
textWidget
|
||||
} from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function PlaceholderCopy(props) {
|
||||
const { t } = useTranslation();
|
||||
const copyType = [
|
||||
{ id: 1, type: "All pages" },
|
||||
{ id: 2, type: "All pages but last" },
|
||||
{ id: 3, type: "All pages but first" },
|
||||
{ id: 4, type: "Next to current widget" }
|
||||
];
|
||||
const [selectCopyType, setSelectCopyType] = useState(1);
|
||||
//function for get copy placeholder position
|
||||
const getCopyPlaceholderPosition = (
|
||||
type,
|
||||
newPlaceholder,
|
||||
newPageNumber,
|
||||
existPlaceholderPosition
|
||||
) => {
|
||||
//remove existing page position when used option copy but first or copy but last
|
||||
const filterPosition =
|
||||
existPlaceholderPosition &&
|
||||
existPlaceholderPosition.filter(
|
||||
(data) => data.xPosition !== newPlaceholder.xPosition
|
||||
);
|
||||
//first option - copy all page but not first
|
||||
//copy all page placeholder at requested position except first page
|
||||
if (newPageNumber === 1 && type === 3) {
|
||||
//when user choose this option and placed widget on first page then do not remove first page widget
|
||||
//it should be copy all page included first page on same postion
|
||||
if (props.pageNumber === 1) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...existPlaceholderPosition]
|
||||
};
|
||||
} //else when user copy widget and choose this option and not at first page then copy all page but not at first
|
||||
else if (filterPosition && filterPosition.length > 0) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...filterPosition]
|
||||
};
|
||||
}
|
||||
}
|
||||
//second option - copy all page but not last
|
||||
//copy all page placeholder at requested position except last page
|
||||
else if (newPageNumber === props.allPages && type === 2) {
|
||||
if (existPlaceholderPosition) {
|
||||
//when user choose this option and placed widget on last page then do not remove last page widget
|
||||
//it should be copy all page included last page on same postion
|
||||
if (props.pageNumber === props.allPages) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...existPlaceholderPosition]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...filterPosition]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
//this optin - copy all page with exiting widgets on same page
|
||||
//copy all page placeholder at requested position with existing placeholder position
|
||||
else if (existPlaceholderPosition) {
|
||||
if (newPageNumber === props.pageNumber) {
|
||||
return {
|
||||
pageNumber: props.pageNumber,
|
||||
pos: [...existPlaceholderPosition]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...existPlaceholderPosition, newPlaceholder]
|
||||
};
|
||||
}
|
||||
}
|
||||
//copy all page placeholder at requested position
|
||||
else {
|
||||
if (newPageNumber === props.pageNumber) {
|
||||
return {
|
||||
pageNumber: props.pageNumber,
|
||||
pos: [...existPlaceholderPosition]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [newPlaceholder]
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
//function for copy placeholder as per select copy type
|
||||
const copyPlaceholder = (type) => {
|
||||
let newPlaceholderPosition = [];
|
||||
let newPageNumber = 1;
|
||||
const signerPosition = props.xyPosition;
|
||||
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();
|
||||
const newPlaceholder = { ...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,
|
||||
newPlaceholder,
|
||||
newPageNumber,
|
||||
existPlaceholderPosition
|
||||
);
|
||||
if (getPlaceholderObj?.pos) {
|
||||
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.setXyPosition(signersData);
|
||||
}
|
||||
//handle signyourself array and copy for single signers placeholder at requested location
|
||||
else {
|
||||
const xyPosition = props.xyPosition;
|
||||
|
||||
const placeholderPosition = xyPosition.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 = xyPosition.filter(
|
||||
(data) => data.pageNumber === newPageNumber
|
||||
);
|
||||
const existPlaceholderPosition =
|
||||
existPlaceholder[0] && existPlaceholder[0].pos;
|
||||
|
||||
const newId = randomId();
|
||||
const newPlaceholder = { ...currentPlaceholder, key: newId };
|
||||
//function for get copy to requested location of placeholder position
|
||||
const getPlaceholderObj = getCopyPlaceholderPosition(
|
||||
type,
|
||||
newPlaceholder,
|
||||
newPageNumber,
|
||||
existPlaceholderPosition
|
||||
);
|
||||
if (getPlaceholderObj) {
|
||||
newPlaceholderPosition.push(getPlaceholderObj);
|
||||
}
|
||||
|
||||
newPageNumber++;
|
||||
}
|
||||
props.setXyPosition(newPlaceholderPosition);
|
||||
}
|
||||
};
|
||||
|
||||
//function for getting selected type placeholder copy
|
||||
const handleApplyCopy = () => {
|
||||
if (selectCopyType === 4) {
|
||||
const signerPosition = props.xyPosition;
|
||||
let currentXYposition;
|
||||
const signerId = props.signerObjId ? props.signerObjId : props.Id;
|
||||
if (signerId) {
|
||||
const filterSignerPosition = signerPosition.filter(
|
||||
(item) => item?.Id === signerId
|
||||
);
|
||||
currentXYposition = filterSignerPosition[0].placeHolder.filter(
|
||||
(data) => data.pageNumber === props?.pageNumber
|
||||
);
|
||||
//get current placeholder position data which user want to copy
|
||||
currentXYposition = currentXYposition[0].pos.find(
|
||||
(position) => position.key === props.signKey
|
||||
);
|
||||
//function to create new widget next to just widget
|
||||
handleCopyNextToWidget(
|
||||
currentXYposition,
|
||||
props.widgetType,
|
||||
props.xyPosition,
|
||||
props.pageNumber,
|
||||
props.setXyPosition,
|
||||
props?.Id
|
||||
);
|
||||
} else {
|
||||
const getIndex = props?.xyPosition.findIndex(
|
||||
(data) => data.pageNumber === props.pageNumber
|
||||
);
|
||||
const placeholderPosition = props?.xyPosition[getIndex];
|
||||
//get current placeholder position data which user want to copy
|
||||
currentXYposition = placeholderPosition.pos.find(
|
||||
(pos) => pos.key === props.signKey
|
||||
);
|
||||
//function to create new widget next to just widget
|
||||
handleCopyNextToWidget(
|
||||
currentXYposition,
|
||||
props.widgetType,
|
||||
props.xyPosition,
|
||||
getIndex,
|
||||
props.setXyPosition
|
||||
);
|
||||
}
|
||||
} else {
|
||||
copyPlaceholder(selectCopyType);
|
||||
}
|
||||
};
|
||||
const handleUniqueId = () => {
|
||||
const signerId = props.signerObjId ? props.signerObjId : props.Id;
|
||||
if (signerId && props.widgetType === textWidget && props.setTempSignerId) {
|
||||
props.setUniqueId(props?.tempSignerId);
|
||||
props.setTempSignerId("");
|
||||
}
|
||||
props.setIsPageCopy(false);
|
||||
setSelectCopyType(1);
|
||||
};
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isPageCopy}
|
||||
title={t("copy-title")}
|
||||
handleClose={() => handleUniqueId()}
|
||||
>
|
||||
<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.id}
|
||||
onChange={() => setSelectCopyType(data.id)}
|
||||
checked={selectCopyType === data.id}
|
||||
/>
|
||||
{t(`copy-type.${data.type}`)}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<div className="flex flex-row bg-[#9f9f9f] w-full my-[15px]"></div>
|
||||
<button
|
||||
onClick={() => {
|
||||
handleApplyCopy();
|
||||
handleUniqueId();
|
||||
}}
|
||||
type="button"
|
||||
disabled={!selectCopyType}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("apply")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-2"
|
||||
onClick={() => handleUniqueId()}
|
||||
>
|
||||
{t("cancel")}
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlaceholderCopy;
|
||||
@@ -1,512 +0,0 @@
|
||||
import React, { useEffect, useState, forwardRef, useRef } from "react";
|
||||
import {
|
||||
getMonth,
|
||||
getYear,
|
||||
radioButtonWidget,
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
months,
|
||||
years,
|
||||
selectCheckbox,
|
||||
checkRegularExpress
|
||||
} from "../../constant/Utils";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import "../../styles/signature.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const textWidgetCls =
|
||||
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] rounded-[2px] border-[1px] border-[#007bff] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap bg-white";
|
||||
const selectWidgetCls =
|
||||
"w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none text-base-content";
|
||||
const widgetCls =
|
||||
"select-none-cls overflow-hidden w-full h-full text-black flex flex-col justify-center items-center";
|
||||
function PlaceholderType(props) {
|
||||
const { t } = useTranslation();
|
||||
const type = props?.pos?.type;
|
||||
const iswidgetEnable =
|
||||
props.isSignYourself ||
|
||||
((props.isSelfSign || props.isNeedSign) &&
|
||||
props.data?.signerObjId === props.signerObjId);
|
||||
const widgetData =
|
||||
props.pos?.options?.defaultValue || props.pos?.options?.response;
|
||||
const widgetTypeTranslation = t(`widgets-name.${props?.pos?.type}`);
|
||||
const inputRef = useRef(null);
|
||||
const [widgetValue, setwidgetValue] = useState();
|
||||
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
|
||||
const [hint, setHint] = useState("");
|
||||
const fontSize = props.calculateFont(props.pos.options?.fontSize);
|
||||
const fontColor = props.pos.options?.fontColor || "black";
|
||||
const textWidgetStyle = {
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
fontFamily: "Arial, sans-serif",
|
||||
overflow: "hidden",
|
||||
textAlign: "start",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
height: "100%"
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (type !== "date") {
|
||||
if (type && type === "checkbox") {
|
||||
setSelectedCheckbox(
|
||||
props?.pos?.options?.response ||
|
||||
props?.pos?.options?.defaultValue ||
|
||||
[]
|
||||
);
|
||||
} else {
|
||||
if (widgetData) {
|
||||
setwidgetValue(widgetData);
|
||||
}
|
||||
}
|
||||
if (props.pos?.options?.hint) {
|
||||
setHint(props.pos?.options.hint);
|
||||
} else if (props.pos?.options?.validation?.type) {
|
||||
checkRegularExpress(props.pos?.options?.validation?.type, setHint);
|
||||
} else {
|
||||
setHint(props.pos?.type);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.pos]);
|
||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
fontFamily: "Arial, sans-serif"
|
||||
}}
|
||||
className={`${selectWidgetCls} overflow-hidden`}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
{value}
|
||||
<i className="fa-light fa-calendar ml-[5px]"></i>
|
||||
</div>
|
||||
));
|
||||
ExampleCustomInput.displayName = "ExampleCustomInput";
|
||||
|
||||
const handleRadioCheck = (data) => {
|
||||
const defaultData = props.pos.options?.defaultValue;
|
||||
if (widgetValue === data) {
|
||||
return true;
|
||||
} else if (defaultData === data) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case "signature":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signature"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
className="w-full h-full select-none-cls "
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium"
|
||||
>
|
||||
{hint || widgetTypeTranslation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "stamp":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="stamp"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
className="w-full h-full select-none-cls"
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium"
|
||||
>
|
||||
{hint || widgetTypeTranslation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "checkbox":
|
||||
return (
|
||||
<div style={{ zIndex: props.isSignYourself && "99" }}>
|
||||
{props.pos.options?.values?.map((data, ind) => {
|
||||
return (
|
||||
<div key={ind} className="select-none-cls pointer-events-none">
|
||||
<label
|
||||
htmlFor={`checkbox-${props.pos.key + ind}`}
|
||||
style={{ fontSize: fontSize, color: fontColor }}
|
||||
className={`mb-0 flex items-center gap-1 ${
|
||||
ind > 0 ? "mt-[3px]" : "mt-[0px]"
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
id={`checkbox-${props.pos.key + ind}`}
|
||||
style={{
|
||||
width: fontSize,
|
||||
height: fontSize
|
||||
}}
|
||||
className="op-checkbox rounded-[1px]"
|
||||
disabled={
|
||||
props.isNeedSign &&
|
||||
(props.pos.options?.isReadOnly ||
|
||||
props.data?.signerObjId !== props.signerObjId)
|
||||
}
|
||||
type="checkbox"
|
||||
readOnly
|
||||
checked={!!selectCheckbox(ind, selectedCheckbox)}
|
||||
/>
|
||||
{!props.pos.options?.isHideLabel && (
|
||||
<span className="leading-none">{data}</span>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
case textInputWidget:
|
||||
return props.isSignYourself || iswidgetEnable ? (
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
placeholder={hint || t("widgets-name.text")}
|
||||
rows={1}
|
||||
value={widgetValue}
|
||||
className={`${
|
||||
props.pos.options?.isReadOnly ||
|
||||
props.data?.signerObjId !== props.signerObjId
|
||||
? "select-none"
|
||||
: textWidgetCls
|
||||
}`}
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
background: props.data?.blockColor,
|
||||
pointerEvents: "none"
|
||||
}}
|
||||
readOnly
|
||||
disabled={
|
||||
props.isNeedSign &&
|
||||
(props.pos.options?.isReadOnly ||
|
||||
props.data?.signerObjId !== props.signerObjId)
|
||||
}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div style={textWidgetStyle} className="select-none-cls">
|
||||
<span>{hint || widgetTypeTranslation}</span>
|
||||
</div>
|
||||
);
|
||||
case "dropdown":
|
||||
return (
|
||||
<div
|
||||
style={textWidgetStyle}
|
||||
className="select-none-cls flex justify-between items-center"
|
||||
>
|
||||
{widgetData || hint || widgetTypeTranslation}
|
||||
<i className="fa-light fa-circle-chevron-down mr-1 "></i>
|
||||
</div>
|
||||
);
|
||||
case "initials":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="initials"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
className="w-full h-full select-none-cls"
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium text-center"
|
||||
>
|
||||
{hint || widgetTypeTranslation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "name":
|
||||
return iswidgetEnable ? (
|
||||
<textarea
|
||||
readOnly
|
||||
ref={inputRef}
|
||||
placeholder={hint || widgetTypeTranslation}
|
||||
rows={1}
|
||||
value={widgetValue}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
pointerEvents: "none"
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full select-none-cls" style={textWidgetStyle}>
|
||||
<span> {props.pos?.options?.hint || widgetTypeTranslation}</span>
|
||||
</div>
|
||||
);
|
||||
case "company":
|
||||
return iswidgetEnable ? (
|
||||
<textarea
|
||||
readOnly
|
||||
ref={inputRef}
|
||||
placeholder={hint || widgetTypeTranslation}
|
||||
rows={1}
|
||||
value={widgetValue}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
pointerEvents: "none"
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div style={textWidgetStyle} className="select-none-cls">
|
||||
<span>{hint || widgetTypeTranslation}</span>
|
||||
</div>
|
||||
);
|
||||
case "job title":
|
||||
return iswidgetEnable ? (
|
||||
<textarea
|
||||
readOnly
|
||||
ref={inputRef}
|
||||
placeholder={hint || widgetTypeTranslation}
|
||||
rows={1}
|
||||
value={widgetValue}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
pointerEvents: "none"
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div style={textWidgetStyle} className="select-none-cls">
|
||||
<span>{hint || widgetTypeTranslation}</span>
|
||||
</div>
|
||||
);
|
||||
case "date":
|
||||
return iswidgetEnable ? (
|
||||
<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={true}
|
||||
closeOnScroll={true}
|
||||
className={`${selectWidgetCls} outline-[#007bff]`}
|
||||
selected={props?.startDate}
|
||||
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
|
||||
style={textWidgetStyle}
|
||||
className="select-none-cls overflow-hidden"
|
||||
>
|
||||
<span>
|
||||
{props.selectDate
|
||||
? props.selectDate?.format
|
||||
: props.pos?.options?.validation?.format
|
||||
? props.pos?.options?.validation?.format
|
||||
: "MM/dd/yyyy"}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
case "image":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="image"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
className="w-full h-full select-none-cls"
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium text-center"
|
||||
>
|
||||
{hint || widgetTypeTranslation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "email":
|
||||
return iswidgetEnable ? (
|
||||
<textarea
|
||||
readOnly
|
||||
ref={inputRef}
|
||||
placeholder={hint || widgetTypeTranslation}
|
||||
rows={1}
|
||||
value={widgetValue}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
fontFamily: "Arial, sans-serif",
|
||||
pointerEvents: "none"
|
||||
}}
|
||||
disabled
|
||||
cols="1"
|
||||
/>
|
||||
) : (
|
||||
<div style={textWidgetStyle} className="select-none-cls">
|
||||
<span>{hint || widgetTypeTranslation}</span>
|
||||
</div>
|
||||
);
|
||||
case radioButtonWidget:
|
||||
return (
|
||||
<div>
|
||||
{props.pos.options?.values.map((data, ind) => {
|
||||
return (
|
||||
<div key={ind} className="select-none-cls pointer-events-none">
|
||||
<label
|
||||
htmlFor={`radio-${props.pos.key + ind}`}
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
marginTop: ind > 0 ? "5px" : "0px"
|
||||
}}
|
||||
className="text-xs mb-0 flex items-center gap-1 "
|
||||
>
|
||||
<input
|
||||
readOnly
|
||||
id={`radio-${props.pos.key + ind}`}
|
||||
style={{
|
||||
width: fontSize,
|
||||
height: fontSize,
|
||||
lineHeight: 2
|
||||
}}
|
||||
className={`op-radio rounded-full border- border-black appearance-none bg-white inline-block align-middle relative ${
|
||||
handleRadioCheck(data) ? "checked-radio" : ""
|
||||
}`}
|
||||
type="radio"
|
||||
disabled={
|
||||
props.isNeedSign &&
|
||||
(props.pos.options?.isReadOnly ||
|
||||
props.data?.signerObjId !== props.signerObjId)
|
||||
}
|
||||
checked={handleRadioCheck(data)}
|
||||
/>
|
||||
{!props.pos.options?.isHideLabel && (
|
||||
<span className="leading-none">{data}</span>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
case textWidget:
|
||||
return (
|
||||
<textarea
|
||||
readOnly
|
||||
placeholder={t("widgets-name.text")}
|
||||
rows={1}
|
||||
value={widgetValue}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontFamily: "Arial, sans-serif",
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return props.pos.SignUrl ? (
|
||||
<div className="pointer-events-none">
|
||||
<img
|
||||
alt="image"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
className="w-full h-full "
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props.pos.isStamp ? <div>stamp</div> : <div>signature</div>}
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium"
|
||||
>
|
||||
{hint || widgetTypeTranslation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PlaceholderType;
|
||||