diff --git a/webpack/config/index.js b/webpack/config/index.js index fb2161627e..8a55e0fb81 100644 --- a/webpack/config/index.js +++ b/webpack/config/index.js @@ -38,6 +38,18 @@ module.exports = { target: 'http://localhost:3000', changeOrigin: true, }, + '/stripe': { + target: 'http://localhost:3000', + changeOrigin: true, + }, + '/amazon': { + target: 'http://localhost:3000', + changeOrigin: true, + }, + '/paypal': { + target: 'http://localhost:3000', + changeOrigin: true, + }, }, // CSS Sourcemaps off by default because relative paths are "buggy" // with this option, according to the CSS-Loader README diff --git a/website/client/components/appMenu.vue b/website/client/components/appMenu.vue index 10d33fcfcf..3e1a0d72f0 100644 --- a/website/client/components/appMenu.vue +++ b/website/client/components/appMenu.vue @@ -51,7 +51,7 @@ div router-link.dropdown-item(:to="{name: 'stats'}") {{ $t('stats') }} router-link.dropdown-item(:to="{name: 'achievements'}") {{ $t('achievements') }} router-link.dropdown-item(:to="{name: 'profile'}") {{ $t('profile') }} - router-link.dropdown-item(:to="{name: 'settings'}") {{ $t('settings') }} + router-link.dropdown-item(:to="{name: 'site'}") {{ $t('settings') }} a.nav-link.dropdown-item(to="/", @click.prevent='logout()') {{ $t('logout') }} diff --git a/website/client/components/payments/amazonModal.vue b/website/client/components/payments/amazonModal.vue new file mode 100644 index 0000000000..857f7a365f --- /dev/null +++ b/website/client/components/payments/amazonModal.vue @@ -0,0 +1,201 @@ + + b-modal#amazon-payment(title="Amazon", :hide-footer="true", size='lg') + button#AmazonPayButton + + + diff --git a/website/client/components/settings/api.vue b/website/client/components/settings/api.vue new file mode 100644 index 0000000000..ec43444703 --- /dev/null +++ b/website/client/components/settings/api.vue @@ -0,0 +1,118 @@ + +.row.standard-page + .col-6 + h2 {{ $t('API') }} + small {{ $t('APIText') }} + + .section + h6 {{ $t('userId') }} + pre.prettyprint {{user.id}} + h6 {{ $t('APIToken') }} + pre.prettyprint {{user.apiToken}} + small(v-html='$t("APITokenWarning", { hrefTechAssistanceEmail })') + + .section + h3 {{ $t('thirdPartyApps') }} + ul + li + a(target='_blank' href='https://www.beeminder.com/habitica') {{ $t('beeminder') }} + br + | {{ $t('beeminderDesc') }} + li + a(target='_blank' href='https://chrome.google.com/webstore/detail/habitrpg-chat-client/hidkdfgonpoaiannijofifhjidbnilbb') {{ $t('chromeChatExtension') }} + br + | {{ $t('chromeChatExtensionDesc') }} + li + a(target='_blank' :href='`http://data.habitrpg.com?uuid= + user._id`') {{ $t('dataTool') }} + br + | {{ $t('dataToolDesc') }} + li(v-html="$t('otherExtensions')") + br + | {{ $t('otherDesc') }} + + hr + + .col-6 + h2 {{ $t('webhooks') }} + table.table.table-striped + thead(v-if='user.webhooks.length') + tr + th {{ $t('enabled') }} + th {{ $t('webhookURL') }} + th + tbody + tr(v-for="(webhook, index) in user.webhooks") + td + input(type='checkbox', v-model='webhook.enabled', @change='saveWebhook(webhook, index)') + td + input.form-control(type='url', v-model='webhook.url') + td + a.btn.btn-warning.checklist-icons(@click='deleteWebhook(webhook, index)') + span.glyphicon.glyphicon-trash(:tooltip="$t('delete')") Delete + a.btn.btn-success.checklist-icons(@click='saveWebhook(webhook, index)') Update + tr + td(colspan=2) + .form-horizontal + .form-group.col-sm-10 + input.form-control(type='url', v-model='newWebhook.url', :placeholder="$t('webhookURL')") + .col-sm-2 + button.btn.btn-sm.btn-primary(type='submit', @click='addWebhook(newWebhook.url)') {{ $t('add') }} + + + + + diff --git a/website/client/components/settings/dataExport.vue b/website/client/components/settings/dataExport.vue new file mode 100644 index 0000000000..07f434fd86 --- /dev/null +++ b/website/client/components/settings/dataExport.vue @@ -0,0 +1,13 @@ + +.row + .col-md-6 + h2 {{ $t('dataExport') }} + small {{ $t('saveData') }} + h4 {{ $t('habitHistory') }} + | {{ $t('exportHistory') }} + a(href="/export/history.csv") {{ $t('csv') }} + h4 {{ $t('userData') }} + | {{ $t('exportUserData') }} + a(href="/export/userdata.xml") {{ $t('xml') }} + a(href="/export/userdata.json") {{ $t('json') }} + diff --git a/website/client/components/settings/deleteModal.vue b/website/client/components/settings/deleteModal.vue new file mode 100644 index 0000000000..a711e90313 --- /dev/null +++ b/website/client/components/settings/deleteModal.vue @@ -0,0 +1,52 @@ + + b-modal#delete(:title="$t('deleteAccount')", :hide-footer='true' size='md') + strong {{ $t('deleteLocalAccountText') }} + br + .row + .col-6 + input.form-control(type='password', v-model='password') + br + .row + #feedback.col-12.form-group + label(for='feedbackTextArea') {{ $t('feedback') }} + textarea#feedbackTextArea.form-control(v-model='feedback') + .modal-footer + button.btn.btn-danger(@click='close()') {{ $t('neverMind') }} + button.btn.btn-primary(@click='deleteAccount()', :disabled='!password') {{ $t('deleteDo') }} + + + diff --git a/website/client/components/settings/index.vue b/website/client/components/settings/index.vue new file mode 100644 index 0000000000..68366e1107 --- /dev/null +++ b/website/client/components/settings/index.vue @@ -0,0 +1,23 @@ + +.row + secondary-menu.col-12 + router-link.nav-link(:to="{name: 'site'}", exact, :class="{'active': $route.name === 'site'}") {{ $t('site') }} + router-link.nav-link(:to="{name: 'api'}", :class="{'active': $route.name === 'api'}") {{ $t('API') }} + router-link.nav-link(:to="{name: 'dataExport'}", :class="{'active': $route.name === 'dataExport'}") {{ $t('dataExport') }} + router-link.nav-link(:to="{name: 'promoCode'}", :class="{'active': $route.name === 'promoCode'}") {{ $t('promoCode') }} + router-link.nav-link(:to="{name: 'subscription'}", :class="{'active': $route.name === 'subscription'}") {{ $t('subscription') }} + router-link.nav-link(:to="{name: 'notifications'}", :class="{'active': $route.name === 'notifications'}") {{ $t('notifications') }} + + .col-12 + router-view + + + diff --git a/website/client/components/settings/notifications.vue b/website/client/components/settings/notifications.vue new file mode 100644 index 0000000000..abaf3ac1c8 --- /dev/null +++ b/website/client/components/settings/notifications.vue @@ -0,0 +1,74 @@ + +.row.standard-page + .col-12 + h1 {{ $t('notifications') }} + .col-12 + .checkbox + label + input(type='checkbox', v-model='user.preferences.pushNotifications.unsubscribeFromAll', + @change='set("pushNotifications", "unsubscribeFromAll")') + span {{ $t('unsubscribeAllPush') }} + + br + + .checkbox + label + input(type='checkbox', v-model='user.preferences.emailNotifications.unsubscribeFromAll', + @change='set("emailNotifications", "unsubscribeFromAll")') + span {{ $t('unsubscribeAllEmails') }} + small {{ $t('unsubscribeAllEmailsText') }} + + .col-8 + table.table + tr + td + th + span {{ $t('email') }} + th + span {{ $t('push') }} + tr(v-for='notification in notifications') + td + span {{ $t(notification) }} + td + input(type='checkbox', v-model='user.preferences.emailNotifications[notification]', + @change='set("emailNotifications", notification)') + td + input(type='checkbox', v-model='user.preferences.pushNotifications[notification]', + @change='set("pushNotifications", notification)') + hr + + + diff --git a/website/client/components/settings/promoCode.vue b/website/client/components/settings/promoCode.vue new file mode 100644 index 0000000000..ef6ace1f96 --- /dev/null +++ b/website/client/components/settings/promoCode.vue @@ -0,0 +1,61 @@ + +.row.standard-page + .col-md-6 + h2 {{ $t('promoCode') }} + .form-inline(role='form') + input.form-control(type='text', v-model='couponCode', :placeholder="$t('promoPlaceholder')") + button.btn.btn-primary(@click='enterCoupon()') {{ $t('submit') }} + div + small {{ $t('couponText') }} + div(v-if='user.contributor.sudo') + hr + h4 {{ $t('generateCodes') }} + .form(role='form') + .form-group + input.form-control(type='text', v-model='codes.event', placeholder="Event code (eg, 'wondercon')") + .form-group + input.form-control(type='number', v-model='codes.count', placeholder="Number of codes to generate (eg, 250)") + .form-group + button.btn.btn-primary(type='submit', @click='generateCodes(codes)') {{ $t('generate') }} + a.btn.btn-default(:href='getCodesUrl') {{ $t('getCodes') }} + + + diff --git a/website/client/components/settings/resetModal.vue b/website/client/components/settings/resetModal.vue new file mode 100644 index 0000000000..a07eb902b9 --- /dev/null +++ b/website/client/components/settings/resetModal.vue @@ -0,0 +1,36 @@ + + b-modal#reset(:title="$t('resetAccount')", :hide-footer='true' size='md') + p {{ $t('resetText1') }} + p {{ $t('resetText2') }} + .modal-footer + button.btn.btn-danger(@click='close()') {{ $t('neverMind') }} + button.btn.btn-primary(@click='reset()') {{ $t('resetDo') }} + + + diff --git a/website/client/components/settings/restoreModal.vue b/website/client/components/settings/restoreModal.vue new file mode 100644 index 0000000000..afe0fb5365 --- /dev/null +++ b/website/client/components/settings/restoreModal.vue @@ -0,0 +1,109 @@ + + b-modal#restore(:title="$t('fixValues')", :hide-footer='true' size='lg') + p {{ $t('fixValuesText1') }} + p {{ $t('fixValuesText2') }} + .form-horizontal + h3 {{ $t('stats') }} + .form-group.row + .col-sm-3 + label.control-label {{ $t('health') }} + .col-sm-9 + input.form-control(type='number', step="any", data-for='stats.hp', v-model='restoreValues.stats.hp') + .form-group.row + .col-sm-3 + label.control-label {{ $t('experience') }} + .col-sm-9 + input.form-control(type='number', step="any", data-for='stats.exp', v-model='restoreValues.stats.exp') + .form-group.row + .col-sm-3 + label.control-label {{ $t('gold') }} + .col-sm-9 + input.form-control(type='number', step="any", data-for='stats.gp', v-model='restoreValues.stats.gp') + //input.form-control(type='number', step="any", data-for='stats.gp', v-model='restoreValues.stats.gp',disabled) + //-p.alert + small {{ $t('disabledWinterEvent') }} + .form-group.row + .col-sm-3 + label.control-label {{ $t('mana') }} + .col-sm-9 + input.form-control(type='number', step="any", data-for='stats.mp', v-model='restoreValues.stats.mp') + .form-group.row + .col-sm-3 + label.control-label {{ $t('level') }} + .col-sm-9 + input.form-control(type='number', data-for='stats.lvl', v-model='restoreValues.stats.lvl') + h3 {{ $t('achievements') }} + .form-group.row + .col-sm-3 + label.control-label {{ $t('fix21Streaks') }} + .col-sm-9 + input.form-control(type='number', data-for='achievements.streak', v-model='restoreValues.achievements.streak') + //- This is causing too many problems for users + h3 {{ $t('other') }} + a.btn.btn-sm.btn-warning(ng-controller='FooterCtrl', ng-click='addMissedDay(1)') {{ $t('triggerDay') }} + .modal-footer + button.btn.btn-danger(@click='close()') {{ $t('discardChanges') }} + button.btn.btn-primary(@click='restore()') {{ $t('saveAndClose') }} + + + diff --git a/website/client/components/settings/site.vue b/website/client/components/settings/site.vue new file mode 100644 index 0000000000..ff8fca4a53 --- /dev/null +++ b/website/client/components/settings/site.vue @@ -0,0 +1,371 @@ + + .row.standard-page + restore-modal + reset-modal + delete-modal + h1.col-12 {{ $t('settings') }} + .col-6 + .form-horizontal + h5 {{ $t('language') }} + select.form-control(v-model='selectedLanguage', + @change='changeLanguage()') + option(v-for='lang in availableLanguages', :value='lang.code') {{lang.name}} + + small + | {{ $t('americanEnglishGovern') }} + br + strong(v-html="$t('helpWithTranslation')") + hr + + .form-horizontal + h5 {{ $t('dateFormat') }} + select.form-control(v-model='user.preferences.dateFormat', + @change='set("dateFormat")') + option(v-for='dateFormat in availableFormats', :value='dateFormat') {{dateFormat}} + hr + + div + .checkbox + label + input(type='checkbox', @click='hideHeader() ', v-model='user.preferences.hideHeader') + span.hint(popover-trigger='mouseenter', popover-placement='right', :popover="$t('showHeaderPop')") {{ $t('showHeader') }} + .checkbox + label + input(type='checkbox', @click='toggleStickyHeader()', v-model='user.preferences.stickyHeader', :disabled="user.preferences.hideHeader") + span.hint(popover-trigger='mouseenter', popover-placement='right', :popover="$t('stickyHeaderPop')") {{ $t('stickyHeader') }} + .checkbox + label + input(type='checkbox', v-model='user.preferences.newTaskEdit', @click='set("newTaskEdit")') + span.hint(popover-trigger='mouseenter', popover-placement='right', :popover="$t('newTaskEditPop')") {{ $t('newTaskEdit') }} + .checkbox + label + input(type='checkbox', v-model='user.preferences.tagsCollapsed', @change='set("tagsCollapsed")') + span.hint(popover-trigger='mouseenter', popover-placement='right', :popover="$t('startCollapsedPop')") {{ $t('startCollapsed') }} + .checkbox + label + input(type='checkbox', v-model='user.preferences.advancedCollapsed', @change='set("advancedCollapsed")') + span.hint(popover-trigger='mouseenter', popover-placement='right', :popover="$t('startAdvCollapsedPop')") {{ $t('startAdvCollapsed') }} + .checkbox + label + input(type='checkbox', v-model='user.preferences.dailyDueDefaultView', @change='set("dailyDueDefaultView")') + span.hint(popover-trigger='mouseenter', popover-placement='right', :popover="$t('dailyDueDefaultViewPop')") {{ $t('dailyDueDefaultView') }} + .checkbox(v-if='party.memberCount === 1') + label + input(type='checkbox', v-model='user.preferences.displayInviteToPartyWhenPartyIs1', @change='set("displayInviteToPartyWhenPartyIs1")') + span.hint(popover-trigger='mouseenter', popover-placement='right', :popover="$t('displayInviteToPartyWhenPartyIs1')") {{ $t('displayInviteToPartyWhenPartyIs1') }} + .checkbox + input(type='checkbox', v-model='user.preferences.suppressModals.levelUp', @change='set("suppressModals", "levelUp")') + label {{ $t('suppressLevelUpModal') }} + .checkbox + input(type='checkbox', v-model='user.preferences.suppressModals.hatchPet', @change='set("suppressModals", "hatchPet")') + label {{ $t('suppressHatchPetModal') }} + .checkbox + input(type='checkbox', v-model='user.preferences.suppressModals.raisePet', @change='set("suppressModals", "raisePet")') + label {{ $t('suppressRaisePetModal') }} + .checkbox + input(type='checkbox', v-model='user.preferences.suppressModals.streak', @change='set("suppressModals", "streak")') + label {{ $t('suppressStreakModal') }} + //- .checkbox + //- label {{ $t('confirmScoreNotes') }} + //- input(type='checkbox', v-model='user.preferences.tasks.confirmScoreNotes', @change='set({"preferences.tasks.confirmScoreNotes": user.preferences.tasks.confirmScoreNotes ? true: false})') + + //- .checkbox + //- label {{ $t('groupTasksByChallenge') }} + //- input(type='checkbox', v-model='user.preferences.tasks.groupByChallenge', @change='set({"preferences.tasks.groupByChallenge": user.preferences.tasks.groupByChallenge ? true: false})') + + hr + + button.btn.btn-primary(@click='showBailey()', popover-trigger='mouseenter', popover-placement='right', :popover="$t('showBaileyPop')") {{ $t('showBailey') }} + button.btn.btn-primary(@click='openRestoreModal()', popover-trigger='mouseenter', popover-placement='right', :popover="$t('fixValPop')") {{ $t('fixVal') }} + button.btn.btn-primary(v-if='user.preferences.disableClasses == true', @click='changeClass({})', + popover-trigger='mouseenter', popover-placement='right', :popover="$t('enableClassPop')") {{ $t('enableClass') }} + + hr + + div + h5 {{ $t('customDayStart') }} + .alert.alert-warning {{ $t('customDayStartInfo1') }} + .form-horizontal + .form-group + .col-7 + select.form-control(v-model='newDayStart') + option(v-for='option in dayStartOptions' :value='option.value') {{option.name}} + + .col-5 + button.btn.btn-block.btn-primary(@click='openDayStartModal()', + :disabled='newDayStart === user.preferences.dayStart') + | {{ $t('saveCustomDayStart') }} + hr + + h5 {{ $t('timezone') }} + .form-horizontal + .form-group + .col-12 + p(v-html="$t('timezoneUTC', {utc: timezoneOffsetToUtc})") + p(v-html="$t('timezoneInfo')") + + .col-6 + h2 {{ $t('registration') }} + .panel-body + div + ul.list-inline + li(v-for='network in SOCIAL_AUTH_NETWORKS') + button.btn.btn-primary(v-if='!user.auth[network.key].id', @click='socialLogin(network.key, user)') {{ $t('registerWithSocial', {network: network.name}) }} + button.btn.btn-primary(disabled='disabled', v-if='!hasBackupAuthOption(network.key) && user.auth[network.key].id') {{ $t('registeredWithSocial', {network: network.name}) }} + button.btn.btn-danger(@click='deleteSocialAuth(network.key)', v-if='hasBackupAuthOption(network.key) && user.auth[network.key].id') {{ $t('detachSocial', {network: network.name}) }} + hr + div(v-if='!user.auth.local.username') + p {{ $t('addLocalAuth') }} + form(ng-submit='http("post", "/api/v3/user/auth/local/register", localAuth, "addedLocalAuth")', name='localAuth', novalidate) + //-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted') {{ $t('fillAll') }} + .form-group + input.form-control(type='text', placeholder="$t('username')", v-model='localAuth.username', required) + .form-group + input.form-control(type='text', placeholder="$t('email')", v-model='localAuth.email', required) + .form-group + input.form-control(type='password', placeholder="$t('password')", v-model='localAuth.password', required) + .form-group + input.form-control(type='password', placeholder="$t('confirmPass')", v-model='localAuth.confirmPassword', required) + button.btn.btn-primary(type='submit', ng-disabled='localAuth.$invalid', value="$t('submit')") + + .usersettings(v-if='user.auth.local.username') + p {{ $t('username') }} + |: {{user.auth.local.username}} + p + small.muted + | {{ $t('loginNameDescription1') }} + | + a(href='/#/options/profile/profile') {{ $t('loginNameDescription2') }} + | + | {{ $t('loginNameDescription3') }} + p {{ $t('email') }} + |: {{user.auth.local.email}} + hr + + h5 {{ $t('changeUsername') }} + .form(v-if='user.auth.local', name='changeUsername', novalidate) + //-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted') {{ $t('fillAll') }} + .form-group + input.form-control(type='text', :placeholder="$t('newUsername')", v-model='usernameUpdates.username', required) + .form-group + input.form-control(type='password', :placeholder="$t('password')", v-model='usernameUpdates.password', required) + button.btn.btn-primary(type='submit', @click='changeUser("username", usernameUpdates)') {{ $t('submit') }} + + h5 {{ $t('changeEmail') }} + .form(v-if='user.auth.local', name='changeEmail', novalidate) + .form-group + input.form-control(type='text', :placeholder="$t('newEmail')", v-model='emailUpdates.newEmail', required) + .form-group + input.form-control(type='password', :placeholder="$t('password')", v-model='emailUpdates.password', required) + button.btn.btn-primary(type='submit', @click='changeUser("email", emailUpdates)') {{ $t('submit') }} + + h5 {{ $t('changePass') }} + .form(v-if='user.auth.local', name='changePassword', novalidate) + .form-group + input.form-control(type='password', :placeholder="$t('oldPass')", v-model='passwordUpdates.password', required) + .form-group + input.form-control(type='password', :placeholder="$t('newPass')", v-model='passwordUpdates.newPassword', required) + .form-group + input.form-control(type='password', :placeholder="$t('confirmPass')", v-model='passwordUpdates.confirmPassword', required) + button.btn.btn-primary(type='submit', @click='changeUser("password", passwordUpdates)') {{ $t('submit') }} + + div + h5 {{ $t('dangerZone') }} + div + button.btn.btn-danger(@click='openResetModal()', + popover-trigger='mouseenter', popover-placement='right', :popover="$t('resetAccPop')") {{ $t('resetAccount') }} + button.btn.btn-danger(@click='openDeleteModal()', + popover-trigger='mouseenter', :popover="$t('deleteAccPop')") {{ $t('deleteAccount') }} + + + + + diff --git a/website/client/components/settings/subscription.vue b/website/client/components/settings/subscription.vue new file mode 100644 index 0000000000..aee1c45913 --- /dev/null +++ b/website/client/components/settings/subscription.vue @@ -0,0 +1,427 @@ + + .standard-page + amazon-payments-modal(:amazon-payments='amazonPayments') + + h1 {{ $t('subscription') }} + .row + .col-6 + h2 {{ $t('benefits') }} + ul + li + span.hint(:popover="$t('buyGemsGoldText', {gemCostTranslation})", + popover-trigger='mouseenter', + popover-placement='right') {{ $t('buyGemsGold') }} + span.badge.badge-success(v-if='subscription.key !== "basic_earned"') {{ $t('buyGemsGoldCap', buyGemsGoldCap) }} + li + span.hint(:popover="$t('retainHistoryText')", popover-trigger='mouseenter', popover-placement='right') {{ $t('retainHistory') }} + li + span.hint(:popover="$t('doubleDropsText')", popover-trigger='mouseenter', popover-placement='right') {{ $t('doubleDrops') }} + li + span.hint(:popover="$t('mysteryItemText')", popover-trigger='mouseenter', popover-placement='right') {{ $t('mysteryItem') }} + div(v-if='subscription.key !== "basic_earned"') + .badge.badge-success {{ $t('mysticHourglass', mysticHourglass) }} + .small.muted {{ $t('mysticHourglassText') }} + li + span.hint(:popover="$t('exclusiveJackalopePetText')", popover-trigger='mouseenter', popover-placement='right') {{ $t('exclusiveJackalopePet') }} + li + span.hint(:popover="$t('supportDevsText')", popover-trigger='mouseenter', popover-placement='right') {{ $t('supportDevs') }} + + .col-6 + h2 Plan + table.table.alert.alert-info(v-if='hasSubscription') + tr(v-if='hasCanceledSubscription'): td.alert.alert-warning + span.noninteractive-button.btn-danger {{ $t('canceledSubscription') }} + i.glyphicon.glyphicon-time + | {{ $t('subCanceled') }} + strong {{user.purchased.plan.dateTerminated | date}} + tr(v-if='!hasCanceledSubscription'): td + h4 {{ $t('subscribed') }} + p(v-if='hasPlan && !hasGroupPlan') {{ $t('purchasedPlanId', {purchasedPlanIdInfo}) }} + p(v-if='hasGroupPlan') {{ $t('youHaveGroupPlan') }} + tr(v-if='user.purchased.plan.extraMonths'): td + span.glyphicon.glyphicon-credit-card + | {{ $t('purchasedPlanExtraMonths', {purchasedPlanExtraMonthsDetails}) }} + tr(v-if='hasConsecutiveSubscription'): td + span.glyphicon.glyphicon-forward + | {{ $t('consecutiveSubscription') }} + ul.list-unstyled + li {{ $t('consecutiveMonths') }} {{user.purchased.plan.consecutive.count + user.purchased.plan.consecutive.offset}} + li {{ $t('gemCapExtra') }}} {{user.purchased.plan.consecutive.gemCapExtra}} + li {{ $t('mysticHourglasses') }} {{user.purchased.plan.consecutive.trinkets}} + + div(v-if='!hasSubscription || hasCanceledSubscription') + h4(v-if='hasCanceledSubscription') {{ $t("resubscribe") }} + .form-group.reduce-top-margin + .radio(v-for='block in subscriptionBlocksOrdered', v-if="block.target !== 'group' && block.canSubscribe === true") + label + input(type="radio", name="subRadio", :value="block.key", v-model='subscription.key') + span(v-if='block.original') + span.label.label-success.line-through + | ${{block.original }} + | {{ $t('subscriptionRateText', {price: block.price, months: block.months}) }} + span(v-if='!block.original') + | {{ $t('subscriptionRateText', {price: block.price, months: block.months}) }} + + .form-inline + .form-group + input.form-control(type='text', v-model='subscription.coupon', :placeholder="$t('couponPlaceholder')") + .form-group + button.btn.btn-primary(type='button', @click='applyCoupon(subscription.coupon)') {{ $t("apply") }} + + div(v-if='hasSubscription') + .btn.btn-primary(v-if='canEditCardDetails', @click='showStripeEdit()') {{ $t('subUpdateCard') }} + .btn.btn-sm.btn-danger(v-if='canCancelSubscription', @click='cancelSubscription()') {{ $t('cancelSub') }} + small(v-if='!canCancelSubscription', v-html='getCancelSubInfo()') + + .subscribe-pay(v-if='!hasSubscription || hasCanceledSubscription') + h3 {{ $t('subscribeUsing') }} + .row.text-center + .col-md-4 + button.purchase.btn.btn-primary(@click='showStripe({subscription:subscription.key, coupon:subscription.coupon})', :disabled='!subscription.key') {{ $t('card') }} + .col-md-4 + a.purchase(:href='paypalPurchaseLink', :disabled='!subscription.key', target='_blank') + img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png', :alt="$t('paypal')") + .col-md-4 + a.purchase(@click="amazonPaymentsInit({type: 'subscription', subscription:subscription.key, coupon:subscription.coupon})") + img(src='https://payments.amazon.com/gp/cba/button', :alt="$t('amazonPayments')") + + .row + .col-6 + h2 {{ $t('giftSubscription') }} + ol + li {{ $t('giftSubscriptionText1') }} + li {{ $t('giftSubscriptionText2') }} + li {{ $t('giftSubscriptionText3') }} + h4 {{ $t('giftSubscriptionText4') }} + + + + + diff --git a/website/client/index.html b/website/client/index.html index 204f884234..0a4a7ea6a6 100644 --- a/website/client/index.html +++ b/website/client/index.html @@ -9,5 +9,11 @@
+ + + +