feat(invitations): add external ID field, CSV header validation, and multi-language parsing#8427
Draft
LWS49 wants to merge 3 commits into
Draft
feat(invitations): add external ID field, CSV header validation, and multi-language parsing#8427LWS49 wants to merge 3 commits into
LWS49 wants to merge 3 commits into
Conversation
37a5204 to
211f960
Compare
211f960 to
67de552
Compare
1314602 to
121b8a7
Compare
23c59ca to
559264e
Compare
546aed9 to
b379882
Compare
… dialog - add ext_id to CourseUser and UserInvitation with unique-per-course index - accept ext_id in bulk CSV and individual invite form; upsert for existing records (enrolled users and pending invitations) - conflicts (duplicate email or ext_id) surface in Failed with reasons
… resolution - validate CSV column headers explicitly instead of checking column count - detect external ID changes on existing users/invitations before applying; surface a confirmation prompt (Keep Existing / Replace) with a side-by-side Current / New External ID table capped at 320px for large uploads - conflict resolution wired into both file upload and individual invite form; file ref preserved on Go Back so admin need not re-select - add ExternalIdResolution and PendingExternalIdConflict types; update invite API and operations layer to detect and return conflict payload - controller rescues PendingExternalIdUpdates, renders jbuilder partial; concern branches on @resolution to populate pending vs updated arrays - i18n: add EN/KO/ZH translations for conflict prompt and new table columns
…canonical column order
121b8a7 to
b8b6ea2
Compare
db90f87 to
b761414
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an optional External ID field to the course invitation flow (CSV and individual form), so admins can assign institution identifiers during invite. The invite-by-file path gains strict CSV header validation that rejects headerless or malformed uploads with a descriptive error, and the parser now accepts headers in English, Chinese, or Korean (any mix) regardless of column order - matching whatever template the admin downloaded. The result dialog is redesigned into per-outcome sections (new invitees, existing users, failed rows) with a dedicated conflict prompt when an uploaded External ID collides with one already assigned to a course member. Template files are replaced with six localized variants (EN/ZH/KO × timeline/no-timeline), and the download link resolves to the locale-matched file. The External ID column in the Manage Users table is moved to sit after Email, matching the CSV column order.
Design decisions
header_alias_map- rather than enforcing canonical column order, the parser builds a{canonical_symbol => column_index}map from the header row, so any permutation of the expected columns is accepted. This lets admins reorder columns in their own spreadsheet tools without rejection.build_header_map!compares the resolved canonical symbol set againstEXPECTED_WITH_TIMELINE/EXPECTED_WITHOUT_TIMELINEusingSet#==. Extra unrecognised columns are silently ignored (they produce no entry in the map); missing or entirely wrong headers raiseCSV::MalformedCSVErrorwith a localised expected-header list.PendingExternalIdUpdatesas a raise-to-surface flow - when an upload would change existing External IDs, the service raises rather than proceeding, and the controller catches it to return apending_conflictresponse. The frontend then shows a confirmation prompt before the admin can re-submit withexternal_id_resolutionset.?urlsuffix. ATEMPLATE_MAPinhelper/index.tsmaps{ locale: { timeline, noTimeline } }to URLs, with EN as fallback for unsupported locales.Regression prevention
Covers: EN/ZH/KO header acceptance, mixed-language headers, order-independent column parsing, headerless CSV rejection, old Phase 1 header rejection, timeline-format CSV on a no-timeline course, error message content, template guard (each static template file passes the parser), External ID round-trip through CSV and individual form, conflict detection and resolution flow, result dialog per-outcome sections.
Manual testing confirmed: happy path invite by CSV (EN/ZH/KO templates), template download locale matching, External ID conflict prompt, individual invite with External ID, ManageUsersTable column order.
Existing behaviour preserved: courses without External ID enabled are unaffected; rows with no External ID column value are accepted with
nil; BOM-prefixed files continue to parse correctly.