diff --git a/app/(dashboard)/agreements/page.tsx b/app/(dashboard)/agreements/page.tsx new file mode 100644 index 0000000..9047c29 --- /dev/null +++ b/app/(dashboard)/agreements/page.tsx @@ -0,0 +1,7 @@ +import React from 'react' + +export default function Agreements() { + return ( +
Agreements
+ ) +} diff --git a/app/(dashboard)/devices/page.tsx b/app/(dashboard)/devices/page.tsx index a3756a9..c61076a 100644 --- a/app/(dashboard)/devices/page.tsx +++ b/app/(dashboard)/devices/page.tsx @@ -1,7 +1,25 @@ import { DevicesTable } from "@/components/devices-table"; +import Filter from "@/components/filter"; import Search from "@/components/search"; -import { Button } from "@/components/ui/button"; +import AddDeviceDialogForm from "@/components/user/add-device-dialog"; +import { getCurrentUser } from "@/lib/auth-utils"; +import { AArrowDown, AArrowUp } from "lucide-react"; import React, { Suspense } from "react"; + +const sortfilterOptions = [ + { + value: 'asc', + label: 'Ascending', + icon: , + }, + { + value: 'desc', + label: 'Descending', + icon: , + }, +] + + export default async function Devices({ searchParams, }: { @@ -12,14 +30,15 @@ export default async function Devices({ status: string; }>; }) { - + const query = (await searchParams)?.query || ""; + const user = await getCurrentUser() return (

My Devices

- +
- +
- +
diff --git a/app/(dashboard)/user-devices/page.tsx b/app/(dashboard)/user-devices/page.tsx index 1761967..ab2aaf6 100644 --- a/app/(dashboard)/user-devices/page.tsx +++ b/app/(dashboard)/user-devices/page.tsx @@ -1,54 +1,9 @@ -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 async function UserDevcies() { return (

- My Devices + User Devices

-
- - , - }, - { - value: "unverified", - label: "Unverfieid", - icon: , - }, - { - value: "verified", - label: "Verified", - icon: , - }, - ]} - defaultOption="all" - queryParamKey="status" - /> -
- - -
); } diff --git a/app/(dashboard)/users/[userId]/verify/page.tsx b/app/(dashboard)/users/[userId]/verify/page.tsx new file mode 100644 index 0000000..06a39cc --- /dev/null +++ b/app/(dashboard)/users/[userId]/verify/page.tsx @@ -0,0 +1,68 @@ +import InputReadOnly from '@/components/input-read-only'; +import { Badge } from '@/components/ui/badge'; +import UserRejectDialog from '@/components/user/user-reject-dialog'; +import { UserVerifyDialog } from '@/components/user/user-verify-dialog'; + +import prisma from '@/lib/db'; +import React from 'react' + +export default async function VerifyUserPage({ + params, +}: { + params: Promise<{ + userId: string; + }>; +}) { + const userId = (await params).userId + const dbUser = await prisma.user.findUnique({ + where: { + id: userId, + }, + }) + return ( +
+
+

+ Verify user +

+
+ {dbUser && !dbUser?.verified && } + {dbUser && !dbUser?.verified && } + {dbUser?.verified && Verified} +
+ +
+
+
+

Database Information

+
+ + + + + +
+
+
+

National Information

+
+ + + + + +
+
+
+ +
+ ) +} diff --git a/app/(dashboard)/users/page.tsx b/app/(dashboard)/users/page.tsx index e859d1b..360c380 100644 --- a/app/(dashboard)/users/page.tsx +++ b/app/(dashboard)/users/page.tsx @@ -2,8 +2,28 @@ import Filter from "@/components/filter"; import Search from "@/components/search"; import { UsersTable } from "@/components/user-table"; import { AdminAuthGuard } from "@/lib/auth-guard"; -import { CheckCheck, Hourglass, Minus } from "lucide-react"; +import { + AArrowDown, + AArrowUp, + CheckCheck, + Hourglass, + Minus, +} from "lucide-react"; import React, { Suspense } from "react"; + +const sortfilterOptions = [ + { + value: "asc", + label: "Ascending", + icon: , + }, + { + value: "desc", + label: "Descending", + icon: , + }, +]; + export default async function AdminUsers({ searchParams, }: { @@ -21,35 +41,39 @@ export default async function AdminUsers({

Users

-
-
- - , - }, - { - value: "unverified", - label: "Unverfieid", - icon: , - }, - { - value: "verified", - label: "Verified", - icon: , - }, - ]} - defaultOption="all" - queryParamKey="status" - /> -
+
+ + , + }, + { + value: "unverified", + label: "Unverfieid", + icon: , + }, + { + value: "verified", + label: "Verified", + icon: , + }, + ]} + defaultOption="all" + queryParamKey="status" + /> + +
diff --git a/components/auth/account-popver.tsx b/components/auth/account-popver.tsx index f715547..966f2e9 100644 --- a/components/auth/account-popver.tsx +++ b/components/auth/account-popver.tsx @@ -6,12 +6,11 @@ import { PopoverTrigger, } from "@/components/ui/popover" import { authClient } from "@/lib/auth-client"; -import type { User } from "@prisma/client"; import { Loader2, User as UserIcon } from "lucide-react" import { useRouter } from "next/navigation" import { useState } from "react" -export function AccountPopover({ user }: { user?: User }) { +export function AccountPopover() { const session = authClient.useSession(); const [loading, setLoading] = useState(false) const router = useRouter() @@ -36,7 +35,6 @@ export function AccountPopover({ user }: { user?: User }) {

{session.data?.user?.phoneNumber}

- {user?.address}
{/*
diff --git a/components/auth/verify-otp-form.tsx b/components/auth/verify-otp-form.tsx index 9cd9aba..c65d40e 100644 --- a/components/auth/verify-otp-form.tsx +++ b/components/auth/verify-otp-form.tsx @@ -5,7 +5,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { authClient } from "@/lib/auth-client"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Loader } from "lucide-react"; +import { Loader2 } from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useTransition } from "react"; @@ -75,7 +75,7 @@ export default function VerifyOTPForm({ disabled={isPending} type="submit" > - {isPending ? : "Login"} + {isPending ? : "Login"}
diff --git a/components/devices-table.tsx b/components/devices-table.tsx index f2a65bb..345c21c 100644 --- a/components/devices-table.tsx +++ b/components/devices-table.tsx @@ -103,8 +103,7 @@ export async function DevicesTable({ {device.mac} - Hi - {/* */} + Parental Controls ))} diff --git a/components/input-read-only.tsx b/components/input-read-only.tsx new file mode 100644 index 0000000..06af355 --- /dev/null +++ b/components/input-read-only.tsx @@ -0,0 +1,20 @@ +import React from 'react' + +export default function InputReadOnly({ label, value }: { label: string, value?: string }) { + return ( +
+ + +
+ + ) +} diff --git a/components/search.tsx b/components/search.tsx index 962f9a6..75db22c 100644 --- a/components/search.tsx +++ b/components/search.tsx @@ -1,7 +1,7 @@ "use client"; import { Input } from "@/components/ui/input"; -import { Loader } from "lucide-react"; +import { Loader2 } from "lucide-react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useRef, useTransition } from "react"; import { Button } from "./ui/button"; @@ -36,7 +36,7 @@ export default function Search({ disabled }: { disabled?: boolean }) { ref={inputRef} placeholder="Search..." type="text" - className="w-full" + className="w-fit" name="search" id="search" defaultValue={searchQuery ? searchQuery : ""} @@ -53,7 +53,7 @@ export default function Search({ disabled }: { disabled?: boolean }) { replace(pathname); }} > - {isPending ? : "Reset"} + {isPending ? : "Reset"}
); diff --git a/components/user-table.tsx b/components/user-table.tsx index fae9a41..2ec9f72 100644 --- a/components/user-table.tsx +++ b/components/user-table.tsx @@ -9,9 +9,10 @@ import { TableRow, } from "@/components/ui/table"; import prisma from "@/lib/db"; +import Link from "next/link"; import Pagination from "./pagination"; import { Badge } from "./ui/badge"; -import { UserVerifyDialog } from "./user/user-verify-dialog"; +import { Button } from "./ui/button"; export async function UsersTable({ searchParams, @@ -43,7 +44,7 @@ export async function UsersTable({ }, }, { - house_name: { + address: { contains: query || "", mode: "insensitive", }, @@ -57,6 +58,7 @@ export async function UsersTable({ ], verified: verified === "all" ? undefined : verified === "verified", }, + }); const totalPages = Math.ceil(totalUsers / 10); @@ -79,7 +81,7 @@ export async function UsersTable({ }, }, { - house_name: { + address: { contains: query || "", mode: "insensitive", }, @@ -92,6 +94,7 @@ export async function UsersTable({ }, ], verified: verified === "all" ? undefined : verified === "verified", + }, include: { island: true, @@ -100,7 +103,7 @@ export async function UsersTable({ skip: offset, take: limit, orderBy: { - name: `${sortBy}` as "asc" | "desc", + id: `${sortBy}` as "asc" | "desc", }, }); @@ -146,7 +149,7 @@ export async function UsersTable({ {user.id_card} {user.atoll?.name} {user.island?.name} - {user.house_name} + {user.address} {user.verified ? ( @@ -175,7 +178,11 @@ export async function UsersTable({ {user.phoneNumber} - + + + ))} diff --git a/components/user/add-device-dialog.tsx b/components/user/add-device-dialog.tsx new file mode 100644 index 0000000..fb3ea56 --- /dev/null +++ b/components/user/add-device-dialog.tsx @@ -0,0 +1,134 @@ +"use client"; + +import { AddDevice } from "@/actions/user-actions"; +import { Button } from "@/components/ui/button"; + +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { Loader2, Plus } from "lucide-react"; +import { useState } from "react"; +import { type SubmitHandler, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; + +export default function AddDeviceDialogForm({ user_id }: { user_id?: string }) { + + const formSchema = z.object({ + name: z.string().min(2, { message: "Name is required." }), + mac_address: z + .string() + .min(2, { message: "MAC Address is required." }) + .regex( + /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/, + "Please enter a valid MAC address", + ), + }); + + const [disabled, setDisabled] = useState(false); + const [open, setOpen] = useState(false); + const { + register, + handleSubmit, + formState: { errors }, + } = useForm>({ + resolver: zodResolver(formSchema), + }); + + if (!user_id) { + return null + } + + const onSubmit: SubmitHandler> = (data) => { + console.log(data); + setDisabled(true) + toast.promise(AddDevice({ mac_address: data.mac_address, name: data.name, user_id: user_id }), { + loading: 'Adding new device...', + success: () => { + setDisabled(false) + setOpen((prev) => !prev) + return 'Device successfully added!' + }, + error: (error) => { + setDisabled(false) + return error || 'Something went wrong.' + }, + }) + }; + + + + return ( + + + + + + + New Device + + To add a new device, enter the device name and mac address below. Click save when you are done. + + +
+
+
+
+ + + + {errors.name?.message} + +
+ +
+ + + + {errors.mac_address?.message} + +
+
+
+ + + +
+
+
+ ); +} diff --git a/components/user/user-reject-dialog.tsx b/components/user/user-reject-dialog.tsx new file mode 100644 index 0000000..a772a46 --- /dev/null +++ b/components/user/user-reject-dialog.tsx @@ -0,0 +1,111 @@ +"use client" + +import { Rejectuser } from "@/actions/user-actions" +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { Label } from "@/components/ui/label" +import { cn } from "@/lib/utils" +import { zodResolver } from "@hookform/resolvers/zod" +import type { User } from "@prisma/client" +import { UserX } from "lucide-react" +import { useState } from "react" +import { type SubmitHandler, useForm } from "react-hook-form" +import { toast } from "sonner" +import { z } from "zod" +import { Textarea } from "../ui/textarea" + + + +const validationSchema = z.object({ + reason: z.string().min(5, { message: "Reason is required" }), +}) + +export default function UserRejectDialog({ user }: { user: User }) { + const [disabled, setDisabled] = useState(false) + const [open, setOpen] = useState(false) + const { + register, + handleSubmit, + formState: { errors }, + } = useForm>({ + resolver: zodResolver(validationSchema), + }) + + const onSubmit: SubmitHandler> = (data) => { + setDisabled(true) + console.log(data) + toast.promise(Rejectuser({ + userId: user.id, + reason: data.reason, + }), { + loading: "Rejecting...", + success: () => { + setDisabled(false) + setOpen((prev) => !prev) + return "Rejected!" + }, + error: (error) => { + setDisabled(false) + return error || "Something went wrong" + }, + }) + setDisabled(false) + + } + + return ( + + + + + + + Are you sure you want to {" "} + reject{" "} + this user? + +
  • Name: {user.name}
  • +
  • ID Card: {user.id_card}
  • +
  • Address: {user.address}
  • +
  • DOB: {new Date(user.dob ?? "").toLocaleDateString("en-US", { + month: "short", + day: "2-digit", + year: "numeric", + })} +
  • +
  • Phone Number: {user.phoneNumber}
  • +
    +
    +
    +
    +
    + +