fix: related tasks add button and task dates in read only view
continuous-integration/drone/pr Build is failing Details

This commit is contained in:
kolaente 2021-12-30 17:51:06 +01:00
parent 0befa58908
commit 05659fa9a1
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
3 changed files with 234 additions and 221 deletions

View File

@ -0,0 +1,39 @@
<template>
<p class="created">
<i18n-t keypath="task.detail.created">
<span v-tooltip="formatDate(task.created)">{{ formatDateSince(task.created) }}</span>
{{ task.createdBy.getDisplayName() }}
</i18n-t>
<template v-if="+new Date(task.created) !== +new Date(task.updated)">
<br/>
<!-- Computed properties to show the actual date every time it gets updated -->
<i18n-t keypath="task.detail.updated">
<span v-tooltip="updatedFormatted">{{ updatedSince }}</span>
</i18n-t>
</template>
<template v-if="task.done">
<br/>
<i18n-t keypath="task.detail.doneAt">
<span v-tooltip="doneFormatted">{{ doneSince }}</span>
</i18n-t>
</template>
</p>
</template>
<script lang="ts" setup>
import {computed, defineProps} from 'vue'
import TaskModel from '@/models/task'
import {formatDateLong, formatDateSince} from '@/helpers/time/formatDate'
const {task} = defineProps({
task: {
type: TaskModel,
required: true,
}
})
const updatedSince = computed(() => formatDateSince(task.updated))
const updatedFormatted = computed(() => formatDateLong(task.updated))
const doneSince = computed(() => formatDateSince(task.doneAt))
const doneFormatted = computed(() => formatDateLong(task.doneAt))
</script>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="task-relations"> <div class="task-relations">
<x-button <x-button
v-if="Object.keys(relatedTasks).length > 0" v-if="editEnabled && Object.keys(relatedTasks).length > 0"
@click="showNewRelationForm = !showNewRelationForm" @click="showNewRelationForm = !showNewRelationForm"
class="is-pulled-right add-task-relation-button" class="is-pulled-right add-task-relation-button"
:class="{'is-active': showNewRelationForm}" :class="{'is-active': showNewRelationForm}"

View File

