Labels page: one-click label packs (GitHub / GitLab defaults) offered when the repo has no labels #324

Closed
opened 2026-09-11 14:48:52 +00:00 by crueber · 3 comments
Owner

What's requested

On the labels page (/:owner/:name/labels), when the repo has no labels yet, offer one-click label packs — the default label sets from GitHub and GitLab. Clicking a pack creates its labels in the repo in one action.

Current state (code evidence)

  • web/src/pages/Labels.jsx renders the empty state as a bare <li class="muted">no labels yet</li> (:49) — no affordance beyond the manual create form (name + 6-hex color, one label per submit).
  • The label shape is {name, color, description} (internal/issues/model.go:150-154); creation is one-per-request POST …/labels (triage-gated, createLabel at internal/issues/http.go:678). No bulk endpoint exists.
  • The empty state is triage-visible to everyone, so the pack offer must be gated like every other write.

Proposed design

  1. Two built-in packs, defined as a pure constant (headless-testable, web/src/lib/ convention):

    GitHub set (names + the familiar colors + GitHub's descriptions):
    bug (#d73a4a, "Something isn't working"), documentation (#0075ca, "Improvements or additions to documentation"), duplicate (#cfd3d7, "This issue or pull request already exists"), enhancement (#a2eeef, "New feature or request"), good first issue (#7057ff, "Good for newcomers"), help wanted (#008672, "Extra attention is needed"), invalid (#e4e669, "This doesn't seem right"), question (#d876e3, "Further information is requested"), wontfix (#ffffff, "This will not be worked on").

    GitLab set (scoped-name style, GitLab colors + descriptions):
    bug (#d9534f), confirmed (#5cb85c, "The bug report is confirmed"), critical (#ffc107, "The issue is of high priority"), documentation (#1aaa55), improvement (#1f75cb, "An improvement to an existing feature"), support (#6cd3ea, "Further information is requested"), devops::pipeline etc. are scope-laden — keep the GitLab pack to its general set (~9 labels, GitLab's starter taxonomy) and spell the final list in the PR from GitLab's documented starter labels.

    (The exact hex values above are from memory of the public palettes — the implementer must verify against the live GitHub/GitLab docs and put the final table in the PR.)

  2. Empty-state UI: when s().labels is empty, render a pack chooser card instead of (or above) the bare "no labels yet" line: two pack cards (name, label preview chips with their colors, count) each with an "Add pack" button. One click → creates all labels in the pack → invalidate labels:{full} → the chooser disappears (labels exist) and the normal list renders.

  3. Creation mechanics. No bulk endpoint exists; options:

    • (a) Client-side sequential create through the existing one-per-request labels.create (9 requests, human-rate, matches uploadFilesSequential precedent) — no backend change, triage gate enforced per request. Partial failure (e.g. a name collision mid-pack) surfaces via the error tray; reload shows what landed.
    • (b) A bulk endpoint POST …/labels/pack — cleaner semantics (atomic-ish, one 403 for non-triage), more wire surface.
      Recommendation: (a) — human-rate, trivially correct, zero backend change; note (b) as a follow-up if atomicity ever matters. Skip existing names rather than failing the whole pack (idempotent-ish add).
  4. Pack offer visibility: the chooser shows only in the empty state (labels.length === 0). A repo that deleted all its labels sees the chooser again — that's fine and arguably useful; note it.

  5. Gating: the "Add pack" buttons are triage-gated client-side like the create form (the server enforces per-label anyway); non-triage viewers see the chooser read-only or not at all — match how the create form behaves for them (form renders but 403s; mirror that, don't invent new gating).

Acceptance criteria

  • Repo with zero labels shows the two pack offers (GitHub / GitLab) with color-accurate label previews; repo with ≥1 label shows the normal page (no chooser).
  • One click on "Add pack" creates the full pack; the list renders the new labels with correct name/color/description; chooser disappears.
  • Adding a pack when some names already exist skips those and creates the rest (no duplicate labels, no whole-pack failure).
  • Non-triage users cannot create via the pack buttons (server 403 enforced; client gating mirrors the create form).
  • Pack definitions are a pure constant with a headless test (count, no duplicate names, valid 6-hex colors per pack).
  • Light/dark themes correct (preview chips legible); works at 390px mobile width.
## What's requested On the labels page (`/:owner/:name/labels`), when the repo has **no labels yet**, offer one-click **label packs** — the default label sets from GitHub and GitLab. Clicking a pack creates its labels in the repo in one action. ## Current state (code evidence) - `web/src/pages/Labels.jsx` renders the empty state as a bare `<li class="muted">no labels yet</li>` (:49) — no affordance beyond the manual create form (name + 6-hex color, one label per submit). - The label shape is `{name, color, description}` (`internal/issues/model.go:150-154`); creation is one-per-request `POST …/labels` (triage-gated, `createLabel` at `internal/issues/http.go:678`). No bulk endpoint exists. - The empty state is triage-visible to everyone, so the pack offer must be gated like every other write. ## Proposed design 1. **Two built-in packs**, defined as a pure constant (headless-testable, `web/src/lib/` convention): **GitHub set** (names + the familiar colors + GitHub's descriptions): `bug` (#d73a4a, "Something isn't working"), `documentation` (#0075ca, "Improvements or additions to documentation"), `duplicate` (#cfd3d7, "This issue or pull request already exists"), `enhancement` (#a2eeef, "New feature or request"), `good first issue` (#7057ff, "Good for newcomers"), `help wanted` (#008672, "Extra attention is needed"), `invalid` (#e4e669, "This doesn't seem right"), `question` (#d876e3, "Further information is requested"), `wontfix` (#ffffff, "This will not be worked on"). **GitLab set** (scoped-name style, GitLab colors + descriptions): `bug` (#d9534f), `confirmed` (#5cb85c, "The bug report is confirmed"), `critical` (#ffc107, "The issue is of high priority"), `documentation` (#1aaa55), `improvement` (#1f75cb, "An improvement to an existing feature"), `support` (#6cd3ea, "Further information is requested"), `devops::pipeline` etc. are scope-laden — keep the GitLab pack to its general set (~9 labels, GitLab's starter taxonomy) and spell the final list in the PR from GitLab's documented starter labels. (The exact hex values above are from memory of the public palettes — the implementer must verify against the live GitHub/GitLab docs and put the final table in the PR.) 2. **Empty-state UI:** when `s().labels` is empty, render a pack chooser card instead of (or above) the bare "no labels yet" line: two pack cards (name, label preview chips with their colors, count) each with an **"Add pack"** button. One click → creates all labels in the pack → invalidate `labels:{full}` → the chooser disappears (labels exist) and the normal list renders. 3. **Creation mechanics.** No bulk endpoint exists; options: - **(a) Client-side sequential create** through the existing one-per-request `labels.create` (9 requests, human-rate, matches `uploadFilesSequential` precedent) — no backend change, triage gate enforced per request. Partial failure (e.g. a name collision mid-pack) surfaces via the error tray; reload shows what landed. - **(b) A bulk endpoint** `POST …/labels/pack` — cleaner semantics (atomic-ish, one 403 for non-triage), more wire surface. **Recommendation: (a)** — human-rate, trivially correct, zero backend change; note (b) as a follow-up if atomicity ever matters. Skip existing names rather than failing the whole pack (idempotent-ish add). 4. **Pack offer visibility:** the chooser shows only in the empty state (`labels.length === 0`). A repo that deleted all its labels sees the chooser again — that's fine and arguably useful; note it. 5. **Gating:** the "Add pack" buttons are triage-gated client-side like the create form (the server enforces per-label anyway); non-triage viewers see the chooser read-only or not at all — match how the create form behaves for them (form renders but 403s; mirror that, don't invent new gating). ## Acceptance criteria - [ ] Repo with zero labels shows the two pack offers (GitHub / GitLab) with color-accurate label previews; repo with ≥1 label shows the normal page (no chooser). - [ ] One click on "Add pack" creates the full pack; the list renders the new labels with correct name/color/description; chooser disappears. - [ ] Adding a pack when some names already exist skips those and creates the rest (no duplicate labels, no whole-pack failure). - [ ] Non-triage users cannot create via the pack buttons (server 403 enforced; client gating mirrors the create form). - [ ] Pack definitions are a pure constant with a headless test (count, no duplicate names, valid 6-hex colors per pack). - [ ] Light/dark themes correct (preview chips legible); works at 390px mobile width.
crueber added this to the v1 milestone 2026-09-11 14:48:52 +00:00
Author
Owner

Fix PR: #333 — empty-state GitHub/GitLab label packs (live-verified palettes in the PR table), one-click sequential create, no backend change. node --test 620/620 green, vite build green.

Fix PR: https://git.packden.us/crueber/walhub/pulls/333 — empty-state GitHub/GitLab label packs (live-verified palettes in the PR table), one-click sequential create, no backend change. node --test 620/620 green, vite build green.
Author
Owner

Review of PR #333 (fix/issue-324, one-click label packs) — verified in scratch worktree at 58f1826 + docs fix f0de0e7 (pushed to origin/fix/issue-324).

PALETTES — both live-verified, no issues:

  • GitHub 8 (lib/label-packs.js:23-36): confirmed via GET https://api.github.com/repos/albandil/Hex/labels — exactly 8 entries flagged default:true, names/colors/descriptions byte-match the pack. documentation (#0075ca) is genuinely absent from the live defaults, so omitting it is correct, not stale.
  • GitLab 8 (label-packs.js:39-52): names match docs.gitlab.com Manage > Labels 'Generate a default set of labels' (bug, confirmed, critical, discussion, documentation, enhancement, suggestion, support — same set, order differs only); colors match lib/gitlab/issues_labels.rb @ master exactly (red #d9534f x3, yellow #f0ad4e x2, blue #428bca x2, green #5cb85c x1). No descriptions is faithful — GitLab generates none. No scoped (::) labels. suggestion-over-improvement correction is right.

CREATION PATH (Labels.jsx:52-74) — as designed, option (a):

  • Sequential labels.create, 8 requests human-rate; law 6 fine (rare action, no backend change, uploadFilesSequential precedent). No bulk endpoint, no seam touched (law 8 clean).
  • Triage enforced per request server-side; buttons (Labels.jsx:108-115) render unconditionally exactly like the create form (Labels.jsx:144) — no invented client gating. Non-triage click 403s per label into the error tray; honest.
  • Partial failure: per-label try/catch + reportError, loop continues, reload shows what landed. Skip-existing via missingFromPack (label-packs.js:64-67), case-insensitive, matches server uniqueness (02 §3.1).
  • SDK labels.create forwards {name,color,description} (web/sdk/src/issues.js:61-62); undefined description for GitLab entries drops in JSON, same shape as manual create. Fine.

VISIBILITY/THEMES: chooser only when labels.length===0 (Labels.jsx:82); 'no labels yet' fallback retained below. Preview chips use color dots + default text on card/border-zinc classes — legible dark+light by construction; sm:grid-cols-2 collapses to one column at 390px. No new deps (package.json untouched; imports are solid-js + local lib only) — law 1 clean. Law 7: no locks/channels touched.

ONE FIX APPLIED DIRECTLY (law 12): the commit message claimed '(02 §11 UI decision)' but added no docs entry — precedent #323 updated 02_issues.md in the same change. Added a Decisions bullet (docs/features/02_issues.md, +19/-0, pushed as f0de0e7): pack sources, sequential-create mechanics, empty-state visibility, no-new-gating stance.

TESTS (scratch worktree, node_modules symlinked from main):

  • node --test web/test/unit/label-packs.test.js: 8/8 pass (counts, no case-insensitive dups, 6-hex, 02 §3.1 bounds, skip semantics).
  • Full node --test web/test/unit/*.test.js: 620/620 pass (note: suite takes ~213s, exceeds the 120s default timeout — timed out once before passing with a longer window).
  • vite build + esbuild SDK bundle: green (run via node_modules/.bin; pnpm absent in this env).
  • Browser: not driven — no browser needed for this change (node tests + reasoning cover it); module/MIME risk is nil (no new entry points, existing Labels route).

RECOMMENDATION: ready to merge (pending CI).

Review of PR #333 (fix/issue-324, one-click label packs) — verified in scratch worktree at 58f1826 + docs fix f0de0e7 (pushed to origin/fix/issue-324). PALETTES — both live-verified, no issues: - GitHub 8 (lib/label-packs.js:23-36): confirmed via GET https://api.github.com/repos/albandil/Hex/labels — exactly 8 entries flagged default:true, names/colors/descriptions byte-match the pack. documentation (#0075ca) is genuinely absent from the live defaults, so omitting it is correct, not stale. - GitLab 8 (label-packs.js:39-52): names match docs.gitlab.com Manage > Labels 'Generate a default set of labels' (bug, confirmed, critical, discussion, documentation, enhancement, suggestion, support — same set, order differs only); colors match lib/gitlab/issues_labels.rb @ master exactly (red #d9534f x3, yellow #f0ad4e x2, blue #428bca x2, green #5cb85c x1). No descriptions is faithful — GitLab generates none. No scoped (::) labels. suggestion-over-improvement correction is right. CREATION PATH (Labels.jsx:52-74) — as designed, option (a): - Sequential labels.create, 8 requests human-rate; law 6 fine (rare action, no backend change, uploadFilesSequential precedent). No bulk endpoint, no seam touched (law 8 clean). - Triage enforced per request server-side; buttons (Labels.jsx:108-115) render unconditionally exactly like the create form (Labels.jsx:144) — no invented client gating. Non-triage click 403s per label into the error tray; honest. - Partial failure: per-label try/catch + reportError, loop continues, reload shows what landed. Skip-existing via missingFromPack (label-packs.js:64-67), case-insensitive, matches server uniqueness (02 §3.1). - SDK labels.create forwards {name,color,description} (web/sdk/src/issues.js:61-62); undefined description for GitLab entries drops in JSON, same shape as manual create. Fine. VISIBILITY/THEMES: chooser only when labels.length===0 (Labels.jsx:82); 'no labels yet' fallback retained below. Preview chips use color dots + default text on card/border-zinc classes — legible dark+light by construction; sm:grid-cols-2 collapses to one column at 390px. No new deps (package.json untouched; imports are solid-js + local lib only) — law 1 clean. Law 7: no locks/channels touched. ONE FIX APPLIED DIRECTLY (law 12): the commit message claimed '(02 §11 UI decision)' but added no docs entry — precedent #323 updated 02_issues.md in the same change. Added a Decisions bullet (docs/features/02_issues.md, +19/-0, pushed as f0de0e7): pack sources, sequential-create mechanics, empty-state visibility, no-new-gating stance. TESTS (scratch worktree, node_modules symlinked from main): - node --test web/test/unit/label-packs.test.js: 8/8 pass (counts, no case-insensitive dups, 6-hex, 02 §3.1 bounds, skip semantics). - Full node --test web/test/unit/*.test.js: 620/620 pass (note: suite takes ~213s, exceeds the 120s default timeout — timed out once before passing with a longer window). - vite build + esbuild SDK bundle: green (run via node_modules/.bin; pnpm absent in this env). - Browser: not driven — no browser needed for this change (node tests + reasoning cover it); module/MIME risk is nil (no new entry points, existing Labels route). RECOMMENDATION: ready to merge (pending CI).
Author
Owner

Fixed by PR #333 (review clean — live-verified packs, sequential-create path, +law-12 doc entry by reviewer; 620/620), merged. Closing.

Fixed by PR #333 (review clean — live-verified packs, sequential-create path, +law-12 doc entry by reviewer; 620/620), merged. Closing.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#324
No description provided.