- Added main application entry point (app.jsx) and bootstrap file for React setup. - Created Wiki index page (Index.jsx) for documentation and file management. - Implemented utility functions for formatting and status handling in utils.js. - Set up Blade template (app.blade.php) for Inertia.js integration. - Defined API routes for metrics and terminal access in api.php. - Established console commands for scheduled tasks in console.php. - Created web routes for various application features including authentication, hypervisors, containers, cloud accounts, security, backups, and wiki management in web.php. - Developed setup scripts for Windows (setup.ps1) and Linux (setup.sh) environments. - Configured Tailwind CSS for styling with custom themes and animations in tailwind.config.js. - Set up Vite for asset bundling and development server in vite.config.js.
116 lines
6.0 KiB
JavaScript
116 lines
6.0 KiB
JavaScript
import AppLayout from '@/Layouts/AppLayout';
|
|
import { Head, useForm } from '@inertiajs/react';
|
|
import { GlassCard } from '@/Components/ui/GlassCard';
|
|
import { Button } from '@/Components/ui/Button';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
function Field({ label, desc, error, children }) {
|
|
return (
|
|
<div className="flex items-start justify-between gap-8 py-4 border-b border-white/[0.06] last:border-0">
|
|
<div className="flex-1">
|
|
<p className="text-sm font-medium text-white">{label}</p>
|
|
{desc && <p className="text-xs text-white/40 mt-0.5">{desc}</p>}
|
|
{error && <p className="text-xs text-red-400 mt-1">{error}</p>}
|
|
</div>
|
|
<div className="w-72 shrink-0">{children}</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Input({ className, ...props }) {
|
|
return (
|
|
<input className={cn(
|
|
'w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-sm text-white placeholder:text-white/20',
|
|
'focus:outline-none focus:border-cyan-500/60 transition-all',
|
|
className
|
|
)} {...props} />
|
|
);
|
|
}
|
|
|
|
function Select({ className, children, ...props }) {
|
|
return (
|
|
<select className={cn(
|
|
'w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-sm text-white',
|
|
'focus:outline-none focus:border-cyan-500/60 transition-all',
|
|
'[&>option]:bg-[#0f1f35]',
|
|
className
|
|
)} {...props}>{children}</select>
|
|
);
|
|
}
|
|
|
|
export default function AdminSettings({ settings }) {
|
|
const { data, setData, put, processing, errors } = useForm({
|
|
'app.name': settings?.['app.name'] ?? 'Infra Manager',
|
|
'app.theme': settings?.['app.theme'] ?? 'dark',
|
|
'security.session_timeout': settings?.['security.session_timeout'] ?? 480,
|
|
'security.mfa_required': settings?.['security.mfa_required'] ?? false,
|
|
'mail.driver': settings?.['mail.driver'] ?? 'log',
|
|
'mail.from': settings?.['mail.from'] ?? '',
|
|
});
|
|
|
|
return (
|
|
<AppLayout title="Settings">
|
|
<Head title="Settings" />
|
|
<div className="max-w-3xl space-y-5">
|
|
<div>
|
|
<h2 className="text-xl font-bold text-white">General Settings</h2>
|
|
<p className="text-sm text-white/40">Application-wide configuration</p>
|
|
</div>
|
|
|
|
<form onSubmit={e => { e.preventDefault(); put('/admin/settings'); }}>
|
|
<div className="space-y-4">
|
|
{/* App */}
|
|
<GlassCard className="p-5">
|
|
<h3 className="text-sm font-semibold text-white/60 uppercase tracking-wider mb-1">Application</h3>
|
|
<Field label="App Name" desc="Displayed in the browser tab and sidebar">
|
|
<Input value={data['app.name']} onChange={e => setData('app.name', e.target.value)} />
|
|
</Field>
|
|
<Field label="Theme" desc="Interface color scheme">
|
|
<Select value={data['app.theme']} onChange={e => setData('app.theme', e.target.value)}>
|
|
<option value="dark">Dark (Default)</option>
|
|
<option value="darker">Darker</option>
|
|
</Select>
|
|
</Field>
|
|
</GlassCard>
|
|
|
|
{/* Security */}
|
|
<GlassCard className="p-5">
|
|
<h3 className="text-sm font-semibold text-white/60 uppercase tracking-wider mb-1">Security</h3>
|
|
<Field label="Session Timeout" desc="Minutes before inactive users are logged out">
|
|
<Input type="number" value={data['security.session_timeout']} onChange={e => setData('security.session_timeout', e.target.value)} />
|
|
</Field>
|
|
<Field label="Require MFA" desc="Force MFA for all users">
|
|
<label className="flex items-center gap-3 cursor-pointer">
|
|
<input type="checkbox" checked={data['security.mfa_required']}
|
|
onChange={e => setData('security.mfa_required', e.target.checked)}
|
|
className="w-4 h-4 rounded border-white/20 bg-white/5 text-cyan-500 focus:ring-0" />
|
|
<span className="text-sm text-white/70">Enabled</span>
|
|
</label>
|
|
</Field>
|
|
</GlassCard>
|
|
|
|
{/* Mail */}
|
|
<GlassCard className="p-5">
|
|
<h3 className="text-sm font-semibold text-white/60 uppercase tracking-wider mb-1">Email / Notifications</h3>
|
|
<Field label="Mail Driver">
|
|
<Select value={data['mail.driver']} onChange={e => setData('mail.driver', e.target.value)}>
|
|
<option value="log">Log (dev only)</option>
|
|
<option value="smtp">SMTP</option>
|
|
<option value="sendmail">Sendmail</option>
|
|
</Select>
|
|
</Field>
|
|
<Field label="From Address" desc="Default sender email for notifications">
|
|
<Input type="email" value={data['mail.from']} onChange={e => setData('mail.from', e.target.value)} placeholder="noreply@example.com" />
|
|
</Field>
|
|
</GlassCard>
|
|
|
|
<div className="flex justify-end">
|
|
<Button type="submit" loading={processing}>Save Settings</Button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</AppLayout>
|
|
);
|
|
}
|