From 686d24084f204c4d45caa7fc7a0f1929b7548cd9 Mon Sep 17 00:00:00 2001 From: Rafi Arrafif Date: Tue, 10 Feb 2026 23:37:03 +0700 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=90=9B=20fix:=20forward=20browser=20c?= =?UTF-8?q?ookies=20to=20backend=20via=20nextjs=20proxy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/helpers/backendFetch.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/helpers/backendFetch.ts b/shared/helpers/backendFetch.ts index 7416c03..f41c5d2 100644 --- a/shared/helpers/backendFetch.ts +++ b/shared/helpers/backendFetch.ts @@ -30,6 +30,7 @@ export const backendFetch = async (path: string, options: RequestInit = {}) => { "Content-Type": "application/json", "x-client-info": JSON.stringify(clientInfo), Authorization: `Bearer ${process.env.BACKEND_API_KEY}`, + cookie: (await headers()).get("cookie") || "", ...options.headers, }, cache: "default", From 9f0f5e9c55bae96753404229007b294314a890ad Mon Sep 17 00:00:00 2001 From: Rafi Arrafif Date: Sat, 14 Feb 2026 21:12:56 +0700 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=92=84=20style:=20add=20logout=20conf?= =?UTF-8?q?irmation=20popup=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/helpers/backendFetch.ts | 2 +- shared/libs/shadcn/ui/alert-dialog.tsx | 184 ++++++++++++++++++ .../widgets/navbar/components/LogoutAlert.tsx | 61 ++++++ .../widgets/navbar/components/UserProfile.tsx | 19 +- 4 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 shared/libs/shadcn/ui/alert-dialog.tsx create mode 100644 shared/widgets/navbar/components/LogoutAlert.tsx diff --git a/shared/helpers/backendFetch.ts b/shared/helpers/backendFetch.ts index f41c5d2..abb0e99 100644 --- a/shared/helpers/backendFetch.ts +++ b/shared/helpers/backendFetch.ts @@ -38,7 +38,7 @@ export const backendFetch = async (path: string, options: RequestInit = {}) => { const resJson = (await res.json()) as BackendResponse; - if (!res.ok || !resJson.success) { + if (!res.ok) { throw new Error(`Elysia error: ${resJson.error}`); } diff --git a/shared/libs/shadcn/ui/alert-dialog.tsx b/shared/libs/shadcn/ui/alert-dialog.tsx new file mode 100644 index 0000000..d22b360 --- /dev/null +++ b/shared/libs/shadcn/ui/alert-dialog.tsx @@ -0,0 +1,184 @@ +"use client" + +import * as React from "react" +import { AlertDialog as AlertDialogPrimitive } from "radix-ui" + +import { cn } from "@/shared/libs/shadcn/lib/utils" +import { Button } from "@/shared/libs/shadcn/ui/button" + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + size = "default", + ...props +}: React.ComponentProps & { + size?: "default" | "sm" +}) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogMedia({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + variant = "default", + size = "default", + ...props +}: React.ComponentProps & + Pick, "variant" | "size">) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + variant = "outline", + size = "default", + ...props +}: React.ComponentProps & + Pick, "variant" | "size">) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogMedia, + AlertDialogOverlay, + AlertDialogPortal, + AlertDialogTitle, + AlertDialogTrigger, +} diff --git a/shared/widgets/navbar/components/LogoutAlert.tsx b/shared/widgets/navbar/components/LogoutAlert.tsx new file mode 100644 index 0000000..1e5b254 --- /dev/null +++ b/shared/widgets/navbar/components/LogoutAlert.tsx @@ -0,0 +1,61 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogMedia, + AlertDialogTitle, +} from "@/shared/libs/shadcn/ui/alert-dialog"; +import { Spinner } from "@/shared/libs/shadcn/ui/spinner"; +import { Button } from "@base-ui/react"; +import { LogOut } from "lucide-react"; +import React from "react"; + +const LogoutAlert = ({ + openState, + setOpenState, +}: { + openState: boolean; + setOpenState: React.Dispatch>; +}) => { + const [isLoading, setIsLoading] = React.useState(false); + + return ( + + setOpenState(false)}> + + Are you sure? + + This action will log you out of your account. You can log back in at + any time. Do you want to proceed? + + + + setOpenState(false)} + > + Cancel + + + + + + + + ); +}; + +export default LogoutAlert; diff --git a/shared/widgets/navbar/components/UserProfile.tsx b/shared/widgets/navbar/components/UserProfile.tsx index a83a307..d3eb8c0 100644 --- a/shared/widgets/navbar/components/UserProfile.tsx +++ b/shared/widgets/navbar/components/UserProfile.tsx @@ -9,6 +9,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/shared/libs/shadcn/ui/dropdown-menu"; +import { Button } from "@base-ui/react"; import { Bookmark, CircleUserRound, @@ -19,9 +20,16 @@ import { Settings, Webhook, } from "lucide-react"; +import LogoutAlert from "./LogoutAlert"; +import React from "react"; const UserProfile = () => { const { session } = useAuth(); + const [openState, setOpenState] = React.useState(false); + const triggerLogoutPopup = () => { + setOpenState(true); + }; + return (
@@ -71,13 +79,18 @@ const UserProfile = () => { - - - Log Out + + +
); }; From 36ad865c337241e1a0ab7170a0ce0eada0a23c31 Mon Sep 17 00:00:00 2001 From: Rafi Arrafif Date: Sat, 14 Feb 2026 21:37:06 +0700 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20logout=20feature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/models/auth/logout.ts | 23 +++++++++++++++++++ .../widgets/navbar/components/LogoutAlert.tsx | 11 ++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 shared/models/auth/logout.ts diff --git a/shared/models/auth/logout.ts b/shared/models/auth/logout.ts new file mode 100644 index 0000000..4e01ddb --- /dev/null +++ b/shared/models/auth/logout.ts @@ -0,0 +1,23 @@ +"use server"; + +import { backendFetch } from "@/shared/helpers/backendFetch"; +import { cookies } from "next/headers"; + +export const logout = async () => { + const res = await backendFetch("auth/logout", { + method: "POST", + }); + + if (res.success) { + (await cookies()).delete("auth_token"); + return { + success: true, + message: "Logged out successfully", + }; + } else { + return { + success: false, + message: "Logout failed", + }; + } +}; diff --git a/shared/widgets/navbar/components/LogoutAlert.tsx b/shared/widgets/navbar/components/LogoutAlert.tsx index 1e5b254..b15bd34 100644 --- a/shared/widgets/navbar/components/LogoutAlert.tsx +++ b/shared/widgets/navbar/components/LogoutAlert.tsx @@ -6,12 +6,11 @@ import { AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, - AlertDialogMedia, AlertDialogTitle, } from "@/shared/libs/shadcn/ui/alert-dialog"; import { Spinner } from "@/shared/libs/shadcn/ui/spinner"; +import { logout } from "@/shared/models/auth/logout"; import { Button } from "@base-ui/react"; -import { LogOut } from "lucide-react"; import React from "react"; const LogoutAlert = ({ @@ -22,6 +21,12 @@ const LogoutAlert = ({ setOpenState: React.Dispatch>; }) => { const [isLoading, setIsLoading] = React.useState(false); + const continueLogout = async () => { + setIsLoading(true); + await logout().then((res) => + res.success ? window.location.reload() : setIsLoading(false), + ); + }; return ( @@ -46,7 +51,7 @@ const LogoutAlert = ({