refactor(accounts): unify account type icons via accountIconByType helper (#180)

## Why

### Problem
Account type icons were defined in two separate places —
`step-account-types.tsx` (onboarding) and `account-type-icon.tsx`
(dashboard) — using different icon choices for the same account types.
For example, `checking` showed `Building2` on the dashboard but `Wallet`
in onboarding, and `retirement` showed `Umbrella` vs `TrendingUp`. This
created a visually inconsistent experience.

## What

### Changes
- Added `accountIconByType(type: AccountType): LucideIcon` helper to
`resources/js/types/account.ts` as the single source of truth for
account type icons
- Updated `AccountTypeIcon` component to delegate to the new helper
instead of maintaining its own map
- Updated `StepAccountTypes` to derive icons from the helper instead of
storing them inline per entry
- Dashboard account cards now use the same icon set as the onboarding
screen (Wallet, PiggyBank, CreditCard, LineChart, TrendingUp, Building2)

## Verification

### Tests
No behaviour change — purely a refactor consolidating icon mapping into
one place. Existing tests remain unaffected.
This commit is contained in:
Víctor Falcón 2026-03-02 08:47:44 +00:00 committed by GitHub
parent edcadace1a
commit 186ae57c2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 70 additions and 75 deletions

View File

@ -1,16 +1,5 @@
import { cn } from '@/lib/utils';
import { AccountType } from '@/types/account';
import {
BadgeQuestionMarkIcon,
Building2,
CreditCard,
FolderKanban,
Landmark,
LucideIcon,
PiggyBank,
TrendingUp,
Umbrella,
} from 'lucide-react';
import { AccountType, accountIconByType } from '@/types/account';
export function AccountTypeIcon({
type,
@ -19,17 +8,7 @@ export function AccountTypeIcon({
type: AccountType;
className?: string;
}) {
const typeMap: Record<AccountType, LucideIcon> = {
checking: Building2, // 🏦 - bank / institution
credit_card: CreditCard, // 💳 - card
investment: TrendingUp, // 📈 - growth / investment
loan: Landmark, // 🏠 - "institution/loan", or use Home if it's a mortgage
retirement: Umbrella, // 🏖️ - retirement / pension
savings: PiggyBank, // 💰 - savings
others: FolderKanban, // 📁 - miscellaneous/other
};
const Icon = typeMap[type] || BadgeQuestionMarkIcon;
const Icon = accountIconByType(type);
return (
<Icon className={cn(['h-5 w-5 text-muted-foreground', className])} />

View File

@ -1,15 +1,8 @@
import { StepButton } from '@/components/onboarding/step-button';
import { StepHeader } from '@/components/onboarding/step-header';
import { accountIconByType } from '@/types/account';
import { __ } from '@/utils/i18n';
import {
Banknote,
Building2,
CreditCard,
LineChart,
PiggyBank,
TrendingUp,
Wallet,
} from 'lucide-react';
import { Banknote } from 'lucide-react';
interface StepAccountTypesProps {
onContinue: () => void;
@ -17,44 +10,38 @@ interface StepAccountTypesProps {
const accountTypes = [
{
type: 'checking',
type: 'checking' as const,
nameKey: 'Checking',
icon: Wallet,
descriptionKey: 'Daily spending and transactions',
hasTransactions: true,
},
{
type: 'savings',
type: 'savings' as const,
nameKey: 'Savings',
icon: PiggyBank,
descriptionKey: 'Save money for goals',
hasTransactions: true,
},
{
type: 'credit_card',
type: 'credit_card' as const,
nameKey: 'Credit Card',
icon: CreditCard,
descriptionKey: 'Track credit card spending',
hasTransactions: true,
},
{
type: 'investment',
type: 'investment' as const,
nameKey: 'Investment',
icon: LineChart,
descriptionKey: 'Stocks, ETFs, and portfolios',
hasTransactions: false,
},
{
type: 'retirement',
type: 'retirement' as const,
nameKey: 'Retirement',
icon: TrendingUp,
descriptionKey: '401k, IRA, pension funds',
hasTransactions: false,
},
{
type: 'loan',
type: 'loan' as const,
nameKey: 'Loan',
icon: Building2,
descriptionKey: 'Mortgages and loans',
hasTransactions: false,
},
@ -73,41 +60,45 @@ export function StepAccountTypes({ onContinue }: StepAccountTypesProps) {
/>
<div className="grid w-full max-w-2xl gap-3 sm:grid-cols-2">
{accountTypes.map((account) => (
<div
key={account.type}
className="group relative flex flex-row items-center gap-2 overflow-hidden rounded-xl border bg-card p-3 transition-all hover:shadow-md sm:items-start sm:p-4"
>
<div className="flex w-full flex-col items-start gap-1 sm:gap-2">
<div className="flex w-full flex-row items-center justify-between gap-2 sm:items-start">
<div className="flex items-center gap-2">
<account.icon
className={`size-4 stroke-muted-foreground`}
/>
{accountTypes.map((account) => {
const Icon = accountIconByType(account.type);
<h3 className="font-semibold">
{__(account.nameKey)}
</h3>
return (
<div
key={account.type}
className="group relative flex flex-row items-center gap-2 overflow-hidden rounded-xl border bg-card p-3 transition-all hover:shadow-md sm:items-start sm:p-4"
>
<div className="flex w-full flex-col items-start gap-1 sm:gap-2">
<div className="flex w-full flex-row items-center justify-between gap-2 sm:items-start">
<div className="flex items-center gap-2">
<Icon
className={`size-4 stroke-muted-foreground`}
/>
<h3 className="font-semibold">
{__(account.nameKey)}
</h3>
</div>
<span
className={`inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium ${
account.hasTransactions
? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400'
: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400'
}`}
>
{account.hasTransactions
? __('Transactions + Balance')
: __('Balance')}
</span>
</div>
<span
className={`inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium ${
account.hasTransactions
? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400'
: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400'
}`}
>
{account.hasTransactions
? __('Transactions + Balance')
: __('Balance')}
</span>
<p className="text-sm text-muted-foreground">
{__(account.descriptionKey)}
</p>
</div>
<p className="text-sm text-muted-foreground">
{__(account.descriptionKey)}
</p>
</div>
</div>
))}
);
})}
</div>
<div className="mt-8 w-full sm:w-auto">

View File

@ -1,4 +1,15 @@
import { __ } from '@/utils/i18n';
import {
BadgeQuestionMarkIcon,
Building2,
CreditCard,
FolderKanban,
LineChart,
LucideIcon,
PiggyBank,
TrendingUp,
Wallet,
} from 'lucide-react';
import { UUID } from './uuid';
export const ACCOUNT_TYPES = [
@ -92,6 +103,20 @@ export function supportsInvestedAmount(
return INVESTED_AMOUNT_ACCOUNT_TYPES.includes(account.type);
}
export function accountIconByType(type: AccountType): LucideIcon {
const typeMap: Record<AccountType, LucideIcon> = {
checking: Wallet,
credit_card: CreditCard,
investment: LineChart,
loan: Building2,
retirement: TrendingUp,
savings: PiggyBank,
others: FolderKanban,
};
return typeMap[type] ?? BadgeQuestionMarkIcon;
}
export function filterTransactionalAccounts<T extends { type: AccountType }>(
accounts: T[],
): T[] {