initially setupuser can update or revnew plan

This commit is contained in:
pramod.mahajan 2025-10-30 00:18:37 +05:30
parent d81ffe86b7
commit 7b4d19a932
7 changed files with 376 additions and 159 deletions

View File

@ -145,7 +145,7 @@
}
.text-xxs { font-size: 0.55rem; } /* 8px */
.text-xs { font-size: 0.75rem; } /* 12px */
.text-sm { font-size: 0.875rem; } /* 14px */
.text-sm { font-size: 0.675rem; } /* 14px */
.text-base { font-size: 1rem; } /* 16px */
.text-lg { font-size: 1.125rem; } /* 18px */
.text-xl { font-size: 1.25rem; } /* 20px */

View File

@ -1,12 +1,12 @@
import React, { useEffect, useState } from "react";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { VIEW_DOCUMENT } from "../../utils/constants";
import { SUPPER_TENANT, VIEW_DOCUMENT } from "../../utils/constants";
import { useProfile } from "../../hooks/useProfile";
import { useParams } from "react-router-dom";
const EmployeeNav = ({ onPillClick, activePill }) => {
const { employeeId } = useParams();
const [isAbleToViewDocuments, setIsAbleToViewDocuments] = useState(false);
const isSuperUser = useHasUserPermission(SUPPER_TENANT)
const canViewDocuments = useHasUserPermission(VIEW_DOCUMENT);
const { profile } = useProfile();
@ -24,6 +24,7 @@ const EmployeeNav = ({ onPillClick, activePill }) => {
icon: "bx bx-file",
label: "Documents",
},
(isSuperUser && { key: "setting", icon: "bx bx-cog", label: "Setting" }),
// { key: "activities", icon: "bx bx-grid-alt", label: "Activities" },
].filter(Boolean);
return (

View File

@ -0,0 +1,51 @@
import React, { useState } from "react";
import SubscriptionOverview from "./SubscriptionOverview";
const ManageSubscription = () => {
const [activeTab, setActiveTab] = useState("plan");
const renderContent = () => {
switch (activeTab) {
case "plan":
return <SubscriptionOverview/>
case "billing":
return <div className="p-3">💳 <strong>Billing Info</strong> section content goes here.</div>;
default:
return null;
}
};
return (
<div className="card page-min-h">
<div className=" border-bottom-0 pb-0 overflow-auto p-2">
<ul className="nav nav-tabs">
<li className="nav-item text-sm">
<small
className={` nav-link ${activeTab === "plan" ? "active" : ""}`}
onClick={() => setActiveTab("plan")}
>
Subscription OverView
</small>
</li>
<li className="nav-item">
<small
className={`nav-link ${activeTab === "billing" ? "active" : ""}`}
onClick={() => setActiveTab("billing")}
>
Manage Subscription
</small>
</li>
</ul>
</div>
<div className="px-2 py-1">
{renderContent()}
</div>
</div>
);
};
export default ManageSubscription;

View File

@ -0,0 +1,87 @@
import React from 'react'
import Avatar from '../../common/Avatar'
const SubscriptionOverview = () => {
return (
<div className='container-md text-start'>
<div className='row d-flex justify-content-between py-1'>
<small className='fw-semibold fs-sm-6 fs-md-5'>Overview</small>
<p className='text-muted m-0'>Summerizees all your payments and subscription for the purchsed Application</p>
<hr className="divider border-2 my-2" />
<div className='col-md-4 d-flex flex-column gap-6'>
<div className=''>
<p className=' d-bolck fw-semibold fs-6'>Contact Details</p>
<small className='d-bolck'>Who should we contact if neccessary ?</small>
</div>
<p className='fw-semibold text-primary'>Manage Contact Details</p>
</div>
<div className='col-md-6 '>
<div className='row border'>
<div className='col-6'>
<div className='d-flex flex-row gap-2 align-items-center p-2'>
<Avatar size='md' firstName={"pramod"} lastName={"Mahajan"}/>
<div >
<small className='d-block fw-semibold fs-6'>Pramod Mahajan</small>
<small className='text-muted text-tiny'>pramod.mahajan@marcoaiot.com</small>
<p className='my-3'>Marco Aiot Pvt Limited</p>
</div>
<div className="vr border-2"></div>
</div>
</div>
<div className='col-6 text-center align-items-center my-auto'>
<a className='text-primary'>Update Details</a>
</div>
</div>
</div>
</div>
<hr className="divider border-2 my-2" />
<div className='row d-flex justify-content-between py-1'>
<div className='col-md-4 d-flex flex-column gap-6'>
<div className=''>
<p className=' d-bolck fw-semibold fs-6'>Current Plan</p>
<small className='d-bolck'>You can update your plan anytime for best benifit from the product and track your projct</small>
</div>
<p className='fw-semibold text-primary'>Switch Plan</p>
</div>
<div className='col-md-6 '>
<div className='row border bg-light-primary p-2'>
<div className='d-flex justify-content-between align-items-center'>
<div className='d-flex flex-row gap-4'>
<small><i className='bx bxs-package'></i></small>
<small className='fw-bold'>Super Package</small>
<small className='fw-bold text-primary'> 5999</small>
</div>
<span className=' bg-primary rounded-circle'><i className='bx bx-md bx-check text-white'></i></span>
</div>
<div className='mt-3'>
<p className='m-0 ms-6 text-muted text-tiny'>Includes up to 100 userss, 10GB individual cloud storage and access maximum features</p>
</div>
</div>
</div>
</div>
<hr className="divider border-2 my-2" />
<div className='row d-flex justify-content-between py-1'>
<div className='col-md-4 d-flex flex-column gap-6'>
<div className=''>
<p className=' d-bolck fw-semibold fs-6'>Billing History</p>
<small className='d-bolck'>Sumary on the payment history for the subscription plan of the application</small>
</div>
<p className='fw-semibold text-primary'>Billing History</p>
</div>
<div className='col-md-6 '>
</div>
</div>
</div>
)
}
export default SubscriptionOverview

View File

@ -23,11 +23,13 @@ const ProcessedPayment = ({
setStepStatus,
resetFormStep,
}) => {
const { planName } = useParams();
const { planName } = useParams();
const { details: client, planId: selectedPlanId,frequency } = useSelector(
(store) => store.localVariables.selfTenant
);
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);
@ -35,7 +37,7 @@ const ProcessedPayment = ({
const {
data: plans,
isError: isPlanError,
isLoading,
isLoading,isError,isRefetching,refetch
} = useSubscription(frequency);
useEffect(() => {
if (!plans || !selectedPlanId) return;
@ -54,12 +56,10 @@ const ProcessedPayment = ({
const { mutate: MakePayment, isPending } = useMakePayment(
(response) => {
unblockUI();
onNext(response);
},
(fail) => {
unblockUI();
setFailPayment(fail);
onNext(fail);
@ -77,18 +77,18 @@ const ProcessedPayment = ({
alert("Failed to load Razorpay SDK");
return;
}
MakePayment({ amount:selectedPlan?.price });
MakePayment({ amount: selectedPlan?.price });
};
const handleRetry = () => {
setFailPayment(null);
if (typeof resetPaymentStep === "function") resetPaymentStep();
};
const handlPrevious=()=>{
const handlPrevious = () => {
setCurrentStep,
setStepStatus((prev) => ({ ...prev, 2: "pending",3:"pending" }));
setCurrentStep(2);
}
setStepStatus((prev) => ({ ...prev, 2: "pending", 3: "pending" }));
setCurrentStep(2);
};
// useEffect(() => {
// if (!client || Object.keys(client).length === 0) {
@ -154,156 +154,190 @@ const ProcessedPayment = ({
and help you maximize productivity.
</p>
</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?.planName}</span>
<i className="bx bx-check-circle text-primary fs-4"></i>
</div>
<div className="d-flex justify-content-between mt-1">
<small className="text-muted">
{selectedPlan?.currency?.symbol} {selectedPlan?.price} /
{frequencyLabel(frequency,false)}
</small>
</div>
<span className="custom-option-body d-block mt-1">
<small>{selectedPlan?.description}</small>
</span>
</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?.planName}
</span>
<i className="bx bx-check-circle text-primary fs-4"></i>
</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>
<div className="d-flex justify-content-between mt-1">
<small className="text-muted">
{selectedPlan?.currency?.symbol} {selectedPlan?.price}{" "}
/{frequencyLabel(frequency, false)}
</small>
</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>
<span className="custom-option-body d-block mt-1">
<small>{selectedPlan?.description}</small>
</span>
</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>
<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 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>
<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>
<div className="d-flex justify-content-between">
<h6 className="fs-4">Total Price</h6>
<h5 className="fs-3">
{formatFigure(selectedPlan?.price, {
type: "currency",
currency: selectedPlan?.currency.currencyCode,
})}
</h5>
</div>
</div>
</>
);
})()}
</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(selectedPlan?.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>
<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 mb-2">
{client.firstName} {client.lastName}
</div>
<div className="col-sm-6">
<strong>Email:</strong>
@ -320,8 +354,6 @@ const ProcessedPayment = ({
</div>
<div className="col-sm-6 mb-2">{client.organizationName}</div>
<div className="col-sm-6 mb-2">
<strong>Onboarding Date:</strong>
</div>
@ -344,19 +376,26 @@ const ProcessedPayment = ({
</div>
</div>
<div className="col-12 d-flex justify-content-between">
<button
type="submit"
className="btn btn-label-primary d-flex align-items-center me-2"
onClick={handlPrevious}
>
<i className="bx bx-chevron-left"></i> Previous
</button>
<button
type="submit"
className="btn btn-label-primary d-flex align-items-center me-2"
onClick={handlPrevious}
>
<i className="bx bx-chevron-left"></i> Previous
</button>
<button
type="submit"
className="btn btn-label-primary d-flex align-items-center me-2"
onClick={() => ProcessToPayment(currentPlan?.price)}
>
{isPending ? <span><i className='bx bx-loader-alt bx-md bx-spin me-2'></i>Please Wait...</span> : "Processed To Payment"}
{isPending ? (
<span>
<i className="bx bx-loader-alt bx-md bx-spin me-2"></i>Please
Wait...
</span>
) : (
"Processed To Payment"
)}
</button>
</div>
</div>

View File

@ -5,6 +5,7 @@ import { useSubscription } from "../../hooks/useAuth";
import { formatFigure, frequencyLabel } from "../../utils/appUtils";
import { setSelfTenant } from "../../slices/localVariablesSlice";
import SelectedPlanSkeleton from "./SelectedPlanSkeleton";
import { error } from "pdf-lib";
const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
const { frequency, planName } = useParams();
@ -22,8 +23,11 @@ const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
const {
data: plans,
isError: isPlanError,
isError,
isLoading,
error,
refetch,
isRefetching,
} = useSubscription(selectedFrequency);
const handleChange = (e) => setSelectedPlan(e.target.value);
@ -137,6 +141,32 @@ const SelectPlan = ({ currentStep, setStepStatus, onNext }) => {
</div>
</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 />
) : (

View File

@ -24,6 +24,7 @@ import EmpDashboard from "../../components/Employee/EmpDashboard";
import EmpDocuments from "../../components/Employee/EmpDocuments";
import EmpActivities from "../../components/Employee/EmpActivities";
import { setProjectId } from "../../slices/localVariablesSlice";
import ManageSubscription from "../../components/UserSubscription/ManageSubscription/ManageSubscription";
const EmployeeProfile = () => {
const { profile } = useProfile();
@ -77,6 +78,14 @@ const EmployeeProfile = () => {
);
break;
}
case "setting": {
return (
<>
<ManageSubscription />
</>
);
break;
}
case "activities": {
return (
<>