Add step layout [SCI-6755]

This commit is contained in:
Anton 2022-04-28 11:13:38 +02:00
parent 8aaab73856
commit 6dde945d61
10 changed files with 195 additions and 38 deletions

View file

@ -39,6 +39,7 @@
@import "reports/*";
@import "settings/*";
@import "shared/*";
@import "steps/*";
@import "themes/*";
@import "*"

View file

@ -0,0 +1,30 @@
.task-protocol {
.insert-step {
align-items: center;
color: $color-concrete;
cursor: pointer;
display: flex;
&::before,
&::after {
background-color: $color-concrete;
content: '';
display: inline-block;
flex-grow: 1;
height: 3px;
}
.fa-plus {
margin: 0 .5em;
}
&:hover {
color: $brand-primary;
&::before,
&::after {
background-color: $brand-primary;
}
}
}
}

View file

@ -27,7 +27,6 @@
}
.task-section {
border-left: 3px solid $color-concrete;
margin: 16px 0;
padding-left: 16px;

View file

@ -0,0 +1,55 @@
.step-container {
padding: 8px 24px;
margin: 20px 0;
.step-header {
align-items: center;
display: flex;
.step-collapse-link {
display: inline-block;
line-height: 24px;
text-align: center;
width: 24px;
&:not(.collapsed) {
@include rotate(90deg);
}
}
.step-state {
border: 2px solid $color-alto;
border-radius: 50%;
cursor: pointer;
height: 24px;
text-align: center;
width: 24px;
&.completed {
background: $brand-success;
border: 2px solid $brand-success;
&::after {
@include font-awesome;
color: $color-white;
content: $font-fas-check;
}
}
}
.step-position {
@include font-main;
line-height: 24px;
margin: 0 4px;
}
.step-name-container {
flex-grow: 1;
}
.step-actions-container {
display: flex;
justify-content: flex-end;
}
}
}

View file

@ -32,13 +32,21 @@ class StepsController < ApplicationController
end
def create
new_step = Step.new(
@step = Step.new(
name: t('protocols.steps.default_name'),
completed: false,
user: current_user,
last_modified_by: current_user
)
render json: @protocol.insert_step(new_step, params[:position]), serializer: StepSerializer
@step = @protocol.insert_step(@step, params[:position])
# Generate activity
if @protocol.in_module?
log_activity(:create_step, @my_module.experiment.project, my_module: @my_module.id)
else
log_activity(:add_step_to_protocol_repository, nil, protocol: @protocol.id)
end
render json: @step, serializer: StepSerializer
end
def create_old
@ -143,6 +151,20 @@ class StepsController < ApplicationController
end
def update
if @step.update(step_params)
# Generate activity
if @protocol.in_module?
log_activity(:edit_step, @my_module.experiment.project, my_module: @my_module.id)
else
log_activity(:edit_step_in_protocol_repository, nil, protocol: @protocol.id)
end
render json: @step, serializer: StepSerializer
else
render json: {}, status: :unprocessable_entity
end
end
def update_old
respond_to do |format|
old_description = @step.description
old_checklists = fetch_old_checklists_data(@step)
@ -327,34 +349,30 @@ class StepsController < ApplicationController
# Complete/uncomplete step
def toggle_step_state
respond_to do |format|
completed = params[:completed] == 'true'
changed = @step.completed != completed
@step.completed = completed
@step.last_modified_by = current_user
@step.completed = params[:completed] == 'true'
@step.last_modified_by = current_user
if @step.save
# Create activity
if changed
completed_steps = @protocol.steps.where(completed: true).count
all_steps = @protocol.steps.count
if @step.save
# Create activity
if @step.saved_change_to_completed
completed_steps = @protocol.steps.where(completed: true).count
all_steps = @protocol.steps.count
type_of = completed ? :complete_step : :uncomplete_step
# Toggling step state can only occur in
# module protocols, so my_module is always
# not nil; nonetheless, check if my_module is present
if @protocol.in_module?
log_activity(type_of,
@protocol.my_module.experiment.project,
my_module: @my_module.id,
num_completed: completed_steps.to_s,
num_all: all_steps.to_s)
end
type_of = @step.completed ? :complete_step : :uncomplete_step
# Toggling step state can only occur in
# module protocols, so my_module is always
# not nil; nonetheless, check if my_module is present
if @protocol.in_module?
log_activity(type_of,
@protocol.my_module.experiment.project,
my_module: @my_module.id,
num_completed: completed_steps.to_s,
num_all: all_steps.to_s)
end
format.json { render json: {}, status: :ok }
else
format.json { render json: {}, status: :unprocessable_entity }
end
render json: @step, serializer: StepSerializer
else
render json: {}, status: :unprocessable_entity
end
end
@ -514,6 +532,10 @@ class StepsController < ApplicationController
end
def step_params
params.require(:step).permit(:name)
end
def step_params_old
params.require(:step).permit(
:name,
:description,

View file

@ -73,13 +73,13 @@
<div class="protocol-steps">
<template v-for="(step, index) in steps">
<div class="step-block" :key="step.id">
<button v-if="index > 0" class="btn btn-primary" @click="addStep(index)">
<div v-if="index > 0" class="insert-step" @click="addStep(index)">
<i class="fas fa-plus"></i>
{{ i18n.t("protocols.steps.new_step") }}
</button>
</div>
<Step
:step.sync="steps[index]"
@step:delete="updateStepsPosition"
@step:update="updateStep"
/>
</div>
</template>
@ -166,6 +166,9 @@
this.reorderSteps(unordered_steps)
},
updateStep(step) {
this.$set(this.steps, step.attributes.position, step)
},
reorderSteps(steps) {
this.steps = steps.sort((a, b) => a.attributes.position - b.attributes.position);
}

View file

@ -1,15 +1,42 @@
<template>
<div class="step-container">
{{ step.attributes.position + 1 }}
{{ step.attributes.name }}
<button class="btn btn-danger" @click="deleteStep">
<i class="fas fa-trash"></i>
{{ i18n.t("protocols.steps.options.delete_title") }}
</button>
<div class="step-header">
<a class="step-collapse-link"
:href="'#stepBody' + step.id"
data-toggle="collapse"
data-remote="true">
<span class="fas fa-caret-right"></span>
</a>
<div class="step-complete-container">
<div :class="`step-state ${step.attributes.completed ? 'completed' : ''}`" @click="changeState"></div>
</div>
<div class="step-position">
{{ step.attributes.position + 1 }}.
</div>
<div class="step-name-container">
<InlineEdit
:value="step.attributes.name"
:characterLimit="255"
:allowBlank="false"
:attributeName="`${i18n.t('Step')} ${i18n.t('name')}`"
@update="updateName"
/>
</div>
<div class="step-actions-container">
<button class="btn icon-btn btn-light" @click="deleteStep">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="collapse in" :id="'stepBody' + step.id">
Components here
</div>
</div>
</template>
<script>
import InlineEdit from 'vue/shared/inline_edit.vue'
export default {
name: 'StepContainer',
props: {
@ -18,6 +45,7 @@
required: true
}
},
components: { InlineEdit },
methods: {
deleteStep() {
$.ajax({
@ -31,6 +59,21 @@
);
}
});
},
changeState() {
$.post(this.step.attributes.urls.state_url, {completed: !this.step.attributes.completed}, (result) => {
this.$emit('step:update', result.data)
})
},
updateName(newName) {
$.ajax({
url: this.step.attributes.urls.update_url,
type: 'PATCH',
data: {step: {name: newName}},
success: (result) => {
this.$emit('step:update', result.data)
}
});
}
}
}

View file

@ -37,7 +37,7 @@
}
},
created( ){
this.newValue = this.value;
this.newValue = this.value || '';
},
mounted() {
if (this.autofocus) {

View file

@ -5,7 +5,9 @@ class StepSerializer < ActiveModel::Serializer
def urls
{
delete_url: step_path(object)
delete_url: step_path(object),
state_url: toggle_step_state_step_path(object),
update_url: step_path(object)
}
end
end

View file

@ -50,6 +50,8 @@ CREATE FUNCTION public.trim_html_tags(input text, OUT output text) RETURNS text
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- Name: active_storage_attachments; Type: TABLE; Schema: public; Owner: -
--