diff --git a/app/components/projects/table_component.html.erb b/app/components/projects/table_component.html.erb index 75c710351551..a697d0ca23ab 100644 --- a/app/components/projects/table_component.html.erb +++ b/app/components/projects/table_component.html.erb @@ -31,10 +31,10 @@ See COPYRIGHT and LICENSE files for more details.
- > +
> <% columns.each do |column| %> - > + <%= tag :col, data: { highlight: column.attribute != :hierarchy } %> <% end %> diff --git a/app/components/table_component.html.erb b/app/components/table_component.html.erb index eb7b80bec8db..9449d5dc4894 100644 --- a/app/components/table_component.html.erb +++ b/app/components/table_component.html.erb @@ -29,12 +29,12 @@ See COPYRIGHT and LICENSE files for more details.
-
+
<% headers.each do |_name, _options| %> - + <% end %> - + diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e42b67248862..85cea8735937 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -172,9 +172,21 @@ def labeled_check_box_tags(name, collection, options = {}) end def html_hours(text) - text.gsub(%r{(\d+)\.(\d+)}, - '\1.\2') - .html_safe + html_safe_gsub(text, + %r{(\d+)\.(\d+)}, + '\1.\2') + end + + def html_safe_gsub(string, *gsub_args, &) + html_safe = string.html_safe? + string.gsub(*gsub_args, &) + + # We only mark the string as safe if the previous string was already safe + if html_safe + string.html_safe # rubocop:disable Rails/OutputSafety + else + string + end end def authoring(created, author, options = {}) @@ -252,19 +264,18 @@ def accesskey(s) # Same as Rails' simple_format helper without using paragraphs def simple_format_without_paragraph(text) - text.to_s - .gsub(/\r\n?/, "\n") # \r\n and \r -> \n - .gsub(/\n\n+/, "

") # 2+ newline -> 2 br - .gsub(/([^\n]\n)(?=[^\n])/, '\1
') # 1 newline -> br - .html_safe + html_safe_gsub(text.to_s, /\r\n?/, "\n") + .then { |res| html_safe_gsub(res, /\n\n+/, "

") } + .then { |res| html_safe_gsub(res, /([^\n]\n)(?=[^\n])/, '\1
') } end def lang_options_for_select(blank = true) - auto = if blank && (valid_languages - all_languages) == (all_languages - valid_languages) - [["(auto)", ""]] - else - [] - end + auto = + if blank && (valid_languages - all_languages) == (all_languages - valid_languages) + [["(auto)", ""]] + else + [] + end mapped_languages = valid_languages.map { |lang| translate_language(lang) } @@ -344,10 +355,10 @@ def current_layout # A hash containing the following keys: # * width: (default '100px') the css-width for the progress bar # * legend: (default: '') the text displayed alond with the progress bar - def progress_bar(pcts, options = {}) + def progress_bar(pcts, options = {}) # rubocop:disable Metrics/AbcSize pcts = Array(pcts).map(&:round) closed = pcts[0] - done = pcts[1] || 0 + done = pcts[1] || 0 width = options[:width] || "100px;" legend = options[:legend] || "" total_progress = options[:hide_total_progress] ? "" : t(:total_progress) @@ -356,7 +367,7 @@ def progress_bar(pcts, options = {}) content_tag :span do progress = content_tag :span, class: "progress-bar", style: "width: #{width}" do concat content_tag(:span, "", class: "inner-progress closed", style: "width: #{closed}%") - concat content_tag(:span, "", class: "inner-progress done", style: "width: #{done}%") + concat content_tag(:span, "", class: "inner-progress done", style: "width: #{done}%") end progress + content_tag(:span, "#{legend}#{percent_sign} #{total_progress}", class: "progress-bar-legend") end @@ -369,8 +380,10 @@ def checked_image(checked = true) end def calendar_for(*_args) - ActiveSupport::Deprecation.warn "calendar_for has been removed. Please use the op-basic-single-date-picker angular component instead", - caller + ActiveSupport::Deprecation.warn( + "calendar_for has been removed. Please use the opce-basic-single-date-picker angular component instead", + caller + ) end def locale_first_day_of_week @@ -435,7 +448,7 @@ def permitted_params def translate_language(lang_code) # rename in-context translation language name for the language select box if lang_code.to_sym == Redmine::I18n::IN_CONTEXT_TRANSLATION_CODE && - ::I18n.locale != Redmine::I18n::IN_CONTEXT_TRANSLATION_CODE + ::I18n.locale != Redmine::I18n::IN_CONTEXT_TRANSLATION_CODE [Redmine::I18n::IN_CONTEXT_TRANSLATION_NAME, lang_code.to_s] else [I18n.t("cldr.language_name", locale: lang_code), lang_code.to_s] diff --git a/app/helpers/custom_fields_helper.rb b/app/helpers/custom_fields_helper.rb index fa2e4b641e9d..e5c8951c2e2d 100644 --- a/app/helpers/custom_fields_helper.rb +++ b/app/helpers/custom_fields_helper.rb @@ -63,7 +63,7 @@ def custom_fields_tabs end # Return custom field html tag corresponding to its format - def custom_field_tag(name, custom_value) + def custom_field_tag(name, custom_value) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity custom_field = custom_value.custom_field field_name = "#{name}[custom_field_values][#{custom_field.id}]" field_id = "#{name}_custom_field_values_#{custom_field.id}" @@ -72,7 +72,7 @@ def custom_field_tag(name, custom_value) tag = case field_format.try(:edit_as) when "date" - angular_component_tag "op-basic-single-date-picker", + angular_component_tag "opce-basic-single-date-picker", inputs: { required: custom_field.is_required, value: custom_value.value, @@ -143,14 +143,14 @@ def custom_field_tag_with_label(name, custom_value) custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value) end - def custom_field_tag_for_bulk_edit(name, custom_field, project = nil) + def custom_field_tag_for_bulk_edit(name, custom_field, project = nil) # rubocop:disable Metrics/AbcSize field_name = "#{name}[custom_field_values][#{custom_field.id}]" field_id = "#{name}_custom_field_values_#{custom_field.id}" field_format = OpenProject::CustomFieldFormat.find_by_name(custom_field.field_format) case field_format.try(:edit_as) when "date" - angular_component_tag "op-modal-single-date-picker", + angular_component_tag "opce-modal-single-date-picker", inputs: { id: field_id, name: field_name diff --git a/app/views/admin/backups/show.html.erb b/app/views/admin/backups/show.html.erb index 4c9d15683957..1b6f6fa64d16 100644 --- a/app/views/admin/backups/show.html.erb +++ b/app/views/admin/backups/show.html.erb @@ -90,7 +90,7 @@ See COPYRIGHT and LICENSE files for more details. <% end %> <% if @backup_token.present? %> -<%= tag :backup, data: { +<%= angular_component_tag "opce-backup", data: { 'job-status-id': @job_status_id, 'last-backup-date': @last_backup_date, 'last-backup-attachment-id': @last_backup_attachment_id, diff --git a/app/views/admin/settings/projects_settings/show.html.erb b/app/views/admin/settings/projects_settings/show.html.erb index aabbee95cf61..f8ee4cf5648e 100644 --- a/app/views/admin/settings/projects_settings/show.html.erb +++ b/app/views/admin/settings/projects_settings/show.html.erb @@ -61,14 +61,13 @@ See COPYRIGHT and LICENSE files for more details. - <%= content_tag 'editable-query-props', - '', - data: { - name: 'settings[project_gantt_query]', - id: 'settings_project_gantt_query', - query: ::Projects::GanttQueryGeneratorService.current_query, - 'url-params': 'true' - } + <%= angular_component_tag 'opce-editable-query-props', + data: { + name: 'settings[project_gantt_query]', + id: 'settings_project_gantt_query', + query: ::Projects::GanttQueryGeneratorService.current_query, + 'url-params': 'true' + } %> diff --git a/app/views/admin/settings/work_packages_settings/_enterprise_feature_hint.html.erb b/app/views/admin/settings/work_packages_settings/_enterprise_feature_hint.html.erb index 88daaf178482..d82dc602b7c1 100644 --- a/app/views/admin/settings/work_packages_settings/_enterprise_feature_hint.html.erb +++ b/app/views/admin/settings/work_packages_settings/_enterprise_feature_hint.html.erb @@ -1,7 +1,7 @@ <% unless EnterpriseToken.allows_to?(ee_feature) %> <%= - angular_component_tag 'op-enterprise-banner', + angular_component_tag 'opce-enterprise-banner', inputs: { collapsible: true, textMessage: explanation, diff --git a/app/views/admin/settings/working_days_and_hours_settings/show.html.erb b/app/views/admin/settings/working_days_and_hours_settings/show.html.erb index 6dd8ceaac2ba..80f82d5479fc 100644 --- a/app/views/admin/settings/working_days_and_hours_settings/show.html.erb +++ b/app/views/admin/settings/working_days_and_hours_settings/show.html.erb @@ -90,7 +90,7 @@ See COPYRIGHT and LICENSE files for more details.

<%= t("working_days.instance_wide_info") %>

