This commit is contained in:
Hafiz
2025-11-06 13:06:31 -06:00
parent ccc7e7d7a7
commit bf1ea90720
14 changed files with 1471 additions and 1464 deletions

View File

@@ -1,8 +1,8 @@
<template>
<div
class="banner d-flex align-items-center justify-content-between py-3 px-4"
id="privacy-banner"
v-if="!hidden"
id="privacy-banner"
class="banner d-flex align-items-center justify-content-between py-3 px-4"
>
<p
class="mr-3 mb-0"

View File

@@ -5,228 +5,228 @@
:hide-header="true"
@change="onChange($event)"
>
<span
v-if="withPin"
class="badge-dialog"
tabindex="0"
@click.prevent.stop="togglePinned()"
@keypress.enter.prevent.stop="togglePinned()"
>
<pin-badge
:pinned="isPinned"
/>
</span>
<div>
<span
class="svg-icon close-icon icon-16 color"
aria-hidden="true"
v-if="withPin"
class="badge-dialog"
tabindex="0"
@click="hideDialog()"
@keypress.enter="hideDialog()"
v-html="icons.close"
></span>
</div>
<div
v-if="item != null"
class="content"
>
<div class="inner-content">
<slot
name="item"
:item="item"
>
<div v-if="showAvatar">
<avatar
:show-visual-buffs="false"
:member="user"
:avatar-only="true"
:hide-class-badge="true"
:with-background="true"
:override-avatar-gear="getAvatarOverrides(item)"
:sprites-margin="'0px auto 0px -24px'"
@click.prevent.stop="togglePinned()"
@keypress.enter.prevent.stop="togglePinned()"
>
<pin-badge
:pinned="isPinned"
/>
</span>
<div>
<span
class="svg-icon close-icon icon-16 color"
aria-hidden="true"
tabindex="0"
@click="hideDialog()"
@keypress.enter="hideDialog()"
v-html="icons.close"
></span>
</div>
<div
v-if="item != null"
class="content"
>
<div class="inner-content">
<slot
name="item"
:item="item"
>
<div v-if="showAvatar">
<avatar
:show-visual-buffs="false"
:member="user"
:avatar-only="true"
:hide-class-badge="true"
:with-background="true"
:override-avatar-gear="getAvatarOverrides(item)"
:sprites-margin="'0px auto 0px -24px'"
/>
</div>
<item
v-else-if="item.key === 'gem'"
class="flat bordered-item"
:item="item"
:item-content-class="item.class"
:show-popover="false"
/>
<item
v-else-if="item.key != 'gem'"
class="flat bordered-item"
:item="item"
:item-content-class="item.class"
:show-popover="false"
/>
</slot>
<div
v-if="!showAvatar && user.items[item.purchaseType]"
class="owned"
:class="totalOwned"
>
<!-- eslint-disable-next-line max-len -->
<span class="owned-text">{{ $t('owned') }}: <span class="user-amount">{{ totalOwned }}</span></span>
</div>
<item
v-else-if="item.key === 'gem'"
class="flat bordered-item"
<h4 class="title">
{{ itemText }}
</h4>
<div class="item-notes">
{{ itemNotes }}
</div>
<slot
name="additionalInfo"
:item="item"
:item-content-class="item.class"
:show-popover="false"
/>
<item
v-else-if="item.key != 'gem'"
class="flat bordered-item"
:item="item"
:item-content-class="item.class"
:show-popover="false"
/>
</slot>
<div
v-if="!showAvatar && user.items[item.purchaseType]"
class="owned"
:class="totalOwned"
>
<!-- eslint-disable-next-line max-len -->
<span class="owned-text">{{ $t('owned') }}: <span class="user-amount">{{ totalOwned }}</span></span>
</div>
<h4 class="title">
{{ itemText }}
</h4>
<div class="item-notes">
{{ itemNotes }}
</div>
<slot
name="additionalInfo"
:item="item"
>
<equipmentAttributesGrid
v-if="showAttributesGrid"
class="attributesGrid"
:item="item"
:user="user"
/>
</slot>
<div
v-if="item.value > 0 && !(item.key === 'gem' && gemsLeft < 1)"
class="purchase-amount"
>
<div class="item-cost justify-content-center my-3">
<span
class="cost d-flex mx-auto"
:class="getPriceClass()"
>
>
<equipmentAttributesGrid
v-if="showAttributesGrid"
class="attributesGrid"
:item="item"
:user="user"
/>
</slot>
<div
v-if="item.value > 0 && !(item.key === 'gem' && gemsLeft < 1)"
class="purchase-amount"
>
<div class="item-cost justify-content-center my-3">
<span
class="svg-icon icon-24 my-auto mr-1"
aria-hidden="true"
v-html="icons[getPriceClass()]"
class="cost d-flex mx-auto"
:class="getPriceClass()"
>
<span
class="svg-icon icon-24 my-auto mr-1"
aria-hidden="true"
v-html="icons[getPriceClass()]"
>
</span>
<span
class="my-auto"
:class="getPriceClass()"
>{{ item.value }}</span>
</span>
<span
class="my-auto"
:class="getPriceClass()"
>{{ item.value }}</span>
</span>
</div>
</div>
<div
v-if="showAmountToBuy(item)"
class="how-many-to-buy"
>
{{ $t('howManyToBuy') }}
</div>
<div
v-if="showAmountToBuy(item)"
>
<number-increment
class="number-increment"
@updateQuantity="selectedAmountToBuy = $event"
/>
<div
:class="{'notEnough': notEnoughCurrency}"
class="total"
v-if="showAmountToBuy(item)"
class="how-many-to-buy"
>
<span class="total-text">{{ $t('sendTotal') }}</span>
<span
class="svg-icon total icon-24"
aria-hidden="true"
v-html="icons[getPriceClass()]"
></span>
<span
class="total-text"
:class="getPriceClass()"
>{{ item.value * selectedAmountToBuy }}</span>
{{ $t('howManyToBuy') }}
</div>
<div
v-if="showAmountToBuy(item)"
>
<number-increment
class="number-increment"
@updateQuantity="selectedAmountToBuy = $event"
/>
<div
:class="{'notEnough': notEnoughCurrency}"
class="total"
>
<span class="total-text">{{ $t('sendTotal') }}</span>
<span
class="svg-icon total icon-24"
aria-hidden="true"
v-html="icons[getPriceClass()]"
></span>
<span
class="total-text"
:class="getPriceClass()"
>{{ item.value * selectedAmountToBuy }}</span>
</div>
</div>
</div>
<div
v-if="item.key === 'gem' && gemsLeft < 1"
class="no-more-gems"
>
{{ $t('notEnoughGemsToBuy') }}
</div>
<div
v-if="nonSubscriberHourglasses"
class="hourglass-nonsub mt-3"
>
{{ $t('mysticHourglassNeededNoSub') }}
</div>
<button
v-if="getPriceClass() === 'gems'
&& !enoughCurrency(getPriceClass(), item.value * selectedAmountToBuy)"
class="btn btn-primary mb-3"
@click="purchaseGems()"
>
{{ $t('purchaseGems') }}
</button>
<button
v-else-if="nonSubscriberHourglasses"
class="btn btn-primary"
@click="viewSubscriptions(item)"
>
{{ $t('viewSubscriptions') }}
</button>
<button
v-else-if="!(item.key === 'gem' && gemsLeft < 1)"
class="btn btn-primary"
:disabled="item.key === 'gem' && gemsLeft === 0 ||
attemptingToPurchaseMoreGemsThanAreLeft || numberInvalid || item.locked ||
!preventHealthPotion ||
!enoughCurrency(getPriceClass(), item.value * selectedAmountToBuy)"
:class="{'notEnough': !preventHealthPotion ||
!enoughCurrency(getPriceClass(), item.value * selectedAmountToBuy)}"
tabindex="0"
@click="buyItem()"
>
{{ $t('buyNow') }}
</button>
</div>
<div
v-if="item.key === 'gem' && gemsLeft < 1"
class="no-more-gems"
>
{{ $t('notEnoughGemsToBuy') }}
</div>
<div
v-if="nonSubscriberHourglasses"
class="hourglass-nonsub mt-3"
>
{{ $t('mysticHourglassNeededNoSub') }}
</div>
<button
v-if="getPriceClass() === 'gems'
&& !enoughCurrency(getPriceClass(), item.value * selectedAmountToBuy)"
class="btn btn-primary mb-3"
@click="purchaseGems()"
>
{{ $t('purchaseGems') }}
</button>
<button
v-else-if="nonSubscriberHourglasses"
class="btn btn-primary"
@click="viewSubscriptions(item)"
>
{{ $t('viewSubscriptions') }}
</button>
<button
v-else-if="!(item.key === 'gem' && gemsLeft < 1)"
class="btn btn-primary"
:disabled="item.key === 'gem' && gemsLeft === 0 ||
attemptingToPurchaseMoreGemsThanAreLeft || numberInvalid || item.locked ||
!preventHealthPotion ||
!enoughCurrency(getPriceClass(), item.value * selectedAmountToBuy)"
:class="{'notEnough': !preventHealthPotion ||
!enoughCurrency(getPriceClass(), item.value * selectedAmountToBuy)}"
tabindex="0"
@click="buyItem()"
>
{{ $t('buyNow') }}
</button>
</div>
</div>
<countdown-banner
v-if="item.end && item.owned == null"
:end-date="endDate"
class="limitedTime available"
/>
<div
v-if="item.key === 'rebirth_orb' && item.value > 0 && user.stats.lvl >= 100"
class="free-rebirth d-flex align-items-center"
>
<div class="m-auto">
<span
class="svg-icon inline icon-16 mr-2 pt-015"
v-html="icons.whiteClock"
></span>
<span v-html="$t('nextFreeRebirth', {days: nextFreeRebirth})"></span>
</div>
</div>
<div
v-if="item.key === 'gem'"
class="d-flex justify-content-center align-items-center"
>
<div
v-if="gemsLeft > 0"
class="gems-left d-flex justify-content-center align-items-center"
>
<strong>{{ $t('monthlyGems') }} &nbsp;</strong>
{{ gemsLeft }} / {{ totalGems }} {{ $t('gemsRemaining') }}
</div>
<div
v-if="gemsLeft === 0"
class="out-of-gems-banner d-flex justify-content-center align-items-center"
>
<strong>{{ $t('monthlyGems') }} &nbsp;</strong>
{{ gemsLeft }} / {{ totalGems }} {{ $t('gemsRemaining') }}
</div>
</div>
<div
slot="modal-footer"
>
<span class="user-balance ml-3 my-auto">{{ $t('yourBalance') }}</span>
<balanceInfo
class="mr-3"
:currency-needed="getPriceClass()"
:amount-needed="item.value"
<countdown-banner
v-if="item.end && item.owned == null"
:end-date="endDate"
class="limitedTime available"
/>
</div>
<div
v-if="item.key === 'rebirth_orb' && item.value > 0 && user.stats.lvl >= 100"
class="free-rebirth d-flex align-items-center"
>
<div class="m-auto">
<span
class="svg-icon inline icon-16 mr-2 pt-015"
v-html="icons.whiteClock"
></span>
<span v-html="$t('nextFreeRebirth', {days: nextFreeRebirth})"></span>
</div>
</div>
<div
v-if="item.key === 'gem'"
class="d-flex justify-content-center align-items-center"
>
<div
v-if="gemsLeft > 0"
class="gems-left d-flex justify-content-center align-items-center"
>
<strong>{{ $t('monthlyGems') }} &nbsp;</strong>
{{ gemsLeft }} / {{ totalGems }} {{ $t('gemsRemaining') }}
</div>
<div
v-if="gemsLeft === 0"
class="out-of-gems-banner d-flex justify-content-center align-items-center"
>
<strong>{{ $t('monthlyGems') }} &nbsp;</strong>
{{ gemsLeft }} / {{ totalGems }} {{ $t('gemsRemaining') }}
</div>
</div>
<div
slot="modal-footer"
>
<span class="user-balance ml-3 my-auto">{{ $t('yourBalance') }}</span>
<balanceInfo
class="mr-3"
:currency-needed="getPriceClass()"
:amount-needed="item.value"
/>
</div>
</b-modal>
<purchaseConfirmModal />
</div>

