diff --git a/app/models/column.rb b/app/models/column.rb
index 22fa01a6f..5131c77c6 100644
--- a/app/models/column.rb
+++ b/app/models/column.rb
@@ -47,6 +47,9 @@ class Column
procedure.find_column(h_id: h_id)
end
+ def dossier_column? = false
+ def champ_column? = false
+
private
def column_id = "#{table}/#{column}"
diff --git a/app/models/columns/champ_column.rb b/app/models/columns/champ_column.rb
index c032b3a35..3cdb05a92 100644
--- a/app/models/columns/champ_column.rb
+++ b/app/models/columns/champ_column.rb
@@ -45,6 +45,8 @@ class Columns::ChampColumn < Column
end
end
+ def champ_column? = true
+
private
def column_id = "type_de_champ/#{stable_id}"
diff --git a/app/models/columns/dossier_column.rb b/app/models/columns/dossier_column.rb
index 72ec97405..82730b2a9 100644
--- a/app/models/columns/dossier_column.rb
+++ b/app/models/columns/dossier_column.rb
@@ -15,4 +15,6 @@ class Columns::DossierColumn < Column
dossier.followers_instructeurs.map(&:email).join(' ')
end
end
+
+ def dossier_column? = true
end
diff --git a/app/models/export_template.rb b/app/models/export_template.rb
index 0e6e503e4..0328e16b4 100644
--- a/app/models/export_template.rb
+++ b/app/models/export_template.rb
@@ -9,7 +9,7 @@ class ExportTemplate < ApplicationRecord
has_one :procedure, through: :groupe_instructeur
has_many :exports, dependent: :nullify
- enum kind: { zip: "zip" }, _prefix: :template
+ enum kind: { zip: 'zip', csv: 'csv', xlsx: 'xlsx', ods: 'ods' }, _prefix: :template
attribute :dossier_folder, :export_item
attribute :export_pdf, :export_item
@@ -30,6 +30,7 @@ class ExportTemplate < ApplicationRecord
end
def self.default(name: nil, kind: 'zip', groupe_instructeur:)
+ # TODO: remove default values for tabular export
dossier_folder = ExportItem.default(prefix: 'dossier')
export_pdf = ExportItem.default(prefix: 'export')
pjs = groupe_instructeur.procedure.exportables_pieces_jointes.map { |tdc| ExportItem.default_pj(tdc) }
@@ -37,6 +38,10 @@ class ExportTemplate < ApplicationRecord
new(name:, kind:, groupe_instructeur:, dossier_folder:, export_pdf:, pjs:)
end
+ def tabular?
+ kind != 'zip'
+ end
+
def tags
tags_categorized.slice(:individual, :etablissement, :dossier).values.flatten
end
@@ -60,6 +65,19 @@ class ExportTemplate < ApplicationRecord
File.join(dossier_folder.path(dossier), file_path) if file_path.present?
end
+ def dossier_exported_columns = exported_columns.filter { _1.column.dossier_column? }
+
+ def columns_for_stable_id(stable_id)
+ exported_columns
+ .filter { _1.column.champ_column? }
+ .filter { _1.column.stable_id == stable_id }
+ end
+
+ def in_export?(exported_column)
+ @template_exported_columns ||= exported_columns.map(&:column)
+ @template_exported_columns.include?(exported_column.column)
+ end
+
private
def ensure_pjs_are_legit
diff --git a/app/types/export_item_type.rb b/app/types/export_item_type.rb
index e2d1f7014..04c37c6ef 100644
--- a/app/types/export_item_type.rb
+++ b/app/types/export_item_type.rb
@@ -29,7 +29,10 @@ class ExportItemType < ActiveRecord::Type::Value
# ruby -> db
def serialize(value)
- if value.is_a?(ExportItem)
+ case value
+ in NilClass
+ nil
+ in ExportItem
JSON.generate({
template: value.template,
enabled: value.enabled,
diff --git a/app/views/instructeurs/procedures/exports.html.haml b/app/views/instructeurs/procedures/exports.html.haml
index e63eb4472..3e92cd894 100644
--- a/app/views/instructeurs/procedures/exports.html.haml
+++ b/app/views/instructeurs/procedures/exports.html.haml
@@ -6,41 +6,59 @@
[t('.title')]] }
.fr-container
- %h1= t('.title')
- = render Dsfr::CalloutComponent.new(title: nil) do |c|
- - c.with_body do
- %p= t('.export_description', expiration_time: Export::MAX_DUREE_CONSERVATION_EXPORT.in_hours.to_i)
+ .fr-tabs.mb-3
+ %ul.fr-tabs__list{ role: 'tablist' }
+ %li{ role: 'presentation' }
+ %button.fr-tabs__tab.fr-tabs__tab--icon-left{ id: "tabpanel-exports", tabindex: "0", role: "tab", "aria-selected": "true", "aria-controls": "tabpanel-exports-panel" } Liste des exports
+ %li{ role: 'presentation' }
+ %button.fr-tabs__tab.fr-tabs__tab--icon-left{ id: "tabpanel-export-templates", tabindex: "-1", role: "tab", "aria-selected": "false", "aria-controls": "tabpanel-export-templates-panel" } Modèles d'export
- - if @exports.present?
- %div{ data: @exports.any?(&:pending?) ? { controller: "turbo-poll", turbo_poll_url_value: "", turbo_poll_interval_value: 10_000, turbo_poll_max_checks_value: 6 } : {} }
- = render Dossiers::ExportLinkComponent.new(procedure: @procedure, exports: @exports, statut: @statut, count: @dossiers_count, class_btn: 'fr-btn--tertiary', export_url: method(:download_export_instructeur_procedure_path))
-
- - if @exports.any?{_1.format == Export.formats.fetch(:zip)}
- = render Dsfr::AlertComponent.new(title: t('.title_zip'), state: :info, extra_class_names: 'fr-mb-3w') do |c|
+ .fr-tabs__panel.fr-tabs__panel--selected{ id: "tabpanel-exports-panel", role: "tabpanel", "aria-labelledby": "tabpanel-exports", tabindex: "0" }
+ = render Dsfr::CalloutComponent.new(title: nil) do |c|
- c.with_body do
- %p= t('.export_description_zip_html')
+ %p= t('.export_description', expiration_time: Export::MAX_DUREE_CONSERVATION_EXPORT.in_hours.to_i)
- - else
- = t('.no_export_html', expiration_time: Export::MAX_DUREE_CONSERVATION_EXPORT.in_hours.to_i )
+ - if @exports.present?
+ %div{ data: @exports.any?(&:pending?) ? { controller: "turbo-poll", turbo_poll_url_value: "", turbo_poll_interval_value: 10_000, turbo_poll_max_checks_value: 6 } : {} }
+ = render Dossiers::ExportLinkComponent.new(procedure: @procedure, exports: @exports, statut: @statut, count: @dossiers_count, class_btn: 'fr-btn--tertiary', export_url: method(:download_export_instructeur_procedure_path))
- - if @procedure.feature_enabled?(:export_template)
- %h2.fr-mb-1w.fr-mt-8w
- Liste des modèles d'export
- %p.fr-hint-text
- Un modèle d'export permet de personnaliser le nom des fichiers (pour un export au format Zip)
- - if @export_templates.any?
- .fr-table.fr-table--no-caption.fr-mt-5w
- %table
- %thead
- %tr
- %th{ scope: 'col' } Nom du modèle
- %th{ scope: 'col' }= "Groupe instructeur" if @procedure.groupe_instructeurs.many?
- %tbody
- - @export_templates.each do |export_template|
- %tr
- %td= link_to export_template.name, [:edit, :instructeur, @procedure, export_template]
- %td= export_template.groupe_instructeur.label if @procedure.groupe_instructeurs.many?
+ - if @exports.any?{_1.format == Export.formats.fetch(:zip)}
+ = render Dsfr::AlertComponent.new(title: t('.title_zip'), state: :info, extra_class_names: 'fr-mb-3w') do |c|
+ - c.with_body do
+ %p= t('.export_description_zip_html')
- %p
- = link_to [:new, :instructeur, @procedure, :export_template], class: 'fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-add-line' do
- Ajouter un modèle d'export
+ - else
+ = t('.no_export_html', expiration_time: Export::MAX_DUREE_CONSERVATION_EXPORT.in_hours.to_i )
+
+ .fr-tabs__panel.fr-tabs__panel{ id: "tabpanel-export-templates-panel", role: "tabpanel", "aria-labelledby": "tabpanel-export-templates", tabindex: "0" }
+ = render Dsfr::AlertComponent.new(state: :info) do |c|
+ - c.with_body do
+ %p= t('.export_template_list_description_html')
+
+
+ .fr-mt-5w
+ = link_to t('.new_zip_export_template'), new_instructeur_procedure_export_template_path(@procedure), class: "fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-add-line fr-mr-1w"
+ = link_to t('.new_tabular_export_template'), new_instructeur_procedure_export_template_path(@procedure, kind: 'tabular'), class: "fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-add-line"
+
+ .fr-table.fr-table--bordered.fr-table--no-caption.fr-mt-5w
+ .fr-table__wrapper
+ .fr-table__container
+ .fr-table__content
+ %table
+ %thead
+ %tr
+ = tag.th "Nom du modèle", scope: 'col'
+ = tag.th "Format", scope: 'col'
+ = tag.th "Date de création", scope: 'col'
+ = tag.th "Partagé avec (groupe instructeurs)", scope: 'col' if @procedure.groupe_instructeurs.many?
+ = tag.th "Actions", scope: 'col'
+ %tbody
+ - @export_templates.each do |export_template|
+ %tr
+ %td= link_to export_template.name, [:edit, :instructeur, @procedure, export_template]
+ %td= pretty_kind(export_template.kind)
+ %td= l(export_template.created_at)
+ = tag.td export_template.groupe_instructeur.label if @procedure.groupe_instructeurs.many?
+ %td
+ = link_to "Modifier", [:edit, :instructeur, @procedure, export_template], class: "fr-btn fr-btn--icon-left fr-icon-edit-line fr-mr-1w"
+ = link_to "Supprimer", [:instructeur, @procedure, export_template], method: :delete, data: { confirm: "Voulez-vous vraiment supprimer ce modèle ? Il sera supprimé pour tous les instructeurs du groupe"}, class: "fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-delete-line"
diff --git a/config/locales/views/instructeurs/header/en.yml b/config/locales/views/instructeurs/header/en.yml
index dbec9594d..ad343adb4 100644
--- a/config/locales/views/instructeurs/header/en.yml
+++ b/config/locales/views/instructeurs/header/en.yml
@@ -12,7 +12,7 @@ en:
button_delay_expiration: "Keep for one more month"
notification_management: notification management
administrators_list: administrators list
- exports_list: exports list
+ exports_list: exports and export templates
exports_notification_label: A new export is ready to download
statistics: statistics
instructeurs: instructors
diff --git a/config/locales/views/instructeurs/header/fr.yml b/config/locales/views/instructeurs/header/fr.yml
index c13a4ded0..46b2d77c1 100644
--- a/config/locales/views/instructeurs/header/fr.yml
+++ b/config/locales/views/instructeurs/header/fr.yml
@@ -13,7 +13,7 @@ fr:
button_delay_expiration: "Conserver un mois de plus"
notification_management: Gestion des notifications
administrators_list: Voir les administrateurs
- exports_list: Voir les exports
+ exports_list: Voir les exports et modèles d'export
exports_notification_label: Un nouvel export est prêt à être téléchargé
statistics: Statistiques
instructeurs: instructeurs
diff --git a/config/locales/views/instructeurs/procedures/exports/en.yml b/config/locales/views/instructeurs/procedures/exports/en.yml
index 81b0dcf8e..f1f7bc753 100644
--- a/config/locales/views/instructeurs/procedures/exports/en.yml
+++ b/config/locales/views/instructeurs/procedures/exports/en.yml
@@ -18,3 +18,9 @@ en:
no_export_html: You have no export at the moment.
Can't find an export? It may have expired, exports are deleted after %{expiration_time} hours.
+
+ export_template_list_description_html: |
+ Each instructor can configure an export template to customize exports (attachments name for a zip export, columns selection for a tabular export). It will be made available to all instructors assigned to the procedure.
+ Find out more about export template configuration
+ new_zip_export_template: Create zip export template
+ new_tabular_export_template: Create tabular export template
diff --git a/config/locales/views/instructeurs/procedures/exports/fr.yml b/config/locales/views/instructeurs/procedures/exports/fr.yml
index 030fa8345..fa0945652 100644
--- a/config/locales/views/instructeurs/procedures/exports/fr.yml
+++ b/config/locales/views/instructeurs/procedures/exports/fr.yml
@@ -17,3 +17,8 @@ fr:
Vous n'arrivez pas à extraire un export au format .zip sur un réseau d'entreprise ? Essayer de renommer l'archive avec un nom plus court et ré-essayer de l'extraire.
no_export_html: Vous n'avez pas d'export pour le moment.
Vous ne trouvez pas un export ? Il a peut-être expiré, les exports sont supprimés au bout de %{expiration_time} heures.
+ export_template_list_description_html: |
+ Chaque instructeur a la possibilité de configurer un modèle d'export pour personnaliser les exports (nom des pièces jointes pour un export au format zip, sélection des colonnes pour un export tabulaire). Il sera mis à disposition de l'ensemble des instructeurs affectés à la démarche
+ En savoir plus sur la configuration des modèles d'export
+ new_zip_export_template: Créer un modèle d'export zip
+ new_tabular_export_template: Créer un modèle d'export tabulaire
diff --git a/spec/system/instructeurs/instruction_spec.rb b/spec/system/instructeurs/instruction_spec.rb
index 098878767..2473c352e 100644
--- a/spec/system/instructeurs/instruction_spec.rb
+++ b/spec/system/instructeurs/instruction_spec.rb
@@ -138,7 +138,7 @@ describe 'Instructing a dossier:', js: true do
expect(page).to have_text('Nous générons cet export.')
- click_on "Voir les exports"
+ click_on "Voir les exports et modèles d'export"
expect(page).to have_text("Export .csv d’un dossier « à suivre » demandé il y a moins d'une minute")
expect(page).to have_text("En préparation")