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}
+
+
+
+
+
+
+ )
+}
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 (
+
+ );
+}
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 (
+
+ )
+}
\ No newline at end of file
diff --git a/components/user/user-verify-dialog.tsx b/components/user/user-verify-dialog.tsx
index d07b506..806bad4 100644
--- a/components/user/user-verify-dialog.tsx
+++ b/components/user/user-verify-dialog.tsx
@@ -35,7 +35,18 @@ export function UserVerifyDialog({ user }: { user: User }) {
Are you sure?
- Are you sure you want to verify {user.name}?
+ Are you sure you want to verify the following 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}
+