mirror of
https://github.com/i701/sarlink-portal.git
synced 2025-07-01 09:13:57 +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:
@ -1,56 +1,54 @@
|
||||
'use client'
|
||||
import { Button } from "@/components/ui/button"
|
||||
"use client";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { Loader2, User as UserIcon } from "lucide-react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useState } from "react"
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Loader2, User as UserIcon } from "lucide-react";
|
||||
import { signOut, useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
export function AccountPopover() {
|
||||
const session = authClient.useSession();
|
||||
const [loading, setLoading] = useState(false)
|
||||
const router = useRouter()
|
||||
const session = useSession();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
if (session.isPending) {
|
||||
<Button variant={"outline"} disabled>
|
||||
<Loader2 className="animate-spin" />
|
||||
</Button>
|
||||
}
|
||||
return (
|
||||
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button className="w-fit px-2" variant="outline">
|
||||
<UserIcon />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-fit">
|
||||
<div className="grid gap-4">
|
||||
<div className="space-y-2">
|
||||
<h4 className="font-medium leading-none">{session.data?.user?.name}</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{session.data?.user?.phoneNumber}
|
||||
</p>
|
||||
</div>
|
||||
<Button disabled={loading} onClick={async () => {
|
||||
setLoading(true)
|
||||
await authClient.signOut({
|
||||
fetchOptions: {
|
||||
onSuccess: () => {
|
||||
router.push("/login"); // redirect to login page
|
||||
},
|
||||
},
|
||||
})
|
||||
setLoading(false)
|
||||
}}>
|
||||
{loading ? <Loader2 className="animate-spin" /> : "Logout"}
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
if (session.status === "loading") {
|
||||
<Button variant={"outline"} disabled>
|
||||
<Loader2 className="animate-spin" />
|
||||
</Button>;
|
||||
}
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button className="w-fit px-2" variant="outline">
|
||||
<UserIcon />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-fit">
|
||||
<div className="grid gap-4">
|
||||
<div className="space-y-2">
|
||||
<h4 className="font-medium leading-none">
|
||||
{session.data?.user?.name}
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{session.data?.user?.phoneNumber}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
disabled={loading}
|
||||
onClick={async () => {
|
||||
setLoading(true);
|
||||
await signOut();
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
{loading ? <Loader2 className="animate-spin" /> : "Logout"}
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
@ -4,33 +4,26 @@ import { Wallet } from "@/components/wallet";
|
||||
import { ModeToggle } from "@/components/theme-toggle";
|
||||
import { AppSidebar } from "@/components/ui/app-sidebar";
|
||||
|
||||
import { authOptions } from "@/app/auth";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import {
|
||||
SidebarInset,
|
||||
SidebarProvider,
|
||||
SidebarTrigger,
|
||||
} from "@/components/ui/sidebar";
|
||||
import { auth } from "@/app/auth";
|
||||
import prisma from "@/lib/db";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { headers } from "next/headers";
|
||||
import { AccountPopover } from "./account-popver";
|
||||
|
||||
export async function ApplicationLayout({
|
||||
children,
|
||||
}: { children: React.ReactNode }) {
|
||||
const session = await auth.api.getSession({
|
||||
headers: await headers(),
|
||||
});
|
||||
const billFormula = await prisma.billFormula.findFirst();
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
id: session?.user?.id,
|
||||
},
|
||||
});
|
||||
const session = await getServerSession(authOptions);
|
||||
|
||||
return (
|
||||
<SidebarProvider>
|
||||
<AppSidebar role={session?.user?.role || "USER"} />
|
||||
<DeviceCartDrawer billFormula={billFormula || null} />
|
||||
<AppSidebar role={"admin"} />
|
||||
{/* <DeviceCartDrawer billFormula={billFormula || null} /> */}
|
||||
<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">
|
||||
<div className="flex items-center gap-2 ">
|
||||
@ -44,7 +37,7 @@ export async function ApplicationLayout({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Wallet walletBalance={user?.walletBalance || 0} />
|
||||
{/* <Wallet walletBalance={user?.walletBalance || 0} /> */}
|
||||
<ModeToggle />
|
||||
<AccountPopover />
|
||||
</div>
|
||||
|
@ -3,7 +3,6 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
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 { Loader2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
@ -37,16 +36,16 @@ export default function VerifyOTPForm({
|
||||
|
||||
const onSubmit: SubmitHandler<z.infer<typeof OTPSchema>> = (data) => {
|
||||
startTransition(async () => {
|
||||
const isVerified = await authClient.phoneNumber.verify({
|
||||
phoneNumber: phone_number,
|
||||
code: data.pin,
|
||||
});
|
||||
console.log({ isVerified });
|
||||
if (!isVerified.error) {
|
||||
router.push("/devices");
|
||||
} else {
|
||||
toast.error(isVerified.error.message);
|
||||
}
|
||||
// const isVerified = await authClient.phoneNumber.verify({
|
||||
// phoneNumber: phone_number,
|
||||
// code: data.pin,
|
||||
// });
|
||||
// console.log({ isVerified });
|
||||
// if (!isVerified.error) {
|
||||
// router.push("/devices");
|
||||
// } else {
|
||||
// toast.error(isVerified.error.message);
|
||||
// }
|
||||
});
|
||||
};
|
||||
|
||||
@ -70,11 +69,7 @@ export default function VerifyOTPForm({
|
||||
<p className="text-red-500 text-sm">{errors.pin.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
className="w-full"
|
||||
disabled={isPending}
|
||||
type="submit"
|
||||
>
|
||||
<Button className="w-full" disabled={isPending} type="submit">
|
||||
{isPending ? <Loader2 className="animate-spin" /> : "Login"}
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { authOptions } from "@/app/auth";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@ -8,9 +9,7 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { auth } from "@/app/auth";
|
||||
import prisma from "@/lib/db";
|
||||
import { headers } from "next/headers";
|
||||
import { getServerSession } from "next-auth";
|
||||
import ClickableRow from "./clickable-row";
|
||||
import DeviceCard from "./device-card";
|
||||
import Pagination from "./pagination";
|
||||
@ -26,85 +25,84 @@ export async function DevicesTable({
|
||||
}>;
|
||||
parentalControl?: boolean;
|
||||
}) {
|
||||
const session = await auth.api.getSession({
|
||||
headers: await headers(),
|
||||
});
|
||||
const isAdmin = session?.user.role === "ADMIN";
|
||||
const session = await getServerSession(authOptions);
|
||||
const isAdmin = session?.user;
|
||||
const query = (await searchParams)?.query || "";
|
||||
const page = (await searchParams)?.page;
|
||||
const sortBy = (await searchParams)?.sortBy || "asc";
|
||||
const totalDevices = await prisma.device.count({
|
||||
where: {
|
||||
userId: isAdmin ? undefined : session?.session.userId,
|
||||
OR: [
|
||||
{
|
||||
name: {
|
||||
contains: query || "",
|
||||
mode: "insensitive",
|
||||
},
|
||||
},
|
||||
{
|
||||
mac: {
|
||||
contains: query || "",
|
||||
mode: "insensitive",
|
||||
},
|
||||
},
|
||||
],
|
||||
NOT: {
|
||||
payments: {
|
||||
some: {
|
||||
paid: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
isActive: isAdmin ? undefined : parentalControl,
|
||||
blocked: isAdmin
|
||||
? undefined
|
||||
: parentalControl !== undefined
|
||||
? undefined
|
||||
: false,
|
||||
},
|
||||
});
|
||||
// const totalDevices = await prisma.device.count({
|
||||
// where: {
|
||||
// userId: isAdmin ? undefined : session?.session.userId,
|
||||
// OR: [
|
||||
// {
|
||||
// name: {
|
||||
// contains: query || "",
|
||||
// mode: "insensitive",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// mac: {
|
||||
// contains: query || "",
|
||||
// mode: "insensitive",
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// NOT: {
|
||||
// payments: {
|
||||
// some: {
|
||||
// paid: false,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// isActive: isAdmin ? undefined : parentalControl,
|
||||
// blocked: isAdmin
|
||||
// ? undefined
|
||||
// : parentalControl !== undefined
|
||||
// ? undefined
|
||||
// : false,
|
||||
// },
|
||||
// });
|
||||
|
||||
const totalPages = Math.ceil(totalDevices / 10);
|
||||
// const totalPages = Math.ceil(totalDevices / 10);
|
||||
const limit = 10;
|
||||
const offset = (Number(page) - 1) * limit || 0;
|
||||
|
||||
const devices = await prisma.device.findMany({
|
||||
where: {
|
||||
userId: session?.session.userId,
|
||||
OR: [
|
||||
{
|
||||
name: {
|
||||
contains: query || "",
|
||||
mode: "insensitive",
|
||||
},
|
||||
},
|
||||
{
|
||||
mac: {
|
||||
contains: query || "",
|
||||
mode: "insensitive",
|
||||
},
|
||||
},
|
||||
],
|
||||
NOT: {
|
||||
payments: {
|
||||
some: {
|
||||
paid: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
isActive: parentalControl,
|
||||
blocked: parentalControl !== undefined ? undefined : false,
|
||||
},
|
||||
// const devices = await prisma.device.findMany({
|
||||
// where: {
|
||||
// userId: session?.session.userId,
|
||||
// OR: [
|
||||
// {
|
||||
// name: {
|
||||
// contains: query || "",
|
||||
// mode: "insensitive",
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// mac: {
|
||||
// contains: query || "",
|
||||
// mode: "insensitive",
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// NOT: {
|
||||
// payments: {
|
||||
// some: {
|
||||
// paid: false,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// isActive: parentalControl,
|
||||
// blocked: parentalControl !== undefined ? undefined : false,
|
||||
// },
|
||||
|
||||
skip: offset,
|
||||
take: limit,
|
||||
orderBy: {
|
||||
name: `${sortBy}` as "asc" | "desc",
|
||||
},
|
||||
});
|
||||
// skip: offset,
|
||||
// take: limit,
|
||||
// orderBy: {
|
||||
// name: `${sortBy}` as "asc" | "desc",
|
||||
// },
|
||||
// });
|
||||
|
||||
return null;
|
||||
return (
|
||||
<div>
|
||||
{devices.length === 0 ? (
|
||||
|
@ -2,123 +2,119 @@
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
} from "@/components/ui/drawer";
|
||||
import {
|
||||
WalletDrawerOpenAtom,
|
||||
walletTopUpValue,
|
||||
} from "@/lib/atoms";
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { WalletDrawerOpenAtom, walletTopUpValue } from "@/lib/atoms";
|
||||
import type { TopupType } from "@/lib/types";
|
||||
import { useAtom, } from "jotai";
|
||||
import {
|
||||
CircleDollarSign,
|
||||
Loader2,
|
||||
Wallet2,
|
||||
} from "lucide-react";
|
||||
import { usePathname, } from "next/navigation";
|
||||
import { useAtom } from "jotai";
|
||||
import { CircleDollarSign, Loader2, Wallet2 } from "lucide-react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import NumberInput from "./number-input";
|
||||
|
||||
|
||||
|
||||
export function Wallet({
|
||||
walletBalance,
|
||||
walletBalance,
|
||||
}: {
|
||||
walletBalance: number;
|
||||
walletBalance: number;
|
||||
}) {
|
||||
const session = authClient.useSession();
|
||||
const pathname = usePathname();
|
||||
const [amount, setAmount] = useAtom(walletTopUpValue);
|
||||
const [isOpen, setIsOpen] = useAtom(WalletDrawerOpenAtom);
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
// const router = useRouter();
|
||||
const session = useSession();
|
||||
const pathname = usePathname();
|
||||
const [amount, setAmount] = useAtom(walletTopUpValue);
|
||||
const [isOpen, setIsOpen] = useAtom(WalletDrawerOpenAtom);
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
// const router = useRouter();
|
||||
|
||||
if (pathname === "/payment") {
|
||||
return null;
|
||||
}
|
||||
if (pathname === "/payment") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data: TopupType = {
|
||||
userId: session?.data?.user.id ?? "",
|
||||
amount: Number.parseFloat(amount.toFixed(2)),
|
||||
paid: false,
|
||||
};
|
||||
const data: TopupType = {
|
||||
userId: session?.data?.user.id ?? "",
|
||||
amount: Number.parseFloat(amount.toFixed(2)),
|
||||
paid: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DrawerTrigger asChild>
|
||||
<Button onClick={() => setIsOpen(!isOpen)} variant="outline">
|
||||
{new Intl.NumberFormat('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(walletBalance)} MVR
|
||||
<Wallet2 />
|
||||
</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent>
|
||||
<div className="mx-auto w-full max-w-sm">
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>Wallet</DrawerTitle>
|
||||
<DrawerDescription asChild>
|
||||
<div>
|
||||
Your wallet balance is{" "}
|
||||
<span className="font-semibold">
|
||||
{new Intl.NumberFormat('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(walletBalance)}
|
||||
</span>{" "}
|
||||
</div>
|
||||
</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
return (
|
||||
<Drawer open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DrawerTrigger asChild>
|
||||
<Button onClick={() => setIsOpen(!isOpen)} variant="outline">
|
||||
{new Intl.NumberFormat("en-US", {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(walletBalance)}{" "}
|
||||
MVR
|
||||
<Wallet2 />
|
||||
</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent>
|
||||
<div className="mx-auto w-full max-w-sm">
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>Wallet</DrawerTitle>
|
||||
<DrawerDescription asChild>
|
||||
<div>
|
||||
Your wallet balance is{" "}
|
||||
<span className="font-semibold">
|
||||
{new Intl.NumberFormat("en-US", {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(walletBalance)}
|
||||
</span>{" "}
|
||||
</div>
|
||||
</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
|
||||
<div className="px-4 flex flex-col gap-4">
|
||||
<NumberInput
|
||||
label="Set amount to top up"
|
||||
value={amount}
|
||||
onChange={(value) => setAmount(value)}
|
||||
maxAllowed={5000}
|
||||
isDisabled={amount === 0}
|
||||
/>
|
||||
|
||||
</div>
|
||||
<DrawerFooter>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
console.log(data)
|
||||
setDisabled(true)
|
||||
// const payment = await createPayment(data)
|
||||
setDisabled(false)
|
||||
// setMonths(1)
|
||||
// if (payment) {
|
||||
// router.push(`/payments/${payment.id}`);
|
||||
// setIsOpen(!isOpen);
|
||||
// } else {
|
||||
// toast.error("Something went wrong.")
|
||||
// }
|
||||
}}
|
||||
className="w-full"
|
||||
disabled={amount === 0 || disabled}
|
||||
>
|
||||
{disabled ? (
|
||||
<>
|
||||
<Loader2 className="ml-2 animate-spin" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Go to payment
|
||||
<CircleDollarSign />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<DrawerClose asChild>
|
||||
<Button variant="outline">Cancel</Button>
|
||||
</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</div>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
<div className="px-4 flex flex-col gap-4">
|
||||
<NumberInput
|
||||
label="Set amount to top up"
|
||||
value={amount}
|
||||
onChange={(value) => setAmount(value)}
|
||||
maxAllowed={5000}
|
||||
isDisabled={amount === 0}
|
||||
/>
|
||||
</div>
|
||||
<DrawerFooter>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
console.log(data);
|
||||
setDisabled(true);
|
||||
// const payment = await createPayment(data)
|
||||
setDisabled(false);
|
||||
// setMonths(1)
|
||||
// if (payment) {
|
||||
// router.push(`/payments/${payment.id}`);
|
||||
// setIsOpen(!isOpen);
|
||||
// } else {
|
||||
// toast.error("Something went wrong.")
|
||||
// }
|
||||
}}
|
||||
className="w-full"
|
||||
disabled={amount === 0 || disabled}
|
||||
>
|
||||
{disabled ? (
|
||||
<>
|
||||
<Loader2 className="ml-2 animate-spin" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Go to payment
|
||||
<CircleDollarSign />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<DrawerClose asChild>
|
||||
<Button variant="outline">Cancel</Button>
|
||||
</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</div>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user