Skip to content

Commit

Permalink
feat: show button to verify vat ID
Browse files Browse the repository at this point in the history
  • Loading branch information
lukicenturi committed Dec 10, 2024
1 parent 01c967e commit 8fefa7e
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 12 deletions.
113 changes: 101 additions & 12 deletions components/account/home/AccountInformation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { storeToRefs } from 'pinia';
import { useMainStore } from '~/store';
import { toMessages } from '~/utils/validation';
const { t } = useI18n();
const store = useMainStore();
const state = reactive({
firstName: '',
Expand All @@ -23,6 +25,10 @@ const movedOffline = computed(
() => get(account)?.address.movedOffline ?? false,
);
const isVatIdValid = computed(
() => get(account)?.vatIdStatus === 'Valid' || false,
);
onBeforeMount(() => {
reset();
});
Expand Down Expand Up @@ -55,13 +61,14 @@ function reset() {
const unavailable = get(movedOffline);
state.vatId = userAccount.address.vatId;
if (unavailable)
return;
state.firstName = userAccount.address.firstName;
state.lastName = userAccount.address.lastName;
state.companyName = userAccount.address.companyName;
state.vatId = userAccount.address.vatId;
get(v$).$reset();
}
Expand Down Expand Up @@ -93,10 +100,94 @@ async function update() {
set(loading, false);
}
const { t } = useI18n();
const waitTime = ref<number>(0);
const loadingCheck = ref<boolean>(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);
pauseTimer();
if (result > 0) {
resume();
}
}
set(loadingCheck, false);
}
const [DefineVAT, ReuseVAT] = createReusableTemplate();
</script>

<template>
<DefineVAT>
<div class="flex gap-4 items-start">
<RuiTextField
id="vat-id"
v-model="state.vatId"
variant="outlined"
color="primary"
class="flex-1"
:label="t('auth.signup.customer_information.form.vat_id')"
:hint="t('auth.signup.customer_information.form.vat_id_hint')"
:error-messages="toMessages(v$.vatId)"
@blur="v$.vatId.$touch()"
/>
<RuiTooltip persist-on-tooltip-hover>
<template #activator>
<RuiIcon
class="mt-4"
:name="isVatIdValid ? 'lu-circle-check' : 'lu-circle-x'"
:color="isVatIdValid ? 'success' : 'error'"
/>
</template>
<div v-if="!isVatIdValid">
<div
v-if="waitTime > 0"
class="flex flex-col gap-1.5 items-center py-1.5"
>
{{ t('auth.signup.vat.cooldown') }}
<div class="text-grey-500">
{{ t('auth.signup.vat.timer', { waitTime }) }}
</div>
</div>
<div
v-else
class="flex flex-col gap-1.5 items-center py-1.5"
>
{{ t('auth.signup.vat.unverified') }}
<RuiButton
color="primary"
size="sm"
:disabled="waitTime > 0"
:loading="loadingCheck"
@click="handleCheckVATClick()"
>
{{ t('auth.signup.vat.verify_now') }}
</RuiButton>
</div>
</div>
<div
v-else
class="flex flex-col gap-1.5 items-center py-1.5"
>
{{ t('auth.signup.vat.verified') }}
</div>
</RuiTooltip>
</div>
</DefineVAT>
<div
v-if="!movedOffline"
class="pt-2"
Expand Down Expand Up @@ -138,16 +229,7 @@ const { t } = useI18n();
@blur="v$.companyName.$touch()"
/>

<RuiTextField
id="vat-id"
v-model="state.vatId"
variant="outlined"
color="primary"
:label="t('auth.signup.customer_information.form.vat_id')"
:hint="t('auth.signup.customer_information.form.vat_id_hint')"
:error-messages="toMessages(v$.vatId)"
@blur="v$.vatId.$touch()"
/>
<ReuseVAT />
</div>

<div class="mt-10 mb-5 border-t border-grey-50" />
Expand Down Expand Up @@ -194,6 +276,13 @@ const { t } = useI18n();
</i18n-t>
</RuiAlert>

<div
v-if="movedOffline"
class="mt-4"
>
<ReuseVAT />
</div>

<FloatingNotification
type="success"
closeable
Expand Down
7 changes: 7 additions & 0 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,13 @@
"description": "Will only be used for payment invoices",
"title": "Address"
}
},
"vat": {
"cooldown": "VAT ID Verification Cooldown",
"timer": "Please wait {waitTime} seconds before trying again",
"unverified": "VAT ID Haven't Verified",
"verify_now": "Verify Now",
"verified": "VAT ID Verified"
}
}
},
Expand Down
4 changes: 4 additions & 0 deletions modules/ui-library/runtime/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
LuCircleCheck,
LuCircleX,
RiAccountCircleLine,
RiAppleLine,
RiArrowDownSLine,
Expand Down Expand Up @@ -102,6 +104,8 @@ export default defineNuxtPlugin((nuxtApp) => {
RiLink,
RiLockLine,
RiLinksLine,
LuCircleX,
LuCircleCheck,
],
mode: 'light',
},
Expand Down
49 changes: 49 additions & 0 deletions store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,53 @@ export const useMainStore = defineStore('main', () => {
}
};

const refreshVATCheckStatus = async (): Promise<void> => {
try {
const response = await fetchWithCsrf<ApiResponse<string>>(
'webapi/account/vat',
{
method: 'GET',
},
);
const { result } = response;
if (result) {
set(account, {
...get(account),
vatIdStatus: result,
});
}
}
catch (error) {
logger.error(error);
}
};

const checkVAT = async (): Promise<number | boolean> => {
try {
const response = await fetchWithCsrf<ApiResponse<boolean>>(
'webapi/account/vat',
{
method: 'POST',
},
);
if (response.result) {
return response.result;
}

return false;
}
catch (error) {
logger.error(error);
if (error instanceof FetchError) {
const status = error?.status || -1;
const result = error.data.result;
if (status === 400 && typeof result === 'number')
return result;
}
return false;
}
};

const resendVerificationCode = async (t: ComposerTranslation): Promise<ActionResult> => {
const onError = (data: ResendVerificationResponse) => {
let message = data.message;
Expand Down Expand Up @@ -588,6 +635,7 @@ export const useMainStore = defineStore('main', () => {
changePassword,
checkout,
checkPendingCryptoPayment,
checkVAT,
cryptoPayment,
deleteAccount,
deletePendingPayment,
Expand All @@ -600,6 +648,7 @@ export const useMainStore = defineStore('main', () => {
pay,
plans,
refreshSession,
refreshVATCheckStatus,
resendVerificationCode,
resumeError,
subscriptions,
Expand Down
1 change: 1 addition & 0 deletions types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export const Account = z.object({
subscriptions: z.array(Subscription),
username: z.string().min(1),
vat: z.number(),
vatIdStatus: z.string(),
});

export type Account = z.infer<typeof Account>;
Expand Down

0 comments on commit 8fefa7e

Please sign in to comment.