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"}
+
+ )}