feat: dynamic tax thresholds from backend (#5342)
* feat: dynamic tax thresholds from backend * fix: lint & i18n
This commit is contained in:
@@ -1,11 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { FileTextIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, defineMessages, PagewideBanner, useVIntl } from '@modrinth/ui'
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { getTaxThreshold } from '@/providers/creator-withdraw.ts'
|
||||
import CreatorTaxFormModal from '~/components/ui/dashboard/CreatorTaxFormModal.vue'
|
||||
import { useGeneratedState } from '~/composables/generated'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const generatedState = useGeneratedState()
|
||||
const taxThreshold = computed(() => getTaxThreshold(generatedState.value?.taxComplianceThresholds))
|
||||
|
||||
const modal = useTemplateRef('modal')
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -16,7 +23,7 @@ const messages = defineMessages({
|
||||
description: {
|
||||
id: 'layout.banner.tax.description',
|
||||
defaultMessage:
|
||||
"You've already withdrawn over $600 from Modrinth this year. To comply with tax regulations, you need to complete a tax form. Your withdrawals are paused until this form is submitted.",
|
||||
"You've already withdrawn over {threshold} from Modrinth this year. To comply with tax regulations, you need to complete a tax form. Your withdrawals are paused until this form is submitted.",
|
||||
},
|
||||
action: {
|
||||
id: 'layout.banner.tax.action',
|
||||
@@ -38,7 +45,9 @@ function openTaxForm(e: MouseEvent) {
|
||||
<span>{{ formatMessage(messages.title) }}</span>
|
||||
</template>
|
||||
<template #description>
|
||||
<span>{{ formatMessage(messages.description) }}</span>
|
||||
<span>{{
|
||||
formatMessage(messages.description, { threshold: formatMoney(taxThreshold) })
|
||||
}}</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<ButtonStyled color="orange">
|
||||
|
||||
@@ -128,12 +128,14 @@ import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue'
|
||||
|
||||
import {
|
||||
createWithdrawContext,
|
||||
getTaxThreshold,
|
||||
getTaxThresholdActual,
|
||||
type PaymentProvider,
|
||||
type PayoutMethod,
|
||||
provideWithdrawContext,
|
||||
TAX_THRESHOLD_ACTUAL,
|
||||
type WithdrawStage,
|
||||
} from '@/providers/creator-withdraw.ts'
|
||||
import { useGeneratedState } from '~/composables/generated'
|
||||
|
||||
import CreatorTaxFormModal from './CreatorTaxFormModal.vue'
|
||||
import CompletionStage from './withdraw-stages/CompletionStage.vue'
|
||||
@@ -191,9 +193,13 @@ defineExpose({
|
||||
const { formatMessage } = useVIntl()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
|
||||
const generatedState = useGeneratedState()
|
||||
const taxComplianceThresholds = computed(() => generatedState.value.taxComplianceThresholds)
|
||||
|
||||
const withdrawContext = createWithdrawContext(
|
||||
props.balance,
|
||||
props.preloadedPaymentData || undefined,
|
||||
taxComplianceThresholds.value,
|
||||
)
|
||||
provideWithdrawContext(withdrawContext)
|
||||
|
||||
@@ -249,13 +255,13 @@ const needsTaxForm = computed(() => {
|
||||
const ytd = props.balance.withdrawn_ytd ?? 0
|
||||
const available = props.balance.available ?? 0
|
||||
const status = props.balance.form_completion_status
|
||||
return status !== 'complete' && ytd + available >= 600
|
||||
return status !== 'complete' && ytd + available >= getTaxThreshold(taxComplianceThresholds.value)
|
||||
})
|
||||
|
||||
const remainingLimit = computed(() => {
|
||||
if (!props.balance) return 0
|
||||
const ytd = props.balance.withdrawn_ytd ?? 0
|
||||
const raw = TAX_THRESHOLD_ACTUAL - ytd
|
||||
const raw = getTaxThresholdActual(taxComplianceThresholds.value) - ytd
|
||||
if (raw <= 0) return 0
|
||||
const cents = Math.floor(raw * 100)
|
||||
return cents / 100
|
||||
|
||||
@@ -94,8 +94,14 @@ import { formatMoney } from '@modrinth/utils'
|
||||
import { useGeolocation } from '@vueuse/core'
|
||||
|
||||
import { useCountries, useFormattedCountries, useUserCountry } from '@/composables/country.ts'
|
||||
import { type PayoutMethod, useWithdrawContext } from '@/providers/creator-withdraw.ts'
|
||||
import {
|
||||
getTaxThreshold,
|
||||
type PayoutMethod,
|
||||
useWithdrawContext,
|
||||
} from '@/providers/creator-withdraw.ts'
|
||||
import { useGeneratedState } from '~/composables/generated'
|
||||
|
||||
const generatedState = useGeneratedState()
|
||||
const debug = useDebugLogger('MethodSelectionStage')
|
||||
const {
|
||||
withdrawData,
|
||||
@@ -165,7 +171,9 @@ const shouldShowTaxLimitWarning = computed(() => {
|
||||
if (!balanceValue) return false
|
||||
|
||||
const formIncomplete = balanceValue.form_completion_status !== 'complete'
|
||||
const wouldHitLimit = (balanceValue.withdrawn_ytd ?? 0) + (balanceValue.available ?? 0) >= 600
|
||||
const wouldHitLimit =
|
||||
(balanceValue.withdrawn_ytd ?? 0) + (balanceValue.available ?? 0) >=
|
||||
getTaxThreshold(generatedState.value.taxComplianceThresholds)
|
||||
|
||||
return formIncomplete && wouldHitLimit
|
||||
})
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
<span class="font-semibold text-contrast">{{ formatMessage(messages.withdrawLimit) }}</span>
|
||||
<div>
|
||||
<span class="text-orange">{{ formatMoney(usedLimit) }}</span> /
|
||||
<span class="text-contrast">{{ formatMoney(600) }}</span>
|
||||
<span class="text-contrast">{{ formatMoney(taxThreshold) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex h-2.5 w-full overflow-hidden rounded-full bg-surface-2">
|
||||
<div
|
||||
v-if="usedLimit > 0"
|
||||
class="gradient-border bg-orange"
|
||||
:style="{ width: `${(usedLimit / 600) * 100}%` }"
|
||||
:style="{ width: `${(usedLimit / taxThreshold) * 100}%` }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,7 +59,7 @@
|
||||
<span>
|
||||
<IntlFormatted
|
||||
:message-id="messages.withdrawLimitUsed"
|
||||
:values="{ withdrawLimit: formatMoney(600) }"
|
||||
:values="{ withdrawLimit: formatMoney(taxThreshold) }"
|
||||
>
|
||||
<template #b="{ children }">
|
||||
<b>
|
||||
@@ -85,7 +85,8 @@ import {
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { TAX_THRESHOLD_ACTUAL } from '@/providers/creator-withdraw.ts'
|
||||
import { getTaxThreshold, getTaxThresholdActual } from '@/providers/creator-withdraw.ts'
|
||||
import { useGeneratedState } from '~/composables/generated'
|
||||
|
||||
const props = defineProps<{
|
||||
balance: any
|
||||
@@ -94,9 +95,15 @@ const props = defineProps<{
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const generatedState = useGeneratedState()
|
||||
const taxThreshold = computed(() => getTaxThreshold(generatedState.value.taxComplianceThresholds))
|
||||
const taxThresholdActual = computed(() =>
|
||||
getTaxThresholdActual(generatedState.value.taxComplianceThresholds),
|
||||
)
|
||||
|
||||
const usedLimit = computed(() => props.balance?.withdrawn_ytd ?? 0)
|
||||
const remainingLimit = computed(() => {
|
||||
const raw = TAX_THRESHOLD_ACTUAL - usedLimit.value
|
||||
const raw = taxThresholdActual.value - usedLimit.value
|
||||
if (raw <= 0) return 0
|
||||
const cents = Math.floor(raw * 100)
|
||||
return cents / 100
|
||||
|
||||
Reference in New Issue
Block a user