feat: questionnaire

This commit is contained in:
Денис Катаев 2024-01-23 00:15:05 +00:00 committed by Victor Ershov
parent 28110b6a2e
commit 037e1078ec
8 changed files with 149 additions and 55 deletions

View File

@ -66,6 +66,7 @@ export interface SubscriptionReceipt {
autorenewable: boolean;
error: string;
links?: IPayPalLink[];
stripe_status?: string;
};
}

View File

@ -232,7 +232,9 @@ function App(): JSX.Element {
<Route
path={routes.client.paymentResult()}
element={<PaymentResultPage />}
/>
>
<Route path=":id" element={<PaymentResultPage />} />
</Route>
<Route
path={routes.client.paymentSuccess()}
element={<PaymentSuccessPage />}

View File

@ -13,9 +13,13 @@ import { useNavigate } from "react-router-dom";
interface ICheckoutFormProps {
children?: JSX.Element | null;
subscriptionReceiptId?: string;
}
export default function CheckoutForm({ children }: ICheckoutFormProps) {
export default function CheckoutForm({
children,
subscriptionReceiptId,
}: ICheckoutFormProps) {
const stripe = useStripe();
const elements = useElements();
const dispatch = useDispatch();
@ -28,7 +32,6 @@ export default function CheckoutForm({ children }: ICheckoutFormProps) {
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!stripe || !elements) {
return;
}
@ -39,7 +42,7 @@ export default function CheckoutForm({ children }: ICheckoutFormProps) {
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: `https://${window.location.host}/payment/result`,
return_url: `https://${window.location.host}/payment/result/${subscriptionReceiptId}`,
},
});
if (error) {
@ -48,14 +51,12 @@ export default function CheckoutForm({ children }: ICheckoutFormProps) {
dispatch(actions.status.update("subscribed"));
navigate(routes.client.paymentSuccess());
}
} catch(error) {
console.log('error -> ', error);
} catch (error) {
console.log("error -> ", error);
setMessage("Oops! Something went wrong.");
} finally {
} finally {
setIsProcessing(false);
}
}
};
return (
@ -66,12 +67,18 @@ export default function CheckoutForm({ children }: ICheckoutFormProps) {
>
{children ? children : null}
<PaymentElement onReady={() => setFormReady(true)} />
<MainButton color="blue" disabled={isProcessing || !formReady} id="submit">
<MainButton
color="blue"
disabled={isProcessing || !formReady}
id="submit"
>
<span id="button-text">
{isProcessing ? "Processing..." : "Pay now"}
</span>
</MainButton>
<Title variant="h5" style={{color: 'red'}}>{message}</Title>
<Title variant="h5" style={{ color: "red" }}>
{message}
</Title>
</form>
);
}

View File

