package main import ( "fmt" "strconv" "strings" "time" ) // Finnish weekday and month names. Go formats dates in English only, and this // app has exactly one locale (PRD §5). var ( weekdaysFI = [...]string{"sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai"} weekdayAbbrFI = [...]string{"su", "ma", "ti", "ke", "to", "pe", "la"} monthsFI = [...]string{"", "tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"} ) func longDateFI(t time.Time) string { return weekdaysFI[t.Weekday()] + " " + t.Format("2.1.2006") } func dayLabelFI(t time.Time) string { return weekdayAbbrFI[t.Weekday()] + " " + t.Format("2.1.") } func monthFI(t time.Time) string { return monthsFI[int(t.Month())] } func isoDate(t time.Time) string { return t.Format(dateLayout) } // dayURL links to a specific day, leaving today as the bare path. func dayURL(base string, d, now time.Time) string { if d.Equal(now) { return base } return base + "?pvm=" + isoDate(d) } // pickSeparator joins a dish onto a day URL, which already carries ?pvm= for // any day but today. func pickSeparator(v logView) string { if v.Date.Equal(v.Today) { return "?" } return "&" } // categoryLabels lists a dish's categories in Finnish, for the catalog rows. func categoryLabels(d Dish) string { names := make([]string, 0, len(d.Categories)) for _, c := range d.Categories { names = append(names, categoryFI[c]) } return strings.Join(names, ", ") } // countFI renders "1 pääruoka" but "16 pääruokaa": Finnish takes the partitive // after every number except one. func countFI(n int, one, many string) string { if n == 1 { return fmt.Sprintf("%d %s", n, one) } return fmt.Sprintf("%d %s", n, many) } templ page(title, current string) { // Dark is the default, set here so it holds even before theme.js runs and // for anyone without JavaScript. { title } @brandbar() { children... } @tabbar(current) } templ brandbar() {
Foodster @themeSwitch()
} // themeSwitch marks the active theme rather than labelling itself with the one // a click would produce. aria-pressed is set by theme.js on load, because only // the device knows what was chosen. // themeSwitch shows the theme that is on right now — moon while dark, sun // while light — and clicking swaps it. Both icons are in the markup and CSS // picks one, so the server never has to know the device's choice. // theme.js writes the label, which names the state and then the action. templ themeSwitch() { } templ iconSun() { } templ iconMoon() { } templ tabbar(current string) { } templ tab(href, label, current string) { if href == current { { label } } else { { label } } } // ---------------------------------------------------------------- Kirjaa templ logPage(v logView) { @page("Foodster", "/") {

Mitä syötiin?

{ longDateFI(v.Date) }

@daySwitch(v)
switch { case v.Chosen != nil: @sidesStep(v) case v.Entry != nil: @loggedCard(v) default: @board(v) }
} } templ daySwitch(v logView) {
@dayButton("Tänään", v.Today, v.Date, v.Today) @dayButton("Eilen", v.Today.AddDate(0, 0, -1), v.Date, v.Today)
} templ dayButton(label string, target, selected, now time.Time) { if target.Equal(selected) { { label } } else { { label } } } templ board(v logView) {
if !v.Date.Equal(v.Today) { }
if len(v.Dishes) > 0 {
for _, d := range v.Dishes { @dishPill(d, v) }
} if len(v.Dishes) == 0 { @quickAddCard(v) } } // quickAddCard turns a search that found nothing into the thing to do next. // Adding a dish here creates it and goes straight to logging it, so the user // never leaves Kirjaa mid-thought. templ quickAddCard(v logView) {
if v.Search == "" {

Lisää ensimmäinen ruoka

Ruokalista on tyhjä. Lisää ruoka tästä, tai tuo koko lista kerralla Ruoat-välilehdeltä.

} else {

Ei osumia. Lisätäänkö?

} if v.New.Err != "" {

{ v.New.Err }

}
Kategoriat
@categoryChip("meat", "liha", v.New) @categoryChip("chicken", "kana", v.New) @categoryChip("fish", "kala", v.New) @categoryChip("vegetarian", "kasvis", v.New)
} templ dishPill(d Dish, v logView) { { d.Name } if d.TimesEaten > 0 { { strconv.Itoa(d.TimesEaten) } } } templ sidesStep(v logView) {

{ v.Chosen.Name }

if v.Chosen.HasSides && len(v.Sides) > 0 {

Lisukkeita?

for _, s := range v.Sides { }
} else {

Tarjoillaan sellaisenaan.

}
Peruuta
} templ loggedCard(v logView) {

if v.Date.Equal(v.Today) { Tänään kirjattu } else { { dayLabelFI(v.Date) } kirjattu }

{ v.Entry.Main.Name }

{ v.Entry.SidesLabel() }

} // ---------------------------------------------------------------- Historia templ historyPage(rows []HistoryRow) { @page("Historia — Foodster", "/historia") {

Historia

if len(rows) == 0 {

Ei vielä merkintöjä.

} for i, row := range rows { if i == 0 || rows[i-1].Date.Month() != row.Date.Month() {

{ monthFI(row.Date) }

} if row.Entry != nil {
{ row.Entry.Main.Name }
{ row.Entry.SidesLabel() }
} else {
Ei merkintää

Ruoat

{ countFI(len(v.Mains), "pääruoka", "pääruokaa") }, { countFI(len(v.Sides), "lisuke", "lisuketta") }

if v.Report != nil { @importReport(v.Report) } @mainForm_(v.Main)

Pääruoat

if len(v.Mains) == 0 {

Ei vielä pääruokia.

} for _, d := range v.Mains {
{ d.Name }
{ categoryLabels(d) } if !d.HasSides { · ei lisukkeita }
@rowActions("/ruoat?muokkaa="+strconv.FormatInt(d.ID, 10), d.ID, "paa")
} @sideForm_(v.Side)

Lisukkeet

if len(v.Sides) == 0 {

Ei vielä lisukkeita.

} for _, s := range v.Sides {
{ s.Name }
@rowActions("/ruoat?muokkaa-lisuke="+strconv.FormatInt(s.ID, 10), s.ID, "lisuke")
}
Tuo ruokia tiedostosta @importForm()
} } templ rowActions(editURL string, id int64, kind string) {
Muokkaa
} templ mainForm_(f mainForm) {

if f.ID == 0 { Lisää pääruoka } else { Muokkaa pääruokaa }

if f.Err != "" {

{ f.Err }

}
if f.ID != 0 { }
Kategoriat
@categoryChip("meat", "liha", f) @categoryChip("chicken", "kana", f) @categoryChip("fish", "kala", f) @categoryChip("vegetarian", "kasvis", f)
if f.ID != 0 { Peruuta }
} templ categoryChip(value, label string, f mainForm) { } templ sideForm_(f sideForm) {

if f.ID == 0 { Lisää lisuke } else { Muokkaa lisuketta }

if f.Err != "" {

{ f.Err }

}
if f.ID != 0 { }
if f.ID != 0 { Peruuta }
} templ importForm() {

Tuo ruokia

Liitä JSON tai valitse tiedosto. Kelvolliset rivit lisätään, virheelliset ohitetaan.

} templ importReport(r *ImportReport) {
0) }>

{ fmt.Sprintf("Lisätty %d, ohitettu %d", r.Added, r.Skipped) }

if len(r.Notes) > 0 {
    for _, note := range r.Notes {
  • { note }
  • }
}
}