Merge pull request #8433 from andrej-scinote/aj_SCI_11799

Add new columns to experiments table [SCI-11799]
This commit is contained in:
andrej-scinote 2025-04-17 11:35:37 +02:00 committed by GitHub
commit 305f689820
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 197 additions and 6 deletions

View file

@ -459,7 +459,7 @@ class ExperimentsController < ApplicationController
end
def experiment_params
params.require(:experiment).permit(:name, :description, :archived)
params.require(:experiment).permit(:name, :description, :archived, :due_date, :start_on, :status)
end
def move_experiment_param

View file

@ -23,6 +23,9 @@
@edit="edit"
@create="create"
@access="access"
@updateDueDate="updateDueDate"
@updateStartDate="updateStartDate"
@changeStatus="changeStatus"
>
<template #card="data">
<ExperimentCard :params="data.params" :dtComponent="data.dtComponent" ></ExperimentCard>
@ -71,6 +74,9 @@ import MoveModal from './modals/move.vue';
import EditModal from './modals/edit.vue';
import NewModal from './modals/new.vue';
import AccessModal from '../shared/access_modal/modal.vue';
import StatusRenderer from './renderers/status.vue';
import DueDateRenderer from '../shared/datatable/renderers/date.vue';
import StartDateRenderer from '../shared/datatable/renderers/date.vue';
import ExperimentCard from './card.vue';
export default {
@ -84,7 +90,10 @@ export default {
EditModal,
NewModal,
AccessModal,
ExperimentCard
ExperimentCard,
StatusRenderer,
StartDateRenderer,
DueDateRenderer
},
props: {
dataSource: { type: String, required: true },
@ -124,6 +133,41 @@ export default {
sortable: true,
minWidth: 80
},
{
field: 'status',
headerName: this.i18n.t('experiments.table.column.status_html'),
sortable: true,
cellRenderer: StatusRenderer,
minWidth: 200
},
{
field: 'start_date',
headerName: this.i18n.t('experiments.table.column.start_date_html'),
sortable: true,
cellRenderer: StartDateRenderer,
cellRendererParams: {
placeholder: this.i18n.t('experiments.table.column.no_start_date_placeholder'),
field: 'start_date_cell',
mode: 'date',
emptyPlaceholder: this.i18n.t('experiments.table.column.no_due_date'),
emitAction: 'updateStartDate'
},
minWidth: 180
},
{
field: 'due_date',
headerName: this.i18n.t('experiments.table.column.due_date_html'),
sortable: true,
cellRenderer: DueDateRenderer,
cellRendererParams: {
placeholder: this.i18n.t('experiments.table.column.no_due_date_placeholder'),
field: 'due_date_cell',
mode: 'date',
emptyPlaceholder: this.i18n.t('experiments.table.column.no_due_date'),
emitAction: 'updateDueDate'
},
minWidth: 200
},
{
field: 'created_at',
headerName: this.i18n.t('experiments.card.start_date'),
@ -148,7 +192,7 @@ export default {
columns.push({
field: 'completed_tasks',
headerName: this.i18n.t('experiments.card.completed_task'),
headerName: this.i18n.t('experiments.table.column.completed_task'),
cellRenderer: CompletedTasksRenderer,
sortable: true,
minWidth: 110
@ -267,6 +311,29 @@ export default {
object: rows[0],
roles_path: this.userRolesUrl
};
},
formatDate(date) {
if (!(date instanceof Date)) return null;
const y = date.getFullYear();
const m = date.getMonth() + 1;
const d = date.getDate();
return `${y}/${m}/${d}`;
},
updateField(url, params) {
axios.put(url, params).then(() => {
this.updateTable();
});
},
changeStatus(value, params) {
this.updateField(params.data.urls.update, { experiment: { status: value } });
},
updateDueDate(value, params) {
this.updateField(params.data.urls.update, { due_date: this.formatDate(value) });
},
updateStartDate(value, params) {
this.updateField(params.data.urls.update, { start_on: this.formatDate(value) });
}
}
};

View file

@ -0,0 +1,68 @@
<template>
<div v-if="params.data.status_cell.editable" class="py-0.5">
<SelectDropdown
:options="statuses"
:value="params.data.status_cell.status"
@change="changeStatus"
size="xs"
:borderless="true"
:optionRenderer="optionRenderer"
:labelRenderer="optionRenderer"
/>
</div>
<div v-else class="flex items-center gap-2 py-0.5">
<div class="w-3 h-3 rounded-full"
:class="this.statusColor(params.data.status_cell.status)"></div>
<span class="truncate">
{{ this.i18n.t(`experiments.table.column.status.${params.data.status_cell.status}`) }}
</span>
</div>
</template>
<script>
import SelectDropdown from '../../shared/select_dropdown.vue';
export default {
name: 'StatusRenderer',
components: {
SelectDropdown
},
props: {
params: {
required: true
}
},
data() {
return {
statuses: [
['not_started', this.i18n.t('experiments.table.column.status.not_started')],
['started', this.i18n.t('experiments.table.column.status.started')],
['completed', this.i18n.t('experiments.table.column.status.completed')]
]
};
},
methods: {
statusColor(status) {
let color = 'bg-sn-grey-500';
if (status === 'started') {
color = 'bg-sn-science-blue';
} else if (status === 'completed') {
color = 'bg-sn-alert-green';
}
return color;
},
optionRenderer(option) {
return `
<div class="flex items-center gap-2">
<div class="${this.statusColor(option[0])} w-3 h-3 rounded-full"></div>
<span>${option[1]}</span>
</div>`;
},
changeStatus(newStatus) {
this.params.dtComponent.$emit('changeStatus', newStatus, this.params);
}
}
};
</script>

View file

@ -79,6 +79,7 @@ export default {
DataTable,
ConfirmationModal,
DueDateRenderer,
StartDateRenderer,
DesignatedUsers,
TagsModal,
NewModal,

View file

@ -1,7 +1,7 @@
<template>
<div class="flex items-center gap-2">
<div
class="w-4 h-4 rounded-full"
class="w-3 h-3 rounded-full"
:class="{ 'bg-sn-grey-500': params.data.status.light_color }"
:style="!params.data.status.light_color && { backgroundColor: params.data.status.color }"
></div>

View file

@ -555,6 +555,14 @@ class Experiment < ApplicationRecord
true
end
def overdue?(date = Date.current)
due_date.present? && date >= due_date
end
def one_day_prior?(date = Date.current)
due_date.present? && date < due_date && date > (due_date - 1.day)
end
private
def log_activity(type_of, current_user, my_module)

View file

@ -9,7 +9,7 @@ module Lists
attributes :name, :code, :created_at, :updated_at, :workflow_img, :description, :completed_tasks,
:total_tasks, :archived_on, :urls, :sa_description, :default_public_user_role_id, :team,
:top_level_assignable, :hidden, :archived, :project_id
:top_level_assignable, :hidden, :archived, :project_id, :due_date_cell, :start_date_cell, :status_cell
def created_at
I18n.l(object.created_at, format: :full_date)
@ -80,5 +80,43 @@ module Lists
def workflow_img
rails_blob_path(object.workflowimg, only_path: true) if object.workflowimg.attached?
end
def status_cell
{
status: object.status,
editable: can_manage_experiment?(object)
}
end
def due_date_cell
{
value: due_date,
value_formatted: due_date,
editable: can_manage_experiment?(object),
icon: (if object.one_day_prior? && !object.completed?
'sn-icon sn-icon-alert-warning text-sn-alert-brittlebush'
elsif object.overdue? && !object.completed?
'sn-icon sn-icon-alert-warning text-sn-delete-red'
end)
}
end
def start_date_cell
{
value: start_date,
value_formatted: start_date,
editable: can_manage_experiment?(object)
}
end
private
def due_date
I18n.l(object.due_date, format: :full_date) if object.due_date
end
def start_date
I18n.l(object.start_on, format: :full_date) if object.start_on
end
end
end

View file

@ -102,7 +102,7 @@ module Lists
{
value: start_date,
value_formatted: start_date_formatted,
editable: can_update_my_module_due_date?(object)
editable: can_update_my_module_start_date?(object)
}
end

View file

@ -1791,6 +1791,15 @@ en:
assigned_html: 'Assigned to'
tags_html: 'Tags'
comments_html: 'Comments'
status_html: 'Status'
completed_task: 'Tasks done'
no_due_date_placeholder: '+ Add due date'
no_due_date: 'not set'
no_start_date_placeholder: "+ Add starting date"
status:
not_started: 'Not started'
started: 'In progress'
completed: 'Done'
modal_move_modules:
title: "Move task(s) to experiment"
confirm: "Move"