- <%= angular_component_tag "op-non-working-days-list", + <%= angular_component_tag "opce-non-working-days-list", data: { modified_non_working_days: @modified_non_working_days } %> diff --git a/app/views/attribute_help_texts/_tab.html.erb b/app/views/attribute_help_texts/_tab.html.erb index edc64251d148..8b59eed73835 100644 --- a/app/views/attribute_help_texts/_tab.html.erb +++ b/app/views/attribute_help_texts/_tab.html.erb @@ -2,12 +2,12 @@ <% if entries.any? %>
-
+
- - - + + + @@ -42,7 +42,7 @@ edit_attribute_help_text_path(attribute_help_text) %>
- <%= angular_component_tag 'attribute-help-text', + <%= angular_component_tag 'opce-attribute-help-text', inputs: { helpTextId: attribute_help_text.id, attribute: attribute_help_text.attribute_name, diff --git a/app/views/augmented/_collapsible_section.html.erb b/app/views/augmented/_collapsible_section.html.erb index b223a5c174aa..4b9986a9ffd9 100644 --- a/app/views/augmented/_collapsible_section.html.erb +++ b/app/views/augmented/_collapsible_section.html.erb @@ -1,6 +1,6 @@ - - - <% end %> @@ -116,8 +117,8 @@ - - + + diff --git a/app/views/custom_fields/_custom_options.html.erb b/app/views/custom_fields/_custom_options.html.erb index 42104014231b..f20f6934f719 100644 --- a/app/views/custom_fields/_custom_options.html.erb +++ b/app/views/custom_fields/_custom_options.html.erb @@ -32,12 +32,12 @@ See COPYRIGHT and LICENSE files for more details.
- +
- - - + + + diff --git a/app/views/custom_fields/_tab.html.erb b/app/views/custom_fields/_tab.html.erb index 61ca5e638b2a..5616d5199745 100644 --- a/app/views/custom_fields/_tab.html.erb +++ b/app/views/custom_fields/_tab.html.erb @@ -39,17 +39,17 @@ See COPYRIGHT and LICENSE files for more details. <% end %> -
+
- - + + <% if tab[:name] == 'WorkPackageCustomField' %> - - + + <% end %> - - + + diff --git a/app/views/custom_styles/show.html.erb b/app/views/custom_styles/show.html.erb index 1368cf54c11d..9ffedb2176f1 100644 --- a/app/views/custom_styles/show.html.erb +++ b/app/views/custom_styles/show.html.erb @@ -156,10 +156,10 @@ See COPYRIGHT and LICENSE files for more details. <% end %> <% custom_export_expanded = @custom_style.id && (@custom_style.export_logo.present? || @custom_style.export_cover.present? || @custom_style.export_cover_text_color.present?) %> - - -
+
- - - - + + + + <% if User.current.allowed_in_project?(:manage_forums, @project) %> - + <% end %> diff --git a/app/views/forums/show.html.erb b/app/views/forums/show.html.erb index 3e14c6529c23..cf7cc4615d4e 100644 --- a/app/views/forums/show.html.erb +++ b/app/views/forums/show.html.erb @@ -69,13 +69,13 @@ See COPYRIGHT and LICENSE files for more details. <% if @topics.any? %>
-
+
- - - - - + + + + + diff --git a/app/views/groups/_memberships.html.erb b/app/views/groups/_memberships.html.erb index 802458872afe..a7d554fff612 100644 --- a/app/views/groups/_memberships.html.erb +++ b/app/views/groups/_memberships.html.erb @@ -35,11 +35,11 @@ See COPYRIGHT and LICENSE files for more details. <% if @group.memberships.any? %>
-
+
- - + + diff --git a/app/views/groups/_users_table.html.erb b/app/views/groups/_users_table.html.erb index b8595fb23a84..3cf29cddc2c2 100644 --- a/app/views/groups/_users_table.html.erb +++ b/app/views/groups/_users_table.html.erb @@ -29,8 +29,8 @@ See COPYRIGHT and LICENSE files for more details.
- + diff --git a/app/views/groups/index.html.erb b/app/views/groups/index.html.erb index 533d13cb5d54..2634231994fc 100644 --- a/app/views/groups/index.html.erb +++ b/app/views/groups/index.html.erb @@ -54,11 +54,11 @@ See COPYRIGHT and LICENSE files for more details. <% if @groups.any? %>
-
+
- - + + diff --git a/app/views/homescreen/blocks/_new_features.html.erb b/app/views/homescreen/blocks/_new_features.html.erb index a84a40236383..d005bbc7e571 100644 --- a/app/views/homescreen/blocks/_new_features.html.erb +++ b/app/views/homescreen/blocks/_new_features.html.erb @@ -1,3 +1,3 @@ <%= render 'homescreen/blocks/header', title: t(:label_new_features) %> - + diff --git a/app/views/individual_principals/_memberships.html.erb b/app/views/individual_principals/_memberships.html.erb index 5879ceaa5816..80c0839358de 100644 --- a/app/views/individual_principals/_memberships.html.erb +++ b/app/views/individual_principals/_memberships.html.erb @@ -40,8 +40,8 @@ See COPYRIGHT and LICENSE files for more details.
- - + + <%= call_individual_principals_memberships_hook(@individual_principal, 'colgroup') %> diff --git a/app/views/journals/_diff.html.erb b/app/views/journals/_diff.html.erb index 0ebdab4b374b..c07444eedc6e 100644 --- a/app/views/journals/_diff.html.erb +++ b/app/views/journals/_diff.html.erb @@ -28,5 +28,5 @@ See COPYRIGHT and LICENSE files for more details. ++#%>
- <%= simple_format_without_paragraph diff.to_html %> + <%= simple_format_without_paragraph diff.to_html.html_safe %>
diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb index f156ae23e787..3fc45b8b3903 100644 --- a/app/views/layouts/base.html.erb +++ b/app/views/layouts/base.html.erb @@ -62,9 +62,9 @@ See COPYRIGHT and LICENSE files for more details.

- - - + + + <% main_menu = render_main_menu(local_assigns.fetch(:menu_name, nil), @project) %> <% side_displayed = content_for?(:sidebar) || content_for?(:main_menu) || !main_menu.blank? %> <% initial_classes = initial_menu_classes(side_displayed, show_decoration) %> @@ -75,7 +75,7 @@ See COPYRIGHT and LICENSE files for more details.

<%= t(:label_top_menu) %>

- + @@ -102,7 +102,7 @@ See COPYRIGHT and LICENSE files for more details. data-application-target="dynamic" >

<%= t(:label_main_menu) %>

- +
+
<%= call_hook(:view_access_tokens_table, user: @user) %>
- diff --git a/app/views/my/access_token_partials/_icalendar_tokens_section.html.erb b/app/views/my/access_token_partials/_icalendar_tokens_section.html.erb index 3fd2595a7564..b27005601990 100644 --- a/app/views/my/access_token_partials/_icalendar_tokens_section.html.erb +++ b/app/views/my/access_token_partials/_icalendar_tokens_section.html.erb @@ -40,17 +40,17 @@ See COPYRIGHT and LICENSE files for more details. <% if ical_tokens_grouped_by_query.any? %>
- +
<%= render partial: "my/access_token_partials/token_table_header", - locals: { + locals: { column_headers: [ t('attributes.name'), WorkPackage.human_attribute_name(:calendar), WorkPackage.human_attribute_name(:project), User.human_attribute_name(:created_at), t('my_account.access_tokens.headers.expiration') - ] - } + ] + } %> <% ical_tokens_grouped_by_query.each do |query_id, tokens| %> @@ -103,5 +103,3 @@ See COPYRIGHT and LICENSE files for more details. <% end %> - - diff --git a/app/views/my/access_token_partials/_oauth_tokens_section.html.erb b/app/views/my/access_token_partials/_oauth_tokens_section.html.erb index eca2435c12f0..846c288f25de 100644 --- a/app/views/my/access_token_partials/_oauth_tokens_section.html.erb +++ b/app/views/my/access_token_partials/_oauth_tokens_section.html.erb @@ -39,15 +39,15 @@ See COPYRIGHT and LICENSE files for more details. <% if granted_applications.any? %>
-
+
<%= render partial: "my/access_token_partials/token_table_header", - locals: { + locals: { column_headers: [ - t('attributes.name'), + t('attributes.name'), User.human_attribute_name(:created_at), t('my_account.access_tokens.headers.expiration') - ] - } + ] + } %> <% granted_applications.each do |application, tokens| %> diff --git a/app/views/my/access_token_partials/_rss_tokens_section.html.erb b/app/views/my/access_token_partials/_rss_tokens_section.html.erb index 3557cecf7c06..45e7389635ac 100644 --- a/app/views/my/access_token_partials/_rss_tokens_section.html.erb +++ b/app/views/my/access_token_partials/_rss_tokens_section.html.erb @@ -40,15 +40,15 @@ See COPYRIGHT and LICENSE files for more details. <% if rss_token %>
-
+
<%= render partial: "my/access_token_partials/token_table_header", - locals: { + locals: { column_headers: [ - t('attributes.name'), - User.human_attribute_name(:created_at), + t('attributes.name'), + User.human_attribute_name(:created_at), t('my_account.access_tokens.headers.expiration') - ] - } + ] + } %> @@ -88,5 +88,3 @@ See COPYRIGHT and LICENSE files for more details. <% end %> - - diff --git a/app/views/my/access_token_partials/_storage_tokens_section.html.erb b/app/views/my/access_token_partials/_storage_tokens_section.html.erb index ede26b4c1c7e..7439402967ff 100644 --- a/app/views/my/access_token_partials/_storage_tokens_section.html.erb +++ b/app/views/my/access_token_partials/_storage_tokens_section.html.erb @@ -39,7 +39,7 @@ See COPYRIGHT and LICENSE files for more details. <% if @storage_tokens.any? %>
-
+
<%= render partial: "my/access_token_partials/token_table_header", locals: { column_headers: [ diff --git a/app/views/my/access_token_partials/_token_table_header.html.erb b/app/views/my/access_token_partials/_token_table_header.html.erb index 5bc75f81206b..429783977266 100644 --- a/app/views/my/access_token_partials/_token_table_header.html.erb +++ b/app/views/my/access_token_partials/_token_table_header.html.erb @@ -28,9 +28,9 @@ See COPYRIGHT and LICENSE files for more details. ++#%> <% column_headers&.length&.times do %> - + <% end %> - + @@ -49,4 +49,4 @@ See COPYRIGHT and LICENSE files for more details. - \ No newline at end of file + diff --git a/app/views/principals/_global_roles.html.erb b/app/views/principals/_global_roles.html.erb index 8b261bc1ae63..cfed1b097d01 100644 --- a/app/views/principals/_global_roles.html.erb +++ b/app/views/principals/_global_roles.html.erb @@ -39,10 +39,10 @@ See COPYRIGHT and LICENSE files for more details. <% else %>
-
+
- + diff --git a/app/views/projects/settings/categories/show.html.erb b/app/views/projects/settings/categories/show.html.erb index dbf2e34c453d..8b8a000aaba6 100644 --- a/app/views/projects/settings/categories/show.html.erb +++ b/app/views/projects/settings/categories/show.html.erb @@ -44,11 +44,11 @@ See COPYRIGHT and LICENSE files for more details. <% if @project.categories.any? %>
-
+
- - + + diff --git a/app/views/projects/settings/custom_fields/_form.html.erb b/app/views/projects/settings/custom_fields/_form.html.erb index 1255ad99ca32..093ebc211c91 100644 --- a/app/views/projects/settings/custom_fields/_form.html.erb +++ b/app/views/projects/settings/custom_fields/_form.html.erb @@ -29,11 +29,11 @@ See COPYRIGHT and LICENSE files for more details.
-
+
- - - + + + diff --git a/app/views/projects/settings/types/_form.html.erb b/app/views/projects/settings/types/_form.html.erb index 6ff1000bcca0..99adecdc6125 100644 --- a/app/views/projects/settings/types/_form.html.erb +++ b/app/views/projects/settings/types/_form.html.erb @@ -29,13 +29,13 @@ See COPYRIGHT and LICENSE files for more details.
-
+
- - - - - + + + + + diff --git a/app/views/repositories/_dir_list.html.erb b/app/views/repositories/_dir_list.html.erb index 5b417c530357..89cd99600bd8 100644 --- a/app/views/repositories/_dir_list.html.erb +++ b/app/views/repositories/_dir_list.html.erb @@ -28,15 +28,15 @@ See COPYRIGHT and LICENSE files for more details. ++#%>
-
+
- - - - - - + + + + + + diff --git a/app/views/repositories/_repository_header.html.erb b/app/views/repositories/_repository_header.html.erb index 81e20bd99845..c4ceadb34974 100644 --- a/app/views/repositories/_repository_header.html.erb +++ b/app/views/repositories/_repository_header.html.erb @@ -27,7 +27,7 @@ See COPYRIGHT and LICENSE files for more details. ++#%> - + <%= toolbar title: t('repositories.named_repository', vendor_name: @repository.class.vendor_name) do %> <% if @instructions && @instructions.available? %> @@ -40,9 +40,9 @@ See COPYRIGHT and LICENSE files for more details. value="<%= @instructions.checkout_url(true) %>" readonly> <% csp_onclick('this.focus(); this.select();', '#repository-checkout-url') %> - - +
- + + + + - - -
diff --git a/app/views/repositories/committers.html.erb b/app/views/repositories/committers.html.erb index 50ca04e46932..510ae2296958 100644 --- a/app/views/repositories/committers.html.erb +++ b/app/views/repositories/committers.html.erb @@ -34,10 +34,10 @@ See COPYRIGHT and LICENSE files for more details. <%= form_tag({}) do %>
- +
- - + + diff --git a/app/views/roles/index.html.erb b/app/views/roles/index.html.erb index a5a3c55aa2ac..2348b1f8ce5f 100644 --- a/app/views/roles/index.html.erb +++ b/app/views/roles/index.html.erb @@ -53,12 +53,12 @@ See COPYRIGHT and LICENSE files for more details.
-
+
- - - + + + diff --git a/app/views/roles/report.html.erb b/app/views/roles/report.html.erb index 0fcfd9c8cba0..cc6255d538c2 100644 --- a/app/views/roles/report.html.erb +++ b/app/views/roles/report.html.erb @@ -61,14 +61,14 @@ See COPYRIGHT and LICENSE files for more details.
-
+
- - - - - - + + + + + + diff --git a/app/views/search/index.html.erb b/app/views/search/index.html.erb index 4646329ee85f..92da85f9c56d 100644 --- a/app/views/search/index.html.erb +++ b/app/views/search/index.html.erb @@ -32,12 +32,12 @@ See COPYRIGHT and LICENSE files for more details. <% end %> - + - + <% if params[:work_packages].present? %> - + <% else %>

<%= t(:label_result_plural) %> (<%= @results_count&.values&.sum || 0 %>)

diff --git a/app/views/statuses/_form.html.erb b/app/views/statuses/_form.html.erb index 0f2f43fc165a..ccc96b2d68d0 100644 --- a/app/views/statuses/_form.html.erb +++ b/app/views/statuses/_form.html.erb @@ -44,7 +44,7 @@ See COPYRIGHT and LICENSE files for more details. <% if disabled %>
<%= - angular_component_tag "op-enterprise-banner", + angular_component_tag "opce-enterprise-banner", inputs: { collapsible: true, textMessage: t("text_wp_status_read_only_html"), diff --git a/app/views/types/form/_form_configuration.html.erb b/app/views/types/form/_form_configuration.html.erb index 8646594c899d..8d4551607bdf 100644 --- a/app/views/types/form/_form_configuration.html.erb +++ b/app/views/types/form/_form_configuration.html.erb @@ -34,12 +34,12 @@ See COPYRIGHT and LICENSE files for more details.
<% if EnterpriseToken.show_banners? %> - <%= angular_component_tag 'op-no-results', + <%= angular_component_tag 'opce-no-results', inputs: { title: t('text_form_configuration') + t('text_custom_field_hint_activate_per_project'), } %> - <%= angular_component_tag 'op-enterprise-banner', + <%= angular_component_tag 'opce-enterprise-banner', inputs: { collapsible: true, opReferrer: 'form-configuration', @@ -55,7 +55,7 @@ See COPYRIGHT and LICENSE files for more details. <% no_filter_query = ::API::V3::Queries::QueryParamsRepresenter.new(Query.new_default.tap { |q| q.filters = [] }).to_json %> <%= f.hidden_field :attribute_groups, value: '', class: 'admin-type-form--hidden-field' %> - <%= content_tag('admin-type-form-configuration', + <%= content_tag('opce-admin-type-form-configuration', '', data: { 'active-groups': form_attributes[:actives], diff --git a/app/views/types/index.html.erb b/app/views/types/index.html.erb index 8743fc7fcc3a..7794772696fe 100644 --- a/app/views/types/index.html.erb +++ b/app/views/types/index.html.erb @@ -54,16 +54,16 @@ See COPYRIGHT and LICENSE files for more details. <% if @types.any? %>
-
+
- - - - - - + + + + + + diff --git a/app/views/versions/show.html.erb b/app/views/versions/show.html.erb index 25b6c95d35e1..b1c43a297acc 100644 --- a/app/views/versions/show.html.erb +++ b/app/views/versions/show.html.erb @@ -109,10 +109,10 @@ See COPYRIGHT and LICENSE files for more details. <% if @version.work_packages.count > 0 %>
- - +
<% end %> diff --git a/app/views/wiki/_page_form.html.erb b/app/views/wiki/_page_form.html.erb index 9b55008f36fc..8614f12f9dbf 100644 --- a/app/views/wiki/_page_form.html.erb +++ b/app/views/wiki/_page_form.html.erb @@ -4,7 +4,7 @@ <%= editable_toolbar(form: f, field_name: :title) do %>
  • - +
  • <% end %> diff --git a/app/views/wiki/history.html.erb b/app/views/wiki/history.html.erb index 1f141a955863..41994db6b0c7 100644 --- a/app/views/wiki/history.html.erb +++ b/app/views/wiki/history.html.erb @@ -37,13 +37,13 @@ See COPYRIGHT and LICENSE files for more details.
    - + + - - - + +
    diff --git a/app/views/work_packages/bulk/edit.html.erb b/app/views/work_packages/bulk/edit.html.erb index 33dcfa74cb66..cfb90829141f 100644 --- a/app/views/work_packages/bulk/edit.html.erb +++ b/app/views/work_packages/bulk/edit.html.erb @@ -151,7 +151,7 @@ See COPYRIGHT and LICENSE files for more details.
    <%= styled_label_tag :work_package_start_date, WorkPackage.human_attribute_name(:start_date) %>
    - <%= angular_component_tag 'op-basic-single-date-picker', + <%= angular_component_tag 'opce-basic-single-date-picker', inputs: { id: "work_package_start_date", name: "work_package[start_date]" @@ -162,7 +162,7 @@ See COPYRIGHT and LICENSE files for more details.
    <%= styled_label_tag :work_package_due_date, WorkPackage.human_attribute_name(:due_date) %>
    - <%= angular_component_tag 'op-basic-single-date-picker', + <%= angular_component_tag 'opce-basic-single-date-picker', inputs: { id: "work_package_due_date", name: "work_package[due_date]" diff --git a/app/views/work_packages/moves/new.html.erb b/app/views/work_packages/moves/new.html.erb index db732055f370..921b0724c6ba 100644 --- a/app/views/work_packages/moves/new.html.erb +++ b/app/views/work_packages/moves/new.html.erb @@ -156,7 +156,7 @@ See COPYRIGHT and LICENSE files for more details.
    - <%= angular_component_tag 'op-basic-single-date-picker', + <%= angular_component_tag 'opce-basic-single-date-picker', inputs: { id: "start_date", name: "start_date" @@ -167,7 +167,7 @@ See COPYRIGHT and LICENSE files for more details.
    - <%= angular_component_tag 'op-basic-single-date-picker', + <%= angular_component_tag 'opce-basic-single-date-picker', inputs: { id: "due_date", name: "due_date" diff --git a/app/views/work_packages/reports/_report.html.erb b/app/views/work_packages/reports/_report.html.erb index eb6b0b9fe23e..f8320605da61 100644 --- a/app/views/work_packages/reports/_report.html.erb +++ b/app/views/work_packages/reports/_report.html.erb @@ -36,10 +36,10 @@ See COPYRIGHT and LICENSE files for more details. <% column_names = report.statuses.map(&:name) + [t(:label_open_work_packages), t(:label_closed_work_packages), t(:label_total)] %>
    - +
    <% column_names.each do |name| %> - + <% end %> diff --git a/app/views/workflows/show.html.erb b/app/views/workflows/show.html.erb index 6d71a6e7d28f..132c765088fb 100644 --- a/app/views/workflows/show.html.erb +++ b/app/views/workflows/show.html.erb @@ -34,15 +34,15 @@ See COPYRIGHT and LICENSE files for more details.
    -
    +
    + - - - - - + + + + diff --git a/docs/development/application-architecture/README.md b/docs/development/application-architecture/README.md index 15a23acfae9a..2c3334808782 100644 --- a/docs/development/application-architecture/README.md +++ b/docs/development/application-architecture/README.md @@ -217,7 +217,7 @@ A response that is fully controlled by Rails but extended by some Angular compon 3. The rendered response is returned to the Browser and Angular is initialized globally once in [`frontend/src/main.ts`](https://github.com/opf/openproject/blob/dev/frontend/src/main.ts#L48-L49). -4. A global service, the [`DynamicBootstrapper`](https://github.com/opf/openproject/blob/dev/frontend/src/app/core/setup/globals/dynamic-bootstrapper.ts), looks for eligible components to bootstrap in the rendered template and forces the global angular application to bootstrap this component. This may result in many dom-separated components in the page to be bootstrapped by Angular for dynamic content. +4. Dynamic components in Angular are registered as [custom elements](https://angular.dev/guide/elements) so they can be used throughout the page as web components. 5. This triggers the [`FormConfigurationComponent`](https://github.com/opf/openproject/blob/dev/frontend/src/app/features/admin/types/type-form-configuration.component.ts) to be initialized and allows the application to include a highly dynamic component (drag & drop organization of attributes) to be used on an admin form that otherwise has no connection to Angular. diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 79d2b8796959..8e1c4fb6f5ce 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -176,6 +176,8 @@ module.exports = { // It's common in Angular to wrap even pure functions in classes for injection purposes "class-methods-use-this": "off", + + "spaced-comment": "off", }, }, { diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 86d3af6284ed..81d9f1356ff7 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { APP_INITIALIZER, ApplicationRef, Injector, NgModule } from '@angular/core'; +import { APP_INITIALIZER, ApplicationRef, DoBootstrap, Injector, NgModule } from '@angular/core'; import { A11yModule } from '@angular/cdk/a11y'; import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http'; import { ReactiveFormsModule } from '@angular/forms'; @@ -38,7 +38,6 @@ import { OpenprojectFieldsModule } from 'core-app/shared/components/fields/openp import { OpSharedModule } from 'core-app/shared/shared.module'; import { OpSpotModule } from 'core-app/spot/spot.module'; import { OpDragScrollDirective } from 'core-app/shared/directives/op-drag-scroll/op-drag-scroll.directive'; -import { DynamicBootstrapper } from 'core-app/core/setup/globals/dynamic-bootstrapper'; import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module'; import { OpenprojectAttachmentsModule } from 'core-app/shared/components/attachments/openproject-attachments.module'; import { OpenprojectEditorModule } from 'core-app/shared/components/editor/openproject-editor.module'; @@ -92,8 +91,6 @@ import { MainMenuResizerComponent } from 'core-app/shared/components/resizer/res import { OpenprojectTabsModule } from 'core-app/shared/components/tabs/openproject-tabs.module'; import { OpenprojectAdminModule } from 'core-app/features/admin/openproject-admin.module'; import { OpenprojectHalModule } from 'core-app/features/hal/openproject-hal.module'; -import { globalDynamicComponents } from 'core-app/core/setup/global-dynamic-components.const'; -import { HookService } from 'core-app/features/plugins/hook-service'; import { OpenprojectPluginsModule } from 'core-app/features/plugins/openproject-plugins.module'; import { LinkedPluginsModule } from 'core-app/features/plugins/linked-plugins.module'; import { @@ -171,12 +168,76 @@ import { import { WorkPackageSplitViewEntryComponent, } from 'core-app/features/work-packages/routing/wp-split-view/wp-split-view-entry.component'; -import { InAppNotificationsDateAlertsUpsaleComponent } from 'core-app/features/in-app-notifications/date-alerts-upsale/ian-date-alerts-upsale.component'; +import { + InAppNotificationsDateAlertsUpsaleComponent, +} from 'core-app/features/in-app-notifications/date-alerts-upsale/ian-date-alerts-upsale.component'; import { ShareUpsaleComponent } from 'core-app/features/enterprise/share-upsale/share-upsale.component'; import { StorageLoginButtonComponent, } from 'core-app/shared/components/storages/storage-login-button/storage-login-button.component'; import { OpCustomModalOverlayComponent } from 'core-app/shared/components/modal/custom-modal-overlay.component'; +import { TimerAccountMenuComponent } from 'core-app/shared/components/time_entries/timer/timer-account-menu.component'; +import { + RemoteFieldUpdaterComponent, +} from 'core-app/shared/components/remote-field-updater/remote-field-updater.component'; +import { + OpModalSingleDatePickerComponent, +} from 'core-app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component'; +import { SpotDropModalPortalComponent } from 'core-app/spot/components/drop-modal/drop-modal-portal.component'; +import { OpModalOverlayComponent } from 'core-app/shared/components/modal/modal-overlay.component'; +import { + InAppNotificationBellComponent, +} from 'core-app/features/in-app-notifications/bell/in-app-notification-bell.component'; +import { BackupComponent } from 'core-app/core/setup/globals/components/admin/backup.component'; +import { + EditableQueryPropsComponent, +} from 'core-app/features/admin/editable-query-props/editable-query-props.component'; +import { + TriggerActionsEntryComponent, +} from 'core-app/shared/components/time_entries/edit/trigger-actions-entry.component'; +import { + WorkPackageOverviewGraphComponent, +} from 'core-app/shared/components/work-package-graphs/overview/wp-overview-graph.component'; +import { + EEActiveSavedTrialComponent, +} from 'core-app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component'; +import { FreeTrialButtonComponent } from 'core-app/features/enterprise/free-trial-button/free-trial-button.component'; +import { EnterpriseBaseComponent } from 'core-app/features/enterprise/enterprise-base.component'; +import { NoResultsComponent } from 'core-app/shared/components/no-results/no-results.component'; +import { + OpNonWorkingDaysListComponent, +} from 'core-app/shared/components/op-non-working-days-list/op-non-working-days-list.component'; +import { EnterpriseBannerComponent } from 'core-app/shared/components/enterprise-banner/enterprise-banner.component'; +import { + CollapsibleSectionComponent, +} from 'core-app/shared/components/collapsible-section/collapsible-section.component'; +import { CopyToClipboardComponent } from 'core-app/shared/components/copy-to-clipboard/copy-to-clipboard.component'; +import { GlobalSearchTitleComponent } from 'core-app/core/global_search/title/global-search-title.component'; +import { ContentTabsComponent } from 'core-app/shared/components/tabs/content-tabs/content-tabs.component'; +import { + AddSectionDropdownComponent, +} from 'core-app/shared/components/hide-section/add-section-dropdown/add-section-dropdown.component'; +import { + HideSectionLinkComponent, +} from 'core-app/shared/components/hide-section/hide-section-link/hide-section-link.component'; +import { PersistentToggleComponent } from 'core-app/shared/components/persistent-toggle/persistent-toggle.component'; +import { TypeFormConfigurationComponent } from 'core-app/features/admin/types/type-form-configuration.component'; +import { ToastsContainerComponent } from 'core-app/shared/components/toaster/toasts-container.component'; +import { GlobalSearchWorkPackagesComponent } from 'core-app/core/global_search/global-search-work-packages.component'; +import { + CustomDateActionAdminComponent, +} from 'core-app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component'; +import { HomescreenNewFeaturesBlockComponent } from 'core-app/features/homescreen/blocks/new-features.component'; +import { GlobalSearchTabsComponent } from 'core-app/core/global_search/tabs/global-search-tabs.component'; +import { + ZenModeButtonComponent, +} from 'core-app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component'; +import { ColorsAutocompleterComponent } from 'core-app/shared/components/colors/colors-autocompleter.component'; +import { + StaticAttributeHelpTextComponent, +} from 'core-app/shared/components/attribute-help-texts/static-attribute-help-text.component'; +import { appBaseSelector, ApplicationBaseComponent } from 'core-app/core/routing/base/application-base.component'; +import { SpotSwitchComponent } from 'core-app/spot/components/switch/switch.component'; export function initializeServices(injector:Injector) { return () => { @@ -324,27 +385,16 @@ export function initializeServices(injector:Injector) { OpDragScrollDirective, ], }) -export class OpenProjectModule { +export class OpenProjectModule implements DoBootstrap { // noinspection JSUnusedGlobalSymbols ngDoBootstrap(appRef:ApplicationRef) { - // Register global dynamic components - // this is necessary to ensure they are not tree-shaken - // (if they are not used anywhere in Angular, they would be removed) - DynamicBootstrapper.register(...globalDynamicComponents); - - // Perform global dynamic bootstrapping of our entry components - // that are in the current DOM response. - DynamicBootstrapper.bootstrapOptionalDocument(appRef, document); - this.registerCustomElements(appRef.injector); + // Try to bootstrap a dynamic root element + const root = document.querySelector(appBaseSelector); + if (root) { + appRef.bootstrap(ApplicationBaseComponent, root); + } - // Call hook service to allow modules to bootstrap additional elements. - // We can't use ngDoBootstrap in nested modules since they are not called. - const hookService = (appRef as any)._injector.get(HookService); - hookService - .call('openProjectAngularBootstrap') - .forEach((results:{ selector:string, cls:any }[]) => { - DynamicBootstrapper.bootstrapOptionalDocument(appRef, document, results); - }); + this.registerCustomElements(appRef.injector); } private registerCustomElements(injector:Injector) { @@ -364,6 +414,7 @@ export class OpenProjectModule { registerCustomElement('opce-ckeditor-augmented-textarea', CkeditorAugmentedTextareaComponent, { injector }); registerCustomElement('opce-draggable-autocompleter', DraggableAutocompleteComponent, { injector }); registerCustomElement('opce-attribute-help-text', AttributeHelpTextComponent, { injector }); + registerCustomElement('opce-static-attribute-help-text', StaticAttributeHelpTextComponent, { injector }); registerCustomElement('opce-exclusion-info', OpExclusionInfoComponent, { injector }); registerCustomElement('opce-attachments', OpAttachmentsComponent, { injector }); registerCustomElement('opce-storage-login-button', StorageLoginButtonComponent, { injector }); @@ -380,5 +431,42 @@ export class OpenProjectModule { registerCustomElement('opce-ian-date-alerts-upsale', InAppNotificationsDateAlertsUpsaleComponent, { injector }); registerCustomElement('opce-share-upsale', ShareUpsaleComponent, { injector }); registerCustomElement('opce-wp-split-view', WorkPackageSplitViewEntryComponent, { injector }); + registerCustomElement('opce-timer-account-menu', TimerAccountMenuComponent, { injector }); + registerCustomElement('opce-remote-field-updater', RemoteFieldUpdaterComponent, { injector }); + registerCustomElement('opce-modal-single-date-picker', OpModalSingleDatePickerComponent, { injector }); + registerCustomElement('opce-basic-single-date-picker', OpBasicSingleDatePickerComponent, { injector }); + registerCustomElement('opce-storage-login-button', StorageLoginButtonComponent, { injector }); + registerCustomElement('opce-spot-drop-modal-portal', SpotDropModalPortalComponent, { injector }); + registerCustomElement('opce-spot-switch', SpotSwitchComponent, { injector }); + registerCustomElement('opce-modal-overlay', OpModalOverlayComponent, { injector }); + registerCustomElement('opce-in-app-notification-bell', InAppNotificationBellComponent, { injector }); + registerCustomElement('opce-backup', BackupComponent, { injector }); + registerCustomElement('opce-editable-query-props', EditableQueryPropsComponent, { injector }); + registerCustomElement('opce-time-entry-trigger-actions', TriggerActionsEntryComponent, { injector }); + registerCustomElement('opce-wp-overview-graph', WorkPackageOverviewGraphComponent, { injector }); + registerCustomElement('opce-header-project-select', OpHeaderProjectSelectComponent, { injector }); + registerCustomElement('opce-enterprise-active-saved-trial', EEActiveSavedTrialComponent, { injector }); + registerCustomElement('opce-free-trial-button', FreeTrialButtonComponent, { injector }); + registerCustomElement('opce-enterprise-base', EnterpriseBaseComponent, { injector }); + registerCustomElement('opce-no-results', NoResultsComponent, { injector }); + registerCustomElement('opce-non-working-days-list', OpNonWorkingDaysListComponent, { injector }); + registerCustomElement('opce-enterprise-banner', EnterpriseBannerComponent, { injector }); + registerCustomElement('opce-collapsible-section-augment', CollapsibleSectionComponent, { injector }); + registerCustomElement('opce-main-menu-toggle', MainMenuToggleComponent, { injector }); + registerCustomElement('opce-main-menu-resizer', MainMenuResizerComponent, { injector }); + registerCustomElement('opce-copy-to-clipboard', CopyToClipboardComponent, { injector }); + registerCustomElement('opce-global-search-title', GlobalSearchTitleComponent, { injector }); + registerCustomElement('opce-content-tabs', ContentTabsComponent, { injector }); + registerCustomElement('opce-add-section-dropdown', AddSectionDropdownComponent, { injector }); + registerCustomElement('opce-hide-section-link', HideSectionLinkComponent, { injector }); + registerCustomElement('opce-persistent-toggle', PersistentToggleComponent, { injector }); + registerCustomElement('opce-admin-type-form-configuration', TypeFormConfigurationComponent, { injector }); + registerCustomElement('opce-toasts-container', ToastsContainerComponent, { injector }); + registerCustomElement('opce-global-search-work-packages', GlobalSearchWorkPackagesComponent, { injector }); + registerCustomElement('opce-custom-date-action-admin', CustomDateActionAdminComponent, { injector }); + registerCustomElement('opce-homescreen-new-features-block', HomescreenNewFeaturesBlockComponent, { injector }); + registerCustomElement('opce-global-search-tabs', GlobalSearchTabsComponent, { injector }); + registerCustomElement('opce-zen-mode-toggle-button', ZenModeButtonComponent, { injector }); + registerCustomElement('opce-colors-autocompleter', ColorsAutocompleterComponent, { injector }); } } diff --git a/frontend/src/app/core/global_search/global-search-work-packages-entry.component.ts b/frontend/src/app/core/global_search/global-search-work-packages-entry.component.ts deleted file mode 100644 index 36a254c9146f..000000000000 --- a/frontend/src/app/core/global_search/global-search-work-packages-entry.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -//-- copyright -// OpenProject is an open source project management software. -// Copyright (C) the OpenProject GmbH -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License version 3. -// -// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -// Copyright (C) 2006-2013 Jean-Philippe Lang -// Copyright (C) 2010-2013 the ChiliProject Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// See COPYRIGHT and LICENSE files for more details. -//++ - -import { Component } from '@angular/core'; -import { - WorkPackageIsolatedQuerySpaceDirective, -} from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; - -export const globalSearchWorkPackagesSelectorEntry = 'global-search-work-packages-entry'; - -/** - * An entry component to be rendered by Rails which opens an isolated query space - * for the work package search embedded table. - */ -@Component({ - selector: globalSearchWorkPackagesSelectorEntry, - hostDirectives: [WorkPackageIsolatedQuerySpaceDirective], - template: '', -}) -export class GlobalSearchWorkPackagesEntryComponent { -} diff --git a/frontend/src/app/core/global_search/global-search-work-packages.component.ts b/frontend/src/app/core/global_search/global-search-work-packages.component.ts index 79538726cced..b6c22ff181f1 100644 --- a/frontend/src/app/core/global_search/global-search-work-packages.component.ts +++ b/frontend/src/app/core/global_search/global-search-work-packages.component.ts @@ -27,26 +27,39 @@ //++ import { - AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, Renderer2, + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + OnDestroy, + OnInit, + Renderer2, } from '@angular/core'; -import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helper'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; -import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; -import { WorkPackageTableConfigurationObject } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; +import { + WorkPackageTableConfigurationObject, +} from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { + WorkPackageViewFiltersService, +} from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; import { debounceTime, distinctUntilChanged, skip } from 'rxjs/operators'; import { combineLatest } from 'rxjs'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; -import { WorkPackageFiltersService } from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; - -export const globalSearchWorkPackagesSelector = 'global-search-work-packages'; +import { + WorkPackageFiltersService, +} from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; +import { + WorkPackageIsolatedQuerySpaceDirective, +} from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; @Component({ - selector: globalSearchWorkPackagesSelector, + selector: 'opce-global-search-work-packages', changeDetection: ChangeDetectionStrategy.OnPush, + hostDirectives: [WorkPackageIsolatedQuerySpaceDirective], template: ` { + .subscribe(() => { this.wpFilters.visible = false; this.setQueryProps(); }); @@ -115,6 +127,7 @@ export class GlobalSearchWorkPackagesComponent extends UntilDestroyedMixin imple } private setQueryProps():void { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ const filters:any[] = []; let columns = ['id', 'project', 'subject', 'type', 'status', 'updatedAt']; diff --git a/frontend/src/app/core/global_search/openproject-global-search.module.ts b/frontend/src/app/core/global_search/openproject-global-search.module.ts index 642ea9dc2794..5b14b2b0cf4a 100644 --- a/frontend/src/app/core/global_search/openproject-global-search.module.ts +++ b/frontend/src/app/core/global_search/openproject-global-search.module.ts @@ -33,9 +33,6 @@ import { GlobalSearchWorkPackagesComponent } from 'core-app/core/global_search/g import { GlobalSearchTabsComponent } from 'core-app/core/global_search/tabs/global-search-tabs.component'; import { GlobalSearchTitleComponent } from 'core-app/core/global_search/title/global-search-title.component'; import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; -import { - GlobalSearchWorkPackagesEntryComponent, -} from 'core-app/core/global_search/global-search-work-packages-entry.component'; import { OpenprojectAutocompleterModule, } from 'core-app/shared/components/autocompleter/openproject-autocompleter.module'; @@ -54,7 +51,6 @@ import { RecentItemsService } from 'core-app/core/recent-items.service'; ], declarations: [ GlobalSearchInputComponent, - GlobalSearchWorkPackagesEntryComponent, GlobalSearchWorkPackagesComponent, GlobalSearchTabsComponent, GlobalSearchTitleComponent, diff --git a/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts b/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts index a93478f3ff87..13dbb323b80d 100644 --- a/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts +++ b/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts @@ -26,24 +26,15 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectorRef, - Component, - OnDestroy, - Injector, - OnInit, - ChangeDetectionStrategy, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnDestroy, OnInit } from '@angular/core'; import { StateService } from '@uirouter/core'; import { Subscription } from 'rxjs'; import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; import { ScrollableTabsComponent } from 'core-app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component'; import { TabDefinition } from 'core-app/shared/components/tabs/tab.interface'; -export const globalSearchTabsSelector = 'global-search-tabs'; - @Component({ - selector: globalSearchTabsSelector, + selector: 'opce-global-search-tabs', templateUrl: '../../../shared/components/tabs/scrollable-tabs/scrollable-tabs.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) @@ -68,12 +59,14 @@ export class GlobalSearchTabsComponent extends ScrollableTabsComponent implement this.currentTabSub = this.globalSearchService .currentTab$ .subscribe((currentTab) => { + /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment */ this.currentTabId = currentTab; }); this.tabsSub = this.globalSearchService .tabs$ .subscribe((tabs) => { + /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment */ this.tabs = tabs; this.tabs.map((tab) => (tab.path = '#')); }); diff --git a/frontend/src/app/core/global_search/title/global-search-title.component.ts b/frontend/src/app/core/global_search/title/global-search-title.component.ts index 96385ff5932a..3477233e9b3c 100644 --- a/frontend/src/app/core/global_search/title/global-search-title.component.ts +++ b/frontend/src/app/core/global_search/title/global-search-title.component.ts @@ -26,6 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, @@ -41,11 +42,10 @@ import { CurrentProjectService } from 'core-app/core/current-project/current-pro import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; -export const globalSearchTitleSelector = 'global-search-title'; - @Component({ - selector: 'global-search-title', + selector: 'opce-global-search-title', templateUrl: './global-search-title.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class GlobalSearchTitleComponent extends UntilDestroyedMixin implements OnInit { @Input() public searchTerm:string; @@ -65,11 +65,13 @@ export class GlobalSearchTitleComponent extends UntilDestroyedMixin implements O in: this.I18n.t('js.label_in'), }; - constructor(readonly elementRef:ElementRef, + constructor( + readonly elementRef:ElementRef, readonly cdRef:ChangeDetectorRef, readonly globalSearchService:GlobalSearchService, readonly I18n:I18nService, - readonly injector:Injector) { + readonly injector:Injector, + ) { super(); } @@ -85,6 +87,7 @@ export class GlobalSearchTitleComponent extends UntilDestroyedMixin implements O ) .subscribe(([newSearchTerm, newProjectScope]) => { this.searchTerm = newSearchTerm; + /* eslint-disable-next-line @typescript-eslint/no-unsafe-argument */ this.project = this.projectText(newProjectScope); this.searchTitle = `${this.text.search_for} ${this.searchTerm} ${this.project === '' ? '' : this.text.in} ${this.project}`; diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.component.ts b/frontend/src/app/core/main-menu/main-menu-toggle.component.ts index 129343eb4f72..ad12425f5926 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.component.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.component.ts @@ -26,9 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit } from '@angular/core'; import { distinctUntilChanged } from 'rxjs/operators'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @@ -36,11 +34,10 @@ import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destr import { MainMenuToggleService } from './main-menu-toggle.service'; import { TopMenuService } from 'core-app/core/top-menu/top-menu.service'; -export const mainMenuToggleSelector = 'main-menu-toggle'; - @Component({ - selector: mainMenuToggleSelector, + selector: 'opce-main-menu-toggle', changeDetection: ChangeDetectionStrategy.OnPush, + // eslint-disable-next-line @angular-eslint/no-host-metadata-property host: { class: 'op-app-menu op-main-menu-toggle', }, diff --git a/frontend/src/app/core/setup/global-dynamic-components.const.ts b/frontend/src/app/core/setup/global-dynamic-components.const.ts deleted file mode 100644 index 006b5c52b797..000000000000 --- a/frontend/src/app/core/setup/global-dynamic-components.const.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { OptionalBootstrapDefinition } from 'core-app/core/setup/globals/dynamic-bootstrapper'; -import { appBaseSelector, ApplicationBaseComponent } from 'core-app/core/routing/base/application-base.component'; -import { - ColorsAutocompleterComponent, - colorsAutocompleterSelector, -} from 'core-app/shared/components/colors/colors-autocompleter.component'; -import { - ZenModeButtonComponent, - zenModeComponentSelector, -} from 'core-app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component'; -import { - GlobalSearchWorkPackagesComponent, - globalSearchWorkPackagesSelector, -} from 'core-app/core/global_search/global-search-work-packages.component'; -import { - CustomDateActionAdminComponent, - customDateActionAdminSelector, -} from 'core-app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component'; -import { - GlobalSearchWorkPackagesEntryComponent, - globalSearchWorkPackagesSelectorEntry, -} from 'core-app/core/global_search/global-search-work-packages-entry.component'; -import { - ToastsContainerComponent, - toastsContainerSelector, -} from 'core-app/shared/components/toaster/toasts-container.component'; -import { - CkeditorAugmentedTextareaComponent, - ckeditorAugmentedTextareaSelector, -} from 'core-app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component'; -import { - PersistentToggleComponent, - persistentToggleSelector, -} from 'core-app/shared/components/persistent-toggle/persistent-toggle.component'; -import { - HideSectionLinkComponent, - hideSectionLinkSelector, -} from 'core-app/shared/components/hide-section/hide-section-link/hide-section-link.component'; -import { - ShowSectionDropdownComponent, - showSectionDropdownSelector, -} from 'core-app/shared/components/hide-section/show-section-dropdown.component'; -import { - AddSectionDropdownComponent, - addSectionDropdownSelector, -} from 'core-app/shared/components/hide-section/add-section-dropdown/add-section-dropdown.component'; -import { - ContentTabsComponent, - contentTabsSelector, -} from 'core-app/shared/components/tabs/content-tabs/content-tabs.component'; -import { - CopyToClipboardComponent, - copyToClipboardSelector, -} from 'core-app/shared/components/copy-to-clipboard/copy-to-clipboard.component'; -import { - collapsibleSectionAugmentSelector, - CollapsibleSectionComponent, -} from 'core-app/shared/components/collapsible-section/collapsible-section.component'; -import { - headerProjectSelectSelector, - OpHeaderProjectSelectComponent, -} from 'core-app/shared/components/header-project-select/header-project-select.component'; -import { - RemoteFieldUpdaterComponent, - remoteFieldUpdaterSelector, -} from 'core-app/shared/components/remote-field-updater/remote-field-updater.component'; -import { - WorkPackageOverviewGraphComponent, - wpOverviewGraphSelector, -} from 'core-app/shared/components/work-package-graphs/overview/wp-overview-graph.component'; -import { - GlobalSearchTitleComponent, - globalSearchTitleSelector, -} from 'core-app/core/global_search/title/global-search-title.component'; -import { - GlobalSearchTabsComponent, - globalSearchTabsSelector, -} from 'core-app/core/global_search/tabs/global-search-tabs.component'; -import { - TriggerActionsEntryComponent, - triggerActionsEntryComponentSelector, -} from 'core-app/shared/components/time_entries/edit/trigger-actions-entry.component'; -import { - AttributeHelpTextComponent, - attributeHelpTextSelector, -} from 'core-app/shared/components/attribute-help-texts/attribute-help-text.component'; -import { SpotSwitchComponent, spotSwitchSelector } from 'core-app/spot/components/switch/switch.component'; -import { BackupComponent, backupSelector } from 'core-app/core/setup/globals/components/admin/backup.component'; -import { - EnterpriseBaseComponent, - enterpriseBaseSelector, -} from 'core-app/features/enterprise/enterprise-base.component'; -import { - FreeTrialButtonComponent, - freeTrialButtonSelector, -} from 'core-app/features/enterprise/free-trial-button/free-trial-button.component'; -import { - EnterpriseBannerComponent, - enterpriseBannerSelector, -} from 'core-app/shared/components/enterprise-banner/enterprise-banner.component'; -import { - EnterprisePageComponent, - enterprisePageSelector, -} from 'core-app/shared/components/enterprise-page/enterprise-page.component'; -import { - nonWorkingDaysListSelector, - OpNonWorkingDaysListComponent, -} from 'core-app/shared/components/op-non-working-days-list/op-non-working-days-list.component'; -import { - EEActiveSavedTrialComponent, - enterpriseActiveSavedTrialSelector, -} from 'core-app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component'; -import { NoResultsComponent, noResultsSelector } from 'app/shared/components/no-results/no-results.component'; -import { - HomescreenNewFeaturesBlockComponent, - homescreenNewFeaturesBlockSelector, -} from 'core-app/features/homescreen/blocks/new-features.component'; -import { MainMenuToggleComponent, mainMenuToggleSelector } from 'core-app/core/main-menu/main-menu-toggle.component'; -import { - MainMenuResizerComponent, - mainMenuResizerSelector, -} from 'core-app/shared/components/resizer/resizer/main-menu-resizer.component'; -import { - adminTypeFormConfigurationSelector, - TypeFormConfigurationComponent, -} from 'core-app/features/admin/types/type-form-configuration.component'; -import { - EditableQueryPropsComponent, - editableQueryPropsSelector, -} from 'core-app/features/admin/editable-query-props/editable-query-props.component'; -import { - InAppNotificationBellComponent, - opInAppNotificationBellSelector, -} from 'core-app/features/in-app-notifications/bell/in-app-notification-bell.component'; -import { - OpModalOverlayComponent, - opModalOverlaySelector, -} from 'core-app/shared/components/modal/modal-overlay.component'; -import { - OpModalSingleDatePickerComponent, - opModalSingleDatePickerSelector, -} from 'core-app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component'; -import { - OpBasicSingleDatePickerComponent, - opBasicSingleDatePickerSelector, -} from 'core-app/shared/components/datepicker/basic-single-date-picker/basic-single-date-picker.component'; -import { - SpotDropModalPortalComponent, - spotDropModalPortalComponentSelector, -} from 'core-app/spot/components/drop-modal/drop-modal-portal.component'; -import { - StaticAttributeHelpTextComponent, - staticAttributeHelpTextSelector, -} from 'core-app/shared/components/attribute-help-texts/static-attribute-help-text.component'; -import { - opStorageLoginButtonSelector, - StorageLoginButtonComponent, -} from 'core-app/shared/components/storages/storage-login-button/storage-login-button.component'; -import { - TimerAccountMenuComponent, - timerAccountSelector, -} from 'core-app/shared/components/time_entries/timer/timer-account-menu.component'; - -export const globalDynamicComponents:OptionalBootstrapDefinition[] = [ - { selector: appBaseSelector, cls: ApplicationBaseComponent }, - { selector: attributeHelpTextSelector, cls: AttributeHelpTextComponent }, - { selector: staticAttributeHelpTextSelector, cls: StaticAttributeHelpTextComponent }, - { selector: colorsAutocompleterSelector, cls: ColorsAutocompleterComponent }, - { selector: zenModeComponentSelector, cls: ZenModeButtonComponent }, - { selector: globalSearchTabsSelector, cls: GlobalSearchTabsComponent }, - { selector: globalSearchWorkPackagesSelector, cls: GlobalSearchWorkPackagesComponent }, - { selector: homescreenNewFeaturesBlockSelector, cls: HomescreenNewFeaturesBlockComponent }, - { selector: customDateActionAdminSelector, cls: CustomDateActionAdminComponent }, - { selector: globalSearchWorkPackagesSelectorEntry, cls: GlobalSearchWorkPackagesEntryComponent }, - { selector: toastsContainerSelector, cls: ToastsContainerComponent }, - { selector: adminTypeFormConfigurationSelector, cls: TypeFormConfigurationComponent }, - { selector: ckeditorAugmentedTextareaSelector, cls: CkeditorAugmentedTextareaComponent, embeddable: true }, - { selector: persistentToggleSelector, cls: PersistentToggleComponent }, - { selector: hideSectionLinkSelector, cls: HideSectionLinkComponent }, - { selector: showSectionDropdownSelector, cls: ShowSectionDropdownComponent }, - { selector: addSectionDropdownSelector, cls: AddSectionDropdownComponent }, - { selector: contentTabsSelector, cls: ContentTabsComponent }, - { selector: globalSearchTitleSelector, cls: GlobalSearchTitleComponent }, - { selector: copyToClipboardSelector, cls: CopyToClipboardComponent }, - { selector: mainMenuResizerSelector, cls: MainMenuResizerComponent }, - { selector: mainMenuToggleSelector, cls: MainMenuToggleComponent }, - { selector: collapsibleSectionAugmentSelector, cls: CollapsibleSectionComponent }, - { selector: enterpriseBannerSelector, cls: EnterpriseBannerComponent }, - { selector: nonWorkingDaysListSelector, cls: OpNonWorkingDaysListComponent }, - { selector: enterprisePageSelector, cls: EnterprisePageComponent }, - { selector: noResultsSelector, cls: NoResultsComponent }, - { selector: enterpriseBaseSelector, cls: EnterpriseBaseComponent }, - { selector: freeTrialButtonSelector, cls: FreeTrialButtonComponent }, - { selector: enterpriseActiveSavedTrialSelector, cls: EEActiveSavedTrialComponent }, - { selector: headerProjectSelectSelector, cls: OpHeaderProjectSelectComponent }, - { selector: wpOverviewGraphSelector, cls: WorkPackageOverviewGraphComponent }, - { selector: triggerActionsEntryComponentSelector, cls: TriggerActionsEntryComponent, embeddable: true }, - { selector: editableQueryPropsSelector, cls: EditableQueryPropsComponent }, - { selector: backupSelector, cls: BackupComponent }, - { selector: opInAppNotificationBellSelector, cls: InAppNotificationBellComponent }, - - { selector: opModalOverlaySelector, cls: OpModalOverlayComponent }, - { selector: spotDropModalPortalComponentSelector, cls: SpotDropModalPortalComponent }, - { selector: spotSwitchSelector, cls: SpotSwitchComponent }, - { selector: opStorageLoginButtonSelector, cls: StorageLoginButtonComponent }, - - { selector: opModalSingleDatePickerSelector, cls: OpModalSingleDatePickerComponent, embeddable: true }, - { selector: opBasicSingleDatePickerSelector, cls: OpBasicSingleDatePickerComponent, embeddable: true }, - - // It is important to initialize the remoteFieldUpdaterSelector after the datepickers, - // because we need to access the input field of the datepickers inside the remoteFieldUpdaterSelector - { selector: remoteFieldUpdaterSelector, cls: RemoteFieldUpdaterComponent }, - - { selector: timerAccountSelector, cls: TimerAccountMenuComponent }, -]; diff --git a/frontend/src/app/core/setup/globals/components/admin/backup.component.ts b/frontend/src/app/core/setup/globals/components/admin/backup.component.ts index 917a7fe2f784..98107f98594d 100644 --- a/frontend/src/app/core/setup/globals/components/admin/backup.component.ts +++ b/frontend/src/app/core/setup/globals/components/admin/backup.component.ts @@ -26,14 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, - Injector, - ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, Injector, ViewChild } from '@angular/core'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; @@ -44,10 +37,8 @@ import { OpenProjectBackupService } from 'core-app/core/backup/op-backup.service import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { HalError } from 'core-app/features/hal/services/hal-error'; -export const backupSelector = 'backup'; - @Component({ - selector: backupSelector, + selector: 'opce-backup', templateUrl: './backup.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) @@ -65,15 +56,13 @@ export class BackupComponent implements AfterViewInit { attachmentsDisabled: this.i18n.t('js.backup.attachments_disabled'), }; - public jobStatusId:string = this.elementRef.nativeElement.dataset.jobStatusId; - - public lastBackupDate:string = this.elementRef.nativeElement.dataset.lastBackupDate; + public jobStatusId = this.elementRef.nativeElement.dataset.jobStatusId as string; - public lastBackupAttachmentId:string = this.elementRef.nativeElement.dataset.lastBackupAttachmentId; + public lastBackupDate = this.elementRef.nativeElement.dataset.lastBackupDate as string; - public mayIncludeAttachments:boolean = this.elementRef.nativeElement.dataset.mayIncludeAttachments != 'false'; + public lastBackupAttachmentId = this.elementRef.nativeElement.dataset.lastBackupAttachmentId as string; - public isInProgress = false; + public mayIncludeAttachments = this.elementRef.nativeElement.dataset.mayIncludeAttachments !== 'false'; public includeAttachments = true; @@ -81,10 +70,10 @@ export class BackupComponent implements AfterViewInit { @InjectField() opBackup:OpenProjectBackupService; - @ViewChild('backupTokenInput') backupTokenInput:ElementRef; + @ViewChild('backupTokenInput') backupTokenInput:ElementRef; constructor( - readonly elementRef:ElementRef, + readonly elementRef:ElementRef, public injector:Injector, protected i18n:I18nService, protected toastService:ToastService, @@ -95,6 +84,7 @@ export class BackupComponent implements AfterViewInit { } ngAfterViewInit():void { + /* eslint-disable-next-line @typescript-eslint/no-unsafe-call */ this.backupTokenInput.nativeElement.focus(); } @@ -107,10 +97,6 @@ export class BackupComponent implements AfterViewInit { return this.pathHelper.attachmentDownloadPath(this.lastBackupAttachmentId, undefined); } - public includeAttachmentsDefault():boolean { - return this.mayIncludeAttachments; - } - public includeAttachmentsTitle():string { return this.mayIncludeAttachments ? '' : this.text.attachmentsDisabled; } @@ -129,7 +115,7 @@ export class BackupComponent implements AfterViewInit { .triggerBackup(backupToken, this.includeAttachments) .subscribe( (resp:HalResource) => { - this.jobStatusId = resp.jobStatusId; + this.jobStatusId = resp.jobStatusId as string; this.opModalService.show(JobStatusModalComponent, 'global', { jobId: resp.jobStatusId }); }, (error:HalError) => { diff --git a/frontend/src/app/core/setup/globals/dynamic-bootstrapper.ts b/frontend/src/app/core/setup/globals/dynamic-bootstrapper.ts deleted file mode 100644 index b2ed5fbaa357..000000000000 --- a/frontend/src/app/core/setup/globals/dynamic-bootstrapper.ts +++ /dev/null @@ -1,129 +0,0 @@ -// -- copyright -// OpenProject is an open source project management software. -// Copyright (C) the OpenProject GmbH -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License version 3. -// -// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -// Copyright (C) 2006-2013 Jean-Philippe Lang -// Copyright (C) 2010-2013 the ChiliProject Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// See COPYRIGHT and LICENSE files for more details. - -import { ComponentType } from '@angular/cdk/portal'; -import { ApplicationRef } from '@angular/core'; -import { debugLog } from 'core-app/shared/helpers/debug_output'; - -/** - * Optional bootstrap definition to allow selecting all matching - * DOM nodes to manually bootstrap them. - * - * This differs from Angular's bootstrap module definition since it expects these - * entries to be present on ALL pages. This is never the case for our optional - * bootstrapped components. - */ -export interface OptionalBootstrapDefinition { - // The DOM selector used to locate an optional node - selector:string; - // The component class tied to it. - cls:ComponentType; - // Whether the component may be embeddable in dynamically generated responses - // e.g., previews - embeddable?:boolean; -} - -/** - * Static lookup table for dynamically bootstrapped components within our application - */ -export class DynamicBootstrapper { - private static optionalBoostrapComponents:OptionalBootstrapDefinition[] = []; - - /** - * Register an optional bootstrap component to be dynamically bootstrapped - * whenever it occurs in the initially loaded DOM. - * - * @param {OptionalBootstrapDefinition} definition - */ - public static register(...defs:OptionalBootstrapDefinition[]) { - this.optionalBoostrapComponents.push(...defs); - } - - /** - * Perform bootstrapping of matched elements within the given document. - * - * @param {ApplicationRef} appRef The application reference to lookup elements. - * @param {Document} doc The document element - * @param {OptionalBootstrapDefinition[]|undefined} definitions An optional set of components to bootstrap - */ - public static bootstrapOptionalDocument(appRef:ApplicationRef, doc:Document, definitions = this.optionalBoostrapComponents) { - this.performBootstrap(appRef, doc, false, definitions); - } - - /** - * Perform bootstrapping of embeddable elements within the given node. - * - * @param {ApplicationRef} appRef The application reference to lookup elements. - * @param {HTMLElement} element A node to bootstrap elements within. - * @param {OptionalBootstrapDefinition[]|undefined} definitions An optional set of components to bootstrap - */ - public static bootstrapOptionalEmbeddable(appRef:ApplicationRef, element:HTMLElement, definitions = this.optionalBoostrapComponents) { - // Delay the execution to avoid bootstrapping the embedded components while - // the app is running the Change Detection. This was throwing "ApplicationRef.tick - // is called recursively" error because of bootstrapOptionalEmbeddable and - // bootstrapOptionalDocument were called too close (ie: ckEditor macros). - Promise.resolve().then(() => this.performBootstrap(appRef, element, true, definitions)); - } - - /** - * Get embeddable components - */ - public static getEmbeddable() { - return this.optionalBoostrapComponents.filter((el) => el.embeddable); - } - - /** - * Bootstrap within a given document (globally, all components available) or within an element (embeddable compoennts - * only). - * - * @param {ApplicationRef} appRef - * @param {Document | HTMLElement} root - * @param {boolean} embedded - */ - private static performBootstrap(appRef:ApplicationRef, root:Document|HTMLElement, embedded:boolean, definitions:OptionalBootstrapDefinition[]) { - definitions - .forEach((el) => { - // Skip non-embeddable components in an embedded bootstrap. - if (embedded && !el.embeddable) { - return; - } - - const elements = root.querySelectorAll(el.selector); - for (let i = 0; i < elements.length; i++) { - const target = elements[i]; - - if (!embedded && target.closest('[op-dynamic-bootstrap]')) { - debugLog(`Skipping nested bootstrap ${el.selector} in %O`, target); - return; - } - - appRef.bootstrap(el.cls, target); - target.setAttribute('op-dynamic-bootstrap', 'true'); - } - }); - } -} diff --git a/frontend/src/app/core/setup/globals/global-listeners/settings.ts b/frontend/src/app/core/setup/globals/global-listeners/settings.ts index 9392ea046b98..0c01896dc864 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/settings.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/settings.ts @@ -31,9 +31,10 @@ export function listenToSettingChanges() { const self = jQuery(select); const id:string = self.attr('id') || ''; const settingName = id.replace('lang-for-', ''); - const newLang = self.val(); + const newLang = self.val() as string; const textArea = jQuery(`#settings-${settingName}`); - const editor = textArea.siblings('ckeditor-augmented-textarea').data('editor'); + /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment */ + const editor = textArea.siblings('opce-ckeditor-augmented-textarea').data('editor'); return { id, settingName, newLang, textArea, editor, diff --git a/frontend/src/app/core/setup/init-globals.ts b/frontend/src/app/core/setup/init-globals.ts index 3558d044bd10..1dc8d4a6f4b4 100644 --- a/frontend/src/app/core/setup/init-globals.ts +++ b/frontend/src/app/core/setup/init-globals.ts @@ -30,5 +30,4 @@ import 'hammerjs'; // Global scripts previously part of the application.js // Avoid require.context since that crashes angular regularly -import './globals/dynamic-bootstrapper'; import './globals/openproject'; diff --git a/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts b/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts index 8197f534dbb8..30706c633fde 100644 --- a/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts +++ b/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts @@ -1,14 +1,11 @@ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { ExternalQueryConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service'; -import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; - -export const editableQueryPropsSelector = 'editable-query-props'; +import { + ExternalQueryConfigurationService, +} from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service'; @Component({ - selector: editableQueryPropsSelector, + selector: 'opce-editable-query-props', templateUrl: './editable-query-props.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) @@ -25,29 +22,32 @@ export class EditableQueryPropsComponent implements OnInit { edit_query: this.I18n.t('js.admin.type_form.edit_query'), }; - constructor(private elementRef:ElementRef, + constructor( + private elementRef:ElementRef, private I18n:I18nService, private cdRef:ChangeDetectorRef, - private urlParamsHelper:UrlParamsHelperService, - private externalQuery:ExternalQueryConfigurationService) { + private externalQuery:ExternalQueryConfigurationService, + ) { } ngOnInit() { const element = this.elementRef.nativeElement; - this.id = element.dataset.id; - this.name = element.dataset.name; + this.id = element.dataset.id as string; + this.name = element.dataset.name as string; this.urlParams = element.dataset.urlParams === 'true'; - this.queryProps = element.dataset.query; + this.queryProps = element.dataset.query as string; } public editQuery() { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const queryProperties = (() => { if (this.urlParams) { return this.queryProps; } try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return JSON.parse(this.queryProps); } catch (e) { console.error(`Failed to parse query props from ${this.queryProps}: ${e}`); @@ -56,9 +56,11 @@ export class EditableQueryPropsComponent implements OnInit { })(); this.externalQuery.show({ + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment currentQuery: queryProperties, urlParams: this.urlParams, - callback: (queryProps:any) => { + callback: (queryProps:string) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment this.queryProps = this.urlParams ? queryProps : JSON.stringify(queryProps); this.cdRef.detectChanges(); }, diff --git a/frontend/src/app/features/admin/types/type-form-configuration.component.ts b/frontend/src/app/features/admin/types/type-form-configuration.component.ts index 9400bd0a1f9a..b59844219594 100644 --- a/frontend/src/app/features/admin/types/type-form-configuration.component.ts +++ b/frontend/src/app/features/admin/types/type-form-configuration.component.ts @@ -1,8 +1,8 @@ -import { AfterViewInit, Component, ElementRef, OnInit, } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { - ExternalRelationQueryConfigurationService + ExternalRelationQueryConfigurationService, } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service'; import { DomAutoscrollService } from 'core-app/shared/helpers/drag-and-drop/dom-autoscroll.service'; import { DragulaService, DrakeWithModels } from 'ng2-dragula'; @@ -30,11 +30,10 @@ export interface TypeGroup { type:TypeGroupType; } -export const adminTypeFormConfigurationSelector = 'admin-type-form-configuration'; export const emptyTypeGroup = '__empty'; @Component({ - selector: adminTypeFormConfigurationSelector, + selector: 'opce-admin-type-form-configuration', templateUrl: './type-form-configuration.html', providers: [ TypeBannerService, @@ -234,7 +233,8 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen this.form.off('submit.typeformupdater'); this.form.trigger('submit'); }) - .catch(() => {}); + .catch(() => { + }); $event.preventDefault(); return false; diff --git a/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component.ts b/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component.ts index 891076f79f52..cdbe0554bae7 100644 --- a/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component.ts +++ b/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component.ts @@ -30,10 +30,9 @@ import { ChangeDetectionStrategy, Component, ElementRef } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { EEActiveTrialBase } from 'core-app/features/enterprise/enterprise-active-trial/ee-active-trial.base'; -export const enterpriseActiveSavedTrialSelector = 'enterprise-active-saved-trial'; @Component({ - selector: enterpriseActiveSavedTrialSelector, + selector: 'opce-enterprise-active-saved-trial', templateUrl: './ee-active-trial.component.html', styleUrls: ['./ee-active-trial.component.sass'], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/frontend/src/app/features/enterprise/enterprise-base.component.ts b/frontend/src/app/features/enterprise/enterprise-base.component.ts index 1546ce4922bf..1e0bb4926215 100644 --- a/frontend/src/app/features/enterprise/enterprise-base.component.ts +++ b/frontend/src/app/features/enterprise/enterprise-base.component.ts @@ -26,18 +26,18 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Injector } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { EnterpriseTrialModalComponent } from 'core-app/features/enterprise/enterprise-modal/enterprise-trial.modal'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { EnterpriseTrialService } from 'core-app/features/enterprise/enterprise-trial.service'; -export const enterpriseBaseSelector = 'enterprise-base'; - @Component({ - selector: enterpriseBaseSelector, + // eslint-disable-next-line @angular-eslint/component-selector + selector: 'enterprise-base', templateUrl: './enterprise-base.component.html', styleUrls: ['./enterprise-base.component.sass'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class EnterpriseBaseComponent { public text = { @@ -52,10 +52,12 @@ export class EnterpriseBaseComponent { try_another_email: this.I18n.t('js.admin.enterprise.trial.try_another_email'), }; - constructor(protected I18n:I18nService, + constructor( + protected I18n:I18nService, protected opModalService:OpModalService, readonly injector:Injector, - public eeTrialService:EnterpriseTrialService) { + public eeTrialService:EnterpriseTrialService, + ) { } public openTrialModal() { diff --git a/frontend/src/app/features/enterprise/free-trial-button/free-trial-button.component.ts b/frontend/src/app/features/enterprise/free-trial-button/free-trial-button.component.ts index e1e03356ed5b..0b3f0e1ce804 100644 --- a/frontend/src/app/features/enterprise/free-trial-button/free-trial-button.component.ts +++ b/frontend/src/app/features/enterprise/free-trial-button/free-trial-button.component.ts @@ -26,13 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Injector, - OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { EnterpriseTrialModalComponent } from 'core-app/features/enterprise/enterprise-modal/enterprise-trial.modal'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; @@ -43,14 +37,13 @@ import { HttpClient } from '@angular/common/http'; import { GonService } from 'core-app/core/gon/gon.service'; import { IEnterpriseData } from 'core-app/features/enterprise/enterprise-trial.model'; -export const freeTrialButtonSelector = 'free-trial-button'; - export interface EETrialKey { created:string; value:string; } + @Component({ - selector: freeTrialButtonSelector, + selector: 'opce-free-trial-button', templateUrl: './free-trial-button.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) @@ -67,14 +60,16 @@ export class FreeTrialButtonComponent implements OnInit { }), }; - constructor(protected I18n:I18nService, + constructor( + protected I18n:I18nService, protected opModalService:OpModalService, readonly injector:Injector, readonly http:HttpClient, readonly cdRef:ChangeDetectorRef, readonly Gon:GonService, public eeTrialService:EnterpriseTrialService, - readonly timezoneService:TimezoneService) { + readonly timezoneService:TimezoneService, + ) { } ngOnInit():void { diff --git a/frontend/src/app/features/homescreen/blocks/new-features.component.ts b/frontend/src/app/features/homescreen/blocks/new-features.component.ts index 38f9dbe0fba7..2079a0ed843d 100644 --- a/frontend/src/app/features/homescreen/blocks/new-features.component.ts +++ b/frontend/src/app/features/homescreen/blocks/new-features.component.ts @@ -32,8 +32,6 @@ import { BcfRestApi } from 'core-app/features/bim/bcf/bcf-constants.const'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { imagePath } from 'core-app/shared/helpers/images/path-helper'; -export const homescreenNewFeaturesBlockSelector = 'homescreen-new-features-block'; - // The key used in the I18n files to distinguish between versions. const OpVersionI18n = '14_4'; @@ -59,7 +57,7 @@ const featureTeaserImage = `${OpVersionI18n}_features.png`; {{ text.learnAbout }} `, - selector: homescreenNewFeaturesBlockSelector, + selector: 'opce-homescreen-new-features-block', styleUrls: ['./new-features.component.sass'], changeDetection: ChangeDetectionStrategy.OnPush, }) diff --git a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts index 3266e8cbe9f2..417a2534b2b1 100644 --- a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts +++ b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts @@ -7,10 +7,9 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { IanBellService } from 'core-app/features/in-app-notifications/bell/state/ian-bell.service'; import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; -export const opInAppNotificationBellSelector = 'op-in-app-notification-bell'; @Component({ - selector: opInAppNotificationBellSelector, + selector: 'opce-in-app-notification-bell', templateUrl: './in-app-notification-bell.component.html', styleUrls: ['./in-app-notification-bell.component.sass'], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/frontend/src/app/features/plugins/plugin-context.ts b/frontend/src/app/features/plugins/plugin-context.ts index 181976dfa9be..01e1604ceff9 100644 --- a/frontend/src/app/features/plugins/plugin-context.ts +++ b/frontend/src/app/features/plugins/plugin-context.ts @@ -1,17 +1,20 @@ -import { ApplicationRef, Injector, NgZone } from '@angular/core'; +import { Injector, NgZone } from '@angular/core'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { ExternalQueryConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service'; +import { + ExternalQueryConfigurationService, +} from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import idFromLink from 'core-app/features/hal/helpers/id-from-link'; import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; import { EditFieldService } from 'core-app/shared/components/fields/edit/edit-field.service'; -import { DynamicBootstrapper } from 'core-app/core/setup/globals/dynamic-bootstrapper'; import { States } from 'core-app/core/states/states.service'; import { CKEditorPreviewService } from 'core-app/shared/components/editor/components/ckeditor/ckeditor-preview.service'; -import { ExternalRelationQueryConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service'; +import { + ExternalRelationQueryConfigurationService, +} from 'core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; @@ -22,7 +25,9 @@ import { HookService } from 'core-app/features/plugins/hook-service'; import { PathHelperService } from '../../core/path-helper/path-helper.service'; import { HTMLSanitizeService } from '../../core/html-sanitize/html-sanitize.service'; import { DynamicContentModalComponent } from '../../shared/components/modals/modal-wrapper/dynamic-content.modal'; -import { PasswordConfirmationModalComponent } from '../../shared/components/modals/request-for-confirmation/password-confirmation.modal'; +import { + PasswordConfirmationModalComponent, +} from '../../shared/components/modals/request-for-confirmation/password-confirmation.modal'; import { DomAutoscrollService } from 'core-app/shared/helpers/drag-and-drop/dom-autoscroll.service'; import { AttachmentsResourceService } from 'core-app/core/state/attachments/attachments.service'; import { HttpClient } from '@angular/common/http'; @@ -78,7 +83,8 @@ export class OpenProjectPluginContext { }; // Hooks - public readonly hooks:{ [hook:string]:(callback:Function) => void } = {}; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + public readonly hooks:{ [hook:string]:(callback:(...args:any[]) => unknown) => void } = {}; // Angular zone reference @InjectField() public readonly zone:NgZone; @@ -88,7 +94,7 @@ export class OpenProjectPluginContext { this ._knownHookNames .forEach((hook:string) => { - this.hooks[hook] = (callback:Function) => this.services.hooks.register(hook, callback); + this.hooks[hook] = (callback:() => void) => this.services.hooks.register(hook, callback); }); } @@ -101,15 +107,4 @@ export class OpenProjectPluginContext { public runInZone(cb:() => void) { this.zone.run(cb); } - - /** - * Bootstrap a dynamically embeddable component - * @param element - */ - public bootstrap(element:HTMLElement) { - DynamicBootstrapper.bootstrapOptionalEmbeddable( - this.injector.get(ApplicationRef), - element, - ); - } } diff --git a/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.html b/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.html index 6e3ea1fbec76..d68c71f61532 100644 --- a/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.html +++ b/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.html @@ -72,11 +72,9 @@ > - +
    • diff --git a/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.ts b/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.ts index 15e60a19aa76..228b3d6518d4 100644 --- a/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.ts @@ -35,14 +35,19 @@ import { Component, ElementRef, Injector, - Input, NgZone, + Input, + NgZone, OnInit, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; -import { WorkPackageCommentFieldHandler } from 'core-app/features/work-packages/components/work-package-comment/work-package-comment-field-handler'; -import { WorkPackagesActivityService } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; +import { + WorkPackageCommentFieldHandler, +} from 'core-app/features/work-packages/components/work-package-comment/work-package-comment-field-handler'; +import { + WorkPackagesActivityService, +} from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; import { CommentService } from 'core-app/features/work-packages/components/wp-activity/comment-service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { UserResource } from 'core-app/features/hal/resources/user-resource'; @@ -51,6 +56,7 @@ import idFromLink from 'core-app/features/hal/helpers/id-from-link'; import { DeviceService } from 'core-app/core/browser/device.service'; @Component({ + // eslint-disable-next-line @angular-eslint/component-selector selector: 'user-activity', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: './user-activity.component.html', @@ -73,7 +79,7 @@ export class UserActivityComponent extends WorkPackageCommentFieldHandler implem public userCanQuote = false; - public userId:string | number; + public userId:string|number; public user:UserResource; @@ -159,7 +165,7 @@ export class UserActivityComponent extends WorkPackageCommentFieldHandler implem return; } const activityElement = document.querySelectorAll(`[data-qa-activity-number='${this.activityNo}']`)[0] as HTMLElement; - const scrollContainer = document.querySelectorAll("[data-notification-selector='notification-scroll-container']")[0]; + const scrollContainer = document.querySelectorAll('[data-notification-selector=\'notification-scroll-container\']')[0]; const scrollOffset = activityElement.offsetTop - (scrollContainer as HTMLElement).offsetTop - this.additionalScrollMargin; scrollContainer.scrollTop = scrollOffset; }); @@ -195,11 +201,13 @@ export class UserActivityComponent extends WorkPackageCommentFieldHandler implem return null; } - public async updateComment() { + public async updateComment():Promise { this.inFlight = true; await this.onSubmit(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-return return this.commentService.updateComment(this.activity, this.rawComment || '') + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access .then((newActivity:HalResource) => { this.activity = newActivity; this.updateCommentText(); @@ -210,8 +218,10 @@ export class UserActivityComponent extends WorkPackageCommentFieldHandler implem .cache .updateWorkPackage(this.workPackage); }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access .finally(() => { - this.deactivate(true); this.inFlight = false; + this.deactivate(true); + this.inFlight = false; }); } @@ -255,6 +265,7 @@ export class UserActivityComponent extends WorkPackageCommentFieldHandler implem } private updateCommentText() { - this.postedComment = this.activity.comment.html; + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-member-access + this.postedComment = this.sanitization.bypassSecurityTrustHtml(this.activity.comment.html); } } diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts index 4f0c96f5cfd8..acfc0949a075 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts @@ -34,12 +34,11 @@ import { Screenfull } from 'screenfull'; import { AbstractWorkPackageButtonComponent } from '../wp-buttons.module'; const screenfull:Screenfull = sfimport as any; -export const zenModeComponentSelector = 'zen-mode-toggle-button'; @Component({ templateUrl: '../wp-button.template.html', changeDetection: ChangeDetectionStrategy.OnPush, - selector: zenModeComponentSelector, + selector: 'opce-zen-mode-toggle-button', }) export class ZenModeButtonComponent extends AbstractWorkPackageButtonComponent { public buttonId = 'work-packages-zen-mode-toggle-button'; diff --git a/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts b/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts index 13c1537de50a..0278f7cd92de 100644 --- a/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts @@ -27,15 +27,19 @@ //++ import { - ApplicationRef, ChangeDetectorRef, Component, ElementRef, OnInit, + ApplicationRef, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + OnInit, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -export const customDateActionAdminSelector = 'custom-date-action-admin'; - @Component({ - selector: customDateActionAdminSelector, + selector: 'opce-custom-date-action-admin', templateUrl: './custom-date-action-admin.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class CustomDateActionAdminComponent implements OnInit { public valueVisible = false; @@ -46,6 +50,7 @@ export class CustomDateActionAdminComponent implements OnInit { public visibleValue:string = ''; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ public selectedOperator:any; private onKey = 'on'; @@ -59,10 +64,12 @@ export class CustomDateActionAdminComponent implements OnInit { { key: this.currentKey, label: this.I18n.t('js.custom_actions.date.current_date') }, ]; - constructor(private elementRef:ElementRef, + constructor( + private elementRef:ElementRef, private cdRef:ChangeDetectorRef, public appRef:ApplicationRef, - private I18n:I18nService) { + private I18n:I18nService, + ) { } // cannot use $onInit as it would be called before the operators gets filled @@ -82,6 +89,7 @@ export class CustomDateActionAdminComponent implements OnInit { } public toggleValueVisibility() { + /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */ this.valueVisible = this.selectedOperator.key === this.onKey; if (this.fieldValue === this.currentFieldValue) { this.fieldValue = ''; @@ -91,6 +99,7 @@ export class CustomDateActionAdminComponent implements OnInit { } private updateDbValue() { + /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */ if (this.selectedOperator.key === this.currentKey) { this.fieldValue = this.currentFieldValue; } diff --git a/frontend/src/app/features/work-packages/components/wp-new/wp-new-full-view.html b/frontend/src/app/features/work-packages/components/wp-new/wp-new-full-view.html index 62ab62ccb4a0..235c38a6dcb0 100644 --- a/frontend/src/app/features/work-packages/components/wp-new/wp-new-full-view.html +++ b/frontend/src/app/features/work-packages/components/wp-new/wp-new-full-view.html @@ -11,7 +11,7 @@
      • - +
    - - + + - - - + + +
    {{text.tableSummary}} diff --git a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html index da5a80a1405c..68482fef358b 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html +++ b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.html @@ -49,8 +49,8 @@ >
  • - - + +