mirror of
				https://github.com/i701/sarlink-portal.git
				synced 2025-10-31 16:07:00 +00:00 
			
		
		
		
	Refactor dashboard components and update global styles
- Updated the title and description in layout.tsx to reflect the new application name. - Replaced the background color in globals.css with a background image for the title section. - Enhanced the Devices and UserDevices pages by adding search and filter components for improved user interaction. - Introduced a new DevicesTable component for displaying device data with pagination. - Updated the Users page to improve layout and added a filter for user status. - Made various UI adjustments across components for better consistency and usability.
This commit is contained in:
		| @@ -1,15 +1,37 @@ | ||||
| import { auth } from "@/lib/auth"; | ||||
| import { headers } from "next/headers"; | ||||
|  | ||||
| export default async function Devices() { | ||||
| 	const session = await auth.api.getSession({ | ||||
| 		headers: await headers(), | ||||
| 	}); | ||||
| import { DevicesTable } from "@/components/devices-table"; | ||||
| import Search from "@/components/search"; | ||||
| import { Button } from "@/components/ui/button"; | ||||
| import React, { Suspense } from "react"; | ||||
| export default async function Devices({ | ||||
| 	searchParams, | ||||
| }: { | ||||
| 	searchParams: Promise<{ | ||||
| 		query: string; | ||||
| 		page: number; | ||||
| 		sortBy: string; | ||||
| 		status: string; | ||||
| 	}>; | ||||
| }) { | ||||
|  | ||||
| 	return ( | ||||
| 		<div> | ||||
| 			<h2>Server session</h2> | ||||
| 			<pre>{JSON.stringify(session?.user, null, 2)}</pre> | ||||
| 			<div className="flex justify-between items-center border-b-2 text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4"> | ||||
| 				<h3> | ||||
| 					My Devices | ||||
| 				</h3> | ||||
| 				<Button>Add new device</Button> | ||||
| 			</div> | ||||
|  | ||||
| 			<div | ||||
| 				id="user-filters" | ||||
| 				className=" border-b-2 pb-4 gap-4 flex items-center justify-start" | ||||
| 			> | ||||
| 				<Search /> | ||||
|  | ||||
| 			</div> | ||||
| 			<Suspense fallback={"loading...."}> | ||||
| 				<DevicesTable searchParams={searchParams} /> | ||||
| 			</Suspense> | ||||
| 		</div> | ||||
| 	); | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,54 @@ | ||||
| import React from "react"; | ||||
| import Filter from "@/components/filter"; | ||||
| import Search from "@/components/search"; | ||||
| import { UsersTable } from "@/components/user-table"; | ||||
| import { CheckCheck, Hourglass, Minus } from "lucide-react"; | ||||
| import React, { Suspense } from "react"; | ||||
| export default async function UserDevcies({ | ||||
| 	searchParams, | ||||
| }: { | ||||
| 	searchParams: Promise<{ | ||||
| 		query: string; | ||||
| 		page: number; | ||||
| 		sortBy: string; | ||||
| 		status: string; | ||||
| 	}>; | ||||
| }) { | ||||
|  | ||||
| export default function UserDevices() { | ||||
| 	return <div>UserDevices</div>; | ||||
| 	return ( | ||||
| 		<div> | ||||
| 			<h3 className="border-b-2 text-2xl font-bold title-bg py-4 px-2 mb-4"> | ||||
| 				My Devices | ||||
| 			</h3> | ||||
| 			<div | ||||
| 				id="user-filters" | ||||
| 				className=" border-b-2 pb-4 gap-4 flex items-center justify-start" | ||||
| 			> | ||||
| 				<Search /> | ||||
| 				<Filter | ||||
| 					options={[ | ||||
| 						{ | ||||
| 							value: "all", | ||||
| 							label: "ALL", | ||||
| 							icon: <Minus size={14} />, | ||||
| 						}, | ||||
| 						{ | ||||
| 							value: "unverified", | ||||
| 							label: "Unverfieid", | ||||
| 							icon: <CheckCheck size={14} />, | ||||
| 						}, | ||||
| 						{ | ||||
| 							value: "verified", | ||||
| 							label: "Verified", | ||||
| 							icon: <Hourglass size={14} />, | ||||
| 						}, | ||||
| 					]} | ||||
| 					defaultOption="all" | ||||
| 					queryParamKey="status" | ||||
| 				/> | ||||
| 			</div> | ||||
| 			<Suspense fallback={"loading...."}> | ||||
| 				<UsersTable searchParams={searchParams} /> | ||||
| 			</Suspense> | ||||
| 		</div> | ||||
| 	); | ||||
| } | ||||
|   | ||||
| @@ -18,35 +18,38 @@ export default async function AdminUsers({ | ||||
|  | ||||
| 	return ( | ||||
| 		<div> | ||||
| 			<h3 className="border-b-2 text-2xl font-bold title-bg py-4 px-2 mb-4"> | ||||
| 			<h3 className="border-b-2 text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4"> | ||||
| 				Users | ||||
| 			</h3> | ||||
| 			<div | ||||
| 				id="user-filters" | ||||
| 				className=" border-b-2 pb-4 gap-4 flex items-center justify-start" | ||||
| 			> | ||||
| 				<Search /> | ||||
| 				<Filter | ||||
| 					options={[ | ||||
| 						{ | ||||
| 							value: "all", | ||||
| 							label: "ALL", | ||||
| 							icon: <Minus size={14} />, | ||||
| 						}, | ||||
| 						{ | ||||
| 							value: "unverified", | ||||
| 							label: "Unverfieid", | ||||
| 							icon: <CheckCheck size={14} />, | ||||
| 						}, | ||||
| 						{ | ||||
| 							value: "verified", | ||||
| 							label: "Verified", | ||||
| 							icon: <Hourglass size={14} />, | ||||
| 						}, | ||||
| 					]} | ||||
| 					defaultOption="all" | ||||
| 					queryParamKey="status" | ||||
| 				/> | ||||
| 				<div className="flex flex-col sm:flex-row flex-wrap items-start justify-start gap-2"> | ||||
| 					<Search /> | ||||
| 					<Filter | ||||
| 						options={[ | ||||
| 							{ | ||||
| 								value: "all", | ||||
| 								label: "ALL", | ||||
| 								icon: <Minus size={14} />, | ||||
| 							}, | ||||
| 							{ | ||||
| 								value: "unverified", | ||||
| 								label: "Unverfieid", | ||||
| 								icon: <CheckCheck size={14} />, | ||||
| 							}, | ||||
| 							{ | ||||
| 								value: "verified", | ||||
| 								label: "Verified", | ||||
| 								icon: <Hourglass size={14} />, | ||||
| 							}, | ||||
| 						]} | ||||
| 						defaultOption="all" | ||||
| 						queryParamKey="status" | ||||
| 					/> | ||||
| 				</div> | ||||
|  | ||||
| 			</div> | ||||
| 			<Suspense fallback={"loading...."}> | ||||
| 				<UsersTable searchParams={searchParams} /> | ||||
|   | ||||
| @@ -89,6 +89,5 @@ body { | ||||
|  | ||||
|  | ||||
| .title-bg { | ||||
|   background-color: #fefefe; | ||||
|   background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23c1d3c8' fill-opacity='0.21' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E"); | ||||
| } | ||||
| @@ -13,8 +13,8 @@ const barlow = Barlow({ | ||||
| }); | ||||
|  | ||||
| export const metadata: Metadata = { | ||||
| 	title: "Create Next App", | ||||
| 	description: "Generated by create next app", | ||||
| 	title: "SAR Link Portal", | ||||
| 	description: "Sarlink Portal", | ||||
| }; | ||||
|  | ||||
| export default function RootLayout({ | ||||
|   | ||||
| @@ -50,7 +50,7 @@ export async function ApplicationLayout({ | ||||
| 						<AccountPopover user={session?.user} /> | ||||
| 					</div> | ||||
| 				</header> | ||||
| 				<div className="px-8 py-6">{children}</div> | ||||
| 				<div className="p-4">{children}</div> | ||||
| 			</SidebarInset> | ||||
| 		</SidebarProvider> | ||||
| 	); | ||||
|   | ||||
| @@ -33,7 +33,7 @@ export default function LoginForm() { | ||||
| 						<p className="text-red-500 text-sm">{state.message}</p> | ||||
| 					)} | ||||
| 					<Button | ||||
| 						className="dark:bg-gray-800 w-full dark:text-white" | ||||
| 						className="" | ||||
| 						disabled={isPending} | ||||
| 						type="submit" | ||||
| 					> | ||||
|   | ||||
| @@ -5,10 +5,10 @@ import Link from "next/link"; | ||||
|  | ||||
| import { signup } from "@/actions/auth-actions"; | ||||
| import { cn } from "@/lib/utils"; | ||||
| import { Loader } from "lucide-react"; | ||||
| import { useActionState } from "react"; | ||||
| import { useSearchParams } from "next/navigation"; | ||||
| import type { Island, Prisma } from "@prisma/client"; | ||||
| import { Loader } from "lucide-react"; | ||||
| import { useSearchParams } from "next/navigation"; | ||||
| import { useActionState } from "react"; | ||||
| import * as React from "react"; | ||||
|  | ||||
| import { | ||||
| @@ -20,6 +20,7 @@ import { | ||||
| 	SelectTrigger, | ||||
| 	SelectValue, | ||||
| } from "@/components/ui/select"; | ||||
| import { Checkbox } from "../ui/checkbox"; | ||||
|  | ||||
| type AtollWithIslands = Prisma.AtollGetPayload<{ | ||||
| 	include: { | ||||
| @@ -31,13 +32,16 @@ export default function SignUpForm({ atolls }: { atolls: AtollWithIslands[] }) { | ||||
| 	const [atoll, setAtoll] = React.useState<AtollWithIslands>(); | ||||
| 	const [islands, setIslands] = React.useState<Island[]>(); | ||||
|  | ||||
| 	const [actionState, action, isPending] = useActionState(signup, { | ||||
| 		message: "", | ||||
| 	}); | ||||
|  | ||||
|  | ||||
| 	React.useEffect(() => { | ||||
| 		setIslands(atoll?.islands); | ||||
| 	}, [atoll]); | ||||
|  | ||||
| 	const [actionState, action, isPending] = useActionState(signup, { | ||||
| 		message: "", | ||||
| 	}); | ||||
|  | ||||
| 	const params = useSearchParams(); | ||||
| 	const phoneNumberFromUrl = params.get("phone_number"); | ||||
| 	const NUMBER_WITHOUT_DASH = phoneNumberFromUrl?.split("-").join(""); | ||||
| @@ -83,7 +87,7 @@ export default function SignUpForm({ atolls }: { atolls: AtollWithIslands[] }) { | ||||
| 						className={cn( | ||||
| 							"text-base", | ||||
| 							actionState.errors?.fieldErrors?.id_card && | ||||
| 								"border-2 border-red-500", | ||||
| 							"border-2 border-red-500", | ||||
| 						)} | ||||
| 						placeholder="ID Card" | ||||
| 					/> | ||||
| @@ -160,26 +164,26 @@ export default function SignUpForm({ atolls }: { atolls: AtollWithIslands[] }) { | ||||
| 				</div> | ||||
|  | ||||
| 				<div> | ||||
| 					<label htmlFor="house_name" className="text-sm"> | ||||
| 						House Name | ||||
| 					<label htmlFor="address" className="text-sm"> | ||||
| 						Address | ||||
| 					</label> | ||||
| 					<Input | ||||
| 						className={cn( | ||||
| 							"text-base", | ||||
| 							actionState.errors?.fieldErrors?.house_name && | ||||
| 								"border-2 border-red-500", | ||||
| 							actionState.errors?.fieldErrors?.address && | ||||
| 							"border-2 border-red-500", | ||||
| 						)} | ||||
| 						disabled={isPending} | ||||
| 						name="house_name" | ||||
| 						name="address" | ||||
| 						defaultValue={ | ||||
| 							(actionState.payload?.get("house_name") || "") as string | ||||
| 							(actionState.payload?.get("address") || "") as string | ||||
| 						} | ||||
| 						type="text" | ||||
| 						placeholder="House Name" | ||||
| 						placeholder="Address" | ||||
| 					/> | ||||
| 					{actionState.errors?.fieldErrors?.house_name && ( | ||||
| 					{actionState.errors?.fieldErrors?.address && ( | ||||
| 						<span className="text-sm inline-block text-red-500"> | ||||
| 							{actionState.errors?.fieldErrors?.house_name} | ||||
| 							{actionState.errors?.fieldErrors?.address} | ||||
| 						</span> | ||||
| 					)} | ||||
| 				</div> | ||||
| @@ -217,8 +221,8 @@ export default function SignUpForm({ atolls }: { atolls: AtollWithIslands[] }) { | ||||
| 						disabled={isPending} | ||||
| 						className={cn( | ||||
| 							!phoneNumberFromUrl && | ||||
| 								actionState.errors?.fieldErrors?.phone_number && | ||||
| 								"border-2 border-red-500 rounded-md", | ||||
| 							actionState.errors?.fieldErrors?.phone_number && | ||||
| 							"border-2 border-red-500 rounded-md", | ||||
| 						)} | ||||
| 						defaultValue={NUMBER_WITHOUT_DASH ?? ""} | ||||
| 						readOnly={Boolean(phoneNumberFromUrl)} | ||||
| @@ -235,6 +239,51 @@ export default function SignUpForm({ atolls }: { atolls: AtollWithIslands[] }) { | ||||
| 						{actionState.message} | ||||
| 					</span> | ||||
| 				)} | ||||
| 				<div className="flex flex-col gap-2 items-start justify-start py-2"> | ||||
| 					<div className="flex gap-2 items-center"> | ||||
| 						<Checkbox | ||||
| 							defaultChecked={(actionState.payload?.get("terms") || "") as string === 'on'} | ||||
| 							name="terms" id="terms" /> | ||||
| 						<label | ||||
| 							htmlFor="terms" | ||||
| 							className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" | ||||
| 						> | ||||
| 							<span> | ||||
| 								i accept | ||||
| 							</span> | ||||
| 							<Link className="ml-1 underline" href=""> | ||||
| 								terms and conditions | ||||
| 							</Link> | ||||
| 						</label> | ||||
| 					</div> | ||||
| 					{actionState.errors?.fieldErrors?.terms && ( | ||||
| 						<span className="text-sm inline-block text-red-500"> | ||||
| 							{actionState.errors?.fieldErrors?.terms} | ||||
| 						</span> | ||||
| 					)} | ||||
| 					<div className="flex gap-2 items-center"> | ||||
|  | ||||
| 						<Checkbox | ||||
| 							name="policy" id="terms" /> | ||||
| 						<label | ||||
| 							htmlFor="terms" | ||||
| 							className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" | ||||
| 						> | ||||
| 							<span> | ||||
| 								i undertand | ||||
| 							</span> | ||||
| 							<Link className="ml-1 underline" href=""> | ||||
| 								the privacy policy | ||||
| 							</Link> | ||||
| 						</label> | ||||
| 					</div> | ||||
| 					{actionState.errors?.fieldErrors?.policy && ( | ||||
| 						<span className="text-sm inline-block text-red-500"> | ||||
| 							{actionState.errors?.fieldErrors?.policy} | ||||
| 						</span> | ||||
| 					)} | ||||
|  | ||||
| 				</div> | ||||
| 				<Button disabled={isPending} className="mt-4 w-full" type="submit"> | ||||
| 					{isPending ? <Loader className="animate-spin" /> : "Submit"} | ||||
| 				</Button> | ||||
|   | ||||
| @@ -53,7 +53,7 @@ export default function VerifyOTPForm({ | ||||
| 	return ( | ||||
| 		<form | ||||
| 			onSubmit={handleSubmit(onSubmit)} | ||||
| 			className="bg-white dark:bg-gray-900 w-full max-w-xs  rounded-lg shadow my-4" | ||||
| 			className="w-full max-w-xs  rounded-lg shadow my-4" | ||||
| 		> | ||||
| 			<div className="grid pb-4 pt-4 gap-4 px-4"> | ||||
| 				<div className=""> | ||||
| @@ -71,7 +71,7 @@ export default function VerifyOTPForm({ | ||||
| 					)} | ||||
| 				</div> | ||||
| 				<Button | ||||
| 					className="dark:bg-gray-800 w-full dark:text-white" | ||||
| 					className="w-full" | ||||
| 					disabled={isPending} | ||||
| 					type="submit" | ||||
| 				> | ||||
|   | ||||
							
								
								
									
										133
									
								
								components/devices-table.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								components/devices-table.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| import { | ||||
| 	Table, | ||||
| 	TableBody, | ||||
| 	TableCaption, | ||||
| 	TableCell, | ||||
| 	TableFooter, | ||||
| 	TableHead, | ||||
| 	TableHeader, | ||||
| 	TableRow, | ||||
| } from "@/components/ui/table"; | ||||
| import prisma from "@/lib/db"; | ||||
| import Pagination from "./pagination"; | ||||
|  | ||||
| export async function DevicesTable({ | ||||
| 	searchParams, | ||||
| }: { | ||||
| 	searchParams: Promise<{ | ||||
| 		query: string; | ||||
| 		page: number; | ||||
| 		sortBy: string; | ||||
| 	}>; | ||||
| }) { | ||||
| 	const query = (await searchParams)?.query || ""; | ||||
| 	const page = (await searchParams)?.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 totalPages = Math.ceil(totalDevices / 10); | ||||
| 	const limit = 10; | ||||
| 	const offset = (Number(page) - 1) * limit || 0; | ||||
|  | ||||
| 	const devices = await prisma.device.findMany({ | ||||
| 		where: { | ||||
| 			OR: [ | ||||
| 				{ | ||||
| 					name: { | ||||
| 						contains: query || "", | ||||
| 						mode: "insensitive", | ||||
| 					}, | ||||
| 				}, | ||||
| 				{ | ||||
| 					mac: { | ||||
| 						contains: query || "", | ||||
| 						mode: "insensitive", | ||||
| 					}, | ||||
| 				}, | ||||
|  | ||||
|  | ||||
| 			], | ||||
| 		}, | ||||
|  | ||||
| 		skip: offset, | ||||
| 		take: limit, | ||||
| 		orderBy: { | ||||
| 			name: `${sortBy}` as "asc" | "desc", | ||||
| 		}, | ||||
| 	}); | ||||
|  | ||||
|  | ||||
| 	return ( | ||||
| 		<div> | ||||
| 			{devices.length === 0 ? ( | ||||
| 				<div className="h-[calc(100svh-400px)] flex flex-col items-center justify-center my-4"> | ||||
| 					<h3>No devices yet.</h3> | ||||
| 				</div> | ||||
| 			) : ( | ||||
| 				<> | ||||
| 					<Table className="overflow-scroll"> | ||||
| 						<TableCaption>Table of all devices.</TableCaption> | ||||
| 						<TableHeader> | ||||
| 							<TableRow> | ||||
| 								<TableHead>Device Name</TableHead> | ||||
| 								<TableHead>MAC Address</TableHead> | ||||
| 								<TableHead>Status</TableHead> | ||||
| 							</TableRow> | ||||
| 						</TableHeader> | ||||
| 						<TableBody className="overflow-scroll"> | ||||
| 							{devices.map((device) => ( | ||||
| 								<TableRow | ||||
|  | ||||
| 									key={device.id} | ||||
| 								> | ||||
| 									<TableCell className="font-medium">{device.name}</TableCell> | ||||
| 									<TableCell className="font-medium">{device.mac}</TableCell> | ||||
|  | ||||
| 									<TableCell> | ||||
| 										Hi | ||||
| 										{/* <UserVerifyDialog user={user} /> */} | ||||
| 									</TableCell> | ||||
| 								</TableRow> | ||||
| 							))} | ||||
| 						</TableBody> | ||||
| 						<TableFooter> | ||||
| 							<TableRow> | ||||
| 								<TableCell colSpan={2}> | ||||
| 									{query.length > 0 && ( | ||||
| 										<p className="text-sm text-muted-foreground"> | ||||
| 											Showing {devices.length} locations for "{query} | ||||
| 											" | ||||
| 										</p> | ||||
| 									)} | ||||
| 								</TableCell> | ||||
| 								<TableCell className="text-muted-foreground"> | ||||
| 									{totalDevices} devices | ||||
| 								</TableCell> | ||||
| 							</TableRow> | ||||
| 						</TableFooter> | ||||
| 					</Table> | ||||
| 					<Pagination totalPages={totalPages} currentPage={page} /> | ||||
| 				</> | ||||
| 			)} | ||||
| 		</div> | ||||
| 	); | ||||
| } | ||||
| @@ -52,7 +52,7 @@ export default function Filter({ | ||||
| 				value={selectedOption} | ||||
| 				onValueChange={(val) => handleFilterChange(val)} | ||||
| 			> | ||||
| 				<SelectTrigger className="w-auto bg-white"> | ||||
| 				<SelectTrigger className="w-auto"> | ||||
| 					<SelectValue> | ||||
| 						{options.find((option) => option.value === selectedOption)?.label} | ||||
| 					</SelectValue> | ||||
|   | ||||
| @@ -1,11 +1,10 @@ | ||||
| "use client"; | ||||
|  | ||||
| import { Input } from "@/components/ui/input"; | ||||
| import { cn } from "@/lib/utils"; | ||||
| import { Loader } from "lucide-react"; | ||||
| import { usePathname, useRouter, useSearchParams } from "next/navigation"; | ||||
| import { useRef, useTransition } from "react"; | ||||
| import { Button } from "./ui/button"; | ||||
| import { Loader } from "lucide-react"; | ||||
|  | ||||
| export default function Search({ disabled }: { disabled?: boolean }) { | ||||
| 	const inputRef = useRef<HTMLInputElement>(null); | ||||
| @@ -32,12 +31,12 @@ export default function Search({ disabled }: { disabled?: boolean }) { | ||||
| 	} | ||||
|  | ||||
| 	return ( | ||||
| 		<div className="flex gap-2 items-center justify-end"> | ||||
| 		<div className="flex w-full gap-2 items-center justify-between"> | ||||
| 			<Input | ||||
| 				ref={inputRef} | ||||
| 				placeholder="Search..." | ||||
| 				className={cn("bg-white")} | ||||
| 				type="text" | ||||
| 				className="w-full" | ||||
| 				name="search" | ||||
| 				id="search" | ||||
| 				defaultValue={searchQuery ? searchQuery : ""} | ||||
|   | ||||
| @@ -30,10 +30,18 @@ const data = { | ||||
| 					title: "Devices", | ||||
| 					url: "/devices", | ||||
| 				}, | ||||
| 				{ | ||||
| 					title: "Parental Controls", | ||||
| 					url: "/parental-controls", | ||||
| 				}, | ||||
| 				{ | ||||
| 					title: "Payments", | ||||
| 					url: "/payments", | ||||
| 				}, | ||||
| 				{ | ||||
| 					title: "Agreements", | ||||
| 					url: "/agreements", | ||||
| 				}, | ||||
| 			], | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -65,7 +73,7 @@ export function AppSidebar({ | ||||
| 	return ( | ||||
| 		<Sidebar {...props} className="z-50"> | ||||
| 			<SidebarHeader> | ||||
| 				<h4 className="bg-gray-200 p-2 rounded shadow text-center uppercase dark:bg-gray-800"> | ||||
| 				<h4 className="p-2 rounded shadow text-center uppercase "> | ||||
| 					Sar Link Portal | ||||
| 				</h4> | ||||
| 			</SidebarHeader> | ||||
|   | ||||
| @@ -9,8 +9,8 @@ import { | ||||
| 	TableRow, | ||||
| } from "@/components/ui/table"; | ||||
| import prisma from "@/lib/db"; | ||||
| import { Badge } from "./ui/badge"; | ||||
| import Pagination from "./pagination"; | ||||
| import { Badge } from "./ui/badge"; | ||||
| import { UserVerifyDialog } from "./user/user-verify-dialog"; | ||||
|  | ||||
| export async function UsersTable({ | ||||
| @@ -139,7 +139,7 @@ export async function UsersTable({ | ||||
| 						<TableBody className="overflow-scroll"> | ||||
| 							{users.map((user) => ( | ||||
| 								<TableRow | ||||
| 									className={`${user.verified && "title-bg"}`} | ||||
| 									className={`${user.verified && "title-bg dark:bg-black"}`} | ||||
| 									key={user.id} | ||||
| 								> | ||||
| 									<TableCell className="font-medium">{user.name}</TableCell> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user