Merge branch 'main' of https://git.marcoaiot.com/admin/marco.pms.web into Issues_Oct_4W_V1

This commit is contained in:
Kartik Sharma 2025-11-07 14:51:05 +05:30
commit 034859e88b
11 changed files with 288 additions and 200 deletions

View File

@ -5,7 +5,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Marco PMS</title>
<title>OnFieldWork.com</title>
<meta name="description" content="" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -0,0 +1,5 @@
<svg width="65" height="65" viewBox="0 0 65 65" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.2" d="M46.5001 10.5288H32.5001L20.2251 26.5288L32.5001 56.5288L60.5001 26.5288L46.5001 10.5288Z" fill="#03C3EC"/>
<path d="M18.5 10.5288H46.5L60.5 26.5288L32.5 56.5288L4.5 26.5288L18.5 10.5288Z" stroke="#03C3EC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.2934 9.92012C33.1042 9.67343 32.8109 9.52881 32.5 9.52881C32.1891 9.52881 31.8958 9.67343 31.7066 9.92012L19.7318 25.5288H4.5C3.94772 25.5288 3.5 25.9765 3.5 26.5288C3.5 27.0811 3.94772 27.5288 4.5 27.5288H19.5537L31.5745 56.9075C31.7282 57.2833 32.094 57.5288 32.5 57.5288C32.906 57.5288 33.2718 57.2833 33.4255 56.9075L45.4463 27.5288H60.5C61.0523 27.5288 61.5 27.0811 61.5 26.5288C61.5 25.9765 61.0523 25.5288 60.5 25.5288H45.2682L33.2934 9.92012ZM42.7474 25.5288L32.5 12.1717L22.2526 25.5288H42.7474ZM21.7146 27.5288L32.5 53.8881L43.2854 27.5288H21.7146Z" fill="#03C3EC"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -15,7 +15,7 @@ const Sidebar = () => {
>
<div className="app-brand" style={{ paddingLeft: "30px" }}>
<Link to="/dashboard" className="app-brand-link">
<span className="app-brand-logo rounded-circle app-brand-logo-border">
{/* <span className="app-brand-logo rounded-circle app-brand-logo-border">
<img
className="app-brand-logo-sidebar"
src="/img/brand/marco.png"
@ -23,8 +23,19 @@ const Sidebar = () => {
aria-label="logo image"
style={{ margin: "5px", paddingRight: "5px" }}
/>
</span>
<span className="app-brand-text menu-text fw-bold ms-2">PMS</span>
</span> */}
<a
href="/"
class="app-brand-link fw-bold navbar-brand text-green fs-6"
>
<span class="app-brand-logo demo">
<img src="/img/brand/marco.png" width="50" />
</span>
<span class="text-blue">OnField</span>
<span>Work</span>
<span class="text-dark">.com</span>
</a>
</Link>
<a className="layout-menu-toggle menu-link text-large ms-auto">

View File

@ -23,13 +23,13 @@ const AuthLayout = () => {
to="/"
className="app-brand-link gap-2 position-fixed top-2 start-0 mx-2 mx-sm-4"
>
<span className="app-brand-logo rounded-circle ">
{/* <span className="app-brand-logo rounded-circle ">
<img
src="/img/brand/marco.png"
alt="marco-logo"
className="app-brand-logo-login"
/>
</span>
</span> */}
</Link>
<Outlet />
</div>

View File

@ -654,3 +654,15 @@ nav.layout-navbar.navbar-active::after {
height: 100%;
object-fit: cover;
}
.light-style .landing-hero {
background: linear-gradient(138.18deg, #eae8fd, #ede7e7 94.44%);
}
.text-green {
color: #49bf3c !important;
}
.text-blue {
color: var(--bs-blue);
}

View File

@ -54,15 +54,27 @@ const LandingPage = () => {
>
<i className="tf-icons bx bx-menu bx-lg align-middle text-heading fw-medium"></i>
</button>
{/* Mobile menu toggle: End*/}
<a href="/" className="app-brand-link">
{/* <a href="/" className="app-brand-link">
<span className="app-brand-logo demo">
<img src="/img/brand/marco.png" width={50}></img>
</span>
<span className="app-brand-text demo menu-text fw-bold ms-2 ps-1 ">
{/* <Link> */} PMS
{/* </Link> */}
PMS
</span>
</a> */}
<a
href="/"
class="app-brand-link fw-bold navbar-brand text-green fs-5"
>
<span class="app-brand-logo demo">
<img src="/img/brand/marco.png" width="50" />
</span>
<span class="text-blue">OnField</span>
<span>Work</span>
<span class="text-dark">.com</span>
</a>
</div>
{/* Menu logo wrapper: End */}
@ -226,7 +238,7 @@ const LandingPage = () => {
</SwiperSlide>
<SwiperSlide>
<SwaperSlideContent
ImageUrl="/app/dashboard-light-04.png"
ImageUrl="/img/app/dashboard-light-09.png"
Title="Role-based Permissions"
Body="Securely control access with customizable roles and permissions."
></SwaperSlideContent>
@ -367,7 +379,7 @@ const LandingPage = () => {
</div>{" "}
<div className="col-lg-3 col-sm-4 text-center features-icon-box">
<div className="text-center mb-4">
<img src="/img/icons/inventory.svg" alt="keyboard" />
<img src="/img/icons/user.svg" alt="keyboard" />
</div>
<h5 className="mb-2">Inventory Management</h5>
<p className="features-icon-description">
@ -436,7 +448,7 @@ const LandingPage = () => {
{" "}
<SwaperBlogContent
ImageUrl="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQs28_JnxJUKdAgaZsiWW4NyekVmfmLtpUtaA&s"
Title="Transforming Residential Project Delivery with Marco PMS"
Title="Transforming Residential Project Delivery with OnFieldWork.com"
Body="Sunrise Builders struggled with delays, cost overruns, and lack of transparency in their multi-phase township project. Different contractors for civil, electrical, and finishing works operated in silos, and communication gaps with the Project Management Consultant (PMC) often led to rework and disputes."
></SwaperBlogContent>
</SwiperSlide>
@ -627,7 +639,7 @@ const LandingPage = () => {
aria-expanded="true"
aria-controls="accordionOne"
>
What is MarcoPMS?
What is OnFieldWork.com?
</button>
</h2>
@ -684,7 +696,7 @@ const LandingPage = () => {
aria-expanded="false"
aria-controls="accordionThree"
>
How secure is Marco PMS?
How secure is OnFieldWork.com?
</button>
</h2>
<div
@ -694,7 +706,7 @@ const LandingPage = () => {
data-bs-parent="#accordionExample"
>
<div className="accordion-body text-start">
Security is at the core of Marco PMS. We use
Security is at the core of OnFieldWork.com. We use
industry-standard encryption (SSL/TLS) to protect data
in transit and advanced encryption to safeguard data at
rest. Role-based access controls ensure that only
@ -754,13 +766,13 @@ const LandingPage = () => {
data-bs-parent="#accordionExample"
>
<div className="accordion-body text-start">
Marco PMS operate under a proprietary license combined
with a subscription model. This means customers dont
own the software but are granted the right to access and
use it through the cloud under our Terms of Service.
Depending on the plan, licensing may be based on users,
features, or usage, and you can upgrade, downgrade, or
cancel at any time. non!
OnFieldWork.com operate under a proprietary license
combined with a subscription model. This means customers
dont own the software but are granted the right to
access and use it through the cloud under our Terms of
Service. Depending on the plan, licensing may be based
on users, features, or usage, and you can upgrade,
downgrade, or cancel at any time. non!
</div>
</div>
</div>
@ -774,7 +786,7 @@ const LandingPage = () => {
aria-expanded="false"
aria-controls="accordionSix"
>
Can I customize Marco PMS for my business needs?
Can I customize OnFieldWork.com for my business needs?
</button>
</h2>
<div
@ -784,11 +796,11 @@ const LandingPage = () => {
data-bs-parent="#accordionExample"
>
<div className="accordion-body text-start">
Yes, Marco PMS is designed to be flexible and adaptable.
You can customize workflows, user roles, permissions,
and reporting to match your organizations unique
processes. Depending on your plan, we also support
advanced customization such as integrating with
Yes, OnFieldWork.com is designed to be flexible and
adaptable. You can customize workflows, user roles,
permissions, and reporting to match your organizations
unique processes. Depending on your plan, we also
support advanced customization such as integrating with
third-party tools, adding custom fields, and tailoring
modules to fit your business requirements.
</div>
@ -823,7 +835,12 @@ const LandingPage = () => {
alt="hero elements"
></img>
</div>
<div className="col-lg-6 text-start text-sm-center text-lg-start">
<div
className="col-lg-6 text-start text-sm-center text-lg-start p-5 rounded"
style={{
border: "1px solid #d5d5d5",
}}
>
<div className="mt-5">
{" "}
<h4 className="text-start mb-1">
@ -1164,7 +1181,7 @@ const LandingPage = () => {
src="/img/brand/marco.png"
width="50"
/>
<span> Marco PMS</span>
<span> OnFieldWork.com</span>
</div>
</Link>
</span>
@ -1252,7 +1269,7 @@ const LandingPage = () => {
<img src="/img/icons/apple-icon.png" alt="apple icon" />
</a>
<a
href="https://play.google.com/store/apps/details?id=com.marco.aiotstage&pcampaignid=web_share"
href="https://play.google.com/store/apps/details?id=com.marcoonfieldwork.aiot&pcampaignid=web_share "
target="_blank"
>
<img

View File

@ -1,6 +1,6 @@
import { useState } from "react";
import { Link } from "react-router-dom";
import { AuthWrapper } from "./AuthWrapper"
import { AuthWrapper } from "./AuthWrapper";
import "./page-auth.css";
import AuthRepository from "../../repositories/AuthRepository";
import showToast from "../../services/toastService";
@ -8,54 +8,76 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const forgotPassSceham = z.object({
email: z.string().trim().email(),
})
});
const ForgotPasswordPage = () => {
const [loding, setLoading] = useState(false);
const [loding, setLoading] = useState(false)
const { register,
const {
register,
handleSubmit,
formState: { errors },
reset,
getValues } = useForm({
resolver: zodResolver(forgotPassSceham),
defaultValues: {
email: ""
}
})
getValues,
} = useForm({
resolver: zodResolver(forgotPassSceham),
defaultValues: {
email: "",
},
});
const onSubmit = async (data) => {
try {
setLoading(true)
const response = await AuthRepository.forgotPassword(data)
setLoading(true);
const response = await AuthRepository.forgotPassword(data);
if (response.data && response.success)
showToast("verification email has been sent to your registered email address", "success")
reset()
setLoading(false)
showToast(
"verification email has been sent to your registered email address",
"success"
);
reset();
setLoading(false);
} catch (err) {
reset()
reset();
if (err.response.status === 404) {
showToast("verification email has been sent to your registered email address", "success")
showToast(
"verification email has been sent to your registered email address",
"success"
);
} else {
showToast("Something wrong", "error")
showToast("Something wrong", "error");
}
setLoading(false)
setLoading(false);
}
}
};
return (
<div className="col-12 col-lg-5 col-xl-4 d-flex align-items-center p-4 p-sm-5 bg-gray-60">
<div className="w-100" style={{ maxWidth: 420, margin: "0 auto" }}>
<h4 className="mb-2">Forgot Password? 🔒</h4>
<div className="col-12 col-lg-5 col-xl-4 d-flex align-items-center p-4 p-sm-5 bg-gray-60">
<div className="w-100 m-auto" style={{ maxWidth: 420 }}>
<div className="d-flex align-items-center justify-content-center ">
<img src="/img/brand/marco.png" width="70" />
<Link aria-label="Go to Home Page" to="/">
<span class="app-brand-logo ">
<span class="text-blue fs-4">OnField</span>
<span className="text-green fs-4">Work</span>
<span class="text-dark fs-4">.com</span>
</span>
<br />
</Link>
</div>
<h5 className="mb-2 mt-5 ">Forgot Password? </h5>
<p className="mb-4">
Enter your email and we'll send you instructions to reset your password
Enter your email and we'll send you instructions to reset your
password
</p>
<form id="formAuthentication" className="mb-3" onSubmit={handleSubmit(onSubmit)}>
<form
id="formAuthentication"
className="mb-3"
onSubmit={handleSubmit(onSubmit)}
>
<div className="mb-3 text-start">
<label htmlFor="email" className="form-label">
Email
@ -78,26 +100,28 @@ const ForgotPasswordPage = () => {
</div>
)}
</div>
<button aria-label="Click me" className="btn btn-primary d-grid w-100">
<button
aria-label="Click me"
className="btn btn-primary d-grid w-100"
>
{loding ? "Please Wait..." : "Send Reset Link"}
</button>
</form>
<div className="text-center">
<Link
aria-label="Go to Login Page"
to="/auth/login"
className="d-flex align-items-center justify-content-center"
>
<i className="bx bx-chevron-left scaleX-n1-rtl bx-sm"></i>
Back to login
</Link>
</div>
<div className="text-center">
<Link
aria-label="Go to Login Page"
to="/auth/login"
className="d-flex align-items-center justify-content-center"
>
<i className="bx bx-chevron-left scaleX-n1-rtl bx-sm"></i>
Back to login
</Link>
</div>
{/* Footer Text */}
</div>
</div>
);
};
export default ForgotPasswordPage;
export default ForgotPasswordPage;

View File

@ -45,11 +45,11 @@ const LoginPage = () => {
if (data.rememberMe) {
localStorage.setItem("jwtToken", response.data.token);
localStorage.setItem("refreshToken", response.data.refreshToken);
removeSession("session")
removeSession("session");
} else {
sessionStorage.setItem("jwtToken", response.data.token);
sessionStorage.setItem("refreshToken", response.data.refreshToken);
removeSession("local")
removeSession("local");
}
setLoading(false);
navigate("/auth/switch/org");
@ -78,25 +78,35 @@ const LoginPage = () => {
}, [IsLoginWithOTP]);
useEffect(() => {
const token =
localStorage.getItem("jwtToken") ||
sessionStorage.getItem("jwtToken");
const token =
localStorage.getItem("jwtToken") || sessionStorage.getItem("jwtToken");
if (token) {
navigate("/dashboard", { replace: true });
}
}, []);
if (token) {
navigate("/dashboard", { replace: true });
}
}, []);
return (
<div className="col-12 col-lg-5 col-xl-4 d-flex align-items-center p-4 p-sm-5 bg-gray-60">
<div className="w-100" style={{ maxWidth: 420, margin: "0 auto" }}>
<h4 className="mb-2">Welcome to PMS!</h4>
<Link aria-label="Go to Home Page" to="/">
<span class="app-brand-logo rounded-circle app-brand-logo-border">
<img src="/img/brand/marco.png" width="70" />
</span>
<br />
<span class="text-dark fs-5">Welcome to</span> <br />
<h4 className="mb-2 ">
{" "}
<span class="text-blue ms-1">OnField</span>
<span className="text-green">Work</span>
<span class="text-dark">.com</span>
</h4>
</Link>
<p className="mb-4">
{IsLoginWithOTP
? "Enter your email to receive a one-time password (OTP)."
: "Please sign in to your account and start the adventure"}
</p>
<form id="formAuthentication" onSubmit={handleSubmit(onSubmit)}>
{/* Email */}
<div className="mb-3 text-start">
@ -219,7 +229,6 @@ const LoginPage = () => {
</>
)}
</form>
{/* Footer Text */}
{!IsLoginWithOTP ? (
<p className="text-center mt-3">

View File

@ -37,33 +37,36 @@ const registerSchema = z.object({
const RegisterPage = () => {
const [registered, setRegristered] = useState(false);
const [industries, setIndustries] = useState([]);
const [Loading,setLoading] = useState(false)
const [Loading, setLoading] = useState(false);
const {
register,
handleSubmit,
formState: { errors },reset
formState: { errors },
reset,
} = useForm({
resolver: zodResolver(registerSchema),
});
const onSubmit = async (data) => {
try {
setLoading(true)
setLoading(true);
const response = await MarketRepository.requestDemo(data);
showToast("Your request has been sent successfully. Please stay in touch!");
showToast(
"Your request has been sent successfully. Please stay in touch!"
);
setRegristered(true);
setLoading(false)
reset()
setLoading(false);
reset();
} catch (error) {
showToast(error.message, "error");
setLoading(false)
setLoading(false);
}
};
useEffect(() => {
fetchIndustries();
}, []);
useEffect(() => { }, [industries]);
useEffect(() => {}, [industries]);
const fetchIndustries = async () => {
try {
@ -76,11 +79,20 @@ const RegisterPage = () => {
};
return (
<>
<div className="col-12 col-lg-5 col-xl-4 d-flex align-items-center p-4 p-sm-5 bg-gray-60">
<div className="w-100" style={{ maxWidth: 420, margin: "0 auto" }}>
<h4 className="mb-2">Adventure starts here </h4>
<div className="d-flex align-items-center justify-content-center ">
<img src="/img/brand/marco.png" width="50" />
<Link aria-label="Go to Home Page" to="/">
<span class="app-brand-logo ">
<span class="text-blue fs-4">OnField</span>
<span className="text-green fs-4">Work</span>
<span class="text-dark fs-4">.com</span>
</span>
<br />
</Link>
</div>
<h5 className="mb-2">Adventure starts here </h5>
<p className="mb-3">Make your app management easy and fun!</p>
<form
@ -88,65 +100,64 @@ const RegisterPage = () => {
className="mb-2"
onSubmit={handleSubmit(onSubmit)}
>
<div className="row">
<div className="col-12 col-sm-6 mb-2 text-start">
<label htmlFor="organizatioinName" className="form-label">
Organization Name
</label>
<input
type="text"
className="form-control form-control-sm"
id="organizatioinName"
{...register("organizatioinName")}
name="organizatioinName"
placeholder="Enter your Organization Name"
autoFocus
/>
{errors.organizatioinName && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.organizatioinName.message}
</div>
)}
</div>
<div className="col-12 col-sm-6 mb-2 text-start">
<label htmlFor="email" className="form-label">
Email
</label>
<input
type="text"
className="form-control form-control-sm"
id="email"
name="email"
placeholder="Enter your email"
{...register("email")}
/>
{errors.email && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.email.message}
</div>
)}
</div>
<div className="col-12 col-sm-6 mb-2 text-start">
<label htmlFor="organizatioinName" className="form-label">
Organization Name
</label>
<input
type="text"
className="form-control form-control-sm"
id="organizatioinName"
{...register("organizatioinName")}
name="organizatioinName"
placeholder="Enter your Organization Name"
autoFocus
/>
{errors.organizatioinName && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.organizatioinName.message}
</div>
)}
</div>
<div className="col-12 col-sm-6 mb-2 text-start">
<label htmlFor="email" className="form-label">
Email
</label>
<input
type="text"
className="form-control form-control-sm"
id="email"
name="email"
placeholder="Enter your email"
{...register("email")}
/>
{errors.email && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.email.message}
</div>
)}
</div>
</div>
<div className="mb-2 form-password-toggle text-start">
<label className="form-label" htmlFor="contactperson">
Contact Person
</label>
<input
type="text"
id="contactperson"
{...register("contactPerson")}
className="form-control form-control-sm"
name="contactPerson"
placeholder="Contact Person"
aria-describedby="contactperson"
/>
<input
type="text"
id="contactperson"
{...register("contactPerson")}
className="form-control form-control-sm"
name="contactPerson"
placeholder="Contact Person"
aria-describedby="contactperson"
/>
{errors.contactPerson && (
<div
className="danger-text text-start"
@ -160,15 +171,15 @@ const RegisterPage = () => {
<label className="form-label" htmlFor="contactnumber">
Contact Number
</label>
<input
type="text"
id="contactnumber"
{...register("contactNumber")}
className="form-control form-control-sm"
name="contactNumber"
placeholder="Contact Number"
aria-describedby="contactnumber"
/>
<input
type="text"
id="contactnumber"
{...register("contactNumber")}
className="form-control form-control-sm"
name="contactNumber"
placeholder="Contact Number"
aria-describedby="contactnumber"
/>
{errors.contactNumber && (
<div
className="danger-text text-start"
@ -182,14 +193,14 @@ const RegisterPage = () => {
<label className="form-label" htmlFor="contactnumber">
About Organization
</label>
<textarea
id="about"
className="form-control"
placeholder="About..."
aria-label="about"
aria-describedby="about"
{...register("about")}
></textarea>
<textarea
id="about"
className="form-control"
placeholder="About..."
aria-label="about"
aria-describedby="about"
{...register("about")}
></textarea>
{errors.about && (
<div
className="danger-text text-start"
@ -203,20 +214,20 @@ const RegisterPage = () => {
<label className="form-label" htmlFor="oragnizationSize">
Organization Size
</label>
<select
className="form-select form-select-sm"
id="oragnizationSize"
name="oragnizationSize"
{...register("oragnizationSize")}
aria-label="Default select example"
>
<option value="">Number of Employees</option>
<option value="1-10">1-10</option>
<option value="10-50">10-50</option>
<option value="50-100">50-100</option>
<option value="100-200">100-200</option>
<option value="more than 200">more than 200</option>
</select>
<select
className="form-select form-select-sm"
id="oragnizationSize"
name="oragnizationSize"
{...register("oragnizationSize")}
aria-label="Default select example"
>
<option value="">Number of Employees</option>
<option value="1-10">1-10</option>
<option value="10-50">10-50</option>
<option value="50-100">50-100</option>
<option value="100-200">100-200</option>
<option value="more than 200">more than 200</option>
</select>
{errors.oragnizationSize && (
<div
className="danger-text text-start"
@ -230,24 +241,24 @@ const RegisterPage = () => {
<label className="form-label" htmlFor="industryId">
Industry
</label>
<select
className="form-select form-select-sm"
id="industryId"
name="industryId"
{...register("industryId")}
aria-label="Default select example"
>
<option value="">Select Industry</option>
{industries.length > 0 ? (
industries.map((item) => (
<option value={item.id} key={item.id}>
{item.name}
</option>
))
) : (
<option disabled>Loading industries...</option>
)}
</select>
<select
className="form-select form-select-sm"
id="industryId"
name="industryId"
{...register("industryId")}
aria-label="Default select example"
>
<option value="">Select Industry</option>
{industries.length > 0 ? (
industries.map((item) => (
<option value={item.id} key={item.id}>
{item.name}
</option>
))
) : (
<option disabled>Loading industries...</option>
)}
</select>
{errors.industryId && (
<div
className="danger-text text-start"
@ -276,7 +287,6 @@ const RegisterPage = () => {
privacy policy & terms
</Link>
</label>
</div>
{errors.terms && (
<div
@ -291,7 +301,7 @@ const RegisterPage = () => {
aria-label="Click me "
className="btn btn-primary d-grid w-100"
>
{Loading ? "Please Wait..." :" Request Demo"}
{Loading ? "Please Wait..." : " Request Demo"}
</button>
</form>
@ -313,4 +323,4 @@ const RegisterPage = () => {
);
};
export default RegisterPage;
export default RegisterPage;