- ApiClient.getCurrentPlanning() — GET /planning/current.
- HomePage.tsx: replaces the old greeting card (now redundant with
AppLayout's sidebar) with the household's current planning — loading /
error / empty ("aucun planning pour cette semaine") / loaded (table of
weekDay/meal/recipe) states, modeled as a discriminated union so an
impossible combination (e.g. loading with data) can't be represented.
No invented weekday/meal grid — the API's `weekDay`/`meal` are free-form
strings (no enum exists yet in the schema), so this renders the items
as returned rather than assuming a specific vocabulary.
- locales/fr/translation.json: home.* replaced (title/loading/error/
empty/table.*), old greeting/logout keys removed (superseded by
layout.greeting/layout.logout from the AppLayout commit).
- cypress/e2e/auth.cy.ts: updated the now-stale "Bonjour Alice Martin"
assertions (greeting moved to the sidebar, first name only) and added
GET /planning/current intercepts so these specs don't depend on a real
backend. Manually verified end-to-end against a real API in the browser
preview (empty state + a seeded planning) — Cypress itself can't run
headless Chromium in this sandboxed dev environment (confirmed
pre-existing on main, unrelated to this change); CI runs the real
suite.
Verified with `pnpm --filter web build` after each of steps 2-4 to keep
every commit in this sequence independently buildable.
81 lines
2.7 KiB
TypeScript
81 lines
2.7 KiB
TypeScript
import type { PlanningView } from "@batch-cooking/shared";
|
|
import { useEffect, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { apiClient } from "../api/client";
|
|
import "./HomePage.scss";
|
|
|
|
/** Load state for the `GET /planning/current` call — a discriminated union so a stale/impossible combination (e.g. "loading" with data) can't be represented. */
|
|
type PlanningState =
|
|
| { status: "loading" }
|
|
| { status: "loaded"; planning: PlanningView | null }
|
|
| { status: "error" };
|
|
|
|
/**
|
|
* Landing page for an authenticated visitor — the household's current
|
|
* planning. Behind {@link RequireAuth} (via `AppLayout`), so this only
|
|
* renders once a session is confirmed; the planning itself still has to be
|
|
* fetched separately, hence the loading/error/empty/loaded states below.
|
|
* `null` from the API is a normal, common state (no planning created yet),
|
|
* not an error — see `apps/api`'s `planning.service.ts`.
|
|
*/
|
|
export function HomePage() {
|
|
const { t } = useTranslation();
|
|
const [state, setState] = useState<PlanningState>({ status: "loading" });
|
|
|
|
useEffect(() => {
|
|
// Guards against setting state after unmount (e.g. the user navigates
|
|
// away before the request resolves) — no cleanup-worthy resource here,
|
|
// just avoids a "set state on unmounted component" warning.
|
|
let cancelled = false;
|
|
|
|
apiClient
|
|
.getCurrentPlanning()
|
|
.then((planning) => {
|
|
if (!cancelled) setState({ status: "loaded", planning });
|
|
})
|
|
.catch(() => {
|
|
if (!cancelled) setState({ status: "error" });
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<div className="home-page">
|
|
<h1>{t("home.title")}</h1>
|
|
|
|
{state.status === "loading" && <p className="home-page__status">{t("home.loading")}</p>}
|
|
|
|
{state.status === "error" && (
|
|
<p className="home-page__status home-page__status--error">{t("home.error")}</p>
|
|
)}
|
|
|
|
{state.status === "loaded" && state.planning === null && (
|
|
<p className="home-page__status">{t("home.empty")}</p>
|
|
)}
|
|
|
|
{state.status === "loaded" && state.planning !== null && (
|
|
<table className="planning-table">
|
|
<thead>
|
|
<tr>
|
|
<th>{t("home.table.day")}</th>
|
|
<th>{t("home.table.meal")}</th>
|
|
<th>{t("home.table.recipe")}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{state.planning.items.map((item) => (
|
|
<tr key={item.id}>
|
|
<td>{item.weekDay}</td>
|
|
<td>{item.meal}</td>
|
|
<td>{item.recipe.name}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|