template-saas-frontend-v2/src/pages/dashboard/SettingsPage.tsx
rckrdmrd eb95d0e276 Initial commit - Frontend de template-saas migrado desde monorepo
Migración desde workspace-v2/projects/template-saas/apps/frontend
Este repositorio es parte del estándar multi-repo v2

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 08:07:16 -06:00

199 lines
6.4 KiB
TypeScript

import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useAuthStore, useUIStore } from '@/stores';
import toast from 'react-hot-toast';
import { Loader2, User, Building, Moon, Sun, Monitor } from 'lucide-react';
interface ProfileFormData {
first_name: string;
last_name: string;
email: string;
}
export function SettingsPage() {
const { user } = useAuthStore();
const { theme, setTheme } = useUIStore();
const [isLoading, setIsLoading] = useState(false);
const {
register,
handleSubmit,
formState: { errors },
} = useForm<ProfileFormData>({
defaultValues: {
first_name: user?.first_name || '',
last_name: user?.last_name || '',
email: user?.email || '',
},
});
const onSubmit = async (_data: ProfileFormData) => {
try {
setIsLoading(true);
// API call would go here
await new Promise((resolve) => setTimeout(resolve, 1000));
toast.success('Profile updated successfully!');
} catch {
toast.error('Failed to update profile');
} finally {
setIsLoading(false);
}
};
const themes = [
{ value: 'light', label: 'Light', icon: Sun },
{ value: 'dark', label: 'Dark', icon: Moon },
{ value: 'system', label: 'System', icon: Monitor },
] as const;
return (
<div className="space-y-6 max-w-3xl">
<div>
<h1 className="text-2xl font-bold text-secondary-900 dark:text-white">
Settings
</h1>
<p className="text-secondary-600 dark:text-secondary-400 mt-1">
Manage your account settings and preferences
</p>
</div>
{/* Profile Settings */}
<div className="card">
<div className="card-header flex items-center gap-3">
<User className="w-5 h-5 text-secondary-500" />
<h2 className="text-lg font-semibold text-secondary-900 dark:text-white">
Profile Information
</h2>
</div>
<div className="card-body">
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label htmlFor="first_name" className="label">
First name
</label>
<input
id="first_name"
type="text"
className="input"
{...register('first_name', { required: 'First name is required' })}
/>
{errors.first_name && (
<p className="mt-1 text-sm text-red-500">{errors.first_name.message}</p>
)}
</div>
<div>
<label htmlFor="last_name" className="label">
Last name
</label>
<input
id="last_name"
type="text"
className="input"
{...register('last_name', { required: 'Last name is required' })}
/>
{errors.last_name && (
<p className="mt-1 text-sm text-red-500">{errors.last_name.message}</p>
)}
</div>
</div>
<div>
<label htmlFor="email" className="label">
Email address
</label>
<input
id="email"
type="email"
className="input"
disabled
{...register('email')}
/>
<p className="mt-1 text-sm text-secondary-500">
Contact support to change your email address
</p>
</div>
<div className="pt-4">
<button type="submit" disabled={isLoading} className="btn-primary">
{isLoading ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Saving...
</>
) : (
'Save changes'
)}
</button>
</div>
</form>
</div>
</div>
{/* Organization Settings */}
<div className="card">
<div className="card-header flex items-center gap-3">
<Building className="w-5 h-5 text-secondary-500" />
<h2 className="text-lg font-semibold text-secondary-900 dark:text-white">
Organization
</h2>
</div>
<div className="card-body">
<div className="flex items-center justify-between p-4 bg-secondary-50 dark:bg-secondary-700/50 rounded-lg">
<div>
<p className="font-medium text-secondary-900 dark:text-white">
Tenant ID
</p>
<p className="text-sm text-secondary-500 dark:text-secondary-400 font-mono">
{user?.tenant_id || 'N/A'}
</p>
</div>
</div>
</div>
</div>
{/* Appearance Settings */}
<div className="card">
<div className="card-header">
<h2 className="text-lg font-semibold text-secondary-900 dark:text-white">
Appearance
</h2>
</div>
<div className="card-body">
<div className="grid grid-cols-3 gap-4">
{themes.map((t) => (
<button
key={t.value}
onClick={() => setTheme(t.value)}
className={`flex flex-col items-center gap-2 p-4 rounded-lg border-2 transition-colors ${
theme === t.value
? 'border-primary-500 bg-primary-50 dark:bg-primary-900/20'
: 'border-secondary-200 dark:border-secondary-700 hover:border-secondary-300 dark:hover:border-secondary-600'
}`}
>
<t.icon
className={`w-6 h-6 ${
theme === t.value
? 'text-primary-600 dark:text-primary-400'
: 'text-secondary-500'
}`}
/>
<span
className={`text-sm font-medium ${
theme === t.value
? 'text-primary-600 dark:text-primary-400'
: 'text-secondary-700 dark:text-secondary-300'
}`}
>
{t.label}
</span>
</button>
))}
</div>
</div>
</div>
</div>
);
}