initial commit
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# Phone Number Formatting
|
||||
|
||||
All phone numbers in SPQ are **stored as raw digits** (no formatting) and **displayed/input as `555-555-5555`** (hyphen-separated, no parentheses).
|
||||
|
||||
## Shared Utility: `src/lib/phone.ts`
|
||||
|
||||
Two exports:
|
||||
|
||||
- **`formatPhoneInput(raw: string): string`** — live input mask for onChange. Strips non-digits, inserts hyphens as user types.
|
||||
- `""` → `""`
|
||||
- `"555"` → `"555"`
|
||||
- `"5555"` → `"555-5"`
|
||||
- `"5555555555"` → `"555-555-5555"`
|
||||
|
||||
- **`formatPhoneDisplay(phone: string | null | undefined): string`** — display formatter for read-only contexts (tables, PDFs, previews).
|
||||
- `"5555555555"` → `"555-555-5555"`
|
||||
- `"5551234"` → `"555-1234"` (7-digit fallback)
|
||||
- Handles legacy formatted data (strips parens, spaces, dots).
|
||||
|
||||
## Input Pattern (onChange + value)
|
||||
|
||||
For every phone `<input>`:
|
||||
|
||||
```tsx
|
||||
import { formatPhoneInput } from '../lib/phone';
|
||||
|
||||
// In the input:
|
||||
value={formatPhoneInput(phoneValue)}
|
||||
onChange={(e) => setPhone(e.target.value.replace(/\D/g, ''))}
|
||||
placeholder="555-555-5555"
|
||||
```
|
||||
|
||||
This stores raw digits (stripped on every keystroke) and displays the mask.
|
||||
|
||||
## Display Pattern
|
||||
|
||||
For every read-only phone render:
|
||||
|
||||
```tsx
|
||||
import { formatPhoneDisplay } from '../lib/phone';
|
||||
|
||||
// In JSX:
|
||||
{formatPhoneDisplay(customer.phone)}
|
||||
// In template literals:
|
||||
`${formatPhoneDisplay(inv.customerPhone)}`
|
||||
```
|
||||
|
||||
## Files Using Phone Formatting
|
||||
|
||||
| File | Applied |
|
||||
|---|---|
|
||||
| `QuoteGenerator.tsx` | CustomerInfoPanel phone field |
|
||||
| `ROForm.tsx` | RO create/edit phone field |
|
||||
| `Appointments.tsx` | 3 inputs + list display |
|
||||
| `Invoices.tsx` | 1 input + printable HTML + preview |
|
||||
| `Customers.tsx` | 1 input + 2 displays (card + detail) |
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **Stored data may be legacy-formatted** — `formatPhoneDisplay` and `formatPhoneInput` both strip non-digits first, so legacy `(555) 555-5555` data is handled.
|
||||
- **Don't store the formatted value** — the `onChange` handler must strip formatting (`.replace(/\D/g, '')`) so PocketBase only contains raw digits.
|
||||
- **Don't create local formatPhone functions** — always import from `src/lib/phone.ts`. Duplicate implementations exist in legacy code and should be replaced.
|
||||
Reference in New Issue
Block a user