66f9de17c5
- Create Dashboard page with stats cards and recent activity feed - Create Backups page with job listing table, create modal, and actions - Create Settings page with tabs for General, Notifications, Security, Logs - Update App.tsx to use new page components - Add delete method to jobsApi in client.ts - TypeScript compiles cleanly
206 lines
6.3 KiB
TypeScript
206 lines
6.3 KiB
TypeScript
import { useState } from 'react';
|
|
|
|
const tabs = [
|
|
{ id: 'general', label: 'General' },
|
|
{ id: 'notifications', label: 'Notifications' },
|
|
{ id: 'security', label: 'Security' },
|
|
{ id: 'logs', label: 'Logs' },
|
|
];
|
|
|
|
function GeneralSettings() {
|
|
return (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
Backup Retention (days)
|
|
</label>
|
|
<input
|
|
type="number"
|
|
defaultValue={30}
|
|
className="w-full max-w-xs rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
Default Backup Strategy
|
|
</label>
|
|
<select
|
|
defaultValue="incremental"
|
|
className="w-full max-w-xs rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
>
|
|
<option value="full">Full</option>
|
|
<option value="incremental">Incremental</option>
|
|
<option value="differential">Differential</option>
|
|
</select>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<input
|
|
type="checkbox"
|
|
id="auto-cleanup"
|
|
defaultChecked
|
|
className="w-4 h-4 text-blue-600 rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="auto-cleanup" className="text-sm text-gray-700">
|
|
Enable automatic cleanup of old backups
|
|
</label>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function NotificationSettings() {
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center gap-3">
|
|
<input
|
|
type="checkbox"
|
|
id="email-notify"
|
|
defaultChecked
|
|
className="w-4 h-4 text-blue-600 rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="email-notify" className="text-sm text-gray-700">
|
|
Enable email notifications
|
|
</label>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
Email Address
|
|
</label>
|
|
<input
|
|
type="email"
|
|
placeholder="admin@example.com"
|
|
className="w-full max-w-md rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
/>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<input
|
|
type="checkbox"
|
|
id="notify-failures"
|
|
defaultChecked
|
|
className="w-4 h-4 text-blue-600 rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="notify-failures" className="text-sm text-gray-700">
|
|
Notify on backup failures only
|
|
</label>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function SecuritySettings() {
|
|
return (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
Encryption Key
|
|
</label>
|
|
<input
|
|
type="password"
|
|
placeholder="Enter encryption key"
|
|
className="w-full max-w-md rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
/>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<input
|
|
type="checkbox"
|
|
id="encrypt-backups"
|
|
className="w-4 h-4 text-blue-600 rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="encrypt-backups" className="text-sm text-gray-700">
|
|
Encrypt all backups
|
|
</label>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<input
|
|
type="checkbox"
|
|
id="require-auth"
|
|
defaultChecked
|
|
className="w-4 h-4 text-blue-600 rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="require-auth" className="text-sm text-gray-700">
|
|
Require authentication for API access
|
|
</label>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function LogSettings() {
|
|
return (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
Log Level
|
|
</label>
|
|
<select
|
|
defaultValue="info"
|
|
className="w-full max-w-xs rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
>
|
|
<option value="debug">Debug</option>
|
|
<option value="info">Info</option>
|
|
<option value="warn">Warning</option>
|
|
<option value="error">Error</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
Log Retention (days)
|
|
</label>
|
|
<input
|
|
type="number"
|
|
defaultValue={7}
|
|
className="w-full max-w-xs rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
/>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<input
|
|
type="checkbox"
|
|
id="verbose-logs"
|
|
className="w-4 h-4 text-blue-600 rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="verbose-logs" className="text-sm text-gray-700">
|
|
Enable verbose logging
|
|
</label>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function Settings() {
|
|
const [activeTab, setActiveTab] = useState('general');
|
|
|
|
const tabContent = {
|
|
general: <GeneralSettings />,
|
|
notifications: <NotificationSettings />,
|
|
security: <SecuritySettings />,
|
|
logs: <LogSettings />,
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<h2 className="text-2xl font-bold text-gray-900">Settings</h2>
|
|
|
|
<div className="bg-white rounded-lg border border-gray-200">
|
|
<div className="border-b border-gray-200">
|
|
<nav className="flex -mb-px">
|
|
{tabs.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => setActiveTab(tab.id)}
|
|
className={`px-6 py-3 text-sm font-medium border-b-2 transition-colors ${
|
|
activeTab === tab.id
|
|
? 'border-blue-500 text-blue-600'
|
|
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
|
}`}
|
|
>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
<div className="p-6">{tabContent[activeTab as keyof typeof tabContent]}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|