Squashed commit of the following:

commit 04fbddfd9a
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 30 16:55:12 2024 -0500

    fix(groups): remove outdated group FAQ modal

commit a7ffdc9593
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 30 16:34:03 2024 -0500

    fix(groups): don't spawn Justin during Groups onboarding

commit c8205de6c7
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 30 16:03:03 2024 -0500

    fix(groups): correct static page account creation flow

commit 700718bd54
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 23 17:47:28 2024 -0500

    chore(payments): start retiring Amazon Payments

commit 0df75b771a
Author: Sabe Jones <sabe@habitica.com>
Date:   Tue Aug 20 10:34:28 2024 -0500

    fix(groups): don't use DO NOT USE modal

commit aed7ff5f47
Author: Sabe Jones <sabe@habitica.com>
Date:   Mon Aug 19 19:40:46 2024 -0500

    refactor(groups): rearrange some CSS for better semantics

commit fd743265cf
Author: Sabe Jones <sabe@habitica.com>
Date:   Fri Aug 16 18:11:47 2024 -0500

    fix(groups): add missing upgrade workflow pieces

commit ae4469703d
Author: Sabe Jones <sabe@habitica.com>
Date:   Thu Aug 15 10:32:36 2024 -0500

    WIP(groups): style and HTML corrections
    Also workflows for static and non-upgrade logged-in scenarios

commit c6a468dabc
Author: Sabe Jones <sabe@habitica.com>
Date:   Tue Aug 13 10:58:43 2024 -0500

    WIP(groups): refactored and revised landing designs
This commit is contained in:
Sabe Jones
2024-09-11 15:02:10 -05:00
parent 9934e59629
commit 91b47e56ff
29 changed files with 304 additions and 1367 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 B

View File

@@ -78,3 +78,15 @@ $gold-color: #FFA624;
$hourglass-color: #2995CD;
$purple-task: #925cf3;
.gray-200 {
color: $gray-200 !important;
}
.purple-300 {
color: $purple-300 !important;
}
.white {
color: $white !important;
}

View File

@@ -1,7 +1,6 @@
<template>
<div>
<buy-gems-modal v-if="user" />
<!--modify-inventory(v-if="isUserLoaded")-->
<footer>
<!-- Product -->
<div class="product">
@@ -22,7 +21,7 @@
</a>
</li>
<li>
<router-link to="/group-plans">
<router-link :to="user ? '/group-plans' : '/static/group-plans'">
{{ $t('groupPlans') }}
</router-link>
</li>

View File

@@ -1,214 +0,0 @@
<!-- THIS IS A VERY OLD FILE DO NOT USE -->
<template>
<div class="create-group-modal-pages">
<div
v-if="activePage === PAGES.CREATE_GROUP"
class="col-12"
>
<h2>{{ $t('nameYourGroup') }}</h2>
<div class="form-group">
<label
class="control-label"
for="new-group-name"
>{{ $t('name') }}</label>
<input
id="new-group-name"
v-model="newGroup.name"
class="form-control input-medium option-content"
required="required"
type="text"
:placeholder="$t('exampleGroupName')"
>
</div>
<div class="form-group">
<label for="new-group-description">{{ $t('description') }}</label>
<textarea
id="new-group-description"
v-model="newGroup.description"
class="form-control option-content"
cols="3"
:placeholder="$t('exampleGroupDesc')"
></textarea>
</div>
<div
v-if="newGroup.type === 'guild'"
class="form-group text-left"
>
<div class="custom-control custom-radio">
<input
v-model="newGroup.privacy"
class="custom-control-input"
type="radio"
name="new-group-privacy"
value="private"
>
<label class="custom-control-label">{{ $t('thisGroupInviteOnly') }}</label>
</div>
</div>
<div class="form-group text-left">
<div class="custom-control custom-checkbox">
<input
id="create-group-leaderOnlyChallenges-checkbox"
v-model="newGroup.leaderOnly.challenges"
class="custom-control-input"
type="checkbox"
>
<label
class="custom-control-label"
for="create-group-leaderOnlyChallenges-checkbox"
>{{ $t('leaderOnlyChallenges') }}</label>
</div>
</div>
<div
v-if="newGroup.type === 'party'"
class="form-group"
>
<button
class="btn btn-secondary form-control"
:value="$t('create')"
@click="createGroup()"
></button>
</div>
<div class="form-group">
<button
class="btn btn-primary btn-lg btn-block"
:disabled="!newGroupIsReady"
@click="createGroup()"
>
{{ $t('create') }}
</button>
</div>
</div>
<div
v-if="activePage === PAGES.PAY"
class="col-12"
>
<h2>{{ $t('choosePaymentMethod') }}</h2>
<payments-buttons
:stripe-fn="() => pay(PAYMENTS.STRIPE)"
:amazon-data="pay(PAYMENTS.AMAZON)"
/>
</div>
</div>
</template>
<style lang="scss" scoped>
h2 {
font-family: 'Varela Round', sans-serif;
font-weight: normal;
font-size: 29px;
color: #34313a;
margin-top: 1em;
}
.box {
border-radius: 2px;
background-color: #ffffff;
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
padding: 2em;
text-align: center;
vertical-align: bottom;
height: 100px;
width: 306px;
margin: 0 auto;
margin-bottom: 1em;
}
.box .svg-icon {
margin: 0 auto;
}
.form-group {
text-align: left;
font-weight: bold;
}
.custom-control-input {
z-index: -1;
opacity: 0;
}
.box:hover {
cursor: pointer;
opacity: 0.7;
}
.btn-block {
margin-bottom: 1em;
}
</style>
<script>
import { mapState } from '@/libs/store';
import paymentsMixin from '../../mixins/payments';
import paymentsButtons from '@/components/payments/buttons/list';
export default {
components: {
paymentsButtons,
},
mixins: [paymentsMixin],
data () {
return {
amazonPayments: {},
PAGES: {
CREATE_GROUP: 'create-group',
UPGRADE_GROUP: 'upgrade-group',
PAY: 'pay',
},
PAYMENTS: {
AMAZON: 'amazon',
STRIPE: 'stripe',
},
activePage: 'create-group',
newGroup: {
type: 'guild',
privacy: 'private',
name: '',
leaderOnly: {
challenges: false,
},
},
};
},
computed: {
...mapState({ user: 'user.data' }),
newGroupIsReady () {
return Boolean(this.newGroup.name);
},
},
methods: {
changePage (page) {
this.activePage = page;
window.scrollTo(0, 0);
},
createGroup () {
this.changePage(this.PAGES.PAY);
},
pay (paymentMethod) {
const subscriptionKey = 'group_monthly';
const paymentData = {
subscription: subscriptionKey,
coupon: null,
};
if (this.upgradingGroup && this.upgradingGroup._id) {
paymentData.groupId = this.upgradingGroup._id;
paymentData.group = this.upgradingGroup;
} else {
paymentData.groupToCreate = this.newGroup;
}
this.paymentMethod = paymentMethod;
if (this.paymentMethod === this.PAYMENTS.STRIPE) {
this.redirectToStripe(paymentData);
} else if (this.paymentMethod === this.PAYMENTS.AMAZON) {
paymentData.type = 'subscription';
return paymentData;
}
return null;
},
},
};
</script>

