All files / src/hooks/merchant-api/events useSeatingRows.ts

92.3% Statements 12/13
60% Branches 3/5
85.71% Functions 6/7
91.66% Lines 11/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69                42x             1107x   42x       1105x                                 42x 9x 9x               3x             2x           1x   1x          
import { useMutation, useQuery, useQueryClient } from "react-query";
import { customInstance } from "@services/api";
import { buildMerchantEndpoints } from "@services/api/utils.api";
import { showMessage } from "@common/Toast";
import { QKEY_SEATING_ROWS } from "@constants/queryKeys";
import { toSeatingRowsPayload } from "@sections/PayBuilder/seating.helpers";
import type { SeatingRow } from "@sections/PayBuilder/types";
 
const SAVE_SEATING_ERROR = "Couldn't save the seating layout. Please try again.";
 
// GET/PUT /merchants/{merchant_id}/products/{product_id}/seating-rows
// merchant_id is injected by buildMerchantEndpoints (getMerchantId()).
// The merchant endpoint returns rows only (assignSeating lives on the product GET).
type SeatingRowsResponse = { rows: SeatingRow[] };
 
const key = (productId: string | number) => [QKEY_SEATING_ROWS, productId];
 
export const useGetSeatingRows = (
  productId: string | number,
  enabled = true,
) =>
  useQuery<SeatingRowsResponse>(
    key(productId),
    () =>
      customInstance({
        url: buildMerchantEndpoints(`products/${productId}/seating-rows`),
        method: "GET",
      }),
    { enabled: Boolean(productId) && enabled, refetchOnWindowFocus: false },
  );
 
// PAY Builder 031 (Figma 6722-6428) — persist ONLY the seating layout, decoupled from the full
// form save. "Save Seating" must not run variants/bulk-create: a brand-new, not-yet-assigned
// ticket would be sent seatless, rejected by the BE (ErrTicketNeedsSeatingRow), silently swallowed
// by the full save's Promise.allSettled, and then wiped by the post-save re-hydration — the ticket
// disappears. This PUT replaces the rows in place (ids preserved via toSeatingRowsPayload so the BE
// upserts) and, on success, invalidates the seating-rows query so the editor re-hydrates with the
// freshly-saved rows (now id-bearing, therefore assignable). Local form Items are left untouched.
export const useSaveSeatingRows = () => {
  const queryClient = useQueryClient();
  return useMutation(
    ({
      productId,
      rows,
    }: {
      productId: string | number;
      rows: SeatingRow[];
    }) =>
      customInstance({
        url: buildMerchantEndpoints(`products/${productId}/seating-rows`),
        method: "PUT",
        data: { rows: toSeatingRowsPayload(rows) },
      }),
    {
      onSuccess: (_data, { productId }) => {
        queryClient.invalidateQueries(key(productId));
      },
      // Surface failures — otherwise a rejected PUT is silent and the layout looks saved when it
      // isn't. Prefer the BE message (e.g. a cross-variant row conflict) over the generic fallback.
      onError: (error) => {
        const message =
          (error as { response?: { data?: { message?: string } } })?.response
            ?.data?.message || SAVE_SEATING_ERROR;
        showMessage("Error", message);
      },
    },
  );
};