refactor: Extract reusable UI components and add TDD documentation
- Add comprehensive TDD documentation to CLAUDE.md with coverage requirements and examples - Extract reusable UI components to frontend/src/components/ui/ (Modal, FormInput, Button, Alert, etc.) - Add shared constants (schedulePresets) and utility hooks (useCrudMutation, useFormValidation) - Update frontend/CLAUDE.md with component documentation and usage examples - Refactor CreateTaskModal to use shared components and constants - Fix test assertions to be more robust and accurate across all test files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,10 @@ This is the React frontend for SmoothSchedule, a multi-tenant scheduling platfor
|
||||
├── frontend/ # This React frontend
|
||||
│ ├── src/
|
||||
│ │ ├── api/client.ts # Axios API client
|
||||
│ │ ├── components/ # Reusable components
|
||||
│ │ ├── components/ # Feature components
|
||||
│ │ │ └── ui/ # Reusable UI components (see below)
|
||||
│ │ ├── constants/ # Shared constants
|
||||
│ │ │ └── schedulePresets.ts # Schedule/cron presets
|
||||
│ │ ├── hooks/ # React Query hooks (useResources, useAuth, etc.)
|
||||
│ │ ├── pages/ # Page components
|
||||
│ │ ├── types.ts # TypeScript interfaces
|
||||
@@ -31,6 +34,125 @@ This is the React frontend for SmoothSchedule, a multi-tenant scheduling platfor
|
||||
└── users/ # User management
|
||||
```
|
||||
|
||||
## Reusable UI Components
|
||||
|
||||
All reusable UI components are in `src/components/ui/`. Import from the barrel file:
|
||||
|
||||
```typescript
|
||||
import { Modal, FormInput, Button, Alert } from '../components/ui';
|
||||
```
|
||||
|
||||
### Available Components
|
||||
|
||||
| Component | Description |
|
||||
|-----------|-------------|
|
||||
| **Modal** | Reusable modal dialog with header, body, footer |
|
||||
| **ModalFooter** | Standardized modal footer with buttons |
|
||||
| **FormInput** | Text input with label, error, hint support |
|
||||
| **FormSelect** | Select dropdown with label, error support |
|
||||
| **FormTextarea** | Textarea with label, error support |
|
||||
| **FormCurrencyInput** | ATM-style currency input (cents) |
|
||||
| **CurrencyInput** | Raw currency input component |
|
||||
| **Button** | Button with variants, loading state, icons |
|
||||
| **SubmitButton** | Pre-configured submit button |
|
||||
| **Alert** | Alert banner (error, success, warning, info) |
|
||||
| **ErrorMessage** | Error alert shorthand |
|
||||
| **SuccessMessage** | Success alert shorthand |
|
||||
| **TabGroup** | Tab navigation (default, pills, underline) |
|
||||
| **StepIndicator** | Multi-step wizard indicator |
|
||||
| **LoadingSpinner** | Loading spinner with variants |
|
||||
| **PageLoading** | Full page loading state |
|
||||
| **Card** | Card container with header/body/footer |
|
||||
| **EmptyState** | Empty state placeholder |
|
||||
| **Badge** | Status badges |
|
||||
|
||||
### Usage Examples
|
||||
|
||||
```typescript
|
||||
// Modal with form
|
||||
<Modal isOpen={isOpen} onClose={onClose} title="Edit Resource" size="lg">
|
||||
<FormInput
|
||||
label="Name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
error={errors.name}
|
||||
required
|
||||
/>
|
||||
<FormSelect
|
||||
label="Type"
|
||||
value={type}
|
||||
onChange={(e) => setType(e.target.value)}
|
||||
options={[
|
||||
{ value: 'STAFF', label: 'Staff' },
|
||||
{ value: 'ROOM', label: 'Room' },
|
||||
]}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
// Alert messages
|
||||
{error && <ErrorMessage message={error} />}
|
||||
{success && <SuccessMessage message="Saved successfully!" />}
|
||||
|
||||
// Tabs
|
||||
<TabGroup
|
||||
tabs={[
|
||||
{ id: 'details', label: 'Details' },
|
||||
{ id: 'schedule', label: 'Schedule' },
|
||||
]}
|
||||
activeTab={activeTab}
|
||||
onChange={setActiveTab}
|
||||
/>
|
||||
```
|
||||
|
||||
## Utility Hooks
|
||||
|
||||
### useCrudMutation
|
||||
|
||||
Factory hook for CRUD mutations with React Query:
|
||||
|
||||
```typescript
|
||||
import { useCrudMutation, createCrudHooks } from '../hooks/useCrudMutation';
|
||||
|
||||
// Simple usage
|
||||
const createResource = useCrudMutation<Resource, CreateResourceData>({
|
||||
endpoint: '/resources',
|
||||
method: 'POST',
|
||||
invalidateKeys: [['resources']],
|
||||
});
|
||||
|
||||
// Create all CRUD hooks at once
|
||||
const { useCreate, useUpdate, useDelete } = createCrudHooks<Resource>('/resources', 'resources');
|
||||
```
|
||||
|
||||
### useFormValidation
|
||||
|
||||
Schema-based form validation:
|
||||
|
||||
```typescript
|
||||
import { useFormValidation, required, email, minLength } from '../hooks/useFormValidation';
|
||||
|
||||
const schema = {
|
||||
email: [required('Email is required'), email('Invalid email')],
|
||||
password: [required(), minLength(8, 'Min 8 characters')],
|
||||
};
|
||||
|
||||
const { errors, validateForm, isValid } = useFormValidation(schema);
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (validateForm(formData)) {
|
||||
// Submit
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Constants
|
||||
|
||||
### Schedule Presets
|
||||
|
||||
```typescript
|
||||
import { SCHEDULE_PRESETS, TRIGGER_OPTIONS, OFFSET_PRESETS } from '../constants/schedulePresets';
|
||||
```
|
||||
|
||||
## Local Development Domain Setup
|
||||
|
||||
### Why lvh.me instead of localhost?
|
||||
|
||||
Reference in New Issue
Block a user