From 5de4bb91008b2365d1d9d190bc0ee6667c5f76cb Mon Sep 17 00:00:00 2001 From: i701 Date: Sat, 5 Jul 2025 20:16:39 +0500 Subject: [PATCH 1/9] =?UTF-8?q?refactor:=20add=20topup=20expiry=20filter?= =?UTF-8?q?=20to=20the=20Topups=20component=20and=20simplify=20expired=20b?= =?UTF-8?q?adge=20display=20in=20TopupsTable=20=F0=9F=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/top-ups/page.tsx | 19 +++++++++++++++++++ components/topups-table.tsx | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/(dashboard)/top-ups/page.tsx b/app/(dashboard)/top-ups/page.tsx index ad41ebc..6aaf80d 100644 --- a/app/(dashboard)/top-ups/page.tsx +++ b/app/(dashboard)/top-ups/page.tsx @@ -48,6 +48,25 @@ export default async function Topups({ }, ], }, + { + label: "Topup Expiry", + name: "is_expired", + type: "radio-group", + options: [ + { + label: "All", + value: "", + }, + { + label: "Expired", + value: "true", + }, + { + label: "Not Expired", + value: "false", + }, + ], + }, { label: "Topup Amount", name: "amount", diff --git a/components/topups-table.tsx b/components/topups-table.tsx index 24aad7b..739388e 100644 --- a/components/topups-table.tsx +++ b/components/topups-table.tsx @@ -125,7 +125,7 @@ export async function TopupsTable({ {topup.paid ? ( {topup.status} ) : topup.is_expired ? ( - Expired + Expired ) : ( {topup.status} )} From 2597ac7d799fe067c85f2af7c70d6cecb59d73da Mon Sep 17 00:00:00 2001 From: Shihaam Abdul Rahman Date: Sat, 5 Jul 2025 20:21:11 +0500 Subject: [PATCH 2/9] clean up dangling images after restart --- .gitea/workflows/docker-build.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml index d0bec23..5bd30fe 100644 --- a/.gitea/workflows/docker-build.yml +++ b/.gitea/workflows/docker-build.yml @@ -32,9 +32,13 @@ jobs: run: docker compose --progress plain build --push - - name: Deploy production + - name: Deploy production if: github.event_name != 'pull_request' run: | ssh root@10.0.1.5 -t "cd /mnt && docker compose --progress plain pull portal-ui" ssh root@10.0.1.5 -t "cd /mnt && docker compose --progress plain down portal-ui" ssh root@10.0.1.5 -t "cd /mnt && docker compose --progress plain up -d portal-ui" + + - name: Clean up dangling images + if: github.event_name != 'pull_request' + run: ssh root@10.0.1.5 -t "docker image prune -f" From c0a71dd811ab51d1901f526b724a1c2e9b804d86 Mon Sep 17 00:00:00 2001 From: Shihaam Abdul Rahman Date: Sat, 5 Jul 2025 20:25:42 +0500 Subject: [PATCH 3/9] fix docker build yml --- .gitea/workflows/docker-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml index 5bd30fe..33a9078 100644 --- a/.gitea/workflows/docker-build.yml +++ b/.gitea/workflows/docker-build.yml @@ -32,7 +32,7 @@ jobs: run: docker compose --progress plain build --push - - name: Deploy production + - name: Deploy production if: github.event_name != 'pull_request' run: | ssh root@10.0.1.5 -t "cd /mnt && docker compose --progress plain pull portal-ui" From 135edf80a8509d81482b6fd3c58c5be6b8881b4b Mon Sep 17 00:00:00 2001 From: Shihaam Abdul Rahman Date: Sat, 5 Jul 2025 20:38:35 +0500 Subject: [PATCH 4/9] deploy in 1 ssh session --- .gitea/workflows/docker-build.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml index 33a9078..9129bf9 100644 --- a/.gitea/workflows/docker-build.yml +++ b/.gitea/workflows/docker-build.yml @@ -35,9 +35,10 @@ jobs: - name: Deploy production if: github.event_name != 'pull_request' run: | - ssh root@10.0.1.5 -t "cd /mnt && docker compose --progress plain pull portal-ui" - ssh root@10.0.1.5 -t "cd /mnt && docker compose --progress plain down portal-ui" - ssh root@10.0.1.5 -t "cd /mnt && docker compose --progress plain up -d portal-ui" + ssh root@10.0.1.5 -t "cd /mnt && \ + docker compose --progress plain pull portal-ui && \ + docker compose --progress plain down portal-ui && \ + docker compose --progress plain up -d portal-ui" - name: Clean up dangling images if: github.event_name != 'pull_request' From 9fe3c3b77474a1b1bc5d2acee1513b7dcaa528df Mon Sep 17 00:00:00 2001 From: i701 Date: Sat, 5 Jul 2025 20:39:28 +0500 Subject: [PATCH 5/9] =?UTF-8?q?feat:=20enhance=20payment=20retrieval=20wit?= =?UTF-8?q?h=20flexible=20query=20parameters=20and=20add=20dynamic=20filte?= =?UTF-8?q?rs=20to=20payments=20page=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- actions/payment.ts | 13 ++++++++++-- app/(dashboard)/payments/page.tsx | 35 ++++++++++++++++++++++++++++--- components/devices-table.tsx | 1 - components/payments-table.tsx | 35 +++++++++++++++++-------------- 4 files changed, 62 insertions(+), 22 deletions(-) diff --git a/actions/payment.ts b/actions/payment.ts index 3a3f478..6098869 100644 --- a/actions/payment.ts +++ b/actions/payment.ts @@ -90,10 +90,19 @@ export async function getPayment({ id }: { id: string }) { return data; } -export async function getPayments() { +type GetPaymentProps = { + [key: string]: string | number | undefined; // Allow additional properties for flexibility +}; + +export async function getPayments(params: GetPaymentProps) { + // Build query string from all defined params + const query = Object.entries(params) + .filter(([_, value]) => value !== undefined && value !== "") + .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`) + .join("&"); const session = await getServerSession(authOptions); const response = await fetch( - `${process.env.SARLINK_API_BASE_URL}/api/billing/payment/`, + `${process.env.SARLINK_API_BASE_URL}/api/billing/payment/?${query}`, { method: "GET", headers: { diff --git a/app/(dashboard)/payments/page.tsx b/app/(dashboard)/payments/page.tsx index 2b6f207..d8b7fb4 100644 --- a/app/(dashboard)/payments/page.tsx +++ b/app/(dashboard)/payments/page.tsx @@ -1,4 +1,5 @@ import { Suspense } from "react"; +import DynamicFilter from "@/components/generic-filter"; import { PaymentsTable } from "@/components/payments-table"; import Search from "@/components/search"; @@ -8,8 +9,6 @@ export default async function Payments({ searchParams: Promise<{ query: string; page: number; - sortBy: string; - status: string; }>; }) { const query = (await searchParams)?.query || ""; @@ -23,7 +22,37 @@ export default async function Payments({ id="user-filters" className=" pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start" > - + {" "} diff --git a/components/devices-table.tsx b/components/devices-table.tsx index c28fdf3..9c82bce 100644 --- a/components/devices-table.tsx +++ b/components/devices-table.tsx @@ -22,7 +22,6 @@ export async function DevicesTable({ searchParams, parentalControl, additionalFilters = {}, - }: { searchParams: Promise<{ [key: string]: unknown; diff --git a/components/payments-table.tsx b/components/payments-table.tsx index 5e6bbbd..014593a 100644 --- a/components/payments-table.tsx +++ b/components/payments-table.tsx @@ -24,13 +24,17 @@ export async function PaymentsTable({ searchParams, }: { searchParams: Promise<{ - query: string; - page: number; - sortBy: string; + [key: string]: unknown; }>; }) { - const query = (await searchParams)?.query || ""; - const [error, payments] = await tryCatch(getPayments()); + const resolvedParams = await searchParams; + const apiParams: Record = {}; + for (const [key, value] of Object.entries(resolvedParams)) { + if (value !== undefined && value !== "") { + apiParams[key] = typeof value === "number" ? value : String(value); + } + } + const [error, payments] = await tryCatch(getPayments(apiParams)); if (error) { if (error.message.includes("Unauthorized")) { @@ -134,18 +138,17 @@ export async function PaymentsTable({ - - {query.length > 0 && ( -

- Showing {payments?.data?.length} payments for " - {query} - " + + + {meta?.total === 1 ? ( +

+ Total {meta?.total} payment.

- )} -
- - {meta.total} payments - + ) : ( +

+ Total {meta?.total} payments. +

+ )}
From 13f0e720f42ec6e357784a5b39ce40bf67a534ff Mon Sep 17 00:00:00 2001 From: i701 Date: Sat, 5 Jul 2025 20:47:31 +0500 Subject: [PATCH 6/9] =?UTF-8?q?feat:=20add=20payment=20method=20filter=20t?= =?UTF-8?q?o=20payments=20page=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(dashboard)/payments/page.tsx | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/app/(dashboard)/payments/page.tsx b/app/(dashboard)/payments/page.tsx index d8b7fb4..c962e1d 100644 --- a/app/(dashboard)/payments/page.tsx +++ b/app/(dashboard)/payments/page.tsx @@ -1,7 +1,6 @@ import { Suspense } from "react"; import DynamicFilter from "@/components/generic-filter"; import { PaymentsTable } from "@/components/payments-table"; -import Search from "@/components/search"; export default async function Payments({ searchParams, @@ -43,6 +42,25 @@ export default async function Payments({ }, ], }, + { + label: "Payment Method", + name: "method", + type: "radio-group", + options: [ + { + label: "All", + value: "", + }, + { + label: "Transfer", + value: "TRANSFER", + }, + { + label: "Wallet", + value: "WALLET", + }, + ], + }, { label: "Number of months", name: "number_of_months", From 3e3b5f15db7e2dbab55e480c0de6585ee7378bc0 Mon Sep 17 00:00:00 2001 From: i701 Date: Sat, 5 Jul 2025 21:21:08 +0500 Subject: [PATCH 7/9] =?UTF-8?q?feat:=20add=20custom=20headers=20configurat?= =?UTF-8?q?ion=20to=20next.js=20config=20for=20stream/suspense=20support?= =?UTF-8?q?=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/next.config.ts b/next.config.ts index da25007..32b8868 100644 --- a/next.config.ts +++ b/next.config.ts @@ -21,6 +21,19 @@ const nextConfig: NextConfig = { ], }, output: "standalone", + async headers() { + return [ + { + source: '/:path*{/}?', + headers: [ + { + key: 'X-Accel-Buffering', + value: 'no', + }, + ], + }, + ] + }, }; export default nextConfig; From d4993530f7f4670df1da0a1a570099d0971d2aa4 Mon Sep 17 00:00:00 2001 From: i701 Date: Sun, 6 Jul 2025 19:48:24 +0500 Subject: [PATCH 8/9] =?UTF-8?q?feat:=20add=20radio=20filter=20for=20topup?= =?UTF-8?q?=20expiry=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/generic-filter.tsx | 19 +++++++++++++++++++ components/ui/app-sidebar.tsx | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/components/generic-filter.tsx b/components/generic-filter.tsx index 5ef3c53..f290848 100644 --- a/components/generic-filter.tsx +++ b/components/generic-filter.tsx @@ -388,6 +388,25 @@ export default function DynamicFilter<

); } + if (config.type === "radio-group") { + const stringValue = value as string; + // For true/false values, display the label instead of the value + if (stringValue === "true" || stringValue === "false") { + const option = config.options.find((opt) => opt.value === stringValue); + const displayValue = option?.label || stringValue; + return ( +

+ {config.label}: {displayValue} +

+ ); + } + // For other values, display as normal + return ( +