View File

@@ -1,16 +1,13 @@
<template>
<b-modal
id="create-group"
:title="activePage === PAGES.CREATE_GROUP ? 'Create your Group' : 'Select Payment'"
:title="$t('createGroupTitle')"
:hide-footer="true"
:hide-header="true"
size="md"
@hide="onHide()"
>
<div
v-if="activePage === PAGES.CREATE_GROUP"
class="col-12"
>
<div class="col-12">
<!-- HEADER -->
<div
class="modal-close"
@@ -25,7 +22,7 @@
class="btn btn-primary next-button"
:value="$t('next')"
:disabled="!newGroupIsReady"
@click="createGroup()"
@click="stripeGroup({ group: newGroup })"
>
{{ $t('next') }}
</button>
@@ -101,25 +98,12 @@
<button
class="btn btn-primary btn-lg btn-block btn-payment"
:disabled="!newGroupIsReady"
@click="createGroup()"
@click="stripeGroup({ group: newGroup })"
>
{{ $t('nextPaymentMethod') }}
</button>
</div>
</div>
<!-- PAYMENT -->
<!-- @TODO: Separate payment into a separate modal -->
<div
v-if="activePage === PAGES.PAY"
class="col-12 payments"
>
<div class="text-center">
<payments-buttons
:stripe-fn="() => pay(PAYMENTS.STRIPE)"
:amazon-data="pay(PAYMENTS.AMAZON)"
/>
</div>
</div>
</b-modal>
</template>
@@ -195,9 +179,6 @@
width: 200px;
height: 215px;
.dollar {
}
.number {
font-size: 60px;
}
@@ -248,31 +229,17 @@
<script>
import paymentsMixin from '../../mixins/payments';
import { mapState } from '@/libs/store';
import paymentsButtons from '@/components/payments/buttons/list';
import selectTranslatedArray from '@/components/tasks/modal-controls/selectTranslatedArray';
import lockableLabel from '@/components/tasks/modal-controls/lockableLabel';
import * as Analytics from '@/libs/analytics';
export default {
components: {
paymentsButtons,
selectTranslatedArray,
lockableLabel,
},
mixins: [paymentsMixin],
data () {
return {
amazonPayments: {},
PAGES: {
CREATE_GROUP: 'create-group',
// UPGRADE_GROUP: 'upgrade-group',
PAY: 'pay',
},
PAYMENTS: {
AMAZON: 'amazon',
STRIPE: 'stripe',
},
paymentMethod: '',
newGroup: {
type: 'guild',
privacy: 'private',
@@ -284,7 +251,6 @@ export default {
demographics: null,
user: '',
},
activePage: 'create-group',
type: 'guild',
};
},
@@ -302,55 +268,9 @@ export default {
close () {
this.$root.$emit('bv::hide::modal', 'create-group');
},
changePage (page) {
this.activePage = page;
},
createGroup () {
this.changePage(this.PAGES.PAY);
},
pay (paymentMethod) {
const subscriptionKey = 'group_monthly'; // @TODO: Get from content API?
const demographicsKey = this.newGroup.demographics;
const paymentData = {
subscription: subscriptionKey,
coupon: null,
demographics: demographicsKey,
};
Analytics.track({
hitType: 'event',
eventName: 'group plan create',
eventAction: 'group plan create',
eventCategory: 'behavior',
demographics: this.newGroup.demographics,
type: this.newGroup.type,
}, { trackOnClient: true });
if (this.upgradingGroup && this.upgradingGroup._id) {
paymentData.groupId = this.upgradingGroup._id;
paymentData.group = this.upgradingGroup;
} else {
paymentData.groupToCreate = this.newGroup;
}
this.paymentMethod = paymentMethod;
if (this.paymentMethod === this.PAYMENTS.AMAZON) {
paymentData.type = 'subscription';
return paymentData;
}
if (this.paymentMethod === this.PAYMENTS.STRIPE) {
this.redirectToStripe(paymentData);
}
return null;
},
onHide () {
this.sendingInProgress = false;
},
},
};
</script>

View File

@@ -1,377 +0,0 @@
<template>
<b-modal
id="group-plan-overview"
title="Empty"
size="lg"
hide-footer="hide-footer"
>
<div
slot="modal-header"
class="header-wrap text-center"
>
<h2 v-once>
{{ $t('gettingStarted') }}
</h2>
<p v-once>
{{ $t('congratsOnGroupPlan') }}
</p>
</div>
<div class="row">
<div class="col-12">
<div
class="card"
:class="{expanded: expandedQuestions.question1}"
>
<div class="question-head">
<div class="q">
Q.
</div>
<div class="title">
{{ $t('whatsIncludedGroup') }}
</div>
<div
class="arrow float-right"
@click="toggle('question1')"
>
<div
v-if="expandedQuestions.question1"
class="svg-icon"
v-html="icons.upIcon"
></div>
<div
v-else
class="svg-icon"
v-html="icons.downIcon"
></div>
</div>
</div>
<div
v-if="expandedQuestions.question1"
class="question-body"
>
<p>{{ $t('whatsIncludedGroupDesc') }}</p>
</div>
</div>
</div>
<div class="col-12">
<div
class="card"
:class="{expanded: expandedQuestions.question2}"
>
<div class="question-head">
<div class="q">
Q.
</div>
<div class="title">
{{ $t('howDoesBillingWork') }}
</div>
<div
class="arrow float-right"
@click="toggle('question2')"
>
<div
v-if="expandedQuestions.question2"
class="svg-icon"
v-html="icons.upIcon"
></div>
<div
v-else
class="svg-icon"
v-html="icons.downIcon"
></div>
</div>
</div>
<div
v-if="expandedQuestions.question2"
class="question-body"
>
<p>{{ $t('howDoesBillingWorkDesc') }}</p>
</div>
</div>
</div>
<div class="col-12">
<div
class="card"
:class="{expanded: expandedQuestions.question3}"
>
<div class="question-head">
<div class="q">
Q.
</div>
<div class="title">
{{ $t('howToAssignTask') }}
</div>
<div
class="arrow float-right"
@click="toggle('question3')"
>
<div
v-if="expandedQuestions.question3"
class="svg-icon"
v-html="icons.upIcon"
></div>
<div
v-else
class="svg-icon"
v-html="icons.downIcon"
></div>
</div>
</div>
<div
v-if="expandedQuestions.question3"
class="question-body"
>
<p>{{ $t('howToAssignTaskDesc') }}</p>
<div class="assign-tasks image-example"></div>
</div>
</div>
</div>
<div class="col-12">
<div
class="card"
:class="{expanded: expandedQuestions.question4}"
>
<div class="question-head">
<div class="q">
Q.
</div>
<div class="title">
{{ $t('howToRequireApproval') }}
</div>
<div
class="arrow float-right"
@click="toggle('question4')"
>
<div
v-if="expandedQuestions.question4"
class="svg-icon"
v-html="icons.upIcon"
></div>
<div
v-else
class="svg-icon"
v-html="icons.downIcon"
></div>
</div>
</div>
<div
v-if="expandedQuestions.question4"
class="question-body"
>
<p>{{ $t('howToRequireApprovalDesc') }}</p>
<div class="requires-approval image-example"></div>
<p>{{ $t('howToRequireApprovalDesc2') }}</p>
<div class="approval-requested image-example"></div>
</div>
</div>
</div>
<div class="col-12">
<div
class="card"
:class="{expanded: expandedQuestions.question5}"
>
<div class="question-head">
<div class="q">
Q.
</div>
<div class="title">
{{ $t('whatIsGroupManager') }}
</div>
<div
class="arrow float-right"
@click="toggle('question5')"
>
<div
v-if="expandedQuestions.question5"
class="svg-icon"
v-html="icons.upIcon"
></div>
<div
v-else
class="svg-icon"
v-html="icons.downIcon"
></div>
</div>
</div>
<div
v-if="expandedQuestions.question5"
class="question-body"
>
<p>{{ $t('whatIsGroupManagerDesc') }}</p>
<div class="promote-leader image-example"></div>
</div>
</div>
</div>
<div class="col-12 text-center">
<button
class="btn btn-primary close-button"
@click="close()"
>
{{ $t('goToTaskBoard') }}
</button>
</div>
</div>
</b-modal>
</template>
<style>
#group-plan-overview___BV_modal_header_ {
border-bottom: none;
}
</style>
<style lang="scss" scoped>
@import url('https://fonts.googleapis.com/css?family=Varela+Round');
.header-wrap {
padding-left: 4em;
padding-right: 4em;
h2 {
font-size: 32px;
font-weight: bold;
margin-top: 1em;
}
p {
color: #878190;
font-size: 16px;
}
}
.row {
margin-bottom: 2em;
.col-12 {
margin-bottom: .5em;
}
}
.card.expanded {
padding-bottom: 1em;
.title {
color: #4f2a93;
}
}
.card {
min-height: 60px;
border-radius: 4px;
background-color: #ffffff;
border: none;
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
.question-head {
.q {
font-family: 'Varela Round', sans-serif;
font-size: 20px;
color: #a5a1ac;
margin: 1em;
}
.title {
font-weight: normal;
}
div {
display: inline-block;
}
.arrow {
margin: 1em;
padding-top: .9em;
.svg-icon {
width: 26px;
height: 16px;
}
}
.arrow:hover {
cursor: pointer;
}
}
.question-body {
padding-left: 4.4em;
padding-right: 4em;
p {
color: #4e4a57;
}
}
}
.image-example {
background-repeat: no-repeat;
margin: 0 auto;
background-position: center;
background-size: contain;
}
.assign-tasks {
background-image: url('~@/assets/images/group-plans/assign-task@3x.png');
width: 400px;
height: 150px;
}
.requires-approval {
background-image: url('~@/assets/images/group-plans/requires-approval@3x.png');
width: 402px;
height: 20px;
margin-bottom: 1em;
}
.approval-requested {
background-image: url('~@/assets/images/group-plans/approval-requested@3x.png');
width: 471px;
height: 204px;
}
.promote-leader {
background-image: url('~@/assets/images/group-plans/promote-leader@3x.png');
width: 423px;
height: 185px;
}
.close-button {
margin-top: 1em;
}
</style>
<script>
import { mapState } from '@/libs/store';
import upIcon from '@/assets/svg/up.svg';
import downIcon from '@/assets/svg/down.svg';
export default {
data () {
return {
icons: Object.freeze({
upIcon,
downIcon,
}),
expandedQuestions: {
question1: false,
question2: false,
question3: false,
question4: false,
question5: false,
},
};
},
computed: {
...mapState({ user: 'user.data' }),
},
methods: {
toggle (question) {
this.expandedQuestions[question] = !this.expandedQuestions[question];
},
close () {
this.$root.$emit('bv::hide::modal', 'group-plan-overview');
},
},
};
</script>

