feat: private accounts, explore/people merge, meal-plan fixes, i18n and theme cleanup
- Private accounts: users.isPrivate hides a user from search and their recipes from search/trending/for-you discovery surfaces (follow-aware where the route already has session context, blanket exclusion where it doesn't); existing followers and direct links are unaffected, no follow-request approval flow was built (explicit scope limit) - Merged /people into the Explore tab (tab=people query param); the old standalone route now redirects there - "Get Ideas" vs "Surprise Me" were doing the same empty-prompt call; Surprise Me now injects a real random constraint (5-ingredient, one-pot, etc.), matching the existing pattern in the AI recipe-generate dialog - Meal-plan day cells now link to their recipe (was dead text) and gained a one-click "mark as cooked" action - Theme toggle is now a real three-way light/dark/system control instead of a binary flip - Nutrition goals form had zero i18n wiring; fully localized now New migration 0027 (users.is_private) generated, left unapplied like the others. Verified with typecheck, lint, and a clean --no-cache docker build. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useTheme } from "next-themes";
|
||||
import { BookOpen, Calendar, Package, ChefHat, User, Rss, FolderOpen, ShoppingCart, Shield, Search, Compass, Menu, Sun, Moon, Apple } from "lucide-react";
|
||||
import { BookOpen, Calendar, Package, ChefHat, User, Rss, FolderOpen, ShoppingCart, Shield, Search, Compass, Menu, Sun, Moon, Monitor, Apple } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import {
|
||||
@@ -44,8 +44,13 @@ export function Nav() {
|
||||
const { data: session } = authClient.useSession();
|
||||
const isAdmin = (session?.user as { role?: string } | undefined)?.role === "admin";
|
||||
const username = (session?.user as { username?: string } | undefined)?.username;
|
||||
const { resolvedTheme, setTheme } = useTheme();
|
||||
const { theme, setTheme } = useTheme();
|
||||
const t = useTranslations("nav");
|
||||
const THEME_OPTIONS = [
|
||||
{ value: "light", icon: Sun, label: t("lightMode") },
|
||||
{ value: "dark", icon: Moon, label: t("darkMode") },
|
||||
{ value: "system", icon: Monitor, label: t("systemMode") },
|
||||
] as const;
|
||||
return (
|
||||
<header className="sticky top-0 z-50 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="container mx-auto flex h-14 items-center gap-6 px-4">
|
||||
@@ -126,13 +131,32 @@ export function Nav() {
|
||||
<DropdownMenuItem>
|
||||
<Link href="/settings" className="w-full">{t("settings")}</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
|
||||
className="flex items-center gap-2"
|
||||
<div
|
||||
role="group"
|
||||
aria-label={t("systemMode")}
|
||||
className="mx-1.5 my-1 flex items-center gap-0.5 rounded-md border bg-muted/50 p-0.5"
|
||||
>
|
||||
{resolvedTheme === "dark" ? <Sun className="h-3.5 w-3.5" /> : <Moon className="h-3.5 w-3.5" />}
|
||||
{resolvedTheme === "dark" ? t("lightMode") : t("darkMode")}
|
||||
</DropdownMenuItem>
|
||||
{THEME_OPTIONS.map(({ value, icon: Icon, label }) => (
|
||||
<button
|
||||
key={value}
|
||||
type="button"
|
||||
title={label}
|
||||
aria-label={label}
|
||||
aria-pressed={theme === value}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setTheme(value);
|
||||
}}
|
||||
className={cn(
|
||||
"flex flex-1 items-center justify-center rounded-sm p-1.5 text-muted-foreground transition-colors hover:text-foreground",
|
||||
theme === value && "bg-background text-foreground shadow-sm"
|
||||
)}
|
||||
>
|
||||
<Icon className="h-3.5 w-3.5" />
|
||||
<span className="sr-only">{label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{isAdmin && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
Reference in New Issue
Block a user