From 4b83feb909c680632f37b352249d456fe3a41a4c Mon Sep 17 00:00:00 2001 From: Ruben Date: Tue, 20 Jan 2026 11:51:00 +0100 Subject: [PATCH 1/2] Change literals to import form --- .../admin/stratified_sortitions/upload_sample.html.erb | 2 +- config/locales/ca.yml | 8 ++++---- config/locales/en.yml | 8 ++++---- config/locales/es.yml | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/upload_sample.html.erb b/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/upload_sample.html.erb index 546324a..825dacd 100644 --- a/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/upload_sample.html.erb +++ b/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/upload_sample.html.erb @@ -17,7 +17,7 @@
- <%= t(".help") %> + <%= t(".help").html_safe %>
<%= form_tag samples_path(id: @stratified_sortition.id), multipart: true, class: 'form' do %> diff --git a/config/locales/ca.yml b/config/locales/ca.yml index e824342..fce1171 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -106,14 +106,14 @@ ca: filename_data: "%{filename}, el dia %{date}, número de registres %{count}" gender: Gènere last_upload_date: La última càrrega va ser el dia %{date} - help: Les quatre primeres columnes són dades del candidat, per exemple correu electrònic, DNI, telèfon o adreça i les següents columnes són els estrats definits en el sorteig estratificat. Cada fila representa un candidat. + help: "La mostra ha de ser un fitxer CSV o Excel.
Cada fila respresenta un candidat/candidata." no_data: Sense dades place: Lloc de naixement - remove_uploaded_samples: Eliminar mostres carregades + remove_uploaded_samples: Eliminar tots els registres confirm_remove_uploaded_samples: Estàs segur que vols eliminar totes les mostres carregades? target: Target - title: Pujar un nou cens - uploaded_file: Dades del cens carregat + title: Importar nova mostra + uploaded_file: Dades de la mostra carregada uploaded_files: "S'han agregat els següents fitxers:" no_uploaded_files: "No hi ha fitxers carregats." import_mailer: diff --git a/config/locales/en.yml b/config/locales/en.yml index a4ba016..29b581a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -104,14 +104,14 @@ en: filename_data: File name, on %{date}, number of records %{count} gender: Gender last_upload_date: The last upload was on February 24, 2025 10:41 - help: The first four columns are candidate data, such as email, ID, phone, or address, and the following columns are the strata defined in the stratified sortition. Each row represents a candidate. + help: "The sample must be a CSV or Excel file.
Each row represents a candidate." no_data: No data place: Place of birth - remove_uploaded_samples: Remove uploaded samples + remove_uploaded_samples: Remove all records confirm_remove_uploaded_samples: Are you sure you want to remove all uploaded samples? target: Target - title: Upload a new census - uploaded_file: Census data + title: Import new sample + uploaded_file: Uploaded sample data uploaded_files: "The following files have been added:" no_uploaded_files: "No files uploaded." import_mailer: diff --git a/config/locales/es.yml b/config/locales/es.yml index aca21a1..ba31b6b 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -106,14 +106,14 @@ es: filename_data: Nombre de archivo, el día %{date}, número de registros %{count} gender: Género last_upload_date: La última carga fue el día Febrero 24, 2025 10:41 - help: Las cuatro primeras columnas son datos del candidato, por ejemplo correo electrónico, DNI, teléfono o dirección y las siguientes columnas son los estratos definidos en el sorteo estratificado. Cada fila representa un candidato. + help: "La muestra debe ser un archivo CSV o Excel.
Cada fila representa un candidato/candidata." no_data: No hay datos place: Lugar de nacimiento - remove_uploaded_samples: Eliminar muestras cargadas + remove_uploaded_samples: Eliminar todos los registros confirm_remove_uploaded_samples: ¿Estás seguro de que quieres eliminar todas las muestras cargadas? target: Objetivo - title: Subir un nuevo censo - uploaded_file: Datos del censo + title: Importar nueva muestra + uploaded_file: Datos de la muestra cargada uploaded_files: "Se han agregado los siguientes archivos:" no_uploaded_files: "No hay archivos cargados." import_mailer: From 85cefe77d02bbd3e1908e5cb701bf42f795c6f5d Mon Sep 17 00:00:00 2001 From: Ruben Date: Tue, 10 Mar 2026 10:45:41 +0100 Subject: [PATCH 2/2] Improvements in manage census view --- .../admin/import_sample.rb | 14 +++++++--- .../admin/samples_controller.rb | 4 ++- .../admin/stratified_sortitions_controller.rb | 1 + .../admin/sample_upload_form.rb | 15 ++++++++++ .../stratified_sortition.rb | 1 + .../decidim/stratified_sortitions/stratum.rb | 3 ++ .../stratified_sortitions/substratum.rb | 3 ++ .../stratified_sortitions/upload_sample.js | 22 ++++++++------- .../admin/stratified_sortitions.scss | 4 +++ .../stratified_sortitions/execute.html.erb | 2 +- .../upload_sample.html.erb | 28 +++++-------------- config/locales/ca.yml | 3 ++ config/locales/en.yml | 4 ++- config/locales/es.yml | 4 ++- 14 files changed, 69 insertions(+), 39 deletions(-) create mode 100644 app/forms/decidim/stratified_sortitions/admin/sample_upload_form.rb diff --git a/app/commands/decidim/stratified_sortitions/admin/import_sample.rb b/app/commands/decidim/stratified_sortitions/admin/import_sample.rb index 01cc0ad..e2b4fdf 100644 --- a/app/commands/decidim/stratified_sortitions/admin/import_sample.rb +++ b/app/commands/decidim/stratified_sortitions/admin/import_sample.rb @@ -7,12 +7,12 @@ module Admin class ImportSample < Decidim::Command # Public: Initializes the command. # - # file - The CSV file to import + # form - A SampleUploadForm with the file blob # stratified_sortition - The stratified sortition to import samples to # user - The user performing the import - def initialize(file, stratified_sortition, user) + def initialize(form, stratified_sortition, user) super() - @file = file + @form = form @stratified_sortition = stratified_sortition @user = user end @@ -24,8 +24,14 @@ def initialize(file, stratified_sortition, user) # # Returns nothing. def call + return broadcast(:invalid) unless @form.valid? + + blob = @form.file + file_content = blob.download + filename = blob.filename.to_s + Decidim::StratifiedSortitions::Admin::ImportSampleJob - .perform_later(@file.read, @file.original_filename, @stratified_sortition, @user) + .perform_later(file_content, filename, @stratified_sortition, @user) broadcast(:ok) end end diff --git a/app/controllers/decidim/stratified_sortitions/admin/samples_controller.rb b/app/controllers/decidim/stratified_sortitions/admin/samples_controller.rb index 6dfbdcf..f695eb2 100644 --- a/app/controllers/decidim/stratified_sortitions/admin/samples_controller.rb +++ b/app/controllers/decidim/stratified_sortitions/admin/samples_controller.rb @@ -26,7 +26,9 @@ def download_template def create enforce_permission_to :upload_sample, :stratified_sortition - Decidim::StratifiedSortitions::Admin::ImportSample.call(params[:file], stratified_sortition, current_user) do + @form = form(SampleUploadForm).from_params(params) + + Decidim::StratifiedSortitions::Admin::ImportSample.call(@form, stratified_sortition, current_user) do on(:ok) do flash[:notice] = I18n.t("sample_imports.create.success", scope: "decidim.stratified_sortitions.admin") redirect_to upload_sample_stratified_sortition_path(stratified_sortition) diff --git a/app/controllers/decidim/stratified_sortitions/admin/stratified_sortitions_controller.rb b/app/controllers/decidim/stratified_sortitions/admin/stratified_sortitions_controller.rb index b10f74f..a224f81 100644 --- a/app/controllers/decidim/stratified_sortitions/admin/stratified_sortitions_controller.rb +++ b/app/controllers/decidim/stratified_sortitions/admin/stratified_sortitions_controller.rb @@ -102,6 +102,7 @@ def upload_sample enforce_permission_to :upload_sample, :stratified_sortition if stratified_sortition.strata_and_substrata_configured? @stratified_sortition = stratified_sortition + @form = SampleUploadForm.new @sample_participants_count = @stratified_sortition.sample_participants.count @last_sample = SampleImport.where(stratified_sortition: @stratified_sortition).order(created_at: :desc).first @samples = SampleImport.where(stratified_sortition: @stratified_sortition).order(created_at: :asc) diff --git a/app/forms/decidim/stratified_sortitions/admin/sample_upload_form.rb b/app/forms/decidim/stratified_sortitions/admin/sample_upload_form.rb new file mode 100644 index 0000000..24caba4 --- /dev/null +++ b/app/forms/decidim/stratified_sortitions/admin/sample_upload_form.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Decidim + module StratifiedSortitions + module Admin + class SampleUploadForm < Decidim::Form + include Decidim::HasUploadValidations + + attribute :file, Decidim::Attributes::Blob + + validates :file, presence: true + end + end + end +end diff --git a/app/models/decidim/stratified_sortitions/stratified_sortition.rb b/app/models/decidim/stratified_sortitions/stratified_sortition.rb index 1d0fe4b..7885792 100644 --- a/app/models/decidim/stratified_sortitions/stratified_sortition.rb +++ b/app/models/decidim/stratified_sortitions/stratified_sortition.rb @@ -17,6 +17,7 @@ class StratifiedSortition < ApplicationRecord component_manifest_name "stratified_sortitions" has_many :strata, class_name: "Decidim::StratifiedSortitions::Stratum", foreign_key: "decidim_stratified_sortition_id", dependent: :destroy + has_many :sample_imports, class_name: "Decidim::StratifiedSortitions::SampleImport", dependent: :destroy has_many :sample_participants, class_name: "Decidim::StratifiedSortitions::SampleParticipant", foreign_key: "decidim_stratified_sortition_id", dependent: :destroy has_one :panel_portfolio, class_name: "Decidim::StratifiedSortitions::PanelPortfolio", foreign_key: "decidim_stratified_sortitions_stratified_sortition_id", dependent: :destroy diff --git a/app/models/decidim/stratified_sortitions/stratum.rb b/app/models/decidim/stratified_sortitions/stratum.rb index 4578f4c..2c0d6fc 100644 --- a/app/models/decidim/stratified_sortitions/stratum.rb +++ b/app/models/decidim/stratified_sortitions/stratum.rb @@ -10,6 +10,9 @@ class Stratum < ApplicationRecord belongs_to :stratified_sortition, class_name: "Decidim::StratifiedSortitions::StratifiedSortition", foreign_key: "decidim_stratified_sortition_id" has_many :substrata, class_name: "Decidim::StratifiedSortitions::Substratum", foreign_key: "decidim_stratified_sortitions_stratum_id", dependent: :destroy + has_many :sample_participant_strata, class_name: "Decidim::StratifiedSortitions::SampleParticipantStratum", + foreign_key: "decidim_stratified_sortitions_stratum_id", + dependent: :destroy KINDS = %w(value numeric_range).freeze diff --git a/app/models/decidim/stratified_sortitions/substratum.rb b/app/models/decidim/stratified_sortitions/substratum.rb index 8af346c..c21274e 100644 --- a/app/models/decidim/stratified_sortitions/substratum.rb +++ b/app/models/decidim/stratified_sortitions/substratum.rb @@ -9,6 +9,9 @@ class Substratum < ApplicationRecord include Decidim::TranslatableAttributes belongs_to :stratum, class_name: "Decidim::StratifiedSortitions::Stratum", foreign_key: "decidim_stratified_sortitions_stratum_id" + has_many :sample_participant_strata, class_name: "Decidim::StratifiedSortitions::SampleParticipantStratum", + foreign_key: "decidim_stratified_sortitions_substratum_id", + dependent: :destroy translatable_fields :name end diff --git a/app/packs/src/decidim/stratified_sortitions/upload_sample.js b/app/packs/src/decidim/stratified_sortitions/upload_sample.js index 0bff8cf..500272c 100644 --- a/app/packs/src/decidim/stratified_sortitions/upload_sample.js +++ b/app/packs/src/decidim/stratified_sortitions/upload_sample.js @@ -1,14 +1,16 @@ -document.addEventListener('DOMContentLoaded', () => { - const fileInput = document.getElementById('sample-file-input'); - const submitButton = document.getElementById('sample-submit-button'); +document.addEventListener("DOMContentLoaded", () => { + const form = document.getElementById("sample-upload-form"); + if (!form) return; - if (!fileInput || !submitButton) return; + const modal = form.querySelector("[data-dialog]"); + if (!modal) return; - const toggle = () => { - const hasFile = fileInput.files && fileInput.files.length > 0; - submitButton.disabled = !hasFile; - }; + const saveButton = modal.querySelector("[data-dropzone-save]"); + if (!saveButton) return; - toggle(); - fileInput.addEventListener('change', toggle); + saveButton.addEventListener("click", () => { + // Allow Decidim's upload_field.js handler to run first (updateActiveUploads), + // then submit the form. + setTimeout(() => form.submit(), 100); + }); }); diff --git a/app/packs/stylesheets/decidim/stratified_sortitions/admin/stratified_sortitions.scss b/app/packs/stylesheets/decidim/stratified_sortitions/admin/stratified_sortitions.scss index 91fdf37..dc88511 100644 --- a/app/packs/stylesheets/decidim/stratified_sortitions/admin/stratified_sortitions.scss +++ b/app/packs/stylesheets/decidim/stratified_sortitions/admin/stratified_sortitions.scss @@ -187,4 +187,8 @@ input[readonly] { border-bottom: 2px solid rgba(21, 90, 191, 1); } } +} + +.upload-census-btn { + margin: 0 !important; } \ No newline at end of file diff --git a/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/execute.html.erb b/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/execute.html.erb index 10e4a1b..7e07565 100644 --- a/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/execute.html.erb +++ b/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/execute.html.erb @@ -10,7 +10,7 @@ <% if stratified_sortition.panel_portfolio&.sampled? %> diff --git a/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/upload_sample.html.erb b/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/upload_sample.html.erb index db74c7b..6ada393 100644 --- a/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/upload_sample.html.erb +++ b/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/upload_sample.html.erb @@ -6,13 +6,6 @@
<%= render 'navigation_menu' %>
-
-

