Files
baya-monorepo/dev/post-phase/server/frontend-backend-gaps.md
T
2026-07-10 11:49:55 +03:30

18 KiB
Raw Blame History

Frontend ↔ backend gaps — REQ reconciliation

Audit date: 2026-07-10 · Sources: dev/shared-working-context/frontend/requests/for-backend.md (REQ-001…015), the frontend phase reports/STATUS (f0f8), the client code's mock flags, the published contracts (dev/contracts/domains/*.md + dev/contracts/openapi/swagger.v1.json), and the server code. Every verdict was checked against both the contract surface and the actual DTO/handler/controller code.

Headline: the backend chain is complete (b0b15), but of the 15 filed REQs only REQ-001 and REQ-015 are effectively satisfied and REQ-010 is a documentation fix — the other 12 are undelivered. Every REQ still reads Status: open in the tracker. As a direct consequence, 11 of the client's 12 service domains still default to mock-primary (only auth is real-default, client/src/services/auth/constants.ts:6). Beyond the filed REQs, the unbuilt frontend phases f9f15 will consume backend surfaces that mostly exist — with one data gap (catalog option groups) and one pre-flagged shape gap (checkout VAT line).

Verdict summary

REQ Ask (short) Verdict One-line evidence
REQ-001 Envelope / casing / pagination shape Done (confirm + caveat) ApiResult + PagedResult match the typed shape; statusCode is an integer enum
REQ-002 codeLength/expiresInSeconds on RequestOtpResult Missing RequestOtpResult.cs:7 has only OtpSent, ResendAvailableInSeconds
REQ-003 Machine error codes for verify_otp Missing envelope has no code slot; lockout differs only by message text
REQ-004 activeRole on MeResult (confirmation) Missing (answer: client owns it) MeResult.cs:9 — no ActiveRole anywhere in the contract
REQ-005 Patient relation + conditions Missing PatientDto.cs:7, create/update commands unchanged
REQ-006 Avatar upload route + avatarUrl Missing zero IFormFile/avatar matches in server/src
REQ-007 Customer name + preferred language Missing upsert body is emergency-contact only
REQ-008 Accept client map pin on address create/update Missing commands have no lat/lng; always geocodes
REQ-009 provinceId on CustomerAddressDto Missing DTO ends at RecipientPhone
REQ-010 pageSize vs page_size Partial server binds pageSize (verified); contract docs still say page_size
REQ-011 Nurse credential_details endpoint + isRequired Missing no such route/command; step DTO lacks isRequired
REQ-012 Search row name/avatar/distance + GET nurses/{id}/profile Missing DTO ids-only; no profile action on NursesController
REQ-013 variantPrice on BookingRequestDto Missing DTO has unit without price
REQ-014 variantLabel/patientAge on list item Missing list DTO omits both
REQ-015 Status enum codes + checkInAddressMatch tri-state (confirmation) Done (verified in code) snake_case string constants on the wire; null-when-no-GPS confirmed

Per-REQ detail

REQ-001 — envelope, casing, pagination — Done, needs a written confirmation + one caveat

  • Frontend expects: payload always under data in { isSuccess, statusCode, message, requestId, data }; camelCase JSON; lists as { items, total, page, pageSize }.
  • Backend ships: exactly that. ApiResult at server/src/Core/Baya.Application/Models/ApiResult/ApiResult.cs:8 (+ generic Data at :28); PagedResult<T>(Items, Total, Page, PageSize) at server/src/Core/Baya.Application/Models/Common/PagedResult.cs:4; camelCase is the System.Text.Json default (no naming-policy override exists in server/src/API); swagger confirms (dev/contracts/openapi/swagger.v1.json:13448 envelope, :14205 paged shape).
  • Caveat to communicate: statusCode serializes as an integer (ApiResultStatusCode enum, swagger :13468-13469) — matches the client's number typing, but worth stating so nobody expects an HTTP-status string.
  • Fix: zero code. Write the confirmation into the REQ and mark it delivered.

REQ-002 — OTP length + expiry — Missing

  • Expected: RequestOtpResult { otpSent, resendAvailableInSeconds, codeLength, expiresInSeconds }.
  • Actual: server/src/Core/Baya.Application/Models/Identity/RequestOtpResult.cs:7 — two fields only; handler returns them at Features/Identity/Commands/RequestOtp/RequestOtpCommand.Handler.cs:67; swagger agrees (swagger.v1.json:16539). The client hardcodes OTP_CODE_LENGTH = 6 (client/src/services/auth/constants.ts:19).
  • Fix: add the two ints (code length is a constant today; TTL from the OTP provider options). S effort.

