isitfake / FrontEnd /src /Settings.jsx
Jennigwen's picture
Feat : Update UI Mobile View
4c8f8a7
Raw
History Blame Contribute Delete
15.8 kB
import { useState, useEffect } from "react";
import { SettingsIcon, User, Sliders, LogOut, Lock } from "lucide-react"
import { useTranslation } from 'react-i18next';
import { useAuth } from './context/AuthContext';
import { useTheme } from './context/ThemeContext';
import { useNavigate } from 'react-router-dom';
function Toggle({ enabled, onToggle }) {
return (
<button
onClick={onToggle}
className={`relative inline-flex w-12 h-6 rounded-full transition-colors duration-200 cursor-pointer focus:outline-none
${enabled ? "bg-blue-500" : "bg-slate-600"}`}
>
<span
className={`absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full shadow transition-transform duration-200
${enabled ? "translate-x-6" : "translate-x-0"}`}
/>
</button>
);
}
export default function Settings() {
const { user, logout } = useAuth();
const navigate = useNavigate();
const { t, i18n } = useTranslation();
const [displayName, setDisplayName] = useState("");
const [email, setEmail] = useState("");
const [emailNotif, setEmailNotif] = useState(true);
const [desktopAlerts, setDesktopAlerts] = useState(false);
const [autoDelete, setAutoDelete] = useState(true);
const [apiKey] = useState("sk-••••••••••••••••••••••••");
const [twoFactor, setTwoFactor] = useState(false);
const [language, setLanguage] = useState(i18n.language || "en");
const [sensitivity, setSensitivity] = useState(75);
useEffect(() => {
i18n.changeLanguage(language);
}, [language, i18n]);
useEffect(() => {
const fetchProfile = async () => {
try {
const apiUrl = import.meta.env.VITE_API_URL || 'http://127.0.0.1:5000';
const token = localStorage.getItem('token');
const response = await fetch(`${apiUrl}/api/profile`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...(token ? { 'Authorization': `Bearer ${token}` } : {})
}
});
const result = await response.json();
if (result.status === 'success' && result.data?.user_aktif) {
setDisplayName(result.data.user_aktif.display_name);
setEmail(result.data.user_aktif.email);
}
} catch (error) {
console.error("Failed to fetch profile settings", error);
}
};
fetchProfile();
}, []);
return (
<div className="flex min-h-screen bg-transparent transition-colors duration-300">
{/* Main */}
<main className={`flex-1 flex flex-col gap-6 px-4 md:px-10 py-6 md:py-8 w-full max-w-[100vw] overflow-x-hidden relative ${!user ? 'overflow-y-hidden h-full max-h-[100vh]' : ''}`}>
{/* Unauthenticated Overlay */}
{!user && (
<div className="absolute inset-0 z-50 flex items-center justify-center bg-slate-50/50 dark:bg-[#0f1117]/60 backdrop-blur-md">
<div className="bg-white dark:bg-[#161b27] border border-slate-200 dark:border-[#1e2538] flex flex-col items-center text-center p-8 max-w-sm w-full mx-4 rounded-2xl shadow-xl">
<div className="w-16 h-16 bg-blue-100 dark:bg-blue-900/30 rounded-full flex items-center justify-center mb-5">
<Lock className="text-blue-600 dark:text-blue-400" size={28} />
</div>
<h2 className="text-xl font-bold text-slate-800 dark:text-white mb-2">{t('analysis.lockedTitle')}</h2>
<p className="text-slate-500 dark:text-slate-400 text-sm mb-6 leading-relaxed">
{t('analysis.lockedMessage')}
</p>
<button
onClick={() => navigate('/login')}
className="w-full bg-blue-600 hover:bg-blue-500 text-white font-semibold py-3 rounded-lg transition-colors cursor-pointer shadow-md"
>
{t('analysis.loginNow')}
</button>
</div>
</div>
)}
{/* Header */}
<header className="flex items-center gap-4 pb-4 border-b border-slate-200 dark:border-[#1e2538]">
<span className="text-cyan-500 dark:text-cyan-400 text-3xl"><SettingsIcon size={30}/></span>
<div>
<h1 className="text-lg lg:text-3xl font-bold text-slate-800 dark:text-slate-200">{t('settings.title')}</h1>
<p className="text-slate-500 dark:text-slate-500 text-sm mt-1">{t('settings.subtitle')}</p>
</div>
</header>
{/* User Block with Theme & Logout */}
<div className="flex flex-col bg-white dark:bg-[#161b27] rounded-2xl p-4 gap-4 border border-slate-200 dark:border-[#1e2538] shadow-sm">
<div className="flex items-center gap-4">
<div className="w-12 h-12 flex-shrink-0 rounded-full bg-blue-600 flex items-center justify-center font-bold text-lg text-white">
{user?.display_name ? user.display_name.charAt(0).toUpperCase() : 'AI'}
</div>
<div className="text-sm overflow-hidden flex-1">
<div className="font-semibold text-base truncate text-slate-800 dark:text-slate-200">{user?.display_name || t('app.adminName', 'Admin User')}</div>
<div className="text-xs text-slate-500 truncate capitalize">{user?.role || t('app.adminRole', 'Administrator')}</div>
</div>
</div>
<div className="mt-1 pt-4 border-t border-slate-200 dark:border-[#1e2538]">
<button
onClick={logout}
className="w-full flex items-center justify-center gap-2 p-2.5 rounded-lg text-red-600 dark:text-red-400 bg-red-50/50 dark:bg-red-900/10 hover:bg-red-50 dark:hover:bg-red-900/30 border border-transparent hover:border-red-100 dark:hover:border-red-900/30 transition-all cursor-pointer font-semibold shadow-sm"
title="Logout"
>
<LogOut size={18} strokeWidth={2.5} />
{t('app.logout', 'Logout')}
</button>
</div>
</div>
{/* Account Settings */}
<section className="bg-white dark:bg-[#161b27] border border-slate-200 dark:border-[#1e2538] rounded-2xl p-7 shadow-sm">
<div className="flex items-center gap-3 mb-6">
<span className="text-cyan-500 dark:text-cyan-400"><User size={24} /></span>
<h2 className="text-lg font-bold text-slate-800 dark:text-slate-200">{t('settings.accountSettings')}</h2>
</div>
<div className="flex flex-col gap-5">
<div>
<label className="block text-sm text-slate-500 dark:text-slate-400 mb-2">{t('settings.displayName')}</label>
<input
type="text"
value={displayName}
onChange={(e) => setDisplayName(e.target.value)}
className="w-full bg-slate-50 dark:bg-[#1e2538] border border-slate-200 dark:border-[#2d3748] rounded-lg px-4 py-3 text-sm text-slate-900 dark:text-slate-200 focus:outline-none focus:border-blue-500 transition-colors"
/>
</div>
<div>
<label className="block text-sm text-slate-500 dark:text-slate-400 mb-2">{t('settings.emailAddress')}</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full bg-slate-50 dark:bg-[#1e2538] border border-slate-200 dark:border-[#2d3748] rounded-lg px-4 py-3 text-sm text-slate-900 dark:text-slate-200 focus:outline-none focus:border-blue-500 transition-colors"
/>
</div>
<div className="flex justify-end">
<button className="bg-blue-600 hover:bg-blue-500 text-white text-sm font-semibold px-5 py-2.5 rounded-lg transition-colors cursor-pointer">
{t('settings.saveChanges')}
</button>
</div>
</div>
</section>
{/*/!* Notifications *!/*/}
{/*<section className="bg-white dark:bg-[#161b27] border border-slate-200 dark:border-[#1e2538] rounded-2xl p-7 shadow-sm">*/}
{/* <div className="flex items-center gap-3 mb-6">*/}
{/* <span className="text-cyan-500 dark:text-cyan-400 text-xl">🔔</span>*/}
{/* <h2 className="text-lg font-bold text-slate-800 dark:text-slate-200">{t('settings.notifications')}</h2>*/}
{/* </div>*/}
{/* <div className="flex flex-col gap-5">*/}
{/* <div className="flex items-center justify-between">*/}
{/* <div>*/}
{/* <div className="text-sm font-semibold text-slate-800 dark:text-slate-200">{t('settings.emailNotifications')}</div>*/}
{/* <div className="text-xs text-slate-500 mt-0.5">{t('settings.emailNotifDesc')}</div>*/}
{/* </div>*/}
{/* <Toggle enabled={emailNotif} onToggle={() => setEmailNotif(!emailNotif)} />*/}
{/* </div>*/}
{/* <div className="border-t border-slate-200 dark:border-[#1e2538]" />*/}
{/* <div className="flex items-center justify-between">*/}
{/* <div>*/}
{/* <div className="text-sm font-semibold text-slate-800 dark:text-slate-200">{t('settings.desktopAlerts')}</div>*/}
{/* <div className="text-xs text-slate-500 mt-0.5">{t('settings.desktopAlertsDesc')}</div>*/}
{/* </div>*/}
{/* <Toggle enabled={desktopAlerts} onToggle={() => setDesktopAlerts(!desktopAlerts)} />*/}
{/* </div>*/}
{/* </div>*/}
{/*</section>*/}
{/*/!* Security & Privacy *!/*/}
{/*<section className="bg-white dark:bg-[#161b27] border border-slate-200 dark:border-[#1e2538] rounded-2xl p-7 shadow-sm">*/}
{/* <div className="flex items-center gap-3 mb-6">*/}
{/* <span className="text-cyan-500 dark:text-cyan-400 text-xl">🛡</span>*/}
{/* <h2 className="text-lg font-bold text-slate-800 dark:text-slate-200">{t('settings.securityPrivacy')}</h2>*/}
{/* </div>*/}
{/* <div className="flex flex-col gap-5">*/}
{/* <div className="flex items-center justify-between">*/}
{/* <div>*/}
{/* <div className="text-sm font-semibold text-slate-800 dark:text-slate-200">{t('settings.autoDelete')}</div>*/}
{/* <div className="text-xs text-slate-500 mt-0.5">{t('settings.autoDeleteDesc')}</div>*/}
{/* </div>*/}
{/* <Toggle enabled={autoDelete} onToggle={() => setAutoDelete(!autoDelete)} />*/}
{/* </div>*/}
{/* <div className="border-t border-slate-200 dark:border-[#1e2538]" />*/}
{/* <div className="flex items-center justify-between">*/}
{/* <div>*/}
{/* <div className="text-sm font-semibold text-slate-800 dark:text-slate-200">{t('settings.twoFactor')}</div>*/}
{/* <div className="text-xs text-slate-500 mt-0.5">{t('settings.twoFactorDesc')}</div>*/}
{/* </div>*/}
{/* <Toggle enabled={twoFactor} onToggle={() => setTwoFactor(!twoFactor)} />*/}
{/* </div>*/}
{/* <div className="border-t border-slate-200 dark:border-[#1e2538]" />*/}
{/* <div>*/}
{/* <label className="block text-sm text-slate-500 dark:text-slate-400 mb-2">{t('settings.apiKey')}</label>*/}
{/* <div className="flex gap-3">*/}
{/* <input*/}
{/* type="text"*/}
{/* value={apiKey}*/}
{/* readOnly*/}
{/* className="flex-1 min-w-0 bg-slate-50 dark:bg-[#1e2538] border border-slate-200 dark:border-[#2d3748] rounded-lg px-4 py-3 text-sm text-slate-500 dark:text-slate-400 focus:outline-none"*/}
{/* />*/}
{/* <button className="shrink-0 bg-slate-100 dark:bg-[#1e2538] hover:bg-slate-200 dark:hover:bg-slate-700 border border-slate-200 dark:border-[#2d3748] text-slate-700 dark:text-slate-300 text-sm px-4 py-3 rounded-lg transition-colors cursor-pointer">*/}
{/* {t('settings.regenerate')}*/}
{/* </button>*/}
{/* </div>*/}
{/* </div>*/}
{/* <div className="border-t border-slate-200 dark:border-[#1e2538]" />*/}
{/* <div>*/}
{/* <button className="border border-red-200 dark:border-red-700 text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/30 text-sm font-semibold px-5 py-2.5 rounded-lg transition-colors cursor-pointer">*/}
{/* {t('settings.clearHistory')}*/}
{/* </button>*/}
{/* </div>*/}
{/* </div>*/}
{/*</section>*/}
{/* Preferences */}
<section className="bg-white dark:bg-[#161b27] border border-slate-200 dark:border-[#1e2538] rounded-2xl p-7 shadow-sm">
<div className="flex items-center gap-3 mb-6">
<span className="text-cyan-500 dark:text-cyan-400"><Sliders size={24} /></span>
<h2 className="text-lg font-bold text-slate-800 dark:text-slate-200">{t('settings.preferences')}</h2>
</div>
<div className="flex flex-col gap-5">
<div className="grid gap-5">
<label className="block text-sm text-slate-500 dark:text-slate-400 mb-2">{t('settings.language')}</label>
<div className="relative">
<select
value={language}
onChange={(e) => setLanguage(e.target.value)}
className="w-full bg-slate-50 dark:bg-[#1e2538] border border-slate-200 dark:border-[#2d3748] rounded-lg px-4 py-3 text-sm text-slate-900 dark:text-slate-200 focus:outline-none focus:border-blue-500 transition-colors cursor-pointer appearance-none"
>
<option value="en" className="text-slate-900 dark:text-slate-200 bg-white dark:bg-[#1e2538]">{t('settings.english')}</option>
<option value="id" className="text-slate-900 dark:text-slate-200 bg-white dark:bg-[#1e2538]">{t('settings.indonesian')}</option>
</select>
<div className="pointer-events-none absolute inset-y-0 right-4 flex items-center">
<svg className="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</div>
</div>
</div>
<div>
<label className="block text-sm text-slate-500 dark:text-slate-400 mb-2">
{t('settings.detectionSensitivity')} — <span className="text-blue-600 dark:text-blue-400 font-semibold">{sensitivity}%</span>
</label>
<input
type="range"
min={0}
max={100}
value={sensitivity}
onChange={(e) => setSensitivity(e.target.value)}
className="w-full accent-blue-500 cursor-pointer"
/>
<div className="flex justify-between text-xs text-slate-500 mt-1">
<span>{t('settings.low')}</span>
<span>{t('settings.high')}</span>
</div>
</div>
</div>
</section>
{/* Help button */}
<div className="flex justify-end pb-2">
<button className="w-8 h-8 rounded-full bg-slate-200 dark:bg-[#1e2538] text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-white hover:bg-slate-300 dark:hover:bg-slate-700 text-sm font-bold transition-colors cursor-pointer shadow-sm">
?
</button>
</div>
</main>
</div>
);
}