159 lines
7.0 KiB
TypeScript
159 lines
7.0 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { OrganizationConfig, SecurityConfig } 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 } from '@/components/ui/card';
|
|
import { Switch } from '@/components/ui/switch';
|
|
import { updateSystemSetting } from '@/lib/actions/settings';
|
|
import { toast } from 'sonner';
|
|
import { Building2, ShieldCheck, Mail, Globe } from 'lucide-react';
|
|
|
|
interface OrganizationSettingsProps {
|
|
orgConfig: OrganizationConfig;
|
|
securityConfig: SecurityConfig;
|
|
onUpdate: (section: 'organization' | 'security', data: any) => void;
|
|
}
|
|
|
|
export function OrganizationSettings({ orgConfig, securityConfig, onUpdate }: OrganizationSettingsProps) {
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
// Internal state for form usage
|
|
const [orgState, setOrgState] = useState(orgConfig);
|
|
const [secState, setSecState] = useState(securityConfig);
|
|
|
|
const handleSaveOrg = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const res = await updateSystemSetting('organization', orgState);
|
|
if (res.success) {
|
|
toast.success('Organization profile updated');
|
|
onUpdate('organization', res.updatedSettings.organization);
|
|
}
|
|
} catch (error) {
|
|
toast.error('Failed to update organization settings');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleSaveSecurity = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const res = await updateSystemSetting('security', secState);
|
|
if (res.success) {
|
|
toast.success('Security policy updated');
|
|
onUpdate('security', res.updatedSettings.security);
|
|
}
|
|
} catch (error) {
|
|
toast.error('Failed to update security settings');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Building2 className="h-5 w-5 text-primary" />
|
|
Organization Profile
|
|
</CardTitle>
|
|
<CardDescription>
|
|
General branding and contact details for the control center and emails.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label>Brand Name</Label>
|
|
<Input
|
|
value={orgState.brandName}
|
|
onChange={(e) => setOrgState({ ...orgState, brandName: e.target.value })}
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Support Email</Label>
|
|
<div className="relative">
|
|
<Mail className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
|
<Input
|
|
className="pl-9"
|
|
value={orgState.supportEmail}
|
|
onChange={(e) => setOrgState({ ...orgState, supportEmail: e.target.value })}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Legal Address</Label>
|
|
<Input
|
|
value={orgState.legalAddress}
|
|
onChange={(e) => setOrgState({ ...orgState, legalAddress: e.target.value })}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex justify-end mt-4">
|
|
<Button onClick={handleSaveOrg} disabled={loading}>
|
|
{loading ? 'Saving...' : 'Save Profile'}
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="border-orange-200 bg-orange-50/10">
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<ShieldCheck className="h-5 w-5 text-orange-600" />
|
|
Security Policy
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Enforce security rules for all Control Center staff members.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
<div className="flex items-center justify-between p-4 border rounded-lg bg-card">
|
|
<div className="space-y-0.5">
|
|
<Label className="text-base">Enforce 2FA</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Require Two-Factor Authentication for all admin accounts.
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={secState.enforce2FA}
|
|
onCheckedChange={(checked) => setSecState({ ...secState, enforce2FA: checked })}
|
|
/>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label>Session Timeout (Minutes)</Label>
|
|
<Input
|
|
type="number"
|
|
value={secState.sessionTimeoutMinutes}
|
|
onChange={(e) => setSecState({ ...secState, sessionTimeoutMinutes: parseInt(e.target.value) })}
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Password Expiration (Days)</Label>
|
|
<Input
|
|
type="number"
|
|
value={secState.passwordExpirationDays}
|
|
onChange={(e) => setSecState({ ...secState, passwordExpirationDays: parseInt(e.target.value) })}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex justify-end">
|
|
<Button variant="outline" onClick={handleSaveSecurity} disabled={loading} className="border-orange-200 text-orange-700 hover:bg-orange-50 hover:text-orange-800">
|
|
{loading ? 'Saving...' : 'Update Security Policy'}
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|