Skip to content

Commit fcb3f82

Browse files
authored
feat: add extension list and detail pages (#64)
* feat: add extension list page * feat: add extension detail page * feat: add detail * feat: store * style: mobile styles * style: adjust styles * style: mobile styles * chore: router link
1 parent 61bfa98 commit fcb3f82

30 files changed

Lines changed: 2750 additions & 530 deletions
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"use client";
2+
3+
import { useParams } from "next/navigation";
4+
import { Suspense } from "react";
5+
6+
import IntegrationDetail from "@/components/integration/ExtensionDetail";
7+
import LoadingScreen from "@/components/LoadingScreen";
8+
import { defaultLocale } from "@/i18n/i18n";
9+
10+
export default function ExtensionDetailPage() {
11+
const params = useParams();
12+
const lang = (params.lang as string) || defaultLocale;
13+
const extensionId = params.id as string;
14+
15+
return (
16+
<Suspense fallback={<LoadingScreen />}>
17+
<IntegrationDetail lang={lang} extensionId={extensionId} />
18+
</Suspense>
19+
);
20+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"use client";
2+
3+
import { useParams } from "next/navigation";
4+
import { Suspense } from "react";
5+
6+
import ExtensionIndex from "@/components/integration/Extension";
7+
import LoadingScreen from "@/components/LoadingScreen";
8+
import { defaultLocale } from "@/i18n/i18n";
9+
10+
export default function Extension() {
11+
const params = useParams();
12+
const lang = (params.lang as string) || defaultLocale;
13+
14+
return (
15+
<Suspense fallback={<LoadingScreen />}>
16+
<ExtensionIndex lang={lang} />
17+
</Suspense>
18+
);
19+
}

app/[lang]/integration/page.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"use client";
2+
3+
import { useParams } from "next/navigation";
4+
import { Suspense } from "react";
5+
6+
import IntegrationIndex from "@/components/integration/index";
7+
import LoadingScreen from "@/components/LoadingScreen";
8+
import { defaultLocale } from "@/i18n/i18n";
9+
10+
export default function Integration() {
11+
const params = useParams();
12+
const lang = (params.lang as string) || defaultLocale;
13+
14+
return (
15+
<Suspense fallback={<LoadingScreen />}>
16+
<IntegrationIndex lang={lang} />
17+
</Suspense>
18+
);
19+
}

app/layout.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ export default function RootLayout({
2323
}: {
2424
children: React.ReactNode;
2525
}) {
26-
return (
27-
<html suppressHydrationWarning>
28-
<body suppressHydrationWarning>{children}</body>
29-
</html>
30-
);
26+
return children;
3127
}
3228

components/header/HeaderMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default function HeaderMenu({ lang }: { lang: string }) {
2323

2424
return (
2525
<div className="hidden md:flex flex-1 justify-center">
26-
<NavTab tabs={links} value={navActive} />
26+
<NavTab tabs={links} value={navActive} lang={lang} />
2727
</div>
2828
);
2929
}

components/header/MobileMenu.tsx

Lines changed: 96 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,96 @@
1-
"use client";
2-
3-
import { MenuIcon } from "lucide-react";
4-
import Image from "next/image";
5-
import Link from "next/link";
6-
import { useState } from "react";
7-
import { CgClose } from "react-icons/cg";
8-
9-
import HeaderLinks from "@/components/header/HeaderLinks";
10-
import { ALL_HEADER } from "@/data/header";
11-
import { siteConfig } from "@/data/site";
12-
import { LangSwitcher } from "./LangSwitcher";
13-
import { ThemedButton } from "./ThemedButton";
14-
15-
export default function MobileMenu({ lang }: { lang: string }) {
16-
const [isMenuOpen, setIsMenuOpen] = useState(false);
17-
18-
const links = ALL_HEADER[`HEADER_${lang.toUpperCase()}`];
19-
20-
return (
21-
<div className="md:hidden">
22-
<button
23-
aria-label="Open Menu"
24-
title="Open Menu"
25-
className="p-2 -mr-1 transition duration-200 rounded focus:outline-none focus:shadow-outline hover:bg-deep-purple-50 focus:bg-deep-purple-50"
26-
onClick={() => setIsMenuOpen(true)}
27-
>
28-
<MenuIcon />
29-
</button>
30-
{isMenuOpen && (
31-
<div className="absolute top-0 left-0 w-full z-50">
32-
<div className="p-5 bg-background border rounded shadow-sm">
33-
<div className="flex items-center justify-between mb-4">
34-
<div>
35-
<Link
36-
href="/"
37-
aria-label="Coco AI"
38-
title="Coco AI"
39-
className="inline-flex items-center"
40-
>
41-
<Image
42-
alt={siteConfig.name}
43-
src="/logo.svg"
44-
className="w-8 h-8"
45-
width={32}
46-
height={32}
47-
/>
48-
<span className="ml-2 text-xl font-bold tracking-wide text-gray-950 dark:text-gray-300">
49-
{siteConfig.name}
50-
</span>
51-
</Link>
52-
</div>
53-
<div>
54-
<button
55-
aria-label="Close Menu"
56-
title="Close Menu"
57-
className="tracking-wide transition-colors duration-200 font-normal"
58-
onClick={() => setIsMenuOpen(false)}
59-
>
60-
<CgClose />
61-
</button>
62-
</div>
63-
</div>
64-
<nav>
65-
<ul className="space-y-4 my-8">
66-
{links.map((link) => (
67-
<li key={link.label + link.value}>
68-
<Link
69-
href={link.external ? link.href : `/${lang}${link.href}`}
70-
aria-label={link.label}
71-
title={link.label}
72-
className="font-medium tracking-wide transition-colors duration-200 hover:text-deep-purple-accent-400"
73-
onClick={() => setIsMenuOpen(false)}
74-
>
75-
{link.label}
76-
</Link>
77-
</li>
78-
))}
79-
</ul>
80-
</nav>
81-
<div className="pt-4">
82-
<div className="flex items-center gap-x-5 justify-between">
83-
<HeaderLinks />
84-
<div className="flex items-center justify-end gap-x-5">
85-
<ThemedButton />
86-
<LangSwitcher lang={lang} />
87-
</div>
88-
</div>
89-
</div>
90-
</div>
91-
</div>
92-
)}
93-
</div>
94-
);
95-
}
96-
1+
"use client";
2+
3+
import { MenuIcon } from "lucide-react";
4+
import Image from "next/image";
5+
import Link from "next/link";
6+
import { useState } from "react";
7+
import { CgClose } from "react-icons/cg";
8+
9+
import HeaderLinks from "@/components/header/HeaderLinks";
10+
import { ALL_HEADER } from "@/data/header";
11+
import { siteConfig } from "@/data/site";
12+
import { LangSwitcher } from "./LangSwitcher";
13+
import { ThemedButton } from "./ThemedButton";
14+
15+
export default function MobileMenu({ lang }: { lang: string }) {
16+
const [isMenuOpen, setIsMenuOpen] = useState(false);
17+
18+
const links = ALL_HEADER[`HEADER_${lang.toUpperCase()}`];
19+
20+
return (
21+
<div className="md:hidden">
22+
<button
23+
aria-label="Open Menu"
24+
title="Open Menu"
25+
className="p-2 -mr-1 transition duration-200 rounded focus:outline-none focus:shadow-outline hover:bg-deep-purple-50 focus:bg-deep-purple-50"
26+
onClick={() => setIsMenuOpen(true)}
27+
>
28+
<MenuIcon />
29+
</button>
30+
{isMenuOpen && (
31+
<div className="absolute top-0 left-0 w-full z-50">
32+
<div className="p-5 bg-background border rounded shadow-sm">
33+
<div className="flex items-center justify-between mb-4">
34+
<div>
35+
<Link
36+
href="/"
37+
aria-label="Coco AI"
38+
title="Coco AI"
39+
className="inline-flex items-center"
40+
>
41+
<Image
42+
alt={siteConfig.name}
43+
src="/logo.svg"
44+
className="w-8 h-8"
45+
width={32}
46+
height={32}
47+
/>
48+
<span className="ml-2 text-xl font-bold tracking-wide text-gray-950 dark:text-gray-300">
49+
{siteConfig.name}
50+
</span>
51+
</Link>
52+
</div>
53+
<div>
54+
<button
55+
aria-label="Close Menu"
56+
title="Close Menu"
57+
className="tracking-wide transition-colors duration-200 font-normal"
58+
onClick={() => setIsMenuOpen(false)}
59+
>
60+
<CgClose />
61+
</button>
62+
</div>
63+
</div>
64+
<nav>
65+
<ul className="space-y-4 my-8">
66+
{links.map((link) => (
67+
<li key={link.label + link.value}>
68+
<Link
69+
href={link.external ? link.href : `/${lang}${link.href}`}
70+
aria-label={link.label}
71+
title={link.label}
72+
className="font-medium tracking-wide transition-colors duration-200 hover:text-deep-purple-accent-400"
73+
target={link.external ? "_blank" : "_self"}
74+
onClick={() => setIsMenuOpen(false)}
75+
>
76+
{link.label}
77+
</Link>
78+
</li>
79+
))}
80+
</ul>
81+
</nav>
82+
<div className="pt-4">
83+
<div className="flex items-center gap-x-5 justify-between">
84+
<HeaderLinks />
85+
<div className="flex items-center justify-end gap-x-5">
86+
<ThemedButton />
87+
<LangSwitcher lang={lang} />
88+
</div>
89+
</div>
90+
</div>
91+
</div>
92+
</div>
93+
)}
94+
</div>
95+
);
96+
}

0 commit comments

Comments
 (0)