From 01b064aee79aa1dabb9387cd3eb528bb0023a2f4 Mon Sep 17 00:00:00 2001 From: i701 Date: Mon, 30 Jun 2025 15:16:36 +0500 Subject: [PATCH 1/2] WIP feat(admin-devices): enhance device management from admin with dynamic filters and improved blocking functionality --- actions/omada-actions.ts | 2 +- app/(dashboard)/devices/page.tsx | 7 - app/(dashboard)/user-devices/page.tsx | 38 ++- components/admin/admin-devices-table.tsx | 357 +++++++++++------------ components/block-device-dialog.tsx | 154 ++++------ components/clickable-row.tsx | 6 +- queries/devices.ts | 9 +- 7 files changed, 266 insertions(+), 307 deletions(-) diff --git a/actions/omada-actions.ts b/actions/omada-actions.ts index af689bd..35dcdba 100644 --- a/actions/omada-actions.ts +++ b/actions/omada-actions.ts @@ -1,8 +1,8 @@ "use server"; +import { revalidatePath } from "next/cache"; import type { GroupProfile, MacAddress, OmadaResponse } from "@/lib/types"; import { formatMacAddress } from "@/lib/utils"; -import { revalidatePath } from "next/cache"; async function fetchOmadaGroupProfiles(siteId: string): Promise { if (!siteId) { diff --git a/app/(dashboard)/devices/page.tsx b/app/(dashboard)/devices/page.tsx index f640915..9cf3b63 100644 --- a/app/(dashboard)/devices/page.tsx +++ b/app/(dashboard)/devices/page.tsx @@ -51,13 +51,6 @@ export default async function Devices({ label: "Vendor", type: "string", placeholder: "Enter vendor name", - }, { - label: "Amount of Devices", - name: "amount", - type: "dual-range-slider", - min: 1, - max: 100, - sliderLabel: "MVR" } ]} /> diff --git a/app/(dashboard)/user-devices/page.tsx b/app/(dashboard)/user-devices/page.tsx index 20da8f7..aac2892 100644 --- a/app/(dashboard)/user-devices/page.tsx +++ b/app/(dashboard)/user-devices/page.tsx @@ -1,9 +1,7 @@ -import { AdminDevicesTable } from "@/components/admin/admin-devices-table"; -import Search from "@/components/search"; import { Suspense } from "react"; - - - +import { AdminDevicesTable } from "@/components/admin/admin-devices-table"; +import DynamicFilter from "@/components/generic-filter"; +import Search from "@/components/search"; export default async function UserDevices({ searchParams, @@ -19,17 +17,37 @@ export default async function UserDevices({ return (
-

- User Devices -

+

User Devices

- - +
diff --git a/components/admin/admin-devices-table.tsx b/components/admin/admin-devices-table.tsx index 0845be0..1680bbd 100644 --- a/components/admin/admin-devices-table.tsx +++ b/components/admin/admin-devices-table.tsx @@ -1,204 +1,181 @@ -// import { -// Table, -// TableBody, -// TableCaption, -// TableCell, -// TableFooter, -// TableHead, -// TableHeader, -// TableRow, -// } from "@/components/ui/table"; -// import { headers } from "next/headers"; -// import Link from "next/link"; -// import BlockDeviceDialog from "../block-device-dialog"; -// import DeviceCard from "../device-card"; -// import Pagination from "../pagination"; +import { HandCoins } from "lucide-react"; +import Link from "next/link"; +import { redirect } from "next/navigation"; +import { getServerSession } from "next-auth"; +import { authOptions } from "@/app/auth"; +import { + Table, + TableBody, + TableCaption, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { cn } from "@/lib/utils"; +import { getDevices } from "@/queries/devices"; +import { tryCatch } from "@/utils/tryCatch"; +import AddDevicesToCartButton from "../add-devices-to-cart-button"; +import BlockDeviceDialog from "../block-device-dialog"; +import ClientErrorMessage from "../client-error-message"; +import DeviceCard from "../device-card"; +import Pagination from "../pagination"; export async function AdminDevicesTable({ searchParams, parentalControl, }: { searchParams: Promise<{ - query: string; - page: number; - sortBy: string; + [key: string]: unknown; }>; parentalControl?: boolean; }) { - console.log(parentalControl); - // const session = await auth.api.getSession({ - // headers: await headers(), - // }); - // const isAdmin = session?.user.role === "ADMIN"; - const query = (await searchParams)?.query || ""; - const page = (await searchParams)?.page; - console.log(query, page); - // const sortBy = (await searchParams)?.sortBy || "asc"; - // const totalDevices = await prisma.device.count({ - // where: { - // OR: [ - // { - // name: { - // contains: query || "", - // mode: "insensitive", - // }, - // }, - // { - // mac: { - // contains: query || "", - // mode: "insensitive", - // }, - // }, - // ], - // }, - // }); + const resolvedParams = await searchParams; + const session = await getServerSession(authOptions); + const isAdmin = session?.user?.is_superuser; - // const totalPages = Math.ceil(totalDevices / 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 devices = await prisma.device.findMany({ - // where: { - // OR: [ - // { - // name: { - // contains: query || "", - // mode: "insensitive", - // }, - // }, - // { - // mac: { - // contains: query || "", - // mode: "insensitive", - // }, - // }, - // ], - // }, - // include: { - // User: true, - // payments: true, - // }, - // skip: offset, - // take: limit, - // orderBy: { - // name: `${sortBy}` as "asc" | "desc", - // }, - // }); - return null; - // return ( - //
- // {devices.length === 0 ? ( - //
- //

No devices yet.

- //
- // ) : ( - // <> - //
- // - // Table of all devices. - // - // - // Device Name - // User - // MAC Address - // isActive - // blocked - // blockedBy - // expiryDate - // - // - // - // {devices.map((device) => ( - // - // - //
- // - // {device.name} - // - // {device.isActive && ( - // - // Active until{" "} - // {new Date().toLocaleDateString("en-US", { - // month: "short", - // day: "2-digit", - // year: "numeric", - // })} - // - // )} + // 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; - // {device.blocked && ( - //
- // Comment: - //

- // blocked because he was watching youtube - //

- //
- // )} - //
- //
- // - // {device.User?.name} - // + const [error, devices] = await tryCatch( + getDevices(apiParams, true), + ); + if (error) { + if (error.message === "UNAUTHORIZED") { + redirect("/auth/signin"); + } else { + return ; + } + } + const { meta, data } = devices; + return ( +
+ {data?.length === 0 ? ( +
+

No devices.

+
+ ) : ( + <> +
+
+ Table of all devices. + + + Device Name + User + MAC Address + Vendor + # + + + + {data?.map((device) => ( + + +
+ + {device.name} + + {device.is_active ? ( +
+ Active until{" "} + + {new Date(device.expiry_date || "").toLocaleDateString( + "en-US", + { + month: "short", + day: "2-digit", + year: "numeric", + }, + )} + +
+ ) : ( +

Device Inactive

+ )} + {device.has_a_pending_payment && ( + + + Payment Pending{" "} + + + + )} - // {device.mac} - // - // {device.isActive ? "Active" : "Inactive"} - // - // - // {device.blocked ? "Blocked" : "Not Blocked"} - // - // - // {device.blocked ? device.blockedBy : ""} - // - // - // {new Date().toLocaleDateString("en-US", { - // month: "short", - // day: "2-digit", - // year: "numeric", - // })} - // - // - // - // - // - // ))} - // - // - // - // - // {query.length > 0 && ( - //

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

- // )} - //
- // - // {totalDevices} devices - // - //
- //
- //
- // - //
- //
- // {devices.map((device) => ( - // - // ))} - //
- // - // )} - //
- // ); + {device.blocked_by === "ADMIN" && device.blocked && ( +
+ Comment +

{device?.reason_for_blocking}

+
+ )} +
+ + {device.user} + {device.mac} + {device?.vendor} + + + + + ))} + + + + + {meta?.total === 1 ? ( +

+ Total {meta?.total} device. +

+ ) : ( +

+ Total {meta?.total} devices. +

+ )} +
+
+
+ + +
+ {data?.map((device) => ( + + ))} +
+ + + ) + } + + ); } diff --git a/components/block-device-dialog.tsx b/components/block-device-dialog.tsx index b5e3d56..209db18 100644 --- a/components/block-device-dialog.tsx +++ b/components/block-device-dialog.tsx @@ -1,5 +1,12 @@ "use client"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { OctagonX } from "lucide-react"; +import { useState } from "react"; +import { type SubmitHandler, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; import { blockDevice as BlockDeviceFromOmada } from "@/actions/omada-actions"; import { Button } from "@/components/ui/button"; import { @@ -14,13 +21,6 @@ import { Label } from "@/components/ui/label"; import type { Device } from "@/lib/backend-types"; import { cn } from "@/lib/utils"; import { blockDevice } from "@/queries/devices"; - -import { zodResolver } from "@hookform/resolvers/zod"; -import { OctagonX } from "lucide-react"; -import { useState } from "react"; -import { type SubmitHandler, useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; import { TextShimmer } from "./ui/text-shimmer"; import { Textarea } from "./ui/textarea"; @@ -44,7 +44,7 @@ export default function BlockDeviceDialog({ const onSubmit: SubmitHandler> = (data) => { setDisabled(true); - console.log(data); + console.log("data in block device dialog", data); toast.promise( blockDevice({ deviceId: String(device.id), @@ -59,8 +59,9 @@ export default function BlockDeviceDialog({ return "Blocked!"; }, error: (error) => { + console.error("Error blocking device:", error); setDisabled(false); - return error || "Something went wrong"; + return error.message || "Something went wrong"; }, }, ); @@ -74,10 +75,10 @@ export default function BlockDeviceDialog({ onClick={() => { setDisabled(true); toast.promise( - BlockDeviceFromOmada({ - macAddress: device.mac, - type: "unblock", - reason: "", + blockDevice({ + blocked_by: "ADMIN", + deviceId: String(device.id), + reason_for_blocking: "", }), { loading: "unblockinig...", @@ -85,9 +86,9 @@ export default function BlockDeviceDialog({ setDisabled(false); return "Unblocked!"; }, - error: () => { + error: (error) => { setDisabled(false); - return "Something went wrong"; + return error.message || "Something went wrong"; }, }, ); @@ -96,84 +97,53 @@ export default function BlockDeviceDialog({ {disabled ? Unblocking : "Unblock"} ) : ( - <> - {!admin ? ( - - ) : ( - - - - - - - - Please provide a reason for blocking this device. - - -
-
-
- -