Skip to content

Commit 81024a4

Browse files
committed
feat(layout, resources): integrate sidebar and resource filters, enhance navigation structure
1 parent 695d4c6 commit 81024a4

File tree

9 files changed

+235
-11
lines changed

9 files changed

+235
-11
lines changed

src/app/layout.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import type { Metadata } from "next";
2+
23
import { Outfit, Roboto_Mono } from "next/font/google";
4+
import { Navigation, Sidebar } from "@/components/layout";
5+
import { BackToTop } from "@/components/ui";
6+
import { ResourceFilters } from "@/components/resources";
37
import { PROJECT_NAME, PROJECT_DESCRIPTION } from "@/config/constants";
4-
import { Footer, Navigation } from "@/components/layout";
58
import Providers from "./providers";
9+
610
import "../styles/globals.css";
711

812
const outfit = Outfit({
@@ -27,13 +31,17 @@ type Props = {
2731

2832
export default function RootLayout({ children }: Props) {
2933
return (
30-
<html lang="en" className="light">
34+
<html lang="en" suppressHydrationWarning>
3135
<body className={`${outfit.variable} ${robotoMono.variable} antialiased`}>
3236
<Providers>
33-
<div className="min-h-screen flex flex-col">
34-
<Navigation />
35-
{children}
36-
<Footer />
37+
<Navigation />
38+
<div className="relative mx-auto max-w-screen-2xl md:flex md:flex-row bg-white dark:bg-neutral-950">
39+
<Sidebar />
40+
<div className="w-full">
41+
<ResourceFilters />
42+
{children}
43+
<BackToTop />
44+
</div>
3745
</div>
3846
</Providers>
3947
</body>

src/components/layout/navigation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
useDisclosure,
1313
Input,
1414
} from "@heroui/react";
15-
import { ArrowBigLeft, Menu, Origami, Search, X } from "lucide-react";
15+
import { Menu, Origami, Search, X } from "lucide-react";
1616
import { ThemeSwitcher } from "@/components/ui";
1717
import { PROJECT_NAME } from "@/config/constants";
1818

src/components/layout/sidebar.tsx

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"use client";
2+
3+
import Link from "next/link";
4+
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+
];
58+
59+
export default function Sidebar() {
60+
const pathname = usePathname();
61+
const isActive = (path: string) => pathname === path;
62+
63+
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">
65+
<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">
67+
<Accordion
68+
isCompact
69+
showDivider={false}
70+
defaultExpandedKeys={sections.map((s) => s.key)}
71+
selectionMode="multiple"
72+
>
73+
{sections.map((section) => (
74+
<AccordionItem
75+
key={section.key}
76+
aria-label={section.title}
77+
title={section.title}
78+
classNames={{ title: "text-sm" }}
79+
>
80+
<ul className="flex flex-col">
81+
{section.items.map((item, index) => (
82+
<Link
83+
key={index}
84+
href={item.path}
85+
className={`text-sm pl-4 py-1 ${
86+
isActive(item.path)
87+
? "text-neutral-950 dark:text-white border-l-2 border-neutral-950 dark:border-white"
88+
: "text-neutral-500 border-l-2 border-neutral-200 dark:border-neutral-800"
89+
}`}
90+
>
91+
{item.title}
92+
</Link>
93+
))}
94+
</ul>
95+
</AccordionItem>
96+
))}
97+
</Accordion>
98+
</nav>
99+
</div>
100+
</aside>
101+
);
102+
}

src/components/resources/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { default as ResourceCard } from "./resource-card";
2+
export { default as ResourceFilters } from "./resource-filters";
23
export { default as ResourceList } from "./resource-list";

src/components/resources/resource-card.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export default function ResourceCard({ resource }: Props) {
1616
return (
1717
<Link
1818
href={`/resources/${resource.slug}`}
19-
className="rounded-xl overflow-hidden bg-white border border-gray-100"
19+
className="rounded-xl overflow-hidden bg-white dark:bg-neutral-950 border-2 border-neutral-200 dark:border-neutral-800"
2020
onMouseEnter={() => setIsHovered(true)}
2121
onMouseLeave={() => setIsHovered(false)}
2222
>
@@ -29,7 +29,7 @@ export default function ResourceCard({ resource }: Props) {
2929
className="w-full object-cover"
3030
/>
3131
{isHovered && (
32-
<div className="absolute inset-0 bg-black/50 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
32+
<div className="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
3333
<h3 className="text-white font-medium text-lg">{resource.title}</h3>
3434
</div>
3535
)}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
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+
];
11+
12+
export default function ResourceFilters() {
13+
const [selectedCategory, setSelectedCategory] = useState(new Set(["all"]));
14+
const [selectedSort, setSelectedSort] = useState(new Set(["latest"]));
15+
16+
const handleCategoryChange = (keys: any) => {
17+
setSelectedCategory(new Set(keys));
18+
};
19+
20+
const handleSortChange = (keys: any) => {
21+
setSelectedSort(new Set(keys));
22+
};
23+
24+
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
28+
size="sm"
29+
className="bg-neutral-950 dark:bg-white text-white dark:text-neutral-950 font-medium"
30+
>
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>
39+
</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>
53+
</div>
54+
);
55+
}

src/components/resources/resource-list.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default function ResourceList({ resources }: Props) {
1212
return (
1313
<section className="py-8">
1414
<div className="container mx-auto px-4">
15-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
15+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
1616
{resources.map((resource) => (
1717
<ResourceCard key={resource.id} resource={resource} />
1818
))}

src/config/constants.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
import {
2+
ChartNoAxesColumn,
3+
Scissors,
4+
Link as Link2,
5+
Globe,
6+
Folder,
7+
Gem,
8+
HeartHandshake,
9+
MousePointer2,
10+
} from "lucide-react";
11+
112
// Project
213
export const PROJECT_NAME = "Code Atlas";
314

@@ -10,6 +21,49 @@ export const PROJECT_DOMAIN = new URL(
1021
// Interface
1122
export const NAV_ITEMS = [{ key: "home", href: "/", label: "Inicio" }];
1223

24+
export const SIDEBAR_ITEMS = [
25+
{
26+
href: "/dashboard",
27+
label: "Dashboard",
28+
icon: <ChartNoAxesColumn size={20} className="flex-shrink-0" />,
29+
},
30+
{
31+
href: "/dashboard/cutter",
32+
label: "Cutter",
33+
icon: <Scissors size={20} className="flex-shrink-0" />,
34+
},
35+
{
36+
href: "/dashboard/links",
37+
label: "Links",
38+
icon: <Link2 size={20} className="flex-shrink-0" />,
39+
},
40+
{
41+
href: "/dashboard/clicks",
42+
label: "Clicks",
43+
icon: <MousePointer2 size={20} className="flex-shrink-0" />,
44+
},
45+
{
46+
href: "/dashboard/domains",
47+
label: "Domains",
48+
icon: <Globe size={20} className="flex-shrink-0" />,
49+
},
50+
{
51+
href: "/dashboard/groups",
52+
label: "Groups",
53+
icon: <Folder size={20} className="flex-shrink-0" />,
54+
},
55+
{
56+
href: "/dashboard/prices",
57+
label: "Prices",
58+
icon: <Gem size={20} className="flex-shrink-0" />,
59+
},
60+
{
61+
href: "/dashboard/support",
62+
label: "Support",
63+
icon: <HeartHandshake size={20} className="flex-shrink-0" />,
64+
},
65+
];
66+
1367
export const FOOTER_LINKS = [
1468
{ name: "Resources", href: "/resources" },
1569
{ name: "Authors", href: "/authors" },

src/lib/supabase/middleware.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import { createServerClient } from "@supabase/ssr";
22
import { NextResponse, type NextRequest } from "next/server";
33

44
const publicRoutes = ["/", "/auth/login", "/resources"];
5-
const publicRoutePrefixes = ["/resources/"];
5+
const publicRoutePrefixes = [
6+
"/resources/",
7+
"/languages/",
8+
"/frameworks-libraries/",
9+
];
610

711
export async function updateSession(request: NextRequest) {
812
let supabaseResponse = NextResponse.next({

0 commit comments

Comments
 (0)