@ -246,8 +246,8 @@
<!-- Comments --> <!-- Comments -->
<comments :can-write="canWrite" :task-id="taskId"/> <comments :can-write="canWrite" :task-id="taskId"/>
</div> </div>
<div class="column is-one-third action-buttons"> <div class="column is-one-third action-buttons" v-if="canWrite || shouldShowClosePopup">
<a @click="$router.back()" class="is-fullwidth is-block has-text-centered mb-4" v-if="shouldShowClosePopup"> <a @click="$router.back()" class="is-fullwidth is-block has-text-centered mb-4">
<icon icon="arrow-left"/> <icon icon="arrow-left"/>
{{ $t('task.detail.closePopup') }} {{ $t('task.detail.closePopup') }}
</a> </a>
@ -385,27 +385,11 @@
</template> </template>
<!-- Created / Updated [by] --> <!-- Created / Updated [by] -->
<p class="created"> <created-updated :task="task"/>
<i18n-t keypath="task.detail.created">
<span v-tooltip="formatDate(task.created)">{{ formatDateSince(task.created) }}</span>
{{ task.createdBy.getDisplayName() }}
</i18n-t>
<template v-if="+new Date(task.created) !== +new Date(task.updated)">
<br/>
<!-- Computed properties to show the actual date every time it gets updated -->
<i18n-t keypath="task.detail.updated">
<span v-tooltip="updatedFormatted">{{ updatedSince }}</span>
</i18n-t>
</template>
<template v-if="task.done">
<br/>
<i18n-t keypath="task.detail.doneAt">
<span v-tooltip="doneFormatted">{{ doneSince }}</span>
</i18n-t>
</template>
</p>
</div> </div>
</div> </div>
<!-- Created / Updated [by] -->
<created-updated :task="task" v-if="!canWrite && !shouldShowClosePopup"/>
</div> </div>
<transition name="modal"> <transition name="modal">
@ -452,10 +436,12 @@ import {CURRENT_LIST} from '@/store/mutation-types'
import {uploadFile} from '@/helpers/attachments' import {uploadFile} from '@/helpers/attachments'
import ChecklistSummary from '../../components/tasks/partials/checklist-summary' import ChecklistSummary from '../../components/tasks/partials/checklist-summary'
import CreatedUpdated from '@/components/tasks/partials/createdUpdated'
export default { export default {
name: 'TaskDetailView', name: 'TaskDetailView',
components: { components: {
CreatedUpdated,
ChecklistSummary, ChecklistSummary,
TaskSubscription, TaskSubscription,
Datepicker, Datepicker,
@ -547,18 +533,6 @@ export default {
canWrite() { canWrite() {
return typeof this.task !== 'undefined' && typeof this.task.maxRight !== 'undefined' && this.task.maxRight > rights.READ return typeof this.task !== 'undefined' && typeof this.task.maxRight !== 'undefined' && this.task.maxRight > rights.READ
}, },
updatedSince() {
return this.formatDateSince(this.task.updated)
},
updatedFormatted() {
return this.formatDate(this.task.updated)
},
doneSince() {
return this.formatDateSince(this.task.doneAt)
},
doneFormatted() {
return this.formatDate(this.task.doneAt)
},
hasAttachments() { hasAttachments() {
return this.$store.state.attachments.attachments.length > 0 return this.$store.state.attachments.attachments.length > 0
}, },
@ -697,239 +671,239 @@ export default {
$flash-background-duration: 750ms; $flash-background-duration: 750ms;
.task-view { .task-view {
// This is a workaround to hide the llama background from the top on the task detail page // This is a workaround to hide the llama background from the top on the task detail page
margin-top: -1.5rem; margin-top: -1.5rem;
padding: 1rem; padding: 1rem;
background-color: var(--site-background); background-color: var(--site-background);
@media screen and (max-width: $desktop) { @media screen and (max-width: $desktop) {
padding-bottom: 0; padding-bottom: 0;
}
.subtitle {
color: var(--grey-500);
margin-bottom: 1rem;
a {
color: var(--grey-800);
}
}
h3 .button {
vertical-align: middle;
}
.icon.is-grey {
color: var(--grey-400);
}
:deep(.heading) {
display: flex;
justify-content: space-between;
text-transform: none;
align-items: center;
@media screen and (max-width: $tablet) {
flex-direction: column;
align-items: start;
}
.title {
margin-bottom: 0;
} }
.title.input { .subtitle {
// 1.8rem is the font-size, 1.125 is the line-height, .3rem padding everywhere, 1px border around the whole thing. color: var(--grey-500);
min-height: calc(1.8rem * 1.125 + .6rem + 2px); margin-bottom: 1rem;
a {
color: var(--grey-800);
}
}
h3 .button {
vertical-align: middle;
}
.icon.is-grey {
color: var(--grey-400);
}
:deep(.heading) {
display: flex;
justify-content: space-between;
text-transform: none;
align-items: center;
@media screen and (max-width: $tablet) { @media screen and (max-width: $tablet) {
margin: 0 -.3rem .5rem -.3rem; // the title has 0.3rem padding - this make the text inside of it align with the rest flex-direction: column;
align-items: start;
} }
}
.title.task-id { .title {
color: var(--grey-400); margin-bottom: 0;
white-space: nowrap; }
}
} .title.input {
// 1.8rem is the font-size, 1.125 is the line-height, .3rem padding everywhere, 1px border around the whole thing.
min-height: calc(1.8rem * 1.125 + .6rem + 2px);
.date-input { @media screen and (max-width: $tablet) {
display: flex; margin: 0 -.3rem .5rem -.3rem; // the title has 0.3rem padding - this make the text inside of it align with the rest
align-items: center; }
}
a.remove { .title.task-id {
color: var(--danger); color: var(--grey-400);
vertical-align: middle; white-space: nowrap;
padding-left: .5rem; }
line-height: 1;
}
}
:deep(.datepicker) { }
width: 100%;
a.show { .date-input {
color: var(--text); display: flex;
padding: .25rem .5rem; align-items: center;
transition: background-color $transition;
border-radius: $radius;
display: block;
margin: .1rem 0;
&:hover { a.remove {
background: var(--white); color: var(--danger);
} vertical-align: middle;
} padding-left: .5rem;
line-height: 1;
}
}
&.disabled a.show:hover { :deep(.datepicker) {
background: transparent; width: 100%;
}
}
.details { a.show {
padding-bottom: 0.75rem; color: var(--text);
flex-flow: row wrap; padding: .25rem .5rem;
margin-bottom: 0; transition: background-color $transition;
border-radius: $radius;
display: block;
margin: .1rem 0;
.detail-title { &:hover {
display: block; background: var(--white);
color: var(--grey-400); }
} }
.none { &.disabled a.show:hover {
font-style: italic; background: transparent;
} }
}
// Break after the 2nd element .details {
.column:nth-child(2n) { padding-bottom: 0.75rem;
page-break-after: always; // CSS 2.1 syntax flex-flow: row wrap;
break-after: always; // New syntax margin-bottom: 0;
}
&.labels-list, .detail-title {
.assignees { display: block;
:deep(.multiselect) { color: var(--grey-400);
.input-wrapper { }
&:not(:focus-within):not(:hover) {
background: transparent !important;
border-color: transparent !important;
}
}
}
}
}
:deep(.details), .none {
:deep(.heading) { font-style: italic;
.input:not(.has-defaults), }
.textarea,
.select:not(.has-defaults) select {
border-color: transparent;
background: transparent;
cursor: pointer;
transition: all $transition-duration;
&::placeholder { // Break after the 2nd element
color: var(--text-light); .column:nth-child(2n) {
opacity: 1; page-break-after: always; // CSS 2.1 syntax
font-style: italic; break-after: always; // New syntax
} }
&:not(:disabled) { &.labels-list,
&:hover, .assignees {
&:active, :deep(.multiselect) {
&:focus { .input-wrapper {
background: var(--scheme-main); &:not(:focus-within):not(:hover) {
border-color: var(--border); background: transparent !important;
cursor: text; border-color: transparent !important;
} }
}
}
}
}
&:hover, :deep(.details),
&:active { :deep(.heading) {
cursor: text; .input:not(.has-defaults),
border-color: var(--link) .textarea,
} .select:not(.has-defaults) select {
} border-color: transparent;
} background: transparent;
cursor: pointer;
transition: all $transition-duration;
.select:not(.has-defaults):after { &::placeholder {
opacity: 0; color: var(--text-light);
} opacity: 1;
font-style: italic;
}
.select:not(.has-defaults):hover:after { &:not(:disabled) {
opacity: 1; &:hover,
} &:active,
} &:focus {
background: var(--scheme-main);
border-color: var(--border);
cursor: text;
}
.attachments { &:hover,
margin-bottom: 0; &:active {
cursor: text;
border-color: var(--link)
}
}
}
table tr:last-child td { .select:not(.has-defaults):after {
border-bottom: none; opacity: 0;
} }
}
.action-buttons { .select:not(.has-defaults):hover:after {
a.button { opacity: 1;
width: 100%; }
margin-bottom: .5rem; }
justify-content: left;
}
}
.created { .attachments {
font-size: .75rem; margin-bottom: 0;
color: var(--grey-500);
text-align: right;
}
.checklist-summary { table tr:last-child td {
margin-left: .25rem; border-bottom: none;
} }
}
.action-buttons {
a.button {
width: 100%;
margin-bottom: .5rem;
justify-content: left;
}
}
.created {
font-size: .75rem;
color: var(--grey-500);
text-align: right;
}
.checklist-summary {
margin-left: .25rem;
}
} }
.task-view-container { .task-view-container {
padding-bottom: 1rem; padding-bottom: 1rem;
@media screen and (max-width: $desktop) { @media screen and (max-width: $desktop) {
padding-bottom: 0; padding-bottom: 0;
} }
.task-view * { .task-view * {
opacity: 0; opacity: 0;
transition: opacity 50ms ease; transition: opacity 50ms ease;
} }
&.is-loading { &.is-loading {
opacity: 1; opacity: 1;
.task-view * { .task-view * {
opacity: 0; opacity: 0;
} }
} }
&.visible:not(.is-loading) .task-view * { &.visible:not(.is-loading) .task-view * {
opacity: 1; opacity: 1;
} }
} }
.task-view-container { .task-view-container {
// simulate sass lighten($primary, 30) by increasing lightness 30% to 73% // simulate sass lighten($primary, 30) by increasing lightness 30% to 73%
--primary-light: hsla(var(--primary-h), var(--primary-s), 73%, var(--primary-a)); --primary-light: hsla(var(--primary-h), var(--primary-s), 73%, var(--primary-a));
} }
.flash-background-enter-from, .flash-background-enter-from,
.flash-background-enter-active { .flash-background-enter-active {
animation: flash-background $flash-background-duration ease 1; animation: flash-background $flash-background-duration ease 1;
} }
@keyframes flash-background { @keyframes flash-background {
0% { 0% {
background: var(--primary-light); background: var(--primary-light);
} }
100% { 100% {
background: transparent; background: transparent;
} }
} }
</style> </style>