import React, { useState, useEffect, useCallback } from 'react';
import {
StyleSheet, View, Text, TouchableOpacity, ScrollView,
ActivityIndicator, Switch, StatusBar, Alert
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { supabase } from '../../lib/supabase';
import { COLORS, SHADOWS } from '../../styles/theme';
import {
Cpu, Database, ShieldCheck, Save, RotateCcw,
Clock, Bell, Inbox
} from 'lucide-react-native';
import { useNotification } from '../../components/NotificationProvider';
import * as Haptics from 'expo-haptics';
const StepperControl = ({ label, value, onValueChange, min = 0.1, max = 1.0, step = 0.05, suffix = '%' }) => {
const handleDecrement = () => {
if (value > min) {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
onValueChange(Math.max(min, parseFloat((value - step).toFixed(2))));
}
};
const handleIncrement = () => {
if (value < max) {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
onValueChange(Math.min(max, parseFloat((value + step).toFixed(2))));
}
};
const displayValue = suffix === '%' ? `${(value * 100).toFixed(0)}%` : value.toFixed(0);
return (
{label}
-
{displayValue}
= max && styles.disabledBtn]}
onPress={handleIncrement}
disabled={value >= max}
>
+
);
};
const AdminSettingsScreen = () => {
const { success, error: toastError } = useNotification();
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
// Settings states
const [confidenceThreshold, setConfidenceThreshold] = useState(0.80);
const [duplicateSensitivity, setDuplicateSensitivity] = useState(0.85);
const [enableAutoResolve, setEnableAutoResolve] = useState(false);
const [autoCloseEnabled, setAutoCloseEnabled] = useState(true);
const [autoCloseDays, setAutoCloseDays] = useState(7);
const [emailNotifications, setEmailNotifications] = useState(true);
const [adminAlerts, setAdminAlerts] = useState(true);
const [digestFrequency, setDigestFrequency] = useState('daily'); // 'daily' | 'weekly'
const fetchSettings = useCallback(async () => {
try {
const { data: { user } } = await supabase.auth.getUser();
if (!user) return;
const { data: profileData } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id)
.single();
if (profileData) {
setProfile(profileData);
const companyId = profileData.company_id;
if (companyId) {
// Fetch settings from system_settings
const { data: settings, error } = await supabase
.from('system_settings')
.select('*')
.eq('company_id', companyId)
.single();
if (!error && settings) {
setConfidenceThreshold(settings.ai_confidence_threshold ?? 0.80);
setDuplicateSensitivity(settings.duplicate_sensitivity ?? 0.85);
setEnableAutoResolve(settings.enable_auto_resolve ?? false);
setAutoCloseEnabled(settings.auto_close_enabled ?? true);
setAutoCloseDays(settings.auto_close_days ?? 7);
setEmailNotifications(settings.email_notifications ?? true);
setAdminAlerts(settings.admin_alerts ?? true);
setDigestFrequency(settings.digest_frequency ?? 'daily');
}
}
}
} catch (e) {
console.error('Fetch settings error:', e);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchSettings();
}, [fetchSettings]);
const handleSaveSettings = async () => {
const companyId = profile?.company_id;
if (!companyId) {
toastError("Link Error", "Your profile is not associated with any company.");
return;
}
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
setSaving(true);
try {
// Upsert into system_settings mapping to precise columns
const { error } = await supabase
.from('system_settings')
.upsert({
company_id: companyId,
ai_confidence_threshold: confidenceThreshold,
duplicate_sensitivity: duplicateSensitivity,
enable_auto_resolve: enableAutoResolve,
auto_close_enabled: autoCloseEnabled,
auto_close_days: autoCloseDays,
email_notifications: emailNotifications,
admin_alerts: adminAlerts,
digest_frequency: digestFrequency,
updated_at: new Date().toISOString()
});
if (error) {
console.error('Save settings error details:', error);
toastError("Save Failed", "Could not synchronize settings to database.");
} else {
success("Settings Saved", "AI thresholds and SLA guidelines synchronized successfully!");
}
} catch (e) {
console.error(e);
toastError("Error", "An unexpected error occurred during save.");
} finally {
setSaving(false);
}
};
const handleReset = () => {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
setConfidenceThreshold(0.80);
setDuplicateSensitivity(0.85);
setEnableAutoResolve(false);
setAutoCloseEnabled(true);
setAutoCloseDays(7);
setEmailNotifications(true);
setAdminAlerts(true);
setDigestFrequency('daily');
};
if (loading) {
return (
);
}
return (
{/* Header */}
System Settings
{/* Section 1: AI Engine Benchmarks */}
AI Neural Benchmarks
Configure standard confidence limits to bypass manual ticketing queues.
Tickets with category classification confidence above this threshold will bypass manual triage.
AI Auto-Resolution
Allow the system to automatically close tickets when a verified solution RAG score exceeds 95%.
{
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
setEnableAutoResolve(val);
}}
trackColor={{ false: '#e5e7eb', true: COLORS.primaryLight }}
thumbColor={enableAutoResolve ? COLORS.primary : '#9ca3af'}
/>
{/* Section 2: Duplication Parameters */}
Duplicate Mitigation
Adjust semantic limits to identify duplicate support inquiries automatically.
A higher percentage demands exact word matching, while a lower percentage flags conceptual duplicates.
{/* Section 3: Auto-Close Tickets */}
Auto-Close Settings
Configure standard procedures for resolving ticket inactivity.
Auto-Close Inactive
Automatically archive resolved tickets after a period of user inactivity.
{
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
setAutoCloseEnabled(val);
}}
trackColor={{ false: '#e5e7eb', true: COLORS.primaryLight }}
thumbColor={autoCloseEnabled ? COLORS.primary : '#9ca3af'}
/>
{autoCloseEnabled && (
<>
>
)}
{/* Section 4: Notifications and Digests */}
Notifications & Digests
Configure administrator alert guidelines and scheduled digest updates.
Email Notifications
Receive summary digests and incident reports via work email.
{
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
setEmailNotifications(val);
}}
trackColor={{ false: '#e5e7eb', true: COLORS.primaryLight }}
thumbColor={emailNotifications ? COLORS.primary : '#9ca3af'}
/>
Critical Admin Alerts
Enable push notifications for critical Priority 1 events.
{
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
setAdminAlerts(val);
}}
trackColor={{ false: '#e5e7eb', true: COLORS.primaryLight }}
thumbColor={adminAlerts ? COLORS.primary : '#9ca3af'}
/>
{emailNotifications && (
<>
Digest Frequency
{
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
setDigestFrequency('daily');
}}
>
Daily
{
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
setDigestFrequency('weekly');
}}
>
Weekly
>
)}
{/* Section 5: SLA Guidelines Summary */}
SLA Policies & Breach Targets
Configure escalation times based on priority categories.
Critical Priority SLA Breach target: 2 Hours
High Priority SLA Breach target: 8 Hours
Medium Priority SLA Breach target: 24 Hours
Low Priority SLA Breach target: 72 Hours
{/* Action buttons */}
Reset Defaults
{saving ? (
) : (
<>
Save Settings
>
)}
);
};
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: COLORS.background },
loadingWrap: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: COLORS.background },
header: { paddingHorizontal: 24, paddingTop: 16, paddingBottom: 16 },
title: { fontSize: 28, fontWeight: '900', color: COLORS.text, letterSpacing: -0.8 },
scrollContent: { paddingBottom: 120 },
// Section card
sectionCard: {
backgroundColor: '#fff',
borderRadius: 24,
marginHorizontal: 20,
padding: 20,
marginBottom: 16,
borderWidth: 1,
borderColor: 'rgba(0,0,0,0.03)',
...SHADOWS.soft,
},
sectionHeader: { flexDirection: 'row', alignItems: 'center', gap: 10, marginBottom: 8 },
sectionTitle: { fontSize: 16, fontWeight: '900', color: COLORS.text },
sectionDesc: { fontSize: 12.5, color: COLORS.textMuted, fontWeight: '600', lineHeight: 18, marginBottom: 16 },
// Controls list
controlsList: { gap: 12 },
settingControlRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
controlLabel: { fontSize: 14, fontWeight: '800', color: COLORS.text },
// Steppers
stepperContainer: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#f3f4f6', borderRadius: 12, padding: 4, height: 40 },
stepperBtn: { width: 32, height: 32, borderRadius: 8, backgroundColor: '#fff', justifyContent: 'center', alignItems: 'center', ...SHADOWS.soft },
disabledBtn: { opacity: 0.4 },
stepperBtnText: { fontSize: 16, fontWeight: '800', color: COLORS.text },
stepperValueContainer: { minWidth: 60, alignItems: 'center', justifyContent: 'center' },
stepperValueText: { fontSize: 13, fontWeight: '800', color: COLORS.text },
frequencyPicker: { flexDirection: 'row', backgroundColor: '#f3f4f6', padding: 3, borderRadius: 10 },
freqBtn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 8 },
activeFreqBtn: { backgroundColor: '#fff', ...SHADOWS.soft },
freqBtnText: { fontSize: 11, fontWeight: '700', color: COLORS.textMuted },
activeFreqBtnText: { color: COLORS.text, fontWeight: '900' },
helpText: { fontSize: 11.5, color: COLORS.textMuted, fontWeight: '500', lineHeight: 16, marginTop: 4 },
separator: { height: 1, backgroundColor: 'rgba(0,0,0,0.05)', marginVertical: 8 },
policyRow: { flexDirection: 'row', alignItems: 'center', gap: 10, marginVertical: 6, paddingHorizontal: 4 },
policyDot: { width: 8, height: 8, borderRadius: 4 },
policyText: { fontSize: 13, color: COLORS.textLight, fontWeight: '500' },
boldText: { fontWeight: '800', color: COLORS.text },
// Buttons
btnRow: { flexDirection: 'row', gap: 12, marginHorizontal: 20, marginTop: 8 },
resetBtn: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
backgroundColor: '#fff',
height: 52,
borderRadius: 18,
borderWidth: 1.5,
borderColor: 'rgba(0,0,0,0.08)',
...SHADOWS.soft
},
resetBtnText: { fontSize: 14, fontWeight: '800', color: COLORS.textLight },
saveBtn: {
flex: 1.5,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
backgroundColor: COLORS.primary,
height: 52,
borderRadius: 18,
...SHADOWS.medium,
shadowColor: COLORS.primary
},
saveBtnText: { fontSize: 14, fontWeight: '800', color: '#fff' }
});
export default AdminSettingsScreen;