Refactor UI components for improved consistency and functionality

- Removed unnecessary border styles from various dashboard components including Devices, DeviceDetails, Parental Control, Payments, and Users pages to enhance visual consistency.
- Updated DevicesTable to utilize a new ClickableRow component for better device interaction and management.
- Refactored AddDevicesToCartButton to use a checkbox for selecting devices, improving user experience.
- Enhanced DeviceCard component to streamline device information display and interaction.
- Overall improvements to the layout and responsiveness of the dashboard components.

These changes enhance the user interface and improve the maintainability of the codebase.
This commit is contained in:
i701 2024-12-26 00:43:39 +05:00
parent 7acd1189ee
commit 0f17800a00
12 changed files with 196 additions and 111 deletions

View File

@ -13,7 +13,7 @@ export default async function DeviceDetails({ params }: {
}) })
return ( return (
<div> <div>
<div className="flex flex-col justify-between items-start border-b-2 text-gray-500 title-bg py-4 px-2 mb-4"> <div className="flex flex-col justify-between items-start text-gray-500 title-bg py-4 px-2 mb-4">
<h3 className='text-2xl font-bold'> <h3 className='text-2xl font-bold'>
{device?.name} {device?.name}
</h3> </h3>
@ -22,7 +22,7 @@ export default async function DeviceDetails({ params }: {
<div <div
id="user-filters" id="user-filters"
className=" border-b-2 pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start" className=" pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start"
> >
{/* <Search /> */} {/* <Search /> */}
{/* <Filter {/* <Filter

View File

@ -21,7 +21,7 @@ export default async function Devices({
const user = await getCurrentUser() const user = await getCurrentUser()
return ( return (
<div> <div>
<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"> <div className="flex justify-between items-center text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4">
<h3> <h3>
My Devices My Devices
</h3> </h3>
@ -30,13 +30,13 @@ export default async function Devices({
<div <div
id="user-filters" id="user-filters"
className=" border-b-2 pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start" className=" pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start"
> >
<Search /> <Search />
</div> </div>
<Suspense key={query} fallback={"loading...."}> <Suspense key={query} fallback={"loading...."}>
<DevicesTable searchParams={searchParams} /> <DevicesTable parentalControl={false} searchParams={searchParams} />
</Suspense> </Suspense>
</div> </div>
); );

View File

@ -18,7 +18,7 @@ export default async function ParentalControl({
const query = (await searchParams)?.query || ""; const query = (await searchParams)?.query || "";
return ( return (
<div> <div>
<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"> <div className="flex justify-between items-center text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4">
<h3> <h3>
Parental Control Parental Control
</h3> </h3>
@ -26,7 +26,7 @@ export default async function ParentalControl({
<div <div
id="user-filters" id="user-filters"
className=" border-b-2 pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start" className=" pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start"
> >
<Search /> <Search />

View File

@ -29,7 +29,7 @@ export default async function PaymentPage({
const formula = await prisma.billFormula.findFirst(); const formula = await prisma.billFormula.findFirst();
return ( return (
<div> <div>
<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"> <div className="flex justify-between items-center text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4">
<h3>Payment</h3> <h3>Payment</h3>
<span className={cn("text-sm border px-4 py-2 rounded-md uppercase font-semibold", payment?.paid ? "text-green-500 bg-green-500/20" : "text-yellow-500 bg-yellow-500/20")}> <span className={cn("text-sm border px-4 py-2 rounded-md uppercase font-semibold", payment?.paid ? "text-green-500 bg-green-500/20" : "text-yellow-500 bg-yellow-500/20")}>
{payment?.paid ? "Paid" : "Pending"} {payment?.paid ? "Paid" : "Pending"}

View File

@ -15,13 +15,13 @@ export default async function Devices({
const query = (await searchParams)?.query || ""; const query = (await searchParams)?.query || "";
return ( return (
<div> <div>
<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"> <div className="flex justify-between items-center text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4">
<h3>My Payments</h3> <h3>My Payments</h3>
</div> </div>
<div <div
id="user-filters" id="user-filters"
className=" border-b-2 pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start" className=" pb-4 gap-4 flex sm:flex-row flex-col items-start justify-start"
> >
<Search /> <Search />
</div> </div>

View File

@ -1,7 +1,7 @@
export default async function UserDevcies() { export default async function UserDevcies() {
return ( return (
<div> <div>
<h3 className="border-b-2 text-2xl font-bold title-bg py-4 px-2 mb-4"> <h3 className="text-2xl font-bold title-bg py-4 px-2 mb-4">
User Devices User Devices
</h3> </h3>
</div> </div>

View File

@ -21,7 +21,7 @@ export default async function VerifyUserPage({
}) })
return ( return (
<div> <div>
<div className='flex items-center justify-between border-b-2 text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4'> <div className='flex items-center justify-between text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4'>
<h3 className=""> <h3 className="">
Verify user Verify user
</h3> </h3>

View File

@ -38,7 +38,7 @@ export default async function AdminUsers({
return ( return (
<div> <div>
<h3 className="border-b-2 text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4"> <h3 className="text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4">
Users Users
</h3> </h3>

View File

@ -2,32 +2,24 @@
import { deviceCartAtom } from '@/lib/atoms' import { deviceCartAtom } from '@/lib/atoms'
import type { Device } from '@prisma/client' import type { Device } from '@prisma/client'
import { useAtomValue, useSetAtom } from 'jotai' import { useAtomValue, useSetAtom } from 'jotai'
import { BadgePlus, CheckCheck } from 'lucide-react'
import React from 'react' import React from 'react'
import { Button } from './ui/button'
export default function AddDevicesToCartButton({ device }: { device: Device }) { export default function AddDevicesToCartButton({ device }: { device: Device }) {
const setDeviceCart = useSetAtom(deviceCartAtom) const setDeviceCart = useSetAtom(deviceCartAtom)
const devices = useAtomValue(deviceCartAtom) const devices = useAtomValue(deviceCartAtom)
const isChecked = devices.some((d) => d.id === device.id);
return ( return (
<Button <input
className='w-full mt-2' type="checkbox"
disabled={devices.some((d) => d.id === device.id)} className='peer accent-[#f49d1b] size-4'
onClick={() => setDeviceCart((prev) => [...prev, device])} checked={isChecked}
> onChange={() => setDeviceCart((prev) =>
{devices.some((d) => d.id === device.id) ? ( isChecked
<> ? prev.filter((d) => d.id !== device.id)
Selected : [...prev, device]
<CheckCheck />
</>
) : (
<>
Select device
<BadgePlus />
</>
)} )}
</Button> />
) )
} }

View File

@ -0,0 +1,68 @@
'use client'
import {
TableCell,
TableRow
} from "@/components/ui/table";
import { deviceCartAtom } from "@/lib/atoms";
import { cn } from "@/lib/utils";
import type { Device } from "@prisma/client";
import { useAtom } from "jotai";
import Link from 'next/link';
import AddDevicesToCartButton from "./add-devices-to-cart-button";
import BlockDeviceDialog from "./block-device-dialog";
export default function ClickableRow({ device, parentalControl }: { device: Device, parentalControl?: boolean }) {
const [devices, setDeviceCart] = useAtom(deviceCartAtom)
return (
<TableRow
key={device.id}
className={cn(parentalControl === false && "cursor-pointer hover:bg-muted",)}
onClick={() => {
if (parentalControl === true) return
setDeviceCart((prev) =>
devices.some((d) => d.id === device.id)
? prev.filter((d) => d.id !== device.id)
: [...prev, device]
)
}}
>
<TableCell>
<div className="flex flex-col items-start">
<Link
className="font-medium hover:underline"
href={`/devices/${device.id}`}
onClick={(e) => e.stopPropagation()}
>
{device.name}
</Link>
<span className="text-muted-foreground">
Active until{" "}
{new Date().toLocaleDateString("en-US", {
month: "short",
day: "2-digit",
year: "numeric",
})}
</span>
{parentalControl && (
<div className="p-2 rounded border my-2">
<span>Comment: </span>
<p className="text-neutral-500">
blocked because he was watching youtube
</p>
</div>
)}
</div>
</TableCell>
<TableCell className="font-medium">{device.mac}</TableCell>
<TableCell>
{!parentalControl ? (
<AddDevicesToCartButton device={device} />
) : (
<BlockDeviceDialog device={device} />
)}
</TableCell>
</TableRow >
)
}

View File

@ -1,48 +1,75 @@
'use client'
import { deviceCartAtom } from '@/lib/atoms'
import { cn } from '@/lib/utils'
import type { Device } from '@prisma/client' import type { Device } from '@prisma/client'
import { useAtom } from 'jotai'
import Link from 'next/link' import Link from 'next/link'
import AddDevicesToCartButton from './add-devices-to-cart-button' import AddDevicesToCartButton from './add-devices-to-cart-button'
import BlockDeviceDialog from './block-device-dialog' import BlockDeviceDialog from './block-device-dialog'
import { Badge } from './ui/badge' import { Badge } from './ui/badge'
export default function DeviceCard({ device, parentalControl }: { device: Device, parentalControl?: boolean }) { export default function DeviceCard({ device, parentalControl }: { device: Device, parentalControl?: boolean }) {
return ( const [devices, setDeviceCart] = useAtom(deviceCartAtom)
<div>
<div className="flex text-sm shadow flex-col items-start p-2 border rounded border-dashed">
<div className="font-semibold flex gap-2 mb-2">
<Link
className="font-medium hover:underline"
href={`/devices/${device.id}`}
>
{device.name}
</Link>
<Badge variant={"outline"}>
<span className="font-medium">
{device.mac}
</span>
</Badge>
</div>
<span className="text-muted-foreground"> const isChecked = devices.some((d) => d.id === device.id);
Active until{" "}
{new Date().toLocaleDateString("en-US", { return (
month: "short", <div
day: "2-digit", onKeyUp={() => { }}
year: "numeric", onClick={() => {
})} if (parentalControl === true) return
</span> setDeviceCart((prev) =>
{device.blocked && ( devices.some((d) => d.id === device.id)
<div className="p-2 rounded border my-2 w-full"> ? prev.filter((d) => d.id !== device.id)
<span>Comment: </span> : [...prev, device]
<p className="text-neutral-500"> )
blocked because he was watching youtube }
</p> }
className="w-full">
<div className={cn("flex text-sm justify-between items-center my-2 p-4 border rounded-md", isChecked ? "bg-accent" : "bg-")}>
<div className=''>
<div className="font-semibold flex flex-col items-start gap-2 mb-2">
<Link
className="font-medium hover:underline"
href={`/devices/${device.id}`}
>
{device.name}
</Link>
<Badge variant={"secondary"}>
<span className="font-medium">
{device.mac}
</span>
</Badge>
</div> </div>
)}
{!parentalControl ? ( {device.isActive && (
<AddDevicesToCartButton device={device} /> <span className="text-muted-foreground">
) : ( Active until{" "}
<BlockDeviceDialog device={device} /> {new Date().toLocaleDateString("en-US", {
)} month: "short",
day: "2-digit",
year: "numeric",
})}
</span>
)}
{device.blocked && (
<div className="p-2 rounded border my-2 w-full">
<span>Comment: </span>
<p className="text-neutral-500">
blocked because he was watching youtube
</p>
</div>
)}
</div>
<div>
{!parentalControl ? (
<AddDevicesToCartButton device={device} />
) : (
<BlockDeviceDialog device={device} />
)}
</div>
</div> </div>
</div> </div>
) )

View File

@ -11,9 +11,7 @@ import {
import { auth } from "@/lib/auth"; import { auth } from "@/lib/auth";
import prisma from "@/lib/db"; import prisma from "@/lib/db";
import { headers } from "next/headers"; import { headers } from "next/headers";
import Link from "next/link"; import ClickableRow from "./clickable-row";
import AddDevicesToCartButton from "./add-devices-to-cart-button";
import BlockDeviceButton from "./block-device-dialog";
import DeviceCard from "./device-card"; import DeviceCard from "./device-card";
import Pagination from "./pagination"; import Pagination from "./pagination";
@ -56,7 +54,7 @@ export async function DevicesTable({
paid: false paid: false
} }
}, },
isActive: parentalControl ? parentalControl : undefined, isActive: parentalControl,
blocked: parentalControl !== undefined ? undefined : false, blocked: parentalControl !== undefined ? undefined : false,
}, },
}); });
@ -89,7 +87,6 @@ export async function DevicesTable({
}, },
isActive: parentalControl, isActive: parentalControl,
blocked: parentalControl !== undefined ? undefined : false, blocked: parentalControl !== undefined ? undefined : false,
}, },
skip: offset, skip: offset,
@ -114,48 +111,49 @@ export async function DevicesTable({
<TableRow> <TableRow>
<TableHead>Device Name</TableHead> <TableHead>Device Name</TableHead>
<TableHead>MAC Address</TableHead> <TableHead>MAC Address</TableHead>
<TableHead>Actions</TableHead> <TableHead>#</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody className="overflow-scroll"> <TableBody className="overflow-scroll">
{devices.map((device) => ( {devices.map((device) => (
<TableRow key={device.id}> // <TableRow key={device.id}>
<TableCell> // <TableCell>
<div className="flex flex-col items-start"> // <div className="flex flex-col items-start">
<Link // <Link
className="font-medium hover:underline" // className="font-medium hover:underline"
href={`/devices/${device.id}`} // href={`/devices/${device.id}`}
> // >
{device.name} // {device.name}
</Link> // </Link>
<span className="text-muted-foreground"> // <span className="text-muted-foreground">
Active until{" "} // Active until{" "}
{new Date().toLocaleDateString("en-US", { // {new Date().toLocaleDateString("en-US", {
month: "short", // month: "short",
day: "2-digit", // day: "2-digit",
year: "numeric", // year: "numeric",
})} // })}
</span> // </span>
{parentalControl && ( // {parentalControl && (
<div className="p-2 rounded border my-2"> // <div className="p-2 rounded border my-2">
<span>Comment: </span> // <span>Comment: </span>
<p className="text-neutral-500"> // <p className="text-neutral-500">
blocked because he was watching youtube // blocked because he was watching youtube
</p> // </p>
</div> // </div>
)} // )}
</div> // </div>
</TableCell> // </TableCell>
<TableCell className="font-medium">{device.mac}</TableCell> // <TableCell className="font-medium">{device.mac}</TableCell>
<TableCell> // <TableCell>
{!parentalControl ? ( // {!parentalControl ? (
<AddDevicesToCartButton device={device} /> // <AddDevicesToCartButton device={device} />
) : ( // ) : (
<BlockDeviceButton device={device} /> // <BlockDeviceButton device={device} />
)} // )}
</TableCell> // </TableCell>
</TableRow> // </TableRow>
<ClickableRow key={device.id} device={device} parentalControl={parentalControl} />
))} ))}
</TableBody> </TableBody>
<TableFooter> <TableFooter>