+ {config.label}: {stringValue} +

+ ); + } return (

{config.label}: {value} diff --git a/components/ui/app-sidebar.tsx b/components/ui/app-sidebar.tsx index a156e9d..5dc6431 100644 --- a/components/ui/app-sidebar.tsx +++ b/components/ui/app-sidebar.tsx @@ -82,7 +82,7 @@ export async function AppSidebar({ }, { title: "Parental Control", - link: "/parental-control", + link: "/parental-control?page=1", icon: , perm_identifier: "device", }, From b9d8e56c3c112133abe229f67236d7385ca5cc1b Mon Sep 17 00:00:00 2001 From: i701 Date: Sun, 6 Jul 2025 19:59:27 +0500 Subject: [PATCH 9/9] =?UTF-8?q?feat:=20update=20cancelTopup=20API=20call?= =?UTF-8?q?=20to=20use=20PATCH=20method=20and=20enhance=20success=20messag?= =?UTF-8?q?e=20with=20topup=20details=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- actions/payment.ts | 14 +++----------- components/billing/cancel-topup-button.tsx | 8 +++++--- components/topups-table.tsx | 22 ++++++++++++---------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/actions/payment.ts b/actions/payment.ts index 6098869..9b0eef9 100644 --- a/actions/payment.ts +++ b/actions/payment.ts @@ -164,24 +164,16 @@ export async function getTopup({ id }: { id: string }) { export async function cancelTopup({ id }: { id: string }) { const session = await getServerSession(authOptions); const response = await fetch( - `${process.env.SARLINK_API_BASE_URL}/api/billing/topup/${id}/delete/`, + `${process.env.SARLINK_API_BASE_URL}/api/billing/topup/${id}/cancel/`, { - method: "DELETE", + method: "PATCH", headers: { "Content-Type": "application/json", Authorization: `Token ${session?.apiToken}`, }, }, ); - if (!response.ok) { - const errorData = (await response.json()) as ApiError; - const errorMessage = - errorData.message || errorData.detail || "An error occurred."; - const error = new Error(errorMessage); - (error as ApiError & { details?: ApiError }).details = errorData; // Attach the errorData to the error object - throw error; - } - return { message: "Topup successfully canceled." }; + return handleApiResponse(response, "cancelTopup"); } export async function cancelPayment({ id }: { id: string }) { diff --git a/components/billing/cancel-topup-button.tsx b/components/billing/cancel-topup-button.tsx index a6ca438..21f71b2 100644 --- a/components/billing/cancel-topup-button.tsx +++ b/components/billing/cancel-topup-button.tsx @@ -17,13 +17,15 @@ export default function CancelTopupButton({ - - {topup.paid ? "Paid" : "Unpaid"} - + {topup.status !== "CANCELLED" && ( + + {topup.paid ? "Paid" : "Unpaid"} + + )}