feat: add Review Management module and UI layout fixes
This commit is contained in:
219
src/features/settings/components/GatewayConfigSheet.tsx
Normal file
219
src/features/settings/components/GatewayConfigSheet.tsx
Normal file
@@ -0,0 +1,219 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { GatewayProvider, GatewayCredentials } from '@/lib/types/settings';
|
||||
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from '@/components/ui/sheet';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { verifyGatewayCredentials, saveGatewayConfig } from '@/lib/actions/payment-settings';
|
||||
import { toast } from 'sonner';
|
||||
import { Lock, CheckBase, Loader2, Copy } from 'lucide-react';
|
||||
import { decrypt } from '@/lib/payment-encryption';
|
||||
|
||||
interface GatewayConfigSheetProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
provider: GatewayProvider;
|
||||
initialConfig: GatewayCredentials;
|
||||
onSave: () => void;
|
||||
}
|
||||
|
||||
export function GatewayConfigSheet({ open, onOpenChange, provider, initialConfig, onSave }: GatewayConfigSheetProps) {
|
||||
const [config, setConfig] = useState<GatewayCredentials>(initialConfig);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [verifying, setVerifying] = useState(false);
|
||||
|
||||
// Reset config when provider changes
|
||||
useEffect(() => {
|
||||
// Decrypt sensitive fields for editing
|
||||
const decrypted = { ...initialConfig };
|
||||
if (decrypted.salt) decrypted.salt = decrypt(decrypted.salt);
|
||||
setConfig(decrypted);
|
||||
}, [provider, initialConfig, open]);
|
||||
|
||||
const handleVerify = async () => {
|
||||
setVerifying(true);
|
||||
try {
|
||||
const res = await verifyGatewayCredentials(provider, config);
|
||||
if (res.success) {
|
||||
toast.success('Credentials Verified', { description: res.message });
|
||||
} else {
|
||||
toast.error('Verification Failed', { description: res.message });
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Verification Error');
|
||||
} finally {
|
||||
setVerifying(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await saveGatewayConfig(provider, config);
|
||||
if (res.success) {
|
||||
toast.success(`Saved ${provider} configuration`);
|
||||
onSave();
|
||||
onOpenChange(false);
|
||||
} else {
|
||||
toast.error(res.message);
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Failed to save configuration');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||
<SheetContent className="w-[400px] sm:w-[540px] overflow-y-auto">
|
||||
<SheetHeader>
|
||||
<SheetTitle className="flex items-center gap-2 capitalize">
|
||||
{provider} Configuration
|
||||
</SheetTitle>
|
||||
<SheetDescription>
|
||||
Configure API keys and secrets for {provider}.
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
<div className="py-6 space-y-6">
|
||||
{/* Environment Toggle */}
|
||||
<div className="flex items-center justify-between p-4 border rounded-lg bg-muted/30">
|
||||
<div className="space-y-0.5">
|
||||
<Label>Mode</Label>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{config.mode === 'live' ? 'Production / Live Traffic' : 'Sandbox / Test Mode'}
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={config.mode === 'live'}
|
||||
onCheckedChange={(c) => setConfig({ ...config, mode: c ? 'live' : 'test' })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="credentials">
|
||||
<TabsList className="w-full">
|
||||
<TabsTrigger value="credentials" className="flex-1">Credentials</TabsTrigger>
|
||||
<TabsTrigger value="features" className="flex-1">Features</TabsTrigger>
|
||||
<TabsTrigger value="webhooks" className="flex-1">Webhooks</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="credentials" className="space-y-4 pt-4">
|
||||
{/* Dynamic inputs based on provider */}
|
||||
{(provider === 'razorpay' || provider === 'easebuzz') && (
|
||||
<div className="space-y-2">
|
||||
<Label>Key ID / Merchant Key</Label>
|
||||
<Input
|
||||
value={config.keyId || config.merchantId || ''}
|
||||
onChange={(e) => setConfig({ ...config, keyId: e.target.value, merchantId: e.target.value })}
|
||||
placeholder="rzp_test_..."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(provider === 'stripe') && (
|
||||
<div className="space-y-2">
|
||||
<Label>Public Key</Label>
|
||||
<Input
|
||||
value={config.publicKey || ''}
|
||||
onChange={(e) => setConfig({ ...config, publicKey: e.target.value })}
|
||||
placeholder="pk_test_..."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(provider === 'payu' || provider === 'easebuzz') && (
|
||||
<div className="space-y-2">
|
||||
<Label>Salt / Secret Key</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type="password"
|
||||
value={config.salt || ''}
|
||||
onChange={(e) => setConfig({ ...config, salt: e.target.value })}
|
||||
placeholder="Enter secret salt"
|
||||
className="pr-10"
|
||||
/>
|
||||
<Lock className="absolute right-3 top-3 h-4 w-4 text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Button variant="secondary" className="w-full mt-4" onClick={handleVerify} disabled={verifying}>
|
||||
{verifying && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
Test Credentials
|
||||
</Button>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="features" className="space-y-4 pt-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="flex items-center space-x-2 border p-3 rounded-md">
|
||||
<Switch
|
||||
id="feat-nb"
|
||||
checked={config.features.netbanking}
|
||||
onCheckedChange={(c) => setConfig({ ...config, features: { ...config.features, netbanking: c } })}
|
||||
/>
|
||||
<Label htmlFor="feat-nb">Netbanking</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 border p-3 rounded-md">
|
||||
<Switch
|
||||
id="feat-upi"
|
||||
checked={config.features.upi}
|
||||
onCheckedChange={(c) => setConfig({ ...config, features: { ...config.features, upi: c } })}
|
||||
/>
|
||||
<Label htmlFor="feat-upi">UPI</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 border p-3 rounded-md">
|
||||
<Switch
|
||||
id="feat-cards"
|
||||
checked={config.features.cards}
|
||||
onCheckedChange={(c) => setConfig({ ...config, features: { ...config.features, cards: c } })}
|
||||
/>
|
||||
<Label htmlFor="feat-cards">Cards</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 border p-3 rounded-md">
|
||||
<Switch
|
||||
id="feat-emi"
|
||||
checked={config.features.emi}
|
||||
onCheckedChange={(c) => setConfig({ ...config, features: { ...config.features, emi: c } })}
|
||||
/>
|
||||
<Label htmlFor="feat-emi">EMI</Label>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="webhooks" className="space-y-4 pt-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Webhook URL</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<Input readOnly value={`https://api.eventify.com/webhooks/${provider}`} />
|
||||
<Button variant="outline" size="icon">
|
||||
<Copy className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">Add this URL to your provider dashboard.</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Webhook Secret</Label>
|
||||
<Input
|
||||
value={config.webhookSecret || ''}
|
||||
onChange={(e) => setConfig({ ...config, webhookSecret: e.target.value })}
|
||||
placeholder="whsec_..."
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
<SheetFooter>
|
||||
<Button onClick={handleSave} disabled={loading} className="w-full sm:w-auto">
|
||||
{loading ? 'Saving...' : 'Save Configuration'}
|
||||
</Button>
|
||||
</SheetFooter>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user