Files
ketopath/apps/web/src/app/[locale]/plan/actions.ts
T
lucianoandClaude Opus 4.7 cdfc685855 chore: fix typecheck on monorepo
- regeneratePlan: switch return type to Promise<void> so it satisfies the
  React form action contract.
- fast.routes PATCH: omit undefined keys instead of passing them, to
  comply with Prisma + exactOptionalPropertyTypes.
- weight-entry-form: cast field value/defaultValues to bypass z.input
  reporting `Date` for `z.coerce.date()` (zod 3.25 still emits the
  target type, not unknown).

pnpm typecheck, lint, test now all green across the monorepo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:16:08 +02:00

76 lines
2.2 KiB
TypeScript

'use server';
import { revalidatePath } from 'next/cache';
import { headers } from 'next/headers';
const API_URL = process.env.API_URL ?? 'http://localhost:4000';
interface SlotRecipe {
id: string;
name: string;
kcal: number;
}
export interface PlanSlot {
id: string;
dayOfWeek: number;
meal: 'COLAZIONE' | 'PRANZO' | 'SPUNTINO' | 'CENA';
recipeId: string | null;
status: string;
selected: SlotRecipe | null;
alternatives: SlotRecipe[];
}
export interface CurrentPlan {
id: string;
weekStart: string;
slots: PlanSlot[];
}
function cookieHeader(): string {
return headers().get('cookie') ?? '';
}
export async function fetchCurrentPlan(): Promise<CurrentPlan | null> {
const res = await fetch(`${API_URL}/me/meal-plans/current`, {
headers: { cookie: cookieHeader() },
cache: 'no-store',
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`api_error_${res.status}`);
const data = (await res.json()) as { plan: CurrentPlan };
return data.plan;
}
// Server action invocata via `<form action={regeneratePlan}>`. La firma è
// `(FormData) => Promise<void>` per allinearsi al type richiesto da React 18.
export async function regeneratePlan(_formData?: FormData): Promise<void> {
const res = await fetch(`${API_URL}/me/meal-plans`, {
method: 'POST',
headers: { cookie: cookieHeader() },
cache: 'no-store',
});
if (!res.ok) {
const body = (await res.json().catch(() => ({}))) as { error?: string };
throw new Error(body.error ?? `api_error_${res.status}`);
}
revalidatePath('/plan');
}
export type SwapResult = { ok: true } | { ok: false; error: string };
export async function swapSlotRecipe(slotId: string, recipeId: string): Promise<SwapResult> {
const res = await fetch(`${API_URL}/me/meal-plans/slots/${slotId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json', cookie: cookieHeader() },
body: JSON.stringify({ recipeId }),
cache: 'no-store',
});
if (!res.ok) {
const body = (await res.json().catch(() => ({}))) as { error?: string };
return { ok: false, error: body.error ?? `api_error_${res.status}` };
}
revalidatePath('/plan');
return { ok: true };
}