meatloaf-config/src/app/components/GeneralPage.tsx

205 lines
7.9 KiB
TypeScript

import { useEffect, useState } from 'react';
import { getFileContents } from '../webdav';
interface GeneralPageProps {
config: any;
setConfig: (config: any) => void;
}
export default function GeneralPage({ config, setConfig }: GeneralPageProps) {
const [timezones, setTimezones] = useState<string[] | null>(null);
const [tzError, setTzError] = useState(false);
useEffect(() => {
getFileContents('/.sys/timezones.json')
.then(async (blob) => {
const parsed = JSON.parse(await blob.text());
if (Array.isArray(parsed)) {
setTimezones(parsed.map((e: any) => (typeof e === 'string' ? e : String(e.name ?? e.value ?? e))));
} else if (parsed && typeof parsed === 'object') {
setTimezones(Object.keys(parsed));
} else {
setTzError(true);
}
})
.catch(() => setTzError(true));
}, []);
const updateSetting = (path: string[], value: any) => {
const newConfig = JSON.parse(JSON.stringify(config));
let current = newConfig;
for (let i = 0; i < path.length - 1; i++) {
current = current[path[i]];
}
current[path[path.length - 1]] = value;
setConfig(newConfig);
};
const general = config.general || {};
return (
<div className="p-4 space-y-4">
<h2 className="text-sm text-neutral-500">General Settings</h2>
<div className="bg-white border border-neutral-200 rounded-lg divide-y divide-neutral-200">
<div className="p-4">
<label className="text-sm text-neutral-500 block mb-2">Appearance</label>
<select
value={general.appearance?.split('|')[0] || 'auto'}
onChange={(e) => updateSetting(['general', 'appearance'], e.target.value)}
className="w-full px-3 py-2 border border-neutral-300 rounded-lg"
>
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="auto">Auto</option>
</select>
</div>
<div className="p-4">
<label className="text-sm text-neutral-500 block mb-2">Language</label>
<select
value={general.language?.split('|')[0] || 'en'}
onChange={(e) => updateSetting(['general', 'language'], e.target.value)}
className="w-full px-3 py-2 border border-neutral-300 rounded-lg"
>
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="de">German</option>
</select>
</div>
<div className="p-4">
<label className="text-sm text-neutral-500 block mb-2">Timezone</label>
{!tzError && timezones ? (
<select
value={general.timezone || ''}
onChange={(e) => updateSetting(['general', 'timezone'], e.target.value)}
className="w-full px-3 py-2 border border-neutral-300 rounded-lg"
>
{general.timezone && !timezones.includes(general.timezone) && (
<option value={general.timezone}>{general.timezone}</option>
)}
{timezones.map((tz) => (
<option key={tz} value={tz}>{tz}</option>
))}
</select>
) : (
<input
type="text"
value={general.timezone || ''}
onChange={(e) => updateSetting(['general', 'timezone'], e.target.value)}
placeholder={timezones === null && !tzError ? 'Loading…' : 'America/Los_Angeles'}
disabled={timezones === null && !tzError}
className="w-full px-3 py-2 border border-neutral-300 rounded-lg disabled:opacity-50"
/>
)}
</div>
<div className="p-4">
<label className="text-sm text-neutral-500 block mb-2">Country</label>
<input
type="text"
value={general.country || ''}
onChange={(e) => updateSetting(['general', 'country'], e.target.value)}
placeholder="US"
className="w-full px-3 py-2 border border-neutral-300 rounded-lg"
/>
</div>
<div className="p-4">
<label className="text-sm text-neutral-500 block mb-2">Device Name</label>
<input
type="text"
value={general.devicename || ''}
onChange={(e) => updateSetting(['general', 'devicename'], e.target.value)}
className="w-full px-3 py-2 border border-neutral-300 rounded-lg"
/>
</div>
<div className="p-4 flex items-center justify-between">
<label className="text-sm text-neutral-500">Rotation Sounds</label>
<button
onClick={() => updateSetting(['general', 'rotationsounds'], general.rotationsounds ? 0 : 1)}
className={`relative w-12 h-6 rounded-full transition-colors ${
general.rotationsounds ? 'bg-blue-600' : 'bg-neutral-300'
}`}
>
<div
className={`absolute top-0.5 w-5 h-5 bg-white rounded-full transition-transform ${
general.rotationsounds ? 'translate-x-6' : 'translate-x-0.5'
}`}
/>
</button>
</div>
<div className="p-4 flex items-center justify-between">
<label className="text-sm text-neutral-500">Config Enabled</label>
<button
onClick={() => updateSetting(['general', 'configenabled'], general.configenabled ? 0 : 1)}
className={`relative w-12 h-6 rounded-full transition-colors ${
general.configenabled ? 'bg-blue-600' : 'bg-neutral-300'
}`}
>
<div
className={`absolute top-0.5 w-5 h-5 bg-white rounded-full transition-transform ${
general.configenabled ? 'translate-x-6' : 'translate-x-0.5'
}`}
/>
</button>
</div>
<div className="p-4 flex items-center justify-between">
<label className="text-sm text-neutral-500">Status Wait Enabled</label>
<button
onClick={() => updateSetting(['general', 'status_wait_enabled'], general.status_wait_enabled ? 0 : 1)}
className={`relative w-12 h-6 rounded-full transition-colors ${
general.status_wait_enabled ? 'bg-blue-600' : 'bg-neutral-300'
}`}
>
<div
className={`absolute top-0.5 w-5 h-5 bg-white rounded-full transition-transform ${
general.status_wait_enabled ? 'translate-x-6' : 'translate-x-0.5'
}`}
/>
</button>
</div>
<div className="p-4 flex items-center justify-between">
<label className="text-sm text-neutral-500">Encrypt Passphrase</label>
<button
onClick={() => updateSetting(['general', 'encrypt_passphrase'], general.encrypt_passphrase ? 0 : 1)}
className={`relative w-12 h-6 rounded-full transition-colors ${
general.encrypt_passphrase ? 'bg-blue-600' : 'bg-neutral-300'
}`}
>
<div
className={`absolute top-0.5 w-5 h-5 bg-white rounded-full transition-transform ${
general.encrypt_passphrase ? 'translate-x-6' : 'translate-x-0.5'
}`}
/>
</button>
</div>
<div className="p-4 flex items-center justify-between">
<label className="text-sm text-neutral-500">Reset with Host</label>
<button
onClick={() => updateSetting(['general', 'reset_with_host'], general.reset_with_host ? 0 : 1)}
className={`relative w-12 h-6 rounded-full transition-colors ${
general.reset_with_host ? 'bg-blue-600' : 'bg-neutral-300'
}`}
>
<div
className={`absolute top-0.5 w-5 h-5 bg-white rounded-full transition-transform ${
general.reset_with_host ? 'translate-x-6' : 'translate-x-0.5'
}`}
/>
</button>
</div>
</div>
</div>
);
}