This commit is contained in:
Phillip Thelen
2025-08-22 14:04:03 +02:00
parent bd46740d0e
commit 118840d6ce
7 changed files with 186 additions and 134 deletions

View File

@@ -5,12 +5,19 @@
class="row" class="row"
> >
<div class="form col-12"> <div class="form col-12">
<div class="btn-group float-right">
<button <button
class="btn btn-danger mt-3 float-right" class="btn btn-danger"
@click="confirmDeleteHero" @click="confirmDeleteHero"
> >
Begin Member deletion <span
v-once
class="svg-icon icon-16 mt-1 mb-1"
v-html="icons.deleteIcon"
></span>
</button> </button>
</div>
<basic-details <basic-details
:user-id="hero._id" :user-id="hero._id"
:auth="hero.auth" :auth="hero.auth"
@@ -18,7 +25,6 @@
:profile="hero.profile" :profile="hero.profile"
/> />
<privileges-and-gems <privileges-and-gems
:hero="hero" :hero="hero"
:reset-counter="resetCounter" :reset-counter="resetCounter"
@@ -202,6 +208,7 @@ import CustomizationsOwned from './customizationsOwned.vue';
import Achievements from './achievements.vue'; import Achievements from './achievements.vue';
import UserHistory from './userHistory.vue'; import UserHistory from './userHistory.vue';
import Stats from './stats.vue'; import Stats from './stats.vue';
import deleteIcon from '@/assets/svg/delete.svg?raw';
import { userStateMixin } from '../../../../mixins/userState'; import { userStateMixin } from '../../../../mixins/userState';
@@ -240,6 +247,9 @@ export default {
adminHasPrivForParty: true, adminHasPrivForParty: true,
deleteHabiticaAccount: true, deleteHabiticaAccount: true,
deleteAmplitudeData: true, deleteAmplitudeData: true,
icons: Object.freeze({
deleteIcon,
}),
}; };
}, },
watch: { watch: {

View File

@@ -341,16 +341,16 @@ export default {
expand: false, expand: false,
}; };
}, },
computed: {
userIsPartyLeader () {
return this.groupPartyData.leader === this.userId;
},
},
watch: { watch: {
resetCounter () { resetCounter () {
resetData(this); resetData(this);
}, },
}, },
computed: {
userIsPartyLeader () {
return this.groupPartyData.leader === this.userId;
}
},
mounted () { mounted () {
resetData(this); resetData(this);
}, },
@@ -363,11 +363,13 @@ export default {
}); });
}, },
async makePartyLeader () { async makePartyLeader () {
await this.$store.dispatch('guilds:update', { group: { await this.$store.dispatch('guilds:update', {
group: {
id: this.groupPartyData._id, id: this.groupPartyData._id,
leader: this.userId, leader: this.userId,
} }); },
} });
},
}, },
}; };
</script> </script>

View File

