From 08f06b9da2e4c3f7db260a3c77d72059cf23ae1f Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Sat, 15 Nov 2025 14:17:09 +0530 Subject: [PATCH 1/5] Correction in UI in ProjectPage. --- src/pages/project/ProjectPage.jsx | 139 ++++++++++++++---------------- 1 file changed, 67 insertions(+), 72 deletions(-) diff --git a/src/pages/project/ProjectPage.jsx b/src/pages/project/ProjectPage.jsx index 9e69d2a6..c2935128 100644 --- a/src/pages/project/ProjectPage.jsx +++ b/src/pages/project/ProjectPage.jsx @@ -40,10 +40,10 @@ const ProjectPage = () => { const [projectList, setProjectList] = useState([]); const [listView, setListView] = useState(false); const [searchTerm, setSearchTerm] = useState(""); - const [coreProjects, setCoreProjects] = useState(() => { - const storedValue = sessionStorage.getItem('whichProjectDisplay'); - return storedValue === 'true'; -}); + const [coreProjects, setCoreProjects] = useState(() => { + const storedValue = sessionStorage.getItem('whichProjectDisplay'); + return storedValue === 'true'; + }); const HasManageProject = useHasUserPermission(MANAGE_PROJECT); const [selectedStatuses, setSelectedStatuses] = useState( @@ -58,11 +58,11 @@ const ProjectPage = () => { }; -const handleToggleProject = (e) => { - const checked = e.target.checked; - setCoreProjects(checked); - sessionStorage.setItem('whichProjectDisplay', String(checked)); -}; + const handleToggleProject = (value) => { + setCoreProjects(value); + sessionStorage.setItem("whichProjectDisplay", String(value)); + }; + return ( @@ -75,10 +75,39 @@ const handleToggleProject = (e) => { />
-
+
-
-
+ {/* LEFT SIDE — DATE TOGGLE BUTTONS */} +
+
+ {/* Service Project Button */} + + {/* Organization Project Button */} + + +
+
+ + + {/* RIGHT SIDE — SEARCH + CARD/LIST + DROPDOWN */} +
+ + {/* Search */} +
{ />
-
+ {/* Card/List Buttons */} +
+
-
+ {/* Dropdown Filter */} +
+
    {PROJECT_STATUS.map(({ id, label }) => (
  • handleStatusChange(id)} @@ -144,56 +168,27 @@ const handleToggleProject = (e) => { ))}
-
- - -
-
- {HasManageProject && ( -
- + {HasManageProject && ( - -
- )} + )} + + +
+ +
-- 2.43.0 From f4b791b430685551f97900fc9ce3a5482877b255 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Sat, 15 Nov 2025 15:06:44 +0530 Subject: [PATCH 2/5] Added columns in jobs list and implementing functionality for daysleft. --- src/components/ServiceProject/JobList.jsx | 67 +++++++++++++-------- src/components/ServiceProject/Jobs.jsx | 19 +----- src/components/ServiceProject/ManageJob.jsx | 2 +- src/hooks/useServiceProject.jsx | 4 +- src/utils/appUtils.js | 25 ++++++++ 5 files changed, 70 insertions(+), 47 deletions(-) diff --git a/src/components/ServiceProject/JobList.jsx b/src/components/ServiceProject/JobList.jsx index a58fdd85..e6d073d2 100644 --- a/src/components/ServiceProject/JobList.jsx +++ b/src/components/ServiceProject/JobList.jsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { getNextBadgeColor } from "../../utils/appUtils"; +import { daysLeft, getNextBadgeColor } from "../../utils/appUtils"; import { useServiceProjectJobs } from "../../hooks/useServiceProject"; import { ITEMS_PER_PAGE } from "../../utils/constants"; import EmployeeAvatarGroup from "../common/EmployeeAvatarGroup"; @@ -37,36 +37,51 @@ const JobList = ({ filterByProject }) => { isAlwaysVisible: true, className: "text-start", }, - { - key: "project", - label: "Project", - getValue: (e) =>
{e?.project?.name}
, - isAlwaysVisible: true, - className: "text-start d-none d-sm-table-cell", - }, - - { - key: "employee", - label: "Team", - getValue: (e) => , - isAlwaysVisible: true, - className: "text-start d-none d-sm-table-cell", - }, - - { - key: "startDate", - label: "Start Date", - getValue: (e) => formatUTCToLocalTime(e.startDate), - isAlwaysVisible: true, - className: "text-center d-none d-sm-table-cell ", - }, { key: "dueDate", - label: "Due To", + label: "Due On", getValue: (e) => formatUTCToLocalTime(e.startDate), isAlwaysVisible: true, - className: "text-center d-none d-sm-table-cell", + className: "text-start d-none d-sm-table-cell", }, + { + key: "status", + label: "Status", + getValue: (e) => { + const statusName = e?.status?.displayName || "N/A"; + const statusColorMap = { + Assigned: "label-primary", + Pending: "label-warning", + Completed: "label-success", + Cancelled: "label-danger", + }; + + const badgeColor = statusColorMap[statusName] || "label-secondary"; + + return ( + + {statusName} + + ); + }, + isAlwaysVisible: true, + className: "text-start d-none d-sm-table-cell", + }, + { + key: "daysLeft", + label: "Days Left", + getValue: (e) => { + const { days, color } = daysLeft(e.startDate, e.dueDate); + + return ( + + {days !== null ? `${days} days` : "N/A"} + + ); + }, + isAlwaysVisible: true, + className: "text-start d-none d-sm-table-cell" + } ]; return ( diff --git a/src/components/ServiceProject/Jobs.jsx b/src/components/ServiceProject/Jobs.jsx index 6870c7d0..e0906f79 100644 --- a/src/components/ServiceProject/Jobs.jsx +++ b/src/components/ServiceProject/Jobs.jsx @@ -58,24 +58,7 @@ const Jobs = () => {
-
-
- {" "} - -
+
- + { return allPages.length + pageNumber; }, }); -}; +}; export const useJobTags = () => { return useQuery({ queryKey: ["Job_Tags"], @@ -215,7 +215,7 @@ export const useCreateServiceProjectJob = (onSuccessCallback) => { return await ServiceProjectRepository.CreateJob(payload); }, onSuccess: (data, variables) => { - queryClient.invalidateQueries({ queryKey: [""] }); + queryClient.invalidateQueries({ queryKey: ["serviceProjectJobs"] }); if (onSuccessCallback) onSuccessCallback(); showToast("Job Created successfully", "success"); diff --git a/src/utils/appUtils.js b/src/utils/appUtils.js index 1c681809..250d3488 100644 --- a/src/utils/appUtils.js +++ b/src/utils/appUtils.js @@ -203,3 +203,28 @@ export function getNextBadgeColor(type = "label") { colorIndex = (colorIndex + 1) % badgeColors.length; return `rounded-pill text-bg-${color}`; } + +export function daysLeft(startDate, dueDate) { + if (!startDate || !dueDate) { + return { days: null, color: "label-secondary" }; + } + + const start = new Date(startDate); + const due = new Date(dueDate); + + const today = new Date(); + const diffTime = due.getTime() - today.getTime(); + const days = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + + let color = "label-primary"; // default + + if (days < 0) { + color = "label-danger"; // overdue → red + } else if (days <= 15) { + color = "label-warning"; // near due → yellow + } else { + color = "label-primary"; // safe range + } + + return { days, color }; +} -- 2.43.0 From 2252a9c489bf69a81fcc43a41744a18fbd9b5f80 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Sat, 15 Nov 2025 15:16:37 +0530 Subject: [PATCH 3/5] At New Project popup show the Active selected all the time when popup open. --- src/components/ServiceProject/ManageServiceProject.jsx | 2 +- src/components/ServiceProject/ServiceProjectSchema.jsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/ServiceProject/ManageServiceProject.jsx b/src/components/ServiceProject/ManageServiceProject.jsx index 15afc436..4af1e741 100644 --- a/src/components/ServiceProject/ManageServiceProject.jsx +++ b/src/components/ServiceProject/ManageServiceProject.jsx @@ -201,7 +201,7 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
s.label === "Active")?.id, contactName: "", contactPhone: "", contactEmail: "", -- 2.43.0 From 031201ea56a08f57c3c8f9c5d01bd64f70ab7f54 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Sat, 15 Nov 2025 15:32:32 +0530 Subject: [PATCH 4/5] Adding validation in Contact number and adding trim functioanlity. --- .../ServiceProject/ManageServiceProject.jsx | 35 +++++++++++----- .../ServiceProject/ServiceProjectSchema.jsx | 40 +++++++++++++++---- src/components/common/DatePicker.jsx | 2 +- 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/components/ServiceProject/ManageServiceProject.jsx b/src/components/ServiceProject/ManageServiceProject.jsx index 4af1e741..e085c291 100644 --- a/src/components/ServiceProject/ManageServiceProject.jsx +++ b/src/components/ServiceProject/ManageServiceProject.jsx @@ -59,7 +59,7 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => { const onSubmit = (formData) => { if (serviceProjectId) { - let existingServiceIds = projectdata?.services?.map((s) => s.id) || []; + let existingServiceIds = projectdata?.services?.map((s) => s.id) || []; const oldServices = projectdata.services.map((service) => ({ serviceId: service.id, @@ -110,10 +110,10 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
-
{serviceProjectId ? "Update Project":"Create Project"}
+
{serviceProjectId ? "Update Project" : "Create Project"}
-
+
@@ -150,6 +150,7 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => { type="text" className="form-control form-control-sm" {...register("name")} + placeholder="Enter Project Name.." /> {errors?.name && ( {errors.name.message} @@ -163,6 +164,7 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => { type="text" className="form-control form-control-sm" {...register("shortName")} + placeholder="Enter Project Short Name.." /> {errors?.shortName && ( {errors.shortName.message} @@ -194,11 +196,11 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => { valueKey="id" label="Select Service" /> - {errors?.services && ( + {errors?.services && ( {errors.services.message} )}
- +
{ + e.target.value = e.target.value.replace(/[^0-9]/g, ""); + }} /> {errors?.contactPhone && ( {errors.contactPhone.message} @@ -244,7 +256,7 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => {
@@ -258,9 +270,10 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => { className="form-control" // maxLength={maxAddressLength} {...register("address")} - // onChange={(e) => { - // setAddressLength(e.target.value.length); - // }} + placeholder="Enter Project Address.." + // onChange={(e) => { + // setAddressLength(e.target.value.length); + // }} />
@@ -290,8 +303,8 @@ const ManageServiceProject = ({ serviceProjectId, onClose }) => { {isPending || isUpdating ? "Please wait..." : serviceProjectId - ? "Update" - : "Submit"} + ? "Update" + : "Submit"}
diff --git a/src/components/ServiceProject/ServiceProjectSchema.jsx b/src/components/ServiceProject/ServiceProjectSchema.jsx index 9c7f0ae6..7f3228d4 100644 --- a/src/components/ServiceProject/ServiceProjectSchema.jsx +++ b/src/components/ServiceProject/ServiceProjectSchema.jsx @@ -3,21 +3,45 @@ import { PROJECT_STATUS } from "../../utils/constants"; //#region Service Project export const projectSchema = z.object({ - name: z.string().min(1, "Name is required"), - shortName: z.string().min(1, "Short name is required"), - clientId: z.string().min(1, { message: "Client is required" }), + name: z + .string() + .trim() + .min(1, "Name is required"), + + shortName: z + .string() + .trim() + .min(1, "Short name is required"), + + clientId: z.string().trim().min(1, { message: "Client is required" }), + services: z .array(z.string()) .min(1, { message: "At least one service required" }), - address: z.string().min(1, "Address is required"), - assignedDate: z.string().min(1, { message: "Date is required" }), - statusId: z.string().min(1, "Status is required"), - contactName: z.string().min(1, "Contact name is required"), + + address: z.string().trim().min(1, "Address is required"), + + assignedDate: z.string().trim().min(1, { message: "Date is required" }), + + statusId: z.string().trim().min(1, "Status is required"), + + contactName: z + .string() + .trim() + .min(1, "Contact name is required") + .regex(/^[A-Za-z\s]+$/, "Contact name can contain only letters"), + contactPhone: z .string() + .trim() + .regex(/^\d+$/, "Phone number must contain only digits") .min(7, "Invalid phone number") .max(15, "Phone number too long"), - contactEmail: z.string().email("Invalid email address"), + + contactEmail: z + .string() + .trim() + .email("Invalid email address"), }); export const defaultProjectValues = { diff --git a/src/components/common/DatePicker.jsx b/src/components/common/DatePicker.jsx index d48b0258..74d1cc8f 100644 --- a/src/components/common/DatePicker.jsx +++ b/src/components/common/DatePicker.jsx @@ -51,7 +51,7 @@ const DatePicker = ({
{ -- 2.43.0 From 7803a678a8be3eda5c403b4a2ec61e145d9fb65f Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Mon, 17 Nov 2025 10:00:05 +0530 Subject: [PATCH 5/5] Adding GST number in Organization. --- src/components/Organization/ManagOrg.jsx | 17 +++++++++++++++-- .../Organization/OrganizationSchema.js | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/Organization/ManagOrg.jsx b/src/components/Organization/ManagOrg.jsx index 122e707f..e73c34e1 100644 --- a/src/components/Organization/ManagOrg.jsx +++ b/src/components/Organization/ManagOrg.jsx @@ -135,6 +135,19 @@ const ManagOrg = () => { )}
+
+ + + {errors.gstNumber && ( + {errors.gstNumber.message} + )} +
+
diff --git a/src/components/Organization/OrganizationSchema.js b/src/components/Organization/OrganizationSchema.js index 4730115c..6d8f8d72 100644 --- a/src/components/Organization/OrganizationSchema.js +++ b/src/components/Organization/OrganizationSchema.js @@ -20,6 +20,7 @@ export const organizationSchema = z.object({ serviceIds: z .array(z.string()) .min(1, { message: "Service isrequired" }), + gstNumber: z.string().optional(), }); export const defaultOrganizationValues = { @@ -29,6 +30,7 @@ export const defaultOrganizationValues = { address: "", email: "", serviceIds: [], + gstNumber : "" }; export const assignedOrgToProject = z.object({ -- 2.43.0