feat(accounts): swap the account type icon with the drag handle on hover

Revert to the icon-swap handle: on the dashboard card the drag handle
takes the place of the account type icon (top-right) on hover.

On the accounts list card, add the account type icon in the bottom-left
of the actions row (buttons follow it) and apply the same hover swap, so
both screens reveal the drag handle the same way.
This commit is contained in:
Víctor Falcón 2026-06-20 19:41:43 +02:00
parent 201974a724
commit 522199483c
3 changed files with 68 additions and 53 deletions

View File

@ -1,11 +1,13 @@
import { show } from '@/actions/App/Http/Controllers/AccountController';
import { AccountName } from '@/components/accounts/account-name';
import { BankLogo } from '@/components/bank-logo';
import { AccountTypeIcon } from '@/components/dashboard/account-type-icon';
import { AmountTrendIndicator } from '@/components/dashboard/amount-trend-indicator';
import { AmountDisplay } from '@/components/ui/amount-display';
import { Card, CardContent } from '@/components/ui/card';
import { useChartColors } from '@/hooks/use-chart-color-scheme';
import { AccountWithMetrics } from '@/hooks/use-dashboard-data';
import { cn } from '@/lib/utils';
import { formatAccountType, supportsInvestedAmount } from '@/types/account';
import { __ } from '@/utils/i18n';
import { Link } from '@inertiajs/react';
@ -139,12 +141,11 @@ export function AccountListCard({
<CardContent className="p-4">
<div className="flex flex-col gap-4">
<div className="flex max-w-full flex-col sm:flex-row sm:items-center sm:justify-between">
<div className="flex min-w-0 items-start">
{dragHandle}
<div className="flex min-w-0 items-start gap-3">
<div className="flex min-w-0 flex-col gap-1">
<Link
href={show.url(account.id)}
className="-my-1 flex min-w-0 items-center rounded-md px-1.5 py-1 transition-colors hover:bg-muted"
className="-my-1 -ml-1.5 flex min-w-0 items-center rounded-md px-1.5 py-1 transition-colors hover:bg-muted"
>
<h3 className="flex min-w-0 items-center gap-2 font-semibold">
{account.bank && (
@ -429,7 +430,22 @@ export function AccountListCard({
</LineChart>
</ResponsiveContainer>
</div>
<div className="flex justify-between">
<div className="flex items-center gap-2">
<div className="relative size-5 shrink-0 text-muted-foreground">
<AccountTypeIcon
type={account.type}
className={cn(
'transition-opacity',
dragHandle && 'group-hover:opacity-0',
)}
/>
{dragHandle && (
<span className="absolute inset-0 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100">
{dragHandle}
</span>
)}
</div>
{!isConnected && (
<Button
className="cursor-pointer"
@ -444,10 +460,7 @@ export function AccountListCard({
</Button>
)}
<Link
href={show.url(account.id)}
className={isConnected ? 'ml-auto' : ''}
>
<Link href={show.url(account.id)} className="ml-auto">
<Button className="cursor-pointer" variant="ghost">
{__('Details')} &rarr;
</Button>

View File

@ -6,6 +6,7 @@ import { AmountDisplay } from '@/components/ui/amount-display';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useChartColors } from '@/hooks/use-chart-color-scheme';
import { AccountWithMetrics } from '@/hooks/use-dashboard-data';
import { cn } from '@/lib/utils';
import { supportsInvestedAmount } from '@/types/account';
import { __ } from '@/utils/i18n';
import { Link } from '@inertiajs/react';
@ -157,54 +158,57 @@ export function AccountBalanceCard({
return (
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<div className="flex min-w-0 items-start">
{dragHandle}
<div className="flex min-w-0 flex-col gap-0.5">
<CardTitle className="text-sm font-medium">
<Link
href={show.url(account.id)}
className="-my-1 flex items-center rounded-md px-1.5 py-1 transition-colors hover:bg-muted"
>
<BankLogo
src={account.bank?.logo ?? null}
name={account.bank?.name}
className="mr-2 inline-block size-5"
/>
<div className="flex flex-col gap-0.5">
<CardTitle className="text-sm font-medium">
<Link
href={show.url(account.id)}
className="-my-1 -ml-1.5 flex items-center rounded-md px-1.5 py-1 transition-colors hover:bg-muted"
>
<BankLogo
src={account.bank?.logo ?? null}
name={account.bank?.name}
className="mr-2 inline-block size-5"
/>
<AccountName
account={account}
length={{ min: 5, max: 15 }}
<AccountName
account={account}
length={{ min: 5, max: 15 }}
/>
</Link>
</CardTitle>
{hasMortgage && linkedLoanMetrics.loanAccount && (
<span className="flex items-center gap-1 pt-0.5 text-xs text-muted-foreground">
{__('Mortgage at')}{' '}
{linkedLoanMetrics.loanAccount.bank && (
<BankLogo
src={
linkedLoanMetrics.loanAccount.bank.logo
}
name={
linkedLoanMetrics.loanAccount.bank.name
}
className="size-3.5 shrink-0"
fallback="letter"
/>
</Link>
</CardTitle>
{hasMortgage && linkedLoanMetrics.loanAccount && (
<span className="flex items-center gap-1 pt-0.5 text-xs text-muted-foreground">
{__('Mortgage at')}{' '}
{linkedLoanMetrics.loanAccount.bank && (
<BankLogo
src={
linkedLoanMetrics.loanAccount.bank
.logo
}
name={
linkedLoanMetrics.loanAccount.bank
.name
}
className="size-3.5 shrink-0"
fallback="letter"
/>
)}
{linkedLoanMetrics.loanAccount.bank?.name ??
linkedLoanMetrics.loanAccount.name}
</span>
)}
</div>
)}
{linkedLoanMetrics.loanAccount.bank?.name ??
linkedLoanMetrics.loanAccount.name}
</span>
)}
</div>
<div className="text-xs font-medium text-muted-foreground">
<div className="relative mr-1 size-5 shrink-0">
<AccountTypeIcon
type={account.type}
className="mr-1 inline-block"
className={cn(
'transition-opacity',
dragHandle && 'group-hover:opacity-0',
)}
/>
{dragHandle && (
<span className="absolute inset-0 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100">
{dragHandle}
</span>
)}
</div>
</CardHeader>
<CardContent>

View File

@ -110,14 +110,12 @@ function SortableItem({
isDragging,
} = useSortable({ id });
// Collapsed to zero width by default; on card hover it expands and slides
// in, pushing the card's leading content (logo + name) to the right.
const dragHandle = (
<button
ref={setActivatorNodeRef}
type="button"
aria-label={__('Drag to reorder')}
className="flex w-0 shrink-0 cursor-grab touch-none items-center overflow-hidden text-muted-foreground/70 opacity-0 transition-all duration-200 ease-out group-hover:w-6 group-hover:opacity-100 hover:text-foreground active:cursor-grabbing"
className="cursor-grab touch-none text-muted-foreground transition-colors hover:text-foreground active:cursor-grabbing"
{...attributes}
{...listeners}
>