- Update ExchangeService to load pricing config from database
- Update validate_eur_amount to use direction-specific limits
- Update apply_premium_for_direction to calculate base + extra premium
- Update create_exchange to use new premium calculation
- Add tests for premium calculation (small trade extra, large trade base only, direction-specific)
- Update existing tests to account for new premium calculation
- Update ExchangeConfigResponse schema with direction-specific fields
- Remove premium_percentage from PriceResponse (now in config)
- Update price endpoint to load pricing config from database
- Update frontend to use direction-specific min/max and calculate premium
- Update tests to seed pricing config
- Add logic to clamp amount when direction changes
- Add pricing API functions to admin.ts
- Create admin pricing page with form and validation
- Add MANAGE_PRICING permission to auth context
- Add pricing to admin navigation
- Add translations for pricing page (en, ca, es)
- Update PageLayout and Header types for new page
- Add PricingConfigResponse and PricingConfigUpdate schemas
- Create PricingService with validation logic
- Add GET and PUT endpoints in routes/pricing.py
- Add MANAGE_PRICING permission to admin role
- Register pricing router in main.py
- Add comprehensive API tests for permissions and validation
- Add seed_pricing_config() function to seed.py
- Seed initial values from shared/constants.json
- Add seeding to seed_e2e.py for E2E tests
- Add tests for seeding functionality
- Create PricingConfig model with all required fields (premium settings, trade limits)
- Implement PricingRepository with singleton pattern (get_current, create_or_update)
- Add comprehensive tests for repository functionality
- Export model and repository in __init__.py files
- Create validate-translations.js script to check all translation keys exist in all locales
- Add translation validation to pre-commit hook
- Validates that es, en, ca translation files have matching keys
- Blocks commits if translations are missing or inconsistent
- Prevents incomplete translations from being committed
- Update modal heading from 'Edit Availability' to 'Edit Time Slots'
- Update button text from 'Clear All' to 'Clear'
- Update button text from '+ Add Time Range' to '+ Add Slot'
- Update button text from 'Cancel' to 'Close'
- All 33 e2e tests now passing
- Create admin.json translation files for es, en, ca with all admin strings
- Update IntlProvider to include admin namespace
- Translate admin/invites/page.tsx - all strings now use translations
- Translate admin/trades/page.tsx - all strings now use translations
- Translate admin/price-history/page.tsx - all strings now use translations
- Translate admin/availability/page.tsx - all strings now use translations
- Add 'saving' key to common.json for all languages
- Fix linting errors: add t to useCallback dependencies
- All admin pages now fully multilingual
- Replace text-based selector that expected English weekdays with data-testid selector
- Dates now use Spanish locale (es-ES) formatting, so weekday text changed
- Using data-testid is more reliable and language-agnostic
- Fixes failing test: 'regular user can access exchange page, all UI elements work, and buy/sell toggle functions'
- Add timeZone='Europe/Madrid' to NextIntlClientProvider
- Fixes ENVIRONMENT_FALLBACK warning about missing timeZone configuration
- Ensures consistent timezone handling across SSR and client rendering
- Create HtmlLang client component that updates document.documentElement.lang
- Add HtmlLang component to layout.tsx to sync HTML lang attribute with locale
- HTML lang now updates automatically when user changes language
- Update all date/time formatting functions to use 'es-ES' locale instead of 'en-US' or 'de-DE'
- Update utility functions in utils/date.ts and utils/exchange.ts
- Update all component files that use date formatting
- Update e2e test helper to match new Spanish date format
- All formatting now uses Spanish locale regardless of selected language as per PR requirements
- Add context.addInitScript in beforeEach hooks to set English locale before page navigation
- Remove debugging code from useLanguage hook
- Remove unused setup file imports
- Fix exchange test to check for English text correctly
- All frontend tests passing
- Translate LoadingState and EmptyState components (common namespace)
- Translate Header navigation labels (navigation namespace)
- Translate StatusBadge trade status labels (exchange namespace)
- Create navigation.json translation files for es, en, ca
- Create exchange.json translation files for status/direction/transfer labels
- Update IntlProvider to load navigation and exchange namespaces
- Update frontend tests to expect Spanish translations (default language)
- Configure Playwright to use English language for e2e tests via storageState
- Fix test expectations to match translated strings
All frontend and e2e tests passing.
- Create LanguageSelector component with dropdown (shows flag + name)
- Add LanguageSelector to Header (right side, near user email/logout)
- Add LanguageSelector to login, signup, and signup/[code] pages
- Create test-utils.tsx with renderWithProviders helper
- Add vitest.setup.ts to mock localStorage
- Update all test files to use renderWithProviders
- Language selector persists choice in localStorage
- HTML lang attribute updates dynamically based on selected language
All frontend and e2e tests passing.
- Install next-intl package
- Create LanguageProvider hook with localStorage persistence
- Create IntlProvider component for next-intl integration
- Create Providers wrapper component
- Update layout.tsx to include providers and set default lang to 'es'
- Create initial translation files (common.json) for es, en, ca
- Fix pre-existing TypeScript errors in various pages
All tests passing, build successful.
- Created useAsyncData hook: Eliminates repetitive data fetching boilerplate
- Handles loading, error, and data state automatically
- Supports enabled/disabled fetching
- Provides refetch function
- Created PageLayout component: Standardizes page structure
- Handles loading state, authorization checks, header, error display
- Reduces ~10 lines of boilerplate per page
- Created useMutation hook: Simplifies action handling
- Manages loading state and errors for mutations
- Supports success/error callbacks
- Used for cancel, create, revoke actions
- Created ErrorDisplay component: Standardizes error UI
- Consistent error banner styling across app
- Integrated into PageLayout
- Created useForm hook: Foundation for form state management
- Handles form data, validation, dirty checking
- Ready for future form migrations
- Migrated pages to use new patterns:
- invites/page.tsx: useAsyncData + PageLayout
- trades/page.tsx: useAsyncData + PageLayout + useMutation
- trades/[id]/page.tsx: useAsyncData
- admin/price-history/page.tsx: useAsyncData + PageLayout
- admin/invites/page.tsx: useMutation for create/revoke
Benefits:
- ~40% reduction in boilerplate code
- Consistent patterns across pages
- Easier to maintain and extend
- Better type safety
All tests passing (32 frontend, 33 e2e)
- Created new api/ directory with domain-specific API modules:
- api/client.ts: Base API client with error handling
- api/auth.ts: Authentication endpoints
- api/exchange.ts: Exchange/price endpoints
- api/trades.ts: User trade endpoints
- api/profile.ts: Profile management endpoints
- api/invites.ts: Invite endpoints
- api/admin.ts: Admin endpoints
- api/index.ts: Centralized exports
- Migrated all API calls from ad-hoc api.get/post/put to typed domain APIs
- Updated all imports across codebase
- Fixed test mocks to use new API structure
- Fixed type issues in validation utilities
- Removed old api.ts file
Benefits:
- Type-safe endpoints (no more string typos)
- Centralized API surface (easy to discover endpoints)
- Better organization (domain-specific modules)
- Uses generated OpenAPI types automatically
Break down the 1300+ line Exchange page into smaller, focused components:
- Create useExchangePrice hook
- Handles price fetching and auto-refresh logic
- Manages price loading and error states
- Centralizes price-related state management
- Create useAvailableSlots hook
- Manages slot fetching and availability checking
- Handles date availability state
- Fetches availability when entering booking/confirmation steps
- Create PriceDisplay component
- Displays market price, agreed price, and premium
- Shows price update timestamp and stale warnings
- Handles loading and error states
- Create ExchangeDetailsStep component
- Step 1 of wizard: direction, payment method, amount selection
- Contains all form logic for trade details
- Validates and displays trade summary
- Create BookingStep component
- Step 2 of wizard: date and slot selection
- Shows trade summary card
- Handles date availability and existing trade warnings
- Create ConfirmationStep component
- Step 3 of wizard: final confirmation
- Shows compressed booking summary
- Displays all trade details for review
- Create StepIndicator component
- Visual indicator of current wizard step
- Shows completed and active steps
- Refactor ExchangePage
- Reduced from 1300+ lines to ~350 lines
- Uses new hooks and components
- Maintains all existing functionality
- Improved maintainability and testability
All frontend tests pass. Linting passes.
- Add commit() method to AvailabilityRepository for transaction control
- Add refresh() method to UserRepository
- Update AvailabilityService to use repository.commit() instead of db.commit()
- Update AuthService to use UserRepository.refresh() instead of db.refresh()
- All services now consistently delegate ALL persistence to repositories
- Add create() and update() methods to ExchangeRepository
- Update ExchangeService to use repository methods instead of direct db operations
- All persistence operations now go through repositories consistently
- Fix indentation errors in ExchangeService
- Add create(), update(), and reload_with_relationships() methods to InviteRepository
- Update InviteService to use repository methods instead of direct db operations
- Add create() and update() methods to UserRepository
- Update ProfileService to use repository.update()
- Update AuthService to use repository.create()
- Add create() and create_multiple() methods to AvailabilityRepository
- Update AvailabilityService to use repository methods instead of direct db operations
- Add get_available_slots() and _expand_availability_to_slots() to ExchangeService
- Update routes/exchange.py to use ExchangeService.get_available_slots()
- Remove all business logic from get_available_slots endpoint
- Add AvailabilityRepository to ExchangeService dependencies
- Add Availability and BookableSlot imports to ExchangeService
- Fix import path for validate_date_in_range (use date_validation module)
- Remove unused user_repo variable and import from routes/invites.py
- Fix mypy error in ValidationError by adding proper type annotation
- Create AvailabilityService with get_availability_for_range(), set_availability_for_date(), and copy_availability()
- Move slot validation logic to service
- Update routes/availability.py to use AvailabilityService
- Remove all direct database queries from routes
- Create PriceService with get_recent_prices() and fetch_and_store_price()
- Update routes/audit.py to use PriceService instead of direct queries
- Use PriceHistoryMapper consistently
- Update test to patch services.price.fetch_btc_eur_price