Implement UI enhancements, change password settings, theme toggle, and custom grid size slider
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m19s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m19s
This commit is contained in:
parent
425598c278
commit
c3b1fe8de1
5 changed files with 226 additions and 26 deletions
|
|
@ -7,6 +7,7 @@ import api from '@/lib/api';
|
|||
|
||||
interface SettingsData {
|
||||
theme: string;
|
||||
grid_size: string;
|
||||
thumbnail_images: string;
|
||||
thumbnail_videos: string;
|
||||
max_versions: string;
|
||||
|
|
@ -20,6 +21,13 @@ export default function SettingsPage() {
|
|||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState(false);
|
||||
|
||||
const [oldPassword, setOldPassword] = useState('');
|
||||
const [newPassword, setNewPassword] = useState('');
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const [pwdSaving, setPwdSaving] = useState(false);
|
||||
const [pwdError, setPwdError] = useState<string | null>(null);
|
||||
const [pwdSuccess, setPwdSuccess] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadSettings();
|
||||
}, []);
|
||||
|
|
@ -43,6 +51,7 @@ export default function SettingsPage() {
|
|||
const s = data.settings || data;
|
||||
const loaded = {
|
||||
theme: s.theme || 'dark',
|
||||
grid_size: s.grid_size || '200',
|
||||
thumbnail_images: s.thumbnail_images || 'true',
|
||||
thumbnail_videos: s.thumbnail_videos || 'true',
|
||||
max_versions: s.max_versions || '1',
|
||||
|
|
@ -76,6 +85,33 @@ export default function SettingsPage() {
|
|||
}
|
||||
}
|
||||
|
||||
async function handlePasswordChange(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
if (newPassword !== confirmPassword) {
|
||||
setPwdError("New passwords do not match.");
|
||||
return;
|
||||
}
|
||||
if (newPassword.length < 8) {
|
||||
setPwdError("Password must be at least 8 characters.");
|
||||
return;
|
||||
}
|
||||
setPwdSaving(true);
|
||||
setPwdError(null);
|
||||
setPwdSuccess(false);
|
||||
try {
|
||||
await api.changePassword(oldPassword, newPassword);
|
||||
setPwdSuccess(true);
|
||||
setOldPassword('');
|
||||
setNewPassword('');
|
||||
setConfirmPassword('');
|
||||
setTimeout(() => setPwdSuccess(false), 3000);
|
||||
} catch (err: any) {
|
||||
setPwdError(err.message || 'Failed to change password');
|
||||
} finally {
|
||||
setPwdSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
|
|
@ -94,20 +130,74 @@ export default function SettingsPage() {
|
|||
content: (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--color-text-primary)' }}>Theme</label>
|
||||
<select
|
||||
value={settings.theme}
|
||||
onChange={(e) => setSettings({ ...settings, theme: e.target.value })}
|
||||
className="w-full max-w-xs px-3 py-2 rounded-lg text-sm outline-none"
|
||||
style={{ backgroundColor: 'var(--color-bg-primary)', border: '1px solid var(--color-border)', color: 'var(--color-text-primary)' }}
|
||||
>
|
||||
<option value="dark">Dark Mode</option>
|
||||
<option value="light">Light Mode</option>
|
||||
</select>
|
||||
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--color-text-primary)' }}>Grid Tile Size ({settings.grid_size}px)</label>
|
||||
<input
|
||||
type="range"
|
||||
min="120"
|
||||
max="250"
|
||||
step="10"
|
||||
value={settings.grid_size}
|
||||
onChange={(e) => setSettings({ ...settings, grid_size: e.target.value })}
|
||||
className="w-full max-w-xs accent-[var(--color-accent)]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'security',
|
||||
title: 'Security',
|
||||
icon: <Shield className="w-5 h-5" />,
|
||||
content: (
|
||||
<form onSubmit={handlePasswordChange} className="space-y-4 max-w-xs">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1" style={{ color: 'var(--color-text-primary)' }}>Current Password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={oldPassword}
|
||||
onChange={e => setOldPassword(e.target.value)}
|
||||
required
|
||||
className="w-full px-3 py-2 rounded-lg text-sm outline-none"
|
||||
style={{ backgroundColor: 'var(--color-bg-primary)', border: '1px solid var(--color-border)', color: 'var(--color-text-primary)' }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1" style={{ color: 'var(--color-text-primary)' }}>New Password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={newPassword}
|
||||
onChange={e => setNewPassword(e.target.value)}
|
||||
required
|
||||
minLength={8}
|
||||
className="w-full px-3 py-2 rounded-lg text-sm outline-none"
|
||||
style={{ backgroundColor: 'var(--color-bg-primary)', border: '1px solid var(--color-border)', color: 'var(--color-text-primary)' }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1" style={{ color: 'var(--color-text-primary)' }}>Confirm New Password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={confirmPassword}
|
||||
onChange={e => setConfirmPassword(e.target.value)}
|
||||
required
|
||||
minLength={8}
|
||||
className="w-full px-3 py-2 rounded-lg text-sm outline-none"
|
||||
style={{ backgroundColor: 'var(--color-bg-primary)', border: '1px solid var(--color-border)', color: 'var(--color-text-primary)' }}
|
||||
/>
|
||||
</div>
|
||||
{pwdError && <p className="text-sm" style={{ color: 'var(--color-error)' }}>{pwdError}</p>}
|
||||
{pwdSuccess && <p className="text-sm" style={{ color: 'var(--color-success)' }}>Password changed successfully!</p>}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={pwdSaving}
|
||||
className="px-4 py-2 rounded-lg text-sm font-medium text-white transition-opacity disabled:opacity-50"
|
||||
style={{ backgroundColor: 'var(--color-accent)' }}
|
||||
>
|
||||
{pwdSaving ? 'Saving...' : 'Change Password'}
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'thumbnails',
|
||||
title: 'Media & Thumbnails',
|
||||
|
|
|
|||
Reference in a new issue