View File

@@ -5,116 +5,116 @@
:hide-header="true"
@change="onChange($event)"
>
<span
v-if="withPin"
class="badge-dialog"
@click.prevent.stop="togglePinned()"
>
<pin-badge
:pinned="isPinned"
/>
</span>
<div class="dialog-close">
<close-icon @click="hideDialog()" />
</div>
<h2 class="text-center textCondensed">
{{ $t('questDetails') }}
</h2>
<div
v-if="item != null"
class="content"
>
<div class="inner-content">
<questDialogContent
:item="item"
:abbreviated="true"
<span
v-if="withPin"
class="badge-dialog"
@click.prevent.stop="togglePinned()"
>
<pin-badge
:pinned="isPinned"
/>
<div
v-if="item.addlNotes"
class="mx-4 mb-3"
>
{{ item.addlNotes }}
</div>
<quest-rewards :quest="item" />
<div
v-if="!item.locked"
class="purchase-amount"
>
<div class="item-cost">
<span
class="cost"
:class="priceType"
>
</span>
<div class="dialog-close">
<close-icon @click="hideDialog()" />
</div>
<h2 class="text-center textCondensed">
{{ $t('questDetails') }}
</h2>
<div
v-if="item != null"
class="content"
>
<div class="inner-content">
<questDialogContent
:item="item"
:abbreviated="true"
/>
<div
v-if="item.addlNotes"
class="mx-4 mb-3"
>
{{ item.addlNotes }}
</div>
<quest-rewards :quest="item" />
<div
v-if="!item.locked"
class="purchase-amount"
>
<div class="item-cost">
<span
class="svg-icon inline icon-24"
aria-hidden="true"
v-html="icons[priceType]"
class="cost"
:class="priceType"
>
<span
class="svg-icon inline icon-24"
aria-hidden="true"
v-html="icons[priceType]"
>
</span>
<span
:class="priceType"
>{{ item.value }}</span>
</span>
</div>
<div class="how-many-to-buy">
<strong>{{ $t('howManyToBuy') }}</strong>
</div>
<div>
<number-increment
@updateQuantity="selectedAmountToBuy = $event"
/>
</div>
<div class="total-row">
<span class="total-text">
{{ $t('sendTotal') }}
</span>
<span
class="svg-icon inline icon-20"
aria-hidden="true"
v-html="currencyIcon"
></span>
<span
class="total"
:class="priceType"
>{{ item.value }}</span>
</span>
</div>
<div class="how-many-to-buy">
<strong>{{ $t('howManyToBuy') }}</strong>
</div>
<div>
<number-increment
@updateQuantity="selectedAmountToBuy = $event"
/>
</div>
<div class="total-row">
<span class="total-text">
{{ $t('sendTotal') }}
</span>
<span
class="svg-icon inline icon-20"
aria-hidden="true"
v-html="currencyIcon"
></span>
<span
class="total"
:class="priceType"
>{{ item.value * selectedAmountToBuy }}</span>
>{{ item.value * selectedAmountToBuy }}</span>
</div>
</div>
<button
v-if="priceType === 'gems'
&& !enoughCurrency(priceType, item.value * selectedAmountToBuy)
&& !item.locked"
class="btn btn-primary mb-3"
@click="purchaseGems()"
>
{{ $t('purchaseGems') }}
</button>
<button
v-else
class="btn btn-primary mb-4"
:class="{'notEnough': !enoughCurrency(priceType, item.value * selectedAmountToBuy)}"
:disabled="numberInvalid"
@click="buyItem()"
>
{{ $t('buyNow') }}
</button>
</div>
<button
v-if="priceType === 'gems'
&& !enoughCurrency(priceType, item.value * selectedAmountToBuy)
&& !item.locked"
class="btn btn-primary mb-3"
@click="purchaseGems()"
>
{{ $t('purchaseGems') }}
</button>
<button
v-else
class="btn btn-primary mb-4"
:class="{'notEnough': !enoughCurrency(priceType, item.value * selectedAmountToBuy)}"
:disabled="numberInvalid"
@click="buyItem()"
>
{{ $t('buyNow') }}
</button>
</div>
</div>
<countdown-banner
v-if="item.end"
:end-date="endDate"
/>
<div
slot="modal-footer"
class="clearfix"
>
<span class="balance float-left">{{ $t('yourBalance') }}</span>
<balanceInfo
class="float-right"
:with-hourglass="priceType === 'hourglasses'"
:currency-needed="priceType"
:amount-needed="item.value"
<countdown-banner
v-if="item.end"
:end-date="endDate"
/>
</div>
<div
slot="modal-footer"
class="clearfix"
>
<span class="balance float-left">{{ $t('yourBalance') }}</span>
<balanceInfo
class="float-right"
:with-hourglass="priceType === 'hourglasses'"
:currency-needed="priceType"
:amount-needed="item.value"
/>
</div>
</b-modal>
<purchaseConfirmModal />
</div>

View File

@@ -64,9 +64,11 @@
<li>sexual orientation; and</li>
<li>information collected from a known child.</li>
</ul>
<p><strong>
NOTE: Please do not provide us sensitive personal information or sensitive personal data, as those terms are defined under applicable privacy laws, unless we directly request that you do so. If you feel, after careful consideration, that it is necessary to provide us certain sensitive personal information or data, please provide us the minimum amount of such information or data that is necessary.
</strong></p>
<p>
<strong>
NOTE: Please do not provide us sensitive personal information or sensitive personal data, as those terms are defined under applicable privacy laws, unless we directly request that you do so. If you feel, after careful consideration, that it is necessary to provide us certain sensitive personal information or data, please provide us the minimum amount of such information or data that is necessary.
</strong>
</p>
<h3 id="section_1_1">
1.1 Information You Provide Directly
</h3>
@@ -617,7 +619,7 @@
7. General Audience Services
</h2>
<p>
The Service is intended for users 18 years or older; you are not permitted to access or use the Service if you are younger than 18. We do not knowingly collect personal information from children under the age of 18 through the Service. We encourage parents and legal guardians to monitor their childrens Internet usage and to help enforce our Privacy Policy by instructing their children to never provide personal information without their permission. If you have reason to believe that a child under the age of 18 has provided personal information to us, please contact us at <a href='mailto:privacy@habitica.com'>privacy@habitica.com</a>, and we will delete that information from our databases.
The Service is intended for users 18 years or older; you are not permitted to access or use the Service if you are younger than 18. We do not knowingly collect personal information from children under the age of 18 through the Service. We encourage parents and legal guardians to monitor their childrens Internet usage and to help enforce our Privacy Policy by instructing their children to never provide personal information without their permission. If you have reason to believe that a child under the age of 18 has provided personal information to us, please contact us at <a href="mailto:privacy@habitica.com">privacy@habitica.com</a>, and we will delete that information from our databases.
</p>
<h2 id="section_8">
@@ -708,7 +710,7 @@
<p><strong><u>Nevada Residents</u></strong></p>
<p>
Nevada residents may opt out of the sale of certain “covered information” collected by operators of websites or online services. We currently do not sell covered information, as “sale” is defined by such law, and do not have plans to do so. In accordance with Nevada law, you may submit to us a verified request instructing us not to sell your covered information by sending an email to <a href='mailto:privacy@habitica.com'>privacy@habitica.com</a>.
Nevada residents may opt out of the sale of certain “covered information” collected by operators of websites or online services. We currently do not sell covered information, as “sale” is defined by such law, and do not have plans to do so. In accordance with Nevada law, you may submit to us a verified request instructing us not to sell your covered information by sending an email to <a href="mailto:privacy@habitica.com">privacy@habitica.com</a>.
</p>
<p><strong><u>Notice to United Kingdom/European/Switzerland Residents.</u></strong></p>
<p>

