mirror of
https://github.com/i701/sarlink-portal.git
synced 2025-04-20 03:50:20 +00:00
refactor: streamline authentication flow by removing unused code, replacing custom auth utilities with NextAuth, and updating session handling in components
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 5m56s
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 5m56s
This commit is contained in:
parent
99c5fef748
commit
ef9f032366
@ -10,6 +10,11 @@ const formSchema = z.object({
|
|||||||
.regex(/^[7|9][0-9]{2}-[0-9]{4}$/, "Please enter a valid phone number"),
|
.regex(/^[7|9][0-9]{2}-[0-9]{4}$/, "Please enter a valid phone number"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type FilterUserResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
verified: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export async function signin(previousState: ActionState, formData: FormData) {
|
export async function signin(previousState: ActionState, formData: FormData) {
|
||||||
const phoneNumber = formData.get("phoneNumber") as string;
|
const phoneNumber = formData.get("phoneNumber") as string;
|
||||||
const result = formSchema.safeParse({ phoneNumber });
|
const result = formSchema.safeParse({ phoneNumber });
|
||||||
@ -31,7 +36,28 @@ export async function signin(previousState: ActionState, formData: FormData) {
|
|||||||
const FORMATTED_MOBILE_NUMBER: string = `${phoneNumber.split("-").join("")}`;
|
const FORMATTED_MOBILE_NUMBER: string = `${phoneNumber.split("-").join("")}`;
|
||||||
console.log({ FORMATTED_MOBILE_NUMBER });
|
console.log({ FORMATTED_MOBILE_NUMBER });
|
||||||
|
|
||||||
const userExistsResponse = await fetch(
|
const user = await fetch(
|
||||||
|
`${process.env.SARLINK_API_BASE_URL}/api/auth/users/filter/?mobile=${FORMATTED_MOBILE_NUMBER}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const userData = (await user.json()) as FilterUserResponse;
|
||||||
|
if (!userData?.ok) {
|
||||||
|
return redirect(`/auth/signup?phone_number=${phoneNumber}`);
|
||||||
|
}
|
||||||
|
if (!userData.verified) {
|
||||||
|
return {
|
||||||
|
message:
|
||||||
|
"Your account is on pending verification. Please wait for a response from admin or contact shihaam.",
|
||||||
|
status: "error",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendOTPResponse = await fetch(
|
||||||
`${process.env.SARLINK_API_BASE_URL}/auth/mobile/`,
|
`${process.env.SARLINK_API_BASE_URL}/auth/mobile/`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -43,23 +69,10 @@ export async function signin(previousState: ActionState, formData: FormData) {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const userExists = await userExistsResponse.json();
|
const otpResponse = await sendOTPResponse.json();
|
||||||
console.log("user exists", userExists);
|
console.log("otpResponse", otpResponse);
|
||||||
if (userExists?.non_field_errors) {
|
|
||||||
return redirect(`/signup?phone_number=${phoneNumber}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userExists?.verified)
|
redirect(`/auth/verify-otp?phone_number=${FORMATTED_MOBILE_NUMBER}`);
|
||||||
return {
|
|
||||||
message:
|
|
||||||
"Your account is on pending verification. Please wait for a response from admin or contact shihaam.",
|
|
||||||
status: "error",
|
|
||||||
};
|
|
||||||
|
|
||||||
// await authClient.phoneNumber.sendOtp({
|
|
||||||
// phoneNumber: NUMBER_WITH_COUNTRY_CODE,
|
|
||||||
// });
|
|
||||||
redirect(`/verify-otp?phone_number=${FORMATTED_MOBILE_NUMBER}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionState = {
|
type ActionState = {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import prisma from "@/lib/db";
|
|
||||||
import type { GroupProfile, MacAddress, OmadaResponse } from "@/lib/types";
|
import type { GroupProfile, MacAddress, OmadaResponse } from "@/lib/types";
|
||||||
import { formatMacAddress } from "@/lib/utils";
|
import { formatMacAddress } from "@/lib/utils";
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
@ -124,11 +123,11 @@ export async function blockDevice({
|
|||||||
if (!macAddress) {
|
if (!macAddress) {
|
||||||
throw new Error("macAddress is a required parameter");
|
throw new Error("macAddress is a required parameter");
|
||||||
}
|
}
|
||||||
const device = await prisma.device.findFirst({
|
// const device = await prisma.device.findFirst({
|
||||||
where: {
|
// where: {
|
||||||
mac: macAddress,
|
// mac: macAddress,
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
try {
|
try {
|
||||||
const baseUrl: string = process.env.OMADA_BASE_URL || "";
|
const baseUrl: string = process.env.OMADA_BASE_URL || "";
|
||||||
const url: string = `${baseUrl}/api/v2/sites/${process.env.OMADA_SITE_ID}/cmd/clients/${formatMacAddress(macAddress)}/${type}`;
|
const url: string = `${baseUrl}/api/v2/sites/${process.env.OMADA_SITE_ID}/cmd/clients/${formatMacAddress(macAddress)}/${type}`;
|
||||||
@ -146,16 +145,16 @@ export async function blockDevice({
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
await prisma.device.update({
|
// await prisma.device.update({
|
||||||
where: {
|
// where: {
|
||||||
id: device?.id,
|
// id: device?.id,
|
||||||
},
|
// },
|
||||||
data: {
|
// data: {
|
||||||
reasonForBlocking: type === "block" ? reason : "",
|
// reasonForBlocking: type === "block" ? reason : "",
|
||||||
blocked: type === "block",
|
// blocked: type === "block",
|
||||||
blockedBy: blockedBy,
|
// blockedBy: blockedBy,
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
revalidatePath("/parental-control");
|
revalidatePath("/parental-control");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error blocking device:", error);
|
console.error("Error blocking device:", error);
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
import LoginForm from "@/components/auth/login-form";
|
import LoginForm from "@/components/auth/login-form";
|
||||||
import { auth } from "@/app/auth";
|
|
||||||
import { headers } from "next/headers";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export default async function LoginPage() {
|
export default async function LoginPage() {
|
||||||
return (
|
return (
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
|
import { authOptions } from "@/app/auth";
|
||||||
import { DevicesTable } from "@/components/devices-table";
|
import { DevicesTable } from "@/components/devices-table";
|
||||||
import Search from "@/components/search";
|
import Search from "@/components/search";
|
||||||
import AddDeviceDialogForm from "@/components/user/add-device-dialog";
|
import AddDeviceDialogForm from "@/components/user/add-device-dialog";
|
||||||
import { getCurrentUser } from "@/lib/auth-utils";
|
import { getServerSession } from "next-auth";
|
||||||
import React, { Suspense } from "react";
|
import React, { Suspense } from "react";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default async function Devices({
|
export default async function Devices({
|
||||||
searchParams,
|
searchParams,
|
||||||
}: {
|
}: {
|
||||||
@ -18,14 +16,12 @@ export default async function Devices({
|
|||||||
}>;
|
}>;
|
||||||
}) {
|
}) {
|
||||||
const query = (await searchParams)?.query || "";
|
const query = (await searchParams)?.query || "";
|
||||||
const user = await getCurrentUser()
|
const session = await getServerSession(authOptions);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
|
<div className="flex justify-between items-center border-[1px] rounded-md border-dashed font-bold title-bg py-4 px-2 mb-4">
|
||||||
<h3 className="text-sarLinkOrange text-2xl">
|
<h3 className="text-sarLinkOrange text-2xl">My Devices</h3>
|
||||||
My Devices
|
<AddDeviceDialogForm user_id={session?.user?.id} />
|
||||||
</h3>
|
|
||||||
<AddDeviceDialogForm user_id={user?.id} />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -33,7 +29,6 @@ export default async function Devices({
|
|||||||
className=" 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 parentalControl={false} searchParams={searchParams} />
|
<DevicesTable parentalControl={false} searchParams={searchParams} />
|
||||||
|
@ -1,56 +1,54 @@
|
|||||||
'use client'
|
"use client";
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover"
|
} from "@/components/ui/popover";
|
||||||
import { authClient } from "@/lib/auth-client";
|
import { Loader2, User as UserIcon } from "lucide-react";
|
||||||
import { Loader2, User as UserIcon } from "lucide-react"
|
import { signOut, useSession } from "next-auth/react";
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation";
|
||||||
import { useState } from "react"
|
import { useState } from "react";
|
||||||
|
|
||||||
export function AccountPopover() {
|
export function AccountPopover() {
|
||||||
const session = authClient.useSession();
|
const session = useSession();
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false);
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
|
|
||||||
if (session.isPending) {
|
if (session.status === "loading") {
|
||||||
<Button variant={"outline"} disabled>
|
<Button variant={"outline"} disabled>
|
||||||
<Loader2 className="animate-spin" />
|
<Loader2 className="animate-spin" />
|
||||||
</Button>
|
</Button>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
<Popover>
|
||||||
<Popover>
|
<PopoverTrigger asChild>
|
||||||
<PopoverTrigger asChild>
|
<Button className="w-fit px-2" variant="outline">
|
||||||
<Button className="w-fit px-2" variant="outline">
|
<UserIcon />
|
||||||
<UserIcon />
|
</Button>
|
||||||
</Button>
|
</PopoverTrigger>
|
||||||
</PopoverTrigger>
|
<PopoverContent className="w-fit">
|
||||||
<PopoverContent className="w-fit">
|
<div className="grid gap-4">
|
||||||
<div className="grid gap-4">
|
<div className="space-y-2">
|
||||||
<div className="space-y-2">
|
<h4 className="font-medium leading-none">
|
||||||
<h4 className="font-medium leading-none">{session.data?.user?.name}</h4>
|
{session.data?.user?.name}
|
||||||
<p className="text-sm text-muted-foreground">
|
</h4>
|
||||||
{session.data?.user?.phoneNumber}
|
<p className="text-sm text-muted-foreground">
|
||||||
</p>
|
{session.data?.user?.phoneNumber}
|
||||||
</div>
|
</p>
|
||||||
<Button disabled={loading} onClick={async () => {
|
</div>
|
||||||
setLoading(true)
|
<Button
|
||||||
await authClient.signOut({
|
disabled={loading}
|
||||||
fetchOptions: {
|
onClick={async () => {
|
||||||
onSuccess: () => {
|
setLoading(true);
|
||||||
router.push("/login"); // redirect to login page
|
await signOut();
|
||||||
},
|
setLoading(false);
|
||||||
},
|
}}
|
||||||
})
|
>
|
||||||
setLoading(false)
|
{loading ? <Loader2 className="animate-spin" /> : "Logout"}
|
||||||
}}>
|
</Button>
|
||||||
{loading ? <Loader2 className="animate-spin" /> : "Logout"}
|
</div>
|
||||||
</Button>
|
</PopoverContent>
|
||||||
</div>
|
</Popover>
|
||||||
</PopoverContent>
|
);
|
||||||
</Popover>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -4,33 +4,26 @@ import { Wallet } from "@/components/wallet";
|
|||||||
import { ModeToggle } from "@/components/theme-toggle";
|
import { ModeToggle } from "@/components/theme-toggle";
|
||||||
import { AppSidebar } from "@/components/ui/app-sidebar";
|
import { AppSidebar } from "@/components/ui/app-sidebar";
|
||||||
|
|
||||||
|
import { authOptions } from "@/app/auth";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import {
|
import {
|
||||||
SidebarInset,
|
SidebarInset,
|
||||||
SidebarProvider,
|
SidebarProvider,
|
||||||
SidebarTrigger,
|
SidebarTrigger,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { auth } from "@/app/auth";
|
import { getServerSession } from "next-auth";
|
||||||
import prisma from "@/lib/db";
|
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import { AccountPopover } from "./account-popver";
|
import { AccountPopover } from "./account-popver";
|
||||||
|
|
||||||
export async function ApplicationLayout({
|
export async function ApplicationLayout({
|
||||||
children,
|
children,
|
||||||
}: { children: React.ReactNode }) {
|
}: { children: React.ReactNode }) {
|
||||||
const session = await auth.api.getSession({
|
const session = await getServerSession(authOptions);
|
||||||
headers: await headers(),
|
|
||||||
});
|
|
||||||
const billFormula = await prisma.billFormula.findFirst();
|
|
||||||
const user = await prisma.user.findFirst({
|
|
||||||
where: {
|
|
||||||
id: session?.user?.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return (
|
return (
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
<AppSidebar role={session?.user?.role || "USER"} />
|
<AppSidebar role={"admin"} />
|
||||||
<DeviceCartDrawer billFormula={billFormula || null} />
|
{/* <DeviceCartDrawer billFormula={billFormula || null} /> */}
|
||||||
<SidebarInset>
|
<SidebarInset>
|
||||||
<header className="flex justify-between sticky top-0 bg-background h-16 shrink-0 items-center gap-2 border-b px-4 z-10">
|
<header className="flex justify-between sticky top-0 bg-background h-16 shrink-0 items-center gap-2 border-b px-4 z-10">
|
||||||
<div className="flex items-center gap-2 ">
|
<div className="flex items-center gap-2 ">
|
||||||
@ -44,7 +37,7 @@ export async function ApplicationLayout({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Wallet walletBalance={user?.walletBalance || 0} />
|
{/* <Wallet walletBalance={user?.walletBalance || 0} /> */}
|
||||||
<ModeToggle />
|
<ModeToggle />
|
||||||
<AccountPopover />
|
<AccountPopover />
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { authClient } from "@/lib/auth-client";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@ -37,16 +36,16 @@ export default function VerifyOTPForm({
|
|||||||
|
|
||||||
const onSubmit: SubmitHandler<z.infer<typeof OTPSchema>> = (data) => {
|
const onSubmit: SubmitHandler<z.infer<typeof OTPSchema>> = (data) => {
|
||||||
startTransition(async () => {
|
startTransition(async () => {
|
||||||
const isVerified = await authClient.phoneNumber.verify({
|
// const isVerified = await authClient.phoneNumber.verify({
|
||||||
phoneNumber: phone_number,
|
// phoneNumber: phone_number,
|
||||||
code: data.pin,
|
// code: data.pin,
|
||||||
});
|
// });
|
||||||
console.log({ isVerified });
|
// console.log({ isVerified });
|
||||||
if (!isVerified.error) {
|
// if (!isVerified.error) {
|
||||||
router.push("/devices");
|
// router.push("/devices");
|
||||||
} else {
|
// } else {
|
||||||
toast.error(isVerified.error.message);
|
// toast.error(isVerified.error.message);
|
||||||
}
|
// }
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,11 +69,7 @@ export default function VerifyOTPForm({
|
|||||||
<p className="text-red-500 text-sm">{errors.pin.message}</p>
|
<p className="text-red-500 text-sm">{errors.pin.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button className="w-full" disabled={isPending} type="submit">
|
||||||
className="w-full"
|
|
||||||
disabled={isPending}
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
{isPending ? <Loader2 className="animate-spin" /> : "Login"}
|
{isPending ? <Loader2 className="animate-spin" /> : "Login"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { authOptions } from "@/app/auth";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@ -8,9 +9,7 @@ import {
|
|||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { auth } from "@/app/auth";
|
import { getServerSession } from "next-auth";
|
||||||
import prisma from "@/lib/db";
|
|
||||||
import { headers } from "next/headers";
|
|
||||||
import ClickableRow from "./clickable-row";
|
import ClickableRow from "./clickable-row";
|
||||||
import DeviceCard from "./device-card";
|
import DeviceCard from "./device-card";
|
||||||
import Pagination from "./pagination";
|
import Pagination from "./pagination";
|
||||||
@ -26,85 +25,84 @@ export async function DevicesTable({
|
|||||||
}>;
|
}>;
|
||||||
parentalControl?: boolean;
|
parentalControl?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const session = await auth.api.getSession({
|
const session = await getServerSession(authOptions);
|
||||||
headers: await headers(),
|
const isAdmin = session?.user;
|
||||||
});
|
|
||||||
const isAdmin = session?.user.role === "ADMIN";
|
|
||||||
const query = (await searchParams)?.query || "";
|
const query = (await searchParams)?.query || "";
|
||||||
const page = (await searchParams)?.page;
|
const page = (await searchParams)?.page;
|
||||||
const sortBy = (await searchParams)?.sortBy || "asc";
|
const sortBy = (await searchParams)?.sortBy || "asc";
|
||||||
const totalDevices = await prisma.device.count({
|
// const totalDevices = await prisma.device.count({
|
||||||
where: {
|
// where: {
|
||||||
userId: isAdmin ? undefined : session?.session.userId,
|
// userId: isAdmin ? undefined : session?.session.userId,
|
||||||
OR: [
|
// OR: [
|
||||||
{
|
// {
|
||||||
name: {
|
// name: {
|
||||||
contains: query || "",
|
// contains: query || "",
|
||||||
mode: "insensitive",
|
// mode: "insensitive",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
mac: {
|
// mac: {
|
||||||
contains: query || "",
|
// contains: query || "",
|
||||||
mode: "insensitive",
|
// mode: "insensitive",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
NOT: {
|
// NOT: {
|
||||||
payments: {
|
// payments: {
|
||||||
some: {
|
// some: {
|
||||||
paid: false,
|
// paid: false,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
isActive: isAdmin ? undefined : parentalControl,
|
// isActive: isAdmin ? undefined : parentalControl,
|
||||||
blocked: isAdmin
|
// blocked: isAdmin
|
||||||
? undefined
|
// ? undefined
|
||||||
: parentalControl !== undefined
|
// : parentalControl !== undefined
|
||||||
? undefined
|
// ? undefined
|
||||||
: false,
|
// : false,
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
const totalPages = Math.ceil(totalDevices / 10);
|
// const totalPages = Math.ceil(totalDevices / 10);
|
||||||
const limit = 10;
|
const limit = 10;
|
||||||
const offset = (Number(page) - 1) * limit || 0;
|
const offset = (Number(page) - 1) * limit || 0;
|
||||||
|
|
||||||
const devices = await prisma.device.findMany({
|
// const devices = await prisma.device.findMany({
|
||||||
where: {
|
// where: {
|
||||||
userId: session?.session.userId,
|
// userId: session?.session.userId,
|
||||||
OR: [
|
// OR: [
|
||||||
{
|
// {
|
||||||
name: {
|
// name: {
|
||||||
contains: query || "",
|
// contains: query || "",
|
||||||
mode: "insensitive",
|
// mode: "insensitive",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
mac: {
|
// mac: {
|
||||||
contains: query || "",
|
// contains: query || "",
|
||||||
mode: "insensitive",
|
// mode: "insensitive",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
NOT: {
|
// NOT: {
|
||||||
payments: {
|
// payments: {
|
||||||
some: {
|
// some: {
|
||||||
paid: false,
|
// paid: false,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
isActive: parentalControl,
|
// isActive: parentalControl,
|
||||||
blocked: parentalControl !== undefined ? undefined : false,
|
// blocked: parentalControl !== undefined ? undefined : false,
|
||||||
},
|
// },
|
||||||
|
|
||||||
skip: offset,
|
// skip: offset,
|
||||||
take: limit,
|
// take: limit,
|
||||||
orderBy: {
|
// orderBy: {
|
||||||
name: `${sortBy}` as "asc" | "desc",
|
// name: `${sortBy}` as "asc" | "desc",
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
return null;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{devices.length === 0 ? (
|
{devices.length === 0 ? (
|
||||||
|
@ -2,123 +2,119 @@
|
|||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
DrawerClose,
|
DrawerClose,
|
||||||
DrawerContent,
|
DrawerContent,
|
||||||
DrawerDescription,
|
DrawerDescription,
|
||||||
DrawerFooter,
|
DrawerFooter,
|
||||||
DrawerHeader,
|
DrawerHeader,
|
||||||
DrawerTitle,
|
DrawerTitle,
|
||||||
DrawerTrigger,
|
DrawerTrigger,
|
||||||
} from "@/components/ui/drawer";
|
} from "@/components/ui/drawer";
|
||||||
import {
|
import { WalletDrawerOpenAtom, walletTopUpValue } from "@/lib/atoms";
|
||||||
WalletDrawerOpenAtom,
|
|
||||||
walletTopUpValue,
|
|
||||||
} from "@/lib/atoms";
|
|
||||||
import { authClient } from "@/lib/auth-client";
|
|
||||||
import type { TopupType } from "@/lib/types";
|
import type { TopupType } from "@/lib/types";
|
||||||
import { useAtom, } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import {
|
import { CircleDollarSign, Loader2, Wallet2 } from "lucide-react";
|
||||||
CircleDollarSign,
|
import { useSession } from "next-auth/react";
|
||||||
Loader2,
|
import { usePathname } from "next/navigation";
|
||||||
Wallet2,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { usePathname, } from "next/navigation";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import NumberInput from "./number-input";
|
import NumberInput from "./number-input";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function Wallet({
|
export function Wallet({
|
||||||
walletBalance,
|
walletBalance,
|
||||||
}: {
|
}: {
|
||||||
walletBalance: number;
|
walletBalance: number;
|
||||||
}) {
|
}) {
|
||||||
const session = authClient.useSession();
|
const session = useSession();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const [amount, setAmount] = useAtom(walletTopUpValue);
|
const [amount, setAmount] = useAtom(walletTopUpValue);
|
||||||
const [isOpen, setIsOpen] = useAtom(WalletDrawerOpenAtom);
|
const [isOpen, setIsOpen] = useAtom(WalletDrawerOpenAtom);
|
||||||
const [disabled, setDisabled] = useState(false);
|
const [disabled, setDisabled] = useState(false);
|
||||||
// const router = useRouter();
|
// const router = useRouter();
|
||||||
|
|
||||||
if (pathname === "/payment") {
|
if (pathname === "/payment") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: TopupType = {
|
const data: TopupType = {
|
||||||
userId: session?.data?.user.id ?? "",
|
userId: session?.data?.user.id ?? "",
|
||||||
amount: Number.parseFloat(amount.toFixed(2)),
|
amount: Number.parseFloat(amount.toFixed(2)),
|
||||||
paid: false,
|
paid: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer open={isOpen} onOpenChange={setIsOpen}>
|
<Drawer open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DrawerTrigger asChild>
|
<DrawerTrigger asChild>
|
||||||
<Button onClick={() => setIsOpen(!isOpen)} variant="outline">
|
<Button onClick={() => setIsOpen(!isOpen)} variant="outline">
|
||||||
{new Intl.NumberFormat('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(walletBalance)} MVR
|
{new Intl.NumberFormat("en-US", {
|
||||||
<Wallet2 />
|
minimumFractionDigits: 2,
|
||||||
</Button>
|
maximumFractionDigits: 2,
|
||||||
</DrawerTrigger>
|
}).format(walletBalance)}{" "}
|
||||||
<DrawerContent>
|
MVR
|
||||||
<div className="mx-auto w-full max-w-sm">
|
<Wallet2 />
|
||||||
<DrawerHeader>
|
</Button>
|
||||||
<DrawerTitle>Wallet</DrawerTitle>
|
</DrawerTrigger>
|
||||||
<DrawerDescription asChild>
|
<DrawerContent>
|
||||||
<div>
|
<div className="mx-auto w-full max-w-sm">
|
||||||
Your wallet balance is{" "}
|
<DrawerHeader>
|
||||||
<span className="font-semibold">
|
<DrawerTitle>Wallet</DrawerTitle>
|
||||||
{new Intl.NumberFormat('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(walletBalance)}
|
<DrawerDescription asChild>
|
||||||
</span>{" "}
|
<div>
|
||||||
</div>
|
Your wallet balance is{" "}
|
||||||
</DrawerDescription>
|
<span className="font-semibold">
|
||||||
</DrawerHeader>
|
{new Intl.NumberFormat("en-US", {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
}).format(walletBalance)}
|
||||||
|
</span>{" "}
|
||||||
|
</div>
|
||||||
|
</DrawerDescription>
|
||||||
|
</DrawerHeader>
|
||||||
|
|
||||||
<div className="px-4 flex flex-col gap-4">
|
<div className="px-4 flex flex-col gap-4">
|
||||||
<NumberInput
|
<NumberInput
|
||||||
label="Set amount to top up"
|
label="Set amount to top up"
|
||||||
value={amount}
|
value={amount}
|
||||||
onChange={(value) => setAmount(value)}
|
onChange={(value) => setAmount(value)}
|
||||||
maxAllowed={5000}
|
maxAllowed={5000}
|
||||||
isDisabled={amount === 0}
|
isDisabled={amount === 0}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
<DrawerFooter>
|
||||||
<DrawerFooter>
|
<Button
|
||||||
<Button
|
onClick={async () => {
|
||||||
onClick={async () => {
|
console.log(data);
|
||||||
console.log(data)
|
setDisabled(true);
|
||||||
setDisabled(true)
|
// const payment = await createPayment(data)
|
||||||
// const payment = await createPayment(data)
|
setDisabled(false);
|
||||||
setDisabled(false)
|
// setMonths(1)
|
||||||
// setMonths(1)
|
// if (payment) {
|
||||||
// if (payment) {
|
// router.push(`/payments/${payment.id}`);
|
||||||
// router.push(`/payments/${payment.id}`);
|
// setIsOpen(!isOpen);
|
||||||
// setIsOpen(!isOpen);
|
// } else {
|
||||||
// } else {
|
// toast.error("Something went wrong.")
|
||||||
// toast.error("Something went wrong.")
|
// }
|
||||||
// }
|
}}
|
||||||
}}
|
className="w-full"
|
||||||
className="w-full"
|
disabled={amount === 0 || disabled}
|
||||||
disabled={amount === 0 || disabled}
|
>
|
||||||
>
|
{disabled ? (
|
||||||
{disabled ? (
|
<>
|
||||||
<>
|
<Loader2 className="ml-2 animate-spin" />
|
||||||
<Loader2 className="ml-2 animate-spin" />
|
</>
|
||||||
</>
|
) : (
|
||||||
) : (
|
<>
|
||||||
<>
|
Go to payment
|
||||||
Go to payment
|
<CircleDollarSign />
|
||||||
<CircleDollarSign />
|
</>
|
||||||
</>
|
)}
|
||||||
)}
|
</Button>
|
||||||
</Button>
|
<DrawerClose asChild>
|
||||||
<DrawerClose asChild>
|
<Button variant="outline">Cancel</Button>
|
||||||
<Button variant="outline">Cancel</Button>
|
</DrawerClose>
|
||||||
</DrawerClose>
|
</DrawerFooter>
|
||||||
</DrawerFooter>
|
</div>
|
||||||
</div>
|
</DrawerContent>
|
||||||
</DrawerContent>
|
</Drawer>
|
||||||
</Drawer>
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
"use server";
|
|
||||||
import { headers } from "next/headers";
|
|
||||||
import { cache } from "react";
|
|
||||||
import { auth } from "../app/auth";
|
|
||||||
|
|
||||||
const getCurrentUserCache = cache(async () => {
|
|
||||||
const session = await auth.api.getSession({
|
|
||||||
headers: await headers(),
|
|
||||||
});
|
|
||||||
return session?.user;
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function getCurrentUser() {
|
|
||||||
return await getCurrentUserCache();
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user