frontend phase 13
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { clientFetch } from '@/lib/api/client';
|
||||
import { unwrap, type ApiEnvelope, type Paginated } from '@/lib/api/types';
|
||||
import type { PageParams } from '@/lib/api/types';
|
||||
import { REVIEWS_PAGE_SIZE } from '../constants';
|
||||
import type {
|
||||
CreateReviewRequest,
|
||||
MyReviewState,
|
||||
NurseReviews,
|
||||
ReviewEligibility,
|
||||
ReviewListItem,
|
||||
ReviewsApi,
|
||||
SubmitReviewResult,
|
||||
} from '../types';
|
||||
|
||||
const API = '/api/v1';
|
||||
|
||||
/** Wire `NurseReviewsResult` — `reviews` is a `PagedResult<ReviewListItemDto>` (camelCase, per api-conventions). */
|
||||
interface NurseReviewsWire {
|
||||
aggregate: { averageRating: number; publishedCount: number };
|
||||
reviews: Paginated<ReviewListItem>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Real HTTP implementation of the `ReviewsApi` seam (b14 contract `dev/contracts/domains/reviews-records.md`,
|
||||
* swagger `dev/contracts/openapi/swagger.v1.json`). Two of the four methods map **published** b14 routes:
|
||||
* - `getNurseReviews` → `GET nurses/{id}/reviews` (aggregate + published page; server filters to published).
|
||||
* - `createReview` → `POST bookings/{id}/review` (the one review per completed booking; `409` if reviewed).
|
||||
*
|
||||
* The other two target contract gaps the frontend filed (**REQ-026**), which is why the domain stays
|
||||
* mock-primary (see `constants.ts`):
|
||||
* - `getReviewEligibility` → whether this booking can still be reviewed (no wire read; proposed slug below).
|
||||
* - `getMyReviewForBooking` → the caller's own review + its moderation state (no wire read; proposed slug).
|
||||
*
|
||||
* NOT the primary implementation this phase (`USE_REVIEWS_MOCK = true`). `clientFetch` returns the raw
|
||||
* envelope so we `unwrap()`; ids come from the route; list params are camelCase (`page`/`pageSize`).
|
||||
*/
|
||||
export const reviewsClientApi: ReviewsApi = {
|
||||
getNurseReviews: async (nurseProfileId: number, params: PageParams): Promise<NurseReviews> => {
|
||||
const query = new URLSearchParams();
|
||||
query.set('page', String(params.page ?? 1));
|
||||
query.set('pageSize', String(params.pageSize ?? REVIEWS_PAGE_SIZE));
|
||||
const wire = unwrap(
|
||||
await clientFetch<ApiEnvelope<NurseReviewsWire>>(
|
||||
`${API}/nurses/${nurseProfileId}/reviews?${query.toString()}`,
|
||||
),
|
||||
);
|
||||
return { aggregate: wire.aggregate, reviews: wire.reviews };
|
||||
},
|
||||
|
||||
// REQ-026: proposed owner-scoped read (no wire endpoint yet). 404s until delivered — never called while
|
||||
// the domain is mock-primary. Kept symmetric so the swap stays a one-line config flip.
|
||||
getReviewEligibility: async (bookingId: number): Promise<ReviewEligibility> =>
|
||||
unwrap(await clientFetch<ApiEnvelope<ReviewEligibility>>(`${API}/bookings/${bookingId}/review_eligibility`)),
|
||||
|
||||
// REQ-026: proposed owner-scoped read of the caller's own review for this booking.
|
||||
getMyReviewForBooking: async (bookingId: number): Promise<MyReviewState> =>
|
||||
unwrap(await clientFetch<ApiEnvelope<MyReviewState>>(`${API}/bookings/${bookingId}/my_review`)),
|
||||
|
||||
createReview: async (bookingId: number, body: CreateReviewRequest): Promise<SubmitReviewResult> =>
|
||||
unwrap(
|
||||
await clientFetch<ApiEnvelope<SubmitReviewResult>>(`${API}/bookings/${bookingId}/review`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ rating: body.rating, body: body.body ?? null, tagCodes: body.tagCodes ?? [] }),
|
||||
}),
|
||||
),
|
||||
};
|
||||
Reference in New Issue
Block a user