Tofu/app/components/pages/PlanningPage.js

87 lines
3.5 KiB
JavaScript

import { html } from 'htm/preact'
import { useState } from 'preact/hooks'
import { StatusLine } from '#tofu/components/atoms/StatusLine.js'
import { DayEntry } from '#tofu/components/molecules/DayEntry.js'
import { WeekNavigation } from '#tofu/components/molecules/WeekNavigation.js'
import { useRecipeIndex } from '#tofu/hooks/useRecipeIndex.js'
import { useWeekPlanning } from '#tofu/hooks/useWeekPlanning.js'
import { formatDateKey } from '#tofu/tools/dates/formatDateKey.js'
import { formatWeekRange } from '#tofu/tools/dates/formatWeekRange.js'
import { getWeekStart } from '#tofu/tools/dates/getWeekStart.js'
import { listWeekDates } from '#tofu/tools/dates/listWeekDates.js'
import { shiftWeeks } from '#tofu/tools/dates/shiftWeeks.js'
import { joinIngredientNames } from '#tofu/tools/text/joinIngredientNames.js'
import { rankRecipeSuggestions } from '#tofu/tools/text/rankRecipeSuggestions.js'
// The sheet itself: the seven days of one week, saved while the user types.
export function PlanningPage({ store, recipeSource }) {
const [weekStart, setWeekStart] = useState(function startOfCurrentWeek() {
return getWeekStart(new Date())
})
const { recipes, ready } = useRecipeIndex(recipeSource)
const { planning, status, updateDay, flush } = useWeekPlanning({ store, weekStart })
function goToPreviousWeek() {
setWeekStart(shiftWeeks(weekStart, -1))
}
function goToNextWeek() {
setWeekStart(shiftWeeks(weekStart, 1))
}
// Submitting the form is the « Terminé » button, and also the Enter key.
function saveAndFinish(event) {
event.preventDefault()
flush()
}
// A suggestion fills the dish name, and the ingredient line only when it is
// still empty: what the user wrote by hand is never overwritten.
function selectRecipe(dateKey, ingredients, recipe) {
if (ingredients.trim() !== '') {
updateDay(dateKey, { dish: recipe.name })
return
}
updateDay(dateKey, { dish: recipe.name, ingredients: joinIngredientNames(recipe.ingredientNames) })
}
function readDay(dateKey) {
return planning.days.find((day) => day.date === dateKey) || { date: dateKey, dish: '', ingredients: '' }
}
// Repeated on all seven days the hint becomes wallpaper, so only Monday
// carries it — it is the line the eye lands on first.
function renderDay(date, index) {
const dateKey = formatDateKey(date)
const { dish, ingredients } = readDay(dateKey)
return html`
<${DayEntry}
key=${dateKey}
date=${date}
dish=${dish}
ingredients=${ingredients}
ingredientsHint=${index === 0 ? 'séparés par des virgules' : ''}
suggestions=${ready ? rankRecipeSuggestions(dish, recipes) : []}
onDishInput=${(value) => updateDay(dateKey, { dish: value })}
onDishSelect=${(recipe) => selectRecipe(dateKey, ingredients, recipe)}
onIngredientsInput=${(value) => updateDay(dateKey, { ingredients: value })}
/>
`
}
return html`
<article class="sheet">
<header class="sheet-header">
<h1 class="sheet-title">${formatWeekRange(weekStart)}</h1>
<${WeekNavigation} onPrevious=${goToPreviousWeek} onNext=${goToNextWeek} />
</header>
<form class="sheet-body" onSubmit=${saveAndFinish}>
${listWeekDates(weekStart).map(renderDay)}
<footer class="sheet-footer">
<button type="submit" class="button">Terminé</button>
<${StatusLine} message=${status.message} state=${status.state} />
</footer>
</form>
</article>
`
}