View File

@@ -15,8 +15,8 @@
<router-view />
</div>
<div
id="bottom-background"
v-if="loginFlow"
id="bottom-background"
class="bg-purple-300"
>
<div class="seamless_mountains_demo_repeat"></div>
@@ -31,7 +31,10 @@
id="bottom-wrap"
class="purple-4"
>
<div id="bottom-background" v-if="!loginFlow">
<div
v-if="!loginFlow"
id="bottom-background"
>
<div class="seamless_mountains_demo_repeat"></div>
<div class="midground_foreground_extended2"></div>
</div>

View File

@@ -158,7 +158,7 @@
BY PURCHASING PREMIUM YOU EXPRESSLY UNDERSTAND AND AGREE TO OUR REFUND POLICY:
</p>
<p>
YOU CAN REQUEST A REFUND OF YOUR MOST RECENT PAYMENT TO US BY CONTACTING US AT <a href='mailto:admin@habitica.com'>ADMIN@HABITICA.COM</a>. THE AMOUNT OF YOUR REFUND, IF ANY, WILL BE BASED ON (1) THE AMOUNT OF YOUR PURCHASED BUT UNUSED SUBSCRIPTION BENEFITS AND (2) THE TERMS IMPOSED ON US BY OUR PAYMENT PROCESSING VENDORS (E.G., WITH RESPECT TO THE DURATION OF THE REFUND PERIOD).
YOU CAN REQUEST A REFUND OF YOUR MOST RECENT PAYMENT TO US BY CONTACTING US AT <a href="mailto:admin@habitica.com">ADMIN@HABITICA.COM</a>. THE AMOUNT OF YOUR REFUND, IF ANY, WILL BE BASED ON (1) THE AMOUNT OF YOUR PURCHASED BUT UNUSED SUBSCRIPTION BENEFITS AND (2) THE TERMS IMPOSED ON US BY OUR PAYMENT PROCESSING VENDORS (E.G., WITH RESPECT TO THE DURATION OF THE REFUND PERIOD).
</p>
<p>
FOR ANY CUSTOMER WHO PURCHASED PREMIUM IN APPLE INC.'s APP STORE ("APP STORE"), PLEASE CONTACT APPLE INC.'s SUPPORT TEAM: <a

View File

@@ -5,66 +5,66 @@
title="Broken Challenge"
size="sm"
:hide-footer="true"
>
<div
v-if="brokenChallengeTask && brokenChallengeTask.challenge"
class="modal-body"
>
<div
v-if="brokenChallengeTask.challenge.broken === 'TASK_DELETED'
|| brokenChallengeTask.challenge.broken === 'CHALLENGE_TASK_NOT_FOUND'"
v-if="brokenChallengeTask && brokenChallengeTask.challenge"
class="modal-body"
>
<h2>{{ $t('brokenTask') }}</h2>
<div>
<button
class="btn btn-primary"
@click="unlink('keep')"
>
{{ $t('keepIt') }}
</button>
<button
class="btn btn-danger"
@click="removeTask(obj)"
>
{{ $t('removeIt') }}
</button>
<div
v-if="brokenChallengeTask.challenge.broken === 'TASK_DELETED'
|| brokenChallengeTask.challenge.broken === 'CHALLENGE_TASK_NOT_FOUND'"
>
<h2>{{ $t('brokenTask') }}</h2>
<div>
<button
class="btn btn-primary"
@click="unlink('keep')"
>
{{ $t('keepIt') }}
</button>
<button
class="btn btn-danger"
@click="removeTask(obj)"
>
{{ $t('removeIt') }}
</button>
</div>
</div>
<div v-if="brokenChallengeTask.challenge.broken === 'CHALLENGE_DELETED'">
<h2>{{ $t('brokenChallenge') }}</h2>
<div>
<button
class="btn btn-primary"
@click="unlink('keep-all')"
>
{{ $t('keepTasks') }}
</button>
<button
class="btn btn-danger"
@click="unlink('remove-all')"
>
{{ $t('removeTasks') }}
</button>
</div>
</div>
<div v-if="brokenChallengeTask.challenge.broken === 'CHALLENGE_CLOSED'">
<h2 v-html="$t('challengeCompleted', {user: brokenChallengeTask.challenge.winner})"></h2>
<div>
<button
class="btn btn-primary"
@click="unlink('keep-all')"
>
{{ $t('keepTasks') }}
</button>
<button
class="btn btn-danger"
@click="unlink('remove-all')"
>
{{ $t('removeTasks') }}
</button>
</div>
</div>
</div>
<div v-if="brokenChallengeTask.challenge.broken === 'CHALLENGE_DELETED'">
<h2>{{ $t('brokenChallenge') }}</h2>
<div>
<button
class="btn btn-primary"
@click="unlink('keep-all')"
>
{{ $t('keepTasks') }}
</button>
<button
class="btn btn-danger"
@click="unlink('remove-all')"
>
{{ $t('removeTasks') }}
</button>
</div>
</div>
<div v-if="brokenChallengeTask.challenge.broken === 'CHALLENGE_CLOSED'">
<h2 v-html="$t('challengeCompleted', {user: brokenChallengeTask.challenge.winner})"></h2>
<div>
<button
class="btn btn-primary"
@click="unlink('keep-all')"
>
{{ $t('keepTasks') }}
</button>
<button
class="btn btn-danger"
@click="unlink('remove-all')"
>
{{ $t('removeTasks') }}
</button>
</div>
</div>
</div>
</b-modal>
<deleteTaskConfirmModal />
</div>

