452 lines
9.7 KiB
Vue
452 lines
9.7 KiB
Vue
<template>
|
||
<form
|
||
class="create-withdraw"
|
||
@submit="onSubmit"
|
||
>
|
||
<Transition
|
||
name="shift"
|
||
mode="out-in"
|
||
>
|
||
<div
|
||
v-if="!showSummary"
|
||
class="withdraw-form"
|
||
title="Withdrawal request"
|
||
>
|
||
<FormHeader
|
||
class="withdraw-form__header"
|
||
title="Withdrawal request"
|
||
:back-link="`/projects/${$route.params.projectId}`"
|
||
:back-text="$t('back_to', [project.name])"
|
||
/>
|
||
|
||
<div class="d-flex align-items-center justify-content-between mb-16">
|
||
<CurrencyName
|
||
:code="route.params.assetCode"
|
||
name="Tether"
|
||
class="create-withdraw__currency-name"
|
||
/>
|
||
|
||
<p class="create-withdraw__balance">
|
||
{{ balance }}
|
||
</p>
|
||
</div>
|
||
|
||
<div
|
||
class="p-16 bg-clr-grey-200"
|
||
style="border-radius: 16px"
|
||
>
|
||
<UiInput
|
||
id="paymentAddress"
|
||
rules="required"
|
||
label="Withdrawal wallet"
|
||
/>
|
||
|
||
<NetworkSelect
|
||
id="network"
|
||
:asset-code="route.params.assetCode"
|
||
model-value="TRC20"
|
||
class="mt-6"
|
||
/>
|
||
</div>
|
||
|
||
<div
|
||
class="mt-16 p-16 bg-clr-grey-200"
|
||
style="border-radius: 16px"
|
||
>
|
||
<UiInput
|
||
id="amount"
|
||
rules="required"
|
||
:label="`Минимальная сумма ${minAmount} ${route.params.assetCode}`"
|
||
:mask="{
|
||
mask: Number,
|
||
min: minAmount,
|
||
max: maxAmount,
|
||
scale: exponent,
|
||
}"
|
||
>
|
||
<template #suffix>
|
||
<UiButton
|
||
type="link"
|
||
size="small"
|
||
@click="setValues({ amount: maxAmount }, false)"
|
||
>
|
||
{{ $t('maximum') }}
|
||
</UiButton>
|
||
</template>
|
||
|
||
<template #caption>
|
||
<div class="create-withdraw__limit">
|
||
<span>Доступный лимит на вывод - 1000 USDT</span>
|
||
|
||
<UiButton
|
||
class="create-withdraw__upgrade"
|
||
type="link"
|
||
size="small"
|
||
href="/verification"
|
||
>
|
||
Повысить лимит
|
||
</UiButton>
|
||
</div>
|
||
</template>
|
||
</UiInput>
|
||
</div>
|
||
|
||
<div class="withdraw-form__bottom">
|
||
<div class="withdraw-amount withdraw-amount--fee">
|
||
<div class="withdraw-amount__title">
|
||
{{ $t('fee') }}
|
||
</div>
|
||
|
||
<div class="withdraw-amount__value">
|
||
{{ fee }}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="withdraw-amount">
|
||
<div class="withdraw-amount__title">
|
||
К отправке
|
||
</div>
|
||
|
||
<div class="withdraw-amount__value">
|
||
{{ total }}
|
||
</div>
|
||
</div>
|
||
|
||
<UiButton
|
||
size="large"
|
||
class="create-withdraw__continue"
|
||
native-type="submit"
|
||
:disabled="balance <= 0"
|
||
>
|
||
{{ $t('continue') }}
|
||
</UiButton>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
v-else
|
||
class="withdraw-summary"
|
||
>
|
||
<FormHeader
|
||
class="withdraw-summary__header"
|
||
title="Confirm the withdrawal"
|
||
:back-link="`/projects/${$route.params.projectId}`"
|
||
:back-text="$t('back_to', [project.name])"
|
||
/>
|
||
|
||
<dl class="withdraw-summary__details withdraw-details">
|
||
<dt>Withdrawal wallet</dt>
|
||
<dd>
|
||
<TextShortener :text="values.paymentAddress" />
|
||
</dd>
|
||
|
||
<dt>Network</dt>
|
||
<dd class="text-clr-green-500">
|
||
<CurrencyName
|
||
:code="route.params.assetCode"
|
||
size="small"
|
||
/>
|
||
</dd>
|
||
|
||
<dt>{{ $t('fee') }}</dt>
|
||
<dd>{{ fee }}</dd>
|
||
|
||
<dt>К отправке</dt>
|
||
<dd class="withdraw-details__amount">
|
||
{{ total }}
|
||
</dd>
|
||
</dl>
|
||
|
||
<UiAlert class="withdraw-summary__alert">
|
||
Пожалуйста, подтвердите, что операцию инициировали именно вы.
|
||
</UiAlert>
|
||
|
||
<UiCodeInput
|
||
id="pincode"
|
||
class="withdraw-summary__pincode"
|
||
title="2FA-код"
|
||
/>
|
||
|
||
<div class="withdraw-summary__actions">
|
||
<UiButton
|
||
color="secondary"
|
||
size="large"
|
||
@click="showSummary = false"
|
||
>
|
||
{{ $t('back') }}
|
||
</UiButton>
|
||
|
||
<UiButton
|
||
size="large"
|
||
native-type="submit"
|
||
:loading="isSubmitting"
|
||
>
|
||
{{ $t('withdraw') }}
|
||
</UiButton>
|
||
</div>
|
||
</div>
|
||
</Transition>
|
||
</form>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { v4 as uuidv4 } from 'uuid'
|
||
import { useForm } from 'vee-validate'
|
||
import { computedAsync } from '@vueuse/core'
|
||
import Decimal from 'decimal.js'
|
||
|
||
definePageMeta({
|
||
middleware: ['auth'],
|
||
centerContent: true,
|
||
})
|
||
|
||
const route = useRoute()
|
||
|
||
const { data: project } = await useAsyncData(
|
||
'project',
|
||
() => {
|
||
return $api(`/online_stores/${route.params.projectId}`, {
|
||
method: 'GET',
|
||
})
|
||
},
|
||
)
|
||
|
||
if (!project.value) {
|
||
throw createError({
|
||
statusCode: 404,
|
||
statusMessage: 'Page Not Found',
|
||
})
|
||
}
|
||
|
||
const notify = useNotify()
|
||
const { t } = useI18n()
|
||
|
||
const { data: tariff } = await useAsyncData('tariff', () => {
|
||
return $api(`/tariffs/current?currency=${route.params.assetCode}`)
|
||
})
|
||
|
||
const { data: account } = await useAsyncData('account', () =>
|
||
$api(`/accounts/${project.value.accountIds[0]}`, {
|
||
method: 'GET',
|
||
}))
|
||
|
||
const showSummary = ref(false)
|
||
|
||
const { isSubmitting, values, setValues, handleSubmit } = useForm({ keepValuesOnUnmount: true })
|
||
|
||
const commission = computedAsync(async () => {
|
||
if (values.amount) {
|
||
try {
|
||
const result = await $api(`/withdrawals/commission`, { method: 'GET', params: { amount: values.amount, currency: route.params.assetCode } })
|
||
|
||
return result?.commission ?? 0
|
||
}
|
||
catch {
|
||
return 0
|
||
}
|
||
}
|
||
else {
|
||
return 0
|
||
}
|
||
}, 0, { lazy: true })
|
||
const fee = computed(() => $money.fullFormat(commission.value, route.params.assetCode))
|
||
const balance = computed(() => $money.format(account.value.balance, route.params.assetCode))
|
||
const maxAmount = computed(() => Math.min(1000, account.value.balance))
|
||
const minAmount = computed(() => tariff.value?.data.minAmount)
|
||
const exponent = computed(() => $money.getExponent(route.params.assetCode))
|
||
const total = computed(() => $money.fullFormat(Decimal.sub(values.amount || 0, commission.value), route.params.assetCode))
|
||
|
||
const onSubmit = handleSubmit(async (values) => {
|
||
if (!showSummary.value) {
|
||
showSummary.value = true
|
||
|
||
return
|
||
}
|
||
|
||
if (values.pincode !== '123123') {
|
||
notify({
|
||
id: 'withdraw_error',
|
||
text: t('invalid_otp_code'),
|
||
type: 'negative',
|
||
})
|
||
|
||
return
|
||
}
|
||
|
||
try {
|
||
// const idempotencyKey = `${route.params.projectId + values.paymentAddress + route.params.assetCode}TRON${values.amount}`
|
||
|
||
await $api('/withdrawals', {
|
||
method: 'POST',
|
||
body: {
|
||
...values,
|
||
idempotencyKey: uuidv4().substring(0, 20),
|
||
amount: +values.amount,
|
||
onlineStoreId: +route.params.projectId,
|
||
currencyCode: route.params.assetCode,
|
||
blockchainCode: 'TRON',
|
||
saveWallet: false,
|
||
pincode: undefined,
|
||
network: undefined,
|
||
},
|
||
})
|
||
|
||
notify({
|
||
id: 'withdraw_success',
|
||
type: 'positive',
|
||
text: 'The withdrawal request has been successfully processed',
|
||
})
|
||
|
||
navigateTo(`/projects/${route.params.projectId}`)
|
||
}
|
||
catch {
|
||
notify({
|
||
id: 'withdraw_error',
|
||
text: t('something_went_wrong'),
|
||
type: 'negative',
|
||
})
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.create-withdraw {
|
||
&__currency-name {
|
||
@include txt-l-sb('currency-name', true);
|
||
@include txt-r-m('currency-name-code', true);
|
||
|
||
--currency-name-gap: 2px 16px;
|
||
}
|
||
|
||
&__balance {
|
||
@include txt-l-sb;
|
||
}
|
||
|
||
&__continue {
|
||
flex: 1;
|
||
}
|
||
|
||
&__limit {
|
||
@include txt-r-m;
|
||
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
color: $clr-grey-500;
|
||
margin-top: 8px;
|
||
}
|
||
}
|
||
|
||
.withdraw-form,
|
||
.withdraw-summary {
|
||
width: 500px;
|
||
margin: 0 auto;
|
||
|
||
&__header {
|
||
margin-bottom: 32px;
|
||
}
|
||
}
|
||
|
||
.withdraw-form {
|
||
&__bottom {
|
||
display: flex;
|
||
gap: 16px;
|
||
margin-top: 32px;
|
||
}
|
||
}
|
||
|
||
.withdraw-summary {
|
||
&__details {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
&__alert {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
&__pincode {
|
||
padding-block: 16px;
|
||
}
|
||
|
||
&__actions {
|
||
display: flex;
|
||
gap: 16px;
|
||
margin-top: 32px;
|
||
|
||
> * {
|
||
flex: 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
.withdraw-amount {
|
||
$self: &;
|
||
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
height: 48px;
|
||
flex: 0 0 146px;
|
||
|
||
&--fee {
|
||
flex: 0 0 100px
|
||
}
|
||
|
||
&__title {
|
||
@include txt-r-b;
|
||
|
||
color: $clr-grey-500;
|
||
}
|
||
|
||
&__value {
|
||
@include txt-l-sb;
|
||
|
||
white-space: nowrap;
|
||
|
||
#{$self}--fee & {
|
||
@include txt-i-sb;
|
||
|
||
color: $clr-grey-600;
|
||
}
|
||
}
|
||
|
||
& + & {
|
||
border-left: 1px solid $clr-grey-300;
|
||
padding-left: 16px;
|
||
}
|
||
}
|
||
|
||
.withdraw-details {
|
||
@include txt-i-m;
|
||
@include txt-i-m('text-shortener', true);
|
||
@include txt-i-m('currency-name-code', true);
|
||
|
||
border-radius: 12px;
|
||
background-color: $clr-grey-200;
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
row-gap: 16px;
|
||
color: $clr-grey-500;
|
||
padding: 16px;
|
||
|
||
dt, dd {
|
||
margin: 0;
|
||
}
|
||
|
||
dt {
|
||
color: $clr-grey-600;
|
||
}
|
||
|
||
dd {
|
||
text-align: right;
|
||
}
|
||
|
||
&__amount {
|
||
@include txt-l-sb;
|
||
|
||
color: $clr-black;
|
||
}
|
||
}
|
||
</style>
|