From c27f72e41decf20271ba22de62fa6bb9b2def097 Mon Sep 17 00:00:00 2001 From: Andrei Date: Fri, 3 Oct 2025 11:59:27 +0000 Subject: [PATCH] refactor: Consolidate settings page save buttons for better UX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improved the settings page by removing individual save buttons from each preference component and adding unified save buttons per section: ## Changes Made ### Component Updates - **TimeZoneSelector**: Converted to controlled component with value/onChange props * Removed internal state management and save button * Removed success/error alerts (now handled by parent) * Added auto-detect as simple button without save - **TimeFormatSelector**: Converted to controlled component with value/onChange props * Removed internal state management and save button * Removed success/error alerts (now handled by parent) * Simplified to just radio buttons with preview ### Settings Page Improvements - Added timezone and timeFormat to local state - Created separate save handlers: * `handleSaveProfile` - for name/email changes * `handleSavePreferences` - for timezone and time format - Three clear sections with dedicated save buttons: 1. **Profile Information** β†’ "Save Profile" button 2. **Preferences** (Language, Units, Timezone, Time Format) β†’ "Save Preferences" button 3. **Notifications** β†’ "Save Notification Settings" button ### User Experience Benefits - Clearer separation between different types of settings - Single save action per logical section instead of multiple buttons - Consistent save pattern across all settings cards - Reduced visual clutter with fewer buttons on page - Better organization: related settings grouped with one save action Files changed: 3 files (TimeZoneSelector, TimeFormatSelector, settings page) πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- ..._IMPLEMENTATION_PLAN-20251003115405.029.md | 784 ++++++++++++++++++ docs/LOCALIZATION_IMPLEMENTATION_PLAN.md | 1 + maternal-web/app/settings/page.tsx | 59 +- .../settings/TimeFormatSelector.tsx | 79 +- .../components/settings/TimeZoneSelector.tsx | 84 +- 5 files changed, 878 insertions(+), 129 deletions(-) create mode 100644 docs/.duckversions/LOCALIZATION_IMPLEMENTATION_PLAN-20251003115405.029.md diff --git a/docs/.duckversions/LOCALIZATION_IMPLEMENTATION_PLAN-20251003115405.029.md b/docs/.duckversions/LOCALIZATION_IMPLEMENTATION_PLAN-20251003115405.029.md new file mode 100644 index 0000000..8949da6 --- /dev/null +++ b/docs/.duckversions/LOCALIZATION_IMPLEMENTATION_PLAN-20251003115405.029.md @@ -0,0 +1,784 @@ +# Localization Implementation Plan + +**Created**: October 3, 2025 +**Priority**: HIGH (Pre-Launch) +**Estimated Duration**: 2-3 days + +## Overview + +Implement comprehensive internationalization (i18n) support for the Maternal App with 5 languages and measurement unit preferences. + +## Supported Languages + +1. **English (en-US)** - Primary/Default +2. **Spanish (es-ES)** +3. **French (fr-FR)** +4. **Portuguese (pt-BR)** +5. **Simplified Chinese (zh-CN)** + +## Current Status - Updated October 3, 2025 + +### βœ… Already Completed (Backend) +- Backend multilanguage support for AI responses +- AI safety responses in 5 languages +- MultiLanguageService with language detection + +### βœ… Completed (Frontend - Phases 1-9) +- βœ… **Phase 1**: i18next framework installed and configured +- βœ… **Phase 2**: Translation files structure created (40 files: 5 languages Γ— 8 namespaces) +- βœ… **Phase 3**: Custom hooks created (useTranslation, useLocale, useFormatting) +- βœ… **Phase 4**: Backend user schema updated with measurementUnit in preferences JSONB +- βœ… **Phase 5**: Language & measurement selectors in Settings page +- βœ… **Phase 7**: Settings page fully localized with preferences +- βœ… **Phase 8**: Measurement unit conversion utilities implemented +- βœ… **Phase 9**: Applied localization to core pages: + - Login & authentication pages + - Dashboard with welcome message, quick actions, summary + - Navigation (AppShell, MobileNav, TabBar) + - Track main page (activity selection) + - Children page (with age formatting & pluralization) + - All connection status indicators + +### βœ… Translation Files Created (40 files) +- `common.json` - UI strings, navigation, connection (all 5 languages) +- `auth.json` - Authentication pages (all 5 languages) +- `dashboard.json` - Dashboard/home page (all 5 languages) +- `tracking.json` - Activity tracking (all 5 languages) +- `children.json` - Child management (all 5 languages) +- `settings.json` - Settings page (all 5 languages) +- `ai.json` - AI assistant (all 5 languages) +- `errors.json` - Error messages (all 5 languages) + +### ⏳ Remaining To Be Implemented +- Language selector in onboarding flow (Phase 6) +- Individual tracking pages (feeding, sleep, diaper, medicine) with unit conversions (Phase 12) +- Family management page localization +- Analytics/insights page localization +- Date/time localization throughout app (Phase 10) +- Number formatting per locale (Phase 11) +- Professional translation review (Phase 13.2) +- Comprehensive testing (Phase 14) + +--- + +## Phase 1: Framework Setup βœ… COMPLETED + +### 1.1 Install Dependencies - latest versions only! βœ… +**Files**: `package.json` + +```bash +npm install i18next react-i18next i18next-browser-languagedetector +npm install date-fns # Already installed, confirm locales +``` + +**Packages**: +- `i18next` - Core i18n framework +- `react-i18next` - React bindings +- `i18next-browser-languagedetector` - Auto-detect user language + +### 1.2 Create i18n Configuration βœ… +**File**: `lib/i18n/config.ts` (CREATED) + +- βœ… Initialize i18next +- βœ… Configure language detector +- βœ… Set up fallback language (en-US) +- βœ… Configure interpolation +- βœ… Load translation resources + +### 1.3 Create i18n Provider βœ… +**File**: `components/providers/I18nProvider.tsx` (CREATED) + +- βœ… Wrap app with I18nextProvider +- βœ… Initialize i18n on mount +- βœ… Handle language loading states + +### 1.4 Update Root Layout βœ… +**File**: `app/layout.tsx` (MODIFIED) + +- βœ… Add I18nProvider to provider stack +- ⏳ Set html lang attribute dynamically (TODO) + +--- + +## Phase 2: Translation Files Structure βœ… COMPLETED + +### 2.1 Directory Structure +``` +locales/ +β”œβ”€β”€ en/ +β”‚ β”œβ”€β”€ common.json # Common UI strings (buttons, labels, etc.) +β”‚ β”œβ”€β”€ auth.json # Authentication pages +β”‚ β”œβ”€β”€ dashboard.json # Dashboard/home page +β”‚ β”œβ”€β”€ tracking.json # Activity tracking +β”‚ β”œβ”€β”€ children.json # Child management +β”‚ β”œβ”€β”€ family.json # Family management +β”‚ β”œβ”€β”€ ai.json # AI assistant +β”‚ β”œβ”€β”€ analytics.json # Analytics/insights +β”‚ β”œβ”€β”€ settings.json # Settings page +β”‚ β”œβ”€β”€ onboarding.json # Onboarding flow +β”‚ β”œβ”€β”€ errors.json # Error messages +β”‚ └── validation.json # Form validation messages +β”œβ”€β”€ es/ +β”‚ └── [same structure] +β”œβ”€β”€ fr/ +β”‚ └── [same structure] +β”œβ”€β”€ pt/ +β”‚ └── [same structure] +└── zh/ + └── [same structure] +``` + +### 2.2 Translation Keys Structure + +**Example - common.json**: +```json +{ + "nav": { + "dashboard": "Dashboard", + "track": "Track", + "children": "Children", + "family": "Family", + "ai": "AI Assistant", + "analytics": "Analytics", + "settings": "Settings" + }, + "buttons": { + "save": "Save", + "cancel": "Cancel", + "delete": "Delete", + "edit": "Edit", + "add": "Add", + "submit": "Submit", + "back": "Back", + "next": "Next", + "confirm": "Confirm" + }, + "units": { + "metric": "Metric", + "imperial": "Imperial", + "ml": "ml", + "oz": "oz", + "cm": "cm", + "in": "in", + "kg": "kg", + "lb": "lb" + } +} +``` + +--- + +## Phase 3: Custom Hooks βœ… COMPLETED + +### 3.1 useTranslation Hook βœ… +**File**: `hooks/useTranslation.ts` (CREATED) + +- βœ… Re-export from react-i18next with type safety +- βœ… Custom hook for easier usage + +### 3.2 useLocale Hook βœ… +**File**: `hooks/useLocale.ts` (CREATED) + +- βœ… Get current locale +- βœ… Change locale +- βœ… Get available locales +- βœ… Get locale display name +- βœ… Measurement system management + +### 3.3 useFormatting Hook βœ… +**File**: `hooks/useFormatting.ts` (CREATED) + +- βœ… Format dates based on locale +- βœ… Format numbers based on locale +- βœ… Format currency based on locale +- βœ… Format units based on preference + +--- + +## Phase 4: Measurement Unit Preference βœ… COMPLETED + +### 4.1 Backend Schema Update βœ… +**Implementation**: Used existing `preferences` JSONB column from V005_add_user_preferences.sql +- No new migration needed - reused existing preferences column +- Added `measurementUnit` as optional field in preferences object + +### 4.2 Update User Entity βœ… +**File**: `src/database/entities/user.entity.ts` (MODIFIED) + +Added to preferences type: +```typescript +@Column({ type: 'jsonb', nullable: true }) +preferences?: { + notifications?: boolean; + emailUpdates?: boolean; + darkMode?: boolean; + measurementUnit?: 'metric' | 'imperial'; +}; +``` + +### 4.3 Update User DTOs βœ… +**File**: `src/modules/auth/dto/update-profile.dto.ts` (CREATED) + +Created with validation: +```typescript +export class UserPreferencesDto { + @IsOptional() + @IsIn(['metric', 'imperial']) + measurementUnit?: 'metric' | 'imperial'; +} +``` + +### 4.4 API Endpoints βœ… +**File**: `src/modules/auth/auth.controller.ts` (MODIFIED) + +- PATCH `/api/v1/auth/profile` - Updated to use UpdateProfileDto with measurementUnit support +- Properly typed and validated + +--- + +## Phase 5: Frontend User Preferences βœ… PARTIALLY COMPLETED + +### 5.1 Redux State ⏳ +**File**: `store/slices/userSlice.ts` (TODO) + +Add to user state: +```typescript +interface UserState { + // ... existing fields + language: string; + measurementUnit: 'metric' | 'imperial'; +} +``` + +### 5.2 Language Selector Component βœ… +**File**: `components/settings/LanguageSelector.tsx` (CREATED) + +Features: +- βœ… Dropdown with 5 language options +- βœ… Shows language names in native script +- βœ… Updates user preference on change +- ⏳ Persists to backend (pending backend schema) +- βœ… Updates i18n instance + +### 5.3 Measurement Unit Selector Component βœ… +**File**: `components/settings/MeasurementUnitSelector.tsx` (CREATED) + +Features: +- βœ… Dropdown (Metric/Imperial) +- βœ… Updates user preference on change +- ⏳ Persists to backend (pending backend schema) +- βœ… Shows unit examples + +--- + +## Phase 6: Onboarding Flow Integration ❌ TODO + +### 6.1 Update Onboarding Page +**File**: `app/(auth)/onboarding/page.tsx` (MODIFY) + +Add new steps: +1. Language selection (Step 2) +2. Measurement unit selection (Step 3) + +### 6.2 Onboarding Language Step Component +**File**: `components/onboarding/LanguageStep.tsx` (NEW) + +- Large, visual language selector +- Show language names in native script +- Save to Redux state +- Update i18n immediately + +### 6.3 Onboarding Measurement Step Component +**File**: `components/onboarding/MeasurementStep.tsx` (NEW) + +- Visual metric/imperial selector +- Show examples (ml vs oz, cm vs in) +- Save to Redux state + +### 6.4 Update Onboarding API Call +**File**: `app/(auth)/onboarding/page.tsx` (MODIFY) + +Include language and measurementUnit in profile update call. + +--- + +## Phase 7: Settings Page Integration βœ… COMPLETED + +### 7.1 Update Settings Page βœ… +**File**: `app/settings/page.tsx` (MODIFIED) + +Add new sections: +- βœ… **Preferences** section + - βœ… Language selector + - βœ… Measurement unit selector + +### 7.2 Settings UI Components βœ… +**Files**: +- βœ… `components/settings/LanguageSelector.tsx` (CREATED) +- βœ… `components/settings/MeasurementUnitSelector.tsx` (CREATED) + +--- + +## Phase 8: Unit Conversion Utilities βœ… COMPLETED + +### 8.1 Conversion Utility βœ… +**File**: `lib/utils/unitConversion.ts` (CREATED) + +Functions: +- βœ… `convertWeight` / `convertWeightToKg` +- βœ… `convertHeight` / `convertHeightToCm` +- βœ… `convertTemperature` / `convertTemperatureToCelsius` +- βœ… `convertVolume` / `convertVolumeToMl` +- βœ… `getUnitSymbol` +- βœ… `getConversionFactor` + +### 8.2 Format Unit Hook βœ… +**File**: `hooks/useFormatting.ts` (CREATED) + +- βœ… Get user's measurement preference +- βœ… Format value with correct unit +- βœ… Convert between units +- βœ… Display with locale-specific formatting + +--- + +## Phase 9: Apply Localization Throughout App βœ… PARTIALLY COMPLETED + +### 9.1 Update All Pages - Status + +**βœ… Completed Priority Pages**: +1. βœ… **Dashboard** (`app/page.tsx`) + - Welcome message with user name interpolation + - Quick actions (all 6 activity cards) + - Today's summary with child name interpolation + - Next predicted activity with variable interpolation + - All UI labels translated in 5 languages + +2. βœ… **Authentication** (`app/(auth)/login/page.tsx`) + - Login form labels (email, password) + - Submit button, forgot password link + - Social login buttons (Google, Apple) + - Biometric authentication (Face ID/Touch ID) + - Sign up link and all helper text + +3. βœ… **Track Main Page** (`app/track/page.tsx`) + - Track Activity title and subtitle + - All 5 activity type labels (Feeding, Sleep, Diaper, Medicine, Activity) + - Translated in all 5 languages + +4. βœ… **Children** (`app/children/page.tsx`) + - Page title and subtitle + - Add/Edit child buttons + - Empty state messages + - Age calculation with proper pluralization (year/years, month/months) + - All error messages + - Gender labels + +**❌ Remaining Pages**: +5. ⏳ **Individual Tracking Pages** (`app/track/feeding/`, etc.) + - Feeding, Sleep, Diaper, Medicine detail pages + - Form labels, unit labels + - Apply unit conversions (Phase 12) + +6. ❌ **Family** (`app/family/page.tsx`) + - Family management labels + +7. ⏳ **AI Assistant** (`app/ai-assistant/page.tsx`) + - Chat interface already uses AI translations from backend + - Frontend labels may need localization + +8. ❌ **Analytics** (`app/analytics/page.tsx`) + - Chart labels, insights + +9. βœ… **Settings** (`app/settings/page.tsx`) + - Already completed in Phase 7 + +### 9.2 Update Components - Status + +**βœ… Completed Components**: +- βœ… `components/layouts/AppShell/AppShell.tsx` - Connection status, presence indicators +- βœ… `components/layouts/MobileNav/MobileNav.tsx` - Navigation menu items, logout +- βœ… `components/layouts/TabBar/TabBar.tsx` - Bottom navigation tabs +- βœ… `components/settings/LanguageSelector.tsx` - Language preference UI +- βœ… `components/settings/MeasurementUnitSelector.tsx` - Measurement preference UI + +**❌ Remaining Components**: +- ⏳ `components/common/` - Common dialogs, buttons (may need review) +- ⏳ `components/features/` - Activity cards, forms (need review) +- ⏳ `components/children/` - Child dialogs (ChildDialog, DeleteConfirmDialog) +- ⏳ `components/family/` - Family components + +--- + +## Phase 10: Date/Time Localization + +### 10.1 Date Formatting Utility +**File**: `lib/i18n/date-formats.ts` (NEW) + +Using date-fns with locales: +```typescript +import { format } from 'date-fns'; +import { enUS, es, fr, ptBR, zhCN } from 'date-fns/locale'; + +const locales = { en: enUS, es, fr, pt: ptBR, zh: zhCN }; + +export function formatDate(date: Date, formatStr: string, locale: string) { + return format(date, formatStr, { locale: locales[locale] }); +} +``` + +### 10.2 Update Date Displays +Apply locale-aware date formatting: +- Activity timestamps +- Child birth dates +- Analytics date ranges + +--- + +## Phase 11: Number Localization + +### 11.1 Number Formatting Utility +**File**: `lib/i18n/number-formats.ts` (NEW) + +Using Intl.NumberFormat: +```typescript +export function formatNumber(value: number, locale: string) { + return new Intl.NumberFormat(locale).format(value); +} + +export function formatDecimal(value: number, locale: string, decimals = 1) { + return new Intl.NumberFormat(locale, { + minimumFractionDigits: decimals, + maximumFractionDigits: decimals, + }).format(value); +} +``` + +### 11.2 Apply Number Formatting +- Activity amounts (ml/oz) +- Sleep duration (hours) +- Weight/height measurements + +--- + +## Phase 12: Tracking Forms with Unit Preferences + +### 12.1 Update Feeding Form +**File**: `app/track/feeding/page.tsx` (MODIFY) + +- Show ml or oz input based on preference +- Convert to metric for API storage +- Display in user's preferred unit + +### 12.2 Update Child Profile Form +**File**: `app/children/page.tsx` (MODIFY) + +- Weight input (kg/lb) +- Height input (cm/in) +- Convert for API storage + +### 12.3 Universal Input Component +**File**: `components/forms/UnitInput.tsx` (NEW) + +Props: +- `type: 'volume' | 'weight' | 'height'` +- `value: number` +- `onChange: (value: number) => void` +- Auto-convert based on user preference + +--- + +## Phase 13: Translation Files Content + +### 13.1 English Translations (Base) +**Estimate**: ~500-800 translation keys across all files + +Create complete English translations for: +- All UI strings +- All form labels/placeholders +- All error messages +- All validation messages + +### 13.2 Professional Translation +**Recommendation**: Use professional translation service for: +- Spanish (es-ES) +- French (fr-FR) +- Portuguese (pt-BR) +- Simplified Chinese (zh-CN) + +**Alternative**: Use AI-assisted translation + native speaker review + +--- + +## Phase 14: Testing + +### 14.1 Translation Coverage Test +- Verify all strings are externalized +- No hardcoded English strings remain +- All translation keys exist in all languages + +### 14.2 Language Switching Test +- Switch between all 5 languages +- Verify UI updates correctly +- Verify no layout breaks + +### 14.3 Unit Conversion Test +- Test all conversions (ml↔oz, kg↔lb, cm↔in) +- Verify correct rounding +- Verify storage in consistent units (metric) + +### 14.4 Date/Time Test +- Verify dates display correctly per locale +- Test all date formats (short, long, relative) + +### 14.5 RTL Support (Future) +**Note**: Chinese doesn't require RTL, but document for future Arabic support + +--- + +## Phase 15: Documentation + +### 15.1 Update Implementation Gaps +**File**: `docs/implementation-gaps.md` (MODIFY) + +Mark localization as βœ… COMPLETED + +### 15.2 Developer Guide +**File**: `docs/LOCALIZATION_GUIDE.md` (NEW) + +Document: +- How to add new translation keys +- How to add new languages +- Translation file structure +- Best practices + +### 15.3 Update User Documentation +Document language and measurement preferences in user guide + +--- + +## Implementation Order + +### Day 1: Framework & Structure +1. βœ… Install dependencies +2. βœ… Create i18n configuration +3. βœ… Create i18n provider +4. βœ… Create hooks (useTranslation, useLocale, useFormatting) +5. βœ… Set up translation file structure +6. βœ… Create English base translations (common, auth, dashboard) + +### Day 2: Preferences & Components +7. βœ… Backend schema update for measurementUnit +8. βœ… Update User entity and DTOs +9. βœ… Create LanguageSelector component +10. βœ… Create MeasurementUnitSelector component +11. βœ… Update onboarding flow +12. βœ… Update settings page +13. βœ… Create unit conversion utilities + +### Day 3: Apply Throughout App +14. βœ… Update all pages with translations +15. βœ… Apply unit conversions to tracking forms +16. βœ… Apply date/time localization +17. βœ… Complete all English translations +18. βœ… Test language switching +19. βœ… Test unit conversions + +### Post-Implementation (Optional) +20. Professional translation for other 4 languages +21. Native speaker review +22. Documentation updates + +--- + +## Key Files Checklist + +### New Files (27 total) +- [ ] `lib/i18n/config.ts` +- [ ] `components/providers/I18nProvider.tsx` +- [ ] `hooks/useLocale.ts` +- [ ] `hooks/useFormatting.ts` +- [ ] `hooks/useUnitFormat.ts` +- [ ] `lib/units/conversions.ts` +- [ ] `lib/i18n/date-formats.ts` +- [ ] `lib/i18n/number-formats.ts` +- [ ] `components/settings/LanguageSelector.tsx` +- [ ] `components/settings/MeasurementUnitSelector.tsx` +- [ ] `components/settings/LanguageSettings.tsx` +- [ ] `components/settings/MeasurementSettings.tsx` +- [ ] `components/onboarding/LanguageStep.tsx` +- [ ] `components/onboarding/MeasurementStep.tsx` +- [ ] `components/forms/UnitInput.tsx` +- [ ] `locales/en/*.json` (11 files) +- [ ] `locales/es/*.json` (11 files - future) +- [ ] `locales/fr/*.json` (11 files - future) +- [ ] `locales/pt/*.json` (11 files - future) +- [ ] `locales/zh/*.json` (11 files - future) +- [ ] `src/database/migrations/V0XX_add_measurement_preference.sql` +- [ ] `docs/LOCALIZATION_GUIDE.md` + +### Modified Files (15 total) +- [ ] `package.json` +- [ ] `app/layout.tsx` +- [ ] `store/slices/userSlice.ts` +- [ ] `src/database/entities/user.entity.ts` +- [ ] `src/modules/auth/dto/register.dto.ts` +- [ ] `src/modules/auth/dto/update-profile.dto.ts` +- [ ] `app/(auth)/onboarding/page.tsx` +- [ ] `app/settings/page.tsx` +- [ ] `app/page.tsx` (dashboard) +- [ ] `app/track/feeding/page.tsx` +- [ ] `app/track/sleep/page.tsx` +- [ ] `app/track/diaper/page.tsx` +- [ ] `app/children/page.tsx` +- [ ] `app/family/page.tsx` +- [ ] `docs/implementation-gaps.md` + +--- + +## Success Criteria + +### βœ… Functionality +- [ ] All 5 languages selectable and functional +- [ ] Language preference persists across sessions +- [ ] Measurement unit preference persists across sessions +- [ ] All UI strings externalized (no hardcoded English) +- [ ] Unit conversions work correctly (ml↔oz, kg↔lb, cm↔in) +- [ ] Dates display correctly per locale +- [ ] Numbers format correctly per locale + +### βœ… User Experience +- [ ] Language can be selected in onboarding +- [ ] Language can be changed in settings +- [ ] Measurement unit can be selected in onboarding +- [ ] Measurement unit can be changed in settings +- [ ] UI updates immediately when language changes +- [ ] No layout breaks when changing languages +- [ ] Form inputs show correct units based on preference + +### βœ… Technical +- [ ] All translation keys defined +- [ ] No missing translation warnings +- [ ] Type-safe translation usage (TypeScript) +- [ ] Backend stores preferences correctly +- [ ] Redux state syncs with backend + +--- + +## Notes + +### Translation Best Practices +1. Use namespaces to organize translations +2. Use interpolation for dynamic values: `t('welcome', { name })` +3. Use pluralization: `t('items', { count })` +4. Keep keys descriptive: `auth.login.emailLabel` not `auth.e1` +5. Avoid concatenation, use complete sentences + +### Unit Conversion Strategy +- **Storage**: Always store in metric (ml, kg, cm) in database +- **Display**: Convert to user's preferred unit for display +- **Input**: Accept user's preferred unit, convert to metric before API call +- **Consistency**: Ensure all measurements use the same preference + +### Performance Considerations +- Lazy load translation files per namespace +- Cache translations in browser +- Preload critical translations (common, errors) + +### Future Enhancements +- Add more languages (Arabic, German, Hindi, etc.) +- Add more units (temperature: Β°C/Β°F) +- Add regional date formats (MM/DD vs DD/MM) +- Add time format preferences (12h vs 24h) + +--- + +## Remaining Tasks Summary + +### πŸ”΄ High Priority (Core Functionality) + +1. **Individual Tracking Pages with Unit Conversions** (Phase 12) + - `/app/track/feeding/page.tsx` - Volume conversion (ml ↔ oz) + - `/app/track/sleep/page.tsx` - Duration formatting + - `/app/track/diaper/page.tsx` - Type labels + - `/app/track/medicine/page.tsx` - Dosage with units + - Implement UnitInput component for automatic conversion + - **Estimated Effort**: 4-6 hours + +2. **Child Dialog Components Localization** + - `components/children/ChildDialog.tsx` - Form labels + - `components/children/DeleteConfirmDialog.tsx` - Confirmation text + - **Estimated Effort**: 1 hour + +3. **Date/Time Localization** (Phase 10) + - Apply date-fns with locale to all date displays + - Activity timestamps + - Child birth dates + - Analytics date ranges + - **Estimated Effort**: 2-3 hours + +### 🟑 Medium Priority (Nice to Have) + +4. **Onboarding Flow** (Phase 6) + - Add language selection step + - Add measurement unit selection step + - Save preferences during onboarding + - **Estimated Effort**: 2-3 hours + +5. **Family Management Page** + - `app/family/page.tsx` localization + - Family member labels, invitation flow + - **Estimated Effort**: 1-2 hours + +6. **Number Formatting** (Phase 11) + - Apply Intl.NumberFormat throughout + - Weight/height values + - Activity counts + - **Estimated Effort**: 1-2 hours + +### 🟒 Low Priority (Future Enhancements) + +7. **Analytics/Insights Page** + - Chart labels + - Insight descriptions + - **Estimated Effort**: 2-3 hours + +8. **Professional Translation Review** (Phase 13.2) + - Review all 4 non-English languages + - Native speaker validation + - Cultural appropriateness check + - **Estimated Effort**: External service, 1-2 weeks + +9. **Comprehensive Testing** (Phase 14) + - Translation coverage test + - Language switching test + - Unit conversion test + - Date/time formatting test + - **Estimated Effort**: 2-4 hours + +10. **Documentation** (Phase 15) + - Create LOCALIZATION_GUIDE.md + - Update implementation-gaps.md + - Developer best practices + - **Estimated Effort**: 1-2 hours + +### πŸ“Š Progress Tracking + +**Completed**: 9 phases (1-5, 7-9) +**In Progress**: 0 phases +**Remaining**: 6 major phases (6, 10-15) + +**Overall Completion**: ~65% (core functionality) + +**Estimated Time to Full Completion**: +- High Priority: 8-11 hours +- Medium Priority: 4-7 hours +- Low Priority: 5-9 hours +- **Total Remaining**: 17-27 hours (2-3.5 days) + +--- + +**Total Project Effort**: 2-3 days (completed) + 2-3.5 days (remaining) = 4-6.5 days +**Complexity**: Medium +**Priority**: HIGH (Pre-Launch) +**Current Status**: Core functionality 65% complete, production-ready for MVP diff --git a/docs/LOCALIZATION_IMPLEMENTATION_PLAN.md b/docs/LOCALIZATION_IMPLEMENTATION_PLAN.md index 8949da6..2c39173 100644 --- a/docs/LOCALIZATION_IMPLEMENTATION_PLAN.md +++ b/docs/LOCALIZATION_IMPLEMENTATION_PLAN.md @@ -54,6 +54,7 @@ Implement comprehensive internationalization (i18n) support for the Maternal App - Individual tracking pages (feeding, sleep, diaper, medicine) with unit conversions (Phase 12) - Family management page localization - Analytics/insights page localization +- Settings page localization - Date/time localization throughout app (Phase 10) - Number formatting per locale (Phase 11) - Professional translation review (Phase 13.2) diff --git a/maternal-web/app/settings/page.tsx b/maternal-web/app/settings/page.tsx index 3851e36..faf8cfa 100644 --- a/maternal-web/app/settings/page.tsx +++ b/maternal-web/app/settings/page.tsx @@ -22,6 +22,8 @@ import { motion } from 'framer-motion'; export default function SettingsPage() { const { user, logout, refreshUser } = useAuth(); const [name, setName] = useState(user?.name || ''); + const [timezone, setTimezone] = useState(user?.timezone || 'UTC'); + const [timeFormat, setTimeFormat] = useState<'12h' | '24h'>(user?.preferences?.timeFormat || '12h'); const [settings, setSettings] = useState({ notifications: true, emailUpdates: false, @@ -40,17 +42,48 @@ export default function SettingsPage() { emailUpdates: user.preferences.emailUpdates ?? false, darkMode: user.preferences.darkMode ?? false, }); + setTimeFormat(user.preferences.timeFormat || '12h'); } }, [user?.preferences]); - // Sync name state when user data changes + // Sync name and timezone state when user data changes useEffect(() => { if (user?.name) { setName(user.name); } + if (user?.timezone) { + setTimezone(user.timezone); + } }, [user]); - const handleSave = async () => { + const handleSavePreferences = async () => { + setIsLoading(true); + setError(null); + + try { + const response = await usersApi.updateProfile({ + timezone, + preferences: { + ...settings, + timeFormat, + } + }); + console.log('βœ… Preferences updated successfully:', response); + + // Refresh user to get latest data from server + await refreshUser(); + + setSuccessMessage('Preferences saved successfully!'); + } catch (err: any) { + console.error('❌ Failed to update preferences:', err); + console.error('Error response:', err.response); + setError(err.response?.data?.message || err.message || 'Failed to save preferences. Please try again.'); + } finally { + setIsLoading(false); + } + }; + + const handleSaveProfile = async () => { // Validate name if (!name || name.trim() === '') { setNameError('Name cannot be empty'); @@ -64,7 +97,6 @@ export default function SettingsPage() { try { const response = await usersApi.updateProfile({ name: name.trim(), - preferences: settings }); console.log('βœ… Profile updated successfully:', response); @@ -143,11 +175,11 @@ export default function SettingsPage() { @@ -170,10 +202,19 @@ export default function SettingsPage() { - + - + + @@ -214,11 +255,11 @@ export default function SettingsPage() { diff --git a/maternal-web/components/settings/TimeFormatSelector.tsx b/maternal-web/components/settings/TimeFormatSelector.tsx index 381c8fc..3a2821b 100644 --- a/maternal-web/components/settings/TimeFormatSelector.tsx +++ b/maternal-web/components/settings/TimeFormatSelector.tsx @@ -1,53 +1,33 @@ 'use client'; -import { useState } from 'react'; +import { useEffect } from 'react'; import { Box, FormControl, RadioGroup, FormControlLabel, Radio, - Button, - CircularProgress, - Alert, Typography, } from '@mui/material'; -import { Save, Schedule } from '@mui/icons-material'; +import { Schedule } from '@mui/icons-material'; import { useAuth } from '@/lib/auth/AuthContext'; -import { usersApi } from '@/lib/api/users'; import { useTranslation } from '@/hooks/useTranslation'; -export function TimeFormatSelector() { - const { user, refreshUser } = useAuth(); +interface TimeFormatSelectorProps { + value: '12h' | '24h'; + onChange: (timeFormat: '12h' | '24h') => void; +} + +export function TimeFormatSelector({ value, onChange }: TimeFormatSelectorProps) { + const { user } = useAuth(); const { t } = useTranslation('settings'); - const [timeFormat, setTimeFormat] = useState<'12h' | '24h'>( - user?.preferences?.timeFormat || '12h' - ); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - const [successMessage, setSuccessMessage] = useState(null); - const handleSave = async () => { - setIsLoading(true); - setError(null); - setSuccessMessage(null); - - try { - await usersApi.updateProfile({ - preferences: { - ...user?.preferences, - timeFormat, - }, - }); - await refreshUser(); - setSuccessMessage('Time format updated successfully'); - } catch (err: any) { - console.error('Failed to update time format:', err); - setError(err.response?.data?.message || 'Failed to update time format'); - } finally { - setIsLoading(false); + // Initialize with user's time format on mount + useEffect(() => { + if (user?.preferences?.timeFormat && !value) { + onChange(user.preferences.timeFormat); } - }; + }, [user?.preferences?.timeFormat]); const currentTime = new Date(); const preview12h = currentTime.toLocaleTimeString('en-US', { @@ -70,26 +50,14 @@ export function TimeFormatSelector() { - {error && ( - setError(null)}> - {error} - - )} - - {successMessage && ( - setSuccessMessage(null)}> - {successMessage} - - )} - - + setTimeFormat(e.target.value as '12h' | '24h')} + value={value || user?.preferences?.timeFormat || '12h'} + onChange={(e) => onChange(e.target.value as '12h' | '24h')} > } + control={} label={ 12-hour format @@ -101,7 +69,7 @@ export function TimeFormatSelector() { /> } + control={} label={ 24-hour format @@ -113,15 +81,6 @@ export function TimeFormatSelector() { /> - - ); } diff --git a/maternal-web/components/settings/TimeZoneSelector.tsx b/maternal-web/components/settings/TimeZoneSelector.tsx index 17aa6cf..a637163 100644 --- a/maternal-web/components/settings/TimeZoneSelector.tsx +++ b/maternal-web/components/settings/TimeZoneSelector.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState } from 'react'; +import { useEffect } from 'react'; import { Box, FormControl, @@ -8,13 +8,10 @@ import { Select, MenuItem, Button, - CircularProgress, - Alert, Typography, } from '@mui/material'; -import { Save, AccessTime } from '@mui/icons-material'; +import { AccessTime } from '@mui/icons-material'; import { useAuth } from '@/lib/auth/AuthContext'; -import { usersApi } from '@/lib/api/users'; import { useTranslation } from '@/hooks/useTranslation'; // Common timezones grouped by region @@ -71,35 +68,25 @@ const TIMEZONES = { ], }; -export function TimeZoneSelector() { - const { user, refreshUser } = useAuth(); +interface TimeZoneSelectorProps { + value: string; + onChange: (timezone: string) => void; +} + +export function TimeZoneSelector({ value, onChange }: TimeZoneSelectorProps) { + const { user } = useAuth(); const { t } = useTranslation('settings'); - const [timezone, setTimezone] = useState(user?.timezone || 'UTC'); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - const [successMessage, setSuccessMessage] = useState(null); - const handleSave = async () => { - setIsLoading(true); - setError(null); - setSuccessMessage(null); - - try { - await usersApi.updateProfile({ timezone }); - await refreshUser(); - setSuccessMessage('Timezone updated successfully'); - } catch (err: any) { - console.error('Failed to update timezone:', err); - setError(err.response?.data?.message || 'Failed to update timezone'); - } finally { - setIsLoading(false); + // Initialize with user's timezone on mount + useEffect(() => { + if (user?.timezone && !value) { + onChange(user.timezone); } - }; + }, [user?.timezone]); const handleAutoDetect = () => { const detectedTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; - setTimezone(detectedTimezone); - setSuccessMessage(`Auto-detected timezone: ${detectedTimezone}`); + onChange(detectedTimezone); }; return ( @@ -111,25 +98,12 @@ export function TimeZoneSelector() { - {error && ( - setError(null)}> - {error} - - )} - - {successMessage && ( - setSuccessMessage(null)}> - {successMessage} - - )} - Time Zone - - - - + ); }