Files
habitica/website/client/src/components/admin-panel/user-support/userHistory.vue
Phillip Thelen 379afa9554 Improve Adminpanel with local logs (#15404)
* log armoire, quoest response and cron events to history

* show user history in admin panel

* allow stats to be edited from admin panel

* Improve admin panel stats input

* improve setting client in history

* fix tests

* fix lint

* fix armoire buying issue

* Improve hero saving

* Formatting fix

* Improve user history logging

* allow class to be changed from admin panel

* make terminating subscriptions easier

* support decimal extraMonths

* Fix editing some achievements in admin panel

* log if a user invites party to quest

* Log more quest events into user history

* make userhistory length configurable

* fix some numbered achievements

* fix extraMonths field

* Automatically set up group plan subs with admin panel

* show party info nicer in admin panel

* improve admin panel sub handling

* add missing brace

* display when there are unsaved changes

* fix setting group plan

* fix showing group id

* Display group plan info in admin panel

* fix setting hourglass promo date

* Improve termination handling in admin panel

* reload data after certain save events in admin panel

* remove console

* fix plan.extraMonths not being reset if terminating a sub

* add more options when cancelling subs

* reload data after group plan change

* Add a way to remove users from a party

* fix issue with removing user from party

* pass party id correctly

* correctly call async function

* Improve sub display in admin panel

* fix line length

* fix line

* shorter

* plaid

* fix(lint): vue code style

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-03-17 16:48:21 -05:00

264 lines
6.4 KiB
Vue

<template>
<div class="card mt-2">
<div class="card-header">
<h3
class="mb-0 mt-0"
:class="{'open': expand}"
@click="toggleHistoryOpen"
>
User History
</h3>
</div>
<div
v-if="expand"
class="card-body"
>
<div>
<div class="clearfix">
<div class="mb-4 float-left">
<button
class="page-header btn-flat tab-button textCondensed"
:class="{'active': selectedTab === 'armoire'}"
@click="selectTab('armoire')"
>
Armoire
</button>
<button
class="page-header btn-flat tab-button textCondensed"
:class="{'active': selectedTab === 'questInvites'}"
@click="selectTab('questInvites')"
>
Quest Invitations
</button>
<button
class="page-header btn-flat tab-button textCondensed"
:class="{'active': selectedTab === 'cron'}"
@click="selectTab('cron')"
>
Cron
</button>
</div>
</div>
<div class="row">
<div
v-if="selectedTab === 'armoire'"
class="col-12"
>
<table class="table">
<tr>
<th
v-once
>
{{ $t('timestamp') }}
</th>
<th v-once>
Client
</th>
<th
v-once
>
Received
</th>
</tr>
<tr
v-for="entry in armoire"
:key="entry.timestamp"
>
<td>
<span
v-b-tooltip.hover="entry.timestamp"
>{{ entry.timestamp | timeAgo }}</span>
</td>
<td>{{ entry.client }}</td>
<td>{{ entry.reward }}</td>
</tr>
</table>
</div>
<div
v-if="selectedTab === 'questInvites'"
class="col-12"
>
<table class="table">
<tr>
<th
v-once
>
{{ $t('timestamp') }}
</th>
<th v-once>
Client
</th>
<th v-once>
Quest Key
</th>
<th v-once>
Response
</th>
</tr>
<tr
v-for="entry in questInviteResponses"
:key="entry.timestamp"
>
<td>
<span
v-b-tooltip.hover="entry.timestamp"
>{{ entry.timestamp | timeAgo }}</span>
</td>
<td>{{ entry.client }}</td>
<td>{{ entry.quest }}</td>
<td>{{ questInviteResponseText(entry.response) }}</td>
</tr>
</table>
</div>
<div
v-if="selectedTab === 'cron'"
class="col-12"
>
<table class="table">
<tr>
<th
v-once
>
{{ $t('timestamp') }}
</th>
<th v-once>
Client
</th>
<th v-once>
Checkin Count
</th>
</tr>
<tr
v-for="entry in cron"
:key="entry.timestamp"
>
<td>
<span
v-b-tooltip.hover="entry.timestamp"
>{{ entry.timestamp | timeAgo }}</span>
</td>
<td>{{ entry.client }}</td>
<td>{{ entry.checkinCount }}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
@import '~@/assets/scss/colors.scss';
.page-header.btn-flat {
background: transparent;
}
.tab-button {
height: 2rem;
font-size: 24px;
font-weight: bold;
font-stretch: condensed;
line-height: 1.33;
letter-spacing: normal;
color: $gray-10;
margin-right: 1.125rem;
padding-left: 0;
padding-right: 0;
padding-bottom: 2.5rem;
&.active, &:hover {
color: $purple-300;
box-shadow: 0px -0.25rem 0px $purple-300 inset;
outline: none;
}
}
</style>
<script>
import moment from 'moment';
import { userStateMixin } from '../../../mixins/userState';
export default {
filters: {
timeAgo (value) {
return moment(value).fromNow();
},
},
mixins: [userStateMixin],
props: {
hero: {
type: Object,
required: true,
},
resetCounter: {
type: Number,
required: true,
},
},
data () {
return {
expand: false,
selectedTab: 'armoire',
armoire: [],
questInviteResponses: [],
cron: [],
};
},
watch: {
resetCounter () {
if (this.expand) {
this.retrieveUserHistory();
}
},
},
methods: {
selectTab (type) {
this.selectedTab = type;
},
async toggleHistoryOpen () {
this.expand = !this.expand;
if (this.expand) {
this.retrieveUserHistory();
}
},
async retrieveUserHistory () {
const history = await this.$store.dispatch('adminPanel:getUserHistory', { userIdentifier: this.hero._id });
this.armoire = history.armoire;
this.questInviteResponses = history.questInviteResponses;
this.cron = history.cron;
},
questInviteResponseText (response) {
if (response === 'accept') {
return 'Accepted';
}
if (response === 'reject') {
return 'Rejected';
}
if (response === 'leave') {
return 'Left active quest';
}
if (response === 'invite') {
return 'Accepted as owner';
}
if (response === 'abort') {
return 'Aborted by owner';
}
if (response === 'abortByLeader') {
return 'Aborted by party leader';
}
if (response === 'cancel') {
return 'Cancelled before start';
}
if (response === 'cancelByLeader') {
return 'Cancelled before start by party leader';
}
return response;
},
},
};
</script>