mirror of
https://github.com/soconnor0919/beenvoice.git
synced 2025-12-13 17:44:44 -05:00
component - Create custom NumberInput component with increment/decrement buttons - Add 0.25 step increments for hours and rates in invoice forms - Implement emerald-themed styling with hover states and accessibility - Add keyboard navigation (arrow keys) and proper ARIA support - Condense invoice editor tax/totals section into efficient grid layout - Update client dropdown to single-line format (name + email) - Add fixed footer with floating action bar pattern matching business forms - Redesign invoice viewer with better space utilization and visual hierarchy - Maintain professional appearance and consistent design system - Fix Next.js 15 params Promise handling across all invoice pages - Resolve TypeScript compilation errors and type-only imports
279 lines
7.8 KiB
Markdown
279 lines
7.8 KiB
Markdown
# Forms Improvement Guide
|
|
|
|
## Overview
|
|
|
|
The business and client creation/editing forms have been significantly improved with better organization, shared components, enhanced validation, and improved user experience.
|
|
|
|
## Key Improvements
|
|
|
|
### 1. Shared Components & Utilities
|
|
|
|
#### Address Form Component (`src/components/ui/address-form.tsx`)
|
|
A reusable address form component that handles:
|
|
- Country-aware formatting (US ZIP codes, Canadian postal codes)
|
|
- State dropdown for US addresses, text input for other countries
|
|
- Popular countries listed first in country dropdown
|
|
- Automatic field adjustments based on country selection
|
|
|
|
```tsx
|
|
<AddressForm
|
|
addressLine1={formData.addressLine1}
|
|
addressLine2={formData.addressLine2}
|
|
city={formData.city}
|
|
state={formData.state}
|
|
postalCode={formData.postalCode}
|
|
country={formData.country}
|
|
onChange={handleInputChange}
|
|
errors={errors}
|
|
required={false}
|
|
/>
|
|
```
|
|
|
|
#### Form Constants & Utilities (`src/lib/form-constants.ts`)
|
|
Centralized location for:
|
|
- US states list with proper formatting
|
|
- All countries with ISO codes
|
|
- Popular countries for quick selection
|
|
- Format functions for phone, postal codes, tax IDs, and URLs
|
|
- Validation utilities and messages
|
|
|
|
### 2. Enhanced Form Validation
|
|
|
|
#### Real-time Validation
|
|
- Errors clear as soon as user starts typing
|
|
- Field-specific validation messages
|
|
- Visual feedback with red borders on invalid fields
|
|
|
|
#### Smart Validation Rules
|
|
- Email: Proper email format checking
|
|
- Phone: US phone number format validation
|
|
- Address: Required fields only if any address field is filled
|
|
- URL: Automatic https:// prefix addition
|
|
|
|
```typescript
|
|
// Example validation
|
|
if (formData.email && !isValidEmail(formData.email)) {
|
|
newErrors.email = VALIDATION_MESSAGES.email;
|
|
}
|
|
```
|
|
|
|
### 3. Better Form Organization
|
|
|
|
#### Card-based Sections
|
|
Forms are now organized into logical sections using cards:
|
|
- **Basic Information**: Core fields like name, tax ID
|
|
- **Contact Information**: Email, phone, website
|
|
- **Address**: Complete address form with smart country handling
|
|
- **Settings**: Business-specific settings like default business flag
|
|
|
|
#### Consistent Layout
|
|
- Maximum width container for better readability
|
|
- Responsive grid layouts that stack on mobile
|
|
- Proper spacing between sections
|
|
- Clear visual hierarchy
|
|
|
|
### 4. Improved User Experience
|
|
|
|
#### Loading States
|
|
- Skeleton loader while fetching data in edit mode
|
|
- Disabled form fields during submission
|
|
- Loading spinner in submit button
|
|
|
|
#### Unsaved Changes Warning
|
|
```typescript
|
|
const handleCancel = () => {
|
|
if (isDirty) {
|
|
const confirmed = window.confirm(
|
|
"You have unsaved changes. Are you sure you want to leave?"
|
|
);
|
|
if (!confirmed) return;
|
|
}
|
|
router.push("/dashboard/businesses");
|
|
};
|
|
```
|
|
|
|
#### Smart Field Formatting
|
|
- Phone numbers: Auto-format as (555) 123-4567
|
|
- Tax ID: Auto-format as 12-3456789
|
|
- Postal codes: Format based on country (US vs Canadian)
|
|
- Website URLs: Auto-add https:// if missing
|
|
|
|
### 5. Responsive Design
|
|
|
|
#### Mobile Optimizations
|
|
- Form sections stack vertically on small screens
|
|
- Touch-friendly input sizes
|
|
- Proper button positioning
|
|
- Readable font sizes
|
|
|
|
#### Desktop Enhancements
|
|
- Two-column layouts for related fields
|
|
- Optimal reading width
|
|
- Side-by-side form actions
|
|
|
|
### 6. Code Reusability
|
|
|
|
#### Shared Between Business & Client Forms
|
|
- Address form component
|
|
- Validation logic
|
|
- Format functions
|
|
- Constants (states, countries)
|
|
- Error handling patterns
|
|
|
|
#### TypeScript Interfaces
|
|
```typescript
|
|
interface FormData {
|
|
name: string;
|
|
email: string;
|
|
phone: string;
|
|
// ... other fields
|
|
}
|
|
|
|
interface FormErrors {
|
|
name?: string;
|
|
email?: string;
|
|
// ... validation errors
|
|
}
|
|
```
|
|
|
|
## Usage Examples
|
|
|
|
### Basic Form Implementation
|
|
```tsx
|
|
export function BusinessForm({ businessId, mode }: BusinessFormProps) {
|
|
const [formData, setFormData] = useState<FormData>(initialFormData);
|
|
const [errors, setErrors] = useState<FormErrors>({});
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [isDirty, setIsDirty] = useState(false);
|
|
|
|
// Handle input changes
|
|
const handleInputChange = (field: string, value: string | boolean) => {
|
|
setFormData((prev) => ({ ...prev, [field]: value }));
|
|
setIsDirty(true);
|
|
|
|
// Clear error when user types
|
|
if (errors[field as keyof FormErrors]) {
|
|
setErrors((prev) => ({ ...prev, [field]: undefined }));
|
|
}
|
|
};
|
|
|
|
// Validate and submit
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (!validateForm()) {
|
|
toast.error("Please correct the errors in the form");
|
|
return;
|
|
}
|
|
|
|
// Submit logic...
|
|
};
|
|
}
|
|
```
|
|
|
|
### Field with Icon and Validation
|
|
```tsx
|
|
<div className="space-y-2">
|
|
<Label htmlFor="email">
|
|
Email
|
|
<span className="text-muted-foreground ml-1 text-xs">(Optional)</span>
|
|
</Label>
|
|
<div className="relative">
|
|
<Mail className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
value={formData.email}
|
|
onChange={(e) => handleInputChange("email", e.target.value)}
|
|
placeholder={PLACEHOLDERS.email}
|
|
className={`pl-10 ${errors.email ? "border-destructive" : ""}`}
|
|
disabled={isSubmitting}
|
|
/>
|
|
</div>
|
|
{errors.email && (
|
|
<p className="text-sm text-destructive">{errors.email}</p>
|
|
)}
|
|
</div>
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### 1. Form State Management
|
|
- Use controlled components for all inputs
|
|
- Track dirty state for unsaved changes warnings
|
|
- Clear errors when user corrects them
|
|
- Disable form during submission
|
|
|
|
### 2. Validation Strategy
|
|
- Validate on submit, not on blur (less annoying)
|
|
- Clear errors immediately when user starts fixing them
|
|
- Show field-level errors below each input
|
|
- Use consistent error message format
|
|
|
|
### 3. Accessibility
|
|
- Proper label associations with htmlFor
|
|
- Required field indicators
|
|
- Error messages linked to fields
|
|
- Keyboard navigation support
|
|
- Focus management
|
|
|
|
### 4. Performance
|
|
- Memoize expensive computations
|
|
- Use debouncing for format functions if needed
|
|
- Lazy load country lists
|
|
- Optimize re-renders with proper state management
|
|
|
|
## Migration Guide
|
|
|
|
### From Old Forms
|
|
1. Replace inline state/country arrays with imported constants
|
|
2. Use `AddressForm` component instead of individual address fields
|
|
3. Apply format functions from `form-constants.ts`
|
|
4. Update validation to use shared utilities
|
|
5. Wrap sections in Card components
|
|
6. Add loading and dirty state tracking
|
|
|
|
### Example Migration
|
|
```tsx
|
|
// Before
|
|
const US_STATES = [
|
|
{ value: "AL", label: "Alabama" },
|
|
// ... duplicated in each form
|
|
];
|
|
|
|
// After
|
|
import { US_STATES, formatPhoneNumber } from "~/lib/form-constants";
|
|
import { AddressForm } from "~/components/ui/address-form";
|
|
```
|
|
|
|
## Future Enhancements
|
|
|
|
### Planned Improvements
|
|
1. **Field-level permissions**: Disable fields based on user role
|
|
2. **Auto-save**: Save draft as user types
|
|
3. **Multi-step forms**: Break long forms into steps
|
|
4. **Conditional fields**: Show/hide fields based on other values
|
|
5. **Bulk operations**: Create multiple records at once
|
|
6. **Import from templates**: Pre-fill common business types
|
|
|
|
### Extensibility
|
|
The form system is designed to be easily extended:
|
|
- Add new format functions to `form-constants.ts`
|
|
- Create additional shared form components
|
|
- Extend validation rules as needed
|
|
- Add new field types with consistent patterns
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **Validation not working**: Ensure field names match FormErrors interface
|
|
2. **Format function not applying**: Check that onChange uses the format function
|
|
3. **Country dropdown not searching**: Verify SearchableSelect has search enabled
|
|
4. **Address validation failing**: Check if country field affects validation rules
|
|
|
|
### Debug Tips
|
|
- Use React DevTools to inspect form state
|
|
- Check console for validation errors
|
|
- Verify API responses match expected format
|
|
- Test with different country selections |