mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
fix(frontend): fix postmigration fix (#3382)
* feat(frontend): migration fixes * feat(admin): post migration fixes * feat(admin): revert docker change * feat(admin): update tests * feat(frontend): add feedback fixes
This commit is contained in:
parent
772ff51581
commit
ccc04dd706
@ -26,11 +26,10 @@
|
||||
"@vee-validate/i18n": "^4.13.2",
|
||||
"@vee-validate/rules": "^4.13.2",
|
||||
"@vee-validate/yup": "^4.13.2",
|
||||
"@vitejs/plugin-vue": "3.2.0",
|
||||
"@vitejs/plugin-vue": "5.1.4",
|
||||
"@vue-leaflet/vue-leaflet": "^0.10.1",
|
||||
"@vue/apollo-composable": "^4.0.2",
|
||||
"@vue/apollo-option": "^4.0.0",
|
||||
"@vue/compat": "^3.4.31",
|
||||
"apollo-boost": "^0.4.9",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
@ -59,13 +58,10 @@
|
||||
"vite-plugin-commonjs": "^0.10.1",
|
||||
"vue": "3.4.31",
|
||||
"vue-apollo": "^3.1.2",
|
||||
"vue-avatar": "^2.3.3",
|
||||
"vue-flatpickr-component": "^8.1.2",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-loading-overlay": "^3.4.2",
|
||||
"vue-router": "^4.4.0",
|
||||
"vue-timer-hook": "^1.0.84",
|
||||
"vue-timers": "^2.0.4",
|
||||
"vuex": "^4.1.0",
|
||||
"vuex-persistedstate": "^4.1.0",
|
||||
"yup": "^1.4.0"
|
||||
@ -75,7 +71,6 @@
|
||||
"@iconify-json/bi": "^1.1.23",
|
||||
"@intlify/eslint-plugin-vue-i18n": "^1.4.0",
|
||||
"@vitest/coverage-v8": "^2.0.5",
|
||||
"@vue/compiler-sfc": "^3.4.35",
|
||||
"@vue/eslint-config-prettier": "^4.0.1",
|
||||
"@vue/test-utils": "^2.4.5",
|
||||
"babel-plugin-component": "^1.1.0",
|
||||
|
||||
@ -359,3 +359,12 @@ input.rounded-input {
|
||||
.fs-7 {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.vue3-avatar.container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
font-family: inherit !important;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
135
frontend/src/components/AppAvatar.vue
Normal file
135
frontend/src/components/AppAvatar.vue
Normal file
@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div
|
||||
class="app-avatar d-flex justify-content-center align-items-center rounded-circle"
|
||||
:style="{
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
backgroundColor,
|
||||
textTransform: 'uppercase',
|
||||
}"
|
||||
>
|
||||
<span
|
||||
:style="{
|
||||
fontSize: `${size * 0.4}px`,
|
||||
lineHeight: '1',
|
||||
color: props.color,
|
||||
}"
|
||||
class="font-medium"
|
||||
>
|
||||
{{ computedInitials }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 50,
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
initials: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
// Enhanced color palette with better contrast ratios
|
||||
const colorPalette = [
|
||||
{ bg: '#4A5568', text: '#FFFFFF' }, // Slate Blue
|
||||
{ bg: '#2C7A7B', text: '#FFFFFF' }, // Teal
|
||||
{ bg: '#805AD5', text: '#FFFFFF' }, // Purple
|
||||
{ bg: '#DD6B20', text: '#FFFFFF' }, // Orange
|
||||
{ bg: '#3182CE', text: '#FFFFFF' }, // Blue
|
||||
{ bg: '#38A169', text: '#FFFFFF' }, // Green
|
||||
{ bg: '#E53E3E', text: '#FFFFFF' }, // Red
|
||||
{ bg: '#6B46C1', text: '#FFFFFF' }, // Indigo
|
||||
{ bg: '#2B6CB0', text: '#FFFFFF' }, // Dark Blue
|
||||
{ bg: '#9C4221', text: '#FFFFFF' }, // Brown
|
||||
]
|
||||
|
||||
// Generate consistent index based on string
|
||||
function stringToIndex(str) {
|
||||
let hash = 0
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hash = str.charCodeAt(i) + ((hash << 5) - hash)
|
||||
}
|
||||
return Math.abs(hash % colorPalette.length)
|
||||
}
|
||||
|
||||
// Parse any color format to RGB
|
||||
function parseColor(color) {
|
||||
const div = document.createElement('div')
|
||||
div.style.color = color
|
||||
document.body.appendChild(div)
|
||||
const computed = window.getComputedStyle(div).color
|
||||
document.body.removeChild(div)
|
||||
|
||||
const match = computed.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/)
|
||||
if (!match) return [0, 0, 0]
|
||||
|
||||
return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])]
|
||||
}
|
||||
|
||||
// Calculate relative luminance using WCAG formula
|
||||
function getLuminance(r, g, b) {
|
||||
const [rs, gs, bs] = [r, g, b].map((c) => {
|
||||
c = c / 255
|
||||
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
|
||||
})
|
||||
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs
|
||||
}
|
||||
|
||||
// Calculate contrast ratio using WCAG formula
|
||||
function getContrastRatio(l1, l2) {
|
||||
const lighter = Math.max(l1, l2)
|
||||
const darker = Math.min(l1, l2)
|
||||
return (lighter + 0.05) / (darker + 0.05)
|
||||
}
|
||||
|
||||
// Get text color based on background ensuring WCAG AA compliance (4.5:1 minimum)
|
||||
function getTextColor(backgroundColor) {
|
||||
const [r, g, b] = parseColor(backgroundColor)
|
||||
const bgLuminance = getLuminance(r, g, b)
|
||||
const whiteLuminance = getLuminance(255, 255, 255)
|
||||
const blackLuminance = getLuminance(0, 0, 0)
|
||||
|
||||
const whiteContrast = getContrastRatio(whiteLuminance, bgLuminance)
|
||||
const blackContrast = getContrastRatio(blackLuminance, bgLuminance)
|
||||
|
||||
// Return the color with better contrast (minimum 4.5:1 for WCAG AA)
|
||||
return whiteContrast >= blackContrast ? '#FFFFFF' : '#000000'
|
||||
}
|
||||
|
||||
const computedInitials = computed(() => {
|
||||
if (props.initials) return props.initials
|
||||
return props.name
|
||||
.split(' ')
|
||||
.map((word) => word[0])
|
||||
.join('')
|
||||
.toUpperCase()
|
||||
.slice(0, 2)
|
||||
})
|
||||
|
||||
const backgroundColor = computed(() => {
|
||||
const colorIndex = stringToIndex(computedInitials.value || props.name)
|
||||
return colorPalette[colorIndex].bg
|
||||
})
|
||||
|
||||
const textColor = computed(() => {
|
||||
if (props.color) {
|
||||
return getTextColor(props.color)
|
||||
}
|
||||
const colorIndex = stringToIndex(computedInitials.value || props.name)
|
||||
return colorPalette[colorIndex].text
|
||||
})
|
||||
</script>
|
||||
@ -90,5 +90,4 @@ function setDefaultCommunity() {
|
||||
}
|
||||
|
||||
onMounted(setDefaultCommunity)
|
||||
onUpdated(setDefaultCommunity)
|
||||
</script>
|
||||
|
||||
@ -16,7 +16,17 @@
|
||||
<parse-message v-bind="message" data-test="message" class="p-2"></parse-message>
|
||||
</BCol>
|
||||
<BCol cols="2">
|
||||
<avatar :username="storeName.username" :initials="storeName.initials"></avatar>
|
||||
<!-- <avatar-->
|
||||
<!-- class="vue3-avatar"-->
|
||||
<!-- :name="storeName.username"-->
|
||||
<!-- :initials="storeName.initials"-->
|
||||
<!-- :border="false"-->
|
||||
<!-- />-->
|
||||
<app-avatar
|
||||
class="vue3-avatar"
|
||||
:name="storeName.username"
|
||||
:initials="storeName.initials"
|
||||
/>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</div>
|
||||
@ -28,14 +38,30 @@
|
||||
<parse-message v-bind="message" data-test="message"></parse-message>
|
||||
</BCol>
|
||||
<BCol cols="2">
|
||||
<avatar :username="storeName.username" :initials="storeName.initials"></avatar>
|
||||
<!-- <avatar-->
|
||||
<!-- class="vue3-avatar"-->
|
||||
<!-- :name="storeName.username"-->
|
||||
<!-- :initials="storeName.initials"-->
|
||||
<!-- :border="false"-->
|
||||
<!-- />-->
|
||||
<app-avatar
|
||||
class="vue3-avatar"
|
||||
:name="storeName.username"
|
||||
:initials="storeName.initials"
|
||||
/>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</div>
|
||||
<div v-else>
|
||||
<BRow class="mb-3 p-2 is-moderator">
|
||||
<BCol cols="2">
|
||||
<avatar :username="moderationName.username" :initials="moderationName.initials"></avatar>
|
||||
<!-- <avatar-->
|
||||
<!-- class="vue3-avatar"-->
|
||||
<!-- :name="moderationName.username"-->
|
||||
<!-- :initials="moderationName.initials"-->
|
||||
<!-- :border="false"-->
|
||||
<!-- />-->
|
||||
<app-avatar :name="moderationName.username" :initials="moderationName.initials" />
|
||||
</BCol>
|
||||
<BCol cols="10">
|
||||
<div class="font-weight-bold">
|
||||
@ -54,13 +80,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Avatar from 'vue-avatar'
|
||||
import ParseMessage from '@/components/ContributionMessages/ParseMessage'
|
||||
import AppAvatar from '@/components/AppAvatar.vue'
|
||||
|
||||
export default {
|
||||
name: 'ContributionMessagesListItem',
|
||||
components: {
|
||||
Avatar,
|
||||
AppAvatar,
|
||||
ParseMessage,
|
||||
},
|
||||
props: {
|
||||
|
||||
@ -6,13 +6,21 @@
|
||||
>
|
||||
<BRow>
|
||||
<BCol cols="3" lg="2" md="2">
|
||||
<avatar
|
||||
<!-- <avatar-->
|
||||
<!-- v-if="firstName"-->
|
||||
<!-- :name="username.username"-->
|
||||
<!-- :initials="username.initials"-->
|
||||
<!-- :border="false"-->
|
||||
<!-- color="#fff"-->
|
||||
<!-- class="vue3-avatar fw-bold"-->
|
||||
<!-- />-->
|
||||
<app-avatar
|
||||
v-if="firstName"
|
||||
:username="username.username"
|
||||
:name="username.username"
|
||||
:initials="username.initials"
|
||||
color="#fff"
|
||||
class="fw-bold"
|
||||
></avatar>
|
||||
class="vue3-avatar fw-bold"
|
||||
/>
|
||||
<BAvatar v-else rounded="lg" :variant="variant" size="4.55em">
|
||||
<variant-icon :icon="icon" variant="white" />
|
||||
</BAvatar>
|
||||
@ -112,13 +120,13 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import Avatar from 'vue-avatar'
|
||||
import CollapseIcon from '../TransactionRows/CollapseIcon'
|
||||
import ContributionMessagesList from '@/components/ContributionMessages/ContributionMessagesList'
|
||||
import { listContributionMessages } from '@/graphql/queries'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useLazyQuery, useQuery } from '@vue/apollo-composable'
|
||||
import AppAvatar from '@/components/AppAvatar.vue'
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
|
||||
@ -162,10 +162,11 @@ describe('CollapseLinksList', () => {
|
||||
transactionLinks: [{ id: 1 }, { id: 2 }],
|
||||
pageSize: 5,
|
||||
})
|
||||
await wrapper.vm.$nextTick()
|
||||
})
|
||||
|
||||
it('renders text in plural with page size links to load', () => {
|
||||
expect(mockT).toHaveBeenCalledWith('link-load', 2, { n: 5 })
|
||||
it('renders text with pageSize as number of links to load', () => {
|
||||
expect(mockT).toHaveBeenCalledWith('link-load-more', { n: 5 })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -11,27 +11,19 @@
|
||||
>
|
||||
<BRow class="mb-4 gap-5">
|
||||
<BCol>
|
||||
<BRow class="bg-248 gradido-border-radius position-relative">
|
||||
<BFormRadio
|
||||
name="shipping"
|
||||
size="md"
|
||||
reverse
|
||||
:value="SEND_TYPES.send"
|
||||
class="transaction-form-radio"
|
||||
>
|
||||
<BRow
|
||||
class="bg-248 gradido-border-radius position-relative transaction-form-radio"
|
||||
>
|
||||
<BFormRadio name="shipping" size="md" reverse :value="SEND_TYPES.send">
|
||||
{{ $t('send_gdd') }}
|
||||
</BFormRadio>
|
||||
</BRow>
|
||||
</BCol>
|
||||
<BCol>
|
||||
<BRow class="bg-248 gradido-border-radius position-relative">
|
||||
<BFormRadio
|
||||
name="shipping"
|
||||
:value="SEND_TYPES.link"
|
||||
size="md"
|
||||
reverse
|
||||
class="transaction-form-radio"
|
||||
>
|
||||
<BRow
|
||||
class="bg-248 gradido-border-radius position-relative transaction-form-radio"
|
||||
>
|
||||
<BFormRadio name="shipping" :value="SEND_TYPES.link" size="md" reverse>
|
||||
{{ $t('send_per_link') }}
|
||||
</BFormRadio>
|
||||
</BRow>
|
||||
@ -287,7 +279,7 @@ label {
|
||||
display: flex !important;
|
||||
justify-content: space-between;
|
||||
|
||||
> input {
|
||||
> div > input {
|
||||
position: absolute;
|
||||
right: 35px;
|
||||
top: 50%;
|
||||
|
||||
@ -4,6 +4,7 @@ import { createRouter, createWebHistory, RouterLink } from 'vue-router'
|
||||
import { createStore } from 'vuex'
|
||||
import Navbar from './Navbar.vue'
|
||||
import { BImg, BNavbar, BNavbarBrand, BNavbarNav } from 'bootstrap-vue-next'
|
||||
import AppAvatar from '@/components/AppAvatar.vue'
|
||||
|
||||
// Mock vue-avatar
|
||||
vi.mock('vue-avatar', () => ({
|
||||
@ -50,6 +51,7 @@ describe('Navbar', () => {
|
||||
BNavbarBrand,
|
||||
BImg,
|
||||
RouterLink,
|
||||
AppAvatar,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
@ -72,11 +74,11 @@ describe('Navbar', () => {
|
||||
|
||||
describe('.avatar element', () => {
|
||||
it('is rendered', () => {
|
||||
expect(wrapper.findComponent({ name: 'Avatar' }).exists()).toBe(true)
|
||||
expect(wrapper.findComponent({ name: 'AppAvatar' }).exists()).toBe(true)
|
||||
})
|
||||
|
||||
it("has the user's initials", () => {
|
||||
const avatar = wrapper.findComponent({ name: 'Avatar' })
|
||||
const avatar = wrapper.findComponent({ name: 'AppAvatar' })
|
||||
expect(avatar.props('initials')).toBe('TU')
|
||||
})
|
||||
})
|
||||
|
||||
@ -20,12 +20,14 @@
|
||||
<router-link to="/settings">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="me-3">
|
||||
<avatar
|
||||
:username="username.username"
|
||||
<app-avatar
|
||||
class="vue3-avatar"
|
||||
:name="username.username"
|
||||
:initials="username.initials"
|
||||
:border="false"
|
||||
:color="'#fff'"
|
||||
:size="61"
|
||||
></avatar>
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div data-test="navbar-item-username">{{ username.username }}</div>
|
||||
@ -45,13 +47,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Avatar from 'vue-avatar'
|
||||
|
||||
export default {
|
||||
name: 'Navbar',
|
||||
components: {
|
||||
Avatar,
|
||||
},
|
||||
props: {
|
||||
balance: { type: Number, required: true },
|
||||
},
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { nextTick } from 'vue'
|
||||
import { nextTick, ref } from 'vue'
|
||||
import SessionLogoutTimeout from './SessionLogoutTimeout.vue'
|
||||
import { useLazyQuery } from '@vue/apollo-composable'
|
||||
import { useStore } from 'vuex'
|
||||
import { useModal } from 'bootstrap-vue-next'
|
||||
|
||||
// Mock external dependencies
|
||||
vi.mock('vuex', () => ({
|
||||
@ -13,17 +14,15 @@ vi.mock('vuex', () => ({
|
||||
vi.mock('@vue/apollo-composable', () => ({
|
||||
useLazyQuery: vi.fn(() => ({
|
||||
load: vi.fn(),
|
||||
loading: false,
|
||||
error: { value: null },
|
||||
loading: ref(false),
|
||||
error: ref(null),
|
||||
})),
|
||||
}))
|
||||
|
||||
// Mock bootstrap-vue-next
|
||||
const mockShow = vi.fn()
|
||||
const mockHide = vi.fn()
|
||||
vi.mock('bootstrap-vue-next', () => ({
|
||||
useModal: vi.fn(() => ({
|
||||
show: mockShow,
|
||||
hide: mockHide,
|
||||
})),
|
||||
BModal: { template: '<div><slot></slot><slot name="modal-footer"></slot></div>' },
|
||||
@ -59,12 +58,12 @@ describe('SessionLogoutTimeout', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
mockShow.mockClear()
|
||||
mockHide.mockClear()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('renders the component div.session-logout-timeout', () => {
|
||||
@ -72,69 +71,115 @@ describe('SessionLogoutTimeout', () => {
|
||||
expect(wrapper.find('div.session-logout-timeout').exists()).toBe(true)
|
||||
})
|
||||
|
||||
describe('tokenExpiresInSeconds computed property', () => {
|
||||
it('returns 0 when token is expired', async () => {
|
||||
wrapper = createWrapper(setTokenTime(-60))
|
||||
await nextTick()
|
||||
expect(wrapper.vm.tokenExpiresInSeconds).toBe(0)
|
||||
})
|
||||
|
||||
it('returns remaining seconds when token is not expired', async () => {
|
||||
wrapper = createWrapper(setTokenTime(120))
|
||||
await nextTick()
|
||||
expect(wrapper.vm.tokenExpiresInSeconds).toBeGreaterThan(0)
|
||||
expect(wrapper.vm.tokenExpiresInSeconds).toBeLessThanOrEqual(120)
|
||||
})
|
||||
})
|
||||
|
||||
describe('checkExpiration', () => {
|
||||
it('shows modal when token expires in less than 75 seconds', async () => {
|
||||
describe('token expiration', () => {
|
||||
it('shows modal when remaining time is 75 seconds or less', async () => {
|
||||
wrapper = createWrapper(setTokenTime(74))
|
||||
await nextTick()
|
||||
|
||||
vi.runAllTimers()
|
||||
|
||||
vi.runOnlyPendingTimers()
|
||||
await nextTick()
|
||||
expect(mockShow).toHaveBeenCalled()
|
||||
|
||||
const modal = wrapper.findComponent({ name: 'BModal' })
|
||||
expect(modal.props('modelValue')).toBe(true)
|
||||
})
|
||||
|
||||
it('emits logout when token is expired', async () => {
|
||||
wrapper = createWrapper(setTokenTime(-1))
|
||||
it('does not show modal when remaining time is more than 75 seconds', async () => {
|
||||
wrapper = createWrapper(setTokenTime(76))
|
||||
await nextTick()
|
||||
|
||||
vi.runOnlyPendingTimers()
|
||||
await nextTick()
|
||||
|
||||
const modal = wrapper.findComponent({ name: 'BModal' })
|
||||
expect(modal.props('modelValue')).toBe(false)
|
||||
})
|
||||
|
||||
it('emits logout when time expires', async () => {
|
||||
wrapper = createWrapper(setTokenTime(1))
|
||||
await nextTick()
|
||||
|
||||
vi.runAllTimers()
|
||||
await nextTick()
|
||||
|
||||
expect(wrapper.emitted('logout')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('handleOk', () => {
|
||||
it('hides modal and does not emit logout on successful verification', async () => {
|
||||
it('hides modal and continues session on successful verification', async () => {
|
||||
const mockLoad = vi.fn().mockResolvedValue({})
|
||||
vi.mocked(useLazyQuery).mockReturnValue({
|
||||
load: mockLoad,
|
||||
loading: false,
|
||||
error: { value: null },
|
||||
loading: ref(false),
|
||||
error: ref(null),
|
||||
})
|
||||
|
||||
wrapper = createWrapper()
|
||||
await wrapper.vm.handleOk({ preventDefault: vi.fn() })
|
||||
await wrapper.findComponent({ name: 'BButton' }).trigger('click')
|
||||
await nextTick()
|
||||
|
||||
expect(mockLoad).toHaveBeenCalled()
|
||||
expect(mockHide).toHaveBeenCalledWith('modalSessionTimeOut')
|
||||
expect(wrapper.emitted('logout')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('emits logout on failed verification', async () => {
|
||||
const mockLoad = vi.fn().mockRejectedValue(new Error('Verification failed'))
|
||||
it('emits logout on verification failure', async () => {
|
||||
const mockLoad = vi.fn().mockResolvedValue({})
|
||||
vi.mocked(useLazyQuery).mockReturnValue({
|
||||
load: mockLoad,
|
||||
loading: false,
|
||||
error: { value: new Error('Verification failed') },
|
||||
loading: ref(false),
|
||||
error: ref(new Error('Verification failed')),
|
||||
})
|
||||
|
||||
wrapper = createWrapper()
|
||||
await wrapper.vm.handleOk({ preventDefault: vi.fn() })
|
||||
await wrapper.findComponent({ name: 'BButton' }).trigger('click')
|
||||
await nextTick()
|
||||
|
||||
expect(mockLoad).toHaveBeenCalled()
|
||||
expect(wrapper.emitted('logout')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('emits logout when verification throws an error', async () => {
|
||||
const mockLoad = vi.fn().mockRejectedValue(new Error('Network error'))
|
||||
vi.mocked(useLazyQuery).mockReturnValue({
|
||||
load: mockLoad,
|
||||
loading: ref(false),
|
||||
error: ref(null),
|
||||
})
|
||||
|
||||
wrapper = createWrapper()
|
||||
await wrapper.findComponent({ name: 'BButton' }).trigger('click')
|
||||
await nextTick()
|
||||
|
||||
expect(mockLoad).toHaveBeenCalled()
|
||||
expect(wrapper.emitted('logout')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('time formatting', () => {
|
||||
it('formats remaining time correctly', async () => {
|
||||
wrapper = createWrapper(setTokenTime(65))
|
||||
await nextTick()
|
||||
|
||||
const warningText = wrapper.find('.text-warning')
|
||||
expect(warningText.text()).toContain('65')
|
||||
})
|
||||
|
||||
it('shows 00 when time is expired', async () => {
|
||||
wrapper = createWrapper(setTokenTime(-1))
|
||||
await nextTick()
|
||||
|
||||
const warningText = wrapper.find('.text-warning')
|
||||
expect(warningText.text()).toContain('00')
|
||||
})
|
||||
})
|
||||
|
||||
describe('cleanup', () => {
|
||||
it('clears interval on unmount', () => {
|
||||
const clearIntervalSpy = vi.spyOn(window, 'clearInterval')
|
||||
wrapper = createWrapper()
|
||||
wrapper.unmount()
|
||||
expect(clearIntervalSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -54,6 +54,7 @@ export default {
|
||||
font-size: 14px;
|
||||
text-wrap: nowrap;
|
||||
color: black !important;
|
||||
border-radius: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,12 +8,20 @@
|
||||
<BRow align-v="center" class="mb-4">
|
||||
<BCol cols="auto">
|
||||
<div class="align-items-center">
|
||||
<avatar
|
||||
<!-- <avatar-->
|
||||
<!-- class="vue3-avatar"-->
|
||||
<!-- :size="72"-->
|
||||
<!-- :color="'#fff'"-->
|
||||
<!-- :name="`${transaction.linkedUser.firstName} ${transaction.linkedUser.lastName}`"-->
|
||||
<!-- :initials="`${transaction.linkedUser.firstName[0]}${transaction.linkedUser.lastName[0]}`"-->
|
||||
<!-- :border="false"-->
|
||||
<!-- />-->
|
||||
<app-avatar
|
||||
:size="72"
|
||||
:color="'#fff'"
|
||||
:username="`${transaction.linkedUser.firstName} ${transaction.linkedUser.lastName}`"
|
||||
:name="`${transaction.linkedUser.firstName} ${transaction.linkedUser.lastName}`"
|
||||
:initials="`${transaction.linkedUser.firstName[0]}${transaction.linkedUser.lastName[0]}`"
|
||||
></avatar>
|
||||
/>
|
||||
</div>
|
||||
</BCol>
|
||||
<BCol class="p-1">
|
||||
@ -43,11 +51,11 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import Avatar from 'vue-avatar'
|
||||
import Name from '@/components/TransactionRows/Name'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { computed } from 'vue'
|
||||
import AppAvatar from '@/components/AppAvatar.vue'
|
||||
const props = defineProps({
|
||||
transactions: {
|
||||
default: () => [],
|
||||
|
||||
@ -60,11 +60,11 @@
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import Avatar from 'vue-avatar'
|
||||
import CollapseIcon from '../TransactionRows/CollapseIcon'
|
||||
import Name from '../TransactionRows/Name'
|
||||
import DecayInformation from '../DecayInformations/DecayInformation'
|
||||
import { BAvatar, BRow } from 'bootstrap-vue-next'
|
||||
import AppAvatar from '@/components/AppAvatar.vue'
|
||||
|
||||
const props = defineProps({
|
||||
transaction: {
|
||||
@ -92,7 +92,7 @@ const isCreationType = computed(() => {
|
||||
})
|
||||
|
||||
const avatarComponent = computed(() => {
|
||||
return isCreationType.value ? BAvatar : Avatar
|
||||
return isCreationType.value ? BAvatar : AppAvatar
|
||||
})
|
||||
|
||||
const avatarProps = computed(() => {
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import TransactionReceive from './TransactionReceive'
|
||||
import Avatar from 'vue-avatar'
|
||||
import CollapseIcon from '../TransactionRows/CollapseIcon'
|
||||
import Name from '../TransactionRows/Name'
|
||||
import DecayInformation from '../DecayInformations/DecayInformation'
|
||||
import { BCol, BCollapse, BRow } from 'bootstrap-vue-next'
|
||||
import AppAvatar from '@/components/AppAvatar.vue'
|
||||
|
||||
// Mock child components
|
||||
vi.mock('vue-avatar', () => ({
|
||||
vi.mock('app-avatar', () => ({
|
||||
default: {
|
||||
name: 'Avatar',
|
||||
name: 'AppAvatar',
|
||||
render: () => null,
|
||||
},
|
||||
}))
|
||||
@ -86,7 +86,7 @@ describe('TransactionReceive', () => {
|
||||
$d: (date, format) => `Mocked ${format} date for ${date}`,
|
||||
},
|
||||
components: {
|
||||
Avatar,
|
||||
AppAvatar,
|
||||
CollapseIcon,
|
||||
Name,
|
||||
DecayInformation,
|
||||
|
||||
@ -3,12 +3,14 @@
|
||||
<BRow class="align-items-center">
|
||||
<BCol cols="3" lg="2" md="2">
|
||||
<!-- <b-avatar :text="avatarText" variant="success" size="3em"></b-avatar> -->
|
||||
<avatar
|
||||
:username="username.username"
|
||||
<app-avatar
|
||||
class="vue3-avatar"
|
||||
:name="username.username"
|
||||
:initials="username.initials"
|
||||
:color="'#fff'"
|
||||
:size="42"
|
||||
></avatar>
|
||||
:border="false"
|
||||
/>
|
||||
</BCol>
|
||||
<BCol>
|
||||
<div>
|
||||
@ -46,7 +48,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Avatar from 'vue-avatar'
|
||||
import CollapseIcon from '../TransactionRows/CollapseIcon'
|
||||
import Name from '../TransactionRows/Name'
|
||||
import DecayInformation from '../DecayInformations/DecayInformation'
|
||||
@ -54,7 +55,6 @@ import DecayInformation from '../DecayInformations/DecayInformation'
|
||||
export default {
|
||||
name: 'TransactionReceive',
|
||||
components: {
|
||||
Avatar,
|
||||
CollapseIcon,
|
||||
Name,
|
||||
DecayInformation,
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import TransactionSend from './TransactionSend'
|
||||
import Avatar from 'vue-avatar'
|
||||
import CollapseIcon from '../TransactionRows/CollapseIcon'
|
||||
import Name from '../TransactionRows/Name'
|
||||
import DecayInformation from '../DecayInformations/DecayInformation'
|
||||
import { BCol, BCollapse, BRow } from 'bootstrap-vue-next'
|
||||
import AppAvatar from '@/components/AppAvatar.vue'
|
||||
|
||||
vi.mock('vue-avatar', () => ({
|
||||
vi.mock('app-avatar', () => ({
|
||||
default: {
|
||||
name: 'Avatar',
|
||||
name: 'AppAvatar',
|
||||
render: () => null,
|
||||
},
|
||||
}))
|
||||
@ -84,7 +84,7 @@ describe('TransactionSend', () => {
|
||||
$d: (date, format) => `Mocked ${format} date for ${date}`,
|
||||
},
|
||||
components: {
|
||||
Avatar,
|
||||
AppAvatar,
|
||||
CollapseIcon,
|
||||
Name,
|
||||
DecayInformation,
|
||||
|
||||
@ -2,11 +2,13 @@
|
||||
<div class="transaction-slot-send" @click="visible = !visible">
|
||||
<BRow class="align-items-center">
|
||||
<BCol cols="3" lg="2" md="2">
|
||||
<avatar
|
||||
:username="username.username"
|
||||
<app-avatar
|
||||
class="vue3-avatar"
|
||||
:name="username.username"
|
||||
:initials="username.initials"
|
||||
:color="'#fff'"
|
||||
:size="42"
|
||||
:border="false"
|
||||
/>
|
||||
</BCol>
|
||||
<BCol>
|
||||
@ -45,7 +47,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Avatar from 'vue-avatar'
|
||||
import CollapseIcon from '../TransactionRows/CollapseIcon'
|
||||
import Name from '../TransactionRows/Name'
|
||||
import DecayInformation from '../DecayInformations/DecayInformation'
|
||||
@ -53,7 +54,6 @@ import DecayInformation from '../DecayInformations/DecayInformation'
|
||||
export default {
|
||||
name: 'TransactionSend',
|
||||
components: {
|
||||
Avatar,
|
||||
CollapseIcon,
|
||||
Name,
|
||||
DecayInformation,
|
||||
|
||||
@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils'
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import UserCard from './UserCard.vue'
|
||||
import { BCol, BRow } from 'bootstrap-vue-next'
|
||||
import AppAvatar from '@/components/AppAvatar.vue'
|
||||
|
||||
vi.mock('vue-avatar', () => ({
|
||||
default: {
|
||||
@ -39,6 +40,7 @@ describe('UserCard', () => {
|
||||
components: {
|
||||
BRow,
|
||||
BCol,
|
||||
AppAvatar,
|
||||
},
|
||||
mocks,
|
||||
},
|
||||
@ -53,12 +55,12 @@ describe('UserCard', () => {
|
||||
expect(wrapper.find('.userdata-card').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('renders the SPAN Element ".vue-avatar--wrapper"', () => {
|
||||
expect(wrapper.find('.vue-avatar--wrapper').exists()).toBe(true)
|
||||
it('renders the DIV Element ".app-avatar"', () => {
|
||||
expect(wrapper.find('.app-avatar').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('displays the first letters of the firstName and lastName', () => {
|
||||
expect(wrapper.find('.vue-avatar--wrapper span').text()).toBe('BB')
|
||||
expect(wrapper.find('.app-avatar').text()).toBe('BB')
|
||||
})
|
||||
|
||||
it('displays the correct balance', () => {
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<div class="userdata-card">
|
||||
<div class="center-per-margin">
|
||||
<avatar
|
||||
:username="username.username"
|
||||
<app-avatar
|
||||
class="vue3-avatar"
|
||||
:name="username.username"
|
||||
:initials="username.initials"
|
||||
:color="'#fff'"
|
||||
:size="90"
|
||||
></avatar>
|
||||
:border="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="justify-content-center mt-5 mb-5">
|
||||
@ -44,14 +46,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Avatar from 'vue-avatar'
|
||||
import CONFIG from '@/config'
|
||||
|
||||
export default {
|
||||
name: 'UserCard',
|
||||
components: {
|
||||
Avatar,
|
||||
},
|
||||
props: {
|
||||
balance: { type: Number, default: 0 },
|
||||
transactionCount: { type: Number, default: 0 },
|
||||
|
||||
@ -4,8 +4,10 @@ import UserNewsletter from './UserNewsletter'
|
||||
import { unsubscribeNewsletter, subscribeNewsletter } from '@/graphql/mutations'
|
||||
import { createStore } from 'vuex'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import { BFormCheckbox } from 'bootstrap-vue-next'
|
||||
import * as bootstrapVueNext from 'bootstrap-vue-next'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
// Mock composables and dependencies
|
||||
const mockToastError = vi.fn()
|
||||
const mockToastSuccess = vi.fn()
|
||||
|
||||
@ -34,7 +36,7 @@ describe('UserNewsletter', () => {
|
||||
let store
|
||||
let i18n
|
||||
|
||||
const createVuexStore = (initialState) =>
|
||||
const createVuexStore = (initialState = {}) =>
|
||||
createStore({
|
||||
state: {
|
||||
language: 'de',
|
||||
@ -42,7 +44,7 @@ describe('UserNewsletter', () => {
|
||||
...initialState,
|
||||
},
|
||||
mutations: {
|
||||
setNewsletterState(state, value) {
|
||||
newsletterState(state, value) {
|
||||
state.newsletterState = value
|
||||
},
|
||||
},
|
||||
@ -63,11 +65,14 @@ describe('UserNewsletter', () => {
|
||||
const createWrapper = (storeState = {}) => {
|
||||
store = createVuexStore(storeState)
|
||||
i18n = createI18nInstance()
|
||||
|
||||
return mount(UserNewsletter, {
|
||||
global: {
|
||||
plugins: [store, i18n],
|
||||
stubs: {
|
||||
BFormCheckbox: true,
|
||||
plugins: [store, i18n, bootstrapVueNext],
|
||||
config: {
|
||||
compilerOptions: {
|
||||
isCustomElement: (tag) => tag.startsWith('b-'),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -82,8 +87,9 @@ describe('UserNewsletter', () => {
|
||||
expect(wrapper.find('div.formusernewsletter').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('has an edit BFormCheckbox switch', () => {
|
||||
expect(wrapper.find('[test="BFormCheckbox"]').exists()).toBe(true)
|
||||
it('has a BFormCheckbox switch', () => {
|
||||
const checkbox = wrapper.findComponent({ name: 'BFormCheckbox' })
|
||||
expect(checkbox.exists()).toBe(true)
|
||||
})
|
||||
|
||||
describe('unsubscribe with success', () => {
|
||||
@ -94,7 +100,12 @@ describe('UserNewsletter', () => {
|
||||
unsubscribeNewsletter: true,
|
||||
},
|
||||
})
|
||||
wrapper.vm.localNewsletterState = false
|
||||
|
||||
const checkbox = wrapper.findComponent({ name: 'BFormCheckbox' })
|
||||
await checkbox.setValue(false)
|
||||
await nextTick()
|
||||
// Ensure all promises are resolved
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
it('calls the unsubscribe mutation', () => {
|
||||
@ -118,7 +129,12 @@ describe('UserNewsletter', () => {
|
||||
subscribeNewsletter: true,
|
||||
},
|
||||
})
|
||||
wrapper.vm.localNewsletterState = true
|
||||
|
||||
const checkbox = wrapper.findComponent({ name: 'BFormCheckbox' })
|
||||
await checkbox.setValue(true)
|
||||
await nextTick()
|
||||
// Ensure all promises are resolved
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
it('calls the subscribe mutation', () => {
|
||||
@ -138,7 +154,12 @@ describe('UserNewsletter', () => {
|
||||
beforeEach(async () => {
|
||||
wrapper = createWrapper({ newsletterState: true })
|
||||
mockUnsubscribeMutate.mockRejectedValue(new Error('Ouch'))
|
||||
wrapper.vm.localNewsletterState = false
|
||||
|
||||
const checkbox = wrapper.findComponent({ name: 'BFormCheckbox' })
|
||||
await checkbox.setValue(false)
|
||||
await nextTick()
|
||||
// Ensure all promises are resolved
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
it('resets the newsletterState', () => {
|
||||
|
||||
@ -36,13 +36,9 @@ watch(localNewsletterState, async (newValue, oldValue) => {
|
||||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
if (localNewsletterState.value) {
|
||||
await newsletterSubscribe()
|
||||
} else {
|
||||
await newsletterUnsubscribe()
|
||||
}
|
||||
localNewsletterState.value ? await newsletterSubscribe() : await newsletterUnsubscribe()
|
||||
|
||||
store.commit('setNewsletterState', localNewsletterState.value)
|
||||
store.commit('newsletterState', localNewsletterState.value)
|
||||
|
||||
toastSuccess(
|
||||
localNewsletterState.value
|
||||
|
||||
@ -19,13 +19,13 @@ export default defineConfig({
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
vue: '@vue/compat',
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
assets: path.join(__dirname, 'src/assets'),
|
||||
},
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
createHtmlPlugin({
|
||||
minify: true,
|
||||
inject: {
|
||||
@ -41,15 +41,6 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
}),
|
||||
vue({
|
||||
template: {
|
||||
compilerOptions: {
|
||||
compatConfig: {
|
||||
MODE: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Components({
|
||||
resolvers: [BootstrapVueNextResolver(), IconsResolve()],
|
||||
dts: true,
|
||||
|
||||
@ -1756,10 +1756,10 @@
|
||||
vee-validate "4.13.2"
|
||||
yup "^1.3.2"
|
||||
|
||||
"@vitejs/plugin-vue@3.2.0":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz#a1484089dd85d6528f435743f84cdd0d215bbb54"
|
||||
integrity sha512-E0tnaL4fr+qkdCNxJ+Xd0yM31UwMkQje76fsDVBBUCoGOUPexu2VDUYHL8P4CwV+zMvWw6nlRw19OnRKmYAJpw==
|
||||
"@vitejs/plugin-vue@5.1.4":
|
||||
version "5.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz#72b8b705cfce36b00b59af196195146e356500c4"
|
||||
integrity sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==
|
||||
|
||||
"@vitest/coverage-v8@^2.0.5":
|
||||
version "2.0.5"
|
||||
@ -1853,15 +1853,6 @@
|
||||
dependencies:
|
||||
throttle-debounce "^5.0.0"
|
||||
|
||||
"@vue/compat@^3.4.31":
|
||||
version "3.4.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compat/-/compat-3.4.36.tgz#9af3b95adf614354f2595548f162e7af4891aba9"
|
||||
integrity sha512-41qHnPjRAjhNGCR77BQeaNKYNyZNqtZDNgg/3LQ3zIfOA8+cqFFPzZ/Ym1K4Viin0RS7bN4C8/fQFaRrvy7lnA==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.24.7"
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-core@3.4.31":
|
||||
version "3.4.31"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.31.tgz#b51a76f1b30e9b5eba0553264dff0f171aedb7c6"
|
||||
@ -1873,17 +1864,6 @@
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-core@3.4.36":
|
||||
version "3.4.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.36.tgz#4e28dfcbaa8a85e135f7a94c44372b6d52329e42"
|
||||
integrity sha512-qBkndgpwFKdupmOPoiS10i7oFdN7a+4UNDlezD0GlQ1kuA1pNrscg9g12HnB5E8hrWSuEftRsbJhL1HI2zpJhg==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.24.7"
|
||||
"@vue/shared" "3.4.36"
|
||||
entities "^5.0.0"
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-core@3.5.12":
|
||||
version "3.5.12"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.12.tgz#bd70b7dabd12b0b6f31bc53418ba3da77994c437"
|
||||
@ -1903,14 +1883,6 @@
|
||||
"@vue/compiler-core" "3.4.31"
|
||||
"@vue/shared" "3.4.31"
|
||||
|
||||
"@vue/compiler-dom@3.4.36":
|
||||
version "3.4.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.36.tgz#32f5f65d1fb242211df2ddc65a336779cd8b974c"
|
||||
integrity sha512-eEIjy4GwwZTFon/Y+WO8tRRNGqylaRlA79T1RLhUpkOzJ7EtZkkb8MurNfkqY6x6Qiu0R7ESspEF7GkPR/4yYg==
|
||||
dependencies:
|
||||
"@vue/compiler-core" "3.4.36"
|
||||
"@vue/shared" "3.4.36"
|
||||
|
||||
"@vue/compiler-dom@3.5.12":
|
||||
version "3.5.12"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz#456d631d11102535b7ee6fd954cf2c93158d0354"
|
||||
@ -1949,21 +1921,6 @@
|
||||
postcss "^8.4.47"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-sfc@^3.4.35":
|
||||
version "3.4.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.36.tgz#887809183a273dc0ef8337d5e84ef6a781727ccc"
|
||||
integrity sha512-rhuHu7qztt/rNH90dXPTzhB7hLQT2OC4s4GrPVqmzVgPY4XBlfWmcWzn4bIPEWNImt0CjO7kfHAf/1UXOtx3vw==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.24.7"
|
||||
"@vue/compiler-core" "3.4.36"
|
||||
"@vue/compiler-dom" "3.4.36"
|
||||
"@vue/compiler-ssr" "3.4.36"
|
||||
"@vue/shared" "3.4.36"
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.30.10"
|
||||
postcss "^8.4.40"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
"@vue/compiler-ssr@3.4.31":
|
||||
version "3.4.31"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.31.tgz#f62ffecdf15bacb883d0099780cf9a1e3654bfc4"
|
||||
@ -1972,14 +1929,6 @@
|
||||
"@vue/compiler-dom" "3.4.31"
|
||||
"@vue/shared" "3.4.31"
|
||||
|
||||
"@vue/compiler-ssr@3.4.36":
|
||||
version "3.4.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.36.tgz#5881f9303ad6a4fdf04fb4238ebb483caf040707"
|
||||
integrity sha512-Wt1zyheF0zVvRJyhY74uxQbnkXV2Le/JPOrAxooR4rFYKC7cFr+cRqW6RU3cM/bsTy7sdZ83IDuy/gLPSfPGng==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.4.36"
|
||||
"@vue/shared" "3.4.36"
|
||||
|
||||
"@vue/compiler-ssr@3.5.12":
|
||||
version "3.5.12"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz#5f1a3fbd5c44b79a6dbe88729f7801d9c9218bde"
|
||||
@ -2073,11 +2022,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.31.tgz#af9981f57def2c3f080c14bf219314fc0dc808a0"
|
||||
integrity sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==
|
||||
|
||||
"@vue/shared@3.4.36":
|
||||
version "3.4.36"
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.36.tgz#7551f41684966acb6a307152b49a8308e7f69203"
|
||||
integrity sha512-fdPLStwl1sDfYuUftBaUVn2pIrVFDASYerZSrlBvVBfylObPA1gtcWJHy5Ox8jLEJ524zBibss488Q3SZtU1uA==
|
||||
|
||||
"@vue/shared@3.5.12":
|
||||
version "3.5.12"
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.12.tgz#f9e45b7f63f2c3f40d84237b1194b7f67de192e3"
|
||||
@ -3312,11 +3256,6 @@ entities@^4.2.0, entities@^4.4.0, entities@^4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||
|
||||
entities@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-5.0.0.tgz#b2ab51fe40d995817979ec79dd621154c3c0f62b"
|
||||
integrity sha512-BeJFvFRJddxobhvEdm5GqHzRV/X+ACeuw0/BuuxsCh1EUZcAIz8+kYmBp/LrQuloy6K1f3a0M7+IhmZ7QnkISA==
|
||||
|
||||
env-paths@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
||||
@ -5927,7 +5866,7 @@ postcss-value-parser@^4.2.0:
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
|
||||
postcss@^8.4.0, postcss@^8.4.18, postcss@^8.4.38, postcss@^8.4.39, postcss@^8.4.40, postcss@^8.4.8:
|
||||
postcss@^8.4.0, postcss@^8.4.18, postcss@^8.4.38, postcss@^8.4.39, postcss@^8.4.8:
|
||||
version "8.4.41"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.41.tgz#d6104d3ba272d882fe18fc07d15dc2da62fa2681"
|
||||
integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==
|
||||
@ -7335,11 +7274,6 @@ vue-apollo@^3.1.2:
|
||||
serialize-javascript "^4.0.0"
|
||||
throttle-debounce "^2.1.0"
|
||||
|
||||
vue-avatar@^2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-avatar/-/vue-avatar-2.3.3.tgz#e125bf4f4a6f4f9480da0c522020266a8609d2a8"
|
||||
integrity sha512-Z57ILRTkFIAuCH9JiFBxX74C5zua5ub/jRDM/KZ+QKXNfscvmUOgWBs3kA2+wrpZMowIvfLHIT0gvQu1z+zpLg==
|
||||
|
||||
vue-component-type-helpers@^2.0.0:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/vue-component-type-helpers/-/vue-component-type-helpers-2.1.6.tgz#f350515b252ed9e76960ac51f135636f8baef3fe"
|
||||
@ -7401,6 +7335,11 @@ vue-timers@^2.0.4:
|
||||
resolved "https://registry.yarnpkg.com/vue-timers/-/vue-timers-2.0.4.tgz#7e1c443abf2109db5eeab6e62b0f5a47e94cf70b"
|
||||
integrity sha512-QOEVdO4V4o9WjFG6C0Kn9tfdTeeECjqvEQozcQlfL1Tn8v0qx4uUPhTYoc1+s6qoJnSbu8f68x8+nm1ZEir0kw==
|
||||
|
||||
vue3-avatar@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/vue3-avatar/-/vue3-avatar-3.1.0.tgz#0941fcd0ea450868af6a70f9aa7ee033467f7d13"
|
||||
integrity sha512-K1h98jJckc3WksZsme80CHxTLDU7aMSOujrhPGFcJ5Yxoq3I+cqgbe4wUh52G8vM65JbxfbS+BmCcladg0o6GA==
|
||||
|
||||
vue@3.4.31:
|
||||
version "3.4.31"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.31.tgz#83a3c4dab8302b0e974b0d4b92a2f6a6378ae797"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user