Implement basic logic for Date/DateTime/Stock reminders [SCI-6554] (#3911)

* Implement basic logic for Date/DateTime/Stock reminders [SCI-6554]

* Implement bell icon [SCI-6500]

* Refactor reminder cells scope [SCI-6554]
This commit is contained in:
artoscinote 2022-03-09 14:13:48 +01:00 committed by GitHub
parent 20ef08907a
commit d577fcc824
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 91 additions and 18 deletions

View file

@ -107,8 +107,8 @@ var MyModuleRepositories = (function() {
searchable: false,
className: 'assigned-column',
sWidth: '1%',
render: function(data) {
return $.fn.dataTable.render.AssignedTasksValue(data);
render: function(data, type, row) {
return $.fn.dataTable.render.AssignedTasksValue(data, row);
}
}, {
targets: 3,

View file

@ -157,14 +157,16 @@ $.fn.dataTable.render.RepositoryNumberValue = function(data) {
</span>`;
};
$.fn.dataTable.render.AssignedTasksValue = function(data) {
$.fn.dataTable.render.AssignedTasksValue = function(data, row) {
let tasksLinkHTML;
if (data.tasks > 0) {
let tooltip = I18n.t('repositories.table.assigned_tooltip', {
tasks: data.tasks,
experiments: data.experiments,
projects: data.projects
});
return `<div class="assign-counter-container dropdown" title="${tooltip}"
tasksLinkHTML = `<div class="assign-counter-container dropdown" title="${tooltip}"
data-task-list-url="${data.task_list_url}">
<a href="#" class="assign-counter has-assigned"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">${data.tasks}</a>
@ -177,8 +179,16 @@ $.fn.dataTable.render.AssignedTasksValue = function(data) {
<div class="tasks"></div>
</div>
</div>`;
} else {
tasksLinkHTML = "<div class='assign-counter-container'><span class='assign-counter'>0</span></div>";
}
return "<div class='assign-counter-container'><span class='assign-counter'>0</span></div>";
if (row.hasActiveReminders) {
return `<i class="fas fa-bell row-reminders-icon" data-row-reminders-url="${row.rowRemindersUrl}"></i>`
+ tasksLinkHTML;
}
return tasksLinkHTML;
};
$.fn.dataTable.render.RepositoryStockValue = function(data) {

View file

@ -431,7 +431,7 @@ var RepositoryDatatable = (function(global) {
className: 'assigned-column',
sWidth: '1%',
render: function(data, type, row) {
let content = $.fn.dataTable.render.AssignedTasksValue(data);
let content = $.fn.dataTable.render.AssignedTasksValue(data, row);
let icon;
if (!row.recordEditable) {
icon = `<i class="repository-row-lock-icon fas fa-lock" title="${I18n.t('repositories.table.locked_item')}"></i>`;

View file

@ -205,14 +205,20 @@
.assigned-column {
position: relative;
.row-reminders-icon {
cursor: pointer;
display: inline-block;
line-height: 35px;
}
.assign-counter-container {
border-radius: $border-radius-tag;
cursor: pointer;
display: inline-block;
line-height: 35px;
padding: 0 5px;
position: absolute;
text-align: center;
top: 1px;
width: calc(100% - 16px);
right: 5px;
.assign-counter {
display: inline-block;

View file

@ -34,7 +34,13 @@
.repository-row-edit-icon {
cursor: pointer;
display: none;
display: inline-block;
opacity: 0;
}
.row-reminders-icon {
cursor: pointer;
display: inline-block;
}
.assign-counter-container {
@ -42,8 +48,7 @@
display: inline-block;
line-height: 35px;
position: absolute;
right: 0;
width: calc(100% - 40px);
right: 5px;
.assign-counter {
display: inline-block;
@ -251,7 +256,7 @@
.assigned-column {
.repository-row-edit-icon {
display: inline-block;
opacity: 1;
}
}
}
@ -273,7 +278,7 @@
tr:hover {
.assigned-column {
.repository-row-edit-icon {
display: none;
opacity: 0;
}
.assign-counter-container {

View file

@ -34,7 +34,7 @@ module RepositoryColumns
private
def repository_column_params
params.require(:repository_column).permit(:name)
params.require(:repository_column).permit(:name, :reminder_delta)
end
def column_type_param

View file

@ -9,7 +9,7 @@ class RepositoryRowsController < ApplicationController
before_action :load_repository, except: %i(show print_modal print)
before_action :load_repository_or_snapshot, only: %i(print_modal print)
before_action :load_repository_row, only: %i(update assigned_task_list)
before_action :check_read_permissions, except: %i(show create update delete_records copy_records)
before_action :check_read_permissions, except: %i(show create update delete_records copy_records reminder_repository_cells)
before_action :check_snapshotting_status, only: %i(create update delete_records copy_records)
before_action :check_create_permissions, only: :create
before_action :check_delete_permissions, only: %i(delete_records archive_records restore_records)
@ -224,6 +224,10 @@ class RepositoryRowsController < ApplicationController
}
end
def reminder_repository_cells
render json: @repository_row.repository_cells.with_active_reminder
end
def archive_records
service = RepositoryActions::ArchiveRowsService.call(repository: @repository,
repository_rows: selected_rows_in_repo_params,

View file

@ -4,6 +4,8 @@ module RepositoryDatatableHelper
include InputSanitizeHelper
def prepare_row_columns(repository_rows, repository, columns_mappings, team, options = {})
repository_row_with_active_reminder_ids = repository_rows.with_active_reminders.pluck(:id)
repository_rows.map do |record|
default_cells = {
'1': assigned_row(record),
@ -18,7 +20,14 @@ module RepositoryDatatableHelper
row = {
'DT_RowId': record.id,
'DT_RowAttr': { 'data-state': row_style(record) },
'recordInfoUrl': Rails.application.routes.url_helpers.repository_repository_row_path(repository, record)
'recordInfoUrl': Rails.application.routes.url_helpers.repository_repository_row_path(repository, record),
'hasActiveReminders': repository_row_with_active_reminder_ids.include?(record.id),
'rowRemindersUrl':
Rails.application.routes.url_helpers
.active_reminder_repository_cells_repository_repository_row_url(
repository,
record
)
}.merge(default_cells)
if record.repository.has_stock_management?

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
module ReminderRepositoryCellJoinable
extend ActiveSupport::Concern
included do
def self.reminder_repository_cells_scope(relation)
relation.joins( # datetime reminders
'LEFT OUTER JOIN "repository_date_time_values" ON '\
'"repository_date_time_values"."id" = "repository_cells"."value_id" AND '\
'"repository_cells"."value_type" = \'RepositoryDateTimeValueBase\' '\
'AND repository_columns.metadata ? \'reminder_delta\' AND '\
'(repository_date_time_values.data - NOW()) <= '\
'(repository_columns.metadata -> \'reminder_delta\')::int * interval \'1 sec\''
).joins( # stock reminders
'LEFT OUTER JOIN "repository_stock_values" ON "repository_stock_values"."id" = "repository_cells"."value_id" AND '\
'"repository_cells"."value_type" = \'RepositoryStockValue\' AND '\
'repository_stock_values.amount <= repository_stock_values.low_stock_threshold'
).where(
'repository_date_time_values.id IS NOT NULL OR repository_stock_values.id IS NOT NULL'
)
end
end
end

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true
class RepositoryCell < ApplicationRecord
include ReminderRepositoryCellJoinable
attr_accessor :importing
belongs_to :repository_row
@ -41,6 +43,10 @@ class RepositoryCell < ApplicationRecord
uniqueness: { scope: :repository_column },
unless: :importing
scope :with_active_reminder, lambda {
reminder_repository_cells_scope(joins(:repository_column))
}
def self.create_with_value!(row, column, data, user)
cell = new(repository_row: row, repository_column: column)
cell.transaction do

View file

@ -25,6 +25,8 @@ class RepositoryColumn < ApplicationRecord
enum data_type: Extends::REPOSITORY_DATA_TYPES
store_accessor :metadata, %i(reminder_delta)
validates :data_type, uniqueness: { if: :repository_stock_value?, scope: :repository_id }
validates :data_type, uniqueness: { if: :repository_stock_consumption_value?, scope: :repository_id }

View file

@ -4,6 +4,7 @@ class RepositoryRow < ApplicationRecord
include SearchableModel
include SearchableByNameModel
include ArchivableModel
include ReminderRepositoryCellJoinable
ID_PREFIX = 'IT'
include PrefixedIdModel
@ -34,7 +35,8 @@ class RepositoryRow < ApplicationRecord
repository_date: 'RepositoryDateValue',
repository_date_time_range: 'RepositoryDateTimeRangeValue',
repository_time_range: 'RepositoryTimeRangeValue',
repository_date_range: 'RepositoryDateRangeValue'
repository_date_range: 'RepositoryDateRangeValue',
repository_stock: 'RepositoryStockValue'
}.each do |relation, class_name|
has_many "#{relation}_cells".to_sym, -> { where(value_type: class_name) }, class_name: 'RepositoryCell',
inverse_of: :repository_row
@ -84,6 +86,10 @@ class RepositoryRow < ApplicationRecord
scope :active, -> { where(archived: false) }
scope :archived, -> { where(archived: true) }
scope :with_active_reminders, lambda {
reminder_repository_cells_scope(joins(repository_cells: :repository_column)).distinct
}
def code
"#{ID_PREFIX}#{parent_id || id}"
end

View file

@ -596,6 +596,7 @@ Rails.application.routes.draw do
end
member do
get :assigned_task_list
get :active_reminder_repository_cells
end
member do
get 'repository_stock_value/new', to: 'repository_stock_values#new', as: 'new_repository_stock'