Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/components/forms/AutoSaveManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,6 +31,7 @@ export const AutoSaveManager: React.FC<AutoSaveManagerProps> = ({
onSaveError,
className = '',
}) => {
const { success, error: notifyError } = useNotification();
const [autoSaveManager] = useState(() => new AutoSaveManagerImpl());
const [saveStatus, setSaveStatus] = useState<SaveStatus>({
status: 'idle',
Expand All @@ -53,6 +55,7 @@ export const AutoSaveManager: React.FC<AutoSaveManagerProps> = ({
onSaveSuccess();
}
} else if (status.status === 'error' && status.error) {
notifyError(`Auto-save Error: ${status.error.message}`);
if (onSaveError) {
onSaveError(status.error);
}
Expand Down
13 changes: 10 additions & 3 deletions src/components/forms/DynamicFormBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -35,6 +36,7 @@ export const DynamicFormBuilder: React.FC<DynamicFormBuilderProps> = ({
const [autoSaveManager] = useState(() => new AutoSaveManagerImpl());
const [formState, setFormState] = useState<FormState>(stateManager.getState());
const [saveStatus, setSaveStatus] = useState<string>('idle');
const { success, error: notifyError } = useNotification();

// Parse configuration
useEffect(() => {
Expand Down Expand Up @@ -65,6 +67,9 @@ export const DynamicFormBuilder: React.FC<DynamicFormBuilderProps> = ({

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
Expand All @@ -79,7 +84,7 @@ export const DynamicFormBuilder: React.FC<DynamicFormBuilderProps> = ({
return () => {
subscription.unsubscribe();
};
}, [autoSave, autoSaveInterval, formConfig, stateManager, autoSaveManager]);
}, [autoSave, autoSaveInterval, formConfig, stateManager, autoSaveManager, notifyError]);

// Subscribe to state changes
useEffect(() => {
Expand Down Expand Up @@ -120,20 +125,22 @@ export const DynamicFormBuilder: React.FC<DynamicFormBuilderProps> = ({
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;
}

// Submit form
if (onSubmit) {
await onSubmit(formState.values);
success('Form submitted successfully!');
}

// Clear draft after successful submission
Expand All @@ -143,7 +150,7 @@ export const DynamicFormBuilder: React.FC<DynamicFormBuilderProps> = ({

stateManager.completeSubmission(true);
} catch (error) {
console.error('Form submission error:', error);
notifyError('Submission failed. Please try again.');
stateManager.completeSubmission(false);
}
};
Expand Down
19 changes: 13 additions & 6 deletions src/components/forms/FormWizardController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down Expand Up @@ -37,6 +38,7 @@ export const FormWizardController: React.FC<FormWizardControllerProps> = ({
const [completedSteps, setCompletedSteps] = useState<number[]>([]);
const [validationEngine] = useState(() => new ValidationEngineImpl());
const [isValidating, setIsValidating] = useState(false);
const { error, success } = useNotification();

const currentStep = steps[currentStepIndex];

Expand Down Expand Up @@ -74,6 +76,10 @@ export const FormWizardController: React.FC<FormWizardControllerProps> = ({
}
}

if (!allValid) {
error('Please fix the errors in this step before continuing.');
}

return allValid;
} finally {
setIsValidating(false);
Expand Down Expand Up @@ -128,14 +134,15 @@ export const FormWizardController: React.FC<FormWizardControllerProps> = ({

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.');
}
}
};

Expand Down
36 changes: 36 additions & 0 deletions src/hooks/use-notification.ts
Original file line number Diff line number Diff line change
@@ -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 };
};
Loading