425 lines
17 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, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import { useSubscription } from "../../hooks/useAuth";
import { formatFigure, frequencyLabel } from "../../utils/appUtils";
import { setSelfTenant } from "../../slices/localVariablesSlice";
import { error } from "pdf-lib";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { CouponDiscount } from "../../pages/Home/HomeSchema";
import SelectedPlanSkeleton from "./SelectedPlaneSkeleton";
const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
const { frequency, planId } = useParams();
const [selectedFrequency, setSelectedFrequency] = useState(
parseInt(frequency)
);
const dispatch = useDispatch();
const client = useSelector(
(store) => store.localVariables.selfTenant.details
);
const [selectedPlan, setSelectedPlan] = useState(planId);
const [currentPlan, setCurrentPlan] = useState(null);
const [failPayment, setFailPayment] = useState(null);
const {
data: plans,
isError,
isLoading,
error,
refetch,
isRefetching,
} = useSubscription(selectedFrequency);
const handleChange = (e) => {
setSelectedPlan(e.target.value);
};
useEffect(() => {
if (!plans || plans.length === 0) return;
// Prefer route param if exists, else default to first plan
const matchingPlan = plans.find((p) => p.planId === planId) || plans[0];
setSelectedPlan(matchingPlan.id);
setCurrentPlan(matchingPlan);
// Dispatch correct plan + frequency only once data is ready
dispatch(
setSelfTenant({
planId: matchingPlan.id,
frequency: selectedFrequency,
})
);
}, [plans, selectedFrequency, planId, dispatch]);
const handleNextStep = () => {
if (!selectedPlan) {
toast.warning("Please select a plan before continuing.");
return;
}
dispatch(
setSelfTenant({
planId: selectedPlan,
frequency: selectedFrequency,
})
);
setStepStatus((prev) => ({ ...prev, 2: "success" }));
onNext();
};
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ resolver: zodResolver(CouponDiscount) });
return (
<div className="container text-start ">
<div className="row gy-5 align-items-center justify-content-around">
<div className="col-sm-12 col-md-8">
<div className="row">
<div className="col-12 mb-3 text-start">
<h4>Choose the Perfect Plan for Your Organization</h4>
<p className="text-muted small mb-3">
Select a plan that fits your teams needs and unlock the
features that drive productivity.
</p>
<div>
<div className="form-check form-check-inline mt-4">
<input
className="form-check-input"
type="radio"
name="frequencyOptions"
id="frequencyMonthly"
value={0}
checked={selectedFrequency === 0}
onChange={(e) =>
setSelectedFrequency(Number(e.target.value))
}
/>
<label
className="form-check-label"
htmlFor="frequencyMonthly"
>
Monthly
</label>
</div>
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="frequencyOptions"
id="frequencyQuarterly"
value={1}
checked={selectedFrequency === 1}
onChange={(e) =>
setSelectedFrequency(Number(e.target.value))
}
/>
<label
className="form-check-label"
htmlFor="frequencyQuarterly"
>
Quarterly
</label>
</div>
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="frequencyOptions"
id="frequencyHalfYear"
value={2}
checked={selectedFrequency === 2}
onChange={(e) =>
setSelectedFrequency(Number(e.target.value))
}
/>
<label
className="form-check-label"
htmlFor="frequencyHalfYear"
>
Half-Yearly
</label>
</div>
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="frequencyOptions"
id="frequencyYearly"
value={3}
checked={selectedFrequency === 3}
onChange={(e) =>
setSelectedFrequency(Number(e.target.value))
}
/>
<label className="form-check-label" htmlFor="frequencyYearly">
Yearly
</label>
</div>
</div>
</div>
{isError && (
<div className="col-12 col-md-6 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 />
) : (
<>
<div className="row">
{plans?.map((plan) => (
<div
key={plan?.id}
className="col-12 col-md-4 mb-md-3 mb-2"
>
<div
className={`form-check custom-option custom-option-basic text-start w-100 bg-light-primary ${
selectedPlan === plan?.id
? "border border-primary shadow-md"
: ""
}`}
>
<label
className="form-check-label custom-option-content w-100"
htmlFor={`customRadioTemp${plan?.id}`}
>
<input
name="customRadioTemp"
className="form-check-input"
type="radio"
value={plan?.id}
id={`customRadioTemp${plan?.id}`}
checked={selectedPlan === plan?.id}
onChange={handleChange}
/>
<span className="custom-option-header d-flex justify-content-between align-items-center">
<span className="h6 mb-0">{plan?.description}</span>
</span>
<span className="custom-option-body d-block ">
<small>
Price -{" "}
<span className="fw-medium">
{plan.currency?.symbol} {plan.price} per{" "}
{frequencyLabel(selectedFrequency)}
</span>
</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(selectedFrequency, true)}
</small>
</span>
</label>
</div>
</div>
))}
</div>
{selectedPlan && (
<div className="col-12 text-start">
<div className="border-warning mt-3">
{(() => {
const selected = plans?.find(
(p) => p.id === selectedPlan
);
if (!selected) return null;
const {
price,
frequency,
trialDays,
maxUser,
maxStorage,
currency,
features,
} = selected;
return (
<>
<div className="row g-2 mb-3">
<div className="col-sm-12 col-md-4 text-center">
<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-12 col-md-4 text-center">
<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-12 col-md-4 text-center">
<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"
>
<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-2">
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>
<div className="d-flex flex-row align-items-end gap-2 mt-1">
<div className="">
<label className="form-lable ">
Apply Coupon
<input
type="text"
{...register("coupon")}
className="form-control form-control-sm mt-1"
/>
</label>
</div>
<div>
{" "}
<button className="btn btn-primary btn-sm">
Apply
</button>
</div>
</div>
{/* {errors.coupon && (<small>{error.coupon.message}</small>)} */}{" "}
<small className="m-0">
Currently, no coupon codes are available!{" "}
</small>
<hr className="divider border-2 my-1" />
<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(frequency, true)}
</h5>
</div>
<div className="d-flex justify-content-between">
<h6 className="fs-4">Total Price</h6>
<h5 className="fs-3">
{formatFigure(
frequencyLabel(
selectedFrequency,
true,
true
)?.planDurationInInt * price,
{
type: "currency",
currency: currency.currencyCode,
}
)}
</h5>
</div>
</div>
</>
);
})()}
</div>
</div>
)}
</>
)}
</div>
</div>
{/* Image Section */}
<div className="d-none d-md-flex col-md-4 justify-content-center align-items-center">
<img
src="/img/illustrations/undraw_pricing.svg"
alt="image"
className="img-fluid"
style={{
maxWidth: "100%",
height: "auto",
objectFit: "contain",
}}
/>
</div>
</div>
<div className="col-12 d-flex justify-content-end m-md-0 mt-3">
<button
type="submit"
className="btn btn-label-primary d-flex align-items-center me-2"
onClick={handleNextStep}
>
Next <i className="bx bx-chevron-right"></i>
</button>
</div>
</div>
);
};
export default SelectPlan;