decidim-stratified_sortitions 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +91 -0
  4. data/Rakefile +42 -0
  5. data/app/cells/decidim/stratified_sortitions/stratified_sortition_cell.rb +21 -0
  6. data/app/cells/decidim/stratified_sortitions/stratified_sortition_l_cell.rb +22 -0
  7. data/app/cells/decidim/stratified_sortitions/stratified_sortition_metadata_cell.rb +63 -0
  8. data/app/commands/decidim/stratified_sortitions/admin/create_stratified_sortition.rb +84 -0
  9. data/app/commands/decidim/stratified_sortitions/admin/destroy_stratified_sortition.rb +49 -0
  10. data/app/commands/decidim/stratified_sortitions/admin/duplicate_stratified_sortition.rb +74 -0
  11. data/app/commands/decidim/stratified_sortitions/admin/import_sample.rb +40 -0
  12. data/app/commands/decidim/stratified_sortitions/admin/remove_uploaded_samples.rb +53 -0
  13. data/app/commands/decidim/stratified_sortitions/admin/update_stratified_sortition.rb +174 -0
  14. data/app/controllers/concerns/decidim/stratified_sortitions/orderable_stratified_sortitions.rb +37 -0
  15. data/app/controllers/concerns/decidim/stratified_sortitions/strata_charts_data.rb +91 -0
  16. data/app/controllers/decidim/stratified_sortitions/admin/application_controller.rb +19 -0
  17. data/app/controllers/decidim/stratified_sortitions/admin/samples_controller.rb +143 -0
  18. data/app/controllers/decidim/stratified_sortitions/admin/stratified_sortitions_controller.rb +235 -0
  19. data/app/controllers/decidim/stratified_sortitions/application_controller.rb +13 -0
  20. data/app/controllers/decidim/stratified_sortitions/charts_pdf_controller_helper.rb +17 -0
  21. data/app/controllers/decidim/stratified_sortitions/stratified_sortitions_controller.rb +61 -0
  22. data/app/forms/decidim/stratified_sortitions/admin/sample_upload_form.rb +15 -0
  23. data/app/forms/decidim/stratified_sortitions/admin/stratified_sortitions_form.rb +155 -0
  24. data/app/forms/decidim/stratified_sortitions/admin/stratum_form.rb +69 -0
  25. data/app/forms/decidim/stratified_sortitions/admin/substratum_form.rb +26 -0
  26. data/app/helpers/decidim/stratified_sortitions/admin/stratified_sortitions_helper.rb +18 -0
  27. data/app/helpers/decidim/stratified_sortitions/application_helper.rb +85 -0
  28. data/app/helpers/decidim/stratified_sortitions/charts_pdf_helper.rb +73 -0
  29. data/app/helpers/decidim/stratified_sortitions/stratified_sortition_cells_helper.rb +56 -0
  30. data/app/javascript/channels/sample_import_progress.js +12 -0
  31. data/app/jobs/decidim/stratified_sortitions/admin/execute_sortition_job.rb +21 -0
  32. data/app/jobs/decidim/stratified_sortitions/admin/import_sample_job.rb +170 -0
  33. data/app/jobs/decidim/stratified_sortitions/admin/remove_samples_job.rb +19 -0
  34. data/app/jobs/decidim/stratified_sortitions/admin/sortition_results_export_job.rb +28 -0
  35. data/app/mailers/decidim/stratified_sortitions/admin/import_mailer.rb +26 -0
  36. data/app/models/decidim/stratified_sortitions/application_record.rb +9 -0
  37. data/app/models/decidim/stratified_sortitions/panel_portfolio.rb +165 -0
  38. data/app/models/decidim/stratified_sortitions/sample_import.rb +14 -0
  39. data/app/models/decidim/stratified_sortitions/sample_participant.rb +12 -0
  40. data/app/models/decidim/stratified_sortitions/sample_participant_stratum.rb +11 -0
  41. data/app/models/decidim/stratified_sortitions/stratified_sortition.rb +74 -0
  42. data/app/models/decidim/stratified_sortitions/stratum.rb +22 -0
  43. data/app/models/decidim/stratified_sortitions/substratum.rb +19 -0
  44. data/app/packs/entrypoints/decidim_stratified_sortitions.js +10 -0
  45. data/app/packs/entrypoints/decidim_stratified_sortitions_admin.js +9 -0
  46. data/app/packs/entrypoints/decidim_stratified_sortitions_admin.scss +1 -0
  47. data/app/packs/images/decidim/stratified_sortitions/icon.svg +1 -0
  48. data/app/packs/src/decidim/stratified_sortitions/application.js +3 -0
  49. data/app/packs/src/decidim/stratified_sortitions/results_tabs.js +59 -0
  50. data/app/packs/src/decidim/stratified_sortitions/stratum_fields.js +302 -0
  51. data/app/packs/src/decidim/stratified_sortitions/substratum_fields.js +165 -0
  52. data/app/packs/src/decidim/stratified_sortitions/upload_sample.js +16 -0
  53. data/app/packs/stylesheets/decidim/stratified_sortitions/admin/stratified_sortitions.scss +210 -0
  54. data/app/packs/stylesheets/decidim/stratified_sortitions/charts_pdf.scss +148 -0
  55. data/app/packs/stylesheets/stratified_sortitions.scss +56 -0
  56. data/app/permissions/decidim/stratified_sortitions/admin/permissions.rb +74 -0
  57. data/app/permissions/decidim/stratified_sortitions/permissions.rb +23 -0
  58. data/app/presenters/decidim/stratified_sortitions/admin_log/stratified_sortition_presenter.rb +35 -0
  59. data/app/queries/decidim/stratified_sortitions/filtered_stratified_sortitions.rb +39 -0
  60. data/app/serializers/decidim/stratified_sortitions/sortition_result_serializer.rb +116 -0
  61. data/app/services/decidim/stratified_sortitions/charts_pdf_generator.rb +96 -0
  62. data/app/services/decidim/stratified_sortitions/fair_sortition_service.rb +247 -0
  63. data/app/services/decidim/stratified_sortitions/leximin/constraint_builder.rb +180 -0
  64. data/app/services/decidim/stratified_sortitions/leximin/distribution_solver.rb +217 -0
  65. data/app/services/decidim/stratified_sortitions/leximin/feasibility_checker.rb +222 -0
  66. data/app/services/decidim/stratified_sortitions/leximin/panel_generator.rb +149 -0
  67. data/app/services/decidim/stratified_sortitions/leximin/panel_sampler.rb +125 -0
  68. data/app/services/decidim/stratified_sortitions/leximin_selector.rb +136 -0
  69. data/app/services/decidim/stratified_sortitions/sortition_results_exporter.rb +161 -0
  70. data/app/views/decidim/admin/components/index.html.erb +54 -0
  71. data/app/views/decidim/stratified_sortitions/admin/import_mailer/import.html.erb +22 -0
  72. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_confirm_execute_modal.html.erb +19 -0
  73. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_confirm_participants_modal.html.erb +20 -0
  74. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_form.html.erb +55 -0
  75. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_navigation_menu.html.erb +10 -0
  76. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_strata.html.erb +22 -0
  77. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_stratum.html.erb +75 -0
  78. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_substrata.html.erb +34 -0
  79. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_substratum.html.erb +54 -0
  80. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/edit.html.erb +21 -0
  81. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/execute.html.erb +155 -0
  82. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/export_charts_pdf.html.erb +38 -0
  83. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/index.html.erb +75 -0
  84. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/new.html.erb +21 -0
  85. data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/upload_sample.html.erb +117 -0
  86. data/app/views/decidim/stratified_sortitions/shared/_strata_charts.html.erb +21 -0
  87. data/app/views/decidim/stratified_sortitions/stratified_sortitions/_results_count.html.erb +15 -0
  88. data/app/views/decidim/stratified_sortitions/stratified_sortitions/_stratified_sortition.html.erb +1 -0
  89. data/app/views/decidim/stratified_sortitions/stratified_sortitions/_stratified_sortitions.html.erb +13 -0
  90. data/app/views/decidim/stratified_sortitions/stratified_sortitions/_tags.html.erb +9 -0
  91. data/app/views/decidim/stratified_sortitions/stratified_sortitions/index.html.erb +22 -0
  92. data/app/views/decidim/stratified_sortitions/stratified_sortitions/index.js.erb +5 -0
  93. data/app/views/decidim/stratified_sortitions/stratified_sortitions/show.html.erb +142 -0
  94. data/app/views/layouts/decidim/admin/_sidebar_menu.html.erb +45 -0
  95. data/app/views/layouts/decidim/stratified_sortitions/charts_pdf.html.erb +14 -0
  96. data/config/assets.rb +24 -0
  97. data/config/locales/ca.yml +361 -0
  98. data/config/locales/en.yml +360 -0
  99. data/config/locales/es.yml +360 -0
  100. data/config/locales/oc.yml +2 -0
  101. data/config/routes.rb +1 -0
  102. data/db/migrate/20251023103900_create_decidim_stratified_sortitions_stratified_sortitions.rb +18 -0
  103. data/db/migrate/20251104120000_create_stratified_sortitions_strata_and_substrata.decidim_stratified_sortitions.rb +27 -0
  104. data/db/migrate/20251219120000_create_sample_imports.rb +17 -0
  105. data/db/migrate/20251219120100_create_sample_participants.rb +19 -0
  106. data/db/migrate/20251219120200_create_sample_participant_strata.rb +16 -0
  107. data/db/migrate/20251230130000_change_value_to_text_in_substrata.rb +11 -0
  108. data/db/migrate/20260102114744_add_status_to_stratified_sortitions.rb +11 -0
  109. data/db/migrate/20260109105643_allow_null_value_in_substrata.rb +10 -0
  110. data/db/migrate/20260113121726_add_position_to_strata_and_substrata.rb +8 -0
  111. data/db/migrate/20260127100000_rename_weighing_to_max_quota_percentage.rb +12 -0
  112. data/db/migrate/20260127120000_create_panel_portfolios.rb +46 -0
  113. data/db/migrate/20260518000000_add_foreign_key_to_stratified_sortitions_component.rb +10 -0
  114. data/db/migrate/20260521000000_add_cascade_to_stratified_sortitions_foreign_keys.rb +64 -0
  115. data/db/migrate/20260616000000_add_execution_error_to_stratified_sortitions.rb +11 -0
  116. data/lib/decidim/stratified_sortitions/admin.rb +10 -0
  117. data/lib/decidim/stratified_sortitions/admin_engine.rb +49 -0
  118. data/lib/decidim/stratified_sortitions/component.rb +41 -0
  119. data/lib/decidim/stratified_sortitions/engine.rb +34 -0
  120. data/lib/decidim/stratified_sortitions/seeds.rb +24 -0
  121. data/lib/decidim/stratified_sortitions/test/factories.rb +80 -0
  122. data/lib/decidim/stratified_sortitions/version.rb +13 -0
  123. data/lib/decidim/stratified_sortitions.rb +29 -0
  124. metadata +222 -0
