diff --git a/src/components/forms/AutoSaveManager.tsx b/src/components/forms/AutoSaveManager.tsx index a0925493..ad81c5af 100644 --- a/src/components/forms/AutoSaveManager.tsx +++ b/src/components/forms/AutoSaveManager.tsx @@ -8,6 +8,7 @@ import React, { useState, useEffect } from 'react'; import { AutoSaveManagerImpl } from '@/form-management/auto-save/auto-save-manager'; import { FormState, SaveStatus } from '@/form-management/types/core'; +import { useNotification } from '@/hooks/use-notification'; interface AutoSaveManagerProps { formId: string; @@ -30,6 +31,7 @@ export const AutoSaveManager: React.FC = ({ onSaveError, className = '', }) => { + const { success, error: notifyError } = useNotification(); const [autoSaveManager] = useState(() => new AutoSaveManagerImpl()); const [saveStatus, setSaveStatus] = useState({ status: 'idle', @@ -53,6 +55,7 @@ export const AutoSaveManager: React.FC = ({ onSaveSuccess(); } } else if (status.status === 'error' && status.error) { + notifyError(`Auto-save Error: ${status.error.message}`); if (onSaveError) { onSaveError(status.error); } diff --git a/src/components/forms/DynamicFormBuilder.tsx b/src/components/forms/DynamicFormBuilder.tsx index f0597f54..14b64e4c 100644 --- a/src/components/forms/DynamicFormBuilder.tsx +++ b/src/components/forms/DynamicFormBuilder.tsx @@ -11,6 +11,7 @@ import { FormConfigurationParser } from '@/form-management/utils/configuration-p import { FormStateManager } from '@/form-management/state/form-state-manager'; import { ValidationEngineImpl } from '@/form-management/validation/validation-engine'; import { AutoSaveManagerImpl } from '@/form-management/auto-save/auto-save-manager'; +import { useNotification } from '@/hooks/use-notification'; interface DynamicFormBuilderProps { config: FormConfiguration | string; @@ -35,6 +36,7 @@ export const DynamicFormBuilder: React.FC = ({ const [autoSaveManager] = useState(() => new AutoSaveManagerImpl()); const [formState, setFormState] = useState(stateManager.getState()); const [saveStatus, setSaveStatus] = useState('idle'); + const { success, error: notifyError } = useNotification(); // Parse configuration useEffect(() => { @@ -65,6 +67,9 @@ export const DynamicFormBuilder: React.FC = ({ const subscription = autoSaveManager.onSaveStatusChange((status) => { setSaveStatus(status.status); + if (status.status === 'error') { + notifyError('Auto-save failed. Your changes might not be synced.'); + } }); // Load draft on mount @@ -79,7 +84,7 @@ export const DynamicFormBuilder: React.FC = ({ return () => { subscription.unsubscribe(); }; - }, [autoSave, autoSaveInterval, formConfig, stateManager, autoSaveManager]); + }, [autoSave, autoSaveInterval, formConfig, stateManager, autoSaveManager, notifyError]); // Subscribe to state changes useEffect(() => { @@ -120,13 +125,14 @@ export const DynamicFormBuilder: React.FC = ({ if (!formConfig) return; stateManager.setSubmitting(true); + const toastId = success('Submitting...'); try { // Validate entire form const validationResult = await validationEngine.validateForm(stateManager.getState()); if (!validationResult.isValid) { - console.error('Form validation failed:', validationResult.fieldResults); + notifyError('Validation failed. Please check the required fields.'); stateManager.setSubmitting(false); return; } @@ -134,6 +140,7 @@ export const DynamicFormBuilder: React.FC = ({ // Submit form if (onSubmit) { await onSubmit(formState.values); + success('Form submitted successfully!'); } // Clear draft after successful submission @@ -143,7 +150,7 @@ export const DynamicFormBuilder: React.FC = ({ stateManager.completeSubmission(true); } catch (error) { - console.error('Form submission error:', error); + notifyError('Submission failed. Please try again.'); stateManager.completeSubmission(false); } }; diff --git a/src/components/forms/FormWizardController.tsx b/src/components/forms/FormWizardController.tsx index 712186bf..bad3284d 100644 --- a/src/components/forms/FormWizardController.tsx +++ b/src/components/forms/FormWizardController.tsx @@ -9,6 +9,7 @@ import React, { useState, useEffect } from 'react'; import { WizardStep, WizardProgress, FormState } from '@/form-management/types/core'; import { FormStateManager } from '@/form-management/state/form-state-manager'; import { ValidationEngineImpl } from '@/form-management/validation/validation-engine'; +import { useNotification } from '@/hooks/use-notification'; interface FormWizardControllerProps { steps: WizardStep[]; @@ -37,6 +38,7 @@ export const FormWizardController: React.FC = ({ const [completedSteps, setCompletedSteps] = useState([]); const [validationEngine] = useState(() => new ValidationEngineImpl()); const [isValidating, setIsValidating] = useState(false); + const { error, success } = useNotification(); const currentStep = steps[currentStepIndex]; @@ -74,6 +76,10 @@ export const FormWizardController: React.FC = ({ } } + if (!allValid) { + error('Please fix the errors in this step before continuing.'); + } + return allValid; } finally { setIsValidating(false); @@ -128,14 +134,15 @@ export const FormWizardController: React.FC = ({ const handleComplete = async () => { const isValid = await validateCurrentStep(); - - if (!isValid) { - console.warn('Final step validation failed'); - return; - } + if (!isValid) return; if (onComplete) { - await onComplete(formState.values); + try { + await onComplete(formState.values); + success('Form submitted successfully!'); + } catch (err) { + error(err instanceof Error ? err.message : 'Failed to complete the form.'); + } } }; diff --git a/src/hooks/use-notification.ts b/src/hooks/use-notification.ts new file mode 100644 index 00000000..c5aedecd --- /dev/null +++ b/src/hooks/use-notification.ts @@ -0,0 +1,36 @@ +import { useCallback } from 'react'; +import toast from 'react-hot-toast'; + +export const useNotification = () => { + const success = useCallback((message: string) => { + toast.success(message, { + duration: 4000, + position: 'bottom-right', + }); + }, []); + + const error = useCallback((message: string | string[] | Error) => { + const finalMessage = Array.isArray(message) + ? message[0] + : message instanceof Error + ? message.message + : message; + + toast.error(finalMessage || 'Something went wrong. Please try again.', { + duration: 5000, + position: 'bottom-right', + }); + }, []); + + const loading = useCallback((message: string) => { + return toast.loading(message, { + position: 'bottom-right', + }); + }, []); + + const dismiss = useCallback((id?: string) => { + toast.dismiss(id); + }, []); + + return { success, error, loading, dismiss, toast }; +};