diff --git a/package-lock.json b/package-lock.json index 822811d0..1bd5fb0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@mui/material": "^5.11.8", "@next/bundle-analyzer": "^10.1.3", "@paypal/react-paypal-js": "^7.8.2", - "@planet-sdk/common": "^0.1.34", + "@planet-sdk/common": "^0.1.42", "@sentry/browser": "^6.2.5", "@sentry/integrations": "^6.2.5", "@sentry/node": "^6.2.5", @@ -1812,9 +1812,9 @@ "dev": true }, "node_modules/@planet-sdk/common": { - "version": "0.1.34", - "resolved": "https://registry.npmjs.org/@planet-sdk/common/-/common-0.1.34.tgz", - "integrity": "sha512-oMPD7QvXVkLYVz5Q3OhU+mFypfs407aKqCGprHo0M9ALvDTDoOKT2HsjiETCXkuu4nJcGOYmLdwGyXbWXWyZGQ==", + "version": "0.1.42", + "resolved": "https://registry.npmjs.org/@planet-sdk/common/-/common-0.1.42.tgz", + "integrity": "sha512-C8rV0i3P9YyAUQlldFT91JxVpUX5rQcmKMB9IDqLKKByJ8794GTiL2y+zaJixBB1UnG/4isqyyPXTE2m1zomYg==", "dependencies": { "@types/geojson": "^7946.0.10" } @@ -10431,9 +10431,9 @@ } }, "@planet-sdk/common": { - "version": "0.1.34", - "resolved": "https://registry.npmjs.org/@planet-sdk/common/-/common-0.1.34.tgz", - "integrity": "sha512-oMPD7QvXVkLYVz5Q3OhU+mFypfs407aKqCGprHo0M9ALvDTDoOKT2HsjiETCXkuu4nJcGOYmLdwGyXbWXWyZGQ==", + "version": "0.1.42", + "resolved": "https://registry.npmjs.org/@planet-sdk/common/-/common-0.1.42.tgz", + "integrity": "sha512-C8rV0i3P9YyAUQlldFT91JxVpUX5rQcmKMB9IDqLKKByJ8794GTiL2y+zaJixBB1UnG/4isqyyPXTE2m1zomYg==", "requires": { "@types/geojson": "^7946.0.10" } diff --git a/package.json b/package.json index c9650888..02ab2ac6 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@mui/material": "^5.11.8", "@next/bundle-analyzer": "^10.1.3", "@paypal/react-paypal-js": "^7.8.2", - "@planet-sdk/common": "^0.1.34", + "@planet-sdk/common": "^0.1.42", "@sentry/browser": "^6.2.5", "@sentry/integrations": "^6.2.5", "@sentry/node": "^6.2.5", diff --git a/pages/index.tsx b/pages/index.tsx index 863c5d5b..1824b14b 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -11,7 +11,7 @@ import countriesData from "./../src/Utils/countriesData.json"; import { setCountryCode } from "src/Utils/setCountryCode"; import { DONATE } from "src/Utils/donationStepConstants"; import { - SentGift, + GiftDetails, FetchedProjectDetails, PaymentOptions, } from "src/Common/Types"; @@ -20,11 +20,13 @@ import { ContactDetails, } from "@planet-sdk/common/build/types/donation"; import { GetServerSideProps } from "next/types"; +import { createProjectDetails } from "src/Utils/createProjectDetails"; +import { NON_GIFTABLE_PROJECT_PURPOSES } from "src/Utils/projects/constants"; interface Props { projectDetails?: FetchedProjectDetails; donationStep: number | null; - giftDetails: SentGift | null; + giftDetails: GiftDetails | null; isGift: boolean; resolvedUrl?: string; isDirectDonation: boolean; @@ -79,7 +81,7 @@ function index({ setSelectedProjects, loadselectedProjects, setGiftDetails, - setisGift, + setIsGift, setpaymentSetup, setcurrency, setContactDetails, @@ -129,7 +131,7 @@ function index({ // If gift details are present, initialize gift in context if (giftDetails && isGift) { setGiftDetails(giftDetails); - setisGift(true); + setIsGift(true); } }, []); @@ -215,7 +217,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { // Variables that will be affected with Gift details let isGift = false; - let giftDetails: SentGift | null = null; + let giftDetails: GiftDetails | null = null; let frequency = "once"; // Variables that will be affected with context let hideTaxDeduction = false; @@ -250,7 +252,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { const queryCountry = context.query.country; const found = countriesData.some( (country) => - country.countryCode?.toUpperCase() === queryCountry.toUpperCase(), + country.countryCode?.toUpperCase() === queryCountry.toUpperCase() ); if (found) { country = queryCountry.toUpperCase(); @@ -276,18 +278,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { const paymentOptionsResponse = await apiRequest(requestParams); const paymentOptionsData: PaymentOptions = paymentOptionsResponse?.data; if (paymentOptionsData) { - projectDetails = { - id: paymentOptionsData.id, - name: paymentOptionsData.name, - description: paymentOptionsData.description, - purpose: paymentOptionsData.purpose, - ownerName: paymentOptionsData.ownerName, - taxDeductionCountries: paymentOptionsData.taxDeductionCountries, - image: paymentOptionsData.image, - ownerAvatar: paymentOptionsData.ownerAvatar, - isApproved: paymentOptionsData.isApproved ? true : false, - isTopProject: paymentOptionsData.isTopProject ? true : false, - }; + projectDetails = createProjectDetails(paymentOptionsData); donationStep = 1; } } catch (err) { @@ -360,18 +351,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { if (paymentSetupData) { currency = paymentSetupData.currency; paymentSetup = paymentSetupData; - projectDetails = { - id: paymentSetupData.id, - name: paymentSetupData.name, - description: paymentSetupData.description, - purpose: paymentSetupData.purpose, - ownerName: paymentSetupData.ownerName, - taxDeductionCountries: paymentSetupData.taxDeductionCountries, - image: paymentSetupData.image, - ownerAvatar: paymentSetupData.ownerAvatar, - isApproved: paymentSetupData.isApproved ? true : false, - isTopProject: paymentSetupData.isTopProject ? true : false, - }; + projectDetails = createProjectDetails(paymentSetupData); donationStep = 3; } } catch (err) { @@ -379,7 +359,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { } allowTaxDeductionChange = false; units = donation.units; - amount = donation.amount; + amount = Number(donation.amount); // Setting contact details from donor details if (donorData) { contactDetails = { @@ -429,29 +409,49 @@ export const getServerSideProps: GetServerSideProps = async (context) => { if (typeof context.query.utm_source === "string") utmSource = context.query.utm_source; - // Set gift details if there is s (support link) in the query params - if (context.query.s) { - try { - const requestParams = { - url: `/app/profiles/${context.query.s}`, - setshowErrorCard, - tenant, - locale, + // Handle s (support link) in the query params + if (typeof context.query.s === "string" && context.query.s.length > 0) { + if ( + projectDetails === null || + projectDetails.classification === "membership" || + NON_GIFTABLE_PROJECT_PURPOSES.includes(projectDetails.purpose) + ) { + // If project cannot have direct gift, remove 's' parameter by redirecting + const pathname = context.resolvedUrl.split("?")[0]; + const query = { ...context.query }; + delete query.s; + const queryString = new URLSearchParams( + query as Record + ).toString(); + + return { + redirect: { + destination: `${pathname}${queryString ? `?${queryString}` : ""}`, + permanent: true, + }, }; - const newProfile = await apiRequest(requestParams); - if (newProfile.data.type !== "tpo") { - isGift = true; - giftDetails = { - recipientName: newProfile.data.displayName, - recipientEmail: "", - message: "", - type: "direct", - recipient: newProfile.data.id, - recipientTreecounter: newProfile.data.slug, + } else { + // Set gift details if there is s (support link) in the query params for an eligible project + try { + const requestParams = { + url: `/app/profiles/${context.query.s}`, + setshowErrorCard, + tenant, + locale, }; + const newProfile = await apiRequest(requestParams); + if (newProfile.data.type !== "tpo") { + isGift = true; + giftDetails = { + recipientName: newProfile.data.displayName, + type: "direct", + recipient: newProfile.data.id, + recipientProfile: newProfile.data.slug, + }; + } + } catch (err) { + console.log("Error", err); } - } catch (err) { - console.log("Error", err); } } diff --git a/public/locales/en/common.json b/public/locales/en/common.json index c0c67c0c..47fe4dd8 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -7,6 +7,7 @@ "tree_other": "trees", "recipientName": "Recipient Name", "recipientNameRequired": "Recipient Name is required", + "recipientNameTooLong": "Recipient Name must be 35 characters or less", "email": "Email", "recipientEmail": "Recipient Email", "emailRequired": "Email is required", @@ -118,7 +119,7 @@ "selectLanguage": "Select Language", "trees": "trees", "logout": "Logout", - "directGiftRecipient": "This donation supports {{name}}'s Forest", + "giftDedicatedTo": "This donation is dedicated to {{name}}", "saveGiftDetails": "Save Gift Details", "giftSomeone": "My donation is dedicated to someone", "or": "OR", @@ -223,4 +224,4 @@ "top_project_standards_fulfilled": "The project inspection revealed that this project fulfilled at least 12 of the 19 Top Project <2>standards.", "standardsLink": "https://www.plant-for-the-planet.org/standards/", "enterValidEmail": "Please enter a valid email" -} \ No newline at end of file +} diff --git a/src/Common/InputTypes/MaterialTextField.tsx b/src/Common/InputTypes/MaterialTextField.tsx index 09e2a7ee..160209f1 100644 --- a/src/Common/InputTypes/MaterialTextField.tsx +++ b/src/Common/InputTypes/MaterialTextField.tsx @@ -5,61 +5,102 @@ import themeProperties from "./../../../styles/themeProperties"; const MaterialTextField = styled(TextField)({ width: "100%", color: "var(--primary-font-color)", - "& .MuiInputBase-input.MuiOutlinedInput-input": { + + // Label styles + "& label": { color: "var(--primary-font-color)", - height: "1.1876em", - lineHeight: "1.1876em", + fontFamily: themeProperties.fontFamily, + fontSize: "14px", + lineHeight: "1", }, - "& .MuiInputAdornment-root": { - color: "var(--primary-font-color)", + + // Base label positioning + "& .MuiInputLabel-root": { + transform: "translate(14px, 16px) scale(1)", // Matches original position + transformOrigin: "left top", + position: "absolute", + left: 0, + top: 0, + display: "block", + padding: 0, + pointerEvents: "none", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + maxWidth: "calc(100% - 24px)", }, + + // Shrunk/focused label "& label.Mui-focused": { color: "var(--primary-font-color)", fontFamily: themeProperties.fontFamily, }, - "& label": { - color: "var(--primary-font-color)", - fontFamily: themeProperties.fontFamily, - fontSize: "14px", - lineHeight: 1, - }, + "& .MuiInputLabel-outlined.MuiInputLabel-shrink": { transform: "translate(14px, -4px) scale(0.75)", - top: "-3px", - }, - "& .MuiOutlinedInput-notchedOutline": { - border: "0px!important", + top: "-3px", // Critical for matching original position + pointerEvents: "auto", + maxWidth: "calc(133% - 32px)", }, - "& .Mui-disabled.MuiInputLabel-root": { + + // Input styles + "& .MuiInputBase-input.MuiOutlinedInput-input": { color: "var(--primary-font-color)", + height: "20px", + lineHeight: "20px", + padding: "14px", }, - "& .Mui-disabled.MuiOutlinedInput-input ": { - color: "var(--disabled-font-color)", - WebkitTextFillColor: "initial", - }, + + // Input container "& .MuiOutlinedInput-root": { backgroundColor: "var(--background-color-dark)", - border: "0px!important", borderRadius: "10px", fontFamily: themeProperties.fontFamily, + minHeight: "44px", + + "& fieldset": { + border: "none", + }, }, - "& .MuiOutlinedInput-input": { - padding: "14px", + + // Input adornment + "& .MuiInputAdornment-root": { + color: "var(--primary-font-color)", + }, + + // Notched outline + "& .MuiOutlinedInput-notchedOutline": { + border: "0px!important", }, + + // Disabled state + "& .Mui-disabled": { + "&.MuiInputLabel-root": { + color: "var(--primary-font-color)", + }, + "&.MuiOutlinedInput-input": { + color: "var(--disabled-font-color)", + WebkitTextFillColor: "initial", + }, + }, + + // Multiline "& .MuiInputBase-multiline": { padding: "0px", }, + + // Autocomplete '& .MuiAutocomplete-inputRoot[class*="MuiOutlinedInput-root"]': { padding: "14px", - }, - '& .MuiAutocomplete-inputRoot[class*="MuiOutlinedInput-root"] .MuiAutocomplete-input': - { + + "& .MuiAutocomplete-input": { padding: "0px", + + "&:first-of-type": { + paddingLeft: "0px", + }, }, - '& .MuiAutocomplete-inputRoot[class*="MuiOutlinedInput-root"] .MuiAutocomplete-input:first-of-type': - { - paddingLeft: "0px", - }, + }, }); export default MaterialTextField; diff --git a/src/Common/Types/QueryParamContextInterface.ts b/src/Common/Types/QueryParamContextInterface.ts index e93aa8bf..e82b925e 100644 --- a/src/Common/Types/QueryParamContextInterface.ts +++ b/src/Common/Types/QueryParamContextInterface.ts @@ -1,10 +1,10 @@ import { CurrencyList, FetchedProjectDetails, + GiftDetails, OnBehalfDonor, PaymentOptions, PlanetCashSignupDetails, - SentGift, } from "."; import { ProjectMapInfo as Project } from "@planet-sdk/common/build/types/project"; import { User } from "@planet-sdk/common/build/types/user"; @@ -20,9 +20,9 @@ import { Dispatch, SetStateAction } from "react"; export default interface QueryParamContextInterface { isGift: boolean; - setisGift: Dispatch>; - giftDetails: SentGift | NoGift; - setGiftDetails: Dispatch>; + setIsGift: Dispatch>; + giftDetails: GiftDetails | NoGift; + setGiftDetails: Dispatch>; contactDetails: ContactDetails; setContactDetails: Dispatch>; country: string; diff --git a/src/Common/Types/index.tsx b/src/Common/Types/index.tsx index bf303391..18a9ce45 100644 --- a/src/Common/Types/index.tsx +++ b/src/Common/Types/index.tsx @@ -14,8 +14,16 @@ import { } from "@planet-sdk/common"; /** planet-donations only allows direct or invitation gifts */ +export type DirectGiftDetails = SentDirectGift & { + recipientName?: string; + recipientProfile?: string; +}; +export type InvitationGiftDetails = SentInvitationGift; + export type SentGift = SentDirectGift | SentInvitationGift; +export type GiftDetails = DirectGiftDetails | InvitationGiftDetails; + export interface PaymentProviderRequest { account?: string; gateway: PaymentGateway; @@ -38,7 +46,7 @@ export interface CreateDonationFunctionProps { paymentSetup: PaymentOptions; currency: string; contactDetails: ContactDetails; - giftDetails: SentGift | NoGift; + giftDetails: GiftDetails | NoGift; isGift: boolean; setIsPaymentProcessing: Dispatch>; setPaymentError: Dispatch>; @@ -101,7 +109,7 @@ export interface CreateDonationDataProps { contactDetails: ContactDetails; taxDeductionCountry: string | null; isGift: boolean; - giftDetails: SentGift | NoGift; + giftDetails: GiftDetails | NoGift; frequency: string; amount?: number | null; callbackUrl: string | undefined; @@ -119,7 +127,7 @@ export interface PlanetCashSignupDetails { purpose: "planet-cash-signup"; } -export interface FetchedProjectDetails { +export interface FetchedBaseProjectDetails { id: string; name: string; description?: string | null; @@ -127,11 +135,32 @@ export interface FetchedProjectDetails { ownerName: string | null; image?: string | null; purpose: ProjectPurpose; + classification: Nullable; taxDeductionCountries?: Array; isApproved: boolean; isTopProject: boolean; } +export interface FetchedTreeProjectDetails extends FetchedBaseProjectDetails { + purpose: "trees"; + classification: TreeProjectClassification; +} + +export interface FetchedFundsProjectDetails extends FetchedBaseProjectDetails { + purpose: "funds"; + classification: FundsProjectClassification; +} + +export interface FetchedOtherProjectDetails extends FetchedBaseProjectDetails { + purpose: "conservation" | "reforestation" | "bouquet" | "planet-cash"; + classification: null; +} + +export type FetchedProjectDetails = + | FetchedTreeProjectDetails + | FetchedFundsProjectDetails + | FetchedOtherProjectDetails; + export type ProjectPurpose = | "trees" | "conservation" @@ -140,7 +169,36 @@ export type ProjectPurpose = | "bouquet" | "planet-cash"; -export interface PaymentOptions extends FetchedProjectDetails { +export type TreeProjectClassification = + | "agroforestry" + | "managed-regeneration" + | "large-scale-planting" + | "mangroves" + | "natural-regeneration" + | "other-planting" + | "urban-planting"; + +export type FundsProjectClassification = + | "academy" + | "endowment" + | "forest-protection" + | "funding" + | "membership" + | "mixed" + | "neutral" + | "neutral-event" + | "penalty" + | "public-funds" + | "research" + | "sponsorship" + | "subscription" + | "subsidy"; + +export type ProjectClassification = + | TreeProjectClassification + | FundsProjectClassification; + +export type PaymentOptions = FetchedProjectDetails & { requestedCountry: string; effectiveCountry: string; frequencies: Frequencies; @@ -152,7 +210,7 @@ export interface PaymentOptions extends FetchedProjectDetails { unitCost: number; currency: string; destination: string; -} +}; export type UnitType = "tree" | "m2" | "currency" | CurrencyCode; @@ -169,6 +227,7 @@ export interface Gateways { paypal: Paypal; stripe: Stripe; offline?: Offline; + "planet-cash"?: PlanetCashGateway; } export interface Paypal { methods?: string[] | null; @@ -196,6 +255,13 @@ export interface Offline { account: string; } +export interface PlanetCashGateway { + account: string; + balance: number; + creditLimit: number; + available: number; +} + export interface OptionsEntity { id?: string; caption: string | null; diff --git a/src/Donations/Components/DonationsForm.tsx b/src/Donations/Components/DonationsForm.tsx index b26266f7..188ade15 100644 --- a/src/Donations/Components/DonationsForm.tsx +++ b/src/Donations/Components/DonationsForm.tsx @@ -34,6 +34,8 @@ import { } from "@planet-sdk/common/build/types/donation"; import { PaymentMethod } from "@stripe/stripe-js/types/api/payment-methods"; import { PaymentRequest } from "@stripe/stripe-js/types/stripe-js/payment-request"; +import { NON_GIFTABLE_PROJECT_PURPOSES } from "src/Utils/projects/constants"; +import { isPlanetCashAllowed } from "src/Utils/donationOptions"; function DonationsForm(): ReactElement { const { @@ -116,19 +118,29 @@ function DonationsForm(): ReactElement { const [isPaymentProcessing, setIsPaymentProcessing] = React.useState(false); const [paymentError, setPaymentError] = React.useState(""); //TODOO - confirm and remove - const canPayWithPlanetCash = + const canPayWithPlanetCash = isPlanetCashAllowed({ + profile, + isSignedUp, + projectDetails, + isGift, + giftDetails, + hasPlanetCashGateway: paymentSetup?.gateways["planet-cash"] !== undefined, + }); + + const canSendDirectGift = + projectDetails !== null && + projectDetails.classification !== "membership" && + !NON_GIFTABLE_PROJECT_PURPOSES.includes(projectDetails.purpose); + + const hasDirectGift = giftDetails.type === "direct"; + const canSendInvitationGift = projectDetails !== null && - projectDetails.purpose !== "funds" && - projectDetails.purpose !== "planet-cash" && - paymentSetup?.unitType === "tree" && //Enables planetcash for restoration projects with unitType tree only (TEMP) - profile !== null && - isSignedUp && - profile.planetCash !== null && - projectDetails.taxDeductionCountries?.includes( - profile.planetCash.country - ) && - !(isGift && giftDetails.recipientName === "") && - !(onBehalf && onBehalfDonor.firstName === ""); + !NON_GIFTABLE_PROJECT_PURPOSES.includes(projectDetails.purpose) && + !hasDirectGift && + ((projectDetails?.classification !== "membership" && + frequency === "once") || + (projectDetails?.classification === "membership" && + frequency !== "once")); //Only used for native pay. Is this still applicable, or should this be removed? const onPaymentFunction = async ( @@ -282,11 +294,6 @@ function DonationsForm(): ReactElement { const handlePlanetCashDonate = async () => { if (projectDetails) { setShowDisablePlanetCashButton(true); - const _onBehalfDonor = { - firstname: onBehalfDonor.firstName, - lastname: onBehalfDonor.lastName, - email: onBehalfDonor.email, - }; const _metadata = { utm_campaign: utmCampaign, @@ -294,14 +301,28 @@ function DonationsForm(): ReactElement { utm_source: utmSource, }; - const _gift = { - ...giftDetails, - }; + // Determine if gift info is complete + const isDirectGiftComplete = + giftDetails.type === "direct" && giftDetails.recipient.length > 0; - if (giftDetails.type === "direct") { - delete _gift.message; - delete _gift.recipientName; - } + const isInvitationGiftComplete = + giftDetails.type === "invitation" && + giftDetails.recipientName.length > 0; + + // Set _gift to null if incomplete, otherwise construct gift object + const _gift = isDirectGiftComplete + ? { + type: "direct", + recipient: giftDetails.recipient, + } + : isInvitationGiftComplete + ? { + type: "invitation", + recipientName: giftDetails.recipientName, + recipientEmail: giftDetails.recipientEmail, + message: giftDetails.message, + } + : null; // create Donation data const donationData = { @@ -309,9 +330,7 @@ function DonationsForm(): ReactElement { project: projectDetails.id, units: quantity, prePaid: true, - onBehalf: onBehalf, metadata: _metadata, - ...(onBehalf && { donor: _onBehalfDonor }), ...(isGift && { gift: _gift }), }; @@ -357,18 +376,14 @@ function DonationsForm(): ReactElement { {projectDetails.purpose !== "funds" && (

