fixed Expense type to Expense Category

This commit is contained in:
pramod.mahajan 2025-11-04 13:01:51 +05:30
parent 192c04fb6f
commit 57d33ab817
8 changed files with 472 additions and 367 deletions

View File

@ -13,7 +13,7 @@ export const ExpenseSchema = (expenseTypes) => {
return z
.object({
projectId: z.string().min(1, { message: "Project is required" }),
expensesTypeId: z
expensesCategoryId: z
.string()
.min(1, { message: "Expense type is required" }),
paymentModeId: z.string().min(1, { message: "Payment mode is required" }),
@ -66,7 +66,7 @@ export const ExpenseSchema = (expenseTypes) => {
)
.superRefine((data, ctx) => {
const expenseType = expenseTypes.find(
(et) => et.id === data.expensesTypeId
(et) => et.id === data.expensesCategoryId
);
if (
expenseType?.noOfPersonsRequired &&
@ -83,7 +83,7 @@ export const ExpenseSchema = (expenseTypes) => {
export const defaultExpense = {
projectId: "",
expensesTypeId: "",
expensesCategoryId: "",
paymentModeId: "",
paidById: "",
transactionDate: "",

View File

@ -1,19 +1,41 @@
import { useState,useMemo } from "react";
import { useState, useMemo } from "react";
import Avatar from "../common/Avatar";
import { formatUTCToLocalTime } from "../../utils/dateUtils";
import Timeline from "../common/TimeLine";
const ExpenseStatusLogs = ({ data }) => {
const [visibleCount, setVisibleCount] = useState(4);
const [visibleCount, setVisibleCount] = useState(4);
const sortedLogs = useMemo(() => {
if (!data?.updateLogs) return [];
return [...data.updateLogs].sort(
(a, b) => new Date(b.updateAt) - new Date(a.updateAt)
(a, b) => new Date(b.updatedAt) - new Date(a.updatedAt)
);
}, [data?.updateLogs]);
const logsToShow = sortedLogs.slice(0, visibleCount);
const logsToShow = useMemo(
() => sortedLogs.slice(0, visibleCount),
[sortedLogs, visibleCount]
);
const timelineData = useMemo(() => {
return logsToShow.map((log, index) => ({
id: index + 1,
title: log.nextStatus?.name || "Status Updated",
description: log.nextStatus?.description || "",
timeAgo: formatTimeAgo(log.updatedAt),
color: log.nextStatus?.color || "primary",
users: log.updatedBy
? [
{
name: `${log.updatedBy.firstName || ""} ${` log?.updatedBy?.lastName` || ""}`.trim(),
role: log.updatedBy.jobRoleName || "",
avatar: log.updatedBy.photo || "assets/img/avatars/default.png",
},
]
: [],
}));
}, [logsToShow])
const handleShowMore = () => {
setVisibleCount((prev) => prev + 4);
@ -37,8 +59,8 @@ const ExpenseStatusLogs = ({ data }) => {
<small className="text-secondary text-tiny ms-2">
<em>{log.action}</em>
</small>
<span className="text-tiny text-secondary d-block" >
{formatUTCToLocalTime(log.updateAt,true)}
<span className="text-tiny text-secondary d-block">
{formatUTCToLocalTime(log.updateAt, true)}
</span>
</div>
<div className="d-flex align-items-center text-muted small mt-1">
@ -60,9 +82,12 @@ const ExpenseStatusLogs = ({ data }) => {
</button>
</div>
)}
<Timeline items={timelineData} />
</>
);
};
export default ExpenseStatusLogs;

View File

@ -40,11 +40,11 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
const [ExpenseType, setExpenseType] = useState();
const dispatch = useDispatch();
const {
ExpenseTypes,
ExpenseCategories,
loading: ExpenseLoading,
error: ExpenseError,
} = useExpenseCategory;
const schema = ExpenseSchema(ExpenseTypes);
} = useExpenseCategory();
const schema = ExpenseSchema(ExpenseCategories);
const {
register,
handleSubmit,
@ -148,7 +148,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
if (expenseToEdit && data) {
reset({
projectId: data.project.id || "",
expensesTypeId: data.expensesType.id || "",
expensesCategoryId: data.expensesType.id || "",
paymentModeId: data.paymentMode.id || "",
paidById: data.paidBy.id || "",
transactionDate: data.transactionDate?.slice(0, 10) || "",
@ -194,10 +194,10 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
CreateExpense(payload);
}
};
const ExpenseTypeId = watch("expensesTypeId");
const ExpenseTypeId = watch("expensesCategoryId");
useEffect(() => {
setExpenseType(ExpenseTypes?.find((type) => type.id === ExpenseTypeId));
setExpenseType(ExpenseCategories?.find((type) => type.id === ExpenseTypeId));
}, [ExpenseTypeId]);
const handleClose = () => {
@ -239,13 +239,13 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
</div>
<div className="col-md-6">
<Label htmlFor="expensesTypeId" className="form-label" required>
Expense Type
<Label htmlFor="expensesCategoryId" className="form-label" required>
Expense Category
</Label>
<select
className="form-select form-select-sm"
id="expensesTypeId"
{...register("expensesTypeId")}
id="expensesCategoryId"
{...register("expensesCategoryId")}
>
<option value="" disabled>
Select Type
@ -253,16 +253,16 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
{ExpenseLoading ? (
<option disabled>Loading...</option>
) : (
ExpenseTypes?.map((expense) => (
ExpenseCategories?.map((expense) => (
<option key={expense.id} value={expense.id}>
{expense.name}
</option>
))
)}
</select>
{errors.expensesTypeId && (
{errors.expensesCategoryId && (
<small className="danger-text">
{errors.expensesTypeId.message}
{errors.expensesCategoryId.message}
</small>
)}
</div>

View File

@ -21,12 +21,12 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
const {
ExpenseTypes,
ExpenseCategories,
loading: ExpenseLoading,
error: ExpenseError,
} = useExpenseCategory();
const schema = PaymentRequestSchema(ExpenseTypes);
const schema = PaymentRequestSchema(ExpenseCategories);
const { register, control, watch, handleSubmit, setValue, reset, formState: { errors }, } = useForm({
resolver: zodResolver(schema),
defaultValues: defaultPaymentRequest,
@ -193,12 +193,12 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
{...register("expenseCategoryId")}
>
<option value="" disabled>
Select Type
Select Category
</option>
{ExpenseLoading ? (
<option disabled>Loading...</option>
) : (
ExpenseTypes?.map((expense) => (
ExpenseCategories?.map((expense) => (
<option key={expense.id} value={expense.id}>
{expense.name}
</option>

View File

@ -112,81 +112,80 @@ const ViewPaymentRequest = ({ requestId }) => {
return (
<form className="container px-3" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 mb-1">
<h5 className="fw-semibold m-0">Payment Request Details</h5>
<hr />
</div>
<div className="row mb-1">
<div className="col-12 mb-1">
<h5 className="fw-semibold m-0">Request Details</h5>
<hr />
</div>
<div className="col-12 text-start fw-semibold my-2">
{data?.paymentRequestUID}
</div>
{/* Row 1 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Project Name :
</label>
<div className="text-muted">
{data.project.name}
<div className="col-12 col-sm-6 col-md-8">
<div className="col-12 text-start fw-semibold my-2">
{data?.paymentRequestUID}
</div>
{/* Row 1 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Project Name :
</label>
<div className="text-muted">{data.project.name}</div>
</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Due Date :
</label>
<div className="text-muted">
{formatUTCToLocalTime(data?.dueDate)}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Due Date :
</label>
<div className="text-muted">
{formatUTCToLocalTime(data?.dueDate)}
</div>
</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Expense Category :
</label>
<div className="text-muted">{data?.expenseCategory?.name}</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Expense Category :
</label>
<div className="text-muted">{data?.expenseCategory?.name}</div>
</div>
</div>
</div>
{/* Row 2 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Supplier :
</label>
<div className="text-muted">{data?.payee}</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Amount :
</label>
<div className="text-muted">
{formatCurrency(data?.amount, data?.currency?.currencyCode)}
{/* Row 2 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Supplier :
</label>
<div className="text-muted">{data?.payee}</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Amount :
</label>
<div className="text-muted">
{formatCurrency(data?.amount, data?.currency?.currencyCode)}
</div>
</div>
</div>
</div>
{/* Row 3 */}
{/* <div className="col-md-6 mb-3">
{/* Row 3 */}
{/* <div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
@ -197,298 +196,306 @@ const ViewPaymentRequest = ({ requestId }) => {
<div className="text-muted">{data?.paymentMode?.name}</div>
</div>
</div> */}
{data?.gstNumber && (
{data?.gstNumber && (
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
GST Number :
</label>
<div className="text-muted">{data?.gstNumber}</div>
</div>
</div>
)}
{/* Row 4 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
GST Number :
Status :
</label>
<div className="text-muted">{data?.gstNumber}</div>
<span
className={`badge bg-label-${
getColorNameFromHex(data?.expenseStatus?.color) || "secondary"
}`}
>
{data?.expenseStatus?.name}
</span>
</div>
</div>
)}
{/* Row 4 */}
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Status :
</label>
<span
className={`badge bg-label-${
getColorNameFromHex(data?.expenseStatus?.color) || "secondary"
}`}
>
{data?.expenseStatus?.name}
</span>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Pre-Approved :
</label>
<div className="text-muted">{data?.preApproved ? "Yes" : "No"}</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Project :
</label>
<div className="text-muted">{data?.project?.name}</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Created At :
</label>
<div className="text-muted">
{formatUTCToLocalTime(data?.createdAt, true)}
</div>
</div>
</div>
{/* Row 6 */}
{data?.createdBy && (
<div className="col-md-6 text-start">
<div className="d-flex align-items-center">
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold"
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Created By :
Pre-Approved :
</label>
<div className="text-muted">
{data?.preApproved ? "Yes" : "No"}
</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Project :
</label>
<div className="text-muted">{data?.project?.name}</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Created At :
</label>
<div className="text-muted">
{formatUTCToLocalTime(data?.createdAt, true)}
</div>
</div>
</div>
{/* Row 6 */}
{data?.createdBy && (
<div className="col-md-6 text-start">
<div className="d-flex align-items-center">
<Avatar
size="xs"
classAvatar="m-0"
firstName={data?.createdBy?.firstName}
lastName={data?.createdBy?.lastName}
/>
<span className="text-muted">
{`${data?.createdBy?.firstName ?? ""} ${
data?.createdBy?.lastName ?? ""
}`.trim() || "N/A"}
</span>
</div>
</div>
</div>
)}
{data?.paidBy && (
<div className="col-md-6 text-start">
<div className="d-flex align-items-center">
<label
className="form-label me-2 mb-0 fw-semibold"
style={{ minWidth: "130px" }}
>
Paid By :
</label>
<div className="d-flex align-items-center ">
<Avatar
size="xs"
classAvatar="m-0"
firstName={data?.paidBy?.firstName}
lastName={data?.paidBy?.lastName}
/>
<span className="text-muted">
{`${data?.paidBy?.firstName ?? ""} ${
data?.paidBy?.lastName ?? ""
}`.trim() || "N/A"}
</span>
</div>
</div>
</div>
)}
<div className="text-start my-1">
<label className="fw-semibold form-label">Description : </label>
<div className="text-muted">{data?.description}</div>
</div>
</div>
<div className="col-12 text-start">
<label className="form-label me-2 mb-2 fw-semibold">Attachment :</label>
<div className="d-flex flex-wrap gap-2">
{data?.documents?.map((doc) => {
const isImage = doc?.contentType?.includes("image");
return (
<div
key={doc.documentId}
className="border rounded hover-scale p-2 d-flex flex-column align-items-center"
style={{
width: "80px",
cursor: isImage ? "pointer" : "default",
}}
onClick={() => {
if (isImage) {
setDocumentView({
IsOpen: true,
Image: doc.preSignedUrl,
});
}
}}
>
<i
className={`bx ${getIconByFileType(doc.contentType)}`}
style={{ fontSize: "30px" }}
></i>
<small
className="text-center text-tiny text-truncate w-100"
title={doc.fileName}
<label
className="form-label me-2 mb-0 fw-semibold"
style={{ minWidth: "130px" }}
>
{doc.fileName}
</small>
Created By :
</label>
<div className="d-flex align-items-center">
<Avatar
size="xs"
classAvatar="m-0"
firstName={data?.createdBy?.firstName}
lastName={data?.createdBy?.lastName}
/>
<span className="text-muted">
{`${data?.createdBy?.firstName ?? ""} ${
data?.createdBy?.lastName ?? ""
}`.trim() || "N/A"}
</span>
</div>
</div>
);
})}
</div>
</div>
{data?.paidTransactionId && (
<div className="row text-start mt-2">
<div className="col-md-6 mb-sm-0 mb-2">
<label className="form-label me-2 mb-0 fw-semibold">
Transaction ID :
</label>
{data?.paidTransactionId }
</div>
<div className="col-md-6 ">
<label className="form-label me-2 mb-0 fw-semibold">
Transaction Date :
</label>
{formatUTCToLocalTime(data?.paidAt)}
</div>
</div>
)}
{data?.paidBy && (
<>
<div className="col-md-6 d-flex align-items-center">
<label className="form-label me-2 mb-0 fw-semibold">
<div className="col-md-6 text-start">
<div className="d-flex align-items-center">
<label
className="form-label me-2 mb-0 fw-semibold"
style={{ minWidth: "130px" }}
>
Paid By :
</label>
<Avatar
size="xs"
classAvatar="m-0 me-1"
firstName={data?.paidBy?.firstName}
lastName={data?.paidBy?.lastName}
/>
<span className="text-muted">
{`${data?.paidBy?.firstName} ${data?.paidBy?.lastName}`.trim()}
</span>
<div className="d-flex align-items-center ">
<Avatar
size="xs"
classAvatar="m-0"
firstName={data?.paidBy?.firstName}
lastName={data?.paidBy?.lastName}
/>
<span className="text-muted">
{`${data?.paidBy?.firstName ?? ""} ${
data?.paidBy?.lastName ?? ""
}`.trim() || "N/A"}
</span>
</div>
</div>
</div>
)}
<div className="text-start my-1">
<label className="fw-semibold form-label">Description : </label>
<div className="text-muted">{data?.description}</div>
</div>
<div className="col-6 text-start">
<label className="form-label me-2 mb-2 fw-semibold">
Attachment :
</label>
<div className="d-flex flex-wrap gap-2">
{data?.documents?.length > 0 ? (
data?.documents?.map((doc) => {
const isImage = doc?.contentType?.includes("image");
return (
<div
key={doc.documentId}
className="border rounded hover-scale p-2 d-flex flex-column align-items-center"
style={{
width: "80px",
cursor: isImage ? "pointer" : "default",
}}
onClick={() => {
if (isImage) {
setDocumentView({
IsOpen: true,
Image: doc.preSignedUrl,
});
}
}}
>
<i
className={`bx ${getIconByFileType(doc.contentType)}`}
style={{ fontSize: "30px" }}
></i>
<small
className="text-center text-tiny text-truncate w-100"
title={doc.fileName}
>
{doc.fileName}
</small>
</div>
);
})
) : (
<p className="m-0">No Attachment</p>
)}
</div>
</div>
{data?.paidTransactionId && (
<div className="row text-start mt-2">
<div className="col-md-6 mb-sm-0 mb-2">
<label className="form-label me-2 mb-0 fw-semibold">
Transaction ID :
</label>
{data?.paidTransactionId}
</div>
<div className="col-md-6 ">
<label className="form-label me-2 mb-0 fw-semibold">
Transaction Date :
</label>
{formatUTCToLocalTime(data?.paidAt)}
</div>
{data?.paidBy && (
<>
<div className="col-md-6 d-flex align-items-center">
<label className="form-label me-2 mb-0 fw-semibold">
Paid By :
</label>
<Avatar
size="xs"
classAvatar="m-0 me-1"
firstName={data?.paidBy?.firstName}
lastName={data?.paidBy?.lastName}
/>
<span className="text-muted">
{`${data?.paidBy?.firstName} ${data?.paidBy?.lastName}`.trim()}
</span>
</div>
</>
)}
</div>
)}
{Array.isArray(data?.nextStatus) && data?.nextStatus.length > 0 && (
<>
{IsPaymentProcess && nextStatusWithPermission?.length > 0 && (
<div className="row">
<div className="col-12 col-md-6 text-start">
<label className="form-label">Transaction Id </label>
<input
type="text"
className="form-control form-control-sm"
{...register("paidTransactionId")}
/>
{errors.paidTransactionId && (
<small className="danger-text">
{errors.paidTransactionId.message}
</small>
)}
</div>
<div className="col-12 col-md-6 text-start">
<label className="form-label">Transaction Date </label>
<DatePicker
name="paidAt"
control={control}
minDate={data?.createdAt}
maxDate={new Date()}
/>
{errors.paidAt && (
<small className="danger-text">
{errors.paidAt.message}
</small>
)}
</div>
<div className="col-12 col-md-6 text-start">
<label className="form-label">Paid By </label>
<EmployeeSearchInput
control={control}
name="paidById"
projectId={null}
/>
</div>
</div>
)}
<div className="col-12 mb-3 text-start">
{((nextStatusWithPermission.length > 0 && !isRejectedRequest) ||
(isRejectedRequest && isCreatedBy)) && (
<>
<Label className="form-label me-2 mb-0" required>
Comment
</Label>
<textarea
className="form-control form-control-sm"
{...register("comment")}
rows="2"
/>
{errors.comment && (
<small className="danger-text">
{errors.comment.message}
</small>
)}
</>
)}
{nextStatusWithPermission?.length > 0 &&
(!isRejectedRequest || isCreatedBy) && (
<div className="text-end flex-wrap gap-2 my-2 mt-3">
{nextStatusWithPermission?.map((status, index) => (
<button
key={status.id || index}
type="button"
onClick={() => {
setClickedStatusId(status.id);
setValue("statusId", status.id);
handleSubmit(onSubmit)();
}}
disabled={isPending || isFetching}
className="btn btn-primary btn-sm cursor-pointer mx-2 border-0"
>
{isPending && clickedStatusId === status.id
? "Please Wait..."
: status.displayName || status.name}
</button>
))}
</div>
)}
</div>
</>
)}
</div>
)}
<hr className="divider my-1 border-2 divider-primary my-2" />
{Array.isArray(data?.nextStatus) && data?.nextStatus.length > 0 && (
<>
{IsPaymentProcess && nextStatusWithPermission?.length > 0 && (
<div className="row">
<div className="col-12 col-md-6 text-start">
<label className="form-label">Transaction Id </label>
<input
type="text"
className="form-control form-control-sm"
{...register("paidTransactionId")}
/>
{errors.paidTransactionId && (
<small className="danger-text">
{errors.paidTransactionId.message}
</small>
)}
</div>
<div className="col-12 col-md-6 text-start">
<label className="form-label">Transaction Date </label>
<DatePicker
name="paidAt"
control={control}
minDate={data?.createdAt}
maxDate={new Date()}
/>
{errors.paidAt && (
<small className="danger-text">
{errors.paidAt.message}
</small>
)}
</div>
<div className="col-12 col-md-6 text-start">
<label className="form-label">Paid By </label>
<EmployeeSearchInput
control={control}
name="paidById"
projectId={null}
/>
</div>
</div>
)}
<div className="col-12 mb-3 text-start">
{((nextStatusWithPermission.length > 0 && !isRejectedRequest) ||
(isRejectedRequest && isCreatedBy)) && (
<>
<Label className="form-label me-2 mb-0" required>
Comment
</Label>
<textarea
className="form-control form-control-sm"
{...register("comment")}
rows="2"
/>
{errors.comment && (
<small className="danger-text">
{errors.comment.message}
</small>
)}
</>
)}
{nextStatusWithPermission?.length > 0 &&
(!isRejectedRequest || isCreatedBy) && (
<div className="text-end flex-wrap gap-2 my-2 mt-3">
{nextStatusWithPermission?.map((status, index) => (
<button
key={status.id || index}
type="button"
onClick={() => {
setClickedStatusId(status.id);
setValue("statusId", status.id);
handleSubmit(onSubmit)();
}}
disabled={isPending || isFetching}
className="btn btn-primary btn-sm cursor-pointer mx-2 border-0"
>
{isPending && clickedStatusId === status.id
? "Please Wait..."
: status.displayName || status.name}
</button>
))}
</div>
)}
</div>
</>
)}
<ExpenseStatusLogs data={data} />
<div className="col-12 col-sm-6 col-md-4">
<ExpenseStatusLogs data={data} />
</div>
</div>
</form>
);
};

View File

@ -0,0 +1,73 @@
import React from "react";
const Timeline = ({ items = [], transparent = true }) => {
return (
<ul className={`timeline ${transparent ? "timeline-transparent text-start" : ""}`}>
{items.map((item) => (
<li
key={item.id}
className={`timeline-item ${transparent ? "timeline-item-transparent" : ""}`}
>
<span className={`timeline-point timeline-point-${item.color || "primary"}`}></span>
<div className="timeline-event">
<div className="timeline-header mb-3 d-flex justify-content-between">
<h6 className="mb-0 text-body">{item.title}</h6>
<small className="text-body-secondary">{item.timeAgo}</small>
</div>
{item.description && <p className="mb-2">{item.description}</p>}
{item.attachments && item.attachments.length > 0 && (
<div className="d-flex align-items-center mb-2">
{item.attachments.map((att, i) => (
<div
key={i}
className="badge bg-lighter rounded d-flex align-items-center gap-2 p-2"
>
{att.icon && <img src={att.icon} alt="file" width="15" className="me-2" />}
<span className="h6 mb-0">{att.name}</span>
</div>
))}
</div>
)}
{item.users && item.users.length > 0 && (
<div className="d-flex flex-wrap align-items-center mb-2">
<ul className="list-unstyled users-list d-flex align-items-center avatar-group m-0">
{item.users.map((user, i) => (
<li key={i} className="avatar me-1" title={user.name}>
{user.avatar ? (
<img
src={user.avatar}
alt={user.name}
className="rounded-circle"
width="32"
height="32"
/>
) : (
<span className="avatar-initial rounded-circle pull-up bg-light">
{user.name}
</span>
)}
</li>
))}
</ul>
{item.users[0]?.role && (
<div className="ms-2">
<p className="mb-0 small fw-medium">{item.users[0].name}</p>
<small>{item.users[0].role}</small>
</div>
)}
</div>
)}
</div>
</li>
))}
</ul>
);
};
export default Timeline;

View File

@ -152,13 +152,13 @@ export const useContactTags = () => {
export const useExpenseCategory = () => {
const {
data: ExpenseTypes = [],
data: ExpenseCategories = [],
isLoading: loading,
error,
} = useQuery({
queryKey: ["Expense Category"],
queryFn: async () => {
const res = await MasterRespository.getExpenseCategory()
const res = await MasterRespository.getExpenseCategory();
return res.data;
},
onError: (error) => {
@ -171,7 +171,7 @@ export const useExpenseCategory = () => {
},
});
return { ExpenseTypes, loading, error };
return { ExpenseCategories, loading, error };
};
export const usePaymentMode = () => {
const {

View File

@ -120,11 +120,11 @@ const PaymentRequestPage = () => {
{ViewRequest.view && (
<GlobalModel
isOpen
size="lg"
size="xl"
modalType="top"
closeModal={() => setVieRequest({ requestId: null, view: false })}
>
<ViewPaymentRequest requestId={ViewRequest?.requestId}/>
<ViewPaymentRequest requestId={ViewRequest?.requestId}/>
</GlobalModel>
)}