428 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useMemo, useEffect } from "react";
import { useSubscription } from "../../hooks/useAuth";
import { useParams } from "react-router-dom";
import { useCreateTenant, useIndustries } from "../../hooks/useTenant";
import {
formatCurrency,
formatFigure,
frequencyLabel,
} from "../../utils/appUtils";
import { formatUTCToLocalTime } from "../../utils/dateUtils";
import { PaymentRepository } from "../../repositories/PaymentRepository";
import { useMakePayment } from "../../hooks/usePayment";
import { useDispatch, useSelector } from "react-redux";
import { setSelfTenant } from "../../slices/localVariablesSlice";
import { unblockUI } from "../../utils/blockUI";
import showToast from "../../services/toastService";
import SelectedPlanSkeleton from "./SelectedPlanSkeleton";
const ProcessedPayment = ({
onNext,
resetPaymentStep,
setCurrentStep,
setStepStatus,
resetFormStep,
}) => {
const { planName } = useParams();
const {
details: client,
planId: selectedPlanId,
frequency,
} = useSelector((store) => store.localVariables.selfTenant);
const [selectedPlan, setSelectedPlan] = useState(null);
const [currentPlan, setCurrentPlan] = useState(null);
const [failPayment, setFailPayment] = useState(null);
const {
data: plans,
isError: isPlanError,
isLoading,
isError,
isRefetching,
refetch,
} = useSubscription(frequency);
useEffect(() => {
if (!plans || !selectedPlanId) return;
const selected = plans.find((p) => p.id === selectedPlanId);
setSelectedPlan(selected);
}, [plans, selectedPlanId]);
const loadScript = (src) =>
new Promise((resolve) => {
const script = document.createElement("script");
script.src = src;
script.onload = () => resolve(true);
script.onerror = () => resolve(false);
document.body.appendChild(script);
});
const { mutate: MakePayment, isPending } = useMakePayment(
(response) => {
unblockUI();
onNext(response);
},
(fail) => {
unblockUI();
setFailPayment(fail);
onNext(fail);
},
currentPlan
);
const ProcessToPayment = async () => {
setStepStatus((prev) => ({ ...prev, 3: "success" }));
setCurrentStep(4);
const res = await loadScript(
"https://checkout.razorpay.com/v1/checkout.js"
);
if (!res) {
alert("Failed to load Razorpay SDK");
return;
}
let price = 0;
price =
frequencyLabel(selectedPlan?.frequency, true, true)?.planDurationInInt *
selectedPlan?.price;
MakePayment({ amount: price });
};
const handleRetry = () => {
setFailPayment(null);
if (typeof resetPaymentStep === "function") resetPaymentStep();
};
const handlPrevious = () => {
setCurrentStep,
setStepStatus((prev) => ({ ...prev, 2: "pending", 3: "pending" }));
setCurrentStep(2);
};
// useEffect(() => {
// if (!client || Object.keys(client).length === 0) {
// setFailPayment(null);
// if (typeof resetFormStep === "function") {
// resetFormStep();
// }
// }
// }, [client]);
if (failPayment) {
return (
<div className="container-md mt-5 text-center">
<div className="d-flex flex-column align-items-center justify-content-center">
<div
className="bg-danger p-3 rounded-circle mb-3 d-flex align-items-center justify-content-center"
style={{ width: "70px", height: "70px" }}
>
<i className="bx bx-x fs-1 text-white fw-bold"></i>
</div>
<h4 className="text-danger mb-2">Payment Failed!</h4>
<p className="text-muted">
Unfortunately, your payment could not be completed. Please try again
or use a different payment method.
</p>
<div className="mt-4 d-flex gap-3 flex-column flex-md-row justify-content-center">
<button
className="btn btn-primary px-4 py-2 fw-semibold"
onClick={handleRetry}
>
Retry Payment
</button>
<a
href="/"
className="btn btn-outline-secondary px-4 py-2 fw-semibold"
>
Go Back to Dashboard
</a>
</div>
{failPayment?.error && (
<div className="alert alert-light-danger mt-4 w-75 mx-auto text-start">
<strong>Error Details:</strong>
<pre className="small mb-0 mt-2 text-wrap">
{JSON.stringify(failPayment.error, null, 2)}
</pre>
</div>
)}
</div>
</div>
);
}
return (
<div className="container-md text-start ">
<div className="row gx-1 gy-3 justify-content-between">
<div className="col-12 col-md-6">
<div className="row">
<div className="col-12 mb-3 text-start">
<h4>Youve Selected the Perfect Plan for Your Organization</h4>
<p className="text-muted small mb-3">
Great choice! This plan is tailored to meet your teams needs
and help you maximize productivity.
</p>
</div>
{isError && (
<div className="col-12 col-md text-center">
<p className="text-muted">{error?.message}</p>
<small>{error?.name}</small>
<small
className={`text-muted ${
isRefetching ? "cursor-notallowed" : "cursor-pointer"
}`}
onClick={refetch}
>
{isRefetching ? (
<>
<i
className={`bx bx-loader-alt ${
isRefetching ? "bx-spin" : ""
}`}
></i>{" "}
Retrying...
</>
) : (
"Try to refetch"
)}
</small>
</div>
)}
{isLoading ? (
<SelectedPlanSkeleton />
) : (
<>
{selectedPlan && (
<div className="col-12 col-md-8 mb-md-3 mb-2">
<div className="custom-option custom-option-basic text-start w-100 bg-light-primary border border-primary shadow-md p-3 rounded-3">
<div className="custom-option-header d-flex justify-content-between align-items-center">
<span className="h6 mb-0">
{selectedPlan?.description}
</span>
<i className="bx bx-check-circle text-primary fs-4"></i>
</div>
<div className="d-flex justify-content-between mt-1">
<small>
Price -{" "}
<span className="fw-medium">
{selectedPlan.currency?.symbol} {selectedPlan.price}{" "}
per {frequencyLabel(frequency)}
</span>
</small>
</div>
<div className="d-flex flex-row gap-3 mt-1 align-items-center">
<small>{selectedPlan?.planName}</small>
<small className="d-block mt-1 text-xs border border-primary text-center rounded text-secondary w-min text-nowrap px-1">
billed {frequencyLabel(frequency, true)}
</small>
</div>
</div>
</div>
)}
{selectedPlan && (
<div className="col-12 text-start">
<div className="border-warning mt-3">
{(() => {
const {
planName,
description,
price,
frequency,
trialDays,
maxUser,
maxStorage,
currency,
features,
} = selectedPlan;
return (
<>
<div className="row g-2 mb-3">
<div className="col-sm-6 col-md-4">
<div className="border rounded-3 p-2 bg-light">
<i className="bx bx-user me-1 text-primary"></i>
<strong>Max Users:</strong> {maxUser}
</div>
</div>
<div className="col-sm-6 col-md-4">
<div className="border rounded-3 p-2 bg-light">
<i className="bx bx-hdd me-1 text-primary"></i>
<strong>Max Storage:</strong> {maxStorage} MB
</div>
</div>
<div className="col-sm-6 col-md-4">
<div className="border rounded-3 p-2 bg-light">
<i className="bx bx-time-five me-1 text-primary"></i>
<strong>Trial Days:</strong> {trialDays}
</div>
</div>
</div>
<h6 className="fw-bold text-secondary mb-2">
Included Features
</h6>
<div className="row ">
{features &&
Object.entries(features?.modules || {})
.filter(([key]) => key !== "id")
.map(([key, mod]) => (
<div
key={key}
className="col-4 mb-2 d-flex align-items-center mb-1"
>
<i
className={`fa-regular ${
mod.enabled
? "fa-circle-check text-success"
: "fa-circle-xmark text-danger"
}`}
></i>
<small className="ms-1">{mod.name}</small>
</div>
))}
</div>
<h6 className="fw-bold text-secondary mt-3 mb-1">
Support
</h6>
<ul className="list-unstyled d-flex flex-wrap gap-3 align-items-center mb-0 small">
{features?.supports?.emailSupport && (
<li className="d-flex align-items-center">
<i className="bx bx-envelope text-primary me-1 fs-5"></i>
Email Support
</li>
)}
{features?.supports?.phoneSupport && (
<li className="d-flex align-items-center">
<i className="bx bx-phone text-primary me-1 fs-5"></i>
Phone Support
</li>
)}
{features?.supports?.prioritySupport && (
<li className="d-flex align-items-center">
<i className="bx bx-star text-warning me-1 fs-5"></i>
Priority Support
</li>
)}
</ul>
<hr className="divider border-2 " />
<div className="d-flex flex-column co-12 ">
<div className="d-flex justify-content-between">
<h6 className="fs-6 m-0">Duration</h6>
<h5 className="fs-6 m-0">
{frequencyLabel(
selectedPlan?.frequency,
true
)}
</h5>
</div>
<div className="d-flex justify-content-between">
<h6 className="fs-4">Total Price</h6>
<h5 className="fs-3">
{formatFigure(
frequencyLabel(
selectedPlan?.frequency,
true,
true
)?.planDurationInInt * price,
{
type: "currency",
currency:
selectedPlan?.currency.currencyCode,
}
)}
</h5>
</div>
</div>
</>
);
})()}
</div>
</div>
)}
</>
)}
</div>
</div>
<div className="col-12 col-md-4 ">
{client && (
<div className="text-start">
<h6 className="fs-md-4 my-4">
Confirm your organization details.
</h6>
<div className="row g-2">
<div className="col-sm-6 mb-2">
<strong>Name:</strong>
</div>
<div className="col-sm-6 mb-2">
{client.firstName} {client.lastName}
</div>
<div className="col-sm-6">
<strong>Email:</strong>
</div>
<div className="col-sm-6 mb-2 text-wrap">{client.email}</div>
<div className="col-sm-6 mb-2">
<strong>Contact Number:</strong>
</div>
<div className="col-sm-6 mb-2">{client.contactNumber}</div>
<div className="col-sm-6 mb-2">
<strong>Organization Name:</strong>
</div>
<div className="col-sm-6 mb-2">{client.organizationName}</div>
<div className="col-sm-6 mb-2">
<strong>Onboarding Date:</strong>
</div>
<div className="col-sm-6">
{formatUTCToLocalTime(client.onBoardingDate)}
</div>
<div className="col-sm-6 mb-2">
<strong>Billing Address:</strong>
</div>
<div className="col-sm-6 mb-2">{client.billingAddress}</div>
<div className="col-sm-6 mb-2">
<strong>Industry :</strong>
</div>
<div className="col-sm-6 mb-2">{client?.industry?.name}</div>
</div>
</div>
)}
</div>
</div>
<div className="col-12 d-flex flex-column flex-md-row justify-content-between gap-2 mt-3">
<button
type="submit"
className="btn btn-label-primary d-flex align-items-center justify-content-center w-md-auto"
onClick={handlPrevious}
>
<i className="bx bx-chevron-left me-1"></i> Previous
</button>
<button
type="submit"
className="btn btn-label-primary d-flex align-items-center justify-content-center w-md-auto"
onClick={() => ProcessToPayment(currentPlan?.price)}
disabled={isPending}
>
{isPending ? (
<>
<i className="bx bx-loader-alt bx-md bx-spin me-2"></i> Please
Wait...
</>
) : (
"Proceed To Payment"
)}
</button>
</div>
</div>
);
};
export default ProcessedPayment;