Add Payment Gateway selection to Settings page

This commit is contained in:
CycroftX
2026-02-03 21:04:57 +05:30
parent 456c2d7d57
commit 10e8e28c52
2 changed files with 138 additions and 31 deletions

View File

@@ -0,0 +1,132 @@
import { CreditCard, CheckCircle2, RotateCw } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Label } from "@/components/ui/label";
import { useState } from "react";
import { toast } from "sonner";
import { cn } from "@/lib/utils";
const availableGateways = [
{
id: "stripe",
name: "Stripe",
logo: "https://upload.wikimedia.org/wikipedia/commons/b/ba/Stripe_Logo%2C_revised_2016.svg",
fee: "2.9% + ₹30",
status: "Connected",
},
{
id: "razorpay",
name: "Razorpay",
logo: "https://upload.wikimedia.org/wikipedia/commons/8/89/Razorpay_logo.svg",
fee: "2%",
status: "Available",
},
{
id: "phonepe",
name: "PhonePe",
logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/PhonePe_Logo.svg/2560px-PhonePe_Logo.svg.png",
fee: "1.9%",
status: "Available",
},
{
id: "cashfree",
name: "Cashfree",
logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/1/12/Cashfree_Payments_Logo.svg/2560px-Cashfree_Payments_Logo.svg.png",
fee: "1.95%",
status: "Available",
}
];
export function PaymentGatewayCard() {
const [activeGateway, setActiveGateway] = useState("stripe");
const [loading, setLoading] = useState<string | null>(null);
const handleGatewayChange = (id: string) => {
if (activeGateway === id) return;
setLoading(id);
// Simulate connection lag
setTimeout(() => {
setActiveGateway(id);
setLoading(null);
toast.success(`Switched active payment gateway to ${availableGateways.find(g => g.id === id)?.name}`);
}, 1500);
};
return (
<div className="neu-card p-6 h-full flex flex-col">
<div className="flex items-center gap-4 mb-6">
<div className="h-12 w-12 rounded-xl bg-indigo-500/10 flex items-center justify-center">
<CreditCard className="h-6 w-6 text-indigo-600 dark:text-indigo-400" />
</div>
<div>
<h3 className="font-bold text-lg text-foreground">Payment Gateway</h3>
<p className="text-sm text-muted-foreground">Collection preferences</p>
</div>
<div className="ml-auto">
{loading ? (
<RotateCw className="h-4 w-4 text-indigo-500 animate-spin" />
) : (
<Badge className="bg-indigo-500/15 text-indigo-600 hover:bg-indigo-500/25 border-none gap-1">
<div className="h-1.5 w-1.5 rounded-full bg-indigo-500 animate-pulse" />
Processing
</Badge>
)}
</div>
</div>
<div className="flex-1 space-y-4">
<RadioGroup value={activeGateway} onValueChange={handleGatewayChange} className="space-y-3">
{availableGateways.map((gateway) => (
<div
key={gateway.id}
className={cn(
"relative flex items-center justify-between p-4 rounded-xl border transition-all cursor-pointer",
activeGateway === gateway.id
? "border-indigo-500/50 bg-indigo-500/5 shadow-neu-sm"
: "border-border/50 bg-card hover:bg-secondary/50"
)}
onClick={() => handleGatewayChange(gateway.id)}
>
<div className="flex items-center gap-4">
<RadioGroupItem value={gateway.id} id={gateway.id} className="border-indigo-500 text-indigo-500" />
<div className="h-8 w-20 flex items-center justify-center bg-white rounded p-1">
{/* Placeholder for logos if external works, else text */}
{/* Using text fallback for safety/offline dev */}
<span className="font-bold text-xs text-black">{gateway.name}</span>
</div>
<div>
<Label htmlFor={gateway.id} className="font-semibold cursor-pointer">{gateway.name}</Label>
<p className="text-xs text-muted-foreground">Fee: {gateway.fee}</p>
</div>
</div>
{gateway.status === "Connected" || activeGateway === gateway.id ? (
<CheckCircle2 className="h-5 w-5 text-indigo-500" />
) : (
<Button
variant="ghost"
size="sm"
className="text-xs text-muted-foreground h-7"
onClick={(e) => {
e.stopPropagation();
handleGatewayChange(gateway.id);
}}
>
Connect
</Button>
)}
</div>
))}
</RadioGroup>
</div>
<div className="mt-6 pt-6 border-t border-border/50">
<p className="text-xs text-muted-foreground text-center">
Processing payments settled in <b>T+2 days</b> via selected gateway.
</p>
</div>
</div>
);
}

View File

@@ -3,6 +3,7 @@ import { OrganizationProfileCard } from '@/features/settings/components/Organiza
import { PayoutBankingCard } from '@/features/settings/components/PayoutBankingCard';
import { TeamSecurityCard } from '@/features/settings/components/TeamSecurityCard';
import { DeveloperSection } from '@/features/settings/components/DeveloperSection';
import { PaymentGatewayCard } from '@/features/settings/components/PaymentGatewayCard';
export default function Settings() {
return (
@@ -11,42 +12,16 @@ export default function Settings() {
description="Manage platform configuration and critical operations."
>
<div className="space-y-6 max-w-[1200px]">
{/* Top Grid */}
{/* Top Grid: Identity & Banking */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 h-auto lg:h-[400px]">
{/* Box 1: Organization Identity (High Priority) */}
<OrganizationProfileCard />
{/* Box 2: Financials & Banking (High Priority) */}
<PayoutBankingCard />
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Box 3: Security (Full Width on Mobile, 2/3 on Desktop if needed, but here simple 1/3 stack or full width row) */}
{/* Adjusting layout: Let's make Security taking full row or sidebar style.
Based on requirements "2x2 Grid or Vertical Stack".
Let's stick to a clean layout where Security sits next to maybe a logs panel or just takes full width.
Actually, the requirements said "Layout: 2x2 Grid".
Let's put Team & Security as the 3rd box, and maybe Developer as 4th?
But Developer is collapsible.
*/}
<div className="lg:col-span-2">
<TeamSecurityCard />
</div>
<div className="lg:col-span-1">
{/* Placeholder or Tip card? Or maybe just make TeamSecurityCard full width if it has lists.
The TeamSecurityCard design I made is card-like.
Let's span it across 2 columns for better readability of the list.
*/}
<div className="neu-card p-6 h-full bg-gradient-to-br from-primary/5 to-transparent border-primary/20">
<h4 className="font-bold text-lg mb-2">Need Help?</h4>
<p className="text-sm text-muted-foreground mb-4">
Contact our dedicated support team for assistance with account configurations.
</p>
<button className="text-sm font-bold text-primary hover:underline">
Open Support Ticket &rarr;
</button>
</div>
</div>
{/* Middle Grid: Gateways & Security */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 h-auto">
<PaymentGatewayCard />
<TeamSecurityCard />
</div>
{/* Collapsible Developer Section */}