Refactor_Expenses #321
@ -41,64 +41,6 @@ import {
|
|||||||
import { useFab } from "../../Context/FabContext";
|
import { useFab } from "../../Context/FabContext";
|
||||||
import ExpenseFilterPanel from "../../components/Expenses/ExpenseFilterPanel";
|
import ExpenseFilterPanel from "../../components/Expenses/ExpenseFilterPanel";
|
||||||
|
|
||||||
const SelectDropdown = ({
|
|
||||||
label,
|
|
||||||
options = [],
|
|
||||||
loading = false,
|
|
||||||
placeholder = "Select...",
|
|
||||||
valueKey = "id",
|
|
||||||
labelKey = "name",
|
|
||||||
selectedValues = [],
|
|
||||||
onChange,
|
|
||||||
isMulti = false,
|
|
||||||
}) => {
|
|
||||||
const handleChange = (e) => {
|
|
||||||
const selected = Array.from(
|
|
||||||
e.target.selectedOptions,
|
|
||||||
(option) => option.value
|
|
||||||
);
|
|
||||||
onChange && onChange(selected);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="select-dropdown">
|
|
||||||
<label>{label}</label>
|
|
||||||
<div className="dropdown-menu show">
|
|
||||||
{options.map((option) => {
|
|
||||||
const checked = selectedValues.includes(option[valueKey]);
|
|
||||||
return (
|
|
||||||
<div key={option[valueKey]} className="form-check">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
className="form-check-input"
|
|
||||||
id={`checkbox-${option[valueKey]}`}
|
|
||||||
checked={checked}
|
|
||||||
onChange={() => {
|
|
||||||
let newSelected;
|
|
||||||
if (checked) {
|
|
||||||
newSelected = selectedValues.filter(
|
|
||||||
(val) => val !== option[valueKey]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
newSelected = [...selectedValues, option[valueKey]];
|
|
||||||
}
|
|
||||||
onChange(newSelected);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
className="form-check-label"
|
|
||||||
htmlFor={`checkbox-${option[valueKey]}`}
|
|
||||||
>
|
|
||||||
{option[labelKey] || option[valueKey]}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ExpenseContext = createContext();
|
export const ExpenseContext = createContext();
|
||||||
export const useExpenseContext = () => {
|
export const useExpenseContext = () => {
|
||||||
const context = useContext(ExpenseContext);
|
const context = useContext(ExpenseContext);
|
||||||
@ -112,8 +54,6 @@ 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 shouldCloseOnOutsideClick = useRef(false);
|
|
||||||
const selectedProjectId = useSelector(
|
const selectedProjectId = useSelector(
|
||||||
(store) => store.localVariables.projectId
|
(store) => store.localVariables.projectId
|
||||||
);
|
);
|
||||||
@ -160,6 +100,7 @@ const ExpensePage = () => {
|
|||||||
selectedProjectId,
|
selectedProjectId,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
const { setOffcanvasContent, setShowTrigger } = useFab();
|
||||||
|
|
||||||
const onSubmit = (data) => {
|
const onSubmit = (data) => {
|
||||||
setFilter(data);
|
setFilter(data);
|
||||||
@ -182,29 +123,6 @@ const ExpensePage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDropdown = () => {
|
|
||||||
setIsOpen((prev) => {
|
|
||||||
shouldCloseOnOutsideClick.current = !prev;
|
|
||||||
return !prev;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function handleClickOutside(event) {
|
|
||||||
if (
|
|
||||||
shouldCloseOnOutsideClick.current &&
|
|
||||||
dropdownRef.current &&
|
|
||||||
dropdownRef.current.contains(event.target)
|
|
||||||
) {
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("mousedown", handleClickOutside);
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("mousedown", handleClickOutside);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
const clearFilter = () => {
|
const clearFilter = () => {
|
||||||
setFilter({
|
setFilter({
|
||||||
projectIds: [],
|
projectIds: [],
|
||||||
@ -217,15 +135,12 @@ const ExpensePage = () => {
|
|||||||
reset();
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
const { setOffcanvasContent, setShowTrigger } = useFab();
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShowTrigger(true);
|
setShowTrigger(true);
|
||||||
|
|
||||||
setOffcanvasContent(
|
setOffcanvasContent(
|
||||||
"Expense Filters",
|
"Expense Filters",
|
||||||
<ExpenseFilterPanel
|
<ExpenseFilterPanel onApply={(data) => setFilter(data)} />
|
||||||
onApply={(data) => setFilter(data)}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
return () => {
|
return () => {
|
||||||
setOffcanvasContent("", null);
|
setOffcanvasContent("", null);
|
||||||
@ -247,142 +162,7 @@ const ExpensePage = () => {
|
|||||||
<div className="card my-1 text-start px-0">
|
<div className="card my-1 text-start px-0">
|
||||||
<div className="card-body py-1 px-1">
|
<div className="card-body py-1 px-1">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-5 col-sm-4 d-flex aligin-items-center">
|
<div className="col-5 col-sm-4 d-flex aligin-items-center"></div>
|
||||||
<div
|
|
||||||
className="dropdown d-inline-block mt-2 align-items-center"
|
|
||||||
ref={dropdownRef}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="bx bx-slider-alt ms-2 d-none"
|
|
||||||
role="button"
|
|
||||||
aria-expanded={isOpen}
|
|
||||||
style={{ cursor: "pointer" }}
|
|
||||||
onClick={() => setIsOpen((v) => !v)}
|
|
||||||
></i>
|
|
||||||
{isOpen && (
|
|
||||||
<div
|
|
||||||
className="dropdown-menu p-3 overflow-hidden show d-flex align-items-center"
|
|
||||||
style={{ minWidth: "500px" }}
|
|
||||||
>
|
|
||||||
<FormProvider {...methods}>
|
|
||||||
<form
|
|
||||||
className="p-2 p-sm-0"
|
|
||||||
onSubmit={handleSubmit((data) => {
|
|
||||||
onSubmit(data);
|
|
||||||
setIsOpen(false);
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<div className="w-100">
|
|
||||||
<DateRangePicker
|
|
||||||
onRangeChange={setDateRange}
|
|
||||||
endDateMode="today"
|
|
||||||
DateDifference="6"
|
|
||||||
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);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Clear
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="btn btn-primary btn-xs"
|
|
||||||
>
|
|
||||||
Apply
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</FormProvider>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-7 col-sm-8 text-end gap-2">
|
<div className="col-7 col-sm-8 text-end gap-2">
|
||||||
{IsCreatedAble && (
|
{IsCreatedAble && (
|
||||||
<button
|
<button
|
||||||
|
Loading…
x
Reference in New Issue
Block a user