mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
Oct 5 fixes (#9163)
* Added removing invites * Addeed messages for empty gems to icon * Added member for challenge members * Fixed task cloning ending * Fixed group task assignment * Added small hack to prevent scrolling issues * Fixed lint
This commit is contained in:
@@ -142,4 +142,22 @@ describe('GET /challenges/:challengeId/members', () => {
|
|||||||
let resIds = res.concat(res2).map(member => member._id);
|
let resIds = res.concat(res2).map(member => member._id);
|
||||||
expect(resIds).to.eql(expectedIds.sort());
|
expect(resIds).to.eql(expectedIds.sort());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports using req.query.search to get search members', async () => {
|
||||||
|
let group = await generateGroup(user, {type: 'party', name: generateUUID()});
|
||||||
|
let challenge = await generateChallenge(user, group);
|
||||||
|
|
||||||
|
let usersToGenerate = [];
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
usersToGenerate.push(generateUser({challenges: [challenge._id]}));
|
||||||
|
}
|
||||||
|
let generatedUsers = await Promise.all(usersToGenerate);
|
||||||
|
let profileNames = generatedUsers.map(generatedUser => generatedUser.profile.name);
|
||||||
|
|
||||||
|
let firstProfileName = profileNames[0];
|
||||||
|
let nameToSearch = firstProfileName.substring(0, 4);
|
||||||
|
|
||||||
|
let response = await user.get(`/challenges/${challenge._id}/members?search=${nameToSearch}`);
|
||||||
|
expect(response[0].profile.name).to.eql(firstProfileName);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -56,7 +56,8 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.modal {
|
/* @TODO: The modal-open class is not being removed. Let's try this for now */
|
||||||
|
.modal, .modal-open {
|
||||||
overflow-y: scroll !important;
|
overflow-y: scroll !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -301,6 +301,9 @@ export default {
|
|||||||
challengeId: this.searchId,
|
challengeId: this.searchId,
|
||||||
tasks: clonedTasks,
|
tasks: clonedTasks,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.$store.state.challengeOptions.cloning = false;
|
||||||
|
this.$store.state.challengeOptions.tasksToClone = [];
|
||||||
},
|
},
|
||||||
async loadChallenge () {
|
async loadChallenge () {
|
||||||
this.challenge = await this.$store.dispatch('challenges:getChallenge', {challengeId: this.searchId});
|
this.challenge = await this.$store.dispatch('challenges:getChallenge', {challengeId: this.searchId});
|
||||||
|
|||||||
@@ -10,8 +10,13 @@ div
|
|||||||
.col-12
|
.col-12
|
||||||
strong(v-once) {{$t('selectChallengeWinnersDescription')}}
|
strong(v-once) {{$t('selectChallengeWinnersDescription')}}
|
||||||
.col-12
|
.col-12
|
||||||
select.form-control(v-model='winnerId')
|
input.form-control(type='text', v-model='searchTerm')
|
||||||
option(v-for='member in members', :value='member._id') {{member.profile.name}}
|
.col-12
|
||||||
|
div(v-for='member in memberResults', @click='selectMember(member._id)')
|
||||||
|
strong {{member.profile.name}}
|
||||||
|
span(v-if='winnerId === member._id') Selected
|
||||||
|
//- select.form-control(v-model='winnerId')
|
||||||
|
//- option(v-for='member in members', :value='member._id') {{member.profile.name}}
|
||||||
.col-12
|
.col-12
|
||||||
button.btn.btn-primary(v-once, @click='closeChallenge') {{$t('awardWinners')}}
|
button.btn.btn-primary(v-once, @click='closeChallenge') {{$t('awardWinners')}}
|
||||||
.col-12
|
.col-12
|
||||||
@@ -72,6 +77,7 @@ div
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||||
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||||
@@ -86,9 +92,25 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
winnerId: '',
|
winnerId: '',
|
||||||
|
searchTerm: '',
|
||||||
|
memberResults: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
searchTerm: debounce(function searchTerm (newSearch) {
|
||||||
|
this.searchChallengeMember(newSearch);
|
||||||
|
}, 500),
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async searchChallengeMember (search) {
|
||||||
|
this.memberResults = await this.$store.dispatch('members:getChallengeMembers', {
|
||||||
|
challengeId: this.challengeId,
|
||||||
|
searchTerm: search,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
selectMember (memberId) {
|
||||||
|
this.winnerId = memberId;
|
||||||
|
},
|
||||||
async closeChallenge () {
|
async closeChallenge () {
|
||||||
this.challenge = await this.$store.dispatch('challenges:selectChallengeWinner', {
|
this.challenge = await this.$store.dispatch('challenges:selectChallengeWinner', {
|
||||||
challengeId: this.challengeId,
|
challengeId: this.challengeId,
|
||||||
|
|||||||
@@ -53,9 +53,16 @@ div
|
|||||||
button.btn.btn-secondary(@click='loadMoreMembers()') {{ $t('loadMore') }}
|
button.btn.btn-secondary(@click='loadMoreMembers()') {{ $t('loadMore') }}
|
||||||
.row.gradient(v-if='members.length > 3')
|
.row.gradient(v-if='members.length > 3')
|
||||||
div(v-if='selectedPage === "invites"')
|
div(v-if='selectedPage === "invites"')
|
||||||
.row(v-for='member in invites')
|
.row(v-for='(member, index) in invites')
|
||||||
.col-11.no-padding-left
|
.col-11.no-padding-left
|
||||||
member-details(:member='member')
|
member-details(:member='member')
|
||||||
|
.col-1.actions
|
||||||
|
b-dropdown(right=true)
|
||||||
|
.svg-icon.inline.dots(slot='button-content', v-html="icons.dots")
|
||||||
|
b-dropdown-item(@click='removeInvite(member, index)', v-if='isLeader')
|
||||||
|
span.dropdown-icon-item
|
||||||
|
.svg-icon.inline(v-html="icons.removeIcon", v-if='isLeader')
|
||||||
|
span.text {{$t('removeInvite')}}
|
||||||
.modal-footer
|
.modal-footer
|
||||||
button.btn.btn-primary(@click='close()') {{ $t('close') }}
|
button.btn.btn-primary(@click='close()') {{ $t('close') }}
|
||||||
</template>
|
</template>
|
||||||
@@ -375,6 +382,14 @@ export default {
|
|||||||
viewInvites () {
|
viewInvites () {
|
||||||
this.selectedPage = 'invites';
|
this.selectedPage = 'invites';
|
||||||
},
|
},
|
||||||
|
async removeInvite (member, index) {
|
||||||
|
this.invites.splice(index, 1);
|
||||||
|
await this.$store.dispatch('members:removeMember', {
|
||||||
|
memberId: member._id,
|
||||||
|
groupId: this.groupId,
|
||||||
|
});
|
||||||
|
this.viewMembers();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -148,6 +148,7 @@
|
|||||||
@click="itemSelected(item)"
|
@click="itemSelected(item)"
|
||||||
)
|
)
|
||||||
span(slot="popoverContent")
|
span(slot="popoverContent")
|
||||||
|
strong(v-if='item.key === "gem" && gemsLeft === 0') {{ $t('maxBuyGems') }}
|
||||||
h4.popover-content-title {{ item.text }}
|
h4.popover-content-title {{ item.text }}
|
||||||
|
|
||||||
template(slot="itemBadge", scope="ctx")
|
template(slot="itemBadge", scope="ctx")
|
||||||
@@ -156,6 +157,8 @@
|
|||||||
:show="userItems[item.purchaseType][item.key] != 0",
|
:show="userItems[item.purchaseType][item.key] != 0",
|
||||||
:count="userItems[item.purchaseType][item.key] || 0"
|
:count="userItems[item.purchaseType][item.key] || 0"
|
||||||
)
|
)
|
||||||
|
.gems-left(v-if='item.key === "gem"')
|
||||||
|
| {{ gemsLeft }}
|
||||||
|
|
||||||
span.badge.badge-pill.badge-item.badge-svg(
|
span.badge.badge-pill.badge-item.badge-svg(
|
||||||
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
|
:class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
|
||||||
@@ -163,6 +166,7 @@
|
|||||||
)
|
)
|
||||||
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
|
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
|
||||||
|
|
||||||
|
|
||||||
div.fill-height
|
div.fill-height
|
||||||
|
|
||||||
drawer(
|
drawer(
|
||||||
@@ -339,6 +343,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gems-left {
|
||||||
|
position: absolute;
|
||||||
|
right: -.5em;
|
||||||
|
top: -.5em;
|
||||||
|
color: $white;
|
||||||
|
background: $purple-200;
|
||||||
|
padding: .15em .55em;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 1px 1px 0 rgba($black, 0.12);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -374,6 +391,7 @@
|
|||||||
import getItemInfo from 'common/script/libs/getItemInfo';
|
import getItemInfo from 'common/script/libs/getItemInfo';
|
||||||
import isPinned from 'common/script/libs/isPinned';
|
import isPinned from 'common/script/libs/isPinned';
|
||||||
import shops from 'common/script/libs/shops';
|
import shops from 'common/script/libs/shops';
|
||||||
|
import planGemLimits from 'common/script/libs/planGemLimits';
|
||||||
|
|
||||||
import _filter from 'lodash/filter';
|
import _filter from 'lodash/filter';
|
||||||
import _sortBy from 'lodash/sortBy';
|
import _sortBy from 'lodash/sortBy';
|
||||||
@@ -553,6 +571,10 @@ export default {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
gemsLeft () {
|
||||||
|
if (!this.user.purchased.plan) return 0;
|
||||||
|
return planGemLimits.convCap + this.user.purchased.plan.consecutive.gemCapExtra - this.user.purchased.plan.gemsBought;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getClassName (classType) {
|
getClassName (classType) {
|
||||||
|
|||||||
@@ -34,8 +34,6 @@
|
|||||||
div.clearfix(slot="modal-footer")
|
div.clearfix(slot="modal-footer")
|
||||||
span.balance.float-left {{ $t('yourBalance') }}
|
span.balance.float-left {{ $t('yourBalance') }}
|
||||||
balanceInfo.float-right
|
balanceInfo.float-right
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
||||||
|
|||||||
@@ -767,7 +767,7 @@ export default {
|
|||||||
|
|
||||||
await this.$store.dispatch('tasks:unassignTask', {
|
await this.$store.dispatch('tasks:unassignTask', {
|
||||||
taskId: this.task._id,
|
taskId: this.task._id,
|
||||||
userId: this.user._id,
|
userId: memberId,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -778,7 +778,7 @@ export default {
|
|||||||
|
|
||||||
await this.$store.dispatch('tasks:assignTask', {
|
await this.$store.dispatch('tasks:assignTask', {
|
||||||
taskId: this.task._id,
|
taskId: this.task._id,
|
||||||
userId: this.user._id,
|
userId: memberId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ export async function getChallengeMembers (store, payload) {
|
|||||||
url += `&lastId=${payload.lastMemberId}`;
|
url += `&lastId=${payload.lastMemberId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (payload.searchTerm) {
|
||||||
|
url += `&search=${payload.searchTerm}`;
|
||||||
|
}
|
||||||
|
|
||||||
let response = await axios.get(url);
|
let response = await axios.get(url);
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,6 +226,10 @@ function _getMembersForItem (type) {
|
|||||||
fields = memberFields;
|
fields = memberFields;
|
||||||
addComputedStats = true;
|
addComputedStats = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req.query.search) {
|
||||||
|
query['profile.name'] = {$regex: req.query.search};
|
||||||
|
}
|
||||||
} else if (type === 'group-members') {
|
} else if (type === 'group-members') {
|
||||||
if (group.type === 'guild') {
|
if (group.type === 'guild') {
|
||||||
query.guilds = group._id;
|
query.guilds = group._id;
|
||||||
|
|||||||
Reference in New Issue
Block a user