mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
feat(webapp): badges admin settings (#8401)
Adds a link to badges settings in the user table, where admins can set the available badges.
This commit is contained in:
parent
7592fe29be
commit
c090db3866
@ -0,0 +1,46 @@
|
||||
import { render, fireEvent, screen } from '@testing-library/vue'
|
||||
import BadgesSection from './BadgesSection.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const badge1 = {
|
||||
id: 'badge1',
|
||||
icon: 'icon1',
|
||||
type: 'type1',
|
||||
description: 'description1',
|
||||
isActive: true,
|
||||
}
|
||||
const badge2 = {
|
||||
id: 'badge2',
|
||||
icon: 'icon2',
|
||||
type: 'type1',
|
||||
description: 'description2',
|
||||
isActive: false,
|
||||
}
|
||||
|
||||
describe('Admin/BadgesSection', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return render(BadgesSection, {
|
||||
localVue,
|
||||
propsData: {
|
||||
badges: [badge1, badge2],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.baseElement).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('emits toggleButton', async () => {
|
||||
const button = screen.getByAltText(badge1.description)
|
||||
await fireEvent.click(button)
|
||||
expect(wrapper.emitted().toggleBadge[0][0]).toEqual(badge1)
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="badge-section">
|
||||
<h4>{{ title }}</h4>
|
||||
<div class="badge-container">
|
||||
<button
|
||||
v-for="badge in badges"
|
||||
:key="badge.id"
|
||||
@click="toggleBadge(badge)"
|
||||
:class="{ badge, inactive: !badge.isActive }"
|
||||
>
|
||||
<img :src="badge.icon" :alt="badge.description" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
badges: {
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleBadge(badge) {
|
||||
this.$emit('toggleBadge', badge)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.badge-section h4 {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.badge-container {
|
||||
display: flex;
|
||||
margin-bottom: 36px;
|
||||
flex-flow: row wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
.badge:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.badge img {
|
||||
width: 70px;
|
||||
}
|
||||
.badge.inactive {
|
||||
opacity: 0.3;
|
||||
filter: grayscale(0.4);
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,36 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Admin/BadgesSection renders 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="badge-section"
|
||||
>
|
||||
<h4>
|
||||
|
||||
</h4>
|
||||
|
||||
<div
|
||||
class="badge-container"
|
||||
>
|
||||
<button
|
||||
class="badge"
|
||||
>
|
||||
<img
|
||||
alt="description1"
|
||||
src="icon1"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="badge inactive"
|
||||
>
|
||||
<img
|
||||
alt="description2"
|
||||
src="icon2"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@ -90,6 +90,23 @@ export const adminUserQuery = () => {
|
||||
`
|
||||
}
|
||||
|
||||
export const adminUserBadgesQuery = () => {
|
||||
return gql`
|
||||
query User($id: ID!) {
|
||||
User(id: $id) {
|
||||
id
|
||||
name
|
||||
badgeTrophies {
|
||||
id
|
||||
}
|
||||
badgeVerification {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export const mapUserQuery = (i18n) => {
|
||||
const lang = i18n.locale().toUpperCase()
|
||||
return gql`
|
||||
|
||||
54
webapp/graphql/admin/Badges.js
Normal file
54
webapp/graphql/admin/Badges.js
Normal file
@ -0,0 +1,54 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const queryBadges = () => gql`
|
||||
query {
|
||||
Badge {
|
||||
id
|
||||
type
|
||||
icon
|
||||
description
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const setVerificationBadge = () => gql`
|
||||
mutation ($badgeId: ID!, $userId: ID!) {
|
||||
setVerificationBadge(badgeId: $badgeId, userId: $userId) {
|
||||
id
|
||||
badgeVerification {
|
||||
id
|
||||
}
|
||||
badgeTrophies {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const rewardTrophyBadge = () => gql`
|
||||
mutation ($badgeId: ID!, $userId: ID!) {
|
||||
rewardTrophyBadge(badgeId: $badgeId, userId: $userId) {
|
||||
id
|
||||
badgeVerification {
|
||||
id
|
||||
}
|
||||
badgeTrophies {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const revokeBadge = () => gql`
|
||||
mutation ($badgeId: ID!, $userId: ID!) {
|
||||
revokeBadge(badgeId: $badgeId, userId: $userId) {
|
||||
id
|
||||
badgeVerification {
|
||||
id
|
||||
}
|
||||
badgeTrophies {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -10,6 +10,28 @@
|
||||
"saveCategories": "Themen speichern"
|
||||
},
|
||||
"admin": {
|
||||
"badges": {
|
||||
"description": "Stelle die verfügbaren Auszeichnungen für diesen Nutzer ein.",
|
||||
"revokeTrophy": {
|
||||
"error": "Trophäe konnte nicht widerrufen werden!",
|
||||
"success": "Trophäe erfolgreich widerrufen"
|
||||
},
|
||||
"revokeVerification": {
|
||||
"error": "Verifizierung konnte nicht gesetzt werden!",
|
||||
"success": "Verifizierung erfolgreich widerrufen"
|
||||
},
|
||||
"rewardTrophy": {
|
||||
"error": "Trophäe konnte nicht vergeben werden!",
|
||||
"success": "Trophäe erfolgreich vergeben!"
|
||||
},
|
||||
"setVerification": {
|
||||
"error": "Verifizierung konnte nicht gesetzt werden!",
|
||||
"success": "Verifizierung erfolgreich gesetzt"
|
||||
},
|
||||
"title": "Auszeichnungen",
|
||||
"trophyBadges": "Trophäen",
|
||||
"verificationBadges": "Verifizierungen"
|
||||
},
|
||||
"categories": {
|
||||
"categoryName": "Name",
|
||||
"name": "Themen",
|
||||
@ -68,13 +90,15 @@
|
||||
"roleChanged": "Rolle erfolgreich geändert!",
|
||||
"table": {
|
||||
"columns": {
|
||||
"badges": "Auszeichnungen",
|
||||
"createdAt": "Erstellt am",
|
||||
"email": "E-Mail",
|
||||
"name": "Name",
|
||||
"number": "Nr.",
|
||||
"role": "Rolle",
|
||||
"slug": "Alias"
|
||||
}
|
||||
},
|
||||
"edit": "Bearbeiten"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -10,6 +10,28 @@
|
||||
"saveCategories": "Save topics"
|
||||
},
|
||||
"admin": {
|
||||
"badges": {
|
||||
"description": "Configure the available badges for this user",
|
||||
"revokeTrophy": {
|
||||
"error": "Trophy could not be revoked!",
|
||||
"success": "Trophy successfully revoked!"
|
||||
},
|
||||
"revokeVerification": {
|
||||
"error": "Verification could not be revoked!",
|
||||
"success": "Verification succesfully revoked"
|
||||
},
|
||||
"rewardTrophy": {
|
||||
"error": "Trophy could not be rewarded!",
|
||||
"success": "Trophy successfully rewarded!"
|
||||
},
|
||||
"setVerification": {
|
||||
"error": "Verification could not be set!",
|
||||
"success": "Verification successfully set!"
|
||||
},
|
||||
"title": "Badges",
|
||||
"trophyBadges": "Trophies",
|
||||
"verificationBadges": "Verifications"
|
||||
},
|
||||
"categories": {
|
||||
"categoryName": "Name",
|
||||
"name": "Topics",
|
||||
@ -68,13 +90,15 @@
|
||||
"roleChanged": "Role changed successfully!",
|
||||
"table": {
|
||||
"columns": {
|
||||
"badges": "Badges",
|
||||
"createdAt": "Created at",
|
||||
"email": "E-mail",
|
||||
"name": "Name",
|
||||
"number": "No.",
|
||||
"role": "Role",
|
||||
"slug": "Slug"
|
||||
}
|
||||
},
|
||||
"edit": "Edit"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -10,6 +10,28 @@
|
||||
"saveCategories": null
|
||||
},
|
||||
"admin": {
|
||||
"badges": {
|
||||
"description": null,
|
||||
"revokeTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"revokeVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"rewardTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"setVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"title": null,
|
||||
"trophyBadges": null,
|
||||
"verificationBadges": null
|
||||
},
|
||||
"categories": {
|
||||
"categoryName": "Nombre",
|
||||
"name": "Categorías",
|
||||
@ -68,13 +90,15 @@
|
||||
"roleChanged": null,
|
||||
"table": {
|
||||
"columns": {
|
||||
"badges": null,
|
||||
"createdAt": "Creado el",
|
||||
"email": "Correo electrónico",
|
||||
"name": "Nombre",
|
||||
"number": "No.",
|
||||
"role": "Rol",
|
||||
"slug": "Alias"
|
||||
}
|
||||
},
|
||||
"edit": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -10,6 +10,28 @@
|
||||
"saveCategories": null
|
||||
},
|
||||
"admin": {
|
||||
"badges": {
|
||||
"description": null,
|
||||
"revokeTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"revokeVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"rewardTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"setVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"title": null,
|
||||
"trophyBadges": null,
|
||||
"verificationBadges": null
|
||||
},
|
||||
"categories": {
|
||||
"categoryName": "Nom",
|
||||
"name": "Catégories",
|
||||
@ -68,13 +90,15 @@
|
||||
"roleChanged": null,
|
||||
"table": {
|
||||
"columns": {
|
||||
"badges": null,
|
||||
"createdAt": "Créé à",
|
||||
"email": "Mail",
|
||||
"name": "Nom",
|
||||
"number": "Num.",
|
||||
"role": "Rôle",
|
||||
"slug": "Slug"
|
||||
}
|
||||
},
|
||||
"edit": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -10,6 +10,28 @@
|
||||
"saveCategories": null
|
||||
},
|
||||
"admin": {
|
||||
"badges": {
|
||||
"description": null,
|
||||
"revokeTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"revokeVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"rewardTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"setVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"title": null,
|
||||
"trophyBadges": null,
|
||||
"verificationBadges": null
|
||||
},
|
||||
"categories": {
|
||||
"categoryName": "Nome",
|
||||
"name": "Categorie",
|
||||
@ -68,13 +90,15 @@
|
||||
"roleChanged": null,
|
||||
"table": {
|
||||
"columns": {
|
||||
"badges": null,
|
||||
"createdAt": null,
|
||||
"email": null,
|
||||
"name": null,
|
||||
"number": null,
|
||||
"role": null,
|
||||
"slug": null
|
||||
}
|
||||
},
|
||||
"edit": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -10,6 +10,28 @@
|
||||
"saveCategories": null
|
||||
},
|
||||
"admin": {
|
||||
"badges": {
|
||||
"description": null,
|
||||
"revokeTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"revokeVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"rewardTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"setVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"title": null,
|
||||
"trophyBadges": null,
|
||||
"verificationBadges": null
|
||||
},
|
||||
"categories": {
|
||||
"categoryName": "Naam",
|
||||
"name": "Categorieën",
|
||||
@ -68,13 +90,15 @@
|
||||
"roleChanged": null,
|
||||
"table": {
|
||||
"columns": {
|
||||
"badges": null,
|
||||
"createdAt": null,
|
||||
"email": null,
|
||||
"name": null,
|
||||
"number": null,
|
||||
"role": null,
|
||||
"slug": null
|
||||
}
|
||||
},
|
||||
"edit": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -10,6 +10,28 @@
|
||||
"saveCategories": null
|
||||
},
|
||||
"admin": {
|
||||
"badges": {
|
||||
"description": null,
|
||||
"revokeTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"revokeVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"rewardTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"setVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"title": null,
|
||||
"trophyBadges": null,
|
||||
"verificationBadges": null
|
||||
},
|
||||
"categories": {
|
||||
"categoryName": "Nazwa",
|
||||
"name": "Kategorie",
|
||||
@ -68,13 +90,15 @@
|
||||
"roleChanged": null,
|
||||
"table": {
|
||||
"columns": {
|
||||
"badges": null,
|
||||
"createdAt": null,
|
||||
"email": null,
|
||||
"name": null,
|
||||
"number": null,
|
||||
"role": null,
|
||||
"slug": null
|
||||
}
|
||||
},
|
||||
"edit": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -10,6 +10,28 @@
|
||||
"saveCategories": null
|
||||
},
|
||||
"admin": {
|
||||
"badges": {
|
||||
"description": null,
|
||||
"revokeTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"revokeVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"rewardTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"setVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"title": null,
|
||||
"trophyBadges": null,
|
||||
"verificationBadges": null
|
||||
},
|
||||
"categories": {
|
||||
"categoryName": "Nome",
|
||||
"name": "Categorias",
|
||||
@ -68,13 +90,15 @@
|
||||
"roleChanged": null,
|
||||
"table": {
|
||||
"columns": {
|
||||
"badges": null,
|
||||
"createdAt": "Criado em",
|
||||
"email": "E-mail",
|
||||
"name": "Nome",
|
||||
"number": "N.º",
|
||||
"role": "Função",
|
||||
"slug": "Slug"
|
||||
}
|
||||
},
|
||||
"edit": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -10,6 +10,28 @@
|
||||
"saveCategories": null
|
||||
},
|
||||
"admin": {
|
||||
"badges": {
|
||||
"description": null,
|
||||
"revokeTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"revokeVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"rewardTrophy": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"setVerification": {
|
||||
"error": null,
|
||||
"success": null
|
||||
},
|
||||
"title": null,
|
||||
"trophyBadges": null,
|
||||
"verificationBadges": null
|
||||
},
|
||||
"categories": {
|
||||
"categoryName": "Имя",
|
||||
"name": "Категории",
|
||||
@ -68,13 +90,15 @@
|
||||
"roleChanged": null,
|
||||
"table": {
|
||||
"columns": {
|
||||
"badges": null,
|
||||
"createdAt": "Дата создания",
|
||||
"email": "Эл. почта",
|
||||
"name": "Имя",
|
||||
"number": "№",
|
||||
"role": "Роль",
|
||||
"slug": "Алиас"
|
||||
}
|
||||
},
|
||||
"edit": null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -207,6 +207,15 @@ export default {
|
||||
'X-API-TOKEN': CONFIG.BACKEND_TOKEN,
|
||||
},
|
||||
},
|
||||
'/img': {
|
||||
// make this configurable (nuxt-dotenv)
|
||||
target: CONFIG.GRAPHQL_URI,
|
||||
toProxy: true, // cloudflare needs that
|
||||
headers: {
|
||||
'X-UI-Request': true,
|
||||
'X-API-TOKEN': CONFIG.BACKEND_TOKEN,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Give apollo module options
|
||||
|
||||
104
webapp/pages/admin/users/__snapshots__/_id.spec.js.snap
Normal file
104
webapp/pages/admin/users/__snapshots__/_id.spec.js.snap
Normal file
@ -0,0 +1,104 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`.vue renders 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<section
|
||||
class="ds-section"
|
||||
>
|
||||
<div
|
||||
class="ds-section-content"
|
||||
>
|
||||
<div
|
||||
class="ds-container ds-container-x-large"
|
||||
>
|
||||
<div
|
||||
class="ds-space"
|
||||
style="margin-bottom: 32px;"
|
||||
>
|
||||
<h1
|
||||
class="ds-heading ds-heading-h3"
|
||||
>
|
||||
|
||||
User1
|
||||
-
|
||||
admin.badges.title
|
||||
|
||||
</h1>
|
||||
|
||||
<p
|
||||
class="ds-text"
|
||||
>
|
||||
admin.badges.description
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<article
|
||||
class="base-card"
|
||||
>
|
||||
<div
|
||||
class="badge-section"
|
||||
>
|
||||
<h4>
|
||||
admin.badges.verificationBadges
|
||||
</h4>
|
||||
|
||||
<div
|
||||
class="badge-container"
|
||||
>
|
||||
<button
|
||||
class="badge"
|
||||
>
|
||||
<img
|
||||
alt="description-v-1"
|
||||
src="icon1"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="badge inactive"
|
||||
>
|
||||
<img
|
||||
alt="description-v-2"
|
||||
src="icon2"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="badge-section"
|
||||
>
|
||||
<h4>
|
||||
admin.badges.trophyBadges
|
||||
</h4>
|
||||
|
||||
<div
|
||||
class="badge-container"
|
||||
>
|
||||
<button
|
||||
class="badge inactive"
|
||||
>
|
||||
<img
|
||||
alt="description-t-1"
|
||||
src="icon3"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="badge"
|
||||
>
|
||||
<img
|
||||
alt="description-t-2"
|
||||
src="icon4"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
326
webapp/pages/admin/users/_id.spec.js
Normal file
326
webapp/pages/admin/users/_id.spec.js
Normal file
@ -0,0 +1,326 @@
|
||||
import { render, fireEvent, screen } from '@testing-library/vue'
|
||||
import BadgesPage from './_id.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const availableBadges = [
|
||||
{
|
||||
id: 'verification-badge-1',
|
||||
icon: 'icon1',
|
||||
type: 'verification',
|
||||
description: 'description-v-1',
|
||||
},
|
||||
{
|
||||
id: 'verification-badge-2',
|
||||
icon: 'icon2',
|
||||
type: 'verification',
|
||||
description: 'description-v-2',
|
||||
},
|
||||
{
|
||||
id: 'trophy-badge-1',
|
||||
icon: 'icon3',
|
||||
type: 'trophy',
|
||||
description: 'description-t-1',
|
||||
},
|
||||
{
|
||||
id: 'trophy-badge-2',
|
||||
icon: 'icon4',
|
||||
type: 'trophy',
|
||||
description: 'description-t-2',
|
||||
},
|
||||
]
|
||||
|
||||
const user = {
|
||||
id: 'user1',
|
||||
name: 'User1',
|
||||
badgeVerification: {
|
||||
id: 'verification-badge-1',
|
||||
},
|
||||
badgeTrophies: [
|
||||
{
|
||||
id: 'trophy-badge-2',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
describe('.vue', () => {
|
||||
let wrapper
|
||||
let mocks
|
||||
|
||||
beforeEach(() => {
|
||||
mocks = {
|
||||
$t: jest.fn((v) => v),
|
||||
$apollo: {
|
||||
User: {
|
||||
query: jest.fn(),
|
||||
},
|
||||
badges: {
|
||||
query: jest.fn(),
|
||||
},
|
||||
mutate: jest.fn(),
|
||||
},
|
||||
$toast: {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
}
|
||||
})
|
||||
const Wrapper = () => {
|
||||
return render(BadgesPage, {
|
||||
mocks,
|
||||
localVue,
|
||||
data: () => ({
|
||||
user,
|
||||
badges: availableBadges,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.baseElement).toMatchSnapshot()
|
||||
})
|
||||
|
||||
describe('after clicking an inactive verification badge', () => {
|
||||
let button
|
||||
beforeEach(() => {
|
||||
button = screen.getByAltText(availableBadges[1].description)
|
||||
})
|
||||
|
||||
describe('and successful server response', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.$apollo.mutate.mockResolvedValue({
|
||||
data: {
|
||||
setVerificationBadge: {
|
||||
id: 'user1',
|
||||
badgeVerification: {
|
||||
id: availableBadges[1].id,
|
||||
},
|
||||
badgeTrophies: [],
|
||||
},
|
||||
},
|
||||
})
|
||||
await fireEvent.click(button)
|
||||
})
|
||||
|
||||
it('calls the mutation', async () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: expect.anything(),
|
||||
variables: {
|
||||
badgeId: availableBadges[1].id,
|
||||
userId: 'user1',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('shows success message', async () => {
|
||||
expect(mocks.$toast.success).toHaveBeenCalledWith('admin.badges.setVerification.success')
|
||||
})
|
||||
})
|
||||
|
||||
describe('and failed server response', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' })
|
||||
await fireEvent.click(button)
|
||||
})
|
||||
|
||||
it('calls the mutation', async () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: expect.anything(),
|
||||
variables: {
|
||||
badgeId: availableBadges[1].id,
|
||||
userId: 'user1',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('shows error message', async () => {
|
||||
expect(mocks.$toast.error).toHaveBeenCalledWith('admin.badges.setVerification.error')
|
||||
})
|
||||
})
|
||||
|
||||
describe('after clicking an inactive trophy badge', () => {
|
||||
let button
|
||||
beforeEach(() => {
|
||||
button = screen.getByAltText(availableBadges[2].description)
|
||||
})
|
||||
|
||||
describe('and successful server response', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.$apollo.mutate.mockResolvedValue({
|
||||
data: {
|
||||
setTrophyBadge: {
|
||||
id: 'user1',
|
||||
badgeVerification: null,
|
||||
badgeTrophies: [
|
||||
{
|
||||
id: availableBadges[2].id,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
await fireEvent.click(button)
|
||||
})
|
||||
|
||||
it('calls the mutation', async () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: expect.anything(),
|
||||
variables: {
|
||||
badgeId: availableBadges[2].id,
|
||||
userId: 'user1',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('shows success message', async () => {
|
||||
expect(mocks.$toast.success).toHaveBeenCalledWith('admin.badges.rewardTrophy.success')
|
||||
})
|
||||
})
|
||||
|
||||
describe('and failed server response', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' })
|
||||
await fireEvent.click(button)
|
||||
})
|
||||
|
||||
it('calls the mutation', async () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: expect.anything(),
|
||||
variables: {
|
||||
badgeId: availableBadges[2].id,
|
||||
userId: 'user1',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('shows error message', async () => {
|
||||
expect(mocks.$toast.error).toHaveBeenCalledWith('admin.badges.rewardTrophy.error')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('after clicking an active verification badge', () => {
|
||||
let button
|
||||
beforeEach(() => {
|
||||
button = screen.getByAltText(availableBadges[0].description)
|
||||
})
|
||||
|
||||
describe('and successful server response', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.$apollo.mutate.mockResolvedValue({
|
||||
data: {
|
||||
setVerificationBadge: {
|
||||
id: 'user1',
|
||||
badgeVerification: null,
|
||||
badgeTrophies: [],
|
||||
},
|
||||
},
|
||||
})
|
||||
await fireEvent.click(button)
|
||||
})
|
||||
|
||||
it('calls the mutation', async () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: expect.anything(),
|
||||
variables: {
|
||||
badgeId: availableBadges[0].id,
|
||||
userId: 'user1',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('shows success message', async () => {
|
||||
expect(mocks.$toast.success).toHaveBeenCalledWith(
|
||||
'admin.badges.revokeVerification.success',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('and failed server response', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' })
|
||||
await fireEvent.click(button)
|
||||
})
|
||||
|
||||
it('calls the mutation', async () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: expect.anything(),
|
||||
variables: {
|
||||
badgeId: availableBadges[0].id,
|
||||
userId: 'user1',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('shows error message', async () => {
|
||||
expect(mocks.$toast.error).toHaveBeenCalledWith('admin.badges.revokeVerification.error')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('after clicking an active trophy badge', () => {
|
||||
let button
|
||||
beforeEach(() => {
|
||||
button = screen.getByAltText(availableBadges[3].description)
|
||||
})
|
||||
|
||||
describe('and successful server response', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.$apollo.mutate.mockResolvedValue({
|
||||
data: {
|
||||
setTrophyBadge: {
|
||||
id: 'user1',
|
||||
badgeVerification: null,
|
||||
badgeTrophies: [
|
||||
{
|
||||
id: availableBadges[3].id,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
await fireEvent.click(button)
|
||||
})
|
||||
|
||||
it('calls the mutation', async () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: expect.anything(),
|
||||
variables: {
|
||||
badgeId: availableBadges[3].id,
|
||||
userId: 'user1',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('shows success message', async () => {
|
||||
expect(mocks.$toast.success).toHaveBeenCalledWith('admin.badges.revokeTrophy.success')
|
||||
})
|
||||
})
|
||||
|
||||
describe('and failed server response', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' })
|
||||
await fireEvent.click(button)
|
||||
})
|
||||
|
||||
it('calls the mutation', async () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: expect.anything(),
|
||||
variables: {
|
||||
badgeId: availableBadges[3].id,
|
||||
userId: 'user1',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('shows error message', async () => {
|
||||
expect(mocks.$toast.error).toHaveBeenCalledWith('admin.badges.revokeTrophy.error')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
163
webapp/pages/admin/users/_id.vue
Normal file
163
webapp/pages/admin/users/_id.vue
Normal file
@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<ds-section>
|
||||
<ds-space>
|
||||
<ds-heading size="h3">
|
||||
{{ user && user.name }}
|
||||
-
|
||||
{{ $t('admin.badges.title') }}
|
||||
</ds-heading>
|
||||
<ds-text>{{ $t('admin.badges.description') }}</ds-text>
|
||||
</ds-space>
|
||||
<base-card>
|
||||
<badges-section
|
||||
:title="$t('admin.badges.verificationBadges')"
|
||||
:badges="verificationBadges"
|
||||
@toggleBadge="toggleBadge"
|
||||
/>
|
||||
<badges-section
|
||||
:title="$t('admin.badges.trophyBadges')"
|
||||
:badges="trophyBadges"
|
||||
@toggleBadge="toggleBadge"
|
||||
/>
|
||||
</base-card>
|
||||
</ds-section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BadgesSection from '~/components/_new/features/Admin/Badges/BadgesSection.vue'
|
||||
import {
|
||||
queryBadges,
|
||||
rewardTrophyBadge,
|
||||
revokeBadge,
|
||||
setVerificationBadge,
|
||||
} from '~/graphql/admin/Badges'
|
||||
import { adminUserBadgesQuery } from '~/graphql/User'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BadgesSection,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
user: null,
|
||||
badges: [],
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
User: {
|
||||
query() {
|
||||
return adminUserBadgesQuery()
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
id: this.$route.params.id,
|
||||
}
|
||||
},
|
||||
update({ User }) {
|
||||
this.user = User[0]
|
||||
},
|
||||
},
|
||||
Badge: {
|
||||
query() {
|
||||
return queryBadges()
|
||||
},
|
||||
update({ Badge }) {
|
||||
this.badges = Badge
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
verificationBadges() {
|
||||
if (!this.user) return []
|
||||
|
||||
return this.badges
|
||||
.filter((badge) => badge.type === 'verification')
|
||||
.map((badge) => ({
|
||||
...badge,
|
||||
isActive: this.user.badgeVerification?.id === badge.id,
|
||||
}))
|
||||
},
|
||||
trophyBadges() {
|
||||
if (!this.user?.badgeTrophies) return []
|
||||
|
||||
return this.badges
|
||||
.filter((badge) => badge.type === 'trophy')
|
||||
.map((badge) => ({
|
||||
...badge,
|
||||
isActive: this.user.badgeTrophies.some((userBadge) => userBadge.id === badge.id),
|
||||
}))
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleBadge(badge) {
|
||||
if (badge.isActive) {
|
||||
this.revokeBadge(badge)
|
||||
return
|
||||
}
|
||||
|
||||
if (badge.type === 'verification') {
|
||||
this.setVerificationBadge(badge.id)
|
||||
} else {
|
||||
this.rewardTrophyBadge(badge.id)
|
||||
}
|
||||
},
|
||||
async rewardTrophyBadge(badgeId) {
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: rewardTrophyBadge(),
|
||||
variables: {
|
||||
badgeId,
|
||||
userId: this.user.id,
|
||||
},
|
||||
})
|
||||
|
||||
this.$toast.success(this.$t('admin.badges.rewardTrophy.success'))
|
||||
} catch (error) {
|
||||
this.$toast.error(this.$t('admin.badges.rewardTrophy.error'))
|
||||
}
|
||||
},
|
||||
async revokeBadge(badge) {
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: revokeBadge(),
|
||||
variables: {
|
||||
badgeId: badge.id,
|
||||
userId: this.user.id,
|
||||
},
|
||||
})
|
||||
|
||||
this.$toast.success(
|
||||
this.$t(
|
||||
badge.type === 'verification'
|
||||
? 'admin.badges.revokeVerification.success'
|
||||
: 'admin.badges.revokeTrophy.success',
|
||||
),
|
||||
)
|
||||
} catch (error) {
|
||||
this.$toast.error(
|
||||
this.$t(
|
||||
badge.type === 'verification'
|
||||
? 'admin.badges.revokeVerification.error'
|
||||
: 'admin.badges.revokeTrophy.error',
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
async setVerificationBadge(badgeId) {
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: setVerificationBadge(),
|
||||
variables: {
|
||||
badgeId,
|
||||
userId: this.user.id,
|
||||
},
|
||||
})
|
||||
|
||||
this.$toast.success(this.$t('admin.badges.setVerification.success'))
|
||||
} catch (error) {
|
||||
this.$toast.error(this.$t('admin.badges.setVerification.error'))
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,6 +1,6 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Vuex from 'vuex'
|
||||
import Users from './users.vue'
|
||||
import Users from './index.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
@ -63,6 +63,16 @@
|
||||
<ds-text v-else>{{ scope.row.role }}</ds-text>
|
||||
</template>
|
||||
</template>
|
||||
<template #badges="scope">
|
||||
<nuxt-link
|
||||
:to="{
|
||||
name: 'admin-users-id',
|
||||
params: { id: scope.row.id },
|
||||
}"
|
||||
>
|
||||
{{ $t('admin.users.table.edit') }}
|
||||
</nuxt-link>
|
||||
</template>
|
||||
</ds-table>
|
||||
<pagination-buttons :hasNext="hasNext" :hasPrevious="hasPrevious" @next="next" @back="back" />
|
||||
</base-card>
|
||||
@ -132,6 +142,10 @@ export default {
|
||||
label: this.$t('admin.users.table.columns.role'),
|
||||
align: 'right',
|
||||
},
|
||||
badges: {
|
||||
label: this.$t('admin.users.table.columns.badges'),
|
||||
align: 'right',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
Loading…
x
Reference in New Issue
Block a user