@@ -136,8 +136,9 @@
</div> </div>
</div> </div>
<formRow <formRow
v-model="hero.purchased.plan.customerId"
label="Customer ID" label="Customer ID"
v-model="hero.purchased.plan.customerId" /> />
<div <div
v-if="hero.purchased.plan.planId === 'group_plan_auto'" v-if="hero.purchased.plan.planId === 'group_plan_auto'"
class="form-group row" class="form-group row"
@@ -188,19 +189,22 @@
</div> </div>
</div> </div>
<formRow <formRow
label="Creation date"
v-if="hero.purchased.plan.dateCreated" v-if="hero.purchased.plan.dateCreated"
v-model="hero.purchased.plan.dateCreated" v-model="hero.purchased.plan.dateCreated"
:suffix="dateFormat(hero.purchased.plan.dateCreated)" /> label="Creation date"
:suffix="dateFormat(hero.purchased.plan.dateCreated)"
/>
<formRow <formRow
label="Current sub start date"
v-if="hero.purchased.plan.dateCurrentTypeCreated" v-if="hero.purchased.plan.dateCurrentTypeCreated"
v-model="hero.purchased.plan.dateCurrentTypeCreated" v-model="hero.purchased.plan.dateCurrentTypeCreated"
:suffix="dateFormat(hero.purchased.plan.dateCurrentTypeCreated)" /> label="Current sub start date"
:suffix="dateFormat(hero.purchased.plan.dateCurrentTypeCreated)"
/>
<formRow <formRow
label="Termination date"
v-model="hero.purchased.plan.dateTerminated" v-model="hero.purchased.plan.dateTerminated"
:suffix="dateFormat(hero.purchased.plan.dateTerminated)"> label="Termination date"
:suffix="dateFormat(hero.purchased.plan.dateTerminated)"
>
<template #suffix> <template #suffix>
<strong class="input-group-text"> <strong class="input-group-text">
{{ dateFormat(hero.purchased.plan.dateTerminated) }} {{ dateFormat(hero.purchased.plan.dateTerminated) }}
@@ -214,23 +218,31 @@
Terminate Terminate
</a> </a>
</template> </template>
<template #helpText v-if="isSubscribed() && !isCancelled()"> <template
v-if="isSubscribed() && !isCancelled()"
#helpText
>
<span class="text-success"> <span class="text-success">
The subscription does not have a termination date and is active. The subscription does not have a termination date and is active.
</span> </span>
</template> </template>
</formRow> </formRow>
<formRow <formRow
label="Cumulative months"
v-model="hero.purchased.plan.cumulativeCount" v-model="hero.purchased.plan.cumulativeCount"
inputType="number" label="Cumulative months"
helpText="Cumulative subscribed months across subscription periods." /> input-type="number"
help-text="Cumulative subscribed months across subscription periods."
/>
<formRow <formRow
label="Extra months"
v-model="hero.purchased.plan.extraMonths" v-model="hero.purchased.plan.extraMonths"
inputType="number" label="Extra months"
helpText="Additional credit that is applied if a subscription is cancelled."> input-type="number"
<template #suffix v-if="hero.purchased.plan.dateTerminated && hero.purchased.plan.extraMonths > 0"> help-text="Additional credit that is applied if a subscription is cancelled."
>
<template
v-if="hero.purchased.plan.dateTerminated && hero.purchased.plan.extraMonths > 0"
#suffix
>
<a <a
class="btn btn-warning" class="btn btn-warning"
@click="applyExtraMonths" @click="applyExtraMonths"
@@ -240,21 +252,24 @@
</template> </template>
</formRow> </formRow>
<formRow <formRow
label="Received hourglass bonus"
v-model="hero.purchased.plan.hourglassPromoReceived" v-model="hero.purchased.plan.hourglassPromoReceived"
:suffix="dateFormat(hero.purchased.plan.hourglassPromoReceived)" /> label="Received hourglass bonus"
:suffix="dateFormat(hero.purchased.plan.hourglassPromoReceived)"
/>
<formRow <formRow
label="Mystic Hourglasses"
v-model="hero.purchased.plan.consecutive.trinkets" v-model="hero.purchased.plan.consecutive.trinkets"
inputType="number" label="Mystic Hourglasses"
min="0" /> input-type="number"
min="0"
/>
<formRow <formRow
label="Gem cap increase"
v-model="hero.purchased.plan.consecutive.gemCapExtra" v-model="hero.purchased.plan.consecutive.gemCapExtra"
inputType="number" label="Gem cap increase"
input-type="number"
min="0" min="0"
max="26" max="26"
step="2" /> step="2"
/>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label"> <label class="col-sm-3 col-form-label">
Total Gem cap: Total Gem cap:
@@ -264,11 +279,12 @@
</strong> </strong>
</div> </div>
<formRow <formRow
label="Gems bought this month"
v-model="hero.purchased.plan.gemsBought" v-model="hero.purchased.plan.gemsBought"
inputType="number" label="Gems bought this month"
input-type="number"
min="0" min="0"
:max="hero.purchased.plan.consecutive.gemCapExtra + 24" /> :max="hero.purchased.plan.consecutive.gemCapExtra + 24"
/>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label"> <label class="col-sm-3 col-form-label">
Mystery Items: Mystery Items:
@@ -601,8 +617,8 @@ export default {
return `${PLAY_CONSOLE_ORDERS_BASE_URL}${this.paymentDetails?.transactionId || ''}`; return `${PLAY_CONSOLE_ORDERS_BASE_URL}${this.paymentDetails?.transactionId || ''}`;
}, },
isGroupPlanMember () { isGroupPlanMember () {
return this.hero.purchased.plan.planId === 'group_plan_auto' return this.hero.purchased.plan.planId === 'group_plan_auto';
} },
}, },
methods: { methods: {
dateFormat (date) { dateFormat (date) {

View File

@@ -1,8 +1,10 @@
<template> <template>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3 col-form-label"><slot name="label">{{ label }}:</slot></label> <label class="col-sm-3 col-form-label"><slot name="label">{{ label }}:</slot></label>
<div class="col-sm-9" <div
:class="editable ? 'editable' : 'col-form-label'"> class="col-sm-9"
:class="editable ? 'editable' : 'col-form-label'"
>
<slot> <slot>
<div class="input-group"> <div class="input-group">
<strong v-if="!editable"> <strong v-if="!editable">
@@ -20,12 +22,15 @@
:value="value" :value="value"
class="form-control" class="form-control"
:type="inputType" :type="inputType"
@input="$emit('input', $event.target.value)"
:min="min" :min="min"
:max="max" :max="max"
:step="step" :step="step"
@input="$emit('input', $event.target.value)"
>
<div
v-if="suffix || $slots.suffix"
class="input-group-append"
> >
<div class="input-group-append" v-if="suffix || $slots.suffix">
<slot name="suffix"> <slot name="suffix">
<strong class="input-group-text"> <strong class="input-group-text">
{{ suffix }} {{ suffix }}
@@ -34,10 +39,18 @@
</div> </div>
</div> </div>
</slot> </slot>
<div class="form-text text-muted" v-if="helpText || $slots.helpText"> <div
<slot name="helpText">{{ helpText }}</slot> v-if="helpText || $slots.helpText"
class="form-text text-muted"
>
<slot name="helpText">
{{ helpText }}
</slot>
</div> </div>
<div class="form-text text-muted mt-1" v-if="$slots.subtitle"> <div
v-if="$slots.subtitle"
class="form-text text-muted mt-1"
>
<slot name="subtitle"></slot> <slot name="subtitle"></slot>
</div> </div>
</div> </div>
@@ -60,8 +73,7 @@
</style> </style>
<script> <script>
import { max } from 'lodash'; import { max, min } from 'lodash';
import { min } from 'lodash';
export default { export default {
model: { model: {
@@ -95,20 +107,20 @@ export default {
min: { min: {
type: [Number, String], type: [Number, String],
default: 0, default: 0,
validator(value) { validator (value) {
return !isNaN(value) && min([value, 0]) === 0; return !isNaN(value) && min([value, 0]) === 0;
}, },
}, },
max: { max: {
type: [Number, String], type: [Number, String],
validator(value) { validator (value) {
return !isNaN(value) && max([value, 100]) === 100; return !isNaN(value) && max([value, 100]) === 100;
}, },
}, },
step: { step: {
type: [Number, String], type: [Number, String],
default: 1, default: 1,
validator(value) { validator (value) {
return !isNaN(value) && min([value, 1]) === 1; return !isNaN(value) && min([value, 1]) === 1;
}, },
}, },

View File

@@ -1,25 +1,30 @@
<template> <template>
<div v-if="group.purchased.plan"> <div v-if="group.purchased.plan">
<form-row <form-row
label="Payment Method"
v-model="group.purchased.plan.paymentMethod" v-model="group.purchased.plan.paymentMethod"
:editable="false" /> label="Payment Method"
:editable="false"
/>
<form-row <form-row
label="Plan ID"
v-model="group.purchased.plan.planId" v-model="group.purchased.plan.planId"
:editable="false" /> label="Plan ID"
:editable="false"
/>
<form-row <form-row
label="Customer ID"
v-model="group.purchased.plan.customerId" v-model="group.purchased.plan.customerId"
:editable="false" /> label="Customer ID"
:editable="false"
/>
<form-row <form-row
label="Creation Date"
v-model="group.purchased.plan.dateCreated" v-model="group.purchased.plan.dateCreated"
:editable="false" /> label="Creation Date"
:editable="false"
/>
<form-row <form-row
label="Termination Date"
v-model="group.purchased.plan.dateTerminated" v-model="group.purchased.plan.dateTerminated"
:editable="false" /> label="Termination Date"
:editable="false"
/>
</div> </div>
</template> </template>

View File

@@ -3,32 +3,37 @@
<h2>{{ group.name }}</h2> <h2>{{ group.name }}</h2>
<router-link <router-link
v-if="isGroupPlan" v-if="isGroupPlan"
:to="{'name': 'groupPlanDetail', 'params': {'groupId': groupId}}"> :to="{'name': 'groupPlanDetail', 'params': {'groupId': groupId}}"
>
Group Plan Page Group Plan Page
</router-link> </router-link>
<supportContainer <supportContainer
:title="$t('groupData')" :title="$t('groupData')"
:onSave="updateGroup"> :on-save="updateGroup"
>
<groupData <groupData
:group="group" :group="group"
/> />
</supportContainer> </supportContainer>
<supportContainer <supportContainer
:title="$t('groupPlanSubscription')"> :title="$t('groupPlanSubscription')"
>
<groupPlan <groupPlan
:group="group" :group="group"
/> />
</supportContainer> </supportContainer>
<supportContainer <supportContainer
v-if="group.type === 'party'" v-if="group.type === 'party'"
:title="$t('questDetails')"> :title="$t('questDetails')"
>
<quest <quest
:group="group" :group="group"
/> />
</supportContainer> </supportContainer>
<supportContainer <supportContainer
:title="$t('members')"> :title="$t('members')"
>
<members <members
:group="group" :group="group"
/> />
@@ -59,14 +64,6 @@ export default {
group: {}, group: {},
}; };
}, },
watch: {
groupId () {
this.loadGroup(this.groupId);
},
},
mounted () {
this.groupId = this.$route.params.groupId;
},
computed: { computed: {
isGroupPlan () { isGroupPlan () {
return this.group return this.group
@@ -75,6 +72,14 @@ export default {
&& this.group.purchased.plan.planId; && this.group.purchased.plan.planId;
}, },
}, },
watch: {
groupId () {
this.loadGroup(this.groupId);
},
},
mounted () {
this.groupId = this.$route.params.groupId;
},
methods: { methods: {
clearData () { clearData () {
this.group = {}; this.group = {};
@@ -82,17 +87,16 @@ export default {
async loadGroup (groupId) { async loadGroup (groupId) {
this.$emit('changeGroupId', groupId); this.$emit('changeGroupId', groupId);
this.group = await this.$store.dispatch('admin:getGroup', { groupId }); this.group = await this.$store.dispatch('admin:getGroup', { groupId });
}, },
async updateGroup () { async updateGroup () {
if (this.group && !this.group.id) { if (this.group && !this.group.id) {
this.group.id = this.group._id || this.groupId; // Ensure group has an id property this.group.id = this.group._id || this.groupId; // Ensure group has an id property
} }
await this.$store.dispatch('guilds:update', { group: this.group }); await this.$store.dispatch('guilds:update', { group: this.group });
this.group = await this.$store.dispatch('admin:getGroup', { groupId: this.group.id }) this.group = await this.$store.dispatch('admin:getGroup', { groupId: this.group.id });
await this.$store.dispatch('snackbars:add', { await this.$store.dispatch('snackbars:add', {
title: '', title: '',
text: `Group updated`, text: 'Group updated',
type: 'info', type: 'info',
}); });
}, },

View File

@@ -1,23 +1,26 @@
<template> <template>
<div> <div>
<form-row <form-row
label="Quest Identifier"
v-model="group.quest.key" v-model="group.quest.key"
:editable="false" /> label="Quest Identifier"
:editable="false"
/>
<form-row <form-row
label="Quest Leader"
v-model="group.quest.leader" v-model="group.quest.leader"
:editable="false"> label="Quest Leader"
:editable="false"
>
<template slot="subtitle"> <template slot="subtitle">
<router-link <router-link
:to="{'name': 'adminPanelUser', 'params': {'userIdentifier': group.quest.leader}}"> :to="{'name': 'adminPanelUser', 'params': {'userIdentifier': group.quest.leader}}"
>
{{ group.quest.leader }} {{ group.quest.leader }}
</router-link> </router-link>
</template> </template>
</form-row> </form-row>
<form-row <form-row
label="Is Quest Active"
v-model="group.quest.active" v-model="group.quest.active"
label="Is Quest Active"
input-type="checkbox" input-type="checkbox"
/> />
</div> </div>