Merge remote-tracking branch 'origin/Subscription_Plan' into Purchase_Invoice_Management
|
Before Width: | Height: | Size: 500 KiB After Width: | Height: | Size: 52 KiB |
BIN
public/img/hero/bg-011.jpg
Normal file
|
After Width: | Height: | Size: 500 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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">
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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 ? (
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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 --> */}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||