Improve the frontend 3
This commit is contained in:
@@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Input, Card } from '../../ui';
|
||||
import { useAuthActions, useAuthLoading, useAuthError } from '../../../stores/auth.store';
|
||||
import { useToast } from '../../../hooks/ui/useToast';
|
||||
import { showToast } from '../../../utils/toast';
|
||||
|
||||
interface LoginFormProps {
|
||||
onSuccess?: () => void;
|
||||
@@ -38,7 +38,7 @@ export const LoginForm: React.FC<LoginFormProps> = ({
|
||||
const { login } = useAuthActions();
|
||||
const isLoading = useAuthLoading();
|
||||
const error = useAuthError();
|
||||
const { success, error: showError } = useToast();
|
||||
|
||||
|
||||
// Auto-focus on email field when component mounts
|
||||
useEffect(() => {
|
||||
@@ -78,7 +78,7 @@ export const LoginForm: React.FC<LoginFormProps> = ({
|
||||
|
||||
try {
|
||||
await login(credentials.email, credentials.password);
|
||||
success('¡Bienvenido de vuelta a tu panadería!', {
|
||||
showToast.success('¡Bienvenido de vuelta a tu panadería!', {
|
||||
title: 'Sesión iniciada correctamente'
|
||||
});
|
||||
onSuccess?.();
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Button, Input, Card } from '../../ui';
|
||||
import { PasswordCriteria, validatePassword, getPasswordErrors } from '../../ui/PasswordCriteria';
|
||||
import { useAuthActions } from '../../../stores/auth.store';
|
||||
import { useToast } from '../../../hooks/ui/useToast';
|
||||
import { showToast } from '../../../utils/toast';
|
||||
import { useResetPassword } from '../../../api/hooks/auth';
|
||||
|
||||
interface PasswordResetFormProps {
|
||||
@@ -39,7 +39,7 @@ export const PasswordResetForm: React.FC<PasswordResetFormProps> = ({
|
||||
const { mutateAsync: resetPasswordMutation, isPending: isResetting } = useResetPassword();
|
||||
const isLoading = isResetting;
|
||||
const error = null;
|
||||
const { showToast } = useToast();
|
||||
|
||||
|
||||
const isResetMode = Boolean(token) || mode === 'reset';
|
||||
|
||||
@@ -62,11 +62,9 @@ export const PasswordResetForm: React.FC<PasswordResetFormProps> = ({
|
||||
setIsTokenValid(isValidFormat);
|
||||
|
||||
if (!isValidFormat) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Token inválido',
|
||||
message: 'El enlace de restablecimiento no es válido o ha expirado'
|
||||
});
|
||||
showToast.error('El enlace de restablecimiento no es válido o ha expirado', {
|
||||
title: 'Token inválido'
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [token, showToast]);
|
||||
@@ -154,16 +152,12 @@ export const PasswordResetForm: React.FC<PasswordResetFormProps> = ({
|
||||
// Note: Password reset request functionality needs to be implemented in backend
|
||||
// For now, show a message that the feature is coming soon
|
||||
setIsEmailSent(true);
|
||||
showToast({
|
||||
type: 'info',
|
||||
title: 'Función en desarrollo',
|
||||
message: 'La solicitud de restablecimiento de contraseña estará disponible próximamente. Por favor, contacta al administrador.'
|
||||
showToast.info('La solicitud de restablecimiento de contraseña estará disponible próximamente. Por favor, contacta al administrador.', {
|
||||
title: 'Función en desarrollo'
|
||||
});
|
||||
} catch (err) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Error de conexión',
|
||||
message: 'No se pudo conectar con el servidor. Verifica tu conexión a internet.'
|
||||
showToast.error('No se pudo conectar con el servidor. Verifica tu conexión a internet.', {
|
||||
title: 'Error de conexión'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -180,10 +174,8 @@ export const PasswordResetForm: React.FC<PasswordResetFormProps> = ({
|
||||
}
|
||||
|
||||
if (!token || isTokenValid === false) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Token inválido',
|
||||
message: 'El enlace de restablecimiento no es válido. Solicita uno nuevo.'
|
||||
showToast.error('El enlace de restablecimiento no es válido. Solicita uno nuevo.', {
|
||||
title: 'Token inválido'
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -195,18 +187,14 @@ export const PasswordResetForm: React.FC<PasswordResetFormProps> = ({
|
||||
new_password: password
|
||||
});
|
||||
|
||||
showToast({
|
||||
type: 'success',
|
||||
title: 'Contraseña actualizada',
|
||||
message: '¡Tu contraseña ha sido restablecida exitosamente! Ya puedes iniciar sesión.'
|
||||
showToast.success('¡Tu contraseña ha sido restablecida exitosamente! Ya puedes iniciar sesión.', {
|
||||
title: 'Contraseña actualizada'
|
||||
});
|
||||
onSuccess?.();
|
||||
} catch (err: any) {
|
||||
const errorMessage = err?.response?.data?.detail || err?.message || 'El enlace ha expirado o no es válido. Solicita un nuevo restablecimiento.';
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Error al restablecer contraseña',
|
||||
message: errorMessage
|
||||
showToast.error(errorMessage, {
|
||||
title: 'Error al restablecer contraseña'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -599,4 +587,4 @@ export const PasswordResetForm: React.FC<PasswordResetFormProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default PasswordResetForm;
|
||||
export default PasswordResetForm;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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 { showToast } from '../../../utils/toast';
|
||||
import { useUpdateProfile, useChangePassword, useAuthProfile } from '../../../api/hooks/auth';
|
||||
|
||||
interface ProfileSettingsProps {
|
||||
@@ -42,7 +42,7 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
initialTab = 'profile'
|
||||
}) => {
|
||||
const user = useAuthUser();
|
||||
const { showToast } = useToast();
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -139,20 +139,16 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
|
||||
// Validate file type
|
||||
if (!file.type.startsWith('image/')) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Archivo inválido',
|
||||
message: 'Por favor, selecciona una imagen válida'
|
||||
showToast.error('Solo se permiten archivos de imagen (JPEG, PNG, GIF, WEBP)', {
|
||||
title: 'Error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate file size (max 5MB)
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Archivo muy grande',
|
||||
message: 'La imagen debe ser menor a 5MB'
|
||||
showToast.error('El archivo es demasiado grande. Máximo 5MB permitido', {
|
||||
title: 'Error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -174,16 +170,12 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
setProfileData(prev => ({ ...prev, avatar: newImageUrl }));
|
||||
setHasChanges(prev => ({ ...prev, profile: true }));
|
||||
|
||||
showToast({
|
||||
type: 'success',
|
||||
title: 'Imagen subida',
|
||||
message: 'Tu foto de perfil ha sido actualizada'
|
||||
showToast.success('¡Éxito!', {
|
||||
title: 'Foto de perfil actualizada correctamente'
|
||||
});
|
||||
} catch (error) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Error al subir imagen',
|
||||
message: 'No se pudo subir la imagen. Intenta de nuevo.'
|
||||
showToast.error('No se pudo actualizar la foto de perfil', {
|
||||
title: 'Error'
|
||||
});
|
||||
} finally {
|
||||
setUploadingImage(false);
|
||||
@@ -283,17 +275,13 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
});
|
||||
|
||||
setHasChanges(false);
|
||||
showToast({
|
||||
type: 'success',
|
||||
title: 'Perfil actualizado',
|
||||
message: 'Tu información ha sido guardada correctamente'
|
||||
showToast.success('¡Éxito!', {
|
||||
title: 'Perfil actualizado correctamente'
|
||||
});
|
||||
onSuccess?.();
|
||||
} catch (err) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
title: 'Error al actualizar',
|
||||
message: 'No se pudo actualizar tu perfil'
|
||||
showToast.error('No se pudo actualizar el perfil', {
|
||||
title: 'Error'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -311,10 +299,8 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
new_password: passwordData.newPassword,
|
||||
});
|
||||
|
||||
showToast({
|
||||
type: 'success',
|
||||
title: 'Contraseña actualizada',
|
||||
message: 'Tu contraseña ha sido cambiada correctamente'
|
||||
showToast.success('¡Éxito!', {
|
||||
title: 'Contraseña cambiada correctamente'
|
||||
});
|
||||
|
||||
setPasswordData({
|
||||
@@ -323,10 +309,8 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
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.'
|
||||
showToast.error('No se pudo cambiar la contraseña', {
|
||||
title: 'Error'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -725,4 +709,4 @@ export const ProfileSettings: React.FC<ProfileSettingsProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileSettings;
|
||||
export default ProfileSettings;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useSearchParams } from 'react-router-dom';
|
||||
import { Button, Input, Card } from '../../ui';
|
||||
import { PasswordCriteria, validatePassword, getPasswordErrors } from '../../ui/PasswordCriteria';
|
||||
import { useAuthActions, useAuthLoading, useAuthError } from '../../../stores/auth.store';
|
||||
import { useToast } from '../../../hooks/ui/useToast';
|
||||
import { showToast } from '../../../utils/toast';
|
||||
import { SubscriptionPricingCards } from '../../subscription/SubscriptionPricingCards';
|
||||
import PaymentForm from './PaymentForm';
|
||||
import { loadStripe } from '@stripe/stripe-js';
|
||||
@@ -68,7 +68,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
const { register } = useAuthActions();
|
||||
const isLoading = useAuthLoading();
|
||||
const error = useAuthError();
|
||||
const { success: showSuccessToast, error: showErrorToast } = useToast();
|
||||
|
||||
|
||||
// Detect pilot program participation
|
||||
const { isPilot, couponCode, trialMonths } = usePilotDetection();
|
||||
@@ -236,12 +236,12 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
? '¡Bienvenido al programa piloto! Tu cuenta ha sido creada con 3 meses gratis.'
|
||||
: '¡Bienvenido! Tu cuenta ha sido creada correctamente.';
|
||||
|
||||
showSuccessToast(t('auth:register.registering', successMessage), {
|
||||
showToast.success(t('auth:register.registering', successMessage), {
|
||||
title: t('auth:alerts.success_create', 'Cuenta creada exitosamente')
|
||||
});
|
||||
onSuccess?.();
|
||||
} catch (err) {
|
||||
showErrorToast(error || t('auth:register.register_button', 'No se pudo crear la cuenta. Verifica que el email no esté en uso.'), {
|
||||
showToast.error(error || t('auth:register.register_button', 'No se pudo crear la cuenta. Verifica que el email no esté en uso.'), {
|
||||
title: t('auth:alerts.error_create', 'Error al crear la cuenta')
|
||||
});
|
||||
}
|
||||
@@ -252,7 +252,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
||||
};
|
||||
|
||||
const handlePaymentError = (errorMessage: string) => {
|
||||
showErrorToast(errorMessage, {
|
||||
showToast.error(errorMessage, {
|
||||
title: 'Error en el pago'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Zap, Key, Settings as SettingsIcon, RefreshCw } from 'lucide-react';
|
||||
import { AddModal, AddModalSection, AddModalField } from '../../ui/AddModal/AddModal';
|
||||
import { posService } from '../../../api/services/pos';
|
||||
import { POSProviderConfig, POSSystem, POSEnvironment } from '../../../api/types/pos';
|
||||
import { useToast } from '../../../hooks/ui/useToast';
|
||||
import { showToast } from '../../../utils/toast';
|
||||
import { statusColors } from '../../../styles/colors';
|
||||
|
||||
interface CreatePOSConfigModalProps {
|
||||
@@ -29,7 +29,7 @@ export const CreatePOSConfigModal: React.FC<CreatePOSConfigModalProps> = ({
|
||||
}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedProvider, setSelectedProvider] = useState<POSSystem | ''>('');
|
||||
const { addToast } = useToast();
|
||||
|
||||
|
||||
// Initialize selectedProvider in edit mode
|
||||
React.useEffect(() => {
|
||||
@@ -250,7 +250,7 @@ export const CreatePOSConfigModal: React.FC<CreatePOSConfigModalProps> = ({
|
||||
// Find selected provider
|
||||
const provider = supportedProviders.find(p => p.id === formData.provider);
|
||||
if (!provider) {
|
||||
addToast('Por favor selecciona un sistema POS', { type: 'error' });
|
||||
showToast.error('Por favor selecciona un sistema POS');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -298,17 +298,17 @@ export const CreatePOSConfigModal: React.FC<CreatePOSConfigModalProps> = ({
|
||||
...payload,
|
||||
config_id: existingConfig.id
|
||||
});
|
||||
addToast('Configuración actualizada correctamente', { type: 'success' });
|
||||
showToast.success('Configuración actualizada correctamente');
|
||||
} else {
|
||||
await posService.createPOSConfiguration(payload);
|
||||
addToast('Configuración creada correctamente', { type: 'success' });
|
||||
showToast.success('Configuración creada correctamente');
|
||||
}
|
||||
|
||||
onSuccess?.();
|
||||
onClose();
|
||||
} catch (error: any) {
|
||||
console.error('Error saving POS configuration:', error);
|
||||
addToast(error?.message || 'Error al guardar la configuración', { type: 'error' });
|
||||
showToast.error(error?.message || 'Error al guardar la configuración');
|
||||
throw error; // Let AddModal handle error state
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -345,7 +345,7 @@ export const CreatePOSConfigModal: React.FC<CreatePOSConfigModalProps> = ({
|
||||
// Custom validation if needed
|
||||
if (errors && Object.keys(errors).length > 0) {
|
||||
const firstError = Object.values(errors)[0];
|
||||
addToast(firstError, { type: 'error' });
|
||||
showToast.error(firstError);
|
||||
}
|
||||
}}
|
||||
onFieldChange={handleFieldChange}
|
||||
|
||||
Reference in New Issue
Block a user