Match facilitator affiliations exactly and case-sensitively#1815
Conversation
|
|
||
| scope :facilitators, -> { where("title LIKE ?", "%facilitator%") } | ||
| # Only the exact, case-sensitive title "Facilitator" counts — variants like | ||
| # "Lead Facilitator" or "facilitator" are deliberately excluded. BINARY forces |
There was a problem hiding this comment.
🤖 From Claude: BINARY is required here — the DB is MySQL/Trilogy with a case-insensitive default collation, so a plain where(title: "Facilitator") would still match "facilitator". This makes the SQL scope agree with the case-sensitive Ruby == in #facilitator?.
|
|
||
| def facilitator_since_date | ||
| @facilitator_since_date ||= affiliations.where("title LIKE ?", "%Facilitator%").minimum(:start_date) | ||
| @facilitator_since_date ||= affiliations.facilitators.minimum(:start_date) |
There was a problem hiding this comment.
🤖 From Claude: These two methods previously matched fuzzily on their own (LIKE '%Facilitator%'); routing them through .facilitators keeps the exact-match rule in one place.
Facilitator status, "facilitator since" dates, tenure badges, program status, the org-active determination, the editor card highlight, and the live JS figures all keyed off a fuzzy match (`LIKE '%facilitator%'` / case-insensitive include), which swept in variants like "Lead Facilitator" and "Co-Facilitator". Restrict to the exact, case-sensitive title "Facilitator" (whitespace-trimmed) so a single canonical role drives those decisions. Since the highlight rule (previously the only exact matcher, `exact_facilitator?`) and the business-logic rule now agree, collapse them into one exact `#facilitator?` / `.facilitators` and drop the redundant `exact_facilitator?`. BINARY is needed for case sensitivity under MySQL's default collation; the JS controllers mirror the same trimmed exact rule. The cosmetic title-chip sort heuristics are intentionally left fuzzy — they order display pills, not the facilitator-status determination. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
b72c70d to
6d617bd
Compare
| # A facilitator affiliation is one whose title is *exactly* "Facilitator" | ||
| # (trimmed, case-sensitive). Variants like "Lead Facilitator" or "facilitator" | ||
| # are deliberately excluded. Mirrors the .facilitators scope so in-memory and | ||
| # SQL checks agree. |
There was a problem hiding this comment.
🤖 From Claude: Collapsed the former exact_facilitator? into #facilitator? — now that business logic is exact too, the two predicates were identical. BINARY TRIM mirrors this strip + case-sensitivity in SQL.
| // Facilitator since/end — same logic filtered by title. Mirror | ||
| // Affiliation#facilitator?: an exact, case-sensitive match on "Facilitator" | ||
| // (trimmed), so the live figure matches what the server will render. | ||
| const facilitatorAffiliations = affiliations.filter(a => |
There was a problem hiding this comment.
🤖 From Claude: Both facilitator Stimulus controllers now mirror the server's exact, trimmed rule so the live figures match what gets rendered/saved (per the CLAUDE.md JS-mirroring convention).
REVIEW NEEDED: 📖 Read — light-logic: small, contained logic changes with low blast radius
What is the goal of this PR and why is this important?
LIKE '%facilitator%'/ case-insensitiveinclude?), which counted variants like "Lead Facilitator" and "Co-Facilitator".How did you approach the change?
Affiliation#facilitator?and the.facilitatorsscope exact (BINARY TRIM(title) = "Facilitator"; BINARY is needed for case sensitivity under MySQL's default collation). Every call site flows through one of them.exact_facilitator?, previously the only exact matcher) and the business-logic rule now agree, so I collapsed them into one#facilitator?and dropped the redundantexact_facilitator?.affiliation_dates,affiliation_facilitator_warning) to mirror the same trimmed exact rule.Anything else to add?