Add Payment Gateway selection to Settings page
This commit is contained in:
132
src/features/settings/components/PaymentGatewayCard.tsx
Normal file
132
src/features/settings/components/PaymentGatewayCard.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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 →
|
||||
</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 */}
|
||||
|
||||
Reference in New Issue
Block a user