From fb93e5acfc7dbfb966e169bba79ec31c94714056 Mon Sep 17 00:00:00 2001 From: Harsh Vitra Date: Thu, 3 Dec 2020 14:18:15 +0530 Subject: [PATCH 1/5] redeem functionality --- .../UserProfile/components/RedeemModal.tsx | 260 +++++++++++++- .../styles/RedeemModal.module.scss | 325 +++++++++++++++++- 2 files changed, 564 insertions(+), 21 deletions(-) diff --git a/src/features/user/UserProfile/components/RedeemModal.tsx b/src/features/user/UserProfile/components/RedeemModal.tsx index 3cc9481b00..670346be45 100644 --- a/src/features/user/UserProfile/components/RedeemModal.tsx +++ b/src/features/user/UserProfile/components/RedeemModal.tsx @@ -5,14 +5,136 @@ import Backdrop from '@material-ui/core/Backdrop'; import Fade from '@material-ui/core/Fade'; import MaterialTextField from '../../../common/InputTypes/MaterialTextField'; import i18next from '../../../../../i18n'; +import { postAuthenticatedRequest } from '../../../../utils/apiRequests/api'; +import { useForm } from 'react-hook-form'; +import { useAuth0 } from '@auth0/auth0-react'; +import Close from '../../../../../public/assets/images/icons/headerIcons/close'; +import ShareOptions from '../../../donations/components/ShareOptions'; +import Snackbar from '@material-ui/core/Snackbar'; +import MuiAlert, { AlertProps } from '@material-ui/lab/Alert'; +import tenantConfig from '../../../../../tenant.config'; -const {useTranslation} = i18next; +const { useTranslation } = i18next; export default function RedeemModal({ redeemModalOpen, handleRedeemModalClose, }: any) { - const {t} = useTranslation(['me', 'common']) + const { t } = useTranslation(['me', 'common', 'donate']); + + const config = tenantConfig(); + + const { + isLoading, + isAuthenticated, + getAccessTokenSilently + } = useAuth0(); + + const imageRef = React.createRef(); + const sendRef = () => imageRef; + + const [errorMessage, setErrorMessage] = React.useState() + + const [codeValidated, setCodeValidated] = React.useState(false) + + const [isUploadingData, setIsUploadingData] = React.useState(false); + + const [validCodeData, setValidCodeData] = React.useState(); + + const [codeRedeemed, setCodeRedeemed] = React.useState(false); + + + const [code, setCode] = React.useState(); + + const [textCopiedsnackbarOpen, setTextCopiedSnackbarOpen] = React.useState( + false, + ); + const handleTextCopiedSnackbarOpen = () => { + setTextCopiedSnackbarOpen(true); + }; + + const handleTextCopiedSnackbarClose = ( + event?: React.SyntheticEvent, + reason?: string, + ) => { + if (reason === 'clickaway') { + return; + } + setTextCopiedSnackbarOpen(false); + }; + + function Alert(props: AlertProps) { + return ; + } + + const { + register, + handleSubmit, + errors, + getValues + } = useForm({ mode: 'onBlur' }); + + async function validateCode(data: any) { + setIsUploadingData(true) + const submitData = { + type: 'gift', + code: data.code + } + if (!isLoading && isAuthenticated) { + let token = await getAccessTokenSilently(); + postAuthenticatedRequest('/api/v1.3/en/validateCode', submitData, token).then((res) => { + if (res.code === 401) { + setErrorMessage(res.message); + setIsUploadingData(false) + } + else if (res.status === 'error') { + setErrorMessage(res.errorText); + setIsUploadingData(false) + } + else if (res.status === 'success') { + setCode(data.code) + setCodeValidated(true); + setValidCodeData(res) + setIsUploadingData(false) + } + }) + } + } + + async function redeemCode() { + setIsUploadingData(true) + const submitData = { + type: 'gift', + code: code + } + if (!isLoading && isAuthenticated) { + let token = await getAccessTokenSilently(); + postAuthenticatedRequest('/api/v1.3/en/convertCode', submitData, token).then((res) => { + console.log('Result', res); + if (res.code === 401) { + setErrorMessage(res.message); + setIsUploadingData(false) + } + else if (res.response.status === 'error') { + setErrorMessage(res.errorText); + setIsUploadingData(false) + } + else if (res.response.status === 'success') { + setCodeRedeemed(true); + setIsUploadingData(false); + setCodeValidated(false); + } + }) + } + } + + + const closeRedeem = () => { + setCodeValidated(false); + setCodeRedeemed(false); + handleRedeemModalClose() + } + return ( -
-

- {t('me:redeem')} -

-
- -
-
{t('common:continue')}
-
+ + {codeRedeemed && validCodeData ? ( + <> +
+
+
closeRedeem()} className={styles.headerCloseIcon}> + +
+
+ Congratulations! +
+
+ + +
+
+
+

+

+
+ My {validCodeData.treeCount} trees will be planted by {validCodeData.tpos[0].tpoName} +

+ {t('donate:plantTreesAtURL', { url: config.tenantURL })} +

+
+
+
+ + {/* hidden div for image download */} +
+
+
+

+

+

+ My {validCodeData.treeCount} trees will be planted by {validCodeData.tpos[0].tpoName} +

+

+ {t('donate:plantTreesAtURL', { url: config.tenantURL })} +

+
+
+ +
+ +
+ + + + + {t('donate:copiedToClipboard')} + + + +
+ + ) : ( +
+ {codeValidated && validCodeData ? ( + <> +
+ {validCodeData.treeCount} + trees +
+ +
+ Planted by +

{validCodeData.tpos[0].tpoName}

+
+ + +
+ {isUploadingData ? ( +
+ ) : ( + 'Add to my trees' + )} +
+ + + + + ) : ( + <> +

+ {t('me:redeem')} +

+ +
+ + +
+ {errors.code && ( + {errors.code.message} + )} + + {errorMessage && ( + {errorMessage} + )} +
+ {isUploadingData ? ( +
+ ) : ( + 'Validate Code' + )} +
+ + )} + +
+ )} +
); diff --git a/src/features/user/UserProfile/styles/RedeemModal.module.scss b/src/features/user/UserProfile/styles/RedeemModal.module.scss index 77f980a811..391ed95399 100644 --- a/src/features/user/UserProfile/styles/RedeemModal.module.scss +++ b/src/features/user/UserProfile/styles/RedeemModal.module.scss @@ -15,32 +15,337 @@ border-radius: 8px; padding: 30px 56px; max-width: 80vw; + min-width: 320px; overflow: hidden; outline: none; display: flex; flex-direction: column; align-items: center; + >h4{ + font-weight: bold; + } } +.modalFinal{ + background-color: #ffffff; + border: 0px; + border-radius: 8px; + padding: 20px; + max-width: 80vw; + min-width: 320px; + overflow: hidden; + outline: none; + display: flex; + flex-direction: column; + align-items: center; +} .inputField { margin-top: 20px; + min-width: 208px; } .continueButton { - background-color: $light; - padding: 8px 40px; - border-radius: 24px; + height: 40px; + min-width: 208px; + border-radius: 100px; + margin-top: 40px; + padding-left: 30px; + padding-right: 30px; + display: flex; + justify-content: center; + background-image: linear-gradient(103deg, #68b030 5%, #007a49 116%); + align-items: center; + font-weight: bold; + color: white; + border: 0px; + transition-duration: 0.5s; + transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1); +} + +.continueButton:hover { + transform: scale(1.05); + cursor: pointer; +} + +// Loader for Buttons +.spinner { + border: 2px solid #f3f3f3; + border-radius: 50%; + border-top: 2px solid transparent; + width: 16px !important; + height: 16px !important; + -webkit-animation: spin 1s linear infinite; /* Safari */ + animation: spin 1s linear infinite; +} + +@-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.formErrors{ + font-family: $primaryFontFamily; + font-size: 14px; + color: #e74c3c; + margin-left: 6px; + margin-top: 6px; + max-width: 208px; + text-align: center; +} + +.codeTreeCount{ + font-size: 48px; + font-weight: bold; + display: flex; + flex-direction: row; + text-align: center; + align-items: baseline; color: $primaryColor; - border-color: $primaryColor; - border-width: 1px; - border-style: solid; + > span { + font-size: 20px; + margin-left: 6px; + } +} + +.plantedBy{ + color: $primaryFontColor; + text-align: center; + > span { + font-style: italic; + } + > p { + font-weight: bold; + } +} + + +.header { + display: flex; + flex-direction: row; + align-items: center; +} + +.headerCloseIcon { + height: 32px; + width: 32px; + justify-content: center; + align-items: center; + display: flex; +} +.headerCloseIcon:hover { + cursor: pointer; +} +.headerTitle { + font-family: $primaryFontFamily; + font-weight: bold; + font-size: 27px; + color: $primaryFontColor; + margin-left: 10px; +} + + +.thankyouImageContainer { + display: flex; + justify-content: center; +} +.thankyouImage { + background-image: url('/tenants/planet/images/donations/donationCompletion.jpg'); + background-position: center; + background-size: cover; + border-radius: 7px; + margin-top: 20px; + margin-bottom: 20px; + width: 280px; + height: 280px; + align-self: center; + position: relative; + display: flex; + justify-content: center; +} + +.tempThankYouImage { + background-image: url('/tenants/planet/images/donations/donationCompletion.jpg'); + background-position: center; + background-size: cover; + width: 2300px; + height: 2300px; + align-self: center; + position: relative; + display: flex; + justify-content: center; +} + + +.tempDonationCount{ + font-size: 130px; + font-weight: 600; + text-align: center; + position: absolute; + bottom: 300px; + padding: 0px 80px; + color: white; + font-variant-ligatures: none; +} + + +.tempDonationTenant{ + font-size: 75px; + font-weight: 400; + font-style: italic; + text-align: center; + position: absolute; + bottom: 60px; + color: white; + font-variant-ligatures: none; +} + +.donationCount { + font-size: 22px; + font-weight: 600; + text-align: center; + position: absolute; + bottom: 20px; + color: white; + font-variant-ligatures: none; +} + +.donationTenant { + font-size: 13px; + font-weight: 400; + font-style: italic; + text-align: center; + margin-top: 16px; + color: white; + font-variant-ligatures: none; +} + +.pfpLogo { + position: absolute; + right: 12px; + top: 12px; + height: 36px; + width: 36px; + background-color: white; + border-radius: 36px; + padding: 4px; +} + +.shareRow { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + position: relative; + top: -30px; + width: fit-content; + left: 50%; + padding: 5px 10px; + border-radius: 30px; + transform: translate(-50%, 0%); + background-color: white; + box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.12); + z-index: 2000; +} + +.shareIcon { + padding: 4px 4px; + // background-color: blue; + border-radius: 30px; + margin: 0px 5px; +} + +.buttonsContainer { + display: flex; + flex-direction: row; + justify-content: center; + z-index: 10; + :hover { + cursor: pointer; + } +} +.downloadButton { + border-radius: 40px; + height: 48px; + padding: 12px 36px; + display: flex; + justify-content: center; + align-items: center; + z-index: 10; + background-color: $primaryColor; + color: white; font-family: $primaryFontFamily; - font-size: $fontSubTitleSize; font-weight: 600; - margin-top: 30px; + :hover { + cursor: pointer; + } } -.continueButton:hover{ - cursor: pointer; + +@include smTabletView { + +.speedDialButton:focus { + outline: none; +} + .container { + width: 412px; + } + .thankyouImage { + width: 372px; + height: 372px; + } } + +:export { + blueishGrey: '#f2f2f7'; +} + +.thankyouImageHeader { + font-size: 20px; + font-weight: 600; + text-align: center; + color: white; + font-variant-ligatures: none; + padding-top: 15%; + white-space: pre-line; +} + +.thankyouImageHeader p h1{ + font-size: 34px; + font-weight: bolder; +} + +.tempthankyouImageHeader{ + font-size: 100px; + font-weight: 600; + text-align: center; + width: 100%; + padding-top: 17%; + color: white; + font-variant-ligatures: none; + white-space: pre-line; +} + +.tempthankyouImageHeader p h1 { + font-size: 150px; +} + +.shareOptions{ + > div { + z-index: 2000; + top: 0px; + } +} \ No newline at end of file From fcede17461d96ed4d0503929822b73309fa6ce3a Mon Sep 17 00:00:00 2001 From: Harsh Vitra Date: Thu, 3 Dec 2020 16:24:51 +0530 Subject: [PATCH 2/5] Translations --- public/static/locales/en/common.json | 3 ++- public/static/locales/en/redeem.json | 6 +++++ .../UserProfile/components/RedeemModal.tsx | 23 +++++++++++-------- 3 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 public/static/locales/en/redeem.json diff --git a/public/static/locales/en/common.json b/public/static/locales/en/common.json index d40ee960e1..57ffe13edc 100644 --- a/public/static/locales/en/common.json +++ b/public/static/locales/en/common.json @@ -42,5 +42,6 @@ "verifyEmailHeader":"Please confirm your email.", "verifyEmailText":"To secure your account, we need to verify your email. Please check your inbox or spam/junk folder for a confirmation email and then continue to login.", "verifyEmailInfo":"If you didn’t receive an email please try logging in again and we’ll send you another email.", - "continueToLogin":"Continue to Login" + "continueToLogin":"Continue to Login", + "plantedBy":"Planted by" } \ No newline at end of file diff --git a/public/static/locales/en/redeem.json b/public/static/locales/en/redeem.json new file mode 100644 index 0000000000..9c527a308d --- /dev/null +++ b/public/static/locales/en/redeem.json @@ -0,0 +1,6 @@ +{ + "myPlantedTreesByOrg": "My {{treeCount}} trees will be planted by {{tpoName}}", + "congratulations":"Congratulations!", + "addToMyTrees":"Add to my trees", + "validateCode":"Validate Code" +} \ No newline at end of file diff --git a/src/features/user/UserProfile/components/RedeemModal.tsx b/src/features/user/UserProfile/components/RedeemModal.tsx index 670346be45..67556c602e 100644 --- a/src/features/user/UserProfile/components/RedeemModal.tsx +++ b/src/features/user/UserProfile/components/RedeemModal.tsx @@ -20,7 +20,7 @@ export default function RedeemModal({ redeemModalOpen, handleRedeemModalClose, }: any) { - const { t } = useTranslation(['me', 'common', 'donate']); + const { t } = useTranslation(['me', 'common', 'donate','redeem']); const config = tenantConfig(); @@ -82,7 +82,8 @@ export default function RedeemModal({ } if (!isLoading && isAuthenticated) { let token = await getAccessTokenSilently(); - postAuthenticatedRequest('/api/v1.3/en/validateCode', submitData, token).then((res) => { + let userLang = localStorage.getItem('language') || 'en'; + postAuthenticatedRequest(`/api/v1.3/${userLang}/validateCode`, submitData, token).then((res) => { if (res.code === 401) { setErrorMessage(res.message); setIsUploadingData(false) @@ -109,7 +110,8 @@ export default function RedeemModal({ } if (!isLoading && isAuthenticated) { let token = await getAccessTokenSilently(); - postAuthenticatedRequest('/api/v1.3/en/convertCode', submitData, token).then((res) => { + let userLang = localStorage.getItem('language') || 'en'; + postAuthenticatedRequest(`/api/v1.3/${userLang}/convertCode`, submitData, token).then((res) => { console.log('Result', res); if (res.code === 401) { setErrorMessage(res.message); @@ -158,7 +160,7 @@ export default function RedeemModal({
- Congratulations! + {t('redeem:congratulations')}
@@ -169,7 +171,7 @@ export default function RedeemModal({

- My {validCodeData.treeCount} trees will be planted by {validCodeData.tpos[0].tpoName} + {t('redeem:myPlantedTreesByOrg', { treeCount: validCodeData.treeCount,tpoName:validCodeData.tpos[0].tpoName })}

{t('donate:plantTreesAtURL', { url: config.tenantURL })}

@@ -184,7 +186,7 @@ export default function RedeemModal({

- My {validCodeData.treeCount} trees will be planted by {validCodeData.tpos[0].tpoName} + {t('redeem:myPlantedTreesByOrg', { treeCount: validCodeData.treeCount,tpoName:validCodeData.tpos[0].tpoName })}

{t('donate:plantTreesAtURL', { url: config.tenantURL })} @@ -219,11 +221,11 @@ export default function RedeemModal({ <>

{validCodeData.treeCount} - trees + {t('common:trees')}
- Planted by + {t('common:plantedBy')}

{validCodeData.tpos[0].tpoName}

@@ -232,7 +234,8 @@ export default function RedeemModal({ {isUploadingData ? (
) : ( - 'Add to my trees' + t('redeem:addToMyTrees') + )} @@ -265,7 +268,7 @@ export default function RedeemModal({ {isUploadingData ? (
) : ( - 'Validate Code' + t('redeem:validateCode') )} From 7f59054cfd2b63a02da4743ad4737c0cf0ca02c5 Mon Sep 17 00:00:00 2001 From: Harsh Vitra Date: Thu, 3 Dec 2020 16:31:18 +0530 Subject: [PATCH 3/5] Plural for trees --- public/static/locales/en/redeem.json | 3 ++- .../UserProfile/components/RedeemModal.tsx | 23 +++---------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/public/static/locales/en/redeem.json b/public/static/locales/en/redeem.json index 9c527a308d..38d6f2af0f 100644 --- a/public/static/locales/en/redeem.json +++ b/public/static/locales/en/redeem.json @@ -1,5 +1,6 @@ { - "myPlantedTreesByOrg": "My {{treeCount}} trees will be planted by {{tpoName}}", + "myPlantedTreesByOrg": "My {{count}} tree will be planted by {{tpoName}}", + "myPlantedTreesByOrg_plural": "My {{count}} trees will be planted by {{tpoName}}", "congratulations":"Congratulations!", "addToMyTrees":"Add to my trees", "validateCode":"Validate Code" diff --git a/src/features/user/UserProfile/components/RedeemModal.tsx b/src/features/user/UserProfile/components/RedeemModal.tsx index 67556c602e..25b0127fed 100644 --- a/src/features/user/UserProfile/components/RedeemModal.tsx +++ b/src/features/user/UserProfile/components/RedeemModal.tsx @@ -34,18 +34,11 @@ export default function RedeemModal({ const sendRef = () => imageRef; const [errorMessage, setErrorMessage] = React.useState() - const [codeValidated, setCodeValidated] = React.useState(false) - const [isUploadingData, setIsUploadingData] = React.useState(false); - const [validCodeData, setValidCodeData] = React.useState(); - const [codeRedeemed, setCodeRedeemed] = React.useState(false); - - const [code, setCode] = React.useState(); - const [textCopiedsnackbarOpen, setTextCopiedSnackbarOpen] = React.useState( false, ); @@ -130,7 +123,6 @@ export default function RedeemModal({ } } - const closeRedeem = () => { setCodeValidated(false); setCodeRedeemed(false); @@ -164,14 +156,13 @@ export default function RedeemModal({ -

- {t('redeem:myPlantedTreesByOrg', { treeCount: validCodeData.treeCount,tpoName:validCodeData.tpos[0].tpoName })} + {t('redeem:myPlantedTreesByOrg', { count: validCodeData.treeCount,tpoName:validCodeData.tpos[0].tpoName })}

{t('donate:plantTreesAtURL', { url: config.tenantURL })}

@@ -186,7 +177,7 @@ export default function RedeemModal({

- {t('redeem:myPlantedTreesByOrg', { treeCount: validCodeData.treeCount,tpoName:validCodeData.tpos[0].tpoName })} + {t('redeem:myPlantedTreesByOrg', { count: validCodeData.treeCount,tpoName:validCodeData.tpos[0].tpoName })}

{t('donate:plantTreesAtURL', { url: config.tenantURL })} @@ -202,7 +193,6 @@ export default function RedeemModal({ />

- {validCodeData.tpos[0].tpoName}

-
{isUploadingData ? (
- ) : ( - t('redeem:addToMyTrees') - - )} + ) : (t('redeem:addToMyTrees'))}
- - - ) : ( <> From 834501b3d6160a700285140c0f1c6cb8ee6944fb Mon Sep 17 00:00:00 2001 From: Norbert Schuler Date: Thu, 3 Dec 2020 12:29:35 +0100 Subject: [PATCH 4/5] added one missing resource --- public/static/locales/en/redeem.json | 3 ++- .../user/UserProfile/components/RedeemModal.tsx | 12 +++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/public/static/locales/en/redeem.json b/public/static/locales/en/redeem.json index 38d6f2af0f..fd2c437589 100644 --- a/public/static/locales/en/redeem.json +++ b/public/static/locales/en/redeem.json @@ -3,5 +3,6 @@ "myPlantedTreesByOrg_plural": "My {{count}} trees will be planted by {{tpoName}}", "congratulations":"Congratulations!", "addToMyTrees":"Add to my trees", - "validateCode":"Validate Code" + "validateCode":"Validate Code", + "enterRedeemCode": "Please enter a code to redeem" } \ No newline at end of file diff --git a/src/features/user/UserProfile/components/RedeemModal.tsx b/src/features/user/UserProfile/components/RedeemModal.tsx index 25b0127fed..e5ef363324 100644 --- a/src/features/user/UserProfile/components/RedeemModal.tsx +++ b/src/features/user/UserProfile/components/RedeemModal.tsx @@ -152,7 +152,7 @@ export default function RedeemModal({
- {t('redeem:congratulations')} + {t('redeem:congratulations')}
@@ -162,7 +162,7 @@ export default function RedeemModal({

- {t('redeem:myPlantedTreesByOrg', { count: validCodeData.treeCount,tpoName:validCodeData.tpos[0].tpoName })} + {t('redeem:myPlantedTreesByOrg', { count: validCodeData.treeCount,tpoName:validCodeData.tpos[0].tpoName })}

{t('donate:plantTreesAtURL', { url: config.tenantURL })}

@@ -177,7 +177,7 @@ export default function RedeemModal({

- {t('redeem:myPlantedTreesByOrg', { count: validCodeData.treeCount,tpoName:validCodeData.tpos[0].tpoName })} + {t('redeem:myPlantedTreesByOrg', { count: validCodeData.treeCount,tpoName:validCodeData.tpos[0].tpoName })}

{t('donate:plantTreesAtURL', { url: config.tenantURL })} @@ -235,7 +235,7 @@ export default function RedeemModal({ @@ -250,9 +250,7 @@ export default function RedeemModal({

{isUploadingData ? (
- ) : ( - t('redeem:validateCode') - )} + ) : (t('redeem:validateCode'))}
)} From 618a39f59c26c24414aa4520e573a2ba1e8774b8 Mon Sep 17 00:00:00 2001 From: Sagar Aryal Date: Thu, 3 Dec 2020 12:51:22 +0100 Subject: [PATCH 5/5] Update RedeemModal.tsx --- src/features/user/UserProfile/components/RedeemModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/user/UserProfile/components/RedeemModal.tsx b/src/features/user/UserProfile/components/RedeemModal.tsx index 25b0127fed..807fcc1d42 100644 --- a/src/features/user/UserProfile/components/RedeemModal.tsx +++ b/src/features/user/UserProfile/components/RedeemModal.tsx @@ -237,7 +237,7 @@ export default function RedeemModal({ value: true, message: 'Please enter a code to redeem.', } - })} name={'code'} placeholder="pp.eco/XADSA-DS-AS" label="" variant="outlined" /> + })} name={'code'} placeholder="XAD-1SA-5F1-A" label="" variant="outlined" /> {errors.code && (