mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-13 20:57:24 +01:00
* Client: fix Apidoc and move email files (#9139) * fix apidoc * move emails files * quest leader can start/end quest; admins can edit challenges/guilds; reverse chat works; remove static/videos link; etc (#9140) * enable link to markdown info on group and challenge edit screen * allow admin (moderators and staff) to edit challenges * allow admin (moderators and staff) to edit guilds Also add some unrelated TODO comments. * allow any party member (not just leader) to start quest from party page * allow quest owner to cancel, begin, abort quest Previously only the party leader could see those buttons. The leader still can. This also hides those buttons from all other party members. * enable reverse chat in guilds and party * remove outdated videos from press kit * adjust various wordings * Be consistent with capitalization of Check-In. (#9118) * limit for inlined svg images and make home leaner by not bundling it with the rest of static pages * sep 27 fixes (#9088) * fix item paddings / drawer width * expand the width of item-rows by the margin of an item * fix hatchedPet-dialog * fix hatching-modal * remove min-height * Oct 3 fixes (#9148) * Only show level after yesterdailies modal * Fixed zindex * Added spcial spells to rewards column * Added single click buy for health and armoire * Prevented task scoring when casting a spell * Renamed generic purchase method * Updated nav for small screen * Hide checklist while casting * fix some text describing menu items (#9145) * 4.1.3 * use `selectGearToPin` instead of updateStore in migration-script (#9102) * Tags redesign in edit-task modal (#9122) * Truncate tags list to maximum number of tasks This commit truncates the list of tags in the edit task modal and displays the remaining selected tasks as a number. * Align tags-select dropdown with "Tags" label * Add tags popup component * Use solid purple for tags-select dropdown * Remove shadow when tags-select is active * Add border-radius to tags-select * Re-add previously disabled transitions * Remove unused template element * Add Clear Tags button to footer of tags popup * Decrease column size for tags to better match design * Truncate tag name to avoid overflows * Add tag name as title to show full name on hover * Grow inline tags select from left to right * Style none button * Add spacing to streak reset button to line up with tags select * Add top offset to tags dropdown toggle to line up with label * Ability to collapse checklists (#9158) * ability to collapse checklists * typo * show warning to use the mobile apps (#9152) * Oct 4 fixes (#9159) * Added gem purchase note * Added notification count * Added party reload * Added description when user can no longer purchase gems this month * Prevent modal from showing when pruchasing recently purchased items * Added progress bar * Prevented non leader from loading approvals * Added group billing * Release fix (#9161) * Merge Develop onto Release (#9123) * Some random quick (#9111) * Switch group button directions * Allowed admins to export challenges * Added scoping to some stable styles * Fixed challenge cloning * Tasks tags (#9112) * Added auto apply and exit * Add challenge tag editing * Fixed lint * Skill fixes (#9113) * Added local storage setting for spell drawer * Added new spell styles * Fixed typo * Reset local creds if access is denied (#9114) * various fixes: group leader's name at top of edit drop-down; Members List; etc (#9117) * fix text describing location of subscription/gem gift box * disable Copy As To-Do in Tavern, guilds, party because it's not working * change members label on group pages to Member List * remove outdated info about seeing number of Gems available to buy * allow Danger Zone to be seen by players without local authentication Also add an hr because the Danger Zone heading was crammed up against the button above it. * put current group leader's name at top of Leader change drop-down * Client Fixes (#9120) * unduplicate logout code * re-enable debug menu * fix pets badge and equipping mounts * close gift modal after sending gems * armoire notifications * Oct 1 fixes (#9121) * Added default tags to task * Added seasonal gear check and show spooky * Disabled spooky sparkles * Fixed challenge remove tasks modal * Hid checklist * Added group gems modal * Purchase with amazon * Added check for user health * Added missing notification file * 4.1.1 * 4.1.2 * Merge develop into release (#9154) * Client: fix Apidoc and move email files (#9139) * fix apidoc * move emails files * quest leader can start/end quest; admins can edit challenges/guilds; reverse chat works; remove static/videos link; etc (#9140) * enable link to markdown info on group and challenge edit screen * allow admin (moderators and staff) to edit challenges * allow admin (moderators and staff) to edit guilds Also add some unrelated TODO comments. * allow any party member (not just leader) to start quest from party page * allow quest owner to cancel, begin, abort quest Previously only the party leader could see those buttons. The leader still can. This also hides those buttons from all other party members. * enable reverse chat in guilds and party * remove outdated videos from press kit * adjust various wordings * Be consistent with capitalization of Check-In. (#9118) * limit for inlined svg images and make home leaner by not bundling it with the rest of static pages * sep 27 fixes (#9088) * fix item paddings / drawer width * expand the width of item-rows by the margin of an item * fix hatchedPet-dialog * fix hatching-modal * remove min-height * Oct 3 fixes (#9148) * Only show level after yesterdailies modal * Fixed zindex * Added spcial spells to rewards column * Added single click buy for health and armoire * Prevented task scoring when casting a spell * Renamed generic purchase method * Updated nav for small screen * Hide checklist while casting * fix some text describing menu items (#9145)
787 lines
24 KiB
Vue
787 lines
24 KiB
Vue
<template lang="pug">
|
|
form(v-if="task", @submit.stop.prevent="submit()")
|
|
b-modal#task-modal(size="sm", @hidden="onClose()")
|
|
.task-modal-header(slot="modal-header", :class="[cssClass]")
|
|
.clearfix
|
|
h1.float-left {{ title }}
|
|
.float-right.d-flex.align-items-center
|
|
span.cancel-task-btn.mr-2(v-if="purpose !== 'create'", v-once, @click="cancel()") {{ $t('cancel') }}
|
|
button.btn.btn-secondary(type="submit", v-once) {{ $t('save') }}
|
|
.form-group
|
|
label(v-once) {{ `${$t('title')}*` }}
|
|
input.form-control.title-input(type='text', :class="[`${cssClass}-modal-input`]", required, v-model="task.text", autofocus)
|
|
.form-group
|
|
label(v-once) {{ $t('notes') }}
|
|
textarea.form-control(:class="[`${cssClass}-modal-input`]", v-model="task.notes", rows="3")
|
|
.task-modal-content
|
|
.option(v-if="task.type === 'reward'")
|
|
label(v-once) {{ $t('cost') }}
|
|
input(type="number", v-model="task.value", required, min="0")
|
|
.svg-icon.gold(v-html="icons.gold")
|
|
.option(v-if="checklistEnabled")
|
|
label(v-once) {{ $t('checklist') }}
|
|
br
|
|
div(v-sortable='', @onsort='sortedChecklist')
|
|
.inline-edit-input-group.checklist-group.input-group(v-for="(item, $index) in checklist")
|
|
span.grippy
|
|
input.inline-edit-input.checklist-item.form-control(type="text", v-model="item.text")
|
|
span.input-group-btn(@click="removeChecklistItem($index)")
|
|
.svg-icon.destroy-icon(v-html="icons.destroy")
|
|
input.inline-edit-input.checklist-item.form-control(type="text", :placeholder="$t('newChecklistItem')", @keydown.enter="addChecklistItem($event)", v-model="newChecklistItem")
|
|
.d-flex.justify-content-center(v-if="task.type === 'habit'")
|
|
.option-item(:class="optionClass(task.up === true)", @click="task.up = !task.up")
|
|
.option-item-box
|
|
.task-control.habit-control(:class="controlClass.up + '-control-habit'")
|
|
.svg-icon.positive(v-html="icons.positive")
|
|
.option-item-label(v-once) {{ $t('positive') }}
|
|
.option-item(:class="optionClass(task.down === true)", @click="task.down = !task.down")
|
|
.option-item-box
|
|
.task-control.habit-control(:class="controlClass.down + '-control-habit'")
|
|
.svg-icon.negative(v-html="icons.negative")
|
|
.option-item-label(v-once) {{ $t('negative') }}
|
|
template(v-if="task.type !== 'reward'")
|
|
label(v-once)
|
|
span.float-left {{ $t('difficulty') }}
|
|
// @TODO .svg-icon.info-icon(v-html="icons.information")
|
|
.d-flex.justify-content-center
|
|
.option-item(:class="optionClass(task.priority === 0.1)", @click="task.priority = 0.1")
|
|
.option-item-box
|
|
.svg-icon.difficulty-trivial-icon(v-html="icons.difficultyTrivial")
|
|
.option-item-label(v-once) {{ $t('trivial') }}
|
|
.option-item(:class="optionClass(task.priority === 1)", @click="task.priority = 1")
|
|
.option-item-box
|
|
.svg-icon.difficulty-normal-icon(v-html="icons.difficultyNormal")
|
|
.option-item-label(v-once) {{ $t('easy') }}
|
|
.option-item(:class="optionClass(task.priority === 1.5)", @click="task.priority = 1.5")
|
|
.option-item-box
|
|
.svg-icon.difficulty-medium-icon(v-html="icons.difficultyMedium")
|
|
.option-item-label(v-once) {{ $t('medium') }}
|
|
.option-item(:class="optionClass(task.priority === 2)", @click="task.priority = 2")
|
|
.option-item-box
|
|
.svg-icon.difficulty-hard-icon(v-html="icons.difficultyHard")
|
|
.option-item-label(v-once) {{ $t('hard') }}
|
|
.option(v-if="task.type === 'todo'")
|
|
label(v-once) {{ $t('dueDate') }}
|
|
datepicker(
|
|
v-model="task.date",
|
|
:clearButton='true',
|
|
clearButtonIcon='category-select',
|
|
:clearButtonText='$t("clear")',
|
|
:todayButton='true',
|
|
todayButtonIcon='category-select',
|
|
:todayButtonText='$t("today")',
|
|
)
|
|
.option(v-if="task.type === 'daily'")
|
|
label(v-once) {{ $t('startDate') }}
|
|
datepicker(
|
|
v-model="task.startDate",
|
|
:clearButton='false',
|
|
:todayButton='true',
|
|
todayButtonIcon='category-select',
|
|
:todayButtonText='$t("today")',
|
|
)
|
|
.option(v-if="task.type === 'daily'")
|
|
.form-group
|
|
label(v-once) {{ $t('repeats') }}
|
|
b-dropdown(:text="$t(task.frequency)")
|
|
b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly', 'yearly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}")
|
|
| {{ $t(frequency) }}
|
|
.form-group
|
|
label(v-once) {{ $t('repeatEvery') }}
|
|
input(type="number", v-model="task.everyX", min="0", required)
|
|
| {{ repeatSuffix }}
|
|
br
|
|
template(v-if="task.frequency === 'weekly'")
|
|
.form-check-inline.weekday-check(
|
|
v-for="(day, dayNumber) in ['su','m','t','w','th','f','s']",
|
|
:key="dayNumber",
|
|
)
|
|
label.custom-control.custom-checkbox
|
|
input.custom-control-input(type="checkbox", v-model="task.repeat[day]")
|
|
span.custom-control-indicator
|
|
span.custom-control-description(v-once) {{ weekdaysMin(dayNumber) }}
|
|
template(v-if="task.frequency === 'monthly'")
|
|
label.custom-control.custom-radio
|
|
input.custom-control-input(type='radio', v-model="repeatsOn", value="dayOfMonth")
|
|
span.custom-control-indicator
|
|
span.custom-control-description {{ $t('dayOfMonth') }}
|
|
label.custom-control.custom-radio
|
|
input.custom-control-input(type='radio', v-model="repeatsOn", value="dayOfWeek")
|
|
span.custom-control-indicator
|
|
span.custom-control-description {{ $t('dayOfWeek') }}
|
|
|
|
.tags-select.option(v-if="isUserTask")
|
|
.tags-inline
|
|
label(v-once) {{ $t('tags') }}
|
|
.category-wrap(@click="showTagsSelect = !showTagsSelect", v-bind:class="{ active: showTagsSelect }")
|
|
span.category-select(v-if='task.tags && task.tags.length === 0')
|
|
.tags-none {{$t('none')}}
|
|
.dropdown-toggle
|
|
span.category-select(v-else)
|
|
.category-label(v-for='tagName in truncatedSelectedTags', :title="tagName") {{ tagName }}
|
|
.tags-more(v-if='remainingSelectedTags.length > 0') +{{ $t('more', { count: remainingSelectedTags.length }) }}
|
|
.dropdown-toggle
|
|
tags-popup(v-if="showTagsSelect", :tags="user.tags", v-model="task.tags")
|
|
|
|
.option(v-if="task.type === 'habit'")
|
|
label(v-once) {{ $t('resetStreak') }}
|
|
b-dropdown.streak-dropdown(:text="$t(task.frequency)")
|
|
b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}")
|
|
| {{ $t(frequency) }}
|
|
|
|
.option(v-if="task.type === 'daily' && isUserTask && purpose === 'edit'")
|
|
.form-group
|
|
label(v-once) {{ $t('restoreStreak') }}
|
|
input(type="number", v-model="task.streak", min="0", required)
|
|
|
|
.option.group-options(v-if='groupId')
|
|
label(v-once) Assigned To
|
|
.category-wrap(@click="showAssignedSelect = !showAssignedSelect")
|
|
span.category-select(v-if='assignedMembers && assignedMembers.length === 0') {{$t('none')}}
|
|
span.category-select(v-else)
|
|
span(v-for='memberId in assignedMembers') {{memberNamesById[memberId]}}
|
|
.category-box(v-if="showAssignedSelect")
|
|
.container
|
|
.row
|
|
.form-check.col-6(
|
|
v-for="member in members",
|
|
:key="member._id",
|
|
)
|
|
label.custom-control.custom-checkbox
|
|
input.custom-control-input(type="checkbox", :value="member._id", v-model="assignedMembers", @change='toggleAssignment(member._id)')
|
|
span.custom-control-indicator
|
|
span.custom-control-description(v-once) {{ member.profile.name }}
|
|
|
|
.row
|
|
button.btn.btn-primary(@click="showAssignedSelect = !showAssignedSelect") {{$t('close')}}
|
|
|
|
.option.group-options(v-if='groupId')
|
|
label(v-once) Needs Approval
|
|
toggle-switch(:label='""',
|
|
:checked="requiresApproval",
|
|
@change="updateRequiresApproval")
|
|
|
|
.task-modal-footer(slot="modal-footer")
|
|
span.cancel-task-btn(v-once, v-if="purpose === 'create'", @click="cancel()") {{ $t('cancel') }}
|
|
span.delete-task-btn(v-once, v-if='canDelete', @click="destroy()") {{ $t('delete') }}
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
@import '~client/assets/scss/colors.scss';
|
|
|
|
#task-modal {
|
|
.modal-dialog.modal-sm {
|
|
max-width: 448px;
|
|
}
|
|
|
|
label {
|
|
font-weight: bold;
|
|
}
|
|
|
|
input, textarea {
|
|
border: none;
|
|
background-color: rgba(0, 0, 0, 0.16);
|
|
|
|
&:focus {
|
|
color: $white !important;
|
|
}
|
|
}
|
|
|
|
.modal-content {
|
|
border-radius: 8px;
|
|
border: none;
|
|
}
|
|
|
|
.modal-header, .modal-body, .modal-footer {
|
|
padding: 0px;
|
|
border: none;
|
|
}
|
|
|
|
.task-modal-content, .task-modal-header {
|
|
padding-left: 23px;
|
|
padding-right: 23px;
|
|
}
|
|
|
|
.task-modal-header {
|
|
color: $white;
|
|
width: 100%;
|
|
border-top-left-radius: 8px;
|
|
border-top-right-radius: 8px;
|
|
padding-top: 16px;
|
|
padding-bottom: 24px;
|
|
|
|
h1 {
|
|
color: $white;
|
|
}
|
|
}
|
|
|
|
.task-modal-content {
|
|
padding-top: 24px;
|
|
|
|
input {
|
|
background: $white;
|
|
border: 1px solid $gray-500;
|
|
color: $gray-200;
|
|
|
|
&:focus {
|
|
color: $gray-50 !important;
|
|
}
|
|
}
|
|
}
|
|
|
|
.info-icon {
|
|
float: left;
|
|
height: 16px;
|
|
width: 16px;
|
|
margin-left: 8px;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.difficulty-trivial-icon {
|
|
width: 16px;
|
|
height: 16px;
|
|
color: #A5A1AC;
|
|
}
|
|
|
|
.difficulty-normal-icon {
|
|
width: 36px;
|
|
height: 16px;
|
|
color: #A5A1AC;
|
|
}
|
|
|
|
.difficulty-medium-icon {
|
|
width: 36px;
|
|
height: 32px;
|
|
color: #A5A1AC;
|
|
}
|
|
|
|
.difficulty-hard-icon {
|
|
width: 36px;
|
|
height: 36px;
|
|
color: #A5A1AC;
|
|
}
|
|
|
|
.option {
|
|
margin-bottom: 12px;
|
|
margin-top: 12px;
|
|
position: relative;
|
|
}
|
|
|
|
.option-item {
|
|
margin-right: 48px;
|
|
cursor: pointer;
|
|
|
|
&:last-child {
|
|
margin-right: 0px;
|
|
}
|
|
|
|
&-selected {
|
|
.svg-icon, .option-item-label {
|
|
color: inherit !important;
|
|
}
|
|
}
|
|
|
|
&-box {
|
|
width: 64px;
|
|
height: 64px;
|
|
border-radius: 2px;
|
|
background: $gray-600;
|
|
margin-bottom: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
.habit-control.task-habit-disabled-control-habit {
|
|
color: $white !important;
|
|
border: none;
|
|
background: $gray-300;
|
|
}
|
|
}
|
|
|
|
&-label {
|
|
color: $gray-50;
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
.category-wrap {
|
|
cursor: pointer;
|
|
margin-top: 0px;
|
|
}
|
|
|
|
.category-box {
|
|
bottom: 0px;
|
|
left: 40px;
|
|
top: auto;
|
|
z-index: 11;
|
|
|
|
.container {
|
|
max-height: 90vh;
|
|
overflow: auto;
|
|
}
|
|
}
|
|
|
|
.tags-select {
|
|
position: relative;
|
|
|
|
.tags-inline {
|
|
align-items: center;
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
|
|
label {
|
|
margin: 0;
|
|
}
|
|
|
|
.category-wrap {
|
|
cursor: inherit;
|
|
position: relative;
|
|
border: 1px solid transparent;
|
|
border-radius: 2px;
|
|
margin-left: 4em;
|
|
|
|
&.active {
|
|
border-color: $purple-500;
|
|
|
|
.category-select {
|
|
box-shadow: none;
|
|
}
|
|
}
|
|
|
|
.category-select {
|
|
align-items: center;
|
|
display: flex;
|
|
padding: .6em;
|
|
padding-right: 2.8em;
|
|
width: 100%;
|
|
|
|
.tags-none {
|
|
margin: .26em 0 .26em .6em;
|
|
|
|
& + .dropdown-toggle {
|
|
right: 1.3em;
|
|
}
|
|
}
|
|
|
|
.tags-more {
|
|
color: #a5a1ac;
|
|
flex: 0 1 auto;
|
|
font-size: 12px;
|
|
text-align: left;
|
|
position: relative;
|
|
left: .5em;
|
|
width: 100%;
|
|
}
|
|
|
|
.dropdown-toggle {
|
|
position: absolute;
|
|
right: 1em;
|
|
top: .8em;
|
|
}
|
|
|
|
.category-label {
|
|
min-width: 68px;
|
|
overflow: hidden;
|
|
padding: .5em 1em;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
width: 68px;
|
|
word-wrap: break-word;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.tags-popup {
|
|
position: absolute;
|
|
top: 3.5em;
|
|
left: 6.2em;
|
|
}
|
|
}
|
|
|
|
.streak-dropdown {
|
|
margin-left: .5em;
|
|
}
|
|
|
|
.checklist-group {
|
|
border-top: 1px solid $gray-500;
|
|
}
|
|
|
|
// From: https://codepen.io/zachariab/pen/wkrbc
|
|
span.grippy {
|
|
content: '....';
|
|
width: 20px;
|
|
height: 20px;
|
|
display: inline-block;
|
|
overflow: hidden;
|
|
line-height: 5px;
|
|
padding: 3px 4px;
|
|
cursor: move;
|
|
vertical-align: middle;
|
|
margin-top: .5em;
|
|
margin-right: .3em;
|
|
font-size: 12px;
|
|
font-family: sans-serif;
|
|
letter-spacing: 2px;
|
|
color: #cccccc;
|
|
text-shadow: 1px 0 1px black;
|
|
}
|
|
|
|
span.grippy::after {
|
|
content: '.. .. .. ..';
|
|
}
|
|
|
|
.checklist-item {
|
|
margin-bottom: 0px;
|
|
border-radius: 0px;
|
|
border: none !important;
|
|
padding-left: 36px;
|
|
|
|
&:last-child {
|
|
background-repeat: no-repeat;
|
|
background-position: center left 10px;
|
|
border-top: 1px solid $gray-500 !important;
|
|
border-bottom: 1px solid $gray-500 !important;
|
|
background-size: 10px 10px;
|
|
background-image: url(~client/assets/svg/for-css/positive.svg);
|
|
}
|
|
|
|
&:hover {
|
|
cursor: move;
|
|
}
|
|
}
|
|
|
|
.delete-task-btn, .cancel-task-btn {
|
|
cursor: pointer;
|
|
|
|
&:hover, &:focus, &:active {
|
|
text-decoration: underline;
|
|
}
|
|
}
|
|
|
|
.task-modal-footer {
|
|
margin: 0 auto;
|
|
padding-bottom: 24px;
|
|
border-top-left-radius: 8px;
|
|
border-top-right-radius: 8px;
|
|
margin-top: 50px;
|
|
|
|
|
|
|
|
.delete-task-btn {
|
|
color: $red-50;
|
|
}
|
|
|
|
.cancel-task-btn {
|
|
color: $blue-10;
|
|
}
|
|
}
|
|
|
|
.weekday-check {
|
|
margin-left: 0px;
|
|
width: 57px;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style lang="scss" scoped>
|
|
.gold {
|
|
width: 24px;
|
|
margin-left: 5em;
|
|
margin-top: -2.4em;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
import TagsPopup from './tagsPopup';
|
|
import bModal from 'bootstrap-vue/lib/components/modal';
|
|
import { mapGetters, mapActions, mapState } from 'client/libs/store';
|
|
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
|
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
|
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
|
import sortable from 'client/directives/sortable.directive';
|
|
import clone from 'lodash/clone';
|
|
import Datepicker from 'vuejs-datepicker';
|
|
import moment from 'moment';
|
|
import uuid from 'uuid';
|
|
|
|
import informationIcon from 'assets/svg/information.svg';
|
|
import difficultyTrivialIcon from 'assets/svg/difficulty-trivial.svg';
|
|
import difficultyMediumIcon from 'assets/svg/difficulty-medium.svg';
|
|
import difficultyHardIcon from 'assets/svg/difficulty-hard.svg';
|
|
import difficultyNormalIcon from 'assets/svg/difficulty-normal.svg';
|
|
import positiveIcon from 'assets/svg/positive.svg';
|
|
import negativeIcon from 'assets/svg/negative.svg';
|
|
import deleteIcon from 'assets/svg/delete.svg';
|
|
import goldIcon from 'assets/svg/gold.svg';
|
|
|
|
export default {
|
|
components: {
|
|
TagsPopup,
|
|
bModal,
|
|
bDropdown,
|
|
bDropdownItem,
|
|
Datepicker,
|
|
toggleSwitch,
|
|
},
|
|
directives: {
|
|
sortable,
|
|
},
|
|
props: ['task', 'purpose', 'challengeId', 'groupId'], // purpose is either create or edit, task is the task created or edited
|
|
data () {
|
|
return {
|
|
maxTags: 3,
|
|
showTagsSelect: false,
|
|
showAssignedSelect: false,
|
|
newChecklistItem: null,
|
|
icons: Object.freeze({
|
|
information: informationIcon,
|
|
difficultyNormal: difficultyNormalIcon,
|
|
difficultyTrivial: difficultyTrivialIcon,
|
|
difficultyMedium: difficultyMediumIcon,
|
|
difficultyHard: difficultyHardIcon,
|
|
negative: negativeIcon,
|
|
positive: positiveIcon,
|
|
destroy: deleteIcon,
|
|
gold: goldIcon,
|
|
}),
|
|
requiresApproval: false, // We can't set task.group fields so we use this field to toggle
|
|
members: [],
|
|
memberNamesById: {},
|
|
assignedMembers: [],
|
|
checklist: [],
|
|
};
|
|
},
|
|
watch: {
|
|
async task () {
|
|
if (this.groupId && this.task.group && this.task.group.approval && this.task.group.approval.required) {
|
|
this.requiresApproval = true;
|
|
}
|
|
|
|
if (this.groupId) {
|
|
let members = await this.$store.dispatch('members:getGroupMembers', {
|
|
groupId: this.groupId,
|
|
includeAllPublicFields: true,
|
|
});
|
|
this.members = members;
|
|
this.members.forEach(member => {
|
|
this.memberNamesById[member._id] = member.profile.name;
|
|
});
|
|
this.assignedMembers = [];
|
|
if (this.task.group && this.task.group.assignedUsers) this.assignedMembers = this.task.group.assignedUsers;
|
|
}
|
|
|
|
// @TODO: This whole component is mutating a prop and that causes issues. We need to not copy the prop similar to group modals
|
|
if (this.task) this.checklist = clone(this.task.checklist);
|
|
},
|
|
},
|
|
computed: {
|
|
...mapGetters({
|
|
getTaskClasses: 'tasks:getTaskClasses',
|
|
getTagsFor: 'tasks:getTagsFor',
|
|
}),
|
|
...mapState({
|
|
user: 'user.data',
|
|
dayMapping: 'constants.DAY_MAPPING',
|
|
}),
|
|
checklistEnabled () {
|
|
return ['daily', 'todo'].indexOf(this.task.type) > -1 && !this.isOriginalChallengeTask;
|
|
},
|
|
isOriginalChallengeTask () {
|
|
let isUserChallenge = Boolean(this.task.userId);
|
|
return !isUserChallenge && (this.challengeId || this.task.challenge && this.task.challenge.id);
|
|
},
|
|
canDelete () {
|
|
let isUserChallenge = Boolean(this.task.userId);
|
|
let activeChallenge = isUserChallenge && this.task.challenge && this.task.challenge.id && !this.task.challenge.broken;
|
|
return this.purpose !== 'create' && !activeChallenge;
|
|
},
|
|
title () {
|
|
const type = this.$t(this.task.type);
|
|
return this.$t(this.purpose === 'edit' ? 'editATask' : 'createTask', {type});
|
|
},
|
|
cssClass () {
|
|
return this.getTaskClasses(this.task, this.purpose === 'edit' ? 'editModal' : 'createModal');
|
|
},
|
|
controlClass () {
|
|
return this.getTaskClasses(this.task, this.purpose === 'edit' ? 'control' : 'controlCreate');
|
|
},
|
|
isUserTask () {
|
|
return !this.challengeId && !this.groupId;
|
|
},
|
|
repeatSuffix () {
|
|
const task = this.task;
|
|
|
|
if (task.frequency === 'daily') {
|
|
return task.everyX === 1 ? this.$t('day') : this.$t('days');
|
|
} else if (task.frequency === 'weekly') {
|
|
return task.everyX === 1 ? this.$t('week') : this.$t('weeks');
|
|
} else if (task.frequency === 'monthly') {
|
|
return task.everyX === 1 ? this.$t('month') : this.$t('months');
|
|
} else if (task.frequency === 'yearly') {
|
|
return task.everyX === 1 ? this.$t('year') : this.$t('years');
|
|
}
|
|
},
|
|
repeatsOn: {
|
|
get () {
|
|
let repeatsOn = 'dayOfMonth';
|
|
|
|
if (this.task.type === 'daily' && this.task.weeksOfMonth && this.task.weeksOfMonth.length > 0) {
|
|
repeatsOn = 'dayOfWeek';
|
|
}
|
|
|
|
return repeatsOn;
|
|
},
|
|
set (newValue) {
|
|
const task = this.task;
|
|
|
|
if (task.frequency === 'monthly' && newValue === 'dayOfMonth') {
|
|
const date = moment(task.startDate).date();
|
|
task.weeksOfMonth = [];
|
|
task.daysOfMonth = [date];
|
|
} else if (task.frequency === 'monthly' && newValue === 'dayOfWeek') {
|
|
const week = Math.ceil(moment(task.startDate).date() / 7) - 1;
|
|
const dayOfWeek = moment(task.startDate).day();
|
|
const shortDay = this.dayMapping[dayOfWeek];
|
|
task.daysOfMonth = [];
|
|
task.weeksOfMonth = [week];
|
|
for (let key in task.repeat) {
|
|
task.repeat[key] = false;
|
|
}
|
|
task.repeat[shortDay] = true;
|
|
}
|
|
},
|
|
},
|
|
selectedTags () {
|
|
return this.getTagsFor(this.task);
|
|
},
|
|
truncatedSelectedTags () {
|
|
return this.selectedTags.slice(0, this.maxTags);
|
|
},
|
|
remainingSelectedTags () {
|
|
return this.selectedTags.slice(this.maxTags);
|
|
},
|
|
},
|
|
methods: {
|
|
...mapActions({saveTask: 'tasks:save', destroyTask: 'tasks:destroy', createTask: 'tasks:create'}),
|
|
sortedChecklist (data) {
|
|
let sorting = clone(this.task.checklist);
|
|
let movingItem = sorting[data.oldIndex];
|
|
sorting.splice(data.oldIndex, 1);
|
|
sorting.splice(data.newIndex, 0, movingItem);
|
|
this.task.checklist = sorting;
|
|
},
|
|
optionClass (activeCondition) {
|
|
if (activeCondition) {
|
|
return [`${this.cssClass}-color`, 'option-item-selected'];
|
|
} else {
|
|
return [];
|
|
}
|
|
},
|
|
addChecklistItem (e) {
|
|
let checkListItem = {
|
|
id: uuid.v4(),
|
|
text: this.newChecklistItem,
|
|
completed: false,
|
|
};
|
|
this.task.checklist.push(checkListItem);
|
|
// @TODO: managing checklist separately to help with sorting on the UI
|
|
this.checklist.push(checkListItem);
|
|
this.newChecklistItem = null;
|
|
if (e) e.preventDefault();
|
|
},
|
|
removeChecklistItem (i) {
|
|
this.task.checklist.splice(i, 1);
|
|
this.checklist = clone(this.task.checklist);
|
|
},
|
|
weekdaysMin (dayNumber) {
|
|
return moment.weekdaysMin(dayNumber);
|
|
},
|
|
async submit () {
|
|
if (this.newChecklistItem) this.addChecklistItem();
|
|
|
|
if (this.purpose === 'create') {
|
|
if (this.challengeId) {
|
|
this.$store.dispatch('tasks:createChallengeTasks', {
|
|
challengeId: this.challengeId,
|
|
tasks: [this.task],
|
|
});
|
|
this.$emit('taskCreated', this.task);
|
|
} else if (this.groupId) {
|
|
await this.$store.dispatch('tasks:createGroupTasks', {
|
|
groupId: this.groupId,
|
|
tasks: [this.task],
|
|
});
|
|
|
|
let promises = this.assignedMembers.map(memberId => {
|
|
return this.$store.dispatch('tasks:assignTask', {
|
|
taskId: this.task._id,
|
|
userId: memberId,
|
|
});
|
|
});
|
|
Promise.all(promises);
|
|
|
|
this.task.group.assignedUsers = this.assignedMembers;
|
|
|
|
this.$emit('taskCreated', this.task);
|
|
} else {
|
|
this.createTask(this.task);
|
|
}
|
|
} else {
|
|
if (this.groupId) {
|
|
this.task.group.assignedUsers = this.assignedMembers;
|
|
this.task.requiresApproval = this.requiresApproval;
|
|
}
|
|
|
|
this.saveTask(this.task);
|
|
this.$emit('taskEdited', this.task);
|
|
}
|
|
this.$root.$emit('hide::modal', 'task-modal');
|
|
},
|
|
destroy () {
|
|
if (!confirm('Are you sure you want to delete this task?')) return;
|
|
this.destroyTask(this.task);
|
|
this.$emit('taskDestroyed', this.task);
|
|
this.$root.$emit('hide::modal', 'task-modal');
|
|
},
|
|
cancel () {
|
|
this.$root.$emit('hide::modal', 'task-modal');
|
|
},
|
|
onClose () {
|
|
this.showTagsSelect = false;
|
|
this.newChecklistItem = '';
|
|
this.$emit('cancel');
|
|
},
|
|
updateRequiresApproval (newValue) {
|
|
let truthy = true;
|
|
if (!newValue) truthy = false; // This return undefined instad of false
|
|
this.requiresApproval = truthy;
|
|
},
|
|
async toggleAssignment (memberId) {
|
|
let assignedIndex = this.assignedMembers.indexOf(memberId);
|
|
|
|
if (assignedIndex === -1) {
|
|
if (this.purpose === 'create') {
|
|
return;
|
|
}
|
|
|
|
await this.$store.dispatch('tasks:unassignTask', {
|
|
taskId: this.task._id,
|
|
userId: this.user._id,
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (this.purpose === 'create') {
|
|
return;
|
|
}
|
|
|
|
await this.$store.dispatch('tasks:assignTask', {
|
|
taskId: this.task._id,
|
|
userId: this.user._id,
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|