mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-11 15:45:34 +08:00
Merge branch 'develop' into features/linked-inventories
This commit is contained in:
commit
feeb350114
36 changed files with 448 additions and 471 deletions
|
@ -50,7 +50,7 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: https://github.com/scinote-eln/yomu
|
||||
revision: 020ab670b2919f3b436e926a890d1dad23d75676
|
||||
revision: 09b7b4910f59453970aab03d7b3ddb60b41db89a
|
||||
branch: master
|
||||
specs:
|
||||
yomu (0.2.4)
|
||||
|
|
|
@ -615,6 +615,7 @@ var ExperimnetTable = {
|
|||
this.appendRows(result.data);
|
||||
this.initDueDatePicker(result.data);
|
||||
this.handleNoResults();
|
||||
this.initProvisioningStatusPolling();
|
||||
}, 100);
|
||||
|
||||
InfiniteScroll.init(this.table, {
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
--dp-secondary-color: var(--sn-grey);
|
||||
--dp-border-color: var(--sn-light-grey);
|
||||
--dp-menu-border-color: var(--sn-light-grey);
|
||||
--dp-border-color-hover: var(--sn-light-grey);
|
||||
--dp-border-color-hover: var(--sn-sleepy-grey);
|
||||
--dp-disabled-color: var(--sn-super-light-grey);
|
||||
--dp-scroll-bar-background: var(--sn-white);
|
||||
--dp-scroll-bar-color: var(--sn-grey);
|
||||
|
|
|
@ -210,7 +210,7 @@ class RepositoryRowsController < ApplicationController
|
|||
return render json: { name: @repository_row.name } if update_params['repository_row'].present?
|
||||
|
||||
column = row_cell_update.column
|
||||
cell = row_cell_update.cell
|
||||
cell = row_cell_update.cell&.reload || row_cell_update.cell
|
||||
data = { value_type: column.data_type, id: column.id, value: nil }
|
||||
|
||||
return render json: data if cell.blank?
|
||||
|
|
|
@ -49,7 +49,6 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
let container = this.$refs.scrollContainer.$el
|
||||
document.body.style.overflow = 'hidden'
|
||||
|
||||
container.addEventListener('ps-scroll-y', (e) => {
|
||||
if (e.target.scrollTop + e.target.clientHeight >= e.target.scrollHeight - 20) {
|
||||
|
@ -57,8 +56,8 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
document.body.style.overflow = 'scroll'
|
||||
beforeUnmount() {
|
||||
document.body.style.overflow = 'scroll';
|
||||
},
|
||||
computed: {
|
||||
filteredNotifications() {
|
||||
|
|
|
@ -122,6 +122,15 @@
|
|||
)
|
||||
}
|
||||
},
|
||||
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) => {
|
||||
|
|
|
@ -12,9 +12,12 @@
|
|||
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" :name="defaultColumns.name"
|
||||
@update="update"></repository-item-sidebar-title>
|
||||
<i id="close-icon" @click="toggleShowHideSidebar(currentItemUrl)"
|
||||
:editable="permissions?.can_manage && !defaultColumns?.archived"
|
||||
:name="defaultColumns.name"
|
||||
:archived="defaultColumns.archived"
|
||||
@update="update">
|
||||
</repository-item-sidebar-title>
|
||||
<i id="close-icon" @click="toggleShowHideSidebar(null)"
|
||||
class="sn-icon sn-icon-close ml-auto cursor-pointer my-auto mx-0"></i>
|
||||
</div>
|
||||
<div id="divider" class="w-500 bg-sn-light-grey flex items-center self-stretch h-px mt-6 mr-6"></div>
|
||||
|
@ -27,7 +30,7 @@
|
|||
|
||||
<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">
|
||||
<div id="left-col" class="flex flex-col gap-4 max-w-[350px]">
|
||||
|
||||
<!-- INFORMATION -->
|
||||
<section id="information-section">
|
||||
|
@ -128,7 +131,7 @@
|
|||
<!-- ASSIGNED -->
|
||||
<section id="assigned-section" class="flex flex-col" ref="assignedSectionRef">
|
||||
<div
|
||||
class="flex flex-row text-base 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] pb-4 leading-7 items-center justify-between transition-colors duration-300"
|
||||
ref="assigned-label"
|
||||
id="assigned-label"
|
||||
>
|
||||
|
@ -156,7 +159,7 @@
|
|||
</div>
|
||||
<div v-for="(assigned, index) in assignedModules.viewable_modules" :key="`assigned_module_${index}`"
|
||||
class="flex flex-col w-[350px] h-auto gap-4">
|
||||
<div class="flex flex-col gap-3.5">
|
||||
<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">
|
||||
|
@ -177,7 +180,7 @@
|
|||
|
||||
<!-- QR -->
|
||||
<section id="qr-section" ref="QR-label">
|
||||
<div id="QR-label" class="font-inter text-base 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-4 mt-0 transition-colors duration-300">
|
||||
{{ i18n.t('repositories.item_card.section.qr') }}
|
||||
</div>
|
||||
<div class="bar-code-container">
|
||||
|
@ -190,12 +193,36 @@
|
|||
|
||||
<!-- 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 right-[4px] ">
|
||||
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' }
|
||||
{
|
||||
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>
|
||||
</div>
|
||||
|
@ -252,6 +279,11 @@ export default {
|
|||
inRepository: false
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
reloadRepoItemSidebar: this.reload,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.repositoryItemSidebarComponent = this;
|
||||
},
|
||||
|
@ -288,28 +320,27 @@ export default {
|
|||
this.isShowing = true;
|
||||
this.loadRepositoryRow(repositoryRowUrl);
|
||||
this.currentItemUrl = repositoryRowUrl;
|
||||
return
|
||||
return;
|
||||
}
|
||||
// click on the same item - should just open/close it
|
||||
else if (this.currentItemUrl === repositoryRowUrl) {
|
||||
this.isShowing = !this.isShowing;
|
||||
return
|
||||
// same item click
|
||||
if (repositoryRowUrl === this.currentItemUrl) {
|
||||
if (this.isShowing) {
|
||||
this.toggleShowHideSidebar(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// explicit close (from emit)
|
||||
else if (repositoryRowUrl === null) {
|
||||
if (repositoryRowUrl === null) {
|
||||
this.isShowing = false;
|
||||
this.currentItemUrl = null;
|
||||
this.myModuleId = null;
|
||||
return
|
||||
return;
|
||||
}
|
||||
// click on a different item - if the item card is already showing should just fetch new data
|
||||
else {
|
||||
this.isShowing = true;
|
||||
this.myModuleId = myModuleId;
|
||||
this.loadRepositoryRow(repositoryRowUrl);
|
||||
this.currentItemUrl = repositoryRowUrl;
|
||||
return
|
||||
}
|
||||
this.isShowing = true;
|
||||
this.myModuleId = myModuleId;
|
||||
this.loadRepositoryRow(repositoryRowUrl);
|
||||
this.currentItemUrl = repositoryRowUrl;
|
||||
},
|
||||
loadRepositoryRow(repositoryRowUrl) {
|
||||
this.dataLoading = true
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
:preventLeavingUntilFilled="true"
|
||||
:attributeName="`${i18n.t('repositories.item_card.header_title')}`" :singleLine="true"
|
||||
@editingEnabled="editingName = true" @editingDisabled="editingName = false" @update="updateName" @delete="handleDelete"></inline-edit>
|
||||
<h4 v-else class="item-name my-auto truncate text-xl" :title="name">
|
||||
{{ name }}
|
||||
<h4 v-else class="item-name my-auto truncate text-xl" :title="computedName">
|
||||
{{ computedName }}
|
||||
</h4>
|
||||
</template>
|
||||
|
||||
|
@ -21,6 +21,12 @@ export default {
|
|||
props: {
|
||||
editable: Boolean,
|
||||
name: String,
|
||||
archived: Boolean,
|
||||
},
|
||||
computed: {
|
||||
computedName() {
|
||||
return this.archived ? `(A) ${this.name}` : this.name;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateName(name) {
|
||||
|
|
|
@ -120,11 +120,6 @@ export default {
|
|||
this.params = params;
|
||||
},
|
||||
dateValue(date) {
|
||||
const typesThatCantBeEmpty = ['dateRange', 'dateTimeRange'];
|
||||
if (date && (date.currentTarget === null) && typesThatCantBeEmpty.includes(this.dateType)) {
|
||||
this.errorMessage = I18n.t('repositories.item_card.date_time.errors.select_valid_value');
|
||||
return;
|
||||
}
|
||||
if(date) return new Date(date)
|
||||
return new Date()
|
||||
},
|
||||
|
|
|
@ -1,297 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<div ref="dateTimeRangeOverlay"
|
||||
class="fixed top-0 left-0 right-0 bottom-0 bg-transparent z-[999] hidden">
|
||||
</div>
|
||||
<div
|
||||
@click="enableEdit"
|
||||
v-click-outside="validateAndSave"
|
||||
class="text-sn-dark-grey font-inter text-sm font-normal leading-5 w-full rounded relative"
|
||||
:class="editableClassName"
|
||||
>
|
||||
<!-- DATE -->
|
||||
<div v-if="dateType === 'date'">
|
||||
<div v-if="isEditing || values?.datetime" ref="edit">
|
||||
<DateTimePicker
|
||||
:disabled="!canEdit"
|
||||
@change="formatDateTime($event)"
|
||||
:selectorId="`DatePicker${colId}`"
|
||||
:dateOnly="true"
|
||||
:defaultValue="dateValue(values?.datetime)"
|
||||
:standAlone="true"
|
||||
/>
|
||||
</div>
|
||||
<div v-else ref="view" :class="{ 'text-sn-dark-grey': !canEdit, 'text-sn-grey': canEdit }" >
|
||||
{{ i18n.t(`repositories.item_card.repository_date_value.${canEdit ? 'placeholder' : 'no_date'}`) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DATE RANGE -->
|
||||
<div v-else-if="dateType === 'dateRange'">
|
||||
<div v-if="isEditing || (timeFrom?.datetime && timeTo?.datetime)" ref="edit" class="w-full flex align-center">
|
||||
<div>
|
||||
<DateTimePicker
|
||||
:disabled="!canEdit"
|
||||
@change="formatDateTime($event, 'start_time')"
|
||||
:selectorId="`DatePickerStart${colId}`"
|
||||
:dateOnly="true"
|
||||
:defaultValue="dateValue(timeFrom?.datetime)"
|
||||
:standAlone="true"
|
||||
:dateClassName="hasMonthText() ? 'w-[135px]' : 'w-[90px]'"
|
||||
/>
|
||||
</div>
|
||||
<span class="mr-3">-</span>
|
||||
<div>
|
||||
<DateTimePicker
|
||||
:disabled="!canEdit"
|
||||
@change="formatDateTime($event, 'end_time')"
|
||||
:selectorId="`DatePickerEnd${colId}`"
|
||||
:dateOnly="true"
|
||||
:defaultValue="dateValue(timeTo?.datetime)"
|
||||
:standAlone="true"
|
||||
:dateClassName="hasMonthText() ? 'w-[135px]' : 'ml-2 w-[90px]'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else ref="view" :class="{ 'text-sn-dark-grey': !canEdit, 'text-sn-grey': canEdit }" >
|
||||
{{ i18n.t(`repositories.item_card.repository_date_range_value.${canEdit ? 'placeholder' : 'no_date_range'}`) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DATE-TIME -->
|
||||
<div v-if="dateType === 'dateTime'">
|
||||
<div v-if="isEditing || values?.datetime" ref="edit" class="w-full">
|
||||
<DateTimePicker
|
||||
:disabled="!canEdit"
|
||||
@change="formatDateTime"
|
||||
:selectorId="`DatePicker${colId}`"
|
||||
:defaultValue="dateValue(values?.datetime)"
|
||||
:standAlone="true"
|
||||
:dateClassName="hasMonthText() ? 'w-[135px]' : 'w-[90px]'"
|
||||
timeClassName="w-11"
|
||||
/>
|
||||
</div>
|
||||
<div v-else ref="view" :class="{ 'text-sn-dark-grey': !canEdit, 'text-sn-grey': canEdit }" >
|
||||
{{ i18n.t(`repositories.item_card.repository_date_time_value.${canEdit ? 'placeholder' : 'no_date_time'}`) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DATE-TIME RANGE -->
|
||||
<div v-else-if="dateType === 'dateTimeRange'">
|
||||
<div v-if="isEditing || (timeFrom?.datetime && timeTo?.datetime)" ref="edit" class="w-full flex">
|
||||
<div>
|
||||
<DateTimePicker
|
||||
:disabled="!canEdit"
|
||||
@change="formatDateTime($event, 'start_time')"
|
||||
:selectorId="`DatePickerStart${colId}`"
|
||||
:defaultValue="dateValue(timeFrom?.datetime)"
|
||||
:timeOnly="false"
|
||||
:dateOnly="false"
|
||||
:standAlone="true"
|
||||
:dateClassName="hasMonthText() ? 'w-[135px]' : 'w-[90px]'"
|
||||
timeClassName="w-11"
|
||||
/>
|
||||
</div>
|
||||
<span class="mx-1">-</span>
|
||||
<div>
|
||||
<DateTimePicker
|
||||
:disabled="!canEdit"
|
||||
@change="formatDateTime($event, 'end_time')"
|
||||
:selectorId="`DatePickerEnd${colId}`"
|
||||
:defaultValue="dateValue(timeTo?.datetime)"
|
||||
:timeOnly="false"
|
||||
:dateOnly="false"
|
||||
:standAlone="true"
|
||||
:dateClassName="hasMonthText() ? 'w-[135px]' : 'ml-2 w-[90px]'"
|
||||
timeClassName="w-11"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else ref="view" :class="{ 'text-sn-dark-grey': !canEdit, 'text-sn-grey': canEdit }" >
|
||||
{{ i18n.t(`repositories.item_card.repository_date_time_range_value.${canEdit ? 'placeholder' : 'no_date_time_range'}`) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TIME -->
|
||||
<div v-else-if="dateType === 'time'">
|
||||
<div v-if="isEditing || values?.datetime" ref="edit">
|
||||
<DateTimePicker
|
||||
:disabled="!canEdit"
|
||||
@change="formatDateTime"
|
||||
:selectorId="`DatePicker${colId}`"
|
||||
:timeOnly="true"
|
||||
:defaultValue="dateValue(values?.datetime)"
|
||||
:standAlone="true"
|
||||
timeClassName="w-11"
|
||||
/>
|
||||
</div>
|
||||
<div v-else ref="view" :class="{ 'text-sn-dark-grey': !canEdit, 'text-sn-grey': canEdit }">
|
||||
{{ i18n.t(`repositories.item_card.repository_time_value.${ canEdit ? 'placeholder' : 'no_time'}`) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TIME RANGE -->
|
||||
<div v-else-if="dateType === 'timeRange'">
|
||||
<div v-if="isEditing || (timeFrom?.datetime && timeTo?.datetime)" ref="edit" class="w-full flex">
|
||||
<div>
|
||||
<DateTimePicker
|
||||
:disabled="!canEdit"
|
||||
@change="formatDateTime($event, 'start_time')"
|
||||
:selectorId="`DatePickerStart${colId}`"
|
||||
:timeOnly="true"
|
||||
:defaultValue="dateValue(timeFrom?.datetime)"
|
||||
:standAlone="true"
|
||||
timeClassName="w-11"
|
||||
/>
|
||||
</div>
|
||||
<span class="mx-1">-</span>
|
||||
<div>
|
||||
<DateTimePicker
|
||||
:disabled="!canEdit"
|
||||
@change="formatDateTime($event, 'end_time')"
|
||||
:selectorId="`DatePickerEnd${colId}`"
|
||||
:timeOnly="true"
|
||||
:defaultValue="dateValue(timeTo?.datetime)"
|
||||
:standAlone="true"
|
||||
timeClassName="ml-2 w-11"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else ref="view" :class="{ 'text-sn-dark-grey': !canEdit, 'text-sn-grey': canEdit }">
|
||||
{{ i18n.t(`repositories.item_card.repository_time_range_value.${canEdit ? 'placeholder' : 'no_time_range'}`) }}
|
||||
</div>
|
||||
</div>
|
||||
<span class="absolute right-2 top-1.5" v-if="values?.reminder">
|
||||
<Reminder :value="values" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-sn-delete-red text-xs w-full " :class="{ visible: errorMessage, invisible: !errorMessage }">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { vOnClickOutside } from '@vueuse/components'
|
||||
import date_time_range from './../mixins/date_time_range';
|
||||
import DateTimePicker from '../../shared/date_time_picker.vue';
|
||||
import Reminder from './../reminder.vue';
|
||||
|
||||
export default {
|
||||
name: 'DateTimeRange',
|
||||
mixins: [date_time_range],
|
||||
components: {
|
||||
DateTimePicker,
|
||||
Reminder
|
||||
},
|
||||
directives: {
|
||||
'click-outside': vOnClickOutside
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
values: {},
|
||||
errorMessage: null,
|
||||
params: null,
|
||||
cellUpdatePath: null,
|
||||
timeFrom: null,
|
||||
timeTo: null,
|
||||
isEditing: false,
|
||||
initValue: null,
|
||||
initStartDate: null,
|
||||
initEndDate: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
dateType: String,
|
||||
colVal: null,
|
||||
colId: null,
|
||||
updatePath: null,
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
editingField: false,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
|
||||
},
|
||||
computed: {
|
||||
editableClassName() {
|
||||
const className = 'border-solid border-[1px] py-2 px-3 sci-cursor-edit'
|
||||
if (this.canEdit && this.errorMessage) return `${className} border-sn-delete-red`;
|
||||
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 ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showOverlay() {
|
||||
const overlay = this.$refs.dateTimeRangeOverlay;
|
||||
overlay.classList.remove('hidden');
|
||||
},
|
||||
hideOverlay() {
|
||||
const overlay = this.$refs.dateTimeRangeOverlay;
|
||||
overlay.classList.add('hidden');
|
||||
},
|
||||
bringCalendarToFront() {
|
||||
const calendarEl = document.querySelector('.dp__instance_calendar');
|
||||
calendarEl.classList.add('z-[9999]');
|
||||
},
|
||||
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');
|
||||
},
|
||||
focusClearedInput() {
|
||||
const firstInput = $(this.$refs.edit)?.find('input')[0];
|
||||
const secondInput = $(this.$refs.edit)?.find('input')[1];
|
||||
|
||||
// first is empty
|
||||
if (!firstInput.value) {
|
||||
firstInput.focus();
|
||||
firstInput.click();
|
||||
}
|
||||
// second is empty
|
||||
if (!secondInput.value) {
|
||||
secondInput.focus();
|
||||
secondInput.click();
|
||||
}
|
||||
|
||||
this.preventBodyScrolling();
|
||||
this.showOverlay();
|
||||
this.bringCalendarToFront();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.cellUpdatePath = this.updatePath;
|
||||
this.values = this.colVal || {};
|
||||
this.timeFrom = this.startTime
|
||||
this.timeTo = this.endTime
|
||||
this.errorMessage = null;
|
||||
this.setParams();
|
||||
this.initDate = this.colVal?.datetime;
|
||||
this.initStartDate = this.startTime?.datetime;
|
||||
this.initEndDate = this.endTime?.datetime;
|
||||
},
|
||||
watch: {
|
||||
isEditing(newValue) {
|
||||
if (!newValue) return;
|
||||
// Focus input field to open date picker
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs.edit)?.find('input')[0]?.focus();
|
||||
})
|
||||
},
|
||||
errorMessage(newVal) {
|
||||
if (newVal !== null) {
|
||||
this.$nextTick(() => {
|
||||
this.focusClearedInput();
|
||||
});
|
||||
}
|
||||
if (newVal === null) {
|
||||
this.hideOverlay();
|
||||
this.allowBodyScrolling();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,19 +1,28 @@
|
|||
<template>
|
||||
<div id="repository-asset-value-wrapper" class="flex flex-col min-min-h-[46px] h-auto gap-[6px]">
|
||||
<div class="font-inter text-sm font-semibold leading-5 truncate" :title="colName">
|
||||
{{ colName }}
|
||||
</div>
|
||||
<div class="w-fit absolute right-0 top-7">
|
||||
<a v-if="!file_name && (!uploading || error) && canEdit "
|
||||
class="btn-text-link font-normal" @click="openFileChooser">
|
||||
<div class="flex flex-row justify-between">
|
||||
<div class="font-inter text-sm font-semibold leading-5 truncate" :title="colName">
|
||||
{{ colName }}
|
||||
</div>
|
||||
<a v-if="!file_name && (!uploading || error) && canEdit"
|
||||
class="btn-text-link font-normal min-w-fit pl-4" @click="openFileChooser">
|
||||
{{ i18n.t('repositories.item_card.repository_asset_value.add_asset') }}
|
||||
</a>
|
||||
<div v-if="file_name && !uploading && canEdit" class="flex whitespace-nowrap gap-4 min-w-fit pl-4">
|
||||
<a class="btn-text-link font-normal" @click="openFileChooser">
|
||||
{{ i18n.t('general.replace') }}
|
||||
</a>
|
||||
<a class="btn-text-link font-normal" @click="clearFile">
|
||||
{{ i18n.t('general.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!uploading">
|
||||
<div v-if="file_name">
|
||||
<div class="flex flex-row justify-between">
|
||||
<div class="w-full cursor-pointer text-sn-science-blue relative" @mouseover="tooltipShowing = true" @mouseout="tooltipShowing = false">
|
||||
<a class="w-full inline-block file-preview-link truncate" :id="modalPreviewLinkId" data-no-turbolink="true"
|
||||
<div class="w-full cursor-pointer relative" @mouseover="tooltipShowing = true" @mouseout="tooltipShowing = false">
|
||||
<a class="w-full inline-block file-preview-link truncate text-sn-science-blue" :id="modalPreviewLinkId" data-no-turbolink="true"
|
||||
data-id="true" data-status="asset-present" :data-preview-url=this?.preview_url :href=this?.url>
|
||||
{{ file_name }}
|
||||
</a>
|
||||
|
@ -21,10 +30,6 @@
|
|||
:preview_url="preview_url" :icon_html="icon_html" :medium_preview_url="medium_preview_url">
|
||||
</tooltip-preview>
|
||||
</div>
|
||||
<div v-if="canEdit" class="flex whitespace-nowrap gap-4 pl-4">
|
||||
<a class="btn-text-link font-normal" @click="openFileChooser"> {{ i18n.t('general.replace') }} </a>
|
||||
<a class="btn-text-link font-normal" @click="clearFile"> {{ i18n.t('general.delete') }} </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="!error" class="flex flex-row items-center font-inter text-sm font-normal leading-5 justify-between"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,35 +1,30 @@
|
|||
<template>
|
||||
<div id="repository-date-range-value-wrapper" class="flex flex-col min-min-h-[46px] h-auto gap-[6px]">
|
||||
<div class="font-inter text-sm font-semibold leading-5 truncate" :title="colName">
|
||||
{{ colName }}
|
||||
</div>
|
||||
<DateTimeRange
|
||||
dateType="dateRange"
|
||||
:startTime="colVal?.start_time"
|
||||
:endTime="colVal?.end_time"
|
||||
<div class="flex flex-col gap-2">
|
||||
|
||||
<DateTimeComponent
|
||||
mode="date"
|
||||
:range="true"
|
||||
:colVal="colVal"
|
||||
:colId="colId"
|
||||
:colName="colName"
|
||||
:updatePath="updatePath"
|
||||
:canEdit="canEdit"
|
||||
:editingField="editingField"
|
||||
@setEditingField="$emit('setEditingField', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeRange from './DateTimeRange.vue';
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateRangeValue',
|
||||
components: { DateTimeRange },
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: null,
|
||||
updatePath: null,
|
||||
editingField: null,
|
||||
colVal: Object,
|
||||
updatePath: String,
|
||||
canEdit: { type: Boolean, default: false },
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
<template>
|
||||
<div id="repository-date-time-range-value-wrapper" class="flex flex-col min-min-h-[46px] h-auto gap-[6px]">
|
||||
<div class="font-inter text-sm font-semibold leading-5 truncate" :title="colName">
|
||||
{{ colName }}
|
||||
</div>
|
||||
<DateTimeRange
|
||||
:editingField="editingField"
|
||||
@setEditingField="$emit('setEditingField', $event)"
|
||||
dateType="dateTimeRange"
|
||||
:startTime="colVal?.start_time"
|
||||
:endTime="colVal?.end_time"
|
||||
<div class="flex flex-col gap-2">
|
||||
<DateTimeComponent
|
||||
mode="datetime"
|
||||
:range="true"
|
||||
:colVal="colVal"
|
||||
:colId="colId"
|
||||
:colName="colName"
|
||||
:updatePath="updatePath"
|
||||
:canEdit="canEdit"
|
||||
/>
|
||||
|
@ -18,18 +13,17 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeRange from './DateTimeRange.vue';
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateTimeRangeValue',
|
||||
components: { DateTimeRange },
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: null,
|
||||
editingField: null,
|
||||
updatePath: String,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
<template>
|
||||
<div id="repository-date-time-value-wrapper" class="flex flex-col min-min-h-[46px] h-auto gap-[6px]">
|
||||
<div class="font-inter text-sm font-semibold leading-5 truncate" :title="colName">
|
||||
{{ colName }}
|
||||
</div>
|
||||
<DateTimeRange
|
||||
:editingField="editingField"
|
||||
@setEditingField="$emit('setEditingField', $event)"
|
||||
dateType="dateTime"
|
||||
<div class="flex flex-col gap-2">
|
||||
<DateTimeComponent
|
||||
mode="datetime"
|
||||
:colVal="colVal"
|
||||
:colId="colId"
|
||||
:colName="colName"
|
||||
:updatePath="updatePath"
|
||||
:canEdit="canEdit"
|
||||
/>
|
||||
|
@ -16,18 +12,17 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeRange from './DateTimeRange.vue';
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateTimeValue',
|
||||
components: { DateTimeRange },
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
colName: String,
|
||||
colVal: Object,
|
||||
updatePath: String,
|
||||
editingField: null,
|
||||
canEdit: { type: Boolean, default: false }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,22 @@
|
|||
<template>
|
||||
<div id="repository-date-value-wrapper" class="flex flex-col min-min-h-[46px] h-auto gap-[6px]">
|
||||
<div class="font-inter text-sm font-semibold leading-5 truncate" :title="colName">
|
||||
{{ colName }}
|
||||
</div>
|
||||
<DateTimeRange
|
||||
:editingField="editingField"
|
||||
@setEditingField="$emit('setEditingField', $event)"
|
||||
dateType="date"
|
||||
<div class="flex flex-col gap2">
|
||||
<DateTimeComponent
|
||||
mode="date"
|
||||
:colVal="colVal"
|
||||
:colId="colId"
|
||||
:colName="colName"
|
||||
:updatePath="updatePath"
|
||||
:dataType="data_type"
|
||||
:canEdit="canEdit"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeRange from './DateTimeRange.vue';
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
|
||||
export default {
|
||||
name: 'RepositoryDateValue',
|
||||
components: { DateTimeRange },
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
:placeholder="i18n.t('repositories.item_card.dropdown_placeholder')"
|
||||
:no-options-placeholder="i18n.t('repositories.item_card.dropdown_placeholder')"
|
||||
:searchPlaceholder="i18n.t('repositories.item_card.dropdown_placeholder')"
|
||||
className="h-[38px] !pl-3"
|
||||
customClass="!h-[38px] !pl-3 sci-cursor-edit"
|
||||
optionsClassName="max-h-[300px]"
|
||||
></select-search>
|
||||
<div v-else-if="text"
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="canEdit" class="w-full">
|
||||
<div v-if="canEdit" class="w-full contents">
|
||||
<text-area :initialValue="(colVal)?.toLocaleString('fullwide', {useGrouping:false}) || ''"
|
||||
:noContentPlaceholder="i18n.t('repositories.item_card.repository_number_value.placeholder')"
|
||||
:placeholder="i18n.t('repositories.item_card.repository_number_value.placeholder')"
|
||||
|
@ -66,7 +66,7 @@ export default {
|
|||
colVal: Number,
|
||||
permissions: null,
|
||||
decimals: { type: Number, default: 0 },
|
||||
canEdit: { type: Boolean, defaul: false}
|
||||
canEdit: { type: Boolean, default: false },
|
||||
},
|
||||
methods: {
|
||||
toggleCollapse() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div id="repository-status-value-wrapper" class="flex flex-col min-min-h-[46px] h-auto gap-[6px]">
|
||||
<div ref="container" id="repository-status-value-wrapper" class="flex flex-col min-min-h-[46px] h-auto gap-[6px]">
|
||||
<div class="font-inter text-sm font-semibold leading-5 truncate" :title="colName">
|
||||
{{ colName }}
|
||||
</div>
|
||||
|
@ -17,7 +17,7 @@
|
|||
:placeholder="i18n.t('repositories.item_card.dropdown_placeholder')"
|
||||
:no-options-placeholder="i18n.t('repositories.item_card.dropdown_placeholder')"
|
||||
:searchPlaceholder="i18n.t('repositories.item_card.dropdown_placeholder')"
|
||||
className="h-[38px] !pl-3"
|
||||
customClass="!h-[38px] !pl-2 sci-cursor-edit"
|
||||
optionsClassName="max-h-[300px]"
|
||||
></select-search>
|
||||
<div v-else-if="status && icon"
|
||||
|
@ -88,16 +88,25 @@ export default {
|
|||
this.isLoading = false;
|
||||
this.selected = this.id;
|
||||
});
|
||||
this.replaceEmojiesInDropdown();
|
||||
},
|
||||
methods: {
|
||||
changeSelected(id) {
|
||||
this.selected = id;
|
||||
if (id) {
|
||||
if (id || id === null) {
|
||||
this.update(id);
|
||||
this.replaceEmojiesInDropdown();
|
||||
}
|
||||
},
|
||||
parseEmoji(content) {
|
||||
return twemoji.parse(content);
|
||||
},
|
||||
replaceEmojiesInDropdown() {
|
||||
setTimeout(() => {
|
||||
twemoji.size = "24x24";
|
||||
twemoji.base = '/images/twemoji/';
|
||||
twemoji.parse(this.$refs.container);
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
:data-manage-stock-url="values?.stock_url"
|
||||
:data-repository-row-id="repositoryId"
|
||||
>
|
||||
<div v-if="values?.stock_formatted" :data-manage-stock-url="values?.stock_url" class="text-sn-dark-grey font-inter text-sm font-normal leading-5 stock-value">
|
||||
<div v-if="values?.stock_formatted" :data-manage-stock-url="values?.stock_url"
|
||||
class="text-sn-dark-grey font-inter text-sm font-normal leading-5 stock-value overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{{ values.stock_formatted }}
|
||||
</div>
|
||||
<div v-else class="font-inter text-sm font-normal leading-5" :class="{ 'text-sn-dark-grey': !canEdit, 'text-sn-grey': canEdit }">
|
||||
|
@ -37,18 +38,18 @@
|
|||
},
|
||||
computed: {
|
||||
editableClassName() {
|
||||
const className = 'border-solid border-[1px] p-2 manage-repository-stock-value-link sci-cursor-edit'
|
||||
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: null,
|
||||
isEditing: false,
|
||||
values: null
|
||||
}
|
||||
},
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="canEdit">
|
||||
<div v-if="canEdit" class="w-full contents">
|
||||
<text-area :initialValue="colVal?.edit"
|
||||
:noContentPlaceholder="i18n.t('repositories.item_card.repository_text_value.placeholder')"
|
||||
:placeholder="i18n.t('repositories.item_card.repository_text_value.placeholder')"
|
||||
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
<div v-else-if="colVal?.edit"
|
||||
ref="textRef"
|
||||
class="text-sn-dark-grey box-content text-sm font-normal leading-5 overflow-y-auto pr-3 py-2 rounded w-[calc(100%-2rem)]]"
|
||||
class="text-sn-dark-grey box-content text-sm font-normal leading-5 overflow-y-auto pr-3 rounded w-[calc(100%-2rem)]]"
|
||||
:class="{
|
||||
'max-h-[4rem]': collapsed,
|
||||
'max-h-[40rem]': !collapsed
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
<template>
|
||||
<div id="repository-time-range-value-wrapper" class="flex flex-col min-min-h-[46px] h-auto gap-[6px]">
|
||||
<div class="font-inter text-sm font-semibold leading-5 truncate" :title="colName">
|
||||
{{ colName }}
|
||||
</div>
|
||||
<DateTimeRange
|
||||
:editingField="editingField"
|
||||
@setEditingField="$emit('setEditingField', $event)"
|
||||
dateType="timeRange"
|
||||
:startTime="colVal?.start_time"
|
||||
:endTime="colVal?.end_time"
|
||||
<div class="flex flex-col gap-2">
|
||||
<DateTimeComponent
|
||||
mode="time"
|
||||
:range="true"
|
||||
:colVal="colVal"
|
||||
:colId="colId"
|
||||
:colName="colName"
|
||||
:updatePath="updatePath"
|
||||
:canEdit="canEdit"
|
||||
/>
|
||||
|
@ -18,10 +13,10 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeRange from './DateTimeRange.vue';
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
export default {
|
||||
name: 'RepositoryTimeRangeValue',
|
||||
components: { DateTimeRange },
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
<template>
|
||||
<div id="repository-time-value-wrapper" class="flex flex-col min-min-h-[46px] h-auto gap-[6px]">
|
||||
<div class="font-inter text-sm font-semibold leading-5 truncate" :title="colName">
|
||||
{{ colName }}
|
||||
</div>
|
||||
<DateTimeRange
|
||||
:editingField="editingField"
|
||||
@setEditingField="$emit('setEditingField', $event)"
|
||||
dateType="time"
|
||||
<div class="flex flex-col gap-2">
|
||||
<DateTimeComponent
|
||||
mode="time"
|
||||
:colVal="colVal"
|
||||
:colId="colId"
|
||||
:colName="colName"
|
||||
:updatePath="updatePath"
|
||||
:canEdit="canEdit"
|
||||
/>
|
||||
|
@ -16,10 +12,10 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DateTimeRange from './DateTimeRange.vue';
|
||||
import DateTimeComponent from './date_time_component.vue';
|
||||
export default {
|
||||
name: 'RepositoryTimeValue',
|
||||
components: { DateTimeRange },
|
||||
components: { DateTimeComponent },
|
||||
props: {
|
||||
data_type: String,
|
||||
colId: Number,
|
||||
|
|
|
@ -42,7 +42,6 @@ export default {
|
|||
},
|
||||
|
||||
mounted() {
|
||||
console.log('mounted');
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
this.initializeComponent();
|
||||
this.$nextTick(() => {
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
<template>
|
||||
<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>
|
||||
<div class="flex flex-col gap-2">
|
||||
<template v-if="!canEdit">
|
||||
<span v-if="range">
|
||||
<template v-if="colVal.start_time && colVal.end_time">
|
||||
{{ colVal.start_time.formatted }} - {{ colVal.end_time.formatted }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ viewPlaceholder }}
|
||||
</template>
|
||||
</span>
|
||||
<span v-else >
|
||||
<template v-if="colVal.formatted">
|
||||
{{ colVal.formatted }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ viewPlaceholder }}
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
<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"/>
|
||||
</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"/>
|
||||
</div>
|
||||
<div class="text-xs text-sn-delete-red" v-if="error">{{ error }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 true
|
||||
},
|
||||
update() {
|
||||
const params = {}
|
||||
|
||||
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')[0]) {
|
||||
$('.dataTable').DataTable().ajax.reload(null, false);
|
||||
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}`;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
ref="modal"
|
||||
class="modal fade"
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
aria-labelledby="manage-stock-value"
|
||||
|
@ -21,7 +21,7 @@
|
|||
</template>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body !pt-[6px]">
|
||||
<p class="text-sm pb-6"> {{ i18n.t('repository_stock_values.manage_modal.enter_amount') }}</p>
|
||||
<form class="flex flex-col gap-6" @submit.prevent novalidate>
|
||||
<fieldset class="w-full flex justify-between">
|
||||
|
@ -72,17 +72,17 @@
|
|||
<template v-if="stockValue?.id">
|
||||
<div class="flex justify-between w-full items-center">
|
||||
<div class="flex flex-col w-[220px] h-24 border-rounded bg-sn-super-light-grey justify-between text-center">
|
||||
<span class="text-sm text-sn-grey leading-5">{{ i18n.t('repository_stock_values.manage_modal.current_stock') }}</span>
|
||||
<span class="text-sm text-sn-grey leading-5 pt-2">{{ i18n.t('repository_stock_values.manage_modal.current_stock') }}</span>
|
||||
<span class="text-2xl text-sn-black font-semibold leading-8" :class="{ 'text-sn-delete-red': stockValue.amount < 0 }">{{ stockValue.amount }}</span>
|
||||
<span class="text-sm text0sn-black leading-5">{{ initUnitLabel }}</span>
|
||||
<span class="text-sm text0sn-black leading-5 pb-2">{{ initUnitLabel }}</span>
|
||||
</div>
|
||||
<i class="sn-icon sn-icon-arrow-right"></i>
|
||||
<div class="flex flex-col w-[220px] h-24 border-rounded bg-sn-super-light-grey justify-between text-center">
|
||||
<span class="text-sm text-sn-grey leading-5">{{ i18n.t('repository_stock_values.manage_modal.new_stock') }}</span>
|
||||
<span class="text-sm text-sn-grey leading-5 pt-2">{{ i18n.t('repository_stock_values.manage_modal.new_stock') }}</span>
|
||||
<span class="text-2xl text-sn-black font-semibold leading-8" :class="{ 'text-sn-delete-red': newAmount < 0 }">
|
||||
{{ (newAmount || newAmount === 0) ? newAmount : '-' }}
|
||||
</span>
|
||||
<span class="text-sm text0sn-black leading-5">{{ unitLabel }}</span>
|
||||
<span class="text-sm text0sn-black leading-5 pb-2">{{ unitLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -209,6 +209,9 @@
|
|||
},
|
||||
methods: {
|
||||
setOperation($event) {
|
||||
if ($event !== this.operation) {
|
||||
this.amount = null;
|
||||
}
|
||||
this.operation = $event;
|
||||
if ([2, 3].includes($event)) {
|
||||
this.unit = this.stockValue.unit;
|
||||
|
@ -246,7 +249,7 @@
|
|||
validateAndsaveStockValue() {
|
||||
let newErrors = {};
|
||||
this.errors = newErrors;
|
||||
if (!this.unit)
|
||||
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');
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
}"
|
||||
:disabled="disabled"
|
||||
@click="toggle">
|
||||
<span>{{ valueLabel || this.placeholder || this.i18n.t('general.select') }}</span>
|
||||
<span class="overflow-hidden text-ellipsis">{{ valueLabel || this.placeholder || this.i18n.t('general.select') }}</span>
|
||||
<i class="sn-icon" :class="{ 'sn-icon-down': !isOpen, 'sn-icon-up': isOpen}"></i>
|
||||
</button>
|
||||
<div :style="optionPositionStyle" class="py-2.5 z-10 bg-white rounded border-[1px] border-sn-light-grey shadow-sn-menu-sm" :class="{ 'hidden': !isOpen }">
|
||||
<div v-if="withButtons" class="px-2.5">
|
||||
<div class="flex gap-2 pl-2 pb-2.5 justify-start items-center w-[calc(100%-10px)]">
|
||||
<div v-if="withButtons" class="px-2.5 pb-[1px]">
|
||||
<div class="flex gap-2 pl-2 justify-start items-center w-[calc(100%-10px)]">
|
||||
<div class="btn btn-light !text-xs h-[30px] px-0 active:bg-sn-super-light-blue"
|
||||
@click="selectedValues = []"
|
||||
:class="{
|
||||
|
@ -48,7 +48,7 @@
|
|||
<input v-model="selectedValues" :value="option.id" :id="option.id" type="checkbox" class="sci-checkbox project-card-selector">
|
||||
<label :for="option.id" class="sci-checkbox-label"></label>
|
||||
</div>
|
||||
<span class="text-ellipsis overflow-hidden max-h-[4rem] ml-1">{{ option.label }}</span>
|
||||
<span :title="option.label" class="text-ellipsis overflow-hidden max-h-[4rem] ml-1 whitespace-normal line-clamp-3">{{ option.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
:class="{
|
||||
'only-time': mode == 'time',
|
||||
}"
|
||||
@closed="closedHandler"
|
||||
@cleared="clearedHandler"
|
||||
v-model="compDatetime"
|
||||
:teleport="teleport"
|
||||
:no-today="true"
|
||||
|
@ -91,7 +93,7 @@
|
|||
watch: {
|
||||
defaultValue: function () {
|
||||
this.datetime = this.defaultValue;
|
||||
if (this.defaultValue) {
|
||||
if (this.defaultValue instanceof Date) {
|
||||
this.time = {
|
||||
hours: this.defaultValue.getHours(),
|
||||
minutes: this.defaultValue.getMinutes()
|
||||
|
@ -103,7 +105,7 @@
|
|||
|
||||
this.time = null;
|
||||
|
||||
if (this.datetime) {
|
||||
if (this.datetime instanceof Date) {
|
||||
this.time = {
|
||||
hours: this.datetime.getHours(),
|
||||
minutes: this.datetime.getMinutes()
|
||||
|
@ -186,6 +188,12 @@
|
|||
close() {
|
||||
this.$refs.datetimePicker.closeMenu();
|
||||
},
|
||||
closedHandler() {
|
||||
this.$emit('closed');
|
||||
},
|
||||
clearedHandler() {
|
||||
this.$emit('cleared');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
class="sn-select__options !relative !top-0 !left-[-1px] !shadow-none scroll-container px-2.5 pt-0 block"
|
||||
:class="{ [optionsClassName]: true }"
|
||||
>
|
||||
|
||||
|
||||
<div v-if="options.length" class="flex flex-col gap-[1px]">
|
||||
<div
|
||||
v-for="option in options"
|
||||
|
@ -80,7 +80,7 @@
|
|||
return option && option[1];
|
||||
},
|
||||
focusElement() {
|
||||
return this.$refs.focusElement || this.$scopedSlots.default()[0].context.$refs.focusElement;
|
||||
return this.$refs.focusElement || this.$parent.$refs.focusElement;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<Select
|
||||
class="sn-select sn-select--search"
|
||||
class="sn-select sn-select--search hover:border-sn-sleepy-grey"
|
||||
:class="customClass"
|
||||
:className="className"
|
||||
:optionsClassName="optionsClassName"
|
||||
:withEditCursor="withEditCursor"
|
||||
|
@ -38,7 +39,8 @@
|
|||
disabled: { type: Boolean },
|
||||
isLoading: { type: Boolean, default: false },
|
||||
className: { type: String, default: '' },
|
||||
optionsClassName: { type: String, default: '' }
|
||||
optionsClassName: { type: String, default: '' },
|
||||
customClass: { type: String, default: '' }
|
||||
},
|
||||
components: { Select },
|
||||
data() {
|
||||
|
|
|
@ -10,7 +10,7 @@ module Cloneable
|
|||
last_clone_number =
|
||||
parent.public_send(self.class.table_name)
|
||||
.select("substring(#{self.class.table_name}.name, '(?:^#{clone_label} )(\\d+)')::int AS clone_number")
|
||||
.where('name ~ ?', "^#{clone_label} \\d+ - #{name}$")
|
||||
.where('name ~ ?', "^#{clone_label} \\d+ - #{Regexp.escape(name)}$")
|
||||
.order(clone_number: :asc)
|
||||
.last&.clone_number
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ module RepositoryDatatable
|
|||
!scope[:repository].is_a?(RepositorySnapshot) &&
|
||||
value_object.data.present? &&
|
||||
value_object.low_stock_threshold.present?
|
||||
data[:reminder] = value_object.low_stock_threshold > value_object.data
|
||||
data[:reminder] = value_object.low_stock_threshold >= value_object.data
|
||||
if data[:reminder] && value_object.data&.positive?
|
||||
data[:reminder_text] =
|
||||
I18n.t('repositories.item_card.reminders.stock_low', stock_formated: value_object.formatted)
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
team,
|
||||
subject.repository.name&.truncate(Constants::NAME_TRUNCATION_LENGTH),
|
||||
title: subject.repository.name) %>
|
||||
<% elsif type_of == 'delete_item_inventory' && values.dig('message_items', 'repository', 'id') %>
|
||||
<% repository = Repository.find(values.dig('message_items', 'repository', 'id')) %>
|
||||
<%= route_to_other_team(repository_path(repository.id, team: repository.team.id),
|
||||
team,
|
||||
repository.name&.truncate(Constants::NAME_TRUNCATION_LENGTH),
|
||||
title: repository.name) %>
|
||||
<% else %>
|
||||
<span title="<%= breadcrumbs['repository'] %>">
|
||||
<%= breadcrumbs['repository']&.truncate(Constants::NAME_TRUNCATION_LENGTH) %>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<%= t("projects.reports.wizard.third_step.protocol_step") %>
|
||||
<div class="divider"></div>
|
||||
<ul class="step-contents report-protocol-settings">
|
||||
<% %i(completed_steps uncompleted_steps step_texts step_checklists step_files step_tables step_well_plates step_comments).each do |step_content| %>
|
||||
<% %i(completed_steps uncompleted_steps step_texts step_files step_tables step_well_plates step_checklists step_comments).each do |step_content| %>
|
||||
<li>
|
||||
<span class="sci-checkbox-container">
|
||||
<input type="checkbox" class="sci-checkbox protocol-setting" value="<%= step_content %>" <%= 'checked' if report.settings.dig(:task, :protocol, step_content) %> />
|
||||
|
@ -128,6 +128,14 @@
|
|||
</div>
|
||||
<div class="divider"></div>
|
||||
<ul class="results-type-contents report-result-settings">
|
||||
<li>
|
||||
<span class="sci-checkbox-container">
|
||||
<input type="checkbox" class="sci-checkbox task-setting" value="text_results" <%= 'checked' if report.settings.dig(:task, :text_results) %>/>
|
||||
<span class="sci-checkbox-label"></span>
|
||||
</span>
|
||||
<%= t("projects.reports.wizard.third_step.text_results") %>
|
||||
<div class="divider"></div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="file-result-title-container">
|
||||
<span class="sci-checkbox-container">
|
||||
|
@ -159,22 +167,14 @@
|
|||
</li>
|
||||
<li>
|
||||
<span class="sci-checkbox-container">
|
||||
<input type="checkbox" class="sci-checkbox task-setting" value="text_results" <%= 'checked' if report.settings.dig(:task, :text_results) %>/>
|
||||
<input type="checkbox" class="sci-checkbox task-setting" value="result_comments" <%= 'checked' if report.settings.dig(:task, :result_comments) %>/>
|
||||
<span class="sci-checkbox-label"></span>
|
||||
</span>
|
||||
<%= t("projects.reports.wizard.third_step.text_results") %>
|
||||
<%= t("projects.reports.wizard.third_step.results_comments") %>
|
||||
<div class="divider"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<span class="sci-checkbox-container">
|
||||
<input type="checkbox" class="sci-checkbox task-setting" value="result_comments" <%= 'checked' if report.settings.dig(:task, :result_comments) %>/>
|
||||
<span class="sci-checkbox-label"></span>
|
||||
</span>
|
||||
<%= t("projects.reports.wizard.third_step.results_comments") %>
|
||||
<div class="divider"></div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="divider"></div>
|
||||
</li>
|
||||
|
|
|
@ -759,7 +759,7 @@ en:
|
|||
assigned_items_description: "Inventories selected below will only contain the items that you assigned to the tasks directly."
|
||||
include_all_assigned_iitems: "Include all assigned items from the following inventories"
|
||||
results: "Results"
|
||||
all_results: "Include all results"
|
||||
all_results: "Include all results elements"
|
||||
order_results: "Order results"
|
||||
atoz: "Name A to Z"
|
||||
ztoa: "Name Z to A"
|
||||
|
@ -767,12 +767,12 @@ en:
|
|||
old: "Added first"
|
||||
new_updated: "Modified last"
|
||||
old_updated: "Modified first"
|
||||
file_results: "File results"
|
||||
file_results: "Result files"
|
||||
insert_pages_from_pdf: "Insert pages from PDF and docx files into the report"
|
||||
pdf_warning: "This may increase report generation time."
|
||||
table_results: "Table results"
|
||||
text_results: "Text results"
|
||||
results_comments: "Include all result comments"
|
||||
table_results: "Result tables"
|
||||
text_results: "Result texts"
|
||||
results_comments: "Result comments"
|
||||
additional_content: "Additional content"
|
||||
task_activity: "Include task activity"
|
||||
archived: "[archived]"
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Metrics/BlockLength
|
||||
|
||||
namespace :data do
|
||||
Rails.logger = Logger.new(STDOUT)
|
||||
|
||||
|
@ -5,7 +9,6 @@ namespace :data do
|
|||
task clean_temp_files: :environment do
|
||||
Rails.logger.info "Cleaning temporary files older than 3 days"
|
||||
TempFile.where("created_at < ?", 3.days.ago).each do |tmp_file|
|
||||
|
||||
TempFile.transaction do
|
||||
begin
|
||||
tmp_file.destroy!
|
||||
|
@ -194,4 +197,37 @@ namespace :data do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Reset protocols creator user assignments'
|
||||
task reset_protocols_creator_user_assignments: :environment do
|
||||
ActiveRecord::Base.transaction do
|
||||
owner_role = UserRole.find_predefined_owner_role
|
||||
protocols =
|
||||
Protocol.where(protocol_type: Protocol::REPOSITORY_TYPES)
|
||||
.joins('LEFT OUTER JOIN "user_assignments" ON "user_assignments"."assignable_type" = \'Protocol\' ' \
|
||||
'AND "user_assignments"."assignable_id" = "protocols"."id" ' \
|
||||
'AND "user_assignments"."assigned" = 1 ' \
|
||||
'AND "user_assignments"."user_id" = "protocols"."added_by_id"')
|
||||
.where('"user_assignments"."id" IS NULL')
|
||||
.distinct
|
||||
protocols.find_each do |protocol|
|
||||
new_user_assignment = protocol.user_assignments
|
||||
.find_or_initialize_by(user: protocol.added_by, team: protocol.team)
|
||||
new_user_assignment.user_role = owner_role
|
||||
new_user_assignment.assigned_by = protocol.added_by
|
||||
new_user_assignment.assigned = :manually
|
||||
new_user_assignment.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Extract missing asset texts'
|
||||
task extract_missing_asset_texts: :environment do
|
||||
Asset.joins(:file_blob)
|
||||
.where.missing(:asset_text_datum)
|
||||
.where(file_blob: { content_type: Constants::TEXT_EXTRACT_FILE_TYPES })
|
||||
.find_each(&:extract_asset_text)
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:enable Metrics/BlockLength
|
||||
|
|
Loading…
Add table
Reference in a new issue