Skip to content

Commit 2ccff0a

Browse files
committed
feat(auth, layout): enhance login page with social login buttons, update layout components, and improve sidebar functionality
1 parent 7139a49 commit 2ccff0a

File tree

7 files changed

+142
-101
lines changed

7 files changed

+142
-101
lines changed

src/app/(public)/auth/login/page.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import { login, signup } from "@/actions/auth";
1+
"use client";
2+
3+
import { GoogleButton, GitHubButton } from "@/components/auth";
24

35
export default function LoginPage() {
46
return (
5-
<form>
6-
<label htmlFor="email">Email:</label>
7-
<input id="email" name="email" type="email" required />
8-
<label htmlFor="password">Password:</label>
9-
<input id="password" name="password" type="password" required />
10-
<button formAction={login}>Log in</button>
11-
<button formAction={signup}>Sign up</button>
12-
</form>
7+
<main className="max-w-md mx-auto flex flex-col gap-4 my-4 p-10 rounded-xl border-2 border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-950">
8+
<header className="border-b-2 border-neutral-200 dark:border-neutral-800 pb-6">
9+
<h1 className="text-2xl font-black">Sign up or Log in</h1>
10+
</header>
11+
<section className="flex flex-col gap-2">
12+
<GoogleButton />
13+
<GitHubButton />
14+
</section>
15+
</main>
1316
);
1417
}

src/app/layout.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import type { Metadata } from "next";
1+
import type { Metadata, Viewport } from "next";
22

33
import { Outfit, Roboto_Mono } from "next/font/google";
4-
import { Navigation, Sidebar } from "@/components/layout";
4+
import { Navbar, Sidebar } from "@/components/layout";
55
import { BackToTop } from "@/components/ui";
66
import { ResourceFilters } from "@/components/resources";
77
import { PROJECT_NAME, PROJECT_DESCRIPTION } from "@/config/constants";
@@ -20,11 +20,19 @@ const robotoMono = Roboto_Mono({
2020
subsets: ["latin"],
2121
display: "swap",
2222
});
23+
2324
export const metadata: Metadata = {
2425
title: `Homepage - ${PROJECT_NAME}`,
2526
description: `${PROJECT_DESCRIPTION}`,
2627
};
2728

29+
export const viewport: Viewport = {
30+
width: "device-width",
31+
initialScale: 1,
32+
maximumScale: 1,
33+
userScalable: false,
34+
};
35+
2836
type Props = {
2937
children: Readonly<React.ReactNode>;
3038
};
@@ -34,14 +42,14 @@ export default function RootLayout({ children }: Props) {
3442
<html lang="en" suppressHydrationWarning>
3543
<body className={`${outfit.variable} ${robotoMono.variable} antialiased`}>
3644
<Providers>
37-
<Navigation />
3845
<div className="relative mx-auto max-w-screen-2xl md:flex md:flex-row bg-white dark:bg-neutral-950">
3946
<Sidebar />
4047
<div className="w-full">
48+
<Navbar />
4149
<ResourceFilters />
4250
{children}
43-
<BackToTop />
4451
</div>
52+
<BackToTop />
4553
</div>
4654
</Providers>
4755
</body>

src/app/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { ResourceList } from "@/components/resources";
33

44
export default async function HomePage() {
55
const resources = await getResources();
6+
if (resources.length === 0) return <div>No resources found</div>;
67

78
return (
8-
<main className="flex flex-col min-h-screen overflow-y-scroll mt-8">
9+
<main className="flex flex-col min-h-screen">
910
<ResourceList resources={resources} />
1011
</main>
1112
);

src/components/layout/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export { default as Navbar } from "./navbar";
12
export { default as Navigation } from "./navigation";
23
export { default as Sidebar } from "./sidebar";

src/components/layout/navbar.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"use client";
2+
3+
import Link from "next/link";
4+
import { useRouter } from "next/navigation";
5+
import { Button, Divider } from "@heroui/react";
6+
import { ChevronLeft } from "lucide-react";
7+
import { Breadcrumbs, ThemeSwitcher } from "@/components/ui";
8+
import { useAuthUser } from "@/hooks/use-auth-user";
9+
import { UserProfile } from "@/components/auth";
10+
11+
export default function Navbar() {
12+
const router = useRouter();
13+
const { user } = useAuthUser();
14+
15+
return (
16+
<nav className="sticky top-0 z-20 flex items-center justify-between gap-2 px-4 py-4 bg-white dark:bg-neutral-950">
17+
<div className="flex items-center gap-4">
18+
<Button
19+
isIconOnly
20+
size="sm"
21+
variant="bordered"
22+
onPress={() => router.back()}
23+
>
24+
<ChevronLeft size={20} />
25+
</Button>
26+
<Divider orientation="vertical" className="w-0.5 h-4 border-8 mx-3" />
27+
<Breadcrumbs />
28+
</div>
29+
<div className="flex items-center gap-2">
30+
<ThemeSwitcher />
31+
<Divider orientation="vertical" className="w-0.5 h-4 border-8 mx-3" />
32+
{user ? (
33+
<UserProfile />
34+
) : (
35+
<>
36+
<Button as={Link} href="/auth/login" variant="bordered" size="sm">
37+
Log In
38+
</Button>
39+
<Button
40+
as={Link}
41+
href="/auth/login"
42+
variant="solid"
43+
size="sm"
44+
className="bg-neutral-950 dark:bg-white text-white dark:text-neutral-950 font-medium"
45+
>
46+
Sign Up
47+
</Button>
48+
</>
49+
)}
50+
</div>
51+
</nav>
52+
);
53+
}

src/components/layout/sidebar.tsx

Lines changed: 37 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,75 +2,55 @@
22

33
import Link from "next/link";
44
import { usePathname } from "next/navigation";
5-
import { Accordion, AccordionItem } from "@heroui/react";
6-
7-
const sections = [
8-
{
9-
key: "4fbcd2fc-14cd-4048-83c5-c683ccd8c212",
10-
title: "Explore",
11-
items: [
12-
{ title: "Resources", path: "/resources" },
13-
{ title: "Authors", path: "/authors" },
14-
],
15-
},
16-
{
17-
key: "4fbcd2fc-14cd-4048-83c5-c683ccd8c216",
18-
title: "Languages",
19-
items: [
20-
{ title: "Python", path: "/languages/python" },
21-
{ title: "JavaScript", path: "/languages/javascript" },
22-
{ title: "TypeScript", path: "/languages/typescript" },
23-
{ title: "Ruby", path: "/languages/ruby" },
24-
{ title: "PHP", path: "/languages/php" },
25-
{ title: "Go", path: "/languages/go" },
26-
{ title: "C#", path: "/languages/csharp" },
27-
{ title: "C++", path: "/languages/cpp" },
28-
{ title: "Java", path: "/languages/java" },
29-
{ title: "Kotlin", path: "/languages/kotlin" },
30-
{ title: "Swift", path: "/languages/swift" },
31-
{ title: "Rust", path: "/languages/rust" },
32-
{ title: "Elixir", path: "/languages/elixir" },
33-
],
34-
},
35-
{
36-
key: "b0f0c9a0-a4c0-4a5f-b3a8-e0a2f5a1c2b1",
37-
title: "Frameworks & Libraries",
38-
items: [
39-
{ title: "Django", path: "/frameworks-libraries/django" },
40-
{ title: "Flask", path: "/frameworks-libraries/flask" },
41-
{ title: "FastAPI", path: "/frameworks-libraries/fastapi" },
42-
{ title: "Express", path: "/frameworks-libraries/express" },
43-
{ title: "Laravel", path: "/frameworks-libraries/laravel" },
44-
{ title: "Ruby on Rails", path: "/frameworks-libraries/ruby-on-rails" },
45-
{ title: "Next.js", path: "/frameworks-libraries/nextjs" },
46-
{ title: "Nuxt.js", path: "/frameworks-libraries/nuxtjs" },
47-
{ title: "Gatsby", path: "/frameworks-libraries/gatsby" },
48-
{ title: "SvelteKit", path: "/frameworks-libraries/sveltekit" },
49-
{ title: "Nest.js", path: "/frameworks-libraries/nestjs" },
50-
{ title: "Sails.js", path: "/frameworks-libraries/sailsjs" },
51-
{ title: "Koa", path: "/frameworks-libraries/koa" },
52-
{ title: "Hapi", path: "/frameworks-libraries/hapi" },
53-
{ title: "Feathers", path: "/frameworks-libraries/feathers" },
54-
{ title: "Strapi", path: "/frameworks-libraries/strapi" },
55-
],
56-
},
57-
];
5+
import { Accordion, AccordionItem, Input, Kbd } from "@heroui/react";
6+
import { Origami, Search } from "lucide-react";
7+
import { PROJECT_NAME, SECTIONS } from "@/config/constants";
588

599
export default function Sidebar() {
6010
const pathname = usePathname();
6111
const isActive = (path: string) => pathname === path;
6212

6313
return (
64-
<aside className="sticky top-12 pl-4 pr-2 pt-4 hidden h-[calc(100vh-60px)] w-[260px] md:flex md:shrink-0 md:flex-col md:justify-between bg-white dark:bg-neutral-950 border-r-2 border-neutral-200 dark:border-neutral-800">
14+
<aside className="sticky top-0 pl-4 pr-2 pb-4 pt-4 hidden h-[100vh] w-[260px] md:flex md:shrink-0 md:flex-col md:gap-4 bg-white dark:bg-neutral-950 border-r-2 border-neutral-200 dark:border-neutral-800">
15+
<header>
16+
<Link href="/" className="flex gap-1">
17+
<Origami size={24} />
18+
<span className="font-bold ml-2">{PROJECT_NAME.toUpperCase()}</span>
19+
</Link>
20+
</header>
21+
<Input
22+
size="sm"
23+
radius="sm"
24+
variant="bordered"
25+
placeholder="Search..."
26+
// value={search}
27+
// onChange={(e) => setSearch(e.target.value)}
28+
className="w-full"
29+
classNames={{
30+
inputWrapper:
31+
"border-2 border-neutral-200 dark:border-neutral-800 text-neutral-950 dark:text-white shadow-none",
32+
}}
33+
startContent={<Search size={20} />}
34+
endContent={
35+
<Kbd
36+
keys={["command"]}
37+
classNames={{
38+
base: "bg-neutral-200 dark:bg-neutral-800 px-1 py-0 rounded-sm text-xs",
39+
}}
40+
>
41+
K
42+
</Kbd>
43+
}
44+
/>
6545
<div className="overflow-hidden relative">
66-
<nav className="styled-scrollbar flex h-[calc(100vh-80px)] flex-col overflow-y-scroll pb-4 pr-2 dark:text-white">
46+
<nav className="styled-scrollbar flex h-[calc(100vh-60px)] flex-col overflow-y-scroll pb-4 pr-2 dark:text-white">
6747
<Accordion
6848
isCompact
6949
showDivider={false}
70-
defaultExpandedKeys={sections.map((s) => s.key)}
50+
defaultExpandedKeys={SECTIONS.map((s) => s.key)}
7151
selectionMode="multiple"
7252
>
73-
{sections.map((section) => (
53+
{SECTIONS.map((section) => (
7454
<AccordionItem
7555
key={section.key}
7656
aria-label={section.title}

src/components/resources/resource-filters.tsx

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@
22

33
import { useState } from "react";
44
import { Button, Select, SelectItem } from "@heroui/react";
5-
6-
const sortOptions = [
7-
{ key: "latest", label: "Recientes" },
8-
{ key: "popular", label: "Populares" },
9-
{ key: "top", label: "Mejor valorados" },
10-
];
5+
import { SORT_OPTIONS } from "@/config/constants";
116

127
export default function ResourceFilters() {
138
const [selectedCategory, setSelectedCategory] = useState(new Set(["all"]));
@@ -22,34 +17,34 @@ export default function ResourceFilters() {
2217
};
2318

2419
return (
25-
<div className="sticky z-20 top-12 flex items-center justify-between gap-2 px-4 py-2 bg-white dark:bg-neutral-950 border-b-2 border-neutral-200 dark:border-neutral-800">
26-
<div className="flex items-center gap-2">
27-
<Button
20+
<div className="sticky z-10 top-0 flex items-center justify-between gap-2 px-4 py-4 bg-white dark:bg-neutral-950 border-b-2 border-neutral-200 dark:border-neutral-800">
21+
<header className="flex items-center gap-2">
22+
<h1 className="font-medium text-xl">Page Title</h1>
23+
<span className="text-xs text-neutral-500">(123)</span>
24+
</header>
25+
<div className="flex justify-end items-center gap-2 w-96">
26+
<div className="w-40">
27+
<Select
28+
size="sm"
29+
variant="bordered"
30+
label="Sort by"
31+
labelPlacement="outside-left"
32+
selectedKeys={selectedSort}
33+
onSelectionChange={handleSortChange}
34+
className="max-w-[400px]"
35+
>
36+
{SORT_OPTIONS.map((opt) => (
37+
<SelectItem key={opt.key}>{opt.label}</SelectItem>
38+
))}
39+
</Select>
40+
</div>
41+
{/* <Button
2842
size="sm"
2943
className="bg-neutral-950 dark:bg-white text-white dark:text-neutral-950 font-medium"
3044
>
31-
Tag Feature
32-
</Button>
33-
<Button size="sm" variant="bordered">
34-
Tag 1
35-
</Button>
36-
<Button size="sm" variant="bordered">
37-
Tag 2
38-
</Button>
45+
Add New
46+
</Button> */}
3947
</div>
40-
<Select
41-
size="sm"
42-
variant="bordered"
43-
label="Sort by"
44-
labelPlacement="outside-left"
45-
selectedKeys={selectedSort}
46-
onSelectionChange={handleSortChange}
47-
className="max-w-[160px]"
48-
>
49-
{sortOptions.map((opt) => (
50-
<SelectItem key={opt.key}>{opt.label}</SelectItem>
51-
))}
52-
</Select>
5348
</div>
5449
);
5550
}

0 commit comments

Comments
 (0)