Sept 22 fixes (#9065)

* Removed lingering checklist

* Added another party data check

* Added move cursor on hover

* Removed task locally

* Prevented user from being able to delete an active challenge task

* Reset tasks when viewing member progress

* Prevented challenge owners from adding checklists

* Hide challenges columns with no tasks

* Add error translations

* Added markdown to challenge description

* Allowed leader to rejoin challenge

* Replaced description with summary

* Fixed delete logic

* Added author

* Added loading message

* Added load more

* Added default sub

* Fixed remove all

* Added lint
This commit is contained in:
Keith Holliday
2017-09-22 16:47:16 -05:00
committed by GitHub
parent 6fcf739c89
commit 6edd1a1fa5
13 changed files with 130 additions and 59 deletions

View File

@@ -10,7 +10,7 @@
h1 {{challenge.name}}
div
strong(v-once) {{$t('createdBy')}}:
span {{challenge.author}}
span {{challenge.leader.profile.name}}
// @TODO: make challenge.author a variable inside the createdBy string (helps with RTL languages)
// @TODO: Implement in V2 strong.margin-left(v-once)
.svg-icon.calendar-icon(v-html="icons.calendarIcon")
@@ -42,10 +42,11 @@
:type="column",
:key="column",
:taskListOverride='tasksByType[column]',
v-on:editTask="editTask")
v-on:editTask="editTask",
v-if='tasksByType[column].length > 0')
.col-4.sidebar.standard-page
.acitons
div(v-if='!isMember && !isLeader')
div(v-if='canJoin')
button.btn.btn-success(v-once, @click='joinChallenge()') {{$t('joinChallenge')}}
div(v-if='isMember')
button.btn.btn-danger(v-once, @click='leaveChallenge()') {{$t('leaveChallenge')}}
@@ -61,6 +62,7 @@
:challengeId="challengeId",
v-on:taskCreated='taskCreated',
v-on:taskEdited='taskEdited',
@taskDestroyed='taskDestroyed'
)
div(v-if='isLeader')
button.btn.btn-secondary(v-once, @click='edit()') {{$t('editChallenge')}}
@@ -74,7 +76,7 @@
h2 {{$t('challengeSummary')}}
p {{challenge.summary}}
h2 {{$t('challengeDescription')}}
p {{challenge.description}}
p(v-markdown='challenge.description')
</template>
<style lang='scss' scoped>
@@ -178,6 +180,7 @@ import { mapState } from 'client/libs/store';
import closeChallengeModal from './closeChallengeModal';
import Column from '../tasks/column';
import TaskModal from '../tasks/taskModal';
import markdownDirective from 'client/directives/markdown';
import challengeModal from './challengeModal';
import challengeMemberProgressModal from './challengeMemberProgressModal';
@@ -189,6 +192,9 @@ import calendarIcon from 'assets/svg/calendar.svg';
export default {
props: ['challengeId'],
directives: {
markdown: markdownDirective,
},
components: {
closeChallengeModal,
challengeModal,
@@ -232,6 +238,9 @@ export default {
if (!this.challenge.leader) return false;
return this.user._id === this.challenge.leader._id;
},
canJoin () {
return !this.isMember;
},
},
mounted () {
if (!this.searchId) this.searchId = this.challengeId;
@@ -327,7 +336,14 @@ export default {
});
this.tasksByType[task.type].splice(index, 1, task);
},
taskDestroyed (task) {
let index = findIndex(this.tasksByType[task.type], (taskItem) => {
return taskItem._id === task._id;
});
this.tasksByType[task.type].splice(index, 1);
},
showMemberModal () {
this.$store.state.memberModalOptions.challengeId = this.challenge._id;
this.$store.state.memberModalOptions.groupId = 'challenge'; // @TODO: change these terrible settings
this.$store.state.memberModalOptions.group = this.group;
this.$store.state.memberModalOptions.viewingMembers = this.members;

View File

@@ -33,6 +33,13 @@ export default {
watch: {
async memberId (id) {
if (!id) return;
this.tasksByType = {
habit: [],
daily: [],
todo: [],
reward: [],
};
let response = await axios.get(`/api/v3/challenges/${this.challengeId}/members/${this.memberId}`);
let tasks = response.data.data.tasks;
tasks.forEach((task) => {

View File

@@ -360,41 +360,44 @@ export default {
},
async createChallenge () {
// @TODO: improve error handling, add it to updateChallenge, make errors translatable. Suggestion: `<% fieldName %> is required` where possible, where `fieldName` is inserted as the translatable string that's used for the field header.
let errors = '';
if (!this.workingChallenge.name) errors += 'Name is required\n';
if (this.workingChallenge.shortName.length < MIN_SHORTNAME_SIZE_FOR_CHALLENGES) errors += 'Tag name is too short\n';
if (!this.workingChallenge.summary) errors += 'Summary is required\n';
if (this.workingChallenge.summary.length > MAX_SUMMARY_SIZE_FOR_CHALLENGES) errors += 'Summary is too long\n';
if (!this.workingChallenge.description) errors += 'Description is required\n';
if (!this.workingChallenge.group) errors += 'Location of challenge is required ("Add to")\n';
if (!this.workingChallenge.categories || this.workingChallenge.categories.length === 0) errors += 'One or more categories must be selected\n';
if (errors) {
alert(errors);
} else {
this.workingChallenge.timestamp = new Date().getTime();
let categoryKeys = this.workingChallenge.categories;
let serverCategories = [];
categoryKeys.forEach(key => {
let catName = this.categoriesHashByKey[key];
serverCategories.push({
slug: key,
name: catName,
});
});
this.workingChallenge.categories = serverCategories;
let errors = [];
let challenge = await this.$store.dispatch('challenges:createChallenge', {challenge: this.workingChallenge});
// @TODO: When to remove from guild instead?
this.user.balance -= this.workingChallenge.prize / 4;
if (!this.workingChallenge.name) errors.push(this.$t('nameRequired'));
if (this.workingChallenge.shortName.length < MIN_SHORTNAME_SIZE_FOR_CHALLENGES) errors.push(this.$t('tagTooShort'));
if (!this.workingChallenge.summary) errors.push(this.$t('summaryRequired'));
if (this.workingChallenge.summary.length > MAX_SUMMARY_SIZE_FOR_CHALLENGES) errors.push(this.$t('summaryTooLong'));
if (!this.workingChallenge.description) errors.push(this.$t('descriptionRequired'));
if (!this.workingChallenge.group) errors.push(this.$t('locationRequired'));
if (!this.workingChallenge.categories || this.workingChallenge.categories.length === 0) errors.push(this.$t('categoiresRequired'));
this.$emit('createChallenge', challenge);
this.resetWorkingChallenge();
if (this.cloning) this.$store.state.challengeOptions.cloning = true;
this.$root.$emit('hide::modal', 'challenge-modal');
this.$router.push(`/challenges/${challenge._id}`);
if (errors.length > 0) {
alert(errors.join('\n'));
return;
}
this.workingChallenge.timestamp = new Date().getTime();
let categoryKeys = this.workingChallenge.categories;
let serverCategories = [];
categoryKeys.forEach(key => {
let catName = this.categoriesHashByKey[key];
serverCategories.push({
slug: key,
name: catName,
});
});
this.workingChallenge.categories = serverCategories;
let challenge = await this.$store.dispatch('challenges:createChallenge', {challenge: this.workingChallenge});
// @TODO: When to remove from guild instead?
this.user.balance -= this.workingChallenge.prize / 4;
this.$emit('createChallenge', challenge);
this.resetWorkingChallenge();
if (this.cloning) this.$store.state.challengeOptions.cloning = true;
this.$root.$emit('hide::modal', 'challenge-modal');
this.$router.push(`/challenges/${challenge._id}`);
},
updateChallenge () {
let categoryKeys = this.workingChallenge.categories;

View File

@@ -7,6 +7,7 @@
.row.header-row
.col-md-8.text-left
h1(v-once) {{$t('findChallenges')}}
h2(v-if='loading') {{ $t('loading') }}
.col-md-4
// @TODO: implement sorting span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t('sort')", right=true)
@@ -63,6 +64,7 @@ export default {
},
data () {
return {
loading: true,
icons: Object.freeze({
positiveIcon,
}),
@@ -125,7 +127,9 @@ export default {
this.$root.$emit('show::modal', 'challenge-modal');
},
async loadchallanges () {
this.loading = true;
this.challenges = await this.$store.dispatch('challenges:getUserChallenges');
this.loading = false;
},
challengeCreated (challenge) {
this.challenges.push(challenge);

View File

@@ -12,7 +12,7 @@ div
.col-9
router-link.title(:to="{ name: 'challenge', params: { challengeId: challenge._id } }")
strong {{challenge.name}}
p {{challenge.description}}
p {{challenge.summary || challenge.name}}
div
.svg-icon.member-icon(v-html="icons.memberIcon")
.member-count {{challenge.memberCount}}