{t("donate")}

)} - {/* show PlanetCashSelector only if user is signed up and have a planetCash account */} {canPayWithPlanetCash && } - {!(onBehalf && onBehalfDonor.firstName === "") && - (projectDetails.purpose === "trees" && - (paymentSetup?.unitType !== "m2" || - giftDetails.type === "direct") ? ( -
- -
- ) : ( - <> - ))} + {(canSendDirectGift && hasDirectGift) || canSendInvitationGift ? ( +
+ +
+ ) : ( + <> + )} {process.env.RECURRENCY && showFrequencyOptions && paymentSetup && diff --git a/src/Donations/LeftPanel/GiftInfo.tsx b/src/Donations/LeftPanel/GiftInfo.tsx index fc1e2456..3b070b12 100644 --- a/src/Donations/LeftPanel/GiftInfo.tsx +++ b/src/Donations/LeftPanel/GiftInfo.tsx @@ -1,10 +1,10 @@ import { ReactElement } from "react"; import { useTranslation } from "next-i18next"; -import { SentGift } from "src/Common/Types"; +import { GiftDetails } from "src/Common/Types"; import styles from "./LeftPanel.module.scss"; interface Props { - giftDetails: SentGift; + giftDetails: GiftDetails; } const GiftInfo = ({ giftDetails }: Props): ReactElement => { @@ -13,11 +13,11 @@ const GiftInfo = ({ giftDetails }: Props): ReactElement => { return (

{t("dedicatedTo")}

- {giftDetails.type === "direct" && giftDetails.recipientTreecounter ? ( + {giftDetails.type === "direct" && giftDetails.recipientProfile ? ( {giftDetails.recipientName} @@ -26,7 +26,7 @@ const GiftInfo = ({ giftDetails }: Props): ReactElement => {

{giftDetails.recipientName}

)} - {giftDetails.message && ( + {giftDetails.type === "invitation" && giftDetails.message && (

{t("message")}: {giftDetails.message}

diff --git a/src/Donations/LeftPanel/LeftPanelInfo.tsx b/src/Donations/LeftPanel/LeftPanelInfo.tsx index 42e9922f..64dea0d1 100644 --- a/src/Donations/LeftPanel/LeftPanelInfo.tsx +++ b/src/Donations/LeftPanel/LeftPanelInfo.tsx @@ -7,7 +7,7 @@ import { PaymentOptions, PlanetCashSignupDetails, OnBehalfDonor, - SentGift, + GiftDetails, } from "src/Common/Types"; import Avatar from "./Avatar"; import TransactionSummary from "./TransactionSummary"; @@ -28,7 +28,7 @@ interface Props { quantity: number; currency: string; frequency: string; - giftDetails: SentGift | NoGift; + giftDetails: GiftDetails | NoGift; contactDetails: ContactDetails; isPlanetCashActive: boolean; isGift: boolean; diff --git a/src/Donations/Micros/DonationTypes/BouquetDonations.tsx b/src/Donations/Micros/DonationTypes/BouquetDonations.tsx index 3893f92d..ed3ce57e 100644 --- a/src/Donations/Micros/DonationTypes/BouquetDonations.tsx +++ b/src/Donations/Micros/DonationTypes/BouquetDonations.tsx @@ -131,12 +131,12 @@ function BouquetDonations({ setopenCurrencyModal }: Props): ReactElement { // return Math.trunc(Math.ceil(cost/5)*5); // } return ( - <> -
+
+
{paymentSetup && paymentSetup.frequencies && paymentSetup.frequencies[`${frequency}`].options.map( @@ -149,14 +149,14 @@ function BouquetDonations({ setopenCurrencyModal }: Props): ReactElement { setisCustomDonation(false); setCustomInputValue(""); }} - className={`funding-selection-option ${ + className={`bouquet-selection-option ${ approximatelyEqual(option.quantity, quantity) && !isCustomDonation - ? "funding-selection-option-selected" + ? "bouquet-selection-option-selected" : "" }`} > -
+
{paymentSetup.purpose === "conservation" ? option.quantity @@ -175,8 +175,8 @@ function BouquetDonations({ setopenCurrencyModal }: Props): ReactElement {
) : (
{ setisCustomDonation(true); @@ -197,7 +197,7 @@ function BouquetDonations({ setopenCurrencyModal }: Props): ReactElement { : []}

) => { // replaces any character other than number to blank @@ -239,7 +239,7 @@ function BouquetDonations({ setopenCurrencyModal }: Props): ReactElement {
) : (

@@ -299,7 +299,7 @@ function BouquetDonations({ setopenCurrencyModal }: Props): ReactElement {

)} - +
); } diff --git a/src/Donations/Micros/DonationTypes/FundingDonations.tsx b/src/Donations/Micros/DonationTypes/FundingDonations.tsx index f0b7eadd..062359da 100644 --- a/src/Donations/Micros/DonationTypes/FundingDonations.tsx +++ b/src/Donations/Micros/DonationTypes/FundingDonations.tsx @@ -103,12 +103,12 @@ function FundingDonations({ setopenCurrencyModal }: Props): ReactElement { const customInputRef = React.useRef(null); return ( - <> -
+
+
{paymentSetup && paymentSetup.frequencies && paymentSetup.frequencies[`${frequency}`] && @@ -280,7 +280,7 @@ function FundingDonations({ setopenCurrencyModal }: Props): ReactElement {
)} - +
); } function getPaymentOptionIcons(logoName: string) { diff --git a/src/Donations/Micros/GiftForm.tsx b/src/Donations/Micros/GiftForm.tsx index b6006674..b1c31dc6 100644 --- a/src/Donations/Micros/GiftForm.tsx +++ b/src/Donations/Micros/GiftForm.tsx @@ -25,14 +25,15 @@ const EMPTY_GIFT_DETAILS: Readonly = { export default function GiftForm(): ReactElement { const { t } = useTranslation("common"); - const [showEmail, setshowEmail] = React.useState(false); - const { giftDetails, setGiftDetails, isGift, setisGift } = + const [showEmail, setShowEmail] = React.useState(false); + const { giftDetails, setGiftDetails, isGift, setIsGift, projectDetails } = React.useContext(QueryParamContext); const defaultDetails: GiftFormData = { recipientName: giftDetails.recipientName || "", - recipientEmail: giftDetails.recipientEmail || "", - message: giftDetails.message || "", + recipientEmail: + (giftDetails.type === "invitation" && giftDetails.recipientEmail) || "", + message: (giftDetails.type === "invitation" && giftDetails.message) || "", }; const { @@ -78,7 +79,7 @@ export default function GiftForm(): ReactElement { name="show-gift-form-toggle" checked={isGift} onChange={() => { - setisGift((isGift) => !isGift); + setIsGift((isGift) => !isGift); }} id="show-gift-form-toggle" /> @@ -90,7 +91,16 @@ export default function GiftForm(): ReactElement { ( )} /> - {errors.recipientName && ( + {errors.recipientName !== undefined && (
- {t("recipientNameRequired")} + {errors.recipientName.message}
)}
- {showEmail ? ( -
-
-
-

{t("giftNotification")}

+ {projectDetails?.classification !== "membership" && ( + <> + {showEmail ? ( +
+
+
+

{t("giftNotification")}

+ +
+ + value.length === 0 || isEmailValid(value), + }, + }} + render={({ field: { onChange, value } }) => ( + + )} + /> + {errors.recipientEmail && ( +
+ {errors.recipientEmail.type === "required" + ? t("emailRequired") + : t("enterValidEmail")} +
+ )} +
+
+ ( + + )} + /> +
+
+ ) : ( +
- - value.length === 0 || isEmailValid(value), - }, - }} - render={({ field: { onChange, value } }) => ( - - )} - /> - {errors.recipientEmail && ( -
- {errors.recipientEmail.type === "required" - ? t("emailRequired") - : t("enterValidEmail")} -
- )} -
-
- ( - - )} - /> -
-
- ) : ( -
- -
+ )} + )}