diff --git a/test/api/v3/integration/hall/GET-hall_heroes_heroId.test.js b/test/api/v3/integration/hall/GET-hall_heroes_heroId.test.js index c83e4cbaf1..6d02f1a2a4 100644 --- a/test/api/v3/integration/hall/GET-hall_heroes_heroId.test.js +++ b/test/api/v3/integration/hall/GET-hall_heroes_heroId.test.js @@ -10,6 +10,7 @@ describe('GET /heroes/:heroId', () => { const heroFields = [ '_id', 'id', 'auth', 'balance', 'contributor', 'flags', 'items', 'lastCron', 'party', 'preferences', 'profile', 'purchased', 'secret', 'achievements', + 'stats', ]; before(async () => { diff --git a/test/api/v3/integration/hall/PUT-hall_heores_heroId.test.js b/test/api/v3/integration/hall/PUT-hall_heores_heroId.test.js index 020b5a56ae..b459664c33 100644 --- a/test/api/v3/integration/hall/PUT-hall_heores_heroId.test.js +++ b/test/api/v3/integration/hall/PUT-hall_heores_heroId.test.js @@ -11,6 +11,7 @@ describe('PUT /heroes/:heroId', () => { const heroFields = [ '_id', 'auth', 'balance', 'contributor', 'flags', 'items', 'lastCron', 'party', 'preferences', 'profile', 'purchased', 'secret', 'permissions', 'achievements', + 'stats', ]; before(async () => { diff --git a/website/client/src/components/admin-panel/index.vue b/website/client/src/components/admin-panel/index.vue index c31d729042..e9c9436153 100644 --- a/website/client/src/components/admin-panel/index.vue +++ b/website/client/src/components/admin-panel/index.vue @@ -92,8 +92,6 @@ export default { params: { userIdentifier }, }).catch(failure => { if (isNavigationFailure(failure, NavigationFailureType.duplicated)) { - // the admin has requested that the same user be displayed again so reload the page - // (e.g., if they changed their mind about changes they were making) this.$router.go(); } }); @@ -101,14 +99,16 @@ export default { async loadUser (userIdentifier) { const id = userIdentifier || this.user._id; - - this.$router.push({ + if (this.$router.currentRoute.name === 'adminPanelUser') { + await this.$router.push({ + name: 'adminPanel', + }); + } + await this.$router.push({ name: 'adminPanelUser', params: { userIdentifier: id }, }).catch(failure => { if (isNavigationFailure(failure, NavigationFailureType.duplicated)) { - // the admin has requested that the same user be displayed again so reload the page - // (e.g., if they changed their mind about changes they were making) this.$router.go(); } }); diff --git a/website/client/src/components/admin-panel/mixins/saveHero.js b/website/client/src/components/admin-panel/mixins/saveHero.js index 8bbef7170b..7f8061ce8e 100644 --- a/website/client/src/components/admin-panel/mixins/saveHero.js +++ b/website/client/src/components/admin-panel/mixins/saveHero.js @@ -1,6 +1,15 @@ +import VueRouter from 'vue-router'; + +const { isNavigationFailure, NavigationFailureType } = VueRouter; + export default { methods: { - async saveHero ({ hero, msg = 'User', clearData }) { + async saveHero ({ + hero, + msg = 'User', + clearData, + reloadData, + }) { await this.$store.dispatch('hall:updateHero', { heroDetails: hero }); await this.$store.dispatch('snackbars:add', { title: '', @@ -14,6 +23,20 @@ export default { // The admin should re-fetch the data if they need to keep working on that user. this.$emit('clear-data'); this.$router.push({ name: 'adminPanel' }); + } else if (reloadData) { + if (this.$router.currentRoute.name === 'adminPanelUser') { + await this.$router.push({ + name: 'adminPanel', + }); + } + await this.$router.push({ + name: 'adminPanelUser', + params: { userIdentifier: hero._id }, + }).catch(failure => { + if (isNavigationFailure(failure, NavigationFailureType.duplicated)) { + this.$router.go(); + } + }); } }, }, diff --git a/website/client/src/components/admin-panel/search.vue b/website/client/src/components/admin-panel/search.vue index 736de6ca5e..f6fcaf2ee0 100644 --- a/website/client/src/components/admin-panel/search.vue +++ b/website/client/src/components/admin-panel/search.vue @@ -7,7 +7,11 @@ > Could not find any matching users. - +
: - {{ itemText(item) }} + {{ item.text || item.key }} - {{ item.key }}
: - {{ itemText(item) }} + {{ item.text || item.key }} - {{ item.key }}
{ self.expandItemType[itemType] = false; }); + const { singularTextKey } = achievementItem; + if (singularTextKey !== undefined) { + return i18n.t(singularTextKey, { count }); + } + return ''; } export default { @@ -241,26 +192,34 @@ export default { }, nestedAchievementKeys: ['quests', 'ultimateGearSets'], integerTypes: ['streak', 'perfect', 'birthday', 'habiticaDays', 'habitSurveys', 'habitBirthdays', - 'valentine', 'congrats', 'shinySeed', 'goodluck', 'thankyou', 'seafoam', 'snowball', 'quests'], + 'valentine', 'congrats', 'shinySeed', 'goodluck', 'thankyou', 'seafoam', 'snowball', 'quests', + 'rebirths', 'rebirthLevel', 'greeting', 'spookySparkles', 'nye', 'costumeContests', 'congrats', + 'getwell', 'beastMasterCount', 'mountMasterCount', 'triadBingoCount', + ], + cardTypes: ['greeting', 'birthday', 'valentine', 'goodluck', 'thankyou', 'greeting', 'nye', + 'congrats', 'getwell'], achievements: [], nestedAchievements: {}, }; }, watch: { resetCounter () { - resetData(this); + this.resetData(); }, }, mounted () { - resetData(this); + this.resetData(); }, methods: { async saveItem (item) { - // prepare the item's new value and path for being saved - this.hero.achievementPath = item.path; - this.hero.achievementVal = item.value; - - await this.saveHero({ hero: this.hero, msg: item.path }); + await this.saveHero({ + hero: { + _id: this.hero._id, + achievementPath: item.path, + achievementVal: item.value, + }, + msg: item.path, + }); item.modified = false; }, enableValueChange (item) { @@ -270,14 +229,84 @@ export default { item.value = !item.value; } }, - itemText (item) { - if (item.key === 'npc') { - return this.$t('npcAchievementName', { key: this.hero.backer && this.hero.backer.npc }); + resetData () { + this.collateItemData(); + this.nestedAchievementKeys.forEach(itemType => { this.expandItemType[itemType] = false; }); + }, + collateItemData () { + const achievements = []; + const nestedAchievements = {}; + const basePath = 'achievements'; + const ownedAchievements = this.hero.achievements; + const allAchievements = content.achievements; + + const ownedKeys = Object.keys(ownedAchievements).sort(); + for (const key of ownedKeys) { + const value = ownedAchievements[key]; + let contentKey = key; + if (this.cardTypes.indexOf(key) !== -1) { + contentKey += 'Cards'; + } + if (typeof value === 'object') { + nestedAchievements[key] = []; + for (const nestedKey of Object.keys(value)) { + const valueIsInteger = this.integerTypes.includes(key); + let text = nestedKey; + if (allAchievements[key] && allAchievements[key][contentKey]) { + text = getText(allAchievements[key][contentKey]); + } + let notes = ''; + if (allAchievements[key] && allAchievements[key][contentKey]) { + notes = getNotes(allAchievements[key][contentKey], ownedAchievements[key]); + } + nestedAchievements[key].push({ + key: nestedKey, + text, + notes, + achievementType: key, + modified: false, + path: `${basePath}.${key}.${nestedKey}`, + value: value[nestedKey], + valueIsInteger, + }); + } + } else { + const valueIsInteger = this.integerTypes.includes(key); + achievements.push({ + key, + text: getText(allAchievements[contentKey]), + notes: getNotes(allAchievements[contentKey], ownedAchievements[key]), + modified: false, + path: `${basePath}.${key}`, + value: ownedAchievements[key], + valueIsInteger, + }); + } } - if (item.key === 'kickstarter') { - return this.$t('kickstartName', { key: this.hero.backer && this.hero.backer.tier }); + + const allKeys = Object.keys(allAchievements).sort(); + + for (const key of allKeys) { + if (key !== '' && !key.endsWith('UltimateGear') && !key.endsWith('Quest')) { + const ownedKey = key.replace('Cards', ''); + if (ownedAchievements[ownedKey] === undefined) { + const valueIsInteger = this.integerTypes.includes(ownedKey); + achievements.push({ + key: ownedKey, + text: getText(allAchievements[key]), + notes: getNotes(allAchievements[key], 0), + modified: false, + path: `${basePath}.${ownedKey}`, + value: valueIsInteger ? 0 : false, + valueIsInteger, + neverOwned: true, + }); + } + } } - return item.text || item.key; + + this.achievements = achievements; + this.nestedAchievements = nestedAchievements; }, }, }; diff --git a/website/client/src/components/admin-panel/user-support/contributorDetails.vue b/website/client/src/components/admin-panel/user-support/contributorDetails.vue index 6d0ee898ad..7dfdcb0bf3 100644 --- a/website/client/src/components/admin-panel/user-support/contributorDetails.vue +++ b/website/client/src/components/admin-panel/user-support/contributorDetails.vue @@ -1,5 +1,12 @@