diff --git a/index.html b/index.html index 1b2b7575..121acff3 100644 --- a/index.html +++ b/index.html @@ -28,6 +28,7 @@ + diff --git a/public/assets/css/hover-utility.css b/public/assets/css/hover-utility.css new file mode 100644 index 00000000..ef080092 --- /dev/null +++ b/public/assets/css/hover-utility.css @@ -0,0 +1,86 @@ +/* Hover background color */ +.hover-bg-light:hover { + background-color: #f8f9fa !important; +} + +.hover-bg-primary:hover { + background-color: var(--bs-primary) !important; + color: #fff !important; +} + +.hover-bg-danger:hover { + background-color: #dc3545 !important; + color: #fff !important; +} + +.hover-bg-success:hover { + background-color: var(--bg-success) !important; + color: #fff !important; +} + +.hover-bg-warning:hover { + background-color: #ffc107 !important; + color: #212529 !important; +} + +/* Hover text color */ +.hover-text-primary:hover { + color: #0d6efd !important; +} + +.hover-text-danger:hover { + color: #dc3545 !important; +} + +.hover-text-success:hover { + color: #198754 !important; +} + +.hover-text-muted:hover { + color: #6c757d !important; +} + +/* Hover shadow */ +.hover-shadow-sm:hover { + box-shadow: 0 .125rem .25rem rgba(0, 0, 0, 0.075); +} + +.hover-shadow:hover { + box-shadow: 0 .5rem 1rem rgba(0, 0, 0, 0.15); +} + +.hover-shadow-lg:hover { + box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175); +} + +/* Hover scale */ +.hover-scale:hover { + transform: scale(1.05); +} + +.hover-scale-sm:hover { + transform: scale(1.03); +} + +.hover-scale-lg:hover { + transform: scale(1.1); +} + +/* Add smooth transition to hover effects */ +.hover-transition { + transition: all 0.2s ease-in-out; +} + +/* Hover border color */ +.hover-border-primary:hover { + border-color: #0d6efd !important; +} + +.hover-border-danger:hover { + border-color: #dc3545 !important; +} + +.thick-divider { + height: 3px; + font-size: 10px; +} \ No newline at end of file diff --git a/src/components/Expenses/ExpenseList.jsx b/src/components/Expenses/ExpenseList.jsx index 4a55cfc9..37e6b9a9 100644 --- a/src/components/Expenses/ExpenseList.jsx +++ b/src/components/Expenses/ExpenseList.jsx @@ -120,7 +120,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => { firstName={e.createdBy?.firstName} lastName={e.createdBy?.lastName} /> - + {`${e.createdBy?.firstName ?? ""} ${ e.createdBy?.lastName ?? "" }`.trim() || "N/A"} diff --git a/src/components/Expenses/ExpenseSkeleton.jsx b/src/components/Expenses/ExpenseSkeleton.jsx index 8f73db5c..dbe1a5d7 100644 --- a/src/components/Expenses/ExpenseSkeleton.jsx +++ b/src/components/Expenses/ExpenseSkeleton.jsx @@ -1,6 +1,5 @@ import React from "react"; - const SkeletonLine = ({ height = 20, width = "100%", className = "" }) => (
( >
); - const ExpenseSkeleton = () => { return (
-
- -
+
+ +
{[...Array(5)].map((_, idx) => (
@@ -52,12 +50,9 @@ const ExpenseSkeleton = () => { export default ExpenseSkeleton; - - - export const ExpenseDetailsSkeleton = () => { return ( -
+
@@ -65,7 +60,7 @@ export const ExpenseDetailsSkeleton = () => { {[...Array(3)].map((_, i) => (
- +
))} @@ -77,30 +72,38 @@ export const ExpenseDetailsSkeleton = () => {
))} - +
+ -
- - {[...Array(2)].map((_, i) => ( -
+
+ {[...Array(2)].map((_, i) => (
-
- - + key={i} + className="border rounded p-2 d-flex flex-column align-items-center" + style={{ width: "80px" }} + > + {/* Icon placeholder */} +
+ {/* Filename placeholder */} +
-
- ))} + ))} +

@@ -123,7 +126,12 @@ export const ExpenseDetailsSkeleton = () => {
); }; -const SkeletonCell = ({ width = "100%", height = 20, className = "", style = {} }) => ( +const SkeletonCell = ({ + width = "100%", + height = 20, + className = "", + style = {}, +}) => (
{ return ( -
+
- - - - - - - - - - + className="card-body table border-top dataTable no-footer dtr-column text-nowrap" + aria-describedby="DataTables_Table_0_info" + id="horizontal-example" + > + + + + + + + + + + + - - {[...Array(groups)].map((_, groupIdx) => ( - - {/* Fake Date Group Header Row */} - - - - - {/* Rows under this group */} - {[...Array(rowsPerGroup)].map((__, rowIdx) => ( - - {/* Expense Type */} - - - {/* Payment Mode */} - - - {/* Paid By (Avatar + name) */} - - - {/* Amount */} - - - {/* Status */} - - - {/* Action */} - + {[...Array(groups)].map((_, groupIdx) => ( + + {/* Fake Date Group Header Row */} + + - ))} - - ))} - -
-
Expense Type
-
-
Payment Mode
-
Paid ByAmountStatusAction
+
Expense Type
+
+
Payment Mode
+
Submitted BySubmittedAmountStatusAction
- -
- - - - -
- - -
-
- - - - -
- {[...Array(3)].map((__, i) => ( - - ))} -
+
+
-
+ + {/* Rows under this group */} + {[...Array(rowsPerGroup)].map((__, rowIdx) => ( + + {/* Expense Type */} + + + + + {/* Payment Mode */} + + + + + {/* Submitted By (Avatar + name) */} + +
+ + +
+ + {/* Submitted */} + + + + + {/* Amount */} + + + + + {/* Status */} + + + + + {/* Action */} + +
+ {[...Array(3)].map((__, i) => ( + + ))} +
+ + + ))} + + ))} + + +
); }; - export const ExpenseFilterSkeleton = () => { return (
diff --git a/src/components/Expenses/ExpenseStatusLogs.jsx b/src/components/Expenses/ExpenseStatusLogs.jsx index 879cbf56..93bfac45 100644 --- a/src/components/Expenses/ExpenseStatusLogs.jsx +++ b/src/components/Expenses/ExpenseStatusLogs.jsx @@ -5,7 +5,10 @@ import { formatUTCToLocalTime } from "../../utils/dateUtils"; const ExpenseStatusLogs = ({ data }) => { const [visibleCount, setVisibleCount] = useState(4); - const logsToShow = data?.expenseLogs?.slice(0, visibleCount) || []; + const logsToShow = [...(data?.expenseLogs || [])] + .sort((a, b) => new Date(b.updateAt) - new Date(a.updateAt)) + .slice(0, visibleCount); + const handleShowMore = () => { setVisibleCount((prev) => prev + 4); diff --git a/src/components/Expenses/ViewExpense.jsx b/src/components/Expenses/ViewExpense.jsx index 52878533..e98eaae3 100644 --- a/src/components/Expenses/ViewExpense.jsx +++ b/src/components/Expenses/ViewExpense.jsx @@ -9,7 +9,7 @@ import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { defaultActionValues, ExpenseActionScheam } from "./ExpenseSchema"; import { useExpenseContext } from "../../pages/Expense/ExpensePage"; -import { getColorNameFromHex } from "../../utils/appUtils"; +import { getColorNameFromHex, getIconByFileType } from "../../utils/appUtils"; import { ExpenseDetailsSkeleton } from "./ExpenseSkeleton"; import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { @@ -271,37 +271,24 @@ const ViewExpense = ({ ExpenseId }) => {
- )} + )}
- -
- - {data?.documents?.map((doc) => { - const getIconByType = (type) => { - if (!type) return "bx bx-file"; +
+ - if (type.includes("pdf")) return "bxs-file-pdf"; - if (type.includes("word")) return "bxs-file-doc"; - if (type.includes("excel") || type.includes("spreadsheet")) - return "bxs-file-xls"; - if (type.includes("image")) return "bxs-file-image"; - if (type.includes("zip") || type.includes("rar")) - return "bxs-file-archive"; +
+ {data?.documents?.map((doc) => { + const isImage = doc.contentType?.includes("image"); - return "bx bx-file"; - }; - - const isImage = doc.contentType?.includes("image"); - - return ( -
+ return (
{ if (isImage) { setDocumentView({ @@ -312,31 +299,22 @@ const ViewExpense = ({ ExpenseId }) => { }} > + + {doc.fileName} +
- -
- {doc.fileName} -
- -
-
-
- ); - })} + ); + })} +
- - {data.expensesReimburse && ( + {data.expensesReimburse && (
)} -
+
{Array.isArray(data?.nextStatus) && data.nextStatus.length > 0 && ( <> @@ -457,11 +435,8 @@ const ViewExpense = ({ ExpenseId }) => { )} - - + - - ); }; diff --git a/src/data/masters.js b/src/data/masters.js index e94de7a3..67044587 100644 --- a/src/data/masters.js +++ b/src/data/masters.js @@ -1,5 +1,14 @@ // it important ------ -export const mastersList = [ {id: 1, name: "Application Role"}, {id: 2, name: "Job Role"}, {id: 3, name: "Activity"},{id: 4, name:"Work Category"},{id:5,name:"Contact Category"},{id:6,name:"Contact Tag"}] +export const mastersList = [ + { id: 1, name: "Application Role" }, + { id: 2, name: "Job Role" }, + { id: 3, name: "Activity" }, + { id: 4, name: "Work Category" }, + { id: 5, name: "Contact Category" }, + { id: 6, name: "Contact Tag" }, + { id: 7, name: "Expense Type" }, + { id: 8, name: "Payment Mode" }, +]; // ------------------- export const dailyTask = [ diff --git a/src/pages/Expense/ExpensePage.jsx b/src/pages/Expense/ExpensePage.jsx index f57e83df..4afdb194 100644 --- a/src/pages/Expense/ExpensePage.jsx +++ b/src/pages/Expense/ExpensePage.jsx @@ -180,7 +180,7 @@ const ExpensePage = () => { {ViewDocument.IsOpen && ( setDocumentView({ IsOpen: false, Image: null })} > diff --git a/src/pages/master/MasterTable.jsx b/src/pages/master/MasterTable.jsx index 0126b339..b5f65857 100644 --- a/src/pages/master/MasterTable.jsx +++ b/src/pages/master/MasterTable.jsx @@ -9,13 +9,18 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => { const selectedMaster = useSelector( (store) => store.localVariables.selectedMaster ); - const hiddenColumns = [ + const hiddenColumns = [ "id", "featurePermission", "tenant", "tenantId", "checkLists", "isSystem", + "isActive", + "noOfPersonsRequired", + "color", + "displayName", + "permissionIds" ]; const safeData = Array.isArray(data) ? data : []; diff --git a/src/utils/appUtils.js b/src/utils/appUtils.js index 94d5b09a..17686ced 100644 --- a/src/utils/appUtils.js +++ b/src/utils/appUtils.js @@ -46,4 +46,18 @@ export const useDebounce = (value, delay = 500) => { }, [value, delay]); return debouncedValue; -}; \ No newline at end of file +}; + +export const getIconByFileType = (type = "") => { + const lower = type.toLowerCase(); + + if (lower === "application/pdf") return "bxs-file-pdf"; + if (lower.includes("word")) return "bxs-file-doc"; + if (lower.includes("excel") || lower.includes("spreadsheet")) + return "bxs-file-xls"; + if (lower === "image/png") return "bxs-file-png"; + if (lower === "image/jpeg" || lower === "image/jpg") return "bxs-file-jpg"; + if (lower.includes("zip") || lower.includes("rar")) return "bxs-file-archive"; + + return "bx bx-file"; +};