fix: mobile layout fixes, i18n coverage, and recipe share link
Mobile:
- Recipes search bar full-width on mobile instead of capped narrow
- Cook mode ingredients panel stacks above the step instead of
squeezing it into a narrow column
- Version history Compare/Restore buttons wrap onto their own row
- Recipe edit ingredient fields wrap instead of forcing horizontal
scroll on narrow viewports
i18n: translates remaining hardcoded strings across recipes
filter/sort, adapt-recipe and AI variations dialogs, the full
settings section (sidebar + 6 sub-pages + BYOK/model-prefs/
API-keys/webhooks managers), explore tab, collections (new/fork/
share dialogs), meal planning (planner, AI generation phases, new
shopping list, shared-plan view), photo import, recipe bulk-select
toolbar, and recipe action-button tooltips. Also fixes the recipes
page subtitle, which wasn't just unworded but missing its {count}
interpolation entirely — it always rendered as the bare word
"results" regardless of how many recipes existed.
Feature: adds a ShareRecipeButton that copies the public /r/{id}
link to the clipboard, with a notice when the recipe isn't Public
yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -244,7 +244,7 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
<div className="max-w-5xl mx-auto space-y-10">
|
||||
{/* Search bar */}
|
||||
<div className="space-y-3">
|
||||
<h1 className="text-3xl font-bold">Explore</h1>
|
||||
<h1 className="text-3xl font-bold">{t("title")}</h1>
|
||||
<form onSubmit={handleSubmit} className="relative">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-muted-foreground pointer-events-none" />
|
||||
<Input
|
||||
@@ -264,10 +264,10 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
<SelectValue placeholder={tCommon("difficulty")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="any">Any difficulty</SelectItem>
|
||||
<SelectItem value="easy">Easy</SelectItem>
|
||||
<SelectItem value="medium">Medium</SelectItem>
|
||||
<SelectItem value="hard">Hard</SelectItem>
|
||||
<SelectItem value="any">{t("anyDifficulty")}</SelectItem>
|
||||
<SelectItem value="easy">{tRecipe("difficulty.easy")}</SelectItem>
|
||||
<SelectItem value="medium">{tRecipe("difficulty.medium")}</SelectItem>
|
||||
<SelectItem value="hard">{tRecipe("difficulty.hard")}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Input
|
||||
@@ -280,7 +280,7 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
/>
|
||||
{!loading && (
|
||||
<span className="text-sm text-muted-foreground ml-auto">
|
||||
{total} {total === 1 ? "recipe" : "recipes"} found
|
||||
{t(total === 1 ? "resultsFoundSingular" : "resultsFoundPlural", { count: total })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -303,8 +303,8 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
{hasQuery && !loading && results.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center py-24 text-muted-foreground">
|
||||
<ChefHat className="h-12 w-12 mb-4 opacity-30" />
|
||||
<p className="text-lg font-medium">No recipes found for “{query}”</p>
|
||||
<p className="text-sm mt-1">Try different keywords or remove filters</p>
|
||||
<p className="text-lg font-medium">{t("noResultsTitle", { query })}</p>
|
||||
<p className="text-sm mt-1">{t("noResultsHint")}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -323,7 +323,7 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
disabled={loadingMore}
|
||||
className="min-w-32"
|
||||
>
|
||||
{loadingMore ? "Loading…" : "Load more"}
|
||||
{loadingMore ? tCommon("loading") : t("loadMore")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -337,9 +337,9 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
<section className="rounded-xl border bg-gradient-to-br from-violet-50 to-indigo-50 dark:from-violet-950/30 dark:to-indigo-950/30 p-6 space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Sparkles className="h-5 w-5 text-violet-500" />
|
||||
<h2 className="text-xl font-semibold">Recipe ideas</h2>
|
||||
<h2 className="text-xl font-semibold">{t("recipeIdeas")}</h2>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">Tell the AI what you're in the mood for, or let it surprise you.</p>
|
||||
<p className="text-sm text-muted-foreground">{t("recipeIdeasHint")}</p>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
@@ -355,9 +355,9 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
/>
|
||||
<Button type="submit" disabled={ideasLoading} variant="secondary" className="shrink-0">
|
||||
{ideasLoading ? (
|
||||
<span className="flex items-center gap-2"><Wand2 className="h-4 w-4 animate-pulse" /> Generating…</span>
|
||||
<span className="flex items-center gap-2"><Wand2 className="h-4 w-4 animate-pulse" /> {t("generating")}</span>
|
||||
) : (
|
||||
<span className="flex items-center gap-2"><Wand2 className="h-4 w-4" /> Get ideas</span>
|
||||
<span className="flex items-center gap-2"><Wand2 className="h-4 w-4" /> {t("getIdeas")}</span>
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -367,7 +367,7 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
onClick={() => { setIdeasPrompt(""); fetchIdeas(""); }}
|
||||
className="shrink-0"
|
||||
>
|
||||
Surprise me
|
||||
{t("surpriseMe")}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -414,9 +414,9 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
onClick={() => generateFromIdea(idea)}
|
||||
>
|
||||
{generatingId === idea.title ? (
|
||||
<span className="flex items-center gap-2"><Wand2 className="h-3.5 w-3.5 animate-pulse" /> Generating…</span>
|
||||
<span className="flex items-center gap-2"><Wand2 className="h-3.5 w-3.5 animate-pulse" /> {t("generating")}</span>
|
||||
) : (
|
||||
<span className="flex items-center gap-2"><ArrowRight className="h-3.5 w-3.5" /> Generate full recipe</span>
|
||||
<span className="flex items-center gap-2"><ArrowRight className="h-3.5 w-3.5" /> {t("generateFullRecipe")}</span>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -428,11 +428,11 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
<section>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Flame className="h-5 w-5 text-orange-500" />
|
||||
<h2 className="text-xl font-semibold">Trending this week</h2>
|
||||
<h2 className="text-xl font-semibold">{t("trendingThisWeek")}</h2>
|
||||
</div>
|
||||
{trending.length === 0 ? (
|
||||
<p className="text-muted-foreground text-sm py-8 text-center">
|
||||
No trending recipes yet. Be the first to share one!
|
||||
{t("noTrending")}
|
||||
</p>
|
||||
) : (
|
||||
<HorizontalScroll>
|
||||
@@ -448,11 +448,11 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
|
||||
<section>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Clock className="h-5 w-5 text-blue-500" />
|
||||
<h2 className="text-xl font-semibold">Recently added</h2>
|
||||
<h2 className="text-xl font-semibold">{t("recentlyAdded")}</h2>
|
||||
</div>
|
||||
{recent.length === 0 ? (
|
||||
<p className="text-muted-foreground text-sm py-8 text-center">
|
||||
No public recipes yet.
|
||||
{t("noRecent")}
|
||||
</p>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
|
||||
Reference in New Issue
Block a user