dd413518f5
* dailies monthly weeks of month summary update monthly day-of-week scheduling summary, add scheduling summary to task form, and add 5th week warning * formatting * scheduling warning and summary UI updates * fix(svg): use extant icon --------- Co-authored-by: Hafiz <hafizbhamidi@gmail.com>
355 lines
11 KiB
Vue
355 lines
11 KiB
Vue
<template>
|
|
<b-modal
|
|
id="task-summary"
|
|
:hide-footer="true"
|
|
@hidden="$emit('cancel')"
|
|
>
|
|
<div
|
|
v-if="task"
|
|
slot="modal-header"
|
|
class="task-modal-header px-4 d-flex align-items-center"
|
|
:class="cssClass('bg')"
|
|
>
|
|
<h2
|
|
class="my-auto"
|
|
:class="cssClass('headings')"
|
|
>
|
|
{{ title }}
|
|
</h2>
|
|
<div
|
|
class="svg-icon color close-x ml-auto my-auto"
|
|
:class="cssClass('headings')"
|
|
aria-hidden="true"
|
|
tabindex="0"
|
|
@click="cancel()"
|
|
@keypress.enter="cancel()"
|
|
v-html="icons.close"
|
|
></div>
|
|
</div>
|
|
<div
|
|
v-if="task"
|
|
class="task-modal-content pt-3 px-4 pb-4"
|
|
>
|
|
<div class="summary-block">
|
|
<h3> {{ $t('title') }} </h3>
|
|
<p> {{ task.text }} </p>
|
|
</div>
|
|
<div
|
|
v-if="task.notes"
|
|
class="summary-block"
|
|
>
|
|
<h3> {{ $t('notes') }} </h3>
|
|
<p> {{ task.notes }} </p>
|
|
</div>
|
|
<div class="summary-block">
|
|
<h3> {{ $t('description') }} </h3>
|
|
<p v-html="summarySentence"></p>
|
|
</div>
|
|
<div
|
|
v-if="task.checklist && task.checklist.length > 0"
|
|
class="summary-block"
|
|
>
|
|
<checklist
|
|
:items.sync="task.checklist"
|
|
:disable-drag="true"
|
|
:disable-edit="true"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-if="assignedUsernames.length > 0"
|
|
class="summary-block"
|
|
>
|
|
<h3> {{ $t('assignedTo') }} </h3>
|
|
<div
|
|
class="d-flex flex-wrap"
|
|
>
|
|
<span
|
|
v-for="member in assignedUsernames"
|
|
:key="member"
|
|
class="assigned-member py-1 px-75 mb-1 mr-1"
|
|
>
|
|
@{{ member }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="task && task.group && task.group.assignedUsersDetail
|
|
&& task.group.assignedUsersDetail[user._id]"
|
|
class="assignment-footer text-center py-2"
|
|
v-html="$t('assignedDateAndUser', {
|
|
username: task.group.assignedUsersDetail[user._id].assigningUsername,
|
|
date: formattedDate(task.group.assignedUsersDetail[user._id].assignedDate),
|
|
})"
|
|
>
|
|
</div>
|
|
</b-modal>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
@import '@/assets/scss/colors.scss';
|
|
#task-summary {
|
|
overflow-y: hidden;
|
|
|
|
.modal-content {
|
|
border-top-left-radius: 10px;
|
|
border-top-right-radius: 10px;
|
|
border: none;
|
|
box-shadow: 0 14px 28px 0 rgba($black, 0.24), 0 10px 10px 0 rgba($black, 0.28);
|
|
}
|
|
|
|
.modal-header, .modal-body, .modal-footer {
|
|
padding: 0px;
|
|
border: none;
|
|
}
|
|
|
|
.modal-dialog {
|
|
width: 448px;
|
|
margin-top: 50vh;
|
|
transform: translateY(-50%);
|
|
}
|
|
|
|
.modal-header {
|
|
padding: 0px;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '@/assets/scss/colors.scss';
|
|
|
|
.assigned-member {
|
|
border: 1px solid $gray-400;
|
|
border-radius: 100px;
|
|
color: $gray-100;
|
|
font-size: 12px;
|
|
line-height: 1.33;
|
|
}
|
|
|
|
.assignment-footer {
|
|
color: $gray-100;
|
|
background-color: $gray-700;
|
|
border-bottom-left-radius: 8px;
|
|
border-bottom-right-radius: 8px;
|
|
}
|
|
|
|
.close-x {
|
|
height: 16px;
|
|
width: 16px;
|
|
position: relative;
|
|
opacity: 0.75;
|
|
cursor: pointer;
|
|
|
|
&:hover, &:focus {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.summary-block:not(:last-of-type) {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.task-modal-content {
|
|
h3 {
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
p {
|
|
margin-bottom: 0px;
|
|
}
|
|
h3, p {
|
|
color: $gray-50;
|
|
}
|
|
}
|
|
|
|
.task-modal-header {
|
|
color: $white;
|
|
height: 60px;
|
|
width: 100%;
|
|
border-top-left-radius: 8px;
|
|
border-top-right-radius: 8px;
|
|
|
|
h2 {
|
|
color: $white;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
import keys from 'lodash/keys';
|
|
import moment from 'moment';
|
|
import pickBy from 'lodash/pickBy';
|
|
|
|
import checklist from './modal-controls/checklist';
|
|
import { mapGetters, mapState } from '@/libs/store';
|
|
|
|
import closeIcon from '@/assets/svg/close.svg?raw';
|
|
|
|
export default {
|
|
components: {
|
|
checklist,
|
|
},
|
|
props: ['task'],
|
|
data () {
|
|
return {
|
|
expandDayString: {
|
|
su: 'Sunday',
|
|
m: 'Monday',
|
|
t: 'Tuesday',
|
|
w: 'Wednesday',
|
|
th: 'Thursday',
|
|
f: 'Friday',
|
|
s: 'Saturday',
|
|
},
|
|
icons: Object.freeze({
|
|
close: closeIcon,
|
|
}),
|
|
};
|
|
},
|
|
computed: {
|
|
...mapGetters({
|
|
getTaskClasses: 'tasks:getTaskClasses',
|
|
}),
|
|
...mapState({
|
|
user: 'user.data',
|
|
}),
|
|
assignedUsernames () {
|
|
if (!this.task.group || !this.task.group.assignedUsers
|
|
|| !this.task.group.assignedUsersDetail) return [];
|
|
const usernames = [];
|
|
for (const user of this.task.group.assignedUsers) {
|
|
usernames.push(this.task.group.assignedUsersDetail[user].assignedUsername);
|
|
}
|
|
return usernames;
|
|
},
|
|
summarySentence () {
|
|
let fifthWeekWarning = '';
|
|
if (this.task.type === 'daily' && this.task.frequency === 'monthly'
|
|
&& this.task.weeksOfMonth && this.task.weeksOfMonth.length > 0
|
|
&& this.task.weeksOfMonth[0] === 4) {
|
|
const activeDays = keys(pickBy(this.task.repeat, value => value === true));
|
|
const dayName = this.expandDayString[activeDays[0]];
|
|
fifthWeekWarning = ` ${this.$t('fifthWeekWarning', { day: dayName })}`;
|
|
}
|
|
if (this.task.type === 'daily' && moment().isBefore(this.task.startDate)) {
|
|
return `This is ${this.formattedDifficulty(this.task.priority)} task that will repeat
|
|
${this.formattedRepeatInterval(this.task.frequency, this.task.everyX)}${this.formattedDays(this.task.frequency, this.task.repeat, this.task.daysOfMonth, this.task.weeksOfMonth, this.task.startDate)}
|
|
starting on <strong>${moment(this.task.startDate).format('MM/DD/YYYY')}</strong>.${fifthWeekWarning}`;
|
|
}
|
|
if (this.task.type === 'daily') {
|
|
return `This is ${this.formattedDifficulty(this.task.priority)} task that repeats
|
|
${this.formattedRepeatInterval(this.task.frequency, this.task.everyX)}${this.formattedDays(this.task.frequency, this.task.repeat, this.task.daysOfMonth, this.task.weeksOfMonth, this.task.startDate)}.${fifthWeekWarning}`;
|
|
}
|
|
if (this.task.date) {
|
|
return `This is ${this.formattedDifficulty(this.task.priority)} task that is due <strong>${moment(this.task.date).format('MM/DD/YYYY')}.`;
|
|
}
|
|
return `This is ${this.formattedDifficulty(this.task.priority)} task.`;
|
|
},
|
|
title () {
|
|
const type = this.$t(this.task.type);
|
|
return this.$t('taskSummary', { type });
|
|
},
|
|
},
|
|
methods: {
|
|
cancel () {
|
|
this.$root.$emit('bv::hide::modal', 'task-summary');
|
|
},
|
|
cssClass (suffix) {
|
|
if (!this.task) {
|
|
return '';
|
|
}
|
|
|
|
return this.getTaskClasses(this.task, `edit-modal-${suffix}`);
|
|
},
|
|
formattedDate (date) {
|
|
return moment(date).format('MM/DD/YYYY');
|
|
},
|
|
formattedDays (frequency, repeat, daysOfMonth, weeksOfMonth, startDate) {
|
|
let activeDays;
|
|
const dayStringArray = [];
|
|
switch (frequency) {
|
|
case 'weekly':
|
|
activeDays = keys(pickBy(repeat, value => value === true));
|
|
if (activeDays.length === 0) return ' on <strong>no days</strong>';
|
|
if (activeDays.length === 7) return ' on <strong>every day of the week</strong>';
|
|
dayStringArray.push(' on <strong>');
|
|
activeDays.forEach((value, index) => {
|
|
if (activeDays.length > 1 && index === activeDays.length - 1) dayStringArray.push(' and');
|
|
dayStringArray.push(` ${this.expandDayString[value]}`);
|
|
if (activeDays.length > 2 && index !== activeDays.length - 1) dayStringArray.push(',');
|
|
});
|
|
dayStringArray.push('</strong>');
|
|
break;
|
|
case 'monthly':
|
|
dayStringArray.push(' on <strong>the ');
|
|
if (daysOfMonth.length > 0) {
|
|
daysOfMonth.forEach((value, index) => {
|
|
const stringDay = String(value);
|
|
const stringFinalDigit = stringDay.slice(-1);
|
|
let ordinalSuffix = 'th';
|
|
if (stringFinalDigit === '1' && stringDay !== '11') ordinalSuffix = 'st';
|
|
if (stringFinalDigit === '2' && stringDay !== '12') ordinalSuffix = 'nd';
|
|
if (stringFinalDigit === '3' && stringDay !== '13') ordinalSuffix = 'rd';
|
|
if (daysOfMonth.length > 1 && index === daysOfMonth.length - 1) dayStringArray.push(' and');
|
|
dayStringArray.push(`${stringDay}${ordinalSuffix}`);
|
|
if (daysOfMonth.length > 2 && index !== daysOfMonth.length - 1) dayStringArray.push(',');
|
|
});
|
|
dayStringArray.push('</strong>');
|
|
} else if (weeksOfMonth.length > 0) {
|
|
const weekNum = weeksOfMonth[0] + 1;
|
|
const weekNumStr = String(weekNum);
|
|
const lastDigit = weekNumStr.slice(-1);
|
|
let ordinalSuffix = 'th';
|
|
if (lastDigit === '1' && weekNumStr !== '11') ordinalSuffix = 'st';
|
|
if (lastDigit === '2' && weekNumStr !== '12') ordinalSuffix = 'nd';
|
|
if (lastDigit === '3' && weekNumStr !== '13') ordinalSuffix = 'rd';
|
|
dayStringArray.push(`${weekNum}${ordinalSuffix}`);
|
|
activeDays = keys(pickBy(repeat, value => value === true));
|
|
dayStringArray.push(` ${this.expandDayString[activeDays[0]]} of the month</strong>`);
|
|
}
|
|
break;
|
|
case 'yearly':
|
|
return ` on <strong>${moment(startDate).format('MMMM Do')}</strong>`;
|
|
default:
|
|
return '';
|
|
}
|
|
return dayStringArray.join('');
|
|
},
|
|
formattedDifficulty (priority) {
|
|
switch (priority) {
|
|
case 0.1:
|
|
return 'a <strong>trivial</strong>';
|
|
case 1:
|
|
return 'an <strong>easy</strong>';
|
|
case 1.5:
|
|
return 'a <strong>medium</strong>';
|
|
case 2:
|
|
return 'a <strong>hard</strong>';
|
|
default:
|
|
return null;
|
|
}
|
|
},
|
|
formattedRepeatInterval (frequency, everyX) {
|
|
const numericX = Number(everyX);
|
|
switch (frequency) {
|
|
case 'daily':
|
|
if (numericX === 1) return '<strong>every day</strong>';
|
|
if (numericX === 2) return '<strong>every other day</strong>';
|
|
return `<strong>every ${numericX} days</strong>`;
|
|
case 'weekly':
|
|
if (numericX === 1) return '<strong>every week</strong>';
|
|
if (numericX === 2) return '<strong>every other week</strong>';
|
|
return `<strong>every ${numericX} weeks</strong>`;
|
|
case 'monthly':
|
|
if (numericX === 1) return `<strong>${this.$t('everyMonth')}</strong>`;
|
|
return `<strong>${this.$t('everyXMonths', { count: numericX })}</strong>`;
|
|
case 'yearly':
|
|
if (numericX === 1) return '<strong>every year</strong>';
|
|
return `<strong>every ${everyX} years</strong>`;
|
|
default:
|
|
return null;
|
|
}
|
|
},
|
|
},
|
|
};
|
|
</script>
|