Merge branch 'develop' into features/linked-inventories

This commit is contained in:
Oleksii Kriuchykhin 2023-12-05 14:12:42 +01:00
commit feeb350114
36 changed files with 448 additions and 471 deletions

View file

@ -50,7 +50,7 @@ GIT
GIT
remote: https://github.com/scinote-eln/yomu
revision: 020ab670b2919f3b436e926a890d1dad23d75676
revision: 09b7b4910f59453970aab03d7b3ddb60b41db89a
branch: master
specs:
yomu (0.2.4)

View file

@ -615,6 +615,7 @@ var ExperimnetTable = {
this.appendRows(result.data);
this.initDueDatePicker(result.data);
this.handleNoResults();
this.initProvisioningStatusPolling();
}, 100);
InfiniteScroll.init(this.table, {

View file

@ -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);

View file

@ -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?

View file

@ -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() {

View file

@ -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) => {

View file

@ -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

View file

@ -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) {

View file

@ -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()
},

View file

@ -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>

View file

@ -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"

View file

@ -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>

View file

@ -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 },
}
}

View file

@ -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 }
}
}

View file

@ -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 }
}
}

View file

@ -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,

View file

@ -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"

View file

@ -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() {

View file

@ -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);
}
}
};

View file

@ -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
}
},

View file

@ -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

View file

@ -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,

View file

@ -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,

View file

@ -42,7 +42,6 @@ export default {
},
mounted() {
console.log('mounted');
window.addEventListener('resize', this.handleResize);
this.initializeComponent();
this.$nextTick(() => {

View file

@ -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>

View file

@ -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');

View file

@ -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>

View file

@ -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>

View file

@ -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() {

View file

@ -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() {

View file

@ -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

View file

@ -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)

View file

@ -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) %>

View file

@ -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>

View file

@ -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]"

View file

@ -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