245 lines
12 KiB
TypeScript
245 lines
12 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { SystemConfig } from '@/lib/types/settings';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from '@/components/ui/card';
|
|
import { Switch } from '@/components/ui/switch';
|
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
|
import { updateSystemSetting, purgeSystemCache } from '@/lib/actions/settings';
|
|
import { toast } from 'sonner';
|
|
import { Server, CreditCard, Radio, Trash2, AlertTriangle, Activity } from 'lucide-react';
|
|
import { Badge } from '@/components/ui/badge';
|
|
|
|
interface SystemHealthProps {
|
|
config: SystemConfig;
|
|
onUpdate: (data: SystemConfig) => void;
|
|
}
|
|
|
|
export function SystemHealthTab({ config, onUpdate }: SystemHealthProps) {
|
|
const [loading, setLoading] = useState(false);
|
|
const [state, setState] = useState(config);
|
|
const [purgeLoading, setPurgeLoading] = useState(false);
|
|
|
|
const handleSave = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const res = await updateSystemSetting('system', state);
|
|
if (res.success && res.updatedSettings.system) {
|
|
toast.success('System configuration updated');
|
|
onUpdate(res.updatedSettings.system);
|
|
}
|
|
} catch (error) {
|
|
toast.error('Failed to update system config');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handlePurgeCache = async () => {
|
|
setPurgeLoading(true);
|
|
try {
|
|
const res = await purgeSystemCache();
|
|
if (res.success) {
|
|
toast.success('Cache purged successfully', {
|
|
description: 'Changes are propagating to edge locations.'
|
|
});
|
|
}
|
|
} catch (error) {
|
|
toast.error('Failed to purge cache');
|
|
} finally {
|
|
setPurgeLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
|
|
{/* System Status Banner */}
|
|
<div className="grid md:grid-cols-3 gap-6">
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">API Status</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center gap-2">
|
|
<div className="h-2.5 w-2.5 rounded-full bg-emerald-500 animate-pulse" />
|
|
<span className="text-2xl font-bold">Operational</span>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-1">Uptime: 99.99%</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">Cache Status</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-2xl font-bold">Healthy</span>
|
|
<Badge variant="outline">TTL: {state.cache.ttl}s</Badge>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
Last Purged: {state.cache.lastPurgedAt ? new Date(state.cache.lastPurgedAt).toLocaleTimeString() : 'Never'}
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">Database</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<span className="text-2xl font-bold">Connected</span>
|
|
<p className="text-xs text-muted-foreground mt-1">Latency: 24ms</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Payment Gateways */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<CreditCard className="h-5 w-5 text-primary" />
|
|
Payment Gateways
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Manage gateway keys and active modes (Test vs Live).
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
{/* Stripe */}
|
|
<div className="p-4 border rounded-lg bg-card/50">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div className="flex items-center gap-2">
|
|
<h4 className="font-semibold">Stripe</h4>
|
|
{state.gateways.stripe.mode === 'live' ?
|
|
<Badge className="bg-emerald-500">Live</Badge> :
|
|
<Badge variant="secondary">Test Mode</Badge>
|
|
}
|
|
</div>
|
|
<Switch
|
|
checked={state.gateways.stripe.enabled}
|
|
onCheckedChange={(c) => setState({
|
|
...state,
|
|
gateways: { ...state.gateways, stripe: { ...state.gateways.stripe, enabled: c } }
|
|
})}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label>Public Key</Label>
|
|
<Input
|
|
type="password"
|
|
value={state.gateways.stripe.publicKey}
|
|
readOnly // For demo safety
|
|
className="font-mono text-xs"
|
|
/>
|
|
</div>
|
|
<div className="flex items-center gap-2 mt-4">
|
|
<Label className="text-xs">Mode:</Label>
|
|
<div className="flex items-center space-x-2">
|
|
<Switch
|
|
checked={state.gateways.stripe.mode === 'live'}
|
|
onCheckedChange={(c) => setState({
|
|
...state,
|
|
gateways: { ...state.gateways, stripe: { ...state.gateways.stripe, mode: c ? 'live' : 'test' } }
|
|
})}
|
|
/>
|
|
<span className={state.gateways.stripe.mode === 'live' ? 'font-bold text-red-600' : 'text-muted-foreground'}>
|
|
{state.gateways.stripe.mode === 'live' ? 'LIVE TRAFFIC' : 'Test Mode'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Razorpay */}
|
|
<div className="p-4 border rounded-lg bg-card/50">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div className="flex items-center gap-2">
|
|
<h4 className="font-semibold">Razorpay</h4>
|
|
{state.gateways.razorpay.mode === 'live' ?
|
|
<Badge className="bg-emerald-500">Live</Badge> :
|
|
<Badge variant="secondary">Test Mode</Badge>
|
|
}
|
|
</div>
|
|
<Switch
|
|
checked={state.gateways.razorpay.enabled}
|
|
onCheckedChange={(c) => setState({
|
|
...state,
|
|
gateways: { ...state.gateways, razorpay: { ...state.gateways.razorpay, enabled: c } }
|
|
})}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label>Key ID</Label>
|
|
<Input
|
|
type="password"
|
|
value={state.gateways.razorpay.keyId}
|
|
readOnly
|
|
className="font-mono text-xs"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter className="justify-end border-t pt-4">
|
|
<Button onClick={handleSave} disabled={loading}>Save Gateway Config</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
|
|
{/* Danger Zone */}
|
|
<h3 className="text-lg font-semibold text-red-600 flex items-center gap-2 mt-8">
|
|
<AlertTriangle className="h-5 w-5" />
|
|
Danger Zone
|
|
</h3>
|
|
|
|
<div className="grid md:grid-cols-2 gap-6">
|
|
<Card className="border-red-200 bg-red-50/10">
|
|
<CardHeader>
|
|
<CardTitle className="text-base text-red-700">Maintenance Mode</CardTitle>
|
|
<CardDescription>
|
|
Disable the public facing app temporarily. Only admins can access.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="flex justify-between items-center">
|
|
<div className="space-y-1">
|
|
<Label>Status</Label>
|
|
<p className="text-sm font-medium">
|
|
{false ? <span className="text-red-600">Active - App Offline</span> : <span className="text-emerald-600">Inactive - App Live</span>}
|
|
</p>
|
|
</div>
|
|
<Button variant="destructive">Enable Maintenance</Button>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="border-orange-200 bg-orange-50/10">
|
|
<CardHeader>
|
|
<CardTitle className="text-base text-orange-700">Cache Control</CardTitle>
|
|
<CardDescription>
|
|
Force purge the CDN cache for all static pages.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="flex justify-between items-center">
|
|
<div className="space-y-1">
|
|
<Label>Metrics</Label>
|
|
<p className="text-xs text-muted-foreground">Items cached: ~14.2k</p>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
className="border-orange-300 text-orange-700 hover:bg-orange-100"
|
|
onClick={handlePurgeCache}
|
|
disabled={purgeLoading}
|
|
>
|
|
{purgeLoading ? (
|
|
<Activity className="h-4 w-4 mr-2 animate-spin" />
|
|
) : (
|
|
<Trash2 className="h-4 w-4 mr-2" />
|
|
)}
|
|
Purge Cache
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|