Align the landing surface with the shared parts: sectionHeader for the regions, the new readout part for every headline count, flush lists for the System sheet, chevron-marked linked cards, and a boxed paginated activity feed. Add the raw-section-title lint rule and classify the remaining hand-written titles; archive the overview-consistency change.
101 lines
4.8 KiB
Go
101 lines
4.8 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
|
|
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
|
|
|
|
package web
|
|
|
|
import (
|
|
"html/template"
|
|
"io/fs"
|
|
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/config"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/embeds"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/forms"
|
|
)
|
|
|
|
// SurfaceRoot is the location trail's first crumb: the surface a page
|
|
// belongs to (design D18 "The location trail on every page, rooted at the
|
|
// surface"). The two values live here, below every template-owning package,
|
|
// so the console's own sets (internal/server) and the integration-owned
|
|
// operator sets (fedwiki, discourse) register the same root and the trail
|
|
// reads "Operator / …" on every operator page whoever renders it.
|
|
type SurfaceRoot struct {
|
|
Label string
|
|
URL string
|
|
}
|
|
|
|
// OperatorSurfaceRoot is the operator surface's root crumb.
|
|
func OperatorSurfaceRoot() SurfaceRoot { return SurfaceRoot{Label: "Operator", URL: "/operator"} }
|
|
|
|
// MemberSurfaceRoot is the member surface's root crumb.
|
|
func MemberSurfaceRoot() SurfaceRoot { return SurfaceRoot{Label: "Home", URL: "/"} }
|
|
|
|
// ParseUIPartials parses the shared application shell
|
|
// (templates/partials/shell_*.html) and the page-anatomy parts
|
|
// (templates/partials/ui_*.html) into t, and installs the template
|
|
// functions they need. Every template set that renders a page or a partial
|
|
// calls it once, so the parts are defined by name (pageHeader,
|
|
// sectionHeader, statusBadge, emptyState, readout, helpIcon,
|
|
// shell_topbar.html, shell_rail_member.html) in every set and no set keeps
|
|
// its own list
|
|
// (docs/design-system.md §6 "Page anatomy"; spec page-anatomy "Every page
|
|
// is built from the shared parts").
|
|
//
|
|
// surfaceRoot renders no root crumb by default: this is the value every
|
|
// caller gets unless it registers its own (design D18 "The location trail
|
|
// on every page, rooted at the surface"). html/template resolves a
|
|
// function call against the template set's current function map at
|
|
// execution time, not at parse time, so a caller may call t.Funcs again
|
|
// with its own "surfaceRoot" — the operator surface's or the member
|
|
// surface's — any time before it executes, whether that happens before or
|
|
// after this call; the last registration wins for every template sharing
|
|
// the set, including ones already parsed. Callers that never override it
|
|
// (most test-only template sets) simply render no root crumb, the same as
|
|
// today.
|
|
func ParseUIPartials(t *template.Template) (*template.Template, error) {
|
|
sub, err := fs.Sub(embeds.Templates, "templates")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
t = t.Funcs(template.FuncMap{
|
|
"deploymentName": config.DeploymentName,
|
|
"supportURL": config.SupportURL,
|
|
"surfaceRoot": func() any { return nil },
|
|
// helpIcon has one implementation everywhere (design D23), unlike
|
|
// surfaceRoot: it builds the {Label, Text} value the helpIcon part
|
|
// renders from. A page-owning template set that itself calls
|
|
// {{ template "helpIcon" (helpIcon ...) }} (most do, once
|
|
// converted) must still register its own copy before parsing that
|
|
// template, the same reason deploymentName/supportURL are
|
|
// registered at those call sites too — this copy only reaches
|
|
// templates parsed here, by this call, or later.
|
|
"helpIcon": func(label, text string) struct{ Label, Text string } {
|
|
return struct{ Label, Text string }{Label: label, Text: text}
|
|
},
|
|
// shellConfirmForm renders the one shared confirm dialog
|
|
// (shell_confirm_modal.html, which this call also parses): a
|
|
// zero-argument function, not a per-page data field, because the
|
|
// dialog needs nothing from the page that opens it (task 5.6). It
|
|
// reads the registry rather than a direct reference to keep this
|
|
// package, which internal/server imports, from importing back:
|
|
// internal/server/shell_forms.go registers "shell.confirm" at its
|
|
// own package's init, always before any template executes.
|
|
"shellConfirmForm": func() forms.FormView {
|
|
spec, ok := forms.Lookup("shell.confirm")
|
|
if !ok {
|
|
return forms.FormView{}
|
|
}
|
|
return forms.Render(spec, forms.Binding{Mode: forms.ModeUnbound})
|
|
},
|
|
})
|
|
// The list scaffold (operator_list_controls.html: listControls,
|
|
// listPager, listNoMatch, listSearch) rides along: the operator shell's
|
|
// landing branch pages its activity feed through listPager
|
|
// (overview-consistency D5), and html/template's escape analysis
|
|
// demands every referenced template exist in a set even on branches a
|
|
// page never takes, so every set that renders operator.html needs the
|
|
// scaffold whether or not its own body lists records (the integrations'
|
|
// operator pages regressed this way once before, discourse-integration
|
|
// findings #8). The scaffold uses no functions, so it parses in any set.
|
|
return t.ParseFS(sub, "partials/shell_*.html", "partials/ui_*.html", "partials/operator_list_controls.html")
|
|
}
|