mirror of
https://github.com/soconnor0919/beenvoice.git
synced 2025-12-13 17:44:44 -05:00
feat: polish invoice editor and viewer UI with custom NumberInput
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
This commit is contained in:
279
docs/forms-guide.md
Normal file
279
docs/forms-guide.md
Normal file
@@ -0,0 +1,279 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user