From 31a05ae9176981d40bab406be8b95d5d2c8109fc Mon Sep 17 00:00:00 2001 From: i701 Date: Wed, 24 Sep 2025 17:46:04 +0500 Subject: [PATCH] =?UTF-8?q?style:=20add=20skeletons=20to=20paymentId=20and?= =?UTF-8?q?=20deviceId=20pages=20=E2=99=BB=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../devices/[deviceId]/loading.tsx | 33 ++ app/(dashboard)/devices/[deviceId]/page.tsx | 100 ++-- app/(dashboard)/devices/loading.tsx | 37 +- .../payments/[paymentId]/loading.tsx | 63 +++ app/(dashboard)/payments/[paymentId]/page.tsx | 140 +++--- app/(dashboard)/payments/loading.tsx | 37 +- components/devices-to-pay.tsx | 35 +- components/wallet-transactions-table.tsx | 438 +++++++++--------- 8 files changed, 501 insertions(+), 382 deletions(-) create mode 100644 app/(dashboard)/devices/[deviceId]/loading.tsx create mode 100644 app/(dashboard)/payments/[paymentId]/loading.tsx diff --git a/app/(dashboard)/devices/[deviceId]/loading.tsx b/app/(dashboard)/devices/[deviceId]/loading.tsx new file mode 100644 index 0000000..cfb6c00 --- /dev/null +++ b/app/(dashboard)/devices/[deviceId]/loading.tsx @@ -0,0 +1,33 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +export default function DeviceLoading() { + return ( +
+
+
+ + + +
+
+ +
+
+ +
+ + {/* */} +
+ {/* + + */} +
+ ); +} diff --git a/app/(dashboard)/devices/[deviceId]/page.tsx b/app/(dashboard)/devices/[deviceId]/page.tsx index 17b4b48..4058770 100644 --- a/app/(dashboard)/devices/[deviceId]/page.tsx +++ b/app/(dashboard)/devices/[deviceId]/page.tsx @@ -1,68 +1,68 @@ +import { redirect } from "next/navigation"; import ClientErrorMessage from "@/components/client-error-message"; import Search from "@/components/search"; import { Badge } from "@/components/ui/badge"; import { getDevice } from "@/queries/devices"; import { tryCatch } from "@/utils/tryCatch"; -import { redirect } from "next/navigation"; export default async function DeviceDetails({ - params, + params, }: { - params: Promise<{ deviceId: string }>; + params: Promise<{ deviceId: string }>; }) { - const deviceId = (await params)?.deviceId; - const [error, device] = await tryCatch(getDevice({ deviceId: deviceId })); - if (error) { - // Handle specific actions for certain errors, but reuse the error message - if (error.message === "UNAUTHORIZED") { - redirect("/auth/signin"); - } else { - // For all other errors, display the error message directly - return ; - } - } - if (!device) return null; + const deviceId = (await params)?.deviceId; + const [error, device] = await tryCatch(getDevice({ deviceId: deviceId })); + if (error) { + // Handle specific actions for certain errors, but reuse the error message + if (error.message === "UNAUTHORIZED") { + redirect("/auth/signin"); + } else { + // For all other errors, display the error message directly + return ; + } + } + if (!device) return null; - return ( -
-
-
-

- {device?.name} -

- {device?.mac} -

- Device active until{" "} - {new Date(device?.expiry_date || "").toLocaleDateString("en-US", { - month: "short", - day: "2-digit", - year: "numeric", - })} -

-
-
- {device?.expiry_date && new Date() < new Date(device.expiry_date) && ( -

- ACTIVE -

- )} -
-
+ return ( +
+
+
+

+ {device?.name} +

+ {device?.mac} +

+ Device active until{" "} + {new Date(device?.expiry_date || "").toLocaleDateString("en-US", { + month: "short", + day: "2-digit", + year: "numeric", + })} +

+
+
+ {device?.expiry_date && new Date() < new Date(device.expiry_date) && ( +

+ ACTIVE +

+ )} +
+
-
- - {/* + + {/* */} -
- {/* +
+ {/* */} -
- ); + + ); } diff --git a/app/(dashboard)/devices/loading.tsx b/app/(dashboard)/devices/loading.tsx index b28a7e1..644748f 100644 --- a/app/(dashboard)/devices/loading.tsx +++ b/app/(dashboard)/devices/loading.tsx @@ -2,21 +2,24 @@ import DevicesTableSkeleton from "@/components/device-table-skeleton"; import { Skeleton } from "@/components/ui/skeleton"; export default function LoadingComponent() { - return ( -
-
- - -
-
- -
-
- ); + return ( +
+
+ + +
+
+ +
+
+ +
+
+ ); } diff --git a/app/(dashboard)/payments/[paymentId]/loading.tsx b/app/(dashboard)/payments/[paymentId]/loading.tsx new file mode 100644 index 0000000..57517f4 --- /dev/null +++ b/app/(dashboard)/payments/[paymentId]/loading.tsx @@ -0,0 +1,63 @@ +import { Skeleton } from "@/components/ui/skeleton"; +import { + Table, + TableBody, + TableCell, + TableFooter, + TableRow, +} from "@/components/ui/table"; + +export default function PaymentLoading() { + return ( +
+
+ +
+
+ +
+
+ {Array.from({ length: 1 }).map((_, i) => ( + + ))} +
+
+ + + + Payment created + + + + + + Total Devices + + + + + + Duration + + + + + + + + Total Due + + + + + +
+
+
+
+
+ ); +} diff --git a/app/(dashboard)/payments/[paymentId]/page.tsx b/app/(dashboard)/payments/[paymentId]/page.tsx index 2ba992c..eb1e1ef 100644 --- a/app/(dashboard)/payments/[paymentId]/page.tsx +++ b/app/(dashboard)/payments/[paymentId]/page.tsx @@ -10,77 +10,77 @@ import { TextShimmer } from "@/components/ui/text-shimmer"; import { cn } from "@/lib/utils"; import { tryCatch } from "@/utils/tryCatch"; export default async function PaymentPage({ - params, + params, }: { - params: Promise<{ paymentId: string }>; + params: Promise<{ paymentId: string }>; }) { - const paymentId = (await params).paymentId; - const [error, payment] = await tryCatch(getPayment({ id: paymentId })); - if (error) { - if (error.message === "Invalid token.") redirect("/auth/signin"); - return ; - } - const [userError, userProfile] = await tryCatch(getProfile()); - if (userError) { - if (userError.message === "Invalid token.") redirect("/auth/signin"); - return ; - } + const paymentId = (await params).paymentId; + const [error, payment] = await tryCatch(getPayment({ id: paymentId })); + if (error) { + if (error.message === "Invalid token.") redirect("/auth/signin"); + return ; + } + const [userError, userProfile] = await tryCatch(getProfile()); + if (userError) { + if (userError.message === "Invalid token.") redirect("/auth/signin"); + return ; + } - return ( -
-
-

Payment

-
- {!payment.is_expired && - payment.paid && - payment.status !== "PENDING" && ( - - )} - {payment.status === "PENDING" && !payment.is_expired && ( - - )} - {!payment.paid && - (payment.is_expired ? ( - - ) : payment.status === "PENDING" ? ( - - ) : payment.status === "CANCELLED" ? ( - - ) : ( - "" - ))} -
-
- {!payment.paid && ( - - )} -
- -
-
- ); + return ( +
+
+

Payment

+
+ {!payment.is_expired && + payment.paid && + payment.status !== "PENDING" && ( + + )} + {payment.status === "PENDING" && !payment.is_expired && ( + + )} + {!payment.paid && + (payment.is_expired ? ( + + ) : payment.status === "PENDING" ? ( + + ) : payment.status === "CANCELLED" ? ( + + ) : ( + "" + ))} +
+
+ {!payment.paid && ( + + )} +
+ +
+
+ ); } diff --git a/app/(dashboard)/payments/loading.tsx b/app/(dashboard)/payments/loading.tsx index 0d99183..28b45f5 100644 --- a/app/(dashboard)/payments/loading.tsx +++ b/app/(dashboard)/payments/loading.tsx @@ -2,21 +2,24 @@ import DevicesTableSkeleton from "@/components/device-table-skeleton"; import { Skeleton } from "@/components/ui/skeleton"; export default function LoadingComponent() { - return ( -
-
- - -
-
- -
-
- ); + return ( +
+
+ + +
+
+ +
+
+ +
+
+ ); } diff --git a/components/devices-to-pay.tsx b/components/devices-to-pay.tsx index c60f5de..592438a 100644 --- a/components/devices-to-pay.tsx +++ b/components/devices-to-pay.tsx @@ -78,11 +78,13 @@ export default function DevicesToPay({ {devices?.map((device) => (
-
{device.name}
-
+
+ {device.name} +
+
{device.mac}
@@ -160,8 +162,10 @@ export default function DevicesToPay({ - Payment created - + + Payment created + + {new Date(payment?.created_at ?? "").toLocaleDateString( "en-US", { @@ -176,22 +180,31 @@ export default function DevicesToPay({ - Total Devices - + + Total Devices + + {devices?.length} - Duration - + + Duration + + {payment?.number_of_months} Months - Total Due - + + Total Due + + {payment?.amount?.toFixed(2)} diff --git a/components/wallet-transactions-table.tsx b/components/wallet-transactions-table.tsx index 3805428..e686dba 100644 --- a/components/wallet-transactions-table.tsx +++ b/components/wallet-transactions-table.tsx @@ -2,13 +2,13 @@ import { Calendar } from "lucide-react"; import Link from "next/link"; import { redirect } from "next/navigation"; import { - Table, - TableBody, - TableCell, - TableFooter, - TableHead, - TableHeader, - TableRow, + Table, + TableBody, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow, } from "@/components/ui/table"; import type { WalletTransaction } from "@/lib/backend-types"; import { cn } from "@/lib/utils"; @@ -19,223 +19,227 @@ import { Badge } from "./ui/badge"; import { Button } from "./ui/button"; export async function WalletTransactionsTable({ - searchParams, + searchParams, }: { - searchParams: Promise<{ - [key: string]: unknown; - }>; + searchParams: Promise<{ + [key: string]: unknown; + }>; }) { - const resolvedParams = await searchParams; - const page = Number.parseInt(resolvedParams.page as string) || 1; - const limit = 10; - const offset = (page - 1) * limit; - // Build params object - 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 [error, transactions] = await tryCatch( - getWaleltTransactions(apiParams), - ); + const resolvedParams = await searchParams; + const page = Number.parseInt(resolvedParams.page as string) || 1; + const limit = 10; + const offset = (page - 1) * limit; + // Build params object + 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 [error, transactions] = await tryCatch( + getWaleltTransactions(apiParams), + ); - if (error) { - if (error.message.includes("Unauthorized")) { - redirect("/auth/signin"); - } else { - return
{JSON.stringify(error, null, 2)}
; - } - } - const { data, meta } = transactions; - const totalDebit = data.reduce( - (acc, trx) => acc + (trx.transaction_type === "DEBIT" ? trx.amount : 0), - 0, - ); - const totalCredit = data.reduce( - (acc, trx) => acc + (trx.transaction_type === "TOPUP" ? trx.amount : 0), - 0, - ); - return ( -
- {data?.length === 0 ? ( -
-

No transactions yet.

-
- ) : ( -
-
-
-
Total Debit
-

{totalDebit.toFixed(2)} MVR

-
-
-
Total Credit
-

{totalCredit.toFixed(2)} MVR

-
-
-
- - - - Description - Amount - Transaction Type - View Details - Created at - - - - {transactions?.data?.map((trx) => ( - - - - {trx.description} - - - {trx.amount.toFixed(2)} MVR + if (error) { + if (error.message.includes("Unauthorized")) { + redirect("/auth/signin"); + } else { + return
{JSON.stringify(error, null, 2)}
; + } + } + const { data, meta } = transactions; + const totalDebit = data.reduce( + (acc, trx) => acc + (trx.transaction_type === "DEBIT" ? trx.amount : 0), + 0, + ); + const totalCredit = data.reduce( + (acc, trx) => acc + (trx.transaction_type === "TOPUP" ? trx.amount : 0), + 0, + ); + return ( +
+ {data?.length === 0 ? ( +
+

No transactions yet.

+
+ ) : ( +
+
+
+
+ Total Debit +
+

{totalDebit.toFixed(2)} MVR

+
+
+
+ Total Credit +
+

{totalCredit.toFixed(2)} MVR

+
+
+
+
+ + + Description + Amount + Transaction Type + View Details + Created at + + + + {transactions?.data?.map((trx) => ( + + + + {trx.description} + + + {trx.amount.toFixed(2)} MVR - - - {trx.transaction_type === "TOPUP" ? ( - - {trx.transaction_type} - - ) : ( - - {trx.transaction_type} - - )} - - + + + {trx.transaction_type === "TOPUP" ? ( + + {trx.transaction_type} + + ) : ( + + {trx.transaction_type} + + )} + + - - - {new Date(trx.created_at).toLocaleDateString("en-US", { - month: "short", - day: "2-digit", - year: "numeric", - minute: "2-digit", - hour: "2-digit", - })} - - - - - - - ))} - - - - - {meta?.total === 1 ? ( -

- Total {meta?.total} transaction. -

- ) : ( -

- Total {meta?.total} transactions. -

- )} -
-
-
-
-
-
- {data.map((trx) => ( - - ))} -
- -
- )} -
- ); + + + {new Date(trx.created_at).toLocaleDateString("en-US", { + month: "short", + day: "2-digit", + year: "numeric", + minute: "2-digit", + hour: "2-digit", + })} + + + + + + + ))} + + + + + {meta?.total === 1 ? ( +

+ Total {meta?.total} transaction. +

+ ) : ( +

+ Total {meta?.total} transactions. +

+ )} +
+
+
+ +
+
+ {data.map((trx) => ( + + ))} +
+ +
+ )} + + ); } function MobileTransactionDetails({ trx }: { trx: WalletTransaction }) { - return ( -
-
-
- - - {new Date(trx.created_at).toLocaleDateString("en-US", { - month: "short", - day: "2-digit", - year: "numeric", - minute: "2-digit", - hour: "2-digit", - })} - -
-

{trx.description}

-
+ return ( +
+
+
+ + + {new Date(trx.created_at).toLocaleDateString("en-US", { + month: "short", + day: "2-digit", + year: "numeric", + minute: "2-digit", + hour: "2-digit", + })} + +
+

{trx.description}

+
-
-
-

Amount

- - {trx.amount.toFixed(2)} MVR - -
- - {trx.transaction_type === "TOPUP" ? ( - - {trx.transaction_type} - - ) : ( - - {trx.transaction_type} - - )} - -
-
- - - -
-
- ); +
+
+

Amount

+ + {trx.amount.toFixed(2)} MVR + +
+ + {trx.transaction_type === "TOPUP" ? ( + + {trx.transaction_type} + + ) : ( + + {trx.transaction_type} + + )} + +
+
+ + + +
+
+ ); }