bulkrax 9.3.4 → 9.4.0

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -1
  3. data/app/assets/javascripts/bulkrax/application.js +2 -1
  4. data/app/assets/javascripts/bulkrax/bulkrax.js +13 -4
  5. data/app/assets/javascripts/bulkrax/bulkrax_utils.js +96 -0
  6. data/app/assets/javascripts/bulkrax/datatables.js +1 -0
  7. data/app/assets/javascripts/bulkrax/entries.js +17 -10
  8. data/app/assets/javascripts/bulkrax/importers.js.erb +9 -2
  9. data/app/assets/javascripts/bulkrax/importers_stepper.js +2420 -0
  10. data/app/assets/stylesheets/bulkrax/application.css +1 -1
  11. data/app/assets/stylesheets/bulkrax/import_export.scss +9 -2
  12. data/app/assets/stylesheets/bulkrax/stepper/_header.scss +83 -0
  13. data/app/assets/stylesheets/bulkrax/stepper/_mixins.scss +26 -0
  14. data/app/assets/stylesheets/bulkrax/stepper/_navigation.scss +103 -0
  15. data/app/assets/stylesheets/bulkrax/stepper/_responsive.scss +46 -0
  16. data/app/assets/stylesheets/bulkrax/stepper/_review.scss +92 -0
  17. data/app/assets/stylesheets/bulkrax/stepper/_settings.scss +106 -0
  18. data/app/assets/stylesheets/bulkrax/stepper/_success.scss +26 -0
  19. data/app/assets/stylesheets/bulkrax/stepper/_summary.scss +171 -0
  20. data/app/assets/stylesheets/bulkrax/stepper/_upload.scss +339 -0
  21. data/app/assets/stylesheets/bulkrax/stepper/_validation.scss +237 -0
  22. data/app/assets/stylesheets/bulkrax/stepper/_variables.scss +46 -0
  23. data/app/assets/stylesheets/bulkrax/stepper.scss +32 -0
  24. data/app/controllers/bulkrax/guided_imports_controller.rb +175 -0
  25. data/app/controllers/bulkrax/importers_controller.rb +34 -28
  26. data/app/controllers/concerns/bulkrax/guided_import_demo_scenarios.rb +201 -0
  27. data/app/controllers/concerns/bulkrax/importer_file_handler.rb +217 -0
  28. data/app/factories/bulkrax/object_factory.rb +3 -2
  29. data/app/factories/bulkrax/valkyrie_object_factory.rb +61 -17
  30. data/app/jobs/bulkrax/export_work_job.rb +1 -3
  31. data/app/jobs/bulkrax/importer_job.rb +11 -4
  32. data/app/models/bulkrax/csv_entry.rb +27 -7
  33. data/app/models/bulkrax/entry.rb +4 -0
  34. data/app/models/bulkrax/importer.rb +31 -1
  35. data/app/models/concerns/bulkrax/has_matchers.rb +2 -2
  36. data/app/models/concerns/bulkrax/importer_exporter_behavior.rb +6 -5
  37. data/app/parsers/bulkrax/application_parser.rb +31 -5
  38. data/app/parsers/bulkrax/csv_parser.rb +42 -10
  39. data/app/parsers/concerns/bulkrax/csv_parser/csv_template_generation.rb +73 -0
  40. data/app/parsers/concerns/bulkrax/csv_parser/csv_validation.rb +133 -0
  41. data/app/parsers/concerns/bulkrax/csv_parser/csv_validation_helpers.rb +282 -0
  42. data/app/parsers/concerns/bulkrax/csv_parser/csv_validation_hierarchy.rb +96 -0
  43. data/app/services/bulkrax/csv_template/column_builder.rb +60 -0
  44. data/app/services/bulkrax/csv_template/column_descriptor.rb +58 -0
  45. data/app/services/bulkrax/csv_template/csv_builder.rb +83 -0
  46. data/app/services/bulkrax/csv_template/explanation_builder.rb +57 -0
  47. data/app/services/bulkrax/csv_template/field_analyzer.rb +56 -0
  48. data/app/services/bulkrax/csv_template/file_path_generator.rb +47 -0
  49. data/app/services/bulkrax/csv_template/file_validator.rb +68 -0
  50. data/app/services/bulkrax/csv_template/mapping_manager.rb +55 -0
  51. data/app/services/bulkrax/csv_template/model_loader.rb +50 -0
  52. data/app/services/bulkrax/csv_template/row_builder.rb +35 -0
  53. data/app/services/bulkrax/csv_template/schema_analyzer.rb +70 -0
  54. data/app/services/bulkrax/csv_template/split_formatter.rb +44 -0
  55. data/app/services/bulkrax/csv_template/value_determiner.rb +68 -0
  56. data/app/services/bulkrax/stepper_response_formatter.rb +347 -0
  57. data/app/services/bulkrax/validation_error_csv_builder.rb +99 -0
  58. data/app/validators/bulkrax/csv_row/child_reference.rb +56 -0
  59. data/app/validators/bulkrax/csv_row/circular_reference.rb +71 -0
  60. data/app/validators/bulkrax/csv_row/controlled_vocabulary.rb +74 -0
  61. data/app/validators/bulkrax/csv_row/duplicate_identifier.rb +63 -0
  62. data/app/validators/bulkrax/csv_row/missing_source_identifier.rb +31 -0
  63. data/app/validators/bulkrax/csv_row/parent_reference.rb +59 -0
  64. data/app/validators/bulkrax/csv_row/required_values.rb +64 -0
  65. data/app/views/bulkrax/entries/_parsed_metadata.html.erb +1 -1
  66. data/app/views/bulkrax/entries/_raw_metadata.html.erb +1 -1
  67. data/app/views/bulkrax/entries/show.html.erb +6 -6
  68. data/app/views/bulkrax/exporters/_form.html.erb +19 -43
  69. data/app/views/bulkrax/exporters/edit.html.erb +2 -2
  70. data/app/views/bulkrax/exporters/index.html.erb +5 -5
  71. data/app/views/bulkrax/exporters/new.html.erb +3 -5
  72. data/app/views/bulkrax/exporters/show.html.erb +3 -3
  73. data/app/views/bulkrax/guided_imports/new.html.erb +567 -0
  74. data/app/views/bulkrax/importers/_bagit_fields.html.erb +9 -9
  75. data/app/views/bulkrax/importers/_browse_everything.html.erb +1 -1
  76. data/app/views/bulkrax/importers/_csv_fields.html.erb +11 -11
  77. data/app/views/bulkrax/importers/_edit_form_buttons.html.erb +23 -23
  78. data/app/views/bulkrax/importers/_edit_item_buttons.html.erb +2 -2
  79. data/app/views/bulkrax/importers/_file_uploader.html.erb +3 -3
  80. data/app/views/bulkrax/importers/_form.html.erb +4 -5
  81. data/app/views/bulkrax/importers/_oai_fields.html.erb +8 -18
  82. data/app/views/bulkrax/importers/_xml_fields.html.erb +13 -13
  83. data/app/views/bulkrax/importers/edit.html.erb +2 -2
  84. data/app/views/bulkrax/importers/index.html.erb +19 -14
  85. data/app/views/bulkrax/importers/new.html.erb +10 -9
  86. data/app/views/bulkrax/importers/show.html.erb +23 -7
  87. data/app/views/bulkrax/importers/upload_corrected_entries.html.erb +6 -6
  88. data/app/views/bulkrax/shared/_bulkrax_errors.html.erb +11 -11
  89. data/app/views/bulkrax/shared/_bulkrax_field_mapping.html.erb +3 -3
  90. data/config/i18n-tasks.yml +195 -0
  91. data/config/locales/bulkrax.de.yml +504 -0
  92. data/config/locales/bulkrax.en.yml +487 -28
  93. data/config/locales/bulkrax.es.yml +504 -0
  94. data/config/locales/bulkrax.fr.yml +504 -0
  95. data/config/locales/bulkrax.it.yml +504 -0
  96. data/config/locales/bulkrax.pt-BR.yml +504 -0
  97. data/config/locales/bulkrax.zh.yml +503 -0
  98. data/config/routes.rb +10 -0
  99. data/lib/bulkrax/data/demo_scenarios.json +2235 -0
  100. data/lib/bulkrax/version.rb +1 -1
  101. data/lib/bulkrax.rb +31 -3
  102. data/lib/tasks/bulkrax_tasks.rake +0 -102
  103. metadata +55 -3
  104. /data/{app/services → lib}/wings/custom_queries/find_by_source_identifier.rb +0 -0
