Skip to content

Commit

Permalink
Refactor Header and Login Screen, Add Logout Functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
NebraskaCoder committed May 17, 2024
1 parent e4c9276 commit b2e5a73
Show file tree
Hide file tree
Showing 17 changed files with 456 additions and 79 deletions.
57 changes: 21 additions & 36 deletions app/[locale]/(loggedIn)/components/layout/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import Link from "next/link";
import Image from "next/image";
import { useTranslations } from "next-intl";

import TopNavigationMenu from "./TopNavigationMenu";
import LogoutButton from "./userMenu/LogoutButton";

import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

import { HomeIcon, HamburgerMenuIcon } from "@radix-ui/react-icons";
import { HamburgerMenuIcon } from "@radix-ui/react-icons";
import UserMenu from "./userMenu/UserMenu";

const Header = () => {
const t = useTranslations("header");
const tApp = useTranslations("app");

return (
<header className="flex items-center justify-between h-16 px-6 bg-slate-800 text-white">
<div className="flex justify-start items-center gap-x-4">
<span className="hidden lg:inline">
<HomeIcon className="w-6 h-6" />
<Image
alt="Trunk-Player NG logo"
src="/images/logo.svg"
width={32}
height={32}
/>
</span>
<span className="lg:hidden">
<Button
Expand All @@ -30,34 +33,16 @@ const Header = () => {
<HamburgerMenuIcon className="w-6 h-6" />
</Button>
</span>
<h1 className="text-large font-semibold">Trunk Player</h1>
<h1 className="text-large font-semibold">{tApp("nameDisplay")}</h1>
</div>
<TopNavigationMenu />
<div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="none"
size="content"
>
<Avatar>
<AvatarFallback className="text-slate-800">TU</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuLabel className="text-center">
Trunk Player User
</DropdownMenuLabel>
<DropdownMenuSeparator />
<Link
href="/logout"
className="block px-2 py-1.5 text-sm focus:bg-slate-100 focus:text-slate-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-slate-800 dark:focus:text-slate-50"
>
Logout
</Link>
</DropdownMenuContent>
</DropdownMenu>
<UserMenu>
<LogoutButton
logoutText={t("logout.logout")}
logoutProgressMessageText={t("logout.logoutProgressMessage")}
/>
</UserMenu>
</div>
</header>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use client";

import { useState } from "react";
import { signOut } from "next-auth/react";
import LogoutDialog from "./LogoutDialog";

export interface LogoutButtonProps {
logoutText: string;
logoutProgressMessageText: string;
}

const LogoutButton = ({
logoutText,
logoutProgressMessageText,
}: LogoutButtonProps) => {
const [isDialogOpen, setIsDialogOpen] = useState(false);

const handleSignOut = async () => {
setIsDialogOpen(true);
await signOut();
setIsDialogOpen(false);
};

return (
<>
<LogoutDialog
isOpen={isDialogOpen}
logoutProgressMessageText={logoutProgressMessageText}
/>
<button
className="block w-full text-left px-2 py-1.5 text-sm focus:bg-slate-100 focus:text-slate-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-slate-800 dark:focus:text-slate-50"
onClick={handleSignOut}
>
{logoutText}
</button>
</>
);
};

export default LogoutButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Skeleton } from "@/components/ui/skeleton";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";

export interface LogoutDialogProps {
isOpen: boolean;
logoutProgressMessageText: string;
}

const LogoutDialog = ({
isOpen,
logoutProgressMessageText,
}: LogoutDialogProps) => {
return (
<Dialog open={isOpen}>
<DialogContent
hasCloseButton={false}
className="DialogContentFit"
>
<DialogHeader className="space-y-5 select-none">
<DialogTitle className="text-center">
{logoutProgressMessageText}
</DialogTitle>
<DialogDescription>
<div className="flex items-center space-x-4">
<Skeleton className="h-12 w-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
);
};

export default LogoutDialog;
52 changes: 52 additions & 0 deletions app/[locale]/(loggedIn)/components/layout/userMenu/UserMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use client";
import { useSession } from "next-auth/react";
import { useMemo, type ReactNode } from "react";
import { getUserInitials } from "@/utils/userSessionUtils";

import { Button } from "@/components/ui/button";

import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

export interface UserMenuProps {
children?: ReactNode;
}

const UserMenu = ({ children }: UserMenuProps) => {
const { data: session } = useSession();

const userName = session?.user?.name;
const userInitials = useMemo(() => getUserInitials(userName), [userName]);

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="none"
size="content"
>
<Avatar>
<AvatarFallback className="text-slate-800">
{userInitials}
</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuLabel className="text-center">
{userName}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{children}
</DropdownMenuContent>
</DropdownMenu>
);
};

export default UserMenu;
19 changes: 0 additions & 19 deletions app/[locale]/(loggedIn)/layoutComponents/sidebar/LogoImage.tsx

This file was deleted.

40 changes: 40 additions & 0 deletions app/[locale]/globals.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

.DialogOverlay {
background: rgba(0 0 0 / 0.5);
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: grid;
place-items: center;
overflow-y: auto;
z-index: 9998;
}

.DialogContent {
min-width: 300px;
width: calc(100% - 60px);
position: relative;
background: white;
margin-top: 30px;
margin-right: 30px;
margin-bottom: 30px;
margin-left: 30px;
padding: 30px;
border-radius: 4px;
z-index: 9999;
}

.DialogContentFit {
min-width: 300px;
position: relative;
background: white;
margin-top: 30px;
margin-right: 30px;
margin-bottom: 30px;
margin-left: 30px;
padding: 30px;
border-radius: 4px;
z-index: 9999;
}
32 changes: 11 additions & 21 deletions app/[locale]/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
import { createTranslator } from "next-intl";
import { notFound } from "next/navigation";
import { useTranslations } from "next-intl";
import Image from "next/image";

import LogoImage from "../(loggedIn)/layoutComponents/sidebar/LogoImage";
import Form from "./components/Form";

async function getMessages(locale: string = "en") {
try {
return (await import(`@/messages/${locale}.json`)).default;
} catch (error) {
notFound();
}
}

export default async function LoginPage({
params: { locale },
export default function LoginPage({
searchParams,
}: {
params: { [key: string]: string | undefined };
searchParams: { [key: string]: string | string[] | undefined };
}) {
const messages = await getMessages(locale);
const t = createTranslator({
locale: locale ?? "en",
messages,
namespace: "loginPage",
});
const t = useTranslations("loginPage");

const callbackUrl = Array.isArray(searchParams.callbackUrl)
? searchParams.callbackUrl[0]
Expand All @@ -35,7 +19,13 @@ export default async function LoginPage({
<div className="flex items-center justify-center px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full">
<div>
<LogoImage className="mx-auto h-48 w-auto" />
<Image
alt="Trunk-Player NG logo"
src="/images/logo.svg"
width={192}
height={192}
className="mx-auto"
/>
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
{t("header")}
</h2>
Expand Down
1 change: 1 addition & 0 deletions app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export const OPTIONS: AuthOptions = {
if (user) {
token.access_token = user.access_token;
token.apiUser = user.apiUser;
token.name = user.name;
}

return token;
Expand Down
Loading

0 comments on commit b2e5a73

Please sign in to comment.