feat: custom recipe cover color/icon + mute auto placeholder palette (v0.51.0)
Two changes to the no-photo cover placeholder shipped last version: 1. Muted the gradient palette (100/40-opacity tints instead of solid -200/ -950 stops) — the original was too saturated next to real cover photos in the same grid, per feedback. 2. New coverIcon/coverColor columns on recipes (nullable text, migration 0048, additive-only) let the author pin a specific color+icon from the recipe editor instead of the automatic per-id pick. getRecipePlaceholder now checks these first, falling back to the deterministic hash pick when unset — existing recipes are unaffected until edited. Wired coverIcon/coverColor through every explicit-column recipe select that feeds a grid card (explore trending/recent, search, feed, for-you) — the relational-query call sites (recipes page, collections, profile pages) already return all columns and needed no changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ import {
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { DietaryTagPicker } from "./dietary-tag-picker";
|
||||
import { PhotoUploader, type PhotoEntry } from "./photo-uploader";
|
||||
import { RecipeCoverPicker } from "./recipe-cover-picker";
|
||||
import { RegenerateRecipeButton } from "./regenerate-recipe-button";
|
||||
import type { RegeneratedRecipe } from "@/lib/ai/features/regenerate-recipe";
|
||||
|
||||
@@ -78,6 +79,8 @@ type RecipeFormProps = {
|
||||
ingredients?: IngredientRow[];
|
||||
steps?: StepRow[];
|
||||
photos?: PhotoEntry[];
|
||||
coverIcon?: string | null;
|
||||
coverColor?: string | null;
|
||||
isBatchCook?: boolean;
|
||||
dishes?: DishRow[];
|
||||
nutrition?: {
|
||||
@@ -155,6 +158,8 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
|
||||
defaultValues?.steps?.length ? defaultValues.steps : [newStep()]
|
||||
);
|
||||
const [photos, setPhotos] = useState<PhotoEntry[]>(defaultValues?.photos ?? []);
|
||||
const [coverIcon, setCoverIcon] = useState<string | null>(defaultValues?.coverIcon ?? null);
|
||||
const [coverColor, setCoverColor] = useState<string | null>(defaultValues?.coverColor ?? null);
|
||||
const [isBatchCook, setIsBatchCook] = useState(defaultValues?.isBatchCook ?? false);
|
||||
const [dishes, setDishes] = useState<DishRow[]>(
|
||||
defaultValues?.dishes?.length ? defaultValues.dishes : [newDish()]
|
||||
@@ -376,6 +381,8 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
|
||||
ingredients: filteredIngredients,
|
||||
steps: filteredSteps,
|
||||
photos: photos.map((p) => ({ key: p.key, isCover: p.isCover })),
|
||||
coverIcon,
|
||||
coverColor,
|
||||
isBatchCook,
|
||||
dishes: filteredDishes,
|
||||
nutrition: addNutrition
|
||||
@@ -684,6 +691,20 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
|
||||
<PhotoUploader recipeId={id} photos={photos} onChange={setPhotos} />
|
||||
</div>
|
||||
|
||||
{/* Cover placeholder (used when there's no photo above) */}
|
||||
<div className="space-y-2">
|
||||
<Label>{t("cover")}</Label>
|
||||
<RecipeCoverPicker
|
||||
recipeType={recipeType}
|
||||
color={coverColor}
|
||||
icon={coverIcon}
|
||||
onChange={({ color, icon }) => {
|
||||
setCoverColor(color);
|
||||
setCoverIcon(icon);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Ingredients */}
|
||||
|
||||
Reference in New Issue
Block a user