This commit is contained in:
+19
-23
@@ -7,41 +7,37 @@ const navItems = [
|
||||
{ key: "products", path: "/products" },
|
||||
{ key: "about", path: "/about" },
|
||||
{ key: "contact", path: "/contact" },
|
||||
{ key: "root", path: "/root" },
|
||||
];
|
||||
|
||||
export function Footer() {
|
||||
type FooterProps = {
|
||||
isAdmin?: boolean;
|
||||
};
|
||||
|
||||
export function Footer({ isAdmin = false }: FooterProps) {
|
||||
const locale = useLocale();
|
||||
const tNav = useTranslations("navigation");
|
||||
const tFooter = useTranslations("footer");
|
||||
|
||||
return (
|
||||
<footer className="border-t border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950">
|
||||
<footer className="border-t border-default bg-surface-soft">
|
||||
<div className="mx-auto flex w-full max-w-6xl flex-col gap-4 px-4 py-8 sm:px-6 lg:px-8">
|
||||
<nav className="flex flex-wrap gap-4 text-sm">
|
||||
{navItems.map((item) => (
|
||||
item.key === "root" ? (
|
||||
<a
|
||||
key={item.key}
|
||||
href="/root"
|
||||
className="text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-300 dark:hover:text-zinc-100"
|
||||
>
|
||||
{tNav(item.key)}
|
||||
</a>
|
||||
) : (
|
||||
<Link
|
||||
key={item.key}
|
||||
href={`/${locale}${item.path}`}
|
||||
className="text-zinc-600 transition hover:text-zinc-900 dark:text-zinc-300 dark:hover:text-zinc-100"
|
||||
>
|
||||
{tNav(item.key)}
|
||||
</Link>
|
||||
)
|
||||
<Link
|
||||
key={item.key}
|
||||
href={`/${locale}${item.path}`}
|
||||
className="text-muted transition hover:text-fg"
|
||||
>
|
||||
{tNav(item.key)}
|
||||
</Link>
|
||||
))}
|
||||
{isAdmin ? (
|
||||
<a href="/root" className="text-muted transition hover:text-fg">
|
||||
{tNav("root")}
|
||||
</a>
|
||||
) : null}
|
||||
</nav>
|
||||
<p className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{tFooter("copyright", { year: new Date().getFullYear() })}
|
||||
</p>
|
||||
<p className="text-xs text-subtle">{tFooter("copyright", { year: new Date().getFullYear() })}</p>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
|
||||
+43
-54
@@ -7,6 +7,7 @@ import { useLocale, useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
|
||||
import { ThemeToggle } from "@/components/theme-toggle";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
|
||||
const navItems = [
|
||||
{ key: "home", path: "" },
|
||||
@@ -14,21 +15,24 @@ const navItems = [
|
||||
{ key: "products", path: "/products" },
|
||||
{ key: "about", path: "/about" },
|
||||
{ key: "contact", path: "/contact" },
|
||||
{ key: "root", path: "/root" },
|
||||
];
|
||||
|
||||
export function Navbar() {
|
||||
type NavbarProps = {
|
||||
isAdmin?: boolean;
|
||||
};
|
||||
|
||||
export function Navbar({ isAdmin = false }: NavbarProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const locale = useLocale();
|
||||
const t = useTranslations("navigation");
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-40 border-b border-zinc-200 bg-white/95 backdrop-blur dark:border-zinc-800 dark:bg-zinc-950/95">
|
||||
<header className="sticky top-0 z-40 border-b border-default bg-surface backdrop-blur">
|
||||
<div className="mx-auto flex w-full max-w-6xl items-center justify-between gap-4 px-4 py-3 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center gap-3">
|
||||
<Link
|
||||
href={`/${locale}`}
|
||||
className="inline-flex items-center rounded-md border border-zinc-300 bg-white px-3 py-2 dark:border-zinc-700 dark:bg-zinc-950"
|
||||
className="inline-flex items-center rounded-md border border-input bg-card px-3 py-2 shadow-xs"
|
||||
>
|
||||
<Image
|
||||
src="/logos/light-primary.svg"
|
||||
@@ -49,80 +53,65 @@ export function Navbar() {
|
||||
</Link>
|
||||
<nav className="hidden items-center gap-5 md:flex">
|
||||
{navItems.map((item) => (
|
||||
item.key === "root" ? (
|
||||
<a
|
||||
key={item.key}
|
||||
href="/root"
|
||||
className="text-sm text-zinc-700 transition hover:text-zinc-900 dark:text-zinc-300 dark:hover:text-zinc-100"
|
||||
>
|
||||
{t(item.key)}
|
||||
</a>
|
||||
) : (
|
||||
<Link
|
||||
key={item.key}
|
||||
href={`/${locale}${item.path}`}
|
||||
className="text-sm text-zinc-700 transition hover:text-zinc-900 dark:text-zinc-300 dark:hover:text-zinc-100"
|
||||
>
|
||||
{t(item.key)}
|
||||
</Link>
|
||||
)
|
||||
<Link
|
||||
key={item.key}
|
||||
href={`/${locale}${item.path}`}
|
||||
className="text-sm text-muted transition hover:text-fg"
|
||||
>
|
||||
{t(item.key)}
|
||||
</Link>
|
||||
))}
|
||||
{isAdmin ? (
|
||||
<a href="/root" className="text-sm text-muted transition hover:text-fg">
|
||||
{t("root")}
|
||||
</a>
|
||||
) : null}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div className="hidden items-center gap-2 md:flex">
|
||||
<ThemeToggle />
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md border border-zinc-300 px-3 py-2 text-xs font-medium text-zinc-600 dark:border-zinc-700 dark:text-zinc-300"
|
||||
>
|
||||
<Button type="button" variant="outline" size="sm" className="text-xs">
|
||||
{t("languagePlaceholder")}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => setIsOpen((open) => !open)}
|
||||
className="inline-flex h-9 w-9 items-center justify-center rounded-md border border-zinc-300 text-zinc-700 md:hidden dark:border-zinc-700 dark:text-zinc-200"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="md:hidden"
|
||||
aria-label={isOpen ? t("closeMenu") : t("openMenu")}
|
||||
>
|
||||
{isOpen ? <X className="h-4 w-4" /> : <Menu className="h-4 w-4" />}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{isOpen ? (
|
||||
<div className="border-t border-zinc-200 px-4 py-4 md:hidden dark:border-zinc-800">
|
||||
<div className="border-t border-default px-4 py-4 md:hidden">
|
||||
<nav className="flex flex-col gap-3">
|
||||
{navItems.map((item) => (
|
||||
item.key === "root" ? (
|
||||
<a
|
||||
key={item.key}
|
||||
href="/root"
|
||||
className="text-sm text-zinc-700 dark:text-zinc-300"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
{t(item.key)}
|
||||
</a>
|
||||
) : (
|
||||
<Link
|
||||
key={item.key}
|
||||
href={`/${locale}${item.path}`}
|
||||
className="text-sm text-zinc-700 dark:text-zinc-300"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
{t(item.key)}
|
||||
</Link>
|
||||
)
|
||||
<Link
|
||||
key={item.key}
|
||||
href={`/${locale}${item.path}`}
|
||||
className="text-sm text-muted"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
{t(item.key)}
|
||||
</Link>
|
||||
))}
|
||||
{isAdmin ? (
|
||||
<a href="/root" className="text-sm text-muted" onClick={() => setIsOpen(false)}>
|
||||
{t("root")}
|
||||
</a>
|
||||
) : null}
|
||||
</nav>
|
||||
<div className="mt-4 flex items-center gap-2">
|
||||
<ThemeToggle />
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md border border-zinc-300 px-3 py-2 text-xs font-medium text-zinc-600 dark:border-zinc-700 dark:text-zinc-300"
|
||||
>
|
||||
<Button type="button" variant="outline" size="sm" className="text-xs">
|
||||
{t("languagePlaceholder")}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -2,6 +2,19 @@
|
||||
|
||||
import { ThemeProvider as NextThemesProvider, type ThemeProviderProps } from "next-themes";
|
||||
|
||||
const defaultThemeProps: ThemeProviderProps = {
|
||||
attribute: "class",
|
||||
defaultTheme: "system",
|
||||
enableSystem: true,
|
||||
disableTransitionOnChange: true,
|
||||
enableColorScheme: true,
|
||||
storageKey: "mohfarawati-theme",
|
||||
};
|
||||
|
||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
||||
return (
|
||||
<NextThemesProvider {...defaultThemeProps} {...props}>
|
||||
{children}
|
||||
</NextThemesProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { Moon, Sun } from "lucide-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
|
||||
export function ThemeToggle() {
|
||||
const { setTheme, theme, resolvedTheme } = useTheme();
|
||||
@@ -14,13 +15,9 @@ export function ThemeToggle() {
|
||||
|
||||
if (!mounted) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-9 w-9 items-center justify-center rounded-md border border-zinc-300 text-zinc-600 dark:border-zinc-700 dark:text-zinc-200"
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
<Button type="button" variant="outline" size="icon" aria-label="Toggle theme">
|
||||
<Moon className="h-4 w-4" />
|
||||
</button>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,13 +25,8 @@ export function ThemeToggle() {
|
||||
const isDark = activeTheme === "dark";
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTheme(isDark ? "light" : "dark")}
|
||||
className="inline-flex h-9 w-9 items-center justify-center rounded-md border border-zinc-300 text-zinc-700 transition hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-800"
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
<Button type="button" variant="outline" size="icon" onClick={() => setTheme(isDark ? "light" : "dark")} aria-label="Toggle theme">
|
||||
{isDark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
||||
</button>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user