-
- <%= t(".upload_file") %> -
-

-
<%= t(".description") %>
@@ -22,21 +15,14 @@ <%= link_to t('.download_template'), download_template_samples_path(id: @stratified_sortition.id), class: 'button button__sm button__secondary' %>
-
- <%= t(".help").html_safe %> -
- <%= form_tag samples_path(id: @stratified_sortition.id), multipart: true, class: 'form' do %> -
- <%= file_field_tag :file, id: 'sample-file-input' %> - - <% if @stratified_sortition.strata_and_substrata_configured? %> - <%= submit_tag t('.upload_file'), id: 'sample-submit-button', class: 'button button__sm button__secondary hollow tiny button--title', disabled: true %> - <% else %> -

<%= t('.configure_strata_first') %>

- <%= submit_tag t('.upload_file'), class: 'button button__sm button__secondary hollow tiny button--title', disabled: true, title: t('.configure_strata_first') %> - <% end %> -
+ <%= decidim_form_for(@form, url: samples_path(id: @stratified_sortition.id), method: :post, html: { id: "sample-upload-form", class: "form", multipart: true }) do |form| %> + <%= form.upload :file, + label: false, + required: true, + help: [t(".help_csv"), t(".help_format")], + button_label: t(".upload_file"), + button_class: "button button__sm button__secondary upload-census-btn" %> <% end %>
diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 4cddc01..4db0ed9 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -183,9 +183,12 @@ ca: confirm_remove_uploaded_samples: Estàs segur que vols eliminar totes les registres? target: Objectiu title: Importar nova mostra + upload_file: Afegir arxiu uploaded_file: Dades de la mostra carregada uploaded_files: "S'han agregat els següents fitxers:" no_uploaded_files: "No hi ha fitxers carregats." + help_csv: Es suporten fitxers CSV + help_format: Fitxer .csv amb el cens import_mailer: subject: "Resultat de la importació de la mostra" import: diff --git a/config/locales/en.yml b/config/locales/en.yml index 909de46..9bf46fe 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -183,10 +183,12 @@ en: strata_not_configured: You must configure strata and substrata first target: Target title: Import new sample - upload_file: Upload a new census + upload_file: Upload file uploaded_file: Uploaded sample data uploaded_files: "The following files have been added:" no_uploaded_files: "No files uploaded." + help_csv: CSV files are supported + help_format: .csv file with census import_mailer: subject: "Sample import result" import: diff --git a/config/locales/es.yml b/config/locales/es.yml index b8435f9..7346130 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -183,10 +183,12 @@ es: strata_not_configured: Primero debes configurar los estratos y subestratos target: Objetivo title: Importar nueva muestra - upload_file: Subir un nuevo censo + upload_file: Subir archivo uploaded_file: Datos de la muestra cargada uploaded_files: "Se han agregado los siguientes archivos:" no_uploaded_files: "No hay archivos cargados." + help_csv: Se admiten archivos CSV + help_format: Archivo .csv con el censo import_mailer: subject: "Resultado de la importación de la muestra" import: