From 128b8f1a2ca71b1469d67246f466184dcd852ce1 Mon Sep 17 00:00:00 2001 From: Konstantinos Paparas Date: Wed, 11 Dec 2024 16:06:35 +0100 Subject: [PATCH 1/2] fix: add local state for vat id check timeout --- .../account/home/AccountInformation.vue | 115 ++++++++++-------- 1 file changed, 61 insertions(+), 54 deletions(-) diff --git a/components/account/home/AccountInformation.vue b/components/account/home/AccountInformation.vue index 06ca3a44..3c1e28cc 100644 --- a/components/account/home/AccountInformation.vue +++ b/components/account/home/AccountInformation.vue @@ -21,21 +21,12 @@ const state = reactive({ const loading = ref(false); const done = ref(false); const vatSuccessMessage = ref(''); +const loadingCheck = ref(false); +const $externalResults = ref>({}); +const remainingTime = ref(0); const { account } = storeToRefs(store); -const movedOffline = computed( - () => get(account)?.address.movedOffline ?? false, -); - -const isVatIdValid = computed( - () => get(account)?.vatIdStatus === VatIdStatus.VALID || false, -); - -onBeforeMount(() => { - reset(); -}); - const rules = { firstName: { required }, lastName: { required }, @@ -43,19 +34,60 @@ const rules = { vatId: {}, }; -const $externalResults = ref>({}); +const waitUntilTime = useLocalStorage('rotki.vat_check.wait_until_time', 0); + +const [DefineVAT, ReuseVAT] = createReusableTemplate(); const v$ = useVuelidate(rules, state, { $autoDirty: true, $externalResults, }); +const { pause: pauseTimer, resume } = useIntervalFn(() => { + if (!updateRemainingTime()) { + set(remainingTime, 0); + pauseTimer(); + } +}, 1000, { immediate: false }); + const { public: { contact: { supportEmail, supportEmailMailto }, }, } = useRuntimeConfig(); +const movedOffline = computed(() => get(account)?.address.movedOffline ?? false); +const isVatIdValid = computed(() => get(account)?.vatIdStatus === VatIdStatus.VALID || false); + +const vatHint = computed(() => { + const wait = get(remainingTime); + if (wait > 0) { + const formatted = formatSeconds(wait); + const time = `${(formatted.minutes || '00').toString().padStart(2, '0')}:${(formatted.seconds || '00').toString().padStart(2, '0')}`; + return t('auth.signup.vat.timer', { time }); + } + + return t('auth.signup.customer_information.form.vat_id_hint'); +}); + +const hideVATVerifyButton = computed(() => { + const status = get(account)?.vatIdStatus; + return status === VatIdStatus.NON_EU_ID; +}); + +const vatErrorMessage = computed(() => { + if (get(remainingTime) > 0) + return ''; + const status = get(account)?.vatIdStatus; + if (status === VatIdStatus.NOT_VALID) { + return t('auth.signup.vat.invalid'); + } + else if (status === VatIdStatus.NOT_CHECKED) { + return t('auth.signup.vat.not_verified'); + } + return ''; +}); + function reset() { const userAccount = get(account); @@ -103,26 +135,15 @@ async function update() { set(loading, false); } -const waitTime = ref(0); -const loadingCheck = ref(false); - -const { pause: pauseTimer, resume } = useIntervalFn(() => { - const wait = get(waitTime); - if (wait > 0) { - set(waitTime, wait - 1); - } - else { - pauseTimer(); - } -}, 1000, { immediate: false }); - async function handleCheckVATClick() { set(loadingCheck, true); const result = await store.checkVAT(); await store.refreshVATCheckStatus(); if (typeof result === 'number') { - set(waitTime, result); + const now = Math.round(Date.now() / 1000); + set(waitUntilTime, now + result); + set(remainingTime, result); pauseTimer(); if (result > 0) { resume(); @@ -137,35 +158,21 @@ async function handleCheckVATClick() { set(loadingCheck, false); } -const [DefineVAT, ReuseVAT] = createReusableTemplate(); +function updateRemainingTime(): boolean { + const now = Math.round(Date.now() / 1000); + const endTime = get(waitUntilTime); -const vatHint = computed(() => { - const wait = get(waitTime); - if (wait > 0) { - const formatted = formatSeconds(wait); - const time = `${(formatted.minutes || '00').toString().padStart(2, '0')}:${(formatted.seconds || '00').toString().padStart(2, '0')}`; - return t('auth.signup.vat.timer', { time }); + if (endTime > now) { + set(remainingTime, endTime - now); + return true; } + return false; +} - return t('auth.signup.customer_information.form.vat_id_hint'); -}); - -const hideVATVerifyButton = computed(() => { - const status = get(account)?.vatIdStatus; - return status === VatIdStatus.NON_EU_ID; -}); - -const vatErrorMessage = computed(() => { - if (get(waitTime) > 0) - return ''; - const status = get(account)?.vatIdStatus; - if (status === VatIdStatus.NOT_VALID) { - return t('auth.signup.vat.invalid'); - } - else if (status === VatIdStatus.NOT_CHECKED) { - return t('auth.signup.vat.not_verified'); - } - return ''; +onBeforeMount(() => { + reset(); + resume(); + updateRemainingTime(); }); @@ -198,7 +205,7 @@ const vatErrorMessage = computed(() => { v-if="!hideVATVerifyButton" color="primary" class="h-10" - :disabled="waitTime > 0" + :disabled="remainingTime > 0 || !state.vatId" :loading="loadingCheck" @click="handleCheckVATClick()" > From 8ea6c6931f87c5acb598740aa97ff99aa42004ba Mon Sep 17 00:00:00 2001 From: Konstantinos Paparas Date: Wed, 11 Dec 2024 17:14:07 +0100 Subject: [PATCH 2/2] fix: properly handle errors when check vat fails --- .../account/home/AccountInformation.vue | 46 +++++--- composables/use-vat-check.ts | 106 ++++++++++++++++++ store/index.ts | 49 -------- 3 files changed, 135 insertions(+), 66 deletions(-) create mode 100644 composables/use-vat-check.ts diff --git a/components/account/home/AccountInformation.vue b/components/account/home/AccountInformation.vue index 3c1e28cc..e0203338 100644 --- a/components/account/home/AccountInformation.vue +++ b/components/account/home/AccountInformation.vue @@ -3,6 +3,7 @@ import { useVuelidate } from '@vuelidate/core'; import { required } from '@vuelidate/validators'; import { get, objectOmit, set } from '@vueuse/core'; import { storeToRefs } from 'pinia'; +import { useVatCheck } from '~/composables/use-vat-check'; import { useMainStore } from '~/store'; import { toMessages } from '~/utils/validation'; import { VatIdStatus } from '~/types/account'; @@ -18,9 +19,10 @@ const state = reactive({ vatId: '', }); -const loading = ref(false); -const done = ref(false); -const vatSuccessMessage = ref(''); +const loading = ref(false); +const done = ref(false); +const vatSuccessMessage = ref(''); +const vatErrorMessage = ref(''); const loadingCheck = ref(false); const $externalResults = ref>({}); const remainingTime = ref(0); @@ -37,6 +39,7 @@ const rules = { const waitUntilTime = useLocalStorage('rotki.vat_check.wait_until_time', 0); const [DefineVAT, ReuseVAT] = createReusableTemplate(); +const { refreshVATCheckStatus, checkVAT } = useVatCheck(); const v$ = useVuelidate(rules, state, { $autoDirty: true, @@ -75,8 +78,8 @@ const hideVATVerifyButton = computed(() => { return status === VatIdStatus.NON_EU_ID; }); -const vatErrorMessage = computed(() => { - if (get(remainingTime) > 0) +const vatStatusErrorMessage = computed(() => { + if (get(remainingTime) > 0 || state.vatId === '') return ''; const status = get(account)?.vatIdStatus; if (status === VatIdStatus.NOT_VALID) { @@ -137,22 +140,31 @@ async function update() { async function handleCheckVATClick() { set(loadingCheck, true); - const result = await store.checkVAT(); - await store.refreshVATCheckStatus(); + const checkResult = await checkVAT(); + await refreshVATCheckStatus(); - if (typeof result === 'number') { + if ('seconds' in checkResult) { const now = Math.round(Date.now() / 1000); - set(waitUntilTime, now + result); - set(remainingTime, result); + set(waitUntilTime, now + checkResult.seconds); + set(remainingTime, checkResult.seconds); pauseTimer(); - if (result > 0) { + if (checkResult.seconds > 0) { resume(); } } - else if (result) { - set(vatSuccessMessage, t('auth.signup.vat.verified')); + else if (checkResult.success) { + const status = get(account)?.vatIdStatus; + if (status === VatIdStatus.VALID) { + set(vatSuccessMessage, t('auth.signup.vat.verified')); + setTimeout(() => { + set(vatSuccessMessage, ''); + }, 3000); + } + } + else { + set(vatErrorMessage, checkResult.message); setTimeout(() => { - set(vatSuccessMessage, ''); + set(vatErrorMessage, ''); }, 3000); } set(loadingCheck, false); @@ -169,7 +181,7 @@ function updateRemainingTime(): boolean { return false; } -onBeforeMount(() => { +onMounted(() => { reset(); resume(); updateRemainingTime(); @@ -188,13 +200,13 @@ onBeforeMount(() => { class="flex-1" :label="t('auth.signup.customer_information.form.vat_id')" :hint="vatHint" - :error-messages="[...toMessages(v$.vatId), vatErrorMessage]" + :error-messages="[...toMessages(v$.vatId), vatStatusErrorMessage, vatErrorMessage]" :success-messages="vatSuccessMessage" @blur="v$.vatId.$touch()" >