diff --git a/app/assets/javascripts/dashboard/quick_start.js b/app/assets/javascripts/dashboard/quick_start.js index 25d1b3c82..20af31ab8 100644 --- a/app/assets/javascripts/dashboard/quick_start.js +++ b/app/assets/javascripts/dashboard/quick_start.js @@ -8,19 +8,19 @@ var DasboardQuickStartWidget = (function() { var newProjectsVisibility = '#create-task-modal .new-projects-visibility'; function initNewReportLink() { - $('.quick-start-widget .new-report').click(() => { + $('.quick-start-buttons .new-report').click(() => { sessionStorage.setItem('scinote-dashboard-new-report', Math.floor(Date.now() / 1000)); }); } function initNewProtocolLink() { - $('.quick-start-widget .new-protocol').click(() => { + $('.quick-start-buttons .new-protocol').click(() => { sessionStorage.setItem('scinote-dashboard-new-protocol', Math.floor(Date.now() / 1000)); }); } function initNewTaskModal() { - $('.quick-start-widget .new-task').click(() => { + $('.quick-start-buttons .new-task').click(() => { $('#create-task-modal').modal('show'); $('#create-task-modal .select-block').attr('data-error', ''); }); @@ -118,7 +118,7 @@ var DasboardQuickStartWidget = (function() { return { init: () => { - if ($('.quick-start-widget').length) { + if ($('.quick-start-buttons').length) { initNewTaskModal(); initNewProtocolLink(); initNewReportLink(); diff --git a/app/assets/stylesheets/dashboard/calendar.scss b/app/assets/stylesheets/dashboard/calendar.scss index 1b91db708..d54d3ed4b 100644 --- a/app/assets/stylesheets/dashboard/calendar.scss +++ b/app/assets/stylesheets/dashboard/calendar.scss @@ -5,8 +5,6 @@ .dashboard-container .calendar-widget { --calendar-day-size: 32px; - grid-column: 10 / span 3; - grid-row: 1 / span 6; min-height: 320px; .dashboard-calendar { @@ -127,17 +125,8 @@ } } -@media (max-width: 1250px) { +@media (max-width: 1600px) { .dashboard-container .calendar-widget { - grid-column: 9 / span 4; - } -} - -@media (max-width: 1000px) { - .dashboard-container .calendar-widget { - grid-column: 1 / span 6; - grid-row: 5 / span 4; - .clndr { .events-container { left: 0; @@ -150,7 +139,5 @@ @media (max-width: 700px) { .dashboard-container .calendar-widget { --calendar-day-size: 28px; - grid-column: 1 / span 12; - grid-row: 2; } } diff --git a/app/assets/stylesheets/dashboard/current_tasks.scss b/app/assets/stylesheets/dashboard/current_tasks.scss index ff181df09..83ff426ba 100644 --- a/app/assets/stylesheets/dashboard/current_tasks.scss +++ b/app/assets/stylesheets/dashboard/current_tasks.scss @@ -2,8 +2,6 @@ // scss-lint:disable NestingDepth .dashboard-container .current-tasks-widget { - grid-column: 1 / span 9; - grid-row: 1 / span 6; .title { flex-shrink: 0; @@ -250,8 +248,6 @@ @media (max-width: 1250px) { .dashboard-container .current-tasks-widget { - grid-column: 1 / span 8; - .task-progress-container { max-width: 150px; } @@ -271,9 +267,6 @@ @media (max-width: 1000px) { .dashboard-container .current-tasks-widget { - grid-column: 1 / span 12; - grid-row: 1 / span 4; - .no-tasks .fas { margin-left: 500px; } @@ -283,7 +276,6 @@ @media (max-width: 700px) { .dashboard-container .current-tasks-widget { --widget-header-size: 72px; - grid-row: 1; min-height: 450px; .widget-title { diff --git a/app/assets/stylesheets/dashboard/quick_start.scss b/app/assets/stylesheets/dashboard/quick_start.scss deleted file mode 100644 index 1ab28051a..000000000 --- a/app/assets/stylesheets/dashboard/quick_start.scss +++ /dev/null @@ -1,48 +0,0 @@ -// scss-lint:disable SelectorDepth -// scss-lint:disable NestingDepth - -.dashboard-container .quick-start-widget { - grid-column: 1 / span 2; - grid-row: 7 / span 6; - - .widget-body { - .quick-start-description { - margin: 16px 16px 24px; - } - - .btn-secondary { - margin: 8px 16px; - text-align: left; - width: calc(100% - 32px); - } - } -} - -@media (max-width: 1700px) { - .dashboard-container .quick-start-widget { - grid-column: 1 / span 3; - } -} - - -@media (max-width: 1300px) { - .dashboard-container .quick-start-widget { - grid-column: 1 / span 4; - } -} - -@media (max-width: 1000px) { - .dashboard-container .quick-start-widget { - grid-column: 7 / span 6; - grid-row: 5 / span 4; - } -} - -@media (max-width: 700px) { - .dashboard-container .quick-start-widget { - --widget-header-size: 36px; - grid-column: 1 / span 12; - grid-row: 4; - min-height: 300px; - } -} diff --git a/app/assets/stylesheets/dashboard/recent_work.scss b/app/assets/stylesheets/dashboard/recent_work.scss index 00fad2df4..696ffeb98 100644 --- a/app/assets/stylesheets/dashboard/recent_work.scss +++ b/app/assets/stylesheets/dashboard/recent_work.scss @@ -2,9 +2,6 @@ // scss-lint:disable NestingDepth .dashboard-container .recent-work-widget { - grid-column: 3 / span 7; - grid-row: 7 / span 6; - .widget-title { flex-grow: 1; } @@ -58,24 +55,9 @@ } } -@media (max-width: 1700px) { - .dashboard-container .recent-work-widget { - grid-column: 4 / span 6; - } -} - - -@media (max-width: 1300px) { - .dashboard-container .recent-work-widget { - grid-column: 5 / span 8; - } -} @media (max-width: 1000px) { .dashboard-container .recent-work-widget { - grid-column: 1 / span 12; - grid-row: 9 / span 4; - .no-results { .no-results-arrow { display: none; @@ -84,14 +66,11 @@ } } -@media (max-width: 700px) { +@media (max-width: 1100px) { .dashboard-container .recent-work-widget { - --widget-header-size: 72px; - grid-row: 3; min-height: 450px; .widget-title { - flex-basis: 100%; line-height: 36px; } diff --git a/app/assets/stylesheets/dashboard/show.scss b/app/assets/stylesheets/dashboard/show.scss index 9a9598352..0ef2994e4 100644 --- a/app/assets/stylesheets/dashboard/show.scss +++ b/app/assets/stylesheets/dashboard/show.scss @@ -1,23 +1,53 @@ // scss-lint:disable SelectorDepth // scss-lint:disable NestingDepth -.dashboard-container { +.dashboard-background { + background: $color-concrete; + height: 100%; + left: 0; + position: fixed; + top: 0; + width: 100%; +} + +.dashboard-view { --dashboard-widgets-gap: 30px; + padding: calc(var(--dashboard-widgets-gap) / 2) + calc(var(--dashboard-widgets-gap) / 2) + var(--dashboard-widgets-gap); +} + +.dashboard-header { + padding-bottom: calc(var(--dashboard-widgets-gap) / 2); +} + +.dashboard-container { --widget-header-size: 44px; display: grid; + grid-auto-rows: 30em; grid-column-gap: var(--dashboard-widgets-gap); grid-row-gap: var(--dashboard-widgets-gap); - grid-template-columns: repeat(12, 1fr); - grid-template-rows: repeat(12, 1fr); - min-height: calc(100vh - 51px); - padding: var(--dashboard-widgets-gap) calc(var(--dashboard-widgets-gap) - 15px); + grid-template-columns: repeat(auto-fit, minmax(7em, 1fr)); width: 100%; .basic-widget { + background: $color-white; border-radius: $border-radius-modal; box-shadow: $flyout-shadow; position: relative; + &.large-widget { + grid-column: auto / 9 span; + } + + &.medium-widget { + grid-column: auto / 6 span; + } + + &.small-widget { + grid-column: auto / 3 span; + } + .widget-header { align-items: center; border-bottom: $border-tertiary; @@ -27,6 +57,7 @@ .widget-title { @include font-h2; + flex-shrink: 0; } } @@ -51,16 +82,50 @@ } } -@media (max-width: 1300px) { +@media (max-width: 1100px) { .dashboard-container { - --dashboard-widgets-gap: 16px; + --dashboard-widgets-gap: 15px; + grid-template-columns: 100%; + + .basic-widget { + &.large-widget, + &.medium-widget, + &.small-widget { + grid-column: auto / 1 span; + } + } + } +} + +@media (max-height: 800px) { + .dashboard-container { + grid-auto-rows: 24em; + } +} + +@media (min-height: 1300px) { + .dashboard-container { + grid-auto-rows: 36em; } } @media (max-width: 700px) { + + .dashboard-view { + --dashboard-widgets-gap: 15px; + } + .dashboard-container { --widget-header-size: 72px; - grid-template-rows: auto; + grid-auto-rows: auto; + + .basic-widget { + &.large-widget, + &.medium-widget, + &.small-widget { + grid-column: 1 span; + } + } .widget-header { flex-wrap: wrap; diff --git a/app/models/report.rb b/app/models/report.rb index 87d49b5e4..84556d095 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Report < ApplicationRecord include SearchableModel include SearchableByNameModel @@ -58,7 +60,7 @@ class Report < ApplicationRecord end def root_elements - (report_elements.order(:position)).select { |el| el.parent.blank? } + report_elements.order(:position).select { |el| el.parent.blank? } end # Save the JSON represented contents to this report @@ -66,7 +68,7 @@ class Report < ApplicationRecord def save_with_contents(json_contents) begin Report.transaction do - #First, save the report itself + # First, save the report itself save! # Secondly, delete existing report elements @@ -80,67 +82,17 @@ class Report < ApplicationRecord rescue ActiveRecord::ActiveRecordError, ArgumentError return false end - return true + true end # Clean report elements from report # the function runs before the report is edit def cleanup_report - report_elements.each do |el| - el.clean_removed_or_archived_elements - end + report_elements.each(&:clean_removed_or_archived_elements) end def self.generate_whole_project_report(project, current_user, current_team) - report_contents = gen_element_content(project, nil, 'project_header', true) - - project.experiments.each do |exp| - modules = [] - - exp.my_modules.each do |my_module| - module_children = [] - - module_children += gen_element_content(my_module, nil, 'my_module_protocol', true) - my_module.protocol.steps.each do |step| - step_children = - gen_element_content(step, step.assets, 'step_asset') - step_children += - gen_element_content(step, step.tables, 'step_table') - step_children += - gen_element_content(step, step.checklists, 'step_checklist') - step_children += - gen_element_content(step, nil, 'step_comments', true, 'asc') - - module_children += - gen_element_content(step, nil, 'step', true, nil, step_children) - end - - my_module.results.each do |result| - result_children = - gen_element_content(result, nil, 'result_comments', true, 'asc') - - result_type = if result.asset - 'result_asset' - elsif result.table - 'result_table' - elsif result.result_text - 'result_text' - end - module_children += - gen_element_content(result, nil, result_type, true, nil, - result_children) - end - - repositories = project.assigned_repositories_and_snapshots - - module_children += gen_element_content(my_module, nil, 'my_module_activity', true, 'asc') - module_children += gen_element_content(my_module, repositories, 'my_module_repository', true, 'asc') - modules += gen_element_content(my_module, nil, 'my_module', true, nil, module_children) - end - - report_contents += - gen_element_content(exp, nil, 'experiment', true, nil, modules) - end + report_contents = gen_element_content(project, Extends::EXPORT_ALL_PROJECT_ELEMENTS) report = Report.new report.name = loop do @@ -155,31 +107,29 @@ class Report < ApplicationRecord report end - def self.gen_element_content(parent_obj, association_objs, type_of, - use_parent_id = false, sort_order = nil, - children = nil) - parent_type = parent_obj.class.name.underscore - type = type_of.split('_').last.singularize - extra_id_needed = use_parent_id && !association_objs.nil? + def self.gen_element_content(parent, children) elements = [] - association_objs ||= [nil] - association_objs.each do |obj| - elements << { - 'type_of' => type_of, - 'id' => {}.tap do |ids_hash| - if use_parent_id - ids_hash["#{parent_type}_id"] = parent_obj.id - else - ids_hash["#{type}_id"] = obj.id - end - ids_hash["#{type}_id"] = obj.id if extra_id_needed - end, - 'sort_order' => sort_order.present? ? sort_order : nil, - 'children' => children.present? ? children : [] + children.each do |element| + element_hash = lambda { |object| + hash_object = { + 'type_of' => element[:type_of] || element[:type_of_lambda].call(object), + 'id' => { element[:id_key] => object.id }, + 'sort_order' => element[:sort_order], + 'children' => gen_element_content(object, element[:children] || []) + } + hash_object['id'][element[:parent_id_key]] = parent.id if element[:parent_id_key] + hash_object } - end + if element[:relation] + (element[:relation].inject(parent) { |p, method| p.public_send(method) }).each do |child| + elements.push(element_hash.call(child)) + end + else + elements.push(element_hash.call(parent)) + end + end elements end diff --git a/app/views/dashboards/_calendar.html.erb b/app/views/dashboards/_calendar.html.erb index fa6195543..0400f6869 100644 --- a/app/views/dashboards/_calendar.html.erb +++ b/app/views/dashboards/_calendar.html.erb @@ -1,4 +1,4 @@ -
+
+
<%= t("dashboard.current_tasks.title") %>
diff --git a/app/views/dashboards/_quick_start.html.erb b/app/views/dashboards/_quick_start.html.erb index 6e321adb1..d40b44e79 100644 --- a/app/views/dashboards/_quick_start.html.erb +++ b/app/views/dashboards/_quick_start.html.erb @@ -1,27 +1,12 @@ -
-
-
- <%= t("dashboard.quick_start.title") %> -
-
-
- <% if current_user.is_guest_of_team?(current_team) %> -
-

<%= t("dashboard.quick_start.placeholder.title") %>

-

<%= t("dashboard.quick_start.placeholder.description") %>

-
- <% else %> -
- <%= t("dashboard.quick_start.description") %> -
-
<%= t("dashboard.quick_start.new_task") %>
- <%= link_to protocols_path, {class: "new-protocol btn btn-secondary btn-block"} do %> - <%= t("dashboard.quick_start.new_protocol") %> - <% end %> - <%= link_to reports_path, {class: "new-report btn btn-secondary btn-block"} do %> - <%= t("dashboard.quick_start.new_report") %> - <% end %> +<% unless current_user.is_guest_of_team?(current_team) %> +
+
<%= t("dashboard.quick_start.new_task") %>
+ <%= link_to protocols_path, {class: "new-protocol btn btn-secondary"} do %> + <%= t("dashboard.quick_start.new_protocol") %> + <% end %> + <%= link_to reports_path, {class: "new-report btn btn-secondary"} do %> + <%= t("dashboard.quick_start.new_report") %> <% end %>
-
-<%= render "create_task_modal" %> \ No newline at end of file + <%= render "create_task_modal" %> +<% end %> diff --git a/app/views/dashboards/_recent_work.html.erb b/app/views/dashboards/_recent_work.html.erb index e6baa06bb..2788e1095 100644 --- a/app/views/dashboards/_recent_work.html.erb +++ b/app/views/dashboards/_recent_work.html.erb @@ -1,4 +1,4 @@ -
+
<%= t('dashboard.recent_work.title') %> @@ -37,4 +37,4 @@
<% end %>
- \ No newline at end of file + diff --git a/app/views/dashboards/show.html.erb b/app/views/dashboards/show.html.erb index 2c4a62741..b46a22b58 100644 --- a/app/views/dashboards/show.html.erb +++ b/app/views/dashboards/show.html.erb @@ -1,10 +1,17 @@ <% provide :head_title, t('nav.label.dashboard') %> <% if current_team %> -
- <%= render "calendar" %> - <%= render "current_tasks" %> - <%= render "recent_work" %> - <%= render "quick_start" %> +
+
+
+ <%= render partial: 'quick_start' %> +
+
+ <% Extends::DEFAULT_DASHBOARD_CONFIGURATION.each do |widget| %> + <% if widget[:visible] %> + <%= render partial: widget[:partial], locals: {widget: widget} %> + <% end %> + <% end %> +
<% end %> diff --git a/config/initializers/extends.rb b/config/initializers/extends.rb index 5959a0edf..df6e5d1eb 100644 --- a/config/initializers/extends.rb +++ b/config/initializers/extends.rb @@ -41,6 +41,84 @@ class Extends my_module_repository: 17, my_module_protocol: 18 } + EXPORT_ALL_PROJECT_ELEMENTS = [ + { + type_of: 'project_header', + id_key: 'project_id' + }, + { + type_of: 'experiment', + id_key: 'experiment_id', + relation: %w(experiments), + children: [ + { + type_of: 'my_module', + id_key: 'my_module_id', + relation: %w(my_modules), + children: [ + { + type_of: 'my_module_protocol', + id_key: 'my_module_id' + }, + { + type_of: 'step', + relation: %w(protocol steps), + id_key: 'step_id', + children: [ + { + type_of: 'step_asset', + relation: %w(assets), + id_key: 'asset_id' + }, + { + type_of: 'step_table', + relation: %w(tables), + id_key: 'table_id' + }, + { + type_of: 'step_checklist', + relation: %w(checklists), + id_key: 'checklist_id' + }, + { + type_of: 'step_comments', + id_key: 'step_id', + sort_order: 'asc' + } + ] + }, + { + type_of_lambda: lambda { |result| + (result.result_asset || + result.result_table || + result.result_text).class.to_s.underscore + }, + relation: %w(results), + id_key: 'result_id', + children: [{ + type_of: 'result_comments', + id_key: 'result_id', + sort_order: 'asc' + }] + }, + { + type_of: 'my_module_activity', + id_key: 'my_module_id', + sort_order: 'asc' + }, + { + type_of: 'my_module_repository', + relation: %w(experiment project assigned_repositories_and_snapshots), + id_key: 'repository_id', + parent_id_key: 'my_module_id', + sort_order: 'asc' + } + ] + } + ] + } + ] + # Data type name should match corresponding model's name REPOSITORY_DATA_TYPES = { RepositoryTextValue: 0, RepositoryDateValue: 1, @@ -123,6 +201,12 @@ class Extends 'Protocol' => :description, 'MyModule' => :description } + DEFAULT_DASHBOARD_CONFIGURATION = [ + { partial: 'dashboards/current_tasks', visible: true, size: 'large-widget', position: 1 }, + { partial: 'dashboards/calendar', visible: true, size: 'small-widget', position: 2 }, + { partial: 'dashboards/recent_work', visible: true, size: 'medium-widget', position: 3 } + ] + ACTIVITY_SUBJECT_TYPES = %w( Team RepositoryBase Project Experiment MyModule Result Protocol Report RepositoryRow ).freeze @@ -292,7 +376,7 @@ class Extends restore_inventory: 145, export_inventory_items_assigned_to_task: 146, export_inventory_snapshot_items_assigned_to_task: 147, - change_status_on_task_flow: 148 # 149, 150, 151 in AdddOn! + change_status_on_task_flow: 148 # 149..157 in AdddOn! } ACTIVITY_GROUPS = {