@@ -0,0 +1,567 @@
1
+ <% provide :page_header do %>
2
+ <h1><span class="fa fa-cloud-upload" aria-hidden="true"></span> <%= t('bulkrax.importer.guided_import.page_title') %></h1>
3
+ <p><%= t('bulkrax.importer.guided_import.page_subtitle') %></p>
4
+ <p>
5
+ <%= link_to t('bulkrax.importer.guided_import.feedback_link'), 'https://github.com/samvera/bulkrax/issues/new?template=guided_import_bug_report.md', target: '_blank', rel: 'noopener noreferrer', class: 'feedback-link' %>
6
+ </p>
7
+ <% end %>
8
+
9
+ <% content_for(:head) do %>
10
+ <meta name="turbolinks-cache-control" content="no-cache">
11
+ <% end %>
12
+
13
+ <script>
14
+ window.BulkraxI18n = <%= raw I18n.t('bulkrax.importer.guided_import.js').to_json %>;
15
+ </script>
16
+
17
+ <div class="bulk-import-stepper-container" data-max-file-size="<%= (defined?(Hyrax) && Hyrax.config.uploader&.[](:maxFileSize)) || 524288000 %>">
18
+ <div class="stepper-wrapper">
19
+
20
+ <!-- Stepper Header -->
21
+ <div class="stepper-header d-flex align-items-center justify-content-center">
22
+ <div class="step-item d-flex flex-column align-items-center" data-step="1">
23
+ <div class="step-circle d-flex align-items-center justify-content-center">
24
+ <span class="fa fa-cloud-upload" aria-hidden="true"></span>
25
+ <span class="step-check fa fa-check" aria-hidden="true"></span>
26
+ </div>
27
+ <span class="step-label"><%= t('bulkrax.importer.guided_import.steps.upload_validate') %></span>
28
+ </div>
29
+ <div class="step-connector"></div>
30
+ <div class="step-item d-flex flex-column align-items-center" data-step="2">
31
+ <div class="step-circle d-flex align-items-center justify-content-center">
32
+ <span class="fa fa-cog" aria-hidden="true"></span>
33
+ <span class="step-check fa fa-check" aria-hidden="true"></span>
34
+ </div>
35
+ <span class="step-label"><%= t('bulkrax.importer.guided_import.steps.configure_settings') %></span>
36
+ </div>
37
+ <div class="step-connector"></div>
38
+ <div class="step-item d-flex flex-column align-items-center" data-step="3">
39
+ <div class="step-circle d-flex align-items-center justify-content-center">
40
+ <span class="fa fa-play" aria-hidden="true"></span>
41
+ <span class="step-check fa fa-check" aria-hidden="true"></span>
42
+ </div>
43
+ <span class="step-label"><%= t('bulkrax.importer.guided_import.steps.review_start') %></span>
44
+ </div>
45
+ </div>
46
+
47
+ <!-- Success State (Hidden by default) -->
48
+ <div class="import-success-state" style="display: none;">
49
+ <div class="success-card text-center">
50
+ <div class="success-icon d-flex align-items-center justify-content-center">
51
+ <span class="fa fa-check-circle" aria-hidden="true"></span>
52
+ </div>
53
+ <h2><%= t('bulkrax.importer.guided_import.success.title') %></h2>
54
+ <p><%= t('bulkrax.importer.guided_import.success.message') %></p>
55
+ <div class="success-actions d-flex justify-content-center">
56
+ <a href="<%= importers_path %>" class="btn btn-secondary"><%= t('bulkrax.importer.guided_import.success.view_queue') %></a>
57
+ <button type="button" class="btn btn-primary" id="start-another-import"><%= t('bulkrax.importer.guided_import.success.start_another') %></button>
58
+ </div>
59
+ </div>
60
+ </div>
61
+
62
+ <!-- Main Form -->
63
+ <div class="stepper-content-wrapper">
64
+ <%= form_for [:bulkrax, Bulkrax::Importer.new], url: guided_import_create_path, method: :post, html: { id: 'bulk-import-stepper-form', multipart: true } do |f| %>
65
+ <%= hidden_field_tag 'locale', I18n.locale %>
66
+ <!-- ====== STEP 1: Upload & Validate ====== -->
67
+ <div class="step-content" data-step="1">
68
+ <div class="step-title">
69
+ <h2 tabindex="-1"><%= t('bulkrax.importer.guided_import.step1.title') %> <span class="text-danger">*</span></h2>
70
+ <p class="text-muted"><%= t('bulkrax.importer.guided_import.step1.description_html').html_safe %></p>
71
+ </div>
72
+
73
+ <!-- Upload Area -->
74
+ <div class="upload-zone-wrapper">
75
+ <!-- Upload Mode Tabs -->
76
+ <div class="upload-mode-tabs" role="tablist">
77
+ <button
78
+ type="button"
79
+ class="upload-mode-tab active"
80
+ data-upload-mode="upload"
81
+ role="tab"
82
+ aria-selected="true"
83
+ >
84
+ <span class="fa fa-cloud-upload" aria-hidden="true"></span> <%= t('bulkrax.importer.guided_import.step1.upload_files') %>
85
+ </button>
86
+ <button
87
+ type="button"
88
+ class="upload-mode-tab"
89
+ data-upload-mode="file_path"
90
+ role="tab"
91
+ aria-selected="false"
92
+ >
93
+ <span class="fa fa-folder-open" aria-hidden="true"></span> <%= t('bulkrax.importer.guided_import.step1.import_path') %>
94
+ </button>
95
+ </div>
96
+
97
+ <!-- Empty State -->
98
+ <div class="upload-zone-empty">
99
+ <label
100
+ for="file-input"
101
+ class="upload-dropzone"
102
+ role="button"
103
+ tabindex="0"
104
+ aria-describedby="dropzone-hint"
105
+ >
106
+ <div class="upload-icon">
107
+ <span class="fa fa-cloud-upload" aria-hidden="true"></span>
108
+ </div>
109
+ <p><%= t('bulkrax.importer.guided_import.step1.dropzone_main_html').html_safe %></p>
110
+ <p class="text-muted small" id="dropzone-hint">
111
+ <%= t('bulkrax.importer.guided_import.step1.dropzone_hint') %>
112
+ </p>
113
+ <div class="upload-accepted-types">
114
+ <p class="text-muted small">
115
+ <span class="fa fa-file-text-o" aria-hidden="true"></span>
116
+ <%= t('bulkrax.importer.guided_import.step1.dropzone_csv_hint') %>
117
+ </p>
118
+ <p class="text-muted small">
119
+ <span class="fa fa-file-archive-o" aria-hidden="true"></span>
120
+ <%= t('bulkrax.importer.guided_import.step1.dropzone_zip_hint') %>
121
+ </p>
122
+ </div>
123
+ </label>
124
+ <input
125
+ type="file"
126
+ id="file-input"
127
+ name="importer[parser_fields][files][]"
128
+ accept=".csv,.zip"
129
+ multiple
130
+ class="sr-only"
131
+ aria-label="<%= t('bulkrax.importer.guided_import.step1.upload_files') %>"
132
+ >
133
+
134
+ <!-- Demo Scenarios (for testing) -->
135
+ <% if Bulkrax.config.guided_import_demo_scenarios_enabled %>
136
+ <div class="demo-scenarios" style="display: none;">
137
+ <p class="small text-primary"><strong>Demo: Pick a validation scenario</strong></p>
138
+ <div style="margin-bottom: 10px;">
139
+ <p class="small text-muted mb-1"><strong>&#10003; Success</strong></p>
140
+ <button type="button" class="btn btn-sm btn-outline-success btn-block scenario-btn" data-scenario="success_no_issues">CSV with Nested Collections and Works</button>
141
+ <button type="button" class="btn btn-sm btn-outline-success btn-block scenario-btn" data-scenario="success_with_files">CSV and ZIP</button>
142
+ <button type="button" class="btn btn-sm btn-outline-success btn-block scenario-btn" data-scenario="success_multi_parent">Multi-parent works</button>
143
+ <button type="button" class="btn btn-sm btn-outline-success btn-block scenario-btn" data-scenario="success_children_ids">Children IDs define hierarchy</button>
144
+ <button type="button" class="btn btn-sm btn-outline-success btn-block scenario-btn" data-scenario="success_with_mixed_hierarchy">Each collection defines hierarchy differently</button>
145
+ </div>
146
+ <div style="margin-bottom: 10px;">
147
+ <p class="small text-muted mb-1"><strong>&#9888; Warnings</strong></p>
148
+ <button type="button" class="btn btn-sm btn-outline-warning btn-block scenario-btn" data-scenario="warning_unrecognized">Unrecognized fields</button>
149
+ <button type="button" class="btn btn-sm btn-outline-warning btn-block scenario-btn" data-scenario="warning_missing_files">Missing files from ZIP</button>
150
+ <button type="button" class="btn btn-sm btn-outline-warning btn-block scenario-btn" data-scenario="warning_no_zip">Files referenced, no ZIP</button>
151
+ <button type="button" class="btn btn-sm btn-outline-warning btn-block scenario-btn" data-scenario="warning_combined">Combined warnings</button>
152
+ </div>
153
+ <div>
154
+ <p class="small text-muted mb-1"><strong>&#10007; Errors</strong></p>
155
+ <button type="button" class="btn btn-sm btn-outline-danger btn-block scenario-btn" data-scenario="error_missing_required">Missing required fields</button>
156
+ <button type="button" class="btn btn-sm btn-outline-danger btn-block scenario-btn" data-scenario="error_combined">Errors + warnings</button>
157
+ </div>
158
+ </div>
159
+ <% end %>
160
+ </div>
161
+
162
+ <!-- Files Uploaded State (hidden initially) -->
163
+ <div class="uploaded-files-container" style="display: none;">
164
+ <div class="uploaded-files-list">
165
+ <!-- File rows will be dynamically added here -->
166
+ </div>
167
+ <div class="upload-info-message">
168
+ <!-- Info messages will be added here based on upload state -->
169
+ </div>
170
+ </div>
171
+
172
+ <!-- Add another file dropzone (shown when 1 file uploaded) -->
173
+ <div class="add-another-dropzone" style="display: none;">
174
+ <label
175
+ for="file-input-additional"
176
+ class="upload-dropzone-small"
177
+ role="button"
178
+ tabindex="0"
179
+ aria-describedby="add-another-hint"
180
+ >
181
+ <div class="upload-icon">
182
+ <span class="fa fa-cloud-upload" aria-hidden="true"></span>
183
+ </div>
184
+ <p><%= t('bulkrax.importer.guided_import.step1.add_another') %></p>
185
+ <p class="text-muted small" id="add-another-hint">
186
+ <%= t('bulkrax.importer.guided_import.step1.add_another_hint_html').html_safe %>
187
+ </p>
188
+ <div class="upload-accepted-types">
189
+ <p class="text-muted small">
190
+ <span class="fa fa-file-text-o" aria-hidden="true"></span>
191
+ <%= t('bulkrax.importer.guided_import.step1.dropzone_csv_hint') %>
192
+ </p>
193
+ <p class="text-muted small">
194
+ <span class="fa fa-file-archive-o" aria-hidden="true"></span>
195
+ <%= t('bulkrax.importer.guided_import.step1.dropzone_zip_hint') %>
196
+ </p>
197
+ </div>
198
+ </label>
199
+ <input
200
+ type="file"
201
+ id="file-input-additional"
202
+ name="importer[parser_fields][files][]"
203
+ accept=".csv,.zip"
204
+ multiple
205
+ class="sr-only"
206
+ aria-label="<%= t('bulkrax.importer.guided_import.step1.add_another') %>"
207
+ >
208
+ </div>
209
+
210
+ <!-- File Path Input (hidden by default) -->
211
+ <div class="file-path-panel" style="display: none;">
212
+ <div class="file-path-input-wrapper">
213
+ <div class="upload-icon">
214
+ <span class="fa fa-folder-open" aria-hidden="true"></span>
215
+ </div>
216
+ <label for="import-file-path" class="control-label">
217
+ <%= t('bulkrax.importer.guided_import.step1.file_path_label') %>
218
+ </label>
219
+ <p class="text-muted small"><%= t('bulkrax.importer.guided_import.step1.file_path_hint') %></p>
220
+ <input
221
+ type="text"
222
+ id="import-file-path"
223
+ name="importer[parser_fields][import_file_path]"
224
+ class="form-control"
225
+ placeholder="<%= t('bulkrax.importer.guided_import.step1.file_path_placeholder') %>"
226
+ >
227
+ <!-- Params require file_style attribute -->
228
+ <%= hidden_field_tag 'importer[parser_fields][file_style]', I18n.t('bulkrax.importer.xml.file_style.server_path') %>
229
+ </div>
230
+ </div>
231
+
232
+ </div>
233
+
234
+ <!-- File Upload Error Messages -->
235
+ <div id="file-upload-errors" class="file-upload-errors" aria-live="polite" style="display: none;"></div>
236
+
237
+ <!-- Admin Set -->
238
+ <div class="form-group">
239
+ <div class="step-title">
240
+ <%= f.label :admin_set_id, for: 'importer-admin-set', class: 'h2' do %>
241
+ <%= t('bulkrax.importer.guided_import.step1.admin_set') %> <span class="text-danger">*</span>
242
+ <% end %>
243
+ <p class="text-muted"><%= t('bulkrax.importer.guided_import.step1.admin_set_hint') %></p>
244
+ </div>
245
+ <% admin_sets = available_admin_sets %>
246
+ <% default_admin_set = admin_sets.find { |name, id| name.include?('Default') }&.last %>
247
+ <%= f.select :admin_set_id,
248
+ options_for_select([[t('bulkrax.importer.guided_import.step1.admin_set_prompt'), '']] + admin_sets, default_admin_set),
249
+ {},
250
+ { id: 'importer-admin-set', class: 'form-control', required: true } %>
251
+ </div>
252
+
253
+ <!-- Download Template Link -->
254
+ <div class="download-template-link">
255
+ <span class="fa fa-download text-primary" aria-hidden="true"></span>
256
+ <a id="download-csv-template-link" href="<%= sample_csv_file_importers_path %>" class="text-primary" download data-sample-csv-url="<%= sample_csv_file_importers_path %>"><%= t('bulkrax.importer.guided_import.step1.download_template') %></a>
257
+ </div>
258
+
259
+ <!-- Validate Button (tab-scoped: only one is visible at a time) -->
260
+ <button type="button" id="validate-upload-btn" class="validate-btn btn btn-primary btn-block btn-lg" disabled>
261
+ <span class="fa fa-file-text" aria-hidden="true"></span> <%= t('bulkrax.importer.guided_import.step1.validate_upload') %>
262
+ </button>
263
+ <button type="button" id="validate-path-btn" class="validate-btn btn btn-primary btn-block btn-lg" style="display: none;" disabled>
264
+ <span class="fa fa-file-text" aria-hidden="true"></span> <%= t('bulkrax.importer.guided_import.step1.validate_path') %>
265
+ </button>
266
+
267
+ <!-- Validation Results (hidden initially) -->
268
+ <div class="validation-results" aria-live="polite" style="display: none;">
269
+
270
+ <!-- Validation Status Accordion -->
271
+ <div class="accordion-wrapper">
272
+ <!-- Validation accordions will be added by JS -->
273
+ </div>
274
+
275
+ <!-- Import Size Gauge -->
276
+ <div class="import-size-gauge">
277
+ <!-- Will be populated by JS -->
278
+ </div>
279
+
280
+ <!-- Import Summary -->
281
+ <div class="import-summary">
282
+ <h3><%= t('bulkrax.importer.guided_import.step1.import_summary') %></h3>
283
+ <div class="row">
284
+ <div class="col-sm-4">
285
+ <div class="summary-card summary-card-collections">
286
+ <div class="summary-number">0</div>
287
+ <div class="summary-label"><%= t('bulkrax.importer.guided_import.step1.collections') %></div>
288
+ </div>
289
+ </div>
290
+ <div class="col-sm-4">
291
+ <div class="summary-card summary-card-works">
292
+ <div class="summary-number">0</div>
293
+ <div class="summary-label"><%= t('bulkrax.importer.guided_import.step1.works') %></div>
294
+ </div>
295
+ </div>
296
+ <div class="col-sm-4">
297
+ <div class="summary-card summary-card-filesets">
298
+ <div class="summary-number">0</div>
299
+ <div class="summary-label"><%= t('bulkrax.importer.guided_import.step1.file_sets') %></div>
300
+ </div>
301
+ </div>
302
+ </div>
303
+
304
+ <!-- Collection/Work Trees -->
305
+ <div class="hierarchy-accordions">
306
+ <!-- Will be populated by JS -->
307
+ </div>
308
+ </div>
309
+
310
+ <!-- Warning Acknowledgment -->
311
+ <div class="warning-acknowledgment" style="display: none;">
312
+ <label class="acknowledgment-checkbox">
313
+ <input type="checkbox" id="warnings-acked">
314
+ <span><%= t('bulkrax.importer.guided_import.step1.warning_ack') %></span>
315
+ </label>
316
+ </div>
317
+ </div>
318
+
319
+ <!-- Step Navigation -->
320
+ <div class="step-navigation d-flex justify-content-between align-items-end">
321
+ <button type="button" class="btn btn-outline-danger start-over-nav-btn d-inline-flex align-items-center">
322
+ <span class="fa fa-refresh" aria-hidden="true"></span> <%= t('bulkrax.importer.guided_import.nav.clear_start_over') %>
323
+ </button>
324
+ <div class="step-nav-right d-flex flex-column align-items-end">
325
+ <label class="skip-validation-label" style="display: none">
326
+ <input type="checkbox" id="skip-validation-checkbox">
327
+ <span><%= t('bulkrax.importer.guided_import.nav.skip_validation') %></span>
328
+ </label>
329
+ <button type="button" class="btn btn-primary step-next-btn" data-next-step="2" disabled>
330
+ <%= t('bulkrax.importer.guided_import.nav.next_step') %> <span class="fa fa-arrow-right" aria-hidden="true"></span>
331
+ </button>
332
+ </div>
333
+ </div>
334
+ </div>
335
+
336
+ <!-- ====== STEP 2: Configure Settings ====== -->
337
+ <div class="step-content" data-step="2" style="display: none;">
338
+ <div class="step-title">
339
+ <h2 tabindex="-1"><%= t('bulkrax.importer.guided_import.step2.title') %></h2>
340
+ <p class="text-muted"><%= t('bulkrax.importer.guided_import.step2.description') %></p>
341
+ </div>
342
+
343
+ <div class="settings-form">
344
+ <!-- Import Name -->
345
+ <div class="form-group">
346
+ <%= f.label :name, t('bulkrax.importer.guided_import.step2.import_name'), class: 'control-label' do %>
347
+ <%= t('bulkrax.importer.guided_import.step2.import_name') %> <span class="text-danger">*</span>
348
+ <% end %>
349
+ <%= f.text_field :name, class: 'form-control', value: "#{t('bulkrax.importer.guided_import.step2.import_name_prefix')}#{Date.today.strftime('%m/%d/%Y')}", required: true %>
350
+ </div>
351
+
352
+ <!-- Default Visibility -->
353
+ <div class="form-group">
354
+ <fieldset>
355
+ <legend class="control-label"><%= t('bulkrax.importer.guided_import.step2.default_visibility') %> <span class="text-danger">*</span></legend>
356
+ <p class="text-muted small"><%= t('bulkrax.importer.guided_import.step2.visibility_hint') %></p>
357
+ <div class="visibility-options row">
358
+ <div class="col-sm-4">
359
+ <div class="visibility-card" data-visibility="open">
360
+ <span class="fa fa-globe visibility-icon" aria-hidden="true"></span>
361
+ <label class="visibility-label" for="visibility_open"><%= t('bulkrax.importer.guided_import.step2.visibility_public') %>
362
+ <input
363
+ type="radio"
364
+ id="visibility_open"
365
+ name="importer[parser_fields][visibility]"
366
+ value="open"
367
+ checked class="sr-only"
368
+ >
369
+ </label>
370
+ <div class="visibility-desc"><%= t('bulkrax.importer.guided_import.step2.visibility_public_desc') %></div>
371
+ </div>
372
+ </div>
373
+ <div class="col-sm-4">
374
+ <div class="visibility-card" data-visibility="authenticated">
375
+ <span class="fa fa-users visibility-icon" aria-hidden="true"></span>
376
+ <label class="visibility-label" for="visibility_authenticated"><%= t('bulkrax.importer.guided_import.step2.visibility_institution') %>
377
+ <input
378
+ type="radio"
379
+ id="visibility_authenticated"
380
+ name="importer[parser_fields][visibility]"
381
+ value="authenticated"
382
+ class="sr-only"
383
+ >
384
+ </label>
385
+ <div class="visibility-desc"><%= t('bulkrax.importer.guided_import.step2.visibility_institution_desc') %></div>
386
+ </div>
387
+ </div>
388
+ <div class="col-sm-4">
389
+ <div class="visibility-card" data-visibility="restricted">
390
+ <span class="fa fa-lock visibility-icon" aria-hidden="true"></span>
391
+ <label class="visibility-label" for="visibility_restricted"><%= t('bulkrax.importer.guided_import.step2.visibility_private') %>
392
+ <input
393
+ type="radio"
394
+ id="visibility_restricted"
395
+ name="importer[parser_fields][visibility]"
396
+ value="restricted"
397
+ class="sr-only"
398
+ >
399
+ </label>
400
+ <div class="visibility-desc"><%= t('bulkrax.importer.guided_import.step2.visibility_private_desc') %></div>
401
+ </div>
402
+ </div>
403
+ </div>
404
+ </fieldset>
405
+ </div>
406
+
407
+ <!-- Optional Settings -->
408
+ <div class="optional-settings-accordion">
409
+ <div class="card">
410
+ <div class="card-header" data-toggle="collapse" data-target="#optional-settings">
411
+ <h3 class="mb-0">
412
+ <button class="btn btn-link btn-block text-left" type="button">
413
+ <span>
414
+ <span class="fa fa-cog" aria-hidden="true"></span>
415
+ <%= t('bulkrax.importer.guided_import.step2.optional_settings') %>
416
+ </span>
417
+ <span class="fa fa-chevron-down" aria-hidden="true"></span>
418
+ </button>
419
+ </h3>
420
+ </div>
421
+ <div id="optional-settings" class="collapse">
422
+ <div class="card-body">
423
+ <div id="default-rights-required-alert" class="alert alert-info" style="display: none;" role="alert">
424
+ <%= t('bulkrax.importer.guided_import.step2.rights_required_alert_html').html_safe %>
425
+ </div>
426
+ <div id="default-rights-skipped-hint" class="alert alert-secondary" style="display: none;" role="status">
427
+ <%= t('bulkrax.importer.guided_import.step2.rights_skipped_hint_html').html_safe %>
428
+ </div>
429
+ <div class="form-group">
430
+ <label class="control-label default-rights-statement-label" for="importer_parser_fields_rights_statement"><%= t('bulkrax.importer.guided_import.step2.default_rights') %></label>
431
+ <% if defined?(::Hyrax) %>
432
+ <% rights_statements = Hyrax.config.rights_statement_service_class.new %>
433
+ <%= select_tag 'importer[parser_fields][rights_statement]',
434
+ options_for_select([[t('bulkrax.importer.guided_import.step2.rights_none'), '']] + rights_statements.select_active_options),
435
+ class: 'form-control' %>
436
+ <% else %>
437
+ <%= select_tag 'importer[parser_fields][rights_statement]',
438
+ options_for_select([[t('bulkrax.importer.guided_import.step2.rights_none'), '']]),
439
+ class: 'form-control' %>
440
+ <% end %>
441
+ </div>
442
+ <div class="form-group">
443
+ <div class="form-check">
444
+ <%= check_box_tag 'importer[parser_fields][override_rights_statement]', '1', false, class: 'form-check-input', id: 'importer_parser_fields_override_rights_statement' %>
445
+ <label class="form-check-label control-label" for="importer_parser_fields_override_rights_statement">
446
+ <%= t('simple_form.labels.defaults.override_rights_statement') %>
447
+ </label>
448
+ </div>
449
+ <small class="form-text text-muted"><%= t('bulkrax.importer.hints.override_rights_statement') %></small>
450
+ </div>
451
+
452
+ <div class="form-group">
453
+ <%= f.label :limit, t('bulkrax.importer.guided_import.step2.test_limit'), class: 'control-label' %>
454
+ <%= f.number_field :limit, class: 'form-control', placeholder: t('bulkrax.importer.guided_import.step2.test_limit_placeholder') %>
455
+ </div>
456
+ </div>
457
+ </div>
458
+ </div>
459
+ </div>
460
+ </div>
461
+
462
+ <!-- Step Navigation -->
463
+ <div class="step-navigation d-flex justify-content-between align-items-end">
464
+ <button type="button" class="btn btn-outline-danger start-over-nav-btn d-inline-flex align-items-center">
465
+ <span class="fa fa-refresh" aria-hidden="true"></span> <%= t('bulkrax.importer.guided_import.nav.clear_start_over') %>
466
+ </button>
467
+ <div class="d-flex align-items-end">
468
+ <button type="button" class="btn btn-secondary step-prev-btn" data-prev-step="1">
469
+ <span class="fa fa-arrow-left" aria-hidden="true"></span> <%= t('bulkrax.importer.guided_import.nav.back') %>
470
+ </button>
471
+ <button type="button" class="btn btn-primary step-next-btn" data-next-step="3" disabled>
472
+ <%= t('bulkrax.importer.guided_import.nav.next_step') %> <span class="fa fa-arrow-right" aria-hidden="true"></span>
473
+ </button>
474
+ </div>
475
+ </div>
476
+ </div>
477
+
478
+ <!-- ====== STEP 3: Review & Start ====== -->
479
+ <div class="step-content" data-step="3" style="display: none;">
480
+ <div class="step-title">
481
+ <h2 tabindex="-1"><%= t('bulkrax.importer.guided_import.step3.title') %></h2>
482
+ <p class="text-muted"><%= t('bulkrax.importer.guided_import.step3.description') %></p>
483
+ </div>
484
+
485
+ <!-- Large Import Warning -->
486
+ <div class="large-import-warning" style="display: none;">
487
+ <div class="alert alert-danger">
488
+ <span class="fa fa-exclamation-triangle" aria-hidden="true"></span>
489
+ <strong><%= t('bulkrax.importer.guided_import.step3.large_import_warning') %></strong>
490
+ <p><%= t('bulkrax.importer.guided_import.step3.large_import_message_html', count: 0).html_safe %></p>
491
+ </div>
492
+ </div>
493
+
494
+ <!-- Review Summary -->
495
+ <div class="review-summary">
496
+ <div class="alert alert-success">
497
+ <div class="review-header">
498
+ <span class="fa fa-check-circle" aria-hidden="true"></span>
499
+ <h3><%= t('bulkrax.importer.guided_import.step3.ready_to_import') %></h3>
500
+ </div>
501
+
502
+ <div class="review-section">
503
+ <h4><%= t('bulkrax.importer.guided_import.step3.section_files') %></h4>
504
+ <div class="review-files">
505
+ <!-- Will be populated by JS -->
506
+ </div>
507
+ </div>
508
+
509
+ <div class="review-section">
510
+ <h4><%= t('bulkrax.importer.guided_import.step3.section_records') %></h4>
511
+ <div class="review-records">
512
+ <!-- Will be populated by JS -->
513
+ </div>
514
+ </div>
515
+
516
+ <div class="review-section">
517
+ <h4><%= t('bulkrax.importer.guided_import.step3.section_settings') %></h4>
518
+ <div class="review-settings">
519
+ <!-- Will be populated by JS -->
520
+ </div>
521
+ </div>
522
+
523
+ <div class="review-section review-warnings" style="display: none;">
524
+ <h4><%= t('bulkrax.importer.guided_import.step3.section_warnings') %></h4>
525
+ <div class="review-warnings-list">
526
+ <!-- Will be populated by JS -->
527
+ </div>
528
+ </div>
529
+ </div>
530
+ </div>
531
+
532
+ <p class="text-center text-muted small"><%= t('bulkrax.importer.guided_import.step3.background_note') %></p>
533
+
534
+ <!-- Step Navigation -->
535
+ <div class="step-navigation d-flex justify-content-between align-items-end">
536
+ <button type="button" class="btn btn-outline-danger start-over-nav-btn d-inline-flex align-items-center">
537
+ <span class="fa fa-refresh" aria-hidden="true"></span> <%= t('bulkrax.importer.guided_import.nav.clear_start_over') %>
538
+ </button>
539
+ <div class="d-flex align-items-end">
540
+ <button type="button" class="btn btn-secondary step-prev-btn" data-prev-step="2">
541
+ <span class="fa fa-arrow-left" aria-hidden="true"></span> <%= t('bulkrax.importer.guided_import.nav.back') %>
542
+ </button>
543
+ <button type="submit" class="btn btn-success" id="start-import-btn">
544
+ <%= t('bulkrax.importer.guided_import.nav.start_import') %>
545
+ </button>
546
+ </div>
547
+ </div>
548
+ </div>
549
+
550
+ <% end %>
551
+ </div>
552
+ </div>
553
+ </div>
554
+
555
+ <!-- Toast notifications (announced to screen readers) -->
556
+ <div id="upload-notifications" aria-live="polite"></div>
557
+
558
+ <!-- Hidden fields for storing state -->
559
+ <div id="stepper-state" style="display: none;"
560
+ data-current-step="1"
561
+ data-validated="false"
562
+ data-has-warnings="false"
563
+ data-warnings-acked="false"
564
+ data-upload-state="empty"
565
+ data-validation-data=""
566
+ data-demo-scenarios-enabled="<%= Bulkrax.config.guided_import_demo_scenarios_enabled %>">
567
+ </div>
@@ -17,9 +17,9 @@
17
17
 
