feat: implement Footer component and integrate it into the layout and updated header component

This commit is contained in:
2025-09-03 23:45:36 -03:00
parent 5af2d72526
commit ff9ca32d4f
4 changed files with 171 additions and 54 deletions

View File

@@ -0,0 +1,53 @@
import { Link } from "@tanstack/react-router";
export default function Footer() {
return (
<footer className="relative mt-8 mb-8">
<div className="container mx-auto max-w-7xl px-4">
<div className="relative overflow-hidden rounded-3xl border border-white/10 bg-white/5 p-5 backdrop-blur sm:p-6">
<div className="flex flex-col items-center justify-between gap-4 sm:flex-row">
<div
className={[
"flex",
"items-center",
"gap-2",
"text-xs",
"text-muted-foreground",
].join(" ")}
>
<span
aria-hidden
className="inline-block h-4 w-4 rounded bg-gradient-to-br from-purple-500 to-fuchsia-500"
/>
<span className="font-medium">Reflecto</span>
<span className="hidden sm:inline"></span>
<span className="hidden text-muted-foreground sm:inline">
Private by default
</span>
</div>
<nav
className={[
"flex",
"items-center",
"gap-4",
"text-xs",
"text-muted-foreground",
].join(" ")}
>
<Link to="/">Home</Link>
<Link to="/dashboard">Dashboard</Link>
</nav>
<div className={["text-xs", "text-muted-foreground"].join(" ")}>
<span className="lining-nums tabular-nums">
© {new Date().getFullYear()}
</span>{" "}
Reflecto
</div>
</div>
</div>
</div>
</footer>
);
}

View File

@@ -9,23 +9,105 @@ export default function Header() {
] as const;
return (
<div>
<div className="flex flex-row items-center justify-between px-2 py-1">
<nav className="flex gap-4 text-lg">
{links.map(({ to, label }) => {
return (
<Link key={to} to={to}>
{label}
<header className="sticky top-0 z-50">
{/* Subtle gradient ribbon behind the header for depth */}
<div className="absolute inset-x-0 top-0 h-24 bg-gradient-to-b from-black/30 to-transparent" />
<div className="relative mx-auto w-full max-w-7xl px-3">
<div className="mt-3 rounded-2xl border border-white/10 bg-white/5 backdrop-blur supports-[backdrop-filter]:bg-white/5">
<div className="relative flex items-center justify-between px-4 py-2">
{/* Logo + primary nav */}
<div className="flex items-center gap-4">
<Link className="inline-flex items-center gap-2" to="/">
<span
aria-hidden
className="inline-block h-6 w-6 rounded bg-gradient-to-br from-purple-500 to-fuchsia-500"
/>
<span className="font-semibold tracking-tight">Reflecto</span>
</Link>
);
})}
</nav>
<div className="flex items-center gap-2">
<ModeToggle />
<UserMenu />
<nav className="hidden items-center gap-2 sm:flex">
{links.map(({ to, label }) => (
<Link
activeProps={{ className: "text-white" }}
className={[
"rounded-lg",
"px-3",
"py-1.5",
"text-sm",
"text-muted-foreground",
"transition-colors",
"hover:text-white",
].join(" ")}
key={to}
to={to}
>
{label}
</Link>
))}
</nav>
</div>
{/* Actions */}
<div className="flex items-center gap-2">
<ModeToggle />
<UserMenu />
</div>
{/* Mobile nav (compact) */}
<nav className="flex items-center gap-2 sm:hidden">
{links.map(({ to, label }) => (
<Link
className={[
"rounded-lg",
"px-2",
"py-1",
"text-sm",
"text-muted-foreground",
"hover:text-white",
].join(" ")}
key={to}
to={to}
>
{label}
</Link>
))}
</nav>
</div>
</div>
</div>
<hr />
</div>
{/* Decorative blobs for subtle flair matching landing visuals */}
<div
className={[
"absolute",
"-z-10",
"left-0",
"top-0",
"h-40",
"w-40",
"rounded-full",
"bg-gradient-to-br",
"from-purple-500/30",
"to-fuchsia-500/20",
"blur-3xl",
].join(" ")}
/>
<div
className={[
"absolute",
"-z-10",
"right-10",
"top-0",
"h-56",
"w-56",
"rounded-full",
"bg-gradient-to-br",
"from-cyan-400/25",
"to-emerald-400/15",
"blur-3xl",
].join(" ")}
/>
</header>
);
}

View File

@@ -7,6 +7,7 @@ import {
useRouterState,
} from "@tanstack/react-router";
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
import Footer from "@/components/footer";
import Header from "@/components/header";
import Loader from "@/components/loader";
import { ThemeProvider } from "@/components/theme-provider";
@@ -14,10 +15,10 @@ import { Toaster } from "@/components/ui/sonner";
import type { trpc } from "@/utils/trpc";
import "../index.css";
export interface RouterAppContext {
export type RouterAppContext = {
trpc: typeof trpc;
queryClient: QueryClient;
}
};
export const Route = createRootRouteWithContext<RouterAppContext>()({
component: RootComponent,
@@ -54,9 +55,10 @@ function RootComponent() {
disableTransitionOnChange
storageKey="vite-ui-theme"
>
<div className="grid h-svh grid-rows-[auto_1fr]">
<div className="grid min-h-svh grid-rows-[auto_1fr_auto]">
<Header />
{isFetching ? <Loader /> : <Outlet />}
<Footer />
</div>
<Toaster richColors />
</ThemeProvider>

View File

@@ -21,16 +21,19 @@ export const Route = createFileRoute("/")({
function HealthBadge() {
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
const status = healthCheck.isLoading
? "Checking"
: healthCheck.data
? "Online"
: "Offline";
const color = healthCheck.isLoading
? "bg-yellow-500"
: healthCheck.data
? "bg-emerald-500"
: "bg-red-500";
let status = "Offline";
if (healthCheck.isLoading) {
status = "Checking";
} else if (healthCheck.data) {
status = "Online";
}
// flatten ternaries for linter by using simple logic
let color = "bg-red-500";
if (healthCheck.isLoading) {
color = "bg-yellow-500";
} else if (healthCheck.data) {
color = "bg-emerald-500";
}
return (
<div className="inline-flex items-center gap-2 rounded-full border px-3 py-1 text-muted-foreground text-xs backdrop-blur">
<span className={`h-2 w-2 rounded-full ${color}`} />
@@ -309,12 +312,9 @@ function HomeComponent() {
</Button>
</div>
<div className="mt-8 flex items-center gap-3 text-muted-foreground text-xs">
<img
alt="Reflecto"
className="h-5 w-5 rounded-sm"
height="20"
src="/logo.png"
width="20"
<span
aria-hidden
className="inline-block h-5 w-5 rounded-sm bg-gradient-to-br from-purple-500 to-fuchsia-500"
/>
<span>Private by default Powered by lightweight AI</span>
</div>
@@ -395,26 +395,6 @@ function HomeComponent() {
</div>
</div>
</section>
{/* Footer */}
<footer className="container mx-auto max-w-7xl px-4 pt-4 pb-10 text-muted-foreground text-xs">
<div className="flex flex-col items-center justify-between gap-2 sm:flex-row">
<div className="flex items-center gap-2">
<img
alt="Reflecto"
className="h-4 w-4 rounded-sm"
height="16"
src="/logo.png"
width="16"
/>
<span>Reflecto</span>
</div>
<div className="flex items-center gap-4">
<Link to="/">Home</Link>
<Link to="/dashboard">Dashboard</Link>
</div>
</div>
</footer>
</div>
</>
);