diff --git a/components/Forms/Button.tsx b/components/Forms/Button.tsx index 4f7a25c76..4f0524a65 100644 --- a/components/Forms/Button.tsx +++ b/components/Forms/Button.tsx @@ -6,6 +6,7 @@ interface ButtonProps { style: 'primary' | 'secondary' | 'supertask' | 'danger' | 'link' custom?: string href?: string + imgHref?: string text: string type?: ButtonType locale?: string @@ -13,15 +14,16 @@ interface ButtonProps { disabled?: boolean ariaLabel?: string attributes?: any + alt?: string } type ButtonType = 'submit' | 'reset' | 'button' const BUTTON_STYLES = { primary: - 'text-white bg-[#26374A] hover:bg-[#2B4380] focus:bg-[#0535D2] border-transparent border-[2px]', + 'text-white visited:text-white bg-[#26374A] hover:bg-[#2B4380] focus:bg-[#0535D2] border-transparent border-[2px]', secondary: - 'text-[#2B4380] focus:text-white bg-white hover:bg-[#D7E5F5] focus:bg-[#0535D2] border-[#2B4380] border-[2px]', + 'text-[#2B4380] visited:text-[#2B4380] focus:text-white bg-white hover:bg-[#D7E5F5] focus:bg-[#0535D2] border-[#2B4380] border-[2px]', supertask: 'text-white bg-[#318000] hover:bg-[#1D4D00] focus:bg-[#1D4D00]', danger: 'text-white bg-[#BC3331] hover:bg-[#942826] focus:bg-[#942826]', link: 'text-[#2B4380] hover:text-[#0535D2] focus:text-[#0535D2]n hover:underline focus:underline', @@ -32,6 +34,7 @@ export const Button: React.FC = ({ style, custom = '', href, + imgHref, text, type = 'button', locale, @@ -39,6 +42,7 @@ export const Button: React.FC = ({ disabled, ariaLabel, attributes, + alt = 'Image alt', }) => { const btnStyle = BUTTON_STYLES[style] @@ -46,8 +50,13 @@ export const Button: React.FC = ({ return href ? ( - - {text} + + {imgHref && {alt}} {text} ) : ( @@ -60,6 +69,7 @@ export const Button: React.FC = ({ aria-label={ariaLabel} {...attributes} > + {imgHref && {alt}} {text} ) diff --git a/components/Layout/Header.tsx b/components/Layout/Header.tsx index 4c1760978..7c803be9e 100644 --- a/components/Layout/Header.tsx +++ b/components/Layout/Header.tsx @@ -48,6 +48,8 @@ export function Header({ if (target === 'main') { targetId = router.pathname === '/' + ? topNavProps.skipToMainPath + : router.pathname === '/results' || router.pathname === '/resultats' ? topNavProps.skipToMainPath : topNavProps.skipToFormPath } else { @@ -58,7 +60,8 @@ export function Header({ if (targetElement) { targetElement.scrollIntoView({ behavior: 'smooth' }) } - + console.log('target', target) + console.log('elem', targetElement, 'targetid', targetId) targetElement.setAttribute('tabindex', '-1') targetElement.focus({ preventScroll: true }) } diff --git a/components/ResultsPage/BenefitCard.tsx b/components/ResultsPage/BenefitCard.tsx index 35530981a..27e3317d4 100644 --- a/components/ResultsPage/BenefitCard.tsx +++ b/components/ResultsPage/BenefitCard.tsx @@ -1,7 +1,10 @@ -import Image from 'next/image' +import { Button } from '../Forms/Button' import React from 'react' import { NextStepText } from '../../utils/api/definitions/types' -import { CustomCollapse } from './CustomCollapse' +import { Router, useRouter } from 'next/router' +import { useTranslation } from '../Hooks' +import { WebTranslations } from '../../i18n/web' +import { BenefitKey } from '../../utils/api/definitions/enums' const AA_BUTTON_CLICK_ATTRIBUTE = 'ESDC-EDSC:Canadian OAS Benefits Est. Result card link click' @@ -11,6 +14,7 @@ export const BenefitCard: React.VFC<{ benefitName: string isEligible: boolean future: boolean + liveInCanada: boolean eligibleText: string collapsedDetails: any children: React.ReactNode @@ -27,6 +31,7 @@ export const BenefitCard: React.VFC<{ benefitName, isEligible, future, + liveInCanada, eligibleText, collapsedDetails, children, @@ -37,11 +42,11 @@ export const BenefitCard: React.VFC<{ const eligibleFlag: JSX.Element = ( @@ -49,49 +54,31 @@ export const BenefitCard: React.VFC<{ ) + const router = useRouter() + const tsln = useTranslation() + return (
-
-

+

{benefitName} -

+ {eligibleFlag}
{children}
- {collapsedDetails && - collapsedDetails.map((detail, index) => ( - -

- - ))} {nextStepText.nextStepTitle && (

-

- {nextStepText.nextStepTitle} -

{links && links.map(({ text, url, icon, alt, action }, index) => ( -

-
- {alt} -
+ diff --git a/components/ResultsPage/BenefitCards.tsx b/components/ResultsPage/BenefitCards.tsx index b6e45d429..bfe2097f2 100644 --- a/components/ResultsPage/BenefitCards.tsx +++ b/components/ResultsPage/BenefitCards.tsx @@ -1,7 +1,11 @@ import React from 'react' import { getTranslations } from '../../i18n/api' import { WebTranslations } from '../../i18n/web' -import { ResultKey, BenefitKey } from '../../utils/api/definitions/enums' +import { + ResultKey, + BenefitKey, + ResultReason, +} from '../../utils/api/definitions/enums' import { BenefitResult, NextStepText } from '../../utils/api/definitions/types' import { useTranslation } from '../Hooks' import { BenefitCard } from './BenefitCard' @@ -24,10 +28,18 @@ export const BenefitCards: React.VFC<{ results: BenefitResult[] futureClientResults: any partnerResults: BenefitResult[] -}> = ({ inputAge, results, futureClientResults, partnerResults }) => { + liveInCanada?: boolean + formYears?: any +}> = ({ + inputAge, + results, + futureClientResults, + partnerResults, + liveInCanada, + formYears, +}) => { const tsln = useTranslation() const apiTsln = getTranslations(tsln._language) - const receivingOAS: boolean = results[0]?.cardDetail?.meta?.receiveOAS /** @@ -62,6 +74,10 @@ export const BenefitCards: React.VFC<{ result.eligibility?.result === ResultKey.INCOME_DEPENDENT ) + const almostEligible = results.filter( + (result) => result.eligibility?.reason === ResultReason.LIVING_COUNTRY + ) + const futureClientEligible = flattenArray(futureClientResults) const resultsNotEligible = results.filter((value) => { @@ -70,7 +86,9 @@ export const BenefitCards: React.VFC<{ ) return ( - value.eligibility?.result === ResultKey.INELIGIBLE && !inFutureEligible + value.eligibility?.result === ResultKey.INELIGIBLE && + !inFutureEligible && + value.eligibility?.reason !== ResultReason.LIVING_COUNTRY ) }) @@ -109,20 +127,44 @@ export const BenefitCards: React.VFC<{ // get... code below was moved to another file to make it a bit cleaner if (benefitKey === BenefitKey.gis) { - getGisNextSteps(result, receivingOAS, nextStepText, apiTsln, tsln) + getGisNextSteps( + result, + receivingOAS, + liveInCanada, + nextStepText, + apiTsln, + tsln + ) } else if (benefitKey === BenefitKey.oas) { getOasNextSteps( result, inputAge, receivingOAS, + liveInCanada, nextStepText, apiTsln, tsln ) } else if (benefitKey === BenefitKey.alw) { - getAlwNextSteps(result, inputAge, nextStepText, apiTsln, tsln) + getAlwNextSteps( + result, + partnerResults, + inputAge, + liveInCanada, + nextStepText, + apiTsln, + tsln + ) } else if (benefitKey === BenefitKey.alws) { - getAlwsNextSteps(result, inputAge, nextStepText, apiTsln, tsln) + getAlwsNextSteps( + result, + inputAge, + liveInCanada, + formYears, + nextStepText, + apiTsln, + tsln + ) } nextStepText.nextStepContent = replaceTextVariables( @@ -164,11 +206,14 @@ export const BenefitCards: React.VFC<{ result.eligibility.result === ResultKey.ELIGIBLE || result.eligibility.result === ResultKey.INCOME_DEPENDENT - const eligibleText = eligibility - ? future - ? apiTsln.result.willBeEligible - : apiTsln.result.eligible - : apiTsln.result.ineligible + const eligibleText = + result.benefitKey !== BenefitKey.oas && + (result.eligibility.reason === ResultReason.LIVING_COUNTRY || + result.eligibility.reason === ResultReason.PARTNER) + ? apiTsln.result.almostEligible + : eligibility + ? apiTsln.result.eligible + : apiTsln.result.ineligible const nextStepText = getNextStepText(result.benefitKey, result) @@ -181,6 +226,10 @@ export const BenefitCards: React.VFC<{ benefitName={titleText} isEligible={eligibility} future={future} + liveInCanada={ + result.eligibility.reason === ResultReason.LIVING_COUNTRY || + result.eligibility.reason === ResultReason.PARTNER + } eligibleText={eligibleText} nextStepText={nextStepText} collapsedDetails={collapsedDetails} @@ -199,8 +248,8 @@ export const BenefitCards: React.VFC<{ __html: result.cardDetail.mainText, }} /> -
{OASdeferralTable}
-
{oasApply(result.benefitKey, result)}
+ {/*
{OASdeferralTable}
*/} + {/*
{oasApply(result.benefitKey, result)}
*/}
) @@ -222,6 +271,11 @@ export const BenefitCards: React.VFC<{ )} + {almostEligible.length > 0 && ( + <> + <>{almostEligible.map((result) => generateCard(result))} + + )} {resultsNotEligible.length > 0 && ( <> <>{resultsNotEligible.map((result) => generateCard(result))} diff --git a/components/ResultsPage/CTA.tsx b/components/ResultsPage/CTA.tsx index fa072de85..d764c19b2 100644 --- a/components/ResultsPage/CTA.tsx +++ b/components/ResultsPage/CTA.tsx @@ -33,9 +33,9 @@ export function CTA({
-

+

{heading} -

+

{body}

+ + + + + )} ) } diff --git a/components/ResultsPage/Estimation.tsx b/components/ResultsPage/Estimation.tsx new file mode 100644 index 000000000..2c28f1bd6 --- /dev/null +++ b/components/ResultsPage/Estimation.tsx @@ -0,0 +1,394 @@ +import { useRouter } from 'next/router' +import { getTranslations, numberToStringCurrency } from '../../i18n/api' +import { WebTranslations } from '../../i18n/web' +import { Language, ResultKey } from '../../utils/api/definitions/enums' +import { + BenefitResult, + BenefitResultsObject, +} from '../../utils/api/definitions/types' +import { useTranslation } from '../Hooks' +import { EstimatedTotalItem } from './EstimatedTotalItem' + +export const Estimation: React.VFC<{ + partner + resultObject + resultArray + age + maritalStatus + partnerReceiving + involSep + isSecondEstimate +}> = ({ + partner, + resultObject, + resultArray, + age, + maritalStatus, + partnerReceiving, + involSep, + isSecondEstimate, +}) => { + const tsln = useTranslation() + const apiTrans = getTranslations(tsln._language) + const roundedAge = Math.trunc(Number(age)) + age = Math.round(age) + + const language = useRouter().locale as Language + + const benefitAge = Object.keys(resultObject)[0] + const benefitType = Object.keys(resultObject[Object.keys(resultObject)[0]]) + + let estimateIsSame = false + + const showPartnerAmounts = () => { + if (!partner) return true + else if (roundedAge < 65) { + return true + } else { + return partner && partnerReceiving + } + } + + const benefitObject: BenefitResultsObject = + resultObject[Object.keys(resultObject)[0]] + + const resultsArray: BenefitResult[] = Object.keys( + resultObject[benefitAge] + ).map((value) => resultObject[benefitAge][value]) + + let eligible = resultsArray.filter( + (result) => + result.eligibility?.result === ResultKey.ELIGIBLE || + result.eligibility?.result === ResultKey.INCOME_DEPENDENT + ) + + const eligibleTotalAmount = eligible.reduce( + (sum, obj) => sum + obj.entitlement.result, + 0 + ) + + let benefitResultArray: BenefitResult[] = [] + for (const prop in resultArray) { + if (prop) { + if (resultArray[prop]) { + benefitResultArray.push( + resultArray[prop][Object.keys(resultArray[prop])[0]] + ) + } + } + } + + const isFirstOasGis = () => { + let isFirst = false + + for (let i = 0; i < benefitResultArray.length; i++) { + const obj = benefitResultArray[i] + if ( + Object.keys(obj[Object.keys(obj)[0]]).includes('oas') || + Object.keys(obj[Object.keys(obj)[0]]).includes('gis') + ) { + // Check if it's the same object + if (JSON.stringify(obj) === JSON.stringify(resultObject)) { + isFirst = true + } + break + } + } + return isFirst + } + + const isLastOasGis = () => { + let isLast = false + + for (let i = benefitResultArray.length - 1; i >= 0; i--) { + const obj = benefitResultArray[i] + if ( + Object.keys(obj[Object.keys(obj)[0]]).includes('oas') || + Object.keys(obj[Object.keys(obj)[0]]).includes('gis') + ) { + // Check if it's the same object + if (JSON.stringify(obj) === JSON.stringify(resultObject)) { + isLast = true + } + break + } + } + + return isLast + } + + //BUILD THE SUMMARY STRINGS FOR EACH BENFIT + const buildSummaryString = () => { + let text = '' + + age = benefitAge == '0' ? age : benefitAge + let isPartnerStr = partner + ? apiTrans.detail.yourPartner + : apiTrans.detail.you + + const displayAge = Math.trunc(Number(age)) + const firstOasGis = isFirstOasGis() + const lastOasGis = isLastOasGis() + + const eligibleAmt = numberToStringCurrency(eligibleTotalAmount, language) + + const arrayOfBen = benefitResultArray + + //ALW & ALWS + if (benefitType.includes('alw') || benefitType.includes('alws')) { + //CURRENT ELIGIBLE + if (benefitAge == '0') { + text = `${apiTrans.detail.youCouldReceiveUntil} 65${ + language == 'fr' ? ' ans' : '' + },` + } + //FUTURE ELIGIBLE + else { + text = `${apiTrans.detail.youCouldReceiveFrom} ${displayAge} ${ + apiTrans.detail.youCouldReceiveTo + } 65${language == 'fr' ? ' ans' : ''},` + } + text += ` ${isPartnerStr} ${apiTrans.detail.youCouldReceive} ${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}:` + } + //OAS AND GIS BENEFIT + else { + //FIRST OAS AND GIS + if (firstOasGis) { + //IS CURRENTLY AVAILABLE + if (benefitAge == '0') { + //FIRST AND LAST + //I1 + if (lastOasGis) { + text += `${ + partner ? apiTrans.detail.yourPartner : apiTrans.detail.you + } ${apiTrans.detail.youCouldReceive}${ + showPartnerAmounts() + ? ` ${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}` + : '' + }:` + } + //FIRST NOT LAST + else { + const nextBenefitResult = + arrayOfBen[arrayOfBen.indexOf(resultObject) + 1] + + //have to get the GIS before hand because when user not in Canada, GIS is null + const gis = + nextBenefitResult[Object.keys(nextBenefitResult)[0]]['gis'] + + const nextBenefitTotal = + nextBenefitResult[Object.keys(nextBenefitResult)[0]]['oas'] + .entitlement.result + (gis ? gis.entitlement.result : 0) + + const nextBenefitAge = Object.keys(nextBenefitResult)[0] + + //IS NEXT RESULT THE SAME + //I1 & I2 + if (eligibleTotalAmount !== nextBenefitTotal) { + text = `${ + apiTrans.detail.youCouldReceiveUntil + } ${nextBenefitAge}${language == 'fr' ? ' ans' : ''}, ${ + partner ? apiTrans.detail.yourPartner : apiTrans.detail.you + } ${apiTrans.detail.youCouldReceive}${ + showPartnerAmounts() + ? ` ${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}` + : '' + }:` + } + text = `${ + partner ? apiTrans.detail.yourPartner : apiTrans.detail.you + } ${apiTrans.detail.youCouldReceive}${ + showPartnerAmounts() + ? ` ${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}` + : '' + }:` + } + } + + //FUTURE + else { + //LAST ESTIMATE + if (lastOasGis) { + //I5 + text = `${apiTrans.detail.youCouldStartReceivingAt} ${displayAge}${ + language == 'fr' ? ' ans' : '' + }, ${partner ? apiTrans.detail.yourPartner : apiTrans.detail.you} ${ + apiTrans.detail.youCouldStartReceiving + }${ + showPartnerAmounts() + ? ` ${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}` + : '' + }:` + } + //NOT LAST + else { + const nextBenefitResult = + arrayOfBen[arrayOfBen.indexOf(resultObject) + 1] + + //have to get the GIS before hand because when user not in Canada, GIS is null + const gis = + nextBenefitResult[Object.keys(nextBenefitResult)[0]]['gis'] + + const nextBenefitTotal = + nextBenefitResult[Object.keys(nextBenefitResult)[0]]['oas'] + .entitlement.result + (gis ? gis.entitlement.result : 0) + + const nextBenefitAge = Math.trunc( + Number(Object.keys(nextBenefitResult)[0]) + ) + + //NEXT SAME + if (eligibleTotalAmount == nextBenefitTotal) { + text = `${ + apiTrans.detail.youCouldStartReceivingAt + } ${displayAge}${language == 'fr' ? ' ans' : ''}, ${ + partner ? apiTrans.detail.yourPartner : apiTrans.detail.you + } ${apiTrans.detail.youCouldStartReceiving}${ + showPartnerAmounts() + ? ` ${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}` + : '' + }:` + } + //NEXT NOT SAME + else { + text = `${apiTrans.detail.youCouldReceiveFrom} ${displayAge} ${ + apiTrans.detail.youCouldReceiveTo + } ${nextBenefitAge}${language == 'fr' ? ' ans' : ''},` + text += ` ${isPartnerStr} ${apiTrans.detail.youCouldReceive}${ + showPartnerAmounts() + ? ` ${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}` + : '' + }:` + } + } + } + } + //NOT FIRST + else { + const previousBenefitResult = + arrayOfBen[arrayOfBen.indexOf(resultObject) - 1] + + //have to get the GIS before hand because when user not in Canada, GIS is null + const gis = + previousBenefitResult[Object.keys(previousBenefitResult)[0]]['gis'] + + const previousBenefitTotal = + previousBenefitResult[Object.keys(previousBenefitResult)[0]]['oas'] + .entitlement.result + (gis ? gis.entitlement.result : 0) + + //PREVIOUS THE SAME + if (previousBenefitTotal === eligibleTotalAmount) { + estimateIsSame = true + text = !isSecondEstimate + ? `${ + partner + ? apiTrans.detail.yourEstimateIsStillPartner + : apiTrans.detail.yourEstimateIsStill + } ${ + showPartnerAmounts() + ? `${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}` + : apiTrans.detail.theSame + }.` + : '' + } + //PREVIOUS NOT THE SAME + else { + //IS LAST + if (lastOasGis) { + text = `${apiTrans.detail.youCouldStartReceivingAt} ${displayAge}${ + language == 'fr' ? ' ans' : '' + }, ${partner ? apiTrans.detail.yourPartner : apiTrans.detail.you} ${ + apiTrans.detail.youCouldContinueReceiving + }${ + showPartnerAmounts() + ? ` ${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}` + : '' + }:` + } else { + const nextBenefitResult = + arrayOfBen[arrayOfBen.indexOf(resultObject) + 1] + + //have to get the GIS before hand because when user not in Canada, GIS is null + const gis = + nextBenefitResult[Object.keys(nextBenefitResult)[0]]['gis'] + + const nextBenefitTotal = + nextBenefitResult[Object.keys(nextBenefitResult)[0]]['oas'] + .entitlement.result + (gis ? gis.entitlement.result : 0) + + const nextBenefitAge = Object.keys(nextBenefitResult)[0] + + if (eligibleTotalAmount == nextBenefitTotal) { + text = `${ + apiTrans.detail.youCouldStartReceivingAt + } ${displayAge}${language == 'fr' ? ' ans' : ''}, ${ + partner ? apiTrans.detail.yourPartner : apiTrans.detail.you + } ${apiTrans.detail.youCouldContinueReceiving}${ + showPartnerAmounts() + ? ` ${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}` + : '' + }:` + } else { + text = `${apiTrans.detail.youCouldReceiveFrom} ${displayAge} ${ + apiTrans.detail.youCouldReceiveTo + } ${Math.trunc(Number(nextBenefitAge))}${ + language == 'fr' ? ' ans' : '' + },` + text += ` ${isPartnerStr} ${ + apiTrans.detail.youCouldContinueReceiving + }${ + showPartnerAmounts() + ? ` ${eligibleAmt} ${apiTrans.detail.youCouldReceivePerMonth}` + : '' + }:` + } + } + } + } + } + return text.charAt(0).toUpperCase() + text.slice(1) + } + + return ( +
+ +

+ + +

    + {!estimateIsSame && + eligible.map((benefit, index) => ( + + ))} +
+ {isFirstOasGis() && + eligible.map((benefit, index) => { + if ( + benefit.cardDetail.meta.residency && + benefit.entitlement.result > 0 + ) { + return ( +

+ {language == 'en' + ? `This estimate is based on ${benefit.cardDetail.meta.residency} years of Canadian residence.` + : `Cette estimation est basée sur ${benefit.cardDetail.meta.residency} années de résidence canadienne.`} +

+ ) + } + })} +
+ ) +} diff --git a/components/ResultsPage/Intro.tsx b/components/ResultsPage/Intro.tsx new file mode 100644 index 000000000..95c274142 --- /dev/null +++ b/components/ResultsPage/Intro.tsx @@ -0,0 +1,55 @@ +import { useRouter } from 'next/router' +import { WebTranslations } from '../../i18n/web' +import { Language } from '../../utils/api/definitions/enums' +import { useTranslation } from '../Hooks' + +export const Intro: React.VFC<{ + hasPartner: boolean + userAge: number + estimateLength: number + hasMultipleOasGis: boolean + alreadyReceiving: boolean +}> = ({ + hasPartner, + userAge, + estimateLength, + hasMultipleOasGis, + alreadyReceiving, +}) => { + const tsln = useTranslation() + const language = useRouter().locale as Language + + return ( + <> +

+

{tsln.resultsPage.yourMonEstimateHeading}

+ {estimateLength == 1 && ( +

{tsln.resultsPage.changeInSituation}

+ )} + {estimateLength > 1 && !hasPartner && ( +

+ {tsln.resultsPage.youEstimateMayChange}{' '} + {tsln.resultsPage.basedYourAge}. {tsln.resultsPage.changeInSituation} +

+ )} + {estimateLength > 1 && hasPartner && ( +
+

{tsln.resultsPage.yourEstimateMayChangeList}

+
    +
  • + {tsln.resultsPage.basedYourAge} + {language == 'fr' ? ';' : ''} +
  • +
  • {tsln.resultsPage.basedYourPartner}
  • +
+

+ {userAge < 70 && hasMultipleOasGis && !alreadyReceiving + ? tsln.resultsPage.ifYouChoseToDefer + : ''}{' '} + {tsln.resultsPage.changeInSituation} +

+
+ )} + + ) +} diff --git a/components/ResultsPage/MayBeEligible.tsx b/components/ResultsPage/MayBeEligible.tsx deleted file mode 100644 index 28128d6ee..000000000 --- a/components/ResultsPage/MayBeEligible.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import Image from 'next/image' -import { getTranslations } from '../../i18n/api' -import { WebTranslations } from '../../i18n/web' -import { BenefitResult } from '../../utils/api/definitions/types' -import { useTranslation } from '../Hooks' - -export const MayBeEligible: React.VFC<{ - resultsEligible: BenefitResult[] - partner?: boolean -}> = ({ resultsEligible, partner = false }) => { - const tsln = useTranslation() - const apiTrans = getTranslations(tsln._language) - const isEligible: boolean = resultsEligible.length > 0 - - // Do nothing if eligible - if (isEligible) return null - - // Displays only when not eligible - return ( - <> -

-
- {' '} -
-
- {isEligible - ? tsln.resultsPage.youMayBeEligible - : partner - ? tsln.resultsPage.partnerNotEligible - : tsln.resultsPage.youAreNotEligible} -
-

-
-

-

    - {resultsEligible.map((benefit) => ( -
  • - {apiTrans.benefit[benefit.benefitKey]} -
  • - ))} -
-
- - ) -} diff --git a/components/ResultsPage/Modal.tsx b/components/ResultsPage/Modal.tsx new file mode 100644 index 000000000..1e7fb62dc --- /dev/null +++ b/components/ResultsPage/Modal.tsx @@ -0,0 +1,125 @@ +import { Button } from '../Forms/Button' +import { useEffect, useRef } from 'react' +import { getTranslations } from '../../i18n/api' +import { WebTranslations } from '../../i18n/web' +import { MaritalStatus } from '../../utils/api/definitions/enums' +import { useTranslation } from '../Hooks' + +export const Modal: React.VFC<{ + isOpen + onClose + partner + maritalStatus + benefitName + involSep +}> = ({ isOpen, onClose, partner, maritalStatus, benefitName, involSep }) => { + const tsln = useTranslation() + const apiTrans = getTranslations(tsln._language) + const modalRef = useRef(null) + + // ########## Block start ########## + + useEffect(() => { + if (isOpen) { + // Focus the first focusable element when the modal opens + const firstFocusableRef = modalRef.current.querySelectorAll('button') + firstFocusableRef[0].focus() + + // Event listener for trapping focus + const handleKeyDown = (event) => { + if (event.key === 'Tab') { + const focusableElements = modalRef.current.querySelectorAll( + 'button, a, input, textarea, select, [tabindex]:not([tabindex="-1"])' + ) + + const firstElement = focusableElements[0] + const lastElement = focusableElements[focusableElements.length - 1] + + if (event.shiftKey && document.activeElement === firstElement) { + // Shift + Tab pressed on first element: move focus to the last element + event.preventDefault() + lastElement.focus() + } else if ( + !event.shiftKey && + document.activeElement === lastElement + ) { + // Tab pressed on the last element: move focus to the first element + event.preventDefault() + firstElement.focus() + } + } + if (event.key === 'Escape') { + onClose() + } + } + + document.addEventListener('keydown', handleKeyDown) + + return () => document.removeEventListener('keydown', handleKeyDown) + } + }, [isOpen]) + + // ########## Block End ########## + + if (!isOpen) return null + + const getModalString = () => { + let text = '' + + if (partner) { + text = + maritalStatus === MaritalStatus.PARTNERED + ? benefitName === tsln.oas + ? apiTrans.modal.partnerIncomeTooHigh + : involSep == 'true' + ? apiTrans.modal.partnerIncomeTooHigh + : apiTrans.modal.partnerCoupleIncomeTooHigh + : apiTrans.modal.partnerIncomeTooHigh + } else { + text = + maritalStatus === MaritalStatus.PARTNERED + ? benefitName === tsln.oas + ? apiTrans.modal.userIncomeTooHigh + : involSep == 'true' + ? apiTrans.modal.userIncomeTooHigh + : apiTrans.modal.userCoupleIncomeTooHigh + : apiTrans.modal.userIncomeTooHigh + } + + return text + } + + return ( +
+
+
+

+ {!partner + ? apiTrans.modal.userHeading + : apiTrans.modal.partnerHeading} +

+

{getModalString()}

+
+
+
+
+ ) +} diff --git a/components/ResultsPage/NextSteps.tsx b/components/ResultsPage/NextSteps.tsx index 1012ede41..3d55749d3 100644 --- a/components/ResultsPage/NextSteps.tsx +++ b/components/ResultsPage/NextSteps.tsx @@ -2,114 +2,54 @@ import legalValues from '../../utils/api/scrapers/output' import { ResultKey, ResultReason } from '../../utils/api/definitions/enums' import { Translations, numberToStringCurrency } from '../../i18n/api' import { WebTranslations } from '../../i18n/web' -import { NextStepText } from '../../utils/api/definitions/types' +import { + BenefitResultsObject, + NextStepText, +} from '../../utils/api/definitions/types' export function getOasNextSteps( result: any, inputAge: number, receivingOAS: boolean, + liveInCanada: boolean, nextStepText: NextStepText, apiTsln: Translations, tsln: WebTranslations ) { if (result.eligibility.result === ResultKey.ELIGIBLE) { nextStepText.nextStepTitle = tsln.resultsPage.nextStepTitle - - if (result.entitlement.clawback > 0) { - if (!receivingOAS && inputAge > 64) { - nextStepText.nextStepContent += `${apiTsln.detail.oas.youShouldHaveReceivedLetter} ${apiTsln.detail.oas.applyOnline}` - } - - if ( - result.eligibility.reason === ResultReason.AGE_70_AND_OVER && - !receivingOAS - ) { - nextStepText.nextStepContent += `

${apiTsln.detail.oas.over70}

` - } - - //code for future --start-- - if (inputAge < 64) { - nextStepText.nextStepContent += apiTsln.detail.oas.youWillReceiveLetter - } else if (inputAge === 64) { - nextStepText.nextStepContent += `${apiTsln.detail.oas.youShouldHaveReceivedLetter} ${apiTsln.detail.oas.ifYouDidnt}` - } else if ( - (result.eligibility.reason === ResultReason.AGE_65_TO_69 || - result.eligibility.reason === ResultReason.AGE_70_AND_OVER) && - result.entitlement.result > 0 && - receivingOAS - ) { - //TODO duplicating the code here, will refactor later TODO + if (receivingOAS) { + if (result.entitlement.result === 0) { + nextStepText.nextStepContent += `

${apiTsln.detail.thisEstimateWhenZero}

` + } else if (result.entitlement.result > 0) { nextStepText.nextStepContent += `

${apiTsln.detail.thisEstimate}

` - } else { - !receivingOAS - ? (nextStepText.nextStepContent += `

${apiTsln.detail.oas.serviceCanadaReviewYourPayment}

`) - : '' } - //code for future --end-- - } else if ( - (result.eligibility.reason === ResultReason.AGE_65_TO_69 || - result.eligibility.reason === ResultReason.AGE_70_AND_OVER) && - result.entitlement.result > 0 && - receivingOAS - ) { - nextStepText.nextStepContent += `

${apiTsln.detail.thisEstimate}

` - } else if ( - (result.eligibility.reason === ResultReason.AGE_65_TO_69 || - result.eligibility.reason === ResultReason.AGE_70_AND_OVER || - result.eligibility.reason === ResultReason.INCOME) && - result.entitlement.result === 0 && - receivingOAS - ) { - nextStepText.nextStepContent += `

${apiTsln.detail.thisEstimateWhenZero}

` - } else if (result.eligibility.reason === ResultReason.AGE_65_TO_69) { - //code for future --start-- - if (inputAge < 64) { - nextStepText.nextStepContent += apiTsln.detail.oas.youWillReceiveLetter - } else if (inputAge === 64) { - nextStepText.nextStepContent += `${apiTsln.detail.oas.youShouldHaveReceivedLetter} ${apiTsln.detail.oas.ifYouDidnt}` + } else { + if (Math.trunc(inputAge) > 69) { + nextStepText.nextStepContent += `

${apiTsln.detail.oas.over70}

` + nextStepText.nextStepContent += `

${ + apiTsln.detail.oas.serviceCanadaReviewYourPayment + } ${ + result.entitlement.result === 0 + ? apiTsln.detail.oas.automaticallyBePaid + : '' + }

` } else { - // default when 65-69 - !receivingOAS - ? (nextStepText.nextStepContent += `${apiTsln.detail.oas.youShouldHaveReceivedLetter} ${apiTsln.detail.oas.applyOnline}`) - : '' - } - //code for future --end-- - } else if ( - result.eligibility.reason === ResultReason.AGE_70_AND_OVER && - receivingOAS - ) { - nextStepText.nextStepContent += `

${apiTsln.detail.thisEstimate}

` - } else if ( - result.eligibility.reason === ResultReason.AGE_70_AND_OVER && - !receivingOAS - ) { - nextStepText.nextStepContent += apiTsln.detail.oas.over70 - } else if (result.entitlement.clawback === 0) { - //code for future --start-- - if (inputAge < 64) { - nextStepText.nextStepContent += apiTsln.detail.oas.youWillReceiveLetter - } else if (inputAge === 64) { - nextStepText.nextStepContent += `${apiTsln.detail.oas.youShouldHaveReceivedLetter} ${apiTsln.detail.oas.ifYouDidnt}` - } else { - !receivingOAS - ? (nextStepText.nextStepContent += `${apiTsln.detail.oas.serviceCanadaReviewYourPayment}`) - : '' + // nextStepText.nextStepContent += `${apiTsln.detail.oas.youWillReceiveLetter}` + // ifYouDidnt: + if (inputAge < 65) { + nextStepText.nextStepContent += `${apiTsln.detail.oas.youWillReceiveLetter}` + } else if (inputAge > 64 && inputAge < 70) { + nextStepText.nextStepContent += `${apiTsln.detail.oas.shouldReceive65to69}` + } + nextStepText.nextStepContent += `

${apiTsln.detail.futureDeferralOptions}

` + nextStepText.nextStepContent += `

${apiTsln.detail.youCanAply}

` - result.eligibility.reason === ResultReason.INCOME - ? (nextStepText.nextStepContent += - ' ' + apiTsln.detail.oas.automaticallyBePaid) - : '' + if (result.entitlement.result == 0) { + nextStepText.nextStepContent += `

${apiTsln.detail.onceEnrolled}

` + } } - //code for future --end-- } - } else if ( - result.eligibility.result === ResultKey.INELIGIBLE && - result.eligibility.reason === ResultReason.AGE_YOUNG_64 - ) { - nextStepText.nextStepTitle = tsln.resultsPage.nextStepTitle - nextStepText.nextStepContent += - apiTsln.detail.oas.youShouldHaveReceivedLetter - nextStepText.nextStepContent += ` ${apiTsln.detail.oas.ifNotReceiveLetter64}` } return nextStepText @@ -118,34 +58,31 @@ export function getOasNextSteps( export function getGisNextSteps( result: any, receivingOAS: boolean, + liveInCanada: boolean, nextStepText: NextStepText, apiTsln: Translations, tsln: WebTranslations ) { - if ( - result.eligibility.result === ResultKey.ELIGIBLE || - result.eligibility.result === ResultKey.INCOME_DEPENDENT - ) { + if (result.eligibility.result === ResultKey.ELIGIBLE) { nextStepText.nextStepTitle = tsln.resultsPage.nextStepTitle - if (result.eligibility.reason === ResultReason.INCOME) { - nextStepText.nextStepContent = tsln.resultsPage.nextStepGis + if (!receivingOAS) { + nextStepText.nextStepContent += `

${apiTsln.detail.gis.youCanApplyGis}

` if (result.entitlement.result === 0) { - if (receivingOAS) { - nextStepText.nextStepContent = apiTsln.detail.gis.ifYouApply - nextStepText.nextStepContent += `

${apiTsln.detail.gis.ifYouAlreadyApplied}

` - } else - nextStepText.nextStepContent += `

${apiTsln.detail.gis.ifYouApply}

` + nextStepText.nextStepContent += `

${apiTsln.detail.gis.ifYouApply}

` + } + } else { + if (result.entitlement.result > 0) { + nextStepText.nextStepContent += `

${apiTsln.detail.gis.canApplyOnline}

` + } else { + nextStepText.nextStepContent += `

${apiTsln.detail.gis.ifYouApply}

` } - } else if (result.entitlement.result > 0 && receivingOAS) { - nextStepText.nextStepContent = - apiTsln.detail.gis.canApplyOnline + - `

${apiTsln.detail.gis.ifYouAlreadyReceive}

` - } else if (result.entitlement.result > 0 && !receivingOAS) { - nextStepText.nextStepContent = tsln.resultsPage.nextStepGis - } else if (result.entitlement.result <= 0 && receivingOAS) { - nextStepText.nextStepContent = apiTsln.detail.gis.ifYouApply nextStepText.nextStepContent += `

${apiTsln.detail.gis.ifYouAlreadyApplied}

` } + } else { + if (result.eligibility.reason === ResultReason.LIVING_COUNTRY) { + nextStepText.nextStepTitle = tsln.resultsPage.nextStepTitle + nextStepText.nextStepContent += `

${apiTsln.detail.mustBeInCanada}

` + } } return nextStepText @@ -154,31 +91,52 @@ export function getGisNextSteps( // export function getAlwNextSteps( result: any, + partnerResults: any, inputAge: number, + liveInCanada: boolean, nextStepText: NextStepText, apiTsln: Translations, tsln: WebTranslations ) { - if (result.eligibility.result === ResultKey.ELIGIBLE) { - const ifYouApplyText = - apiTsln.detail.alwIfYouApply + - `${numberToStringCurrency( - legalValues.alw.alwIncomeLimit, - apiTsln._language, - { rounding: 0 } - )}.` + const ifYouApplyText = + apiTsln.detail.alwIfYouApply + + `${numberToStringCurrency( + legalValues.alw.alwIncomeLimit, + apiTsln._language, + { rounding: 0 } + )}.` + nextStepText.nextStepTitle = tsln.resultsPage.nextStepTitle + const partnerAlw = partnerResults.find((item) => item['benefitKey'] === 'alw') - if (inputAge < 60) { - nextStepText.nextStepTitle = tsln.resultsPage.nextStepTitle + if ( + result.eligibility.result === ResultKey.ELIGIBLE || + result.eligibility.result === ResultKey.WILL_BE_ELIGIBLE + ) { + if (result.entitlement.result > 0) { nextStepText.nextStepContent += apiTsln.detail.alwsApply - if (result.entitlement.result === 0) { - nextStepText.nextStepContent += ifYouApplyText + } else { + if (inputAge < 60) { + nextStepText.nextStepContent += apiTsln.detail.alwsApply } - } else if (result.entitlement.result === 0) { - nextStepText.nextStepTitle = tsln.resultsPage.nextStepTitle - nextStepText.nextStepContent += ifYouApplyText + nextStepText.nextStepContent += `

${ifYouApplyText}

` + } + } else if (result.eligibility.result === ResultKey.INELIGIBLE) { + if (result.eligibility.reason === ResultReason.LIVING_COUNTRY) { + nextStepText.nextStepContent += apiTsln.detail.mustBeInCanada + } else if (result.eligibility.reason === ResultReason.PARTNER) { + if (!liveInCanada) + nextStepText.nextStepContent += apiTsln.detail.mustBeInCanada + else nextStepText.nextStepContent += apiTsln.detail.partnerMustBeEligible + } else { + nextStepText.nextStepContent += `

${apiTsln.detail.alw.forIndividuals}

` + nextStepText.nextStepContent += `
    +
  • ${apiTsln.detail.alw.age60to64}
  • +
  • ${apiTsln.detail.alw.livingInCanada}
  • +
  • ${apiTsln.detail.alw.spouseReceives}
  • +
` } } + // } return nextStepText } @@ -187,31 +145,46 @@ export function getAlwNextSteps( export function getAlwsNextSteps( result: any, inputAge: number, + liveInCanada: boolean, + yearsinCan: any, nextStepText: NextStepText, apiTsln: Translations, tsln: WebTranslations ) { - if (result.eligibility.result === ResultKey.ELIGIBLE) { - const ifYouApplyText = `${ - apiTsln.detail.alwsIfYouApply - }${numberToStringCurrency( + const ifYouApplyText = + apiTsln.detail.alwsIfYouApply + + `${numberToStringCurrency( legalValues.alw.afsIncomeLimit, apiTsln._language, { rounding: 0 } )}.` + nextStepText.nextStepTitle = tsln.resultsPage.nextStepTitle - if (inputAge < 60) { - nextStepText.nextStepTitle = tsln.resultsPage.nextStepTitle - nextStepText.nextStepContent += `${apiTsln.detail.alwsApply}` - - if (result.entitlement.result === 0) { - nextStepText.nextStepContent += ifYouApplyText + if ( + result.eligibility.result === ResultKey.ELIGIBLE || + result.eligibility.result === ResultKey.WILL_BE_ELIGIBLE + ) { + if (result.entitlement.result > 0) { + nextStepText.nextStepContent += apiTsln.detail.alwsApply + } else { + if (Math.trunc(inputAge) < 60 || yearsinCan < 10) { + nextStepText.nextStepContent += apiTsln.detail.alwsApply } - } else if (result.entitlement.result === 0) { - nextStepText.nextStepTitle = tsln.resultsPage.nextStepTitle - nextStepText.nextStepContent += ifYouApplyText + nextStepText.nextStepContent += `

${ifYouApplyText}

` + } + } else if (result.eligibility.result === ResultKey.INELIGIBLE) { + if (result.eligibility.reason === ResultReason.LIVING_COUNTRY) { + nextStepText.nextStepContent += apiTsln.detail.mustBeInCanada + } else { + nextStepText.nextStepContent += `

${apiTsln.detail.alws.forWidowedIndividuals}

` + nextStepText.nextStepContent += `
    +
  • ${apiTsln.detail.alw.age60to64}
  • +
  • ${apiTsln.detail.alw.livingInCanada}
  • +
  • ${apiTsln.detail.alws.haveNotRemarried}
  • +
` } } + // } return nextStepText } diff --git a/components/ResultsPage/SummaryEstimates.tsx b/components/ResultsPage/SummaryEstimates.tsx new file mode 100644 index 000000000..8b99dbe3e --- /dev/null +++ b/components/ResultsPage/SummaryEstimates.tsx @@ -0,0 +1,319 @@ +import { useRouter } from 'next/router' +import { useEffect, useLayoutEffect } from 'react' +import { getTranslations } from '../../i18n/api' +import { WebTranslations } from '../../i18n/web' +import { + BenefitKey, + Language, + ResultKey, +} from '../../utils/api/definitions/enums' +import { BenefitResult } from '../../utils/api/definitions/types' +import { useTranslation } from '../Hooks' +import { CustomCollapse } from './CustomCollapse' +import { DeferralTable } from './DeferralTable' +import { Estimation } from './Estimation' + +export const SummaryEstimates: React.VFC<{ + headings + userResults + partnerResults + userAge + partnerAge + maritalStatus + partnerReceiving + involSep +}> = ({ + headings, + userResults, + partnerResults, + userAge, + partnerAge, + maritalStatus, + partnerReceiving, + involSep, +}) => { + const tsln = useTranslation() + const apiTrans = getTranslations(tsln._language) + + const language = useRouter().locale as Language + + const currentYear = new Date().getFullYear() + + const getDeferralTable = (benefitKey, result, future, key?): any => { + return benefitKey === BenefitKey.oas && + result.eligibility.result === ResultKey.ELIGIBLE && + result.entitlement.result > 0 && + result.cardDetail.meta?.tableData !== null ? ( + + ) : null + } + + let collapsed = [] + + //To remove recovery tax EC + useLayoutEffect(() => { + const element = + document.getElementById( + `collapse-${apiTrans.detailWithHeading.recoveryTaxPartner.heading}` + ) || + document.getElementById( + `collapse-${apiTrans.detailWithHeading.nonResidentTaxPartner.heading}` + ) || + document.getElementById( + `collapse-${apiTrans.detailWithHeading.recoveryTax.heading}` + ) || + document.getElementById( + `collapse-${apiTrans.detailWithHeading.nonResidentTax.heading}` + ) + + const recoveryBoth = + document.getElementById( + `collapse-${apiTrans.detailWithHeading.recoveryTaxBoth.heading}` + ) || + document.getElementById( + `collapse-${apiTrans.detailWithHeading.nonResidentTaxBoth.heading}` + ) + + if (recoveryBoth) { + element?.remove() + } + }) + + return ( + <> + {headings.map((year, index) => { + const userResult = userResults + ? userResults.some((obj) => year in obj) + : null + + const partnerResult = partnerResults + ? partnerResults.filter((elm) => elm).length > 0 + ? partnerResults.some((obj) => year in obj) + : null + : null + let heading + + if (year == apiTrans.detail.currentEligible) { + heading = apiTrans.detail.currentEligible + } else if (index < headings.length - 1) { + heading = year + } else { + heading = + language == 'fr' + ? `${apiTrans.detail.lastYearEligible} ${year}` + : `${year} ${apiTrans.detail.lastYearEligible}` + } + + // Get all results under a year (there can be multiple) + const yearResults = userResult + ? userResults.filter((obj) => obj && year in obj) + : null + + const yearResultsParnter = partnerResult + ? partnerResults.filter((obj) => obj && year in obj) + : null + + //Get the Result Objects: {"eligibility age": {Result}} + const userResultObjects = yearResults + ? yearResults.map((el) => { + return el + }) + : null + + const partnerResultObjects = yearResultsParnter + ? yearResultsParnter.map((el) => { + return el + }) + : null + + let eligible = [] + if (userResultObjects) { + if (userResultObjects.length > 0) { + userResultObjects.forEach((resultObject, index) => { + const benefitItem = resultObject[Object.keys(resultObject)[0]] + const benefitAge = Object.keys(benefitItem)[0] + + const resultsArray: BenefitResult[] = Object.keys( + benefitItem[benefitAge] + ).map((value) => benefitItem[benefitAge][value]) + + const eligibleResults = resultsArray.filter( + (result) => + result.eligibility?.result === ResultKey.ELIGIBLE || + result.eligibility?.result === ResultKey.INCOME_DEPENDENT + ) + + eligibleResults.forEach((item) => { + eligible.push(item) + }) + }) + } + } + + let partnerEli = [] + if (partnerResultObjects) { + if (partnerResultObjects.length > 0) { + partnerResultObjects.forEach((resultObject, index) => { + const benefitItem = resultObject[Object.keys(resultObject)[0]] + const benefitAge = Object.keys(benefitItem)[0] + + const resultsArray: BenefitResult[] = Object.keys( + benefitItem[benefitAge] + ).map((value) => benefitItem[benefitAge][value]) + + const eligibleResults = resultsArray.filter( + (result) => + result.eligibility?.result === ResultKey.ELIGIBLE || + result.eligibility?.result === ResultKey.INCOME_DEPENDENT + ) + + eligibleResults.forEach((item) => { + partnerEli.push(item) + }) + }) + } + } + + eligible = eligible.concat(partnerEli) + + return ( +
+

+ {heading} +

+
+
+ {userResult && + userResultObjects.map((result, index) => { + return ( + 0} + /> + ) + })} + + {partnerResult && + partnerResultObjects.map((result, index) => { + return ( + 0} + /> + ) + })} +
+ {eligible && + eligible.map((benefit: BenefitResult, index) => { + const collapsedDetails = benefit.cardDetail?.collapsedText + + const newCollapsedDetails = [...collapsedDetails] + if (newCollapsedDetails) { + //Find all indexes of deferral options + let indexes = newCollapsedDetails.reduce( + (acc, item, index) => { + if ( + item.heading === + apiTrans.detailWithHeading.yourDeferralOptions.heading + ) + acc.push(index) + return acc + }, + [] + ) + + //While there are still multiple deferral options, remove the first one + while (indexes.length > 1) { + newCollapsedDetails.splice(indexes[0], 1) // Remove the first occurrence + indexes.shift() // Remove the first index from the list + + //Recalculate the index since removing the duplicates + indexes = newCollapsedDetails.reduce( + (acc, item, index) => { + if ( + item.heading === + apiTrans.detailWithHeading.yourDeferralOptions + .heading + ) + acc.push(index) + return acc + }, + [] + ) + } + } + + return ( +
+ {newCollapsedDetails && + newCollapsedDetails.map((detail, index) => { + if (!collapsed.includes(detail.heading)) { + collapsed.push(detail.heading) + return ( + +

+ {getDeferralTable( + benefit.benefitKey, + benefit, + true + ) && + detail.heading === + apiTrans.detailWithHeading + .yourDeferralOptions.heading && + getDeferralTable( + benefit.benefitKey, + benefit, + true, + index + )} + + ) + } + })} +

+ ) + })} +
+ {headings.length > 1 && + index < year.length && + index != headings.length - 1 && ( +
+ )} +
+ ) + })} + + ) +} diff --git a/components/ResultsPage/WillBeEligible.tsx b/components/ResultsPage/WillBeEligible.tsx deleted file mode 100644 index 7714d7cc8..000000000 --- a/components/ResultsPage/WillBeEligible.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import Image from 'next/image' -import { useRouter } from 'next/router' -import { getTranslations, numberToStringCurrency } from '../../i18n/api' -import { WebTranslations } from '../../i18n/web' -import { Language, ResultKey } from '../../utils/api/definitions/enums' -import { BenefitResult } from '../../utils/api/definitions/types' -import { useTranslation } from '../Hooks' -import { EstimatedTotalItem } from './EstimatedTotalItem' - -export const WillBeEligible: React.VFC<{ - futureResults: any - partner?: boolean - partnerNoOAS: boolean - multipleResults: boolean - eligibleOAS: boolean -}> = ({ - futureResults, - partner = false, - partnerNoOAS, - multipleResults, - eligibleOAS, -}) => { - const tsln = useTranslation() - const apiTrans = getTranslations(tsln._language) - const language = useRouter().locale as Language - - const multipleOAS_GIS = - futureResults.filter((obj) => !!obj[Object.keys(obj)[0]]['oas']).length > 1 - - for (let i = futureResults.length - 1; i >= 0; i--) { - if (i > 0) { - if ( - Object.values(Object.values(Object.values(futureResults)[i])[0]) - .length != 1 - ) { - if ( - Object.values(Object.values(futureResults[i])[0])[0]?.entitlement - .result == - Object.values(Object.values(futureResults[i - 1])[0])[0] - ?.entitlement.result && - Object.values(Object.values(futureResults[i])[0])[1]?.entitlement - .result == - Object.values(Object.values(futureResults[i - 1])[0])[1] - ?.entitlement.result - ) { - futureResults.pop() - } - } - } - } - - return ( - <> -

- - {partner - ? tsln.resultsPage.partnerFutureEligible - : tsln.resultsPage.futureEligible} -

- - {futureResults.map((resultObj, idx) => { - const age = Object.keys(resultObj)[0] - const onlyOASGIS = Object.keys( - resultObj[Object.keys(resultObj)[0]] - ).filter((key) => key === 'oas' || key === 'gis') - - // show if some are non zero - const nonZeroExist = onlyOASGIS.some( - (key) => resultObj[age][key].entitlement?.result > 0 - ) - - // - // an overcomplicated condition for useless information - // - const mainStr = - (multipleOAS_GIS && nonZeroExist && !multipleResults && idx > 0) || - (multipleOAS_GIS && nonZeroExist && multipleResults) || - (multipleOAS_GIS && nonZeroExist && idx > 0) || - eligibleOAS - ? partner - ? apiTrans.detail.partnerContinues - : apiTrans.detail.continueReceiving - : apiTrans.at - - const partnerText = - (multipleOAS_GIS && nonZeroExist && !multipleResults && idx > 0) || - (multipleOAS_GIS && nonZeroExist && multipleResults) || - (multipleOAS_GIS && nonZeroExist && idx > 0) || - eligibleOAS - ? tsln.resultsPage.theyToReceive - : tsln.resultsPage.partnerToReceive - - const text = `${mainStr} ${Math.floor(Number(age))}${apiTrans.atAge} ${ - partner ? partnerText : tsln.resultsPage.toReceive - }` - - const resultsArray: BenefitResult[] = Object.keys(resultObj[age]).map( - (value) => resultObj[age][value] - ) - let eligible = resultsArray.filter( - (result) => - result.eligibility?.result === ResultKey.ELIGIBLE || - result.eligibility?.result === ResultKey.INCOME_DEPENDENT - ) - - // If partner answers "No" to receiving OAS, the amounts should not show - if (partner && partnerNoOAS) { - eligible = eligible.map((benefit) => { - return { - ...benefit, - entitlement: { ...benefit.entitlement, result: 0 }, - } - }) - } - - const eligibleTotalAmount = eligible.reduce( - (sum, obj) => sum + obj.entitlement.result, - 0 - ) - - return ( -
-

- -

    - {eligible.map((benefit) => ( - - ))} -
- {eligible.length > 1 && eligibleTotalAmount > 0 && ( -

- {partner - ? tsln.resultsPage.futurePartnerTotal - : tsln.resultsPage.futureTotal} - - {numberToStringCurrency(eligibleTotalAmount, language)} - - . -

- )} -
- ) - })} - - ) -} diff --git a/components/ResultsPage/YourAnswers.tsx b/components/ResultsPage/YourAnswers.tsx index 1d191da80..941834134 100644 --- a/components/ResultsPage/YourAnswers.tsx +++ b/components/ResultsPage/YourAnswers.tsx @@ -257,8 +257,8 @@ export const YourAnswers: React.VFC<{ return deferralVal === 0 ? `${tsln.no}` : deferralVal > 1 - ? `
${deferralVal} ${tsln.duration.months.toLowerCase()}
` - : `
${deferralVal} ${tsln.resultsPage.month}
` + ? `
${deferralVal} ${tsln.duration.months.toLowerCase()}
` + : `
${deferralVal} ${tsln.resultsPage.month}
` default: throw new Error(`field type not supported in YourAnswers: ${fieldType}`) } diff --git a/components/ResultsPage/index.tsx b/components/ResultsPage/index.tsx index 2ee86c303..96bf52bf6 100644 --- a/components/ResultsPage/index.tsx +++ b/components/ResultsPage/index.tsx @@ -1,9 +1,9 @@ import { useRouter } from 'next/router' import { useRef } from 'react' -import { useSessionStorage } from 'react-use' import { FieldInput } from '../../client-state/InputHelper' import { WebTranslations } from '../../i18n/web' import { + LivingCountry, MaritalStatus, PartnerBenefitStatus, ResultKey, @@ -17,10 +17,17 @@ import { import { Button } from '../Forms/Button' import { useTranslation } from '../Hooks' import { BenefitCards } from './BenefitCards' -import { EstimatedTotal } from './EstimatedTotal' -import { MayBeEligible } from './MayBeEligible' -import { WillBeEligible } from './WillBeEligible' import { YourAnswers } from './YourAnswers' +import { Translations, getTranslations } from '../../i18n/api' +import { SummaryEstimates } from './SummaryEstimates' +import { Intro } from './Intro' + +const getEligibility = ( + resultsEligible: BenefitResult[], + key: string +): boolean => { + return resultsEligible.some((benefit) => benefit.benefitKey === key) +} const ResultsPage: React.VFC<{ inputs: FieldInput[] @@ -40,11 +47,51 @@ const ResultsPage: React.VFC<{ const ref = useRef() const tsln = useTranslation() const router = useRouter() + const apiTsln = getTranslations(tsln._language) const isPartnered = inputs.find((input) => input.key === FieldKey.MARITAL_STATUS)['value'] === MaritalStatus.PARTNERED + const involSep = isPartnered + ? inputs.find((input) => input.key === FieldKey.INV_SEPARATED)['value'] + : null + + const alreadyReceiving = + inputs.find((input) => input.key === FieldKey.ALREADY_RECEIVE_OAS) !== + undefined + ? inputs.find((input) => input.key === FieldKey.ALREADY_RECEIVE_OAS)[ + 'value' + ] + : false + + const yearsinCan = inputs.find( + (input) => input.key === FieldKey.YEARS_IN_CANADA_SINCE_18 + ) + ? inputs.find((input) => input.key === FieldKey.YEARS_IN_CANADA_SINCE_18)[ + 'value' + ] + : null + + const maritalStatus = inputs.find( + (input) => input.key === FieldKey.MARITAL_STATUS + )['value'] + + const userAge = inputs.find((input) => input.key === FieldKey.AGE)['value'] + + const partnerAge = isPartnered + ? inputs.find((input) => input.key === FieldKey.PARTNER_AGE)['value'] + : null + + const partnerReceiving = isPartnered + ? inputs.find((input) => input.key === FieldKey.PARTNER_BENEFIT_STATUS)?.[ + 'value' + ] === PartnerBenefitStatus.OAS_GIS || + inputs.find((input) => input.key === FieldKey.PARTNER_BENEFIT_STATUS)?.[ + 'value' + ] === PartnerBenefitStatus.HELP_ME + : null + const partnerNoOAS = inputs.find((input) => input.key === FieldKey.PARTNER_BENEFIT_STATUS)?.[ 'value' @@ -72,79 +119,149 @@ const ResultsPage: React.VFC<{ result.eligibility?.result === ResultKey.INCOME_DEPENDENT ) + const userResultObject = + resultsEligible.length > 0 + ? resultsEligible.reduce((acc, item) => { + // Use the value of benefitKey as the key in the resulting object + acc[item.benefitKey] = item + return acc + }, {}) + : null + + const partnerResultObject = + partnerResultsEligible.length > 0 + ? partnerResultsEligible.reduce((acc, item) => { + // Use the value of benefitKey as the key in the resulting object + acc[item.benefitKey] = item + return acc + }, {}) + : null + + let userObj = {} + // userObj['0'] = userResultObject + if (userResultObject) { + userObj['0'] = userResultObject + } else { + userObj = null + } + const userArr = userObj ? [userObj] : [] + + let partnerObj = {} + // partnerObj['0'] = partnerResultObject + if (partnerResultObject) { + partnerObj['0'] = partnerResultObject + } else { + partnerObj = null + } + const partnerArr = partnerObj ? [partnerObj] : [] + + const userArrNew = userArr.concat(futureClientResults) + const partnerArrNew = partnerArr.concat(futurePartnerResults) + + const currentYear = new Date().getFullYear() + + const newestUser = userArrNew.map((item, index) => { + if (item) { + const age = Number(Object.keys(item)[0]) + const headingYear = Math.trunc(currentYear + (age - Number(userAge))) + let key + if (age == 0) { + key = apiTsln.detail.currentEligible + } else { + key = headingYear + } + return { [key]: item } + } + }) + + const newestPartner = isPartnered + ? partnerArrNew.map((item, index) => { + if (item) { + const age = Number(Object.keys(item)[0]) + const headingYear = Math.trunc( + currentYear + (age - Number(partnerAge)) + ) + let key + if (age == 0) { + key = apiTsln.detail.currentEligible + } else { + key = headingYear + } + return { [key]: item } + } + }) + : null + + const userKeys = newestUser.flatMap((obj) => { + // Check if the object is not null or undefined before extracting keys + return obj ? Object.keys(obj) : [] + }) + + const partnerKeys = isPartnered + ? newestPartner.flatMap((obj) => { + // Check if the object is not null or undefined before extracting keys + return obj ? Object.keys(obj) : [] + }) + : [] + + const arr1 = userKeys.length > partnerKeys.length ? userKeys : partnerKeys + const arr2 = arr1 == partnerKeys ? userKeys : partnerKeys + + //get the headings to display user and partner results + const headings = [...new Set([...arr1, ...arr2])] + + //has multiple oas benefits + const multipleOAS_GIS = + userArrNew + .filter((item) => item !== null) + .filter((obj) => !!obj[Object.keys(obj)[0]]['oas']).length > 1 + return (
-
- - {resultsEligible.length > 0 && ( - - )} - - {futureClientResults && ( - 0} - eligibleOAS={ - resultsEligible.filter((obj) => obj.benefitKey === 'oas') - .length > 0 - } - /> - )} - - {!futureClientResults && ( - - )} - - {isPartnered && partnerResultsEligible.length > 0 && ( - - )} - - {futurePartnerResults && ( - 0} - eligibleOAS={ - partnerResultsEligible.filter((obj) => obj.benefitKey === 'oas') - .length > 0 - } - /> - )} - - {isPartnered && !futurePartnerResults && ( - - )} + element !== null).length + + partnerArrNew.filter((element) => element !== null).length + } + hasMultipleOasGis={multipleOAS_GIS} + alreadyReceiving={alreadyReceiving === 'true'} + /> + {/* Summary Estimates section */} +
+ {headings && ( + + )} +
+

{apiTsln.nextStepTitle}

input.key === 'age').value) - )} + inputAge={Number(userAge)} results={resultsArray} futureClientResults={futureClientResults} partnerResults={partnerResultsArray} + liveInCanada={ + inputs.find((input) => input.key === 'livingCountry').value === + LivingCountry.CANADA + } + formYears={yearsinCan} />
", + firstYearEligible: '{FIRST_ELIGIBLE_YEAR}', + lastYearEligible: 'À partir de ', + currentEligible: 'Présentement', + you: 'vous pourriez', + yourPartner: 'votre conjoint pourrait', + youCouldReceivePerMonth: 'par mois', + youCouldReceiveTo: 'à', + youCouldReceive: 'recevoir', + youCouldReceiveUntil: 'Jusqu’à', + youCouldReceiveFrom: 'De', + youCouldStartReceivingAt: 'À', + youCouldContinueReceiving: 'continuer de recevoir', + youCouldStartReceiving: 'commencer à recevoir', + yourEstimateIsStill: 'Votre estimation est encore', + yourEstimateIsStillPartner: `L'estimation de votre conjoint est encore`, + theSame: 'la même', + thisEstimateIsBased: + 'Cette estimation est basée sur {ENTITLEMENT_AMOUNT_FOR_BENEFIT} années de résidence au Canada.', oas: { eligibleIfIncomeIsLessThan: "Vous êtes probablement admissible à cette prestation si votre revenu est moins que {INCOME_LESS_THAN}. Si votre revenu dépasse {OAS_RECOVERY_TAX_CUTOFF}, vous devrez peut-être payer de l'{LINK_RECOVERY_TAX}.", @@ -457,7 +490,9 @@ const fr: Translations = { automaticallyBePaid: 'Vous recevrez automatiquement des paiements si votre revenu est admissible.', youWillReceiveLetter: - "Vous devriez recevoir une lettre au sujet de votre statut d'inscription le mois après votre 64e anniversaire.", + "Votre statut d'inscription devrait être confirmé par la poste le mois après votre 64e anniversaire.", + shouldReceive65to69: + "Votre statut d'inscription aurait dû être confirmé par la poste le mois après votre 64e anniversaire. Si vous n'avez pas reçu de lettre, communiquez avec nous pour savoir si vous devez présenter une demande.", youShouldReceiveLetter: "Vous devriez recevoir une lettre au sujet de votre statut d'inscription le mois après votre 64e anniversaire.", youShouldHaveReceivedLetter: @@ -473,11 +508,13 @@ const fr: Translations = { ifNotReceiveLetter64: "Si vous ne l'avez pas reçue, communiquez avec nous pour savoir si vous devez présenter une demande.", chooseToDefer: - "Vous pouvez choisir de reporter votre pension ou augmenter vos années de résidence au Canada. Pour savoir quelle option serait la meilleure pour vous, communiquez avec nous.", + "Vous pouvez choisir de reporter votre pension ou augmenter vos années de résidence au Canada. Pour savoir quelle option serait la meilleure pour vous, communiquez avec nous.", receivePayment: 'Vous pourriez recevoir un paiement pour un maximum des 11 derniers mois.', }, gis: { + youCanApplyGis: + 'Vous pouvez faire une demande pour le Supplément de revenu garanti lorsque vous présentez votre demande pour la pension de la Sécurité de la vieillesse.', eligibleDependingOnIncomeNoEntitlement: 'Vous pourriez probablement recevoir cette prestation si {INCOME_SINGLE_OR_COMBINED} est moins que {INCOME_LESS_THAN}. Fournissez {YOUR_OR_COMPLETE} pour obtenir une estimation de paiement mensuel.', incomeTooHigh: @@ -486,13 +523,27 @@ const fr: Translations = { 'Vous pourriez être admissible lorsque vous aurez {EARLIEST_ELIGIBLE_AGE}. Si votre revenu reste le même, vous ne recevrez peut-être pas de paiement mensuel.', ifYouApply: 'Si vous présentez une demande, Service Canada révisera votre déclaration de revenus chaque année. Vous recevrez automatiquement des paiements si votre revenu est admissible.', - canApplyOnline: - 'Vous pouvez faire une demande pour cette prestation en ligne.', + canApplyOnline: 'Vous pouvez faire une demande pour cette prestation.', ifYouAlreadyApplied: - 'Si vous avez déjà fait une demande pour le Supplément de revenu garanti, vous pouvez confirmer que vos renseignements sont à jour dans votre compte {MY_SERVICE_CANADA}.', + "Si vous avez déjà demandé le Supplément de revenu garanti, vous pouvez vous connecter à Mon dossier Service Canada pour confirmer que vos renseignements sont à jour.", ifYouAlreadyReceive: 'Si vous recevez déjà le Supplément de revenu garanti, vous pouvez confirmer que vos renseignements sont à jour dans votre compte {MY_SERVICE_CANADA}.', }, + alw: { + forIndividuals: 'Cette prestation est pour les personnes :', + age60to64: 'âgées de 60 à 64 ans;', + livingInCanada: 'qui vivent au Canada;', + spouseReceives: + 'dont le conjoint reçoit le Supplément de revenu garanti.', + yourPartnerCanApply: + 'Votre conjoint peut faire une demande de 6 à 11 mois avant d’être admissible à 65 ans.', + }, + alws: { + forWidowedIndividuals: + 'Cette prestation est pour les personnes veuves :', + haveNotRemarried: + 'qui ne se sont pas remariées ou engagées dans une nouvelle union de fait.', + }, }, detailWithHeading: { ifYouDeferYourPension: { @@ -513,11 +564,11 @@ const fr: Translations = { }, oasIncreaseAt75: { heading: 'Vos paiements augmenteront lorsque vous aurez 75 ans', - text: 'Lorsque vous aurez 75 ans, vos paiements augmenteront de 10 %.', + text: 'Lorsque vous aurez 75 ans, vos paiements de la pension de la Sécurité de la vieillesse augmenteront de 10 %.', }, oasIncreaseAt75Applied: { heading: 'Vos paiements ont augmenté car vous avez plus de 75 ans', - text: 'Parce que vous avez plus de 75 ans, vos paiements ont augmenté de 10 %.', + text: 'Parce que vous avez plus de 75 ans, vos paiements de la pension de la Sécurité de la vieillesse ont augmenté de 10 %.', }, calculatedBasedOnIndividualIncome: { heading: @@ -534,7 +585,56 @@ const fr: Translations = { }, partnerEligibleButAnsweredNo: { heading: 'Votre conjoint pourrait être admissible', - text: 'Vous pouvez modifier vos réponses pour voir ce que vous et votre partenaire pourriez recevoir si votre partenaire recevait la pension de la Sécurité de la vieillesse.', + text: 'Vous pouvez modifier vos réponses pour voir ce que vous et votre partenaire pourriez recevoir si votre partenaire recevait la pension de la Sécurité de la vieillesse.', + }, + recoveryTax: { + heading: 'L’impôt de récupération s’appliquera à votre pension', + text: "Puisque votre revenu est plus grand que {OAS_RECOVERY_TAX_CUTOFF}, vous ne recevrez pas une partie ou la totalité de votre pension de la Sécurité de la vieillesse en raison de l'{LINK_RECOVERY_TAX}.", + }, + recoveryTaxPartner: { + heading: + 'L’impôt de récupération s’appliquera à la pension de votre conjoint', + text: "Puisque le revenu de votre conjoint est plus grand que {OAS_RECOVERY_TAX_CUTOFF}, il ne recevra pas une partie ou la totalité de sa pension de la Sécurité de la vieillesse en raison de l'{LINK_RECOVERY_TAX}.", + }, + recoveryTaxBoth: { + heading: 'L’impôt de récupération s’appliquera à vos pensions', + text: "Puisque vos revenus et ceux de votre conjoint sont plus grand que {OAS_RECOVERY_TAX_CUTOFF}, vous ne recevrez pas une partie ou la totalité de vos pensions de la Sécurité de la vieillesse en raison de l'{LINK_RECOVERY_TAX}.", + }, + nonResidentTax: { + heading: 'Des impôts s’appliqueront à votre pension', + text: "Puisque votre revenu est plus grand que {OAS_RECOVERY_TAX_CUTOFF} et que vous vivez à l'extérieur du Canada, vous ne recevrez pas une partie ou la totalité de votre pension de la Sécurité de la vieillesse en raison de :
  • l'{LINK_RECOVERY_TAX};
  • l'{LINK_NON_RESIDENT_TAX}.
", + }, + nonResidentTaxPartner: { + heading: 'Des impôts s’appliqueront à la pension de votre conjoint', + text: "Puisque le revenu de votre conjoint est plus grand que {OAS_RECOVERY_TAX_CUTOFF} et qu’il vit à l’extérieur du Canada, il ne recevra pas une partie ou la totalité de sa pension de la Sécurité de la vieillesse en raison de :
  • l'{LINK_RECOVERY_TAX};
  • l'{LINK_NON_RESIDENT_TAX}.
", + }, + nonResidentTaxBoth: { + heading: 'Des impôts s’appliqueront à vos pensions', + text: "Puisque vos revenus et ceux de votre conjoint sont plus grand que {OAS_RECOVERY_TAX_CUTOFF} et que vous vivez à l'extérieur du Canada, vous ne recevrez pas une partie ou la totalité de vos pensions de la Sécurité de la vieillesse en raison de :
  • l'{LINK_RECOVERY_TAX};
  • l'{LINK_NON_RESIDENT_TAX}.
", + }, + yourDeferralOptions: { + heading: 'Vos options de report', + text: "Vous pouvez commencer à recevoir vos paiements de la pension de la Sécurité de la vieillesse à 65 ans ou attendre d'avoir 70 ans.", + }, + deferralDelay: { + heading: 'Vos options de report', + text: 'Vous pouvez reporter votre pension pour encore {DELAY_MONTHS} mois.', + }, + retroactivePayment: { + heading: 'Paiement rétroactif', + text: 'Vous pourriez recevoir un paiement pour un maximum des 11 derniers mois.', + }, + mayBecomeEligible: { + heading: 'Paiement rétroactif', + text: 'Vous pourriez recevoir un paiement pour un maximum des 11 derniers mois.', + }, + socialSecurityEligible: { + heading: 'Vous pourriez devenir admissible plus tôt', + text: "Vous pourriez devenir admissible plus tôt parce que vous avez vécu dans un pays avec un accord de sécurité sociale avec le Canada. Ceci pourrait affecter votre estimation. Communiquez avec nous pour plus de détails.", + }, + socialSecurityEligiblePartner: { + heading: 'Votre conjoint pourrait devenir admissible plus tôt', + text: "Votre conjoint pourrait devenir admissible plus tôt parce qu’il a vécu dans un pays avec un accord de sécurité sociale avec le Canada. Ceci pourrait affecter son estimation. Communiquez avec nous pour plus de détails.", }, }, summaryTitle: { @@ -565,6 +665,19 @@ const fr: Translations = { futureHeadingAge: 'Si vous commencez votre pension à...', headingAmount: 'Vous pourriez recevoir chaque mois...', }, + modal: { + userHeading: 'Est-ce que vous pouvez recevoir cette prestation?', + partnerHeading: 'Est-ce que votre conjoint peut recevoir cette prestation?', + userIncomeTooHigh: + 'Vous pouvez faire une demande pour cette prestation, mais votre revenu est trop élevé pour recevoir un paiement mensuel pour le moment.', + partnerIncomeTooHigh: + 'Votre conjoint peut faire une demande pour cette prestation, mais son revenu est trop élevé pour recevoir un paiement mensuel pour le moment.', + userCoupleIncomeTooHigh: + 'Vous pouvez faire une demande pour cette prestation, mais votre revenu de couple est trop élevé pour recevoir un paiement mensuel pour le moment.', + partnerCoupleIncomeTooHigh: + 'Votre conjoint peut faire une demande pour cette prestation, mais votre revenu de couple est trop élevé pour recevoir un paiement mensuel pour le moment.', + close: 'Fermer', + }, links, incomeSingle: 'votre revenu', incomeCombined: 'le revenu combiné de vous et votre conjoint', diff --git a/i18n/api/index.ts b/i18n/api/index.ts index 26b0c3a0d..56ea97135 100644 --- a/i18n/api/index.ts +++ b/i18n/api/index.ts @@ -62,16 +62,22 @@ export interface Translations { eligibleEntitlementUnavailable: string eligiblePartialOas: string yourDeferralOptions: string + deferralWillBeEligible: string + deferralEligible: string + deferralNoGis: string + deferralYearsInCanada: string retroactivePay: string sinceYouAreSixty: string futureDeferralOptions: string youCanAply: string + onceEnrolled: string delayMonths: string eligibleWhen60ApplyNow: string eligibleWhen65ApplyNow: string eligibleWhen60: string eligibleWhen65: string mustBeInCanada: string + partnerMustBeEligible: string mustBeOasEligible: string mustCompleteOasCheck: string mustMeetIncomeReq: string @@ -95,6 +101,7 @@ export interface Translations { alwsIfYouApply: string afsNotEligible: string alwsApply: string + alwPartnerEligible: string autoEnrollTrue: string autoEnrollFalse: string expectToReceive: string @@ -105,6 +112,23 @@ export interface Translations { oasClawbackInCanada: string futureOasClawbackInCanada: string oasClawbackNotInCanada: string + firstYearEligible: string + lastYearEligible: string + currentEligible: string + you: string + yourPartner: string + youCouldReceive: string + youCouldReceiveTo: string + youCouldReceivePerMonth: string + youCouldReceiveUntil: string + youCouldReceiveFrom: string + youCouldStartReceivingAt: string + youCouldContinueReceiving: string + youCouldStartReceiving: string + yourEstimateIsStill: string + yourEstimateIsStillPartner: string + theSame: string + thisEstimateIsBased: string oas: { eligibleIfIncomeIsLessThan: string dependOnYourIncome: string @@ -113,6 +137,7 @@ export interface Translations { serviceCanadaReviewYourPayment: string automaticallyBePaid: string youWillReceiveLetter: string + shouldReceive65to69: string youShouldReceiveLetter: string youShouldHaveReceivedLetter: string ifYouDidnt: string @@ -124,6 +149,7 @@ export interface Translations { receivePayment: string } gis: { + youCanApplyGis: string eligibleDependingOnIncomeNoEntitlement: string incomeTooHigh: string futureEligibleIncomeTooHigh: string @@ -132,6 +158,17 @@ export interface Translations { ifYouAlreadyApplied: string ifYouAlreadyReceive: string } + alw: { + forIndividuals: string + age60to64: string + livingInCanada: string + spouseReceives: string + yourPartnerCanApply: string + } + alws: { + forWidowedIndividuals: string + haveNotRemarried: string + } } detailWithHeading: { ifYouDeferYourPension: { heading: string; text: string } @@ -144,6 +181,18 @@ export interface Translations { partnerEligible: { heading: string; text: string } partnerDependOnYourIncome: { heading: string; text: string } partnerEligibleButAnsweredNo: { heading: string; text: string } + recoveryTax: { heading: string; text: string } + recoveryTaxPartner: { heading: string; text: string } + recoveryTaxBoth: { heading: string; text: string } + nonResidentTax: { heading: string; text: string } + nonResidentTaxPartner: { heading: string; text: string } + nonResidentTaxBoth: { heading: string; text: string } + yourDeferralOptions: { heading: string; text: string } + deferralDelay: { heading: string; text: string } + retroactivePayment: { heading: string; text: string } + mayBecomeEligible: { heading: string; text: string } + socialSecurityEligible: { heading: string; text: string } + socialSecurityEligiblePartner: { heading: string; text: string } } summaryTitle: { [key in SummaryState]?: string } summaryDetails: { [key in SummaryState]?: string } @@ -153,6 +202,15 @@ export interface Translations { futureHeadingAge: string headingAmount: string } + modal: { + userHeading: string + partnerHeading: string + userIncomeTooHigh: string + partnerIncomeTooHigh: string + userCoupleIncomeTooHigh: string + partnerCoupleIncomeTooHigh: string + close: string + } links: LinkDefinitions incomeSingle: string incomeCombined: string diff --git a/i18n/api/links/en.ts b/i18n/api/links/en.ts index 6066586df..904d3c892 100644 --- a/i18n/api/links/en.ts +++ b/i18n/api/links/en.ts @@ -137,6 +137,13 @@ export const links: LinkDefinitions = { action: 'Apply for ALWS', }, }, + SignInSC: { + text: 'Sign in to My Service Canada Account', + url: 'https://www.canada.ca/en/employment-social-development/services/my-account.html', + order: 24, + icon: LinkIcon.link, + action: 'Sign in to My Service Canada', + }, SC: { text: 'contact us', url: 'https://www.canada.ca/en/employment-social-development/corporate/contact/oas.html', diff --git a/i18n/api/links/fr.ts b/i18n/api/links/fr.ts index de3850107..9d72ad589 100644 --- a/i18n/api/links/fr.ts +++ b/i18n/api/links/fr.ts @@ -137,6 +137,13 @@ export const links: LinkDefinitions = { action: 'Apply for ALWS', }, }, + SignInSC: { + text: 'Se connecter à Mon dossier Service Canada', + url: 'https://www.canada.ca/fr/emploi-developpement-social/services/mon-dossier.html', + order: 24, + icon: LinkIcon.link, + action: 'Se connecter à Mon dossier Service Canada', + }, SC: { text: 'communiquez avec nous', url: 'https://www.canada.ca/fr/emploi-developpement-social/ministere/coordonnees/sv.html', diff --git a/i18n/api/links/index.ts b/i18n/api/links/index.ts index 896e04fb1..9e2aebe6f 100644 --- a/i18n/api/links/index.ts +++ b/i18n/api/links/index.ts @@ -19,6 +19,7 @@ export interface LinkDefinitions { oasDefer: Link oasRetroactive: Link apply: { [key in BenefitKey]: LinkWithAction } + SignInSC: LinkWithAction SC: Link SCAccount: Link oasDeferClickHere: Link diff --git a/i18n/web/en.ts b/i18n/web/en.ts index 5dc308be4..084c0d8ac 100644 --- a/i18n/web/en.ts +++ b/i18n/web/en.ts @@ -312,6 +312,15 @@ const en: WebTranslations = { 'EC Economics and Industry;Allowances;Benefits;Survivor benefits;Finance;Personal finance;Income;Pensions;Public pensions,PE Persons;Adults;Seniors,So Society and Culture;Old age', }, resultsPage: { + moreInformation: 'More information on ', + yourMonEstimateHeading: 'Your monthly estimate', + changeInSituation: 'Changes in your situation may impact your results.', + youEstimateMayChange: 'Your estimate may change over time based on', + yourEstimateMayChangeList: 'Your estimate may change over time based on:', + basedYourAge: 'your age', + basedYourPartner: 'the benefits your partner receives', + ifYouChoseToDefer: + 'If you choose to defer your pension, your future estimate will be higher.', header: 'Table of estimated monthly amounts', general: 'The following is only an estimate of your eligibility and monthly payments based on current rates. Amounts may increase with the cost of living.

You must be a citizen or legal resident of Canada to receive these benefits.

', @@ -471,6 +480,7 @@ const en: WebTranslations = { tooltip: { moreInformation: 'More information', }, + openNewTab: 'Opens in a new tab', partnerIsNotEligible: 'Your partner is not eligible', partnerLegalStatusNotEligible: "Your partner's legal status indicates that they are not receiving the Old Age Security pension.", diff --git a/i18n/web/fr.ts b/i18n/web/fr.ts index 6e2a3386f..0ee6fe8e4 100644 --- a/i18n/web/fr.ts +++ b/i18n/web/fr.ts @@ -314,6 +314,18 @@ const fr: WebTranslations = { homeSubject: `EC Économie et industrie;Allocation;Avantages sociaux;Prestation au survivant;Finances;Finances personnelles;Revenu;Pension;Pension publique,PE Personnes;Adulte;Aîné,SO Société et culture;Vieillesse`, }, resultsPage: { + moreInformation: "Plus d'information", + yourMonEstimateHeading: 'Votre estimation mensuelle', + changeInSituation: + 'Si votre situation change, vos résultats pourraient changer.', + youEstimateMayChange: + 'Votre estimation peut changer au fil du temps en fonction', + yourEstimateMayChangeList: + 'Votre estimation peut changer au fil du temps en fonction\xa0:', + basedYourAge: 'de votre âge', + basedYourPartner: 'des prestations que votre conjoint reçoit.', + ifYouChoseToDefer: + 'Si vous choisissez de reporter votre pension, votre estimation future sera plus élevée.', header: "Tableau des résultats d'estimation", general: `Les résultats suivants ne sont qu'une estimation de votre admissibilité et de vos paiements mensuels basée sur les montants actuels. Ceux-ci peuvent augmenter avec le coût de la vie.

Vous devez être citoyen ou résident autorisé du Canada pour recevoir ces prestations.

`, onThisPage: 'Sur cette page', @@ -482,6 +494,8 @@ const fr: WebTranslations = { tooltip: { moreInformation: "Plus d'information", }, + openNewTab: "s'ouvre dans un nouvel onglet", + partnerIsNotEligible: "Votre conjoint n'est pas admissible", partnerLegalStatusNotEligible: "Le statut légal de votre conjoint indique qu'il ne reçoit pas la pension de la Sécurité de la vieillesse.", diff --git a/i18n/web/index.ts b/i18n/web/index.ts index 5aee72246..a2d722496 100644 --- a/i18n/web/index.ts +++ b/i18n/web/index.ts @@ -220,6 +220,14 @@ export type WebTranslations = { } //results page resultsPage: { + moreInformation: string + yourMonEstimateHeading: string + changeInSituation: string + youEstimateMayChange: string + yourEstimateMayChangeList: string + basedYourAge: string + basedYourPartner: string + ifYouChoseToDefer: string header: string general: string onThisPage: string @@ -291,6 +299,8 @@ export type WebTranslations = { moreInformation: string } + openNewTab: string + partnerIsNotEligible: string partnerLegalStatusNotEligible: string partnerYearsLivingCanadaNotEligible: string diff --git a/public/moreInfo.png b/public/moreInfo.png new file mode 100644 index 000000000..c8f4526ef Binary files /dev/null and b/public/moreInfo.png differ diff --git a/public/openNewTabWhite.svg b/public/openNewTabWhite.svg new file mode 100644 index 000000000..e8a6149ce --- /dev/null +++ b/public/openNewTabWhite.svg @@ -0,0 +1,30 @@ + + + \ No newline at end of file diff --git a/styles/globals.css b/styles/globals.css index 5dcf90c38..5a09d4aca 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -408,6 +408,10 @@ html { font-size: 12px; } + a#inCanada, a.link-no-deco:visited{ + color:#284162 + } + a:visited { color: #7834bc; } diff --git a/utils/api/benefitHandler.ts b/utils/api/benefitHandler.ts index d862cbf4f..f88ffadef 100644 --- a/utils/api/benefitHandler.ts +++ b/utils/api/benefitHandler.ts @@ -163,12 +163,14 @@ export class BenefitHandler { const clientOasNoDeferral = new OasBenefit( this.input.client, this.fields.translations, + null, false, this.future, false, this.input.client.age, this.formAge, - this.formYearsInCanada + this.formYearsInCanada, + this.input.client.receiveOAS ) // If the client needs help, check their partner's OAS. // no defer and defer options? @@ -176,6 +178,7 @@ export class BenefitHandler { const partnerOasNoDeferral = new OasBenefit( this.input.partner, this.fields.translations, + clientOasNoDeferral, true ) @@ -211,10 +214,14 @@ export class BenefitHandler { clientOasWithDeferral = new OasBenefit( clientOasHelper.newInput, this.fields.translations, + null, false, this.future, true, - this.input.client.age + this.input.client.age, + this.formAge, + this.formYearsInCanada, + this.input.client.receiveOAS ) consoleDev('WITH DEFERRAL', clientOasWithDeferral) @@ -229,7 +236,10 @@ export class BenefitHandler { this.fields.translations, clientOasNoDeferral.info, false, - this.future + this.future, + null, + this.formAge, + this.formYearsInCanada ) consoleDev( @@ -245,7 +255,9 @@ export class BenefitHandler { clientOasWithDeferral.info, false, this.future, - this.input.client + this.input.client, + this.formAge, + this.formYearsInCanada ) consoleDev( @@ -305,7 +317,8 @@ export class BenefitHandler { this.input.client, clientOasWithDeferral.eligibility, // 65to74 entitlement is equivalent to entitlement at age of eligibility with years of residency at age of eligibility and 0 months deferral clientOasWithDeferral.entitlement, - this.future + this.future, + this.formYearsInCanada ) } else { // Scenario when client age is same as eligibility age. They could choose not to receive OAS yet until later so we show the deferral table. @@ -316,7 +329,8 @@ export class BenefitHandler { this.input.client, clientOasNoDeferral.eligibility, clientOasNoDeferral.entitlement, - this.future + this.future, + this.formYearsInCanada ) } else { clientOas.cardDetail.meta = OasBenefit.buildMetadataObj( @@ -325,7 +339,8 @@ export class BenefitHandler { this.input.client, clientOasWithDeferral.eligibility, // 65to74 entitlement is equivalent to entitlement at age of eligibility with years of residency at age of eligibility and 0 months deferral clientOasWithDeferral.entitlement, - this.future + this.future, + this.formYearsInCanada ) } } @@ -336,7 +351,8 @@ export class BenefitHandler { this.input.client, clientOasNoDeferral.eligibility, // 65to74 entitlement is equivalent to entitlement at age of eligibility with years of residency at age of eligibility and 0 months deferral clientOasNoDeferral.entitlement, - this.future + this.future, + this.formYearsInCanada ) } } @@ -349,6 +365,7 @@ export class BenefitHandler { const partnerOas = new OasBenefit( this.input.partner, this.fields.translations, + clientOas, true ) this.setValueForAllResults(allResults, 'partner', 'oas', partnerOas) @@ -466,6 +483,7 @@ export class BenefitHandler { const partnerOas = new OasBenefit( this.input.partner, this.fields.translations, + clientOas, true ) this.setValueForAllResults(allResults, 'partner', 'oas', partnerOas) @@ -490,7 +508,10 @@ export class BenefitHandler { this.fields.translations, allResults.client.oas, false, - this.future + this.future, + null, + this.formAge, + this.formYearsInCanada ) this.setValueForAllResults(allResults, 'client', 'gis', clientGis) } diff --git a/utils/api/benefits/_base.ts b/utils/api/benefits/_base.ts index 03d474994..844126219 100644 --- a/utils/api/benefits/_base.ts +++ b/utils/api/benefits/_base.ts @@ -67,6 +67,8 @@ export abstract class BaseBenefit { return this.eligibility.result === ResultKey.ELIGIBLE } + // Remove This code + /** * The main text content that will always be visible within each benefit's card. */ @@ -93,7 +95,7 @@ export abstract class BaseBenefit { this.eligibility.result === ResultKey.ELIGIBLE && this.entitlement.result > 0 ) { - text += ` ${this.translations.detail.expectToReceive}` + // text += ` ${this.translations.detail.expectToReceive}` } if ( @@ -103,7 +105,7 @@ export abstract class BaseBenefit { ) { text += `
${this.translations.detail.autoEnrollTrue}
` } - + text = null return text } diff --git a/utils/api/benefits/alwBenefit.ts b/utils/api/benefits/alwBenefit.ts index 8e37f58a6..4e4727489 100644 --- a/utils/api/benefits/alwBenefit.ts +++ b/utils/api/benefits/alwBenefit.ts @@ -156,7 +156,9 @@ export class AlwBenefit extends BaseBenefit { } else if (!meetsReqPartner) { return { result: ResultKey.INELIGIBLE, - reason: ResultReason.PARTNER, + reason: !this.input.partnerBenefitStatus.provided + ? ResultReason.NONE + : ResultReason.PARTNER, //detail: this.translations.detail.alwNotEligible, detail: !this.input.partnerBenefitStatus.provided ? this.translations.detail.alwNotEligible @@ -173,7 +175,7 @@ export class AlwBenefit extends BaseBenefit { } else if (!meetsReqCountry) { return { result: ResultKey.INELIGIBLE, - reason: ResultReason.INCOME, + reason: ResultReason.LIVING_COUNTRY, detail: this.translations.detail.mustBeInCanada, } } else if (!meetsReqYears) { @@ -282,62 +284,37 @@ export class AlwBenefit extends BaseBenefit { return false } - protected getCardText(): string { - let text = this.eligibility.detail - + protected getCardLinks(): LinkWithAction[] { + const links: LinkWithAction[] = [] if ( - this.eligibility.result === ResultKey.ELIGIBLE && - this.entitlement.result > 0 + this.eligibility.result === ResultKey.ELIGIBLE || + this.eligibility.result === ResultKey.INCOME_DEPENDENT || + (this.eligibility.result === ResultKey.INELIGIBLE && + this.eligibility.reason === ResultReason.AGE_YOUNG) ) { - text += this.future - ? ` ${this.translations.detail.futureExpectToReceive}` - : ` ${this.translations.detail.expectToReceive}` + links.push(this.translations.links.apply[BenefitKey.alw]) } - return text + links.push(this.translations.links.overview[BenefitKey.alw]) + return links } protected getCardCollapsedText(): CardCollapsedText[] { let cardCollapsedText = super.getCardCollapsedText() + if (this.input.everLivedSocialCountry) { + cardCollapsedText.push( + this.partner + ? this.translations.detailWithHeading.socialSecurityEligiblePartner + : this.translations.detailWithHeading.socialSecurityEligible + ) + } + if ( this.eligibility.result !== ResultKey.ELIGIBLE && this.eligibility.result !== ResultKey.INCOME_DEPENDENT ) return cardCollapsedText - // partner is eligible, IF income was not provided the result = 0 - // when IF income > 0 AND invSeparated = true the amount is incorrectly calculated - // the correct amount is on the benefitHandler. - if (this.partner) { - if (this.entitlement.result > 0) { - if (this.eligibility.result !== ResultKey.INCOME_DEPENDENT) { - if (!this.input.invSeparated) { - cardCollapsedText.push( - this.translations.detailWithHeading.partnerEligible - ) - } - } else { - cardCollapsedText.push( - this.translations.detailWithHeading.partnerDependOnYourIncome - ) - } - } - } - return cardCollapsedText } - - protected getCardLinks(): LinkWithAction[] { - const links: LinkWithAction[] = [] - if ( - this.eligibility.result === ResultKey.ELIGIBLE || - this.eligibility.result === ResultKey.INCOME_DEPENDENT || - (this.eligibility.result === ResultKey.INELIGIBLE && - this.eligibility.reason === ResultReason.AGE_YOUNG) - ) { - links.push(this.translations.links.apply[BenefitKey.alw]) - } - links.push(this.translations.links.overview[BenefitKey.alw]) - return links - } } diff --git a/utils/api/benefits/gisBenefit.ts b/utils/api/benefits/gisBenefit.ts index c937e99f0..27735c569 100644 --- a/utils/api/benefits/gisBenefit.ts +++ b/utils/api/benefits/gisBenefit.ts @@ -22,6 +22,8 @@ import { EntitlementFormula } from './entitlementFormula' export class GisBenefit extends BaseBenefit { partner: Boolean future: Boolean + formAge: number + formYearsInCanada: number originalInput?: ProcessedInput constructor( input: ProcessedInput, @@ -29,12 +31,16 @@ export class GisBenefit extends BaseBenefit { private oasResult: BenefitResult, partner?: Boolean, future?: Boolean, - originalInput?: ProcessedInput + originalInput?: ProcessedInput, + formAge?: number, + formYearsInCanada?: number ) { super(input, translations, BenefitKey.gis) this.partner = partner this.future = future this.originalInput = originalInput + this.formAge = formAge + this.formYearsInCanada = formYearsInCanada } protected getEligibility(): EligibilityResult { @@ -261,54 +267,12 @@ export class GisBenefit extends BaseBenefit { this.eligibility.result === ResultKey.ELIGIBLE || this.eligibility.result === ResultKey.INCOME_DEPENDENT ) { - !this.future && links.push(this.translations.links.apply[BenefitKey.gis]) + links.push(this.translations.links.apply[BenefitKey.gis]) } links.push(this.translations.links.overview[BenefitKey.gis]) return links } - protected getCardText(): string { - /** - * The following IF block is a copy from benefitHandler.translateResults, - * the issue is that cardDetail object is updated only once if undefined, and could have the wrong information. - * overwrite eligibility.detail and autoEnrollment when entitlement.type = none. - */ - - if ( - this.eligibility.result === ResultKey.ELIGIBLE && - this.entitlement.type === EntitlementResultType.NONE - ) { - //this.eligibility.result = ResultKey.INELIGIBLE - this.eligibility.reason = ResultReason.INCOME - this.eligibility.detail = this.future - ? this.translations.detail.gis.futureEligibleIncomeTooHigh - : this.translations.detail.gis.incomeTooHigh - this.entitlement.autoEnrollment = this.getAutoEnrollment() - } - - // another hack, to avoid adding message expectToReceive - if ( - this.eligibility.result === ResultKey.ELIGIBLE && - this.eligibility.reason === ResultReason.INCOME && - this.entitlement.result > 0 - ) { - return this.eligibility.detail - } - - let text = this.eligibility.detail - - if ( - this.eligibility.result === ResultKey.ELIGIBLE && - this.entitlement.result > 0 - ) { - text += this.future - ? ` ${this.translations.detail.futureExpectToReceive}` - : ` ${this.translations.detail.expectToReceive}` - } - - return text - } - public updateCollapsedText(): CardCollapsedText[] { return this.getCardCollapsedText() } @@ -316,41 +280,65 @@ export class GisBenefit extends BaseBenefit { protected getCardCollapsedText(): CardCollapsedText[] { let cardCollapsedText = super.getCardCollapsedText() - if ( - this.eligibility.result !== ResultKey.ELIGIBLE && - this.eligibility.result !== ResultKey.INCOME_DEPENDENT - ) - return cardCollapsedText + if (this.input.everLivedSocialCountry) { + cardCollapsedText.push( + this.partner + ? this.translations.detailWithHeading.socialSecurityEligiblePartner + : this.translations.detailWithHeading.socialSecurityEligible + ) + } + + //Deferral options Expand-Collapse + if (!this.partner) { + let text = '' + let heading - const inputs = this.originalInput === null ? this.input : this.originalInput - // Related to OAS Deferral, don't show if already receiving - if (inputs) { - const ageInOasRange = inputs.age >= 65 && inputs.age < 70 if ( - this.partner !== true && - this.entitlement.result !== 0 && - ageInOasRange && - !inputs.receiveOAS && - !this.future + this.oasResult.eligibility.result === ResultKey.ELIGIBLE || + this.oasResult.eligibility.result === ResultKey.WILL_BE_ELIGIBLE ) { - cardCollapsedText.push( - this.translations.detailWithHeading.ifYouDeferYourPension - ) - } - } + // const ageToCheck = this.originalInput?.age + // ? this.originalInput?.age + // : this.formAge - if (this.partner) { - if (this.input.income.provided && this.entitlement.result !== 0) { - if ( - this.input.partnerBenefitStatus.value === PartnerBenefitStatus.NONE - ) { - cardCollapsedText.push( - this.translations.detailWithHeading.partnerEligibleButAnsweredNo - ) - } else { - cardCollapsedText.push( - this.translations.detailWithHeading.partnerEligible - ) + const ageToCheck = + this.formAge ?? this.originalInput?.age ?? this.input.age + + if (this.oasResult.cardDetail.meta.receiveOAS == false) { + heading = this.translations.detail.yourDeferralOptions + + if ( + this.oasResult.entitlement.result > 0 && + ageToCheck < 70 && + this.input.age < 70 + ) { + if (ageToCheck >= 65 && ageToCheck < 70) { + //CHECK IF RECEIVING OAS + text += this.translations.detail.deferralEligible + } else if (ageToCheck < 65) { + text += this.translations.detail.deferralWillBeEligible + } + if (text !== '') { + if (this.entitlement.result !== 0) { + text += `

${this.translations.detail.deferralNoGis}

` + } + + //Removing due to complexity, revisit potential for EC6- EC5 + // if (!this.input.livedOnlyInCanada && ageToCheck > 64) { + // if ( + // ageToCheck != this.input.age && + // this.formYearsInCanada <= 40 && + // this.formYearsInCanada != this.input.yearsInCanadaSince18 + // ) { + // text += `

${this.translations.detail.deferralYearsInCanada}

` + // } + // } + } + } + + if (text !== '') { + this.oasResult.cardDetail.collapsedText.push({ heading, text }) + } } } } diff --git a/utils/api/benefits/oasBenefit.ts b/utils/api/benefits/oasBenefit.ts index 62e09b14f..e0485eb1d 100644 --- a/utils/api/benefits/oasBenefit.ts +++ b/utils/api/benefits/oasBenefit.ts @@ -2,6 +2,8 @@ import { Translations } from '../../../i18n/api' import { BenefitKey, EntitlementResultType, + LivingCountry, + MaritalStatus, PartnerBenefitStatus, ResultKey, ResultReason, @@ -15,6 +17,7 @@ import { MetaDataObject, MonthsYears, } from '../definitions/types' +import { LivingCountryHelper } from '../helpers/fieldClasses' import roundToTwo from '../helpers/roundToTwo' import { getDeferralIncrease } from '../helpers/utils' import legalValues from '../scrapers/output' @@ -28,15 +31,19 @@ export class OasBenefit extends BaseBenefit { inputAge: number // Age on the form. Needed as a reference when calculating eligibility for a different age ONLY for non-future benefits formAge: number formYearsInCanada: number + userOas: OasBenefit + formReceiving: boolean constructor( input: ProcessedInput, translations: Translations, + userOas?: OasBenefit, partner?: Boolean, future?: Boolean, deferral: boolean = false, inputAge?: number, formAge?: number, - formYearsInCanada?: number + formYearsInCanada?: number, + formReceiving?: boolean ) { super(input, translations, BenefitKey.oas) this.partner = partner @@ -48,6 +55,8 @@ export class OasBenefit extends BaseBenefit { this.inputAge = inputAge this.formAge = formAge this.formYearsInCanada = formYearsInCanada + this.userOas = userOas + this.formReceiving = formReceiving } protected getEligibility(): EligibilityResult { @@ -343,7 +352,8 @@ export class OasBenefit extends BaseBenefit { this.input, this.eligibility, this.entitlement, - this.future + this.future, + this.formYearsInCanada ) } else { return { @@ -361,16 +371,26 @@ export class OasBenefit extends BaseBenefit { input, eligibility, entitlement, - future + future, + formYearsInCanada ): MetaDataObject { const eligible = eligibility.result === ResultKey.ELIGIBLE || eligibility.result === ResultKey.INCOME_DEPENDENT + //Check future first, if !future don't bother + const filledYears = + future && !input.livedOnlyInCanada + ? +input.yearsInCanadaSince18 !== +formYearsInCanada + ? input.yearsInCanadaSince18 + : null + : null + const meta: MetaDataObject = { tableData: null, currentAge: null, monthsTo70: null, + residency: filledYears, receiveOAS: false, } @@ -427,14 +447,26 @@ export class OasBenefit extends BaseBenefit { tableData: null, currentAge: null, monthsTo70: null, + residency: filledYears, receiveOAS: receivingOAS, } } } + public updateCollapsedText(): CardCollapsedText[] { + return this.getCardCollapsedText() + } + protected getCardCollapsedText(): CardCollapsedText[] { let cardCollapsedText = super.getCardCollapsedText() + if (this.input.everLivedSocialCountry) { + cardCollapsedText.push( + this.partner + ? this.translations.detailWithHeading.socialSecurityEligiblePartner + : this.translations.detailWithHeading.socialSecurityEligible + ) + } // if not eligible, don't bother with any of the below if ( this.eligibility.result !== ResultKey.ELIGIBLE && @@ -442,180 +474,112 @@ export class OasBenefit extends BaseBenefit { ) return cardCollapsedText - if (this.partner && this.entitlement.result !== 0) { - if ( - // eslint-disable-next-line prettier/prettier - this.input.partnerBenefitStatus.value === - PartnerBenefitStatus.OAS_GIS || - this.input.partnerBenefitStatus.value === PartnerBenefitStatus.HELP_ME - ) { - cardCollapsedText.push( - this.translations.detailWithHeading.partnerEligible - ) - } else { - cardCollapsedText.push( - this.translations.detailWithHeading.partnerEligibleButAnsweredNo - ) - } - - return cardCollapsedText - } - - // getCardText reset the eligibility reason - if (this.eligibility.reason === ResultReason.INCOME) - return cardCollapsedText - - // increase at 75 - if (this.currentEntitlementAmount !== this.age75EntitlementAmount) { - if (!this.future) { - cardCollapsedText.push( - this.translations.detailWithHeading.oasIncreaseAt75 - ) + if (this.partner) { + //EC15 + if (this.entitlement.result > 0) { + if ( + this.input.partnerBenefitStatus.value === PartnerBenefitStatus.NONE + ) { + cardCollapsedText.push( + this.translations.detailWithHeading.partnerEligibleButAnsweredNo + ) + } } - } else - cardCollapsedText.push( - this.translations.detailWithHeading.oasIncreaseAt75Applied - ) - - // deferral - // if (this.deferralIncrease) - // cardCollapsedText.push( - // this.translations.detailWithHeading.oasDeferralApplied - // ) - // else if (this.input.age >= 65 && this.input.age < 70) - // cardCollapsedText.push( - // this.translations.detailWithHeading.oasDeferralAvailable - // ) - - return cardCollapsedText - } - - protected getCardText(): string { - // overwrite eligibility detail if income too high - if ( - this.eligibility.result === ResultKey.ELIGIBLE && - this.entitlement.type === EntitlementResultType.NONE - ) { - //this.eligibility.result = ResultKey.INELIGIBLE - this.eligibility.reason = ResultReason.INCOME - this.eligibility.detail = this.future - ? this.translations.detail.futureEligibleIncomeTooHigh - : this.translations.detail.eligibleIncomeTooHigh - this.entitlement.autoEnrollment = this.getAutoEnrollment() - } - - // INTRO 1 - general eligibility already in the detail - let text = this.eligibility.detail - - // INTRO 2 - "expect to receive" variation - if ( - this.eligibility.result === ResultKey.ELIGIBLE && - this.eligibility.reason !== ResultReason.INCOME && - this.entitlement.result > 0 - ) { - if (this.future) { - if (!this.input.livedOnlyInCanada) { - text += ` ${this.translations.detail.futureExpectToReceivePartial1}` - if ( - this.formAge != this.input.age && - this.formYearsInCanada <= 40 && - this.formYearsInCanada != this.input.yearsInCanadaSince18 - ) { - text += `${this.translations.detail.futureExpectToReceivePartial2}` + //Handle EC9 - EC14 + if (this.userOas) { + if ( + this.eligibility.result === ResultKey.ELIGIBLE && + this.userOas.eligibility.result === ResultKey.ELIGIBLE + ) { + if (this.clawbackAmount > 0 && this.userOas.clawbackAmount > 0) { + //EC13 + if ( + this.userOas.input.livingCountry.value !== LivingCountry.CANADA + ) { + cardCollapsedText.push( + this.translations.detailWithHeading.nonResidentTaxBoth + ) + } + //EC14 + else { + cardCollapsedText.push( + this.translations.detailWithHeading.recoveryTaxBoth + ) + } + } + } + if (this.clawbackAmount > 0 && this.userOas.clawbackAmount <= 0) { + //EC11 + if (this.input.livingCountry.value !== LivingCountry.CANADA) { + cardCollapsedText.push( + this.translations.detailWithHeading.nonResidentTaxPartner + ) + } + //EC12 + else { + cardCollapsedText.push( + this.translations.detailWithHeading.recoveryTaxPartner + ) } - text += ` ${this.translations.detail.futureExpectToReceivePartial3}` - } else { - text += ` ${this.translations.detail.futureExpectToReceive}` } - } else { - text += ` ${this.translations.detail.expectToReceive}` } } - // special case - if (this.eligibility.result === ResultKey.INCOME_DEPENDENT) { - text += `

${this.translations.detail.oas.dependOnYourIncome}

` - } - - // special case - if ( - this.eligibility.result === ResultKey.INELIGIBLE && - this.eligibility.reason === ResultReason.AGE_YOUNG - ) { - text += this.translations.nextStepTitle - //text += `

${this.translations.detail.oas.youShouldReceiveLetter}

` - text += `

${this.translations.detail.oas.youShouldHaveReceivedLetter}

` - } - - if (this.eligibility.reason !== ResultReason.INCOME) { - const clawbackValue = this.entitlement.clawback - - if (!this.partner && this.input.livingCountry.canada) { - text += - clawbackValue > 0 - ? this.future - ? `
${this.translations.detail.futureOasClawbackInCanada}
` - : `
${this.translations.detail.oasClawbackInCanada}
` - : '' - } else { - text += - clawbackValue > 0 - ? `
${this.translations.detail.oasClawbackNotInCanada}
` - : '' + if (!this.partner) { + //RECOVER TAX MESSAGE - if partnered better to handle it in the partnered section to access both benefits + if (this.clawbackAmount > 0) { + //EC10 + if (this.input.livingCountry.value !== LivingCountry.CANADA) { + cardCollapsedText.push( + this.translations.detailWithHeading.nonResidentTax + ) + } + //EC09 + else { + cardCollapsedText.push( + this.translations.detailWithHeading.recoveryTax + ) + } } - } - // RETROACTIVE PAY - if ( - !this.future && - this.eligibility.result === ResultKey.ELIGIBLE && - !this.partner && - (!this.input.receiveOAS || this.deferral) && - (this.input.age > 70 || this.inputAge > 70) && - this.eligibility.reason !== ResultReason.INCOME - ) { - // if (this.inputAge !== this.input.age) { - // Retroactive pay - text += `

${this.translations.detail.retroactivePay}

` - text += `

${this.translations.detail.oas.receivePayment}

` - // } - } - - // DEFERRAL - if ( - this.eligibility.result === ResultKey.ELIGIBLE && - !this.partner && - (!this.input.receiveOAS || this.deferral) && - this.input.age < 70 && - this.inputAge < 70 - ) { - // your Deferral Options - - // if income too high - if (this.eligibility.reason === ResultReason.INCOME) { - if (!this.future) { - text += `

${this.translations.detail.yourDeferralOptions}

` - text += this.translations.detail.delayMonths - } + if ( + this.entitlement.result == 0 && + this.inputAge > 64 && + this.inputAge < 70 && + !this.input.receiveOAS + ) { + cardCollapsedText.push( + this.translations.detailWithHeading.deferralDelay + ) } + const ageCalc = this.formAge ? this.formAge : this.inputAge - // normal case - if (this.entitlement.result > 0) { - text += `

${this.translations.detail.yourDeferralOptions}

` - if (this.future) { - // can also check if this.entitlement.clawback === 0 - text += this.translations.detail.futureDeferralOptions - } else { - text += this.translations.detail.sinceYouAreSixty + //EC8 + if ( + ageCalc >= 70 && + this.inputAge == ageCalc && + !this.formReceiving && + this.entitlement.result > 0 + ) { + cardCollapsedText.push( + this.translations.detailWithHeading.retroactivePayment + ) + } + //EC19 && EC20 + if (ageCalc >= 75 && this.entitlement.result > 0) { + cardCollapsedText.push( + this.translations.detailWithHeading.oasIncreaseAt75Applied + ) + } - if (!this.deferral && this.input.yearsInCanadaSince18 < 40) { - text += `

${this.translations.detail.oas.chooseToDefer}

` - } - } + if (ageCalc > 64 && ageCalc < 75 && this.entitlement.result > 0) { + cardCollapsedText.push( + this.translations.detailWithHeading.oasIncreaseAt75 + ) } } - return text + return cardCollapsedText } protected getCardLinks(): LinkWithAction[] { @@ -625,7 +589,9 @@ export class OasBenefit extends BaseBenefit { this.eligibility.result === ResultKey.INCOME_DEPENDENT || this.eligibility.reason === ResultReason.AGE_YOUNG_64 ) - links.push(this.translations.links.apply[this.benefitKey]) + this.formReceiving + ? links.push(this.translations.links.SignInSC) + : links.push(this.translations.links.apply[this.benefitKey]) links.push(this.translations.links.overview[this.benefitKey]) return links } diff --git a/utils/api/definitions/enums.ts b/utils/api/definitions/enums.ts index 21e4bff05..848e57383 100644 --- a/utils/api/definitions/enums.ts +++ b/utils/api/definitions/enums.ts @@ -57,6 +57,7 @@ export enum ResultKey { INVALID = 'invalid', INCOME_DEPENDENT = 'incomeDependent', WILL_BE_ELIGIBLE = 'willBeEligible', + ALMOST_ELIGIBLE = 'almostEligible', } // not displayed in the UI diff --git a/utils/api/definitions/textReplacementRules.ts b/utils/api/definitions/textReplacementRules.ts index c7bb8f9cf..cec0725d5 100644 --- a/utils/api/definitions/textReplacementRules.ts +++ b/utils/api/definitions/textReplacementRules.ts @@ -132,7 +132,7 @@ export const textReplacementRules: TextReplacementRules = { } export function generateLink(link: Link, opensNewWindow?: string): string { - return `${link.text}` + return `${link.text}` } export function getEligibleAgeWithMonths(age: number, language: string) { diff --git a/utils/api/definitions/types.ts b/utils/api/definitions/types.ts index 0e454b1d3..a5308f0a3 100644 --- a/utils/api/definitions/types.ts +++ b/utils/api/definitions/types.ts @@ -205,6 +205,7 @@ export interface MetaDataObject { tableData?: null | TableData[] currentAge?: null | number monthsTo70?: null | number + residency?: null | number receiveOAS: boolean } diff --git a/utils/api/fieldsHandler.ts b/utils/api/fieldsHandler.ts index 2a04081c8..2ee80ea61 100644 --- a/utils/api/fieldsHandler.ts +++ b/utils/api/fieldsHandler.ts @@ -317,19 +317,22 @@ export class FieldsHandler { textToProcess: string, benefitResult?: BenefitResult ): string { - const re: RegExp = new RegExp(/{(\w*?)}/g) - const matches: IterableIterator = - textToProcess.matchAll(re) - for (const match of matches) { - const key: string = match[1] - const replacementRule = textReplacementRules[key] - if (!replacementRule) - throw new Error(`no text replacement rule for ${key}`) - textToProcess = textToProcess.replace( - `{${key}}`, - replacementRule(benefitHandlerInstance, benefitResult) - ) + if (textToProcess) { + const re: RegExp = new RegExp(/{(\w*?)}/g) + const matches: IterableIterator = + textToProcess.matchAll(re) + for (const match of matches) { + const key: string = match[1] + const replacementRule = textReplacementRules[key] + if (!replacementRule) + throw new Error(`no text replacement rule for ${key}`) + textToProcess = textToProcess.replace( + `{${key}}`, + replacementRule(benefitHandlerInstance, benefitResult) + ) + } } + return textToProcess } @@ -424,12 +427,14 @@ export class FieldsHandler { * Accepts a string, generally containing newlines (\n), and capitalizes the first character of each line. */ static capitalizeEachLine(s: string): string { - const lines: string[] = s.split('\n') - return lines - .reduce((result, line) => { - return result + this.capitalizeFirstChar(line) + '\n' - }, '') - .trim() + if (s) { + const lines: string[] = s.split('\n') + return lines + .reduce((result, line) => { + return result + this.capitalizeFirstChar(line) + '\n' + }, '') + .trim() + } } /** diff --git a/utils/api/invSeparated.ts b/utils/api/invSeparated.ts index 0a59bf5ea..53a24cc98 100644 --- a/utils/api/invSeparated.ts +++ b/utils/api/invSeparated.ts @@ -305,12 +305,6 @@ export function InvSeparatedAllCases( if (clientGis.entitlement.result === 0) { isApplicantGisAvailable = false - - if (partnerAlwCalcSingle > 0) { - partnerAlw.cardDetail.collapsedText.push( - translations.detailWithHeading.partnerEligible - ) - } } else { allResults.client.gis.cardDetail.collapsedText.push( translations.detailWithHeading.calculatedBasedOnIndividualIncome @@ -318,10 +312,6 @@ export function InvSeparatedAllCases( allResults.client.gis.eligibility = clientGis.eligibility allResults.client.gis.entitlement.result = applicantGisResultT1 allResults.client.gis.entitlement.type = EntitlementResultType.FULL - allResults.client.gis.eligibility.detail, - (allResults.client.gis.cardDetail.mainText = future - ? `${translations.detail.futureEligible} ${translations.detail.futureExpectToReceive}` - : `${translations.detail.eligible} ${translations.detail.expectToReceive}`) allResults.partner.alw.cardDetail = partnerAlw.cardDetail allResults.partner.alw.entitlement.result = partnerAlwCalcSingle @@ -330,10 +320,6 @@ export function InvSeparatedAllCases( allResults.partner.alw.cardDetail.collapsedText.push( translations.detailWithHeading.calculatedBasedOnIndividualIncome ) - - partnerAlw.cardDetail.collapsedText.push( - translations.detailWithHeading.partnerEligible - ) } } } @@ -467,14 +453,6 @@ export function InvSeparatedAllCases( // .calculatedBasedOnIndividualIncome // ) } - if ( - !allResults.partner.gis.cardDetail.collapsedText.includes( - translations.detailWithHeading.partnerEligible - ) - ) - allResults.partner.gis.cardDetail.collapsedText.unshift( - translations.detailWithHeading.partnerEligible - ) // If client is eligible for ALW, need to recalculate estimate based on individual income if (clientAlw.eligibility.result === 'eligible') { @@ -513,12 +491,6 @@ export function InvSeparatedAllCases( detail: translations.detail.eligible, } - // cardDetails - allResults.client.alw.eligibility.detail, - (allResults.client.alw.cardDetail.mainText = future - ? `${translations.detail.futureEligible60} ${translations.detail.futureExpectToReceive}` - : `${translations.detail.eligible} ${translations.detail.expectToReceive}`) - allResults.client.alw.cardDetail.collapsedText.push( translations.detailWithHeading.calculatedBasedOnIndividualIncome ) @@ -628,16 +600,6 @@ export function InvSeparatedAllCases( ) } - if ( - allResults.client.gis.eligibility.reason === ResultReason.INCOME && - clientGis.entitlement.result > 0 - ) { - allResults.client.gis.eligibility.detail, - (allResults.client.gis.cardDetail.mainText = future - ? `${translations.detail.futureEligible} ${translations.detail.futureExpectToReceive}` - : `${translations.detail.eligible} ${translations.detail.expectToReceive}`) - } - consoleDev( '--- both are not eligible for alw - applicant oas > 0 & partner oas =0 --- end' ) @@ -690,16 +652,6 @@ export function InvSeparatedAllCases( } else { allResults.partner.gis.entitlement.result = partnerGisResultT1 allResults.partner.gis.entitlement.type = EntitlementResultType.FULL - - if ( - !partnerGis.cardDetail.collapsedText.includes( - translations.detailWithHeading.partnerEligible - ) - ) { - partnerGis.cardDetail.collapsedText.unshift( - translations.detailWithHeading.partnerEligible - ) - } } // add the amount calculated to the card.