View File

@@ -3,7 +3,6 @@
class="standard-page"
@click="openCreateBtn ? openCreateBtn = false : null"
>
<group-plan-overview-modal />
<task-modal
ref="taskModal"
:task="workingTask"
@@ -187,7 +186,6 @@ import taskDefaults from '@/../../common/script/libs/taskDefaults';
import TaskColumn from '../tasks/column';
import TaskModal from '../tasks/taskModal';
import TaskSummary from '../tasks/taskSummary';
import GroupPlanOverviewModal from './groupPlanOverviewModal';
import toggleSwitch from '@/components/ui/toggleSwitch';
import sync from '../../mixins/sync';
@@ -208,7 +206,6 @@ export default {
TaskColumn,
TaskModal,
TaskSummary,
GroupPlanOverviewModal,
toggleSwitch,
},
mixins: [sync],
@@ -309,10 +306,6 @@ export default {
if (!this.searchId) this.searchId = this.groupId;
this.load();
if (this.$route.query.showGroupOverview) {
this.$root.$emit('bv::show::modal', 'group-plan-overview');
}
this.$root.$on('habitica:team-sync', () => {
this.loadTasks();
this.loadGroupCompletedTodos();

View File

@@ -1,465 +0,0 @@
<template>
<!-- @TODO: Move to group plans folder-->
<div>
<group-plan-creation-modal />
<div>
<div class="header">
<h1
v-once
class="text-center"
>
{{ $t('groupPlanTitle') }}
</h1>
<div class="row">
<div class="col-8 offset-2 text-center">
<h2
v-once
class="sub-text"
>
{{ $t('groupBenefitsDescription') }}
</h2>
</div>
</div>
</div>
<div class="container benefits">
<div class="row">
<div class="col-4">
<div class="box">
<img
class="box1"
src="~@/assets/images/group-plans/group-14@3x.png"
>
<hr>
<h2 v-once>
{{ $t('teamBasedTasks') }}
</h2>
<p v-once>
{{ $t('teamBasedTasksListDesc') }}
</p>
</div>
</div>
<div class="col-4">
<div class="box">
<img
class="box2"
src="~@/assets/images/group-plans/group-12@3x.png"
>
<hr>
<h2 v-once>
{{ $t('groupManagementControls') }}
</h2>
<p v-once>
{{ $t('groupManagementControlsDesc') }}
</p>
</div>
</div>
<div class="col-4">
<div class="box">
<img
class="box3"
src="~@/assets/images/group-plans/group-13@3x.png"
>
<hr>
<h2 v-once>
{{ $t('inGameBenefits') }}
</h2>
<p v-once>
{{ $t('inGameBenefitsDesc') }}
</p>
</div>
</div>
</div>
</div>
<!-- Upgrading an existing group -->
<div
v-if="upgradingGroup._id"
id="upgrading-group"
class="container payment-options"
>
<h1 class="text-center purple-header">
Are you ready to upgrade?
</h1>
<div class="row">
<div class="col-12 text-center mb-4 d-flex justify-content-center">
<div class="purple-box">
<div class="amount-section">
<div class="dollar">
$
</div>
<div class="number">
9
</div>
<div class="name">
Group Owner Subscription
</div>
</div>
<div class="plus">
<div
class="svg-icon"
v-html="icons.positiveIcon"
></div>
</div>
<div class="amount-section">
<div class="dollar">
$
</div>
<div class="number">
3
</div>
<div class="name">
Each Individual Group Member
</div>
</div>
</div>
<div class="box payment-providers">
<payments-buttons
:stripe-fn="() => pay(PAYMENTS.STRIPE)"
:amazon-data="pay(PAYMENTS.AMAZON)"
/>
</div>
</div>
</div>
</div>
<!-- Create a new group -->
<div
v-if="!upgradingGroup._id"
class="container col-6 offset-3 create-option"
>
<div class="row">
<h1 class="col-12 text-center purple-header">
Create Your Group Today!
</h1>
</div>
<div class="row">
<div class="col-12 text-center">
<button
class="btn btn-primary create-group"
@click="launchModal('create-page')"
>
Create Your New Group!
</button>
</div>
</div>
<div class="row pricing justify-content-center align-items-center">
<div class="dollar">
$
</div>
<div class="number">
9
</div>
<div class="name">
<div>Group Owner</div>
<div>Subscription</div>
</div>
<div class="plus">
+
</div>
<div class="dollar">
$
</div>
<div class="number">
3
</div>
<div class="name">
<div>Each Additional</div>
<div>Member</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
#upgrading-group {
.amount-section {
position: relative;
}
.dollar {
position: absolute;
left: -16px;
top: 16px;
}
.purple-box {
color: #bda8ff;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
}
.number {
font-weight: bold;
color: #fff;
}
.plus .svg-icon{
width: 24px;
}
.payment-providers {
width: 350px;
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
}
}
.header {
background: #432874;
background: linear-gradient(180deg, #4F2A93 0%, #432874 100%);
color: #fff;
padding: 32px;
height: 340px;
margin-bottom: 32px;
margin-left: -12px;
margin-right: -12px;
h1 {
font-size: 48px;
line-height: 1.16;
margin-top: 12px;
color: #fff;
}
h2.sub-text {
color: #D5C8FF;
font-size: 24px;
font-weight: 400;
line-height: 1.33;
}
}
.benefits {
margin-top: -10em;
.box {
height: 416px;
border-radius: 8px;
}
h2 {
color: #6133b4;
}
}
.box {
border-radius: 2px;
background-color: #ffffff;
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
padding: 2em;
text-align: center;
display: inline-block !important;
vertical-align: bottom;
margin-right: 1em;
img {
margin: 0 auto;
margin-top: 2em;
margin-bottom: 1em;
}
}
img.box1 {
width: 266px;
}
img.box2 {
margin-top: 3.5em;
width: 262px;
margin-bottom: 3.7em;
}
img.box3 {
width: 225px;
margin-bottom: 3.0em;
}
button.create-group {
width: 330px;
height: 96px;
border-radius: 8px;
font-size: 1.5rem;
}
.purple-header {
color: #6133b4;
font-size: 48px;
margin-top: 16px;
}
.pricing {
margin-top: 32px;
margin-bottom: 64px;
.dollar, .number, .name {
display: inline-block;
vertical-align: bottom;
color: #a5a1ac;
}
.plus {
font-size: 2.125rem;
color: #a5a1ac;
margin-left: 16px;
margin-right: 16px;
}
.dollar {
margin-bottom: 24px;
font-size: 2rem;
font-weight: bold;
}
.name {
font-size: 1.5rem;
margin-left: 8px;
margin-right: 8px;
}
.number {
font-size: 4.5rem;
font-weight: bolder;
}
}
.payment-options {
margin-bottom: 64px;
h4 {
color: #34313a;
}
.purple-box {
background-color: #4f2a93;
color: #fff;
padding: 8px;
border-radius: 8px;
width: 200px;
height: 215px;
.dollar {
}
.number {
font-size: 60px;
}
.name {
width: 100px;
margin-left: 4.8px;
}
.plus {
width: 100%;
text-align: center;
}
div {
display: inline-block;
}
}
.box, .purple-box {
display: inline-block;
vertical-align: bottom;
}
}
</style>
<script>
import paymentsMixin from '../../mixins/payments';
import { mapState } from '@/libs/store';
import positiveIcon from '@/assets/svg/positive.svg';
import paymentsButtons from '@/components/payments/buttons/list';
import groupPlanCreationModal from '../group-plans/groupPlanCreationModal';
export default {
components: {
paymentsButtons,
groupPlanCreationModal,
},
mixins: [paymentsMixin],
data () {
return {
amazonPayments: {},
icons: Object.freeze({
positiveIcon,
}),
PAGES: {
CREATE_GROUP: 'create-group',
UPGRADE_GROUP: 'upgrade-group',
PAY: 'pay',
},
PAYMENTS: {
AMAZON: 'amazon',
STRIPE: 'stripe',
},
paymentMethod: '',
newGroup: {
type: 'guild',
privacy: 'private',
name: '',
leaderOnly: {
challenges: false,
},
},
activePage: '',
type: 'guild', // Guild or Party @TODO enum this
};
},
computed: {
newGroupIsReady () {
return Boolean(this.newGroup.name);
},
upgradingGroup () {
return this.$store.state.upgradingGroup;
},
// @TODO: can we move this to payment mixin?
...mapState({ user: 'user.data' }),
},
mounted () {
this.activePage = this.PAGES.BENEFITS;
this.$store.dispatch('common:setTitle', {
section: this.$t('groupPlans'),
});
},
methods: {
launchModal () {
this.$root.$emit('bv::show::modal', 'create-group');
},
createGroup () {
this.changePage(this.PAGES.PAY);
},
pay (paymentMethod) {
const subscriptionKey = 'group_monthly'; // @TODO: Get from content API?
const paymentData = {
subscription: subscriptionKey,
coupon: null,
};
if (this.upgradingGroup && this.upgradingGroup._id) {
paymentData.groupId = this.upgradingGroup._id;
paymentData.group = this.upgradingGroup;
} else {
paymentData.groupToCreate = this.newGroup;
}
this.paymentMethod = paymentMethod;
if (this.paymentMethod === this.PAYMENTS.AMAZON) {
paymentData.type = 'subscription';
return paymentData;
}
if (this.paymentMethod === this.PAYMENTS.STRIPE) {
this.redirectToStripe(paymentData);
}
return null;
},
},
};
</script>

View File

@@ -529,7 +529,7 @@ export default {
// List of prompts for user on changes.
// Sounds like we may need a refactor here, but it is clean for now
if (!this.user.flags.welcomed) {
if (!this.user.flags.welcomed && !this.$route.name.includes('groupPlan')) {
if (this.$store.state.avatarEditorOptions) {
this.$store.state.avatarEditorOptions.editingUser = false;
}

View File

@@ -28,12 +28,6 @@
:alt="$t('paypal')"
>&nbsp;
</button>
<amazon-button
v-if="amazonAvailable"
class="payment-item"
:disabled="disabled"
:amazon-data="amazonData"
/>
</div>
</template>
@@ -92,21 +86,14 @@
</style>
<script>
import amazonButton from '@/components/payments/buttons/amazon';
import creditCardIcon from '@/assets/svg/credit-card-icon.svg';
export default {
components: {
amazonButton,
},
props: {
disabled: {
type: Boolean,
default: false,
},
amazonData: {
type: Object,
},
stripeFn: {
type: Function,
},
@@ -128,9 +115,6 @@ export default {
paypalAvailable () {
return typeof this.paypalFn === 'function';
},
amazonAvailable () {
return this.amazonData !== undefined;
},
},
};
</script>

View File

@@ -1,228 +1,204 @@
<template>
<div class="group-plan-static text-center">
<amazon-payments-modal />
<div class="container">
<div class="row top">
<div>
<group-plan-creation-modal />
<div class="d-flex justify-content-center">
<div
class="group-plan-page text-center"
:class="{ static: isStaticPage }"
>
<div class="top-left"></div>
<div class="col-6 offset-3">
<div class="col-6 offset-3 mb-100">
<img
class="party"
src="../../assets/images/group-plans-static/party@3x.png"
>
<h1>{{ $t('groupPlanTitle') }}</h1>
<p>{{ $t('groupPlanDesc') }}</p>
<div class="pricing">
<h1 class="mt-5" v-if="upgradingGroup._id">{{ $t('upgradeYourCrew') }}</h1>
<h1 class="mt-5" v-else>{{ $t('groupPlanTitle') }}</h1>
<p class="mb-0">{{ $t('groupPlanDesc') }}</p>
<div class="pricing mt-5">
<span>Just</span>
<span class="number">$9</span>
<span class="bold">per month +</span>
<span class="number">$3</span>
<span class="bold">per member*</span>
<span class="bold">per additional member*</span>
</div>
<div class="text-center">
<button
class="btn btn-primary cta-button"
class="btn btn-primary cta-button white mt-4 mb-3"
@click="goToNewGroupPage()"
>
{{ $t('getStarted') }}
</button>
</div>
<small>{{ $t('billedMonthly') }}</small>
<p class="gray-200">{{ $t('billedMonthly') }}</p>
</div>
<div class="top-right"></div>
</div>
<div class="row">
<div class="text-col col-12 col-md-6 text-left">
<h2>{{ $t('teamBasedTasksList') }}</h2>
<p>{{ $t('teamBasedTasksListDesc') }}</p>
<div class="d-flex justify-content-between align-items-middle w-100 gap-72 mb-100">
<div class="ml-auto my-auto w-448 text-left">
<h2 class="mt-0">{{ $t('teamBasedTasksList') }}</h2>
<p>{{ $t('teamBasedTasksListDesc') }}</p>
</div>
<div class="mr-auto my-auto">
<img src="../../assets/images/group-plans-static/group-management@3x.png">
</div>
</div>
<div class="col-12 col-md-6">
<div
class="team-based"
v-html="svg.teamBased"
></div>
<div class="d-flex justify-content-between align-items-middle w-100 gap-72 mb-100">
<div class="ml-auto my-auto">
<img src="../../assets/images/group-plans-static/team-based@3x.png">
</div>
<div class="mr-auto my-auto w-448 text-left">
<h2 class="mt-0">{{ $t('groupManagementControls') }}</h2>
<p>{{ $t('groupManagementControlsDesc') }}</p>
</div>
</div>
</div>
<div class="row">
<div class="col-12 col-md-6">
<div
class="group-management"
v-html="svg.groupManagement"
></div>
</div>
<div class="text-col col-12 col-md-6 text-left">
<h2>{{ $t('groupManagementControls') }}</h2>
<p>{{ $t('groupManagementControlsDesc') }}</p>
</div>
</div>
<div class="row">
<div class="col-12 col-md-6 offset-md-3 text-center">
<div class="d-flex flex-column justify-content-center">
<img
class="big-gem"
class="big-gem mb-3 mx-auto"
src="../../assets/images/group-plans-static/big-gem@3x.png"
>
<h2>{{ $t('inGameBenefits') }}</h2>
<p>{{ $t('inGameBenefitsDesc') }}</p>
<h2 class="mt-3">{{ $t('inGameBenefits') }}</h2>
<p class="final-paragraph mx-auto">{{ $t('inGameBenefitsDesc') }}</p>
</div>
</div>
<div class="row">
<div class="bot-left"></div>
<div class="col-6 offset-3">
<h2 class="purple">
{{ $t('inspireYourParty') }}
</h2>
<div class="pricing">
<span>Just</span>
<span class="number">$9</span>
<span class="bold">per month +</span>
<span class="number">$3</span>
<span class="bold">per member*</span>
<div class="text-center mb-128">
<div class="bot-left"></div>
<div class="col-6 offset-3">
<h2 class="purple-300 mt-0 mb-4" v-if="upgradingGroup._id">
{{ $t('readyToUpgrade') }}
</h2>
<h2 v-else class="purple-300 mt-0 mb-4">
{{ $t('createGroupToday') }}
</h2>
<div class="pricing mb-4">
<span>Just</span>
<span class="number">$9</span>
<span class="bold">per month +</span>
<span class="number">$3</span>
<span class="bold">per member*</span>
</div>
<div class="text-center mb-3">
<button
class="btn btn-primary cta-button white"
@click="goToNewGroupPage()"
>
{{ $t('getStarted') }}
</button>
</div>
<p class="gray-200">{{ $t('billedMonthly') }}</p>
</div>
<div class="text-center">
<button
class="btn btn-primary cta-button"
@click="goToNewGroupPage()"
>
{{ $t('getStarted') }}
</button>
</div>
<small>{{ $t('billedMonthly') }}</small>
<div class="bot-right"></div>
</div>
<div class="bot-right"></div>
<b-modal
id="group-plan"
title
size="md"
:hide-footer="true"
:hide-header="true"
>
<div>
<h2>{{ $t('letsMakeAccount') }}</h2>
<auth-form @authenticate="authenticate()" />
</div>
</b-modal>
</div>
</div>
<b-modal
id="group-plan"
title
size="md"
:hide-footer="true"
:hide-header="true"
<div
class="bottom-banner text-center"
:class="{ static: isStaticPage }"
>
<div v-if="modalPage === 'account'">
<h2>{{ $t('letsMakeAccount') }}</h2>
<auth-form @authenticate="authenticate()" />
</div>
<div v-if="modalPage === 'purchaseGroup'">
<create-group-modal-pages />
</div>
</b-modal>
<h2 class="white">{{ $t('interestedLearningMore') }}</h2>
<p class="purple-600" v-html="$t('checkGroupPlanFAQ')"></p>
</div>
</div>
</template>
<style lang='scss'>
.bottom-banner > .purple-600 {
color: #D5C8FF !important;
a {
color: #D5C8FF;
text-decoration: underline;
}
}
</style>
<style lang='scss' scoped>
@import url('https://fonts.googleapis.com/css?family=Varela+Round');
@import '~@/assets/scss/colors.scss';
// General typography tweaks
h1, h2 {
font-family: 'Varela Round', sans-serif;
font-weight: normal;
}
.party {
width: 386px;
margin-top: 4em;
}
.team-based {
background-image: url('../../assets/images/group-plans-static/group-management@3x.png');
background-size: contain;
position: absolute;
height: 356px;
width: 411px;
margin-top: -2em;
}
.group-management {
background-image: url('../../assets/images/group-plans-static/team-based@3x.png');
background-size: contain;
position: absolute;
height: 294px;
width: 411px;
}
.top-left, .top-right, .bot-left, .bot-right {
width: 273px;
height: 396px;
background-size: contain;
position: absolute;
}
.top-left {
background-image: url('../../assets/images/group-plans-static/top-left@3x.png');
left: 4em;
height: 420px;
}
.top-right {
background-image: url('../../assets/images/group-plans-static/top-right@3x.png');
right: 4em;
height: 420px;
}
.bot-left {
background-image: url('../../assets/images/group-plans-static/bot-left@3x.png');
left: 4em;
bottom: 1em;
}
.bot-right {
background-image: url('../../assets/images/group-plans-static/bot-right@3x.png');
right: 4em;
bottom: 1em;
font-weight: 400;
}
h1 {
font-size: 42px;
color: #34313a;
line-height: 1.17;
color: $purple-300;
font-size: 48px;
line-height: 56px;
}
h2 {
font-size: 29px;
color: #34313a;
margin-top: 1em;
}
.purple {
color: #6133b4;
color: $gray-50;
font-size: 32px;
line-height: 40px;
}
p {
color: $gray-100;
font-size: 20px;
color: #878190;
line-height: 28px;
}
.group-plan-static {
margin-top: 6em;
position: relative;
}
// Major layout elements
.row {
margin-top: 10em;
margin-bottom: 10em;
}
.bottom-banner {
height: 152px;
background-image: linear-gradient(rgba(97, 51, 180), rgba(79, 42, 147));
padding-top: 32px;
width: 100vw;
.text-col {
margin-top: 3em;
}
&.static {
padding-top: 16px;
}
.big-gem {
width: 138.5px;
&:not(.static) {
margin-left: -12px;
}
}
.cta-button {
font-family: 'Varela Round', sans-serif;
font-weight: normal;
padding: 1em 2em;
margin-top: 1em;
margin-bottom: 1em;
border-radius: 4px;
background-color: #6133b4;
border-radius: 8px;
background-color: $purple-300;
box-shadow: inset 0 -4px 0 0 rgba(52, 49, 58, 0.4);
font-size: 20px;
color: #fff;
line-height: 28px;
&.btn-primary:hover {
background-color: $purple-400;
}
}
.final-paragraph {
width: 684px;
margin-bottom: 11rem;
}
.group-plan-page {
max-width: 1440px;
position: relative;
&.static {
margin-top: 56px;
}
}
.pricing {
color: #878190;
color: $gray-100;
font-size: 24px;
span {
@@ -234,40 +210,103 @@
}
.number {
color: #1ca372;
color: $green-10;
font-weight: bold;
}
}
small {
font-size: 16px;
color: #a5a1ac;
// One-off spacing adjustments
.gap-72 {
gap: 72px;
}
.mb-100 {
margin-bottom: 100px !important;
}
.mb-128 {
margin-bottom: 128px !important;
}
.w-448 {
width: 448px;
}
// Images
.big-gem {
width: 138.5px;
}
.bot-left, .bot-right, .top-left, .top-right {
width: 246px;
height: 340px;
background-size: contain;
position: absolute;
background-repeat: no-repeat;
}
.bot-left {
background-image: url('../../assets/images/group-plans-static/bot-left@3x.png');
left: 48px;
bottom: 48px;
}
.bot-right {
background-image: url('../../assets/images/group-plans-static/bot-right@3x.png');
right: 48px;
bottom: 48px;
}
.party {
width: 386px;
margin-top: 100px;
}
.top-left {
background-image: url('../../assets/images/group-plans-static/top-left@3x.png');
top: 48px;
left: 48px;
}
.top-right {
background-image: url('../../assets/images/group-plans-static/top-right@3x.png');
right: 48px;
top: 48px;
}
</style>
<script>
import { setup as setupPayments } from '@/libs/payments';
import amazonPaymentsModal from '@/components/payments/amazonModal';
import paymentsMixin from '../../mixins/payments';
import AuthForm from '../auth/authForm.vue';
import CreateGroupModalPages from '../group-plans/createGroupModalPages.vue';
import party from '../../assets/images/group-plans-static/party.svg';
import GroupPlanCreationModal from '../group-plans/groupPlanCreationModal.vue';
export default {
components: {
AuthForm,
CreateGroupModalPages,
amazonPaymentsModal,
GroupPlanCreationModal,
},
mixins: [paymentsMixin],
data () {
return {
svg: {
party,
},
modalTitle: this.$t('register'),
modalOption: '',
modalPage: 'account',
modalTitle: this.$t('register'),
};
},
computed: {
isStaticPage () {
return this.$route.meta.requiresLogin === false;
},
upgradingGroup () {
return this.$store.state.upgradingGroup;
},
user () {
return this.$store.state.user?.data;
},
},
mounted () {
this.$nextTick(() => {
// Load external scripts after the app has been rendered
@@ -278,11 +317,19 @@ export default {
});
},
methods: {
goToNewGroupPage () {
this.$root.$emit('bv::show::modal', 'group-plan');
},
authenticate () {
this.modalPage = 'purchaseGroup';
this.$root.$emit('bv::hide::modal', 'group-plan');
this.$root.$emit('bv::show::modal', 'create-group');
},
goToNewGroupPage () {
if (this.isStaticPage && !this.user) {
this.modalOption = 'static';
return this.$root.$emit('bv::show::modal', 'group-plan');
}
if (this.upgradingGroup._id) {
return this.stripeGroup({ group: this.upgradingGroup, upgrade: true });
}
return this.$root.$emit('bv::show::modal', 'create-group');
},
},
};

View File

@@ -8,7 +8,10 @@
'white-header': $route.name === 'plans'
}"
/>
<div class="static-wrapper">
<div
class="static-wrapper"
:class="{ 'groups-bg': $route.name === 'groupPlans' }"
>
<router-view />
</div>
<div
@@ -205,6 +208,13 @@
.strong {
font-weight: bold;
}
&.groups-bg {
background-color: $white;
background-image: url('../../assets/images/group-plans-static/top.svg');
background-repeat: no-repeat;
background-position-y: 56px;
}
}
</style>

View File

@@ -6,6 +6,7 @@ import { mapState } from '@/libs/store';
import encodeParams from '@/libs/encodeParams';
import notificationsMixin from '@/mixins/notifications';
import { CONSTANTS, setLocalSetting } from '@/libs/userlocalManager';
import * as Analytics from '@/libs/analytics';
const STRIPE_PUB_KEY = process.env.STRIPE_PUB_KEY;
@@ -198,6 +199,16 @@ export default {
alert(`Error while redirecting to Stripe: ${checkoutSessionResult.error.message}`);
throw checkoutSessionResult.error;
}
if (paymentType === 'groupPlan') {
Analytics.track({
hitType: 'event',
eventName: 'group plan create',
eventAction: 'group plan create',
eventCategory: 'behavior',
demographics: appState.newGroup.demographics,
type: appState.newGroup.type,
}, { trackOnClient: true });
}
} catch (err) {
console.error('Error while redirecting to Stripe', err); // eslint-disable-line
alert(`Error while redirecting to Stripe: ${err.message}`);
@@ -370,5 +381,20 @@ export default {
window.alert(e.response.data.message); // eslint-disable-line no-alert
}
},
stripeGroup (options = { group: {}, upgrade: false }) {
const paymentData = {
subscription: 'group_monthly',
coupon: null,
};
if (options.upgrade && options.group._id) {
paymentData.groupId = options.group._id;
paymentData.group = options.group;
} else {
paymentData.groupToCreate = options.group;
}
this.redirectToStripe(paymentData);
},
},
};

View File

@@ -24,7 +24,7 @@
<app-menu />
<div
class="container-fluid"
:class="{'no-margin': noMargin}"
:class="{'no-margin': noMargin, 'groups-background': $route.fullPath === '/group-plans' }"
>
<app-header />
<buyModal
@@ -67,6 +67,12 @@
.container-fluid {
flex: 1 0 auto;
&.groups-background {
background-color: white;
background-image: url('../assets/images/group-plans-static/top.svg');
background-repeat: no-repeat;
}
}
.no-margin {

View File

@@ -37,15 +37,6 @@ export default function handleRedirect (to, from, next) {
const newGroup = newAppState.group;
if (newGroup && newGroup._id) {
// Handle new user signup
if (newAppState.newSignup === true) {
return next({
name: 'groupPlanDetailTaskInformation',
params: { groupId: newGroup._id },
query: { showGroupOverview: 'true' },
});
}
return next({
name: 'groupPlanDetailTaskInformation',
params: { groupId: newGroup._id },

View File

@@ -41,7 +41,7 @@ const StablePage = () => import(/* webpackChunkName: "inventory" */'@/components
// Guilds & Parties
const GroupPage = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/group');
const GroupPlansAppPage = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/groupPlan');
const GroupPlansAppPage = () => import(/* webpackChunkName: "guilds" */ '@/components/static/groupPlans');
const LookingForParty = () => import(/* webpackChunkName: "guilds" */ '@/components/groups/lookingForParty');
// Group Plans

View File

@@ -339,16 +339,19 @@
"worldBossBullet3": "You can continue with normal Quest Bosses, damage will apply to both",
"worldBossBullet4": "Check the Tavern regularly to see World Boss progress and Rage attacks",
"worldBoss": "World Boss",
"groupPlanTitle": "Need more for your crew?",
"groupPlanDesc": "Managing a small team or organizing household chores? Our group plans grant you exclusive access to a private task board and chat area dedicated to you and your group members!",
"groupPlanTitle": "Need More for Your Crew?",
"upgradeYourCrew": "Ready to Upgrade Your Crew?",
"groupPlanDesc": "Organizing the household chores or managing a small class project? Habiticas Group Plans provide a shared task experience and dedicated chat space to help you and your group stay motivated.",
"billedMonthly": "*billed as a monthly subscription",
"teamBasedTasksList": "Team-Based Task List",
"teamBasedTasksListDesc": "Set up an easily-viewed shared task list for the group. Assign tasks to your fellow group members, or let them claim their own tasks to make it clear what everyone is working on!",
"groupManagementControls": "Group Management Controls",
"groupManagementControlsDesc": "View task status to verify that a task that was completed, add Group Managers to share responsibilities, and enjoy a private group chat for all team members.",
"inGameBenefits": "In-Game Benefits",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly equipment sets and the ability to buy gems with gold.",
"inspireYourParty": "Inspire your party, gamify life together.",
"teamBasedTasksList": "Shared Task Board",
"teamBasedTasksListDesc": "Group members can all work from the same task board to ensure the group is staying on top of things. Complete tasks from the shared task board or copy them to your personal tasks to complete them on the go.",
"groupManagementControls": "Flexible Accountability",
"groupManagementControlsDesc": "Share responsibilities by assigning tasks to any number of members, or leave tasks open as a challenge to see who can complete it first. Group members can stay up-to-date on each other's progress by viewing the task status.",
"inGameBenefits": "All the Benefits!",
"inGameBenefitsDesc": "Group members get an exclusive Jackalope Mount, as well as full subscription benefits, including special monthly Equipment sets and the ability to buy Gems with Gold.",
"createGroupToday": "Create Your Group Today!",
"createGroupTitle": "Create Group",
"readyToUpgrade": "Ready to Upgrade?",
"letsMakeAccount": "First, lets make you an account",
"nameYourGroup": "Next, Name Your Group",
"exampleGroupName": "Example: Avengers Academy",
@@ -368,7 +371,7 @@
"nameStarText": "Add a title",
"descriptionOptional": "Description",
"descriptionOptionalText": "Add a description",
"nextPaymentMethod": "Next: Payment Method",
"nextPaymentMethod": "Next: Payment",
"congratsOnGroupPlan": "Congratulations on creating your new Group! Here are a few answers to some of the more commonly asked questions.",
"whatsIncludedGroup": "What's included in the subscription",
"whatsIncludedGroupDesc": "All members of the Group receive full subscription benefits, including the monthly subscriber items, the ability to buy Gems with Gold, and the Royal Purple Jackalope mount, which is exclusive to users with a Group Plan membership.",
@@ -425,5 +428,7 @@
"tavernDiscontinued": "The Tavern and Guilds have been discontinued",
"tavernDiscontinuedDetail": "Due to a number of factors, including changes in how our player base interacts with Habitica, the resources necessary to maintain these spaces became disproportionate to the number of people participating in them and unsustainable over the long term.",
"tavernDiscontinuedLinks": "Read more about the <a href='/static/faq/tavern-and-guilds'>Tavern and Guild Service Discontinuation</a> or head back to the <a href='/'>homepage</a>.",
"chatSunsetWarning": "⚠️ <strong>Habitica Guilds and Tavern chat will be discontinued on 8/8/2023.</strong> <a href='/static/faq/tavern-and-guilds'>Click here</a> to read more about this change."
"chatSunsetWarning": "⚠️ <strong>Habitica Guilds and Tavern chat will be discontinued on 8/8/2023.</strong> <a href='/static/faq/tavern-and-guilds'>Click here</a> to read more about this change.",
"interestedLearningMore": "Interested in Learning More?",
"checkGroupPlanFAQ": "Check out the <a href='/static/faq#what-is-group-plan'>Group Plans FAQ</a> to learn how to get the most out of your shared task experience."
}