Refactor_Expenses #321

Merged
pramod.mahajan merged 249 commits from Refactor_Expenses into hotfix/MasterActivity 2025-08-01 13:14:59 +00:00
Showing only changes of commit e2d45e54a0 - Show all commits

View File

@ -33,7 +33,11 @@ import {
SearchSchema, SearchSchema,
} from "../../components/Expenses/ExpenseSchema"; } from "../../components/Expenses/ExpenseSchema";
import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { CREATE_EXEPENSE } from "../../utils/constants"; import {
CREATE_EXEPENSE,
VIEW_ALL_EXPNESE,
VIEW_SELF_EXPENSE,
} from "../../utils/constants";
const SelectDropdown = ({ const SelectDropdown = ({
label, label,
@ -100,12 +104,12 @@ export const useExpenseContext = () => {
throw new Error("useExpenseContext must be used within an ExpenseProvider"); throw new Error("useExpenseContext must be used within an ExpenseProvider");
} }
return context; return context;
} };
const ExpensePage = () => { const ExpensePage = () => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [filters,setFilter] = useState() const [filters, setFilter] = useState();
const IsCreatedAble = useHasUserPermission(CREATE_EXEPENSE) const IsCreatedAble = useHasUserPermission(CREATE_EXEPENSE);
const dropdownRef = useRef(null); const dropdownRef = useRef(null);
const shouldCloseOnOutsideClick = useRef(false); const shouldCloseOnOutsideClick = useRef(false);
const selectedProjectId = useSelector( const selectedProjectId = useSelector(
@ -123,7 +127,8 @@ const ExpensePage = () => {
IsOpen: false, IsOpen: false,
Image: null, Image: null,
}); });
const IsViewAll = useHasUserPermission(VIEW_ALL_EXPNESE);
const IsViewSelf = useHasUserPermission(VIEW_SELF_EXPENSE);
const contextValue = { const contextValue = {
setViewExpense, setViewExpense,
setManageExpenseModal, setManageExpenseModal,
@ -155,26 +160,25 @@ const ExpensePage = () => {
); );
const onSubmit = (data) => { const onSubmit = (data) => {
setFilter(data) setFilter(data);
};
const isValidDate = (date) => {
return date instanceof Date && !isNaN(date);
}; };
const isValidDate = (date) => {
return date instanceof Date && !isNaN(date);
};
const setDateRange = ({ startDate, endDate }) => { const setDateRange = ({ startDate, endDate }) => {
const parsedStart = new Date(startDate); const parsedStart = new Date(startDate);
const parsedEnd = new Date(endDate); const parsedEnd = new Date(endDate);
setValue(
"startDate",
isValidDate(parsedStart) ? parsedStart.toISOString().split("T")[0] : null
);
setValue(
"endDate",
isValidDate(parsedEnd) ? parsedEnd.toISOString().split("T")[0] : null
);
};
setValue(
"startDate",
isValidDate(parsedStart) ? parsedStart.toISOString().split("T")[0] : null
);
setValue(
"endDate",
isValidDate(parsedEnd) ? parsedEnd.toISOString().split("T")[0] : null
);
};
const toggleDropdown = () => { const toggleDropdown = () => {
setIsOpen((prev) => { setIsOpen((prev) => {
@ -199,19 +203,17 @@ const setDateRange = ({ startDate, endDate }) => {
document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("mousedown", handleClickOutside);
}; };
}, []); }, []);
const clearFilter =()=>{ const clearFilter = () => {
setFilter( setFilter({
{ projectIds: [],
projectIds: [], statusIds: [],
statusIds: [], createdByIds: [],
createdByIds: [], paidById: [],
paidById: [], startDate: null,
startDate: null, endDate: null,
endDate: null, });
}) reset();
reset() };
}
return ( return (
<ExpenseContext.Provider value={contextValue}> <ExpenseContext.Provider value={contextValue}>
@ -222,167 +224,182 @@ const setDateRange = ({ startDate, endDate }) => {
{ label: "Expense", link: null }, { label: "Expense", link: null },
]} ]}
/> />
<div className="card my-1 text-start px-0"> {IsViewAll || IsViewSelf ? (
<div className="card-body py-1 px-1"> <>
<div className="row"> <div className="card my-1 text-start px-0">
<div className="col-5 col-sm-4 d-flex aligin-items-center"> <div className="card-body py-1 px-1">
<div <div className="row">
className="dropdown d-inline-block mt-2 align-items-center" <div className="col-5 col-sm-4 d-flex aligin-items-center">
ref={dropdownRef}
>
<i
className="bx bx-slider-alt ms-2"
role="button"
aria-expanded={isOpen}
style={{ cursor: "pointer" }}
onClick={() => setIsOpen((v) => !v)}
></i>
{isOpen && (
<div <div
className="dropdown-menu p-3 overflow-hidden show d-flex align-items-center" className="dropdown d-inline-block mt-2 align-items-center"
style={{ minWidth: "500px" }} ref={dropdownRef}
> >
<FormProvider {...methods}> <i
<form className="bx bx-slider-alt ms-2"
className="p-2 p-sm-0" role="button"
onSubmit={handleSubmit((data) => { aria-expanded={isOpen}
onSubmit(data); style={{ cursor: "pointer" }}
setIsOpen(false); onClick={() => setIsOpen((v) => !v)}
})} ></i>
{isOpen && (
<div
className="dropdown-menu p-3 overflow-hidden show d-flex align-items-center"
style={{ minWidth: "500px" }}
> >
<div className="w-100"> <FormProvider {...methods}>
<DateRangePicker <form
onRangeChange={setDateRange} className="p-2 p-sm-0"
endDateMode="today" onSubmit={handleSubmit((data) => {
DateDifference="6" onSubmit(data);
dateFormat="DD-MM-YYYY"
/>
</div>
<div className="row g-2">
<div className="col-12 ">
<label className="form-label d-block text-secondary">
Select Status
</label>
<div className="d-flex flex-wrap">
{ExpenseStatus.map((status) => (
<Controller
key={status.id}
control={control}
name="statusIds"
render={({
field: { value = [], onChange },
}) => (
<div className="d-flex align-items-center me-4 mb-2">
<input
type="checkbox"
className="form-check-input form-check-input-sm"
value={status.id}
checked={value.includes(status.id)}
onChange={(e) => {
if (e.target.checked) {
onChange([...value, status.id]);
} else {
onChange(
value.filter(
(v) => v !== status.id
)
);
}
}}
/>
<label className="ms-2 mb-0">
{status.displayName}
</label>
</div>
)}
/>
))}
</div>
</div>
</div>
<div className="row g-2">
<SelectMultiple
name="projectIds"
label="Select Projects"
options={projectNames}
labelKey="name"
valueKey="id"
IsLoading={projectLoading}
/>
<SelectMultiple
name="createdByIds"
label="Select creator"
options={employees}
labelKey={(item) =>
`${item.firstName} ${item.lastName}`
}
valueKey="id"
IsLoading={empLoading}
/>
<SelectMultiple
name="paidById"
label="Select Paid by"
options={employees}
labelKey={(item) =>
`${item.firstName} ${item.lastName}`
}
valueKey="id"
IsLoading={empLoading}
/>
</div>
<div className="d-flex justify-content-end py-1 gap-2">
<button
type="button"
className="btn btn-secondary btn-xs"
onClick={() => {
clearFilter()
setIsOpen(false); setIsOpen(false);
}} })}
> >
Clear <div className="w-100">
</button> <DateRangePicker
<button onRangeChange={setDateRange}
type="submit" endDateMode="today"
className="btn btn-primary btn-xs" DateDifference="6"
> dateFormat="DD-MM-YYYY"
Apply />
</button> </div>
</div>
</form> <div className="row g-2">
</FormProvider> <div className="col-12 ">
<label className="form-label d-block text-secondary">
Select Status
</label>
<div className="d-flex flex-wrap">
{ExpenseStatus.map((status) => (
<Controller
key={status.id}
control={control}
name="statusIds"
render={({
field: { value = [], onChange },
}) => (
<div className="d-flex align-items-center me-4 mb-2">
<input
type="checkbox"
className="form-check-input form-check-input-sm"
value={status.id}
checked={value.includes(
status.id
)}
onChange={(e) => {
if (e.target.checked) {
onChange([
...value,
status.id,
]);
} else {
onChange(
value.filter(
(v) => v !== status.id
)
);
}
}}
/>
<label className="ms-2 mb-0">
{status.displayName}
</label>
</div>
)}
/>
))}
</div>
</div>
</div>
<div className="row g-2">
<SelectMultiple
name="projectIds"
label="Select Projects"
options={projectNames}
labelKey="name"
valueKey="id"
IsLoading={projectLoading}
/>
<SelectMultiple
name="createdByIds"
label="Select creator"
options={employees}
labelKey={(item) =>
`${item.firstName} ${item.lastName}`
}
valueKey="id"
IsLoading={empLoading}
/>
<SelectMultiple
name="paidById"
label="Select Paid by"
options={employees}
labelKey={(item) =>
`${item.firstName} ${item.lastName}`
}
valueKey="id"
IsLoading={empLoading}
/>
</div>
<div className="d-flex justify-content-end py-1 gap-2">
<button
type="button"
className="btn btn-secondary btn-xs"
onClick={() => {
clearFilter();
setIsOpen(false);
}}
>
Clear
</button>
<button
type="submit"
className="btn btn-primary btn-xs"
>
Apply
</button>
</div>
</form>
</FormProvider>
</div>
)}
</div> </div>
)} </div>
<div className="col-7 col-sm-8 text-end gap-2">
{IsCreatedAble && (
<button
type="button"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip"
title="Add New Expense"
className={`p-1 me-2 bg-primary rounded-circle `}
onClick={() =>
setManageExpenseModal({
IsOpen: true,
expenseId: null,
})
}
>
<i className="bx bx-plus fs-4 text-white"></i>
</button>
)}
</div>
</div> </div>
</div> </div>
<div className="col-7 col-sm-8 text-end gap-2">
{IsCreatedAble && (
<button
type="button"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip"
title="Add New Expense"
className={`p-1 me-2 bg-primary rounded-circle `}
onClick={() =>
setManageExpenseModal({
IsOpen: true,
expenseId: null,
})
}
>
<i className="bx bx-plus fs-4 text-white"></i>
</button>
)}
</div>
</div> </div>
<ExpenseList filters={filters} />
</>
) : (
<div className="card text-center py-1">
<i className="fa-solid fa-triangle-exclamation fs-5"></i>
<p>
Access Denied: You don't have permission to perform this action. !
</p>
</div> </div>
</div> )}
<ExpenseList filters={filters} />
{ManageExpenseModal.IsOpen && ( {ManageExpenseModal.IsOpen && (
<GlobalModel <GlobalModel
isOpen={ManageExpenseModal.IsOpen} isOpen={ManageExpenseModal.IsOpen}