diff --git a/frontend/package.json b/frontend/package.json index 07a6a28da..5e62aac10 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -48,6 +48,7 @@ "portal-vue": "^3.0.0", "qrcanvas-vue": "3", "regenerator-runtime": "^0.13.7", + "tua-body-scroll-lock": "^1.5.1", "uuid": "^9.0.0", "vee-validate": "^4.13.2", "vite": "3.2.10", diff --git a/frontend/src/App.spec.js b/frontend/src/App.spec.js index 183aa1f1c..e04b483be 100644 --- a/frontend/src/App.spec.js +++ b/frontend/src/App.spec.js @@ -1,68 +1,5 @@ -// import { shallowMount, RouterLinkStub } from '@vue/test-utils' -// import App from './App' -// -// const localVue = global.localVue -// const mockStoreCommit = jest.fn() -// -// const stubs = { -// RouterLink: RouterLinkStub, -// RouterView: true, -// } -// -// describe('App', () => { -// const mocks = { -// $i18n: { -// locale: 'en', -// }, -// $t: jest.fn((t) => t), -// $store: { -// commit: mockStoreCommit, -// state: { -// token: null, -// }, -// }, -// $route: { -// meta: { -// requiresAuth: false, -// }, -// params: {}, -// }, -// } -// -// let wrapper -// -// const Wrapper = () => { -// return shallowMount(App, { localVue, mocks, stubs }) -// } -// -// describe('mount', () => { -// beforeEach(() => { -// wrapper = Wrapper() -// }) -// -// it('renders the App', () => { -// expect(wrapper.find('#app').exists()).toBe(true) -// }) -// -// it('has a component AuthLayout', () => { -// expect(wrapper.findComponent({ name: 'AuthLayout' }).exists()).toBe(true) -// }) -// -// describe('route requires authorization', () => { -// beforeEach(async () => { -// mocks.$route.meta.requiresAuth = true -// wrapper = Wrapper() -// }) -// -// it('has a component DashboardLayout', () => { -// expect(wrapper.findComponent({ name: 'DashboardLayout' }).exists()).toBe(true) -// }) -// }) -// }) -// }) - import { shallowMount } from '@vue/test-utils' -import { describe, it, expect, beforeEach, vi } from 'vitest' +import { describe, it, expect, beforeEach } from 'vitest' import App from './App' import DashboardLayout from '@/layouts/DashboardLayout' import AuthLayout from '@/layouts/AuthLayout' diff --git a/frontend/src/assets/scss/gradido-template.scss b/frontend/src/assets/scss/gradido-template.scss index 8701f7d98..85d2a783c 100644 --- a/frontend/src/assets/scss/gradido-template.scss +++ b/frontend/src/assets/scss/gradido-template.scss @@ -345,3 +345,15 @@ a:hover, .gdd-toaster-body { color: rgb(255 255 255); } + +.gdd-toaster-body-darken { + color: #2c2c2c; +} + +input.rounded-input { + border-radius: 17px; +} + +.fs-7 { + font-size: 14px !important; +} diff --git a/frontend/src/components/DecayInformations/CollapseLinksList.vue b/frontend/src/components/DecayInformations/CollapseLinksList.vue index 73f1963fa..fcd58dbb0 100644 --- a/frontend/src/components/DecayInformations/CollapseLinksList.vue +++ b/frontend/src/components/DecayInformations/CollapseLinksList.vue @@ -13,7 +13,7 @@
{ }) it('has the placeholder "input-field-placeholder"', () => { - expect(wrapper.find('input').attributes('placeholder')).toBe('input-field-placeholder') + expect(wrapper.find('input').attributes('placeholder')).toBe('form.email') }) it('has the label "input-field-label"', () => { - expect(wrapper.find('label').text()).toBe('input-field-label') + expect(wrapper.find('label').text()).toBe('form.email') }) it('has the label for "input-field-name-input-field"', () => { diff --git a/frontend/src/components/Inputs/InputEmail.vue b/frontend/src/components/Inputs/InputEmail.vue index 89a147bf2..935a8510b 100644 --- a/frontend/src/components/Inputs/InputEmail.vue +++ b/frontend/src/components/Inputs/InputEmail.vue @@ -11,6 +11,7 @@ :placeholder="defaultTranslations.placeholder" type="email" trim + class="rounded-input" :class="$route.path === '/send' ? 'bg-248' : ''" :disabled="disabled" autocomplete="off" @@ -33,14 +34,6 @@ const props = defineProps({ type: String, default: 'email', }, - label: { - type: String, - default: 'Email', - }, - placeholder: { - type: String, - default: 'Email', - }, disabled: { type: Boolean, default: false, @@ -54,8 +47,8 @@ const { value, errorMessage, validate, meta } = useField(() => props.name, 'requ const { t } = useI18n() const defaultTranslations = computed(() => ({ - label: props.label ?? t('form.email'), - placeholder: props.placeholder ?? t('form.email'), + label: t('form.email'), + placeholder: t('form.email'), })) const normalizeEmail = (emailAddress) => { diff --git a/frontend/src/components/Inputs/InputPassword.spec.js b/frontend/src/components/Inputs/InputPassword.spec.js index 94e50e026..9d6f5481d 100644 --- a/frontend/src/components/Inputs/InputPassword.spec.js +++ b/frontend/src/components/Inputs/InputPassword.spec.js @@ -80,7 +80,7 @@ describe('InputPassword', () => { }) it('has the placeholder "input-field-placeholder"', () => { - expect(wrapper.find('input').attributes('placeholder')).toEqual('input-field-placeholder') + expect(wrapper.find('input').attributes('placeholder')).toEqual('form.password') }) it('has the value ""', () => { @@ -88,7 +88,7 @@ describe('InputPassword', () => { }) it('has the label "input-field-label"', () => { - expect(wrapper.find('label').text()).toEqual('input-field-label') + expect(wrapper.find('label').text()).toEqual('form.password') }) it('has the label for "input-field-name-input-field"', () => { diff --git a/frontend/src/components/Inputs/InputPassword.vue b/frontend/src/components/Inputs/InputPassword.vue index fa5f60a7e..b496d09a3 100644 --- a/frontend/src/components/Inputs/InputPassword.vue +++ b/frontend/src/components/Inputs/InputPassword.vue @@ -50,14 +50,6 @@ const props = defineProps({ type: String, default: 'password', }, - label: { - type: String, - default: 'Password', - }, - placeholder: { - type: String, - default: 'Password', - }, immediate: { type: Boolean, default: false, @@ -78,18 +70,11 @@ const { value, errorMessage, meta, errors, validate } = useField(name, props.rul validateOnMount: props.immediate, }) -// onMounted(async () => { -// await nextTick() -// if (props.immediate) { -// await validate() -// } -// }) - const { t } = useI18n() const defaultTranslations = computed(() => ({ - label: props.label ?? t('form.password'), - placeholder: props.placeholder ?? t('form.password'), + label: t('form.password'), + placeholder: t('form.password'), })) const showPassword = ref(false) @@ -109,3 +94,9 @@ const ariaMsg = computed(() => ({ const labelFor = computed(() => `${props.name}-input-field`) + + diff --git a/frontend/src/components/Menu/Navbar.vue b/frontend/src/components/Menu/Navbar.vue index cd6c0929d..5b1654278 100644 --- a/frontend/src/components/Menu/Navbar.vue +++ b/frontend/src/components/Menu/Navbar.vue @@ -129,8 +129,9 @@ button.navbar-toggler > span.navbar-toggler-icon { .navbar-element { z-index: 1000; position: fixed; - width: 100%; background-color: #f5f5f5e6; + left: 0; + right: 0; } .sheet-img { diff --git a/frontend/src/components/MobileSidebar/MobileSidebar.vue b/frontend/src/components/MobileSidebar/MobileSidebar.vue index bfc76b25b..d416db5f8 100644 --- a/frontend/src/components/MobileSidebar/MobileSidebar.vue +++ b/frontend/src/components/MobileSidebar/MobileSidebar.vue @@ -7,6 +7,7 @@ no-header-close horizontal skip-animation + @update:model-value="isMobileMenuOpen = $event" >
@@ -17,14 +18,30 @@ diff --git a/frontend/src/components/TransactionRows/Name.spec.js b/frontend/src/components/TransactionRows/Name.spec.js index 90bec127f..1504120b4 100644 --- a/frontend/src/components/TransactionRows/Name.spec.js +++ b/frontend/src/components/TransactionRows/Name.spec.js @@ -1,4 +1,4 @@ -import { mount } from '@vue/test-utils' +import { mount, RouterLinkStub } from '@vue/test-utils' import { describe, it, expect, beforeEach, vi } from 'vitest' import Name from './Name' import { BLink } from 'bootstrap-vue-next' @@ -44,7 +44,7 @@ describe('Name', () => { global: { mocks, stubs: { - BLink, + RouterLink: RouterLinkStub, }, }, props: propsData, @@ -88,31 +88,18 @@ describe('Name', () => { it('has a link', () => { expect( - wrapper - .find('div.gdd-transaction-list-item-name') - .findComponent({ name: 'BLink' }) - .exists(), + wrapper.find('div.gdd-transaction-list-item-name').findComponent(RouterLinkStub).exists(), ).toBe(true) }) - describe('click link', () => { - beforeEach(async () => { - await wrapper.findComponent({ name: 'BLink' }).trigger('click') - }) - - it('pushes router to send', () => { - expect(routerPushMock).toHaveBeenCalledWith({ - path: '/send', - }) - }) - - it('pushes params for gradidoID and community UUID', () => { - expect(routerPushMock).toHaveBeenCalledWith({ - params: { - communityIdentifier: 'community UUID', - userIdentifier: 'gradido-ID', - }, - }) + it('RouterLink has correct to prop', () => { + const routerLink = wrapper.findComponent(RouterLinkStub) + expect(routerLink.props().to).toEqual({ + name: 'Send', + params: { + communityIdentifier: 'community UUID', + userIdentifier: 'gradido-ID', + }, }) }) }) diff --git a/frontend/src/components/TransactionRows/Name.vue b/frontend/src/components/TransactionRows/Name.vue index 1eb94319b..246fbf933 100644 --- a/frontend/src/components/TransactionRows/Name.vue +++ b/frontend/src/components/TransactionRows/Name.vue @@ -2,9 +2,9 @@
- + {{ itemText }} - +
{{ itemText }}
@@ -45,6 +45,15 @@ export default { (this.linkedUser.communityName ? ' / ' + this.linkedUser.communityName : '') : this.text }, + pushTo() { + return { + name: 'Send', + params: { + userIdentifier: this.linkedUser.gradidoID, + communityIdentifier: this.linkedUser.communityUuid, + }, + } + }, }, methods: { async tunnelEmail() { diff --git a/frontend/src/composables/useToast.js b/frontend/src/composables/useToast.js index bc9bdbe49..826119ec2 100644 --- a/frontend/src/composables/useToast.js +++ b/frontend/src/composables/useToast.js @@ -22,6 +22,7 @@ export function useAppToast() { toast(message, { title: t('navigation.info'), variant: 'warning', + bodyClass: 'gdd-toaster-body-darken', }) } diff --git a/frontend/src/layouts/DashboardLayout.spec.js b/frontend/src/layouts/DashboardLayout.spec.js index 37feb0d2f..0906793ff 100644 --- a/frontend/src/layouts/DashboardLayout.spec.js +++ b/frontend/src/layouts/DashboardLayout.spec.js @@ -1,6 +1,6 @@ import { mount } from '@vue/test-utils' import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest' -import { nextTick } from 'vue' +import { nextTick, ref } from 'vue' import DashboardLayout from './DashboardLayout' import { createStore } from 'vuex' import { createRouter, createWebHistory } from 'vue-router' @@ -15,11 +15,15 @@ vi.mock('@/composables/useToast', () => ({ })) const mockQueryFn = vi.fn() +const mockRefetchFn = vi.fn() const mockMutateFn = vi.fn() +const mockQueryResult = ref(null) vi.mock('@vue/apollo-composable', () => ({ useLazyQuery: vi.fn(() => ({ load: mockQueryFn, + refetch: mockRefetchFn, + result: mockQueryResult, onResult: vi.fn(), onError: vi.fn(), })), @@ -136,25 +140,25 @@ describe('DashboardLayout', () => { describe('update transactions', () => { beforeEach(async () => { - mockQueryFn.mockResolvedValue({ + mockQueryResult.value = { transactionList: { balance: { - balanceGDT: 100, + balanceGDT: '100', count: 4, linkCount: 8, - balance: 1450, - decay: 1250, + balance: '1450', }, - transactions: ['transaction', 'transaction', 'transaction', 'transaction'], + transactions: ['transaction1', 'transaction2', 'transaction3', 'transaction4'], }, - }) - await wrapper - .findComponent({ ref: 'router-view' }) - .vm.$emit('update-transactions', { currentPage: 2, pageSize: 5 }) - await nextTick() + } + + mockQueryFn.mockResolvedValue(mockQueryResult.value) + + await wrapper.vm.updateTransactions({ currentPage: 2, pageSize: 5 }) + await nextTick() // Ensure all promises are resolved }) - it('calls the API', () => { + it('load call to the API', () => { expect(mockQueryFn).toHaveBeenCalled() }) @@ -164,10 +168,10 @@ describe('DashboardLayout', () => { it('updates transactions', () => { expect(wrapper.vm.transactions).toEqual([ - 'transaction', - 'transaction', - 'transaction', - 'transaction', + 'transaction1', + 'transaction2', + 'transaction3', + 'transaction4', ]) }) diff --git a/frontend/src/layouts/DashboardLayout.vue b/frontend/src/layouts/DashboardLayout.vue index c06f46c46..bfd9f2049 100755 --- a/frontend/src/layouts/DashboardLayout.vue +++ b/frontend/src/layouts/DashboardLayout.vue @@ -215,7 +215,11 @@ import { useAppToast } from '@/composables/useToast' const store = useStore() const router = useRouter() const { load: useCommunityStatsQuery } = useLazyQuery(communityStatistics) -const { load: useTransactionsQuery } = useLazyQuery(transactionsQuery) +const { + load: useTransactionsQuery, + refetch: useRefetchTransactionsQuery, + result: transactionQueryResult, +} = useLazyQuery(transactionsQuery) const { mutate: useLogoutMutation } = useMutation(logout) const { t } = useI18n() const { toastError } = useAppToast() @@ -239,7 +243,7 @@ const testModal = () => { } onMounted(() => { - updateTransactions({ currentPage: 0, pageSize: 10 }) + updateTransactions({ currentPage: 1, pageSize: 10 }) getCommunityStatistics() setTimeout(() => { skeleton.value = false @@ -260,9 +264,9 @@ const logoutUser = async () => { const updateTransactions = async ({ currentPage, pageSize }) => { pending.value = true try { - const result = await useTransactionsQuery() - if (!result) return // TODO this return mitigate an error when this method is called second time but without actual request - const { transactionList } = result + await loadOrFetchTransactionQuery({ currentPage, pageSize }) + if (!transactionQueryResult) return + const { transactionList } = transactionQueryResult.value GdtBalance.value = transactionList.balance.balanceGDT === null ? 0 : Number(transactionList.balance.balanceGDT) transactions.value = transactionList.transactions @@ -277,6 +281,13 @@ const updateTransactions = async ({ currentPage, pageSize }) => { } } +const loadOrFetchTransactionQuery = async (queryVariables = { currentPage: 1, pageSize: 25 }) => { + return ( + (await useTransactionsQuery(transactionsQuery, queryVariables)) || + (await useRefetchTransactionsQuery(queryVariables)) + ) +} + const getCommunityStatistics = async () => { try { const result = await useCommunityStatsQuery() diff --git a/frontend/src/pages/Login.vue b/frontend/src/pages/Login.vue index 26828257e..f02fc64ed 100644 --- a/frontend/src/pages/Login.vue +++ b/frontend/src/pages/Login.vue @@ -19,10 +19,11 @@ - + import('@/pages/Send'), + name: 'Send', + props: true, meta: { requiresAuth: true, pageTitle: 'send', diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c48c758f5..a4d6d07ca 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -6772,6 +6772,11 @@ tslib@^2.1.0, tslib@^2.3.0, tslib@^2.6.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== +tua-body-scroll-lock@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/tua-body-scroll-lock/-/tua-body-scroll-lock-1.5.1.tgz#1b8b7316dff55a821d5bec3fef045f995e7627a5" + integrity sha512-AOjusG9EjTGxqqL1xqg6JeMauJ+IQoX9ITW1qP7UugySUdH6lzi2CqJRmU+oYqOv7vCQjOs5CQrjIakGlbOenQ== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"