Fix some UI issues 2
This commit is contained in:
@@ -2,6 +2,8 @@ import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Button, Input, Card, Select, Avatar, Modal } from '../../ui';
|
||||
import { useAuthUser } from '../../../stores/auth.store';
|
||||
import { useToast } from '../../../hooks/ui/useToast';
|
||||
import { useUpdateProfile, useChangePassword, useAuthProfile } from '../../../api/hooks/auth';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface ProfileSettingsProps {
|
||||
onSuccess?: () => void;
|
||||
@@ -16,7 +18,7 @@ interface ProfileFormData {
|
||||
phone: string;
|
||||
language: string;
|
||||
timezone: string;
|
||||
avatar_url?: string;
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,16 +50,39 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
|
||||
const [activeTab, setActiveTab] = useState<'profile' | 'security' | 'preferences' | 'notifications'>(initialTab);
|
||||
|
||||
// Mock data for profile
|
||||
const updateProfileMutation = useUpdateProfile();
|
||||
const changePasswordMutation = useChangePassword();
|
||||
const { data: userProfile, isLoading: profileLoading } = useAuthProfile();
|
||||
|
||||
const [profileData, setProfileData] = useState<ProfileFormData>({
|
||||
first_name: 'María',
|
||||
last_name: 'González Pérez',
|
||||
email: 'admin@bakery.com',
|
||||
phone: '+34 612 345 678',
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
language: 'es',
|
||||
timezone: 'Europe/Madrid',
|
||||
avatar_url: 'https://images.unsplash.com/photo-1494790108755-2616b612b372?w=150&h=150&fit=crop&crop=face'
|
||||
avatar: '' // Using empty string to allow Avatar to show default bakery-themed icon
|
||||
});
|
||||
|
||||
const originalProfileDataRef = useRef<ProfileFormData | null>(null);
|
||||
|
||||
// Load profile data from API when available
|
||||
useEffect(() => {
|
||||
if (userProfile) {
|
||||
const profileFormData: ProfileFormData = {
|
||||
first_name: userProfile.full_name?.split(' ')[0] || '',
|
||||
last_name: userProfile.full_name?.split(' ').slice(1).join(' ') || '',
|
||||
email: userProfile.email || '',
|
||||
phone: userProfile.phone || '',
|
||||
language: userProfile.language || 'es',
|
||||
timezone: userProfile.timezone || 'Europe/Madrid',
|
||||
avatar: userProfile.avatar || ''
|
||||
};
|
||||
|
||||
setProfileData(profileFormData);
|
||||
originalProfileDataRef.current = profileFormData; // Store original data for reset
|
||||
}
|
||||
}, [userProfile]);
|
||||
|
||||
const [notificationSettings, setNotificationSettings] = useState<NotificationSettings>({
|
||||
email_notifications: true,
|
||||
@@ -106,25 +131,7 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
{ value: 'Europe/Rome', label: 'Roma (CET/CEST)' }
|
||||
];
|
||||
|
||||
// Mock update profile function
|
||||
const updateProfile = async (data: any): Promise<boolean> => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// Simulate API delay
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
|
||||
// Simulate successful update
|
||||
console.log('Profile updated:', data);
|
||||
setIsLoading(false);
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError('Error updating profile');
|
||||
setIsLoading(false);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Profile picture upload handler
|
||||
const handleImageUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -165,7 +172,7 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const newImageUrl = URL.createObjectURL(file); // Temporary URL
|
||||
setProfileData(prev => ({ ...prev, avatar_url: newImageUrl }));
|
||||
setProfileData(prev => ({ ...prev, avatar: newImageUrl }));
|
||||
setHasChanges(prev => ({ ...prev, profile: true }));
|
||||
|
||||
showToast({
|
||||
@@ -186,7 +193,7 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
};
|
||||
|
||||
const handleRemoveImage = () => {
|
||||
setProfileData(prev => ({ ...prev, avatar_url: '' }));
|
||||
setProfileData(prev => ({ ...prev, avatar: '' }));
|
||||
setHasChanges(prev => ({ ...prev, profile: true }));
|
||||
setShowDeleteConfirm(false);
|
||||
};
|
||||
@@ -268,27 +275,26 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
}
|
||||
|
||||
try {
|
||||
const success = await updateProfile(profileData);
|
||||
if (success) {
|
||||
setHasChanges(false);
|
||||
showToast({
|
||||
type: 'success',
|
||||
title: 'Perfil actualizado',
|
||||
message: 'Tu información ha sido guardada correctamente'
|
||||
});
|
||||
onSuccess?.();
|
||||
} else {
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Error al actualizar',
|
||||
message: error || 'No se pudo actualizar tu perfil'
|
||||
});
|
||||
}
|
||||
await updateProfileMutation.mutateAsync({
|
||||
full_name: `${profileData.first_name} ${profileData.last_name}`.trim() || profileData.first_name,
|
||||
email: profileData.email,
|
||||
phone: profileData.phone,
|
||||
language: profileData.language,
|
||||
timezone: profileData.timezone,
|
||||
});
|
||||
|
||||
setHasChanges(false);
|
||||
showToast({
|
||||
type: 'success',
|
||||
title: 'Perfil actualizado',
|
||||
message: 'Tu información ha sido guardada correctamente'
|
||||
});
|
||||
onSuccess?.();
|
||||
} catch (err) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Error de conexión',
|
||||
message: 'No se pudo conectar con el servidor'
|
||||
title: 'Error al actualizar',
|
||||
message: 'No se pudo actualizar tu perfil'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -300,19 +306,30 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: This would typically call a separate password change endpoint
|
||||
// For now, we'll show a placeholder message
|
||||
showToast({
|
||||
type: 'success',
|
||||
title: 'Contraseña actualizada',
|
||||
message: 'Tu contraseña ha sido cambiada correctamente'
|
||||
});
|
||||
|
||||
setPasswordData({
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
confirmNewPassword: ''
|
||||
});
|
||||
try {
|
||||
await changePasswordMutation.mutateAsync({
|
||||
current_password: passwordData.currentPassword,
|
||||
new_password: passwordData.newPassword,
|
||||
});
|
||||
|
||||
showToast({
|
||||
type: 'success',
|
||||
title: 'Contraseña actualizada',
|
||||
message: 'Tu contraseña ha sido cambiada correctamente'
|
||||
});
|
||||
|
||||
setPasswordData({
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
confirmNewPassword: ''
|
||||
});
|
||||
} catch (error) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Error al cambiar contraseña',
|
||||
message: 'No se pudo cambiar tu contraseña. Por favor, verifica tu contraseña actual.'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleProfileInputChange = (field: keyof ProfileFormData) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -353,13 +370,13 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
{ id: 'preferences' as const, label: 'Preferencias', icon: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z' }
|
||||
];
|
||||
|
||||
// Mock user data for display
|
||||
const mockUser = {
|
||||
first_name: 'María',
|
||||
last_name: 'González',
|
||||
email: 'admin@bakery.com',
|
||||
bakery_name: 'Panadería San Miguel',
|
||||
avatar_url: 'https://images.unsplash.com/photo-1494790108755-2616b612b372?w=150&h=150&fit=crop&crop=face'
|
||||
// User data for display - based on actual profile data
|
||||
const displayUser = {
|
||||
first_name: profileData.first_name || '',
|
||||
last_name: profileData.last_name || '',
|
||||
email: profileData.email || '',
|
||||
bakery_name: user?.tenant_id ? 'Current Bakery' : 'No Bakery Assigned',
|
||||
avatar: profileData.avatar || ''
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -369,8 +386,9 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
<div className="flex items-center space-x-6">
|
||||
<div className="relative">
|
||||
<Avatar
|
||||
src={mockUser.avatar_url}
|
||||
name={`${mockUser.first_name} ${mockUser.last_name}`}
|
||||
src={displayUser.avatar || undefined}
|
||||
alt={`${displayUser.first_name} ${displayUser.last_name}` || 'Usuario'}
|
||||
name={displayUser.avatar ? `${displayUser.first_name} ${displayUser.last_name}` : undefined}
|
||||
size="xl"
|
||||
className="w-20 h-20 border-4 border-background-primary shadow-lg"
|
||||
/>
|
||||
@@ -378,14 +396,14 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h1 className="text-3xl font-bold text-text-primary mb-2">
|
||||
{mockUser.first_name} {mockUser.last_name}
|
||||
{displayUser.first_name} {displayUser.last_name}
|
||||
</h1>
|
||||
<p className="text-text-secondary text-lg mb-1">{mockUser.email}</p>
|
||||
<p className="text-text-secondary text-lg mb-1">{displayUser.email}</p>
|
||||
<p className="text-sm text-text-tertiary flex items-center">
|
||||
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||
</svg>
|
||||
Trabajando en {mockUser.bakery_name}
|
||||
Trabajando en {displayUser.bakery_name}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
@@ -428,8 +446,9 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
<div className="flex items-center space-x-8 mb-8 p-6 bg-background-secondary rounded-lg">
|
||||
<div className="relative">
|
||||
<Avatar
|
||||
src={profileData.avatar_url}
|
||||
name={`${profileData.first_name} ${profileData.last_name}`}
|
||||
src={profileData.avatar || undefined}
|
||||
alt={`${profileData.first_name} ${profileData.last_name}` || 'Usuario'}
|
||||
name={profileData.avatar ? `${profileData.first_name} ${profileData.last_name}` : undefined}
|
||||
size="xl"
|
||||
className="w-24 h-24"
|
||||
/>
|
||||
@@ -556,15 +575,10 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
variant="outline"
|
||||
size="lg"
|
||||
onClick={() => {
|
||||
setProfileData({
|
||||
first_name: 'María',
|
||||
last_name: 'González Pérez',
|
||||
email: 'admin@bakery.com',
|
||||
phone: '+34 612 345 678',
|
||||
language: 'es',
|
||||
timezone: 'Europe/Madrid',
|
||||
avatar_url: 'https://images.unsplash.com/photo-1494790108755-2616b612b372?w=150&h=150&fit=crop&crop=face'
|
||||
});
|
||||
// Reset to original profile data
|
||||
if (originalProfileDataRef.current) {
|
||||
setProfileData(originalProfileDataRef.current);
|
||||
}
|
||||
setHasChanges(prev => ({ ...prev, profile: false }));
|
||||
setErrors({});
|
||||
}}
|
||||
|
||||
@@ -69,7 +69,7 @@ export interface ProfileFormData {
|
||||
phone: string;
|
||||
language: string;
|
||||
timezone: string;
|
||||
avatar_url?: string;
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
export interface BakeryFormData {
|
||||
|
||||
Reference in New Issue
Block a user