REQ-003 — machine-readable verify_otp errors — Missing

  • Expected: stable code (otp_invalid | otp_expired | otp_locked) + retryAfterSeconds on lockout.
  • Actual: the envelope has no code slot (ApiResult.cs:8; OperationResult carries only boolean flags — Models/Common/OperationResult.cs:14-31). Wrong/expired share one anti-enumeration message (Features/Identity/Commands/VerifyOtp/VerifyOtpCommand.Handler.cs:24,30,34,50); lockout is a different English string only (:38). The client keys off the mock-only otp_locked code (client/src/services/auth/constants.ts:29).
  • Fix: add an optional code (+ optional data) to the failure envelope — a small OperationResult/ApiResult extension — and emit otp_locked + retryAfterSeconds from the lockout branch; keep wrong-vs-expired collapsed if enumeration-safety is preferred (state that in the REQ answer). SM effort (the only REQ touching a cross-cutting type).

REQ-004 — activeRole confirmation — Missing (recommend: answer "client owns it")

  • Actual: no activeRole on MeResult (server/src/Core/Baya.Application/Models/Identity/MeResult.cs:9) or anywhere in the contract (schema scan). No endpoint persists a current-role choice.
  • Fix: zero code — write the decision (client-owned intended_role stands) into the REQ so the router behavior is contract-blessed. If the product later wants a persisted active role, it's a me/select_role extension.

REQ-005 — patient relation + conditions — Missing

  • Actual: PatientDto ends at InitialMedicalNotes/IsActive (Models/Identity/PatientDto.cs:7,15-16); create/update commands unchanged (Features/Identity/Commands/CreatePatient/CreatePatientCommand.cs:12-19, UpdatePatient/UpdatePatientCommand.cs:8-16); no relation/conditions in any schema.
  • Fix: relation as a nullable code column; conditions as stable codes (JSON column or child table — child table if search/filtering is ever wanted). Gate: flips USE_PATIENTS_MOCK (client/src/services/patients/constants.ts:8). SM effort.

REQ-006 — avatar upload + avatarUrl — Missing

  • Actual: zero IFormFile/multipart/avatar usage in server/src (repo-wide grep); no avatarUrl on NurseProfileDto (swagger :18945) or CustomerProfileDto (Models/Identity/CustomerProfileDto.cs:7). The client's real path deliberately throws 501 (client/src/services/profiles/apis/clientApi.ts:84).
  • Fix: POST api/v1/{nurse|customer}_profiles/avatar (multipart, size/type-validated) storing via IObjectStorage + avatar_url column on both profiles. Note it also feeds REQ-012 (search card avatar) and REQ-013 (nurse avatar on request detail) — deliver before or with those. M effort (first multipart endpoint; pairs with the object-storage swap, plan §5.5).

REQ-007 — customer name + preferred language — Missing

  • Actual: upsert body is emergency-contact only (Features/Identity/Commands/UpsertCustomerProfile/UpsertCustomerProfileCommand.cs:11); MeResult exposes name read-only (MeResult.cs:12); no preferredLanguage anywhere (schema scan).
  • Fix: decide the home (recommend: extend the upsert to write Users.FirstName/LastName + preferred_language on the customer profile) and answer the REQ. S effort.

REQ-008 — accept the client map pin — Missing

  • Actual: create/update commands have no coordinates; the server always geocodes (Features/Addresses/Commands/CreateAddress/CreateAddressCommand.cs:9-12, UpdateAddress/UpdateAddressCommand.cs:8-9; swagger :18051). The user's pin is silently discarded on the real path — exactly what the REQ warned. This also degrades EVV accuracy (b9 measures distance to the stored coordinate; a mock/geocoded centroid is ±5 km off — CrossCutting/Seams/MockGeocoder.cs:52).
  • Fix: optional latitude/longitude on both bodies; when present store as source user_pin, else geocode as today. S effort; do before the real geocoder swap (plan §5.4).

REQ-009 — provinceId on CustomerAddressDto — Missing

  • Actual: DTO fields run Id..RecipientPhone (Models/Addresses/CustomerAddressDto.cs:9-24); no provinceId (swagger :17983 — the property exists only on CityDto).
  • Fix: join cities.province_id into the address projections. S effort. Gate (with REQ-008): USE_ADDRESSES_MOCK (client/src/services/addresses/constants.ts:10).

REQ-010 — pageSize param name — Partial (server verified; docs stale)

  • Actual: every list binds a PageSize record property via [FromQuery] — so the working wire name is camelCase pageSize (case-insensitive), and page_size silently does not bind. Verified: Features/ServiceAreas/Queries/ListMyServiceAreas/ListMyServiceAreasQuery.cs:8, Features/Variants/Queries/ListMyVariants/ListMyVariantsQuery.cs:8, Controllers/V1/NurseServiceAreasController.cs:35, AdminPayoutsController.cs:54; swagger names the parameter pageSize (swagger.v1.json:3104). But the requested deliverable — fixing the docs — never happened: dev/contracts/domains/catalog.md:41 and config-reference.md:11 (and others, e.g. bookings-evv.md, verification.md) still write page_size.
  • Fix: sweep the contract docs to pageSize, answer the REQ. Zero server code.

REQ-011 — nurse credential_details + isRequired — Missing

  • Actual: the nurse-facing controller exposes only submit/status/upload_url/documents/run (Controllers/V1/NurseVerificationController.cs:26-57); repo-wide grep for credential_details|SubmitCredential finds nothing. VerificationStepDto has no IsRequired (Models/Verification/VerificationDtos.cs:17); the flag exists only on the admin step-type catalog. Consequence on the real path: the INO number + specialties a nurse types are silently dropped (verificationClientApi.submitCredentialDetails no-ops — dev/shared-working-context/reports/frontend-phase-5-report.md:101).
  • Fix: POST api/v1/nurse_verification/credential_details writing the structured nurse_credentials fields (the registry table already stores number/authority/expiry), + project isRequired onto the step DTO. M effort. Gate: USE_VERIFICATION_MOCK (client/src/services/verification/constants.ts:9).

