DateTime.toString() renders "2026-07-13 00:00:00.000Z" while the backend
sends "2026-07-13T00:00:00.000Z", so every day comparison was false and no
booked slot was ever detected. Both calendars showed fully free days.
The client calendar now also treats an unloaded agenda as busy instead of
free: rendering a taken slot as available leads straight to a double booking.
Note: the client still reads /services/professional/calendar, which returns
the *caller's* calendar. There is no endpoint for another professional's
agenda, so a client cannot yet see which of that professional's slots are
taken. That needs a backend endpoint.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
_isOccupied matched any service on the slot regardless of status. Unblocking
sets the service to cancelled, so a released slot still rendered as occupied
and the unblock looked like it did nothing. Cancelled and denied services are
now ignored, and self-booked slots get their own "Bloqueado" state with a
release action, mirroring the web.
Self-booking now spans the professional's configured slot duration instead of
a hardcoded two hours, which no longer matched the generated slots.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Root cause behind most "stuck loading" reports: the backend changed shape
(schedules became an array, location_preferences a string) while the mobile
parser still hard-cast to Map/int. The TypeError was swallowed by a silent
catch that returned null, and screens only handled the success state, so a
parse failure rendered as a permanent spinner. Same class of bug appeared
across service lists via non-null map lookups and a total absence of
request timeouts.
Ported from prosappweb:
- in-app suggestions (POST /suggestions)
- policies/terms from GET /settings/policies
- configurable appointment length (slot_duration_minutes)
- block/unblock calendar slots (POST /services/block)
- GPS city detection on the profile (Nominatim)
- server-side professional search with haversine distance
- retry cooldown after a rejected professional application
Reliability:
- parse schedules array (day_of_week 0=Mon) and string location_preferences
- read times as wall clock, so 08:00 stays 08:00 across timezones
- carry minutes into hours in TimeOfDay.add; a minute-based step used to
loop forever and freeze the calendar (covered by test/time_slots_test.dart)
- semver update check instead of string equality, which blocked every build
that did not exactly match the configured version
- request timeouts across all repositories
- surface HTTP >= 400 instead of reporting failed writes as success
- error states with retry instead of an indefinite shimmer
Includes pre-existing uncommitted work from the UI redesign.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Port the same card UI pattern from the redesigned solicitudes screen
to the 4 remaining list screens (pro/user active services + history):
- 4px colored left strip matching service status color
- Circular avatar with status-colored border + letter fallback
- Status badge with icon + colored border (replaces solid filled badges)
- Date/time row with calendar icon
- Theme-aware colors via _Th extension (respects dark/light mode)
- AppBar with brand blue (replaces default grey)
- Empty state with icon + descriptive text
- Shimmer placeholders as rounded cards (replaces ListTile shimmer)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Port the 3 redesigned views from prosappweb to prosappco:
- Solicitudes: modern cards with 4px colored left strip, status badge with
icon+border, avatar with status-colored ring, date/time row, empty state
- Calendario: card-wrapped TableCalendar with custom CalendarStyle (blue
selected/ring today), day header card with date box + occupied/available
stat pills, slot cards with colored strip and time badge instead of
gradient circles
- Perfil profesional: gradient banner header with profession pill + camera
overlay, section cards with icon+title, payment method chips (animated)
instead of checkboxes, schedule rows with colored active/inactive dots,
theme-aware colors throughout (respects dark/light mode)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- professional_denied_screen: full UI with icon, bullet list of next steps,
re-register button and logout option (was just a Text widget)
- professional_pending_screen: replace static GIF screen with step-by-step
progress indicator (submitted → in review → approved), notification tip,
logout button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- MyUser: add isPhoneVerified field (from is_phone_verified in API)
- ApiUserRepository: signInWithPhoneNumber calls POST /auth/send-otp,
verifyOTP calls POST /auth/phone, linkWithOTP calls POST /auth/verify-phone
Added updateFcmToken and changePassword methods
- AuthBloc: remove Firebase PhoneVerificationService, use repository OTP flow
- app_view.dart: gate authenticated users without verified phone to
PhoneVerifyRequiredScreen before showing HomeScreen
- welcome_screen.dart: full redesign with gradient header, logo, integrated
OTP flow (phone → code in same screen), 60s resend timer
- PhoneVerifyRequiredScreen: new screen for users who need to verify phone
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
score.createdAt is now String (ISO 8601) not Firebase Timestamp, so .toDate()
was a compile error. Use DateTime.tryParse() with fallback to DateTime.now().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace all Firebase* repositories with Api* repositories using HTTP + SharedPreferences JWT
- Remove Firebase.initializeApp() and firebase_messaging background handler from main.dart
- Update DI (app_di.dart) to inject Api* repositories instead of Firebase* ones
- Replace all Timestamp/cloud_firestore usage with ISO 8601 String dates
- Stub PhoneVerificationService (Firebase phone OTP → backend OTP when implemented)
- Add ApiService singleton with JWT management in lib/services/
- Legacy firebase_*_repository.dart files preserved for Fase 4 cleanup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>