fix(spaces): address QA review nits

- Replace the native window.confirm() on space deletion with the app's
  AlertDialog, matching how every other destructive action confirms.
- Translate the 'Spaces settings' breadcrumb (add the es.json key).
- Show an empty state on the dashboard when the active space has no
  accounts, instead of a bare 'Accounts' heading.
This commit is contained in:
Víctor Falcón 2026-07-09 15:33:04 +02:00
parent c163e592d9
commit d76b98c837
3 changed files with 74 additions and 25 deletions

View File

@ -2229,5 +2229,8 @@
"Switch": "Cambiar",
"Rename": "Renombrar",
"Rename space": "Renombrar espacio",
"Remove or move this space's accounts before deleting it.": "Elimina o mueve las cuentas de este espacio antes de borrarlo."
"Remove or move this space's accounts before deleting it.": "Elimina o mueve las cuentas de este espacio antes de borrarlo.",
"Spaces settings": "Configuración de espacios",
"Delete space": "Eliminar espacio",
"This space has no accounts yet.": "Este espacio aún no tiene cuentas."
}

View File

@ -311,21 +311,27 @@ export default function Dashboard() {
)}
</div>
<div className="grid gap-4 md:grid-cols-2">
{gridAccounts.map((account) => (
<AccountBalanceCard
key={account.id}
account={account}
onBalanceUpdated={refetch}
linkedLoanMetrics={
linkedLoanMetricsMap[account.id]
}
displayCurrencyCode={
netWorthEvolution.currency_code
}
/>
))}
</div>
{gridAccounts.length === 0 ? (
<p className="rounded-lg border border-dashed border-border py-8 text-center text-sm text-muted-foreground">
{__('This space has no accounts yet.')}
</p>
) : (
<div className="grid gap-4 md:grid-cols-2">
{gridAccounts.map((account) => (
<AccountBalanceCard
key={account.id}
account={account}
onBalanceUpdated={refetch}
linkedLoanMetrics={
linkedLoanMetricsMap[account.id]
}
displayCurrencyCode={
netWorthEvolution.currency_code
}
/>
))}
</div>
)}
<AccountsManagerDialog
open={editOpen}

View File

@ -6,6 +6,16 @@ import {
update as updateSpace,
} from '@/actions/App/Http/Controllers/SpaceController';
import HeadingSmall from '@/components/heading-small';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button';
import {
Dialog,
@ -36,6 +46,7 @@ export default function Spaces() {
const { spaces, currentSpace } = usePage<SharedData>().props;
const [createOpen, setCreateOpen] = useState(false);
const [renaming, setRenaming] = useState<Space | null>(null);
const [deleting, setDeleting] = useState<Space | null>(null);
const [name, setName] = useState('');
const [processing, setProcessing] = useState(false);
@ -73,16 +84,16 @@ export default function Spaces() {
);
};
const remove = (space: Space) => {
if (
!window.confirm(
__('Delete this space? This cannot be undone.') +
` (${space.name})`,
)
) {
const confirmDelete = () => {
if (!deleting) {
return;
}
router.delete(destroySpace(space.id).url, { preserveScroll: true });
setProcessing(true);
router.delete(destroySpace(deleting.id).url, {
preserveScroll: true,
onFinish: () => setProcessing(false),
onSuccess: () => setDeleting(null),
});
};
return (
@ -169,7 +180,7 @@ export default function Spaces() {
size="sm"
className="text-destructive hover:text-destructive"
onClick={() =>
remove(space)
setDeleting(space)
}
>
{__('Delete')}
@ -270,6 +281,35 @@ export default function Spaces() {
</form>
</DialogContent>
</Dialog>
<AlertDialog
open={deleting !== null}
onOpenChange={(open) => !open && setDeleting(null)}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{__('Delete space')}
</AlertDialogTitle>
<AlertDialogDescription>
{__('Delete this space? This cannot be undone.')}
{deleting ? ` (${deleting.name})` : ''}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={processing}>
{__('Cancel')}
</AlertDialogCancel>
<AlertDialogAction
onClick={confirmDelete}
disabled={processing}
variant="destructive"
>
{__('Delete')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</AppLayout>
);
}