87 lines
3.5 KiB
JavaScript
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>
|
|
`
|
|
}
|