progress in expense update

This commit is contained in:
pramod mahajan 2025-07-24 02:55:39 +05:30
parent 5e93798d01
commit 289a732600
9 changed files with 414 additions and 115 deletions

View File

@ -27,6 +27,7 @@
<link rel="stylesheet" href="/assets/vendor/css/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="/assets/css/core-extend.css" />
<link rel="stylesheet" href="/assets/css/default.css" />
<link rel="stylesheet" href="/assets/css/skeleton.css" />
<link rel="stylesheet" href="/assets/vendor/libs/perfect-scrollbar/perfect-scrollbar.css" />

32
public/assets/css/skeleton.css vendored Normal file
View File

@ -0,0 +1,32 @@
/* skeleton.css */
.skeleton {
background-color: #e2e8f0; /* Tailwind's gray-300 */
border-radius: 0.25rem; /* Tailwind's rounded */
position: relative;
overflow: hidden;
}
.skeleton::after {
content: '';
display: block;
position: absolute;
top: 0; left: -150px;
height: 100%;
width: 150px;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.4),
transparent
);
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% {
left: -150px;
}
100% {
left: 100%;
}
}

View File

@ -6,9 +6,10 @@ import { formatDate, formatUTCToLocalTime } from "../../utils/dateUtils";
import Pagination from "../common/Pagination";
import { ITEMS_PER_PAGE } from "../../utils/constants";
import { AppColorconfig, getColorNameFromHex } from "../../utils/appUtils";
import { ExpenseTableSkeleton } from "./ExpenseSkeleton";
const ExpenseList = () => {
const { setViewExpense,setExpenseModal } = useExpenseContext();
const { setViewExpense,setManageExpenseModal } = useExpenseContext();
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 10;
@ -22,8 +23,9 @@ const ExpenseList = () => {
};
const { data, isLoading, isError,isInitialLoading } = useExpenseList(10, currentPage, filter);
if (isInitialLoading) return <div>Loading...</div>;
const { data, isLoading, isError,isInitialLoading,error } = useExpenseList(10, currentPage, filter);
if (isInitialLoading) return <ExpenseTableSkeleton/>;
if (isError) return <div>{error}</div>;
const items = data.data ?? [];
const totalPages = data?.totalPages ?? 1;
const hasMore = currentPage < totalPages;
@ -192,7 +194,7 @@ const ExpenseList = () => {
</span>
<span
className="cursor-pointer"
onClick={()=>setExpenseModal({isOpen:true,ExpEdit:expense})}
onClick={()=>setManageExpenseModal({IsOpen:true,expenseId:expense.id})}
>
<i className='bx bx-edit bx-sm text-secondary'></i>
</span>

View File

@ -35,7 +35,7 @@ export const ExpenseSchema = (expenseTypes) => {
.array(
z.object({
fileName: z.string().min(1, { message: "Filename is required" }),
base64Data: z.string().min(1, { message: "File data is required" }),
base64Data: z.string().nullable(),
contentType: z.string().refine((val) => ALLOWED_TYPES.includes(val), {
message: "Only PDF, PNG, JPG, or JPEG files are allowed",
}),

View File

@ -0,0 +1,218 @@
import React from "react";
const SkeletonLine = ({ height = 20, width = "100%", className = "" }) => (
<div
className={`skeleton mb-2 ${className}`}
style={{
height,
width,
}}
></div>
);
const ExpenseSkeleton = () => {
return (
<div className="container p-3">
<div className="d-flex justify-content-center">
<SkeletonLine height={20} width="200px" />
</div>
{[...Array(5)].map((_, idx) => (
<div className="row my-2" key={idx}>
<div className="col-md-6">
<SkeletonLine />
</div>
<div className="col-md-6">
<SkeletonLine />
</div>
</div>
))}
<div className="row my-2">
<div className="col-md-12">
<SkeletonLine height={60} />
</div>
</div>
<div className="row my-2">
<div className="col-md-12">
<SkeletonLine height={120} />
</div>
</div>
<div className="d-flex justify-content-center gap-2 mt-3">
<SkeletonLine height={35} width="100px" />
<SkeletonLine height={35} width="100px" />
</div>
</div>
);
};
export default ExpenseSkeleton;
export const ExpenseDetailsSkeleton = () => {
return (
<div className="container px-3">
<div className="row mb-3">
<div className="d-flex justify-content-center mb-3">
<SkeletonLine height={20} width="180px" className="mb-2" />
</div>
{[...Array(3)].map((_, i) => (
<div className="col-12 col-md-4 mb-3" key={`row-1-${i}`}>
<SkeletonLine height={14} className="mb-1" />
<SkeletonLine />
</div>
))}
{[...Array(6)].map((_, i) => (
<div className="col-12 col-md-4 mb-3" key={`row-2-${i}`}>
<SkeletonLine height={14} className="mb-1" />
<SkeletonLine />
</div>
))}
<div className="col-12 my-2">
<SkeletonLine height={14} width="100px" className="mb-2" />
{[...Array(2)].map((_, i) => (
<div
className="list-group-item d-flex align-items-center mb-2"
key={i}
>
<div
className="rounded me-2"
style={{
height: "50px",
width: "80px",
backgroundColor: "#dcdcdc",
borderRadius: "4px",
}}
/>
<div className="w-100">
<SkeletonLine height={14} width="60%" className="mb-1" />
<SkeletonLine height={14} width="20%" />
</div>
</div>
))}
</div>
<hr className="divider my-1" />
<div className="col-12 mb-3">
<SkeletonLine height={14} width="80px" className="mb-1" />
<SkeletonLine height={60} className="mb-2" />
<div className="d-flex gap-2 flex-wrap">
{[...Array(2)].map((_, i) => (
<SkeletonLine
key={i}
height={30}
width="100px"
className="rounded"
/>
))}
</div>
</div>
</div>
</div>
);
};
const SkeletonCell = ({ width = "100%", height = 20, className = "" }) => (
<div
className={`skeleton ${className}`}
style={{
width,
height,
borderRadius: 4,
}}
/>
);
export const ExpenseTableSkeleton = ({ rows = 5 }) => {
return (
<table
className="card-body table border-top dataTable no-footer dtr-column text-nowrap"
aria-describedby="DataTables_Table_0_info"
id="horizontal-example"
>
<thead>
<tr>
<th colSpan={2}>
<div className="text-start ms-6">Date Time</div>
</th>
<th className="d-none d-sm-table-cell">
<div className="text-start ms-5">Expense Type</div>
</th>
<th className="d-none d-sm-table-cell">
<div className="text-start ms-5">Payment Mode</div>
</th>
<th className="d-none d-sm-table-cell">Paid By</th>
<th className="d-none d-md-table-cell">Amount</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{[...Array(rows)].map((_, idx) => (
<tr key={idx} className={idx % 2 === 0 ? "odd" : "even"}>
{/* Date Time colSpan=2 */}
<td colSpan={2} className="sorting_1">
<div className="d-flex justify-content-start align-items-center user-name ms-6">
<SkeletonCell width="120px" height={18} />
</div>
</td>
{/* Expense Type */}
<td className="text-start d-none d-sm-table-cell ms-5">
<SkeletonCell width="90px" height={16} />
</td>
{/* Payment Mode */}
<td className="text-start d-none d-sm-table-cell ms-5">
<SkeletonCell width="90px" height={16} />
</td>
{/* Paid By (Avatar + name) */}
<td className="text-start d-none d-sm-table-cell ms-5">
<div className="d-flex align-items-center gap-2">
<SkeletonCell width="30px" height={30} className="rounded-circle" />
<SkeletonCell width="80px" height={16} />
</div>
</td>
{/* Amount */}
<td className="d-none d-md-table-cell text-end">
<SkeletonCell width="60px" height={16} />
</td>
{/* Status */}
<td>
<SkeletonCell width="80px" height={22} className="rounded" />
</td>
{/* Action (icons) */}
<td>
<div className="d-flex justify-content-center align-items-center gap-2">
{[...Array(3)].map((__, i) => (
<SkeletonCell
key={i}
width={20}
height={20}
className="rounded"
style={{ display: "inline-block" }}
/>
))}
</div>
</td>
</tr>
))}
</tbody>
</table>
);
};

View File

@ -16,9 +16,19 @@ import {
useEmployeesByProject,
} from "../../hooks/useEmployees";
import Avatar from "../common/Avatar";
import { useCreateExpnse } from "../../hooks/useExpense";
import {
useCreateExpnse,
useExpense,
useUpdateExepse,
} from "../../hooks/useExpense";
import ExpenseSkeleton from "./ExpenseSkeleton";
const CreateExpense = ({closeModal,expenseToEdit,}) => {
const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
const {
data,
isLoading,
error: ExpenseErrorLoad,
} = useExpense(expenseToEdit);
const [ExpenseType, setExpenseType] = useState();
const dispatch = useDispatch();
const {
@ -38,27 +48,6 @@ const CreateExpense = ({closeModal,expenseToEdit,}) => {
resolver: zodResolver(schema),
defaultValues: defaultExpense,
});
console.log(expenseToEdit)
useEffect(() => {
if (expenseToEdit) {
reset({
projectId: expenseToEdit.project.id || "",
expensesTypeId: expenseToEdit.expensesType.id
|| "",
paymentModeId: expenseToEdit.paymentMode.id || "",
paidById: expenseToEdit.paidBy.id || "",
transactionDate: expenseToEdit.transactionDate?.slice(0, 10) || "",
transactionId: expenseToEdit.transactionId || "",
description: expenseToEdit.description || "",
location: expenseToEdit.location || "",
supplerName: expenseToEdit.supplerName || "",
amount: expenseToEdit.amount || "",
noOfPersons: expenseToEdit.noOfPersons || "",
billAttachments: expenseToEdit.billAttachments || [],
});
}
}, [expenseToEdit, reset]);
const selectedproject = watch("projectId");
@ -83,6 +72,7 @@ const CreateExpense = ({closeModal,expenseToEdit,}) => {
error: EmpError,
} = useEmployeesByProject(selectedproject);
const files = watch("billAttachments");
const onFileChange = async (e) => {
const newFiles = Array.from(e.target.files);
@ -132,27 +122,66 @@ const CreateExpense = ({closeModal,expenseToEdit,}) => {
setValue("billAttachments", newFiles, { shouldValidate: true });
};
const {mutate:CreateExpense,isPending} = useCreateExpnse(()=>{
useEffect(() => {
if (expenseToEdit && data) {
reset({
projectId: data.project.id || "",
expensesTypeId: data.expensesType.id || "",
paymentModeId: data.paymentMode.id || "",
paidById: data.paidBy.id || "",
transactionDate: data.transactionDate?.slice(0, 10) || "",
transactionId: data.transactionId || "",
description: data.description || "",
location: data.location || "",
supplerName: data.supplerName || "",
amount: data.amount || "",
noOfPersons: data.noOfPersons || "",
billAttachments: data.documents
? data.documents.map((doc) => ({
fileName: doc.fileName,
base64Data: null,
contentType: doc.contentType,
fileSize: 0,
description: "",
preSignedUrl: doc.preSignedUrl,
}))
: [],
});
}
}, [data, reset, employees]);
const { mutate: UpdateExpense, isPending } = useUpdateExepse(() =>
handleClose()
})
);
const { mutate: CreateExpense, isPending: createPending } = useCreateExpnse(
() => {
handleClose();
}
);
const onSubmit = (payload) => {
console.log("Form Data:", payload);
CreateExpense(payload)
if (expenseToEdit) {
const editPayload = { ...payload, id: data.id };
UpdateExpense({ id: data.id, payload: editPayload });
} else {
CreateExpense(payload);
}
};
const ExpenseTypeId = watch("expensesTypeId");
useEffect(() => {
setExpenseType(ExpenseTypes?.find((type) => type.id === ExpenseTypeId));
return () => reset(defaultExpense);
}, [ExpenseTypeId]);
const handleClose =()=>{
reset()
closeModal()
}
const handleClose = () => {
reset();
closeModal();
};
if(EmpLoading || StatusLoadding || projectLoading || ExpenseLoading || isLoading) return <ExpenseSkeleton/>
return (
<div className="container p-3">
<h5 className="m-0">Create New Expense</h5>
<h5 className="m-0">{expenseToEdit ? "Update Expense ": "Create New Expense"}</h5>
<form id="expenseForm" onSubmit={handleSubmit(onSubmit)}>
<div className="row my-2">
<div className="col-md-6">
@ -257,16 +286,13 @@ const CreateExpense = ({closeModal,expenseToEdit,}) => {
) : (
employees?.map((emp) => (
<option key={emp.id} value={emp.id}>
{`${emp.firstName} ${emp.lastName} `}
</option>
))
)}
</select>
{errors.paidById && (
<small className="danger-text">
{errors.paidById.message}
</small>
{errors.paidById && (
<small className="danger-text">{errors.paidById.message}</small>
)}
</div>
</div>
@ -401,9 +427,7 @@ const CreateExpense = ({closeModal,expenseToEdit,}) => {
<div className="row my-2">
<div className="col-md-12">
<label className="form-label ">
Upload Bill{" "}
</label>
<label className="form-label ">Upload Bill </label>
<div
className="border border-secondary border-dashed rounded p-4 text-center bg-textMuted position-relative"
@ -440,29 +464,37 @@ const CreateExpense = ({closeModal,expenseToEdit,}) => {
{files.map((file, idx) => (
<a
key={idx}
class="d-flex justify-content-between text-start p-1"
className="d-flex justify-content-between text-start p-1"
href={file.preSignedUrl || "#"}
target="_blank"
rel="noopener noreferrer"
>
<div>
<span className="mb-0 text-secondary small d-block">
{file.fileName}
</span>
<span className="text-body-secondary small d-block">
{formatFileSize(file.fileSize)}
{file.fileSize ? formatFileSize(file.fileSize) : ""}
</span>
</div>
<i
className="bx bx-trash bx-sm cursor-pointer text-danger"
onClick={() => removeFile(idx)}
onClick={(e) => {
e.preventDefault();
removeFile(idx);
}}
></i>
</a>
))}
</div>
)}
{Array.isArray(errors.billAttachments) &&
errors.billAttachments.map((fileError, index) => (
<div key={index} className="danger-text small mt-1">
{fileError?.fileSize?.message ||
fileError?.contentType?.message}
fileError?.contentType?.message ||
fileError?.base64Data?.message}
</div>
))}
</div>
@ -470,10 +502,18 @@ const CreateExpense = ({closeModal,expenseToEdit,}) => {
<div className="d-flex justify-content-center gap-2">
{" "}
<button type="submit" className="btn btn-primary btn-sm mt-3" disabled={isPending}>
{isPending ? "Please Wait...":"Submit"}
<button
type="submit"
className="btn btn-primary btn-sm mt-3"
disabled={isPending}
>
{isPending ? "Please Wait..." : expenseToEdit ? "Update" : "Submit"}
</button>
<button type="reset" onClick={handleClose} className="btn btn-secondary btn-sm mt-3">
<button
type="reset"
onClick={handleClose}
className="btn btn-secondary btn-sm mt-3"
>
Cancel
</button>
</div>
@ -482,5 +522,4 @@ const CreateExpense = ({closeModal,expenseToEdit,}) => {
);
};
export default CreateExpense;
export default ManageExpense;

View File

@ -5,12 +5,13 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { ActionSchema } from "./ExpenseSchema";
import { useExpenseContext } from "../../pages/Expense/ExpensePage";
import { getColorNameFromHex } from "../../utils/appUtils";
import { ExpenseDetailsSkeleton } from "./ExpenseSkeleton";
const ViewExpense = ({ ExpenseId }) => {
const { data, isLoading, isError, error } = useExpense(ExpenseId);
const [imageLoaded, setImageLoaded] = useState({});
const { setDocumentView } = useExpenseContext();
const {
register,
handleSubmit,
@ -36,16 +37,7 @@ const ViewExpense = ({ ExpenseId }) => {
MakeAction(Payload);
};
if (isLoading) {
return (
<div
className="d-flex justify-content-center align-items-center"
style={{ minHeight: "300px" }}
>
<div className="fs-5 text-muted">Loading...</div>
</div>
);
}
if (isLoading) return <ExpenseDetailsSkeleton/>
const handleImageLoad = (id) => {
setImageLoaded((prev) => ({ ...prev, [id]: true }));
};
@ -59,7 +51,7 @@ const ViewExpense = ({ ExpenseId }) => {
</div>
{/* Expense Info Rows */}
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="col-12 col-md-4 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold">
Transaction Date :
@ -70,7 +62,7 @@ const ViewExpense = ({ ExpenseId }) => {
</div>
</div>
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="col-12 col-md-4 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold">
Expense Type :
@ -79,7 +71,7 @@ const ViewExpense = ({ ExpenseId }) => {
</div>
</div>
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="col-12 col-md-4 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold">
Supplier :
@ -88,14 +80,14 @@ const ViewExpense = ({ ExpenseId }) => {
</div>
</div>
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="col-12 col-md-4 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold">Amount :</label>
<div className="text-muted"> {data.amount}</div>
</div>
</div>
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="col-12 col-md-4 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold">
Payment Mode :
@ -104,7 +96,7 @@ const ViewExpense = ({ ExpenseId }) => {
</div>
</div>
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="col-12 col-md-4 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold">
Paid By :
@ -115,76 +107,72 @@ const ViewExpense = ({ ExpenseId }) => {
</div>
</div>
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="d-flex align-items-center">
<div className="col-12 text-start col-md-4 mb-3">
<label className="form-label me-2 mb-0 fw-semibold">Status :</label>
<span className={`badge bg-label-${getColorNameFromHex(ExpenseId?.status?.color) || 'secondary'}`}>
{ExpenseId.status.displayName}
<span className={`badge bg-label-${getColorNameFromHex(data?.status?.color) || 'secondary'}`}>
{data?.status?.displayName}
</span>
</div>
</div>
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="d-flex">
<div className="col-12 text-start d-flex col-md-4 mb-3">
<label className="form-label me-2 mb-0 fw-semibold">
Pre-Approved :
</label>
<div className="text-muted">{data.preApproved ? "Yes" : "No"}</div>
</div>
</div>
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="col-12 col-md-4 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold">
Project :
</label>
<div className="text-muted text-start">{data.project.name}</div>
<div className="text-muted text-start">{data?.project?.name}</div>
</div>
</div>
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="col-12 col-md-4 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold">
Created By :
</label>
<div className="text-muted">
{data.createdBy.firstName} {data.createdBy.lastName}
{data?.createdBy?.firstName} {data?.createdBy?.lastName}
</div>
</div>
</div>
<div className="col-12 col-sm-6 col-md-4 mb-3">
<div className="col-12 col-md-4 mb-3">
<div className="d-flex">
<label className="form-label me-2 mb-0 fw-semibold">
Created At :
</label>
<div className="text-muted">
{formatUTCToLocalTime(data.createdAt, true)}
{formatUTCToLocalTime(data?.createdAt, true)}
</div>
</div>
</div>
</div>
</div>
<div className="text-start">
<label className="form-label me-2 mb-0 fw-semibold">Description:</label>
<div className="text-muted">{data.description}</div>
<div className="text-muted">{data?.description}</div>
</div>
<div className="col-12 my-2 text-start ">
<label className="form-label me-2 mb-0 fw-semibold">
Attachement :
</label>
{data.documents?.map((doc) => (
<a
{data?.documents && data?.documents?.map((doc) => (
<div
className="list-group-item list-group-item-action d-flex align-items-center"
key={doc.id}
>
<div
className="rounded me-3 d-flex align-items-center justify-content-center"
className="rounded me-1 d-flex align-items-center justify-content-center cursor-pointer"
style={{ height: "50px", width: "80px", position: "relative" }}
>
{doc.contentType === "application/pdf" ? (
<i className="bx bxs-file-pdf"></i>
<div><i className="bx bxs-file-pdf" style={{fontSize:"45px"}}></i></div>
) : (
<>
{!imageLoaded[doc.id] && (
@ -192,7 +180,6 @@ const ViewExpense = ({ ExpenseId }) => {
Loading...
</div>
)}
<img
src={doc.thumbPreSignedUrl}
alt={doc.fileName}
@ -214,12 +201,12 @@ const ViewExpense = ({ ExpenseId }) => {
</div>
<div className="w-100">
<p className="mb-0">{doc.fileName}</p>
<small className="mb-0 small">{doc.fileName}</small>
<div className="d">
<i className="bx bx-cloud-download cursor-pointer"></i>
</div>
</div>
</a>
</div>
))}
</div>

View File

@ -54,7 +54,7 @@ export const useUpdateExepse =()=>{
const queryClient = useQueryClient();
return useMutation({
mutationFn:async (id,payload)=>{
mutationFn:async ({id,payload})=>{
const response = await ExpenseRepository.UpdateExpense(id,payload)
},
onSuccess:(updatedExpense,variables)=>{

View File

@ -1,27 +1,33 @@
import React, { createContext, useContext, useState } from "react";
import ExpenseList from "../../components/Expenses/ExpenseList";
import CreateExpense from "../../components/Expenses/ManageExpense";
import ViewExpense from "../../components/Expenses/ViewExpense";
import Breadcrumb from "../../components/common/Breadcrumb";
import GlobalModel from "../../components/common/GlobalModel";
import PreviewDocument from "../../components/Expenses/PreviewDocument";
import ManageExpense from "../../components/Expenses/ManageExpense";
export const ExpenseContext = createContext();
export const useExpenseContext = () => useContext(ExpenseContext);
const ExpensePage = () => {
const [expenseModal, setExpenseModal] = useState({
isOpen: false,
ExpEdit: false,
const [ManageExpenseModal, setManageExpenseModal] = useState({
IsOpen: null,
expenseId: null,
});
const [viewExpense, setViewExpense] = useState({
expenseId: null,
view: false,
});
const [ViewDocument, setDocumentView] = useState({
IsOpen: false,
Image: null,
});
const contextValue = {
setViewExpense,
setExpenseModal,
setManageExpenseModal,
setDocumentView,
};
return (
@ -56,7 +62,12 @@ const ExpensePage = () => {
data-bs-custom-class="tooltip"
title="Add New Expense"
className={`p-1 me-2 bg-primary rounded-circle `}
onClick={() => setNewExpense(true)}
onClick={() =>
setManageExpenseModal({
IsOpen: true,
expenseId: null,
})
}
>
<i className="bx bx-plus fs-4 text-white"></i>
</button>
@ -66,25 +77,22 @@ const ExpensePage = () => {
</div>
<ExpenseList />
{expenseModal.isOpen && (
{ManageExpenseModal.IsOpen && (
<GlobalModel
isOpen={expenseModal.isOpen}
isOpen={ManageExpenseModal.IsOpen}
size="lg"
closeModal={() =>
setExpenseModal({
isOpen: false,
ExpEdit:null
setManageExpenseModal({
IsOpen: null,
expenseId: null,
})
}
>
<CreateExpense
expenseToEdit={expenseModal.ExpEdit}
<ManageExpense
key={ManageExpenseModal.expenseId ?? "new"}
expenseToEdit={ManageExpenseModal.expenseId}
closeModal={() =>
setExpenseModal({
isOpen: false,
ExpEdit:null
})
setManageExpenseModal({ IsOpen: null, expenseId: null })
}
/>
</GlobalModel>
@ -94,6 +102,7 @@ const ExpensePage = () => {
<GlobalModel
isOpen={viewExpense.view}
size="lg"
modalType="top"
closeModal={() =>
setViewExpense({
expenseId: null,
@ -104,6 +113,17 @@ const ExpensePage = () => {
<ViewExpense ExpenseId={viewExpense.expenseId} />
</GlobalModel>
)}
{ViewDocument.IsOpen && (
<GlobalModel
size="lg"
key={ViewDocument.IsOpen ?? "new"}
isOpen={ViewDocument.IsOpen}
closeModal={() => setDocumentView({ IsOpen: false, Image: null })}
>
<PreviewDocument imageUrl={ViewDocument.Image} />
</GlobalModel>
)}
</div>
</ExpenseContext.Provider>
);