diff --git a/website/client/assets/scss/index.scss b/website/client/assets/scss/index.scss index 0c5f59f097..1a7e8cd777 100644 --- a/website/client/assets/scss/index.scss +++ b/website/client/assets/scss/index.scss @@ -13,6 +13,7 @@ @import './loading-screen'; // Global styles +@import './misc'; @import './typography'; @import './markdown'; @import './form'; diff --git a/website/client/assets/scss/misc.scss b/website/client/assets/scss/misc.scss new file mode 100644 index 0000000000..164a05f0e7 --- /dev/null +++ b/website/client/assets/scss/misc.scss @@ -0,0 +1,17 @@ +.expand-toggle:after { + display: inline-block; + width: 0; + height: 0; + content: ""; + border-bottom: 4px solid transparent; + border-top: 4px solid transparent; + border-right: 4px solid transparent; + border-left: 4px solid; +} + +.expand-toggle.open:after { + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + border-bottom: 0; +} \ No newline at end of file diff --git a/website/client/assets/svg/checklist.svg b/website/client/assets/svg/checklist.svg new file mode 100644 index 0000000000..2dbfd3921f --- /dev/null +++ b/website/client/assets/svg/checklist.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/client/components/tasks/task.vue b/website/client/components/tasks/task.vue index 7e9581bc53..9a2414bf71 100644 --- a/website/client/components/tasks/task.vue +++ b/website/client/components/tasks/task.vue @@ -17,8 +17,16 @@ h3.task-title(:class="{ 'has-notes': task.notes }", v-markdown="task.text") .task-notes.small-text(v-markdown="task.notes") .checklist(v-if="canViewchecklist") + .d-inline-flex + .collapse-checklist.d-flex.align-items-center.expand-toggle( + v-if="isUser", + @click="collapseChecklist(task)", + :class="{open: !task.collapseChecklist}", + ) + .svg-icon(v-html="icons.checklist") + span {{ checklistProgress }} label.custom-control.custom-checkbox.checklist-item( - v-if='!castingSpell', + v-if='!castingSpell && !task.collapseChecklist', v-for="item in task.checklist", :class="{'checklist-item-done': item.completed}", ) input.custom-control-input(type="checkbox", :checked="item.completed", @change="toggleChecklistItem(item)") @@ -119,6 +127,26 @@ margin-top: 8px; } + .collapse-checklist { + padding: 2px 6px; + margin-bottom: 9px; + border-radius: 1px; + background-color: $gray-600; + font-size: 10px; + line-height: 1.2; + text-align: center; + color: $gray-200; + + span { + margin: 0px 4px; + } + + .svg-icon { + width: 12px; + height: 8px; + } + } + .checklist-item { color: $gray-50; font-size: 14px; @@ -308,6 +336,7 @@ import calendarIcon from 'assets/svg/calendar.svg'; import challengeIcon from 'assets/svg/challenge.svg'; import tagsIcon from 'assets/svg/tags.svg'; import checkIcon from 'assets/svg/check.svg'; +import checklistIcon from 'assets/svg/checklist.svg'; import bPopover from 'bootstrap-vue/lib/components/popover'; import markdownDirective from 'client/directives/markdown'; import notifications from 'client/mixins/notifications'; @@ -336,6 +365,7 @@ export default { challenge: challengeIcon, tags: tagsIcon, check: checkIcon, + checklist: checklistIcon, }), }; }, @@ -353,6 +383,13 @@ export default { let userIsTaskUser = this.task.userId ? this.task.userId === this.user._id : true; return hasChecklist && userIsTaskUser; }, + checklistProgress () { + const totalItems = this.task.checklist.length; + const completedItems = this.task.checklist.reduce((total, item) => { + return item.completed ? total + 1 : total; + }, 0); + return `${completedItems}/${totalItems}`; + }, leftControl () { const task = this.task; if (task.type === 'reward') return false; @@ -392,10 +429,13 @@ export default { }, }, methods: { - ...mapActions({scoreChecklistItem: 'tasks:scoreChecklistItem'}), + ...mapActions({ + scoreChecklistItem: 'tasks:scoreChecklistItem', + collapseChecklist: 'tasks:collapseChecklist', + }), toggleChecklistItem (item) { if (this.castingSpell) return; - item.completed = !item.completed; + item.completed = !item.completed; // @TODO this should go into the action? this.scoreChecklistItem({taskId: this.task._id, itemId: item.id}); }, edit (e, task) { diff --git a/website/client/store/actions/tasks.js b/website/client/store/actions/tasks.js index 81cf6ba608..4e7b170571 100644 --- a/website/client/store/actions/tasks.js +++ b/website/client/store/actions/tasks.js @@ -124,6 +124,13 @@ export async function scoreChecklistItem (store, {taskId, itemId}) { await axios.post(`/api/v3/tasks/${taskId}/checklist/${itemId}/score`); } +export async function collapseChecklist (store, task) { + task.collapseChecklist = !task.collapseChecklist; + await axios.put(`/api/v3/tasks/${task._id}`, { + collapseChecklist: task.collapseChecklist, + }); +} + export async function destroy (store, task) { const list = store.state.tasks.data[`${task.type}s`]; const taskIndex = list.findIndex(t => t._id === task._id);