From 1c89633e50e14eb8a6e97028dca13f5087bbda27 Mon Sep 17 00:00:00 2001 From: Anand Suthar Date: Sun, 4 Aug 2024 22:43:11 +0530 Subject: [PATCH 1/3] refactor : Rename billing to transactions --- app/(pages)/admin/components/Sidebar/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(pages)/admin/components/Sidebar/index.tsx b/app/(pages)/admin/components/Sidebar/index.tsx index 86baaa3..daf5044 100644 --- a/app/(pages)/admin/components/Sidebar/index.tsx +++ b/app/(pages)/admin/components/Sidebar/index.tsx @@ -19,7 +19,7 @@ import { TbCurrencyRupee } from "react-icons/tb"; const SidebarConfig = [ { title: "Dashboard", uri: "", icon: }, { title: "Add Admin", uri: "add-admin", icon: }, - { title: "Billing", uri: "billing", icon: }, + { title: "Transactions", uri: "transactions", icon: }, { title: "Hospitals", uri: "hospitals", icon: }, { title: "Users", uri: "users", icon: }, { title: "Reports", uri: "reports", icon: }, From 504743c1b4d12ca205319769495cef304171e457 Mon Sep 17 00:00:00 2001 From: Anand Suthar Date: Sun, 4 Aug 2024 22:44:11 +0530 Subject: [PATCH 2/3] feat: Create transactions page with search, filter, and pagination - Added Transactions component with table display - Implemented search functionality by patient name - Added status filter dropdown - Implemented pagination with rows per page selection --- app/(pages)/admin/billing/page.tsx | 5 - app/(pages)/admin/transactions/page.tsx | 357 ++++++++++++++++++++++++ 2 files changed, 357 insertions(+), 5 deletions(-) delete mode 100644 app/(pages)/admin/billing/page.tsx create mode 100644 app/(pages)/admin/transactions/page.tsx diff --git a/app/(pages)/admin/billing/page.tsx b/app/(pages)/admin/billing/page.tsx deleted file mode 100644 index 876e6b7..0000000 --- a/app/(pages)/admin/billing/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; - -export default function Billing() { - return
Overview of financial transactions
; -} diff --git a/app/(pages)/admin/transactions/page.tsx b/app/(pages)/admin/transactions/page.tsx new file mode 100644 index 0000000..ebe1ca4 --- /dev/null +++ b/app/(pages)/admin/transactions/page.tsx @@ -0,0 +1,357 @@ +"use client"; + +import React from "react"; +import { getFormattedDate } from "@utils/getDate"; +import { + Table, + TableHeader, + TableColumn, + TableBody, + TableRow, + TableCell, + Input, + Button, + Dropdown, + DropdownTrigger, + DropdownMenu, + DropdownItem, + Chip, + Pagination, + Spacer, + ScrollShadow, + Card, + Image, +} from "@nextui-org/react"; +import { BiDownArrow, BiPlus, BiSearch } from "react-icons/bi"; + +const transactions = [ + { + transaction_id: "6ce5a08c17660df643efbf56", + patient_name: "John Doe", + hospital_name: "City Hospital", + hospital_profile: + "https://marketplace.canva.com/EAE8eSD-Zyo/1/0/1600w/canva-blue%2C-white-and-green-medical-care-logo-oz1ox2GedbU.jpg", + disease: "Common Cold", + description: "Prescription Medication", + amount: 300, + status: "Success", + date: "2024-04-05T14:23:12.456Z", + }, + { + transaction_id: "6ce5a08c17660df643efbf56", + patient_name: "John Doe", + hospital_name: "City Hospital", + hospital_profile: + "https://thumbs.dreamstime.com/b/red-cross-vector-icon-hospital-sign-medical-health-first-aid-symbol-isolated-vhite-modern-gradient-design-141217893.jpg", + disease: "Common Cold", + description: "Prescription Medication", + amount: 500, + status: "Failed", + date: "2024-04-05T14:23:12.456Z", + }, + { + transaction_id: "6ce5a08c17660df643efbf56", + patient_name: "John Doe", + hospital_name: "City Hospital", + hospital_profile: + "https://marketplace.canva.com/EAE8eSD-Zyo/1/0/1600w/canva-blue%2C-white-and-green-medical-care-logo-oz1ox2GedbU.jpg", + disease: "Common Cold", + description: "Prescription Medication", + amount: 300, + status: "Success", + date: "2024-04-05T14:23:12.456Z", + }, + { + transaction_id: "6ce5a08c17660df643efbf56", + patient_name: "John Doe", + hospital_name: "City Hospital", + hospital_profile: + "https://thumbs.dreamstime.com/b/red-cross-vector-icon-hospital-sign-medical-health-first-aid-symbol-isolated-vhite-modern-gradient-design-141217893.jpg", + disease: "Common Cold", + description: "Prescription Medication", + amount: 500, + status: "Failed", + date: "2024-04-05T14:23:12.456Z", + }, + { + transaction_id: "6ce5a08c17660df643efbf56", + patient_name: "John Doe", + hospital_name: "City Hospital", + hospital_profile: + "https://marketplace.canva.com/EAE8eSD-Zyo/1/0/1600w/canva-blue%2C-white-and-green-medical-care-logo-oz1ox2GedbU.jpg", + disease: "Common Cold", + description: "Prescription Medication", + amount: 300, + status: "Success", + date: "2024-04-05T14:23:12.456Z", + }, + { + transaction_id: "6ce5a08c17660df643efbf56", + patient_name: "John Doe", + hospital_name: "City Hospital", + hospital_profile: + "https://thumbs.dreamstime.com/b/red-cross-vector-icon-hospital-sign-medical-health-first-aid-symbol-isolated-vhite-modern-gradient-design-141217893.jpg", + disease: "Common Cold", + description: "Prescription Medication", + amount: 500, + status: "Failed", + date: "2024-04-05T14:23:12.456Z", + }, +]; + +const statusColorMap: any = { + Failed: "error", + Success: "success", +}; + +export default function Transactions() { + const [filterValue, setFilterValue] = React.useState(""); + const [statusFilter, setStatusFilter] = React.useState("all"); + const [rowsPerPage, setRowsPerPage] = React.useState(5); + const [sortDescriptor, setSortDescriptor] = React.useState({ + column: "date", + direction: "ascending", + }); + const [page, setPage] = React.useState(1); + + const hasSearchFilter = Boolean(filterValue); + + const filteredItems = React.useMemo(() => { + let filteredTransactions = [...transactions]; + + if (hasSearchFilter) { + filteredTransactions = filteredTransactions.filter((transaction) => + transaction.patient_name + .toLowerCase() + .includes(filterValue.toLowerCase()) + ); + } + if (statusFilter !== "all") { + filteredTransactions = filteredTransactions.filter( + (transaction) => transaction.status === statusFilter + ); + } + + return filteredTransactions; + }, [transactions, filterValue, statusFilter]); + + const pages = Math.ceil(filteredItems.length / rowsPerPage); + + const items = React.useMemo(() => { + const start = (page - 1) * rowsPerPage; + const end = start + rowsPerPage; + + return filteredItems.slice(start, end); + }, [page, filteredItems, rowsPerPage]); + + const sortedItems = React.useMemo(() => { + return [...items].sort((a: any, b: any) => { + const first = new Date(a[sortDescriptor.column]); + const second = new Date(b[sortDescriptor.column]); + const cmp = first < second ? -1 : first > second ? 1 : 0; + + return sortDescriptor.direction === "descending" ? -cmp : cmp; + }); + }, [sortDescriptor, items]); + + const renderCell = React.useCallback((transaction: any, columnKey: any) => { + switch (columnKey) { + case "hospital_name": + return ( + {transaction.hospital_name} + ); + case "status": + return ( + + {transaction.status} + + ); + default: + return transaction[columnKey]; + } + }, []); + + const onNextPage = React.useCallback(() => { + if (page < pages) { + setPage(page + 1); + } + }, [page, pages]); + + const onPreviousPage = React.useCallback(() => { + if (page > 1) { + setPage(page - 1); + } + }, [page]); + + const onRowsPerPageChange = React.useCallback((e: any) => { + setRowsPerPage(Number(e.target.value)); + setPage(1); + }, []); + + const onSearchChange = React.useCallback((value: any) => { + if (value) { + setFilterValue(value); + setPage(1); + } else { + setFilterValue(""); + } + }, []); + + const topContent = React.useMemo(() => { + return ( +
+
+ } + value={filterValue} + onClear={() => setFilterValue("")} + onValueChange={onSearchChange} + /> +
+ + + + + + All + Success + Failed + + + +
+
+
+ + Total {transactions.length} transactions + + +
+
+ ); + }, [ + filterValue, + statusFilter, + onRowsPerPageChange, + transactions.length, + onSearchChange, + ]); + + const bottomContent = React.useMemo(() => { + return ( +
+ +
+ + +
+
+ ); + }, [page, pages]); + + return ( +
+ +

+ Transaction Details +

+
+ + + Sr. No. + Transaction ID + Hospital + Disease + Description + Amount + Status + Date + + + {sortedItems.map((transaction, index) => ( + + {index + 1} + {transaction.transaction_id} + + {renderCell(transaction, "hospital_name")} + + {transaction.disease} + {transaction.description} + ₹{transaction.amount} + {renderCell(transaction, "status")} + + {getFormattedDate(new Date(transaction.date))} + + + ))} + +
+
+
+
+ ); +} From 18671f8f6bcdaf36189f3eb32b5c0114a154c387 Mon Sep 17 00:00:00 2001 From: Anand Suthar Date: Mon, 5 Aug 2024 23:33:13 +0530 Subject: [PATCH 3/3] feat : Add loading ui --- app/(pages)/admin/page.tsx | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/app/(pages)/admin/page.tsx b/app/(pages)/admin/page.tsx index f525a38..d06f91c 100644 --- a/app/(pages)/admin/page.tsx +++ b/app/(pages)/admin/page.tsx @@ -57,12 +57,10 @@ export default function Admin() { const [recentUsers, setRecentUsers] = useState([]); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); - const [isLoading, setIsLoading] = useState(false); const observer = useRef(null); const lastUserElementRef = useCallback( (node: HTMLDivElement | null) => { - if (isLoading) return; if (observer.current) observer.current.disconnect(); observer.current = new IntersectionObserver((entries) => { if (entries[0].isIntersecting && hasMore) { @@ -71,7 +69,7 @@ export default function Admin() { }); if (node) observer.current.observe(node); }, - [isLoading, hasMore] + [hasMore] ); useEffect(() => { @@ -87,8 +85,8 @@ export default function Admin() { }, [page]); const fetchRecentUsers = async () => { - if (isLoading || !hasMore) return; - setIsLoading(true); + if (!hasMore) return; + try { const response = await fetch( `/api/admin/dashboard/recent-users?page=${page}&limit=10` @@ -98,8 +96,6 @@ export default function Admin() { setHasMore(data.page < data.totalPages); } catch (error) { console.error("Error fetching recent users:", error); - } finally { - setIsLoading(false); } }; @@ -189,11 +185,6 @@ export default function Admin() {

{user.timeSince}

))} - {isLoading && ( -
- -
- )}