Adds a multi-step CSV import panel to /customers/create that appears once a customer type is
selected
Three new API endpoints handle the pipeline: upload → preview → approve
No records are written until the user explicitly clicks Approve Import, giving full visibility
before commit
How it works
Pick a file — Upload a .csv file; server stores it in the temp directory and returns the column
headers
Map fields — Auto-matches CSV columns to customer type attributes by normalized name; unmatched
attributes show a dropdown of remaining (unused) columns
Preview — Submits the file + mapping to the server which processes every row and returns a status
per row: OK (will import), Duplicate (already exists — reported but still importable), or Empty
(will be skipped). No data is written at this stage
Approve Import — Re-processes the file server-side and inserts all non-empty rows. Per-row errors
are caught individually and reported in the summary without stopping the rest of the import. Audit
log entries are written for every inserted record
Files changed
┌────────────────────────┬───────────────────────────────────────────────────────────────────────┐
│ File │ Change │
├────────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ routes/web.php │ 3 new POST routes: /customers/import/upload, │
│ │ /customers/import/preview, /customers/import/approve │
├────────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ CustomerController.php │ importUpload, importPreview, importApprove methods; reuses existing │
│ │ FileImportService │
├────────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ customers/create.php │ x-data moved to .content-stack; CSV import panel added below the │
│ │ manual entry form │
├────────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ public/js/app.js │ customerForm extended with 14 state properties and 10 methods for the │
│ │ full import flow │
├────────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ public/css/site.css │ Mapping table, scrollable preview table with sticky header, status │
│ │ badges, stats bar │
└────────────────────────┴───────────────────────────────────────────────────────────────────────┘
Test plan
Select a customer type — import panel appears; switching types resets the import state
Upload a valid CSV — headers are detected and auto-matched where column names align
Unmatched attributes show a dropdown of remaining columns; selecting one removes it from other
dropdowns
Preview returns correct OK / Duplicate / Empty counts without inserting anything
Approve inserts expected rows; check /customers list and audit table
Upload a CSV with some intentionally bad rows — errors are reported per-row, remaining rows still
insert
Confirm no records appear after Preview (before Approve)
Confirm the manual Save Customer form still works normally alongside the import panel
● Add CSV import to Customer create screen
Summary
- Adds a multi-step CSV import panel to /customers/create that appears once a customer type is
selected
- Three new API endpoints handle the pipeline: upload → preview → approve
- No records are written until the user explicitly clicks Approve Import, giving full visibility
before commit
How it works
1. Pick a file — Upload a .csv file; server stores it in the temp directory and returns the column
headers
2. Map fields — Auto-matches CSV columns to customer type attributes by normalized name; unmatched
attributes show a dropdown of remaining (unused) columns
3. Preview — Submits the file + mapping to the server which processes every row and returns a status
per row: OK (will import), Duplicate (already exists — reported but still importable), or Empty
(will be skipped). No data is written at this stage
4. Approve Import — Re-processes the file server-side and inserts all non-empty rows. Per-row errors
are caught individually and reported in the summary without stopping the rest of the import. Audit
log entries are written for every inserted record
Files changed
┌────────────────────────┬───────────────────────────────────────────────────────────────────────┐
│ File │ Change │
├────────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ routes/web.php │ 3 new POST routes: /customers/import/upload, │
│ │ /customers/import/preview, /customers/import/approve │
├────────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ CustomerController.php │ importUpload, importPreview, importApprove methods; reuses existing │
│ │ FileImportService │
├────────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ customers/create.php │ x-data moved to .content-stack; CSV import panel added below the │
│ │ manual entry form │
├────────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ public/js/app.js │ customerForm extended with 14 state properties and 10 methods for the │
│ │ full import flow │
├────────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ public/css/site.css │ Mapping table, scrollable preview table with sticky header, status │
│ │ badges, stats bar │
└────────────────────────┴───────────────────────────────────────────────────────────────────────┘
Test plan
- Select a customer type — import panel appears; switching types resets the import state
- Upload a valid CSV — headers are detected and auto-matched where column names align
- Unmatched attributes show a dropdown of remaining columns; selecting one removes it from other
dropdowns
- Preview returns correct OK / Duplicate / Empty counts without inserting anything
- Approve inserts expected rows; check /customers list and audit table
- Upload a CSV with some intentionally bad rows — errors are reported per-row, remaining rows still
insert
- Confirm no records appear after Preview (before Approve)
- Confirm the manual Save Customer form still works normally alongside the import panel
● Add CSV import to Customer create screen
Summary
How it works
Files changed
┌────────────────────────┬───────────────────────────────────────────────────────────────────────┐ │ File │ Change │ ├────────────────────────┼───────────────────────────────────────────────────────────────────────┤ │ routes/web.php │ 3 new POST routes: /customers/import/upload, │ │ │ /customers/import/preview, /customers/import/approve │ ├────────────────────────┼───────────────────────────────────────────────────────────────────────┤ │ CustomerController.php │ importUpload, importPreview, importApprove methods; reuses existing │ │ │ FileImportService │ ├────────────────────────┼───────────────────────────────────────────────────────────────────────┤ │ customers/create.php │ x-data moved to .content-stack; CSV import panel added below the │ │ │ manual entry form │ ├────────────────────────┼───────────────────────────────────────────────────────────────────────┤ │ public/js/app.js │ customerForm extended with 14 state properties and 10 methods for the │ │ │ full import flow │ ├────────────────────────┼───────────────────────────────────────────────────────────────────────┤ │ public/css/site.css │ Mapping table, scrollable preview table with sticky header, status │ │ │ badges, stats bar │ └────────────────────────┴───────────────────────────────────────────────────────────────────────┘
Test plan
13193214a2.