Release: one log page, grouped dishes, live search (#1)
Structure - Kirjaa and Historia are one page. They were two views of the same thing — every history row already linked into the logger, and the logger had a day switcher. Two tabs instead of three. Also closed a gap: on an already-logged day there was no way to swap to a different dish, only to re-pick its sides. - Ruoat → Ruuat, label and route. - The catalog has a structure. It had no top-level headings at all — the mains simply began with "Liha". Both halves now carry a heading and a count, categories are visibly subordinate, and the add/edit forms collapse instead of filling the screen before any content. Finding things - Dishes grouped by category on both screens, Sekalaiset for multi-category ones. Derived from the stored set, not a fifth category, so one Tortillat still covers all four for the §8.1 suggester later. - Live search on both lists, 250 ms after typing stops. Both remain plain GET forms, so they still filter with JavaScript off. - History is paged 30 days at a time — it previously rendered every day back to the first entry, forever. Correctness - Future meals refused. The picker offered them and ?pvm= accepted them. - today() wasn't midnight, so it never equalled a date parsed from ?pvm= — after saving, the card read "la 5.9. kirjattu" instead of "Tänään kirjattu". - Deletes ask first, for dishes and logged meals. The meal is the more destructive: a dish is only soft-deleted. - DB open failures name the path and uid, instead of unable to open database file (14). Visual - Category icons replace colour dots — steak, drumstick, fish, leaf, quartered circle. - Row actions are a pencil and a bin; the header has a surface. Housekeeping - Datastar SDK dropped — one JSON decode was pulling in four modules including an HTTP compression stack. Five lines replace it. - Release policy documented: main protected, releases arrive as PRs. Co-authored-by: Esa Kataja <[email protected]> Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
+89
-20
@@ -43,6 +43,16 @@ check() {
|
||||
fi
|
||||
}
|
||||
|
||||
# refute <name> <haystack> <needle>
|
||||
refute() {
|
||||
if printf '%s' "$2" | grep -qF -- "$3"; then
|
||||
echo " FAIL $1 (should not contain: $3)"
|
||||
fail=1
|
||||
else
|
||||
echo " ok $1"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "smoke: http://$addr"
|
||||
|
||||
check "unauthenticated request is refused" \
|
||||
@@ -75,29 +85,29 @@ check "manifest has the right content type" \
|
||||
"application/manifest+json"
|
||||
|
||||
check "catalog starts empty" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/ruoat")" "0 pääruokaa"
|
||||
"$(curl -s -u ":$pass" "http://$addr/ruuat")" "0 pääruokaa"
|
||||
|
||||
out=$(curl -s -u ":$pass" -F "tiedosto=@seeds/testi.json" "http://$addr/ruoat/tuonti")
|
||||
out=$(curl -s -u ":$pass" -F "tiedosto=@seeds/testi.json" "http://$addr/ruuat/tuonti")
|
||||
check "file upload imports the seed bundle" "$out" "Lisätty 22, ohitettu 0"
|
||||
check "counts update after import" "$out" "16 pääruokaa, 6 lisuketta"
|
||||
|
||||
check "re-import refuses duplicates" \
|
||||
"$(curl -s -u ":$pass" -F "tiedosto=@seeds/testi.json" "http://$addr/ruoat/tuonti")" \
|
||||
"$(curl -s -u ":$pass" -F "tiedosto=@seeds/testi.json" "http://$addr/ruuat/tuonti")" \
|
||||
"jo listalla"
|
||||
|
||||
check "pasted JSON imports" \
|
||||
"$(curl -s -u ":$pass" -F 'json={"mains":[],"sides":[{"name":"Perunasalaatti"}]}' \
|
||||
"http://$addr/ruoat/tuonti")" "Lisätty 1"
|
||||
"http://$addr/ruuat/tuonti")" "Lisätty 1"
|
||||
|
||||
check "unknown category is reported" \
|
||||
"$(curl -s -u ":$pass" -F 'json={"mains":[{"name":"Rikki","categories":["kana"]}],"sides":[]}' \
|
||||
"http://$addr/ruoat/tuonti")" "tuntematon kategoria"
|
||||
"http://$addr/ruuat/tuonti")" "tuntematon kategoria"
|
||||
|
||||
check "empty submit is explained" \
|
||||
"$(curl -s -u ":$pass" -F 'json=' "http://$addr/ruoat/tuonti")" "Ei tuotavaa"
|
||||
"$(curl -s -u ":$pass" -F 'json=' "http://$addr/ruuat/tuonti")" "Ei tuotavaa"
|
||||
|
||||
check "malformed JSON is explained" \
|
||||
"$(curl -s -u ":$pass" -F 'json={nope' "http://$addr/ruoat/tuonti")" "JSON ei kelpaa"
|
||||
"$(curl -s -u ":$pass" -F 'json={nope' "http://$addr/ruuat/tuonti")" "JSON ei kelpaa"
|
||||
|
||||
# ---- the log flow, against the dishes imported above --------------------
|
||||
|
||||
@@ -122,8 +132,13 @@ check "saving redirects back to the day" \
|
||||
check "the saved day shows what was eaten" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/?pvm=2026-09-05")" "kirjattu"
|
||||
|
||||
check "history lists the entry" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/historia")" "syyskuu"
|
||||
check "history is on the same page as the logger" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/")" "Aiemmin"
|
||||
|
||||
# Deleting a logged meal drops the row outright, so it asks first.
|
||||
saved=$(curl -s -u ":$pass" "http://$addr/?pvm=2026-09-05&poista=1")
|
||||
check "deleting a meal asks first" "$saved" "Poistetaanko merkintä?"
|
||||
refute "and does not delete while asking" "$saved" "Ei merkintää"
|
||||
|
||||
check "deleting redirects back" \
|
||||
"$(curl -s -o /dev/null -w '%{http_code}' -u ":$pass" \
|
||||
@@ -135,6 +150,43 @@ check "the day is empty again" \
|
||||
check "search filters the board" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/?haku=keitto")" "keitto"
|
||||
|
||||
# ---- live search: Datastar sends signals as JSON in ?datastar= -----------
|
||||
|
||||
live=$(curl -s -u ":$pass" --get --data-urlencode 'datastar={"haku":"keitto"}' "http://$addr/etsi")
|
||||
check "live search returns the board fragment" "$live" 'id="lauta"'
|
||||
check "live search applies the term" "$live" "keitto"
|
||||
refute "live search excludes non-matches" "$live" "Lihapullat"
|
||||
refute "the fragment is not a whole page" "$live" "<html"
|
||||
|
||||
check "live search is served as html for Datastar to patch" \
|
||||
"$(curl -s -o /dev/null -w '%{content_type}' -u ":$pass" \
|
||||
--get --data-urlencode 'datastar={"haku":"keitto"}' "http://$addr/etsi")" \
|
||||
"text/html"
|
||||
|
||||
cat_live=$(curl -s -u ":$pass" --get --data-urlencode 'datastar={"haku":"riisi"}' "http://$addr/ruuat/etsi")
|
||||
check "catalog live search returns its fragment" "$cat_live" 'id="ruokalista"'
|
||||
check "catalog live search matches sides too" "$cat_live" "Riisi"
|
||||
refute "catalog live search excludes non-matches" "$cat_live" "Lihapullat"
|
||||
|
||||
# The plain form still works without JavaScript.
|
||||
check "catalog search works as a plain form too" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/ruuat?haku=riisi")" "Riisi"
|
||||
|
||||
# Nothing was eaten tomorrow. A future date is clamped rather than logged.
|
||||
future=$(date -d '+30 days' +%Y-%m-%d)
|
||||
check "a future date falls back to today" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/?pvm=$future")" "$(date +%-d.%-m.%Y)"
|
||||
|
||||
check "saving a future date is clamped too" \
|
||||
"$(curl -s -o /dev/null -w '%{redirect_url}' -u ":$pass" \
|
||||
-d "pvm=$future&ruoka=$ruoka" "http://$addr/kirjaa")" "/"
|
||||
|
||||
check "tomorrow was not written to the log" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/?pvm=$future")" "$(date +%-d.%-m.%Y)"
|
||||
|
||||
# Clean up the entry that clamped onto today.
|
||||
curl -s -o /dev/null -u ":$pass" -d "pvm=$(date +%Y-%m-%d)" "http://$addr/poista"
|
||||
|
||||
# ---- adding a dish without leaving Kirjaa --------------------------------
|
||||
|
||||
miss=$(curl -s -u ":$pass" "http://$addr/?haku=Poronkariste")
|
||||
@@ -157,37 +209,54 @@ check "the quick-added dish is on the board" \
|
||||
|
||||
check "adding a main redirects" \
|
||||
"$(curl -s -o /dev/null -w '%{http_code}' -u ":$pass" \
|
||||
-d 'nimi=uunikala&kategoria=fish&lisukkeita=1' "http://$addr/ruoat/paaruoka")" "303"
|
||||
-d 'nimi=uunikala&kategoria=fish&lisukkeita=1' "http://$addr/ruuat/paaruoka")" "303"
|
||||
|
||||
catalog=$(curl -s -u ":$pass" "http://$addr/ruoat")
|
||||
catalog=$(curl -s -u ":$pass" "http://$addr/ruuat")
|
||||
check "the new main is listed, sentence-cased" "$catalog" "Uunikala"
|
||||
|
||||
check "a duplicate name is refused" \
|
||||
"$(curl -s -u ":$pass" -d 'nimi=UUNIKALA&kategoria=fish' "http://$addr/ruoat/paaruoka")" \
|
||||
"$(curl -s -u ":$pass" -d 'nimi=UUNIKALA&kategoria=fish' "http://$addr/ruuat/paaruoka")" \
|
||||
"Nimi on jo listalla."
|
||||
|
||||
check "a main with no category is refused" \
|
||||
"$(curl -s -u ":$pass" -d 'nimi=Kategoriaton' "http://$addr/ruoat/paaruoka")" \
|
||||
"$(curl -s -u ":$pass" -d 'nimi=Kategoriaton' "http://$addr/ruuat/paaruoka")" \
|
||||
"Valitse vähintään yksi kategoria."
|
||||
|
||||
check "a nameless dish is refused" \
|
||||
"$(curl -s -u ":$pass" -d 'nimi=+++&kategoria=fish' "http://$addr/ruoat/paaruoka")" \
|
||||
"$(curl -s -u ":$pass" -d 'nimi=+++&kategoria=fish' "http://$addr/ruuat/paaruoka")" \
|
||||
"Anna nimi."
|
||||
|
||||
check "adding a side redirects" \
|
||||
"$(curl -s -o /dev/null -w '%{http_code}' -u ":$pass" \
|
||||
-d 'nimi=lohkoperunat' "http://$addr/ruoat/lisuke")" "303"
|
||||
-d 'nimi=lohkoperunat' "http://$addr/ruuat/lisuke")" "303"
|
||||
|
||||
check "the new side is listed" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/ruoat")" "Lohkoperunat"
|
||||
"$(curl -s -u ":$pass" "http://$addr/ruuat")" "Lohkoperunat"
|
||||
|
||||
uusi=$(printf '%s' "$catalog" | grep -o 'muokkaa=[0-9]*' | head -n1 | cut -d= -f2)
|
||||
# The id of Uunikala specifically: the catalog is grouped and alphabetical, so
|
||||
# the first id on the page belongs to some other dish entirely.
|
||||
uusi=$(printf '%s' "$catalog" | grep -o 'Uunikala.*' | grep -o 'muokkaa=[0-9]*' | head -n1 | cut -d= -f2)
|
||||
if [ -z "$uusi" ]; then
|
||||
echo " FAIL could not find Uunikala's id in the catalog"
|
||||
fail=1
|
||||
uusi=0
|
||||
fi
|
||||
check "the edit form is prefilled" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/ruoat?muokkaa=$uusi")" "Muokkaa pääruokaa"
|
||||
"$(curl -s -u ":$pass" "http://$addr/ruuat?muokkaa=$uusi")" "Muokkaa pääruokaa"
|
||||
|
||||
check "deleting a main redirects" \
|
||||
# A bin icon is easy to hit by accident, so the row asks before anything goes.
|
||||
check "the bin asks before deleting" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/ruuat?poista=$uusi&tyyppi=paa")" "Poista?"
|
||||
|
||||
check "the dish is still there while it asks" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/ruuat?poista=$uusi&tyyppi=paa")" "Uunikala"
|
||||
|
||||
check "confirming the delete redirects" \
|
||||
"$(curl -s -o /dev/null -w '%{http_code}' -u ":$pass" \
|
||||
-d "id=$uusi&tyyppi=paa" "http://$addr/ruoat/poista")" "303"
|
||||
-d "id=$uusi&tyyppi=paa" "http://$addr/ruuat/poista")" "303"
|
||||
|
||||
refute "the dish is gone once confirmed" \
|
||||
"$(curl -s -u ":$pass" "http://$addr/ruuat")" "Uunikala"
|
||||
|
||||
if [ "$fail" -ne 0 ]; then
|
||||
echo "smoke: FAILED"
|
||||
|
||||
Reference in New Issue
Block a user