Files
sure/app/javascript/controllers/drag_and_drop_import_controller.js
Carlos Adames b56dbdb9eb Feat: /import endpoint & drag-n-drop imports (#501)
* Implement API v1 Imports controller

- Add Api::V1::ImportsController with index, show, and create actions
- Add Jbuilder views for index and show
- Add integration tests
- Implement row generation logic in create action
- Update routes

* Validate import account belongs to family

- Add validation to Import model to ensure account belongs to the same family
- Add regression test case in Api::V1::ImportsControllerTest

* updating docs to be more detailed

* Rescue StandardError instead of bare rescue in ImportsController

* Optimize Imports API and fix documentation

- Implement rows_count counter cache for Imports
- Preload rows in Api::V1::ImportsController#show
- Update documentation to show correct OAuth scopes

* Fix formatting in ImportsControllerTest

* Permit all import parameters and fix unknown attribute error

* Restore API routes for auth, chats, and messages

* removing pr summary

* Fix trailing whitespace and configured? test failure

- Update Import#configured? to use rows_count for performance and consistency
- Mock rows_count in TransactionImportTest
- Fix trailing whitespace in migration

* Harden security and fix mass assignment in ImportsController

- Handle type and account_id explicitly in create action
- Rename import_params to import_config_params for clarity
- Validate type against Import::TYPES

* Fix MintImport rows_count update and migration whitespace

- Update MintImport#generate_rows_from_csv to update rows_count counter cache
- Fix trailing whitespace and final newline in AddRowsCountToImports migration

* Implement full-screen Drag and Drop CSV import on Transactions page

- Add DragAndDropImport Stimulus controller listening on document
- Add full-screen overlay with icon and text to Transactions index
- Update ImportsController to handle direct file uploads via create action
- Add system test for drag and drop functionality

* Implement Drag and Drop CSV upload on Import Upload page

- Add drag-and-drop-import controller to import/uploads/show
- Add full-screen overlay to import/uploads/show
- Annotate upload form and input with drag-and-drop targets
- Add PR_SUMMARY.md

* removing pr summary

* Add file validation to ImportsController

- Validate file size (max 10MB) and MIME type in create action
- Prevent memory exhaustion and invalid file processing
- Defined MAX_CSV_SIZE and ALLOWED_MIME_TYPES in Import model

* Refactor dragLeave logic with counter pattern to prevent flickering

* Extract shared drag-and-drop overlay partial

- Create app/views/imports/_drag_drop_overlay.html.erb
- Update transactions/index and import/uploads/show to use the partial
- Reduce code duplication in views

* Update Brakeman and harden ImportsController security

- Update brakeman to 7.1.2
- Explicitly handle type assignment in ImportsController#create to avoid mass assignment
- Remove :type from permitted import parameters

* Fix trailing whitespace in DragAndDropImportTest

* Don't commit LLM comments as file

* FIX add api validation

---------

Co-authored-by: Carlos Adames <cj@Carloss-MacBook-Air.local>
Co-authored-by: Juan José Mata <jjmata@jjmata.com>
Co-authored-by: sokie <sokysrm@gmail.com>
2026-01-10 16:39:18 +01:00

66 lines
1.8 KiB
JavaScript

import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["input", "form", "overlay"]
dragDepth = 0
connect() {
this.boundDragOver = this.dragOver.bind(this)
this.boundDragEnter = this.dragEnter.bind(this)
this.boundDragLeave = this.dragLeave.bind(this)
this.boundDrop = this.drop.bind(this)
// Listen on the document to catch drags anywhere
document.addEventListener("dragover", this.boundDragOver)
document.addEventListener("dragenter", this.boundDragEnter)
document.addEventListener("dragleave", this.boundDragLeave)
document.addEventListener("drop", this.boundDrop)
}
disconnect() {
document.removeEventListener("dragover", this.boundDragOver)
document.removeEventListener("dragenter", this.boundDragEnter)
document.removeEventListener("dragleave", this.boundDragLeave)
document.removeEventListener("drop", this.boundDrop)
}
dragEnter(event) {
event.preventDefault()
this.dragDepth++
if (this.dragDepth === 1) {
this.overlayTarget.classList.remove("hidden")
}
}
dragOver(event) {
event.preventDefault()
}
dragLeave(event) {
event.preventDefault()
this.dragDepth--
if (this.dragDepth <= 0) {
this.dragDepth = 0
this.overlayTarget.classList.add("hidden")
}
}
drop(event) {
event.preventDefault()
this.dragDepth = 0
this.overlayTarget.classList.add("hidden")
if (event.dataTransfer.files.length > 0) {
const file = event.dataTransfer.files[0]
// Simple validation
if (file.type === "text/csv" || file.name.toLowerCase().endsWith(".csv")) {
this.inputTarget.files = event.dataTransfer.files
this.formTarget.requestSubmit()
} else {
alert("Please upload a valid CSV file.")
}
}
}
}