mirror of
https://github.com/FranP-code/Reflecto.git
synced 2025-10-13 00:43:31 +00:00
feat: implement Footer component and integrate it into the layout and updated header component
This commit is contained in:
53
apps/web/src/components/footer.tsx
Normal file
53
apps/web/src/components/footer.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,23 +9,105 @@ export default function Header() {
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<header className="sticky top-0 z-50">
|
||||||
<div className="flex flex-row items-center justify-between px-2 py-1">
|
{/* Subtle gradient ribbon behind the header for depth */}
|
||||||
<nav className="flex gap-4 text-lg">
|
<div className="absolute inset-x-0 top-0 h-24 bg-gradient-to-b from-black/30 to-transparent" />
|
||||||
{links.map(({ to, label }) => {
|
|
||||||
return (
|
<div className="relative mx-auto w-full max-w-7xl px-3">
|
||||||
<Link key={to} to={to}>
|
<div className="mt-3 rounded-2xl border border-white/10 bg-white/5 backdrop-blur supports-[backdrop-filter]:bg-white/5">
|
||||||
{label}
|
<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>
|
</Link>
|
||||||
);
|
|
||||||
})}
|
<nav className="hidden items-center gap-2 sm:flex">
|
||||||
</nav>
|
{links.map(({ to, label }) => (
|
||||||
<div className="flex items-center gap-2">
|
<Link
|
||||||
<ModeToggle />
|
activeProps={{ className: "text-white" }}
|
||||||
<UserMenu />
|
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>
|
||||||
</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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
useRouterState,
|
useRouterState,
|
||||||
} from "@tanstack/react-router";
|
} from "@tanstack/react-router";
|
||||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||||
|
import Footer from "@/components/footer";
|
||||||
import Header from "@/components/header";
|
import Header from "@/components/header";
|
||||||
import Loader from "@/components/loader";
|
import Loader from "@/components/loader";
|
||||||
import { ThemeProvider } from "@/components/theme-provider";
|
import { ThemeProvider } from "@/components/theme-provider";
|
||||||
@@ -14,10 +15,10 @@ import { Toaster } from "@/components/ui/sonner";
|
|||||||
import type { trpc } from "@/utils/trpc";
|
import type { trpc } from "@/utils/trpc";
|
||||||
import "../index.css";
|
import "../index.css";
|
||||||
|
|
||||||
export interface RouterAppContext {
|
export type RouterAppContext = {
|
||||||
trpc: typeof trpc;
|
trpc: typeof trpc;
|
||||||
queryClient: QueryClient;
|
queryClient: QueryClient;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const Route = createRootRouteWithContext<RouterAppContext>()({
|
export const Route = createRootRouteWithContext<RouterAppContext>()({
|
||||||
component: RootComponent,
|
component: RootComponent,
|
||||||
@@ -54,9 +55,10 @@ function RootComponent() {
|
|||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
storageKey="vite-ui-theme"
|
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 />
|
<Header />
|
||||||
{isFetching ? <Loader /> : <Outlet />}
|
{isFetching ? <Loader /> : <Outlet />}
|
||||||
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
<Toaster richColors />
|
<Toaster richColors />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
@@ -21,16 +21,19 @@ export const Route = createFileRoute("/")({
|
|||||||
|
|
||||||
function HealthBadge() {
|
function HealthBadge() {
|
||||||
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
||||||
const status = healthCheck.isLoading
|
let status = "Offline";
|
||||||
? "Checking"
|
if (healthCheck.isLoading) {
|
||||||
: healthCheck.data
|
status = "Checking";
|
||||||
? "Online"
|
} else if (healthCheck.data) {
|
||||||
: "Offline";
|
status = "Online";
|
||||||
const color = healthCheck.isLoading
|
}
|
||||||
? "bg-yellow-500"
|
// flatten ternaries for linter by using simple logic
|
||||||
: healthCheck.data
|
let color = "bg-red-500";
|
||||||
? "bg-emerald-500"
|
if (healthCheck.isLoading) {
|
||||||
: "bg-red-500";
|
color = "bg-yellow-500";
|
||||||
|
} else if (healthCheck.data) {
|
||||||
|
color = "bg-emerald-500";
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="inline-flex items-center gap-2 rounded-full border px-3 py-1 text-muted-foreground text-xs backdrop-blur">
|
<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}`} />
|
<span className={`h-2 w-2 rounded-full ${color}`} />
|
||||||
@@ -309,12 +312,9 @@ function HomeComponent() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-8 flex items-center gap-3 text-muted-foreground text-xs">
|
<div className="mt-8 flex items-center gap-3 text-muted-foreground text-xs">
|
||||||
<img
|
<span
|
||||||
alt="Reflecto"
|
aria-hidden
|
||||||
className="h-5 w-5 rounded-sm"
|
className="inline-block h-5 w-5 rounded-sm bg-gradient-to-br from-purple-500 to-fuchsia-500"
|
||||||
height="20"
|
|
||||||
src="/logo.png"
|
|
||||||
width="20"
|
|
||||||
/>
|
/>
|
||||||
<span>Private by default • Powered by lightweight AI</span>
|
<span>Private by default • Powered by lightweight AI</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -395,26 +395,6 @@ function HomeComponent() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</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>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user