feat(frontend): add Dashboard, Backups, and Settings pages
- 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
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user