Implements spec #137 (spec 4 of 4 from wayfinder map #114). Closes #137. Tickets: #164, #165, #166, #167, #168, #169, #170, #171, #172, #173 — all closed, landed on this branch. ## Summary - #164/#168: sixth outcome word `not_found`; per-Site completed marker predicate. - #165/#169: `poll_failures` row is the failure state; the pass remembers a Site-reported completion. - #166/#171: two new admin filters (`failing`, `unverified`); outbound owner notification + stall condition. - #167/#170: a failure names itself on the Series page; the completion hint reaches the owner and decides nothing. - #172: the other three fault conditions (no-browser-route, sidecar-down, adapter-broken) feeding the notifier. - #173: the landing verdict line shares the same `latest.FaultsFrom` judgement the notifier uses, so the page and the push cannot disagree. `cd backend && go test ./...` green on the merged branch (8 packages). Reviewed-on: #174 Co-authored-by: Sulthan Zaki <sultankiki05@gmail.com> Co-committed-by: Sulthan Zaki <sultankiki05@gmail.com>
This commit was merged in pull request #174.
This commit is contained in:
@@ -25,21 +25,25 @@ const seriesPageSize = 50
|
||||
// the labels are read by later admin tickets too, so the map and the
|
||||
// constants cannot drift apart.
|
||||
var seriesFilterLabels = map[string]string{
|
||||
store.SeriesFilterAll: "All series",
|
||||
store.SeriesFilterNoURL: "No series URL",
|
||||
store.SeriesFilterNoChapter: "Never read a chapter",
|
||||
store.SeriesFilterNoReaders: "No Readers",
|
||||
store.SeriesFilterNeverChecked: "Never checked",
|
||||
store.SeriesFilterStale: "Not checked in 12h",
|
||||
store.SeriesFilterNoCover: "No cover",
|
||||
store.SeriesFilterReaderReport: "Latest from a Reader",
|
||||
store.SeriesFilterFinished: "Finished",
|
||||
store.SeriesFilterAll: "All series",
|
||||
store.SeriesFilterNoURL: "No series URL",
|
||||
store.SeriesFilterNoChapter: "Never read a chapter",
|
||||
store.SeriesFilterNoReaders: "No Readers",
|
||||
store.SeriesFilterNeverChecked: "Never checked",
|
||||
store.SeriesFilterStale: "Not checked in 12h",
|
||||
store.SeriesFilterNoCover: "No cover",
|
||||
store.SeriesFilterReaderReport: "Latest from a Reader",
|
||||
store.SeriesFilterFailing: "Failing over 12h",
|
||||
store.SeriesFilterUnverified: "Unverified Reader number",
|
||||
store.SeriesFilterFinished: "Finished",
|
||||
store.SeriesFilterSiteCompleted: "Site says completed",
|
||||
}
|
||||
|
||||
// seriesFilterOrder is the select's render order: All first, then the
|
||||
// permanent repairs, then the fixable ones (issue #140). Finished rides the
|
||||
// tail, last — deliberate, not a repair — and the Overview's stats block
|
||||
// renders the same tail, which is what sits the finished figure last there.
|
||||
// permanent repairs, then the fixable ones (issue #140). Finished and the
|
||||
// site-completed hint ride the tail — deliberate, not repairs — and the
|
||||
// Overview's stats block renders the same tail, which is what sits the
|
||||
// finished and site-completed figures last there.
|
||||
var seriesFilterOrder = []string{
|
||||
store.SeriesFilterAll,
|
||||
store.SeriesFilterNoURL,
|
||||
@@ -49,7 +53,10 @@ var seriesFilterOrder = []string{
|
||||
store.SeriesFilterStale,
|
||||
store.SeriesFilterNoCover,
|
||||
store.SeriesFilterReaderReport,
|
||||
store.SeriesFilterFailing,
|
||||
store.SeriesFilterUnverified,
|
||||
store.SeriesFilterFinished,
|
||||
store.SeriesFilterSiteCompleted,
|
||||
}
|
||||
|
||||
// seriesListView is the Series list page's data. The template renders strings
|
||||
@@ -119,6 +126,11 @@ type seriesRowView struct {
|
||||
// page, where a press that retires a Series from the Lane is on purpose
|
||||
// and confirm-gated (issue #158).
|
||||
Finished bool
|
||||
// Failure names the standing failure and how long it has stood — "not
|
||||
// found · 3d ago", "" while no failure row stands. Its own field, never a
|
||||
// Notes chip: the chips cap at two plus a tail, so the one fact that
|
||||
// names the failure would be the most likely to be truncated away.
|
||||
Failure string
|
||||
}
|
||||
|
||||
// adminSeries renders the filterable, bookmarkable Series list: filter, Site,
|
||||
@@ -679,6 +691,9 @@ func seriesRow(a store.AdminSeries, i int, now time.Time) seriesRowView {
|
||||
row.Ch = "—"
|
||||
}
|
||||
row.Age = checkedAge(now, a.LatestCheckedAt)
|
||||
if a.FailureOutcome != "" {
|
||||
row.Failure = outcomeWord(a.FailureOutcome) + " · " + checkedAge(now, a.FailingSince)
|
||||
}
|
||||
notes := seriesNotes(a, now)
|
||||
if n := len(notes); n > 2 {
|
||||
row.Notes, row.More = notes[:2], n-2
|
||||
@@ -689,6 +704,20 @@ func seriesRow(a store.AdminSeries, i int, now time.Time) seriesRowView {
|
||||
return row
|
||||
}
|
||||
|
||||
// outcomeWord spells the wire failure word as the Lanes chips spell it — a
|
||||
// space, not the underscore: not_found reads "not found", no_chapter "no
|
||||
// chapter". The remaining words are their own spelling, so an unknown word
|
||||
// degrades to itself rather than vanishing from the page.
|
||||
func outcomeWord(wire string) string {
|
||||
switch wire {
|
||||
case "not_found":
|
||||
return "not found"
|
||||
case "no_chapter":
|
||||
return "no chapter"
|
||||
}
|
||||
return wire
|
||||
}
|
||||
|
||||
// seriesNotes are a row's hygiene chips in the design's order: no URL, no
|
||||
// cover, orphan, stale, reader sighting.
|
||||
func seriesNotes(a store.AdminSeries, now time.Time) []string {
|
||||
|
||||
Reference in New Issue
Block a user