You've already forked AstralRinth
forked from didirus/AstralRinth
fix: allow payouts that go over the tax limit by prefilling form (#4478)
* feat: start on fix * fix: withdraw btn * fix: lint issues * fix: use button rather than span * fix: lint issues --------- Co-authored-by: --global <--global>
This commit is contained in:
@@ -239,6 +239,7 @@ async function continueForm() {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error occurred while continuing tax form:', error)
|
console.error('Error occurred while continuing tax form:', error)
|
||||||
|
handleCancel()
|
||||||
} finally {
|
} finally {
|
||||||
manualLoading.value = false
|
manualLoading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,25 +68,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group mt-4">
|
<div class="input-group mt-4">
|
||||||
<span
|
<ButtonStyled color="brand">
|
||||||
:class="{
|
<nuxt-link
|
||||||
'disabled-cursor-wrapper': userBalance.available < minWithdraw || blockedByTax,
|
v-if="!(userBalance.available < minWithdraw || blockedByTax)"
|
||||||
}"
|
to="/dashboard/revenue/withdraw"
|
||||||
>
|
>
|
||||||
<ButtonStyled color="brand">
|
<TransferIcon /> Withdraw
|
||||||
<nuxt-link
|
</nuxt-link>
|
||||||
:aria-disabled="
|
<button v-else class="disabled"><TransferIcon /> Withdraw</button>
|
||||||
userBalance.available < minWithdraw || blockedByTax ? 'true' : 'false'
|
</ButtonStyled>
|
||||||
"
|
|
||||||
:class="{ 'disabled-link': userBalance.available < minWithdraw || blockedByTax }"
|
|
||||||
:disabled="!!(userBalance.available < minWithdraw || blockedByTax) || null"
|
|
||||||
:tabindex="userBalance.available < minWithdraw || blockedByTax ? -1 : undefined"
|
|
||||||
to="/dashboard/revenue/withdraw"
|
|
||||||
>
|
|
||||||
<TransferIcon /> Withdraw
|
|
||||||
</nuxt-link>
|
|
||||||
</ButtonStyled>
|
|
||||||
</span>
|
|
||||||
<ButtonStyled>
|
<ButtonStyled>
|
||||||
<NuxtLink to="/dashboard/revenue/transfers">
|
<NuxtLink to="/dashboard/revenue/transfers">
|
||||||
<HistoryIcon />
|
<HistoryIcon />
|
||||||
@@ -176,9 +166,19 @@ const { addNotification, handleError } = injectNotificationManager()
|
|||||||
const auth = await useAuth()
|
const auth = await useAuth()
|
||||||
const minWithdraw = ref(0.01)
|
const minWithdraw = ref(0.01)
|
||||||
|
|
||||||
const { data: userBalance } = await useAsyncData(`payout/balance`, () =>
|
const { data: userBalance } = await useAsyncData(`payout/balance`, async () => {
|
||||||
useBaseFetch(`payout/balance`, { apiVersion: 3 }),
|
const response = await useBaseFetch(`payout/balance`, { apiVersion: 3 })
|
||||||
)
|
return {
|
||||||
|
...response,
|
||||||
|
available: parseFloat(response.available),
|
||||||
|
withdrawn_lifetime: parseFloat(response.withdrawn_lifetime),
|
||||||
|
withdrawn_ytd: parseFloat(response.withdrawn_ytd),
|
||||||
|
pending: parseFloat(response.pending),
|
||||||
|
dates: Object.fromEntries(
|
||||||
|
Object.entries(response.dates).map(([date, value]) => [date, parseFloat(value)]),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const blockedByTax = computed(() => {
|
const blockedByTax = computed(() => {
|
||||||
const status = userBalance.value?.form_completion_status ?? 'unknown'
|
const status = userBalance.value?.form_completion_status ?? 'unknown'
|
||||||
@@ -250,14 +250,6 @@ strong {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled-cursor-wrapper {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled-link {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-display {
|
.grid-display {
|
||||||
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<CreatorTaxFormModal
|
||||||
|
ref="taxFormModalRef"
|
||||||
|
@success="onTaxFormSuccess"
|
||||||
|
@cancelled="onTaxFormCancelled"
|
||||||
|
/>
|
||||||
<section class="universal-card">
|
<section class="universal-card">
|
||||||
<Breadcrumbs
|
<Breadcrumbs
|
||||||
current-title="Withdraw"
|
current-title="Withdraw"
|
||||||
@@ -135,6 +140,11 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p v-if="willTriggerTaxForm" class="font-bold text-orange">
|
||||||
|
This withdrawal will exceed $600 for the year. You will be prompted to complete a tax form
|
||||||
|
before proceeding.
|
||||||
|
</p>
|
||||||
|
|
||||||
<p v-if="blockedByTax" class="font-bold text-orange">
|
<p v-if="blockedByTax" class="font-bold text-orange">
|
||||||
You have withdrawn over $600 this year. To continue withdrawing, you must complete a tax form.
|
You have withdrawn over $600 this year. To continue withdrawing, you must complete a tax form.
|
||||||
</p>
|
</p>
|
||||||
@@ -207,6 +217,7 @@ import { all } from 'iso-3166-1'
|
|||||||
import { Multiselect } from 'vue-multiselect'
|
import { Multiselect } from 'vue-multiselect'
|
||||||
|
|
||||||
import VenmoIcon from '~/assets/images/external/venmo.svg?component'
|
import VenmoIcon from '~/assets/images/external/venmo.svg?component'
|
||||||
|
import CreatorTaxFormModal from '~/components/ui/dashboard/CreatorTaxFormModal.vue'
|
||||||
|
|
||||||
const { addNotification } = injectNotificationManager()
|
const { addNotification } = injectNotificationManager()
|
||||||
const auth = await useAuth()
|
const auth = await useAuth()
|
||||||
@@ -227,7 +238,19 @@ const country = ref(
|
|||||||
|
|
||||||
const [{ data: userBalance }, { data: payoutMethods, refresh: refreshPayoutMethods }] =
|
const [{ data: userBalance }, { data: payoutMethods, refresh: refreshPayoutMethods }] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
useAsyncData(`payout/balance`, () => useBaseFetch(`payout/balance`, { apiVersion: 3 })),
|
useAsyncData(`payout/balance`, async () => {
|
||||||
|
const response = await useBaseFetch(`payout/balance`, { apiVersion: 3 })
|
||||||
|
return {
|
||||||
|
...response,
|
||||||
|
available: parseFloat(response.available),
|
||||||
|
withdrawn_lifetime: parseFloat(response.withdrawn_lifetime),
|
||||||
|
withdrawn_ytd: parseFloat(response.withdrawn_ytd),
|
||||||
|
pending: parseFloat(response.pending),
|
||||||
|
dates: Object.fromEntries(
|
||||||
|
Object.entries(response.dates).map(([date, value]) => [date, parseFloat(value)]),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}),
|
||||||
useAsyncData(`payout/methods?country=${country.value.id}`, () =>
|
useAsyncData(`payout/methods?country=${country.value.id}`, () =>
|
||||||
useBaseFetch(`payout/methods?country=${country.value.id}`, { apiVersion: 3 }),
|
useBaseFetch(`payout/methods?country=${country.value.id}`, { apiVersion: 3 }),
|
||||||
),
|
),
|
||||||
@@ -334,6 +357,13 @@ const blockedByTax = computed(() => {
|
|||||||
return thresholdMet && status !== 'complete'
|
return thresholdMet && status !== 'complete'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const willTriggerTaxForm = computed(() => {
|
||||||
|
const status = userBalance.value?.form_completion_status ?? 'unknown'
|
||||||
|
const currentWithdrawn = userBalance.value?.withdrawn_ytd ?? 0
|
||||||
|
const wouldExceedThreshold = currentWithdrawn + parsedAmount.value >= 600
|
||||||
|
return wouldExceedThreshold && status !== 'complete' && !blockedByTax.value
|
||||||
|
})
|
||||||
|
|
||||||
watch(country, async () => {
|
watch(country, async () => {
|
||||||
await refreshPayoutMethods()
|
await refreshPayoutMethods()
|
||||||
if (payoutMethods.value && payoutMethods.value[0]) {
|
if (payoutMethods.value && payoutMethods.value[0]) {
|
||||||
@@ -353,7 +383,18 @@ watch(selectedMethod, () => {
|
|||||||
agreedTerms.value = false
|
agreedTerms.value = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const taxFormModalRef = ref(null)
|
||||||
|
const taxFormCancelled = ref(false)
|
||||||
|
|
||||||
async function withdraw() {
|
async function withdraw() {
|
||||||
|
if (willTriggerTaxForm.value) {
|
||||||
|
taxFormCancelled.value = false
|
||||||
|
if (taxFormModalRef.value && taxFormModalRef.value.startTaxForm) {
|
||||||
|
await taxFormModalRef.value.startTaxForm(new MouseEvent('click'))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
startLoading()
|
startLoading()
|
||||||
try {
|
try {
|
||||||
const auth = await useAuth()
|
const auth = await useAuth()
|
||||||
@@ -386,6 +427,31 @@ async function withdraw() {
|
|||||||
}
|
}
|
||||||
stopLoading()
|
stopLoading()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onTaxFormSuccess() {
|
||||||
|
// Refresh user balance to get updated form completion status
|
||||||
|
const updatedBalance = await useBaseFetch(`payout/balance`, { apiVersion: 3 })
|
||||||
|
userBalance.value = updatedBalance
|
||||||
|
|
||||||
|
if (updatedBalance?.form_completion_status === 'complete') {
|
||||||
|
await withdraw()
|
||||||
|
} else {
|
||||||
|
addNotification({
|
||||||
|
title: 'Tax form incomplete',
|
||||||
|
text: 'You must complete a tax form for this withdrawal.',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTaxFormCancelled() {
|
||||||
|
taxFormCancelled.value = true
|
||||||
|
addNotification({
|
||||||
|
title: 'Withdrawal canceled',
|
||||||
|
text: 'You must complete a tax form for this withdrawal.',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user