Commit Graph

6 Commits

Author SHA1 Message Date
bvandeusen c3be0f3e6e refactor(web): consolidate error-copy table to JSON source of truth
Three admin pages had divergent inline error-code → user-copy switch
tables (with wording drift, including a stale "Settings → Integrations"
that should have been Admin). Consolidate into web/src/lib/styles/error-copy.json,
expose via web/src/lib/api/error-copy.ts (ERROR_COPY + copyForCode),
and refactor the three pages to import the helper.

Fixes the three-way drift; the only user-visible behavior change is
the quarantine page now correctly says "Admin → Integrations".

Sets up the JSON for the M7 Flutter client (#356) to consume the same
table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 15:01:46 -04:00
bvandeusen f77245bc41 feat(requests): show ingest progress while requests are in flight
After Lidarr accepts a request the local reconciler imports albums and
tracks as they arrive — but until the request hit 'completed' the
operator had no way to see "is anything happening?" The /requests and
/admin/requests rows now surface a live progress line whenever the
request's matched entity has children in our library.

Backend:
- New CountAlbumsByArtist + CountTracksByArtist sqlc queries.
- requestView gains imported_album_count and imported_track_count.
- New fillProgress helper computes them from the matched entity:
  - kind=artist  → counts albums + tracks under matched_artist_id
  - kind=album   → counts tracks under matched_album_id
  - kind=track   → 1 once matched_track_id is set
  N+1 in the list endpoints; acceptable at admin scale.
- handleListRequests, handleGetRequest, and handleListAdminRequests
  populate the new fields on every response.

Frontend:
- LidarrRequest TS type extended with the two counters.
- Both the operator's /requests page and the admin /admin/requests
  page render an accent-colored line under the row meta when at
  least one counter is non-zero, e.g.:
    Artist:  "5 albums · 47 tracks ingested"
    Album:   "12 tracks ingested"
    Track:   "Track ingested"
- Updated test fixtures to include the new required fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 13:04:38 -04:00
bvandeusen 7d33166f23 fix(lidarr): include artistName + metadataProfileId on add-artist/add-album
The 'lidarr_rejected' the operator was hitting on Approve was Lidarr 4xx-ing
the POST with two field-validation errors:

  'Metadata Profile Id' must be greater than '0'.
  'Artist Name' must not be empty.

Our payload was missing both. Lidarr's metadata profile is a separate
concept from quality profile (it controls which release types are
tracked: albums, singles, EPs, etc.) and is required by /api/v1/artist
and /api/v1/album. The 'already exists' line in the toast copy was an
incorrect guess at the cause; the real issue was an invalid payload.

Changes:

- internal/lidarr/types.go: AddArtistParams + AddAlbumParams gain
  ArtistName and MetadataProfileID. New MetadataProfile struct mirroring
  QualityProfile.
- internal/lidarr/client.go: AddArtist + AddAlbum payloads include both
  new fields. New ListMetadataProfiles fetches GET /api/v1/metadataprofile.
- internal/lidarrrequests/service.go: Approve fetches the metadata
  profile list and uses the first as a default (Lidarr installs ship
  'Standard' at id=1 OOB, so this works for vanilla setups). Picker
  on /admin/integrations is a follow-up. ArtistName flows from the
  request row.
- web/src/routes/admin/requests/+page.svelte: drop the
  speculative 'usually means already in library' copy on lidarr_rejected;
  point operators at server logs for the field-level reason instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 23:57:39 -04:00
bvandeusen ab8235dd0b fix(admin): approve flow surfaces real errors; auto-default Lidarr picks
The "I can't approve a request, the toast just says unknown" bug was
four problems compounded:

1. Lidarr config let enabled=true save with default_quality_profile_id=0
   and default_root_folder_path=''. Approve then sent invalid POST bodies
   to Lidarr, which 5xx'd.
2. lidarr.client.post() discarded Lidarr's response body on error, so
   we couldn't tell why Lidarr 5xx'd from server logs.
3. handleApproveRequest's error switch didn't map ErrServerError or
   ErrLookupFailed — both fell through to a generic 500 server_error.
4. apiFetch only parsed {error: {code, message}} envelopes, but admin
   endpoints write {error: 'code_string'}. Every admin error toast
   rendered as 'unknown'.

Fixes:

- internal/lidarr/client.go: capture up to 512 bytes of Lidarr's response
  body when it returns 4xx/5xx; include in the wrapped error so server
  logs show what Lidarr actually said instead of just the status bucket.
- internal/lidarrrequests/service.go: new ErrDefaultsIncomplete fires
  before the Lidarr call when QP=0 or root_folder=''. Stops the bad
  POST entirely.
- internal/api/admin_requests.go: handleApproveRequest now maps
  ErrDefaultsIncomplete -> 'lidarr_defaults_incomplete' (400),
  ErrServerError -> 'lidarr_server_error' (502),
  ErrLookupFailed -> 'lidarr_rejected' (502).
- internal/api/admin_lidarr.go: handlePutLidarrConfig now requires
  QP + root folder to be set whenever enabled=true.
- web/src/lib/api/client.ts: apiFetch handles both error envelope shapes
  so admin error codes propagate to toasts.
- web/src/routes/admin/integrations/+page.svelte: auto-default to the
  first quality profile and first root folder Lidarr returns when the
  operator hasn't picked one yet — saves a click for typical
  one-profile/one-folder home setups.
- web/src/routes/admin/requests/+page.svelte: friendly toast copy for
  the new error codes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 23:37:17 -04:00
bvandeusen ab9be40bb2 feat(web): add /admin/requests approval queue with override modal
Pending/Approved/Completed/Rejected tabs (active count only). Override modal
collapsed by default; reject modal with notes textarea. Lidarr-unreachable
toast surfaces sync-Approve failures. Cancel buttons in both modals are
Pewter ghost; Reject/Confirm reject use Bronze (the design system reserves
Oxblood for irreversible actions like Disconnect).
2026-04-29 23:31:18 -04:00
bvandeusen b895e9ef7e feat(web): add /admin/requests approval queue with override modal
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 23:24:34 -04:00