feat: temporary tax compliance impl (#4393)

* feat: temporary tax compliance impl

* fix: lint & intl

* Update banner, reload page on submit, and fix withdraw button disabled state

---------

Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
Calum H.
2025-09-21 23:23:07 +01:00
committed by GitHub
parent b425c66832
commit 5b44454e18
6 changed files with 640 additions and 26 deletions

View File

@@ -68,23 +68,37 @@
</div>
</div>
<div class="input-group mt-4">
<span :class="{ 'disabled-cursor-wrapper': userBalance.available < minWithdraw }">
<nuxt-link
:aria-disabled="userBalance.available < minWithdraw ? 'true' : 'false'"
:class="{ 'disabled-link': userBalance.available < minWithdraw }"
:disabled="userBalance.available < minWithdraw ? 'true' : 'false'"
:tabindex="userBalance.available < minWithdraw ? -1 : undefined"
class="iconified-button brand-button"
to="/dashboard/revenue/withdraw"
>
<TransferIcon /> Withdraw
</nuxt-link>
<span
:class="{
'disabled-cursor-wrapper': userBalance.available < minWithdraw || blockedByTax,
}"
>
<ButtonStyled color="brand">
<nuxt-link
:aria-disabled="
userBalance.available < minWithdraw || blockedByTax ? 'true' : 'false'
"
:class="{ 'disabled-link': userBalance.available < minWithdraw || blockedByTax }"
:disabled="userBalance.available < minWithdraw || blockedByTax ? 'true' : 'false'"
:tabindex="userBalance.available < minWithdraw || blockedByTax ? -1 : undefined"
to="/dashboard/revenue/withdraw"
>
<TransferIcon /> Withdraw
</nuxt-link>
</ButtonStyled>
</span>
<NuxtLink class="iconified-button" to="/dashboard/revenue/transfers">
<HistoryIcon />
View transfer history
</NuxtLink>
<ButtonStyled>
<NuxtLink to="/dashboard/revenue/transfers">
<HistoryIcon />
View transfer history
</NuxtLink>
</ButtonStyled>
</div>
<p v-if="blockedByTax" class="text-sm font-bold text-orange">
You have withdrawn over $600 this year. To continue withdrawing, you must complete a tax
form.
</p>
<p class="text-sm text-secondary">
By uploading projects to Modrinth and withdrawing money from your account, you agree to the
<nuxt-link class="text-link" to="/legal/cmp">Rewards Program Terms</nuxt-link>. For more
@@ -101,10 +115,12 @@
email
{{ auth.user.payout_data.paypal_address }}
</p>
<button class="btn mt-4" @click="handleRemoveAuthProvider('paypal')">
<XIcon />
Disconnect account
</button>
<ButtonStyled>
<button class="mt-4" @click="handleRemoveAuthProvider('paypal')">
<XIcon />
Disconnect account
</button>
</ButtonStyled>
</template>
<template v-else>
<p>Connect your PayPal account to enable withdrawing to your PayPal balance.</p>
@@ -126,15 +142,16 @@
id="venmo"
v-model="auth.user.payout_data.venmo_handle"
autocomplete="off"
class="mt-4"
name="search"
placeholder="@example"
type="search"
/>
<button class="btn btn-secondary" @click="updateVenmo">
<SaveIcon />
Save information
</button>
<ButtonStyled color="brand">
<button class="mt-4" @click="updateVenmo">
<SaveIcon />
Save information
</button>
</ButtonStyled>
</section>
</div>
</template>
@@ -148,7 +165,7 @@ import {
UnknownIcon,
XIcon,
} from '@modrinth/assets'
import { injectNotificationManager } from '@modrinth/ui'
import { ButtonStyled, injectNotificationManager } from '@modrinth/ui'
import { formatDate } from '@modrinth/utils'
import dayjs from 'dayjs'
import { computed } from 'vue'
@@ -163,6 +180,12 @@ const { data: userBalance } = await useAsyncData(`payout/balance`, () =>
useBaseFetch(`payout/balance`, { apiVersion: 3 }),
)
const blockedByTax = computed(() => {
const status = userBalance.value?.form_completion_status ?? 'unknown'
const thresholdMet = (userBalance.value?.withdrawn_ytd ?? 0) >= 600
return thresholdMet && status !== 'complete'
})
const deadlineEnding = computed(() => {
let deadline = dayjs().subtract(2, 'month').endOf('month').add(60, 'days')
if (deadline.isBefore(dayjs().startOf('day'))) {

View File

@@ -135,6 +135,10 @@
</template>
</div>
<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.
</p>
<div class="confirm-text">
<template v-if="knownErrors.length === 0 && amount">
<Checkbox v-if="fees > 0" v-model="agreedFees" description="Consent to fee">
@@ -175,7 +179,8 @@
!amount ||
!agreedTransfer ||
!agreedTerms ||
(fees > 0 && !agreedFees)
(fees > 0 && !agreedFees) ||
blockedByTax
"
class="iconified-button brand-button"
@click="withdraw"
@@ -323,6 +328,12 @@ const agreedTransfer = ref(false)
const agreedFees = ref(false)
const agreedTerms = ref(false)
const blockedByTax = computed(() => {
const status = userBalance.value?.form_completion_status ?? 'unknown'
const thresholdMet = (userBalance.value?.withdrawn_ytd ?? 0) >= 600
return thresholdMet && status !== 'complete'
})
watch(country, async () => {
await refreshPayoutMethods()
if (payoutMethods.value && payoutMethods.value[0]) {