habitica-self-host/website/client/src/components/groups/questDetailModal.vue
negue a8b58815b4
Update Party / Group Sidebar / Quest states (#12793)
* move groups/sidebar to groupSidebar.vue

* lint files

* extract group/party sidebar to rightSidebar.vue

* wip stories with example data

* update stories - wip sidebar re-styling

* message party / group leader + move items to the menu

* update paddings /place for quest section

* invite to party / guild

* update labels (* Party / Guild )

* guild-background to group-background

* correct menu order + missing a label based on the group type

* no quest - styles / layout applied

* quest owner / not started - styles applied   + extracted questActions from questDetailsModal.vue to a mixin

* no challenge style

* hover with underlines

* quest-pending area layout / margins

* "Collection Quest/Quest Owner Participating" Styling Done

* group sidebar menu with icons / background

* remove most participate button styles

* fix quest-invite panel

* move "Start Quest" + add "Leave Quest"

* Not Participating + Boss + Rage Quests restyling

* party quest changes - invitedToQuest + button styles + no-items style + view details

* fix icons + rage value + colors

* fix duplicate key

* hide items label if 0 items found + hide pending damage if there is none + sidebar section margin + fix percent calculation 0 => 0%

* combine quest abandon / cancel to one call + hide begin if quest has already started + close modal if quest was canceled

* remove unused translate string

* allow leaving an accepted but inactive quest + disable leave when user is quest leader

* update "are you sure" questions - remove "doubleSureAbort" - add "sureLeaveInactive"

* sidebar margins + menu icon color

* refactored css rules

* improve some styles

* fix button spacing

* fix dropmenu with icon hover

* hide leave quest for leaders + fix quest buttons spacing

* add pending items label

* remove "X items found" label

* first round of fixes

* last v-once

* Update Quest Dialogs (#13112)

* new quest rewards panel + extract questPopover and itemWithLabel

* WIP: questInfo still not applying the row-height..

* split up start-quest-modal into select and detail modal - also rename the current quest-details to be the group-quest-details modal

* remove start-quest-modal from modal-scss

* update package-lock

* WIP before using the quest sidebar branch as a base

* move quest detail actions to the "new" details dialog

* quest details layout for owner / participant

* fix quest rewards - open details modal from sidebar

* apply quest-details dialog styles to the buyQuestModal one

* fix quest reward icons / popover / texts

* WIP back to quest selection

* fix lint

* merge selectQuestModal.vue with questDetailModal.vue + UI for the select quest

* fix margins / layout / labels

* fix quest detail + wip invitationListModal.vue / participantListModal.vue

* fix questmodal user label centered

* fix centered reward items + grouping items and adding a count-badge

* sort quests by AZ or quantity

* invitations modal

* remove console.info

* complete participantListModal.vue + extracted getClassName

* missed a file for getClassName extraction

* fix invitations

* select the actual quest on details

* fix margins on invite to party / start quest buttons

* replace buyQuestModal close button and title

* fix recursion due to the same name

* missing import

* sort quantity by highest first

* fix "Can't find a Quest to start" styles

* fix "your balance" padding

* fix quest collections / drop items

* fix member details in participants list

* fix quest info

* remove nullable because the build doesn't like it (on this file..)

* add questCompleted to the stories + fix getDropName

* replace quest-rewards in questCompleted.vue

* fix questCompleted.vue style

* delete obsolete components

* add missing spritesheets to storebook

* requested pr changes

* refactored fetchMember

* revert optional chaining

* fix merge conflicts

* fix rightSidebar hover colors - $scss var to css var

* overflow auto instead of scroll

* prevent wrapping of quest collections

* rollback to multi line quest items

* use min-width for the quest popover
2021-05-28 16:11:43 -05:00

450 lines
9.9 KiB
Vue

<template>
<b-modal
id="quest-detail-modal"
title="Empty"
size="md"
:hide-footer="true"
:hide-header="true"
:modal-class="dialogClass"
>
<div class="dialog-close">
<close-icon @click="close()" />
</div>
<h2 class="text-center textCondensed">
{{ selectMode ? $t('selectQuest') : $t('questDetailsTitle') }}
</h2>
<div class="quest-panel" v-if="selectMode">
<div class="quest-panel-header">
<h3>
{{ $t('yourQuests') }}
</h3>
<div class="sort-by">
<span class="dropdown-label">{{ $t('sort') }}</span>
<select-translated-array
:right="true"
:items="['quantity', 'AZ']"
:value="sortBy"
class="inline"
:inline-dropdown="false"
@select="sortBy = $event"
/>
</div>
</div>
<div class="quest-items">
<div
v-for="item in questsInfoList"
:key="item.key"
class="quest-col"
@click="selectQuest(item)"
>
<item
:key="item.key"
:item="item"
:item-content-class="item.class"
>
<countBadge
slot="itemBadge"
:show="item.amount !== 1"
:count="item.amount"
/>
<template
slot="popoverContent"
slot-scope="context"
>
<div
class="questPopover"
>
<h4 class="popover-content-title">
{{ context.item.text }}
</h4>
<questInfo :quest="context.item" />
</div>
</template>
</item>
</div>
</div>
<div class="row">
<div class="col-10 offset-1 text-center">
<span
v-once
class="no-quest-to-start"
>
<b>{{ $t('noQuestToStartTitle') }}</b> <br>
<span v-html="$t('noQuestToStart', { questShop: '/shops/quests' })"></span>
</span>
</div>
</div>
</div>
<div v-else>
<div v-if="questData" class="quest-combined-content">
<questDialogContent
:item="questData"
:group="group"
class="quest-detail"
/>
<quest-rewards :quest="questData" class="mt-4" />
</div>
<div
v-if="!groupHasQuest"
class="text-center"
>
<button
class="btn btn-primary mt-0 invite-btn"
:disabled="!Boolean(selectedQuest) || loading"
@click="questInit()"
>
{{ $t('inviteToPartyOrQuest') }}
</button>
</div>
<div
v-if="fromSelectionDialog"
class="text-center back-to-selection"
@click="goBackToQuestSelection()"
>
<span
v-once
class="svg-icon color"
v-html="icons.navigationBack"
>
</span>
<span>
{{ $t('backToSelection') }}
</span>
</div>
<div
v-if="groupHasQuest && canEditQuest"
class="text-center actions"
>
<div>
<button
v-if="!onActiveQuest"
v-once
class="btn btn-success mb-3"
@click="questConfirm()"
>
{{ $t('startQuest') }}
</button>
</div>
<div>
<div
v-once
class="cancel"
@click="questCancel()"
>
{{ $t('cancelQuest') }}
</div>
</div>
</div>
</div>
</b-modal>
</template>
<style lang='scss' scoped>
@import '~@/assets/scss/colors.scss';
h2 {
color: $purple-300;
margin-top: 1rem;
}
.invite-btn {
margin-bottom: 1rem;
}
.back-to-selection {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
cursor: pointer;
color: $blue-10;
font-size: 14px;
line-height: 1.71;
text-align: right;
&:hover {
text-decoration: underline;
}
.svg-icon {
color: $blue-50;
height: 14px;
width: 9px;
margin-right: 0.5rem;
}
}
.quest-panel {
background-color: $gray-700;
// reset margin
margin-left: -1rem;
margin-right: -1rem;
margin-bottom: -1rem;
// add padding back
padding-top: 1rem;
padding-left: 1.5rem;
padding-right: 1.5rem;
padding-bottom: 2rem;
border-bottom-left-radius: 1rem;
border-bottom-right-radius: 1rem;
}
.quest-panel-header {
padding-bottom: 1rem;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.quest-items {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 0.5rem;
// somehow the browser felt like setting this 398px instead
// now its fixed to 400 :)
width: 400px;
margin-bottom: 1.5rem;
.quest-col {
::v-deep {
.item-wrapper {
margin-bottom: 0;
}
}
}
}
.quest-detail {
margin-left: 1rem;
margin-right: 1rem;
}
.no-quest-to-start {
font-size: 12px;
line-height: 1.33;
text-align: center;
color: $gray-100;
a {
color: $blue-10;
}
}
#quest-detail-modal {
::v-deep & {
.modal-dialog {
width: 448px !important;
}
}
.quest-combined-content {
margin-bottom: 1.5rem;
}
::v-deep &:not(.need-bottom-padding) {
.modal-content {
// so that the rounded corners still apply
overflow: hidden;
}
.modal-body {
padding-bottom: 0;
}
.quest-combined-content {
margin-bottom: 0;
}
}
@media only screen and (max-width: 1000px) {
.modal-dialog {
max-width: 80%;
width: 80% !important;
.modal-body {
flex-direction: column;
display: flex;
}
}
}
.actions {
padding-bottom: .5em;
.cancel {
color: $maroon-50;
}
.cancel:hover {
cursor: pointer;
text-decoration: underline;
}
}
}
</style>
<script>
import orderBy from 'lodash/orderBy';
import { mapState } from '@/libs/store';
import * as Analytics from '@/libs/analytics';
import * as quests from '@/../../common/script/content/quests';
import navigationBack from '@/assets/svg/navigation_back.svg';
import questDialogContent from '../shops/quests/questDialogContent';
import closeIcon from '../shared/closeIcon';
import QuestRewards from '../shops/quests/questRewards';
import questActionsMixin from './questActions.mixin';
import SelectTranslatedArray from '../tasks/modal-controls/selectTranslatedArray';
import QuestInfo from '../shops/quests/questInfo';
import Item from '@/components/inventory/item';
import getItemInfo from '../../../../common/script/libs/getItemInfo';
import CountBadge from '../ui/countBadge';
export default {
components: {
CountBadge,
QuestRewards,
questDialogContent,
closeIcon,
SelectTranslatedArray,
QuestInfo,
Item,
},
mixins: [questActionsMixin],
props: ['group'],
data () {
return {
loading: false,
selectMode: true,
selectedQuest: {},
fromSelectionDialog: false,
icons: Object.freeze({
navigationBack,
}),
shareUserIdShown: false,
quests,
sortBy: 'AZ',
};
},
computed: {
...mapState({ user: 'user.data' }),
questData () {
return quests.quests[this.selectedQuest];
},
dialogClass () {
if (!this.groupHasQuest || this.fromSelectionDialog || this.canEditQuest) {
return 'need-bottom-padding';
}
return '';
},
questsInfoList () {
const availableQuests = Object.entries(this.user.items.quests)
.filter(([, amount]) => amount > 0);
return orderBy(availableQuests.map(([key, amount]) => {
const questItem = quests.quests[key];
return {
...getItemInfo(this.user, 'quests', questItem),
amount,
};
}), item => (this.sortBy === 'AZ' ? item.text : -item.amount));
},
},
mounted () {
const userQuests = this.user.items.quests;
for (const key in userQuests) {
if (userQuests[key] > 0) {
this.selectedQuest = key;
break;
}
}
this.$root.$on('bv::show::modal', this.handleOpen);
},
beforeDestroy () {
},
methods: {
selectQuest (selectQuestPayload) {
this.selectMode = false;
this.selectedQuest = selectQuestPayload.key;
this.fromSelectionDialog = true;
},
async questInit () {
this.loading = true;
Analytics.updateUser({
partyID: this.group._id,
partySize: this.group.memberCount,
});
const groupId = this.group._id || this.user.party._id;
const key = this.selectedQuest;
try {
const response = await this.$store.dispatch('guilds:inviteToQuest', { groupId, key });
const quest = response.data.data;
// TODO move the state updates to the action itself
const partyState = this.$store.state.party;
if (!partyState.data) {
partyState.data = {};
}
partyState.data.quest = quest;
} finally {
this.loading = false;
}
this.close();
},
close () {
this.$root.$emit('bv::hide::modal', 'quest-detail-modal');
},
async questConfirm () {
const accepted = await this.questActionsConfirmQuest();
if (accepted) {
this.close();
}
},
async questCancel () {
const accepted = await this.questActionsCancelOrAbortQuest();
if (accepted) {
this.close();
}
},
goBackToQuestSelection () {
this.selectMode = true;
},
handleOpen (_, selectQuestPayload) {
this.fromSelectionDialog = false;
if (selectQuestPayload) {
this.selectMode = false;
this.selectedQuest = selectQuestPayload.key;
} else {
this.selectMode = true;
}
},
},
};
</script>