Merge remote-tracking branch 'origin/Subscription_Plan' into Purchase_Invoice_Management

This commit is contained in:
pramod.mahajan 2025-12-16 11:06:57 +05:30
commit bc114b6ae0
23 changed files with 545 additions and 474 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 KiB

After

Width:  |  Height:  |  Size: 52 KiB

BIN
public/img/hero/bg-011.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@ -53,17 +53,17 @@ const ExpenseAnalysis = () => {
dataLabels: { enabled: true, formatter: (val) => `${val.toFixed(0)}%` }, dataLabels: { enabled: true, formatter: (val) => `${val.toFixed(0)}%` },
colors: flatColors, colors: flatColors,
tooltip: { tooltip: {
y: { y: {
formatter: function (value) { formatter: function (value) {
return formatCurrency(value); return formatCurrency(value);
},
},
x: {
formatter: function (label) {
return label;
},
}, },
}, },
x: {
formatter: function (label) {
return label;
},
},
},
plotOptions: { plotOptions: {
pie: { pie: {
donut: { donut: {
@ -100,7 +100,7 @@ const ExpenseAnalysis = () => {
<div className="text-end text-sm-end"> <div className="text-end text-sm-end">
<FormProvider {...methods}> <FormProvider {...methods}>
<DateRangePicker1 /> <DateRangePicker1 pastDays="30" />
</FormProvider> </FormProvider>
</div> </div>
</div> </div>
@ -152,7 +152,9 @@ const ExpenseAnalysis = () => {
className="col-6" className="col-6"
key={idx} key={idx}
style={{ style={{
borderLeft: `3px solid ${flatColors[idx % flatColors.length]}`, borderLeft: `3px solid ${
flatColors[idx % flatColors.length]
}`,
}} }}
> >
<div className="d-flex flex-column text-start"> <div className="d-flex flex-column text-start">
@ -177,7 +179,6 @@ const ExpenseAnalysis = () => {
</span> </span>
</div> </div>
</div> </div>
))} ))}
</div> </div>
</div> </div>

View File

@ -7,8 +7,14 @@ import DatePicker from "../common/DatePicker";
import { useAppForm, useAppWatch } from "../../hooks/appHooks/useAppForm"; import { useAppForm, useAppWatch } from "../../hooks/appHooks/useAppForm";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod"; import { z } from "zod";
import { setProjectId } from "../../slices/localVariablesSlice";
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
const ProjectWiseTeamCount = () => { const ProjectWiseTeamCount = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { control } = useAppForm({ const { control } = useAppForm({
resolver: zodResolver( resolver: zodResolver(
z.object({ z.object({
@ -20,6 +26,11 @@ const ProjectWiseTeamCount = () => {
}, },
}); });
const goToProject = (projectId) => () => {
dispatch(setProjectId(projectId));
navigate(`/projects/details`);
};
const selectedDate = useAppWatch({ control, name: "date" }); const selectedDate = useAppWatch({ control, name: "date" });
const { data, isLoading, isFetching, isError, error } = const { data, isLoading, isFetching, isError, error } =
@ -33,7 +44,7 @@ const ProjectWiseTeamCount = () => {
<div className="card h-100 p-3"> <div className="card h-100 p-3">
{/* Header */} {/* Header */}
<div className="d-flex justify-content-between text-start mb-2"> <div className="d-flex justify-content-between text-start mb-2">
<h5 className="card-title m-0 me-2">Project wise Employee</h5> <h5 className="card-title m-0 me-2">Attendance by Project</h5>
<DatePicker name="date" control={control} maxDate={new Date()} /> <DatePicker name="date" control={control} maxDate={new Date()} />
</div> </div>
@ -72,11 +83,21 @@ const ProjectWiseTeamCount = () => {
className="d-flex align-items-center text-wrap my-2 text-start" className="d-flex align-items-center text-wrap my-2 text-start"
style={{ width: "180px" }} style={{ width: "180px" }}
> >
<span className="text-heading">{item.projectName}</span> <a
onClick={goToProject(item.projectId)}
className="text-heading text-truncate cursor-pointer"
>
{" "}
<span className="text-heading">{item.projectName}</span>
</a>
</div> </div>
</td> </td>
<td className="text-center" style={{ width: 80 }}>{item.teamCount}</td> <td className="text-center" style={{ width: 80 }}>
<td className="text-center" style={{ width: 80 }} >{item.attendanceCount}</td> {item.teamCount}
</td>
<td className="text-center" style={{ width: 80 }}>
{item.attendanceCount}
</td>
{/* <td>{percent(item.teamCount, item.attendanceCount)}%</td> */} {/* <td>{percent(item.teamCount, item.attendanceCount)}%</td> */}
</tr> </tr>
))} ))}

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect, useRef, useState } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { useJobsProgression } from "../../hooks/useDashboard_Data"; import { useJobsProgression } from "../../hooks/useDashboard_Data";
import { SpinnerLoader } from "../common/Loader"; import { SpinnerLoader } from "../common/Loader";
@ -7,55 +7,90 @@ import { useServiceProject } from "../../hooks/useServiceProject";
const ServiceJobs = () => { const ServiceJobs = () => {
const { projectId } = useParams(); const { projectId } = useParams();
const { data, isLoading, isError } = useJobsProgression(projectId); const { data, isLoading, isError } = useJobsProgression(projectId);
const jobs = data || {}; const jobs = data || {};
const { data: projectData, isLoading: projectLoading } = useServiceProject(projectId);
const { data: projectData, isLoading: projectLoading } =
useServiceProject(projectId);
const [activeTab, setActiveTab] = useState("tab-new");
// 👇 prevents re-running auto logic after first load
const hasInitializedTab = useRef(false);
const tabMapping = [ const tabMapping = [
{ id: "tab-new", label: "My Jobs", key: "myJobs" }, { id: "tab-new", label: "My Jobs", key: "myJobs" },
{ id: "tab-preparing", label: "Assigned", key: "assignedJobs" },
{ id: "tab-shipping", label: "In Progress", key: "inProgressJobs" }, { id: "tab-shipping", label: "In Progress", key: "inProgressJobs" },
{ id: "tab-preparing", label: "Assigned", key: "assignedJobs" },
]; ];
/* ---------- INITIAL TAB SELECTION ONLY ---------- */
useEffect(() => {
if (hasInitializedTab.current || !jobs) return;
if (jobs.myJobs?.length > 0) {
setActiveTab("tab-new");
} else if (jobs.inProgressJobs?.length > 0) {
setActiveTab("tab-shipping");
} else {
setActiveTab("tab-preparing");
}
hasInitializedTab.current = true;
}, [jobs]);
return ( return (
<div className=""> <div>
<div className="card page-min-h"> <div className="card page-min-h">
{/* Header */}
<div className="card-header d-flex justify-content-between"> <div className="card-header d-flex justify-content-between">
<div className="card-title mb-0 text-start"> <div className="card-title mb-0 text-start">
<h5 className="mb-1 fw-bold">Service Jobs</h5> <h5 className="mb-1 fw-bold">Service Jobs</h5>
<p className="card-subtitle"> <p className="card-subtitle">
{projectLoading ? "Loading..." : projectData?.name || "All Projects"} {projectLoading
? "Loading..."
: projectData?.name || "All Projects"}
</p> </p>
</div> </div>
</div> </div>
<div className="card-body p-0"> <div className="card-body p-0">
<div className="nav-align-top"> <div className="nav-align-top">
{/* Tabs */}
{/* ---------------- Tabs ---------------- */} <ul className="nav nav-tabs nav-fill rounded-0 timeline-indicator-advanced">
<ul className="nav nav-tabs nav-fill rounded-0 timeline-indicator-advanced" role="tablist"> {tabMapping.map((tab) => (
{tabMapping.map((t, index) => ( <li className="nav-item" key={tab.id}>
<li className="nav-item" key={t.id}>
<button <button
className={`nav-link ${index === 0 ? "active" : ""}`} type="button"
data-bs-toggle="tab" className={`nav-link ${
data-bs-target={`#${t.id}`} activeTab === tab.id ? "active" : ""
}`}
onClick={() => setActiveTab(tab.id)}
> >
{t.label} {tab.label}
</button> </button>
</li> </li>
))} ))}
</ul> </ul>
{/* ---------------- Tab Content ---------------- */} {/* Content */}
<div className="tab-content border-0 mx-1 text-start"> <div className="tab-content border-0 mx-1 text-start">
{isLoading && ( {isLoading && (
<div className="text-center" style={{ height: "250px", display: "flex", justifyContent: "center", alignItems: "center" }}> <div
className="text-center"
style={{
height: "250px",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<SpinnerLoader /> <SpinnerLoader />
</div> </div>
)} )}
{isError && ( {isError && (
<p <p
className="text-center" className="text-center"
@ -69,19 +104,19 @@ const ServiceJobs = () => {
> >
No data found No data found
</p> </p>
)} )}
{!isLoading && {!isLoading &&
!isError && !isError &&
tabMapping.map((t, index) => { tabMapping.map((tab) => {
const list = jobs[t.key] || []; const list = jobs[tab.key] || [];
return ( return (
<div <div
key={t.id} key={tab.id}
className={`tab-pane fade ${index === 0 ? "show active" : ""}`} className={`tab-pane fade ${
id={t.id} activeTab === tab.id ? "show active" : ""
}`}
> >
{list.length === 0 ? ( {list.length === 0 ? (
<p <p
@ -96,24 +131,19 @@ const ServiceJobs = () => {
> >
No jobs found No jobs found
</p> </p>
) : ( ) : (
<div className="job-scroll-wrapper"> <div className="job-scroll-wrapper">
{list.map((job, i) => ( {list.map((job, index) => (
<React.Fragment key={i}> <React.Fragment key={index}>
<ul className="timeline mb-0"> <ul className="timeline mb-0">
{/* Assigned By */}
<li className="timeline-item ps-6 border-left-dashed"> <li className="timeline-item ps-6 border-left-dashed">
<span className="timeline-indicator-advanced timeline-indicator-success border-0 shadow-none"> <span className="timeline-indicator-advanced timeline-indicator-success border-0 shadow-none">
<i className="bx bx-check-circle"></i> <i className="bx bx-check-circle"></i>
</span> </span>
<div className="timeline-event ps-1"> <div className="timeline-event ps-1">
<div className="timeline-header"> <small className="text-success text-uppercase">
<small className="text-success text-uppercase"> Assigned By
Assigned By </small>
</small>
</div>
<h6 className="my-50">{job.assignedBy}</h6> <h6 className="my-50">{job.assignedBy}</h6>
<p className="text-body mb-0"> <p className="text-body mb-0">
{formatUTCToLocalTime(job.assignedAt)} {formatUTCToLocalTime(job.assignedAt)}
@ -121,23 +151,23 @@ const ServiceJobs = () => {
</div> </div>
</li> </li>
{/* Project */}
<li className="timeline-item ps-6 border-transparent"> <li className="timeline-item ps-6 border-transparent">
<span className="timeline-indicator-advanced timeline-indicator-primary border-0 shadow-none"> <span className="timeline-indicator-advanced timeline-indicator-primary border-0 shadow-none">
<i className="bx bx-map"></i> <i className="bx bx-map"></i>
</span> </span>
<div className="timeline-event ps-1"> <div className="timeline-event ps-1">
<div className="timeline-header"> <small className="text-primary text-uppercase">
<small className="text-primary text-uppercase">Project</small> Project
</div> </small>
<h6 className="my-50">{job.project}</h6> <h6 className="my-50">{job.project}</h6>
<p className="text-body mb-0">{job.title}</p> <p className="text-body mb-0">
{job.title}
</p>
</div> </div>
</li> </li>
</ul> </ul>
{/* Divider */} {index < list.length - 1 && (
{i < list.length - 1 && (
<div className="border-1 border-light border-top border-dashed my-4"></div> <div className="border-1 border-light border-top border-dashed my-4"></div>
)} )}
</React.Fragment> </React.Fragment>

View File

@ -13,22 +13,12 @@ const Sidebar = () => {
id="layout-menu" id="layout-menu"
className="layout-menu menu-vertical menu bg-menu-theme " className="layout-menu menu-vertical menu bg-menu-theme "
> >
<div className="app-brand" style={{ paddingLeft: "30px" }}> <div className="app-brand" style={{ paddingLeft: "15px" }}>
<Link to="/dashboard" className="app-brand-link"> <Link to="/dashboard" className="app-brand-link">
{/* <span className="app-brand-logo rounded-circle app-brand-logo-border">
<img
className="app-brand-logo-sidebar"
src="/img/brand/marco.png"
alt="logo"
aria-label="logo image"
style={{ margin: "5px", paddingRight: "5px" }}
/>
</span> */}
<span className="app-brand-logo demo d-flex align-items-center"> <span className="app-brand-logo demo d-flex align-items-center">
<img <img
src="/img/brand/marco.png" src="/img/brand/marco.png"
width="40" width="50"
height="40" height="40"
alt="OnFieldWork logo" alt="OnFieldWork logo"
/> />

View File

@ -55,7 +55,7 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
const { data: assignedServices, isLoading: servicesLoading } = const { data: assignedServices, isLoading: servicesLoading } =
useProjectAssignedServices(projectId); useProjectAssignedServices(projectId);
const { control } = useForm({ const { control, setValue } = useForm({
defaultValues: { defaultValues: {
serviceId: selectedService || "", serviceId: selectedService || "",
}, },
@ -74,6 +74,22 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
}; };
useEffect(() => {
if (!servicesLoading && assignedServices?.length === 1) {
const serviceId = assignedServices[0].id;
// set form value
setValue("serviceId", serviceId, {
shouldDirty: true,
shouldValidate: true,
});
// sync redux
dispatch(setService(serviceId));
}
}, [assignedServices, servicesLoading, setValue, dispatch]);
return ( return (
<> <>
{showModalBuilding && ( {showModalBuilding && (
@ -132,31 +148,27 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
<div className="col-md-4 col-12 dataTables_length text-start py-2 px-2"> <div className="col-md-4 col-12 dataTables_length text-start py-2 px-2">
<div className="ms-4 mt-n1"> <div className="ms-4 mt-n1">
{!servicesLoading && assignedServices?.length > 0 && ( {!servicesLoading && assignedServices?.length > 0 && (
assignedServices.length > 1 ? ( <AppFormController
<AppFormController name="serviceId"
name="serviceId" control={control}
control={control} render={({ field }) => (
render={({ field }) => ( <SelectField
<SelectField label="Select Service"
label="Select Service" options={[...(assignedServices ?? [])]}
options={[{ id: "", name: "All Services" }, ...(assignedServices ?? [])]} placeholder="Choose a Service"
placeholder="Choose a Service" labelKey="name"
valueKey="id"
labelKey="name" value={field.value}
valueKey="id" onChange={(val) => {
value={field.value} field.onChange(val);
onChange={(val) => { dispatch(setService(val));
field.onChange(val); }}
handleServiceChange(val); isLoading={servicesLoading}
}} />
isLoading={servicesLoading} )}
/> />
)}
/>
) : (
<h5>{assignedServices[0].name}</h5>
)
)} )}
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import TeamEmployeeList from "./TeamEmployeeList"; import TeamEmployeeList from "./TeamEmployeeList";
import { useOrganization } from "../../../hooks/useDirectory"; import { useOrganization } from "../../../hooks/useDirectory";
import { useOrganizationsList } from "../../../hooks/useOrganization"; import { useOrganizationsList } from "../../../hooks/useOrganization";
@ -14,56 +14,48 @@ const TeamAssignToProject = ({ closeModal }) => {
const project = useSelectedProject(); const project = useSelectedProject();
const { data, isLoading, isError, error } = const { data, isLoading, isError, error } =
useProjectAssignedOrganizationsName(project); useProjectAssignedOrganizationsName(project);
const { control, watch, formState: { errors } } = useForm({ const { control, watch, setValue, formState: { errors } } = useForm({
defaultValues: { organizationId: "" }, defaultValues: { organizationId: "" },
}); });
useEffect(() => {
if (data?.length === 1) {
setValue("organizationId", data[0].id, {
shouldValidate: true,
shouldDirty: true,
});
}
}, [data, setValue]);
return ( return (
<div className="container"> <div className="container">
{/* <p className="fs-5 fs-seminbod ">Assign Employee To Project </p> */} {/* <p className="fs-5 fs-seminbod ">Assign Employee To Project </p> */}
<h5 className="mb-4">Assign Employee To Project</h5> <h5 className="mb-4">Assign Employee To Project</h5>
<div className="row align-items-center gx-5 text-start"> <div className="row align-items-center gx-5 text-start">
<div className="col-12 col-md-6 mb-2 mt-5"> <div className="col-12 col-md-6 mb-2">
<AppFormController
{!isLoading && data && ( name="organizationId"
<> control={control}
{data.length === 1 && ( rules={{ required: "Organization is required" }}
<h5 className="mb-2">{data[0].name}</h5> render={({ field }) => (
)} <SelectField
label="Select Organization"
{/* If multiple organizations → show dropdown */} options={data ?? []}
{data.length > 1 && ( placeholder="Choose an Organization"
<div className="col-12 col-md-6 mb-2 text-start"> required
<AppFormController labelKey="name"
name="organizationId" valueKey="id"
control={control} value={field.value}
rules={{ required: "Organization is required" }} onChange={field.onChange}
render={({ field }) => ( isLoading={isLoading}
<SelectField className="m-0 w-100"
label="Select Organization" />
options={[...data]} )}
placeholder="Choose an Organization" />
required {errors.organizationId && (
labelKey="name" <small className="danger-text">{errors.organizationId.message}</small>
valueKey="id"
value={field.value}
onChange={field.onChange}
isLoading={isLoading}
className="m-0 w-100"
/>
)}
/>
{errors.organizationId && (
<small className="danger-text">
{errors.organizationId.message}
</small>
)}
</div>
)}
</>
)} )}
{/* </div> */}
</div> </div>
<div className="col-12 col-md-6 mt-n2"> <div className="col-12 col-md-6 mt-n2">
<div className="d-flex flex-column"> <div className="d-flex flex-column">

View File

@ -34,7 +34,7 @@ const Teams = () => {
const [selectedEmployee, setSelectedEmployee] = useState(null); const [selectedEmployee, setSelectedEmployee] = useState(null);
const [deleteEmployee, setDeleteEmplyee] = useState(null); const [deleteEmployee, setDeleteEmplyee] = useState(null);
const [activeEmployee, setActiveEmployee] = useState(false); const [activeEmployee, setActiveEmployee] = useState(false);
const { control, watch } = useForm({ const { control, watch, setValue } = useForm({
defaultValues: { defaultValues: {
selectedService: "", selectedService: "",
searchTerm: "", searchTerm: "",
@ -136,6 +136,18 @@ const Teams = () => {
return () => eventBus.off("employee", employeeHandler); return () => eventBus.off("employee", employeeHandler);
}, [employeeHandler]); }, [employeeHandler]);
useEffect(() => {
if (!servicesLoading && assignedServices?.length === 1) {
const serviceId = assignedServices[0].id;
setValue("selectedService", serviceId, {
shouldDirty: true,
shouldValidate: true,
});
}
}, [assignedServices, servicesLoading, setValue]);
return ( return (
<> <>
{AssigTeam && ( {AssigTeam && (
@ -162,37 +174,30 @@ const Teams = () => {
<div className="card-body"> <div className="card-body">
<div className="row align-items-center justify-content-between mb-4 g-3"> <div className="row align-items-center justify-content-between mb-4 g-3">
<div className="col-md-6 col-12 d-flex flex-wrap align-items-center gap-3"> <div className="col-md-6 col-12 d-flex flex-wrap align-items-center gap-3">
{!servicesLoading && assignedServices && ( {!servicesLoading && assignedServices?.length > 0 && (
<> <div className="col-12 col-md-6 mb-2 text-start">
{assignedServices.length === 1 && ( <AppFormController
<h5 className="mb-2">{assignedServices[0].name}</h5> name="selectedService"
)} control={control}
render={({ field }) => (
{assignedServices.length > 1 && ( <SelectField
<div className="col-12 col-md-6 mb-2 text-start"> label="Select Service"
<AppFormController options={[ ...assignedServices]}
name="selectedService" placeholder="Choose a Service"
control={control} labelKey="name"
render={({ field }) => ( valueKey="id"
<SelectField value={field.value}
label="Select Service" onChange={field.onChange}
options={[{ id: "", name: "All Services" }, ...assignedServices]} isLoading={servicesLoading}
placeholder="Choose a Service" className="w-100"
labelKey="name"
valueKey="id"
value={field.value}
onChange={field.onChange}
isLoading={servicesLoading}
className="w-100"
/>
)}
/> />
</div> )}
)} />
</> </div>
)} )}
<div className="form-check form-switch d-flex align-items-center text-nowrap"> <div className="form-check form-switch d-flex align-items-center text-nowrap">
<input <input
type="checkbox" type="checkbox"

View File

@ -93,6 +93,7 @@ export const DateRangePicker1 = ({
resetSignal, resetSignal,
defaultRange = true, defaultRange = true,
maxDate = null, maxDate = null,
pastDays = 6,
...rest ...rest
}) => { }) => {
const inputRef = useRef(null); const inputRef = useRef(null);
@ -105,7 +106,7 @@ export const DateRangePicker1 = ({
const applyDefaultDates = () => { const applyDefaultDates = () => {
const today = new Date(); const today = new Date();
const past = new Date(); const past = new Date();
past.setDate(today.getDate() - 6); past.setDate(today.getDate() - pastDays);
const format = (d) => flatpickr.formatDate(d, "d-m-Y"); const format = (d) => flatpickr.formatDate(d, "d-m-Y");
const formattedStart = format(past); const formattedStart = format(past);

View File

@ -8,7 +8,7 @@ const Progress = ({
total = 0, total = 0,
height = 100, height = 100,
width = 100, width = 100,
completed =0, completed = 0,
}) => { }) => {
const options = { const options = {
chart: { chart: {
@ -24,16 +24,19 @@ const Progress = ({
value: { value: {
show: true, show: true,
fontSize: "13px", fontSize: "13px",
width:"12px", width: "12px",
textWrap:"wrap", textWrap: "wrap",
color: color, color: color,
fontWeight: 400, fontWeight: 400,
offsetY: 7, offsetY: 7,
formatter: () => `${formatFigure(completed,{notation:"compact"})} / ${formatFigure(total,{notation:"compact"})}`, formatter: () =>
`${formatFigure(completed, {
notation: "compact",
})} / ${formatFigure(total, { notation: "compact" })}`,
},
style: {
textWrap: "wrap",
}, },
style:{
textWrap:"wrap",
}
}, },
}, },
}, },
@ -54,7 +57,6 @@ const Progress = ({
}; };
export default Progress; export default Progress;
// const Progress = ({ // const Progress = ({
// completed = 0, // completed = 0,
// inProgress = 0, // inProgress = 0,

View File

@ -9,9 +9,11 @@ const ReportsDonutCard = ({
donutClass = "", donutClass = "",
footer = "Team members present on the site", footer = "Team members present on the site",
chartColor, chartColor,
legend1 = "Completed",
legend2 = "Pending",
}) => { }) => {
return ( return (
<div className="border-top card border-primary py-4 px-2"> <div className="border-top card border-primary py-4 px-2 h-100">
<h4 className="reports-card-title">{title}</h4> <h4 className="reports-card-title">{title}</h4>
<div className="d-flex justify-content-center align-items-center flex-column"> <div className="d-flex justify-content-center align-items-center flex-column">
@ -24,7 +26,7 @@ const ReportsDonutCard = ({
completed={value} completed={value}
total={total} total={total}
/> />
<ReportsLegend /> <ReportsLegend legend1={legend1} legend2={legend2} />
</div> </div>
</div> </div>
@ -51,7 +53,6 @@ export const ReportsCard = ({
<div className="d-flex justify-content-start align-items-center flex-column"> <div className="d-flex justify-content-start align-items-center flex-column">
<div className="d-inline-flex flex-row align-items-center gap-12 gap-md-3"> <div className="d-inline-flex flex-row align-items-center gap-12 gap-md-3">
<ReportsLegend /> <ReportsLegend />
</div> </div>
</div> </div>
@ -62,5 +63,3 @@ export const ReportsCard = ({
</div> </div>
); );
}; };

View File

@ -1,19 +1,14 @@
const ReportsLegend = () => { const ReportsLegend = ({legend1, legend2}) => {
return ( return (
<div className=" d-inline-flex flex-column text-start gap-2 pe-5"> <div className=" d-inline-flex flex-column text-start gap-2 pe-5">
<div className=" d-flex align-items-center gap-2"> <div className=" d-flex align-items-center gap-2">
<span className="reports-legend-color reports-legend-green"></span> <span className="reports-legend-color reports-legend-green"></span>
<span>Completed</span> <span>{legend1}</span>
</div> </div>
<div className=" d-flex align-items-center gap-2"> <div className=" d-flex align-items-center gap-2">
<span className="reports-legend-color reports-legend-blue"></span> <span className="reports-legend-color reports-legend-blue"></span>
<span>In Progress</span> <span>{legend2}</span>
</div>
<div className=" d-flex align-items-center gap-2">
<span className="reports-legend-color reports-legend-gray"></span>
<span>Pending</span>
</div> </div>
</div> </div>
); );

View File

@ -4,6 +4,7 @@ import Progress from "./Progress";
import { formatUTCToLocalTime } from "../../utils/dateUtils"; import { formatUTCToLocalTime } from "../../utils/dateUtils";
import { localToUtc } from "../../utils/appUtils"; import { localToUtc } from "../../utils/appUtils";
import ReportsDonutCard, { ReportsCard } from "./ReportsDonutCard"; import ReportsDonutCard, { ReportsCard } from "./ReportsDonutCard";
import { SpinnerLoader } from "../common/Loader";
const ReportDPR = ({ project, date }) => { const ReportDPR = ({ project, date }) => {
const { data, isLoading, isError, error } = useProjectReportByProject( const { data, isLoading, isError, error } = useProjectReportByProject(
@ -12,88 +13,117 @@ const ReportDPR = ({ project, date }) => {
); );
return ( return (
<> <>
<div className="card py-2"> <div className="card py-2 mb-5">
<div className="d-flex text-start px-2 mt-2"> <div className="d-flex text-start px-2 mt-2">
Project Status Reported - Generated at{" "} Project Status Reported - Generated at{" "}
{formatUTCToLocalTime(data?.date, true)} {formatUTCToLocalTime(data?.date, true)}
</div> </div>
{/* <!-- Status Cards */} {/* <!-- Status Cards */}
<div className="d-flex justify-content-between flex-wrap gap-5 px-3 py-4"> <div className="row px-5 py-4">
<ReportsDonutCard <div className="col-6 col-md-3" style={{ minHeight: "255px" }}>
title={"TODAY'S ATTENDANCE"} {" "}
total={data?.totalEmployees} <ReportsDonutCard
value={data?.todaysAttendances} title={"TODAY'S ATTENDANCE"}
/> total={data?.totalEmployees}
<ReportsDonutCard value={data?.todaysAttendances}
title={"DAILY TASKS COMPLETED"} legend1="Today's Attendance"
total={data?.totalCompletedTask} legend2="Total Employees"
value={data?.totalPlannedWork} />
chartColor={"blue"} </div>
footer="" <div className="col-6 col-md-3" style={{ minHeight: "255px" }}>
/> {" "}
<ReportsDonutCard
<ReportsDonutCard title={"DAILY TASKS COMPLETED"}
title={"PROJECT COMPLETION STATUS"} total={data?.totalCompletedTask}
total={data?.totalPlannedWork} value={data?.totalPlannedWork}
value={data?.totalCompletedWork} chartColor={"blue"}
chartColor={"green"} footer=""
footer="" legend1="Completed Work"
/> legend2="Planned Work"
/>
<div className="border-top card border-warning py-4 px-2"> </div>
<h5 className="reports-card-title">Attendance Pending Report</h5> <div className="col-6 col-md-3" style={{ minHeight: "255px" }}>
<div className="d-flex flex-column gap-2"> {" "}
<div className="d-flex justify-content-between"> <ReportsDonutCard
<span className="text-secondry"> Regualrization</span>{" "} title={"PROJECT COMPLETION STATUS"}
<span className="text-secondry"> total={data?.totalPlannedWork}
{" "} value={data?.totalCompletedWork}
{data?.regularizationPending} chartColor={"green"}
</span>{" "} footer=""
</div> legend1="Completed Work"
<div className="d-flex justify-content-between"> legend2="Planned Work"
<span className="text-secondry"> Checking</span>{" "} />
<span className="text-secondry"> {data?.checkoutPending}</span>{" "} </div>
</div> <div className="col-6 col-md-3" style={{ minHeight: "255px" }}>
<div className="d-flex justify-content-between"> <div className="border-top card border-warning py-4 px-2 h-100">
<span className="text-secondry"> Total Employee</span>{" "} <h5 className="reports-card-title">Attendance Pending Report</h5>
<span className="text-secondry"> <div className="d-flex flex-column gap-2">
{" "} <div className="d-flex justify-content-between">
{data?.todaysAttendances} <span className="text-secondry"> Regualrization</span>{" "}
</span>{" "} <span className="text-secondry">
{" "}
{data?.regularizationPending}
</span>{" "}
</div>
<div className="d-flex justify-content-between">
<span className="text-secondry"> Checking</span>{" "}
<span className="text-secondry">
{" "}
{data?.checkoutPending}
</span>{" "}
</div>
<div className="d-flex justify-content-between">
<span className="text-secondry"> Total Employee</span>{" "}
<span className="text-secondry">
{" "}
{data?.todaysAttendances}
</span>{" "}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="row px-3"> <div className="row px-5 pb-5">
<div className="col-4"> <div className="col-4">
<div className="reports-card"> <div className="reports-card h-100" style={{ minHeight: "250px" }}>
{/* {/* <!-- Row 1: Header */} {/* {/* <!-- Row 1: Header */}
<div> <div>
<h4 className="reports-card-title">Team Strength on Site</h4> <h4 className="reports-card-title">Team Strength on Site</h4>
</div> </div>
<table style={{ width: "100%" }}> <table style={{ width: "100%" }}>
<tbody> <tbody>
{data?.teamOnSite {data?.teamOnSite?.filter(
?.filter((item) => item?.numberofEmployees > 0) (item) => item?.numberofEmployees > 0
?.map((member, index) => ( ).length > 0 ? (
<tr key={index}> data?.teamOnSite
<td style={{ textAlign: "left" }}>
{member?.roleName}
</td>
<td style={{ textAlign: "right" }}> ?.filter((item) => item?.numberofEmployees > 0)
{member?.numberofEmployees} ?.map((member, index) => (
</td> <tr key={index}>
</tr> <td style={{ textAlign: "left" }}>
))} {member?.roleName}
</td>
<td style={{ textAlign: "right" }}>
{member?.numberofEmployees}
</td>
</tr>
))
) : (
<tr>
<td colSpan="2" style={{ textAlign: "center" }}>
No Records Available
</td>
</tr>
)}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div className="col-8"> <div className="col-8">
{/* {/* <!-- Activities */} {/* {/* <!-- Activities */}
<div className="reports-card"> <div className="reports-card h-100" style={{ minHeight: "250px" }}>
<h4 className="reports-card-title">Employee In-Out Report </h4> <h4 className="reports-card-title">Employee In-Out Report </h4>
<table className="reports-table"> <table className="reports-table">
<thead> <thead>
@ -105,18 +135,34 @@ const ReportDPR = ({ project, date }) => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{data?.performedAttendance?.map((att, index) => ( {data?.performedAttendance?.length > 0 ? (
<tr key={att.index + att.name}> data?.performedAttendance?.map((att, index) => (
<td>{att.name}</td> <tr key={att.index + att.name}>
<td>{att.roleName}</td> <td>{att.name}</td>
<td>{formatUTCToLocalTime(att.inTime, true)}</td> <td>{att.roleName}</td>
<td> <td>{formatUTCToLocalTime(att.inTime, true)}</td>
{att.outTime <td>
? formatUTCToLocalTime(att.outTime, true) {att.outTime
: "--"} ? formatUTCToLocalTime(att.outTime, true)
: "--"}
</td>
</tr>
))
) : (
<tr>
<td
colSpan="4"
className="text-center py-4 border-0"
style={{ height: "200px" }}
>
{isLoading ? (
<SpinnerLoader />
) : (
<div className="py-8">No Records Available</div>
)}
</td> </td>
</tr> </tr>
))} )}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -136,6 +136,15 @@ const AttendancePage = () => {
: []), : []),
]; ];
// --- END: Tab Configuration Array // --- END: Tab Configuration Array
useEffect(() => {
if (!orgLoading && organizations?.length === 1) {
setAppliedFilters((prev) => ({
...prev,
selectedOrganization: organizations[0].id,
}));
}
}, [orgLoading, organizations]);
return ( return (
<> <>
@ -173,19 +182,21 @@ const AttendancePage = () => {
{/* Tabs header with search and filter */} {/* Tabs header with search and filter */}
<div className="nav-align-top nav-tabs-shadow bg-white border-bottom pt-5"> <div className="nav-align-top nav-tabs-shadow bg-white border-bottom pt-5">
<div className="row align-items-center g-0 mb-3 mb-md-0 mx-1 mx-sm-5"> <div className="row align-items-center g-0 mb-3 mb-md-0 mx-1 mx-sm-5">
{/* Tabs Buttons */}
{/* Tabs Buttons - Now col-12 col-md-6 */} <div className="col-12 col-md mt-1">
<div className="col-12 col-md-6 mt-1">
<ul className="nav nav-tabs" role="tablist"> <ul className="nav nav-tabs" role="tablist">
{tabsData.map((tab) => ( {tabsData.map((tab) => (
<li <li
className={`nav-item ${tab.id === "regularization" && !DoRegularized ? "d-none" : "" className={`nav-item ${tab.id === "regularization" && !DoRegularized
}`} ? "d-none"
: ""
}`} // Keep the d-none logic for regularization, although it's filtered above
key={tab.id} key={tab.id}
> >
<button <button
type="button" type="button"
className={`nav-link ${activeTab === tab.id ? "active" : ""} fs-6`} className={`nav-link ${activeTab === tab.id ? "active" : ""
} fs-6`}
onClick={() => handleTabChange(tab.id)} onClick={() => handleTabChange(tab.id)}
data-bs-toggle="tab" data-bs-toggle="tab"
data-bs-target={`#navs-top-${tab.id}`} data-bs-target={`#navs-top-${tab.id}`}
@ -197,51 +208,29 @@ const AttendancePage = () => {
</ul> </ul>
</div> </div>
{/* Search + Organization Filter - Now col-12 col-md-6 */} {/* Search + Organization filter */}
<div className="col-12 col-md-6 mb-2 mt-md-0"> <div className="col-12 col-md-auto mb-2 mt-md-0 ms-md-auto nav">
<div className="row g-0"> <div className="row g-2">
{/* Organization */}
<div className="col-12 col-sm-6"> <div className="col-12 col-sm-6">
<select
{/* If only 1 organization → show name only */} className="form-select form-select-sm"
{!orgLoading && organizations?.length === 1 && ( value={appliedFilters.selectedOrganization}
<h5 className="m-0 ">{organizations[0].name}</h5> onChange={(e) =>
)} setAppliedFilters((prev) => ({
...prev,
{/* If multiple organizations → show dropdown */} selectedOrganization: e.target.value,
{!orgLoading && organizations?.length > 1 && ( }))
<select }
className="form-select form-select-sm" disabled={orgLoading}
value={appliedFilters.selectedOrganization} >
onChange={(e) => <option value="">All Organizations</option>
setAppliedFilters((prev) => ({ {organizations?.map((org, ind) => (
...prev, <option key={`${org.id}-${ind}`} value={org.id}>
selectedOrganization: e.target.value, {org.name}
})) </option>
} ))}
disabled={orgLoading} </select>
>
<option value="">All Organizations</option>
{organizations?.map((org, ind) => (
<option key={`${org.id}-${ind}`} value={org.id}>
{org.name}
</option>
))}
</select>
)}
{/* Loading case (optional) */}
{orgLoading && (
<div className="form-control form-control-sm disabled">
Loading...
</div>
)}
</div> </div>
{/* Search */}
<div className="col-12 col-sm-6"> <div className="col-12 col-sm-6">
<input <input
type="text" type="text"
@ -251,14 +240,11 @@ const AttendancePage = () => {
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Tab Content */} {/* Tab Content */}
<div className="tab-content attedanceTabs py-0 px-1 px-sm-3 pb-10"> <div className="tab-content attedanceTabs py-0 px-1 px-sm-3 pb-10">
{selectedProject ? ( {selectedProject ? (

View File

@ -15,7 +15,7 @@ const TaskPlanning = () => {
const selectedProject = useSelectedProject(); const selectedProject = useSelectedProject();
const selectedService = useCurrentService(); const selectedService = useCurrentService();
const dispatch = useDispatch(); const dispatch = useDispatch();
const { control } = useForm({ const { control,setValue } = useForm({
defaultValues: { defaultValues: {
serviceFilter: selectedService ?? "" serviceFilter: selectedService ?? ""
}, },
@ -39,6 +39,20 @@ const TaskPlanning = () => {
return <div className="text-center py-5">Loading...</div>; return <div className="text-center py-5">Loading...</div>;
} }
useEffect(() => {
if (!servicesLoading && data?.length === 1) {
const serviceId = data[0].id;
setValue("serviceFilter", serviceId, {
shouldValidate: true,
shouldDirty: true,
});
dispatch(setService(serviceId));
}
}, [servicesLoading, data, setValue, dispatch]);
return ( return (
<div className="container-fluid"> <div className="container-fluid">
<Breadcrumb <Breadcrumb
@ -49,44 +63,31 @@ const TaskPlanning = () => {
/> />
<div className="card py-2 page-min-h"> <div className="card py-2 page-min-h">
<div className="col-sm-4 col-md-3 col-12 px-4 py-2 ms-1 text-start"> <div className="col-sm-4 col-md-3 col-12 px-4 py-2 text-start">
{/* When no services available */}
{data?.length === 0 ? ( {data?.length === 0 ? (
<p className="badge bg-label-secondary m-0"></p> <p className="badge bg-label-secondary m-0"></p>
) : ( ) : (
<> <AppFormController
{/* When exactly 1 service assigned → display heading */} name="serviceFilter"
{data?.length === 1 && ( control={control}
<h5 className="mb-3">{data[0].name}</h5> render={({ field }) => (
)} <SelectField
label="Services"
{/* When multiple services → show dropdown */} placeholder="All Services"
{data?.length > 1 && ( options={[{ id: "", name: "All Services" }, ...(data ?? [])]}
<AppFormController labelKey="name"
name="serviceFilter" valueKey="id"
control={control} isLoading={servicesLoading}
render={({ field }) => ( value={field.value}
<SelectField onChange={(val) => {
label="Select Services" field.onChange(val);
placeholder="All Services" dispatch(setService(val));
options={[{ id: "", name: "All Services" }, ...(data ?? [])]} }}
labelKey="name" className="m-0"
valueKey="id"
isLoading={servicesLoading}
value={field.value}
onChange={(val) => {
field.onChange(val);
dispatch(setService(val));
}}
className="m-0"
/>
)}
/> />
)} )}
</> />
)} )}
</div> </div>

View File

@ -46,7 +46,7 @@ const DailyProgrssReport = () => {
filter, filter,
}; };
const { control } = useForm({ const { control,setValue } = useForm({
defaultValues: { defaultValues: {
serviceFilter: "" serviceFilter: ""
} }
@ -85,6 +85,22 @@ const DailyProgrssReport = () => {
return updated; return updated;
}); });
}; };
useEffect(() => {
if (!isLoading && data?.length === 1) {
const serviceId = data[0].id;
// set form value
setValue("serviceFilter", serviceId, {
shouldDirty: true,
shouldValidate: true,
});
// set local state
setService(serviceId);
}
}, [isLoading, data, setValue]);
return ( return (
<div className="container-fluid"> <div className="container-fluid">
<DailyProgrssContext.Provider value={contextDispatcher}> <DailyProgrssContext.Provider value={contextDispatcher}>
@ -120,44 +136,31 @@ const DailyProgrssReport = () => {
/> />
<div className="card page-min-h p-5"> <div className="card page-min-h p-5">
<div className="text-start"> {data?.length > 0 && (
{/* Service Heading or Dropdown */} <div className="col-sm-4 col-md-3 col-12 text-start">
{!isLoading && data && ( <AppFormController
<> name="serviceFilter"
{/* If only 1 service assigned → show heading */} control={control}
{data.length === 1 && ( defaultValue={service ?? ""}
<h5 className="mb-3">{data[0].name}</h5> render={({ field }) => (
<SelectField
label="Services"
options={[{ id: "", name: "All Services" }, ...(data ?? [])]}
placeholder="Select Service"
labelKey="name"
valueKey="id"
isLoading={isLoading}
value={field.value}
onChange={(val) => {
field.onChange(val);
setService(val);
}}
className="m-0"
/>
)} )}
/>
{/* If multiple services → show dropdown */} </div>
{data.length > 1 && ( )}
<div className="col-12 col-md-3 mb-3 text-start">
<AppFormController
name="serviceFilter"
control={control}
defaultValue={service ?? ""}
render={({ field }) => (
<SelectField
label="Select Service"
options={[{ id: "", name: "All Services" }, ...data]}
placeholder="Choose a Service"
labelKey="name"
valueKey="id"
value={field.value}
isLoading={isLoading}
onChange={(val) => {
field.onChange(val);
setService(val);
}}
className="w-100"
/>
)}
/>
</div>
)}
</>
)}
</div>
<div> <div>
<TaskReportList filter={filter} <TaskReportList filter={filter}

View File

@ -86,7 +86,7 @@ const LandingPage = () => {
<li className="nav-item ms-1"> <li className="nav-item ms-1">
<a <a
className="btn btn-sm btn-green btn-ovel-small px-3 my-1" className="btn btn-sm btn-green btn-ovel-small px-3 my-1"
href="#" href="/auth/reqest/demo"
> >
Request For Demo Request For Demo
</a> </a>
@ -162,7 +162,7 @@ const LandingPage = () => {
Make data-driven decisions with real-time project analytics. Make data-driven decisions with real-time project analytics.
</p> </p>
<a <a
href="#" href="/auth/reqest/demo"
className="btn btn-green btn-square-small btn-lg mt-3 p-3" className="btn btn-green btn-square-small btn-lg mt-3 p-3"
> >
View Demo View Demo
@ -185,7 +185,7 @@ const LandingPage = () => {
Eliminate Paper Receipts. Take Control of Your Cash Flow. Eliminate Paper Receipts. Take Control of Your Cash Flow.
</p> </p>
<a <a
href="#" href="/auth/reqest/demo"
className="btn btn-green btn-square-small btn-lg mt-3 p-3" className="btn btn-green btn-square-small btn-lg mt-3 p-3"
> >
View Demo View Demo
@ -320,65 +320,6 @@ const LandingPage = () => {
</p> </p>
{/* <SubscriptionPlans/> */} {/* <SubscriptionPlans/> */}
<SubscriptionPlans></SubscriptionPlans> <SubscriptionPlans></SubscriptionPlans>
{/* <div className="row g-4 justify-content-center hidden">
<div className="col-md-4">
<div className="card pricing-card border-0 shadow-sm">
<div className="card-body">
<h5 className="text-green fw-bold">Starter</h5>
<h2>
499<span className="fs-6 text-muted">/month</span>
</h2>
<ul className="list-unstyled mt-3 mb-4 text-muted">
<li>Up to 10 users</li>
<li>Basic reporting</li>
<li>Project tracking</li>
</ul>
<a href="#" className="btn btn-outline-success">
Request Demo
</a>
</div>
</div>
</div>
<div className="col-md-4">
<div className="card pricing-card border-primary shadow-lg">
<div className="card-body">
<h5 className="text-green fw-bold">Professional</h5>
<h2>
999<span className="fs-6 text-muted">/month</span>
</h2>
<ul className="list-unstyled mt-3 mb-4 text-muted">
<li>Unlimited projects</li>
<li>Expense & attendance tracking</li>
<li>Advanced analytics</li>
</ul>
<a
href="https://ofw.marcoaiot.com/auth/reqest/demo"
className="btn btn-green btn-square-small"
>
Request Demo
</a>
</div>
</div>
</div>
<div className="col-md-4">
<div className="card pricing-card border-0 shadow-sm">
<div className="card-body">
<h5 className="text-green fw-bold">Enterprise</h5>
<h2>Custom</h2>
<ul className="list-unstyled mt-3 mb-4 text-muted">
<li>Dedicated support</li>
<li>Custom integrations</li>
<li>Private cloud hosting</li>
</ul>
<a href="#" className="btn btn-outline-success">
Request Demo
</a>
</div>
</div>
</div>
</div> */}
</div> </div>
</section> </section>
{/* <!-- About --> */} {/* <!-- About --> */}

View File

@ -9,7 +9,8 @@ const SubscriptionPlans = () => {
const [frequency, setFrequency] = useState(1); const [frequency, setFrequency] = useState(1);
const { data, isLoading, isError, error } = useSubscription(frequency); const { data, isLoading, isError, error } = useSubscription(frequency);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [isOpen, setIsOpen] = useState(true);
console.log(data);
const frequencyLabel = (freq) => { const frequencyLabel = (freq) => {
switch (freq) { switch (freq) {
case 0: case 0:
@ -35,9 +36,8 @@ const SubscriptionPlans = () => {
<button <button
key={idx} key={idx}
type="button" type="button"
className={`btn btn-${ className={`btn btn-${frequency === idx ? "primary" : "outline-secondary"
frequency === idx ? "primary" : "outline-secondary" }`}
}`}
onClick={() => setFrequency(idx)} onClick={() => setFrequency(idx)}
> >
{label} {label}
@ -64,7 +64,7 @@ const SubscriptionPlans = () => {
<p>{error.name}</p> <p>{error.name}</p>
</div> </div>
) : ( ) : (
data.map((plan) => ( data.map((plan, index) => (
<div key={plan.id} className="col-xl-4 col-lg-6 col-md-6"> <div key={plan.id} className="col-xl-4 col-lg-6 col-md-6">
<div className="card h-100 shadow-lg border-0 p-3 text-center p-10"> <div className="card h-100 shadow-lg border-0 p-3 text-center p-10">
{/* Header */} {/* Header */}
@ -102,24 +102,69 @@ const SubscriptionPlans = () => {
<h6 className="fw-bold text-uppercase border-top pt-3 mb-3 text-center"> <h6 className="fw-bold text-uppercase border-top pt-3 mb-3 text-center">
Features Features
</h6> </h6>
<ul className="list-unstyled text-start mb-4 ms-7 fs-6">
<div className="accordion" id={`planFeatures-${plan.id}`}>
{plan.features?.modules && {plan.features?.modules &&
Object.values(plan.features.modules).map((mod) => Object.entries(plan.features.modules)
mod && mod.name ? ( .sort(([, a], [, b]) => Number(b.enabled) - Number(a.enabled))
<li .map(([key, mod]) => {
key={mod.id}
className="d-flex align-items-center mb-2" if (!mod || !mod.name) return null;
> const isFirst = index === 0;
{mod.enabled ? (
<i className="fa-regular fa-circle-check text-success me-2"></i> return (
) : ( <div className="accordion-item mb-2" key={`${plan.id}-${mod.id}`}>
<i className="fa-regular fa-circle-xmark text-danger me-2"></i> <h2 id={`heading-${plan.id}-${mod.id}`} className="accordion-header">
)} <button
{mod.name} className="accordion-button py-2 d-flex justify-content-between align-items-center"
</li> type="button"
) : null data-bs-toggle="collapse"
)} data-bs-target={`#collapse-${plan.id}-${mod.id}`}
</ul> aria-expanded={`${isFirst ? "true" : "false"}`}
aria-controls={`collapse-${plan.id}-${mod.id}`}
>
<span className="fw-semibold d-flex align-items-center">
{mod.enabled ? (
<i className="fa-regular fa-circle-check text-success me-2"></i>
) : (
<i className="fa-regular fa-circle-xmark text-danger me-2"></i>
)}
{mod.name}
</span>
<i className="bx bx-chevron-down ms-2"></i>
</button>
</h2>
<div
id={`collapse-${plan.id}-${mod.id}`}
className={`accordion-collapse collapse ${isFirst ? "show" : ""}`}
aria-labelledby={`heading-${plan.id}-${mod.id}`}
>
<div className="accordion-body py-2">
{mod.features?.length > 0 ? (
<ul className="list-unstyled ms-2">
{mod.features.map((feat) => (
<li key={feat.id} className="d-flex align-items-start mb-1">
<i
className={`bx bxs-circle ${mod.enabled ? "text-success" : "text-danger"
} me-2 mt-1`}
style={{ fontSize: "8px" }}
></i>
<span>{feat.name}</span>
</li>
))}
</ul>
) : (
<p className="text-muted small mb-0">No additional features</p>
)}
</div>
</div>
</div>
);
})}
</div>
{/* Button */} {/* Button */}
<div className="mt-auto"> <div className="mt-auto">
@ -138,6 +183,7 @@ const SubscriptionPlans = () => {
</div> </div>
</div> </div>
</div> </div>
)) ))
)} )}
</div> </div>

View File

@ -1,5 +1,5 @@
import React from 'react' import React from "react";
import LoginWithOtp from './LoginWithOtp' import LoginWithOtp from "./LoginWithOtp";
const MainLoginWithOTPPage = () => { const MainLoginWithOTPPage = () => {
return ( return (
@ -8,7 +8,7 @@ const MainLoginWithOTPPage = () => {
<div className="d-none d-lg-flex col-lg-7 col-xl-8 align-items-center p-5"> <div className="d-none d-lg-flex col-lg-7 col-xl-8 align-items-center p-5">
<div className="w-100 d-flex justify-content-center"> <div className="w-100 d-flex justify-content-center">
<img <img
src="/img/illustrations/worker_03.png" src="/img/illustrations/registration.jpg"
className="img-fluid" className="img-fluid"
alt="Login image" alt="Login image"
width="70%" width="70%"
@ -20,7 +20,7 @@ const MainLoginWithOTPPage = () => {
<LoginWithOtp /> <LoginWithOtp />
</div> </div>
</> </>
) );
} };
export default MainLoginWithOTPPage export default MainLoginWithOTPPage;

View File

@ -1,14 +1,14 @@
import React from 'react' import React from "react";
import ResetPasswordPage from './ResetPassword' import ResetPasswordPage from "./ResetPassword";
const MainResetPasswordPage = () => { const MainResetPasswordPage = () => {
return ( return (
<> <>
<div className="authentication-inner row m-0"> <div className="authentication-inner row m-0">
<div className="d-none d-lg-flex col-lg-7 col-xl-8 align-items-center p-5"> <div className="d-none d-lg-flex col-lg-7 col-xl-8 align-items-center p-5">
<div className="w-100 d-flex justify-content-center"> <div className="w-100 d-flex justify-content-center">
<img <img
src="/img/illustrations/worker_03.png" src="/img/illustrations/registration.jpg"
className="img-fluid" className="img-fluid"
alt="Login image" alt="Login image"
width="70%" width="70%"
@ -20,7 +20,7 @@ const MainResetPasswordPage = () => {
<ResetPasswordPage /> <ResetPasswordPage />
</div> </div>
</> </>
) );
} };
export default MainResetPasswordPage export default MainResetPasswordPage;