View File

@@ -87,7 +87,7 @@
ref="tasksList"
class="sortable-tasks"
:disabled="activeFilter.label === 'scheduled' || !canBeDragged()"
scrollSensitivity="64"
scroll-sensitivity="64"
:delay-on-touch-only="true"
:delay="100"
@update="taskSorted"

View File

@@ -3,422 +3,422 @@
<div
class="task-wrapper"
draggable
>
<div
class="task transition"
:class="[{
'groupTask': task.group.id,
'task-not-editable': !teamManagerAccess,
'task-not-scoreable': showTaskLockIcon,
'link-exempt': !isChallengeTask && !isGroupTask,
}, `type_${task.type}`
]"
@click="castEnd($event, task)"
>
<div
class="d-flex"
:class="{'task-not-scoreable': showTaskLockIcon }"
class="task transition"
:class="[{
'groupTask': task.group.id,
'task-not-editable': !teamManagerAccess,
'task-not-scoreable': showTaskLockIcon,
'link-exempt': !isChallengeTask && !isGroupTask,
}, `type_${task.type}`
]"
@click="castEnd($event, task)"
>
<!-- Habits left side control-->
<div
v-if="task.type === 'habit'"
class="left-control d-flex justify-content-center pt-3"
:class="[{
'control-bottom-box': task.group.id && !isOpenTask,
'control-top-box': approvalsClass
}, controlClass.up.bg]"
class="d-flex"
:class="{'task-not-scoreable': showTaskLockIcon }"
>
<!-- Habits left side control-->
<div
class="task-control habit-control"
v-if="task.type === 'habit'"
class="left-control d-flex justify-content-center pt-3"
:class="[{
'habit-control-positive-enabled': task.up && !showTaskLockIcon,
'habit-control-positive-disabled': !task.up && !showTaskLockIcon,
'task-not-scoreable': showTaskLockIcon,
}, controlClass.up.inner]"
tabindex="0"
role="button"
:aria-label="$t('scoreUp')"
:aria-disabled="showTaskLockIcon || (!task.up && !showTaskLockIcon)"
@click="score('up')"
@keypress.enter="score('up')"
'control-bottom-box': task.group.id && !isOpenTask,
'control-top-box': approvalsClass
}, controlClass.up.bg]"
>
<div
v-if="showTaskLockIcon"
class="svg-icon lock"
:class="task.up ? controlClass.up.icon : 'positive'"
v-html="icons.lock"
></div>
<div
v-else
class="svg-icon positive"
v-html="icons.positive"
></div>
</div>
</div>
<!-- Dailies and todos left side control-->
<div
v-if="task.type === 'daily' || task.type === 'todo'"
class="left-control d-flex justify-content-center"
:class="[{
'control-bottom-box': task.group.id && !isOpenTask,
'control-top-box': approvalsClass}, controlClass.bg]"
>
<div
class="task-control daily-todo-control"
:class="[
{ 'task-not-scoreable': showTaskLockIcon },
controlClass.inner,
]"
tabindex="0"
role="checkbox"
@click="score(showCheckIcon ? 'down' : 'up' )"
@keypress.enter="score(showCheckIcon ? 'down' : 'up' )"
>
<div
v-if="showTaskLockIcon"
class="svg-icon lock"
:class="controlClass.icon"
v-html="icons.lock"
></div>
<div
v-else
class="svg-icon check"
:class="{
'display-check-icon': showCheckIcon,
[controlClass.checkbox]: true,
}"
v-html="icons.check"
></div>
</div>
</div>
<!-- Task title, description and icons-->
<div
class="task-content"
:class="contentClass"
>
<div
class="task-clickable-area pt-1 pl-75 pb-0"
:class="{ 'cursor-auto': !teamManagerAccess }"
tabindex="0"
@click="edit($event, task)"
@keypress.enter="edit($event, task)"
>
<div class="d-flex justify-content-between">
<h3
v-markdown="task.text"
class="task-title markdown"
:class="{ 'has-notes': task.notes }"
></h3>
<menu-dropdown
v-if="!isRunningYesterdailies && showOptions"
ref="taskDropdown"
v-b-tooltip.hover.top="$t('options')"
tabindex="0"
class="task-dropdown mr-1"
:right="task.type === 'reward'"
>
<div slot="dropdown-toggle">
<div
class="svg-icon dropdown-icon"
v-html="icons.menu"
></div>
</div>
<div slot="dropdown-content">
<div
v-if="showEdit"
ref="editTaskItem"
class="dropdown-item edit-task-item"
tabindex="0"
@keypress.enter="edit($event, task)"
>
<span class="dropdown-icon-item">
<span
class="svg-icon inline edit-icon"
v-html="icons.edit"
></span>
<span class="text">{{ $t('edit') }}</span>
</span>
</div>
<div
class="dropdown-item"
tabindex="0"
@click="moveToTop"
@keypress.enter="moveToTop"
>
<span class="dropdown-icon-item">
<span
class="svg-icon inline push-to-top"
v-html="icons.top"
></span>
<span class="text">{{ $t('taskToTop') }}</span>
</span>
</div>
<div
class="dropdown-item"
tabindex="0"
@click="moveToBottom"
@keypress.enter="moveToBottom"
>
<span class="dropdown-icon-item">
<span
class="svg-icon inline push-to-bottom"
v-html="icons.bottom"
></span>
<span class="text">{{ $t('taskToBottom') }}</span>
</span>
</div>
<div
v-if="showDelete"
class="dropdown-item"
tabindex="0"
@click="destroy"
@keypress.enter="destroy"
>
<span class="dropdown-icon-item delete-task-item">
<span
class="svg-icon inline delete"
v-html="icons.delete"
></span>
<span class="text">{{ $t('delete') }}</span>
</span>
</div>
</div>
</menu-dropdown>
</div>
<div
v-markdown="task.notes"
class="task-notes small-text"
:class="{'has-checklist': task.notes && hasChecklist}"
></div>
</div>
<div
v-if="canViewchecklist"
class="checklist"
:class="{isOpen: !task.collapseChecklist}"
>
<div class="d-inline-flex">
<div
v-b-tooltip.hover.right="$t(`${task.collapseChecklist
? 'expand': 'collapse'}Checklist`)"
class="collapse-checklist mb-2 d-flex align-items-center expand-toggle"
:class="{open: !task.collapseChecklist}"
tabindex="0"
@click="collapseChecklist(task)"
@keypress.enter="collapseChecklist(task)"
>
<div
v-once
class="svg-icon"
v-html="icons.checklist"
></div>
<span>{{ checklistProgress }}</span>
</div>
</div>
<!-- eslint-disable vue/no-use-v-if-with-v-for -->
<div
v-for="item in task.checklist"
v-if="!task.collapseChecklist"
:key="item.id"
class="custom-control custom-checkbox checklist-item"
:class="{'checklist-item-done': item.completed, 'cursor-auto': showTaskLockIcon}"
>
<!-- eslint-enable vue/no-use-v-if-with-v-for -->
<input
:id="`checklist-${item.id}-${random}`"
class="custom-control-input"
tabindex="0"
type="checkbox"
:checked="item.completed"
:disabled="castingSpell || showTaskLockIcon"
@change="toggleChecklistItem(item)"
@keypress.enter="toggleChecklistItem(item)"
>
<label
v-markdown="item.text"
class="custom-control-label"
:class="{ 'cursor-auto': showTaskLockIcon }"
:for="`checklist-${item.id}-${random}`"
></label>
</div>
</div>
<div class="icons small-text d-flex align-items-center">
<div
v-if="task.type === 'todo' && task.date"
class="d-flex align-items-center"
:class="{'due-overdue': checkIfOverdue() }"
class="task-control habit-control"
:class="[{
'habit-control-positive-enabled': task.up && !showTaskLockIcon,
'habit-control-positive-disabled': !task.up && !showTaskLockIcon,
'task-not-scoreable': showTaskLockIcon,
}, controlClass.up.inner]"
tabindex="0"
role="button"
:aria-label="$t('scoreUp')"
:aria-disabled="showTaskLockIcon || (!task.up && !showTaskLockIcon)"
@click="score('up')"
@keypress.enter="score('up')"
>
<div
v-b-tooltip.hover.bottom="$t('dueDate')"
class="svg-icon calendar my-auto"
v-html="icons.calendar"
v-if="showTaskLockIcon"
class="svg-icon lock"
:class="task.up ? controlClass.up.icon : 'positive'"
v-html="icons.lock"
></div>
<div
v-else
class="svg-icon positive"
v-html="icons.positive"
></div>
<span>{{ formatDueDate() }}</span>
</div>
<div class="icons-right d-flex justify-content-end">
</div>
<!-- Dailies and todos left side control-->
<div
v-if="task.type === 'daily' || task.type === 'todo'"
class="left-control d-flex justify-content-center"
:class="[{
'control-bottom-box': task.group.id && !isOpenTask,
'control-top-box': approvalsClass}, controlClass.bg]"
>
<div
class="task-control daily-todo-control"
:class="[
{ 'task-not-scoreable': showTaskLockIcon },
controlClass.inner,
]"
tabindex="0"
role="checkbox"
@click="score(showCheckIcon ? 'down' : 'up' )"
@keypress.enter="score(showCheckIcon ? 'down' : 'up' )"
>
<div
v-if="showStreak"
class="d-flex align-items-center"
>
<div
v-b-tooltip.hover.bottom="task.type === 'daily'
? $t('streakCounter') : $t('counter')"
class="svg-icon streak"
v-html="icons.streak"
></div>
<span v-if="task.type === 'daily'">{{ task.streak }}</span>
<span v-if="task.type === 'habit'">
<span
v-if="task.up && task.counterUp != 0 && task.down"
class="m-0"
>+{{ task.counterUp }}</span>
<span
v-else-if=" task.counterUp !=0 && task.counterDown ==0"
class="m-0"
>{{ task.counterUp }}</span>
<span
v-else-if="task.up"
class="m-0"
>0</span>
<span
v-if="task.up && task.down"
class="m-0"
>&nbsp;|&nbsp;</span>
<span
v-if="task.down && task.counterDown != 0 && task.up"
class="m-0"
>-{{ task.counterDown }}</span>
<span
v-else-if="task.counterDown !=0 && task.counterUp ==0"
class="m-0"
>{{ task.counterDown }}</span>
<span
v-else-if="task.down"
class="m-0"
>0</span>
</span>
</div>
v-if="showTaskLockIcon"
class="svg-icon lock"
:class="controlClass.icon"
v-html="icons.lock"
></div>
<div
v-if="task.challenge && task.challenge.id"
class="d-flex align-items-center"
>
<div
v-if="!task.challenge.broken"
v-b-tooltip.hover.bottom="shortName"
class="svg-icon challenge"
v-html="icons.challenge"
></div>
<div
v-if="task.challenge.broken"
v-b-tooltip.hover.bottom="$t('brokenChaLink')"
class="svg-icon challenge broken"
@click="handleBrokenTask(task)"
v-html="icons.brokenChallengeIcon"
></div>
</div>
<div
v-if="hasTags && !task.group.id"
:id="`tags-icon-${task._id}`"
class="d-flex align-items-center"
>
<div
class="svg-icon tags"
v-html="icons.tags"
></div>
</div>
<b-popover
v-if="hasTags && !task.group.id"
:target="`tags-icon-${task._id}`"
triggers="hover"
placement="bottom"
>
<div class="tags-popover">
<div class="d-flex align-items-center tags-container">
v-else
class="svg-icon check"
:class="{
'display-check-icon': showCheckIcon,
[controlClass.checkbox]: true,
}"
v-html="icons.check"
></div>
</div>
</div>
<!-- Task title, description and icons-->
<div
class="task-content"
:class="contentClass"
>
<div
class="task-clickable-area pt-1 pl-75 pb-0"
:class="{ 'cursor-auto': !teamManagerAccess }"
tabindex="0"
@click="edit($event, task)"
@keypress.enter="edit($event, task)"
>
<div class="d-flex justify-content-between">
<h3
v-markdown="task.text"
class="task-title markdown"
:class="{ 'has-notes': task.notes }"
></h3>
<menu-dropdown
v-if="!isRunningYesterdailies && showOptions"
ref="taskDropdown"
v-b-tooltip.hover.top="$t('options')"
tabindex="0"
class="task-dropdown mr-1"
:right="task.type === 'reward'"
>
<div slot="dropdown-toggle">
<div
v-once
class="tags-popover-title"
>
{{ `${$t('tags')}:` }}
</div>
<div
v-for="tag in getTagsFor(task)"
:key="tag"
v-markdown="tag"
class="tag-label"
class="svg-icon dropdown-icon"
v-html="icons.menu"
></div>
</div>
<div slot="dropdown-content">
<div
v-if="showEdit"
ref="editTaskItem"
class="dropdown-item edit-task-item"
tabindex="0"
@keypress.enter="edit($event, task)"
>
<span class="dropdown-icon-item">
<span
class="svg-icon inline edit-icon"
v-html="icons.edit"
></span>
<span class="text">{{ $t('edit') }}</span>
</span>
</div>
<div
class="dropdown-item"
tabindex="0"
@click="moveToTop"
@keypress.enter="moveToTop"
>
<span class="dropdown-icon-item">
<span
class="svg-icon inline push-to-top"
v-html="icons.top"
></span>
<span class="text">{{ $t('taskToTop') }}</span>
</span>
</div>
<div
class="dropdown-item"
tabindex="0"
@click="moveToBottom"
@keypress.enter="moveToBottom"
>
<span class="dropdown-icon-item">
<span
class="svg-icon inline push-to-bottom"
v-html="icons.bottom"
></span>
<span class="text">{{ $t('taskToBottom') }}</span>
</span>
</div>
<div
v-if="showDelete"
class="dropdown-item"
tabindex="0"
@click="destroy"
@keypress.enter="destroy"
>
<span class="dropdown-icon-item delete-task-item">
<span
class="svg-icon inline delete"
v-html="icons.delete"
></span>
<span class="text">{{ $t('delete') }}</span>
</span>
</div>
</div>
</menu-dropdown>
</div>
<div
v-markdown="task.notes"
class="task-notes small-text"
:class="{'has-checklist': task.notes && hasChecklist}"
></div>
</div>
<div
v-if="canViewchecklist"
class="checklist"
:class="{isOpen: !task.collapseChecklist}"
>
<div class="d-inline-flex">
<div
v-b-tooltip.hover.right="$t(`${task.collapseChecklist
? 'expand': 'collapse'}Checklist`)"
class="collapse-checklist mb-2 d-flex align-items-center expand-toggle"
:class="{open: !task.collapseChecklist}"
tabindex="0"
@click="collapseChecklist(task)"
@keypress.enter="collapseChecklist(task)"
>
<div
v-once
class="svg-icon"
v-html="icons.checklist"
></div>
<span>{{ checklistProgress }}</span>
</div>
</b-popover>
</div>
<!-- eslint-disable vue/no-use-v-if-with-v-for -->
<div
v-for="item in task.checklist"
v-if="!task.collapseChecklist"
:key="item.id"
class="custom-control custom-checkbox checklist-item"
:class="{'checklist-item-done': item.completed, 'cursor-auto': showTaskLockIcon}"
>
<!-- eslint-enable vue/no-use-v-if-with-v-for -->
<input
:id="`checklist-${item.id}-${random}`"
class="custom-control-input"
tabindex="0"
type="checkbox"
:checked="item.completed"
:disabled="castingSpell || showTaskLockIcon"
@change="toggleChecklistItem(item)"
@keypress.enter="toggleChecklistItem(item)"
>
<label
v-markdown="item.text"
class="custom-control-label"
:class="{ 'cursor-auto': showTaskLockIcon }"
:for="`checklist-${item.id}-${random}`"
></label>
</div>
</div>
<div class="icons small-text d-flex align-items-center">
<div
v-if="task.type === 'todo' && task.date"
class="d-flex align-items-center"
:class="{'due-overdue': checkIfOverdue() }"
>
<div
v-b-tooltip.hover.bottom="$t('dueDate')"
class="svg-icon calendar my-auto"
v-html="icons.calendar"
></div>
<span>{{ formatDueDate() }}</span>
</div>
<div class="icons-right d-flex justify-content-end">
<div
v-if="showStreak"
class="d-flex align-items-center"
>
<div
v-b-tooltip.hover.bottom="task.type === 'daily'
? $t('streakCounter') : $t('counter')"
class="svg-icon streak"
v-html="icons.streak"
></div>
<span v-if="task.type === 'daily'">{{ task.streak }}</span>
<span v-if="task.type === 'habit'">
<span
v-if="task.up && task.counterUp != 0 && task.down"
class="m-0"
>+{{ task.counterUp }}</span>
<span
v-else-if=" task.counterUp !=0 && task.counterDown ==0"
class="m-0"
>{{ task.counterUp }}</span>
<span
v-else-if="task.up"
class="m-0"
>0</span>
<span
v-if="task.up && task.down"
class="m-0"
>&nbsp;|&nbsp;</span>
<span
v-if="task.down && task.counterDown != 0 && task.up"
class="m-0"
>-{{ task.counterDown }}</span>
<span
v-else-if="task.counterDown !=0 && task.counterUp ==0"
class="m-0"
>{{ task.counterDown }}</span>
<span
v-else-if="task.down"
class="m-0"
>0</span>
</span>
</div>
<div
v-if="task.challenge && task.challenge.id"
class="d-flex align-items-center"
>
<div
v-if="!task.challenge.broken"
v-b-tooltip.hover.bottom="shortName"
class="svg-icon challenge"
v-html="icons.challenge"
></div>
<div
v-if="task.challenge.broken"
v-b-tooltip.hover.bottom="$t('brokenChaLink')"
class="svg-icon challenge broken"
@click="handleBrokenTask(task)"
v-html="icons.brokenChallengeIcon"
></div>
</div>
<div
v-if="hasTags && !task.group.id"
:id="`tags-icon-${task._id}`"
class="d-flex align-items-center"
>
<div
class="svg-icon tags"
v-html="icons.tags"
></div>
</div>
<b-popover
v-if="hasTags && !task.group.id"
:target="`tags-icon-${task._id}`"
triggers="hover"
placement="bottom"
>
<div class="tags-popover">
<div class="d-flex align-items-center tags-container">
<div
v-once
class="tags-popover-title"
>
{{ `${$t('tags')}:` }}
</div>
<div
v-for="tag in getTagsFor(task)"
:key="tag"
v-markdown="tag"
class="tag-label"
></div>
</div>
</div>
</b-popover>
</div>
</div>
</div>
</div>
<!-- Habits right side control-->
<div
v-if="task.type === 'habit'"
class="right-control d-flex justify-content-center pt-3"
:class="[{
'control-bottom-box': task.group.id && !isOpenTask,
'control-top-box': approvalsClass}, controlClass.down.bg]"
>
<!-- Habits right side control-->
<div
class="task-control habit-control"
v-if="task.type === 'habit'"
class="right-control d-flex justify-content-center pt-3"
:class="[{
'habit-control-negative-enabled': task.down && !showTaskLockIcon,
'habit-control-negative-disabled': !task.down && !showTaskLockIcon,
'task-not-scoreable': showTaskLockIcon,
}, controlClass.down.inner]"
'control-bottom-box': task.group.id && !isOpenTask,
'control-top-box': approvalsClass}, controlClass.down.bg]"
>
<div
class="task-control habit-control"
:class="[{
'habit-control-negative-enabled': task.down && !showTaskLockIcon,
'habit-control-negative-disabled': !task.down && !showTaskLockIcon,
'task-not-scoreable': showTaskLockIcon,
}, controlClass.down.inner]"
tabindex="0"
role="button"
:aria-label="$t('scoreDown')"
:aria-disabled="showTaskLockIcon || (!task.down && !showTaskLockIcon)"
@click="score('down')"
@keypress.enter="score('down')"
>
<div
v-if="showTaskLockIcon"
class="svg-icon lock"
:class="task.down ? controlClass.down.icon : 'negative'"
v-html="icons.lock"
></div>
<div
v-else
class="svg-icon negative"
v-html="icons.negative"
></div>
</div>
</div>
<!-- Rewards right side control-->
<div
v-if="task.type === 'reward'"
class="right-control d-flex align-items-center justify-content-center reward-control"
:class="[
{ 'task-not-scoreable': showTaskLockIcon },
controlClass.bg,
]"
tabindex="0"
role="button"
:aria-label="$t('scoreDown')"
:aria-disabled="showTaskLockIcon || (!task.down && !showTaskLockIcon)"
@click="score('down')"
@keypress.enter="score('down')"
>
<div
v-if="showTaskLockIcon"
class="svg-icon lock"
:class="task.down ? controlClass.down.icon : 'negative'"
class="svg-icon color lock"
v-html="icons.lock"
></div>
<div
v-else
class="svg-icon negative"
v-html="icons.negative"
class="svg-icon mb-1"
v-html="icons.gold"
></div>
<div class="small-text">
{{ task.value }}
</div>
</div>
</div>
<!-- Rewards right side control-->
<div
v-if="task.type === 'reward'"
class="right-control d-flex align-items-center justify-content-center reward-control"
:class="[
{ 'task-not-scoreable': showTaskLockIcon },
controlClass.bg,
]"
tabindex="0"
@click="score('down')"
@keypress.enter="score('down')"
>
<div
v-if="showTaskLockIcon"
class="svg-icon color lock"
v-html="icons.lock"
></div>
<div
v-else
class="svg-icon mb-1"
v-html="icons.gold"
></div>
<div class="small-text">
{{ task.value }}
</div>
</div>
<approval-footer
v-if="task.group.id && !isOpenTask"
:task="task"
:group="group"
/>
</div>
<approval-footer
v-if="task.group.id && !isOpenTask"
:task="task"
:group="group"
/>
</div>
</div>
<deleteTaskConfirmModal />
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -25,8 +25,8 @@
type="checkbox"
:checked="isChecked"
:value="value"
@change="handleChange"
:disabled="disabled"
@change="handleChange"
>
<label
class="toggle-switch-label"

View File

@@ -1,7 +1,8 @@
<template>
<tr>
<td colspan="3"
<td
v-if="!mixinData.inlineSettingMixin.modalVisible"
colspan="3"
>
<div class="d-flex justify-content-between align-items-center">
<h3
@@ -18,8 +19,9 @@
</a>
</div>
</td>
<td colspan="3"
<td
v-if="mixinData.inlineSettingMixin.modalVisible"
colspan="3"
>
<h3
v-once
@@ -59,8 +61,8 @@
{{ $t('performanceAnalytics') }}
</label>
<toggle-switch
class="mb-auto"
v-model="user.preferences.analyticsConsent"
class="mb-auto"
@change="prefToggled()"
/>
</div>
@@ -151,14 +153,14 @@ import { mapState } from '@/libs/store';
import alert from '@/assets/svg/for-css/alert.svg?raw';
export default {
mixins: [
GenericUserPreferencesMixin,
InlineSettingMixin,
],
components: {
SaveCancelButtons,
ToggleSwitch,
},
mixins: [
GenericUserPreferencesMixin,
InlineSettingMixin,
],
data () {
return {
icons: Object.freeze({