Merge branch 'upgrade_Expense' of https://git.marcoaiot.com/admin/marco.pms.web into Recurring_Expense
This commit is contained in:
commit
76fe342c1e
@ -13,7 +13,7 @@ const ExpenseByProject = () => {
|
|||||||
const [viewMode, setViewMode] = useState("Category");
|
const [viewMode, setViewMode] = useState("Category");
|
||||||
const [chartData, setChartData] = useState({ categories: [], data: [] });
|
const [chartData, setChartData] = useState({ categories: [], data: [] });
|
||||||
|
|
||||||
const { ExpenseTypes, loading: typeLoading } = useExpenseCategory();
|
const { ExpenseCategories, loading: typeLoading } = useExpenseCategory();
|
||||||
|
|
||||||
const { data: expenseApiData, isLoading } = useExpenseDataByProject(
|
const { data: expenseApiData, isLoading } = useExpenseDataByProject(
|
||||||
projectId,
|
projectId,
|
||||||
@ -133,7 +133,7 @@ const ExpenseByProject = () => {
|
|||||||
style={{ maxWidth: "200px" }}
|
style={{ maxWidth: "200px" }}
|
||||||
>
|
>
|
||||||
<option value="">All Types</option>
|
<option value="">All Types</option>
|
||||||
{ExpenseTypes.map((type) => (
|
{ExpenseCategories?.map((type) => (
|
||||||
<option key={type.id} value={type.id}>
|
<option key={type.id} value={type.id}>
|
||||||
{type.name}
|
{type.name}
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
@ -2,7 +2,8 @@ import { useState, useMemo } from "react";
|
|||||||
import Avatar from "../common/Avatar";
|
import Avatar from "../common/Avatar";
|
||||||
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||||
import Timeline from "../common/TimeLine";
|
import Timeline from "../common/TimeLine";
|
||||||
|
import moment from "moment";
|
||||||
|
import { getColorNameFromHex } from "../../utils/appUtils";
|
||||||
const ExpenseStatusLogs = ({ data }) => {
|
const ExpenseStatusLogs = ({ data }) => {
|
||||||
const [visibleCount, setVisibleCount] = useState(4);
|
const [visibleCount, setVisibleCount] = useState(4);
|
||||||
|
|
||||||
@ -23,27 +24,28 @@ const timelineData = useMemo(() => {
|
|||||||
id: index + 1,
|
id: index + 1,
|
||||||
title: log.nextStatus?.name || "Status Updated",
|
title: log.nextStatus?.name || "Status Updated",
|
||||||
description: log.nextStatus?.description || "",
|
description: log.nextStatus?.description || "",
|
||||||
timeAgo: formatTimeAgo(log.updatedAt),
|
timeAgo: moment.utc(log?.updatedAt).local().fromNow(),
|
||||||
color: log.nextStatus?.color || "primary",
|
color: getColorNameFromHex(log.nextStatus?.color) || "primary",
|
||||||
users: log.updatedBy
|
users: log.updatedBy
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
name: `${log.updatedBy.firstName || ""} ${` log?.updatedBy?.lastName` || ""}`.trim(),
|
firstName: log.updatedBy.firstName || "",
|
||||||
|
lastName: log?.updatedBy?.lastName || "",
|
||||||
role: log.updatedBy.jobRoleName || "",
|
role: log.updatedBy.jobRoleName || "",
|
||||||
avatar: log.updatedBy.photo || "assets/img/avatars/default.png",
|
avatar: log.updatedBy.photo,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
}));
|
}));
|
||||||
}, [logsToShow])
|
}, [logsToShow]);
|
||||||
|
|
||||||
const handleShowMore = () => {
|
const handleShowMore = () => {
|
||||||
setVisibleCount((prev) => prev + 4);
|
setVisibleCount((prev) => prev + 4);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="page-min-h overflow-auto">
|
||||||
<div className="row g-2">
|
{/* <div className="row g-2">
|
||||||
{logsToShow.map((log) => (
|
{logsToShow.map((log) => (
|
||||||
<div key={log.id} className="col-12 d-flex align-items-start mb-1">
|
<div key={log.id} className="col-12 d-flex align-items-start mb-1">
|
||||||
<Avatar
|
<Avatar
|
||||||
@ -81,13 +83,11 @@ const timelineData = useMemo(() => {
|
|||||||
Show More
|
Show More
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
<Timeline items={timelineData} />
|
<Timeline items={timelineData} />
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ExpenseStatusLogs;
|
export default ExpenseStatusLogs;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
|
|||||||
|
|
||||||
const { profile } = useProfile();
|
const { profile } = useProfile();
|
||||||
|
|
||||||
const schema = PaymentRequestSchema(ExpenseTypes);
|
const schema = PaymentRequestSchema(ExpenseCategories);
|
||||||
const { register, control, watch, handleSubmit, setValue, reset, formState: { errors }, } = useForm({
|
const { register, control, watch, handleSubmit, setValue, reset, formState: { errors }, } = useForm({
|
||||||
resolver: zodResolver(schema),
|
resolver: zodResolver(schema),
|
||||||
defaultValues: defaultPaymentRequest,
|
defaultValues: defaultPaymentRequest,
|
||||||
@ -492,7 +492,7 @@ setValue('payee',profile?.employeeInfo.id)
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="reset"
|
type="reset"
|
||||||
disabled={createPending}
|
disabled={createPending || isPending}
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
className="btn btn-label-secondary btn-sm mt-3"
|
className="btn btn-label-secondary btn-sm mt-3"
|
||||||
>
|
>
|
||||||
@ -501,9 +501,9 @@ setValue('payee',profile?.employeeInfo.id)
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-primary btn-sm mt-3"
|
className="btn btn-primary btn-sm mt-3"
|
||||||
disabled={createPending}
|
disabled={createPending || isPending}
|
||||||
>
|
>
|
||||||
{createPending
|
{createPending || isPending
|
||||||
? "Please Wait..."
|
? "Please Wait..."
|
||||||
: requestToEdit
|
: requestToEdit
|
||||||
? "Update"
|
? "Update"
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { EXPENSE_DRAFT, EXPENSE_REJECTEDBY, ITEMS_PER_PAGE } from "../../utils/constants";
|
import {
|
||||||
|
EXPENSE_DRAFT,
|
||||||
|
EXPENSE_REJECTEDBY,
|
||||||
|
ITEMS_PER_PAGE,
|
||||||
|
} from "../../utils/constants";
|
||||||
import {
|
import {
|
||||||
formatCurrency,
|
formatCurrency,
|
||||||
getColorNameFromHex,
|
getColorNameFromHex,
|
||||||
@ -13,6 +17,7 @@ import { ExpenseTableSkeleton } from "../Expenses/ExpenseSkeleton";
|
|||||||
import ConfirmModal from "../common/ConfirmModal";
|
import ConfirmModal from "../common/ConfirmModal";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
|
import Error from "../common/Error";
|
||||||
|
|
||||||
const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
||||||
const { setManageRequest, setVieRequest } = usePaymentRequestContext();
|
const { setManageRequest, setVieRequest } = usePaymentRequestContext();
|
||||||
@ -37,7 +42,8 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
displayField = "Status";
|
displayField = "Status";
|
||||||
break;
|
break;
|
||||||
case "submittedBy":
|
case "submittedBy":
|
||||||
key = `${item?.createdBy?.firstName ?? ""} ${item.createdBy?.lastName ?? ""
|
key = `${item?.createdBy?.firstName ?? ""} ${
|
||||||
|
item.createdBy?.lastName ?? ""
|
||||||
}`.trim();
|
}`.trim();
|
||||||
displayField = "Submitted By";
|
displayField = "Submitted By";
|
||||||
break;
|
break;
|
||||||
@ -91,7 +97,8 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
label: "Submitted By",
|
label: "Submitted By",
|
||||||
align: "text-start",
|
align: "text-start",
|
||||||
getValue: (e) =>
|
getValue: (e) =>
|
||||||
`${e.createdBy?.firstName ?? ""} ${e.createdBy?.lastName ?? ""
|
`${e.createdBy?.firstName ?? ""} ${
|
||||||
|
e.createdBy?.lastName ?? ""
|
||||||
}`.trim() || "N/A",
|
}`.trim() || "N/A",
|
||||||
customRender: (e) => (
|
customRender: (e) => (
|
||||||
<div
|
<div
|
||||||
@ -105,7 +112,8 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
lastName={e.createdBy?.lastName}
|
lastName={e.createdBy?.lastName}
|
||||||
/>
|
/>
|
||||||
<span className="text-truncate">
|
<span className="text-truncate">
|
||||||
{`${e.createdBy?.firstName ?? ""} ${e.createdBy?.lastName ?? ""
|
{`${e.createdBy?.firstName ?? ""} ${
|
||||||
|
e.createdBy?.lastName ?? ""
|
||||||
}`.trim() || "N/A"}
|
}`.trim() || "N/A"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -135,7 +143,8 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
align: "text-center",
|
align: "text-center",
|
||||||
getValue: (e) => (
|
getValue: (e) => (
|
||||||
<span
|
<span
|
||||||
className={`badge bg-label-${getColorNameFromHex(e?.expenseStatus?.color) || "secondary"
|
className={`badge bg-label-${
|
||||||
|
getColorNameFromHex(e?.expenseStatus?.color) || "secondary"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{e?.expenseStatus?.name || "Unknown"}
|
{e?.expenseStatus?.name || "Unknown"}
|
||||||
@ -147,7 +156,8 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const debouncedSearch = useDebounce(search, 500);
|
const debouncedSearch = useDebounce(search, 500);
|
||||||
|
|
||||||
const { data, isLoading, isError, error, isFetching } = usePaymentRequestList(
|
const { data, isLoading, isError, error, isRefetching, refetch } =
|
||||||
|
usePaymentRequestList(
|
||||||
ITEMS_PER_PAGE,
|
ITEMS_PER_PAGE,
|
||||||
currentPage,
|
currentPage,
|
||||||
filters,
|
filters,
|
||||||
@ -159,12 +169,7 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
const totalPages = data?.data?.totalPages || 1;
|
const totalPages = data?.data?.totalPages || 1;
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
return (
|
return <Error error={error} isFeteching={isRefetching} refetch={refetch} />;
|
||||||
<div className="text-center text-danger py-5">
|
|
||||||
<p>Failed to load payment requests.</p>
|
|
||||||
<small>{error?.message || "Something went wrong."}</small>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const header = [
|
const header = [
|
||||||
"Request ID",
|
"Request ID",
|
||||||
@ -188,13 +193,14 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
const canEditExpense = (paymentRequest) => {
|
const canEditExpense = (paymentRequest) => {
|
||||||
return (
|
return (
|
||||||
(paymentRequest?.expenseStatus?.id === EXPENSE_DRAFT ||
|
(paymentRequest?.expenseStatus?.id === EXPENSE_DRAFT ||
|
||||||
EXPENSE_REJECTEDBY.includes(paymentRequest?.expenseStatus?.id)) &&
|
EXPENSE_REJECTEDBY.includes(paymentRequest?.expenseStatus.id)) &&
|
||||||
paymentRequest?.createdBy?.id === SelfId
|
paymentRequest?.createdBy?.id === SelfId
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const canDetetExpense = (request) => {
|
const canDetetExpense = (request) => {
|
||||||
return (
|
return (
|
||||||
request?.expenseStatus?.id === EXPENSE_DRAFT && request?.createdBy?.id === SelfId
|
request?.expenseStatus?.id === EXPENSE_DRAFT &&
|
||||||
|
request?.createdBy?.id === SelfId
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -282,8 +288,7 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
></i>
|
></i>
|
||||||
{canDetetExpense(paymentRequest) &&
|
{canEditExpense(paymentRequest) && (
|
||||||
canEditExpense(paymentRequest) && (
|
|
||||||
<div className="dropdown z-2">
|
<div className="dropdown z-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -311,10 +316,13 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
>
|
>
|
||||||
<a className="dropdown-item px-2 cursor-pointer py-1">
|
<a className="dropdown-item px-2 cursor-pointer py-1">
|
||||||
<i className="bx bx-edit text-primary bx-xs me-2"></i>
|
<i className="bx bx-edit text-primary bx-xs me-2"></i>
|
||||||
<span className="align-left ">Modify</span>
|
<span className="align-left ">
|
||||||
|
Modify
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
{canDetetExpense(paymentRequest) && (
|
||||||
<li
|
<li
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsDeleteModalOpen(true);
|
setIsDeleteModalOpen(true);
|
||||||
@ -323,11 +331,15 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
>
|
>
|
||||||
<a className="dropdown-item px-2 cursor-pointer py-1">
|
<a className="dropdown-item px-2 cursor-pointer py-1">
|
||||||
<i className="bx bx-trash text-danger bx-xs me-2"></i>
|
<i className="bx bx-trash text-danger bx-xs me-2"></i>
|
||||||
<span className="align-left">Delete</span>
|
<span className="align-left">
|
||||||
|
Delete
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>)}
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -355,7 +367,8 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
|
|||||||
{[...Array(totalPages)].map((_, index) => (
|
{[...Array(totalPages)].map((_, index) => (
|
||||||
<li
|
<li
|
||||||
key={index}
|
key={index}
|
||||||
className={`page-item ${currentPage === index + 1 ? "active" : ""
|
className={`page-item ${
|
||||||
|
currentPage === index + 1 ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -118,21 +118,22 @@ const ViewPaymentRequest = ({ requestId }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="row mb-1">
|
<div className="row mb-1">
|
||||||
<div className="col-12 col-sm-6 col-md-8">
|
<div className="col-12 col-sm-6 col-md-8">
|
||||||
<div className="col-12 text-start fw-semibold my-2">
|
<div className="row">
|
||||||
|
<div className="col-12 text-start fw-semibold mb-2">
|
||||||
{data?.paymentRequestUID}
|
{data?.paymentRequestUID}
|
||||||
</div>
|
</div>
|
||||||
{/* Row 1 */}
|
|
||||||
<div className="col-md-6 mb-3">
|
<div className="col-md-6 mb-3">
|
||||||
<div className="d-flex">
|
<div className="d-block d-md-flex align-items-center">
|
||||||
<label
|
<label
|
||||||
className="form-label me-2 mb-0 fw-semibold text-start"
|
className="form-label me-2 mb-0 fw-semibold text-start"
|
||||||
style={{ minWidth: "130px" }}
|
style={{ minWidth: "130px" }}
|
||||||
>
|
>
|
||||||
Project Name:
|
Project Name:
|
||||||
</label>
|
</label>
|
||||||
<div className="text-muted">{data.project.name}</div>
|
<div className="text-muted">{data?.project?.name || "—"}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-md-6 mb-3">
|
<div className="col-md-6 mb-3">
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
<label
|
<label
|
||||||
@ -221,7 +222,8 @@ const ViewPaymentRequest = ({ requestId }) => {
|
|||||||
</label>
|
</label>
|
||||||
<span
|
<span
|
||||||
className={`badge bg-label-${
|
className={`badge bg-label-${
|
||||||
getColorNameFromHex(data?.expenseStatus?.color) || "secondary"
|
getColorNameFromHex(data?.expenseStatus?.color) ||
|
||||||
|
"secondary"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{data?.expenseStatus?.name}
|
{data?.expenseStatus?.name}
|
||||||
@ -447,7 +449,8 @@ const ViewPaymentRequest = ({ requestId }) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="col-12 mb-3 text-start">
|
<div className="col-12 mb-3 text-start">
|
||||||
{((nextStatusWithPermission.length > 0 && !isRejectedRequest) ||
|
{((nextStatusWithPermission.length > 0 &&
|
||||||
|
!isRejectedRequest) ||
|
||||||
(isRejectedRequest && isCreatedBy)) && (
|
(isRejectedRequest && isCreatedBy)) && (
|
||||||
<>
|
<>
|
||||||
<Label className="form-label me-2 mb-0" required>
|
<Label className="form-label me-2 mb-0" required>
|
||||||
@ -492,6 +495,7 @@ const ViewPaymentRequest = ({ requestId }) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="col-12 col-sm-6 col-md-4">
|
<div className="col-12 col-sm-6 col-md-4">
|
||||||
<ExpenseStatusLogs data={data} />
|
<ExpenseStatusLogs data={data} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,20 +1,80 @@
|
|||||||
import React from 'react'
|
import React from "react";
|
||||||
|
|
||||||
const Error = ({error,close}) => {
|
const Error = ({ error = {}, isFeteching=false, refetch, close }) => {
|
||||||
console.log(error)
|
const statusCode = error.statusCode || error?.response?.status || "Error";
|
||||||
return (
|
|
||||||
<div className="container text-center py-5">
|
const message =
|
||||||
<h1 className="display-4 fw-bold text-danger">{error.statusCode || error?.response?.status
|
error.message ||
|
||||||
}</h1>
|
error?.response?.data?.message ||
|
||||||
<h2 className="mb-3">Internal Server Error</h2>
|
"Something went wrong. Please try again later.";
|
||||||
<p className="lead">
|
|
||||||
{error.message}
|
const getErrorInfo = (code) => {
|
||||||
</p>
|
if (code >= 500) {
|
||||||
<a href="/" className="btn btn-primary btn-sm mt-3" onClick={()=>close()}>
|
return {
|
||||||
Go to Home
|
icon: "bx bx-server text-danger",
|
||||||
</a>
|
title: "Internal Server Error",
|
||||||
</div>
|
subtitle: "Our servers are currently experiencing issues.",
|
||||||
)
|
};
|
||||||
}
|
}
|
||||||
|
if (code === 404) {
|
||||||
|
return {
|
||||||
|
icon: "bx bx-error text-warning",
|
||||||
|
title: "Page Not Found",
|
||||||
|
subtitle: "The requested resource could not be found.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (code === 403) {
|
||||||
|
return {
|
||||||
|
icon: "bx bx-block text-danger",
|
||||||
|
title: "Access Denied",
|
||||||
|
subtitle: "You do not have permission to access this resource.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (code === 401) {
|
||||||
|
return {
|
||||||
|
icon: "bx bx-lock text-danger",
|
||||||
|
title: "Unauthorized",
|
||||||
|
subtitle: "Please log in to continue.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (code === 0 || code === "ERR_NETWORK") {
|
||||||
|
return {
|
||||||
|
icon: "bx bx-wifi-off text-secondary",
|
||||||
|
title: "Network Error",
|
||||||
|
subtitle: "Please check your internet connection.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
icon: "bx bx-error-circle text-danger",
|
||||||
|
title: "Unexpected Error",
|
||||||
|
subtitle: "An unknown error occurred.",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default Error
|
const { icon, title, subtitle } = getErrorInfo(statusCode);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container-fluid d-flex flex-column align-items-center justify-content-center text-center py-5">
|
||||||
|
|
||||||
|
<h4 className="mb-2">{title}</h4>
|
||||||
|
<p className="text-muted mb-2">{subtitle}</p>
|
||||||
|
|
||||||
|
{message && (
|
||||||
|
<p className="lead small text-body-secondary mb-4">{message}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div class="d-grid gap-2 col-4 mx-auto">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-outline-secondary btn-lg"
|
||||||
|
type="button"
|
||||||
|
onClick={refetch}
|
||||||
|
disabled={isFeteching}
|
||||||
|
>
|
||||||
|
<i className={`bx bx-refresh ${isFeteching ? "bx-spin":""} me-1`}></i> Retry
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Error;
|
||||||
|
|||||||
@ -1,15 +1,25 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import Avatar from "./Avatar";
|
||||||
|
|
||||||
const Timeline = ({ items = [], transparent = true }) => {
|
const Timeline = ({ items = [], transparent = true }) => {
|
||||||
return (
|
return (
|
||||||
<ul className={`timeline ${transparent ? "timeline-transparent text-start" : ""}`}>
|
<ul
|
||||||
|
className={`timeline ${
|
||||||
|
transparent ? "timeline-transparent text-start" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<li
|
<li
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={`timeline-item ${transparent ? "timeline-item-transparent" : ""}`}
|
className={`timeline-item ${
|
||||||
|
transparent ? "timeline-item-transparent" : ""
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<span className={`timeline-point timeline-point-${item.color || "primary"}`}></span>
|
<span
|
||||||
|
className={`timeline-point timeline-point-${
|
||||||
|
item.color || "primary"
|
||||||
|
}`}
|
||||||
|
></span>
|
||||||
|
|
||||||
<div className="timeline-event">
|
<div className="timeline-event">
|
||||||
<div className="timeline-header mb-3 d-flex justify-content-between">
|
<div className="timeline-header mb-3 d-flex justify-content-between">
|
||||||
@ -26,7 +36,14 @@ const Timeline = ({ items = [], transparent = true }) => {
|
|||||||
key={i}
|
key={i}
|
||||||
className="badge bg-lighter rounded d-flex align-items-center gap-2 p-2"
|
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" />}
|
{att.icon && (
|
||||||
|
<img
|
||||||
|
src={att.icon}
|
||||||
|
alt="file"
|
||||||
|
width="15"
|
||||||
|
className="me-2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<span className="h6 mb-0">{att.name}</span>
|
<span className="h6 mb-0">{att.name}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -47,17 +64,18 @@ const Timeline = ({ items = [], transparent = true }) => {
|
|||||||
height="32"
|
height="32"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className="avatar-initial rounded-circle pull-up bg-light">
|
<Avatar
|
||||||
{user.name}
|
firstName={user.firstName}
|
||||||
</span>
|
lastName={user.lastName}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{item.users[0]?.role && (
|
{item.users?.length === 1 && (
|
||||||
<div className="ms-2">
|
<div className="m-0">
|
||||||
<p className="mb-0 small fw-medium">{item.users[0].name}</p>
|
<p className="mb-0 small fw-medium">{`${item.users[0].firstName} ${item.users[0].lastName}`}</p>
|
||||||
<small>{item.users[0].role}</small>
|
<small>{item.users[0].role}</small>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -67,8 +67,8 @@ export const PROCESS_EXPENSE = "ea5a1529-4ee8-4828-80ea-0e23c9d4dd11";
|
|||||||
export const EXPENSE_MANAGE = "ea5a1529-4ee8-4828-80ea-0e23c9d4dd11";
|
export const EXPENSE_MANAGE = "ea5a1529-4ee8-4828-80ea-0e23c9d4dd11";
|
||||||
|
|
||||||
export const EXPENSE_REJECTEDBY = [
|
export const EXPENSE_REJECTEDBY = [
|
||||||
"d1ee5eec-24b6-4364-8673-a8f859c60729",
|
|
||||||
"965eda62-7907-4963-b4a1-657fb0b2724b",
|
"965eda62-7907-4963-b4a1-657fb0b2724b",
|
||||||
|
"d1ee5eec-24b6-4364-8673-a8f859c60729",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user