REQ-012 — search enrichment + public nurse profile — Missing (highest-leverage gap)

  • Actual: NurseSearchResultDto carries ids + price/rating/gender/geo only (Models/Search/NurseSearchResultDto.cs:8-19); the public NursesController has trust_badge, reviews, review_tags — no /profile (Controllers/V1/NursesController.cs:24-36); no avatarUrl/distanceKm anywhere in the contract (schema scan).
  • Why it leads the priority list: C2/C3 are the trust funnel — the family picks a named, faced, priced nurse here; this single REQ keeps services/search mock-primary (client/src/services/search/constants.ts:9) and blocks the whole discovery→request→booking real-path chain (search feeds C4's nurse/variant ids).
  • Fix: (a) denormalize nurse_name/avatar_url into nurse_search_index (the maintainer already re-derives rows from source — Persistence/Services/Search/SearchIndexMaintainer.cs:25; add columns + reindex-on-profile-change) or join at query time in SqlNurseSearch; distanceKm is optional — the district model makes it derived-if-cheap. (b) an aggregated GET nurses/{id}/profile composing existing reads (profile + variants + trust badge + latest published review). M effort; depends on REQ-006 for the avatar itself.

REQ-013 — variantPrice on BookingRequestDto — Missing

  • Actual: the DTO has VariantLabel + VariantPriceUnit but no price and no nurse avatar (Models/Booking/BookingRequestDto.cs:20-21, full list :11-42; swagger :16674).
  • Fix: join the variant's Price (IRR digit-string, consistent with the money convention) into the projection. The money-free rule stays intact — this is the display rate, not an engagement total (the request row still stores no money). S effort.

REQ-014 — variantLabel/patientAge on the inbox row — Missing

  • Actual: BookingRequestListItemDto has neither (Models/Booking/BookingRequestListItemDto.cs:10-21; swagger :16903) — the nurse inbox can't show which service was requested without opening the detail.
  • Fix: add variantLabel (already on the detail DTO); patientAge as a coarse band if product wants it. S effort. Gate (with REQ-013): USE_BOOKING_REQUESTS_MOCK (client/src/services/bookingRequests/constants.ts:14) — though that flip also needs the upstream domains real (see below).

REQ-015 — enum codes + checkInAddressMatch tri-state — Done (verified), needs a written confirmation

  • Verified in code: statuses are stored/projected as snake_case string constants — exactly the client unions: Domain/Entities/Booking/BookingStatus.cs:11-30, BookingSessionStatus.cs:10-19, VisitVerificationStatus.cs:11-17; DTOs copy them verbatim (Models/Booking/BookingDtos.cs:97), so no PascalCase/int ever hits the wire. checkInAddressMatch is bool? (BookingDtos.cs:104) assigned only inside the lat/lng-present branch (Features/Bookings/Commands/CheckInVisit/CheckInVisitCommand.Handler.cs:64-77) → null when GPS is absent; a false is advisory only (support alert + notification, no block — :90-109).
  • One nuance to include in the answer: null also occurs when GPS was sent but the frozen booking address has no resolvable coordinates — the client copy for «موقعیت ثبت نشد» should tolerate that.
  • Fix: zero code; write the confirmation, mark delivered.

Beyond the filed REQs — what f9f15 will hit

Frontend phases f0f8 are built (reports exist); f9f15 are specs only. Reconciling their declared consumption against the shipped backend:

Upcoming phase Consumes Backend reality Verdict
f9 checkout/card b10 payments.md + b11 invoice endpoints exist (initiate/webhook/GET invoices/{booking_id}), but no checkout-summary read with the VAT line — f8 already flagged BookingDetailDto has no tax field (reports/frontend-phase-8-report.md:113); f9's spec expects vat_irr/vat_rate/redirect_url shapes (dev/phases/frontend/frontend-phase-9-b10.md:117,330) Partial — pre-file the checkout-summary REQ now
f10 refund status b11 refunds-invoices.md GET refunds/{id}/status + GET invoices/{booking_id} shipped (Controllers/V1/RefundsController, InvoicesController) Done (verify shapes when f10 runs)
f11 BNPL b12 bnpl.md full eligibility→initiate→status surface shipped (CheckoutBnplController) Done (verify shapes)
f12 nurse earnings b13 payouts.md nurse_payouts/history + admin console shipped (NursePayoutsController, AdminPayoutsController) Done (verify shapes)
f13 reviews/care records b14 reviews-records.md submit/list/tags/moderation + care records shipped (5 controllers) Done (verify shapes)
f14 tickets + notifications b15 + b1 notifications tickets shipped; notifications verified present: GET notifications/get_notifications/get_unread_count, POST mark_notification_read/mark_all_read (Controllers/V1/NotificationsController.cs:24-42) — the f14 spec's worry about missing b1 endpoints is unfounded Done
f15 admin/partner consoles admin endpoints across b1/b6/b11/b13/b14/b15 all shipped per the chain (verification queue, refunds, payouts, moderation, config/holidays/audit/support-alerts, partner centers) Done (expect shape-polish REQs when f15 runs)

Data gap (not a contract gap): flipping USE_CATALOG_MOCK against a fresh backend yields categories with no option groups — only the 5 categories are seeded; groups/values are admin-authored and the admin catalog UI is f15 (reports/frontend-phase-4-report.md:92). Until f15 (or a seed migration), the variant builder's required-option step has nothing to render on the real path. Recommend: a small representative option-group seed, or prioritize the f15 catalog manager.

Tracker hygiene: all 15 REQs read Status: open (for-backend.md:32…216) and the mocks-registry's early block contradicts its own later rows (see plan §7.6). Whoever lands this batch should update both in the same change.


Frontend-unblock priority

  1. REQ-012 (search row enrichment + public profile) — unlocks the discovery funnel; everything downstream needs C2/C3 real. Include the nurse_search_index columns + reindex.
  2. REQ-005, REQ-008, REQ-009 — the booking-request inputs (patients, addresses) go real; REQ-008 also protects EVV accuracy.
  3. REQ-013, REQ-014 — the request flow prices/labels itself; with (1)+(2) the whole search→request→accept chain can flip to real.
  4. REQ-006, REQ-007 — profile/avatar polish; REQ-006 also feeds (1) and (3)'s avatar fields.
  5. REQ-011 — verification detail capture (stops silent INO/specialty data loss).
  6. REQ-002, REQ-003 — auth UX polish (real path already works without them).
  7. Zero-code batch: REQ-001, REQ-004, REQ-010, REQ-015 — written confirmations + contract-doc page_size sweep + tracker statuses.
  8. Pre-file the f9 checkout-summary REQ (VAT line, redirect_url, idempotency header echo) so b-side work can precede the f9 build.