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:
parent
41ac3e90e1
commit
f4b5cfa2e9
|
|
@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue