Files
projax/web/templates/bulk_section.tmpl
mAi 0e490bb600 feat(phase 3d auto-tag): backfill area tags, bulk-edit UI, soft-delete cleanup
- migration 0012: one-shot populate empty tags from each item's area-roots
  (so chips on /?tag=work etc. actually filter the 40+ mai-backfilled rows)
- migration 0013: cleanup 12 orphan item_links + BEFORE-UPDATE trigger that
  cascades soft-delete to item_links going forward — closes the data drift
  that made TestItemsUnifiedSurfacesMaiPointer fail since 3c
- /admin/bulk page: flat filter+checkbox list with one-tx Apply for add/
  remove tag, set management, set status. Per-row inline chip add/remove
  via /admin/bulk/chip. Reuses tree_filter URL params 1:1.
- design.md §3.2 + §4.1 updated; tag+management section notes 0012
- bulk + tag-backfill + soft-delete-cascade tests cover the new surface
2026-05-15 18:49:58 +02:00

144 lines
5.3 KiB
Cheetah
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{define "bulk-section"}}
<section id="bulk-section" class="bulk-section">
<section class="tagbar" id="bulk-filterbar">
<form id="bulk-filter" class="search"
hx-get="/admin/bulk"
hx-target="#bulk-section"
hx-swap="outerHTML"
hx-trigger="keyup changed delay:200ms from:input[name=q], change from:select, change from:input[type=hidden], change from:input[name=show-archived]"
hx-push-url="true">
<input type="search" name="q" value="{{.Filter.Q}}" placeholder="search title, slug, content…" autocomplete="off">
<label>tag&nbsp;
<select name="tag" multiple size="3">
{{$selTags := .Filter.Tags}}
{{range .AllTags}}<option value="{{.}}" {{if contains $selTags .}}selected{{end}}>{{.}}</option>{{end}}
</select>
</label>
<label>mgmt&nbsp;
<select name="mgmt" multiple size="4">
{{$selM := .Filter.Management}}
<option value="mai" {{if contains $selM "mai"}}selected{{end}}>mai</option>
<option value="self" {{if contains $selM "self"}}selected{{end}}>self</option>
<option value="external" {{if contains $selM "external"}}selected{{end}}>external</option>
<option value="unmanaged" {{if contains $selM "unmanaged"}}selected{{end}}>unmanaged</option>
</select>
</label>
<label>status&nbsp;
<select name="status" multiple size="3">
{{$selS := .Filter.Status}}
<option value="active" {{if contains $selS "active"}}selected{{end}}>active</option>
<option value="done" {{if contains $selS "done"}}selected{{end}}>done</option>
<option value="archived" {{if contains $selS "archived"}}selected{{end}}>archived</option>
</select>
</label>
<label class="checkbox">
<input type="checkbox" name="show-archived" value="1" {{if .Filter.ShowArchived}}checked{{end}}>
show archived
</label>
</form>
<p class="counts"><strong>{{.Matched}}</strong> / <strong>{{.Total}}</strong> items match</p>
</section>
<form id="bulk-actions"
method="post"
action="/admin/bulk/apply"
hx-post="/admin/bulk/apply"
hx-target="#bulk-section"
hx-swap="outerHTML"
hx-include="#bulk-filter">
<fieldset class="actions">
<legend>Bulk action <small>(applies to all checked rows)</small></legend>
<div class="action-row">
<label>+ tag <input name="add_tag" placeholder="tag-slug"></label>
<label> tag <input name="remove_tag" placeholder="tag-slug"></label>
<label>set management
<select name="set_mgmt">
<option value="">—</option>
<option value="mai">mai</option>
<option value="self">self</option>
<option value="external">external</option>
<option value="clear">clear</option>
</select>
</label>
<label>set status
<select name="set_status">
<option value="">—</option>
<option value="active">active</option>
<option value="done">done</option>
<option value="archived">archived</option>
</select>
</label>
<button type="submit">Apply</button>
</div>
</fieldset>
<table class="bulk">
<thead>
<tr>
<th><input type="checkbox" id="bulk-all" onclick="document.querySelectorAll('input[name=ids]').forEach(c=>c.checked=this.checked)"></th>
<th>slug</th>
<th>primary path</th>
<th>tags</th>
<th>mgmt</th>
<th>status</th>
</tr>
</thead>
<tbody>
{{range .Rows}}
<tr id="bulk-row-{{.ID}}">
<td><input type="checkbox" name="ids" value="{{.ID}}"></td>
<td><a href="/i/{{.PrimaryPath}}">{{.Slug}}</a></td>
<td><small class="muted">{{.PrimaryPath}}</small></td>
<td class="cell-tags" id="cell-tags-{{.ID}}">
{{template "bulk-chip-tags" .}}
</td>
<td class="cell-mgmt" id="cell-mgmt-{{.ID}}">
{{template "bulk-chip-mgmt" .}}
</td>
<td><span class="status status-{{.Status}}">{{.Status}}</span></td>
</tr>
{{else}}
<tr><td colspan="6"><em>No items match. Loosen the filters.</em></td></tr>
{{end}}
</tbody>
</table>
</form>
</section>
{{end}}
{{define "bulk-chip-tags"}}
{{range .Tags}}
<span class="tag">{{.}}
<button class="chip-x"
hx-post="/admin/bulk/chip"
hx-vals='{"id":"{{$.ID}}","op":"remove","kind":"tag","value":"{{.}}"}'
hx-target="#cell-tags-{{$.ID}}"
hx-swap="innerHTML"
type="button"
title="remove tag">×</button>
</span>
{{end}}
<form class="chip-add" onsubmit="return false"
hx-post="/admin/bulk/chip"
hx-target="#cell-tags-{{.ID}}"
hx-swap="innerHTML"
hx-trigger="submit">
<input type="hidden" name="id" value="{{.ID}}">
<input type="hidden" name="op" value="add">
<input type="hidden" name="kind" value="tag">
<input name="value" placeholder="+tag" size="6">
</form>
{{end}}
{{define "bulk-chip-mgmt"}}
{{range .Management}}<span class="mgmt mgmt-{{.}}">{{.}}</span>{{end}}
{{if not .Management}}<span class="muted">unmanaged</span>{{end}}
{{end}}