mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-09-20 14:45:56 +08:00
Merge branch 'develop' into features/new-datatable
This commit is contained in:
commit
de72f60cc1
|
@ -25,13 +25,13 @@
|
|||
"max-len": [
|
||||
"error",
|
||||
{
|
||||
"code": 120
|
||||
"code": 180
|
||||
}
|
||||
],
|
||||
"vue/max-len": [
|
||||
"error",
|
||||
{
|
||||
"code": 120,
|
||||
"code": 180,
|
||||
"template": 240,
|
||||
"tabWidth": 2
|
||||
}
|
||||
|
|
|
@ -442,7 +442,7 @@ GEM
|
|||
net-smtp (0.3.3)
|
||||
net-protocol
|
||||
newrelic_rpm (9.2.2)
|
||||
nio4r (2.5.9)
|
||||
nio4r (2.7.0)
|
||||
nokogiri (1.14.5)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
|
@ -505,7 +505,7 @@ GEM
|
|||
pry (>= 0.10.4)
|
||||
psych (3.3.4)
|
||||
public_suffix (5.0.1)
|
||||
puma (6.3.1)
|
||||
puma (6.4.2)
|
||||
nio4r (~> 2.0)
|
||||
raabro (1.4.0)
|
||||
racc (1.7.1)
|
||||
|
|
4
app/assets/images/icon_small/info.svg
Normal file
4
app/assets/images/icon_small/info.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 16.5H13V11H12V16.5ZM12.5034 21C11.2588 21 10.0887 20.7638 8.9931 20.2915C7.89748 19.8192 6.94444 19.1782 6.13397 18.3685C5.32352 17.5588 4.68192 16.6066 4.20915 15.512C3.73638 14.4174 3.5 13.2479 3.5 12.0033C3.5 10.7588 3.73616 9.58872 4.20848 8.4931C4.68081 7.39748 5.32183 6.44444 6.13153 5.63398C6.94123 4.82352 7.89337 4.18192 8.98795 3.70915C10.0826 3.23638 11.2521 3 12.4966 3C13.7412 3 14.9113 3.23616 16.0069 3.70848C17.1025 4.18081 18.0556 4.82183 18.866 5.63153C19.6765 6.44123 20.3181 7.39337 20.7908 8.48795C21.2636 9.58255 21.5 10.7521 21.5 11.9967C21.5 13.2412 21.2638 14.4113 20.7915 15.5069C20.3192 16.6025 19.6782 17.5556 18.8685 18.366C18.0588 19.1765 17.1066 19.8181 16.012 20.2908C14.9174 20.7636 13.7479 21 12.5034 21ZM12.5 20C14.7333 20 16.625 19.225 18.175 17.675C19.725 16.125 20.5 14.2333 20.5 12C20.5 9.76667 19.725 7.875 18.175 6.325C16.625 4.775 14.7333 4 12.5 4C10.2667 4 8.375 4.775 6.825 6.325C5.275 7.875 4.5 9.76667 4.5 12C4.5 14.2333 5.275 16.125 6.825 17.675C8.375 19.225 10.2667 20 12.5 20Z" fill="#E9A845"/>
|
||||
<path d="M12.9385 9.05385C12.8205 9.1718 12.6744 9.23077 12.5 9.23077C12.3257 9.23077 12.1795 9.1718 12.0615 9.05385C11.9436 8.9359 11.8846 8.78974 11.8846 8.61537C11.8846 8.44102 11.9436 8.29487 12.0615 8.17692C12.1795 8.05897 12.3257 8 12.5 8C12.6744 8 12.8205 8.05897 12.9385 8.17692C13.0564 8.29487 13.1154 8.44102 13.1154 8.61537C13.1154 8.78974 13.0564 8.9359 12.9385 9.05385Z" fill="#E9A845"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
106
app/assets/images/icon_small/unlink.svg
Normal file
106
app/assets/images/icon_small/unlink.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 22 KiB |
|
@ -127,6 +127,10 @@ var MyModuleRepositories = (function() {
|
|||
render: function(data, type, row) {
|
||||
return "<a href='" + row.recordInfoUrl + "' class='record-info-link'>" + data + '</a>';
|
||||
}
|
||||
}, {
|
||||
targets: 4,
|
||||
class: 'relationship',
|
||||
render: (data, type, row) => $.fn.dataTable.render.RelationshipValue(data, row)
|
||||
});
|
||||
} else {
|
||||
columnDefs.push({
|
||||
|
|
|
@ -267,3 +267,12 @@ $.fn.dataTable.render.RepositoryStockConsumptionValue = function(data = {}) {
|
|||
$.fn.dataTable.render.defaultRepositoryStockConsumptionValue = function() {
|
||||
return $.fn.dataTable.render.RepositoryStockConsumptionValue();
|
||||
};
|
||||
|
||||
$.fn.dataTable.render.RelationshipValue = function(data, row) {
|
||||
return `<a
|
||||
style="text-decoration: none !important;"
|
||||
class="relationships-info-link !text-sn-blue !no-underline pl-4"
|
||||
href=${row.recordInfoUrl}>
|
||||
${data}
|
||||
</a>`;
|
||||
};
|
||||
|
|
|
@ -674,8 +674,16 @@ var RepositoryDatatable = (function(global) {
|
|||
+ "class='record-info-link' data-e2e='e2e-TL-invInventoryTR-Item-" + row.DT_RowId + "'>" + data + '</a>';
|
||||
}
|
||||
}, {
|
||||
// Added on column
|
||||
targets: 4,
|
||||
class: 'relationship',
|
||||
searchable: false,
|
||||
orderable: true,
|
||||
render: function(data, type, row) {
|
||||
return $.fn.dataTable.render.RelationshipValue(data, row);
|
||||
}
|
||||
}, {
|
||||
// Added on column
|
||||
targets: 5,
|
||||
class: 'added-on',
|
||||
visible: true
|
||||
}, {
|
||||
|
@ -747,7 +755,6 @@ var RepositoryDatatable = (function(global) {
|
|||
|
||||
// Hide edit button if not all selected rows are on the current page
|
||||
$('#editRepositoryRecord').prop('disabled', !allSelectedRowsAreOnPage());
|
||||
|
||||
TABLE.columns([archivedOnIndex, archivedByIndex]).visible(archived);
|
||||
},
|
||||
preDrawCallback: function() {
|
||||
|
@ -1137,6 +1144,7 @@ var RepositoryDatatable = (function(global) {
|
|||
clearRowSelection();
|
||||
},
|
||||
selectedRows: () => { return rowsSelected; },
|
||||
repositoryId: () => $(TABLE_ID).data('repository-id'),
|
||||
redrawTableOnSidebarToggle: redrawTableOnSidebarToggle,
|
||||
checkAvailableColumns: checkAvailableColumns
|
||||
});
|
||||
|
|
|
@ -283,7 +283,7 @@ var RepositoryColumns = (function() {
|
|||
var scrollPosition = $columnsList.scrollTop();
|
||||
// Clear the list
|
||||
$columnsList.find('li[data-position]').remove();
|
||||
_.each(TABLE.columns().header(), function(el, index) {
|
||||
_.each(TABLE.columns().header(), (el) => {
|
||||
if (!el.dataset.unmanageable) {
|
||||
let colId = $(el).attr('id');
|
||||
let colIndex = $(el).attr('data-column-index');
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
if ($(table).parent().hasClass('table-wrapper')) return;
|
||||
|
||||
$(table).wrap(`
|
||||
<div class="table-wrapper" style="overflow: auto; width: ${$($(rtf)[0]).parent().width()}px"></div>
|
||||
<div class="table-wrapper w-full" style="overflow: auto;"></div>
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,11 +37,9 @@
|
|||
function initializeHandsonTable(el) {
|
||||
var input = el.siblings('input.hot-table-contents');
|
||||
var inputObj = JSON.parse(input.attr('value'));
|
||||
var metadataJson = el.siblings('input.hot-table-metadata');
|
||||
var data = inputObj.data;
|
||||
var metadata;
|
||||
const metadata = JSON.parse(el.siblings('input.hot-table-metadata').val() || '{}');
|
||||
|
||||
metadata = JSON.parse(metadataJson.val() || '{}');
|
||||
el.handsontable({
|
||||
disableVisualSelection: true,
|
||||
rowHeaders: tableColRowName.tableRowHeaders(metadata.plateTemplate),
|
||||
|
|
|
@ -6,11 +6,9 @@
|
|||
function initializeHandsonTable(el) {
|
||||
var input = el.siblings('input.hot-table-contents');
|
||||
var inputObj = JSON.parse(input.attr('value'));
|
||||
var metadataJson = el.siblings('input.hot-table-metadata');
|
||||
var data = inputObj.data;
|
||||
var metadata;
|
||||
const metadata = JSON.parse(el.siblings('input.hot-table-metadata').val() || '{}');
|
||||
|
||||
metadata = JSON.parse(metadataJson.val() || '{}');
|
||||
el.handsontable({
|
||||
disableVisualSelection: true,
|
||||
rowHeaders: true,
|
||||
|
|
|
@ -148,9 +148,6 @@ let inlineEditing = (function() {
|
|||
$(editBlocks).click();
|
||||
}
|
||||
})
|
||||
.on('blur', `${editBlocks} textarea, ${editBlocks} input`, function(e) {
|
||||
saveAllEditFields();
|
||||
})
|
||||
.on('click', editBlocks, function(e) {
|
||||
// 'A' mean that, if we click on <a></a> element we will not go in edit mode
|
||||
var container = $(this);
|
||||
|
|
|
@ -3,14 +3,24 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
$(document).on('click', '.record-info-link', function(e) {
|
||||
$(document).on('click', '.relationships-info-link', (e) => {
|
||||
const myModuleId = $('.my-module-content').data('task-id');
|
||||
const repositoryRowURL = $(e.target).attr('href');
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
window.repositoryItemSidebarComponent.toggleShowHideSidebar(repositoryRowURL, myModuleId, 'relationships-section');
|
||||
});
|
||||
|
||||
$(document).on('click', '.record-info-link', function (e) {
|
||||
const myModuleId = $('.my-module-content').data('task-id');
|
||||
const repositoryRowURL = $(this).attr('href');
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
window.repositoryItemSidebarComponent.toggleShowHideSidebar(repositoryRowURL, myModuleId);
|
||||
window.repositoryItemSidebarComponent.toggleShowHideSidebar(repositoryRowURL, myModuleId, null);
|
||||
});
|
||||
|
||||
$(document).on('click', '.print-label-button', function(e) {
|
||||
|
@ -25,8 +35,10 @@
|
|||
PrintModalComponent.openModal();
|
||||
if (selectedRows && selectedRows.length) {
|
||||
$('#modal-info-repository-row').modal('hide');
|
||||
PrintModalComponent.repository_id = $(this).data('repositoryId');
|
||||
PrintModalComponent.row_ids = selectedRows;
|
||||
} else {
|
||||
PrintModalComponent.repository_id = RepositoryDatatable.repositoryId();
|
||||
PrintModalComponent.row_ids = [...RepositoryDatatable.selectedRows()];
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +95,7 @@
|
|||
updateCallback = (data) => {
|
||||
if (!data?.value) return;
|
||||
// reload dataTable
|
||||
if ($('.dataTable')[0]) $('.dataTable').DataTable().ajax.reload(null, false);
|
||||
if ($('.dataTable.repository-dataTable')[0]) $('.dataTable.repository-dataTable').DataTable().ajax.reload(null, false);
|
||||
// update item card stock column
|
||||
window.manageStockCallback && window.manageStockCallback(data.value);
|
||||
$link.data('manageStockUrl', data.value.stock_url)
|
||||
|
|
|
@ -178,7 +178,8 @@ var zebraPrint = (function() {
|
|||
printer_name: string,
|
||||
number_of_copies: int,
|
||||
label_template_id: int,
|
||||
repository_row_ids: array[]
|
||||
repository_row_ids: array[],
|
||||
repository_id: int
|
||||
}
|
||||
*/
|
||||
print: function(modalUrl, progressModal, printModal, printData) {
|
||||
|
|
|
@ -513,3 +513,22 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#repositoryItemRelationshipsModal {
|
||||
.item-options {
|
||||
max-height: 23rem;
|
||||
|
||||
.option-label {
|
||||
-webkit-line-clamp: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.inventory-options {
|
||||
left: 0 !important;
|
||||
max-height: 28rem;
|
||||
|
||||
.option-label {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,11 +107,11 @@
|
|||
.dp__input_icon {
|
||||
display: flex;
|
||||
height: 1.5rem;
|
||||
left: .5rem;
|
||||
left: .75rem;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 1rem;
|
||||
}
|
||||
.dp__clear_icon {
|
||||
width: 1.375rem;
|
||||
}
|
||||
|
||||
.dp__input {
|
||||
|
@ -178,6 +178,13 @@
|
|||
--dp-range-between-border-color: var(--dp-hover-color, var(--sn-super-light-grey));
|
||||
}
|
||||
|
||||
.dp__input_focus {
|
||||
&:hover {
|
||||
border-color: var(--sn-science-blue);
|
||||
}
|
||||
border-color: var(--sn-science-blue);
|
||||
}
|
||||
|
||||
:root {
|
||||
/*General*/
|
||||
--dp-font-family: inherit; /*Font family*/
|
||||
|
@ -204,7 +211,7 @@
|
|||
--dp-overlay-col-padding: .25rem; /*Padding in the overlay column*/
|
||||
--dp-time-inc-dec-button-size: 1.5rem; /*Sizing for arrow buttons in the time picker*/
|
||||
--dp-menu-padding: 1rem; /*Menu padding*/
|
||||
--dp-input-icon-padding: 2rem; /*Padding on the left side of the input if icon is present*/
|
||||
--dp-input-icon-padding: 2.25rem; /*Padding on the left side of the input if icon is present*/
|
||||
|
||||
/*Font sizes*/
|
||||
--dp-font-size: .875rem; /*Default font-size*/
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
}
|
||||
|
||||
&.disabled {
|
||||
background: var(--sn-sleepy-grey);
|
||||
background: var(--sn-light-grey);
|
||||
pointer-events: none;
|
||||
|
||||
.caret {
|
||||
|
|
|
@ -8,3 +8,7 @@ $modal-shadow: 0px 4px 16px rgba(35, 31, 32, 0.15);
|
|||
.sn-shadow-menu-sm {
|
||||
box-shadow: 0px 16px 32px 0px rgba(16, 24, 40, 0.07);
|
||||
}
|
||||
|
||||
.sn-shadow-menu-lg {
|
||||
box-shadow: 0px 32px 64px -12px rgba(16, 24, 40, 0.14);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,3 @@
|
|||
/* Hide arrows on number type input field */
|
||||
@layer utilities {
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.sci-btn-group {
|
||||
@apply inline-flex items-center gap-2 relative;
|
||||
|
|
|
@ -11,6 +11,15 @@ module Api
|
|||
|
||||
class FilterParamError < StandardError; end
|
||||
|
||||
class MutuallyExclusiveParamsError < StandardError
|
||||
attr_reader :first_param, :second_param
|
||||
|
||||
def initialize(first_param, second_param)
|
||||
@first_param = first_param
|
||||
@second_param = second_param
|
||||
end
|
||||
end
|
||||
|
||||
class PermissionError < StandardError
|
||||
attr_reader :klass, :mode
|
||||
|
||||
|
@ -28,6 +37,14 @@ module Api
|
|||
:bad_request)
|
||||
end
|
||||
|
||||
rescue_from MutuallyExclusiveParamsError do |e|
|
||||
render_error(I18n.t('api.core.errors.mutually_exclusive_params_error.title'),
|
||||
I18n.t('api.core.errors.mutually_exclusive_params_error.detail',
|
||||
first_param: e.first_param,
|
||||
second_param: e.second_param),
|
||||
:bad_request)
|
||||
end
|
||||
|
||||
rescue_from FilterParamError do |e|
|
||||
logger.error e.message
|
||||
logger.error e.backtrace.join("\n")
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V2
|
||||
class InventoryItemRelationshipsController < BaseController
|
||||
before_action :load_team, :load_inventory, :load_inventory_item
|
||||
before_action :check_manage_permission, only: %w(create destroy)
|
||||
before_action :load_create_params, only: :create
|
||||
|
||||
def create
|
||||
parent = @relation == :parent ? @inventory_item : @inventory_item_to_link
|
||||
child = @relation == :child ? @inventory_item : @inventory_item_to_link
|
||||
|
||||
@connection = RepositoryRowConnection.create!(
|
||||
parent_id: parent,
|
||||
child_id: child,
|
||||
created_by: current_user,
|
||||
last_modified_by: current_user
|
||||
)
|
||||
|
||||
render jsonapi: @connection, serializer: InventoryItemRelationshipSerializer, status: :created
|
||||
end
|
||||
|
||||
def destroy
|
||||
@connection = @inventory_item.parent_connections
|
||||
.or(@inventory_item.child_connections)
|
||||
.find(params.require(:id))
|
||||
@connection.destroy!
|
||||
render body: nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_manage_permission
|
||||
raise PermissionError.new(Repository, :manage) unless can_manage_repository?(@inventory)
|
||||
end
|
||||
|
||||
def load_create_params
|
||||
if connection_params[:parent_id].present? && connection_params[:child_id].present?
|
||||
raise MutuallyExclusiveParamsError.new(:parent_id, :child_id)
|
||||
end
|
||||
|
||||
if connection_params[:parent_id].present?
|
||||
@relation = :parent
|
||||
@inventory_item_to_link = RepositoryRow.find(connection_params[:parent_id])
|
||||
elsif connection_params[:child_id].present?
|
||||
@relation = :child
|
||||
@inventory_item_to_link = RepositoryRow.find(connection_params[:child_id])
|
||||
end
|
||||
|
||||
raise ActiveRecord::RecordNotFound unless @inventory_item_to_link
|
||||
end
|
||||
|
||||
def connection_params
|
||||
raise TypeError unless params.require(:data).require(:type) == 'inventory_item_relationships'
|
||||
|
||||
params.require(:data).require(:attributes).permit(%i(parent_id child_id))
|
||||
end
|
||||
|
||||
def permitted_includes
|
||||
%w(parent child)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,14 +7,14 @@ module Api
|
|||
steps = timestamps_filter(@protocol.steps).page(params.dig(:page, :number))
|
||||
.per(params.dig(:page, :size))
|
||||
|
||||
render jsonapi: steps, each_serializer: StepSerializer,
|
||||
render jsonapi: steps, each_serializer: Api::V2::StepSerializer,
|
||||
include: include_params,
|
||||
rte_rendering: render_rte?,
|
||||
team: @team
|
||||
end
|
||||
|
||||
def show
|
||||
render jsonapi: @step, serializer: StepSerializer,
|
||||
render jsonapi: @step, serializer: Api::V2::StepSerializer,
|
||||
include: include_params,
|
||||
rte_rendering: render_rte?,
|
||||
team: @team
|
||||
|
@ -31,7 +31,7 @@ module Api
|
|||
last_modified_by_id: current_user.id)
|
||||
)
|
||||
end
|
||||
render jsonapi: @step, serializer: StepSerializer, status: :created
|
||||
render jsonapi: @step, serializer: Api::V2::StepSerializer, status: :created
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -48,7 +48,7 @@ module Api
|
|||
num_completed: completed_steps.to_s,
|
||||
num_all: all_steps.to_s)
|
||||
end
|
||||
render jsonapi: @step, serializer: StepSerializer, status: :ok
|
||||
render jsonapi: @step, serializer: Api::V2::StepSerializer, status: :ok
|
||||
else
|
||||
render body: nil, status: :no_content
|
||||
end
|
||||
|
|
190
app/controllers/repository_row_connections_controller.rb
Normal file
190
app/controllers/repository_row_connections_controller.rb
Normal file
|
@ -0,0 +1,190 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RepositoryRowConnectionsController < ApplicationController
|
||||
before_action :load_repository, except: %i(repositories)
|
||||
before_action :load_create_vars, only: :create
|
||||
before_action :check_read_permissions, except: :repositories
|
||||
before_action :load_repository_row, except: %i(repositories repository_rows)
|
||||
before_action :check_manage_permissions, only: %i(create destroy)
|
||||
|
||||
def index
|
||||
parents = @repository_row.parent_connections
|
||||
.joins('INNER JOIN repository_rows ON
|
||||
repository_rows.id = repository_row_connections.parent_id')
|
||||
.select(:id, 'repository_rows.id AS repository_row_id',
|
||||
'repository_rows.name AS repository_row_name')
|
||||
.map do |row|
|
||||
{
|
||||
id: row.id,
|
||||
name: row.repository_row_name,
|
||||
code: "#{RepositoryRow::ID_PREFIX}#{row.repository_row_id}"
|
||||
}
|
||||
end
|
||||
children = @repository_row.child_connections
|
||||
.joins('INNER JOIN repository_rows ON
|
||||
repository_rows.id = repository_row_connections.child_id')
|
||||
.select(:id, 'repository_rows.id AS repository_row_id',
|
||||
'repository_rows.name AS repository_row_name')
|
||||
.map do |row|
|
||||
{
|
||||
id: row.id,
|
||||
name: row.repository_row_name,
|
||||
code: "#{RepositoryRow::ID_PREFIX}#{row.repository_row_id}"
|
||||
}
|
||||
end
|
||||
render json: { parents: parents, children: children }
|
||||
end
|
||||
|
||||
def create
|
||||
RepositoryRowConnection.transaction do
|
||||
@connection_repository.repository_rows
|
||||
.where(id: connection_params[:relation_ids])
|
||||
.where.not(id: @repository_row.id)
|
||||
.where.not(
|
||||
id: if @relation_type == 'parent'
|
||||
@repository_row.parent_connections.select(:parent_id)
|
||||
else
|
||||
@repository_row.child_connections.select(:child_id)
|
||||
end
|
||||
)
|
||||
.find_each do |linked_repository_row|
|
||||
build_connection(linked_repository_row)
|
||||
log_activity(:inventory_item_relationships_linked,
|
||||
@repository_row.repository,
|
||||
{ repository_row: @repository_row.id,
|
||||
repository_row_linked: linked_repository_row.id,
|
||||
relationship_type: @relation_type == 'parent' ? 'parent' : 'child' })
|
||||
end
|
||||
|
||||
@repository_row.save!
|
||||
|
||||
relation_key = @relation_type == 'parent' ? :parents : :children
|
||||
render json: { relation_key => connected_rows_by_relation_type }
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
Rails.logger.error e.message
|
||||
render json: { errors: @repository_row.errors.full_messages }, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def destroy
|
||||
RepositoryRowConnection.transaction do
|
||||
connection = @repository_row.parent_connections.or(@repository_row.child_connections).find(params[:id])
|
||||
unlinked_repository_row = connection.parent?(@repository_row) ? connection.child : connection.parent
|
||||
|
||||
log_activity(:inventory_item_relationships_unlinked,
|
||||
@repository_row.repository,
|
||||
{ repository_row: @repository_row.id,
|
||||
repository_row_unlinked: unlinked_repository_row.id })
|
||||
|
||||
connection.destroy!
|
||||
head :no_content
|
||||
end
|
||||
rescue StandardError
|
||||
head :unprocessable_entity
|
||||
end
|
||||
|
||||
def repositories
|
||||
repositories = Repository.accessible_by_teams(current_team)
|
||||
.search_by_name_and_id(current_user, current_user.teams, params[:query])
|
||||
.order(name: :asc)
|
||||
.page(params[:page] || 1)
|
||||
.per(Constants::SEARCH_LIMIT)
|
||||
render json: {
|
||||
data: repositories.select(:id, :name, :archived)
|
||||
.map { |repository| { id: repository.id, name: repository.name_with_label } },
|
||||
next_page: repositories.next_page
|
||||
}
|
||||
end
|
||||
|
||||
def repository_rows
|
||||
repository_rows = @repository.repository_rows
|
||||
.search_by_name_and_id(current_user, current_user.teams, params[:query])
|
||||
.order(name: :asc)
|
||||
.page(params[:page] || 1)
|
||||
.per(Constants::SEARCH_LIMIT)
|
||||
render json: {
|
||||
data: repository_rows.select(:id, :name, :archived, :repository_id)
|
||||
.preload(:repository)
|
||||
.map { |row| { id: row.id, name: row.name_with_label } },
|
||||
next_page: repository_rows.next_page
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_create_vars
|
||||
@relation_type = connection_params[:relation] if connection_params[:relation].in?(%w(parent child))
|
||||
|
||||
return render_422(t('.invalid_params')) unless @relation_type
|
||||
|
||||
@connection_repository = Repository.accessible_by_teams(current_team)
|
||||
.find_by(id: connection_params[:connection_repository_id])
|
||||
return render_404 unless @connection_repository
|
||||
return render_403 unless can_connect_repository_rows?(@connection_repository)
|
||||
end
|
||||
|
||||
def load_repository
|
||||
@repository = Repository.accessible_by_teams(current_team).find_by(id: params[:repository_id])
|
||||
render_404 unless @repository
|
||||
end
|
||||
|
||||
def load_repository_row
|
||||
@repository_row = @repository.repository_rows.find_by(id: params[:repository_row_id])
|
||||
render_404 unless @repository_row
|
||||
end
|
||||
|
||||
def check_read_permissions
|
||||
render_403 unless can_read_repository?(@repository)
|
||||
end
|
||||
|
||||
def check_manage_permissions
|
||||
render_403 unless can_connect_repository_rows?(@repository)
|
||||
end
|
||||
|
||||
def connection_params
|
||||
params.require(:repository_row_connection).permit(:connection_repository_id, :relation, relation_ids: [])
|
||||
end
|
||||
|
||||
def build_connection(linked_repository_row)
|
||||
connection_params = {
|
||||
created_by: current_user,
|
||||
last_modified_by: current_user
|
||||
}
|
||||
|
||||
if @relation_type == 'parent'
|
||||
@repository_row.parent_connections.build(connection_params.merge(parent: linked_repository_row))
|
||||
else
|
||||
@repository_row.child_connections.build(connection_params.merge(child: linked_repository_row))
|
||||
end
|
||||
end
|
||||
|
||||
def connected_rows_by_relation_type
|
||||
repository_rows = if @relation_type == 'parent'
|
||||
@repository_row.parent_repository_rows
|
||||
else
|
||||
@repository_row.child_repository_rows
|
||||
end
|
||||
|
||||
repository_rows.preload(:repository)
|
||||
.map do |repository_row|
|
||||
{
|
||||
name: repository_row.name,
|
||||
code: repository_row.code,
|
||||
path: repository_repository_row_path(repository_row.repository, repository_row),
|
||||
repository_name: repository_row.repository.name,
|
||||
repository_path: repository_path(repository_row.repository)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def log_activity(type_of, repository, message_items = {})
|
||||
message_items = { repository: repository.id }.merge(message_items)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: type_of,
|
||||
owner: current_user,
|
||||
subject: repository,
|
||||
team: repository.team,
|
||||
message_items: message_items)
|
||||
end
|
||||
end
|
|
@ -5,18 +5,15 @@ class RepositoryRowsController < ApplicationController
|
|||
include MyModulesHelper
|
||||
include RepositoryDatatableHelper
|
||||
|
||||
MAX_PRINTABLE_ITEM_NAME_LENGTH = 64
|
||||
before_action :load_repository, except: %i(show print rows_to_print print_zpl
|
||||
validate_label_template_columns actions_toolbar)
|
||||
before_action :load_repository_row_print, only: %i(print rows_to_print print_zpl validate_label_template_columns)
|
||||
before_action :load_show_vars, only: %i(show)
|
||||
before_action :load_repository_or_snapshot, only: %i(print rows_to_print print_zpl
|
||||
before_action :load_repository, except: %i(show print rows_to_print print_zpl validate_label_template_columns)
|
||||
before_action :load_repository_or_snapshot, only: %i(show print rows_to_print print_zpl
|
||||
validate_label_template_columns)
|
||||
before_action :load_repository_row, only: %i(update update_cell assigned_task_list active_reminder_repository_cells)
|
||||
before_action :check_read_permissions, except: %i(create update delete_records
|
||||
copy_records reminder_repository_cells
|
||||
delete_records archive_records restore_records
|
||||
actions_toolbar)
|
||||
before_action :load_repository_row, only: %i(show update update_cell assigned_task_list
|
||||
active_reminder_repository_cells relationships)
|
||||
before_action :load_repository_rows, only: %i(print rows_to_print print_zpl validate_label_template_columns)
|
||||
|
||||
before_action :check_read_permissions, except: %i(create update update_cell delete_records
|
||||
copy_records archive_records restore_records)
|
||||
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)
|
||||
|
@ -101,7 +98,7 @@ class RepositoryRowsController < ApplicationController
|
|||
def validate_label_template_columns
|
||||
label_template = LabelTemplate.where(team_id: current_team.id).find(params[:label_template_id])
|
||||
|
||||
label_code = LabelTemplates::RepositoryRowService.new(label_template, @repository_row.first).render
|
||||
label_code = LabelTemplates::RepositoryRowService.new(label_template, @repository_rows.first).render
|
||||
if label_code[:error].empty?
|
||||
render json: { label_code: label_code[:label] }
|
||||
else
|
||||
|
@ -111,7 +108,7 @@ class RepositoryRowsController < ApplicationController
|
|||
|
||||
def print_zpl
|
||||
label_template = LabelTemplate.find_by(id: params[:label_template_id])
|
||||
labels = @repository_row.flat_map do |repository_row|
|
||||
labels = @repository_rows.flat_map do |repository_row|
|
||||
LabelTemplates::RepositoryRowService.new(label_template,
|
||||
repository_row).render[:label]
|
||||
end
|
||||
|
@ -128,7 +125,7 @@ class RepositoryRowsController < ApplicationController
|
|||
end
|
||||
|
||||
def rows_to_print
|
||||
render json: @repository_row, each_serializer: RepositoryRowSerializer, user: current_user
|
||||
render json: @repository_rows, each_serializer: RepositoryRowSerializer, user: current_user
|
||||
end
|
||||
|
||||
def print
|
||||
|
@ -141,7 +138,7 @@ class RepositoryRowsController < ApplicationController
|
|||
label_printer = LabelPrinter.find(params[:label_printer_id])
|
||||
label_template = LabelTemplate.find_by(id: params[:label_template_id])
|
||||
|
||||
job_ids = @repository_row.flat_map do |repository_row|
|
||||
job_ids = @repository_rows.flat_map do |repository_row|
|
||||
LabelPrinters::PrintJob.perform_later(
|
||||
label_printer,
|
||||
LabelTemplates::RepositoryRowService.new(label_template,
|
||||
|
@ -349,6 +346,14 @@ class RepositoryRowsController < ApplicationController
|
|||
}
|
||||
end
|
||||
|
||||
def relationships
|
||||
render json: {
|
||||
repository_row: RepositoryRowSerializer.new(@repository_row),
|
||||
parents: @repository_row.parent_repository_rows.map { |r| RepositoryRowSerializer.new(r) },
|
||||
children: @repository_row.child_repository_rows.map { |r| RepositoryRowSerializer.new(r) }
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
include StringUtility
|
||||
|
@ -361,26 +366,10 @@ class RepositoryRowsController < ApplicationController
|
|||
render_404 unless @repository
|
||||
end
|
||||
|
||||
def load_repository_row_print
|
||||
@repository_row = RepositoryRow.where(id: params[:rows])
|
||||
|
||||
render_404 unless @repository_row
|
||||
end
|
||||
|
||||
def load_repository_or_snapshot
|
||||
@repository = Repository.accessible_by_teams(current_team).find_by(id: @repository_row&.first&.repository_id)
|
||||
@repository ||= RepositorySnapshot.find_by(id: @repository_row&.first&.repository_id)
|
||||
|
||||
render_404 unless @repository
|
||||
end
|
||||
|
||||
def load_show_vars
|
||||
@repository = Repository.accessible_by_teams(current_team).find_by(id: params[:repository_id])
|
||||
@repository ||= RepositorySnapshot.find_by(id: params[:repository_id])
|
||||
@repository = Repository.accessible_by_teams(current_team).find_by(id: params[:repository_id]) ||
|
||||
RepositorySnapshot.find_by(id: params[:repository_id])
|
||||
return render_404 unless @repository
|
||||
|
||||
@repository_row = @repository.repository_rows.eager_load(:repository_columns).find_by(id: params[:id])
|
||||
render_404 unless @repository_row
|
||||
end
|
||||
|
||||
def load_repository_row
|
||||
|
@ -388,6 +377,12 @@ class RepositoryRowsController < ApplicationController
|
|||
render_404 unless @repository_row
|
||||
end
|
||||
|
||||
def load_repository_rows
|
||||
@repository_rows = @repository.repository_rows.eager_load(:repository_columns).where(id: params[:row_ids])
|
||||
|
||||
render_404 if @repository_rows.blank?
|
||||
end
|
||||
|
||||
def check_read_permissions
|
||||
render_403 unless can_read_repository?(@repository)
|
||||
end
|
||||
|
|
|
@ -60,26 +60,4 @@ module DatabaseHelper
|
|||
"AS t WHERE t.id = #{id};"
|
||||
).getvalue(0, 0).to_i
|
||||
end
|
||||
|
||||
# Adds a check constraint to the table
|
||||
def add_check_constraint(table, constraint_name, constraint)
|
||||
ActiveRecord::Base.connection.execute(
|
||||
"ALTER TABLE " \
|
||||
"#{table} " \
|
||||
"DROP CONSTRAINT IF EXISTS #{constraint_name}, " \
|
||||
"ADD CONSTRAINT " \
|
||||
"#{constraint_name} " \
|
||||
"CHECK ( #{constraint} ) " \
|
||||
"not valid;"
|
||||
)
|
||||
end
|
||||
|
||||
# Remove constraint from the table
|
||||
def drop_constraint(table, constraint_name)
|
||||
ActiveRecord::Base.connection.execute(
|
||||
"ALTER TABLE " \
|
||||
"#{table} " \
|
||||
"DROP CONSTRAINT IF EXISTS #{constraint_name}; "
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,7 +24,12 @@ module RepositoryDatatableHelper
|
|||
.active_reminder_repository_cells_repository_repository_row_url(
|
||||
repository,
|
||||
record
|
||||
)
|
||||
),
|
||||
relationshipsUrl:
|
||||
Rails.application.routes.url_helpers
|
||||
.relationships_repository_repository_row_url(record.repository_id, record.id),
|
||||
relationships_enabled: repository_row_connections_enabled,
|
||||
code: record.code
|
||||
)
|
||||
|
||||
if reminders_enabled
|
||||
|
@ -226,10 +231,11 @@ module RepositoryDatatableHelper
|
|||
'1': assigned_row(record),
|
||||
'2': record.code,
|
||||
'3': escape_input(record.name),
|
||||
'4': I18n.l(record.created_at, format: :full),
|
||||
'5': escape_input(record.created_by.full_name),
|
||||
'6': (record.archived_on ? I18n.l(record.archived_on, format: :full) : ''),
|
||||
'7': escape_input(record.archived_by&.full_name)
|
||||
'4': "#{record.parent_connections_count || 0} / #{record.child_connections_count || 0}",
|
||||
'5': I18n.l(record.created_at, format: :full),
|
||||
'6': escape_input(record.created_by.full_name),
|
||||
'7': (record.archived_on ? I18n.l(record.archived_on, format: :full) : ''),
|
||||
'8': escape_input(record.archived_by&.full_name)
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -311,4 +317,8 @@ module RepositoryDatatableHelper
|
|||
def display_stock_warnings?(repository)
|
||||
!repository.is_a?(RepositorySnapshot)
|
||||
end
|
||||
|
||||
def repository_row_connections_enabled
|
||||
Repository.repository_row_connections_enabled?
|
||||
end
|
||||
end
|
||||
|
|
15
app/javascript/packs/vue/item_relationships.js
Normal file
15
app/javascript/packs/vue/item_relationships.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* global notTurbolinksPreview */
|
||||
|
||||
import { createApp } from 'vue/dist/vue.esm-bundler.js';
|
||||
import PerfectScrollbar from 'vue3-perfect-scrollbar';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
import { mountWithTurbolinks } from './helpers/turbolinks.js';
|
||||
import ItemRelationshipsModal from '../../vue/item_relationships/ItemRelationshipsModal.vue';
|
||||
|
||||
const app = createApp({});
|
||||
app.component('ItemRelationshipsModal', ItemRelationshipsModal);
|
||||
app.use(ItemRelationshipsModal);
|
||||
app.use(PerfectScrollbar);
|
||||
app.directive('click-outside', vOnClickOutside);
|
||||
app.config.globalProperties.i18n = window.I18n;
|
||||
mountWithTurbolinks(app, '#itemRelationshipsModalWrapper');
|
15
app/javascript/packs/vue/repository_item_relationships.js
Normal file
15
app/javascript/packs/vue/repository_item_relationships.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* global notTurbolinksPreview */
|
||||
|
||||
import { createApp } from 'vue/dist/vue.esm-bundler.js';
|
||||
import PerfectScrollbar from 'vue3-perfect-scrollbar';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
import { mountWithTurbolinks } from './helpers/turbolinks.js';
|
||||
import RepositoryItemRelationshipsModal from '../../vue/item_relationships/RepositoryItemRelationshipsModal.vue';
|
||||
|
||||
const app = createApp({});
|
||||
app.component('RepositoryItemRelationshipsModal', RepositoryItemRelationshipsModal);
|
||||
app.use(RepositoryItemRelationshipsModal);
|
||||
app.use(PerfectScrollbar);
|
||||
app.directive('click-outside', vOnClickOutside);
|
||||
app.config.globalProperties.i18n = window.I18n;
|
||||
mountWithTurbolinks(app, '#repositoryItemRelationshipsModalWrapper');
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
import PerfectScrollbar from 'vue3-perfect-scrollbar';
|
||||
import { createApp } from 'vue/dist/vue.esm-bundler.js';
|
||||
import RepositoryItemSidebar from '../../vue/repository_item_sidebar/RepositoryItemSidebar.vue';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
import { mountWithTurbolinks } from './helpers/turbolinks.js';
|
||||
import { vOnClickOutside } from '@vueuse/components'
|
||||
import RepositoryItemSidebar from '../../vue/repository_item_sidebar/RepositoryItemSidebar.vue';
|
||||
|
||||
const app = createApp({});
|
||||
app.component('RepositoryItemSidebar', RepositoryItemSidebar);
|
||||
|
|
|
@ -127,7 +127,7 @@
|
|||
import SelectDropdown from "../shared/select_dropdown.vue";
|
||||
|
||||
export default {
|
||||
name: "AssignItemsToTaskModalContainer",
|
||||
name: 'AssignItemsToTaskModalContainer',
|
||||
props: {
|
||||
visibility: Boolean,
|
||||
urls: Object,
|
||||
|
@ -143,17 +143,17 @@ export default {
|
|||
selectedTask: null,
|
||||
projectsLoading: null,
|
||||
experimentsLoading: null,
|
||||
tasksLoading: null,
|
||||
tasksLoading: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
SelectDropdown
|
||||
},
|
||||
mounted() {
|
||||
$(this.$refs.modal).on("shown.bs.modal", () => {
|
||||
$(this.$refs.modal).on('shown.bs.modal', () => {
|
||||
this.projectsLoading = true;
|
||||
|
||||
$.get(this.projectURL, data => {
|
||||
$.get(this.projectURL, (data) => {
|
||||
if (Array.isArray(data)) {
|
||||
this.projects = data;
|
||||
return false;
|
||||
|
@ -164,9 +164,9 @@ export default {
|
|||
});
|
||||
});
|
||||
|
||||
$(this.$refs.modal).on("hidden.bs.modal", () => {
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.resetSelectors();
|
||||
this.$emit("close");
|
||||
this.$emit('close');
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
|
@ -176,36 +176,36 @@ export default {
|
|||
experimentsSelectorPlaceholder() {
|
||||
if (this.selectedProject) {
|
||||
return this.i18n.t(
|
||||
"repositories.modal_assign_items_to_task.body.experiment_select.placeholder"
|
||||
'repositories.modal_assign_items_to_task.body.experiment_select.placeholder'
|
||||
);
|
||||
}
|
||||
return this.i18n.t(
|
||||
"repositories.modal_assign_items_to_task.body.experiment_select.disabled_placeholder"
|
||||
'repositories.modal_assign_items_to_task.body.experiment_select.disabled_placeholder'
|
||||
);
|
||||
},
|
||||
tasksSelectorPlaceholder() {
|
||||
if (this.selectedExperiment) {
|
||||
return this.i18n.t(
|
||||
"repositories.modal_assign_items_to_task.body.task_select.placeholder"
|
||||
'repositories.modal_assign_items_to_task.body.task_select.placeholder'
|
||||
);
|
||||
}
|
||||
return this.i18n.t(
|
||||
"repositories.modal_assign_items_to_task.body.task_select.disabled_placeholder"
|
||||
'repositories.modal_assign_items_to_task.body.task_select.disabled_placeholder'
|
||||
);
|
||||
},
|
||||
projectURL() {
|
||||
return `${this.urls.projects}`;
|
||||
},
|
||||
experimentURL() {
|
||||
return `${this.urls.experiments}?project_id=${this.selectedProject ||
|
||||
""}`;
|
||||
return `${this.urls.experiments}?project_id=${this.selectedProject
|
||||
|| ''}`;
|
||||
},
|
||||
taskURL() {
|
||||
return `${this.urls.tasks}?experiment_id=${this.selectedExperiment ||
|
||||
""}`;
|
||||
return `${this.urls.tasks}?experiment_id=${this.selectedExperiment
|
||||
|| ''}`;
|
||||
},
|
||||
assignURL() {
|
||||
return this.urls.assign.replace(":module_id", this.selectedTask);
|
||||
return this.urls.assign.replace(':module_id', this.selectedTask);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -219,10 +219,10 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
showModal() {
|
||||
$(this.$refs.modal).modal("show");
|
||||
$(this.$refs.modal).modal('show');
|
||||
},
|
||||
hideModal() {
|
||||
$(this.$refs.modal).modal("hide");
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
changeProject(value) {
|
||||
this.selectedProject = value;
|
||||
|
@ -230,7 +230,7 @@ export default {
|
|||
this.resetTaskSelector();
|
||||
|
||||
this.experimentsLoading = true;
|
||||
$.get(this.experimentURL, data => {
|
||||
$.get(this.experimentURL, (data) => {
|
||||
if (Array.isArray(data)) {
|
||||
this.experiments = data;
|
||||
return false;
|
||||
|
@ -245,7 +245,7 @@ export default {
|
|||
this.resetTaskSelector();
|
||||
|
||||
this.tasksLoading = true;
|
||||
$.get(this.taskURL, data => {
|
||||
$.get(this.taskURL, (data) => {
|
||||
if (Array.isArray(data)) {
|
||||
this.tasks = data;
|
||||
return false;
|
||||
|
@ -280,16 +280,16 @@ export default {
|
|||
|
||||
$.ajax({
|
||||
url: this.assignURL,
|
||||
type: "PATCH",
|
||||
dataType: "json",
|
||||
type: 'PATCH',
|
||||
dataType: 'json',
|
||||
data: { rows_to_assign: this.rowsToAssign }
|
||||
}).done(({assigned_count}) => {
|
||||
}).done(({ assigned_count }) => {
|
||||
const skipped_count = this.rowsToAssign.length - assigned_count;
|
||||
|
||||
if (skipped_count) {
|
||||
HelperModule.flashAlertMsg(this.i18n.t('repositories.modal_assign_items_to_task.assign.flash_some_assignments_success', {assigned_count: assigned_count, skipped_count: skipped_count }), 'success');
|
||||
HelperModule.flashAlertMsg(this.i18n.t('repositories.modal_assign_items_to_task.assign.flash_some_assignments_success', { assigned_count, skipped_count }), 'success');
|
||||
} else {
|
||||
HelperModule.flashAlertMsg(this.i18n.t('repositories.modal_assign_items_to_task.assign.flash_all_assignments_success', {count: assigned_count}), 'success');
|
||||
HelperModule.flashAlertMsg(this.i18n.t('repositories.modal_assign_items_to_task.assign.flash_all_assignments_success', { count: assigned_count }), 'success');
|
||||
}
|
||||
}).fail(() => {
|
||||
HelperModule.flashAlertMsg(this.i18n.t('repositories.modal_assign_items_to_task.assign.flash_assignments_failure'), 'danger');
|
||||
|
|
|
@ -74,136 +74,135 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {debounce} from '../shared/debounce.js';
|
||||
import { debounce } from '../shared/debounce.js';
|
||||
|
||||
export default {
|
||||
name: 'ActionToolbar',
|
||||
props: {
|
||||
actionsUrl: { type: String, required: true }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
actions: [],
|
||||
shown: false,
|
||||
multiple: false,
|
||||
params: {},
|
||||
reloadCallback: null,
|
||||
actionsLoadedCallback: null,
|
||||
loaded: false,
|
||||
loading: false,
|
||||
width: 0,
|
||||
bottomOffset: 0,
|
||||
leftOffset: 0,
|
||||
buttonOverflow: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.actionToolbarComponent = this;
|
||||
window.onresize = this.setWidth;
|
||||
export default {
|
||||
name: 'ActionToolbar',
|
||||
props: {
|
||||
actionsUrl: { type: String, required: true }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
actions: [],
|
||||
shown: false,
|
||||
multiple: false,
|
||||
params: {},
|
||||
reloadCallback: null,
|
||||
actionsLoadedCallback: null,
|
||||
loaded: false,
|
||||
loading: false,
|
||||
width: 0,
|
||||
bottomOffset: 0,
|
||||
leftOffset: 0,
|
||||
buttonOverflow: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
window.actionToolbarComponent = this;
|
||||
window.onresize = this.setWidth;
|
||||
|
||||
this.debouncedFetchActions = debounce((params) => {
|
||||
this.params = params;
|
||||
this.debouncedFetchActions = debounce((params) => {
|
||||
this.params = params;
|
||||
|
||||
$.get(`${this.actionsUrl}?${new URLSearchParams(this.params).toString()}`, (data) => {
|
||||
this.actions = data.actions;
|
||||
this.loading = false;
|
||||
this.setButtonOverflow();
|
||||
if (this.actionsLoadedCallback) this.$nextTick(this.actionsLoadedCallback);
|
||||
});
|
||||
}, 10);
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(this.setWidth);
|
||||
window.addEventListener('scroll', this.setLeftOffset);
|
||||
},
|
||||
beforeUnmount() {
|
||||
delete window.actionToolbarComponent;
|
||||
window.removeEventListener('scroll', this.setLeftOffset);
|
||||
},
|
||||
computed: {
|
||||
paramsAreBlank() {
|
||||
let values = Object.values(this.params);
|
||||
|
||||
if (values.length === 0) return true;
|
||||
|
||||
return !values.some((v) => v.length);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setWidth() {
|
||||
this.width = $(this.$el).parent().width();
|
||||
$.get(`${this.actionsUrl}?${new URLSearchParams(this.params).toString()}`, (data) => {
|
||||
this.actions = data.actions;
|
||||
this.loading = false;
|
||||
this.setButtonOverflow();
|
||||
},
|
||||
setButtonOverflow() {
|
||||
// detects if the last action button is outside the toolbar container
|
||||
this.buttonOverflow = false;
|
||||
if (this.actionsLoadedCallback) this.$nextTick(this.actionsLoadedCallback);
|
||||
});
|
||||
}, 10);
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(this.setWidth);
|
||||
window.addEventListener('scroll', this.setLeftOffset);
|
||||
},
|
||||
beforeUnmount() {
|
||||
delete window.actionToolbarComponent;
|
||||
window.removeEventListener('scroll', this.setLeftOffset);
|
||||
},
|
||||
computed: {
|
||||
paramsAreBlank() {
|
||||
const values = Object.values(this.params);
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (
|
||||
!(this.$el.getBoundingClientRect &&
|
||||
document.querySelector('.sn-action-toolbar__action:last-child'))
|
||||
) return;
|
||||
if (values.length === 0) return true;
|
||||
|
||||
let containerRect = this.$el.getBoundingClientRect();
|
||||
let lastActionRect = document.querySelector('.sn-action-toolbar__action:last-child').getBoundingClientRect();
|
||||
return !values.some((v) => v.length);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setWidth() {
|
||||
this.width = $(this.$el).parent().width();
|
||||
this.setButtonOverflow();
|
||||
},
|
||||
setButtonOverflow() {
|
||||
// detects if the last action button is outside the toolbar container
|
||||
this.buttonOverflow = false;
|
||||
|
||||
this.buttonOverflow = containerRect.left + containerRect.width < lastActionRect.left + lastActionRect.width;
|
||||
});
|
||||
},
|
||||
setLeftOffset() {
|
||||
this.leftOffset = -(window.pageXOffset || document.documentElement.scrollLeft);
|
||||
},
|
||||
setBottomOffset(pixels) {
|
||||
this.bottomOffset = pixels;
|
||||
},
|
||||
fetchActions(params) {
|
||||
this.loading = true;
|
||||
this.debouncedFetchActions(params);
|
||||
},
|
||||
setReloadCallback(func) {
|
||||
this.reloadCallback = func;
|
||||
},
|
||||
setActionsLoadedCallback(func) {
|
||||
this.actionsLoadedCallback = func;
|
||||
},
|
||||
doAction(action, event) {
|
||||
switch(action.type) {
|
||||
case 'legacy':
|
||||
// do nothing, this is handled by legacy code based on the button class
|
||||
break;
|
||||
case 'link':
|
||||
// do nothing, already handled by href
|
||||
break;
|
||||
case 'modal':
|
||||
// do nothihg, boostrap modal handled by data-toggle="modal" and data-target
|
||||
case 'remote-modal':
|
||||
// do nothing, handled by the data-action="remote-modal" binding
|
||||
break;
|
||||
case 'download':
|
||||
event.stopPropagation();
|
||||
window.location.href = action.path;
|
||||
break;
|
||||
case 'request':
|
||||
event.stopPropagation();
|
||||
this.$nextTick(() => {
|
||||
if (
|
||||
!(this.$el.getBoundingClientRect
|
||||
&& document.querySelector('.sn-action-toolbar__action:last-child'))
|
||||
) return;
|
||||
|
||||
$.ajax({
|
||||
type: action.request_method,
|
||||
url: action.path,
|
||||
data: this.params
|
||||
}).done((data) => {
|
||||
HelperModule.flashAlertMsg(data.responseJSON && data.responseJSON.message || data.message, 'success');
|
||||
}).fail((data) => {
|
||||
HelperModule.flashAlertMsg(data.responseJSON && data.responseJSON.message || data.message, 'danger');
|
||||
}).always(() => {
|
||||
if (this.reloadCallback) this.reloadCallback();
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
closeExportDropdown(event) {
|
||||
event.preventDefault();
|
||||
$(event.target).closest('.export-actions-dropdown').removeClass('open')
|
||||
const containerRect = this.$el.getBoundingClientRect();
|
||||
const lastActionRect = document.querySelector('.sn-action-toolbar__action:last-child').getBoundingClientRect();
|
||||
|
||||
this.buttonOverflow = containerRect.left + containerRect.width < lastActionRect.left + lastActionRect.width;
|
||||
});
|
||||
},
|
||||
setLeftOffset() {
|
||||
this.leftOffset = -(window.pageXOffset || document.documentElement.scrollLeft);
|
||||
},
|
||||
setBottomOffset(pixels) {
|
||||
this.bottomOffset = pixels;
|
||||
},
|
||||
fetchActions(params) {
|
||||
this.loading = true;
|
||||
this.debouncedFetchActions(params);
|
||||
},
|
||||
setReloadCallback(func) {
|
||||
this.reloadCallback = func;
|
||||
},
|
||||
setActionsLoadedCallback(func) {
|
||||
this.actionsLoadedCallback = func;
|
||||
},
|
||||
doAction(action, event) {
|
||||
switch (action.type) {
|
||||
case 'legacy':
|
||||
// do nothing, this is handled by legacy code based on the button class
|
||||
break;
|
||||
case 'link':
|
||||
// do nothing, already handled by href
|
||||
break;
|
||||
case 'modal':
|
||||
// do nothihg, boostrap modal handled by data-toggle="modal" and data-target
|
||||
case 'remote-modal':
|
||||
// do nothing, handled by the data-action="remote-modal" binding
|
||||
break;
|
||||
case 'download':
|
||||
event.stopPropagation();
|
||||
window.location.href = action.path;
|
||||
break;
|
||||
case 'request':
|
||||
event.stopPropagation();
|
||||
$.ajax({
|
||||
type: action.request_method,
|
||||
url: action.path,
|
||||
data: this.params
|
||||
}).done((data) => {
|
||||
HelperModule.flashAlertMsg(data.responseJSON && data.responseJSON.message || data.message, 'success');
|
||||
}).fail((data) => {
|
||||
HelperModule.flashAlertMsg(data.responseJSON && data.responseJSON.message || data.message, 'danger');
|
||||
}).always(() => {
|
||||
if (this.reloadCallback) this.reloadCallback();
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
closeExportDropdown(event) {
|
||||
event.preventDefault();
|
||||
$(event.target).closest('.export-actions-dropdown').removeClass('open');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<div ref="itemRelationshipsModal" @keydown.esc="cancel" id="itemRelationshipsModal" tabindex="-1" role="dialog"
|
||||
class="modal">
|
||||
<div class="modal-dialog modal-sm" role="document">
|
||||
<div class="modal-content">
|
||||
|
||||
<!-- header -->
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="modal-destroy-team-label">
|
||||
{{ rowCode }}
|
||||
</h4>
|
||||
</div>
|
||||
<div v-if="isLoading" class="flex justify-center h-40 w-auto">
|
||||
<p class="m-auto h-fit w-fit">Loading...</p>
|
||||
</div>
|
||||
<div v-else class="modal-body ">
|
||||
|
||||
<!-- parents -->
|
||||
<div v-if="parents?.length > 0">
|
||||
<p>Parents:</p>
|
||||
<div v-for="(parentObj, index) in parents" :key="index" class="flex flex-col">
|
||||
<div>{{ parentObj?.code }} | {{ parentObj?.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- children -->
|
||||
<div v-if="children?.length > 0">
|
||||
<p>Children:</p>
|
||||
<div v-for="(childObj, index) in children" :key="index" class="flex flex-col">
|
||||
<div>{{ childObj?.code }} | {{ childObj?.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- footer -->
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="cancel">{{ i18n.t('general.cancel') }}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ItemRelationshipsModal',
|
||||
created() {
|
||||
window.itemRelationshipsModal = this;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
rowCode: null,
|
||||
parents: [],
|
||||
children: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
show(relationshipsUrl) {
|
||||
$(this.$refs.itemRelationshipsModal).modal('show');
|
||||
this.fetchItemRelationshipsData(relationshipsUrl);
|
||||
},
|
||||
fetchItemRelationshipsData(relationshipsUrl) {
|
||||
this.isLoading = true;
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
url: relationshipsUrl,
|
||||
dataType: 'json',
|
||||
success: (result) => {
|
||||
this.isLoading = false;
|
||||
this.parents = result.parents;
|
||||
this.children = result.children;
|
||||
this.rowCode = result.repository_row.code;
|
||||
}
|
||||
});
|
||||
},
|
||||
cancel() {
|
||||
$(this.$refs.itemRelationshipsModal).modal('hide');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,280 @@
|
|||
<template>
|
||||
<div
|
||||
v-if="canConnectRows"
|
||||
ref="repositoryItemRelationshipsModal"
|
||||
@keydown.esc="close"
|
||||
id="repositoryItemRelationshipsModal"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
class="modal ">
|
||||
<div class="modal-dialog modal-sm" role="document">
|
||||
<div class="modal-content w-[400px] m-auto">
|
||||
|
||||
<!-- header -->
|
||||
<div class="modal-header h-[76px] flex !flex-col gap-[6px]">
|
||||
|
||||
<!-- header title with close icon -->
|
||||
<div class="h-[30px] w-full flex flex-row-reverse">
|
||||
<i id="close-icon" class="sn-icon sn-icon-close ml-auto cursor-pointer my-auto mx-0" data-dismiss="modal"
|
||||
aria-label="Close" @click="close"></i>
|
||||
<h4 class="modal-title" id="modal-destroy-team-label">
|
||||
{{ i18n.t('repositories.item_card.repository_item_relationships_modal.header_title') }}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<!-- header subtitle -->
|
||||
<div class="h-10 overflow-hidden break-words text-sn-dark-grey text-sm font-normal leading-5 self-start"
|
||||
style="-webkit-box-orient: vertical; -webkit-line-clamp: 2; display: -webkit-box;">
|
||||
{{ i18n.t('repositories.item_card.repository_item_relationships_modal.header_subtitle') }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-body flex flex-col gap-6" :class="{ '!pb-3': notification }">
|
||||
<!-- inventory -->
|
||||
<div class="flex flex-col gap-[7px]">
|
||||
<div class="h-5 whitespace-nowrap overflow-auto">
|
||||
{{ i18n.t('repositories.item_card.repository_item_relationships_modal.inventory_section_title') }}</div>
|
||||
<div class="h-11">
|
||||
<select-search ref="ChangeSelectedInventoryDropdownSelector" @change="changeSelectedInventory" :value="selectedInventoryValue"
|
||||
:options="inventoryOptions"
|
||||
:isLoading="isLoadingInventories"
|
||||
:lazyLoadEnabled="true"
|
||||
:optionsUrl="inventoriesUrl"
|
||||
optionsClassName="inventory-options"
|
||||
:placeholder="i18n.t('repositories.item_card.repository_item_relationships_modal.select_inventory_placeholder')"
|
||||
:noOptionsPlaceholder="i18n.t('repositories.item_card.repository_item_relationships_modal.select_inventory_no_options_placeholder')"
|
||||
:searchPlaceholder="i18n.t('repositories.item_card.repository_item_relationships_modal.select_inventory_placeholder')"
|
||||
@update-options="updateInventories"
|
||||
@reached-end="fetchInventories"
|
||||
></select-search>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- item -->
|
||||
<div class="flex flex-col gap-[7px]">
|
||||
<div class="h-5 whitespace-nowrap overflow-auto">
|
||||
{{ i18n.t('repositories.item_card.repository_item_relationships_modal.item_section_title') }}</div>
|
||||
<div class="h-11">
|
||||
<ChecklistSearch
|
||||
ref="ChangeSelectedItemChecklistSelector"
|
||||
:shouldUpdateWithoutValues="true"
|
||||
:lazyLoadEnabled="true"
|
||||
:withButtons="false"
|
||||
:withEditCursor="false"
|
||||
optionsClassName="item-options"
|
||||
:optionsUrl="inventoryItemsUrl"
|
||||
:options="itemOptions"
|
||||
:placeholder="i18n.t('repositories.item_card.repository_item_relationships_modal.select_item_placeholder')"
|
||||
:noOptionsPlaceholder="i18n.t('repositories.item_card.repository_item_relationships_modal.select_item_no_options_placeholder')"
|
||||
:initialSelectedValues="selectedItemValues"
|
||||
:shouldUpdateOnToggleClose="true"
|
||||
:params="itemParams"
|
||||
@update-options="updateInventoryItems"
|
||||
@update="selectedItemValues = $event"
|
||||
@reached-end="() => fetchInventoryItems(selectedInventoryValue)"
|
||||
></ChecklistSearch>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- relationship -->
|
||||
<div class="flex flex-col gap-[7px]">
|
||||
<div class="h-5 whitespace-nowrap overflow-auto">
|
||||
{{ i18n.t('repositories.item_card.repository_item_relationships_modal.relationship_section_title') }}
|
||||
</div>
|
||||
<div class="h-11">
|
||||
<Select
|
||||
ref="ChangeSelectedRelationshipDropdownSelector"
|
||||
class="hover:border-sn-sleepy-grey"
|
||||
@change="selectedRelationshipValue = $event"
|
||||
:value="selectedRelationshipValue"
|
||||
:options="[['parent', 'Parent'], ['child', 'Child']]"
|
||||
:placeholder="i18n.t('repositories.item_card.repository_item_relationships_modal.select_relationship_placeholder')"
|
||||
></Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notification -->
|
||||
<template v-if="notification">
|
||||
<hr class="bg-sn-light-grey mb-6 mt-3" />
|
||||
<div class="w-full mb-6">
|
||||
<div class="flex align-center gap-2.5">
|
||||
<img class="w-6 h-6" :src="notificationIconPath" alt="warning" />
|
||||
<span class="my-auto">{{ notification.message }}</span>
|
||||
</div>
|
||||
<div v-html="notification.support_html" class="pl-2.5"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- footer -->
|
||||
<div class="modal-footer">
|
||||
<div class="flex justify-end gap-4">
|
||||
<button class="btn btn-secondary w-[78px] h-10 whitespace-nowrap" @click="close">
|
||||
{{ i18n.t('repositories.item_card.repository_item_relationships_modal.cancel_button') }}
|
||||
</button>
|
||||
<button class="btn btn-primary w-[59px] h-10 whitespace-nowrap"
|
||||
:class="{ 'disabled': !shouldEnableAddButton }" @click="() => addRelation(selectedRelationshipValue)">
|
||||
{{ i18n.t('repositories.item_card.repository_item_relationships_modal.add_button') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SelectSearch from '../shared/select_search.vue';
|
||||
import ChecklistSearch from '../shared/checklist_search.vue';
|
||||
import Select from '../shared/select.vue';
|
||||
import ChecklistSelect from '../shared/checklist_select.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryItemRelationshipsModal',
|
||||
components: {
|
||||
'select-search': SelectSearch,
|
||||
ChecklistSearch,
|
||||
Select,
|
||||
'checklist-select': ChecklistSelect
|
||||
},
|
||||
created() {
|
||||
window.repositoryItemRelationshipsModal = this;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoadingInventories: false,
|
||||
inventoriesUrl: '',
|
||||
inventoryItemsUrl: '',
|
||||
createConnectionUrl: '',
|
||||
createConnectionUrlValue: '',
|
||||
selectedInventoryValue: null,
|
||||
selectedItemValues: [],
|
||||
selectedRelationshipValue: null,
|
||||
inventoryOptions: [],
|
||||
itemOptions: [],
|
||||
nextInventoriesPage: 1,
|
||||
nextItemsPage: 1,
|
||||
itemParams: [],
|
||||
notification: null,
|
||||
notificationIconPath: null,
|
||||
canConnectRows: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
shouldEnableAddButton() {
|
||||
return ((this.selectedInventoryValue !== null) && (this.selectedRelationshipValue !== null) && (this.selectedItemValues.length > 0));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchInventories() {
|
||||
if (!this.nextInventoriesPage) return;
|
||||
|
||||
this.loadingInventories = true;
|
||||
$.ajax({
|
||||
url: `${this.inventoriesUrl}?page=${this.nextInventoriesPage}`,
|
||||
success: (result) => {
|
||||
this.inventoryOptions = this.inventoryOptions.concat(result.data.map((val) => [val.id, val.name]));
|
||||
this.loadingInventories = false;
|
||||
this.nextInventoriesPage = result.next_page;
|
||||
}
|
||||
});
|
||||
},
|
||||
fetchInventoryItems(inventoryValue = null) {
|
||||
if (!inventoryValue || !this.nextItemsPage) return;
|
||||
|
||||
this.loadingItems = true;
|
||||
$.ajax({
|
||||
url: `${this.inventoryItemsUrl}/?page=${this.nextItemsPage}&repository_id=${inventoryValue}`,
|
||||
success: (result) => {
|
||||
this.itemOptions = this.itemOptions.concat(result.data.map((val) => ({ id: val.id, label: val.name })));
|
||||
this.loadingItems = false;
|
||||
this.nextItemsPage = result.next_page;
|
||||
}
|
||||
});
|
||||
},
|
||||
updateInventories(currentOptions, result) {
|
||||
this.inventoryOptions = currentOptions.concat(result.data?.map((val) => [val.id, val.name]));
|
||||
},
|
||||
updateInventoryItems(currentOptions, result) {
|
||||
this.itemOptions = currentOptions.concat(result.data.map(({ id, name }) => ({ id, label: name })));
|
||||
},
|
||||
show(params = {}) {
|
||||
const {
|
||||
relation,
|
||||
optionUrls,
|
||||
addRelationCallback,
|
||||
notificationIconPath,
|
||||
notification,
|
||||
canConnectRows
|
||||
} = params;
|
||||
this.inventoriesUrl = optionUrls.inventories_url;
|
||||
this.inventoryItemsUrl = optionUrls.inventory_items_url;
|
||||
this.createConnectionUrl = optionUrls.create_url;
|
||||
this.addRelationCallback = addRelationCallback;
|
||||
this.notificationIconPath = notificationIconPath;
|
||||
this.notification = notification;
|
||||
this.canConnectRows = canConnectRows;
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs.repositoryItemRelationshipsModal).modal('show');
|
||||
});
|
||||
|
||||
if (['parent', 'child'].includes(relation)) {
|
||||
this.selectedRelationshipValue = relation;
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.fetchInventories();
|
||||
});
|
||||
},
|
||||
changeSelectedInventory(value) {
|
||||
this.selectedInventoryValue = value;
|
||||
this.itemOptions = [];
|
||||
this.nextItemsPage = 1;
|
||||
if (value) {
|
||||
this.loadingItems = true;
|
||||
this.itemParams = [`repository_id=${value}`];
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.fetchInventoryItems(value);
|
||||
});
|
||||
},
|
||||
close() {
|
||||
Object.assign(this.$data, {
|
||||
selectedInventoryValue: null,
|
||||
selectedItemValues: [],
|
||||
selectedRelationshipValue: null,
|
||||
nextInventoriesPage: 1,
|
||||
nextItemsPage: 1,
|
||||
inventoriesUrl: '',
|
||||
inventoryItemsUrl: '',
|
||||
createConnectionUrl: '',
|
||||
itemOptions: [],
|
||||
inventoryOptions: [],
|
||||
itemParams: [],
|
||||
canConnectRows: null
|
||||
});
|
||||
$(this.$refs.repositoryItemRelationshipsModal).modal('hide');
|
||||
},
|
||||
addRelation(relation) {
|
||||
const $this = this;
|
||||
$.ajax({
|
||||
url: this.createConnectionUrl,
|
||||
method: 'POST',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
repository_row_connection: {
|
||||
relation: this.selectedRelationshipValue,
|
||||
relation_ids: this.selectedItemValues,
|
||||
connection_repository_id: this.selectedInventoryValue
|
||||
}
|
||||
},
|
||||
success: (result) => {
|
||||
$this.addRelationCallback(result, relation);
|
||||
if ($('.dataTable.repository-dataTable')[0]) $('.dataTable.repository-dataTable').DataTable().ajax.reload(null, false);
|
||||
}
|
||||
});
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -75,137 +75,129 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const DPI_RESOLUTION_OPTIONS = [
|
||||
{ value: 6, label: '152 dpi' },
|
||||
{ value: 8, label: '203 dpi' },
|
||||
{ value: 12, label: '300 dpi'},
|
||||
{ value: 24, label: '600 dpi' }
|
||||
]
|
||||
<script>
|
||||
|
||||
const DPMM_RESOLUTION_OPTIONS = [
|
||||
{ value: 6, label: '6 dpmm (152 dpi)' },
|
||||
{ value: 8, label: '8 dpmm (203 dpi)' },
|
||||
{ value: 12, label: '12 dpmm (300 dpi)' },
|
||||
{ value: 24, label: '24 dpmm (600 dpi)' }
|
||||
]
|
||||
const DPI_RESOLUTION_OPTIONS = [
|
||||
{ value: 6, label: '152 dpi' },
|
||||
{ value: 8, label: '203 dpi' },
|
||||
{ value: 12, label: '300 dpi' },
|
||||
{ value: 24, label: '600 dpi' }
|
||||
];
|
||||
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
|
||||
export default {
|
||||
name: 'LabelPreview',
|
||||
components: { DropdownSelector },
|
||||
props: {
|
||||
template: { type: Object, required: true},
|
||||
zpl: { type: String, required: true },
|
||||
previewUrl: { type: String, required: true },
|
||||
viewOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
export default {
|
||||
name: 'LabelPreview',
|
||||
components: { DropdownSelector },
|
||||
props: {
|
||||
template: { type: Object, required: true },
|
||||
zpl: { type: String, required: true },
|
||||
previewUrl: { type: String, required: true },
|
||||
viewOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
DPMM_RESOLUTION_OPTIONS,
|
||||
DPI_RESOLUTION_OPTIONS,
|
||||
optionsOpen: false,
|
||||
width: this.template.attributes.unit == 'in' ? this.template.attributes.width_mm / 25.4 : this.template.attributes.width_mm,
|
||||
height: this.template.attributes.unit == 'in' ? this.template.attributes.height_mm / 25.4 : this.template.attributes.height_mm,
|
||||
unit: this.template.attributes.unit,
|
||||
density: this.template.attributes.density,
|
||||
base64Image: null,
|
||||
imageStyle: ''
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.refreshPreview();
|
||||
},
|
||||
computed: {
|
||||
widthMm() {
|
||||
return this.unit === 'in' ? this.width * 25.4 : this.width;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
DPMM_RESOLUTION_OPTIONS,
|
||||
DPI_RESOLUTION_OPTIONS,
|
||||
optionsOpen: false,
|
||||
width: this.template.attributes.unit == 'in' ? this.template.attributes.width_mm / 25.4 : this.template.attributes.width_mm,
|
||||
height: this.template.attributes.unit == 'in' ? this.template.attributes.height_mm / 25.4 : this.template.attributes.height_mm,
|
||||
unit: this.template.attributes.unit,
|
||||
density: this.template.attributes.density,
|
||||
base64Image: null,
|
||||
imageStyle: ''
|
||||
}
|
||||
heightMm() {
|
||||
return this.unit === 'in' ? this.height * 25.4 : this.height;
|
||||
},
|
||||
mounted() {
|
||||
canManage() {
|
||||
return this.template.attributes.urls.update;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
unit() {
|
||||
this.setDefaults();
|
||||
},
|
||||
zpl() {
|
||||
this.refreshPreview();
|
||||
},
|
||||
computed: {
|
||||
widthMm() {
|
||||
return this.unit === 'in' ? this.width * 25.4 : this.width;
|
||||
},
|
||||
heightMm() {
|
||||
return this.unit === 'in' ? this.height * 25.4 : this.height;
|
||||
},
|
||||
canManage() {
|
||||
return this.template.attributes.urls.update;
|
||||
template() {
|
||||
this.unit = this.template.attributes.unit;
|
||||
this.width = this.template.attributes.unit == 'in' ? this.template.attributes.width_mm / 25.4 : this.template.attributes.width_mm;
|
||||
this.height = this.template.attributes.unit == 'in' ? this.template.attributes.height_mm / 25.4 : this.template.attributes.height_mm;
|
||||
this.density = this.template.attributes.density;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setDefaults() {
|
||||
!this.unit && (this.unit = 'in');
|
||||
!this.density && (this.density = 12);
|
||||
!this.width && (this.width = this.unit === 'in' ? 2 : 50.8);
|
||||
!this.height && (this.height = this.unit === 'in' ? 1 : 25.4);
|
||||
},
|
||||
recalculateUnits() {
|
||||
if (this.unit === 'in') {
|
||||
this.width /= 25.4;
|
||||
this.height /= 25.4;
|
||||
} else {
|
||||
this.width *= 25.4;
|
||||
this.height *= 25.4;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
unit() {
|
||||
this.setDefaults();
|
||||
},
|
||||
zpl() {
|
||||
this.refreshPreview();
|
||||
},
|
||||
template() {
|
||||
this.unit = this.template.attributes.unit
|
||||
this.width = this.template.attributes.unit == 'in' ? this.template.attributes.width_mm / 25.4 : this.template.attributes.width_mm
|
||||
this.height = this.template.attributes.unit == 'in' ? this.template.attributes.height_mm / 25.4 : this.template.attributes.height_mm
|
||||
this.density = this.template.attributes.density
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setDefaults() {
|
||||
!this.unit && (this.unit = 'in');
|
||||
!this.density && (this.density = 12);
|
||||
!this.width && (this.width = this.unit === 'in' ? 2 : 50.8);
|
||||
!this.height && (this.height = this.unit === 'in' ? 1 : 25.4);
|
||||
},
|
||||
recalculateUnits() {
|
||||
if (this.unit === 'in') {
|
||||
this.width /= 25.4;
|
||||
this.height /= 25.4;
|
||||
} else {
|
||||
this.width *= 25.4;
|
||||
this.height *= 25.4;
|
||||
}
|
||||
},
|
||||
refreshPreview() {
|
||||
if (this.zpl.length === 0) return;
|
||||
refreshPreview() {
|
||||
if (this.zpl.length === 0) return;
|
||||
|
||||
this.base64Image = null;
|
||||
this.base64Image = null;
|
||||
|
||||
$.ajax({
|
||||
url: this.previewUrl,
|
||||
type: 'GET',
|
||||
data: {
|
||||
zpl: this.zpl,
|
||||
width: this.widthMm,
|
||||
height: this.heightMm,
|
||||
density: this.density
|
||||
},
|
||||
success: (result) => {
|
||||
this.base64Image = result.base64_preview;
|
||||
if (this.base64Image.length > 0) {
|
||||
this.$emit('preview:valid');
|
||||
} else {
|
||||
this.$emit('preview:invalid');
|
||||
}
|
||||
},
|
||||
error: (result) => {
|
||||
this.base64Image = '';
|
||||
$.ajax({
|
||||
url: this.previewUrl,
|
||||
type: 'GET',
|
||||
data: {
|
||||
zpl: this.zpl,
|
||||
width: this.widthMm,
|
||||
height: this.heightMm,
|
||||
density: this.density
|
||||
},
|
||||
success: (result) => {
|
||||
this.base64Image = result.base64_preview;
|
||||
if (this.base64Image.length > 0) {
|
||||
this.$emit('preview:valid');
|
||||
} else {
|
||||
this.$emit('preview:invalid');
|
||||
}
|
||||
},
|
||||
error: (result) => {
|
||||
this.base64Image = '';
|
||||
this.$emit('preview:invalid');
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
updateUnit(unit) {
|
||||
if (this.unit === unit) return;
|
||||
this.unit = unit;
|
||||
this.recalculateUnits();
|
||||
this.$emit('unit:update', this.unit);
|
||||
},
|
||||
updateDensity(density) {
|
||||
this.density = density;
|
||||
this.$emit('density:update', this.density);
|
||||
},
|
||||
densityLabel() {
|
||||
let resolutions = this.unit === 'in' ? DPI_RESOLUTION_OPTIONS : DPMM_RESOLUTION_OPTIONS;
|
||||
return resolutions.find(element => {
|
||||
return element.value === this.density;
|
||||
}).label;
|
||||
}
|
||||
});
|
||||
},
|
||||
updateUnit(unit) {
|
||||
if (this.unit === unit) return;
|
||||
this.unit = unit;
|
||||
this.recalculateUnits();
|
||||
this.$emit('unit:update', this.unit);
|
||||
},
|
||||
updateDensity(density) {
|
||||
this.density = density;
|
||||
this.$emit('density:update', this.density);
|
||||
},
|
||||
densityLabel() {
|
||||
const resolutions = this.unit === 'in' ? DPI_RESOLUTION_OPTIONS : DPMM_RESOLUTION_OPTIONS;
|
||||
return resolutions.find((element) => element.value === this.density).label;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -30,51 +30,51 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'logoInsertModal',
|
||||
props: {
|
||||
unit: { type: String, required: true },
|
||||
density: { type: Number, required: true },
|
||||
dimension: { type: Array, required: true }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
ratio: 1
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.$emit('cancel');
|
||||
});
|
||||
this.width = this.dimension[0] / this.density
|
||||
this.height = this.dimension[1] / this.density
|
||||
if (this.unit == 'in') {
|
||||
this.width = this.width / 25.4
|
||||
this.height = this.height / 25.4
|
||||
}
|
||||
<script>
|
||||
export default {
|
||||
name: 'logoInsertModal',
|
||||
props: {
|
||||
unit: { type: String, required: true },
|
||||
density: { type: Number, required: true },
|
||||
dimension: { type: Array, required: true }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
ratio: 1
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.$emit('cancel');
|
||||
});
|
||||
this.width = this.dimension[0] / this.density;
|
||||
this.height = this.dimension[1] / this.density;
|
||||
if (this.unit == 'in') {
|
||||
this.width /= 25.4;
|
||||
this.height /= 25.4;
|
||||
}
|
||||
|
||||
this.width = Math.round(this.width * 100) / 100
|
||||
this.height = Math.round(this.height * 100) / 100
|
||||
this.ratio = this.dimension[0] / this.dimension[1]
|
||||
this.width = Math.round(this.width * 100) / 100;
|
||||
this.height = Math.round(this.height * 100) / 100;
|
||||
this.ratio = this.dimension[0] / this.dimension[1];
|
||||
},
|
||||
methods: {
|
||||
updateHeight() {
|
||||
this.height = Math.round(this.width * 10 / this.ratio) / 10;
|
||||
},
|
||||
methods: {
|
||||
updateHeight() {
|
||||
this.height = Math.round(this.width * 10 / this.ratio) / 10
|
||||
},
|
||||
updateWidth() {
|
||||
this.width = Math.round(this.height * this.ratio * 10) / 10
|
||||
},
|
||||
confirm() {
|
||||
this.$emit('insert:tag', {tag: `{{LOGO, ${this.unit}, ${this.width}, ${this.height}}}`});
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
updateWidth() {
|
||||
this.width = Math.round(this.height * this.ratio * 10) / 10;
|
||||
},
|
||||
confirm() {
|
||||
this.$emit('insert:tag', { tag: `{{LOGO, ${this.unit}, ${this.width}, ${this.height}}}` });
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -107,214 +107,217 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script>
|
||||
|
||||
import InlineEdit from '../shared/inline_edit.vue'
|
||||
import InsertFieldDropdown from './insert_field_dropdown.vue'
|
||||
import LabelPreview from './components/label_preview.vue'
|
||||
import InlineEdit from '../shared/inline_edit.vue';
|
||||
import InsertFieldDropdown from './insert_field_dropdown.vue';
|
||||
import LabelPreview from './components/label_preview.vue';
|
||||
|
||||
export default {
|
||||
name: 'LabelTemplateContainer',
|
||||
props: {
|
||||
labelTemplateUrl: String,
|
||||
labelTemplatesUrl: String,
|
||||
previewUrl: String,
|
||||
newLabel: Boolean
|
||||
export default {
|
||||
name: 'LabelTemplateContainer',
|
||||
props: {
|
||||
labelTemplateUrl: String,
|
||||
labelTemplatesUrl: String,
|
||||
previewUrl: String,
|
||||
newLabel: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
labelTemplate: {
|
||||
attributes: {}
|
||||
},
|
||||
editingName: false,
|
||||
editingDescription: false,
|
||||
editingContent: false,
|
||||
newContent: '',
|
||||
newLabelWidth: null,
|
||||
newLabelHeight: null,
|
||||
newLabelDensity: null,
|
||||
newLabelUnit: null,
|
||||
previewContent: '',
|
||||
previewValid: false,
|
||||
skipSave: false,
|
||||
codeErrorMessage: '',
|
||||
cursorPos: 0
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
newContent() {
|
||||
this.showErrors();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
labelTemplate: {
|
||||
attributes: {}
|
||||
},
|
||||
editingName: false,
|
||||
editingDescription: false,
|
||||
editingContent: false,
|
||||
newContent: '',
|
||||
newLabelWidth: null,
|
||||
newLabelHeight: null,
|
||||
newLabelDensity: null,
|
||||
newLabelUnit: null,
|
||||
previewContent: '',
|
||||
previewValid: false,
|
||||
skipSave: false,
|
||||
codeErrorMessage: '',
|
||||
cursorPos: 0
|
||||
}
|
||||
previewValid() {
|
||||
this.showErrors();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasError() {
|
||||
return this.codeErrorMessage.length > 0;
|
||||
},
|
||||
watch: {
|
||||
newContent() {
|
||||
this.showErrors();
|
||||
},
|
||||
previewValid() {
|
||||
this.showErrors();
|
||||
}
|
||||
canManage() {
|
||||
return this.labelTemplate.attributes.urls.update && this.notFluics;
|
||||
},
|
||||
computed: {
|
||||
hasError() {
|
||||
return this.codeErrorMessage.length > 0
|
||||
},
|
||||
canManage() {
|
||||
return this.labelTemplate.attributes.urls.update && this.notFluics
|
||||
},
|
||||
notFluics() {
|
||||
return this.labelTemplate.attributes.type !== "FluicsLabelTemplate"
|
||||
}
|
||||
notFluics() {
|
||||
return this.labelTemplate.attributes.type !== 'FluicsLabelTemplate';
|
||||
}
|
||||
},
|
||||
components: { InlineEdit, InsertFieldDropdown, LabelPreview },
|
||||
created() {
|
||||
$.get(this.labelTemplateUrl, (result) => {
|
||||
this.labelTemplate = result.data;
|
||||
this.newContent = this.labelTemplate.attributes.content;
|
||||
this.previewContent = this.labelTemplate.attributes.content;
|
||||
this.newLabelWidth = this.labelTemplate.attributes.width_mm;
|
||||
this.newLabelHeight = this.labelTemplate.attributes.height_mm;
|
||||
this.newLabelDensity = this.labelTemplate.attributes.density;
|
||||
this.newLabelUnit = this.labelTemplate.attributes.unit;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
setNewHeight(val) {
|
||||
this.newLabelHeight = val;
|
||||
},
|
||||
components: {InlineEdit, InsertFieldDropdown, LabelPreview},
|
||||
created() {
|
||||
$.get(this.labelTemplateUrl, (result) => {
|
||||
this.labelTemplate = result.data
|
||||
this.newContent = this.labelTemplate.attributes.content
|
||||
this.previewContent = this.labelTemplate.attributes.content
|
||||
this.newLabelWidth = this.labelTemplate.attributes.width_mm
|
||||
this.newLabelHeight = this.labelTemplate.attributes.height_mm
|
||||
this.newLabelDensity = this.labelTemplate.attributes.density
|
||||
this.newLabelUnit = this.labelTemplate.attributes.unit
|
||||
})
|
||||
setNewWidth(val) {
|
||||
this.newLabelWidth = val;
|
||||
},
|
||||
methods: {
|
||||
setNewHeight(val) {
|
||||
this.newLabelHeight = val;
|
||||
},
|
||||
setNewWidth(val) {
|
||||
this.newLabelWidth = val;
|
||||
},
|
||||
setNewDensity(val) {
|
||||
this.newLabelDensity = val;
|
||||
},
|
||||
setNewUnit(val) {
|
||||
this.newLabelUnit = val;
|
||||
},
|
||||
enableContentEdit() {
|
||||
this.editingContent = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.contentInput.focus();
|
||||
const contentInput = this.$refs.contentInput;
|
||||
const contentLength = contentInput.value.length;
|
||||
contentInput.setSelectionRange(contentLength, contentLength);
|
||||
});
|
||||
},
|
||||
disableContentEdit() {
|
||||
this.editingContent = false;
|
||||
this.newContent = this.labelTemplate.attributes.content;
|
||||
this.previewContent = this.labelTemplate.attributes.content;
|
||||
},
|
||||
updateName(newName) {
|
||||
$.ajax({
|
||||
url: this.labelTemplate.attributes.urls.update,
|
||||
type: 'PATCH',
|
||||
data: {label_template: {name: newName}},
|
||||
success: (result) => {
|
||||
this.labelTemplate.attributes.name = result.data.attributes.name
|
||||
}
|
||||
});
|
||||
},
|
||||
updateDescription(newDescription) {
|
||||
$.ajax({
|
||||
url: this.labelTemplate.attributes.urls.update,
|
||||
type: 'PATCH',
|
||||
data: {label_template: {description: newDescription}},
|
||||
success: (result) => {
|
||||
this.labelTemplate.attributes.description = result.data.attributes.description
|
||||
}
|
||||
});
|
||||
},
|
||||
updateContent() {
|
||||
this.previewValid = true;
|
||||
|
||||
this.saveLabelDimensions();
|
||||
|
||||
if (!this.editingContent) return;
|
||||
|
||||
if (this.skipSave) {
|
||||
this.skipSave = false;
|
||||
return;
|
||||
setNewDensity(val) {
|
||||
this.newLabelDensity = val;
|
||||
},
|
||||
setNewUnit(val) {
|
||||
this.newLabelUnit = val;
|
||||
},
|
||||
enableContentEdit() {
|
||||
this.editingContent = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.contentInput.focus();
|
||||
const { contentInput } = this.$refs;
|
||||
const contentLength = contentInput.value.length;
|
||||
contentInput.setSelectionRange(contentLength, contentLength);
|
||||
});
|
||||
},
|
||||
disableContentEdit() {
|
||||
this.editingContent = false;
|
||||
this.newContent = this.labelTemplate.attributes.content;
|
||||
this.previewContent = this.labelTemplate.attributes.content;
|
||||
},
|
||||
updateName(newName) {
|
||||
$.ajax({
|
||||
url: this.labelTemplate.attributes.urls.update,
|
||||
type: 'PATCH',
|
||||
data: { label_template: { name: newName } },
|
||||
success: (result) => {
|
||||
this.labelTemplate.attributes.name = result.data.attributes.name;
|
||||
}
|
||||
});
|
||||
},
|
||||
updateDescription(newDescription) {
|
||||
$.ajax({
|
||||
url: this.labelTemplate.attributes.urls.update,
|
||||
type: 'PATCH',
|
||||
data: { label_template: { description: newDescription } },
|
||||
success: (result) => {
|
||||
this.labelTemplate.attributes.description = result.data.attributes.description;
|
||||
}
|
||||
});
|
||||
},
|
||||
updateContent() {
|
||||
this.previewValid = true;
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.hasError) return;
|
||||
this.saveLabelDimensions();
|
||||
|
||||
$.ajax({
|
||||
url: this.labelTemplate.attributes.urls.update,
|
||||
type: 'PATCH',
|
||||
data: {label_template: {
|
||||
content: this.newContent,
|
||||
}},
|
||||
success: (result) => {
|
||||
this.labelTemplate.attributes.content = result.data.attributes.content;
|
||||
this.editingContent = false;
|
||||
if (!this.editingContent) return;
|
||||
|
||||
if (this.skipSave) {
|
||||
this.skipSave = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.hasError) return;
|
||||
|
||||
$.ajax({
|
||||
url: this.labelTemplate.attributes.urls.update,
|
||||
type: 'PATCH',
|
||||
data: {
|
||||
label_template: {
|
||||
content: this.newContent
|
||||
}
|
||||
});
|
||||
},
|
||||
success: (result) => {
|
||||
this.labelTemplate.attributes.content = result.data.attributes.content;
|
||||
this.editingContent = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
saveLabelDimensions() {
|
||||
if (this.newLabelWidth == this.labelTemplate.attributes.width_mm &&
|
||||
this.newLabelHeight == this.labelTemplate.attributes.height_mm &&
|
||||
this.newLabelDensity == this.labelTemplate.attributes.density &&
|
||||
this.newLabelUnit == this.labelTemplate.attributes.unit) {
|
||||
return
|
||||
}
|
||||
});
|
||||
},
|
||||
saveLabelDimensions() {
|
||||
if (this.newLabelWidth == this.labelTemplate.attributes.width_mm
|
||||
&& this.newLabelHeight == this.labelTemplate.attributes.height_mm
|
||||
&& this.newLabelDensity == this.labelTemplate.attributes.density
|
||||
&& this.newLabelUnit == this.labelTemplate.attributes.unit) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: this.labelTemplate.attributes.urls.update,
|
||||
type: 'PATCH',
|
||||
data: {label_template: {
|
||||
$.ajax({
|
||||
url: this.labelTemplate.attributes.urls.update,
|
||||
type: 'PATCH',
|
||||
data: {
|
||||
label_template: {
|
||||
width_mm: this.newLabelWidth,
|
||||
height_mm: this.newLabelHeight,
|
||||
density: this.newLabelDensity,
|
||||
unit: this.newLabelUnit
|
||||
}},
|
||||
success: (result) => {
|
||||
this.labelTemplate.attributes.width_mm = result.data.attributes.width_mm;
|
||||
this.labelTemplate.attributes.height_mm = result.data.attributes.height_mm;
|
||||
this.labelTemplate.attributes.density = result.data.attributes.density;
|
||||
this.labelTemplate.attributes.unit = result.data.attributes.unit;
|
||||
}
|
||||
});
|
||||
},
|
||||
generatePreview(skipSave = false) {
|
||||
this.skipSave = skipSave;
|
||||
if (!skipSave && this.previewContent === this.newContent && this.previewValid) {
|
||||
this.updateContent();
|
||||
} else {
|
||||
this.previewContent = this.newContent;
|
||||
},
|
||||
success: (result) => {
|
||||
this.labelTemplate.attributes.width_mm = result.data.attributes.width_mm;
|
||||
this.labelTemplate.attributes.height_mm = result.data.attributes.height_mm;
|
||||
this.labelTemplate.attributes.density = result.data.attributes.density;
|
||||
this.labelTemplate.attributes.unit = result.data.attributes.unit;
|
||||
}
|
||||
});
|
||||
},
|
||||
generatePreview(skipSave = false) {
|
||||
this.skipSave = skipSave;
|
||||
if (!skipSave && this.previewContent === this.newContent && this.previewValid) {
|
||||
this.updateContent();
|
||||
} else {
|
||||
this.previewContent = this.newContent;
|
||||
}
|
||||
},
|
||||
invalidPreview() {
|
||||
this.previewValid = false;
|
||||
this.skipSave = false;
|
||||
},
|
||||
saveCursorPosition() {
|
||||
this.cursorPos = $(this.$refs.contentInput).prop('selectionStart');
|
||||
},
|
||||
insertTag(tag) {
|
||||
this.enableContentEdit();
|
||||
const textBefore = this.newContent.substring(0, this.cursorPos);
|
||||
const textAfter = this.newContent.substring(this.cursorPos, this.newContent.length);
|
||||
this.newContent = textBefore + tag + textAfter;
|
||||
this.cursorPos += tag.length;
|
||||
|
||||
},
|
||||
invalidPreview() {
|
||||
this.previewValid = false;
|
||||
this.skipSave = false;
|
||||
},
|
||||
saveCursorPosition() {
|
||||
this.cursorPos = $(this.$refs.contentInput).prop('selectionStart');
|
||||
},
|
||||
insertTag(tag) {
|
||||
this.enableContentEdit();
|
||||
let textBefore = this.newContent.substring(0, this.cursorPos);
|
||||
let textAfter = this.newContent.substring(this.cursorPos, this.newContent.length);
|
||||
this.newContent = textBefore + tag + textAfter;
|
||||
this.cursorPos = this.cursorPos + tag.length;
|
||||
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs.contentInput).prop('selectionStart', this.cursorPos);
|
||||
$(this.$refs.contentInput).prop('selectionEnd', this.cursorPos);
|
||||
});
|
||||
},
|
||||
showErrors() {
|
||||
if (this.editingContent) {
|
||||
if (this.newContent.length === 0) {
|
||||
this.codeErrorMessage = this.i18n.t('label_templates.show.code_errors.empty')
|
||||
} else if (this.newContent.length > 10000) {
|
||||
this.codeErrorMessage = this.i18n.t('label_templates.show.code_errors.too_long')
|
||||
} else if (!this.previewValid) {
|
||||
this.codeErrorMessage = this.i18n.t('label_templates.show.code_errors.invalid')
|
||||
} else {
|
||||
this.codeErrorMessage = ''
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs.contentInput).prop('selectionStart', this.cursorPos);
|
||||
$(this.$refs.contentInput).prop('selectionEnd', this.cursorPos);
|
||||
});
|
||||
},
|
||||
showErrors() {
|
||||
if (this.editingContent) {
|
||||
if (this.newContent.length === 0) {
|
||||
this.codeErrorMessage = this.i18n.t('label_templates.show.code_errors.empty');
|
||||
} else if (this.newContent.length > 10000) {
|
||||
this.codeErrorMessage = this.i18n.t('label_templates.show.code_errors.too_long');
|
||||
} else if (!this.previewValid) {
|
||||
this.codeErrorMessage = this.i18n.t('label_templates.show.code_errors.invalid');
|
||||
} else {
|
||||
this.codeErrorMessage = ''
|
||||
this.codeErrorMessage = '';
|
||||
}
|
||||
} else {
|
||||
this.codeErrorMessage = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -82,30 +82,30 @@ export default {
|
|||
props: {
|
||||
labelTemplate: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fields: {
|
||||
default: [],
|
||||
common: [],
|
||||
repositories: [],
|
||||
repositories: []
|
||||
},
|
||||
openLogoModal: false,
|
||||
logoDimension: null,
|
||||
searchValue: '',
|
||||
searchValue: ''
|
||||
};
|
||||
},
|
||||
components: { LogoInsertModal },
|
||||
computed: {
|
||||
tooltipTemplate() {
|
||||
return `<div class="tooltip" role="tooltip">
|
||||
<div class="tooltip-arrow"></div>
|
||||
<div class="tooltip-body">
|
||||
<div class="tooltip-inner"></div>
|
||||
</div>
|
||||
</div>`;
|
||||
<div class="tooltip-arrow"></div>
|
||||
<div class="tooltip-body">
|
||||
<div class="tooltip-inner"></div>
|
||||
</div>
|
||||
</div>`;
|
||||
},
|
||||
filteredFields() {
|
||||
this.$nextTick(() => {
|
||||
|
@ -113,28 +113,24 @@ export default {
|
|||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
|
||||
if (this.searchValue.length === 0) {
|
||||
if (this.searchValue.length == 0) {
|
||||
return this.fields;
|
||||
}
|
||||
return {
|
||||
default: this.filterArray(this.fields.default, 'key'),
|
||||
common: this.filterArray(this.fields.common, 'key'),
|
||||
repositories: this.filterArray(this.fields.repositories, 'repository_name').map((repo) => (
|
||||
{ ...repo, tags: this.filterArray(repo.tags, 'key') }
|
||||
)),
|
||||
repositories: this.filterArray(this.fields.repositories, 'repository_name').map((repo) => ({ ...repo, tags: this.filterArray(repo.tags, 'key') }))
|
||||
};
|
||||
},
|
||||
noResults() {
|
||||
const defaultField = this.filteredFields.default;
|
||||
return defaultField.concat(this.filteredFields.common, this.filteredFields.repositories).length === 0;
|
||||
},
|
||||
return this.filteredFields.default.concat(this.filteredFields.common, this.filteredFields.repositories).length === 0;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
$.get(this.labelTemplate.attributes.urls.fields, (result) => {
|
||||
result.default.map((value) => {
|
||||
const newValue = value;
|
||||
newValue.key = this.i18n.t(`label_templates.default_columns.${value.key}`);
|
||||
return newValue;
|
||||
value.key = this.i18n.t(`label_templates.default_columns.${value.key}`);
|
||||
return value;
|
||||
});
|
||||
|
||||
this.fields = result;
|
||||
|
@ -153,7 +149,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
insertTag(field) {
|
||||
if (field.id === 'logo') {
|
||||
if (field.id == 'logo') {
|
||||
this.logoDimension = field.dimension;
|
||||
this.openLogoModal = true;
|
||||
return;
|
||||
|
@ -163,9 +159,9 @@ export default {
|
|||
filterArray(array, key) {
|
||||
return array.filter((field) => (
|
||||
field[key].toLowerCase().indexOf(this.searchValue.toLowerCase()) !== -1
|
||||
|| field.tags
|
||||
|| field.tags
|
||||
));
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -61,24 +61,24 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import BreadcrumbsDropdown from "./breadcrumbs_dropdown.vue";
|
||||
import BreadcrumbsDropdown from './breadcrumbs_dropdown.vue';
|
||||
|
||||
const State = Object.freeze({
|
||||
INITIAL: "initial",
|
||||
EXPENDED: "expended",
|
||||
SHORTENED: "shortened",
|
||||
SHORTENED_COLLAPSED: "shortened_collapsed"
|
||||
INITIAL: 'initial',
|
||||
EXPENDED: 'expended',
|
||||
SHORTENED: 'shortened',
|
||||
SHORTENED_COLLAPSED: 'shortened_collapsed'
|
||||
});
|
||||
const dropdownWidth = 60;
|
||||
export default {
|
||||
name: "Breadcrumbs",
|
||||
name: 'Breadcrumbs',
|
||||
props: {
|
||||
breadcrumbsItems: String,
|
||||
delimiterUrl: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dropdownWidth: dropdownWidth,
|
||||
dropdownWidth,
|
||||
State,
|
||||
state: State.INITIAL,
|
||||
items: [],
|
||||
|
@ -136,10 +136,9 @@ export default {
|
|||
} else if (this.state === this.State.SHORTENED) {
|
||||
if (width < scrollWidth) {
|
||||
this.state = this.State.SHORTENED_COLLAPSED;
|
||||
let visibleWidth =
|
||||
this.dropdownWidth +
|
||||
this.$refs.firstItem.offsetWidth +
|
||||
this.$refs.lastItem.offsetWidth;
|
||||
let visibleWidth = this.dropdownWidth
|
||||
+ this.$refs.firstItem.offsetWidth
|
||||
+ this.$refs.lastItem.offsetWidth;
|
||||
let index = this.middleItems.length - 1;
|
||||
|
||||
while (visibleWidth <= width && index >= 0) {
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
import { PerfectScrollbar } from 'vue3-perfect-scrollbar';
|
||||
|
||||
export default {
|
||||
name: "BreadcrumbsDropdown",
|
||||
name: 'BreadcrumbsDropdown',
|
||||
props: {
|
||||
items: Array,
|
||||
delimiterUrl: String
|
||||
|
|
|
@ -38,12 +38,12 @@
|
|||
|
||||
<script>
|
||||
|
||||
import NavigatorItem from './navigator_item.vue'
|
||||
import Vue3DraggableResizable from 'vue3-draggable-resizable';
|
||||
import NavigatorItem from './navigator_item.vue';
|
||||
import axios from '../../packs/custom_axios.js';
|
||||
import Vue3DraggableResizable from 'vue3-draggable-resizable'
|
||||
|
||||
export default {
|
||||
name : 'NavigatorContainer',
|
||||
name: 'NavigatorContainer',
|
||||
components: {
|
||||
NavigatorItem,
|
||||
Vue3DraggableResizable
|
||||
|
@ -58,7 +58,7 @@ export default {
|
|||
currentItemId: null,
|
||||
archived: null,
|
||||
width: null
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
reloadCurrentLevel: Boolean,
|
||||
|
@ -90,13 +90,13 @@ export default {
|
|||
});
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.vueResizable.style.width = this.getNavigatorWidth()
|
||||
this.$refs.vueResizable.style.width = this.getNavigatorWidth();
|
||||
},
|
||||
watch: {
|
||||
archived() {
|
||||
this.loadTree();
|
||||
},
|
||||
reloadCurrentLevel: function() {
|
||||
reloadCurrentLevel() {
|
||||
if (this.reloadCurrentLevel && (
|
||||
this.currentItemId?.length === 0
|
||||
|| this.menuItems.filter((item) => item.id === this.currentItemId)
|
||||
|
@ -123,10 +123,10 @@ export default {
|
|||
console.error('An error occurred while fetching the data', error);
|
||||
}
|
||||
},
|
||||
onScrollY({target}) {
|
||||
onScrollY({ target }) {
|
||||
this.navigatorYScroll = target.scrollTop;
|
||||
},
|
||||
onScrollX({target}) {
|
||||
onScrollX({ target }) {
|
||||
this.navigatorXScroll = target.scrollLeft;
|
||||
},
|
||||
getNavigatorWidth() {
|
||||
|
@ -135,7 +135,7 @@ export default {
|
|||
},
|
||||
onResizeMove(event) {
|
||||
if (event.w > 400) event.w = 400;
|
||||
document.documentElement.style.setProperty('--navigator-navigation-width', event.w + 'px');
|
||||
document.documentElement.style.setProperty('--navigator-navigation-width', `${event.w}px`);
|
||||
},
|
||||
onResizeStart() {
|
||||
document.body.style.cursor = 'url(/images/icon_small/Resize.svg) 0 0, auto';
|
||||
|
@ -150,7 +150,7 @@ export default {
|
|||
document.body.style.cursor = 'default';
|
||||
$('.sci--layout-navigation-navigator').removeClass('!transition-none');
|
||||
$('.sci--layout').removeClass('!transition-none');
|
||||
this.changeNavigatorState(event.w)
|
||||
this.changeNavigatorState(event.w);
|
||||
},
|
||||
async changeNavigatorState(newWidth) {
|
||||
try {
|
||||
|
@ -161,6 +161,6 @@ export default {
|
|||
console.error('An error occurred while sending the request', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -54,7 +54,7 @@ export default {
|
|||
reloadCurrentLevel: Boolean,
|
||||
reloadChildrenLevel: Boolean
|
||||
},
|
||||
data: function() {
|
||||
data() {
|
||||
return {
|
||||
childrenExpanded: false,
|
||||
childrenLoaded: false,
|
||||
|
@ -62,13 +62,13 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
hasChildren: function() {
|
||||
hasChildren() {
|
||||
if (this.item.disabled) return false;
|
||||
if (this.item.has_children) return true;
|
||||
if (this.children && this.children.length > 0) return true;
|
||||
return false
|
||||
return false;
|
||||
},
|
||||
sortedMenuItems: function() {
|
||||
sortedMenuItems() {
|
||||
if (!this.children) return [];
|
||||
|
||||
return this.children.sort((a, b) => {
|
||||
|
@ -81,44 +81,43 @@ export default {
|
|||
return 0;
|
||||
});
|
||||
},
|
||||
activeItem: function() {
|
||||
activeItem() {
|
||||
return this.item.id == this.currentItemId;
|
||||
},
|
||||
itemIcon: function() {
|
||||
switch(this.item.type) {
|
||||
itemIcon() {
|
||||
switch (this.item.type) {
|
||||
case 'folder':
|
||||
return 'sn-icon mini sn-icon-mini-folder-left';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
itemToolTip: function() {
|
||||
if (this.item.type == 'folder')
|
||||
return this.item.name;
|
||||
return this.i18n.t('sidebar.elements_tooltip', { type: this.i18n.t(`activerecord.models.${this.item.type}`), name: this.item.name});
|
||||
itemToolTip() {
|
||||
if (this.item.type == 'folder') return this.item.name;
|
||||
return this.i18n.t('sidebar.elements_tooltip', { type: this.i18n.t(`activerecord.models.${this.item.type}`), name: this.item.name });
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
created() {
|
||||
if (this.item.children) this.children = this.item.children;
|
||||
},
|
||||
mounted: function() {
|
||||
mounted() {
|
||||
this.selectItem();
|
||||
},
|
||||
watch: {
|
||||
currentItemId: function() {
|
||||
currentItemId() {
|
||||
this.selectItem();
|
||||
},
|
||||
reloadChildrenLevel: function() {
|
||||
reloadChildrenLevel() {
|
||||
if (this.reloadChildrenLevel && this.item.id == this.currentItemId) {
|
||||
this.loadChildren();
|
||||
}
|
||||
},
|
||||
reloadCurrentLevel: function() {
|
||||
reloadCurrentLevel() {
|
||||
if (this.reloadCurrentLevel && this.children.find((item) => item.id == this.currentItemId)) {
|
||||
this.loadChildren();
|
||||
}
|
||||
},
|
||||
children: function() {
|
||||
children() {
|
||||
if (this.children && this.children.length > 0) {
|
||||
this.childrenExpanded = true;
|
||||
} else if (this.childrenLoaded) {
|
||||
|
@ -127,21 +126,21 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
toggleChildren: function() {
|
||||
toggleChildren() {
|
||||
this.childrenExpanded = !this.childrenExpanded;
|
||||
if (this.childrenExpanded) this.loadChildren();
|
||||
},
|
||||
loadChildren: function() {
|
||||
$.get(this.item.children_url, {archived: this.archived}, (data) => {
|
||||
loadChildren() {
|
||||
$.get(this.item.children_url, { archived: this.archived }, (data) => {
|
||||
this.children = data.items;
|
||||
this.childrenLoaded = true;
|
||||
});
|
||||
},
|
||||
treeExpand: function() {
|
||||
treeExpand() {
|
||||
this.childrenExpanded = true;
|
||||
this.$emit('item:expand');
|
||||
},
|
||||
selectItem: function() {
|
||||
selectItem() {
|
||||
if (this.activeItem && !this.childrenExpanded) {
|
||||
this.$emit('item:expand');
|
||||
if (this.hasChildren) {
|
||||
|
@ -150,6 +149,6 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -26,7 +26,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
icon() {
|
||||
switch(this.notification.attributes.type_of) {
|
||||
switch (this.notification.attributes.type_of) {
|
||||
case 'deliver':
|
||||
return 'fas fa-truck';
|
||||
case 'assignment':
|
||||
|
@ -38,5 +38,5 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
<script>
|
||||
|
||||
import NotificationItem from './notification_item.vue'
|
||||
import NotificationItem from './notification_item.vue';
|
||||
import axios from '../../../packs/custom_axios.js';
|
||||
|
||||
export default {
|
||||
|
@ -43,20 +43,20 @@ export default {
|
|||
nextPageUrl: null,
|
||||
scrollBar: null,
|
||||
loadingPage: false
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.nextPageUrl = this.notificationsUrl;
|
||||
this.loadNotifications();
|
||||
},
|
||||
mounted() {
|
||||
let container = this.$refs.scrollContainer.$el
|
||||
const container = this.$refs.scrollContainer.$el;
|
||||
|
||||
container.addEventListener('ps-scroll-y', (e) => {
|
||||
if (e.target.scrollTop + e.target.clientHeight >= e.target.scrollHeight - 20) {
|
||||
this.loadNotifications();
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
document.body.style.overflow = 'auto';
|
||||
|
@ -66,10 +66,10 @@ export default {
|
|||
this.loadNotifications();
|
||||
},
|
||||
todayNotifications() {
|
||||
return this.notifications.filter(n => n.attributes.today);
|
||||
return this.notifications.filter((n) => n.attributes.today);
|
||||
},
|
||||
olderNotifications() {
|
||||
return this.notifications.filter(n => !n.attributes.today);
|
||||
return this.notifications.filter((n) => !n.attributes.today);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -79,18 +79,16 @@ export default {
|
|||
this.loadingPage = true;
|
||||
|
||||
axios.get(this.nextPageUrl)
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
this.notifications = this.notifications.concat(response.data.data);
|
||||
this.nextPageUrl = response.data.links.next;
|
||||
this.loadingPage = false;
|
||||
this.$emit('update:unseenNotificationsCount');
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
this.loadingPage = false;
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -58,123 +58,122 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import NotificationsFlyout from './notifications/notifications_flyout.vue';
|
||||
import DropdownSelector from '../shared/legacy/dropdown_selector.vue';
|
||||
import SelectDropdown from "../shared/select_dropdown.vue";
|
||||
import MenuDropdown from '../shared/menu_dropdown.vue';
|
||||
import NotificationsFlyout from './notifications/notifications_flyout.vue';
|
||||
import DropdownSelector from '../shared/legacy/dropdown_selector.vue';
|
||||
import Select from '../shared/select.vue';
|
||||
import MenuDropdown from '../shared/menu_dropdown.vue';
|
||||
|
||||
export default {
|
||||
name: 'TopMenuContainer',
|
||||
components: {
|
||||
DropdownSelector,
|
||||
NotificationsFlyout,
|
||||
MenuDropdown,
|
||||
SelectDropdown
|
||||
},
|
||||
props: {
|
||||
url: String,
|
||||
notificationsUrl: String,
|
||||
unseenNotificationsUrl: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rootUrl: null,
|
||||
teamSwitchUrl: null,
|
||||
currentTeam: null,
|
||||
teams: null,
|
||||
searchUrl: null,
|
||||
user: null,
|
||||
helpMenu: null,
|
||||
settingsMenu: null,
|
||||
userMenu: null,
|
||||
notificationsOpened: false,
|
||||
unseenNotificationsCount: 0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData();
|
||||
export default {
|
||||
name: 'TopMenuContainer',
|
||||
components: {
|
||||
DropdownSelector,
|
||||
NotificationsFlyout,
|
||||
Select,
|
||||
MenuDropdown
|
||||
},
|
||||
props: {
|
||||
url: String,
|
||||
notificationsUrl: String,
|
||||
unseenNotificationsUrl: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rootUrl: null,
|
||||
teamSwitchUrl: null,
|
||||
currentTeam: null,
|
||||
teams: null,
|
||||
searchUrl: null,
|
||||
user: null,
|
||||
helpMenu: null,
|
||||
settingsMenu: null,
|
||||
userMenu: null,
|
||||
notificationsOpened: false,
|
||||
unseenNotificationsCount: 0
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetchData();
|
||||
this.checkUnseenNotifications();
|
||||
|
||||
$(document).on('turbolinks:load', () => {
|
||||
this.notificationsOpened = false;
|
||||
this.checkUnseenNotifications();
|
||||
this.refreshCurrentTeam();
|
||||
});
|
||||
|
||||
$(document).on('turbolinks:load', () => {
|
||||
this.notificationsOpened = false;
|
||||
this.checkUnseenNotifications();
|
||||
this.refreshCurrentTeam();
|
||||
})
|
||||
|
||||
// Track name update in user profile settings
|
||||
$(document).on('inlineEditing::updated', '.inline-editing-container[data-field-to-update="full_name"]', this.fetchData);
|
||||
},
|
||||
beforeUnmount: function(){
|
||||
clearTimeout(this.unseenNotificationsTimeout);
|
||||
},
|
||||
computed: {
|
||||
settingsMenuItems() {
|
||||
return this.settingsMenu.map((item) => {
|
||||
return { text: item.name, url: item.url } }
|
||||
).concat(
|
||||
{
|
||||
text: this.i18n.t('left_menu_bar.support_links.core_version'),
|
||||
modalTarget: '#aboutModal', url: ''
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
notificationsOpened(newVal) {
|
||||
if (newVal === true) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else if (newVal === false) {
|
||||
document.body.style.overflow = 'scroll';
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
$.get(this.url, (result) => {
|
||||
this.rootUrl = result.root_url;
|
||||
this.teamSwitchUrl = result.team_switch_url;
|
||||
this.currentTeam = result.current_team;
|
||||
this.teams = result.teams;
|
||||
this.searchUrl = result.search_url;
|
||||
this.helpMenu = result.help_menu;
|
||||
this.settingsMenu = result.settings_menu;
|
||||
this.userMenu = result.user_menu;
|
||||
this.user = result.user;
|
||||
})
|
||||
},
|
||||
switchTeam(team) {
|
||||
if (this.currentTeam == team) return;
|
||||
|
||||
let newTeam = this.teams.find(e => e[0] == team);
|
||||
|
||||
if (!newTeam) return;
|
||||
|
||||
$.post(this.teamSwitchUrl, {team_id: team}, (result) => {
|
||||
this.currentTeam = result.current_team
|
||||
dropdownSelector.selectValues('#sciNavigationTeamSelector', this.currentTeam);
|
||||
$('body').attr('data-current-team-id', this.currentTeam);
|
||||
window.open(this.rootUrl, '_self')
|
||||
}).fail((msg) => {
|
||||
HelperModule.flashAlertMsg(msg.responseJSON.message, 'danger');
|
||||
});
|
||||
},
|
||||
searchValue(e) {
|
||||
window.open(`${this.searchUrl}?q=${e.target.value}`, '_self')
|
||||
},
|
||||
checkUnseenNotifications() {
|
||||
clearTimeout(this.unseenNotificationsTimeout);
|
||||
$.get(this.unseenNotificationsUrl, (result) => {
|
||||
this.unseenNotificationsCount = result.unseen;
|
||||
this.unseenNotificationsTimeout = setTimeout(this.checkUnseenNotifications, 30000);
|
||||
})
|
||||
},
|
||||
refreshCurrentTeam() {
|
||||
let newTeam = parseInt($('body').attr('data-current-team-id'));
|
||||
if (newTeam !== this.currentTeam) {
|
||||
this.currentTeam = newTeam;
|
||||
dropdownSelector.selectValues('#sciNavigationTeamSelector', this.currentTeam);
|
||||
// Track name update in user profile settings
|
||||
$(document).on('inlineEditing::updated', '.inline-editing-container[data-field-to-update="full_name"]', this.fetchData);
|
||||
},
|
||||
beforeUnmount() {
|
||||
clearTimeout(this.unseenNotificationsTimeout);
|
||||
},
|
||||
computed: {
|
||||
settingsMenuItems() {
|
||||
return this.settingsMenu.map((item) => ({ text: item.name, url: item.url })).concat(
|
||||
{
|
||||
text: this.i18n.t('left_menu_bar.support_links.core_version'),
|
||||
modalTarget: '#aboutModal',
|
||||
url: ''
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
notificationsOpened(newVal) {
|
||||
if (newVal === true) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else if (newVal === false) {
|
||||
document.body.style.overflow = 'scroll';
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
$.get(this.url, (result) => {
|
||||
this.rootUrl = result.root_url;
|
||||
this.teamSwitchUrl = result.team_switch_url;
|
||||
this.currentTeam = result.current_team;
|
||||
this.teams = result.teams;
|
||||
this.searchUrl = result.search_url;
|
||||
this.helpMenu = result.help_menu;
|
||||
this.settingsMenu = result.settings_menu;
|
||||
this.userMenu = result.user_menu;
|
||||
this.user = result.user;
|
||||
});
|
||||
},
|
||||
switchTeam(team) {
|
||||
if (this.currentTeam == team) return;
|
||||
|
||||
const newTeam = this.teams.find((e) => e[0] == team);
|
||||
|
||||
if (!newTeam) return;
|
||||
|
||||
$.post(this.teamSwitchUrl, { team_id: team }, (result) => {
|
||||
this.currentTeam = result.current_team;
|
||||
dropdownSelector.selectValues('#sciNavigationTeamSelector', this.currentTeam);
|
||||
$('body').attr('data-current-team-id', this.currentTeam);
|
||||
window.open(this.rootUrl, '_self');
|
||||
}).fail((msg) => {
|
||||
HelperModule.flashAlertMsg(msg.responseJSON.message, 'danger');
|
||||
});
|
||||
},
|
||||
searchValue(e) {
|
||||
window.open(`${this.searchUrl}?q=${e.target.value}`, '_self');
|
||||
},
|
||||
checkUnseenNotifications() {
|
||||
clearTimeout(this.unseenNotificationsTimeout);
|
||||
$.get(this.unseenNotificationsUrl, (result) => {
|
||||
this.unseenNotificationsCount = result.unseen;
|
||||
this.unseenNotificationsTimeout = setTimeout(this.checkUnseenNotifications, 30000);
|
||||
});
|
||||
},
|
||||
refreshCurrentTeam() {
|
||||
const newTeam = parseInt($('body').attr('data-current-team-id'));
|
||||
if (newTeam !== this.currentTeam) {
|
||||
this.currentTeam = newTeam;
|
||||
dropdownSelector.selectValues('#sciNavigationTeamSelector', this.currentTeam);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -29,135 +29,135 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import { blobToBase64 } from '../shared/blobToBase64.js'
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import { blobToBase64 } from '../shared/blobToBase64.js';
|
||||
|
||||
export default {
|
||||
name: 'OpenVectorEditor',
|
||||
props: {
|
||||
fileUrl: { type: String },
|
||||
fileName: { type: String },
|
||||
updateUrl: { type: String },
|
||||
canEditFile: { type: String },
|
||||
oveWarning: { type: String }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
sequenceName: null,
|
||||
closeAfterSave: false,
|
||||
readOnly: this.canEditFile !== 'true'
|
||||
export default {
|
||||
name: 'OpenVectorEditor',
|
||||
props: {
|
||||
fileUrl: { type: String },
|
||||
fileName: { type: String },
|
||||
updateUrl: { type: String },
|
||||
canEditFile: { type: String },
|
||||
oveWarning: { type: String }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
sequenceName: null,
|
||||
closeAfterSave: false,
|
||||
readOnly: this.canEditFile !== 'true'
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let editorConfig = {
|
||||
onSave: this.saveFile,
|
||||
generatePng: true,
|
||||
showMenuBar: true,
|
||||
alwaysAllowSave: true,
|
||||
menuFilter: this.menuFilter,
|
||||
beforeReadOnlyChange: this.readOnlyHandler,
|
||||
showCircularity: true,
|
||||
ToolBarProps: {
|
||||
toolList: [
|
||||
'saveTool',
|
||||
'downloadTool',
|
||||
{ name: 'importTool', tooltip: I18n.t('open_vector_editor.editor.tooltips.importTool'), disabled: this.readOnly },
|
||||
'undoTool',
|
||||
'redoTool',
|
||||
'cutsiteTool',
|
||||
'featureTool',
|
||||
'partTool',
|
||||
'oligoTool',
|
||||
'orfTool',
|
||||
// Hide allignment tool
|
||||
// 'alignmentTool',
|
||||
'editTool',
|
||||
'findTool',
|
||||
'visibilityTool'
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let editorConfig = {
|
||||
onSave: this.saveFile,
|
||||
generatePng: true,
|
||||
showMenuBar: true,
|
||||
alwaysAllowSave: true,
|
||||
menuFilter: this.menuFilter,
|
||||
beforeReadOnlyChange: this.readOnlyHandler,
|
||||
showCircularity: true,
|
||||
};
|
||||
|
||||
if (this.readOnly) {
|
||||
editorConfig = {
|
||||
...editorConfig,
|
||||
showReadOnly: false,
|
||||
ToolBarProps: {
|
||||
toolList: [
|
||||
'saveTool',
|
||||
'downloadTool',
|
||||
{ name: 'importTool', tooltip: I18n.t('open_vector_editor.editor.tooltips.importTool'), disabled: this.readOnly },
|
||||
'undoTool',
|
||||
'redoTool',
|
||||
'cutsiteTool',
|
||||
'featureTool',
|
||||
'partTool',
|
||||
'oligoTool',
|
||||
'orfTool',
|
||||
// Hide allignment tool
|
||||
// 'alignmentTool',
|
||||
'editTool',
|
||||
'findTool',
|
||||
'visibilityTool'
|
||||
]
|
||||
toolList: []
|
||||
}
|
||||
}
|
||||
|
||||
if (this.readOnly) {
|
||||
editorConfig = {
|
||||
...editorConfig,
|
||||
showReadOnly: false,
|
||||
ToolBarProps: {
|
||||
toolList: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.editor = window.createVectorEditor(this.$refs.container, editorConfig);
|
||||
this.sequenceName = this.fileName;
|
||||
|
||||
if (this.fileUrl) {
|
||||
this.loadFile();
|
||||
} else {
|
||||
this.editor.updateEditor(
|
||||
{
|
||||
sequenceData: { circular: true, name: this.sequenceName },
|
||||
readOnly: this.readOnly
|
||||
}
|
||||
);
|
||||
}
|
||||
this.$nextTick(this.attachEditorHelpCallback);
|
||||
},
|
||||
methods: {
|
||||
loadFile() {
|
||||
fetch(this.fileUrl).then((response) => response.json()).then(
|
||||
(json) => this.editor.updateEditor(
|
||||
{ sequenceData: json, readOnly: this.readOnly }
|
||||
)
|
||||
);
|
||||
},
|
||||
saveAndClose() {
|
||||
this.closeAfterSave = true;
|
||||
document.querySelector('[data-test=saveTool]').click()
|
||||
},
|
||||
saveFile(opts, sequenceDataToSave) {
|
||||
if (this.readOnly) return;
|
||||
blobToBase64(opts.pngFile).then((base64image) => {
|
||||
(this.fileUrl ? axios.patch : axios.post)(
|
||||
this.updateUrl,
|
||||
{
|
||||
sequence_data: sequenceDataToSave,
|
||||
sequence_name: this.sequenceName || this.i18n.t('open_vector_editor.default_sequence_name'),
|
||||
base64_image: base64image
|
||||
}
|
||||
).then(() => {
|
||||
parent.document.getElementById('iFrameModal').dispatchEvent(new Event('sequenceSaved'));
|
||||
if (this.closeAfterSave) this.closeModal();
|
||||
});
|
||||
});
|
||||
},
|
||||
closeModal() {
|
||||
if (parent !== window) {
|
||||
parent.document.getElementById('iFrameModal').dispatchEvent(new Event('hide'));
|
||||
}
|
||||
},
|
||||
menuFilter(menus) {
|
||||
return menus.map(menu => {
|
||||
if(menu.text !== 'Help') return menu;
|
||||
const menuOverride = {
|
||||
...menu,
|
||||
submenu: menu.submenu.filter(item => item.cmd !== 'versionNumber')
|
||||
}
|
||||
|
||||
return menuOverride;
|
||||
});
|
||||
},
|
||||
readOnlyHandler(val) { this.readOnly = val; return true },
|
||||
// override click event for github issue link in editor help -> about modal
|
||||
attachEditorHelpCallback() {
|
||||
$(window.document).on('click', '.bp3-dialog-container .bp3-dialog .bp3-alert-contents a', function(event) {
|
||||
event.preventDefault();
|
||||
$('.bp3-dialog .bp3-dialog-footer button[type*=submit]').trigger('click');
|
||||
window.open($(event.target).attr('href'), '_blank');
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
this.editor = window.createVectorEditor(this.$refs.container, editorConfig);
|
||||
this.sequenceName = this.fileName;
|
||||
|
||||
if (this.fileUrl) {
|
||||
this.loadFile();
|
||||
} else {
|
||||
this.editor.updateEditor(
|
||||
{
|
||||
sequenceData: { circular: true, name: this.sequenceName },
|
||||
readOnly: this.readOnly
|
||||
}
|
||||
);
|
||||
}
|
||||
this.$nextTick(this.attachEditorHelpCallback);
|
||||
},
|
||||
methods: {
|
||||
loadFile() {
|
||||
fetch(this.fileUrl).then((response) => response.json()).then(
|
||||
(json) => this.editor.updateEditor(
|
||||
{ sequenceData: json, readOnly: this.readOnly }
|
||||
)
|
||||
);
|
||||
},
|
||||
saveAndClose() {
|
||||
this.closeAfterSave = true;
|
||||
document.querySelector('[data-test=saveTool]').click();
|
||||
},
|
||||
saveFile(opts, sequenceDataToSave) {
|
||||
if (this.readOnly) return;
|
||||
blobToBase64(opts.pngFile).then((base64image) => {
|
||||
(this.fileUrl ? axios.patch : axios.post)(
|
||||
this.updateUrl,
|
||||
{
|
||||
sequence_data: sequenceDataToSave,
|
||||
sequence_name: this.sequenceName || this.i18n.t('open_vector_editor.default_sequence_name'),
|
||||
base64_image: base64image
|
||||
}
|
||||
).then(() => {
|
||||
parent.document.getElementById('iFrameModal').dispatchEvent(new Event('sequenceSaved'));
|
||||
if (this.closeAfterSave) this.closeModal();
|
||||
});
|
||||
});
|
||||
},
|
||||
closeModal() {
|
||||
if (parent !== window) {
|
||||
parent.document.getElementById('iFrameModal').dispatchEvent(new Event('hide'));
|
||||
}
|
||||
},
|
||||
menuFilter(menus) {
|
||||
return menus.map((menu) => {
|
||||
if (menu.text !== 'Help') return menu;
|
||||
const menuOverride = {
|
||||
...menu,
|
||||
submenu: menu.submenu.filter((item) => item.cmd !== 'versionNumber')
|
||||
};
|
||||
|
||||
return menuOverride;
|
||||
});
|
||||
},
|
||||
readOnlyHandler(val) { this.readOnly = val; return true; },
|
||||
// override click event for github issue link in editor help -> about modal
|
||||
attachEditorHelpCallback() {
|
||||
$(window.document).on('click', '.bp3-dialog-container .bp3-dialog .bp3-alert-contents a', (event) => {
|
||||
event.preventDefault();
|
||||
$('.bp3-dialog .bp3-dialog-footer button[type*=submit]').trigger('click');
|
||||
window.open($(event.target).attr('href'), '_blank');
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -224,243 +224,244 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import InlineEdit from '../shared/inline_edit.vue'
|
||||
import Step from './step'
|
||||
import ProtocolMetadata from './protocolMetadata'
|
||||
import ProtocolOptions from './protocolOptions'
|
||||
import Tinymce from '../shared/tinymce.vue'
|
||||
import ReorderableItemsModal from '../shared/reorderable_items_modal.vue'
|
||||
import PublishProtocol from './modals/publish_protocol.vue'
|
||||
import clipboardPasteModal from '../shared/content/attachments/clipboard_paste_modal.vue'
|
||||
import AssetPasteMixin from '../shared/content/attachments/mixins/paste.js'
|
||||
<script>
|
||||
import InlineEdit from '../shared/inline_edit.vue';
|
||||
import Step from './step';
|
||||
import ProtocolMetadata from './protocolMetadata';
|
||||
import ProtocolOptions from './protocolOptions';
|
||||
import Tinymce from '../shared/tinymce.vue';
|
||||
import ReorderableItemsModal from '../shared/reorderable_items_modal.vue';
|
||||
import PublishProtocol from './modals/publish_protocol.vue';
|
||||
import clipboardPasteModal from '../shared/content/attachments/clipboard_paste_modal.vue';
|
||||
import AssetPasteMixin from '../shared/content/attachments/mixins/paste.js';
|
||||
|
||||
import UtilsMixin from '../mixins/utils.js'
|
||||
import stackableHeadersMixin from '../mixins/stackableHeadersMixin';
|
||||
import moduleNameObserver from '../mixins/moduleNameObserver';
|
||||
import UtilsMixin from '../mixins/utils.js';
|
||||
import stackableHeadersMixin from '../mixins/stackableHeadersMixin';
|
||||
import moduleNameObserver from '../mixins/moduleNameObserver';
|
||||
|
||||
export default {
|
||||
name: 'ProtocolContainer',
|
||||
props: {
|
||||
protocolUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
export default {
|
||||
name: 'ProtocolContainer',
|
||||
props: {
|
||||
protocolUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Step, InlineEdit, ProtocolOptions, Tinymce, ReorderableItemsModal, ProtocolMetadata, PublishProtocol, clipboardPasteModal
|
||||
},
|
||||
mixins: [UtilsMixin, stackableHeadersMixin, moduleNameObserver, AssetPasteMixin],
|
||||
computed: {
|
||||
inRepository() {
|
||||
return this.protocol.attributes.in_repository;
|
||||
},
|
||||
components: { Step, InlineEdit, ProtocolOptions, Tinymce, ReorderableItemsModal, ProtocolMetadata, PublishProtocol, clipboardPasteModal},
|
||||
mixins: [UtilsMixin, stackableHeadersMixin, moduleNameObserver, AssetPasteMixin],
|
||||
computed: {
|
||||
inRepository() {
|
||||
return this.protocol.attributes.in_repository
|
||||
},
|
||||
linked() {
|
||||
return this.protocol.attributes.linked;
|
||||
},
|
||||
urls() {
|
||||
return this.protocol.attributes.urls || {}
|
||||
},
|
||||
linked() {
|
||||
return this.protocol.attributes.linked;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
protocol: {
|
||||
attributes: {}
|
||||
},
|
||||
steps: [],
|
||||
reordering: false,
|
||||
publishing: false,
|
||||
stepToReload: null,
|
||||
activeDragStep: null
|
||||
}
|
||||
urls() {
|
||||
return this.protocol.attributes.urls || {};
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
protocol: {
|
||||
attributes: {}
|
||||
},
|
||||
steps: [],
|
||||
reordering: false,
|
||||
publishing: false,
|
||||
stepToReload: null,
|
||||
activeDragStep: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
$.get(this.protocolUrl, (result) => {
|
||||
this.protocol = result.data;
|
||||
this.$nextTick(() => {
|
||||
this.refreshProtocolStatus();
|
||||
if (!this.inRepository) {
|
||||
window.addEventListener('scroll', this.initStackableHeaders, false);
|
||||
this.initStackableHeaders();
|
||||
}
|
||||
});
|
||||
$.get(this.urls.steps_url, (result) => {
|
||||
this.steps = result.data;
|
||||
});
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (!this.inRepository) {
|
||||
window.removeEventListener('scroll', this.initStackableHeaders, false);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getHeader() {
|
||||
return this.$refs.header;
|
||||
},
|
||||
mounted() {
|
||||
$.get(this.protocolUrl, (result) => {
|
||||
this.protocol = result.data;
|
||||
this.$nextTick(() => {
|
||||
this.refreshProtocolStatus();
|
||||
if (!this.inRepository) {
|
||||
window.addEventListener('scroll', this.initStackableHeaders, false);
|
||||
this.initStackableHeaders();
|
||||
}
|
||||
});
|
||||
$.get(this.urls.steps_url, (result) => {
|
||||
this.steps = result.data
|
||||
})
|
||||
reloadStep(step) {
|
||||
this.stepToReload = step;
|
||||
},
|
||||
collapseSteps() {
|
||||
$('.step-container .collapse').collapse('hide');
|
||||
},
|
||||
expandSteps() {
|
||||
$('.step-container .collapse').collapse('show');
|
||||
},
|
||||
deleteSteps() {
|
||||
$.post(this.urls.delete_steps_url, () => {
|
||||
this.steps = [];
|
||||
}).fail(() => {
|
||||
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (!this.inRepository) {
|
||||
window.removeEventListener('scroll', this.initStackableHeaders, false);
|
||||
}
|
||||
refreshProtocolStatus() {
|
||||
if (this.inRepository) return;
|
||||
// legacy method from app/assets/javascripts/my_modules/protocols.js
|
||||
refreshProtocolStatusBar();
|
||||
|
||||
// Update protocol options drowpdown for linked tasks
|
||||
this.refreshProtocolDropdownOptions();
|
||||
},
|
||||
methods: {
|
||||
getHeader() {
|
||||
return this.$refs.header;
|
||||
},
|
||||
reloadStep(step) {
|
||||
this.stepToReload = step;
|
||||
},
|
||||
collapseSteps() {
|
||||
$('.step-container .collapse').collapse('hide')
|
||||
},
|
||||
expandSteps() {
|
||||
$('.step-container .collapse').collapse('show')
|
||||
},
|
||||
deleteSteps() {
|
||||
$.post(this.urls.delete_steps_url, () => {
|
||||
this.steps = []
|
||||
}).fail(() => {
|
||||
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger')
|
||||
})
|
||||
},
|
||||
refreshProtocolStatus() {
|
||||
if (this.inRepository) return
|
||||
// legacy method from app/assets/javascripts/my_modules/protocols.js
|
||||
refreshProtocolStatusBar();
|
||||
refreshProtocolDropdownOptions() {
|
||||
if (!this.linked && this.inRepository) return;
|
||||
|
||||
// Update protocol options drowpdown for linked tasks
|
||||
this.refreshProtocolDropdownOptions();
|
||||
},
|
||||
refreshProtocolDropdownOptions() {
|
||||
if (!this.linked && this.inRepository) return
|
||||
|
||||
$.get(this.protocolUrl, (result) => {
|
||||
this.protocol.attributes.urls = result.data.attributes.urls;
|
||||
});
|
||||
},
|
||||
updateProtocol(attributes) {
|
||||
this.protocol.attributes = attributes
|
||||
},
|
||||
updateName(newName) {
|
||||
this.protocol.attributes.name = newName;
|
||||
$.ajax({
|
||||
type: 'PATCH',
|
||||
url: this.urls.update_protocol_name_url,
|
||||
data: { protocol: { name: newName } },
|
||||
success: () => {
|
||||
this.refreshProtocolStatus();
|
||||
}
|
||||
});
|
||||
},
|
||||
updateDescription(protocol) {
|
||||
this.protocol.attributes = protocol.attributes
|
||||
this.refreshProtocolStatus();
|
||||
},
|
||||
addStep(position) {
|
||||
$.post(this.urls.add_step_url, {position: position}, (result) => {
|
||||
result.data.newStep = true
|
||||
this.updateStepsPosition(result.data);
|
||||
|
||||
// scroll to bottom if step was appended at the end
|
||||
if(position === this.steps.length - 1) {
|
||||
this.$nextTick(() => this.scrollToBottom());
|
||||
}
|
||||
$.get(this.protocolUrl, (result) => {
|
||||
this.protocol.attributes.urls = result.data.attributes.urls;
|
||||
});
|
||||
},
|
||||
updateProtocol(attributes) {
|
||||
this.protocol.attributes = attributes;
|
||||
},
|
||||
updateName(newName) {
|
||||
this.protocol.attributes.name = newName;
|
||||
$.ajax({
|
||||
type: 'PATCH',
|
||||
url: this.urls.update_protocol_name_url,
|
||||
data: { protocol: { name: newName } },
|
||||
success: () => {
|
||||
this.refreshProtocolStatus();
|
||||
}).fail((data) => {
|
||||
HelperModule.flashAlertMsg(data.responseJSON.error ? Object.values(data.responseJSON.error).join(', ') : I18n.t('errors.general'), 'danger');
|
||||
})
|
||||
},
|
||||
updateStepsPosition(step, action = 'add') {
|
||||
let position = step.attributes.position;
|
||||
if (action === 'delete') {
|
||||
this.steps.splice(position, 1)
|
||||
}
|
||||
let unordered_steps = this.steps.map( s => {
|
||||
if (s.attributes.position >= position) {
|
||||
if (action === 'add') {
|
||||
s.attributes.position += 1;
|
||||
} else {
|
||||
s.attributes.position -= 1;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
})
|
||||
if (action === 'add') {
|
||||
unordered_steps.push(step);
|
||||
});
|
||||
},
|
||||
updateDescription(protocol) {
|
||||
this.protocol.attributes = protocol.attributes;
|
||||
this.refreshProtocolStatus();
|
||||
},
|
||||
addStep(position) {
|
||||
$.post(this.urls.add_step_url, { position }, (result) => {
|
||||
result.data.newStep = true;
|
||||
this.updateStepsPosition(result.data);
|
||||
|
||||
// scroll to bottom if step was appended at the end
|
||||
if (position === this.steps.length - 1) {
|
||||
this.$nextTick(() => this.scrollToBottom());
|
||||
}
|
||||
this.reorderSteps(unordered_steps)
|
||||
},
|
||||
updateStep(attributes) {
|
||||
this.steps[attributes.position].attributes = {
|
||||
...this.steps[attributes.position].attributes,
|
||||
...attributes
|
||||
};
|
||||
this.refreshProtocolStatus();
|
||||
},
|
||||
reorderSteps(steps) {
|
||||
this.steps = steps.sort((a, b) => a.attributes.position - b.attributes.position);
|
||||
this.refreshProtocolStatus();
|
||||
},
|
||||
updateStepOrder(orderedSteps) {
|
||||
orderedSteps.forEach((step, position) => {
|
||||
let index = this.steps.findIndex((e) => e.id === step.id);
|
||||
this.steps[index].attributes.position = position;
|
||||
});
|
||||
|
||||
let stepPositions =
|
||||
{
|
||||
step_positions: this.steps.map(
|
||||
(step) => [step.id, step.attributes.position]
|
||||
)
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: this.protocol.attributes.urls.reorder_steps_url,
|
||||
data: JSON.stringify(stepPositions),
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
error: (() => HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger')),
|
||||
success: (() => this.reorderSteps(this.steps))
|
||||
});
|
||||
},
|
||||
startStepReorder() {
|
||||
this.reordering = true;
|
||||
},
|
||||
closeStepReorderModal() {
|
||||
this.reordering = false;
|
||||
},
|
||||
startPublish() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: this.urls.version_comment_url,
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
success: (result) => {
|
||||
this.protocol.attributes.version_comment = result.version_comment;
|
||||
this.publishing = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
closePublishModal() {
|
||||
this.publishing = false;
|
||||
},
|
||||
scrollToBottom() {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
},
|
||||
publishProtocol(comment) {
|
||||
this.protocol.attributes.version_comment = comment;
|
||||
$.post(this.urls.publish_url, {version_comment: comment, view: 'show'})
|
||||
},
|
||||
scrollTop() {
|
||||
window.scrollTo(0, 0);
|
||||
setTimeout(() => {
|
||||
$('.my_module-name .view-mode').trigger('click');
|
||||
$('.my_module-name .input-field').focus();
|
||||
}, 300)
|
||||
},
|
||||
dragEnter(id) {
|
||||
this.activeDragStep = id;
|
||||
},
|
||||
uploadFilesToStep(file, stepId) {
|
||||
this.$refs.steps.find(child => child.step?.id == stepId).uploadFiles(file);
|
||||
},
|
||||
firstObjectInViewport() {
|
||||
let step = $('.step-container:not(.locked)').toArray().find(element => {
|
||||
const { top, bottom } = element.getBoundingClientRect()
|
||||
return bottom > 0 && top < window.innerHeight
|
||||
})
|
||||
return step ? step.dataset.id : null
|
||||
}).fail((data) => {
|
||||
HelperModule.flashAlertMsg(data.responseJSON.error ? Object.values(data.responseJSON.error).join(', ') : I18n.t('errors.general'), 'danger');
|
||||
});
|
||||
},
|
||||
updateStepsPosition(step, action = 'add') {
|
||||
const { position } = step.attributes;
|
||||
if (action === 'delete') {
|
||||
this.steps.splice(position, 1);
|
||||
}
|
||||
const unordered_steps = this.steps.map((s) => {
|
||||
if (s.attributes.position >= position) {
|
||||
if (action === 'add') {
|
||||
s.attributes.position += 1;
|
||||
} else {
|
||||
s.attributes.position -= 1;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
});
|
||||
if (action === 'add') {
|
||||
unordered_steps.push(step);
|
||||
}
|
||||
this.reorderSteps(unordered_steps);
|
||||
},
|
||||
updateStep(attributes) {
|
||||
this.steps[attributes.position].attributes = {
|
||||
...this.steps[attributes.position].attributes,
|
||||
...attributes
|
||||
};
|
||||
this.refreshProtocolStatus();
|
||||
},
|
||||
reorderSteps(steps) {
|
||||
this.steps = steps.sort((a, b) => a.attributes.position - b.attributes.position);
|
||||
this.refreshProtocolStatus();
|
||||
},
|
||||
updateStepOrder(orderedSteps) {
|
||||
orderedSteps.forEach((step, position) => {
|
||||
const index = this.steps.findIndex((e) => e.id === step.id);
|
||||
this.steps[index].attributes.position = position;
|
||||
});
|
||||
|
||||
const stepPositions = {
|
||||
step_positions: this.steps.map(
|
||||
(step) => [step.id, step.attributes.position]
|
||||
)
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: this.protocol.attributes.urls.reorder_steps_url,
|
||||
data: JSON.stringify(stepPositions),
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
error: (() => HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger')),
|
||||
success: (() => this.reorderSteps(this.steps))
|
||||
});
|
||||
},
|
||||
startStepReorder() {
|
||||
this.reordering = true;
|
||||
},
|
||||
closeStepReorderModal() {
|
||||
this.reordering = false;
|
||||
},
|
||||
startPublish() {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: this.urls.version_comment_url,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
success: (result) => {
|
||||
this.protocol.attributes.version_comment = result.version_comment;
|
||||
this.publishing = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
closePublishModal() {
|
||||
this.publishing = false;
|
||||
},
|
||||
scrollToBottom() {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
},
|
||||
publishProtocol(comment) {
|
||||
this.protocol.attributes.version_comment = comment;
|
||||
$.post(this.urls.publish_url, { version_comment: comment, view: 'show' });
|
||||
},
|
||||
scrollTop() {
|
||||
window.scrollTo(0, 0);
|
||||
setTimeout(() => {
|
||||
$('.my_module-name .view-mode').trigger('click');
|
||||
$('.my_module-name .input-field').focus();
|
||||
}, 300);
|
||||
},
|
||||
dragEnter(id) {
|
||||
this.activeDragStep = id;
|
||||
},
|
||||
uploadFilesToStep(file, stepId) {
|
||||
this.$refs.steps.find((child) => child.step?.id == stepId).uploadFiles(file);
|
||||
},
|
||||
firstObjectInViewport() {
|
||||
const step = $('.step-container:not(.locked)').toArray().find((element) => {
|
||||
const { top, bottom } = element.getBoundingClientRect();
|
||||
return bottom > 0 && top < window.innerHeight;
|
||||
});
|
||||
return step ? step.dataset.id : null;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -21,23 +21,23 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'deleteStepModal',
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.$emit('cancel');
|
||||
});
|
||||
<script>
|
||||
export default {
|
||||
name: 'deleteStepModal',
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.$emit('cancel');
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('confirm');
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('confirm');
|
||||
},
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -20,26 +20,26 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'deleteStepsModal',
|
||||
mounted() {
|
||||
// move modal to body to avoid z-index issues
|
||||
$('body').append($(this.$refs.modal));
|
||||
<script>
|
||||
export default {
|
||||
name: 'deleteStepsModal',
|
||||
mounted() {
|
||||
// move modal to body to avoid z-index issues
|
||||
$('body').append($(this.$refs.modal));
|
||||
|
||||
$(this.$refs.modal).modal('show');
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.$emit('close');
|
||||
});
|
||||
$(this.$refs.modal).modal('show');
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.$emit('close');
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('confirm');
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('confirm');
|
||||
},
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -30,30 +30,30 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'publishProtocol',
|
||||
props: {
|
||||
protocol: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
<script>
|
||||
export default {
|
||||
name: 'publishProtocol',
|
||||
props: {
|
||||
protocol: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.$emit('cancel');
|
||||
});
|
||||
$(this.$refs.textarea).focus();
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('publish', this.protocol.attributes.version_comment);
|
||||
},
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.$emit('cancel');
|
||||
});
|
||||
$(this.$refs.textarea).focus();
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('publish', this.protocol.attributes.version_comment);
|
||||
},
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -98,22 +98,22 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DeleteStepsModals from './modals/delete_steps'
|
||||
<script>
|
||||
import DeleteStepsModals from './modals/delete_steps';
|
||||
|
||||
export default {
|
||||
|
||||
name: "ProtocolOptions",
|
||||
name: 'ProtocolOptions',
|
||||
components: { DeleteStepsModals },
|
||||
data() {
|
||||
return {
|
||||
stepsDeleting: false
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
protocol: {
|
||||
type: Object,
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
canDeleteSteps: {
|
||||
type: Boolean,
|
||||
|
@ -135,29 +135,29 @@ export default {
|
|||
},
|
||||
loadProtocol() {
|
||||
$.get(
|
||||
this.protocol.attributes.urls.load_from_repo_url + "?type=recent"
|
||||
`${this.protocol.attributes.urls.load_from_repo_url}?type=recent`
|
||||
).done((data) => {
|
||||
$(this.$refs.loadProtocol).trigger("ajax:success", data);
|
||||
$(this.$refs.loadProtocol).trigger('ajax:success', data);
|
||||
});
|
||||
},
|
||||
unlinkProtocol() {
|
||||
$.get(this.protocol.attributes.urls.unlink_url).done((data) => {
|
||||
$(this.$refs.unlinkProtocol).trigger("ajax:success", data);
|
||||
$(this.$refs.unlinkProtocol).trigger('ajax:success', data);
|
||||
});
|
||||
},
|
||||
updateProtocol() {
|
||||
$.get(this.protocol.attributes.urls.update_protocol_url).done((data) => {
|
||||
$(this.$refs.updateProtocol).trigger("ajax:success", data);
|
||||
$(this.$refs.updateProtocol).trigger('ajax:success', data);
|
||||
});
|
||||
},
|
||||
revertProtocol() {
|
||||
$.get(this.protocol.attributes.urls.revert_protocol_url).done((data) => {
|
||||
$(this.$refs.revertProtocol).trigger("ajax:success", data);
|
||||
$(this.$refs.revertProtocol).trigger('ajax:success', data);
|
||||
});
|
||||
},
|
||||
deleteSteps() {
|
||||
this.$emit('protocol:delete_steps')
|
||||
this.$emit('protocol:delete_steps');
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -77,88 +77,88 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import StorageUsage from '../storage_usage.vue'
|
||||
<script>
|
||||
import StorageUsage from '../storage_usage.vue';
|
||||
|
||||
import WopiFileModal from './mixins/wopi_file_modal.js'
|
||||
import WopiFileModal from './mixins/wopi_file_modal.js';
|
||||
|
||||
export default {
|
||||
name: 'fileModal',
|
||||
props: {
|
||||
step: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragingFile: false,
|
||||
attachmentsChanged: false
|
||||
export default {
|
||||
name: 'fileModal',
|
||||
props: {
|
||||
step: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragingFile: false,
|
||||
attachmentsChanged: false
|
||||
};
|
||||
},
|
||||
components: { StorageUsage },
|
||||
mixins: [WopiFileModal],
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
MarvinJsEditor.initNewButton('.add-file-modal .new-marvinjs-upload-button', () => {
|
||||
this.attachmentsChanged = true;
|
||||
$(this.$refs.modal).modal('hide');
|
||||
});
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
global.removeEventListener('paste', this.onImageFilePaste, false);
|
||||
|
||||
if (this.attachmentsChanged) {
|
||||
this.$emit('attachmentsChanged');
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
});
|
||||
global.addEventListener('paste', this.onImageFilePaste, false);
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$nextTick(() => this.$emit('cancel'));
|
||||
},
|
||||
components: {StorageUsage},
|
||||
mixins: [WopiFileModal],
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
MarvinJsEditor.initNewButton('.add-file-modal .new-marvinjs-upload-button', () => {
|
||||
this.attachmentsChanged = true;
|
||||
$(this.$refs.modal).modal('hide');
|
||||
});
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
global.removeEventListener('paste', this.onImageFilePaste, false);
|
||||
|
||||
if (this.attachmentsChanged) {
|
||||
this.$emit('attachmentsChanged');
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
});
|
||||
global.addEventListener('paste', this.onImageFilePaste, false);
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$nextTick(() => this.$emit('cancel'));
|
||||
},
|
||||
onImageFilePaste (pasteEvent) {
|
||||
if (pasteEvent.clipboardData !== false) {
|
||||
let items = pasteEvent.clipboardData.items
|
||||
for (let i = 0; i < items.length; i += 1) {
|
||||
if (items[i].type.indexOf('image') !== -1) {
|
||||
this.$emit('copyPasteImageModal', items[i]);
|
||||
$(this.$refs.modal).modal('hide');
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
dropFile(e) {
|
||||
e.stopPropagation();
|
||||
if (e.dataTransfer && e.dataTransfer.files.length) {
|
||||
this.$emit('files', e.dataTransfer.files);
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
},
|
||||
uploadFiles() {
|
||||
this.$emit('files', this.$refs.fileSelector.files);
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$parent.$parent.$nextTick(() => {
|
||||
this.$parent.$el.scrollIntoView(false)
|
||||
});
|
||||
},
|
||||
openMarvinJsModal() {
|
||||
},
|
||||
openWopiFileModal() {
|
||||
this.initWopiFileModal(this.step, (_e, data, status) => {
|
||||
if (status === 'success') {
|
||||
onImageFilePaste(pasteEvent) {
|
||||
if (pasteEvent.clipboardData !== false) {
|
||||
const { items } = pasteEvent.clipboardData;
|
||||
for (let i = 0; i < items.length; i += 1) {
|
||||
if (items[i].type.indexOf('image') !== -1) {
|
||||
this.$emit('copyPasteImageModal', items[i]);
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('attachmentUploaded', data);
|
||||
} else {
|
||||
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
|
||||
return;
|
||||
}
|
||||
});
|
||||
},
|
||||
openOVEditor() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
window.showIFrameModal(this.step.attributes.open_vector_editor_context.new_sequence_asset_url);
|
||||
}
|
||||
}
|
||||
},
|
||||
dropFile(e) {
|
||||
e.stopPropagation();
|
||||
if (e.dataTransfer && e.dataTransfer.files.length) {
|
||||
this.$emit('files', e.dataTransfer.files);
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
},
|
||||
uploadFiles() {
|
||||
this.$emit('files', this.$refs.fileSelector.files);
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$parent.$parent.$nextTick(() => {
|
||||
this.$parent.$el.scrollIntoView(false);
|
||||
});
|
||||
},
|
||||
openMarvinJsModal() {
|
||||
},
|
||||
openWopiFileModal() {
|
||||
this.initWopiFileModal(this.step, (_e, data, status) => {
|
||||
if (status === 'success') {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('attachmentUploaded', data);
|
||||
} else {
|
||||
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
|
||||
}
|
||||
});
|
||||
},
|
||||
openOVEditor() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
window.showIFrameModal(this.step.attributes.open_vector_editor_context.new_sequence_asset_url);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -44,21 +44,22 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ContextMenuMixin from './mixins/context_menu.js'
|
||||
import ContextMenu from './context_menu.vue'
|
||||
export default {
|
||||
name: 'thumbnailAttachment',
|
||||
mixins: [ContextMenuMixin],
|
||||
components: { ContextMenu },
|
||||
props: {
|
||||
attachment: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
stepId: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
import ContextMenuMixin from './mixins/context_menu.js';
|
||||
import ContextMenu from './context_menu.vue';
|
||||
|
||||
export default {
|
||||
name: 'thumbnailAttachment',
|
||||
mixins: [ContextMenuMixin],
|
||||
components: { ContextMenu },
|
||||
props: {
|
||||
attachment: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
stepId: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -28,82 +28,84 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ProtocolFileImportModal',
|
||||
props: {
|
||||
importUrl: { type: String, required: true },
|
||||
protocolTemplateTableUrl: { type: String }
|
||||
export default {
|
||||
name: 'ProtocolFileImportModal',
|
||||
props: {
|
||||
importUrl: { type: String, required: true },
|
||||
protocolTemplateTableUrl: { type: String }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
state: 'confirm',
|
||||
files: null,
|
||||
jobPollInterval: null,
|
||||
pollCount: 0,
|
||||
jobId: null,
|
||||
refreshCallback: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
window.protocolFileImportModal = this;
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
$('.protocol-import-dropdown-button').dropdown('toggle');
|
||||
$(this.$refs.modal).modal('show');
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
state: "confirm",
|
||||
files: null,
|
||||
jobPollInterval: null,
|
||||
pollCount: 0,
|
||||
jobId: null,
|
||||
refreshCallback: null
|
||||
close() {
|
||||
if (this.state === 'done' && this.refreshCallback) {
|
||||
this.refreshCallback();
|
||||
}
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
mounted() {
|
||||
window.protocolFileImportModal = this;
|
||||
init(files, refreshCallback) {
|
||||
this.refreshCallback = refreshCallback;
|
||||
this.pollCount = 0;
|
||||
this.jobId = null;
|
||||
this.files = files;
|
||||
this.state = 'confirm';
|
||||
this.open();
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
$('.protocol-import-dropdown-button').dropdown('toggle');
|
||||
$(this.$refs.modal).modal('show');
|
||||
},
|
||||
close() {
|
||||
if (this.state === "done" && this.refreshCallback) {
|
||||
this.refreshCallback();
|
||||
}
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
init(files, refreshCallback) {
|
||||
this.refreshCallback = refreshCallback;
|
||||
this.pollCount = 0;
|
||||
this.jobId = null;
|
||||
this.files = files;
|
||||
this.state = "confirm";
|
||||
this.open();
|
||||
},
|
||||
confirm() {
|
||||
let formData = new FormData();
|
||||
Array.from(this.files).forEach(file => formData.append('files[]', file, file.name));
|
||||
confirm() {
|
||||
const formData = new FormData();
|
||||
Array.from(this.files).forEach((file) => formData.append('files[]', file, file.name));
|
||||
|
||||
$.post({ url: this.importUrl, data: formData, processData: false, contentType: false }, (data) => {
|
||||
this.state = 'in_progress';
|
||||
this.jobId = data.job_id
|
||||
this.jobPollInterval = setInterval(this.fetchJobStatus, 1000);
|
||||
});
|
||||
},
|
||||
fetchJobStatus() {
|
||||
this.pollCount += 1;
|
||||
$.post({
|
||||
url: this.importUrl, data: formData, processData: false, contentType: false
|
||||
}, (data) => {
|
||||
this.state = 'in_progress';
|
||||
this.jobId = data.job_id;
|
||||
this.jobPollInterval = setInterval(this.fetchJobStatus, 1000);
|
||||
});
|
||||
},
|
||||
fetchJobStatus() {
|
||||
this.pollCount += 1;
|
||||
|
||||
if (this.pollCount > 4) {
|
||||
this.state = 'not_yet_done';
|
||||
clearInterval(this.jobPollInterval);
|
||||
return;
|
||||
}
|
||||
|
||||
$.get(`/jobs/${this.jobId}/status`, (data) => {
|
||||
let status = data.status;
|
||||
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
break;
|
||||
case 'running':
|
||||
break;
|
||||
case 'done':
|
||||
this.state = 'done';
|
||||
clearInterval(this.jobPollInterval);
|
||||
break;
|
||||
case 'failed':
|
||||
this.state = 'failed';
|
||||
clearInterval(this.jobPollInterval);
|
||||
break;
|
||||
}
|
||||
});
|
||||
if (this.pollCount > 4) {
|
||||
this.state = 'not_yet_done';
|
||||
clearInterval(this.jobPollInterval);
|
||||
return;
|
||||
}
|
||||
|
||||
$.get(`/jobs/${this.jobId}/status`, (data) => {
|
||||
const { status } = data;
|
||||
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
break;
|
||||
case 'running':
|
||||
break;
|
||||
case 'done':
|
||||
this.state = 'done';
|
||||
clearInterval(this.jobPollInterval);
|
||||
break;
|
||||
case 'failed':
|
||||
this.state = 'failed';
|
||||
clearInterval(this.jobPollInterval);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -5,16 +5,15 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ColumnElement',
|
||||
props: {
|
||||
column: Object
|
||||
},
|
||||
methods: {
|
||||
addFilter() {
|
||||
this.$emit('columns:addFilter', this.column)
|
||||
},
|
||||
export default {
|
||||
name: 'ColumnElement',
|
||||
props: {
|
||||
column: Object
|
||||
},
|
||||
methods: {
|
||||
addFilter() {
|
||||
this.$emit('columns:addFilter', this.column);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -55,106 +55,109 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ColumnElement from './column.vue'
|
||||
import FiltersList from './filters_list.vue'
|
||||
import SavedFilterElement from './saved_filter.vue'
|
||||
<script>
|
||||
import ColumnElement from './column.vue';
|
||||
import FiltersList from './filters_list.vue';
|
||||
import SavedFilterElement from './saved_filter.vue';
|
||||
|
||||
export default {
|
||||
name: 'FilterContainer',
|
||||
props: {
|
||||
defaultFilters: Array,
|
||||
my_modules: Array,
|
||||
container: Object,
|
||||
columns: Array,
|
||||
savedFilters: Array,
|
||||
canManageFilters: Boolean,
|
||||
filterName: String, default: () => null
|
||||
export default {
|
||||
name: 'FilterContainer',
|
||||
props: {
|
||||
defaultFilters: Array,
|
||||
my_modules: Array,
|
||||
container: Object,
|
||||
columns: Array,
|
||||
savedFilters: Array,
|
||||
canManageFilters: Boolean,
|
||||
filterName: String,
|
||||
default: () => null
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filters: this.defaultFilters,
|
||||
savedFilterScrollbar: null,
|
||||
filterListKey: true
|
||||
};
|
||||
},
|
||||
components: { ColumnElement, FiltersList, SavedFilterElement },
|
||||
methods: {
|
||||
addFilter(column) {
|
||||
const id = this.filters.length ? this.filters[this.filters.length - 1].id + 1 : 1;
|
||||
this.filters.push({
|
||||
id, column, isBlank: true, data: {}
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filters: this.defaultFilters,
|
||||
savedFilterScrollbar: null,
|
||||
filterListKey: true
|
||||
}
|
||||
updateFilter(filter) {
|
||||
const index = this.filters.findIndex((f) => f.id === filter.id);
|
||||
this.filters[index].data = filter.data;
|
||||
this.filters[index].isBlank = filter.isBlank;
|
||||
this.$emit('filters:update', this.filters);
|
||||
},
|
||||
components: { ColumnElement, FiltersList, SavedFilterElement },
|
||||
methods: {
|
||||
addFilter(column) {
|
||||
const id = this.filters.length ? this.filters[this.filters.length - 1].id + 1 : 1
|
||||
this.filters.push({ id: id, column: column, isBlank: true, data: {} });
|
||||
},
|
||||
updateFilter(filter) {
|
||||
const index = this.filters.findIndex((f) => f.id === filter.id);
|
||||
this.filters[index].data = filter.data;
|
||||
this.filters[index].isBlank = filter.isBlank;
|
||||
this.$emit("filters:update", this.filters);
|
||||
},
|
||||
clearFilters() {
|
||||
this.$emit('filters:clear');
|
||||
this.filterListKey = !this.filterListKey;
|
||||
},
|
||||
deleteFilter(index) {
|
||||
this.filters.splice(index, 1);
|
||||
},
|
||||
closeDropdowns() {
|
||||
this.closeColumnsFilters();
|
||||
this.closeSavedFilters();
|
||||
},
|
||||
closeColumnsFilters() {
|
||||
$('#filtersColumnsDropdown').removeClass('open');
|
||||
},
|
||||
toggleColumnsFilters(e) {
|
||||
e.stopPropagation();
|
||||
$('.filters-columns-list').scrollTop(0);
|
||||
this.closeSavedFilters();
|
||||
$('#filtersColumnsDropdown').toggleClass('open');
|
||||
},
|
||||
loadFilters(filterUrl) {
|
||||
this.filters = [];
|
||||
$.get(filterUrl, (data) => {
|
||||
let filters = [];
|
||||
let rawFilters = data.data.attributes.default_columns.concat((data.included || []).map(f => f.attributes))
|
||||
let id = 0;
|
||||
$.each(rawFilters, (i, f) => {
|
||||
filters.push({
|
||||
id: id,
|
||||
column: this.columns.find(c => c.id == f.repository_column_id),
|
||||
isBlank: false,
|
||||
data: {
|
||||
operator: f.operator,
|
||||
parameters: f.parameters
|
||||
}
|
||||
});
|
||||
id++;
|
||||
})
|
||||
this.$emit('filters:update-current-name', data.data.attributes.name);
|
||||
this.filters = filters;
|
||||
|
||||
// set up save modal
|
||||
let $saveFiltersModal = $('#modalSaveRepositoryTableFilter');
|
||||
let $overwriteLink = $('#overwriteFilterLink');
|
||||
$overwriteLink.removeClass('hidden');
|
||||
$saveFiltersModal.data('repositoryTableFilterId', data.data.id);
|
||||
$('#currentFilterName').html(data.data.attributes.name);
|
||||
$saveFiltersModal.data('repositoryTableFilterName', data.data.attributes.name);
|
||||
clearFilters() {
|
||||
this.$emit('filters:clear');
|
||||
this.filterListKey = !this.filterListKey;
|
||||
},
|
||||
deleteFilter(index) {
|
||||
this.filters.splice(index, 1);
|
||||
},
|
||||
closeDropdowns() {
|
||||
this.closeColumnsFilters();
|
||||
this.closeSavedFilters();
|
||||
},
|
||||
closeColumnsFilters() {
|
||||
$('#filtersColumnsDropdown').removeClass('open');
|
||||
},
|
||||
toggleColumnsFilters(e) {
|
||||
e.stopPropagation();
|
||||
$('.filters-columns-list').scrollTop(0);
|
||||
this.closeSavedFilters();
|
||||
$('#filtersColumnsDropdown').toggleClass('open');
|
||||
},
|
||||
loadFilters(filterUrl) {
|
||||
this.filters = [];
|
||||
$.get(filterUrl, (data) => {
|
||||
const filters = [];
|
||||
const rawFilters = data.data.attributes.default_columns.concat((data.included || []).map((f) => f.attributes));
|
||||
let id = 0;
|
||||
$.each(rawFilters, (i, f) => {
|
||||
filters.push({
|
||||
id,
|
||||
column: this.columns.find((c) => c.id == f.repository_column_id),
|
||||
isBlank: false,
|
||||
data: {
|
||||
operator: f.operator,
|
||||
parameters: f.parameters
|
||||
}
|
||||
});
|
||||
id++;
|
||||
});
|
||||
},
|
||||
closeSavedFilters() {
|
||||
$('#savedFiltersContainer').removeClass('open');
|
||||
return true;
|
||||
},
|
||||
toggleSavedFilters(e) {
|
||||
e.stopPropagation();
|
||||
$('.saved-filters-list').scrollTop(0);
|
||||
if (this.savedFilterScrollbar) {
|
||||
this.savedFilterScrollbar.update();
|
||||
} else {
|
||||
this.savedFilterScrollbar = new PerfectScrollbar($('.saved-filters-list')[0]);
|
||||
}
|
||||
this.closeColumnsFilters();
|
||||
$('#savedFiltersContainer').toggleClass('open');
|
||||
},
|
||||
this.$emit('filters:update-current-name', data.data.attributes.name);
|
||||
this.filters = filters;
|
||||
|
||||
// set up save modal
|
||||
const $saveFiltersModal = $('#modalSaveRepositoryTableFilter');
|
||||
const $overwriteLink = $('#overwriteFilterLink');
|
||||
$overwriteLink.removeClass('hidden');
|
||||
$saveFiltersModal.data('repositoryTableFilterId', data.data.id);
|
||||
$('#currentFilterName').html(data.data.attributes.name);
|
||||
$saveFiltersModal.data('repositoryTableFilterName', data.data.attributes.name);
|
||||
});
|
||||
},
|
||||
closeSavedFilters() {
|
||||
$('#savedFiltersContainer').removeClass('open');
|
||||
return true;
|
||||
},
|
||||
toggleSavedFilters(e) {
|
||||
e.stopPropagation();
|
||||
$('.saved-filters-list').scrollTop(0);
|
||||
if (this.savedFilterScrollbar) {
|
||||
this.savedFilterScrollbar.update();
|
||||
} else {
|
||||
this.savedFilterScrollbar = new PerfectScrollbar($('.saved-filters-list')[0]);
|
||||
}
|
||||
this.closeColumnsFilters();
|
||||
$('#savedFiltersContainer').toggleClass('open');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
<<<<<<< HEAD
|
||||
// filter types
|
||||
import RepositoryNonEmptyTextValue from './filters/repositoryNonEmptyTextValue.vue'
|
||||
import RepositoryAssetValue from './filters/repositoryAssetValue.vue'
|
||||
|
@ -37,38 +38,56 @@
|
|||
import RepositoryUserValue from './filters/repositoryUserValue.vue'
|
||||
import RepositoryStockValue from './filters/repositoryStockValue.vue'
|
||||
import DropdownSelector from '../shared/legacy/dropdown_selector.vue'
|
||||
=======
|
||||
// filter types
|
||||
import RepositoryNonEmptyTextValue from './filters/repositoryNonEmptyTextValue.vue';
|
||||
import RepositoryAssetValue from './filters/repositoryAssetValue.vue';
|
||||
import RepositoryTextValue from './filters/repositoryTextValue.vue';
|
||||
import RepositoryNumberValue from './filters/repositoryNumberValue.vue';
|
||||
import RepositoryMyModuleValue from './filters/repositoryMyModuleValue.vue';
|
||||
import RepositoryDateValue from './filters/repositoryDateValue.vue';
|
||||
import RepositoryDateRangeValue from './filters/repositoryDateRangeValue.vue';
|
||||
import RepositoryDateTimeValue from './filters/repositoryDateTimeValue.vue';
|
||||
import RepositoryDateTimeRangeValue from './filters/repositoryDateTimeRangeValue.vue';
|
||||
import RepositoryTimeValue from './filters/repositoryTimeValue.vue';
|
||||
import RepositoryTimeRangeValue from './filters/repositoryTimeRangeValue.vue';
|
||||
import RepositoryListValue from './filters/repositoryListValue.vue';
|
||||
import RepositoryStatusValue from './filters/repositoryStatusValue.vue';
|
||||
import RepositoryChecklistValue from './filters/repositoryChecklistValue.vue';
|
||||
import RepositoryUserValue from './filters/repositoryUserValue.vue';
|
||||
import RepositoryStockValue from './filters/repositoryStockValue.vue';
|
||||
import DropdownSelector from '../shared/dropdown_selector.vue';
|
||||
>>>>>>> develop
|
||||
|
||||
|
||||
export default {
|
||||
name: "FilterElement",
|
||||
props: {
|
||||
filter: Object,
|
||||
my_modules: Array
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
RepositoryNonEmptyTextValue,
|
||||
RepositoryAssetValue,
|
||||
RepositoryTextValue,
|
||||
RepositoryNumberValue,
|
||||
RepositoryMyModuleValue,
|
||||
RepositoryDateValue,
|
||||
RepositoryDateRangeValue,
|
||||
RepositoryTimeValue,
|
||||
RepositoryTimeRangeValue,
|
||||
RepositoryDateTimeValue,
|
||||
RepositoryDateTimeRangeValue,
|
||||
RepositoryListValue,
|
||||
RepositoryStatusValue,
|
||||
RepositoryChecklistValue,
|
||||
RepositoryListValue,
|
||||
RepositoryUserValue,
|
||||
RepositoryStockValue
|
||||
},
|
||||
methods: {
|
||||
updateFilter(value) {
|
||||
this.$emit('filter:update', value)
|
||||
}
|
||||
export default {
|
||||
name: 'FilterElement',
|
||||
props: {
|
||||
filter: Object,
|
||||
my_modules: Array
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
RepositoryNonEmptyTextValue,
|
||||
RepositoryAssetValue,
|
||||
RepositoryTextValue,
|
||||
RepositoryNumberValue,
|
||||
RepositoryMyModuleValue,
|
||||
RepositoryDateValue,
|
||||
RepositoryDateRangeValue,
|
||||
RepositoryTimeValue,
|
||||
RepositoryTimeRangeValue,
|
||||
RepositoryDateTimeValue,
|
||||
RepositoryDateTimeRangeValue,
|
||||
RepositoryListValue,
|
||||
RepositoryStatusValue,
|
||||
RepositoryChecklistValue,
|
||||
RepositoryUserValue,
|
||||
RepositoryStockValue
|
||||
},
|
||||
methods: {
|
||||
updateFilter(value) {
|
||||
this.$emit('filter:update', value);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -22,42 +22,42 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryAssetValue',
|
||||
mixins: [FilterMixin],
|
||||
props: {
|
||||
filter: Object
|
||||
export default {
|
||||
name: 'RepositoryAssetValue',
|
||||
mixins: [FilterMixin],
|
||||
props: {
|
||||
filter: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'file_contains', label: this.i18n.t('repositories.show.repository_filter.filters.operators.file_contains') },
|
||||
{ value: 'file_attached', label: this.i18n.t('repositories.show.repository_filter.filters.operators.file_attached') },
|
||||
{ value: 'file_not_attached', label: this.i18n.t('repositories.show.repository_filter.filters.operators.file_not_attached') }
|
||||
],
|
||||
operator: 'file_contains',
|
||||
value: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
operator() {
|
||||
if (this.operator !== 'file_contains') this.value = '';
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'file_contains', label: this.i18n.t('repositories.show.repository_filter.filters.operators.file_contains') },
|
||||
{ value: 'file_attached', label: this.i18n.t('repositories.show.repository_filter.filters.operators.file_attached') },
|
||||
{ value: 'file_not_attached', label: this.i18n.t('repositories.show.repository_filter.filters.operators.file_not_attached') }
|
||||
],
|
||||
operator: 'file_contains',
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
operator() {
|
||||
if(this.operator !== 'file_contains') this.value = '';
|
||||
},
|
||||
value() {
|
||||
this.parameters = this.operator === 'file_contains' ? { text: this.value } : {}
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
computed: {
|
||||
isBlank(){
|
||||
return this.operator == 'file_contains' && !this.value;
|
||||
}
|
||||
value() {
|
||||
this.parameters = this.operator === 'file_contains' ? { text: this.value } : {};
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
computed: {
|
||||
isBlank() {
|
||||
return this.operator == 'file_contains' && !this.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -27,40 +27,41 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
export default {
|
||||
name: 'RepositoryChecklistValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'any_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.any_of') },
|
||||
{ value: 'all_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.all_of') },
|
||||
{ value: 'none_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.none_of') }
|
||||
],
|
||||
operator: 'any_of',
|
||||
value: []
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { item_ids: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue(value) {
|
||||
this.value = value
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank(){
|
||||
return this.operator == 'any_of' && this.value.length == 0;
|
||||
}
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryChecklistValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'any_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.any_of') },
|
||||
{ value: 'all_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.all_of') },
|
||||
{ value: 'none_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.none_of') }
|
||||
],
|
||||
operator: 'any_of',
|
||||
value: []
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { item_ids: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue(value) {
|
||||
this.value = value;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank() {
|
||||
return this.operator === 'any_of' && this.value.length === 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -19,45 +19,45 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import RangeDateTimeFilterMixin from '../mixins/filters/range_date_time_filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue'
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import RangeDateTimeFilterMixin from '../mixins/filters/range_date_time_filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateRangeValue',
|
||||
mixins: [FilterMixin, RangeDateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'date',
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.on') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.after') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.before') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.not_on') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null
|
||||
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
|
||||
}
|
||||
export default {
|
||||
name: 'RepositoryDateRangeValue',
|
||||
mixins: [FilterMixin, RangeDateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'date',
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.on') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.after') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.before') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.not_on') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null;
|
||||
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -21,45 +21,45 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import RangeDateTimeFilterMixin from '../mixins/filters/range_date_time_filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue'
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import RangeDateTimeFilterMixin from '../mixins/filters/range_date_time_filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateRangeValue',
|
||||
mixins: [FilterMixin, RangeDateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'datetime',
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.on') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.after') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.before') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.not_on') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null
|
||||
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
||||
}
|
||||
export default {
|
||||
name: 'RepositoryDateRangeValue',
|
||||
mixins: [FilterMixin, RangeDateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'datetime',
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.on') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.after') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.before') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.not_on') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null;
|
||||
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -23,64 +23,88 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DateTimeFilterMixin from '../mixins/filters/date_time_filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue'
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DateTimeFilterMixin from '../mixins/filters/date_time_filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateValue',
|
||||
mixins: [FilterMixin, DateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'datetime',
|
||||
operators: [
|
||||
{ value: 'today', label: this.i18n.t('repositories.show.repository_filter.filters.operators.today'), params: {
|
||||
export default {
|
||||
name: 'RepositoryDateValue',
|
||||
mixins: [FilterMixin, DateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'datetime',
|
||||
operators: [
|
||||
{
|
||||
value: 'today',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.today'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.today')
|
||||
} },
|
||||
{ value: 'yesterday', label: this.i18n.t('repositories.show.repository_filter.filters.operators.yesterday'), params: {
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'yesterday',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.yesterday'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.yesterday')
|
||||
} },
|
||||
{ value: 'last_week', label: this.i18n.t('repositories.show.repository_filter.filters.operators.last_week'), params: {
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'last_week',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.last_week'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.last_week')
|
||||
} },
|
||||
{ value: 'this_month', label: this.i18n.t('repositories.show.repository_filter.filters.operators.this_month'), params: {
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'this_month',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.this_month'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.this_month')
|
||||
} },
|
||||
{ value: 'this_year', label: this.i18n.t('repositories.show.repository_filter.filters.operators.this_year'), params: {
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'this_year',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.this_year'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.this_year')
|
||||
} },
|
||||
{ value: 'last_year', label: this.i18n.t('repositories.show.repository_filter.filters.operators.last_year'), params: {
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'last_year',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.last_year'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.last_year')
|
||||
} },
|
||||
{ value: '', label: '', params: { delimiter: true } },
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.on') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.after') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.before') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.not_on') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null
|
||||
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
||||
}
|
||||
}
|
||||
},
|
||||
{ value: '', label: '', params: { delimiter: true } },
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.on') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.after') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.before') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.not_on') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null;
|
||||
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -21,64 +21,88 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DateTimeFilterMixin from '../mixins/filters/date_time_filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue'
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DateTimeFilterMixin from '../mixins/filters/date_time_filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateValue',
|
||||
mixins: [FilterMixin, DateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'date',
|
||||
operators: [
|
||||
{ value: 'today', label: this.i18n.t('repositories.show.repository_filter.filters.operators.today'), params: {
|
||||
export default {
|
||||
name: 'RepositoryDateValue',
|
||||
mixins: [FilterMixin, DateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'date',
|
||||
operators: [
|
||||
{
|
||||
value: 'today',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.today'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.today')
|
||||
} },
|
||||
{ value: 'yesterday', label: this.i18n.t('repositories.show.repository_filter.filters.operators.yesterday'), params: {
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'yesterday',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.yesterday'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.yesterday')
|
||||
} },
|
||||
{ value: 'last_week', label: this.i18n.t('repositories.show.repository_filter.filters.operators.last_week'), params: {
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'last_week',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.last_week'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.last_week')
|
||||
} },
|
||||
{ value: 'this_month', label: this.i18n.t('repositories.show.repository_filter.filters.operators.this_month'), params: {
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'this_month',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.this_month'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.this_month')
|
||||
} },
|
||||
{ value: 'this_year', label: this.i18n.t('repositories.show.repository_filter.filters.operators.this_year'), params: {
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'this_year',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.this_year'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.this_year')
|
||||
} },
|
||||
{ value: 'last_year', label: this.i18n.t('repositories.show.repository_filter.filters.operators.last_year'), params: {
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'last_year',
|
||||
label: this.i18n.t('repositories.show.repository_filter.filters.operators.last_year'),
|
||||
params: {
|
||||
tooltip: this.i18n.t('repositories.show.repository_filter.filters.operators.tooltips.last_year')
|
||||
} },
|
||||
{ value: '', label: '', params: { delimiter: true } },
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.on') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.after') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.before') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.not_on') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null
|
||||
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
|
||||
}
|
||||
}
|
||||
},
|
||||
{ value: '', label: '', params: { delimiter: true } },
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.on') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.after') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.before') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.date.not_on') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null;
|
||||
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -27,39 +27,40 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
export default {
|
||||
name: 'RepositoryListValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'any_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.any_of') },
|
||||
{ value: 'none_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.none_of') }
|
||||
],
|
||||
operator: 'any_of',
|
||||
value: []
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { item_ids: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue(value) {
|
||||
this.value = value;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank(){
|
||||
return this.operator == 'any_of' && this.value.length == 0;
|
||||
}
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryListValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'any_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.any_of') },
|
||||
{ value: 'none_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.none_of') }
|
||||
],
|
||||
operator: 'any_of',
|
||||
value: []
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { item_ids: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue(value) {
|
||||
this.value = value;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank() {
|
||||
return this.operator == 'any_of' && this.value.length == 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -29,45 +29,46 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
export default {
|
||||
name: 'RepositoryMyModuleValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'any_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.any_of') },
|
||||
{ value: 'all_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.all_of') },
|
||||
{ value: 'none_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.none_of') }
|
||||
],
|
||||
operator: 'any_of',
|
||||
value: []
|
||||
}
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryMyModuleValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'any_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.any_of') },
|
||||
{ value: 'all_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.all_of') },
|
||||
{ value: 'none_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.none_of') }
|
||||
],
|
||||
operator: 'any_of',
|
||||
value: []
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { my_module_ids: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue(value) {
|
||||
this.value = value;
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { my_module_ids: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue(value) {
|
||||
this.value = value;
|
||||
},
|
||||
renderOption(data) {
|
||||
return `<span class="task-option">
|
||||
renderOption(data) {
|
||||
return `<span class="task-option">
|
||||
${data.label}
|
||||
</span>`;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank(){
|
||||
return this.operator == 'any_of' && this.value.length == 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank() {
|
||||
return this.operator == 'any_of' && this.value.length == 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -22,34 +22,35 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
export default {
|
||||
name: 'RepositoryNonEmptyTextValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'contains', label: this.i18n.t('repositories.show.repository_filter.filters.operators.contains') },
|
||||
{ value: 'doesnt_contain', label: this.i18n.t('repositories.show.repository_filter.filters.operators.does_not_contain') }
|
||||
],
|
||||
operator: 'contains',
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { text: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank(){
|
||||
return this.operator == 'contains' && !this.value;
|
||||
}
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryNonEmptyTextValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'contains', label: this.i18n.t('repositories.show.repository_filter.filters.operators.contains') },
|
||||
{ value: 'doesnt_contain', label: this.i18n.t('repositories.show.repository_filter.filters.operators.does_not_contain') }
|
||||
],
|
||||
operator: 'contains',
|
||||
value: ''
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { text: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank() {
|
||||
return this.operator == 'contains' && !this.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -43,58 +43,59 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
export default {
|
||||
name: 'RepositoryNumberValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.equal_to')},
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.unequal_to') },
|
||||
{ value: 'greater_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than_or_equal_to') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than') },
|
||||
{ value: 'less_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than_or_equal_to') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
value: '',
|
||||
from: '',
|
||||
to: ''
|
||||
}
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryNumberValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.equal_to') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.unequal_to') },
|
||||
{ value: 'greater_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than_or_equal_to') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than') },
|
||||
{ value: 'less_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than_or_equal_to') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
value: '',
|
||||
from: '',
|
||||
to: ''
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
methods: {
|
||||
validateNumber(number) {
|
||||
return number.replace(/[^0-9.]/g, '').match(/^\d*(\.\d{0,10})?/)[0];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.value = this.validateNumber(this.value);
|
||||
this.parameters = { number: this.value };
|
||||
this.updateFilter();
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
to() {
|
||||
this.to = this.validateNumber(this.to);
|
||||
this.parameters = { from: this.from, to: this.to };
|
||||
this.updateFilter();
|
||||
},
|
||||
methods: {
|
||||
validateNumber(number) {
|
||||
return number.replace(/[^0-9.]/g, '').match(/^\d*(\.\d{0,10})?/)[0]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.value = this.validateNumber(this.value)
|
||||
this.parameters = { number: this.value }
|
||||
this.updateFilter();
|
||||
},
|
||||
to() {
|
||||
this.to = this.validateNumber(this.to)
|
||||
this.parameters = {from: this.from, to: this.to}
|
||||
this.updateFilter();
|
||||
},
|
||||
from() {
|
||||
this.from = this.validateNumber(this.from)
|
||||
this.parameters = {from: this.from, to: this.to}
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank(){
|
||||
return (!this.value && this.operator != 'between') ||
|
||||
((!this.to || !this.from) && this.operator == 'between');
|
||||
}
|
||||
from() {
|
||||
this.from = this.validateNumber(this.from);
|
||||
this.parameters = { from: this.from, to: this.to };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank() {
|
||||
return (!this.value && this.operator !== 'between')
|
||||
|| ((!this.to || !this.from) && this.operator === 'between');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -27,39 +27,40 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
export default {
|
||||
name: 'RepositoryStatusValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'any_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.any_of') },
|
||||
{ value: 'none_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.none_of') }
|
||||
],
|
||||
operator: 'any_of',
|
||||
value: []
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { item_ids: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue(value) {
|
||||
this.value = value
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank(){
|
||||
return this.operator == 'any_of' && this.value.length == 0;
|
||||
}
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryStatusValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'any_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.any_of') },
|
||||
{ value: 'none_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.none_of') }
|
||||
],
|
||||
operator: 'any_of',
|
||||
value: []
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { item_ids: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue(value) {
|
||||
this.value = value;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank() {
|
||||
return this.operator === 'any_of' && this.value.length === 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -53,83 +53,84 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
export default {
|
||||
name: 'RepositoryStockValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.equal_to')},
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.unequal_to') },
|
||||
{ value: 'greater_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than_or_equal_to') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than') },
|
||||
{ value: 'less_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than_or_equal_to') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
value: '',
|
||||
from: '',
|
||||
to: '',
|
||||
stock_unit: 'all'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
methods: {
|
||||
validateNumber(number) {
|
||||
return number.replace(/[^0-9.]/g, '').match(/^\d*(\.\d{0,10})?/)[0]
|
||||
},
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
|
||||
prepareUnitOptions() {
|
||||
return [
|
||||
{ label: this.i18n.t('repositories.show.repository_filter.filters.types.RepositoryStockValue.all_units'), value: 'all'},
|
||||
{ label: this.i18n.t('repositories.show.repository_filter.filters.types.RepositoryStockValue.no_unit'), value: 'none'}
|
||||
].concat(this.filter.column.items);
|
||||
},
|
||||
export default {
|
||||
name: 'RepositoryStockValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.equal_to') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.unequal_to') },
|
||||
{ value: 'greater_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than_or_equal_to') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than') },
|
||||
{ value: 'less_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than_or_equal_to') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
value: '',
|
||||
from: '',
|
||||
to: '',
|
||||
stock_unit: 'all'
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
methods: {
|
||||
validateNumber(number) {
|
||||
return number.replace(/[^0-9.]/g, '').match(/^\d*(\.\d{0,10})?/)[0];
|
||||
},
|
||||
|
||||
updateStockUnit(value) {
|
||||
this.stock_unit = value
|
||||
}
|
||||
prepareUnitOptions() {
|
||||
return [
|
||||
{ label: this.i18n.t('repositories.show.repository_filter.filters.types.RepositoryStockValue.all_units'), value: 'all' },
|
||||
{ label: this.i18n.t('repositories.show.repository_filter.filters.types.RepositoryStockValue.no_unit'), value: 'none' }
|
||||
].concat(this.filter.column.items);
|
||||
},
|
||||
|
||||
updateStockUnit(value) {
|
||||
this.stock_unit = value;
|
||||
}
|
||||
|
||||
},
|
||||
created() {
|
||||
if (this.parameters) {
|
||||
this.value = this.parameters.value || '';
|
||||
this.from = this.parameters.from || '';
|
||||
this.to = this.parameters.to || '';
|
||||
this.stock_unit = this.parameters.stock_unit || 'all';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
stock_unit() {
|
||||
this.parameters.stock_unit = this.stock_unit;
|
||||
this.updateFilter();
|
||||
},
|
||||
created() {
|
||||
if (this.parameters) {
|
||||
this.value = this.parameters.value || ''
|
||||
this.from = this.parameters.from || ''
|
||||
this.to = this.parameters.to || ''
|
||||
this.stock_unit = this.parameters.stock_unit || 'all'
|
||||
}
|
||||
value() {
|
||||
this.value = this.validateNumber(this.value);
|
||||
this.parameters = { value: this.value, stock_unit: this.stock_unit };
|
||||
this.updateFilter();
|
||||
},
|
||||
watch: {
|
||||
stock_unit() {
|
||||
this.parameters.stock_unit = this.stock_unit
|
||||
this.updateFilter();
|
||||
},
|
||||
value() {
|
||||
this.value = this.validateNumber(this.value)
|
||||
this.parameters = { value: this.value, stock_unit: this.stock_unit }
|
||||
this.updateFilter();
|
||||
},
|
||||
to() {
|
||||
this.to = this.validateNumber(this.to)
|
||||
this.parameters = {from: this.from, to: this.to, stock_unit: this.stock_unit}
|
||||
this.updateFilter();
|
||||
},
|
||||
from() {
|
||||
this.from = this.validateNumber(this.from)
|
||||
this.parameters = {from: this.from, to: this.to, stock_unit: this.stock_unit}
|
||||
this.updateFilter();
|
||||
}
|
||||
to() {
|
||||
this.to = this.validateNumber(this.to);
|
||||
this.parameters = { from: this.from, to: this.to, stock_unit: this.stock_unit };
|
||||
this.updateFilter();
|
||||
},
|
||||
computed: {
|
||||
isBlank(){
|
||||
return (!this.value && this.operator != 'between') ||
|
||||
((!this.to || !this.from) && this.operator == 'between');
|
||||
}
|
||||
from() {
|
||||
this.from = this.validateNumber(this.from);
|
||||
this.parameters = { from: this.from, to: this.to, stock_unit: this.stock_unit };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank() {
|
||||
return (!this.value && this.operator != 'between')
|
||||
|| ((!this.to || !this.from) && this.operator == 'between');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -22,35 +22,36 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
export default {
|
||||
name: 'RepositoryTextValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'contains', label: this.i18n.t('repositories.show.repository_filter.filters.operators.contains') },
|
||||
{ value: 'doesnt_contain', label: this.i18n.t('repositories.show.repository_filter.filters.operators.does_not_contain') },
|
||||
{ value: 'empty', label: this.i18n.t('repositories.show.repository_filter.filters.operators.empty') }
|
||||
],
|
||||
operator: 'contains',
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { text: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank(){
|
||||
return this.operator == 'contains' && !this.value;
|
||||
}
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryTextValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'contains', label: this.i18n.t('repositories.show.repository_filter.filters.operators.contains') },
|
||||
{ value: 'doesnt_contain', label: this.i18n.t('repositories.show.repository_filter.filters.operators.does_not_contain') },
|
||||
{ value: 'empty', label: this.i18n.t('repositories.show.repository_filter.filters.operators.empty') }
|
||||
],
|
||||
operator: 'contains',
|
||||
value: ''
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { text: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank() {
|
||||
return this.operator === 'contains' && !this.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -19,47 +19,47 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import RangeDateTimeFilterMixin from '../mixins/filters/range_date_time_filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue'
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import RangeDateTimeFilterMixin from '../mixins/filters/range_date_time_filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryTimeRangeValue',
|
||||
mixins: [FilterMixin, RangeDateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'time',
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.equal_to')},
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.unequal_to') },
|
||||
{ value: 'greater_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than_or_equal_to') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than') },
|
||||
{ value: 'less_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than_or_equal_to') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null
|
||||
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
||||
}
|
||||
export default {
|
||||
name: 'RepositoryTimeRangeValue',
|
||||
mixins: [FilterMixin, RangeDateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'time',
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.equal_to') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.unequal_to') },
|
||||
{ value: 'greater_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than_or_equal_to') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than') },
|
||||
{ value: 'less_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than_or_equal_to') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null;
|
||||
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -21,47 +21,47 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DateTimeFilterMixin from '../mixins/filters/date_time_filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue'
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DateTimeFilterMixin from '../mixins/filters/date_time_filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryTimeValue',
|
||||
mixins: [FilterMixin, DateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'time',
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.equal_to')},
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.unequal_to') },
|
||||
{ value: 'greater_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than_or_equal_to') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than') },
|
||||
{ value: 'less_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than_or_equal_to') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null
|
||||
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
||||
}
|
||||
export default {
|
||||
name: 'RepositoryTimeValue',
|
||||
mixins: [FilterMixin, DateTimeFilterMixin],
|
||||
data() {
|
||||
return {
|
||||
timeType: 'time',
|
||||
operators: [
|
||||
{ value: 'equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.equal_to') },
|
||||
{ value: 'unequal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.unequal_to') },
|
||||
{ value: 'greater_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than') },
|
||||
{ value: 'greater_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.greater_than_or_equal_to') },
|
||||
{ value: 'less_than', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than') },
|
||||
{ value: 'less_than_or_equal_to', label: this.i18n.t('repositories.show.repository_filter.filters.operators.less_than_or_equal_to') },
|
||||
{ value: 'between', label: this.i18n.t('repositories.show.repository_filter.filters.operators.between') }
|
||||
],
|
||||
operator: 'equal_to',
|
||||
date: null,
|
||||
dateTo: null,
|
||||
value: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
DateTimePicker
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = this.value;
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formattedDate(date) {
|
||||
if (!date) return null;
|
||||
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -30,54 +30,55 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterMixin from '../mixins/filter.js'
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue'
|
||||
export default {
|
||||
name: 'RepositoryUserValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'any_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.any_of') },
|
||||
{ value: 'none_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.none_of') }
|
||||
],
|
||||
operator: 'any_of',
|
||||
value: [],
|
||||
users: null
|
||||
}
|
||||
import FilterMixin from '../mixins/filter.js';
|
||||
import DropdownSelector from '../../shared/legacy/dropdown_selector.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryUserValue',
|
||||
mixins: [FilterMixin],
|
||||
data() {
|
||||
return {
|
||||
operators: [
|
||||
{ value: 'any_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.any_of') },
|
||||
{ value: 'none_of', label: this.i18n.t('repositories.show.repository_filter.filters.operators.none_of') }
|
||||
],
|
||||
operator: 'any_of',
|
||||
value: [],
|
||||
users: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
mounted() {
|
||||
const params = {};
|
||||
if (this.filter.column.id === 'archived_by') params.archived_by = true;
|
||||
$.get($('#filterContainer').data('users-url'), params, (data) => {
|
||||
this.users = data.users;
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { user_ids: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue(value) {
|
||||
this.value = value;
|
||||
},
|
||||
components: {
|
||||
DropdownSelector
|
||||
},
|
||||
mounted() {
|
||||
let params = {}
|
||||
if (this.filter.column.id === 'archived_by') params.archived_by = true
|
||||
$.get($('#filterContainer').data('users-url'), params, (data) => {
|
||||
this.users = data.users;
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.parameters = { user_ids: this.value };
|
||||
this.updateFilter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateValue(value) {
|
||||
this.value = value
|
||||
},
|
||||
renderOption(data) {
|
||||
return `<span class="user-filter-option" title="${data.label.trim()} | ${data.params.email}">
|
||||
renderOption(data) {
|
||||
return `<span class="user-filter-option" title="${data.label.trim()} | ${data.params.email}">
|
||||
<img class="item-avatar" src="${data.params.avatar_url}"/>
|
||||
${data.label}
|
||||
</span>`;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank(){
|
||||
return (this.operator === 'any_of' && this.value.length === 0) ||
|
||||
(this.filter.column.id === 'archived_by' && $('.repository-show').hasClass('active') ) ;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isBlank() {
|
||||
return (this.operator === 'any_of' && this.value.length === 0)
|
||||
|| (this.filter.column.id === 'archived_by' && $('.repository-show').hasClass('active'));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -12,19 +12,19 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FilterElement from './filter.vue'
|
||||
import FilterElement from './filter.vue';
|
||||
|
||||
export default {
|
||||
name: 'FiltersList',
|
||||
props: {
|
||||
filters: Array,
|
||||
my_modules: Array,
|
||||
},
|
||||
components: {FilterElement},
|
||||
methods: {
|
||||
updateFilter(value) {
|
||||
this.$emit('filter:update', value)
|
||||
}
|
||||
export default {
|
||||
name: 'FiltersList',
|
||||
props: {
|
||||
filters: Array,
|
||||
my_modules: Array
|
||||
},
|
||||
components: { FilterElement },
|
||||
methods: {
|
||||
updateFilter(value) {
|
||||
this.$emit('filter:update', value);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -8,25 +8,25 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SavedFilterElement',
|
||||
props: {
|
||||
savedFilter: Object,
|
||||
canManageFilters: Boolean
|
||||
export default {
|
||||
name: 'SavedFilterElement',
|
||||
props: {
|
||||
savedFilter: Object,
|
||||
canManageFilters: Boolean
|
||||
},
|
||||
methods: {
|
||||
loadFilters() {
|
||||
this.$emit('savedFilter:load', this.savedFilter.attributes.show_url);
|
||||
},
|
||||
methods: {
|
||||
loadFilters() {
|
||||
this.$emit('savedFilter:load', this.savedFilter.attributes.show_url)
|
||||
},
|
||||
deleteFilter() {
|
||||
$.ajax({
|
||||
url: this.savedFilter.attributes.delete_url,
|
||||
type: 'DELETE',
|
||||
success: ()=> {
|
||||
this.$emit('savedFilter:delete')
|
||||
}
|
||||
});
|
||||
}
|
||||
deleteFilter() {
|
||||
$.ajax({
|
||||
url: this.savedFilter.attributes.delete_url,
|
||||
type: 'DELETE',
|
||||
success: () => {
|
||||
this.$emit('savedFilter:delete');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
leave-to-class="translate-x-full w-0"
|
||||
v-click-outside="handleOutsideClick">
|
||||
<div ref="wrapper" v-show="isShowing" id="repository-item-sidebar-wrapper"
|
||||
class='items-sidebar-wrapper bg-white gap-2.5 self-stretch rounded-tl-4 rounded-bl-4 shadow-lg h-full w-[565px]'>
|
||||
class='items-sidebar-wrapper bg-white gap-2.5 self-stretch rounded-tl-4 rounded-bl-4 sn-shadow-menu-lg h-full w-[565px]'>
|
||||
|
||||
<div id="repository-item-sidebar" class="w-full h-full pl-6 bg-white flex flex-col">
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
|||
class="sticky top-0 right-0 bg-white flex z-50 flex-col h-[78px] pt-6">
|
||||
<div class="header flex w-full h-[30px] pr-6">
|
||||
<repository-item-sidebar-title v-if="defaultColumns"
|
||||
:editable="permissions?.can_manage && !defaultColumns?.archived"
|
||||
:editable="permissions.can_manage && !defaultColumns?.archived"
|
||||
:name="defaultColumns.name"
|
||||
:archived="defaultColumns.archived"
|
||||
@update="update">
|
||||
|
@ -31,12 +31,12 @@
|
|||
|
||||
<div v-else class="flex flex-1 flex-grow-1 justify-between" ref="scrollSpyContent" id="scrollSpyContent">
|
||||
|
||||
<div id="left-col" class="flex flex-col gap-4 max-w-[350px]">
|
||||
<div id="left-col" class="flex flex-col gap-6 max-w-[350px]">
|
||||
|
||||
<!-- INFORMATION -->
|
||||
<section id="information-section">
|
||||
<div ref="information-label" id="information-label"
|
||||
class="font-inter text-lg font-semibold leading-7 mb-4 transition-colors duration-300">{{
|
||||
class="font-inter text-lg font-semibold leading-7 mb-6 transition-colors duration-300">{{
|
||||
i18n.t('repositories.item_card.section.information') }}
|
||||
</div>
|
||||
<div v-if="defaultColumns">
|
||||
|
@ -113,13 +113,13 @@
|
|||
|
||||
<div id="divider" class="w-500 bg-sn-light-grey flex items-center self-stretch h-px "></div>
|
||||
|
||||
<!-- CUSTOM COLUMNS, ASSIGNED, QR CODE -->
|
||||
<div id="custom-col-assigned-qr-wrapper" class="flex flex-col gap-4">
|
||||
<!-- CUSTOM COLUMNS, RELATIONSHIPS, ASSIGNED, QR CODE -->
|
||||
<div id="custom-col-assigned-qr-wrapper" class="flex flex-col gap-6">
|
||||
|
||||
<!-- CUSTOM COLUMNS -->
|
||||
<section id="custom-columns-section" class="flex flex-col min-h-[64px] h-auto">
|
||||
<div ref="custom-columns-label" id="custom-columns-label"
|
||||
class="font-inter text-lg font-semibold leading-7 pb-4 transition-colors duration-300">
|
||||
class="font-inter text-lg font-semibold leading-7 mb-6 transition-colors duration-300">
|
||||
{{ i18n.t('repositories.item_card.custom_columns_label') }}
|
||||
</div>
|
||||
<CustomColumns :customColumns="customColumns" :repositoryRowId="repositoryRowId"
|
||||
|
@ -129,10 +129,109 @@
|
|||
|
||||
<div id="divider" class="w-500 bg-sn-light-grey flex px-8 items-center self-stretch h-px"></div>
|
||||
|
||||
<!-- RELATIONSHIPS -->
|
||||
<section id="relationships-section" class="flex flex-col" ref="relationshipsSectionRef">
|
||||
<div ref="relationships-label" id="relationships-label"
|
||||
class="font-inter text-lg font-semibold leading-7 mb-6 transition-colors duration-300">
|
||||
{{ i18n.t('repositories.item_card.section.relationships') }}
|
||||
</div>
|
||||
<div class="font-inter text-sm leading-5 w-full">
|
||||
<div class="flex flex-row justify-between mb-4">
|
||||
<div class="font-semibold">
|
||||
{{ i18n.t('repositories.item_card.relationships.parents.count', { count: parentsCount || 0 }) }}
|
||||
</div>
|
||||
<a
|
||||
v-if="permissions.can_connect_rows"
|
||||
class="relationships-add-link btn-text-link font-normal"
|
||||
@click="handleOpenAddRelationshipsModal($event, 'parent')">
|
||||
{{ i18n.t('repositories.item_card.add_relationship_button_text') }}
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="parentsCount">
|
||||
<details v-for="(parent) in parents" @toggle="updateOpenState(parent.code, $event.target.open)" :key="parent.code" class="flex flex-col font-normal gap-4 group cursor-default">
|
||||
<summary class="flex flex-row gap-3 mb-4 relative">
|
||||
<img :src="icons.delimiter_path" class="w-3 h-3 cursor-pointer flex-shrink-0 relative top-1"
|
||||
:class="{ 'rotate-90': relationshipDetailsState[parent.code] }" />
|
||||
<span>
|
||||
<span>{{ i18n.t('repositories.item_card.relationships.item') }}</span>
|
||||
<a :href="parent.path" class="record-info-link btn-text-link !text-sn-science-blue">{{ parent.name }}</a>
|
||||
<button v-if="permissions.can_connect_rows" @click="openUnlinkModal(parent)"
|
||||
class=" ml-2 bg-transparent border-none opacity-0 group-hover:opacity-100 cursor-pointer">
|
||||
<img :src="icons.unlink_path" />
|
||||
</button>
|
||||
</span>
|
||||
</summary>
|
||||
<p class="flex flex-col gap-3 ml-6 mb-4">
|
||||
<span>
|
||||
{{ i18n.t('repositories.item_card.relationships.id', { code: parent.code }) }}
|
||||
</span>
|
||||
<span>
|
||||
<span>{{ i18n.t('repositories.item_card.relationships.inventory') }}</span>
|
||||
<a :href="parent.repository_path" class="btn-text-link !text-sn-science-blue">
|
||||
{{ parent.repository_name }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
<div v-else class="text-sn-dark-grey max-h-5">
|
||||
{{ i18n.t('repositories.item_card.relationships.parents.empty') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sci-divider pb-4"></div>
|
||||
|
||||
<div class="font-inter text-sm leading-5 w-full">
|
||||
<div class="flex flex-row justify-between" :class="{ 'mb-4': childrenCount }">
|
||||
<div class="font-semibold">
|
||||
{{ i18n.t('repositories.item_card.relationships.children.count', { count: childrenCount || 0 }) }}
|
||||
</div>
|
||||
<a
|
||||
v-if="permissions.can_connect_rows"
|
||||
class="relationships-add-link btn-text-link font-normal"
|
||||
@click="handleOpenAddRelationshipsModal($event, 'child')">
|
||||
{{ i18n.t('repositories.item_card.add_relationship_button_text') }}
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="childrenCount">
|
||||
<details v-for="(child) in children" :key="child.code" @toggle="updateOpenState(child.code, $event.target.open)"
|
||||
class="flex flex-col font-normal gap-4 group last-of-type:[&>p:last-child]:mb-0">
|
||||
<summary class="flex flex-row gap-3 mb-4 relative"
|
||||
:class="{ 'group-last-of-type:mb-0': !relationshipDetailsState[child.code] }">
|
||||
<img :src="icons.delimiter_path" class="w-3 h-3 flex-shrink-0 cursor-pointer relative top-1"
|
||||
:class="{ 'rotate-90': relationshipDetailsState[child.code] }"/>
|
||||
<span class="group/child">
|
||||
<span>{{ i18n.t('repositories.item_card.relationships.item') }}</span>
|
||||
<a :href="child.path" class="record-info-link btn-text-link !text-sn-science-blue">{{ child.name }}</a>
|
||||
<button v-if="permissions.can_connect_rows" @click="openUnlinkModal(child)"
|
||||
class="ml-2 bg-transparent border-none opacity-0 group-hover:opacity-100 cursor-pointer">
|
||||
<img :src="icons.unlink_path" />
|
||||
</button>
|
||||
</span>
|
||||
</summary>
|
||||
<p class="flex flex-col gap-3 ml-6 mb-4">
|
||||
<span>{{ i18n.t('repositories.item_card.relationships.id', { code: child.code }) }}</span>
|
||||
<span>
|
||||
<span>{{ i18n.t('repositories.item_card.relationships.inventory') }}</span>
|
||||
<a :href="child.repository_path" class="btn-text-link !text-sn-science-blue">
|
||||
{{ child.repository_name }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
<div v-else class="text-sn-dark-grey max-h-5">
|
||||
{{ i18n.t('repositories.item_card.relationships.children.empty') }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="divider" class="w-500 bg-sn-light-grey flex px-8 items-center self-stretch h-px"></div>
|
||||
|
||||
<!-- ASSIGNED -->
|
||||
<section id="assigned-section" class="flex flex-col" ref="assignedSectionRef">
|
||||
<div
|
||||
class="flex flex-row text-lg font-semibold w-[350px] pb-4 leading-7 items-center justify-between transition-colors duration-300"
|
||||
class="flex flex-row text-lg font-semibold w-[350px] mb-6 leading-7 items-center justify-between transition-colors duration-300"
|
||||
ref="assigned-label"
|
||||
id="assigned-label"
|
||||
>
|
||||
|
@ -163,7 +262,10 @@
|
|||
<div class="flex flex-col gap-2">
|
||||
<div v-for="(item, index_assigned) in assigned" :key="`assigned_element_${index_assigned}`">
|
||||
{{ i18n.t(`repositories.item_card.assigned.labels.${item.type}`) }}
|
||||
<a :href="item.url" class="text-sn-science-blue hover:text-sn-science-blue hover:no-underline">
|
||||
<a v-if="defaultColumns.archived" :href="item.url" class="text-sn-science-blue hover:text-sn-science-blue hover:no-underline">
|
||||
{{ i18n.t('labels.archived')}} {{ item.value }}
|
||||
</a>
|
||||
<a v-else :href="item.url" class="text-sn-science-blue hover:text-sn-science-blue hover:no-underline">
|
||||
{{ item.archived ? i18n.t('labels.archived') : '' }} {{ item.value }}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -181,7 +283,7 @@
|
|||
|
||||
<!-- QR -->
|
||||
<section id="qr-section" ref="QR-label">
|
||||
<div id="QR-label" class="font-inter text-lg font-semibold leading-7 mb-4 mt-0 transition-colors duration-300">
|
||||
<div id="QR-label" class="font-inter text-lg font-semibold leading-7 mb-6 mt-0 transition-colors duration-300">
|
||||
{{ i18n.t('repositories.item_card.section.qr') }}
|
||||
</div>
|
||||
<div class="bar-code-container">
|
||||
|
@ -195,37 +297,8 @@
|
|||
<!-- NAVIGATION -->
|
||||
<div v-if="isShowing && !dataLoading" ref="navigationRef" id="navigation"
|
||||
class="flex item-end gap-x-4 min-w-[130px] min-h-[130px] h-fit sticky top-0 pr-6 [scrollbar-gutter:stable_both-edges] ">
|
||||
<scroll-spy :itemsToCreate="[
|
||||
{
|
||||
id: 'highlight-item-1',
|
||||
textId: 'text-item-1',
|
||||
labelAlias: 'information_label',
|
||||
label: 'information-label',
|
||||
sectionId: 'information-section'
|
||||
},
|
||||
{
|
||||
id: 'highlight-item-2',
|
||||
textId: 'text-item-2',
|
||||
labelAlias: 'custom_columns_label',
|
||||
label: 'custom-columns-label',
|
||||
sectionId: 'custom-columns-section'
|
||||
},
|
||||
{
|
||||
id: 'highlight-item-3',
|
||||
textId: 'text-item-3',
|
||||
labelAlias: 'assigned_label',
|
||||
label: 'assigned-label',
|
||||
sectionId: 'assigned-section'
|
||||
},
|
||||
{
|
||||
id: 'highlight-item-4',
|
||||
textId: 'text-item-4',
|
||||
labelAlias: 'QR_label',
|
||||
label: 'QR-label',
|
||||
sectionId: 'qr-section'
|
||||
}
|
||||
]" v-show="isShowing">
|
||||
</scroll-spy>
|
||||
|
||||
<scroll-spy v-show="isShowing" :initialSectionId="initialSectionId" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -235,7 +308,8 @@
|
|||
<div id="divider" class="w-500 bg-sn-light-grey flex px-8 items-center self-stretch h-px mb-6"></div>
|
||||
<div id="bottom-button-wrapper" class="flex h-10 justify-end">
|
||||
<button type="button" class="btn btn-primary print-label-button" data-e2e="e2e-BT-invInventoryItemSB-print"
|
||||
:data-rows="JSON.stringify([repositoryRowId])">
|
||||
:data-rows="JSON.stringify([repositoryRowId])"
|
||||
:data-repository-id="repository?.id">
|
||||
{{ i18n.t('repositories.item_card.print_label') }}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -245,6 +319,10 @@
|
|||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<Teleport to="body">
|
||||
<unlink-modal v-if="selectedToUnlink" @cancel="closeUnlinkModal" @unlink="unlinkItem" />
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -252,7 +330,9 @@ import { vOnClickOutside } from '@vueuse/components';
|
|||
import InlineEdit from '../shared/inline_edit.vue';
|
||||
import ScrollSpy from './repository_values/ScrollSpy.vue';
|
||||
import CustomColumns from './customColumns.vue';
|
||||
import RepositoryItemSidebarTitle from './Title.vue'
|
||||
import RepositoryItemSidebarTitle from './Title.vue';
|
||||
import UnlinkModal from './unlink_modal.vue';
|
||||
import axios from '../../packs/custom_axios.js';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryItemSidebar',
|
||||
|
@ -261,6 +341,7 @@ export default {
|
|||
'repository-item-sidebar-title': RepositoryItemSidebarTitle,
|
||||
'inline-edit': InlineEdit,
|
||||
'scroll-spy': ScrollSpy,
|
||||
'unlink-modal': UnlinkModal
|
||||
},
|
||||
directives: {
|
||||
'click-outside': vOnClickOutside
|
||||
|
@ -274,20 +355,30 @@ export default {
|
|||
repository: null,
|
||||
defaultColumns: null,
|
||||
customColumns: null,
|
||||
parentsCount: 0,
|
||||
childrenCount: 0,
|
||||
parents: null,
|
||||
children: null,
|
||||
assignedModules: null,
|
||||
isShowing: false,
|
||||
barCodeSrc: null,
|
||||
permissions: null,
|
||||
permissions: {},
|
||||
repositoryRowUrl: null,
|
||||
actions: null,
|
||||
myModuleId: null,
|
||||
inRepository: false
|
||||
}
|
||||
inRepository: false,
|
||||
icons: null,
|
||||
notification: null,
|
||||
relationshipDetailsState: {},
|
||||
relationshipsEnabled: false,
|
||||
selectedToUnlink: null,
|
||||
initialSectionId: null
|
||||
};
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
reloadRepoItemSidebar: this.reload,
|
||||
}
|
||||
reloadRepoItemSidebar: this.reload
|
||||
};
|
||||
},
|
||||
created() {
|
||||
window.repositoryItemSidebarComponent = this;
|
||||
|
@ -297,6 +388,18 @@ export default {
|
|||
return this.defaultColumns?.archived ? `${I18n.t('labels.archived')} ${this.defaultColumns?.name}` : this.defaultColumns?.name;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
parents(newParents) {
|
||||
newParents.forEach((parent) => {
|
||||
this.relationshipDetailsState[parent.code] = false;
|
||||
});
|
||||
},
|
||||
children(newChildren) {
|
||||
newChildren.forEach((child) => {
|
||||
this.relationshipDetailsState[child.code] = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// Add a click event listener to the document
|
||||
this.inRepository = $('.assign-items-to-task-modal-container').length > 0;
|
||||
|
@ -305,6 +408,30 @@ export default {
|
|||
delete window.repositoryItemSidebarComponent;
|
||||
},
|
||||
methods: {
|
||||
handleOpenAddRelationshipsModal(event, relation) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
const addRelationCallback = (data, connectionRelation) => {
|
||||
if (connectionRelation === 'parent') {
|
||||
this.parentsCount = data.parents.length;
|
||||
this.parents = data.parents;
|
||||
}
|
||||
if (connectionRelation === 'child') {
|
||||
this.childrenCount = data.children.length;
|
||||
this.children = data.children;
|
||||
}
|
||||
};
|
||||
window.repositoryItemRelationshipsModal.show(
|
||||
{
|
||||
relation,
|
||||
addRelationCallback,
|
||||
optionUrls: { ...this.actions.row_connections },
|
||||
notificationIconPath: this.icons.notification_path,
|
||||
notification: this.notification,
|
||||
canConnectRows: this.permissions.can_connect_rows
|
||||
}
|
||||
);
|
||||
},
|
||||
handleOutsideClick(event) {
|
||||
if (!this.isShowing) return;
|
||||
|
||||
|
@ -326,7 +453,11 @@ export default {
|
|||
this.toggleShowHideSidebar(null);
|
||||
}
|
||||
},
|
||||
toggleShowHideSidebar(repositoryRowUrl, myModuleId = null) {
|
||||
toggleShowHideSidebar(repositoryRowUrl, myModuleId = null, initialSectionId = null) {
|
||||
if (initialSectionId) {
|
||||
this.initialSectionId = initialSectionId;
|
||||
} else this.initialSectionId = null;
|
||||
|
||||
// initial click
|
||||
if (this.currentItemUrl === null) {
|
||||
this.myModuleId = myModuleId;
|
||||
|
@ -368,11 +499,18 @@ export default {
|
|||
this.optionsPath = result.options_path;
|
||||
this.updatePath = result.update_path;
|
||||
this.defaultColumns = result.default_columns;
|
||||
this.relationshipsEnabled = result.relationships.enabled;
|
||||
this.parentsCount = result.relationships.parents_count;
|
||||
this.childrenCount = result.relationships.children_count;
|
||||
this.parents = result.relationships.parents;
|
||||
this.children = result.relationships.children;
|
||||
this.customColumns = result.custom_columns;
|
||||
this.assignedModules = result.assigned_modules;
|
||||
this.permissions = result.permissions;
|
||||
this.actions = result.actions;
|
||||
this.icons = result.icons;
|
||||
this.dataLoading = false;
|
||||
this.notification = result.notification;
|
||||
this.$nextTick(() => {
|
||||
this.generateBarCode(this.defaultColumns.code);
|
||||
|
||||
|
@ -412,15 +550,30 @@ export default {
|
|||
dataType: 'json',
|
||||
data: {
|
||||
id: this.id,
|
||||
...params,
|
||||
},
|
||||
...params
|
||||
}
|
||||
}).done((response) => {
|
||||
if (response) {
|
||||
this.customColumns = this.customColumns.map(col => col.id === response.id ? { ...col, ...response } : col)
|
||||
if ($('.dataTable')[0]) $('.dataTable').DataTable().ajax.reload(null, false);
|
||||
this.customColumns = this.customColumns.map((col) => (col.id === response.id ? { ...col, ...response } : col));
|
||||
if ($('.dataTable.repository-dataTable')[0]) $('.dataTable.repository-dataTable').DataTable().ajax.reload(null, false);
|
||||
}
|
||||
});
|
||||
},
|
||||
updateOpenState(code, isOpen) {
|
||||
this.relationshipDetailsState[code] = isOpen;
|
||||
},
|
||||
openUnlinkModal(item) {
|
||||
this.selectedToUnlink = item;
|
||||
},
|
||||
closeUnlinkModal() {
|
||||
this.selectedToUnlink = null;
|
||||
},
|
||||
async unlinkItem() {
|
||||
await axios.delete(this.selectedToUnlink.unlink_path);
|
||||
this.loadRepositoryRow(this.currentItemUrl);
|
||||
if ($('.dataTable.repository-dataTable')[0]) $('.dataTable.repository-dataTable').DataTable().ajax.reload(null, false);
|
||||
this.selectedToUnlink = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -10,28 +10,28 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import InlineEdit from "../shared/inline_edit.vue";
|
||||
import InlineEdit from '../shared/inline_edit.vue';
|
||||
|
||||
export default {
|
||||
name: "RepositoryItemSidebarTitle",
|
||||
name: 'RepositoryItemSidebarTitle',
|
||||
components: {
|
||||
"inline-edit": InlineEdit
|
||||
'inline-edit': InlineEdit
|
||||
},
|
||||
emits: ['update'],
|
||||
props: {
|
||||
editable: Boolean,
|
||||
name: String,
|
||||
archived: Boolean,
|
||||
archived: Boolean
|
||||
},
|
||||
computed: {
|
||||
computedName() {
|
||||
return this.archived ? `(A) ${this.name}` : this.name;
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateName(name) {
|
||||
this.$emit('update', { 'repository_row': { name: name } });
|
||||
},
|
||||
},
|
||||
this.$emit('update', { repository_row: { name } });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -30,55 +30,55 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import RepositoryStockValue from './repository_values/RepositoryStockValue.vue';
|
||||
import RepositoryTextValue from './repository_values/RepositoryTextValue.vue';
|
||||
import RepositoryNumberValue from './repository_values/RepositoryNumberValue.vue';
|
||||
import RepositoryAssetValue from './repository_values/RepositoryAssetValue.vue';
|
||||
import RepositoryListValue from './repository_values/RepositoryListValue.vue';
|
||||
import RepositoryChecklistValue from './repository_values/RepositoryChecklistValue.vue';
|
||||
import RepositoryStatusValue from './repository_values/RepositoryStatusValue.vue';
|
||||
import RepositoryDateTimeValue from './repository_values/RepositoryDateTimeValue.vue';
|
||||
import RepositoryDateTimeRangeValue from './repository_values/RepositoryDateTimeRangeValue.vue';
|
||||
import RepositoryDateValue from './repository_values/RepositoryDateValue.vue';
|
||||
import RepositoryDateRangeValue from './repository_values/RepositoryDateRangeValue.vue';
|
||||
import RepositoryTimeRangeValue from './repository_values/RepositoryTimeRangeValue.vue'
|
||||
import RepositoryTimeValue from './repository_values/RepositoryTimeValue.vue'
|
||||
import RepositoryStockValue from './repository_values/RepositoryStockValue.vue';
|
||||
import RepositoryTextValue from './repository_values/RepositoryTextValue.vue';
|
||||
import RepositoryNumberValue from './repository_values/RepositoryNumberValue.vue';
|
||||
import RepositoryAssetValue from './repository_values/RepositoryAssetValue.vue';
|
||||
import RepositoryListValue from './repository_values/RepositoryListValue.vue';
|
||||
import RepositoryChecklistValue from './repository_values/RepositoryChecklistValue.vue';
|
||||
import RepositoryStatusValue from './repository_values/RepositoryStatusValue.vue';
|
||||
import RepositoryDateTimeValue from './repository_values/RepositoryDateTimeValue.vue';
|
||||
import RepositoryDateTimeRangeValue from './repository_values/RepositoryDateTimeRangeValue.vue';
|
||||
import RepositoryDateValue from './repository_values/RepositoryDateValue.vue';
|
||||
import RepositoryDateRangeValue from './repository_values/RepositoryDateRangeValue.vue';
|
||||
import RepositoryTimeRangeValue from './repository_values/RepositoryTimeRangeValue.vue';
|
||||
import RepositoryTimeValue from './repository_values/RepositoryTimeValue.vue';
|
||||
|
||||
export default {
|
||||
name: 'CustomColumns',
|
||||
components: {
|
||||
RepositoryStockValue,
|
||||
RepositoryTextValue,
|
||||
RepositoryNumberValue,
|
||||
RepositoryAssetValue,
|
||||
RepositoryListValue,
|
||||
RepositoryChecklistValue,
|
||||
RepositoryStatusValue,
|
||||
RepositoryDateTimeValue,
|
||||
RepositoryDateTimeRangeValue,
|
||||
RepositoryDateValue,
|
||||
RepositoryDateRangeValue,
|
||||
RepositoryTimeRangeValue,
|
||||
RepositoryTimeValue
|
||||
},
|
||||
props: {
|
||||
customColumns: { type: Array, default: () => [] },
|
||||
permissions: { type: Object, default: () => {} },
|
||||
updatePath: { type: String, default: '' },
|
||||
repositoryRowId: { type: Number, default: null },
|
||||
repositoryId: { type: Number, default: null },
|
||||
inArchivedRepositoryRow: { type: Boolean, default: false },
|
||||
actions: {type: Object, default: () => {}}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editingField: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
update(params) {
|
||||
this.$emit('update', params);
|
||||
}
|
||||
export default {
|
||||
name: 'CustomColumns',
|
||||
components: {
|
||||
RepositoryStockValue,
|
||||
RepositoryTextValue,
|
||||
RepositoryNumberValue,
|
||||
RepositoryAssetValue,
|
||||
RepositoryListValue,
|
||||
RepositoryChecklistValue,
|
||||
RepositoryStatusValue,
|
||||
RepositoryDateTimeValue,
|
||||
RepositoryDateTimeRangeValue,
|
||||
RepositoryDateValue,
|
||||
RepositoryDateRangeValue,
|
||||
RepositoryTimeRangeValue,
|
||||
RepositoryTimeValue
|
||||
},
|
||||
props: {
|
||||
customColumns: { type: Array, default: () => [] },
|
||||
permissions: { type: Object, default: () => {} },
|
||||
updatePath: { type: String, default: '' },
|
||||
repositoryRowId: { type: Number, default: null },
|
||||
repositoryId: { type: Number, default: null },
|
||||
inArchivedRepositoryRow: { type: Boolean, default: false },
|
||||
actions: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editingField: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
update(params) {
|
||||
this.$emit('update', params);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -74,7 +74,7 @@ export default {
|
|||
break;
|
||||
}
|
||||
Object.assign($this.$data, { isEditing: false, isSaving: false, values: result?.value });
|
||||
if ($('.dataTable')[0]) $('.dataTable').DataTable().ajax.reload(null, false);
|
||||
if ($('.dataTable.repository-dataTable')[0]) $('.dataTable.repository-dataTable').DataTable().ajax.reload(null, false);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -7,24 +7,24 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Reminder',
|
||||
props: {
|
||||
value: null
|
||||
},
|
||||
computed: {
|
||||
reminderColor() {
|
||||
if (this.value?.reminder && (this.value?.stock_amount > 0 || this.value?.days_left > 0)) {
|
||||
return 'bg-sn-alert-brittlebush';
|
||||
}
|
||||
return 'bg-sn-alert-passion';
|
||||
},
|
||||
reminderTitle() {
|
||||
let title = this.value?.reminder_text
|
||||
if (this.value?.reminder_message) title = `${title}\n${this.value?.reminder_message}`;
|
||||
|
||||
return title;
|
||||
export default {
|
||||
name: 'Reminder',
|
||||
props: {
|
||||
value: null
|
||||
},
|
||||
computed: {
|
||||
reminderColor() {
|
||||
if (this.value?.reminder && (this.value?.stock_amount > 0 || this.value?.days_left > 0)) {
|
||||
return 'bg-sn-alert-brittlebush';
|
||||
}
|
||||
return 'bg-sn-alert-passion';
|
||||
},
|
||||
reminderTitle() {
|
||||
let title = this.value?.reminder_text;
|
||||
if (this.value?.reminder_message) title = `${title}\n${this.value?.reminder_message}`;
|
||||
|
||||
return title;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -48,12 +48,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import TooltipPreview from './TooltipPreview.vue'
|
||||
import TooltipPreview from './TooltipPreview.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryAssetvalue',
|
||||
components: {
|
||||
"tooltip-preview": TooltipPreview
|
||||
'tooltip-preview': TooltipPreview
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -67,7 +67,7 @@ export default {
|
|||
uploading: false,
|
||||
progress: 0,
|
||||
error: false
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
data_type: String,
|
||||
|
@ -80,18 +80,18 @@ export default {
|
|||
canEdit: { type: Boolean, default: false }
|
||||
},
|
||||
created() {
|
||||
if (!this.colVal) return
|
||||
if (!this.colVal) return;
|
||||
|
||||
this.id = this.colVal.id
|
||||
this.url = this.colVal.url
|
||||
this.preview_url = this.colVal.preview_url
|
||||
this.file_name = this.colVal.file_name
|
||||
this.icon_html = this.colVal.icon_html
|
||||
this.medium_preview_url = this.colVal.medium_preview_url
|
||||
this.id = this.colVal.id;
|
||||
this.url = this.colVal.url;
|
||||
this.preview_url = this.colVal.preview_url;
|
||||
this.file_name = this.colVal.file_name;
|
||||
this.icon_html = this.colVal.icon_html;
|
||||
this.medium_preview_url = this.colVal.medium_preview_url;
|
||||
},
|
||||
computed: {
|
||||
modalPreviewLinkId() {
|
||||
return `modal_link${this.id}`
|
||||
return `modal_link${this.id}`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -114,21 +114,25 @@ export default {
|
|||
this.error = '';
|
||||
|
||||
if (file.size > GLOBAL_CONSTANTS.FILE_MAX_SIZE_MB * 1024 * 1024) {
|
||||
this.error = I18n.t('repositories.item_card.repository_asset_value.errors.file_too_big',
|
||||
{ file_size: GLOBAL_CONSTANTS.FILE_MAX_SIZE_MB });
|
||||
this.error = I18n.t(
|
||||
'repositories.item_card.repository_asset_value.errors.file_too_big',
|
||||
{ file_size: GLOBAL_CONSTANTS.FILE_MAX_SIZE_MB }
|
||||
);
|
||||
this.uploading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const upload = new ActiveStorage.DirectUpload(file,
|
||||
this.actions.direct_file_upload_path,
|
||||
{
|
||||
directUploadWillStoreFileWithXHR: (request) => {
|
||||
request.upload.addEventListener('progress', (e) => {
|
||||
this.progress = parseInt((e.loaded / e.total) * 100, 10);
|
||||
});
|
||||
}
|
||||
});
|
||||
const upload = new ActiveStorage.DirectUpload(
|
||||
file,
|
||||
this.actions.direct_file_upload_path,
|
||||
{
|
||||
directUploadWillStoreFileWithXHR: (request) => {
|
||||
request.upload.addEventListener('progress', (e) => {
|
||||
this.progress = parseInt((e.loaded / e.total) * 100, 10);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
upload.create((error, blob) => {
|
||||
if (error) {
|
||||
|
@ -149,7 +153,7 @@ export default {
|
|||
}
|
||||
},
|
||||
success: (result) => {
|
||||
let assetRepositoryCell = result?.value;
|
||||
const assetRepositoryCell = result?.value;
|
||||
this.uploading = false;
|
||||
|
||||
if (assetRepositoryCell) {
|
||||
|
@ -162,7 +166,7 @@ export default {
|
|||
} else {
|
||||
this.file_name = '';
|
||||
}
|
||||
if ($('.dataTable')[0]) $('.dataTable').DataTable().ajax.reload(null, false);
|
||||
if ($('.dataTable.repository-dataTable')[0]) $('.dataTable.repository-dataTable').DataTable().ajax.reload(null, false);
|
||||
},
|
||||
error: () => {
|
||||
this.error = I18n.t('repositories.item_card.repository_asset_value.errors.upload_failed_general');
|
||||
|
@ -171,5 +175,5 @@ export default {
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
:options="checklistItems"
|
||||
:placeholder="i18n.t('repositories.item_card.dropdown_placeholder')"
|
||||
:no-options-placeholder="i18n.t('repositories.item_card.dropdown_placeholder')"
|
||||
className="h-[38px] pl-3"
|
||||
className="h-[38px] !pl-3"
|
||||
optionsClassName="max-h-[300px]"
|
||||
></checklist-select>
|
||||
</div>
|
||||
|
@ -39,14 +39,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ChecklistSelect from "../../shared/legacy/checklist_select.vue";
|
||||
import repositoryValueMixin from "./mixins/repository_value.js";
|
||||
import ChecklistSelect from '../../shared/legacy/checklist_select.vue';
|
||||
import repositoryValueMixin from './mixins/repository_value.js';
|
||||
|
||||
export default {
|
||||
name: "RepositoryChecklistValue",
|
||||
name: 'RepositoryChecklistValue',
|
||||
mixins: [repositoryValueMixin],
|
||||
components: {
|
||||
"checklist-select": ChecklistSelect
|
||||
'checklist-select': ChecklistSelect
|
||||
},
|
||||
props: {
|
||||
data_type: String,
|
||||
|
@ -67,20 +67,20 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
this.fetchChecklistItems();
|
||||
if(this.colVal) {
|
||||
if (this.colVal) {
|
||||
this.selectedChecklistItems = Array.isArray(this.colVal) ? this.colVal : [this.colVal];
|
||||
this.selectedValues = this.selectedChecklistItems.map(item => item?.value);
|
||||
this.selectedValues = this.selectedChecklistItems.map((item) => item?.value);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchChecklistItems() {
|
||||
this.isLoading = true;
|
||||
|
||||
$.get(this.optionsPath, data => {
|
||||
$.get(this.optionsPath, (data) => {
|
||||
if (Array.isArray(data)) {
|
||||
this.checklistItems = data.map(option => {
|
||||
this.checklistItems = data.map((option) => {
|
||||
const { value, label } = option;
|
||||
return { id: value, label: label };
|
||||
return { id: value, label };
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -16,16 +16,16 @@
|
|||
<script>
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateRangeValue',
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: String,
|
||||
canEdit: { type: Boolean, default: false },
|
||||
}
|
||||
export default {
|
||||
name: 'RepositoryDateRangeValue',
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: String,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -13,18 +13,18 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateTimeRangeValue',
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: String,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
export default {
|
||||
name: 'RepositoryDateTimeRangeValue',
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: String,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -12,18 +12,18 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateTimeValue',
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: String,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
export default {
|
||||
name: 'RepositoryDateTimeValue',
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: String,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="flex flex-col gap2">
|
||||
<div class="flex flex-col gap-2">
|
||||
<DateTimeComponent
|
||||
mode="date"
|
||||
:colVal="colVal"
|
||||
|
@ -12,7 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateValue',
|
||||
|
@ -26,5 +26,5 @@ export default {
|
|||
editingField: null,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -35,13 +35,13 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import SelectSearch from "../../shared/legacy/select_search.vue";
|
||||
import repositoryValueMixin from "./mixins/repository_value.js";
|
||||
import SelectSearch from '../../shared/legacy/select_search.vue';
|
||||
import repositoryValueMixin from './mixins/repository_value.js';
|
||||
|
||||
export default {
|
||||
name: "RepositoryListValue",
|
||||
name: 'RepositoryListValue',
|
||||
components: {
|
||||
"select-search": SelectSearch
|
||||
'select-search': SelectSearch
|
||||
},
|
||||
mixins: [repositoryValueMixin],
|
||||
props: {
|
||||
|
@ -51,7 +51,7 @@ export default {
|
|||
colVal: Object,
|
||||
optionsPath: String,
|
||||
permissions: null,
|
||||
inArchivedRepositoryRow: Boolean,
|
||||
inArchivedRepositoryRow: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -69,9 +69,9 @@ export default {
|
|||
mounted() {
|
||||
this.isLoading = true;
|
||||
|
||||
$.get(this.optionsPath, data => {
|
||||
$.get(this.optionsPath, (data) => {
|
||||
if (Array.isArray(data)) {
|
||||
this.options = data.map(option => {
|
||||
this.options = data.map((option) => {
|
||||
const { value, label } = option;
|
||||
return [value, label];
|
||||
});
|
||||
|
|
|
@ -43,20 +43,20 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import repositoryValueMixin from "./mixins/repository_value.js";
|
||||
import Textarea from "../../shared/legacy/Textarea.vue";
|
||||
import repositoryValueMixin from './mixins/repository_value.js';
|
||||
import Textarea from '../../shared/legacy/Textarea.vue';
|
||||
|
||||
export default {
|
||||
name: "RepositoryNumberValue",
|
||||
name: 'RepositoryNumberValue',
|
||||
mixins: [repositoryValueMixin],
|
||||
components: {
|
||||
'text-area': Textarea,
|
||||
'text-area': Textarea
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expandable: false,
|
||||
collapsed: true,
|
||||
numberValue: '',
|
||||
numberValue: ''
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
@ -66,7 +66,16 @@ export default {
|
|||
colVal: Number,
|
||||
permissions: null,
|
||||
decimals: { type: Number, default: 0 },
|
||||
canEdit: { type: Boolean, default: false },
|
||||
canEdit: { type: Boolean, default: false }
|
||||
},
|
||||
mounted() {
|
||||
const maxCollapsedHeight = 60;
|
||||
const numberRefEl = this.$refs.numberRef;
|
||||
this.$nextTick(() => {
|
||||
if (!numberRefEl) return;
|
||||
const isExpandable = numberRefEl.scrollHeight > maxCollapsedHeight;
|
||||
this.expandable = isExpandable;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
toggleCollapse() {
|
||||
|
@ -76,7 +85,7 @@ export default {
|
|||
},
|
||||
toggleExpandableState(expandable) {
|
||||
this.expandable = expandable;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -36,14 +36,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import SelectSearch from "../../shared/legacy/select_search.vue";
|
||||
import repositoryValueMixin from "./mixins/repository_value.js";
|
||||
import twemoji from "twemoji";
|
||||
import twemoji from 'twemoji';
|
||||
import SelectSearch from '../../shared/legacy/select_search.vue';
|
||||
import repositoryValueMixin from './mixins/repository_value.js';
|
||||
|
||||
export default {
|
||||
name: "RepositoryStatusValue",
|
||||
name: 'RepositoryStatusValue',
|
||||
components: {
|
||||
"select-search": SelectSearch
|
||||
'select-search': SelectSearch
|
||||
},
|
||||
mixins: [repositoryValueMixin],
|
||||
data() {
|
||||
|
@ -63,7 +63,7 @@ export default {
|
|||
colVal: Object,
|
||||
optionsPath: String,
|
||||
permissions: null,
|
||||
inArchivedRepositoryRow: Boolean,
|
||||
inArchivedRepositoryRow: Boolean
|
||||
},
|
||||
created() {
|
||||
if (!this.colVal) return;
|
||||
|
@ -75,9 +75,9 @@ export default {
|
|||
mounted() {
|
||||
this.isLoading = true;
|
||||
|
||||
$.get(this.optionsPath, data => {
|
||||
$.get(this.optionsPath, (data) => {
|
||||
if (Array.isArray(data)) {
|
||||
this.options = data.map(option => {
|
||||
this.options = data.map((option) => {
|
||||
const { value, label } = option;
|
||||
return [value, label];
|
||||
});
|
||||
|
@ -103,7 +103,7 @@ export default {
|
|||
},
|
||||
replaceEmojiesInDropdown() {
|
||||
setTimeout(() => {
|
||||
twemoji.size = "24x24";
|
||||
twemoji.size = '24x24';
|
||||
twemoji.base = '/images/twemoji/';
|
||||
twemoji.parse(this.$refs.container);
|
||||
}, 300);
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<div id="repository-stock-value-wrapper" class="flex flex-col min-min-h-[46px] h-auto gap-[6px]">
|
||||
<div class="font-inter text-sm font-semibold leading-5 relative h-[20px]">
|
||||
<span class="truncate w-full inline-block pr-[50px]" :title="colName">{{ colName }}</span>
|
||||
<div class="font-inter text-sm font-semibold leading-5 relative h-[20px] flex flex-row">
|
||||
<div class="flex flex-row gap-1">
|
||||
<span class="truncate w-fit inline-block" :title="colName">{{ colName }}</span>
|
||||
<div v-if="values?.reminder" class="bg-sn-alert-passion w-1.5 h-1.5 min-w-[0.375rem] min-h-[0.375rem] rounded hover:cursor-pointer" :title="values.reminder_text"></div>
|
||||
</div>
|
||||
<a style="text-decoration: none;" class="absolute right-0 btn-text-link font-normal export-consumption-button"
|
||||
v-if="permissions?.can_export_repository_stock === true && values?.stock_formatted" :data-rows="JSON.stringify([repositoryRowId])"
|
||||
:data-object-id="repositoryId">
|
||||
|
@ -22,68 +25,66 @@
|
|||
<div v-else class="font-inter text-sm font-normal leading-5" :class="{ 'text-sn-dark-grey': !canEdit, 'text-sn-grey': canEdit }">
|
||||
{{ i18n.t(`repositories.item_card.repository_stock_value.${canEdit ? 'placeholder' : 'no_stock'}`) }}
|
||||
</div>
|
||||
<span class="absolute right-2 reminder" :class="{ 'top-1.5': canEdit, 'top-0': !canEdit, hidden: !values?.reminder }">
|
||||
<Reminder :value="values" />
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Reminder from '../reminder.vue';
|
||||
export default {
|
||||
name: 'RepositoryStockValue',
|
||||
components: {
|
||||
Reminder
|
||||
import Reminder from '../reminder.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryStockValue',
|
||||
components: {
|
||||
Reminder
|
||||
},
|
||||
computed: {
|
||||
editableClassName() {
|
||||
const className = 'border-solid border-[1px] p-2 pl-3 manage-repository-stock-value-link sci-cursor-edit';
|
||||
if (this.canEdit && this.isEditing) return `${className} border-sn-science-blue`;
|
||||
if (this.canEdit) return `${className} border-sn-light-grey hover:border-sn-sleepy-grey`;
|
||||
return '';
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
stock_formatted: null,
|
||||
stock_amount: null,
|
||||
low_stock_threshold: null,
|
||||
isEditing: false,
|
||||
values: null
|
||||
};
|
||||
},
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
repositoryId: Number,
|
||||
repositoryRowId: null,
|
||||
permissions: null,
|
||||
canEdit: { type: Boolean, default: false },
|
||||
actions: null
|
||||
},
|
||||
mounted() {
|
||||
this.values = this.colVal || {};
|
||||
this.values.stock_url = this.actions?.stock_value_url;
|
||||
window.manageStockCallback = this.submitCallback;
|
||||
},
|
||||
unmounted() {
|
||||
delete window.manageStockCallback;
|
||||
},
|
||||
methods: {
|
||||
enableEditing() {
|
||||
this.isEditing = true;
|
||||
const $this = this;
|
||||
// disable edit
|
||||
$('#manageStockValueModal').on('hide.bs.modal', () => {
|
||||
$this.isEditing = false;
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
editableClassName() {
|
||||
const className = 'border-solid border-[1px] p-2 pl-3 manage-repository-stock-value-link sci-cursor-edit'
|
||||
if (this.canEdit && this.isEditing) return `${className} border-sn-science-blue`;
|
||||
if (this.canEdit) return `${className} border-sn-light-grey hover:border-sn-sleepy-grey`;
|
||||
return ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
stock_formatted: null,
|
||||
stock_amount: null,
|
||||
low_stock_threshold: null,
|
||||
isEditing: false,
|
||||
values: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
repositoryId: Number,
|
||||
repositoryRowId: null,
|
||||
permissions: null,
|
||||
canEdit: { type: Boolean, default: false },
|
||||
actions: null
|
||||
},
|
||||
mounted() {
|
||||
this.values = this.colVal || {};
|
||||
this.values.stock_url = this.actions?.stock_value_url
|
||||
window.manageStockCallback = this.submitCallback;
|
||||
},
|
||||
unmounted(){
|
||||
delete window.manageStockCallback
|
||||
},
|
||||
methods: {
|
||||
enableEditing(){
|
||||
this.isEditing = true
|
||||
const $this = this;
|
||||
// disable edit
|
||||
$('#manageStockValueModal').on('hide.bs.modal', function() {
|
||||
$this.isEditing = false;
|
||||
})
|
||||
},
|
||||
submitCallback(values) {
|
||||
if (values) this.values = values;
|
||||
}
|
||||
submitCallback(values) {
|
||||
if (values) this.values = values;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -45,20 +45,20 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import repositoryValueMixin from "./mixins/repository_value.js";
|
||||
import Textarea from "../../shared/legacy/Textarea.vue";
|
||||
import repositoryValueMixin from './mixins/repository_value.js';
|
||||
import Textarea from '../../shared/legacy/Textarea.vue';
|
||||
|
||||
export default {
|
||||
name: "RepositoryTextValue",
|
||||
name: 'RepositoryTextValue',
|
||||
mixins: [repositoryValueMixin],
|
||||
components: {
|
||||
'text-area': Textarea,
|
||||
'text-area': Textarea
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expandable: false,
|
||||
collapsed: true,
|
||||
textValue: '',
|
||||
textValue: ''
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
@ -70,7 +70,16 @@ export default {
|
|||
},
|
||||
created() {
|
||||
// constants
|
||||
this.noContentPlaceholder = this.i18n.t("repositories.item_card.repository_text_value.no_text");
|
||||
this.noContentPlaceholder = this.i18n.t('repositories.item_card.repository_text_value.no_text');
|
||||
},
|
||||
mounted() {
|
||||
const maxCollapsedHeight = 60;
|
||||
const textRefEl = this.$refs.textRef;
|
||||
this.$nextTick(() => {
|
||||
if (!textRefEl) return;
|
||||
const isExpandable = textRefEl.scrollHeight > maxCollapsedHeight;
|
||||
this.expandable = isExpandable;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
toggleCollapse() {
|
||||
|
@ -80,7 +89,7 @@ export default {
|
|||
},
|
||||
toggleExpandableState(expandable) {
|
||||
this.expandable = expandable;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -13,18 +13,19 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
export default {
|
||||
name: 'RepositoryTimeRangeValue',
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: null,
|
||||
editingField: null,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryTimeRangeValue',
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: null,
|
||||
editingField: null,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -12,18 +12,19 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
export default {
|
||||
name: 'RepositoryTimeValue',
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: null,
|
||||
editingField: null,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
}
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryTimeValue',
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: null,
|
||||
editingField: null,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -21,11 +21,49 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
const items = [
|
||||
{
|
||||
id: 'highlight-item-1',
|
||||
textId: 'text-item-1',
|
||||
labelAlias: 'information_label',
|
||||
label: 'information-label',
|
||||
sectionId: 'information-section'
|
||||
},
|
||||
{
|
||||
id: 'highlight-item-2',
|
||||
textId: 'text-item-2',
|
||||
labelAlias: 'custom_columns_label',
|
||||
label: 'custom-columns-label',
|
||||
sectionId: 'custom-columns-section'
|
||||
},
|
||||
{
|
||||
id: 'highlight-item-3',
|
||||
textId: 'text-item-3',
|
||||
labelAlias: 'relationships_label',
|
||||
label: 'relationships-label',
|
||||
sectionId: 'relationships-section'
|
||||
},
|
||||
{
|
||||
id: 'highlight-item-4',
|
||||
textId: 'text-item-4',
|
||||
labelAlias: 'assigned_label',
|
||||
label: 'assigned-label',
|
||||
sectionId: 'assigned-section'
|
||||
},
|
||||
{
|
||||
id: 'highlight-item-5',
|
||||
textId: 'text-item-5',
|
||||
labelAlias: 'QR_label',
|
||||
label: 'QR-label',
|
||||
sectionId: 'qr-section'
|
||||
}
|
||||
];
|
||||
export default {
|
||||
name: 'ScrollSpy',
|
||||
|
||||
props: {
|
||||
itemsToCreate: Array,
|
||||
itemsToCreate: { type: Array, default: () => items },
|
||||
initialSectionId: String || null
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -37,25 +75,23 @@ export default {
|
|||
thresholds: [],
|
||||
navigationItemsStatus: [], // highlighted or not
|
||||
scrollPosition: null,
|
||||
centerOfScrollThumb: null,
|
||||
centerOfScrollThumb: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
this.initializeComponent();
|
||||
this.$nextTick(() => {
|
||||
this.calculateAllSectionsCumulativeHeight()
|
||||
this.calculateAllSectionsCumulativeHeight();
|
||||
this.calculateSectionsHeight();
|
||||
this.constructThresholds()
|
||||
this.handleScroll()
|
||||
this.constructThresholds();
|
||||
this.handleScroll();
|
||||
|
||||
if (!this.initialSectionId) {
|
||||
this.navigateToSection(this.itemsToCreate[0])
|
||||
}
|
||||
else {
|
||||
const itemToNavigateTo = this.itemsToCreate.find((item) => item.sectionId === this.initialSectionId)
|
||||
this.navigateToSection(itemToNavigateTo)
|
||||
this.navigateToSection(this.itemsToCreate[0]);
|
||||
} else {
|
||||
const itemToNavigateTo = this.itemsToCreate.find((item) => item.sectionId === this.initialSectionId);
|
||||
this.navigateToSection(itemToNavigateTo);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -64,12 +100,11 @@ export default {
|
|||
window.removeEventListener('resize', this.handleResize);
|
||||
this.removeScrollListener();
|
||||
},
|
||||
|
||||
methods: {
|
||||
initializeComponent() {
|
||||
const bodyWrapperEl = document.getElementById('body-wrapper')
|
||||
const scrollSpyContentEl = document.getElementById('scrollSpyContent')
|
||||
this.bodyContainerEl = bodyWrapperEl
|
||||
const bodyWrapperEl = document.getElementById('body-wrapper');
|
||||
const scrollSpyContentEl = document.getElementById('scrollSpyContent');
|
||||
this.bodyContainerEl = bodyWrapperEl;
|
||||
this.sections = Array.from(scrollSpyContentEl.querySelectorAll('section[id]'));
|
||||
this.navigationItemsStatus = Array(this.sections.length).fill(false);
|
||||
this.navigationItemsStatus[0] = true;
|
||||
|
@ -85,18 +120,18 @@ export default {
|
|||
},
|
||||
|
||||
calculateAllSectionsCumulativeHeight() {
|
||||
let totalHeight = 0
|
||||
let totalHeight = 0;
|
||||
|
||||
this.itemsToCreate.forEach((item) => {
|
||||
const sectionEl = document.getElementById(item.sectionId);
|
||||
totalHeight += sectionEl.offsetHeight
|
||||
})
|
||||
this.allSectionsCumulativeHeight = totalHeight
|
||||
totalHeight += sectionEl.offsetHeight;
|
||||
});
|
||||
this.allSectionsCumulativeHeight = totalHeight;
|
||||
},
|
||||
|
||||
calculateSectionsHeight() {
|
||||
// Initialize an array to store the height data for each section
|
||||
this.sectionsWithHeight = this.itemsToCreate.map(item => {
|
||||
this.sectionsWithHeight = this.itemsToCreate.map((item) => {
|
||||
// Find the DOM element for the section
|
||||
const sectionEl = document.getElementById(item.sectionId);
|
||||
|
||||
|
@ -107,8 +142,8 @@ export default {
|
|||
// Return an object containing the section ID and its percentage height
|
||||
return {
|
||||
sectionId: item.sectionId,
|
||||
heightPx: heightPx,
|
||||
percentHeight: percentHeight
|
||||
heightPx,
|
||||
percentHeight
|
||||
};
|
||||
});
|
||||
},
|
||||
|
@ -118,57 +153,55 @@ export default {
|
|||
// on the % of vertical space of scrollable content that they occupy
|
||||
constructThresholds() {
|
||||
const scrollableArea = this.bodyContainerEl;
|
||||
const deltaTravel = scrollableArea.scrollHeight - scrollableArea.clientHeight
|
||||
const deltaTravel = scrollableArea.scrollHeight - scrollableArea.clientHeight;
|
||||
const viewportHeight = scrollableArea.clientHeight;
|
||||
const scrollableAreaHeight = scrollableArea.scrollHeight;
|
||||
const scrollThumbHeight = Math.round(viewportHeight / scrollableAreaHeight * viewportHeight);
|
||||
const scrollThumbCenter = Math.round(scrollThumbHeight / 2)
|
||||
this.centerOfScrollThumb = scrollThumbCenter
|
||||
this.scrollPosition = scrollThumbCenter
|
||||
const scrollThumbCenter = Math.round(scrollThumbHeight / 2);
|
||||
this.centerOfScrollThumb = scrollThumbCenter;
|
||||
this.scrollPosition = scrollThumbCenter;
|
||||
|
||||
let prevThreshold = scrollThumbCenter
|
||||
let prevThreshold = scrollThumbCenter;
|
||||
|
||||
for (let i = 0; i < this.sectionsWithHeight.length; i++) {
|
||||
// first section
|
||||
if (i === 0) {
|
||||
const from = prevThreshold
|
||||
const to = Math.round(deltaTravel * this.sectionsWithHeight[i].percentHeight / 100) + prevThreshold
|
||||
const id = this.sectionsWithHeight[i].sectionId
|
||||
prevThreshold = to + 1
|
||||
const from = prevThreshold;
|
||||
const to = Math.round(deltaTravel * this.sectionsWithHeight[i].percentHeight / 100) + prevThreshold;
|
||||
const id = this.sectionsWithHeight[i].sectionId;
|
||||
prevThreshold = to + 1;
|
||||
const threshold = {
|
||||
id,
|
||||
index: i,
|
||||
from,
|
||||
to
|
||||
}
|
||||
this.thresholds[i] = threshold
|
||||
}
|
||||
// last section
|
||||
else if (i === this.sectionsWithHeight.length - 1) {
|
||||
const from = prevThreshold
|
||||
const to = scrollableArea.scrollHeight
|
||||
const id = this.sectionsWithHeight[i].sectionId
|
||||
};
|
||||
this.thresholds[i] = threshold;
|
||||
} else if (i === this.sectionsWithHeight.length - 1) {
|
||||
// last section
|
||||
const from = prevThreshold;
|
||||
const to = scrollableArea.scrollHeight;
|
||||
const id = this.sectionsWithHeight[i].sectionId;
|
||||
const threshold = {
|
||||
id,
|
||||
index: i,
|
||||
from,
|
||||
to
|
||||
}
|
||||
this.thresholds[i] = threshold
|
||||
}
|
||||
else {
|
||||
};
|
||||
this.thresholds[i] = threshold;
|
||||
} else {
|
||||
// other sections
|
||||
const from = prevThreshold
|
||||
const to = Math.round(deltaTravel * this.sectionsWithHeight[i].percentHeight / 100) + prevThreshold - 1
|
||||
const id = this.sectionsWithHeight[i].sectionId
|
||||
prevThreshold = to + 1
|
||||
const from = prevThreshold;
|
||||
const to = Math.round(deltaTravel * this.sectionsWithHeight[i].percentHeight / 100) + prevThreshold - 1;
|
||||
const id = this.sectionsWithHeight[i].sectionId;
|
||||
prevThreshold = to + 1;
|
||||
const threshold = {
|
||||
id,
|
||||
index: i,
|
||||
from,
|
||||
to
|
||||
}
|
||||
this.thresholds[i] = threshold
|
||||
};
|
||||
this.thresholds[i] = threshold;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -187,26 +220,24 @@ export default {
|
|||
this.removeScrollListener();
|
||||
|
||||
const scrollableArea = this.bodyContainerEl;
|
||||
const foundThreshold = this.thresholds.find((obj) => obj.id === navigationItem.sectionId)
|
||||
const domElToScrollTo = document.getElementById(navigationItem.label)
|
||||
const foundThreshold = this.thresholds.find((obj) => obj.id === navigationItem.sectionId);
|
||||
const domElToScrollTo = document.getElementById(navigationItem.label);
|
||||
|
||||
if (foundThreshold.index === 0) {
|
||||
// scroll to top
|
||||
this.bodyContainerEl.scrollTo({
|
||||
top: 0,
|
||||
behavior: "auto"
|
||||
behavior: 'auto'
|
||||
});
|
||||
}
|
||||
else if (foundThreshold.index === this.thresholds.length - 1) {
|
||||
} else if (foundThreshold.index === this.thresholds.length - 1) {
|
||||
// scroll to bottom
|
||||
this.bodyContainerEl.scrollTo({
|
||||
top: 99999,
|
||||
behavior: "auto"
|
||||
behavior: 'auto'
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// scroll to the start of a section's threshold, adjusted for the center thumb value (true center)
|
||||
scrollableArea.scrollTop = foundThreshold.from - this.centerOfScrollThumb
|
||||
scrollableArea.scrollTop = foundThreshold.from - this.centerOfScrollThumb;
|
||||
}
|
||||
this.flashTitleColor(domElToScrollTo);
|
||||
|
||||
|
@ -215,7 +246,7 @@ export default {
|
|||
},
|
||||
|
||||
flashTitleColor(domEl) {
|
||||
if (!domEl) return
|
||||
if (!domEl) return;
|
||||
|
||||
domEl.classList.add('text-sn-science-blue');
|
||||
setTimeout(() => domEl.classList.remove('text-sn-science-blue'), 300);
|
||||
|
@ -223,9 +254,9 @@ export default {
|
|||
|
||||
handleResize() {
|
||||
this.$nextTick(() => {
|
||||
this.calculateAllSectionsCumulativeHeight()
|
||||
this.calculateAllSectionsCumulativeHeight();
|
||||
this.calculateSectionsHeight();
|
||||
this.constructThresholds()
|
||||
this.constructThresholds();
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -247,7 +278,7 @@ export default {
|
|||
this.navigationItemsStatus[index] = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -19,8 +19,8 @@ export default {
|
|||
return {
|
||||
showTop: false,
|
||||
showImage: false,
|
||||
imageLoading: false,
|
||||
}
|
||||
imageLoading: false
|
||||
};
|
||||
},
|
||||
props: {
|
||||
tooltipId: String,
|
||||
|
@ -31,8 +31,8 @@ export default {
|
|||
medium_preview_url: String || null,
|
||||
defaultLoaderHeight: {
|
||||
type: Number,
|
||||
default: 254,
|
||||
},
|
||||
default: 254
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onImageLoaded(event) {
|
||||
|
@ -53,11 +53,11 @@ export default {
|
|||
const rect = el.parentElement.getBoundingClientRect();
|
||||
|
||||
return (
|
||||
(rect.bottom + height) <=
|
||||
(window.innerHeight || document.documentElement.clientHeight)
|
||||
(rect.bottom + height)
|
||||
<= (window.innerHeight || document.documentElement.clientHeight)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
<template>
|
||||
<div ref="dateTimeRangeOverlay"
|
||||
class="fixed top-0 left-0 right-0 bottom-0 bg-transparent z-[99] hidden">
|
||||
</div>
|
||||
<div class="flex gap-1">
|
||||
<div class="text-sm font-bold truncate" :title="colName">
|
||||
{{ colName }}
|
||||
</div>
|
||||
<div v-if="colVal.reminder" class="bg-sn-alert-passion w-1.5 h-1.5 rounded" :title="colVal.reminder_text"></div>
|
||||
<div v-if="colVal.reminder" class="bg-sn-alert-passion w-1.5 h-1.5 min-w-[0.375rem] min-h-[0.375rem] rounded hover:cursor-pointer" :title="colVal.reminder_text"></div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<template v-if="!canEdit">
|
||||
<span v-if="range">
|
||||
<span v-if="range" class="text-sn-dark-grey">
|
||||
<template v-if="colVal.start_time && colVal.end_time">
|
||||
{{ colVal.start_time.formatted }} - {{ colVal.end_time.formatted }}
|
||||
</template>
|
||||
|
@ -15,7 +18,7 @@
|
|||
{{ viewPlaceholder }}
|
||||
</template>
|
||||
</span>
|
||||
<span v-else >
|
||||
<span v-else class="text-sn-dark-grey">
|
||||
<template v-if="colVal.formatted">
|
||||
{{ colVal.formatted }}
|
||||
</template>
|
||||
|
@ -27,11 +30,11 @@
|
|||
<template v-else>
|
||||
<div>
|
||||
<span class="text-xs capitalize" v-if="range">{{ i18n.t('general.from') }}</span>
|
||||
<DateTimePicker :defaultValue="defaultStartDate" @closed="update" @change="updateStartDate" :mode="mode" :placeholder="placeholder" :clearable="true"/>
|
||||
<DateTimePicker :defaultValue="defaultStartDate" @closed="update" @change="updateStartDate" :mode="mode" :placeholder="placeholder" :clearable="true" ref="dateTimePickerFrom"/>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-xs capitalize" v-if="range">{{ i18n.t('general.to') }}</span>
|
||||
<DateTimePicker :defaultValue="defaultEndDate" @closed="update" v-if="range" @change="updateEndDate" :placeholder="placeholder" :mode="mode" :clearable="true"/>
|
||||
<DateTimePicker :defaultValue="defaultEndDate" @closed="update" v-if="range" @change="updateEndDate" :placeholder="placeholder" :mode="mode" :clearable="true" ref="dateTimePickerTo"/>
|
||||
</div>
|
||||
<div class="text-xs text-sn-delete-red" v-if="error">{{ error }}</div>
|
||||
</template>
|
||||
|
@ -39,156 +42,232 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue';
|
||||
import Reminder from '../reminder.vue';
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue';
|
||||
import Reminder from '../reminder.vue';
|
||||
|
||||
export default {
|
||||
name: 'DateTimeComponent',
|
||||
components: {
|
||||
DateTimePicker,
|
||||
Reminder
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
error: null,
|
||||
defaultStartDate: null,
|
||||
defaultEndDate: null,
|
||||
}
|
||||
},
|
||||
inject: ['reloadRepoItemSidebar'],
|
||||
props: {
|
||||
mode: String,
|
||||
range: { type: Boolean, default: false },
|
||||
colVal: { type: Object, default: {} },
|
||||
colId: Number,
|
||||
updatePath: String,
|
||||
canEdit: { type: Boolean, default: false },
|
||||
colName: String,
|
||||
},
|
||||
created() {
|
||||
if (this.range) {
|
||||
if (this.colVal.start_time?.datetime) this.startDate = new Date(this.colVal.start_time.datetime)
|
||||
if (this.colVal.end_time?.datetime) this.endDate = new Date(this.colVal.end_time.datetime)
|
||||
} else {
|
||||
if (this.colVal.datetime) this.startDate = new Date(this.colVal.datetime)
|
||||
}
|
||||
|
||||
this.defaultStartDate = this.startDate;
|
||||
this.defaultEndDate = this.endDate;
|
||||
},
|
||||
computed: {
|
||||
value: {
|
||||
get () {
|
||||
if (this.range) {
|
||||
if (!(this.startDate instanceof Date) && !(this.endDate instanceof Date)) return null;
|
||||
|
||||
return {
|
||||
start_time: this.formatDate(this.startDate),
|
||||
end_time: this.formatDate(this.endDate)
|
||||
};
|
||||
} else {
|
||||
if (!(this.startDate instanceof Date)) return null;
|
||||
|
||||
return this.formatDate(this.startDate);
|
||||
}
|
||||
},
|
||||
},
|
||||
placeholder() {
|
||||
switch (this.mode) {
|
||||
case 'date':
|
||||
return this.i18n.t('repositories.item_card.repository_date_value.placeholder');
|
||||
case 'time':
|
||||
return this.i18n.t('repositories.item_card.repository_time_value.placeholder');
|
||||
case 'datetime':
|
||||
return this.i18n.t('repositories.item_card.repository_date_time_value.placeholder');
|
||||
}
|
||||
},
|
||||
viewPlaceholder() {
|
||||
switch (this.mode) {
|
||||
case 'date':
|
||||
if (this.range) {
|
||||
return this.i18n.t('repositories.item_card.repository_date_range_value.no_date_range');
|
||||
}
|
||||
return this.i18n.t('repositories.item_card.repository_date_value.no_date');
|
||||
case 'time':
|
||||
if (this.range) {
|
||||
return this.i18n.t('repositories.item_card.repository_time_range_value.no_time_range');
|
||||
}
|
||||
return this.i18n.t('repositories.item_card.repository_time_value.no_time');
|
||||
case 'datetime':
|
||||
if (this.range) {
|
||||
return this.i18n.t('repositories.item_card.repository_date_time_range_value.no_date_time_range');
|
||||
}
|
||||
return this.i18n.t('repositories.item_card.repository_date_time_value.no_date_time');
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateStartDate(date) {
|
||||
this.startDate = date;
|
||||
if (!(this.startDate instanceof Date)) this.update();
|
||||
},
|
||||
updateEndDate(date) {
|
||||
this.endDate = date;
|
||||
if (!(this.endDate instanceof Date)) this.update();
|
||||
},
|
||||
validateValue() {
|
||||
this.error = null;
|
||||
// Date is not changed
|
||||
if (this.defaultStartDate == this.startDate && this.defaultEndDate == this.endDate) return false;
|
||||
export default {
|
||||
name: 'DateTimeComponent',
|
||||
components: {
|
||||
DateTimePicker,
|
||||
Reminder
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
error: null,
|
||||
defaultStartDate: null,
|
||||
defaultEndDate: null,
|
||||
fromPicker: null,
|
||||
toPicker: null
|
||||
};
|
||||
},
|
||||
inject: ['reloadRepoItemSidebar'],
|
||||
props: {
|
||||
mode: String,
|
||||
range: { type: Boolean, default: false },
|
||||
colVal: { type: Object, default: {} },
|
||||
colId: Number,
|
||||
updatePath: String,
|
||||
canEdit: { type: Boolean, default: false },
|
||||
colName: String
|
||||
},
|
||||
created() {
|
||||
if (this.range) {
|
||||
if (this.colVal.start_time?.datetime) this.startDate = new Date(this.colVal.start_time.datetime);
|
||||
if (this.colVal.end_time?.datetime) this.endDate = new Date(this.colVal.end_time.datetime);
|
||||
} else if (this.colVal.datetime) this.startDate = new Date(this.colVal.datetime);
|
||||
|
||||
this.defaultStartDate = this.startDate;
|
||||
this.defaultEndDate = this.endDate;
|
||||
},
|
||||
mounted() {
|
||||
this.fromPicker = this.$refs.dateTimePickerFrom;
|
||||
this.toPicker = this.$refs.dateTimePickerTo;
|
||||
document.addEventListener('click', this.logClick);
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.fromPicker = null;
|
||||
this.toPicker = null;
|
||||
document.removeEventListener('click', this.logClick);
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
},
|
||||
computed: {
|
||||
value: {
|
||||
get() {
|
||||
if (this.range) {
|
||||
// Both empty
|
||||
if (!(this.startDate instanceof Date) && !(this.endDate instanceof Date)) return true;
|
||||
if (!(this.startDate instanceof Date) && !(this.endDate instanceof Date)) return null;
|
||||
|
||||
// One empty
|
||||
if (!(this.startDate instanceof Date) || !(this.endDate instanceof Date)) {
|
||||
this.error = this.i18n.t('repositories.item_card.date_time.errors.not_valid_range')
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
start_time: this.formatDate(this.startDate),
|
||||
end_time: this.formatDate(this.endDate)
|
||||
};
|
||||
}
|
||||
if (!(this.startDate instanceof Date)) return null;
|
||||
|
||||
// Start date is after end date
|
||||
if (this.startDate > this.endDate) {
|
||||
this.error = this.i18n.t('repositories.item_card.date_time.errors.not_valid_range')
|
||||
return false;
|
||||
return this.formatDate(this.startDate);
|
||||
}
|
||||
},
|
||||
placeholder() {
|
||||
switch (this.mode) {
|
||||
case 'date':
|
||||
return this.i18n.t('repositories.item_card.repository_date_value.placeholder');
|
||||
case 'time':
|
||||
return this.i18n.t('repositories.item_card.repository_time_value.placeholder');
|
||||
case 'datetime':
|
||||
return this.i18n.t('repositories.item_card.repository_date_time_value.placeholder');
|
||||
}
|
||||
},
|
||||
viewPlaceholder() {
|
||||
switch (this.mode) {
|
||||
case 'date':
|
||||
if (this.range) {
|
||||
return this.i18n.t('repositories.item_card.repository_date_range_value.no_date_range');
|
||||
}
|
||||
return this.i18n.t('repositories.item_card.repository_date_value.no_date');
|
||||
case 'time':
|
||||
if (this.range) {
|
||||
return this.i18n.t('repositories.item_card.repository_time_range_value.no_time_range');
|
||||
}
|
||||
return this.i18n.t('repositories.item_card.repository_time_value.no_time');
|
||||
case 'datetime':
|
||||
if (this.range) {
|
||||
return this.i18n.t('repositories.item_card.repository_date_time_range_value.no_date_time_range');
|
||||
}
|
||||
return this.i18n.t('repositories.item_card.repository_date_time_value.no_date_time');
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateStartDate(date) {
|
||||
this.startDate = date;
|
||||
if (!(this.startDate instanceof Date)) this.update();
|
||||
},
|
||||
updateEndDate(date) {
|
||||
this.endDate = date;
|
||||
if (!(this.endDate instanceof Date)) this.update();
|
||||
},
|
||||
validateValue() {
|
||||
this.error = null;
|
||||
// Date is not changed
|
||||
if (this.defaultStartDate === this.startDate && this.defaultEndDate === this.endDate) return false;
|
||||
|
||||
if (this.range) {
|
||||
// Both empty
|
||||
if (!(this.startDate instanceof Date) && !(this.endDate instanceof Date)) return true;
|
||||
|
||||
// One empty
|
||||
if (!(this.startDate instanceof Date) || !(this.endDate instanceof Date)) {
|
||||
this.error = this.i18n.t('repositories.item_card.date_time.errors.not_valid_range');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
update() {
|
||||
const params = {}
|
||||
// Start date is after end date
|
||||
if (this.startDate > this.endDate) {
|
||||
this.error = this.i18n.t('repositories.item_card.date_time.errors.not_valid_range');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.validateValue()) return;
|
||||
return true;
|
||||
},
|
||||
update() {
|
||||
const params = {};
|
||||
|
||||
params[this.colId] = this.value
|
||||
$.ajax({
|
||||
method: 'PUT',
|
||||
url: this.updatePath,
|
||||
dataType: 'json',
|
||||
data: { repository_cells: params },
|
||||
success: () => {
|
||||
this.defaultStartDate = this.startDate;
|
||||
this.defaultEndDate = this.endDate;
|
||||
if ($('.dataTable')[0]) {
|
||||
$('.dataTable').DataTable().ajax.reload(null, false);
|
||||
}
|
||||
this.reloadRepoItemSidebar();
|
||||
if (!this.validateValue()) return;
|
||||
|
||||
params[this.colId] = this.value;
|
||||
$.ajax({
|
||||
method: 'PUT',
|
||||
url: this.updatePath,
|
||||
dataType: 'json',
|
||||
data: { repository_cells: params },
|
||||
success: () => {
|
||||
this.defaultStartDate = this.startDate;
|
||||
this.defaultEndDate = this.endDate;
|
||||
if ($('.dataTable.repository-dataTable')[0]) {
|
||||
$('.dataTable.repository-dataTable').DataTable().ajax.reload(null, false);
|
||||
}
|
||||
});
|
||||
},
|
||||
formatDate(date) {
|
||||
if (!(date instanceof Date)) return null;
|
||||
this.reloadRepoItemSidebar();
|
||||
}
|
||||
});
|
||||
},
|
||||
formatDate(date) {
|
||||
if (!(date instanceof Date)) return null;
|
||||
|
||||
const y = date.getFullYear();
|
||||
const m = date.getMonth() + 1;
|
||||
const d = date.getDate();
|
||||
const hours = date.getHours();
|
||||
const mins = date.getMinutes();
|
||||
return `${y}/${m}/${d} ${hours}:${mins}`;
|
||||
},
|
||||
const y = date.getFullYear();
|
||||
const m = date.getMonth() + 1;
|
||||
const d = date.getDate();
|
||||
const hours = date.getHours();
|
||||
const mins = date.getMinutes();
|
||||
return `${y}/${m}/${d} ${hours}:${mins}`;
|
||||
},
|
||||
showOverlay() {
|
||||
const overlay = this.$refs.dateTimeRangeOverlay;
|
||||
overlay.classList.remove('hidden');
|
||||
},
|
||||
hideOverlay() {
|
||||
const overlay = this.$refs.dateTimeRangeOverlay;
|
||||
overlay.classList.add('hidden');
|
||||
},
|
||||
preventBodyScrolling() {
|
||||
document.body.classList.add('overflow-hidden');
|
||||
document.body.classList.remove('overflow-auto');
|
||||
},
|
||||
allowBodyScrolling() {
|
||||
document.body.classList.remove('overflow-hidden');
|
||||
document.body.classList.add('overflow-auto');
|
||||
},
|
||||
bringCalendarToFront() {
|
||||
const calendarEl = document.querySelector('.dp__instance_calendar');
|
||||
calendarEl.classList.add('z-[9999]');
|
||||
},
|
||||
focusClearedInput() {
|
||||
if (!this.fromPicker.datetime) {
|
||||
const fromInput = this.fromPicker.$el.querySelector('input');
|
||||
fromInput.focus();
|
||||
fromInput.click();
|
||||
}
|
||||
if (!this.toPicker.datetime) {
|
||||
const toInput = this.toPicker.$el.querySelector('input');
|
||||
toInput.focus();
|
||||
toInput.click();
|
||||
}
|
||||
|
||||
this.preventBodyScrolling();
|
||||
this.showOverlay();
|
||||
this.$nextTick(() => {
|
||||
this.bringCalendarToFront();
|
||||
});
|
||||
},
|
||||
logClick() {
|
||||
if (this.error) this.focusClearedInput();
|
||||
},
|
||||
handleKeyDown(event) {
|
||||
if (event.key === 'Escape' || (event.ctrlKey && event.key === 'z')) {
|
||||
if (this.startDate === null && this.fromPicker) {
|
||||
this.startDate = this.defaultStartDate;
|
||||
this.fromPicker.datetime = this.defaultStartDate;
|
||||
this.error = null;
|
||||
}
|
||||
if (this.endDate === null && this.toPicker) {
|
||||
this.endDate = this.defaultEndDate;
|
||||
this.toPicker.datetime = this.defaultEndDate;
|
||||
this.error = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
error(newVal) {
|
||||
if (newVal !== null) {
|
||||
this.focusClearedInput();
|
||||
} else {
|
||||
this.hideOverlay();
|
||||
this.allowBodyScrolling();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
48
app/javascript/vue/repository_item_sidebar/unlink_modal.vue
Normal file
48
app/javascript/vue/repository_item_sidebar/unlink_modal.vue
Normal file
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<div ref="modal" @keydown.esc="cancel" class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-sm" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" @click="cancel" aria-label="Close"><i class="sn-icon sn-icon-close"></i></button>
|
||||
<h4 class="modal-title">
|
||||
{{ i18n.t('repositories.item_card.relationships.unlink_modal.title') }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ i18n.t('repositories.item_card.relationships.unlink_modal.description') }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="cancel">{{ i18n.t('general.cancel') }}</button>
|
||||
<button class="btn btn-primary" @click="confirm">{{ i18n.t('repositories.item_card.relationships.unlink_modal.unlink') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UnlinkModal',
|
||||
mounted() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
this.hide(() => {
|
||||
this.$emit('cancel');
|
||||
});
|
||||
},
|
||||
confirm() {
|
||||
this.hide(() => {
|
||||
this.$emit('unlink');
|
||||
});
|
||||
},
|
||||
hide(callback) {
|
||||
$(this.$refs.modal).one('hidden.bs.modal', () => {
|
||||
callback();
|
||||
});
|
||||
$(this.$refs.modal).modal('hide');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -5,17 +5,17 @@
|
|||
<div v-if="availablePrinters.length > 0" class="printers-available">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="sn-icon sn-icon-close"></i></button>
|
||||
<p class="modal-title">
|
||||
<template v-if="rows.length == 1">
|
||||
<b>{{ i18n.t('repository_row.modal_print_label.head_title', {repository_row: rows[0].attributes.name}) }}</b>
|
||||
<div class="modal-title">
|
||||
<div v-if="rows.length == 1" class="flex flex-row">
|
||||
<div class="font-bold">{{ i18n.t('repository_row.modal_print_label.head_title', {repository_row: rows[0].attributes.name}) }}</div>
|
||||
<span class="id-label">
|
||||
{{ i18n.t('repository_row.modal_print_label.id_label', {repository_row_id: rows[0].attributes.code}) }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<b>{{ i18n.t('repository_row.modal_print_label.head_title_multiple', {repository_rows: rows.length}) }}</b>
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="font-bold">{{ i18n.t('repository_row.modal_print_label.head_title_multiple', {repository_rows: rows.length}) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class=printers-container>
|
||||
|
@ -91,174 +91,178 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DropdownSelector from '../shared/legacy/dropdown_selector.vue'
|
||||
import LabelPreview from '../label_template/components/label_preview.vue'
|
||||
import DropdownSelector from '../shared/legacy/dropdown_selector.vue';
|
||||
import LabelPreview from '../label_template/components/label_preview.vue';
|
||||
|
||||
export default {
|
||||
name: 'PrintModalContainer',
|
||||
props: {
|
||||
showModal: Boolean,
|
||||
row_ids: Array,
|
||||
urls: Object
|
||||
export default {
|
||||
name: 'PrintModalContainer',
|
||||
props: {
|
||||
showModal: Boolean,
|
||||
row_ids: Array,
|
||||
repository_id: Number,
|
||||
urls: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rows: [],
|
||||
printers: [],
|
||||
templates: [],
|
||||
selectedPrinter: null,
|
||||
selectedTemplate: null,
|
||||
copies: 1,
|
||||
zebraPrinters: null,
|
||||
labelTemplateError: null,
|
||||
labelTemplateCode: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
LabelPreview
|
||||
},
|
||||
mounted() {
|
||||
$.get(this.urls.labelTemplates, (result) => {
|
||||
this.templates = result.data;
|
||||
this.selectDefaultLabelTemplate();
|
||||
});
|
||||
|
||||
$.get(this.urls.printers, (result) => {
|
||||
this.printers = result.data;
|
||||
});
|
||||
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.copies = 1;
|
||||
this.zebraPrinters = null;
|
||||
this.$emit('close');
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
availableTemplates() {
|
||||
let { templates } = this;
|
||||
if (this.selectedPrinter && this.selectedPrinter.attributes.type_of === 'zebra') {
|
||||
templates = templates.filter((i) => i.attributes.type === 'ZebraLabelTemplate');
|
||||
}
|
||||
|
||||
return templates.map((i) => ({
|
||||
value: i.id,
|
||||
label: i.attributes.name,
|
||||
params: {
|
||||
icon: i.attributes.icon_url,
|
||||
description: i.attributes.description || ''
|
||||
}
|
||||
})).sort((temp1, temp2) => (temp1.label?.toLowerCase() > temp2.label?.toLowerCase() ? 1 : -1));
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rows: [],
|
||||
printers: [],
|
||||
templates: [],
|
||||
selectedPrinter: null,
|
||||
selectedTemplate: null,
|
||||
copies: 1,
|
||||
zebraPrinters: null,
|
||||
labelTemplateError: null,
|
||||
labelTemplateCode: null
|
||||
availablePrinters() {
|
||||
return this.printers.map((i) => ({
|
||||
value: i.id,
|
||||
label: i.attributes.display_name
|
||||
}));
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
showModal() {
|
||||
if (this.showModal) {
|
||||
this.initZebraPrinter();
|
||||
$(this.$refs.modal).modal('show');
|
||||
this.validateTemplate();
|
||||
}
|
||||
},
|
||||
components: {
|
||||
DropdownSelector,
|
||||
LabelPreview
|
||||
row_ids() {
|
||||
$.get(this.urls.rows, { repository_id: this.repository_id, row_ids: this.row_ids }, (result) => {
|
||||
this.rows = result.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectDefaultLabelTemplate() {
|
||||
if (this.selectedPrinter && this.templates) {
|
||||
const template = this.templates.find((i) => i.attributes.default
|
||||
&& i.type.includes(this.selectedPrinter.attributes.type_of));
|
||||
if (template) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.labelTemplateDropdown.selectValues(template.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
$.get(this.urls.labelTemplates, (result) => {
|
||||
this.templates = result.data
|
||||
this.selectDefaultLabelTemplate();
|
||||
})
|
||||
selectPrinter(value) {
|
||||
this.selectedPrinter = this.printers.find((i) => i.id === value);
|
||||
this.selectDefaultLabelTemplate();
|
||||
},
|
||||
selectTemplate(value) {
|
||||
this.selectedTemplate = this.templates.find((i) => i.id === value);
|
||||
this.validateTemplate();
|
||||
},
|
||||
validateTemplate() {
|
||||
if (!this.selectedTemplate || this.row_ids.length == 0) return;
|
||||
|
||||
$.get(this.urls.printers, (result) => {
|
||||
this.printers = result.data
|
||||
})
|
||||
|
||||
$(this.$refs.modal).on('hidden.bs.modal', () => {
|
||||
this.zebraPrinters = null;
|
||||
this.$emit('close');
|
||||
$.post(this.urls.printValidation, {
|
||||
repository_id: this.repository_id,
|
||||
label_template_id: this.selectedTemplate.id,
|
||||
row_ids: this.row_ids
|
||||
}, (result) => {
|
||||
this.labelTemplateError = null;
|
||||
this.labelTemplateCode = result.label_code;
|
||||
}).fail((result) => {
|
||||
this.labelTemplateError = result.responseJSON.error;
|
||||
this.labelTemplateCode = result.responseJSON.label_code;
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
availableTemplates() {
|
||||
let templates = this.templates;
|
||||
if (this.selectedPrinter && this.selectedPrinter.attributes.type_of === 'zebra') {
|
||||
templates = templates.filter(i => i.attributes.type === 'ZebraLabelTemplate')
|
||||
}
|
||||
|
||||
return templates.map(i => {
|
||||
return {
|
||||
value: i.id,
|
||||
label: i.attributes.name,
|
||||
params: {
|
||||
icon: i.attributes.icon_url,
|
||||
description: i.attributes.description || ''
|
||||
}
|
||||
}
|
||||
}).sort((temp1, temp2) => (temp1.label?.toLowerCase() > temp2.label?.toLowerCase() ? 1 : -1));
|
||||
},
|
||||
availablePrinters() {
|
||||
return this.printers.map(i => {
|
||||
return {
|
||||
value: i.id,
|
||||
label: i.attributes.display_name
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
showModal() {
|
||||
if (this.showModal) {
|
||||
this.initZebraPrinter();
|
||||
$(this.$refs.modal).modal('show');
|
||||
this.validateTemplate();
|
||||
}
|
||||
},
|
||||
row_ids() {
|
||||
$.get(this.urls.rows, {rows: this.row_ids}, (result) => {
|
||||
this.rows = result.data
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectDefaultLabelTemplate() {
|
||||
if (this.selectedPrinter && this.templates) {
|
||||
let template = this.templates.find(i => i.attributes.default
|
||||
&& i.type.includes(this.selectedPrinter.attributes.type_of));
|
||||
if (template) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.labelTemplateDropdown.selectValues(template.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
selectPrinter(value) {
|
||||
this.selectedPrinter = this.printers.find(i => i.id === value)
|
||||
this.selectDefaultLabelTemplate();
|
||||
},
|
||||
selectTemplate(value) {
|
||||
this.selectedTemplate = this.templates.find(i => i.id === value);
|
||||
this.validateTemplate();
|
||||
},
|
||||
validateTemplate() {
|
||||
if (!this.selectedTemplate || this.row_ids.length == 0) return;
|
||||
|
||||
$.post(this.urls.printValidation, {label_template_id: this.selectedTemplate.id, rows: this.row_ids}, (result) => {
|
||||
this.labelTemplateError = null;
|
||||
this.labelTemplateCode = result.label_code;
|
||||
}).fail((result) => {
|
||||
this.labelTemplateError = result.responseJSON.error;
|
||||
this.labelTemplateCode = result.responseJSON.label_code;
|
||||
})
|
||||
},
|
||||
submitPrint() {
|
||||
this.$nextTick(() => {
|
||||
if (this.selectedPrinter.attributes.type_of === 'zebra') {
|
||||
this.zebraPrinters.print(
|
||||
this.urls.zebraProgress,
|
||||
'.label-printing-progress-modal',
|
||||
'#modal-print-repository-row-label',
|
||||
{
|
||||
printer_name: this.selectedPrinter.attributes.name,
|
||||
number_of_copies: this.copies,
|
||||
label_template_id: this.selectedTemplate.id,
|
||||
rows: this.row_ids
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$.post(this.urls.print, {
|
||||
rows: this.row_ids,
|
||||
label_printer_id: this.selectedPrinter.id,
|
||||
submitPrint() {
|
||||
this.$nextTick(() => {
|
||||
if (this.selectedPrinter.attributes.type_of === 'zebra') {
|
||||
this.zebraPrinters.print(
|
||||
this.urls.zebraProgress,
|
||||
'.label-printing-progress-modal',
|
||||
'#modal-print-repository-row-label',
|
||||
{
|
||||
printer_name: this.selectedPrinter.attributes.name,
|
||||
number_of_copies: this.copies,
|
||||
label_template_id: this.selectedTemplate.id,
|
||||
copies: this.copies
|
||||
}, (data) => {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('close');
|
||||
PrintProgressModal.init(data);
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
initZebraPrinter() {
|
||||
this.zebraPrinters = zebraPrint.init($('#LabelPrinterSelector'), {
|
||||
clearSelectorOnFirstDevice: false,
|
||||
appendDevice: (device) => {
|
||||
this.printers.push({
|
||||
id: `zebra${this.printers.length}`,
|
||||
attributes: {
|
||||
name: device.name,
|
||||
display_name: device.name,
|
||||
type_of: 'zebra'
|
||||
}
|
||||
})
|
||||
}
|
||||
}, false);
|
||||
},
|
||||
templateOption(option) {
|
||||
return `
|
||||
row_ids: this.row_ids,
|
||||
repository_id: this.repository_id
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$.post(this.urls.print, {
|
||||
row_ids: this.row_ids,
|
||||
repository_id: this.repository_id,
|
||||
label_printer_id: this.selectedPrinter.id,
|
||||
label_template_id: this.selectedTemplate.id,
|
||||
copies: this.copies
|
||||
}, (data) => {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
this.$emit('close');
|
||||
PrintProgressModal.init(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
initZebraPrinter() {
|
||||
this.zebraPrinters = zebraPrint.init($('#LabelPrinterSelector'), {
|
||||
clearSelectorOnFirstDevice: false,
|
||||
appendDevice: (device) => {
|
||||
this.printers.push({
|
||||
id: `zebra${this.printers.length}`,
|
||||
attributes: {
|
||||
name: device.name,
|
||||
display_name: device.name,
|
||||
type_of: 'zebra'
|
||||
}
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
},
|
||||
templateOption(option) {
|
||||
return `
|
||||
<div class="label-template-option" data-toggle="tooltip" data-placement="right" title="${option.params.description}">
|
||||
<img src="${option.params.icon}" style=""></img>
|
||||
${option.label}
|
||||
</div>
|
||||
`
|
||||
},
|
||||
initTooltip() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
}
|
||||
`;
|
||||
},
|
||||
initTooltip() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -33,50 +33,50 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ExportStockConsumptionModal',
|
||||
props: {
|
||||
exportUrl: { type: String, required: true }
|
||||
export default {
|
||||
name: 'ExportStockConsumptionModal',
|
||||
props: {
|
||||
exportUrl: { type: String, required: true }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
repository: null,
|
||||
selectedRows: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
window.exportStockConsumptionModalComponent = this;
|
||||
},
|
||||
beforeUnmount() {
|
||||
delete window.exportStockConsumptionModalComponent;
|
||||
},
|
||||
methods: {
|
||||
exportConsumption() {
|
||||
$.post(this.repository.export_consumption_url, { row_ids: this.selectedRows })
|
||||
.done((data) => {
|
||||
HelperModule.flashAlertMsg(data.message, 'success');
|
||||
})
|
||||
.fail((data) => {
|
||||
HelperModule.flashAlertMsg(data.responseJSON.message, 'danger');
|
||||
})
|
||||
.always(() => {
|
||||
this.closeModal();
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
repository: null,
|
||||
selectedRows: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.exportStockConsumptionModalComponent = this;
|
||||
},
|
||||
beforeUnmount() {
|
||||
delete window.exportStockConsumptionModalComponent;
|
||||
},
|
||||
methods: {
|
||||
exportConsumption() {
|
||||
$.post(this.repository.export_consumption_url, { row_ids: this.selectedRows })
|
||||
.done((data) => {
|
||||
HelperModule.flashAlertMsg(data.message, 'success');
|
||||
})
|
||||
.fail((data) => {
|
||||
HelperModule.flashAlertMsg(data.responseJSON.message, 'danger');
|
||||
})
|
||||
.always(() => {
|
||||
this.closeModal();
|
||||
});
|
||||
},
|
||||
fetchRepositoryData(selectedRows, params) {
|
||||
this.selectedRows = selectedRows;
|
||||
$.get(this.exportUrl, params)
|
||||
.done( (data) => {
|
||||
fetchRepositoryData(selectedRows, params) {
|
||||
this.selectedRows = selectedRows;
|
||||
$.get(this.exportUrl, params)
|
||||
.done((data) => {
|
||||
this.repository = data;
|
||||
this.showModal();
|
||||
});
|
||||
},
|
||||
closeModal() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
showModal() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
}
|
||||
},
|
||||
closeModal() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
showModal() {
|
||||
$(this.$refs.modal).modal('show');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -56,8 +56,13 @@
|
|||
<label :class="`text-sm font-normal ${errors.unit ? 'text-sn-delete-red' : 'text-sn-grey'}`" for="stock-unit">
|
||||
{{ i18n.t('repository_stock_values.manage_modal.unit') }}
|
||||
</label>
|
||||
<<<<<<< HEAD
|
||||
<SelectDropdown
|
||||
:disabled="[2, 3].includes(operation)"
|
||||
=======
|
||||
<Select
|
||||
:disabled="['add', 'remove'].includes(operation)"
|
||||
>>>>>>> develop
|
||||
:value="unit"
|
||||
:options="units"
|
||||
:placeholder="i18n.t('repository_stock_values.manage_modal.unit_prompt')"
|
||||
|
@ -86,8 +91,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="repository-stock-reminder-selector">
|
||||
<div class="sci-checkbox-container">
|
||||
<div class="repository-stock-reminder-selector flex">
|
||||
<div class="sci-checkbox-container my-auto">
|
||||
<input type="checkbox" name="reminder-enabled" tabindex="4" class="sci-checkbox" id="reminder-selector-checkbox" :checked="reminderEnabled" @change="reminderEnabled = $event.target.checked"/>
|
||||
<span class="sci-checkbox-label"></span>
|
||||
</div>
|
||||
|
@ -140,150 +145,153 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import SelectDropdown from './../shared/select_dropdown.vue';
|
||||
import Input from '../shared/legacy/input.vue';
|
||||
import Decimal from 'decimal.js';
|
||||
import Decimal from 'decimal.js';
|
||||
import Select from '../shared/legacy/select.vue';
|
||||
import Input from '../shared/legacy/input.vue';
|
||||
|
||||
export default {
|
||||
name: 'ManageStockValueModal',
|
||||
components: {
|
||||
SelectDropdown,
|
||||
Input
|
||||
export default {
|
||||
name: 'ManageStockValueModal',
|
||||
components: {
|
||||
Select,
|
||||
Input
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
operation: null,
|
||||
operations: [],
|
||||
stockValue: null,
|
||||
amount: '',
|
||||
repositoryRowName: null,
|
||||
stockUrl: null,
|
||||
units: null,
|
||||
unit: null,
|
||||
reminderEnabled: false,
|
||||
lowStockTreshold: null,
|
||||
comment: null,
|
||||
errors: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
unitLabel() {
|
||||
const currentUnit = this.units?.find((option) => option[0] === this.unit);
|
||||
return currentUnit ? currentUnit[1] : '';
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
operation: null,
|
||||
operations: [],
|
||||
stockValue: null,
|
||||
amount: '',
|
||||
repositoryRowName: null,
|
||||
stockUrl: null,
|
||||
units: null,
|
||||
unit: null,
|
||||
reminderEnabled: false,
|
||||
lowStockTreshold: null,
|
||||
comment: null,
|
||||
errors: {}
|
||||
initUnitLabel() {
|
||||
const unit = this.units?.find((option) => option[0] === this.stockValue?.unit);
|
||||
return unit ? unit[1] : '';
|
||||
},
|
||||
newAmount() {
|
||||
const currentAmount = new Decimal(this.stockValue?.amount || 0);
|
||||
const amount = new Decimal(this.amount || 0);
|
||||
let value;
|
||||
switch (this.operation) {
|
||||
case 'add':
|
||||
value = currentAmount.plus(amount);
|
||||
break;
|
||||
case 'remove':
|
||||
value = currentAmount.minus(amount);
|
||||
break;
|
||||
default:
|
||||
value = amount;
|
||||
break;
|
||||
}
|
||||
return Number(value);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.manageStockModalComponent = this;
|
||||
},
|
||||
beforeDestroy() {
|
||||
delete window.manageStockModalComponent;
|
||||
},
|
||||
mounted() {
|
||||
// Focus stock amount input field
|
||||
$(this.$refs.modal).on('show.bs.modal', () => {
|
||||
setTimeout(() => {
|
||||
$('#stock-amount')[0]?.focus();
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
setOperation($event) {
|
||||
if ($event !== this.operation) {
|
||||
this.amount = null;
|
||||
}
|
||||
this.operation = $event;
|
||||
if (['add', 'remove'].includes($event)) {
|
||||
this.unit = this.stockValue.unit;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
unitLabel: function() {
|
||||
const currentUnit = this.units?.find(option => option[0] === this.unit);
|
||||
return currentUnit ? currentUnit[1] : ''
|
||||
},
|
||||
initUnitLabel: function() {
|
||||
const unit = this.units?.find(option => option[0] === this.stockValue?.unit);
|
||||
return unit ? unit[1] : ''
|
||||
},
|
||||
newAmount: function() {
|
||||
const currentAmount = new Decimal(this.stockValue?.amount || 0);
|
||||
const amount = new Decimal(this.amount || 0)
|
||||
let value;
|
||||
switch (this.operation) {
|
||||
case 2:
|
||||
value = currentAmount.plus(amount);
|
||||
break;
|
||||
case 3:
|
||||
value = currentAmount.minus(amount);
|
||||
break;
|
||||
default:
|
||||
value = amount;
|
||||
break;
|
||||
fetchStockValueData(stockValueUrl) {
|
||||
if (!stockValueUrl) return;
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
url: stockValueUrl,
|
||||
dataType: 'json',
|
||||
success: (result) => {
|
||||
this.repositoryRowName = result.repository_row_name;
|
||||
this.stockValue = result.stock_value;
|
||||
this.amount = Number(new Decimal(result.stock_value.amount || 0));
|
||||
this.units = result.stock_value.units;
|
||||
this.unit = result.stock_value.unit;
|
||||
this.reminderEnabled = result.stock_value.reminder_enabled;
|
||||
this.lowStockTreshold = result.stock_value.low_stock_treshold;
|
||||
this.operation = 'set';
|
||||
this.stockUrl = result.stock_url;
|
||||
/* eslint-disable no-undef */
|
||||
this.operations = [
|
||||
['set', `${I18n.t('repository_stock_values.manage_modal.set')}`],
|
||||
['add', `${I18n.t('repository_stock_values.manage_modal.add')}`],
|
||||
['remove', `${I18n.t('repository_stock_values.manage_modal.remove')}`]
|
||||
];
|
||||
/* eslint-enable no-undef */
|
||||
this.errors = {};
|
||||
}
|
||||
return Number(value)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.manageStockModalComponent = this;
|
||||
},
|
||||
beforeDestroy() {
|
||||
delete window.manageStockModalComponent;
|
||||
},
|
||||
mounted() {
|
||||
// Focus stock amount input field
|
||||
$(this.$refs.modal).on('show.bs.modal', function() {
|
||||
setTimeout(() => {
|
||||
$('#stock-amount')[0]?.focus()
|
||||
}, 500)
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
setOperation($event) {
|
||||
if ($event !== this.operation) {
|
||||
this.amount = null;
|
||||
}
|
||||
this.operation = $event;
|
||||
if ([2, 3].includes($event)) {
|
||||
this.unit = this.stockValue.unit;
|
||||
}
|
||||
},
|
||||
fetchStockValueData(stockValueUrl) {
|
||||
if (!stockValueUrl) return;
|
||||
$.ajax({
|
||||
method: 'GET',
|
||||
url: stockValueUrl,
|
||||
dataType: 'json',
|
||||
success: (result) => {
|
||||
this.repositoryRowName = result.repository_row_name
|
||||
this.stockValue = result.stock_value
|
||||
this.amount = Number(new Decimal(result.stock_value.amount || 0))
|
||||
this.units = result.stock_value.units
|
||||
this.unit = result.stock_value.unit
|
||||
this.reminderEnabled = result.stock_value.reminder_enabled
|
||||
this.lowStockTreshold = result.stock_value.low_stock_treshold
|
||||
this.operation = 1;
|
||||
this.stockUrl = result.stock_url;
|
||||
this.operations = [[1, 'set'], [2, 'add'], [3, 'remove']];
|
||||
this.errors = {};
|
||||
}
|
||||
});
|
||||
},
|
||||
closeModal() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
showModal(stockValueUrl, closeCallback) {
|
||||
$(this.$refs.modal).modal('show');
|
||||
this.fetchStockValueData(stockValueUrl);
|
||||
this.closeCallback = closeCallback;
|
||||
},
|
||||
validateAndsaveStockValue() {
|
||||
let newErrors = {};
|
||||
this.errors = newErrors;
|
||||
if (!this.unit)
|
||||
newErrors['unit'] = I18n.t('repository_stock_values.manage_modal.unit_error');
|
||||
if (!this.amount)
|
||||
newErrors['amount'] = I18n.t('repository_stock_values.manage_modal.amount_error');
|
||||
if (this.amount && this.amount < 0)
|
||||
newErrors['amount'] = I18n.t('repository_stock_values.manage_modal.negative_error');
|
||||
if (this.reminderEnabled && !this.lowStockTreshold)
|
||||
newErrors['tresholdAmount'] = I18n.t('repository_stock_values.manage_modal.amount_error');
|
||||
closeModal() {
|
||||
$(this.$refs.modal).modal('hide');
|
||||
},
|
||||
showModal(stockValueUrl, closeCallback) {
|
||||
$(this.$refs.modal).modal('show');
|
||||
this.fetchStockValueData(stockValueUrl);
|
||||
this.closeCallback = closeCallback;
|
||||
},
|
||||
validateAndsaveStockValue() {
|
||||
const newErrors = {};
|
||||
this.errors = newErrors;
|
||||
if (!this.unit) { newErrors.unit = I18n.t('repository_stock_values.manage_modal.unit_error'); }
|
||||
if (!this.amount) { newErrors.amount = I18n.t('repository_stock_values.manage_modal.amount_error'); }
|
||||
if (this.amount && this.amount < 0) { newErrors.amount = I18n.t('repository_stock_values.manage_modal.negative_error'); }
|
||||
if (this.reminderEnabled && !this.lowStockTreshold) { newErrors.tresholdAmount = I18n.t('repository_stock_values.manage_modal.amount_error'); }
|
||||
|
||||
this.errors = newErrors;
|
||||
this.errors = newErrors;
|
||||
|
||||
if (!$.isEmptyObject(newErrors)) return;
|
||||
if (!$.isEmptyObject(newErrors)) return;
|
||||
|
||||
const $this = this
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: this.stockUrl,
|
||||
dataType: 'json',
|
||||
data: {
|
||||
repository_stock_value: {
|
||||
unit_item_id: this.unit,
|
||||
amount: this.newAmount,
|
||||
comment: this.comment,
|
||||
low_stock_threshold: this.reminderEnabled ? this.lowStockTreshold : null
|
||||
},
|
||||
operator: this.operations.find(operation => operation[0] == this.operation)?.[1],
|
||||
change_amount: Math.abs(this.amount),
|
||||
const $this = this;
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: this.stockUrl,
|
||||
dataType: 'json',
|
||||
data: {
|
||||
repository_stock_value: {
|
||||
unit_item_id: this.unit,
|
||||
amount: this.newAmount,
|
||||
comment: this.comment,
|
||||
low_stock_threshold: this.reminderEnabled ? this.lowStockTreshold : null
|
||||
},
|
||||
success: function(result) {
|
||||
$this.stockValue = null;
|
||||
$this.closeModal();
|
||||
$this.closeCallback && $this.closeCallback(result);
|
||||
}
|
||||
})
|
||||
}
|
||||
operator: this.operations.find((operation) => operation[0] == this.operation)?.[0],
|
||||
change_amount: Math.abs(this.amount)
|
||||
|
||||
},
|
||||
success: (result) => {
|
||||
$this.stockValue = null;
|
||||
$this.closeModal();
|
||||
$this.closeCallback && $this.closeCallback(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue