frontend phase 15

This commit is contained in:
hamid
2026-07-10 20:28:06 +03:30
parent bc51cf59b4
commit 70cf00ce4a
151 changed files with 10711 additions and 44 deletions
@@ -2,9 +2,12 @@ import { clientFetch } from '@/lib/api/client';
import { unwrap, type ApiEnvelope } from '@/lib/api/types';
import { ApiError } from '@/lib/api/errors';
import type {
AdminRefundResult,
CancelBookingInput,
CancellationPolicyPreview,
InitiateRefundInput,
RefundChannel,
RefundPreview,
RefundStatus,
RefundSummary,
RefundsApi,
@@ -12,6 +15,7 @@ import type {
const BOOKINGS = '/api/v1/bookings';
const REFUNDS = '/api/v1/refunds';
const ADMIN_REFUNDS = '/api/v1/admin_refunds';
/**
* The thin b11 customer refund payload (`GET refunds/{id}/status`) — the only refund shape the contract
@@ -93,4 +97,52 @@ export const refundsClientApi: RefundsApi = {
getRefund: async (refundId: number) =>
toSummary(unwrap(await clientFetch<ApiEnvelope<RefundStatusWire>>(`${REFUNDS}/${refundId}/status`))),
// REQ-035: refund preview endpoint. b11 computes the fee-leg decomposition only *on create* (there is no
// read-only preview route), yet the admin console must disclose the split before initiating. Filed as a
// proposed `GET api/v1/admin_refunds/preview?booking_id=&ticket_id=`; the mock serves it today.
getRefundPreview: async (bookingId: number, ticketId: number | null) => {
const params = new URLSearchParams({ booking_id: String(bookingId) });
if (ticketId != null) params.set('ticket_id', String(ticketId));
return unwrap(
await clientFetch<ApiEnvelope<RefundPreview>>(`${ADMIN_REFUNDS}/preview?${params.toString()}`),
);
},
// Create + immediately execute a ticket-linked refund (b11 `POST api/v1/admin_refunds`). The response is
// already `AdminRefundResult`-shaped (refundId/status/channel/decomposed legs/eta/clawbackId).
initiateRefund: async (input: InitiateRefundInput) =>
unwrap(
await clientFetch<ApiEnvelope<AdminRefundResult>>(ADMIN_REFUNDS, {
method: 'POST',
body: JSON.stringify({
bookingId: input.bookingId,
ticketId: input.ticketId,
refundPercentage: input.refundPercentage,
refundChannel: input.refundChannel,
reasonCategory: input.reasonCategory,
reasonNotes: input.reasonNotes,
}),
}),
),
// REQ-035: approve/reject a refund. b11 has no separate approve/reject step — `POST admin_refunds` both
// creates and executes — so a failed-channel retry and an explicit rejection are proposed as
// `POST api/v1/admin_refunds/{id}/approve` and `.../{id}/reject`; the mock serves both today.
approveRefund: async (refundId: number) =>
unwrap(
await clientFetch<ApiEnvelope<AdminRefundResult>>(`${ADMIN_REFUNDS}/${refundId}/approve`, {
method: 'POST',
}),
),
// REQ-035: see approveRefund. Reject records a reason and moves the refund to `rejected`.
rejectRefund: async (refundId: number, reason: string) => {
unwrap(
await clientFetch<ApiEnvelope<true>>(`${ADMIN_REFUNDS}/${refundId}/reject`, {
method: 'POST',
body: JSON.stringify({ reason }),
}),
);
},
};