Skip to content

User-created Price Guide entries: create one inline when missing, edit when wrong #360

Description

@holden

Problem

Every record assumes a linked Price Guide Price (PGP) in the prices table. The PGP is the canonical source for artist, label, format, year range, detail, footnote, and price range — pulled from the imported Goldmine guide. With a linked PGP a record is "real": valuation works, metadata is full, search lights up. Without one, the record is half-real and falls out of most flows.

Today, the only way to link a price is via Price Guide Review (/admin/price_review/:id, see #320), which can ONLY pick from existing PGPs. Two cases break:

  1. Creating a record where no PGP exists yet → the record gets stranded with no price link, no proper metadata, no valuation. Nothing in the UI lets you create the missing PGP.
  2. A linked PGP is wrong (wrong pressing, wrong year, typo'd detail, bad price range) → there's no way to fix it. You can clear the link, but then you're back to case 1.

We used to support user-created PGPs — schema still has prices.user_id (every PGP, imported or not, has one), and there are real user-created entries already in the DB from a 2017 "priceless records" feature that was removed without a replacement (commit 4fe7b67 added it, 41085fa deleted it).

This is a single-user workflow. No flagging, no review queues, no new schema — the user creating or editing the PGP is the same person who'll use it. Just give them the tools.


Proposed Solution — two flows

Flow 1 — Create a PGP inline while creating a record

The record creation form (/records/new, app/views/records/_form.html.erb) currently has no price field at all. Add a price selector that:

  1. Searches the existing prices table as you type (artist + label + detail), same query the Price Review page uses
  2. If a match exists → pick it, done
  3. If nothing matches → "+ Create new price guide entry" opens an inline form that prefills from what you've already entered on the record form (artist, label, format, year, media type)
  4. On submit, the new PGP is created and the record is linked to it in the same save
┌──────────────────────────────────────────────────────────┐
│  New Record                                              │
│  ──────────────────────────────────────────────────────  │
│  Artist *      [ EL DORADOS              ]               │
│  Label *       [ VEE-JAY                 ]               │
│  Format *      [ Singles: 7-inch       ▼ ]               │
│  Year          [ 1955 ]                                  │
│  Condition     [ VG+                   ▼ ]               │
│                                                          │
│  Price Guide Entry                                       │
│  ┌──────────────────────────────────────────────────┐    │
│  │ 🔍 Search…                                        │    │
│  └──────────────────────────────────────────────────┘    │
│   ↳ no matches found for \"EL DORADOS VEE-JAY\"            │
│                                                          │
│     [ + Create new price guide entry ]                   │
│                                                          │
│           [ Cancel ]    [ Create Record ]                │
└──────────────────────────────────────────────────────────┘

Clicking "+ Create new price guide entry" expands inline (Turbo frame, no nav):

│  Price Guide Entry                                       │
│  ┌──────────────────────────────────────────────────┐    │
│  │  Artist *      [ EL DORADOS         ] (from form)│    │
│  │  Label *       [ VEE-JAY            ] (from form)│    │
│  │  Format *      [ Singles: 7-inch  ▼ ] (from form)│    │
│  │  Media type    [ Black vinyl        ]            │    │
│  │  Detail        [ 147 \"At My Front Door\" ]        │    │
│  │  Year range    [ 1955 ] – [ 1958     ]           │    │
│  │  Price low  $  [ 6000 ]                          │    │
│  │  Price high $  [ 8000 ]                          │    │
│  │  Footnote      [____________________________]    │    │
│  │                                                  │    │
│  │       [ Cancel ]   [ Use this entry ]            │    │
│  └──────────────────────────────────────────────────┘    │

"Use this entry" stages the PGP in the form (doesn't save yet — saving happens when the user submits the record). Submitting the record creates the PGP with user_id = current_user.id and links it.

This same inline-create affordance also belongs in the Price Review page (#320) for existing records that came in without a PGP — the empty state of search there should show the same "+ Create new price guide entry" CTA, prefilled from the record.


Flow 2 — Edit a wrong PGP, or clear and recreate

When a record's linked PGP is wrong, the user has two options from the record show page:

A) Edit the PGP directly (preferred when only some fields are wrong — bad price range, typo in detail, missing footnote):

Add an "Edit price guide entry" button to the price card alongside the existing "Review / Change Price" button. It opens an edit form for the linked Price record. Saving updates the PGP — and because PGPs are shared (prices has_many :records), this affects every record linked to it. That's correct behavior in a single-user system, but the UI should warn:

┌──────────────────────────────────────────────────────────┐
│  Edit Price Guide Entry                                  │
│  ──────────────────────────────────────────────────────  │
│  ⓘ This entry is linked to 3 records. Editing affects    │
│     all of them.                                         │
│                                                          │
│  Artist *      [ EL DORADOS              ]               │
│  Label *       [ VEE-JAY                 ]               │
│  Format *      [ Singles: 7-inch       ▼ ]               │
│  Detail        [ 147 \"At My Front Door\"  ]               │
│  Year range    [ 1955 ] – [ 1958          ]              │
│  Price low  $  [ 6000 ]                                  │
│  Price high $  [ 8000 ]                                  │
│  Footnote      [____________________________]            │
│                                                          │
│            [ Cancel ]    [ Save changes ]                │
└──────────────────────────────────────────────────────────┘

B) Clear the link and create a fresh one (when the linked PGP is for a totally different pressing — wrong artist, wrong format, wrong record entirely):

This is already half-built. The Price Review page (#320) has a "Clear price link" button. After clearing, the page shows the search panel with "No price linked." The fix: the empty-search-results state on Price Review should expose the same "+ Create new price guide entry" CTA from Flow 1, prefilled from the record. Clear → search yields nothing → create → linked. One screen, no dead ends.


Distinguishing user-created vs imported PGPs

prices.user_id is required on every PGP, so we can't just check IS NOT NULL. Imports are presumably attributed to a known system/admin user. Before building anything, verify in production which user_id the imported entries point at — then user-created PGPs are simply WHERE user_id != <import_user_id>.

No new column, no new table, no schema change. If we ever need a sortier label like 👤 User-submitted next to entries in Price Review search, it can derive from this same query.


Things to design around

  1. Duplicates — two records each create a near-identical PGP for the same pressing instead of sharing one. Mitigation: the search-then-create pattern in Flow 1 already pushes users to pick the existing entry first; the create CTA only appears after a search returns nothing.
  2. Editing a shared PGP affects all linked records — surface this with the "linked to N records" notice in the edit form. Single-user system, so the trade-off is fine; just make it visible.
  3. Bad price ranges — basic form validation: price_low <= price_high, both positive integers. No fancy outlier detection.

Files involved (for the implementer)

  • app/controllers/api/prices_controller.rb — add #create and #update
  • app/controllers/admin/price_review_controller.rb — add create CTA to empty state
  • app/views/admin/price_review/show.html.erb:142-149 — replace generic empty state with create CTA
  • app/views/records/_form.html.erb — add price selector with search + inline create
  • app/views/admin/records/show.html.erb — add "Edit price guide entry" button next to "Review / Change Price"
  • app/views/records/show.html.erb — same edit button if appropriate for non-admin
  • config/routes.rb:54 — extend prices: [:index] to include :create, :update
  • app/javascript/controllers/form_autocomplete_controller.js — already supports create-on-the-fly pattern (lines 137-222), reuse for the price selector

Verification checklist

Flow 1 — create during record creation

  • /records/new form has a price selector that searches existing PGPs as you type
  • Search results dropdown matches the same query Price Review uses
  • Empty results show a "+ Create new price guide entry" CTA
  • CTA opens an inline form prefilled with artist, label, format, year, media type from the record form
  • Submitting the record creates the PGP and links it in one action
  • New PGP has user_id = current_user.id

Flow 1b — create from Price Review for existing records

  • On /admin/price_review/:id with no search results, the same "+ Create new price guide entry" CTA appears
  • After clearing a wrong linked PGP, the user can search → fail → create in the same screen, no dead end

Flow 2 — edit a wrong PGP

  • Record show page has an "Edit price guide entry" button next to "Review / Change Price"
  • Edit form shows "⚠ Linked to N records" notice when N > 1
  • Saving updates the PGP, all linked records reflect the change
  • Validation: price_low <= price_high, both positive

Out of scope / won't do

  • ❌ No flagging system, no review queue, no badges
  • ❌ No new schema columns (source, verified_at, etc.) — derive from existing user_id
  • ❌ No multi-user trust hierarchy — single-user system

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions