diff --git a/app/mailers/emails/work_items.rb b/app/mailers/emails/work_items.rb index a243158d82fc5e321173cf0f807e7349902321bf..b14111c94eb7314d29e07e78ce96f03b2b947aad 100644 --- a/app/mailers/emails/work_items.rb +++ b/app/mailers/emails/work_items.rb @@ -8,7 +8,7 @@ def import_work_items_csv_email(user_id, project_id, results) @results = results email_with_layout( - to: @user.notification_email_for(@project), + to: @user.notification_email_for(@project.group), subject: subject('Imported work items')) end diff --git a/app/views/notify/import_work_items_csv_email.html.haml b/app/views/notify/import_work_items_csv_email.html.haml index 425cf42fbd06224eab1b39da3254999d93bec555..d4326d6bdf912d8965051d9514a31c9a6de0f9e5 100644 --- a/app/views/notify/import_work_items_csv_email.html.haml +++ b/app/views/notify/import_work_items_csv_email.html.haml @@ -1,18 +1,49 @@ -- text_style = 'font-size:16px; text-align:center; line-height:30px;' +- info_style = 'font-size:16px; text-align:center; line-height:24px;' +- error_style = 'font-size:13px; text-align:center; line-height:16px; color:#dd2b0e;' -%p{ style: text_style } +%p{ style: info_style } - project_link = link_to(@project.full_name, project_url(@project), style: "color:#3777b0; text-decoration:none;") - = s_('Notify|Your CSV import of work items for project %{project_link} has been completed.').html_safe % { project_link: project_link } + = s_('Notify|Here are the results for your CSV import for %{project_link}.').html_safe % { project_link: project_link } -%p{ style: text_style } - - work_items = n_('%d work item', '%d work items', @results[:success]) % @results[:success] - = s_('Notify|%{work_items} imported.') % { work_items: work_items } +- success_lines = @results[:success] +%p{ style: info_style } + - if success_lines > 0 + - work_items = n_('%d work item', '%d work items', success_lines) % success_lines + = s_('Notify|%{work_items} successfully imported.') % { work_items: work_items } + - else + = s_('Notify|No work items have been imported.') -- if @results[:error_lines].present? - %p{ style: text_style } - = s_('Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check that these lines have the following fields: %{required_headers}.') % { singular_or_plural_line: n_('line', 'lines', @results[:error_lines].size), required_headers: WorkItems::ImportCsvService.required_headers.join(', '), - error_lines: @results[:error_lines].join(', ') } + - if @results[:parse_error] + %p{ style: info_style } + = s_('Notify|Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.') -- if @results[:parse_error] - %p{ style: text_style } - = s_('Notify|Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.') +- type_errors = @results[:type_errors] +- if type_errors + %p{ style: info_style } + = s_('Notify|Some values in the "type" column could not be matched with supported work item types:') + + - blank_lines = type_errors[:blank] + - missing_lines = type_errors[:missing] + - disallowed_lines = type_errors[:disallowed] + + - if blank_lines.present? + %p{ style: error_style } + = s_('Notify|%{singular_or_plural_line} %{error_lines}: Work item type is empty.') % { singular_or_plural_line: n_('Line', 'Lines', blank_lines.size), error_lines: blank_lines.join(', ') } + + - if missing_lines.present? + %p{ style: error_style } + = s_('Notify|%{singular_or_plural_line} %{error_lines}: Work item type cannot be found or is not supported.') % { singular_or_plural_line: n_('Line', 'Lines', missing_lines.size), error_lines: missing_lines.join(', ') } + + - if disallowed_lines.present? + %p{ style: error_style } + = s_('Notify|%{singular_or_plural_line} %{error_lines}: Work item type is not available. Please check your license and permissions.') % { singular_or_plural_line: n_('Line', 'Lines', disallowed_lines.size), error_lines: disallowed_lines.join(', ') } + +- error_lines = @results[:error_lines] +- if error_lines.present? + %p{ style: error_style } + = s_('Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check that these lines have the following fields: %{required_headers}.') % { singular_or_plural_line: n_('line', 'lines', error_lines.size), required_headers: WorkItems::ImportCsvService.required_headers.join(', '), + error_lines: error_lines.join(', ') } + +- if error_lines.present? || type_errors + %p{ style: info_style } + = s_('Notify|Please fix the lines with errors and try the CSV import again.') diff --git a/app/views/notify/import_work_items_csv_email.text.erb b/app/views/notify/import_work_items_csv_email.text.erb index 5793131af894408d2df5f250dfe54633f326276f..059dbc95cbcc9c915dd933b15460390e8bd35ed7 100644 --- a/app/views/notify/import_work_items_csv_email.text.erb +++ b/app/views/notify/import_work_items_csv_email.text.erb @@ -1,11 +1,48 @@ -Your CSV import for project <%= @project.full_name %> (<%= project_url(@project) %>) has been completed. +<%= s_('Notify|Here are the results for your CSV import for %{project_name} (%{project_link}).') % { project_name: @project.full_name, project_link: project_url(@project) } %> -<%= pluralize(@results[:success], 'work item') %> imported. +<% success_lines = @results[:success] %> +<% if success_lines > 0 %> + <% work_items = n_('%d work item', '%d work items', success_lines) % success_lines %> + <%= s_('Notify|%{work_items} successfully imported.') % { work_items: work_items } %> +<% else %> + <%= s_('Notify|No work items have been imported.') %> -<% if @results[:error_lines].present? %> -Errors found on line <%= 'number'.pluralize(@results[:error_lines].size) %>: <%= @results[:error_lines].join(', ') %>. Please check that these lines have the following fields: <%= WorkItems::ImportCsvService.required_headers.join(', ') %>. + <% if @results[:parse_error] %> + <%= s_('Notify|Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.') %> + <% end %> <% end %> -<% if @results[:parse_error] %> -Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values. +<% type_errors = @results[:type_errors] %> +<% + if type_errors + blank_lines = type_errors[:blank] + missing_lines = type_errors[:missing] + disallowed_lines = type_errors[:disallowed] +%> + <%= s_('Notify|Some values in the "type" column could not be matched with supported work item types:') %> + + <% if blank_lines.present? %> + <%= s_('Notify|%{singular_or_plural_line} %{error_lines}: Work item type is empty.') % { singular_or_plural_line: n_('Line', 'Lines', blank_lines.size), error_lines: blank_lines.join(', ') } %> + <% end %> + + <% if missing_lines.present? %> + <%= s_('Notify|%{singular_or_plural_line} %{error_lines}: Work item type cannot be found or is not supported.') % { singular_or_plural_line: n_('Line', 'Lines', missing_lines.size), error_lines: missing_lines.join(', ') } %> + <% end %> + + <% if disallowed_lines.present? %> + <%= s_('Notify|%{singular_or_plural_line} %{error_lines}: Work item type is not available. Please check your license and permissions.') % { singular_or_plural_line: n_('Line', 'Lines', disallowed_lines.size), error_lines: disallowed_lines.join(', ') } %> + <% end %> +<% end %> + +<% + error_lines = @results[:error_lines] + if error_lines.present? +%> + <%= s_('Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check that these lines have the following fields: %{required_headers}.') % { singular_or_plural_line: n_('line', 'lines', error_lines.size), required_headers: WorkItems::ImportCsvService.required_headers.join(', '), + error_lines: error_lines.join(', ') } %> +<% end %> + +<% if error_lines.present? || type_errors %> + <%= s_('Notify|Please fix the lines with errors and try the CSV import again.') %> <% end %> + diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d2d43c5a25532719825b55736c12ab08bbdfb111..1bdaaa97b16736b962c3a78e27a4093827252989 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -25528,6 +25528,11 @@ msgstr "" msgid "Limiting mode" msgstr "" +msgid "Line" +msgid_plural "Lines" +msgstr[0] "" +msgstr[1] "" + msgid "Line changes" msgstr "" @@ -29048,13 +29053,22 @@ msgstr "" msgid "Notify|%{project_link_start}Download%{project_link_end} the project export." msgstr "" +msgid "Notify|%{singular_or_plural_line} %{error_lines}: Work item type cannot be found or is not supported." +msgstr "" + +msgid "Notify|%{singular_or_plural_line} %{error_lines}: Work item type is empty." +msgstr "" + +msgid "Notify|%{singular_or_plural_line} %{error_lines}: Work item type is not available. Please check your license and permissions." +msgstr "" + msgid "Notify|%{update_at_start} Last update at %{update_at_mid} %{last_update_at} %{update_at_end}" msgstr "" msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}" msgstr "" -msgid "Notify|%{work_items} imported." +msgid "Notify|%{work_items} successfully imported." msgstr "" msgid "Notify|A new GPG key was added to your account:" @@ -29111,6 +29125,12 @@ msgstr "" msgid "Notify|Fingerprint: %{fingerprint}" msgstr "" +msgid "Notify|Here are the results for your CSV import for %{project_link}." +msgstr "" + +msgid "Notify|Here are the results for your CSV import for %{project_name} (%{project_link})." +msgstr "" + msgid "Notify|Hi %{username}!" msgstr "" @@ -29177,6 +29197,9 @@ msgstr "" msgid "Notify|No preview for this file type" msgstr "" +msgid "Notify|No work items have been imported." +msgstr "" + msgid "Notify|Pipeline #%{pipeline_id} has failed!" msgstr "" @@ -29186,6 +29209,9 @@ msgstr "" msgid "Notify|Pipeline has been fixed and #%{pipeline_id} has passed!" msgstr "" +msgid "Notify|Please fix the lines with errors and try the CSV import again." +msgstr "" + msgid "Notify|Project %{old_path_with_namespace} was moved to another location." msgstr "" @@ -29198,6 +29224,9 @@ msgstr "" msgid "Notify|Remote mirror" msgstr "" +msgid "Notify|Some values in the \"type\" column could not be matched with supported work item types:" +msgstr "" + msgid "Notify|The Administrator created an account for you. Now you are a member of the company GitLab application." msgstr "" @@ -29246,9 +29275,6 @@ msgstr "" msgid "Notify|Your CSV import for project %{project_link} has been completed." msgstr "" -msgid "Notify|Your CSV import of work items for project %{project_link} has been completed." -msgstr "" - msgid "Notify|Your account has been created successfully." msgstr "" diff --git a/spec/views/notify/import_work_items_csv_email.html.haml_spec.rb b/spec/views/notify/import_work_items_csv_email.html.haml_spec.rb index 8a01c11646ad6b8f1ad2438554c1db979fbac0d2..989481fc2e6201b3d23ac46039969fe7d0a0acf9 100644 --- a/spec/views/notify/import_work_items_csv_email.html.haml_spec.rb +++ b/spec/views/notify/import_work_items_csv_email.html.haml_spec.rb @@ -5,57 +5,129 @@ RSpec.describe 'notify/import_work_items_csv_email.html.haml', feature_category: :team_planning do let_it_be(:user) { create(:user) } # rubocop:disable RSpec/FactoryBot/AvoidCreate let_it_be(:project) { create(:project) } # rubocop:disable RSpec/FactoryBot/AvoidCreate - let_it_be(:correct_results) { { success: 3, parse_error: false } } - let_it_be(:errored_results) { { success: 3, error_lines: [5, 6, 7], parse_error: false } } - let_it_be(:parse_error_results) { { success: 0, parse_error: true } } + + let(:parse_error) { "Error parsing CSV file. Please make sure it has the correct format" } before do assign(:user, user) assign(:project, project) + assign(:results, results) + + render end - context 'when no errors found while importing' do - before do - assign(:results, correct_results) + shared_examples_for 'no records created' do + specify do + expect(rendered).to have_content("No work items have been imported.") + expect(rendered).not_to have_content("work items successfully imported.") end + end - it 'renders correctly' do - render + shared_examples_for 'work item records created' do + specify do + expect(rendered).not_to have_content("No work items have been imported.") + expect(rendered).to have_content("work items successfully imported.") + end + end + shared_examples_for 'contains project link' do + specify do expect(rendered).to have_link(project.full_name, href: project_url(project)) - expect(rendered).to have_content("3 work items imported") - expect(rendered).not_to have_content("Errors found on line") - expect(rendered).not_to have_content( - "Error parsing CSV file. Please make sure it has the correct format: \ -a delimited text file that uses a comma to separate values.") end end - context 'when import errors reported' do - before do - assign(:results, errored_results) + shared_examples_for 'contains parse error' do + specify do + expect(rendered).to have_content(parse_error) + end + end + + shared_examples_for 'does not contain parse error' do + specify do + expect(rendered).not_to have_content(parse_error) end + end + + context 'when no errors found while importing' do + let(:results) { { success: 3, parse_error: false } } it 'renders correctly' do - render + expect(rendered).not_to have_content("Errors found on line") + end + + it_behaves_like 'contains project link' + it_behaves_like 'work item records created' + it_behaves_like 'does not contain parse error' + end + + context 'when import errors reported' do + let(:results) { { success: 3, error_lines: [5, 6, 7], parse_error: false } } - expect(rendered).to have_content("Errors found on lines: #{errored_results[:error_lines].join(', ')}. \ -Please check that these lines have the following fields: title") - expect(rendered).not_to have_content("Error parsing CSV file. Please make sure it has the correct format: \ -a delimited text file that uses a comma to separate values.") + it 'renders correctly' do + expect(rendered).to have_content("Errors found on lines: #{results[:error_lines].join(', ')}. \ +Please check that these lines have the following fields: title, type") end + + it_behaves_like 'contains project link' + it_behaves_like 'work item records created' + it_behaves_like 'does not contain parse error' end context 'when parse error reported while importing' do - before do - assign(:results, parse_error_results) + let(:results) { { success: 0, parse_error: true } } + + it_behaves_like 'contains project link' + it_behaves_like 'no records created' + it_behaves_like 'contains parse error' + end + + context 'when work item type column contains blank entries' do + let(:results) { { success: 0, parse_error: false, type_errors: { blank: [4] } } } + + it 'renders with missing work item message' do + expect(rendered).to have_content("Work item type is empty") + end + + it_behaves_like 'contains project link' + it_behaves_like 'no records created' + it_behaves_like 'does not contain parse error' + end + + context 'when work item type column contains missing entries' do + let(:results) { { success: 0, parse_error: false, type_errors: { missing: [5] } } } + + it 'renders with missing work item message' do + expect(rendered).to have_content("Work item type cannot be found or is not supported.") + end + + it_behaves_like 'contains project link' + it_behaves_like 'no records created' + it_behaves_like 'does not contain parse error' + end + + context 'when work item type column contains disallowed entries' do + let(:results) { { success: 0, parse_error: false, type_errors: { disallowed: [6] } } } + + it 'renders with missing work item message' do + expect(rendered).to have_content("Work item type is not available.") end - it 'renders with parse error' do - render + it_behaves_like 'contains project link' + it_behaves_like 'no records created' + it_behaves_like 'does not contain parse error' + end - expect(rendered).to have_content("Error parsing CSV file. \ -Please make sure it has the correct format: a delimited text file that uses a comma to separate values.") + context 'when CSV contains multiple kinds of work item type errors' do + let(:results) { { success: 0, parse_error: false, type_errors: { blank: [4], missing: [5], disallowed: [6] } } } + + it 'renders with missing work item message' do + expect(rendered).to have_content("Work item type is empty") + expect(rendered).to have_content("Work item type cannot be found or is not supported.") + expect(rendered).to have_content("Work item type is not available. Please check your license and permissions.") end + + it_behaves_like 'contains project link' + it_behaves_like 'no records created' + it_behaves_like 'does not contain parse error' end end