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

This commit is contained in:
Kartik Sharma 2025-11-14 15:12:23 +05:30
commit 314dd67118
7 changed files with 65 additions and 41 deletions

View File

@ -22,7 +22,7 @@ const JobComments = ({ data }) => {
formState: { errors },
} = useAppForm({
resolver: zodResolver(JobCommentSchema),
default: { comment: "", attachments: [] },
defaultValues:{ comment: "", attachments: [] }
});
const {
@ -94,6 +94,7 @@ const JobComments = ({ data }) => {
const newFiles = files.filter((_, i) => i !== index);
setValue("attachments", newFiles, { shouldValidate: true });
};
console.log(watch("attachments"))
return (
<div className="row">
<div className="sticky-section bg-white p-3">

View File

@ -1,14 +1,19 @@
import React from "react";
import Avatar from "../common/Avatar";
const JobStatusLog = ({ data }) => {
console.log(data)
return (
<div className="card shadow-none p-0">
<div className="card-body p-0">
<div className="list-group">
{data?.map((item) => (
<div key={item.id} className="list-group-item border-0 border-bottom pb-3">
<div
key={item.id}
className="list-group-item border-0 border-bottom p-1"
>
<div className="d-flex justify-content-between">
<div>
<span className="fw-semibold">
@ -20,27 +25,30 @@ const JobStatusLog = ({ data }) => {
Level {item.nextStatus?.level ?? item.status?.level}
</span>
</div>
<p className="mb-1 mt-2 text-muted" style={{ fontSize: "0.9rem" }}>
{item.comment}
</p>
<div className="d-flex align-items-center mt-2">
<div className="rounded-circle bg-light d-flex justify-content-center align-items-center"
style={{ width: 32, height: 32 }}>
<i className="bx bx-user"></i>
</div>
<div className="ms-2">
<div className="fw-semibold" style={{ fontSize: "0.85rem" }}>
<div className="d-flex align-items-start mt-2 mx-0 px-0">
<Avatar
firstName={item.updatedBy?.firstName}
lastName={item.updatedBy?.lastName}
/>
<div className="">
<div className="d-flex flex-row gap-3">
<span className="fw-semibold">
{item.updatedBy?.firstName} {item.updatedBy?.lastName}
</span>
<span className="text-secondary">
{/* <em>{formatUTCToLocalTime(item?.createdAt, true)}</em> */}
</span>
</div>
<div className="text-muted" style={{ fontSize: "0.75rem" }}>
{item.updatedBy?.jobRoleName}
</div>
</div>
<div className="text-muted text-secondary">
{item?.updatedBy?.jobRoleName}
</div>
<div className="text-wrap">
<p className="mb-1 mt-2 text-muted">{item.comment}</p>
</div>
</div>
</div>
</div>
))}
</div>

View File

@ -75,9 +75,6 @@ const Jobs = () => {
</div>
</div>
<GlobalModel>
<PreviewDocument />
</GlobalModel>
</JonContext.Provider>
</>
);

View File

@ -17,12 +17,14 @@ const ManageJobTicket = ({ Job }) => {
{
id: "comment",
title: "Coments",
icon:"bx bx-comment me-2",
active: true,
content: <JobComments data={data} />,
},
{
id: "history",
title: "History",
icon:"bx bx-time me-2",
active: false,
content: <JobStatusLog data={data?.updateLogs} />,
},
@ -50,17 +52,17 @@ const ManageJobTicket = ({ Job }) => {
<div className="d-flex flex-wrap my-3">
<p>{data?.description}</p>
</div>
<div className="d-flex flex-row gap-3">
<div className="d-flex flex-row gap-5">
<span className="fw-medium">
Start Date : {formatUTCToLocalTime(data?.startDate)}
<i className="bx bx-calendar"></i> Start Date : {formatUTCToLocalTime(data?.startDate)}
</span>{" "}
<i className="bx bx-right-arrow-alt"></i>{" "}
<span className="fw-medium">
Due To : {formatUTCToLocalTime(data?.startDate)}
<i className="bx bx-calendar"></i> Due To : {formatUTCToLocalTime(data?.startDate)}
</span>
</div>
<div className="d-flex justify-content-between">
<div className="d-flex align-items-center">
<div className="d-block mt-2">
<div className="d-flex align-items-center mb-2">
<small className="fs-6 text-secondary fs-italic me-3 mt-2">
<em>Created By</em>
</small>{" "}
@ -69,13 +71,26 @@ const ManageJobTicket = ({ Job }) => {
firstName={data?.createdBy?.firstName}
lastName={data?.createdBy?.lastName}
/>{" "}
<span className="fw-medium">{`${data?.createdBy?.firstName} ${data?.createdBy?.lastName}`}</span>
<div className="d-flex flex-column">
<p className="m-0">{`${data?.createdBy?.firstName} ${data?.createdBy?.lastName}`}</p>
<small className="text-secondary">{data?.createdBy?.jobRoleName}</small>
</div>
</div>
<div className="d-flex align-items-center">
<small className="fs-6 text-secondary fs-italic me-3 mt-2">
<em>Assigned</em>
</small>
<EmployeeAvatarGroup employees={data?.assignees} />
<div className="d-flex flex-row gap-3">
{data?.assignees?.map((emp)=>(
<div className="d-flex flex-row ">
<Avatar size="xs" firstName={emp.firstName} lastName={emp.lastName}/>
<div className="d-flex flex-column">
<p className="m-0">{`${emp.firstName} ${emp.lastName}`}</p>
<small className="text-secondary">{emp.jobRoleName}</small>
</div>
</div>
))}
</div>
</div>
</div>
</div>
@ -91,7 +106,7 @@ const ManageJobTicket = ({ Job }) => {
data-bs-target={`#tab-${tab.id}`}
role="tab"
>
{tab.title}
<i className={tab.icon} /> {tab.title}
</button>
</li>
))}

View File

@ -59,21 +59,24 @@ const ALLOWED_TYPES = [
];
export const JobCommentSchema = z.object({
comment: z.string().min(1, { message: "Please leave comment" }),
attachments: z.array(
attachments: z
.array(
z.object({
fileName: z.string().min(1, { message: "Filename is required" }),
fileName: z.string().min(1),
base64Data: z.string().nullable(),
contentType: z.string().refine((val) => ALLOWED_TYPES.includes(val), {
message: "Only PDF, PNG, JPG, or JPEG files are allowed",
}),
documentId: z.string().optional(),
fileSize: z.number().max(MAX_FILE_SIZE, {
message: "File size must be less than or equal to 5MB",
}),
fileSize: z.number().max(MAX_FILE_SIZE),
description: z.string().optional(),
isActive: z.boolean().default(true),
})
),
)
.optional()
.default([]),
});
export const defaultJobValue = {
title: "",

View File

@ -51,7 +51,7 @@ const DatePicker = ({
<div className={`position-relative ${className} w-max `}>
<input
type="text"
className="form-control form-control-sm"
className="form-control form-control"
placeholder={placeholder}
value={displayValue}
onChange={(e) => {

View File

@ -159,7 +159,7 @@ export const useAddCommentJob = (onSuccessCallback) => {
queryKey: ["jobComments", variables?.jobTicketId],
});
if (onSuccessCallback) onSuccessCallback();
showToast("Job Created successfully", "success");
showToast("Comment added successfully", "success");
},
onError: (error) => {
showToast(