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

This commit is contained in:
pramod.mahajan 2025-11-06 13:18:00 +05:30
commit 1278e32da9
13 changed files with 322 additions and 285 deletions

View File

@ -14,8 +14,7 @@ const formSchema = z.object({
selectedRole: z.record(z.boolean()), selectedRole: z.record(z.boolean()),
}); });
const ManageRole = ( {employeeId, onClosed} ) => const ManageRole = ({ employeeId, onClosed }) => {
{
const dispatch = useDispatch(); const dispatch = useDispatch();
const formStateRef = useRef({}); const formStateRef = useRef({});
@ -38,7 +37,7 @@ const ManageRole = ( {employeeId, onClosed} ) =>
}); });
const { const {
updateRoles, updateRoles,
isPending : isLoading, isPending: isLoading,
isError, isError,
error, error,
} = useUpdateEmployeeRoles({ } = useUpdateEmployeeRoles({
@ -112,56 +111,57 @@ const ManageRole = ( {employeeId, onClosed} ) =>
const isLoadingData = roleLoading || empLoading; const isLoadingData = roleLoading || empLoading;
return ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="text-start mb-3"> <div className="text-center mb-3">
<h5 className="lead">Select Roles :</h5> <h5 className="lead">Select Roles :</h5>
</div> </div>
{isLoadingData ? ( {isLoadingData ? (
<p>Loading...</p> <p>Loading...</p>
) : ( ) : (
<div <div
className="d-flex flex-wrap justify-content-between pb-4" className="d-flex flex-wrap justify-content-between pb-4"
style={{ maxHeight: "70vh", overflowY: "auto" }} style={{ maxHeight: "70vh", overflowY: "auto" }}
> >
{roles.map((role) => ( {roles.slice()
<div className="col-md-6 col-lg-4 mb-3" key={role.id}> .sort((a, b) => a.role.localeCompare(b.role)).map((role) => (
<div className="form-check ms-2 text-start"> <div className="col-md-6 col-lg-4 mb-3" key={role.id}>
<input <div className="form-check ms-2 text-start">
className="form-check-input" <input
type="checkbox" className="form-check-input"
id={role.id} type="checkbox"
{...register(`selectedRole.${role.id}`)} id={role.id}
/> {...register(`selectedRole.${role.id}`)}
<label className="form-check-label" htmlFor={role.id}> />
<small>{role.role || "--"}</small> <label className="form-check-label" htmlFor={role.id}>
</label> <small>{role.role || "--"}</small>
</div> </label>
</div>
))}
</div> </div>
)}
{errors.selectedRole && (
<div className="text-danger text-center">
{errors.selectedRole.message}
</div>
)}
<div className="text-end mt-1">
<button
type="button"
className="btn btn-sm btn-label-secondary me-2"
data-bs-dismiss="modal"
aria-label="Close"
>
Cancel
</button>
<button type="submit" className="btn btn-sm btn-primary" disabled={isLoading}>
{isLoading ? "Please Wait..." : "Submit"}
</button>
</div> </div>
</form> ))}
</div>
)}
{errors.selectedRole && (
<div className="text-danger text-center">
{errors.selectedRole.message}
</div>
)}
<div className="text-end mt-1">
<button
type="button"
className="btn btn-sm btn-label-secondary me-2"
data-bs-dismiss="modal"
aria-label="Close"
>
Cancel
</button>
<button type="submit" className="btn btn-sm btn-primary" disabled={isLoading}>
{isLoading ? "Please Wait..." : "Submit"}
</button>
</div>
</form>
); );
}; };

View File

