Skip to content

Commit 2eaa157

Browse files
committed
improve directory structure
1 parent 8aba92f commit 2eaa157

File tree

6 files changed

+57
-51
lines changed

6 files changed

+57
-51
lines changed

src/app/layout.test.tsx

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,6 @@ describe("UserMenu", () => {
2121
expect(initialsElement).toBeTruthy();
2222
});
2323

24-
it("shows sign out option in dropdown menu when clicked", async () => {
25-
const userEvent = (await import("@testing-library/user-event")).default;
26-
const user = userEvent.setup();
27-
const { container } = render(<UserMenu userName="Test User" />);
28-
29-
// Find and click the trigger (button with user name) within this render
30-
const trigger = container.querySelector("button");
31-
expect(trigger).toBeTruthy();
32-
if (!trigger) return;
33-
34-
await user.click(trigger);
35-
36-
// Check that sign out menu item appears
37-
const signOutItem = screen.getByText("Sign out");
38-
expect(signOutItem).toBeTruthy();
39-
});
40-
4124
it("calls signOut when sign out menu item is clicked", async () => {
4225
const userEvent = (await import("@testing-library/user-event")).default;
4326
render(<UserMenu userName="Jane Smith" />);

src/app/layout.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import type { Metadata } from "next";
22
import { Geist, Geist_Mono } from "next/font/google";
3-
import { headers } from "next/headers";
43
import { Toaster } from "sonner";
5-
import { UserMenu } from "@/components/user-menu";
6-
import { auth } from "@/lib/auth/auth";
4+
import { Navbar } from "@/components/navbar";
75
import "@/lib/api-client";
86
import "./globals.css";
97

@@ -22,25 +20,17 @@ export const metadata: Metadata = {
2220
description: "Generated by create next app",
2321
};
2422

25-
export default async function RootLayout({
23+
export default function RootLayout({
2624
children,
2725
}: Readonly<{
2826
children: React.ReactNode;
2927
}>) {
30-
const session = await auth.api.getSession({
31-
headers: await headers(),
32-
});
33-
3428
return (
3529
<html lang="en">
3630
<body
3731
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
3832
>
39-
<header className="border-b">
40-
<div className="container mx-auto px-4 py-4">
41-
{session?.user?.name && <UserMenu userName={session.user.name} />}
42-
</div>
43-
</header>
33+
<Navbar />
4434
{children}
4535
<Toaster
4636
richColors

src/components/navbar.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { headers } from "next/headers";
2+
import { UserMenu } from "@/components/user-menu";
3+
import { auth } from "@/lib/auth/auth";
4+
5+
export async function Navbar() {
6+
const session = await auth.api.getSession({
7+
headers: await headers(),
8+
});
9+
10+
return (
11+
<header className="border-b">
12+
<div className="container mx-auto px-4 py-4">
13+
{session?.user?.name && <UserMenu userName={session.user.name} />}
14+
</div>
15+
</header>
16+
);
17+
}
Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,29 @@
11
"use client";
22

3-
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
43
import { Button } from "@/components/ui/button";
54
import {
65
DropdownMenu,
76
DropdownMenuContent,
8-
DropdownMenuItem,
97
DropdownMenuTrigger,
108
} from "@/components/ui/dropdown-menu";
11-
import { signOut } from "@/lib/auth/auth-client";
9+
import { SignOutMenuItem } from "./sign-out-menu-item";
10+
import { UserAvatar } from "./user-avatar";
1211

1312
interface UserMenuProps {
1413
userName: string;
1514
}
1615

17-
function getInitials(name: string): string {
18-
return name
19-
.split(" ")
20-
.map((word) => word[0])
21-
.join("")
22-
.toUpperCase();
23-
}
24-
2516
export function UserMenu({ userName }: UserMenuProps) {
26-
const initials = getInitials(userName);
27-
28-
const handleSignOut = async () => {
29-
await signOut();
30-
};
31-
3217
return (
3318
<DropdownMenu>
3419
<DropdownMenuTrigger asChild>
3520
<Button variant="ghost" className="flex items-center gap-2">
36-
<Avatar>
37-
<AvatarFallback>{initials}</AvatarFallback>
38-
</Avatar>
21+
<UserAvatar userName={userName} />
3922
<span>{userName}</span>
4023
</Button>
4124
</DropdownMenuTrigger>
4225
<DropdownMenuContent>
43-
<DropdownMenuItem onSelect={handleSignOut}>Sign out</DropdownMenuItem>
26+
<SignOutMenuItem />
4427
</DropdownMenuContent>
4528
</DropdownMenu>
4629
);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
2+
import { signOut } from "@/lib/auth/auth-client";
3+
4+
export function SignOutMenuItem() {
5+
const handleSignOut = async () => {
6+
await signOut();
7+
};
8+
9+
return <DropdownMenuItem onSelect={handleSignOut}>Sign out</DropdownMenuItem>;
10+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
2+
3+
interface UserAvatarProps {
4+
userName: string;
5+
}
6+
7+
function getInitials(name: string): string {
8+
return name
9+
.split(" ")
10+
.map((word) => word[0])
11+
.join("")
12+
.toUpperCase();
13+
}
14+
15+
export function UserAvatar({ userName }: UserAvatarProps) {
16+
const initials = getInitials(userName);
17+
18+
return (
19+
<Avatar>
20+
<AvatarFallback>{initials}</AvatarFallback>
21+
</Avatar>
22+
);
23+
}

0 commit comments

Comments
 (0)