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).
64 lines
2.4 KiB
Cheetah
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}}
|