mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
Merge branch 'sabrecat/panel-subscription' into release
This commit is contained in:
@@ -22,11 +22,6 @@
|
|||||||
Account created:
|
Account created:
|
||||||
<strong>{{ hero.auth.timestamps.created | formatDate }}</strong>
|
<strong>{{ hero.auth.timestamps.created | formatDate }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
Most recent cron:
|
|
||||||
<strong>{{ hero.auth.timestamps.loggedin | formatDate }}</strong>
|
|
||||||
("auth.timestamps.loggedin")
|
|
||||||
</div>
|
|
||||||
<div v-if="cronError">
|
<div v-if="cronError">
|
||||||
"lastCron" value:
|
"lastCron" value:
|
||||||
<strong>{{ hero.lastCron | formatDate }}</strong>
|
<strong>{{ hero.lastCron | formatDate }}</strong>
|
||||||
@@ -36,6 +31,19 @@
|
|||||||
("auth.timestamps.loggedin" and "lastCron" dates are different).
|
("auth.timestamps.loggedin" and "lastCron" dates are different).
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-inline">
|
||||||
|
<div>
|
||||||
|
Most recent cron:
|
||||||
|
<strong>{{ hero.auth.timestamps.loggedin | formatDate }}</strong>
|
||||||
|
("auth.timestamps.loggedin")
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="btn btn-primary ml-2"
|
||||||
|
@click="resetCron()"
|
||||||
|
>
|
||||||
|
Reset Cron to Yesterday
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="subsection-start">
|
<div class="subsection-start">
|
||||||
Time zone:
|
Time zone:
|
||||||
<strong>{{ hero.preferences.timezoneOffset | formatTimeZone }}</strong>
|
<strong>{{ hero.preferences.timezoneOffset | formatTimeZone }}</strong>
|
||||||
@@ -218,6 +226,10 @@ export default {
|
|||||||
await this.saveHero({ hero: this.hero, msg: 'API Token' });
|
await this.saveHero({ hero: this.hero, msg: 'API Token' });
|
||||||
this.tokenModified = true;
|
this.tokenModified = true;
|
||||||
},
|
},
|
||||||
|
resetCron () {
|
||||||
|
this.hero.resetCron = true;
|
||||||
|
this.saveHero({ hero: this.hero, msg: 'Last Cron', clearData: true });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
:reset-counter="resetCounter"
|
:reset-counter="resetCounter"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<subscription-and-perks
|
||||||
|
:hero="hero"
|
||||||
|
/>
|
||||||
|
|
||||||
<cron-and-auth
|
<cron-and-auth
|
||||||
:hero="hero"
|
:hero="hero"
|
||||||
:reset-counter="resetCounter"
|
:reset-counter="resetCounter"
|
||||||
@@ -97,6 +101,7 @@ import AvatarAndDrops from './avatarAndDrops';
|
|||||||
import PrivilegesAndGems from './privilegesAndGems';
|
import PrivilegesAndGems from './privilegesAndGems';
|
||||||
import ContributorDetails from './contributorDetails';
|
import ContributorDetails from './contributorDetails';
|
||||||
import Transactions from './transactions';
|
import Transactions from './transactions';
|
||||||
|
import SubscriptionAndPerks from './subscriptionAndPerks';
|
||||||
|
|
||||||
import { userStateMixin } from '../../../mixins/userState';
|
import { userStateMixin } from '../../../mixins/userState';
|
||||||
|
|
||||||
@@ -110,6 +115,7 @@ export default {
|
|||||||
PrivilegesAndGems,
|
PrivilegesAndGems,
|
||||||
ContributorDetails,
|
ContributorDetails,
|
||||||
Transactions,
|
Transactions,
|
||||||
|
SubscriptionAndPerks,
|
||||||
},
|
},
|
||||||
mixins: [userStateMixin],
|
mixins: [userStateMixin],
|
||||||
data () {
|
data () {
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div class="accordion-group">
|
||||||
|
<h3
|
||||||
|
class="expand-toggle"
|
||||||
|
:class="{'open': expand}"
|
||||||
|
@click="expand = !expand"
|
||||||
|
>
|
||||||
|
Subscription, Monthly Perks
|
||||||
|
</h3>
|
||||||
|
<div v-if="expand">
|
||||||
|
<form @submit.prevent="saveHero({ hero, msg: 'Subscription Perks' })">
|
||||||
|
<div v-if="hero.purchased.plan.paymentMethod">
|
||||||
|
Payment method:
|
||||||
|
<strong>{{ hero.purchased.plan.paymentMethod }}</strong>
|
||||||
|
</div>
|
||||||
|
<div v-if="hero.purchased.plan.planId">
|
||||||
|
Payment schedule ("basic-earned" is monthly):
|
||||||
|
<strong>{{ hero.purchased.plan.planId }}</strong>
|
||||||
|
</div>
|
||||||
|
<div v-if="hero.purchased.plan.dateCreated">
|
||||||
|
Creation date:
|
||||||
|
<strong>{{ dateFormat(hero.purchased.plan.dateCreated) }}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Termination date:
|
||||||
|
<strong
|
||||||
|
v-if="hero.purchased.plan.dateTerminated"
|
||||||
|
>
|
||||||
|
{{ dateFormat(hero.purchased.plan.dateTerminated) }}
|
||||||
|
</strong>
|
||||||
|
<strong v-else> None </strong>
|
||||||
|
</div>
|
||||||
|
<div class="form-inline">
|
||||||
|
<label>
|
||||||
|
Consecutive months:
|
||||||
|
<input
|
||||||
|
v-model="hero.purchased.plan.consecutive.count"
|
||||||
|
class="form-control"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Months until renewal:
|
||||||
|
<strong>{{ hero.purchased.plan.consecutive.offset }}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Next Mystic Hourglass:
|
||||||
|
<strong>{{ nextHourglassDate }}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="form-inline">
|
||||||
|
<label>
|
||||||
|
Mystic Hourglasses:
|
||||||
|
<input
|
||||||
|
v-model="hero.purchased.plan.consecutive.trinkets"
|
||||||
|
class="form-control"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Gem cap:
|
||||||
|
<strong>{{ hero.purchased.plan.consecutive.gemCapExtra + 25 }}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="form-inline">
|
||||||
|
<label>
|
||||||
|
Gems bought this month:
|
||||||
|
<input
|
||||||
|
v-model="hero.purchased.plan.gemsBought"
|
||||||
|
class="form-control"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
:max="hero.purchased.plan.consecutive.gemCapExtra + 25"
|
||||||
|
step="1"
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="hero.purchased.plan.extraMonths > 0"
|
||||||
|
>
|
||||||
|
Additional credit (applied upon cancellation):
|
||||||
|
<strong>{{ hero.purchased.plan.extraMonths }}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Mystery Items:
|
||||||
|
<span
|
||||||
|
v-if="hero.purchased.plan.mysteryItems.length > 0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-for="(item, index) in hero.purchased.plan.mysteryItems"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<strong v-if="index < hero.purchased.plan.mysteryItems.length - 1">
|
||||||
|
{{ item }},
|
||||||
|
</strong>
|
||||||
|
<strong v-else> {{ item }} </strong>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<strong>None</strong>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
value="Save"
|
||||||
|
class="btn btn-primary mt-1"
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import moment from 'moment';
|
||||||
|
import saveHero from '../mixins/saveHero';
|
||||||
|
import { getPlanContext } from '@/../../common/script/cron';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [saveHero],
|
||||||
|
props: {
|
||||||
|
hero: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
expand: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
nextHourglassDate () {
|
||||||
|
const currentPlanContext = getPlanContext(this.hero, new Date());
|
||||||
|
|
||||||
|
return currentPlanContext.nextHourglassDate.format('MMMM');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'hero.purchased.plan.consecutive.count' () { // eslint-disable-line object-shorthand
|
||||||
|
this.hero.purchased.plan.consecutive.gemCapExtra = Math.min(
|
||||||
|
Math.floor(this.hero.purchased.plan.consecutive.count / 3) * 5, 25,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
dateFormat (date) {
|
||||||
|
return moment(date).format('YYYY/MM/DD');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -213,5 +213,6 @@
|
|||||||
"transaction_release_mounts": "Released mounts",
|
"transaction_release_mounts": "Released mounts",
|
||||||
"transaction_reroll": "Used Fortify Potion",
|
"transaction_reroll": "Used Fortify Potion",
|
||||||
"transaction_subscription_perks": "From subscription perk",
|
"transaction_subscription_perks": "From subscription perk",
|
||||||
"transaction_admin_update_balance": "Admin given"
|
"transaction_admin_update_balance": "Admin given",
|
||||||
|
"transaction_admin_update_hourglasses": "Admin updated"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,6 +273,28 @@ api.updateHero = {
|
|||||||
hero.balance = updateData.balance;
|
hero.balance = updateData.balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (updateData.purchased && updateData.purchased.plan) {
|
||||||
|
if (updateData.purchased.plan.gemsBought) {
|
||||||
|
hero.purchased.plan.gemsBought = updateData.purchased.plan.gemsBought;
|
||||||
|
}
|
||||||
|
if (updateData.purchased.plan.consecutive) {
|
||||||
|
if (updateData.purchased.plan.consecutive.trinkets) {
|
||||||
|
await hero.updateHourglasses(
|
||||||
|
updateData.purchased.plan.consecutive.trinkets
|
||||||
|
- hero.purchased.plan.consecutive.trinkets,
|
||||||
|
'admin_update_hourglasses', '', 'Updated by Habitica staff',
|
||||||
|
);
|
||||||
|
hero.purchased.plan.consecutive.trinkets = updateData.purchased.plan.consecutive.trinkets;
|
||||||
|
}
|
||||||
|
if (updateData.purchased.plan.consecutive.gemCapExtra) {
|
||||||
|
hero.purchased.plan.consecutive.gemCapExtra = updateData.purchased.plan.consecutive.gemCapExtra; // eslint-disable-line max-len
|
||||||
|
}
|
||||||
|
if (updateData.purchased.plan.consecutive.count) {
|
||||||
|
hero.purchased.plan.consecutive.count = updateData.purchased.plan.consecutive.count; // eslint-disable-line max-len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// give them gems if they got an higher level
|
// give them gems if they got an higher level
|
||||||
// tier = level in this context
|
// tier = level in this context
|
||||||
let newTier = updateData.contributor && updateData.contributor.level;
|
let newTier = updateData.contributor && updateData.contributor.level;
|
||||||
@@ -331,6 +353,13 @@ api.updateHero = {
|
|||||||
hero.apiToken = common.uuid();
|
hero.apiToken = common.uuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (updateData.resetCron) {
|
||||||
|
// Set last cron to yesterday. Quick approach so we don't need moment() for one line
|
||||||
|
const yesterday = new Date(new Date().setDate(new Date().getDate() - 1));
|
||||||
|
hero.lastCron = yesterday;
|
||||||
|
hero.auth.timestamps.loggedin = yesterday; // so admin panel doesn't gripe about mismatch
|
||||||
|
}
|
||||||
|
|
||||||
const savedHero = await hero.save();
|
const savedHero = await hero.save();
|
||||||
const heroJSON = savedHero.toJSON();
|
const heroJSON = savedHero.toJSON();
|
||||||
heroJSON.secret = savedHero.getSecretData();
|
heroJSON.secret = savedHero.getSecretData();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import baseModel from '../libs/baseModel';
|
|||||||
const { Schema } = mongoose;
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
export const currencies = ['gems', 'hourglasses'];
|
export const currencies = ['gems', 'hourglasses'];
|
||||||
export const transactionTypes = ['buy_money', 'buy_gold', 'spend', 'gift_send', 'gift_receive', 'debug', 'create_challenge', 'create_bank_challenge', 'create_guild', 'change_class', 'rebirth', 'release_pets', 'release_mounts', 'reroll', 'contribution', 'subscription_perks', 'admin_update_balance'];
|
export const transactionTypes = ['buy_money', 'buy_gold', 'spend', 'gift_send', 'gift_receive', 'debug', 'create_challenge', 'create_bank_challenge', 'create_guild', 'change_class', 'rebirth', 'release_pets', 'release_mounts', 'reroll', 'contribution', 'subscription_perks', 'admin_update_balance', 'admin_update_hourglasses'];
|
||||||
|
|
||||||
export const schema = new Schema({
|
export const schema = new Schema({
|
||||||
currency: { $type: String, enum: currencies, required: true },
|
currency: { $type: String, enum: currencies, required: true },
|
||||||
|
|||||||
@@ -577,3 +577,22 @@ schema.methods.updateBalance = async function updateBalance (amount,
|
|||||||
currentAmount: this.balance,
|
currentAmount: this.balance,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
schema.methods.updateHourglasses = async function updateHourglasses (
|
||||||
|
amount,
|
||||||
|
transactionType,
|
||||||
|
reference,
|
||||||
|
referenceText,
|
||||||
|
) {
|
||||||
|
this.purchased.plan.consecutive.trinkets += amount;
|
||||||
|
|
||||||
|
await Transaction.create({
|
||||||
|
currency: 'hourglasses',
|
||||||
|
userId: this._id,
|
||||||
|
transactionType,
|
||||||
|
amount,
|
||||||
|
reference,
|
||||||
|
referenceText,
|
||||||
|
currentAmount: this.purchased.plan.consecutive.trinkets,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user