Merge branch 'mai/kahn/phase-5i-phase-a-design' (phase 5i slice A: project filter dim + descendants toggle)
# Conflicts: # web/dashboard.go # web/server.go # web/templates/dashboard_section.tmpl
This commit is contained in:
@@ -162,18 +162,24 @@ func New(s *store.Store, logger *slog.Logger) (*Server, error) {
|
||||
pages[name] = t
|
||||
}
|
||||
// tree bundles the tree-section partial so HTMX swaps and the initial
|
||||
// page render share definitions.
|
||||
// page render share definitions. project_chip.tmpl is the Phase 5i Slice
|
||||
// A shared partial that every Views-supporting page includes inside its
|
||||
// filter strip.
|
||||
treeTmpl, err := template.New("tree").Funcs(funcs).ParseFS(templatesFS,
|
||||
"templates/layout.tmpl",
|
||||
"templates/tree.tmpl",
|
||||
"templates/tree_section.tmpl",
|
||||
"templates/project_chip.tmpl",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse tree: %w", err)
|
||||
}
|
||||
pages["tree"] = treeTmpl
|
||||
// Standalone tree-section template for HTMX fragment responses.
|
||||
treeSection, err := template.New("tree_section").Funcs(funcs).ParseFS(templatesFS, "templates/tree_section.tmpl")
|
||||
treeSection, err := template.New("tree_section").Funcs(funcs).ParseFS(templatesFS,
|
||||
"templates/tree_section.tmpl",
|
||||
"templates/project_chip.tmpl",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse tree_section: %w", err)
|
||||
}
|
||||
@@ -251,6 +257,7 @@ func New(s *store.Store, logger *slog.Logger) (*Server, error) {
|
||||
"templates/dashboard.tmpl",
|
||||
"templates/dashboard_section.tmpl",
|
||||
"templates/dashboard_tiles.tmpl",
|
||||
"templates/project_chip.tmpl",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse dashboard: %w", err)
|
||||
@@ -259,6 +266,7 @@ func New(s *store.Store, logger *slog.Logger) (*Server, error) {
|
||||
dashSection, err := template.New("dashboard_section").Funcs(funcs).ParseFS(templatesFS,
|
||||
"templates/dashboard_section.tmpl",
|
||||
"templates/dashboard_tiles.tmpl",
|
||||
"templates/project_chip.tmpl",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse dashboard_section: %w", err)
|
||||
@@ -270,12 +278,16 @@ func New(s *store.Store, logger *slog.Logger) (*Server, error) {
|
||||
"templates/layout.tmpl",
|
||||
"templates/timeline.tmpl",
|
||||
"templates/timeline_section.tmpl",
|
||||
"templates/project_chip.tmpl",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse timeline: %w", err)
|
||||
}
|
||||
pages["timeline"] = timelineTmpl
|
||||
timelineSection, err := template.New("timeline_section").Funcs(funcs).ParseFS(templatesFS, "templates/timeline_section.tmpl")
|
||||
timelineSection, err := template.New("timeline_section").Funcs(funcs).ParseFS(templatesFS,
|
||||
"templates/timeline_section.tmpl",
|
||||
"templates/project_chip.tmpl",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse timeline_section: %w", err)
|
||||
}
|
||||
@@ -288,12 +300,16 @@ func New(s *store.Store, logger *slog.Logger) (*Server, error) {
|
||||
"templates/layout.tmpl",
|
||||
"templates/calendar.tmpl",
|
||||
"templates/calendar_section.tmpl",
|
||||
"templates/project_chip.tmpl",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse calendar: %w", err)
|
||||
}
|
||||
pages["calendar"] = calTmpl
|
||||
calSection, err := template.New("calendar_section").Funcs(funcs).ParseFS(templatesFS, "templates/calendar_section.tmpl")
|
||||
calSection, err := template.New("calendar_section").Funcs(funcs).ParseFS(templatesFS,
|
||||
"templates/calendar_section.tmpl",
|
||||
"templates/project_chip.tmpl",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse calendar_section: %w", err)
|
||||
}
|
||||
@@ -427,15 +443,18 @@ func (s *Server) handleTree(w http.ResponseWriter, r *http.Request) {
|
||||
roots, orphans, total, orphanN, matched := applyTreeFilter(items, filter, linkKinds)
|
||||
counts := computeChipCounts(items, filter, linkKinds, tags)
|
||||
data := map[string]any{
|
||||
"Title": "tree",
|
||||
"Roots": roots,
|
||||
"Orphans": orphans,
|
||||
"Total": total,
|
||||
"OrphanN": orphanN,
|
||||
"Matched": matched,
|
||||
"AllTags": tags,
|
||||
"Filter": filter,
|
||||
"Counts": counts,
|
||||
"Title": "tree",
|
||||
"Roots": roots,
|
||||
"Orphans": orphans,
|
||||
"Total": total,
|
||||
"OrphanN": orphanN,
|
||||
"Matched": matched,
|
||||
"AllTags": tags,
|
||||
"Filter": filter,
|
||||
"Counts": counts,
|
||||
"Projects": parentOptionsFromItems(items),
|
||||
"BasePath": "/",
|
||||
"ProjectChipTarget": "#tree-section",
|
||||
// ActiveTags kept for backwards-compat with the old template path; removed
|
||||
// after the template migrates fully.
|
||||
"ActiveTags": filter.Tags,
|
||||
@@ -880,15 +899,20 @@ func (s *Server) parentOptions(ctx context.Context) ([]ParentOption, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []ParentOption
|
||||
return parentOptionsFromItems(items), nil
|
||||
}
|
||||
|
||||
// parentOptionsFromItems builds the same flat option list parentOptions
|
||||
// returns, but from an already-loaded items slice. Callers that have already
|
||||
// fetched items (handleTree, handleDashboard, …) use this to avoid a second
|
||||
// ListAll round-trip when they only need the picker options.
|
||||
func parentOptionsFromItems(items []*store.Item) []ParentOption {
|
||||
out := make([]ParentOption, 0, len(items))
|
||||
for _, it := range items {
|
||||
// Surface every primary path as a candidate parent — multi-parent
|
||||
// items appear once per parent option using their primary path so the
|
||||
// UI stays unambiguous.
|
||||
out = append(out, ParentOption{ID: it.ID, Path: it.PrimaryPath()})
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Path < out[j].Path })
|
||||
return out, nil
|
||||
return out
|
||||
}
|
||||
|
||||
// (buildForest + nodeHasAllTags removed in Phase 3b — superseded by
|
||||
|
||||
Reference in New Issue
Block a user