mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 08:20:58 +00:00
283 lines
12 KiB
TypeScript
283 lines
12 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { Save } from 'lucide-react';
|
|
import { systemConfigApi } from '@/lib/api';
|
|
|
|
type Tab = 'general' | 'payment' | 'integrations' | 'configurations';
|
|
|
|
export default function SettingsPage() {
|
|
const [activeTab, setActiveTab] = useState<Tab>('general');
|
|
const [seatHoldMinutes, setSeatHoldMinutes] = useState('5');
|
|
const [holdCutoffHours, setHoldCutoffHours] = useState('2');
|
|
const [throttleAuthLimit, setThrottleAuthLimit] = useState('5');
|
|
const [throttleStrictLimit, setThrottleStrictLimit] = useState('20');
|
|
const [throttleDefaultLimit, setThrottleDefaultLimit] = useState('100');
|
|
const [configLoading, setConfigLoading] = useState(false);
|
|
const [configSaving, setConfigSaving] = useState(false);
|
|
const [configMessage, setConfigMessage] = useState('');
|
|
|
|
useEffect(() => {
|
|
if (activeTab !== 'configurations') return;
|
|
setConfigLoading(true);
|
|
systemConfigApi.getAll()
|
|
.then((data) => {
|
|
if (data?.seat_hold_duration_minutes) setSeatHoldMinutes(data.seat_hold_duration_minutes);
|
|
if (data?.hold_cutoff_hours_before_departure) setHoldCutoffHours(data.hold_cutoff_hours_before_departure);
|
|
if (data?.throttle_auth_limit) setThrottleAuthLimit(data.throttle_auth_limit);
|
|
if (data?.throttle_strict_limit) setThrottleStrictLimit(data.throttle_strict_limit);
|
|
if (data?.throttle_default_limit) setThrottleDefaultLimit(data.throttle_default_limit);
|
|
})
|
|
.catch(() => {})
|
|
.finally(() => setConfigLoading(false));
|
|
}, [activeTab]);
|
|
|
|
const saveConfigurations = async () => {
|
|
setConfigSaving(true);
|
|
setConfigMessage('');
|
|
try {
|
|
await systemConfigApi.update({
|
|
seat_hold_duration_minutes: seatHoldMinutes,
|
|
hold_cutoff_hours_before_departure: holdCutoffHours,
|
|
throttle_auth_limit: throttleAuthLimit,
|
|
throttle_strict_limit: throttleStrictLimit,
|
|
throttle_default_limit: throttleDefaultLimit,
|
|
});
|
|
setConfigMessage('Saved successfully.');
|
|
} catch {
|
|
setConfigMessage('Failed to save.');
|
|
} finally {
|
|
setConfigSaving(false);
|
|
}
|
|
};
|
|
|
|
const tabs: { id: Tab; label: string }[] = [
|
|
{ id: 'general', label: 'General' },
|
|
{ id: 'payment', label: 'Payment' },
|
|
{ id: 'integrations', label: 'Integrations' },
|
|
{ id: 'configurations', label: 'Configurations' },
|
|
];
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-foreground">Settings</h1>
|
|
<p className="text-muted-foreground">Manage system settings and configurations</p>
|
|
</div>
|
|
{activeTab !== 'configurations' && (
|
|
<button className="btn btn-primary flex items-center gap-2">
|
|
<Save className="h-4 w-4" />
|
|
Save Changes
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex gap-2 border-b border-border">
|
|
{tabs.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => setActiveTab(tab.id)}
|
|
className={`px-4 py-2 font-medium ${activeTab === tab.id ? 'border-b-2 border-primary text-primary' : 'text-muted-foreground'}`}
|
|
>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{activeTab === 'general' && (
|
|
<div className="card space-y-4">
|
|
<div>
|
|
<label className="label">Platform Name</label>
|
|
<input type="text" className="input" defaultValue="EDR Passenger Platform" />
|
|
</div>
|
|
<div>
|
|
<label className="label">Support Email</label>
|
|
<input type="email" className="input" defaultValue="support@edr-platform.com" />
|
|
</div>
|
|
<div>
|
|
<label className="label">Support Phone</label>
|
|
<input type="tel" className="input" defaultValue="+251911234567" />
|
|
</div>
|
|
<div>
|
|
<label className="label">Default Currency</label>
|
|
<select className="input">
|
|
<option>ETB - Ethiopian Birr</option>
|
|
<option>DJF - Djiboutian Franc</option>
|
|
<option>USD - US Dollar</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'payment' && (
|
|
<div className="space-y-6">
|
|
<div className="card">
|
|
<h3 className="mb-4 text-lg font-semibold text-foreground">Payment Providers</h3>
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between rounded-lg border border-border p-4">
|
|
<div>
|
|
<p className="font-medium text-foreground">Telebirr</p>
|
|
<p className="text-sm text-muted-foreground">Mobile payment provider</p>
|
|
</div>
|
|
<label className="relative inline-flex cursor-pointer items-center">
|
|
<input type="checkbox" className="peer sr-only" defaultChecked />
|
|
<div className="peer h-6 w-11 rounded-full bg-gray-200 dark:bg-gray-700 after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:bg-primary peer-checked:after:translate-x-full peer-checked:after:border-white"></div>
|
|
</label>
|
|
</div>
|
|
<div className="flex items-center justify-between rounded-lg border border-border p-4">
|
|
<div>
|
|
<p className="font-medium text-foreground">CBE Birr</p>
|
|
<p className="text-sm text-muted-foreground">Bank payment provider</p>
|
|
</div>
|
|
<label className="relative inline-flex cursor-pointer items-center">
|
|
<input type="checkbox" className="peer sr-only" defaultChecked />
|
|
<div className="peer h-6 w-11 rounded-full bg-gray-200 dark:bg-gray-700 after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:bg-primary peer-checked:after:translate-x-full peer-checked:after:border-white"></div>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'integrations' && (
|
|
<div className="space-y-6">
|
|
<div className="card">
|
|
<h3 className="mb-4 text-lg font-semibold text-foreground">Verifayda 2.0 Integration</h3>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="label">API URL</label>
|
|
<input type="text" className="input" defaultValue="https://api.verifayda.gov.et/v2" />
|
|
</div>
|
|
<div>
|
|
<label className="label">API Key</label>
|
|
<input type="password" className="input" defaultValue="••••••••••••" />
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<input type="checkbox" id="verifayda-enabled" defaultChecked />
|
|
<label htmlFor="verifayda-enabled" className="text-sm text-foreground">
|
|
Enable Verifayda verification
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="card">
|
|
<h3 className="mb-4 text-lg font-semibold text-foreground">Corporate IAM Integration</h3>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="label">IAM API URL</label>
|
|
<input type="text" className="input" defaultValue="https://iam.tria-plc.com/api" />
|
|
</div>
|
|
<div>
|
|
<label className="label">API Key</label>
|
|
<input type="password" className="input" defaultValue="••••••••••••" />
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<input type="checkbox" id="iam-enabled" />
|
|
<label htmlFor="iam-enabled" className="text-sm text-foreground">
|
|
Enable IAM authentication
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'configurations' && (
|
|
<div className="card space-y-6">
|
|
<h3 className="text-lg font-semibold text-foreground">Rate Limiting (requests / minute / IP)</h3>
|
|
{!configLoading && (
|
|
<div className="max-w-sm space-y-4">
|
|
<div className="space-y-2">
|
|
<label className="label" htmlFor="throttle-auth">Auth endpoints limit</label>
|
|
<input
|
|
id="throttle-auth"
|
|
type="number" min="1" className="input"
|
|
value={throttleAuthLimit}
|
|
onChange={(e) => setThrottleAuthLimit(e.target.value)}
|
|
/>
|
|
<p className="text-xs text-muted-foreground">Login, register, OTP. Default: 5.</p>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<label className="label" htmlFor="throttle-strict">Strict endpoints limit</label>
|
|
<input
|
|
id="throttle-strict"
|
|
type="number" min="1" className="input"
|
|
value={throttleStrictLimit}
|
|
onChange={(e) => setThrottleStrictLimit(e.target.value)}
|
|
/>
|
|
<p className="text-xs text-muted-foreground">Sensitive operations. Default: 20.</p>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<label className="label" htmlFor="throttle-default">Default endpoints limit</label>
|
|
<input
|
|
id="throttle-default"
|
|
type="number" min="1" className="input"
|
|
value={throttleDefaultLimit}
|
|
onChange={(e) => setThrottleDefaultLimit(e.target.value)}
|
|
/>
|
|
<p className="text-xs text-muted-foreground">All other endpoints including search. Default: 100.</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
<h3 className="text-lg font-semibold text-foreground">Seat Booking</h3>
|
|
{configLoading ? (
|
|
<p className="text-sm text-muted-foreground">Loading...</p>
|
|
) : (
|
|
<>
|
|
<div className="max-w-sm space-y-2">
|
|
<label className="label" htmlFor="hold-duration">
|
|
Seat Hold Duration (minutes)
|
|
</label>
|
|
<input
|
|
id="hold-duration"
|
|
type="number"
|
|
min="1"
|
|
max="60"
|
|
className="input"
|
|
value={seatHoldMinutes}
|
|
onChange={(e) => setSeatHoldMinutes(e.target.value)}
|
|
/>
|
|
<p className="text-xs text-muted-foreground">
|
|
How long a seat hold remains active before it expires automatically. Default: 5 minutes.
|
|
</p>
|
|
</div>
|
|
<div className="max-w-sm space-y-2">
|
|
<label className="label" htmlFor="hold-cutoff">
|
|
Hold Cutoff Before Departure (hours)
|
|
</label>
|
|
<input
|
|
id="hold-cutoff"
|
|
type="number"
|
|
min="0"
|
|
max="24"
|
|
className="input"
|
|
value={holdCutoffHours}
|
|
onChange={(e) => setHoldCutoffHours(e.target.value)}
|
|
/>
|
|
<p className="text-xs text-muted-foreground">
|
|
Seat holds are rejected when this many hours or fewer remain before departure. Default: 2 hours.
|
|
</p>
|
|
</div>
|
|
</>
|
|
)}
|
|
<div className="flex items-center gap-3">
|
|
<button
|
|
className="btn btn-primary flex items-center gap-2"
|
|
onClick={saveConfigurations}
|
|
disabled={configSaving || configLoading}
|
|
>
|
|
<Save className="h-4 w-4" />
|
|
{configSaving ? 'Saving...' : 'Save Changes'}
|
|
</button>
|
|
{configMessage && (
|
|
<span className="text-sm text-muted-foreground">{configMessage}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|