SoftTraderFrontend/components/pages/SettingsPage.tsx
2026-03-20 11:10:13 -04:00

322 lines
11 KiB
TypeScript

'use client'
import { useState, useEffect } from 'react'
import { Moon, Volume2, Sun, Clock, MapPin, Thermometer } from 'lucide-react'
import {
getCurrentWeather,
setWeatherLocation,
setLocationHint,
getTempUnit,
setTempUnit,
getTimeFormat,
setTimeFormat,
type CurrentWeather,
type TempUnit,
type TimeFormat,
} from '@/lib/weatherApi'
export function SettingsPage() {
const [isDark, setIsDark] = useState(false)
const [volume, setVolume] = useState(70)
const [brightness, setBrightness] = useState(80)
const [timeout, setTimeout_] = useState(5)
const [mounted, setMounted] = useState(false)
// Weather location state
const [currentWeather, setCurrentWeather] = useState<CurrentWeather | null>(null)
const [weatherLoading, setWeatherLoading] = useState(true)
const [cityInput, setCityInput] = useState('')
const [savingCity, setSavingCity] = useState(false)
const [cityError, setCityError] = useState<string | null>(null)
const [citySuccess, setCitySuccess] = useState(false)
// Temperature unit state
const [tempUnit, setTempUnitState] = useState<TempUnit>('F')
// Time format state
const [timeFormatState, setTimeFormatState] = useState<TimeFormat>('12h')
useEffect(() => {
setMounted(true)
const isDarkMode = document.documentElement.classList.contains('dark')
setIsDark(isDarkMode)
// Load temperature unit preference
setTempUnitState(getTempUnit())
// Load time format preference
setTimeFormatState(getTimeFormat())
// Fetch current weather to show current city
getCurrentWeather()
.then((data) => setCurrentWeather(data))
.catch(() => {})
.finally(() => setWeatherLoading(false))
}, [])
const handleTempUnitChange = (unit: TempUnit) => {
setTempUnitState(unit)
setTempUnit(unit)
}
const handleTimeFormatChange = (format: TimeFormat) => {
setTimeFormatState(format)
setTimeFormat(format)
}
const toggleTheme = () => {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark')
localStorage.setItem('theme', 'light')
setIsDark(false)
} else {
document.documentElement.classList.add('dark')
localStorage.setItem('theme', 'dark')
setIsDark(true)
}
}
const handleCitySubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!cityInput.trim()) return
setSavingCity(true)
setCityError(null)
setCitySuccess(false)
try {
await setWeatherLocation(cityInput.trim())
setLocationHint(true)
const data = await getCurrentWeather()
setCurrentWeather(data)
setCityInput('')
setCitySuccess(true)
// Clear success message after 3 seconds
setTimeout(() => setCitySuccess(false), 3000)
} catch (err) {
setCityError(err instanceof Error ? err.message : 'Failed to update location')
} finally {
setSavingCity(false)
}
}
if (!mounted) return null
return (
<div className="h-full flex flex-col">
{/* Header */}
<div className="bg-background border-b border-border px-6 py-4">
<h1 className="text-2xl font-bold">Settings</h1>
</div>
{/* Settings Content */}
<div className="flex-1 overflow-y-auto p-6">
<div className="space-y-6 max-w-md">
{/* Dark Mode Toggle */}
<div className="border border-border bg-card p-4 space-y-3">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
{isDark ? <Moon size={20} /> : <Sun size={20} />}
<div>
<div className="font-medium">Dark Mode</div>
<div className="text-xs text-muted-foreground">
{isDark ? 'Enabled' : 'Disabled'}
</div>
</div>
</div>
<button
onClick={toggleTheme}
className={`
w-12 h-6 rounded-full transition-colors flex items-center px-1
${isDark ? 'bg-primary' : 'bg-muted'}
`}
>
<div
className={`
w-4 h-4 bg-white rounded-full transition-transform
${isDark ? 'translate-x-6' : 'translate-x-0'}
`}
/>
</button>
</div>
</div>
{/* Volume Control */}
<div className="border border-border bg-card p-4 space-y-3">
<div className="flex items-center gap-3">
<Volume2 size={20} />
<div className="flex-1">
<div className="font-medium">Volume</div>
<div className="text-xs text-muted-foreground">
{volume}%
</div>
</div>
</div>
<input
type="range"
min="0"
max="100"
value={volume}
onChange={(e) => setVolume(Number(e.target.value))}
className="w-full h-2 bg-muted cursor-pointer"
/>
</div>
{/* Brightness Control */}
<div className="border border-border bg-card p-4 space-y-3">
<div className="flex items-center gap-3">
<Sun size={20} />
<div className="flex-1">
<div className="font-medium">Brightness</div>
<div className="text-xs text-muted-foreground">
{brightness}%
</div>
</div>
</div>
<input
type="range"
min="0"
max="100"
value={brightness}
onChange={(e) => setBrightness(Number(e.target.value))}
className="w-full h-2 bg-muted cursor-pointer"
/>
</div>
{/* Timeout Control */}
<div className="border border-border bg-card p-4 space-y-3">
<div className="flex items-center gap-3">
<Clock size={20} />
<div className="flex-1">
<div className="font-medium">Auto-Lock Timeout</div>
<div className="text-xs text-muted-foreground">
{timeout} minutes
</div>
</div>
</div>
<select
value={timeout}
onChange={(e) => setTimeout_(Number(e.target.value))}
className="w-full border border-border bg-input text-foreground p-2"
>
<option value={1}>1 minute</option>
<option value={3}>3 minutes</option>
<option value={5}>5 minutes</option>
<option value={10}>10 minutes</option>
<option value={15}>15 minutes</option>
<option value={30}>30 minutes</option>
</select>
</div>
{/* Weather Location */}
<div className="border border-border bg-card p-4 space-y-3">
<div className="flex items-center gap-3">
<MapPin size={20} />
<div className="flex-1">
<div className="font-medium">Weather Location</div>
<div className="text-xs text-muted-foreground">
{weatherLoading
? 'Loading...'
: currentWeather?.city || 'Not set'}
</div>
</div>
</div>
<form onSubmit={handleCitySubmit} className="flex gap-2">
<input
type="text"
value={cityInput}
onChange={(e) => setCityInput(e.target.value)}
placeholder="Enter new city"
disabled={savingCity}
className="flex-1 border border-border bg-input text-foreground text-sm px-3 py-1.5"
/>
<button
type="submit"
disabled={savingCity || !cityInput.trim()}
className="bg-primary text-primary-foreground text-sm px-3 py-1.5 disabled:opacity-50"
>
{savingCity ? 'Saving...' : 'Update'}
</button>
</form>
{cityError && (
<div className="text-xs text-negative">{cityError}</div>
)}
{citySuccess && (
<div className="text-xs text-positive">Location updated successfully</div>
)}
</div>
{/* Temperature Unit */}
<div className="border border-border bg-card p-4 space-y-3">
<div className="flex items-center gap-3">
<Thermometer size={20} />
<div className="flex-1">
<div className="font-medium">Temperature Unit</div>
<div className="text-xs text-muted-foreground">
{tempUnit === 'C' ? 'Celsius' : 'Fahrenheit'}
</div>
</div>
</div>
<div className="flex gap-2">
<button
onClick={() => handleTempUnitChange('F')}
className={`flex-1 py-2 text-sm font-medium transition-colors ${
tempUnit === 'F'
? 'bg-primary text-primary-foreground'
: 'bg-muted text-muted-foreground hover:bg-muted/80'
}`}
>
Fahrenheit (°F)
</button>
<button
onClick={() => handleTempUnitChange('C')}
className={`flex-1 py-2 text-sm font-medium transition-colors ${
tempUnit === 'C'
? 'bg-primary text-primary-foreground'
: 'bg-muted text-muted-foreground hover:bg-muted/80'
}`}
>
Celsius (°C)
</button>
</div>
</div>
{/* Time Format */}
<div className="border border-border bg-card p-4 space-y-3">
<div className="flex items-center gap-3">
<Clock size={20} />
<div className="flex-1">
<div className="font-medium">Time Format</div>
<div className="text-xs text-muted-foreground">
{timeFormatState === '24h' ? '24-hour' : '12-hour'}
</div>
</div>
</div>
<div className="flex gap-2">
<button
onClick={() => handleTimeFormatChange('12h')}
className={`flex-1 py-2 text-sm font-medium transition-colors ${
timeFormatState === '12h'
? 'bg-primary text-primary-foreground'
: 'bg-muted text-muted-foreground hover:bg-muted/80'
}`}
>
12-hour
</button>
<button
onClick={() => handleTimeFormatChange('24h')}
className={`flex-1 py-2 text-sm font-medium transition-colors ${
timeFormatState === '24h'
? 'bg-primary text-primary-foreground'
: 'bg-muted text-muted-foreground hover:bg-muted/80'
}`}
>
24-hour
</button>
</div>
</div>
</div>
</div>
</div>
)
}