@@ -0,0 +1,117 @@
1
+ <% add_decidim_page_title(t(".title")) %>
2
+ <div class="item_show__header stratified_sortition_header">
3
+ <h1 class="item_show__header-title">
4
+ <%= t(".title") %>
5
+ </h1>
6
+ </div>
7
+ <%= render 'navigation_menu' %>
8
+ <div class="card mt-12">
9
+ <div class="item_show__header">
10
+ <h1 class="item_show__header-title">
11
+ <div>
12
+ <%= t(".upload_census") %>
13
+ </div>
14
+ </h1>
15
+ </div>
16
+ <div class="mt-1">
17
+ <%= t(".description").html_safe %>
18
+ </div>
19
+ <div class="card-section mt-4 example-file-census-container">
20
+ <table>
21
+ <tr>
22
+ <td><%= t('.example_headers.personal_data_1') %></td>
23
+ <td><%= t('.example_headers.personal_data_2') %></td>
24
+ <td><%= t('.example_headers.personal_data_3') %></td>
25
+ <td><%= t('.example_headers.personal_data_4') %></td>
26
+ <td><%= t('.example_headers.gender') %></td>
27
+ <td><%= t('.example_headers.age') %></td>
28
+ </tr>
29
+ <% Array(t('.example_rows')).each do |row| %>
30
+ <tr>
31
+ <% row.each do |cell| %>
32
+ <td><%= cell %></td>
33
+ <% end %>
34
+ </tr>
35
+ <% end %>
36
+ </table>
37
+ <div class="mt-12">
38
+ <button type="button" class="button button__sm button__secondary" data-toggle="sample-template-dropdown">
39
+ <%= t(".download_template") %>
40
+ <%= icon "arrow-down-s-line", class: "dropdown-filter-icon" %>
41
+ </button>
42
+ <div class="dropdown-pane" id="sample-template-dropdown" data-dropdown data-position="bottom" data-alignment="left" data-auto-focus="true" data-close-on-click="true">
43
+ <ul class="vertical menu add-components">
44
+ <%= link_to download_template_samples_path(id: @stratified_sortition.id, format: :csv) do %>
45
+ <li><%= t(".download_template_csv") %></li>
46
+ <% end %>
47
+ <%= link_to download_template_samples_path(id: @stratified_sortition.id, format: :xlsx) do %>
48
+ <li><%= t(".download_template_xlsx") %></li>
49
+ <% end %>
50
+ </ul>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ <div class="row mt-4 <%= 'upload-disabled-wrapper' if @stratified_sortition.executed? %>">
55
+ <%= decidim_form_for(@form, url: samples_path(id: @stratified_sortition.id), method: :post, html: { id: "sample-upload-form", class: "form", multipart: true }) do |form| %>
56
+ <%= form.upload :file,
57
+ label: false,
58
+ required: true,
59
+ help: [t(".help_csv"), t(".help_submit")],
60
+ button_label: t(".upload_file"),
61
+ button_class: "button button__sm button__secondary upload-census-btn" %>
62
+ <% end %>
63
+ </div>
64
+ </div>
65
+ <div class="card mt-16">
66
+ <div class="item_show__header">
67
+ <h1 class="item_show__header-title">
68
+ <div>
69
+ <%= t(".uploaded_file") %>
70
+ </div>
71
+ </h1>
72
+ </div>
73
+ <div class="mt-2">
74
+ <% if @stratified_sortition.status == "pending" %>
75
+ <%= link_to t('.remove_uploaded_samples'), remove_multiple_samples_path(id: @stratified_sortition.id), class: 'button button__sm button__secondary', data: { method: :delete, confirm: t('.confirm_remove_uploaded_samples') } %>
76
+ <% end %>
77
+ </div>
78
+ <div class="mt-4 mb-4">
79
+ <div class="flex justify-between gap-x-4">
80
+ <span>
81
+ <%= t(".count_uploaded_files", count: @sample_participants_count).html_safe %>
82
+ </span>
83
+ <% if @last_sample.present? %>
84
+ <span class="last-census-upload">
85
+ <%= t(".last_upload_date", date: @last_sample.created_at.strftime("%d/%m/%Y %H:%M")) %>
86
+ </span>
87
+ <% end %>
88
+ </div>
89
+ </div>
90
+ <div class="mt-4 mb-4">
91
+ <div class="row chart-row">
92
+ <div class="col-md-6 charts-column">
93
+ <%= render partial: "decidim/stratified_sortitions/shared/strata_charts", locals: { strata_data: @strata_data, title: t(".target") } %>
94
+ </div>
95
+ <div class="col-md-6 charts-column target">
96
+ <%= render partial: "decidim/stratified_sortitions/shared/strata_charts", locals: { strata_data: @candidates_data, title: t(".candidates") } %>
97
+ </div>
98
+ </div>
99
+ <div class="mt-16 census_file_type">
100
+ <span>
101
+ <%= t(".uploaded_files") %>
102
+ </span>
103
+ <% if @samples.empty? %>
104
+ <p><%= t('.no_uploaded_files') %></p>
105
+ <% else %>
106
+ <div>
107
+ <% @samples.each do |sample| %>
108
+ <p><%= t('.filename_data', filename: sample.filename, date: sample.created_at.strftime("%d/%m/%Y %H:%M"), count: sample.imported_rows) %></p>
109
+ <% end %>
110
+ </div>
111
+ <% end %>
112
+ </div>
113
+ </div>
114
+ </div>
115
+
116
+ <%= append_javascript_pack_tag "decidim_stratified_sortitions_admin" %>
117
+ <%= append_stylesheet_pack_tag "decidim_stratified_sortitions_admin" %>
@@ -0,0 +1,21 @@
1
+ <span>
2
+ <h3 class="h4 mb-4">
3
+ <%= local_assigns[:title] || "" %>
4
+ </h3>
5
+ </span>
6
+ <% (strata_data || []).each do |stratum_data| %>
7
+ <div class="chart-col">
8
+ <b><%= translated_attribute(stratum_data[:stratum].name).upcase %></b>
9
+ <% if stratum_data[:chart_data].present? %>
10
+ <div style="width:100%;min-width:0">
11
+ <%= pie_chart stratum_data[:chart_data],
12
+ legend: "bottom",
13
+ donut: false %>
14
+ </div>
15
+ <% else %>
16
+ <p class="text-muted">
17
+ <%= t("no_data", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
18
+ </p>
19
+ <% end %>
20
+ </div>
21
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <% executed = stratified_sortition.panel_portfolio&.sampled? %>
2
+ <% count = executed ? stratified_sortition.panel_portfolio.selected_panel.size : stratified_sortition.num_candidates %>
3
+
4
+ <div id="stratified-sortition-<%= stratified_sortition.id %>-results-count">
5
+ <span class="stratified-sortition__aside__count-number">
6
+ <%= count %>
7
+ </span>
8
+ <span class="stratified-sortition__aside__count-text">
9
+ <% if executed %>
10
+ <%= t(".count_selected", count:) %>
11
+ <% else %>
12
+ <%= t(".count", count:) %>
13
+ <% end %>
14
+ </span>
15
+ </div>
@@ -0,0 +1 @@
1
+ <%= card_for stratified_sortition %>
@@ -0,0 +1,13 @@
1
+ <% if @stratified_sortitions.any? %>
2
+ <h2 class="h5 md:h3 decorator" aria-live="polite" aria-atomic="true"><%= t("count", scope: "decidim.stratified_sortitions.stratified_sortitions.stratified_sortitions_count", count: @stratified_sortitions.total_count) %></h2>
3
+
4
+ <%= order_selector available_orders, i18n_scope: "decidim.stratified_sortitions.stratified_sortitions.orders" %>
5
+
6
+ <div class="card__list-list">
7
+ <%= render @stratified_sortitions %>
8
+ </div>
9
+
10
+ <%= decidim_paginate @stratified_sortitions %>
11
+ <% else %>
12
+ <%= cell "decidim/announcement", t("empty", scope: "decidim.stratified_sortitions.stratified_sortitions.index") %>
13
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <ul>
2
+ <li>
3
+ <%= link_to t("filters.pending", scope: "decidim.stratified_sortitions.stratified_sortitions"),
4
+ stratified_sortitions_path(filter: { with_any_state: "pending" }) if stratified_sortition.status == "pending" %>
5
+
6
+ <%= link_to t("filters.executed", scope: "decidim.stratified_sortitions.stratified_sortitions"),
7
+ stratified_sortitions_path(filter: { with_any_state: "executed" }) if stratified_sortition.status == "executed" %>
8
+ </li>
9
+ </ul>
@@ -0,0 +1,22 @@
1
+ <% add_decidim_meta_tags(
2
+ description: translated_attribute(current_component.participatory_space.try(:description)),
3
+ title: t("decidim.components.pagination.page_title",
4
+ component_name:,
5
+ current_page: @stratified_sortitions.current_page,
6
+ total_pages: @stratified_sortitions.total_pages ),
7
+ url: stratified_sortitions_url,
8
+ resource: current_component) %>
9
+
10
+ <% content_for :aside do %>
11
+ <h1 class="title-decorator"><%= component_name %></h1>
12
+
13
+ <%= render layout: "decidim/shared/filters", locals: { filter_sections: filter_sections_stratified_sortitions, search_variable: :search_text_cont, skip_to_id: "stratified_sortitions" } do %>
14
+ <%= hidden_field_tag :order, order, id: nil, class: "order_filter" %>
15
+ <% end %>
16
+ <% end %>
17
+
18
+ <%= render layout: "layouts/decidim/shared/layout_two_col" do %>
19
+ <section id="stratified_sortitions" class="layout-main__section layout-main__heading">
20
+ <%= render partial: "stratified_sortitions" %>
21
+ </section>
22
+ <% end %>
@@ -0,0 +1,5 @@
1
+ var $stratified_sortitions = $('#stratified_sortitions');
2
+ var $orderFilterInput = $('.order_filter');
3
+
4
+ $stratified_sortitions.html('<%= j(render partial: "stratified_sortitions").strip.html_safe %>');
5
+ $orderFilterInput.val('<%= order %>');
@@ -0,0 +1,142 @@
1
+ <% add_decidim_meta_tags({
2
+ description: decidim_sanitize_editor(translated_attribute(stratified_sortition.description)),
3
+ title: decidim_escape_translated(stratified_sortition.title),
4
+ url: stratified_sortition_url(stratified_sortition.to_param)
5
+ }) %>
6
+
7
+ <%
8
+ edit_link(
9
+ resource_locator(stratified_sortition).edit,
10
+ :update,
11
+ :stratified_sortition,
12
+ stratified_sortition:
13
+ )
14
+ %>
15
+
16
+ <%= append_javascript_pack_tag "decidim_stratified_sortitions" %>
17
+ <%= append_stylesheet_pack_tag "decidim_stratified_sortitions" %>
18
+
19
+ <%= render layout: "layouts/decidim/shared/layout_item", locals: { back_path: :stratified_sortitions, commentable: nil } do %>
20
+ <section class="layout-main__section layout-main__heading">
21
+ <h1 class="h1 decorator mb-4">
22
+ <%= decidim_escape_translated(stratified_sortition.title) %>
23
+ </h1>
24
+ </section>
25
+
26
+ <% if translated_attribute(stratified_sortition.description).present? %>
27
+ <section class="layout-main__section">
28
+ <h2 class="h3">
29
+ <%= t("description", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
30
+ </h2>
31
+ <div class="editor-content">
32
+ <%= decidim_sanitize_editor_admin translated_attribute stratified_sortition.description %>
33
+ </div>
34
+ </section>
35
+ <% end %>
36
+
37
+ <% if translated_attribute(stratified_sortition.selection_criteria).present? %>
38
+ <section class="layout-main__section">
39
+ <h2 class="h3">
40
+ <%= t("selection_criteria", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
41
+ </h2>
42
+ <div class="editor-content">
43
+ <%= decidim_sanitize_editor_admin translated_attribute stratified_sortition.selection_criteria %>
44
+ </div>
45
+ </section>
46
+ <% end %>
47
+
48
+ <% if translated_attribute(stratified_sortition.selected_profiles_description).present? %>
49
+ <section class="layout-main__section">
50
+ <h2 class="h3">
51
+ <% if stratified_sortition.executed? %>
52
+ <%= t("selected_profiles_description", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
53
+ <% else %>
54
+ <%= t("to_be_selected_profiles_description", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
55
+ <% end %>
56
+ </h2>
57
+ <div class="editor-content">
58
+ <%= decidim_sanitize_editor_admin translated_attribute stratified_sortition.selected_profiles_description %>
59
+ </div>
60
+ </section>
61
+ <% end %>
62
+
63
+ <% if @strata_data.present? || @candidates_data.present? %>
64
+ <section class="layout-main__section">
65
+ <h2 class="h3">
66
+ <%= t("strata_charts", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
67
+ </h2>
68
+ <div class="flex public-charts">
69
+ <div class="charts-column">
70
+ <%= render partial: "decidim/stratified_sortitions/shared/strata_charts", locals: { strata_data: @strata_data, title: t("target", scope: "decidim.stratified_sortitions.stratified_sortitions.show") } %>
71
+ </div>
72
+
73
+ <% if @candidates_data %>
74
+ <div class="charts-column target">
75
+ <%= render partial: "decidim/stratified_sortitions/shared/strata_charts", locals: { strata_data: @candidates_data, title: t("candidates", scope: "decidim.stratified_sortitions.stratified_sortitions.show") } %>
76
+ </div>
77
+ <% end %>
78
+
79
+ <% if @results_data.present? %>
80
+ <div class="charts-column target">
81
+ <%= render partial: "decidim/stratified_sortitions/shared/strata_charts", locals: { strata_data: @results_data, title: t("results", scope: "decidim.stratified_sortitions.stratified_sortitions.show") } %>
82
+ </div>
83
+ <% end %>
84
+ </div>
85
+ </section>
86
+ <% end %>
87
+
88
+ <% content_for :aside do %>
89
+ <section class="layout-aside__section">
90
+ <%= render partial: "results_count", locals: { stratified_sortition: } %>
91
+ </section>
92
+ <section class="layout-aside__section">
93
+ <div class="text-sm text-gray-2 uppercase font-semibold mb-4">
94
+ <%= t("sortition_details", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
95
+ </div>
96
+ <div class="stratified-sortition__aside-element bg-background p-4 rounded">
97
+ <div class="stratified-sortition__aside-element-item">
98
+ <span class="stratified-sortition__aside-element-item__title">
99
+ <%= t("status", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
100
+ </span>
101
+ <span class="stratified-sortition__aside-element-item__text">
102
+ <%= t(stratified_sortition.status, scope: "decidim.stratified_sortitions.stratified_sortitions.show.statuses") %>
103
+ </span>
104
+ </div>
105
+ <div class="stratified-sortition__aside-element-item">
106
+ <span class="stratified-sortition__aside-element-item__title">
107
+ <%= t("algorithm_description", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
108
+ </span>
109
+ <div class="stratified-sortition__aside-element-item">
110
+ <%= link_to t("algorithm_name", scope: "decidim.stratified_sortitions.stratified_sortitions.show"), Decidim::StratifiedSortitions.leximin_algorithm, class: "stratified-sortition__aside-element-item__title" %>
111
+ </div>
112
+ </div>
113
+ <% if stratified_sortition.panel_portfolio&.sampled? %>
114
+ <div class="stratified-sortition__aside-element-item">
115
+ <span class="stratified-sortition__aside-element-item__title">
116
+ <%= t("random_value_used", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
117
+ </span>
118
+ <span class="stratified-sortition__aside-element-item__text">
119
+ <%= stratified_sortition.panel_portfolio.random_value_used %>
120
+ </span>
121
+ </div>
122
+ <div class="stratified-sortition__aside-element-item">
123
+ <span class="stratified-sortition__aside-element-item__title">
124
+ <%= t("selected_at", scope: "decidim.stratified_sortitions.stratified_sortitions.show") %>
125
+ </span>
126
+ <span class="stratified-sortition__aside-element-item__text">
127
+ <%= l stratified_sortition.panel_portfolio.selected_at, format: :decidim_short %>
128
+ </span>
129
+ </div>
130
+ <% end %>
131
+ </div>
132
+ </section>
133
+ <section class="layout-aside__section actions__secondary">
134
+ <%= cell "decidim/share_button", nil %>
135
+ </section>
136
+ <% end %>
137
+ <% content_for :item_footer do %>
138
+ <ul class="metadata__container layout-main__section" data-metadata-footer>
139
+ <%= content_tag :li, resource_reference(stratified_sortition), class: "metadata__item" %>
140
+ </ul>
141
+ <% end %>
142
+ <% end %>
@@ -0,0 +1,45 @@
1
+ <%
2
+ allowed_emails = ENV.fetch("STRATIFIED_SORTITIONS_ALLOWED_EMAILS", "").split(",").map { |email| email.strip.downcase }.reject(&:blank?)
3
+ stratified_sortitions_paths = if defined?(current_participatory_space) && current_participatory_space.respond_to?(:components) && !allowed_emails.include?(current_user&.email.to_s.strip.downcase)
4
+ current_participatory_space.components.filter_map do |component|
5
+ manage_component_path(component) if component.manifest_name.to_s == "stratified_sortitions"
6
+ end
7
+ else
8
+ []
9
+ end
10
+ menu_html = sidebar_menu(secondary_root_menu).render
11
+
12
+ if stratified_sortitions_paths.any?
13
+ menu_fragment = Nokogiri::HTML::DocumentFragment.parse(menu_html)
14
+ menu_fragment.css("a").each do |link|
15
+ next unless stratified_sortitions_paths.include?(link["href"])
16
+
17
+ link.ancestors("li").first&.remove
18
+ end
19
+ menu_html = menu_fragment.to_html.html_safe
20
+ end
21
+ %>
22
+
23
+ <div class="sidebar-menu__container">
24
+ <div class="sidebar-menu">
25
+ <div id="admin-sidebar-menu-settings">
26
+ <div class="hidden md:block">
27
+ <%= menu_html %>
28
+ </div>
29
+ <button data-component="dropdown" data-target="dropdown-menu-sidebar" data-auto-close="true" data-scroll-to-menu="true">
30
+ <span><%= t("menu", scope: "decidim.admin.titles") %></span>
31
+ <%= icon "arrow-down-s-line" %>
32
+ <%= icon "arrow-up-s-line" %>
33
+ </button>
34
+
35
+ <ul id="dropdown-menu-sidebar" class="md:hidden" aria-hidden="true">
36
+ <%= menu_html %>
37
+ </ul>
38
+ </div>
39
+ </div>
40
+
41
+ <div class="item_show__wrapper">
42
+ <%= admin_tabs(tab_menu_name).render if defined?(has_tab_menu?) && has_tab_menu? %>
43
+ <%= yield %>
44
+ </div>
45
+ </div>
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="<%= I18n.locale %>" class="no-js">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
+ <meta name="viewport" content="width=device-width">
6
+ <title><%= @title %></title>
7
+ <style>
8
+ <%= File.read(Decidim::StratifiedSortitions::Engine.root.join("app/packs/stylesheets/decidim/stratified_sortitions/charts_pdf.scss")).html_safe %>
9
+ </style>
10
+ </head>
11
+ <body>
12
+ <%= yield %>
13
+ </body>
14
+ </html>
data/config/assets.rb ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is located at `config/assets.rb` of your module.
4
+
5
+ # Define the base path of your module. Please note that `Rails.root` may not be
6
+ # used because we are not inside the Rails environment when this file is loaded.
7
+ base_path = File.expand_path("..", __dir__)
8
+
9
+ # Register an additional load path for webpack. All the assets within these
10
+ # directories will be available for inclusion within the Decidim assets. For
11
+ # example, if you have `app/packs/src/decidim/foo.js`, you can include that file
12
+ # in your JavaScript entrypoints (or other JavaScript files within Decidim)
13
+ # using `import "src/decidim/foo"` after you have registered the additional path
14
+ # as follows.
15
+ Decidim::Webpacker.register_path("#{base_path}/app/packs")
16
+
17
+ # Register the entrypoints for your module. These entrypoints can be included
18
+ # within your application using `javascript_pack_tag` and if you include any
19
+ # SCSS files within the entrypoints, they become available for inclusion using
20
+ # `stylesheet_pack_tag`.
21
+ Decidim::Webpacker.register_entrypoints(
22
+ decidim_stratified_sortitions: "#{base_path}/app/packs/entrypoints/decidim_stratified_sortitions.js",
23
+ decidim_stratified_sortitions_admin: "#{base_path}/app/packs/entrypoints/decidim_stratified_sortitions_admin.js",
24
+ )