From c34285c66d8468f15cb1f915be3bb9f6f59ff0e2 Mon Sep 17 00:00:00 2001 From: i701 Date: Wed, 23 Jul 2025 23:12:10 +0500 Subject: [PATCH] =?UTF-8?q?feat(admin):=20add=20admin=20payment=20tables?= =?UTF-8?q?=20+=20filters=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- actions/payment.ts | 4 +- app/(dashboard)/user-payments/page.tsx | 57 ++++ components/admin/user-payments-table.tsx | 386 ++++++++++------------- 4 files changed, 225 insertions(+), 227 deletions(-) diff --git a/README.md b/README.md index 071e69e..5d7f9a0 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ This is a web portal for SAR Link customers. ## Admin Controls ### Users - [x] Show users table +- [ ] handle verify api no response case - [ ] Add all relavant filters for users table - [x] Verify or reject users with a custom message - [ ] Add functionality to send custom sms to users in user:id page @@ -40,6 +41,6 @@ This is a web portal for SAR Link customers. - [x] Add all relevant filters for user devices table ### User Payments -- [ ] Show user payments table -- [ ] Add relevant filters for user payments table +- [x] Show user payments table +- [x] Add relevant filters for user payments table - [ ] Add computation of total value for filtered results \ No newline at end of file diff --git a/actions/payment.ts b/actions/payment.ts index f21e3f2..1a35f27 100644 --- a/actions/payment.ts +++ b/actions/payment.ts @@ -93,7 +93,7 @@ type GetPaymentProps = { [key: string]: string | number | undefined; // Allow additional properties for flexibility }; -export async function getPayments(params: GetPaymentProps) { +export async function getPayments(params: GetPaymentProps, allPayments = false) { // Build query string from all defined params const query = Object.entries(params) .filter(([_, value]) => value !== undefined && value !== "") @@ -101,7 +101,7 @@ export async function getPayments(params: GetPaymentProps) { .join("&"); const session = await getServerSession(authOptions); const response = await fetch( - `${process.env.SARLINK_API_BASE_URL}/api/billing/payment/?${query}`, + `${process.env.SARLINK_API_BASE_URL}/api/billing/payment/?${query}&all_payments=${allPayments}`, { method: "GET", headers: { diff --git a/app/(dashboard)/user-payments/page.tsx b/app/(dashboard)/user-payments/page.tsx index ccd08b5..788b390 100644 --- a/app/(dashboard)/user-payments/page.tsx +++ b/app/(dashboard)/user-payments/page.tsx @@ -1,5 +1,6 @@ import { Suspense } from "react"; import { UsersPaymentsTable } from "@/components/admin/user-payments-table"; +import DynamicFilter from "@/components/generic-filter"; export default async function UserPayments({ searchParams, @@ -18,6 +19,62 @@ export default async function UserPayments({

User Payments

+ diff --git a/components/admin/user-payments-table.tsx b/components/admin/user-payments-table.tsx index 8c8f7e3..9c5f991 100644 --- a/components/admin/user-payments-table.tsx +++ b/components/admin/user-payments-table.tsx @@ -1,236 +1,176 @@ -// import Pagination from "@/components/pagination"; -// import { Badge } from "@/components/ui/badge"; -// import { Button } from "@/components/ui/button"; -// import { -// Table, -// TableBody, -// TableCaption, -// TableCell, -// TableFooter, -// TableHead, -// TableHeader, -// TableRow, -// } from "@/components/ui/table"; -// import Link from "next/link"; +import Link from "next/link"; +import { redirect } from "next/navigation"; +import { getServerSession } from "next-auth"; +import { getPayments } from "@/actions/payment"; +import { authOptions } from "@/app/auth"; +import Pagination from "@/components/pagination"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Table, + TableBody, + TableCaption, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { tryCatch } from "@/utils/tryCatch"; +import ClientErrorMessage from "../client-error-message"; export async function UsersPaymentsTable({ searchParams, }: { searchParams: Promise<{ - query: string; - page: number; - sortBy: string; - status: string; + [key: string]: unknown; }>; }) { - const query = (await searchParams)?.query || ""; - console.log(query); - // const page = (await searchParams)?.page; - // const sortBy = (await searchParams)?.sortBy || "asc"; - // const totalPayments = await prisma.payment.count({ - // where: { - // OR: [ - // { - // user: { - // name: { - // contains: query || "", - // mode: "insensitive", - // } - // }, - // }, - // { - // user: { - // phoneNumber: { - // contains: query || "", - // mode: "insensitive", - // } - // }, - // }, - // { - // user: { - // address: { - // contains: query || "", - // mode: "insensitive", - // } - // }, - // }, - // { - // user: { - // id_card: { - // contains: query || "", - // mode: "insensitive", - // } - // }, - // }, - // ], - // }, - // }); + const resolvedParams = await searchParams; - // const totalPages = Math.ceil(totalPayments / 10); - // const limit = 10; - // const offset = (Number(page) - 1) * limit || 0; + const page = Number.parseInt(resolvedParams.page as string) || 1; + const limit = 10; + const offset = (page - 1) * limit; - // const payments = await prisma.payment.findMany({ - // where: { - // OR: [ - // { - // user: { - // name: { - // contains: query || "", - // mode: "insensitive", - // } - // }, - // }, - // { - // user: { - // phoneNumber: { - // contains: query || "", - // mode: "insensitive", - // } - // }, - // }, - // { - // user: { - // address: { - // contains: query || "", - // mode: "insensitive", - // } - // }, - // }, - // { - // user: { - // id_card: { - // contains: query || "", - // mode: "insensitive", - // } - // }, - // }, - // ], - // }, - // include: { - // user: true, - // devices: true, - // }, - // skip: offset, - // take: limit, - // orderBy: { - // id: `${sortBy}` as "asc" | "desc", - // }, - // }); + // Build params object for getDevices + const apiParams: Record = {}; + for (const [key, value] of Object.entries(resolvedParams)) { + if (value !== undefined && value !== "") { + apiParams[key] = typeof value === "number" ? value : String(value); + } + } + apiParams.limit = limit; + apiParams.offset = offset; - // const users = await prisma.user.findMany({ - // where: { - // role: "USER", - // }, - // include: { - // atoll: true, - // island: true, - // }, - // }); - return null; - // return ( - //
- // {payments.length === 0 ? ( - //
- //

No user payments yet.

- //
- // ) : ( - // <> - // - // Table of all users. - // - // - // Devices paid - // User - // Amount - // Duration - // Payment Status - // Payment Method - // Paid At - // Action - // - // - // - // {payments.map((payment) => ( - // - // - //
    - // {payment.devices.map((device) => ( - //
  1. - // {device.name} - //
  2. - // ))} - //
- //
- // - // {payment.user.id_card} - // - // {payment.user?.name} - // {payment.user?.name} - // {payment.id} + const [error, payments] = await tryCatch(getPayments(apiParams, true)); + if (error) { + if (error.message === "UNAUTHORIZED") { + redirect("/auth/signin"); + } else { + return ; + } + } + const { meta, data } = payments; - // - // {payment.paid ? ( - // - // Verified - // - // ) : ( - // - // Unverified - // - // )} - // - // - // {new Date(payment.paidAt ?? "").toLocaleDateString( - // "en-US", - // { - // month: "short", - // day: "2-digit", - // year: "numeric", - // }, - // )} - // + // return
{JSON.stringify(payments, null, 2)}
; + return ( +
+ {data.length === 0 ? ( +
+

No user payments yet.

+
+ ) : ( + <> +
+ Table of all users. + + + Devices paid + User + Amount + Duration + Payment Status + Payment Method + MIB Reference + Paid At + Action + + + + {data.map((payment) => ( + + +
    + {payment.devices.map((device) => ( +
  1. + {device.name} +
  2. + ))} +
+
+ + {/* {payment.user.id_card} */} +
+ {payment.devices[0]?.user?.name} + + {payment.devices[0]?.user?.id_card} + +
{" "} +
+ {payment.amount} + {payment.number_of_months} Months - // {payment.id} - // - // - // - // - // - //
- // ))} - //
- // - // - // - // {query.length > 0 && ( - //

- // Showing {payments.length} locations for "{query} - // " - //

- // )} - //
- // - // {totalPayments} payments - // - //
- //
- //
- // - // - // )} - //
- // ); + + {payment.status === "PENDING" ? ( + + {payment.status} + + ) : payment.status === "PAID" ? ( + + {payment.status} + + ) : ( + + {payment.status} + + )} + + {payment.method} + {payment.mib_reference} + + {new Date(payment.paid_at ?? "").toLocaleDateString( + "en-US", + { + month: "short", + day: "2-digit", + year: "numeric", + minute: "2-digit", + hour: "2-digit", + }, + )} + + + + + + + + + ))} + + + + + {meta?.total === 1 ? ( +

Total {meta?.total} payment.

+ ) : ( +

Total {meta?.total} payments.

+ )}{" "} +
+
+
+ + + + )} + + ); }