Remove balance stat from subscription paywall

The balance column on the paywall's stat card showed summed account
balances per currency (e.g. "58.031 MXN 65 US$"). As a variable-length,
multi-currency string it overflowed the card on mobile and broke the
four-column layout. The remaining stats (accounts, transactions,
categories) are short integer counts, so dropping balance leaves a clean
three-column row.

Also removes the now-unused per-account balance computation in
getUserStats (which ran an N+1 query — one AccountBalance lookup per
account) and the never-rendered automationRulesCount stat, so the
endpoint only ships data the paywall actually displays.
This commit is contained in:
Víctor Falcón 2026-07-18 08:02:42 +02:00
parent 41ac3e90e1
commit f4b5cfa2e9
3 changed files with 2 additions and 69 deletions

View File

@ -4,7 +4,6 @@ namespace App\Http\Controllers;
use App\Actions\Subscription\RefundSelfServe;
use App\Features\SubscriptionExperiment;
use App\Models\AccountBalance;
use App\Models\User;
use App\Models\UserLead;
use App\Services\Discord\DiscordWebhook;
@ -52,32 +51,14 @@ class SubscriptionController extends Controller
}
/**
* @return array{accountsCount: int, transactionsCount: int, categoriesCount: int, automationRulesCount: int, balancesByCurrency: array<string, int>}
* @return array{accountsCount: int, transactionsCount: int, categoriesCount: int}
*/
private function getUserStats(User $user): array
{
$accounts = $user->accounts()->get();
$balancesByCurrency = [];
foreach ($accounts as $account) {
$latestBalance = AccountBalance::query()
->where('account_id', $account->id)
->orderBy('balance_date', 'desc')
->value('balance') ?? 0;
$currency = $account->currency_code;
if (! isset($balancesByCurrency[$currency])) {
$balancesByCurrency[$currency] = 0;
}
$balancesByCurrency[$currency] += $latestBalance;
}
return [
'accountsCount' => $accounts->count(),
'accountsCount' => $user->accounts()->count(),
'transactionsCount' => $user->transactions()->count(),
'categoriesCount' => $user->categories()->count(),
'automationRulesCount' => $user->automationRules()->count(),
'balancesByCurrency' => $balancesByCurrency,
];
}

View File

@ -20,7 +20,6 @@ import {
ReceiptIcon,
TrendingUpIcon,
UsersIcon,
WalletIcon,
} from 'lucide-react';
import { useEffect, useState } from 'react';
@ -28,8 +27,6 @@ interface PaywallStats {
accountsCount: number;
transactionsCount: number;
categoriesCount: number;
automationRulesCount: number;
balancesByCurrency: Record<string, number>;
}
interface ExperimentOffer {
@ -210,41 +207,6 @@ function StatItem({
);
}
function BalanceDisplay({
balancesByCurrency,
}: {
balancesByCurrency: Record<string, number>;
}) {
const locale = useLocale();
const entries = Object.entries(balancesByCurrency);
if (entries.length === 0) {
return null;
}
return (
<div className="flex flex-1 flex-col items-center gap-0.5">
<WalletIcon className="mb-1.5 h-4 w-4 text-emerald-500" />
<div className="flex flex-col items-center">
{entries.map(([currency, amount]) => (
<span key={currency} className="text-xl font-bold">
{formatCurrency(
Math.abs(amount),
currency,
locale,
0,
0,
)}
</span>
))}
</div>
<span className="text-xs text-muted-foreground">
{__('Balance')}
</span>
</div>
);
}
function FinancialSnapshot({ stats }: { stats: PaywallStats }) {
const hasData =
stats.accountsCount > 0 ||
@ -282,11 +244,6 @@ function FinancialSnapshot({ stats }: { stats: PaywallStats }) {
delay={300}
/>
)}
{Object.keys(stats.balancesByCurrency).length > 0 && (
<BalanceDisplay
balancesByCurrency={stats.balancesByCurrency}
/>
)}
</CardContent>
</Card>
);

View File

@ -2,7 +2,6 @@
use App\Http\Middleware\HandleInertiaRequests;
use App\Models\Account;
use App\Models\AccountBalance;
use App\Models\BankingConnection;
use App\Models\Category;
use App\Models\Transaction;
@ -50,7 +49,6 @@ test('paywall page includes user stats', function () {
$user = User::factory()->onboarded()->create();
$account = Account::factory()->for($user)->create(['currency_code' => 'USD']);
AccountBalance::factory()->for($account)->create(['balance' => 150000]);
Transaction::factory()->count(3)->for($user)->for($account)->create();
Category::factory()->count(2)->for($user)->create();
@ -64,11 +62,8 @@ test('paywall page includes user stats', function () {
->has('stats.accountsCount')
->has('stats.transactionsCount')
->has('stats.categoriesCount')
->has('stats.automationRulesCount')
->has('stats.balancesByCurrency')
->where('stats.accountsCount', 1)
->where('stats.transactionsCount', 3)
->where('stats.balancesByCurrency.USD', 150000)
);
});