feat: manual nutrition entry on recipe editor

Lets authors enter nutrition values by hand instead of relying only on
the AI estimate; the detail-page panel now shows whether data was
manually entered or AI-estimated and offers to switch between them.

v0.35.0
This commit is contained in:
Arnaud
2026-07-14 15:55:42 +02:00
parent bd3d8c88f0
commit 1577b8de01
18 changed files with 5275 additions and 15 deletions
@@ -19,11 +19,13 @@ type NutritionData = {
interface NutritionPanelProps {
recipeId: string;
initialData?: NutritionData | null;
initialManual?: boolean;
}
export function NutritionPanel({ recipeId, initialData }: NutritionPanelProps) {
export function NutritionPanel({ recipeId, initialData, initialManual }: NutritionPanelProps) {
const t = useTranslations("nutritionPanel");
const [nutrition, setNutrition] = useState<NutritionData | null>(initialData ?? null);
const [manual, setManual] = useState(!!initialManual);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -39,6 +41,7 @@ export function NutritionPanel({ recipeId, initialData }: NutritionPanelProps) {
}
const data = await res.json() as { nutrition: NutritionData };
setNutrition(data.nutrition);
setManual(false);
} catch (err) {
setError(err instanceof Error ? err.message : t("error"));
} finally {
@@ -69,9 +72,12 @@ export function NutritionPanel({ recipeId, initialData }: NutritionPanelProps) {
disabled={loading}
className="text-xs text-muted-foreground"
>
{loading ? t("estimating") : t("reEstimateButton")}
{loading ? t("estimating") : manual ? t("estimateInsteadButton") : t("reEstimateButton")}
</Button>
</div>
{manual && !loading && (
<p className="text-xs text-muted-foreground">{t("manualBadge")}</p>
)}
{error && <p className="text-sm text-destructive">{error}</p>}
</CardHeader>
{nutrition && (