mirror of
https://github.com/elisiariocouto/leggen.git
synced 2025-12-13 09:02:23 +00:00
- Add drawer-based bank account connection flow with country/bank selection - Create bank connection success page with redirect handling - Add bank connections status card showing all requisitions and their states - Move account management actions to appropriate sections (add to connections, edit in accounts) - Implement proper delete functionality for bank connections via GoCardless API - Add proper TypeScript types for all bank-related data structures - Improve error handling for bank connection operations with specific HTTP status codes - Remove transaction data when disconnecting accounts while preserving history 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
204 lines
6.2 KiB
TypeScript
204 lines
6.2 KiB
TypeScript
import { useState } from "react";
|
|
import { useQuery, useMutation } from "@tanstack/react-query";
|
|
import { Plus, Building2, ExternalLink } from "lucide-react";
|
|
import { apiClient } from "../lib/api";
|
|
import { Button } from "./ui/button";
|
|
import { Label } from "./ui/label";
|
|
import {
|
|
Drawer,
|
|
DrawerClose,
|
|
DrawerContent,
|
|
DrawerDescription,
|
|
DrawerFooter,
|
|
DrawerHeader,
|
|
DrawerTitle,
|
|
DrawerTrigger,
|
|
} from "./ui/drawer";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "./ui/select";
|
|
import { Alert, AlertDescription } from "./ui/alert";
|
|
|
|
export default function AddBankAccountDrawer() {
|
|
const [open, setOpen] = useState(false);
|
|
const [selectedCountry, setSelectedCountry] = useState<string>("");
|
|
const [selectedBank, setSelectedBank] = useState<string>("");
|
|
|
|
const { data: countries } = useQuery({
|
|
queryKey: ["supportedCountries"],
|
|
queryFn: apiClient.getSupportedCountries,
|
|
});
|
|
|
|
const { data: banks, isLoading: banksLoading } = useQuery({
|
|
queryKey: ["bankInstitutions", selectedCountry],
|
|
queryFn: () => apiClient.getBankInstitutions(selectedCountry),
|
|
enabled: !!selectedCountry,
|
|
});
|
|
|
|
const connectBankMutation = useMutation({
|
|
mutationFn: (institutionId: string) =>
|
|
apiClient.createBankConnection(institutionId),
|
|
onSuccess: (data) => {
|
|
// Redirect to the bank's authorization link
|
|
if (data.link) {
|
|
window.open(data.link, "_blank");
|
|
setOpen(false);
|
|
}
|
|
},
|
|
onError: (error) => {
|
|
console.error("Failed to create bank connection:", error);
|
|
},
|
|
});
|
|
|
|
const handleCountryChange = (country: string) => {
|
|
setSelectedCountry(country);
|
|
setSelectedBank("");
|
|
};
|
|
|
|
const handleConnect = () => {
|
|
if (selectedBank) {
|
|
connectBankMutation.mutate(selectedBank);
|
|
}
|
|
};
|
|
|
|
const resetForm = () => {
|
|
setSelectedCountry("");
|
|
setSelectedBank("");
|
|
};
|
|
|
|
return (
|
|
<Drawer
|
|
open={open}
|
|
onOpenChange={(isOpen) => {
|
|
setOpen(isOpen);
|
|
if (!isOpen) {
|
|
resetForm();
|
|
}
|
|
}}
|
|
>
|
|
<DrawerTrigger asChild>
|
|
<Button size="sm">
|
|
<Plus className="h-4 w-4 mr-2" />
|
|
Add New Account
|
|
</Button>
|
|
</DrawerTrigger>
|
|
<DrawerContent className="max-h-[80vh]">
|
|
<DrawerHeader>
|
|
<DrawerTitle>Connect Bank Account</DrawerTitle>
|
|
<DrawerDescription>
|
|
Select your country and bank to connect your account to Leggen
|
|
</DrawerDescription>
|
|
</DrawerHeader>
|
|
|
|
<div className="px-6 space-y-6 overflow-y-auto">
|
|
{/* Country Selection */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="country">Country</Label>
|
|
<Select value={selectedCountry} onValueChange={handleCountryChange}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select your country" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{countries?.map((country) => (
|
|
<SelectItem key={country.code} value={country.code}>
|
|
{country.name}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* Bank Selection */}
|
|
{selectedCountry && (
|
|
<div className="space-y-2">
|
|
<Label htmlFor="bank">Bank</Label>
|
|
{banksLoading ? (
|
|
<div className="p-4 text-center text-muted-foreground">
|
|
Loading banks...
|
|
</div>
|
|
) : banks && banks.length > 0 ? (
|
|
<Select value={selectedBank} onValueChange={setSelectedBank}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select your bank" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{banks.map((bank) => (
|
|
<SelectItem key={bank.id} value={bank.id}>
|
|
<div className="flex items-center space-x-2">
|
|
{bank.logo ? (
|
|
<img
|
|
src={bank.logo}
|
|
alt={`${bank.name} logo`}
|
|
className="w-4 h-4 object-contain"
|
|
/>
|
|
) : (
|
|
<Building2 className="w-4 h-4" />
|
|
)}
|
|
<span>{bank.name}</span>
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
) : (
|
|
<Alert>
|
|
<AlertDescription>
|
|
No banks available for the selected country.
|
|
</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Instructions */}
|
|
{selectedBank && (
|
|
<Alert>
|
|
<AlertDescription>
|
|
You'll be redirected to your bank's website to authorize the
|
|
connection. After approval, you'll return to Leggen and your
|
|
account will start syncing.
|
|
</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
|
|
{/* Error Display */}
|
|
{connectBankMutation.isError && (
|
|
<Alert variant="destructive">
|
|
<AlertDescription>
|
|
Failed to create bank connection. Please try again.
|
|
</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
</div>
|
|
|
|
<DrawerFooter>
|
|
<div className="flex space-x-2">
|
|
<Button
|
|
onClick={handleConnect}
|
|
disabled={!selectedBank || connectBankMutation.isPending}
|
|
className="flex-1"
|
|
>
|
|
<ExternalLink className="h-4 w-4 mr-2" />
|
|
{connectBankMutation.isPending
|
|
? "Connecting..."
|
|
: "Open Bank Authorization"}
|
|
</Button>
|
|
<DrawerClose asChild>
|
|
<Button
|
|
variant="outline"
|
|
disabled={connectBankMutation.isPending}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
</DrawerClose>
|
|
</div>
|
|
</DrawerFooter>
|
|
</DrawerContent>
|
|
</Drawer>
|
|
);
|
|
}
|