Files
projax/web/templates/tree.tmpl
mAi 9f905de461 feat: Go HTTP server with tree / detail / new / classify
cmd/projax/main.go boots a pgxpool against PROJAX_DB_URL (falls back to
SUPABASE_DATABASE_URL), auto-applies embedded migrations on start
(disable with PROJAX_AUTO_MIGRATE=off), and serves on PROJAX_LISTEN_ADDR
(default :8080).

store package wraps the unified view + projax.items writes. Item has
helper methods for templates: IsArea, Editable, SourceRefDeref. The
Promote() flow runs the insert + item_links link inside a single
transaction so the source row drops out of items_unified atomically.

web package: per-page html/template instances parsed against a shared
layout.tmpl, embedded static/style.css, HTMX from CDN. Pages:
  GET  /                   tree of items_unified
  GET  /i/{path}           detail (editable for projax, read-only +
                           promote form for mai.projects)
  POST /i/{path}           update projax-native item
  POST /i/{path}/promote   one-page promote (HTMX-aware fragment for
                           inline classify)
  GET  /new?parent={path}  create form
  POST /new                create projax-native item
  GET  /admin/classify     orphan list with inline HTMX promote
  GET  /healthz            DB ping
  GET  /static/*           embedded assets

Auth is intentionally out of scope for v1 — service binds to whatever
PROJAX_LISTEN_ADDR points at, deploy guidance pins it to the Tailscale
interface (covered in 1d README).

Tests (skip when DB env is unset):
  TestTreeRenders, TestHealthz,
  TestDetailProjaxNativeEditable, TestDetailMaiProjectsReadOnly,
  TestClassifyListsOrphans, TestPromoteRoundTrip.
2026-05-15 13:24:44 +02:00

53 lines
1.4 KiB
Cheetah

{{define "content"}}
<h1>Tree</h1>
<p class="counts">
<strong>{{.ProjaxCount}}</strong> projax · <strong>{{.MaiCount}}</strong> mai.projects orphans
{{if .MaiCount}}<a href="/admin/classify">→ classify</a>{{end}}
</p>
<section class="tree">
<h2>Areas + projax items</h2>
<ul class="forest">
{{range .Areas}}
<li class="node area">
<a href="/i/{{.Item.Path}}">{{.Item.Title}}</a>
<span class="slug">{{.Item.Path}}</span>
<a class="add" href="/new?parent={{.Item.Path}}">+</a>
{{template "children" .}}
</li>
{{end}}
</ul>
</section>
{{if .Orphans}}
<section class="orphans">
<h2>mai.projects orphans <small>(unclassified)</small></h2>
<ul class="flat">
{{range .Orphans}}
<li class="node orphan">
<a href="/i/{{.Path}}">{{.Title}}</a>
<span class="slug">{{.Path}}</span>
<span class="status status-{{.Status}}">{{.Status}}</span>
</li>
{{end}}
</ul>
</section>
{{end}}
{{end}}
{{define "children"}}
{{if .Children}}
<ul>
{{range .Children}}
<li class="node project">
<a href="/i/{{.Item.Path}}">{{.Item.Title}}</a>
<span class="slug">{{.Item.Path}}</span>
<span class="status status-{{.Item.Status}}">{{.Item.Status}}</span>
<a class="add" href="/new?parent={{.Item.Path}}">+</a>
{{template "children" .}}
</li>
{{end}}
</ul>
{{end}}
{{end}}