@ -1,15 +1,91 @@
import { useNavigate } from "react-router-dom";
import routes from "@/routes";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";
import { useDispatch } from "react-redux";
import { actions } from "@/store";
// import { SubscriptionReceipts, useApi, useApiCall } from "@/api";
// import { useAuth } from "@/auth";
import styles from "./styles.module.css";
import Loader from "@/components/Loader";
function PaymentResultPage(): JSX.Element {
// const api = useApi();
// const { token } = useAuth();
const navigate = useNavigate();
const dispatch = useDispatch();
const [searchParams] = useSearchParams();
const status = searchParams.get("redirect_status");
status;
// const { id } = useParams();
// const requestTimeOutRef = useRef<NodeJS.Timeout>();
const [isLoading] = useState(true);
// const [subscriptionReceipt, setSubscriptionReceipt] =
// useState<SubscriptionReceipts.SubscriptionReceipt>();
// const loadData = useCallback(async () => {
// if (!id) {
// return null;
// }
// const getSubscriptionReceiptStatus = async () => {
// const { subscription_receipt } = await api.getSubscriptionReceipt({
// token,
// id,
// });
// const { stripe_status } = subscription_receipt.data;
// if (stripe_status === "incomplete") {
// requestTimeOutRef.current = setTimeout(
// getSubscriptionReceiptStatus,
// 3000
// );
// }
// setSubscriptionReceipt(subscription_receipt);
// return { subscription_receipt };
// };
// return getSubscriptionReceiptStatus();
// }, [api, id, token]);
// useApiCall<SubscriptionReceipts.Response | null>(loadData);
// useEffect(() => {
// if (!subscriptionReceipt) {
// if (id?.length) return;
// return () => {
// if (requestTimeOutRef.current) {
// clearTimeout(requestTimeOutRef.current);
// }
// navigate(routes.client.paymentFail());
// };
// }
// const { stripe_status } = subscriptionReceipt.data;
// if (stripe_status === "succeeded") {
// dispatch(actions.status.update("subscribed"));
// setIsLoading(false);
// return () => {
// if (requestTimeOutRef.current) {
// clearTimeout(requestTimeOutRef.current);
// }
// navigate(routes.client.paymentSuccess());
// };
// } else if (stripe_status === "payment_failed") {
// setIsLoading(false);
// return () => {
// if (requestTimeOutRef.current) {
// clearTimeout(requestTimeOutRef.current);
// }
// navigate(routes.client.paymentFail());
// };
// }
// }, [dispatch, id, navigate, subscriptionReceipt]);
// useEffect(() => {
// return () => {
// if (requestTimeOutRef.current) {
// clearTimeout(requestTimeOutRef.current);
// }
// };
// }, []);
useEffect(() => {
if (status === "succeeded") {
@ -19,7 +95,7 @@ function PaymentResultPage(): JSX.Element {
return navigate(routes.client.paymentFail());
}, [navigate, status, dispatch]);
return <></>;
return <div className={styles.page}>{isLoading && <Loader />}</div>;
}
export default PaymentResultPage;

View File

@ -0,0 +1,15 @@
.page {
position: relative;
height: fit-content;
min-height: calc(100vh - 103px);
flex: auto;
/* max-height: -webkit-fill-available; */
color: #fff;
overflow-y: scroll;
padding-bottom: 180px;
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
justify-content: center;
}

View File

@ -7,15 +7,25 @@ import {
import { PaymentRequest } from "@stripe/stripe-js";
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
import styles from "./styles.module.css";
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import routes from "@/routes";
interface ApplePayButtonProps {
activeSubPlan: ISubscriptionPlan | null;
client_secret: string;
subscriptionReceiptId?: string;
}
function ApplePayButton({ activeSubPlan, client_secret }: ApplePayButtonProps) {
function ApplePayButton({
activeSubPlan,
client_secret,
subscriptionReceiptId,
}: ApplePayButtonProps) {
const stripe = useStripe();
const elements = useElements();
const dispatch = useDispatch();
const navigate = useNavigate();
const [paymentRequest, setPaymentRequest] = useState<PaymentRequest | null>(
null
);
@ -63,13 +73,22 @@ function ApplePayButton({ activeSubPlan, client_secret }: ApplePayButtonProps) {
return;
}
navigate(`${routes.client.paymentResult()}/${subscriptionReceiptId}`);
// Show a success message to your customer
// There's a risk of the customer closing the window before callback
// execution. Set up a webhook or plugin to listen for the
// payment_intent.succeeded event that handles any business critical
// post-payment actions.
});
}, [activeSubPlan, client_secret, elements, stripe]);
}, [
activeSubPlan,
client_secret,
dispatch,
elements,
navigate,
stripe,
subscriptionReceiptId,
]);
return (
<>

View File

@ -28,21 +28,12 @@ export function StripePage(): JSX.Element {
useState<Promise<Stripe | null> | null>(null);
const [subPlans, setSubPlans] = useState<ISubscriptionPlan[] | null>(null);
const [clientSecret, setClientSecret] = useState<string>("");
const [subscriptionReceiptId, setSubscriptionReceiptId] =
useState<string>("");
const [isLoading, setIsLoading] = useState(true);
if (!activeSubPlan) {
navigate(routes.client.priceList());
}
// const timeoutRef = useRef<NodeJS.Timeout>();
// const appleReceipt = async () => {
// const { subscription_receipt } = await api.createSubscriptionReceipt({
// token,
// receiptData: "",
// autorenewable: true,
// sandbox: true,
// });
// console.log(subscription_receipt);
// };
useEffect(() => {
(async () => {
@ -59,26 +50,6 @@ export function StripePage(): JSX.Element {
})();
}, [activeSubPlan, api, locale, navigate]);
// useEffect(() => {
// timeoutRef.current = setTimeout(async () => {
// const { subscription_receipt } = await api.createSubscriptionReceipt({
// token,
// way: "stripe",
// subscription_receipt: {
// sub_plan_id: activeSubPlan?.id || "stripe.7",
// },
// });
// const { client_secret } = subscription_receipt.data;
// setClientSecret(client_secret);
// setIsLoading(false);
// }, 4000);
// return () => {
// if (timeoutRef.current) {
// clearTimeout(timeoutRef.current);
// }
// };
// }, [api, token]);
useEffect(() => {
(async () => {
const { subscription_receipt } = await api.createSubscriptionReceipt({
@ -88,7 +59,9 @@ export function StripePage(): JSX.Element {
sub_plan_id: activeSubPlan?.id || "stripe.7",
},
});
const { id } = subscription_receipt;
const { client_secret } = subscription_receipt.data;
setSubscriptionReceiptId(id);
setClientSecret(client_secret);
setIsLoading(false);
})();
@ -109,16 +82,17 @@ export function StripePage(): JSX.Element {
<p className={styles.email}>{email}</p>
</>
)}
{stripePromise && clientSecret && (
{stripePromise && clientSecret && subscriptionReceiptId && (
<Elements stripe={stripePromise} options={{ clientSecret }}>
<ApplePayButton
activeSubPlan={activeSubPlan}
client_secret={clientSecret}
subscriptionReceiptId={subscriptionReceiptId}
/>
{activeSubPlan && (
<SubPlanInformation subPlan={activeSubPlan} subPlans={subPlans} />
)}
<CheckoutForm></CheckoutForm>
<CheckoutForm subscriptionReceiptId={subscriptionReceiptId} />
</Elements>
)}
</div>

View File

@ -1,14 +1,14 @@
import Title from "@/components/Title";
// import Title from "@/components/Title";
import styles from "./styles.module.css";
import MainButton from "@/components/MainButton";
import { useNavigate } from "react-router-dom";
import routes from "@/routes";
import { useSelector } from "react-redux";
import { selectors } from "@/store";
// import { useSelector } from "react-redux";
// import { selectors } from "@/store";
function WorksForUsPage() {
const navigate = useNavigate();
const { relationshipProblem } = useSelector(selectors.selectQuestionnaire);
// const { relationshipProblem } = useSelector(selectors.selectQuestionnaire);
const handleBack = () => {
navigate(-1);
@ -22,7 +22,7 @@ function WorksForUsPage() {
<section className={`${styles.page} page`}>
<img src="/starry_key.svg" alt="The starry key" />
<div>
{relationshipProblem === "very_unhappy" && (
{/* {relationshipProblem === "very_unhappy" && (
<Title variant="h1" className={styles.title}>
Weve got you covered! Well start with small, personalized insights
into you and your partners personality traits.
@ -40,7 +40,7 @@ function WorksForUsPage() {
<br /> Let's find out what's working (and what isnt) and go from
there.
</Title>
)}
)} */}
<p className={styles.text}>
Now, we need some information about{" "}
<span className={styles.blue}>Your Partners Profile</span> to create