@ -386,7 +386,7 @@ const ViewExpense = ({ ExpenseId }) => {
)} )}
</div> </div>
)} )}
<hr className="divider my-1 border-2 divider-primary my-2" /> {/* <hr className="divider my-1 border-2 divider-primary my-2" /> */}
{Array.isArray(data?.nextStatus) && data.nextStatus.length > 0 && ( {Array.isArray(data?.nextStatus) && data.nextStatus.length > 0 && (
<> <>
@ -526,7 +526,7 @@ const ViewExpense = ({ ExpenseId }) => {
</div> </div>
<div className="col-12 col-lg-5 col-xl-4"> <div className="col-12 col-lg-5 col-xl-4">
<div className="d-flex align-items-center text-secondary mb-"> <div className="d-flex align-items-center text-secondary mb-4">
<i className="bx bx-time-five me-2"></i>{" "} <i className="bx bx-time-five me-2"></i>{" "}
<p className=" m-0">TimeLine</p> <p className=" m-0">TimeLine</p>
</div> </div>

View File

@ -253,7 +253,7 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
<td colSpan={8} className="text-start"> <td colSpan={8} className="text-start">
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
{" "} {" "}
<small className="fs-6 py-1"> <small className="fs-6 py-1 ms-1">
{displayField} :{" "} {displayField} :{" "}
</small>{" "} </small>{" "}
<small className="fs-6 ms-3"> <small className="fs-6 ms-3">

View File

@ -411,7 +411,7 @@ const ViewPaymentRequest = ({ requestId }) => {
</div> </div>
<div className="col-12 col-md-6 text-start mb-1"> <div className="col-12 col-md-6 text-start mb-1">
<label className="form-label">Transaction Date </label> <label className="form-label">Transaction Date </label>
<DatePicker <DatePicker className="w-100"
name="paidAt" name="paidAt"
control={control} control={control}
minDate={data?.createdAt} minDate={data?.createdAt}

View File

@ -4,6 +4,7 @@ import {
EXPENSE_REJECTEDBY, EXPENSE_REJECTEDBY,
FREQUENCY_FOR_RECURRING, FREQUENCY_FOR_RECURRING,
ITEMS_PER_PAGE, ITEMS_PER_PAGE,
PAYEE_RECURRING_EXPENSE,
} from "../../utils/constants"; } from "../../utils/constants";
import { formatCurrency, useDebounce } from "../../utils/appUtils"; import { formatCurrency, useDebounce } from "../../utils/appUtils";
import { formatUTCToLocalTime } from "../../utils/dateUtils"; import { formatUTCToLocalTime } from "../../utils/dateUtils";
@ -16,7 +17,7 @@ import { useRecurringExpenseContext } from "../../pages/RecurringExpense/Recurri
import { useRecurringExpenseList } from "../../hooks/useExpense"; import { useRecurringExpenseList } from "../../hooks/useExpense";
const RecurringExpenseList = ({ search, filterStatuses }) => { const RecurringExpenseList = ({ search, filterStatuses }) => {
const { setManageRequest, setVieRequest , setViewRecurring} = useRecurringExpenseContext(); const { setManageRequest, setVieRequest, setViewRecurring } = useRecurringExpenseContext();
const navigate = useNavigate(); const navigate = useNavigate();
const [IsDeleteModalOpen, setIsDeleteModalOpen,] = useState(false); const [IsDeleteModalOpen, setIsDeleteModalOpen,] = useState(false);
const [deletingId, setDeletingId] = useState(null); const [deletingId, setDeletingId] = useState(null);
@ -25,6 +26,13 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
(store) => store?.globalVariables?.loginUser?.employeeInfo?.id (store) => store?.globalVariables?.loginUser?.employeeInfo?.id
); );
const statusColorMap = {
"da462422-13b2-45cc-a175-910a225f6fc8": "primary", // Active
"306856fb-5655-42eb-bf8b-808bb5e84725": "success", // Completed
"3ec864d2-8bf5-42fb-ba70-5090301dd816": "danger", // De-Activated
"8bfc9346-e092-4a80-acbf-515ae1ef6868": "warning", // Paused
};
const recurringExpenseColumns = [ const recurringExpenseColumns = [
{ {
key: "expenseCategory", key: "expenseCategory",
@ -72,8 +80,18 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
{ {
key: "status", key: "status",
label: "Status", label: "Status",
align: "text-start", align: "text-center",
getValue: (e) => e?.status?.name || "N/A", getValue: (e) => {
const color = statusColorMap[e?.status?.id] || "secondary";
const label = PAYEE_RECURRING_EXPENSE.find(
(s) => s.id === e?.status?.id
)?.label;
return (
<span className={`badge bg-label-${color}`}>
{label || e?.status?.name || "N/A"}
</span>
);
},
}, },
]; ];
@ -192,10 +210,10 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
<i <i
className="bx bx-show text-primary cursor-pointer" className="bx bx-show text-primary cursor-pointer"
onClick={() => onClick={() =>
setViewRecurring({ setViewRecurring({
recurringId: recurringExpense?.id, recurringId: recurringExpense?.id,
view: true, view: true,
}) })
} }
></i> ></i>
@ -261,9 +279,8 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
{[...Array(totalPages)].map((_, index) => ( {[...Array(totalPages)].map((_, index) => (
<li <li
key={index} key={index}
className={`page-item ${ className={`page-item ${currentPage === index + 1 ? "active" : ""
currentPage === index + 1 ? "active" : "" }`}
}`}
> >
<button <button
className="page-link" className="page-link"

View File

@ -48,19 +48,6 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
}; };
const collectionColumns = [ const collectionColumns = [
{
key: "invoiceDate",
label: "Invoice Date",
getValue: (col) => (
<span
className="text-truncate d-inline-block"
style={{ maxWidth: "200px" }}
>
{formatUTCToLocalTime(col.invoiceDate)}
</span>
),
align: "text-start",
},
{ {
key: "invoiceId", key: "invoiceId",
label: "Invoice No", label: "Invoice No",
@ -88,18 +75,33 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
align: "text-start", align: "text-start",
}, },
{ {
key: "submittedDate", key: "invoiceDate",
label: "Submission Date", label: "Invoice Date",
getValue: (col) => ( getValue: (col) => (
<span <span
className="text-truncate d-inline-block" className="text-truncate d-inline-block"
style={{ maxWidth: "200px" }} style={{ maxWidth: "200px" }}
> >
{formatUTCToLocalTime(col.createdAt)} {formatUTCToLocalTime(col.invoiceDate)}
</span> </span>
), ),
align: "text-center", align: "text-start",
}, },
// {
// key: "submittedDate",
// label: "Submission Date",
// getValue: (col) => (
// <span
// className="text-truncate d-inline-block"
// style={{ maxWidth: "200px" }}
// >
// {formatUTCToLocalTime(col.createdAt)}
// </span>
// ),
// align: "text-center",
// },
{ {
key: "expectedSubmittedDate", key: "expectedSubmittedDate",
label: "Expected Payment Date", label: "Expected Payment Date",
@ -145,6 +147,19 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
), ),
align: "text-end", align: "text-end",
}, },
{
key: "status",
label: "Status",
getValue: (col) => (
<span
className={`badge ${col.markAsCompleted ? "bg-label-primary" : "bg-label-danger"
}`}
>
{col.markAsCompleted ? "Active" : "In-active"}
</span>
),
align: "text-center",
},
]; ];
if (isLoading) return <CollectionTableSkeleton />; if (isLoading) return <CollectionTableSkeleton />;
@ -186,84 +201,84 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
canViewCollection || canViewCollection ||
canEditCollection || canEditCollection ||
canCreate) && ( canCreate) && (
<td <td
className="sticky-action-column text-center" className="sticky-action-column text-center"
style={{ padding: "12px 8px" }} style={{ padding: "12px 8px" }}
> >
<div className="dropdown z-2"> <div className="dropdown z-2">
<button <button
type="button" type="button"
className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0" className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0"
data-bs-toggle="dropdown" data-bs-toggle="dropdown"
aria-expanded="false" aria-expanded="false"
> >
<i <i
className="bx bx-dots-vertical-rounded bx-sm text-muted" className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-offset="0,8" data-bs-offset="0,8"
data-bs-placement="top" data-bs-placement="top"
data-bs-custom-class="tooltip-dark" data-bs-custom-class="tooltip-dark"
title="More Action" title="More Action"
></i> ></i>
</button> </button>
<ul className="dropdown-menu dropdown-menu-end"> <ul className="dropdown-menu dropdown-menu-end">
{/* View */} {/* View */}
<li> <li>
<a <a
className="dropdown-item cursor-pointer" className="dropdown-item cursor-pointer"
onClick={() => setViewCollection(row.id)} onClick={() => setViewCollection(row.id)}
> >
<i className="bx bx-show me-2 text-primary"></i> <i className="bx bx-show me-2 text-primary"></i>
<span>View</span> <span>View</span>
</a> </a>
</li> </li>
{/* Only if not completed */} {/* Only if not completed */}
{!row?.markAsCompleted && ( {!row?.markAsCompleted && (
<> <>
{/* Add Payment */} {/* Add Payment */}
{(isAdmin || canAddPayment) && ( {(isAdmin || canAddPayment) && (
<li> <li>
<a <a
className="dropdown-item cursor-pointer" className="dropdown-item cursor-pointer"
onClick={() => onClick={() =>
setAddPayment({ setAddPayment({
isOpen: true, isOpen: true,
invoiceId: row.id, invoiceId: row.id,
}) })
} }
> >
<i className="bx bx-wallet me-2 text-warning"></i> <i className="bx bx-wallet me-2 text-warning"></i>
<span>Add Payment</span> <span>Add Payment</span>
</a> </a>
</li> </li>
)} )}
{/* Mark Payment */} {/* Mark Payment */}
{isAdmin && ( {isAdmin && (
<li> <li>
<a <a
className="dropdown-item cursor-pointer" className="dropdown-item cursor-pointer"
onClick={() => onClick={() =>
setProcessedPayment({ setProcessedPayment({
isOpen: true, isOpen: true,
invoiceId: row.id, invoiceId: row.id,
}) })
} }
> >
<i className="bx bx-check-circle me-2 text-success"></i> <i className="bx bx-check-circle me-2 text-success"></i>
<span>Mark Payment</span> <span>Mark Payment</span>
</a> </a>
</li> </li>
)} )}
</> </>
)} )}
</ul> </ul>
</div> </div>
</td> </td>
)} )}
</tr> </tr>
)) ))
) : ( ) : (

View File

@ -227,7 +227,7 @@ const ManageCollection = ({ collectionId, onClose }) => {
</div> </div>
<div className="col-12 col-md-6 mb-2"> <div className="col-12 col-md-6 mb-2">
<Label required>Invoice Date</Label> <Label required>Invoice Date</Label>
<DatePicker <DatePicker className="w-100"
name="invoiceDate" name="invoiceDate"
control={control} control={control}
maxDate={new Date()} maxDate={new Date()}
@ -240,7 +240,7 @@ const ManageCollection = ({ collectionId, onClose }) => {
</div> </div>
<div className="col-12 col-md-6 mb-2"> <div className="col-12 col-md-6 mb-2">
<Label required>Expected Payment Date</Label> <Label required>Expected Payment Date</Label>
<DatePicker <DatePicker className="w-100"
name="exceptedPaymentDate" name="exceptedPaymentDate"
control={control} control={control}
minDate={watch("invoiceDate")} minDate={watch("invoiceDate")}
@ -253,7 +253,7 @@ const ManageCollection = ({ collectionId, onClose }) => {
</div> </div>
<div className="col-12 col-md-6 mb-2"> <div className="col-12 col-md-6 mb-2">
<Label required>Submission Date (Client)</Label> <Label required>Submission Date (Client)</Label>
<DatePicker <DatePicker className="w-100"
name="clientSubmitedDate" name="clientSubmitedDate"
control={control} control={control}
maxDate={new Date()} maxDate={new Date()}

View File

@ -29,47 +29,76 @@ const ViewCollection = ({ onClose }) => {
return ( return (
<div className="container p-3"> <div className="container p-3">
<p className="fs-5 fw-semibold">Collection Details</p> <p className="fs-5 fw-semibold">Collection Details</p>
<div className="row text-start ">
<div className="row text-start">
{/* Row 0: Balance Amount + Expected Payment Date */}
<div className="row mb-3 px-2">
{/* Balance Amount */}
<div className="col-md-6 d-flex align-items-center">
<div className="fw-bold fs-6 text-dark me-2 ms-1">Balance Amount :</div>
<div className="fw-bold fs-6 text-dark">
{formatFigure(data?.balanceAmount, {
type: "currency",
currency: "INR",
})}
</div>
</div>
{/* Expected Payment Date */}
<div className="col-md-6 d-flex align-items-center">
<div className="fw-bold fs-6 text-dark me-2 ms-3">Expected Payment Date :</div>
<div className="fw-bold fs-6 text-dark">
{formatUTCToLocalTime(data?.exceptedPaymentDate)}
</div>
</div>
</div>
{/* Project and Status */}
<div className="col-12 mb-3 d-flex justify-content-between"> <div className="col-12 mb-3 d-flex justify-content-between">
<div className="d-flex"> <div className="d-flex">
<label <label
className=" me-2 mb-0 fw-semibold" className="me-2 mb-0 fw-semibold"
style={{ minWidth: "130px" }} style={{ minWidth: "120px" }}
> >
Project : Project :
</label> </label>
<div className="text-muted">{data?.project?.name}</div> <div className="text-muted">{data?.project?.name}</div>
</div> </div>
<div> <div>
{" "}
<span <span
className={`badge bg-label-${ className={`badge bg-label-${data?.markAsCompleted ? "primary" : "danger"}`}
data?.isActive ? "primary" : "danger"
}`}
> >
{data?.isActive ? "Active" : "Inactive"} {data?.markAsCompleted ? "Active" : "Inactive"}
</span> </span>
{(isAdmin || canEditCollection) && {(isAdmin || canEditCollection) &&
!data?.receivedInvoicePayments && ( !data?.receivedInvoicePayments &&
!data?.markAsCompleted && (
<span onClick={handleEdit} className="ms-2 cursor-pointer"> <span onClick={handleEdit} className="ms-2 cursor-pointer">
<i className="bx bx-edit text-primary bx-sm"></i> <i className="bx bx-edit text-primary bx-sm"></i>
</span> </span>
)} )}
</div> </div>
</div> </div>
{/* Title */}
<div className="col-md-6"> <div className="col-md-6">
<div className="row mb-2 text-wrap"> <div className="row mb-2 text-wrap">
<div className="col-4 fw-semibold">Title :</div> <div className="col-4 fw-semibold">Title :</div>
<div className="col-8 text-wrap">{data?.title}</div> <div className="col-8 text-wrap">{data?.title}</div>
</div> </div>
</div> </div>
{/* Invoice Number */}
<div className="col-md-6"> <div className="col-md-6">
<div className="row mb-4 align-items-end"> <div className="row mb-4 align-items-end">
<div className="col-4 fw-semibold">Invoice Number:</div> <div className="col-4 fw-semibold">Invoice Number:</div>
<div className="col-8">{data?.invoiceNumber}</div> <div className="col-8">{data?.invoiceNumber}</div>
</div> </div>
</div> </div>
{/* Row 2: E-Invoice Number + Project */}
{/* E-Invoice Number */}
<div className="col-md-6"> <div className="col-md-6">
<div className="row mb-4 align-items-end"> <div className="row mb-4 align-items-end">
<div className="col-4 fw-semibold">E-Invoice Number:</div> <div className="col-4 fw-semibold">E-Invoice Number:</div>
@ -77,15 +106,15 @@ const ViewCollection = ({ onClose }) => {
</div> </div>
</div> </div>
{/* Row 3: Invoice Date + Client Submitted Date */} {/* Invoice Date */}
<div className="col-md-6"> <div className="col-md-6">
<div className="row mb-4 align-items-end"> <div className="row mb-4 align-items-end">
<div className="col-4 fw-semibold">Invoice Date:</div> <div className="col-4 fw-semibold">Invoice Date:</div>
<div className="col-8"> <div className="col-8">{formatUTCToLocalTime(data?.invoiceDate)}</div>
{formatUTCToLocalTime(data?.invoiceDate)}
</div>
</div> </div>
</div> </div>
{/* Client Submitted Date */}
<div className="col-md-6"> <div className="col-md-6">
<div className="row mb-4 align-items-end"> <div className="row mb-4 align-items-end">
<div className="col-4 fw-semibold">Client Submission Date:</div> <div className="col-4 fw-semibold">Client Submission Date:</div>
@ -94,17 +123,8 @@ const ViewCollection = ({ onClose }) => {
</div> </div>
</div> </div>
</div> </div>
{/* Row 4: Expected Payment Date + Mark as Completed */}
<div className="col-md-6">
<div className="row mb-4 align-items-end">
<div className="col-4 fw-semibold">Expected Payment Date:</div>
<div className="col-8">
{formatUTCToLocalTime(data?.exceptedPaymentDate)}
</div>
</div>
</div>
{/* Row 5: Basic Amount + Tax Amount */} {/* Basic Amount */}
<div className="col-md-6"> <div className="col-md-6">
<div className="row mb-4 align-items-end"> <div className="row mb-4 align-items-end">
<div className="col-4 fw-semibold">Basic Amount :</div> <div className="col-4 fw-semibold">Basic Amount :</div>
@ -116,6 +136,8 @@ const ViewCollection = ({ onClose }) => {
</div> </div>
</div> </div>
</div> </div>
{/* Tax Amount */}
<div className="col-md-6"> <div className="col-md-6">
<div className="row mb-4 align-items-end"> <div className="row mb-4 align-items-end">
<div className="col-4 fw-semibold">Tax Amount :</div> <div className="col-4 fw-semibold">Tax Amount :</div>
@ -127,25 +149,16 @@ const ViewCollection = ({ onClose }) => {
</div> </div>
</div> </div>
</div> </div>
{/* Row 6: Balance Amount + Created At */}
<div className="col-md-6"> {/* Created At */}
<div className="row mb-4 align-items-end">
<div className="col-4 fw-semibold">Balance Amount :</div>
<div className="col-8">
{formatFigure(data?.balanceAmount, {
type: "currency",
currency: "INR",
})}
</div>
</div>
</div>
<div className="col-md-6"> <div className="col-md-6">
<div className="row mb-4 align-items-end"> <div className="row mb-4 align-items-end">
<div className="col-4 fw-semibold">Created At :</div> <div className="col-4 fw-semibold">Created At :</div>
<div className="col-8">{formatUTCToLocalTime(data?.createdAt)}</div> <div className="col-8">{formatUTCToLocalTime(data?.createdAt)}</div>
</div> </div>
</div> </div>
{/* Row 7: Created By */}
{/* Created By */}
<div className="col-md-6"> <div className="col-md-6">
<div className="row mb-4 align-items-center"> <div className="row mb-4 align-items-center">
<div className="col-4 fw-semibold">Created By :</div> <div className="col-4 fw-semibold">Created By :</div>
@ -161,23 +174,23 @@ const ViewCollection = ({ onClose }) => {
</div> </div>
</div> </div>
</div> </div>
{/* Description */} {/* Description */}
<div className="col-12 my-1 mb-2"> <div className="col-12 my-1 mb-2">
<div className=" me-2 mb-0 fw-semibold">Description :</div> <div className="me-2 mb-0 fw-semibold">Description :</div>
<div className="text-muted">{data?.description}</div> <div className="text-muted">{data?.description}</div>
</div> </div>
{/* Attachments */}
<div className="col-12 text-start"> <div className="col-12 text-start">
<label className=" me-2 mb-2 fw-semibold">Attachment :</label> <label className="me-2 mb-2 fw-semibold">Attachment :</label>
<div className="d-flex flex-wrap gap-2"> <div className="d-flex flex-wrap gap-2">
{data?.attachments?.map((doc) => { {data?.attachments?.map((doc) => {
const isImage = doc.contentType?.includes("image"); const isImage = doc.contentType?.includes("image");
return ( return (
<div <div
key={doc.documentId} key={doc.documentId}
className="border rounded hover-scale p-2 d-flex flex-column align-items-center" className="border rounded hover-scale p-2 d-flex flex-column align-items-center"
style={{ style={{
width: "80px", width: "80px",
cursor: isImage ? "pointer" : "default", cursor: isImage ? "pointer" : "default",
@ -207,14 +220,12 @@ const ViewCollection = ({ onClose }) => {
</div> </div>
</div> </div>
<div className="container px-1"> {/* Tabs Section */}
{/* Tabs Navigation */} <div className="container px-1 mt-4">
<ul className="nav nav-tabs" role="tablist"> <ul className="nav nav-tabs" role="tablist">
<li className="nav-item"> <li className="nav-item">
<button <button
className={`nav-link ${ className={`nav-link ${activeTab === "payments" ? "active" : ""}`}
activeTab === "payments" ? "active" : ""
}`}
onClick={() => setActiveTab("payments")} onClick={() => setActiveTab("payments")}
type="button" type="button"
> >
@ -223,9 +234,7 @@ const ViewCollection = ({ onClose }) => {
</li> </li>
<li className="nav-item"> <li className="nav-item">
<button <button
className={`nav-link ${ className={`nav-link ${activeTab === "details" ? "active" : ""}`}
activeTab === "details" ? "active" : ""
}`}
onClick={() => setActiveTab("details")} onClick={() => setActiveTab("details")}
type="button" type="button"
> >
@ -235,7 +244,6 @@ const ViewCollection = ({ onClose }) => {
</li> </li>
</ul> </ul>
{/* Tab Content */}
<div className="tab-content px-1 py-0 border-top-0"> <div className="tab-content px-1 py-0 border-top-0">
{activeTab === "payments" && ( {activeTab === "payments" && (
<div className="tab-pane fade show active"> <div className="tab-pane fade show active">
@ -252,6 +260,7 @@ const ViewCollection = ({ onClose }) => {
</div> </div>
</div> </div>
</div> </div>
); );
}; };

View File

@ -178,7 +178,7 @@ export const DateRangePicker1 = ({
<div className={`position-relative ${className} w-max `}> <div className={`position-relative ${className} w-max `}>
<input <input
type="text" type="text"
className="form-control form-control-sm ps-2 pe-3 me-2 cursor-pointer fw-medium" className="form-control form-control-sm ps-2 pe-3 me-4 cursor-pointer"
placeholder={placeholder} placeholder={placeholder}
defaultValue={formattedValue} defaultValue={formattedValue}
ref={(el) => { ref={(el) => {

View File

@ -84,6 +84,7 @@ export const useMarkedPaymentReceived = (onSuccessCallBack) => {
await CollectionRepository.markPaymentReceived(payload), await CollectionRepository.markPaymentReceived(payload),
onSuccess: async () => { onSuccess: async () => {
client.invalidateQueries({ queryKey: ["collections"] }); client.invalidateQueries({ queryKey: ["collections"] });
client.invalidateQueries({ queryKey: ["collection"] });
showToast("Payment Received marked Successfully", "success"); showToast("Payment Received marked Successfully", "success");
if (onSuccessCallBack) onSuccessCallBack(); if (onSuccessCallBack) onSuccessCallBack();
}, },

View File

@ -73,13 +73,13 @@ const PaymentRequestPage = () => {
{/* Top Bar */} {/* Top Bar */}
<div className="card my-3 px-sm-4 px-0"> <div className="card my-3 px-sm-4 px-0">
<div className="card-body py-2 px-3"> <div className="card-body py-2 px-0">
<div className="row align-items-center"> <div className="row align-items-center">
<div className="col-6"> <div className="col-6">
<input <input
type="search" type="search"
className="form-control form-control-sm w-auto" className="form-control form-control-sm w-auto"
placeholder="Search Payment Req.." placeholder="Search Payment Request"
value={search} value={search}
onChange={(e) => setSearch(e.target.value)} onChange={(e) => setSearch(e.target.value)}
/> />

View File

@ -47,7 +47,7 @@ const RecurringExpensePage = () => {
<div className="container-fluid"> <div className="container-fluid">
{/* Breadcrumb */} {/* Breadcrumb */}
<Breadcrumb <Breadcrumb
data={[ data={[
{ label: "Home", link: "/dashboard" }, { label: "Home", link: "/dashboard" },
{ label: "Recurring Expense", link: null }, { label: "Recurring Expense", link: null },
]} ]}
@ -56,46 +56,48 @@ const RecurringExpensePage = () => {
{/* Top Bar */} {/* Top Bar */}
<div className="card my-3 px-sm-4 px-0"> <div className="card my-3 px-sm-4 px-0">
<div className="card-body py-2 px-1"> <div className="card-body py-2 px-1">
<div className="d-flex flex-wrap align-items-center justify-content-between"> <div className="row align-items-center mb-0">
{/* Left side: Search + Filter */} {/* Left Column: Search + Filter */}
<div className="d-flex align-items-center flex-wrap"> <div className="col-md-8 col-sm-12 mb-2 mb-md-0">
<input <div className="d-flex align-items-center flex-wrap gap-0">
type="search" <input
className="form-control form-control-sm w-auto" type="search"
placeholder="Search Recurring Expense..." className="form-control form-control-sm w-25"
value={search} placeholder="Search Recurring Expense"
onChange={(e) => setSearch(e.target.value)} value={search}
/> onChange={(e) => setSearch(e.target.value)}
/>
<div className="dropdown"> <div className="dropdown">
<a <a
className="dropdown-toggle hide-arrow cursor-pointer p-1" className="dropdown-toggle hide-arrow cursor-pointer p-1"
data-bs-toggle="dropdown" data-bs-toggle="dropdown"
aria-expanded="false" aria-expanded="false"
title="Filter" title="Filter"
> >
<i className="bx bx-slider-alt ms-1"></i> <i className="bx bx-slider-alt ms-1"></i>
</a> </a>
<ul className="dropdown-menu p-2 text-capitalize"> <ul className="dropdown-menu p-2 text-capitalize">
{PAYEE_RECURRING_EXPENSE.map(({ id, label }) => ( {PAYEE_RECURRING_EXPENSE.map(({ id, label }) => (
<li key={id}> <li key={id}>
<div className="form-check"> <div className="form-check">
<input <input
className="form-check-input" className="form-check-input"
type="checkbox" type="checkbox"
checked={selectedStatuses.includes(id)} checked={selectedStatuses.includes(id)}
onChange={() => handleStatusChange(id)} onChange={() => handleStatusChange(id)}
/> />
<label className="form-check-label">{label}</label> <label className="form-check-label">{label}</label>
</div> </div>
</li> </li>
))} ))}
</ul> </ul>
</div>
</div> </div>
</div> </div>
{/* Right side: Add Button */} {/* Right Column: Add Button */}
<div className="mt-2 mt-sm-0"> <div className="col-md-4 col-sm-12 text-md-end text-start">
<button <button
className="btn btn-sm btn-primary" className="btn btn-sm btn-primary"
type="button" type="button"
@ -113,6 +115,7 @@ const RecurringExpensePage = () => {
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -135,7 +138,7 @@ const RecurringExpensePage = () => {
/> />
</GlobalModel> </GlobalModel>
)} )}
{viewRecurring.view && ( {viewRecurring.view && (
<GlobalModel <GlobalModel
isOpen isOpen
size="lg" size="lg"
@ -150,7 +153,7 @@ const RecurringExpensePage = () => {
} }
recurringId={viewRecurring.recurringId} recurringId={viewRecurring.recurringId}
/> />
<ViewRecurringExpense/> <ViewRecurringExpense />
</GlobalModel> </GlobalModel>
)} )}

View File

@ -82,18 +82,18 @@ const CollectionPage = () => {
const handleMarkedPayment = (payload) => { const handleMarkedPayment = (payload) => {
MarkedReceived(payload); MarkedReceived(payload);
}; };
if (isAdmin === undefined || if (isAdmin === undefined ||
canAddPayment === undefined || canAddPayment === undefined ||
canEditCollection === undefined || canEditCollection === undefined ||
canViewCollection === undefined || canViewCollection === undefined ||
canCreate === undefined canCreate === undefined
) { ) {
return <div className="text-center py-5">Checking access...</div>; return <div className="text-center py-5">Checking access...</div>;
} }
if (!isAdmin && !canAddPayment && !canEditCollection && !canViewCollection && !canCreate) { if (!isAdmin && !canAddPayment && !canEditCollection && !canViewCollection && !canCreate) {
return <AccessDenied data={[{ label: "Home", link: "/" }, { label: "Collection" }]} />; return <AccessDenied data={[{ label: "Home", link: "/" }, { label: "Collection" }]} />;
} }
return ( return (
<CollectionContext.Provider value={contextMassager}> <CollectionContext.Provider value={contextMassager}>
<div className="container-fluid"> <div className="container-fluid">
@ -101,15 +101,15 @@ if (!isAdmin && !canAddPayment && !canEditCollection && !canViewCollection && !c
data={[{ label: "Home", link: "/" }, { label: "Collection" }]} data={[{ label: "Home", link: "/" }, { label: "Collection" }]}
/> />
<div className="card my-3 py-2 px-sm-4 px-0"> <div className="card my-3 py-2 px-sm-4 px-2">
<div className="row px-3"> <div className="row align-items-center">
<div className="col-12 col-md-3 mb-1"> {/* Left side: Date Picker + Show Pending */}
<div className="col-8 col-md-6 d-flex flex-wrap align-items-center gap-3">
<FormProvider {...methods}> <FormProvider {...methods}>
<DateRangePicker1 howManyDay={180} /> <DateRangePicker1 howManyDay={180} />
</FormProvider> </FormProvider>
</div>
<div className="col-12 col-md-3 d-flex align-items-center gap-2 "> <div className="form-check form-switch d-flex align-items-center mb-0">
<div className="form-check form-switch text-start align-items-center">
<input <input
type="checkbox" type="checkbox"
className="form-check-input" className="form-check-input"
@ -119,40 +119,32 @@ if (!isAdmin && !canAddPayment && !canEditCollection && !canViewCollection && !c
onChange={(e) => setShowPending(e.target.checked)} onChange={(e) => setShowPending(e.target.checked)}
/> />
<label <label
className="form-check-label ms-0" className="form-check-label ms-2"
htmlFor="inactiveEmployeesCheckbox" htmlFor="inactiveEmployeesCheckbox"
> >
Show Pending Show Completed Collections
</label> </label>
</div> </div>
</div> </div>
<div className="col-12 col-md-6 d-flex justify-content-end gap-4"> {/* Right side: Search + Add Button */}
<div className=" w-md-auto"> <div className="col-4 col-md-6 d-flex justify-content-md-end align-items-center gap-3 mt-3 mt-md-0">
{" "} <div className="w-md-auto flex-grow-1 flex-md-grow-0">
<input <input
type="search" type="search"
value={searchText} value={searchText}
onChange={(e) => setSearchText(e.target.value)} onChange={(e) => setSearchText(e.target.value)}
placeholder="search Collection" placeholder="Search Collection"
className="form-control form-control-sm" className="form-control form-control-sm"
/> />
</div> </div>
{ (canCreate || isAdmin) && (
<button
className="btn btn-sm btn-primary"
type="button"
onClick={() => setCollection({ isOpen: true, invoiceId: null })}
>
<i className="bx bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block">Add New Collection</span>
</button>
)}
{(canCreate || isAdmin) && (<button className="btn btn-sm btn-primary" type="button" onClick={() => setCollection({ isOpen: true, invoiceId: null })} > <i className="bx bx-plus-circle me-2"></i> <span className="d-none d-md-inline-block">Add New Collection</span> </button>)}
</div> </div>
</div> </div>
</div> </div>
<CollectionList <CollectionList
fromDate={fromDate} fromDate={fromDate}
toDate={toDate} toDate={toDate}
@ -209,7 +201,7 @@ if (!isAdmin && !canAddPayment && !canEditCollection && !canViewCollection && !c
<ConfirmModal <ConfirmModal
type="success" type="success"
header="Payment Successful Received" header="Payment Successful Received"
message="Payment has been recored successfully." message="Payment has been recorded successfully."
isOpen={processedPayment?.isOpen} isOpen={processedPayment?.isOpen}
loading={isPending} loading={isPending}
onSubmit={() => handleMarkedPayment(processedPayment?.invoiceId)} onSubmit={() => handleMarkedPayment(processedPayment?.invoiceId)}