New client group plan (#8948)

* Added stripe payment for group plan

* Began adding amazon

* Added amazon payments for group

* Added get group plans route

* Added group plan nav

* Added initial task page

* Added create and edit group plans

* Added initial approval header and footer

* Added assignment and approved requirement

* Added minor text fixes

* Added inital approval flow

* Added approval modal

* Removed always true

* Added more styles for filters

* Added search

* Added env vars

* Fixed router issues

* Added env to social login

* Fixed merge conflict
This commit is contained in:
Keith Holliday
2017-08-14 13:19:41 -06:00
committed by GitHub
parent 6e89197b3f
commit eb43f83c71
21 changed files with 1522 additions and 524 deletions

View File

@@ -1,126 +1,149 @@
<template lang="pug">
form(
v-if="task",
@submit.stop.prevent="submit()",
)
b-modal#task-modal(
size="sm",
@hidden="cancel()",
form(
v-if="task",
@submit.stop.prevent="submit()",
)
.task-modal-header(
slot="modal-header",
:class="[cssClass]",
b-modal#task-modal(
size="sm",
@hidden="cancel()",
)
h1 {{ title }}
.form-group
label(v-once) {{ `${$t('title')}*` }}
input.form-control(type='text', :class="[`${cssClass}-modal-input`]", required, v-model="task.text")
.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")
.option(v-if="['daily', 'todo'].indexOf(task.type) > -1")
label(v-once) {{ $t('checklist') }}
br
.inline-edit-input-group.checklist-group.input-group(v-for="(item, $index) in task.checklist")
input.inline-edit-input.checklist-item.form-control(type="text", :value="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') }}
.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")
.task-modal-header(
slot="modal-header",
:class="[cssClass]",
)
h1 {{ title }}
.form-group
label(v-once) {{ `${$t('title')}*` }}
input.form-control(type='text', :class="[`${cssClass}-modal-input`]", required, v-model="task.text")
.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")
.option(v-if="['daily', 'todo'].indexOf(task.type) > -1")
label(v-once) {{ $t('checklist') }}
br
.inline-edit-input-group.checklist-group.input-group(v-for="(item, $index) in task.checklist")
input.inline-edit-input.checklist-item.form-control(type="text", :value="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
.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")
.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
.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")
.option(v-if="task.type === 'daily'")
label(v-once) {{ $t('startDate') }}
datepicker(v-model="task.startDate")
.option(v-if="task.type === 'daily'")
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) }}
label(v-once) {{ $t('repeatEvery') }}
input.form-control(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]")
.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') }}
.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")
.option(v-if="task.type === 'daily'")
label(v-once) {{ $t('startDate') }}
datepicker(v-model="task.startDate")
.option(v-if="task.type === 'daily'")
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) }}
label(v-once) {{ $t('repeatEvery') }}
input.form-control(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(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') }}
.option
label(v-once) {{ $t('tags') }}
.category-wrap(@click="showTagsSelect = !showTagsSelect")
span.category-select(v-if='task.tags && task.tags.length === 0') {{$t('none')}}
span.category-select(v-else) {{getTagsFor(task)[0]}}
.category-box(v-if="showTagsSelect")
.form-check(
v-for="tag in user.tags",
:key="tag.id",
)
label.custom-control.custom-checkbox
input.custom-control-input(type="checkbox", :value="tag.id", v-model="task.tags")
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(v-once) {{ tag.name }}
button.btn.btn-primary(@click="showTagsSelect = !showTagsSelect") {{$t('close')}}
.option(v-if="task.type === 'habit'")
label(v-once) {{ $t('resetStreak') }}
b-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) }}
span.custom-control-description {{ $t('dayOfWeek') }}
.task-modal-footer(slot="modal-footer")
button.btn.btn-primary(type="submit", v-once) {{ $t('save') }}
span.cancel-task-btn(v-once, v-if="purpose === 'create'", @click="cancel()") {{ $t('cancel') }}
span.delete-task-btn(v-once, v-else, @click="destroy()") {{ $t('delete') }}
.option
label(v-once) {{ $t('tags') }}
.category-wrap(@click="showTagsSelect = !showTagsSelect")
span.category-select(v-if='task.tags && task.tags.length === 0') {{$t('none')}}
span.category-select(v-else) {{getTagsFor(task)[0]}}
.category-box(v-if="showTagsSelect")
.form-check(
v-for="tag in user.tags",
:key="tag.id",
)
label.custom-control.custom-checkbox
input.custom-control-input(type="checkbox", :value="tag.id", v-model="task.tags")
span.custom-control-indicator
span.custom-control-description(v-once) {{ tag.name }}
button.btn.btn-primary(@click="showTagsSelect = !showTagsSelect") {{$t('close')}}
.option(v-if="task.type === 'habit'")
label(v-once) {{ $t('resetStreak') }}
b-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.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")
.form-check(
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 }}
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")
button.btn.btn-primary(type="submit", v-once) {{ $t('save') }}
span.cancel-task-btn(v-once, v-if="purpose === 'create'", @click="cancel()") {{ $t('cancel') }}
span.delete-task-btn(v-once, v-else, @click="destroy()") {{ $t('delete') }}
</template>
<style lang="scss">
@@ -325,6 +348,7 @@ 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 Datepicker from 'vuejs-datepicker';
import moment from 'moment';
import uuid from 'uuid';
@@ -344,11 +368,13 @@ export default {
bDropdown,
bDropdownItem,
Datepicker,
toggleSwitch,
},
props: ['task', 'purpose', 'challengeId'], // purpose is either create or edit, task is the task created or edited
props: ['task', 'purpose', 'challengeId', 'groupId'], // purpose is either create or edit, task is the task created or edited
data () {
return {
showTagsSelect: false,
showAssignedSelect: false,
newChecklistItem: null,
icons: Object.freeze({
information: informationIcon,
@@ -360,8 +386,31 @@ export default {
positive: positiveIcon,
destroy: deleteIcon,
}),
requiresApproval: false, // We can't set task.group fields so we use this field to toggle
members: [],
memberNamesById: {},
assignedMembers: [],
};
},
watch: {
async task () {
if (this.groupId && this.task.group && 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 = this.task.group.assignedUsers;
}
},
},
computed: {
...mapGetters({
getTaskClasses: 'tasks:getTaskClasses',
@@ -457,10 +506,21 @@ export default {
tasks: [this.task],
});
this.$emit('taskCreated', this.task);
} else if (this.groupId) {
this.$store.dispatch('tasks:createGroupTasks', {
groupId: this.groupId,
tasks: [this.task],
});
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);
}
@@ -474,6 +534,24 @@ export default {
this.showTagsSelect = false;
this.$emit('cancel');
},
updateRequiresApproval (newValue) {
let truthy = true;
if (!newValue) truthy = false; // This return undefined instad of false
this.requiresApproval = truthy;
},
async toggleAssignment (memberId) {
if (this.assignedMembers.indexOf(memberId) === -1) {
await this.$store.dispatch('tasks:unassignTask', {
taskId: this.task._id,
userId: this.user._id,
});
return;
}
await this.$store.dispatch('tasks:assignTask', {
taskId: this.task._id,
userId: this.user._id,
});
},
},
};
</script>