From 6d1b0eef158902b16305ad52c2022e6809f26d6e Mon Sep 17 00:00:00 2001 From: "Calum H." Date: Tue, 27 Jan 2026 19:03:54 +0000 Subject: [PATCH] feat: badge for tax 2025 bug (#5230) --- .../ui/dashboard/CreatorWithdrawModal.vue | 2 ++ apps/frontend/src/layouts/default.vue | 14 ++++++++++---- .../frontend/src/pages/dashboard/revenue/index.vue | 3 ++- apps/frontend/src/providers/creator-withdraw.ts | 8 +++++++- apps/labrinth/src/models/v3/users.rs | 1 + packages/utils/types.ts | 1 + 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/components/ui/dashboard/CreatorWithdrawModal.vue b/apps/frontend/src/components/ui/dashboard/CreatorWithdrawModal.vue index b356c7118..f248bb025 100644 --- a/apps/frontend/src/components/ui/dashboard/CreatorWithdrawModal.vue +++ b/apps/frontend/src/components/ui/dashboard/CreatorWithdrawModal.vue @@ -159,6 +159,7 @@ interface UserBalanceResponse { const props = defineProps<{ balance: UserBalanceResponse | null preloadedPaymentData?: { country: string; methods: PayoutMethod[] } | null + userBadges?: number }>() const emit = defineEmits<{ @@ -194,6 +195,7 @@ const { addNotification } = injectNotificationManager() const withdrawContext = createWithdrawContext( props.balance, props.preloadedPaymentData || undefined, + props.userBadges, ) provideWithdrawContext(withdrawContext) diff --git a/apps/frontend/src/layouts/default.vue b/apps/frontend/src/layouts/default.vue index dd4a34c5d..8d0b00f9a 100644 --- a/apps/frontend/src/layouts/default.vue +++ b/apps/frontend/src/layouts/default.vue @@ -761,13 +761,19 @@ const { data: payoutBalance } = await useAsyncData('payout/balance', () => { const showTaxComplianceBanner = computed(() => { if (flags.value.testTaxForm && auth.value.user) return true + const user = auth.value.user + if (!user) return false const bal = payoutBalance.value - if (!bal) return false - const thresholdMet = (bal.withdrawn_ytd ?? 0) >= 600 - const status = bal.form_completion_status ?? 'unknown' + const status = bal?.form_completion_status ?? 'unknown' const isComplete = status === 'complete' const isTinMismatch = status === 'tin-mismatch' - return !!auth.value.user && thresholdMet && !isComplete && !isTinMismatch + if (isComplete || isTinMismatch) return false + // Show banner if user has the 2025 tax form badge override + if (user.badges & UserBadge.TAX_FORM_2025_REQUIRED) return true + // Otherwise, show if threshold met + if (!bal) return false + const thresholdMet = (bal.withdrawn_ytd ?? 0) >= 600 + return thresholdMet }) const showTinMismatchBanner = computed(() => { diff --git a/apps/frontend/src/pages/dashboard/revenue/index.vue b/apps/frontend/src/pages/dashboard/revenue/index.vue index f3ec3ab4e..978227190 100644 --- a/apps/frontend/src/pages/dashboard/revenue/index.vue +++ b/apps/frontend/src/pages/dashboard/revenue/index.vue @@ -3,6 +3,7 @@ ref="withdrawModal" :balance="userBalance" :preloaded-payment-data="preloadedPaymentMethods" + :user-badges="auth.user?.badges" @refresh-data="refreshData" />
@@ -272,7 +273,7 @@ import RevenueTransaction from '~/components/ui/dashboard/RevenueTransaction.vue const { formatMessage } = useVIntl() -await useAuth() +const auth = await useAuth() // TODO: Deduplicate these types & interfaces in @modrinth/api-client PR. type FormCompletionStatus = 'unknown' | 'unrequested' | 'unsigned' | 'tin-mismatch' | 'complete' diff --git a/apps/frontend/src/providers/creator-withdraw.ts b/apps/frontend/src/providers/creator-withdraw.ts index 0f16842bc..39b81c10d 100644 --- a/apps/frontend/src/providers/creator-withdraw.ts +++ b/apps/frontend/src/providers/creator-withdraw.ts @@ -8,6 +8,7 @@ import { } from '@modrinth/assets' import type { MessageDescriptor } from '@modrinth/ui' import { createContext, getCurrencyIcon, paymentMethodMessages, useDebugLogger } from '@modrinth/ui' +import { UserBadge } from '@modrinth/utils' import { type Component, computed, type ComputedRef, type Ref, ref } from 'vue' import { getRailConfig } from '@/utils/muralpay-rails' @@ -379,6 +380,7 @@ const STATE_EXPIRY_MS = 15 * 60 * 1000 // 15 minutes export function createWithdrawContext( balance: any, preloadedPaymentData?: { country: string; methods: PayoutMethod[] }, + userBadges?: number, ): WithdrawContextValue { const debug = useDebugLogger('CreatorWithdraw') const currentStage = ref() @@ -420,15 +422,18 @@ export function createWithdrawContext( const usedLimit = balance?.withdrawn_ytd ?? 0 const available = balance?.available ?? 0 + const hasBadgeOverride = !!(userBadges && userBadges & UserBadge.TAX_FORM_2025_REQUIRED) const needsTaxForm = - balance?.form_completion_status !== 'complete' && usedLimit + available >= 600 + balance?.form_completion_status !== 'complete' && + (usedLimit + available >= 600 || hasBadgeOverride) debug('Tax form check:', { usedLimit, available, total: usedLimit + available, status: balance?.form_completion_status, + hasBadgeOverride, needsTaxForm, }) @@ -817,6 +822,7 @@ export function createWithdrawContext( calculation: { amount: 0, fee: null, + netUsd: null, exchangeRate: null, }, providerData: { diff --git a/apps/labrinth/src/models/v3/users.rs b/apps/labrinth/src/models/v3/users.rs index 0f276fc8f..c3e8cbf10 100644 --- a/apps/labrinth/src/models/v3/users.rs +++ b/apps/labrinth/src/models/v3/users.rs @@ -18,6 +18,7 @@ bitflags::bitflags! { const CONTRIBUTOR = 1 << 5; const TRANSLATOR = 1 << 6; const AFFILIATE = 1 << 7; + const TAX_FORM_2025_REQUIRED = 1 << 8; } } diff --git a/packages/utils/types.ts b/packages/utils/types.ts index 03dbd1009..79556830f 100644 --- a/packages/utils/types.ts +++ b/packages/utils/types.ts @@ -342,6 +342,7 @@ export enum UserBadge { CONTRIBUTOR = 1 << 5, TRANSLATOR = 1 << 6, AFFILIATE = 1 << 7, + TAX_FORM_2025_REQUIRED = 1 << 8, } export type UserBadges = number