Files
projax/web/templates/detail.tmpl
mAi e055e4607e feat(phase 3c per-events): event_date on item_links, Documents UI, PER URL resolver, MCP date-aware add_link
migration 0011_item_links_event_date.sql: ADD event_date date + partial
index (idempotent). Day granularity by design per the PER spec; the
column lands NULL on every existing row, no backfill.

store:
- ItemLink gains an EventDate *time.Time (every read path scans it).
- AddLinkDated(ctx, item, refType, refID, rel, note, date, metadata)
  upserts with COALESCE(new, old) for note + event_date so partial
  callers don't clobber prior state.
- DatedLinks(item) returns event_date IS NOT NULL ordered DESC.

web:
- per.go: parsePER strips a trailing .YYMMDD (rejects invalid dates like
  Feb 30); collisionTag yields a/b/.../z/aa/ab/...; computePERs walks
  DatedLinks output and assigns render-time collision tags inside each
  date group. Tags are never stored.
- handleDetail: 404 retry with PER stripped — /i/mfin.house1.260515
  resolves to the house1 item with HighlightDate=2026-05-15.
- documents_section.tmpl: add-form (ref_type/date/ref_id/note),
  date-sorted rows with computed PER, ref-type badge, remove × with
  anti-forgery item-id check, highlight row when HighlightDate matches.
- POST /i/{path}/links/add and /links/remove handlers; HTMX swap on the
  fragment, redirect for non-HTMX callers.

mcp:
- add_link accepts event_date: "YYYY-MM-DD" (parsed strict, hands back
  fmt.Errorf on bad form). linkView.event_date surfaces it on responses.
- Existing add_link callers without event_date keep working unchanged.

docs:
- docs/standards/per.md gains an Implementation section pointing at
  item_links.event_date + ref_types + render-time collision policy.
- docs/design.md adds a Documents/dated artifacts section with the
  schema delta, conflict policy, and URL routing rules.

tests:
- per_test.go: parsePER (valid/invalid dates, non-numeric, wrong
  length); collisionTag (1..53); computePERs (bare-then-.a, skips
  undated, multi-date grouping).
2026-05-15 18:35:21 +02:00

64 lines
2.4 KiB
Cheetah

{{define "content"}}
<h1>{{.Item.Title}}</h1>
<p class="meta">
<span class="slug">{{.Item.PrimaryPath}}</span>
<span class="status status-{{.Item.Status}}">{{.Item.Status}}</span>
{{range .Item.Management}}<span class="mgmt mgmt-{{.}}">{{.}}</span>{{end}}
{{range .Item.Tags}}<span class="tag">{{.}}</span>{{end}}
{{if .Item.Pinned}}<span class="pin">pinned</span>{{end}}
{{if .Item.Archived}}<span class="archived">archived</span>{{end}}
{{if .Item.SourceRefDeref}}<span class="muted">mai id: {{.Item.SourceRefDeref}}</span>{{end}}
</p>
{{if .Item.OtherPaths}}
<p class="meta muted">Also at: {{range $i, $p := .Item.OtherPaths}}{{if $i}}, {{end}}<a href="/i/{{$p}}">{{$p}}</a>{{end}}</p>
{{end}}
{{if .CalDAVOn}}
{{template "tasks-section" .}}
{{end}}
{{if and .GiteaOn .Issues}}
{{template "issues-section" .}}
{{end}}
{{template "documents-section" .}}
<form method="post" action="/i/{{.Item.PrimaryPath}}" class="edit">
<label>Title <input name="title" value="{{.Item.Title}}" required></label>
<label>Slug <input name="slug" value="{{.Item.Slug}}" required pattern="[^.]+"></label>
<label>Parents <small class="muted">(hold Ctrl/Cmd to pick multiple — same row can live under several branches)</small>
<select name="parent_ids" multiple size="6">
{{range .ParentOptions}}
<option value="{{.ID}}" {{if contains $.Item.ParentIDs .ID}}selected{{end}}>{{.Path}}</option>
{{end}}
</select>
</label>
<label>Status
<select name="status">
{{range $opt := .StatusOptions}}
<option value="{{$opt}}" {{if eq $opt $.Item.Status}}selected{{end}}>{{$opt}}</option>
{{end}}
</select>
</label>
<label>Tags
<input name="tags" value="{{join "," .Item.Tags}}" placeholder="comma-separated, e.g. work, dev">
</label>
<label>Management
<input name="management" value="{{join "," .Item.Management}}" placeholder="comma-separated: self, mai, external">
</label>
<label class="checkbox">
<input type="checkbox" name="pinned" value="1" {{if .Item.Pinned}}checked{{end}}> pinned
</label>
<label class="checkbox">
<input type="checkbox" name="archived" value="1" {{if .Item.Archived}}checked{{end}}> archived
</label>
<label>Content
<textarea name="content_md" rows="14">{{.Item.ContentMD}}</textarea>
</label>
<div class="actions">
<button type="submit">Save</button>
<a class="cancel" href="/">Cancel</a>
</div>
</form>
{{end}}