18
18
  <%= fi.input :visibility,
19
19
  collection: [
20
- ['Public', 'open'],
21
- ['Private', 'restricted'],
22
- ['Institution', 'authenticated']
20
+ [t('bulkrax.importer.bagit.visibility.public'), 'open'],
21
+ [t('bulkrax.importer.bagit.visibility.private'), 'restricted'],
22
+ [t('bulkrax.importer.bagit.visibility.institution'), 'authenticated']
23
23
  ],
24
24
  selected: importer.parser_fields['visibility'] || 'open',
25
25
  input_html: { class: 'form-control' }
@@ -35,15 +35,15 @@
35
35
  input_html: { class: 'form-control' } ,
36
36
  required: false
37
37
  %>
38
- <%= fi.input :override_rights_statement, as: :boolean, hint: 'If checked, always use the selected rights statement. If unchecked, use rights or rights_statement from the record and only use the provided value if dc:rights is blank.', input_html: { checked: (importer.parser_fields['override_rights_statement'] == "1") } %>
38
+ <%= fi.input :override_rights_statement, as: :boolean, hint: t('bulkrax.importer.hints.override_rights_statement'), input_html: { checked: (importer.parser_fields['override_rights_statement'] == "1") } %>
39
39
  <% end %>
40
- <h4>Bag or Bags to Import:</h4>
41
- <p>File upload and Cloud File upload must be a Zip file containing a single BagIt Bag, or a folder containing multiple BagIt Bags.</p>
42
- <p>The Server Path can point to a BagIt Bag, a folder containing BagIt Bags, or a zip file containing either.</p>
40
+ <h4><%= t('bulkrax.importer.bagit.bags_to_import') %></h4>
41
+ <p><%= t('bulkrax.importer.bagit.file_upload_hint') %></p>
42
+ <p><%= t('bulkrax.importer.bagit.server_path_hint') %></p>
43
43
 
44
44
  <%= fi.input :file_style,
45
- collection: ['Upload a File', 'Specify a Path on the Server'] +
46
- (defined?(::Hyrax) && Hyrax.config.browse_everything? ? ['Add Cloud File'] : []),
45
+ collection: [t('bulkrax.importer.bagit.file_style.upload'), t('bulkrax.importer.bagit.file_style.server_path')] +
46
+ (defined?(::Hyrax) && Hyrax.config.browse_everything? ? [t('bulkrax.importer.bagit.file_style.cloud')] : []),
47
47
  as: :radio_buttons, label: false %>
48
48
  <div id='file_upload'>
49
49
  <% if defined?(::Hyrax) %>
@@ -7,6 +7,6 @@
7
7
  <button type="button" data-toggle="browse-everything" data-route="<%=browse_everything_engine.root_path%>"
8
8
  data-target="#<%= f %>" class="btn btn-primary" id="browse">
9
9
  <span class="fa fa-plus"></span>
10
- Add Cloud Files
10
+ <%= t('bulkrax.importer.browse_everything.add_cloud_files') %>
11
11
  </button>
12
12
  </div>