xtablo-source/docs/BOOK_SLOT_HOOK_MIGRATION.md
2025-11-10 08:53:03 +01:00

4.9 KiB

Book Slot Hook Migration to Shared Package

Date: 2025-11-08
Status: Completed

Overview

Moved the useBookSlot hook from apps/main/src/hooks/book.ts to the @xtablo/shared package, making it available to all apps in the monorepo. Updated the @xtablo/external app to use this shared hook instead of the non-existent useCreateTabloWithOwner hook.

Changes Made

1. Created Shared Hook

Created /packages/shared/src/hooks/book.ts with:

  • Exported useBookSlot hook with improved API signature
  • Added optional onSuccess callback parameter for custom success handling
  • Made hook flexible to work in different contexts (embedded widgets, modal views, default navigation)

Hook Signature:

export const useBookSlot = (
  api: AxiosInstance,
  accessToken?: string,
  onSuccess?: (data: BookSlotResponse) => void
) => { /* ... */ }

2. Updated Main App

File: apps/main/src/hooks/book.ts

  • Replaced entire implementation with re-export from @xtablo/shared
  • Maintains backward compatibility for existing imports

File: apps/main/src/pages/PublicBookingPage.tsx

  • Updated useBookSlot() call to pass required arguments: useBookSlot(api, session?.access_token)

3. Updated External App

Files Modified:

  • apps/external/src/FloatingBookingWidget.tsx
  • apps/external/src/EmbeddedBookingPage.tsx

Changes:

  1. Removed non-existent useCreateTabloWithOwner import
  2. Added useBookSlot import from @xtablo/shared/hooks/book
  3. Refactored booking logic to use the booking slot API endpoint instead of direct tablo creation
  4. Updated both logged-in and non-logged-in user flows
  5. Maintained custom success callbacks for modal/widget closing behavior

Before:

const { mutateAsync: createTabloWithOwner } = useCreateTabloWithOwner(api, () => {
  handleCloseModal();
});

await createTabloWithOwner({
  name: eventType?.name || "",
  status: "todo",
  owner_short_id: shortUserId || "",
  event: { /* ... */ },
  access_token: session?.access_token || "",
});

After:

const { mutateAsync: bookSlot } = useBookSlot(api, session?.access_token, () => {
  handleCloseModal();
});

await bookSlot({
  event_type_standard_name: eventTypeStandardName || "",
  owner_short_id: shortUserId || "",
  event_details: {
    start_date: selectedSlot?.slot.date || "",
    start_time: startTime,
    end_time: endTime,
  },
  user_details: {
    name: formData.name,
    email: formData.email,
  },
});

4. Updated Shared Package Exports

File: packages/shared/src/index.ts

  • Added export for ./hooks/book
  • Added export for ./hooks/public (for invalidatePublicSlots)
  • Added exports for all type files to ensure proper type resolution

5. Fixed Import Dependencies

File: packages/shared/src/hooks/book.ts

  • Fixed import of queryClient from ../lib/api (not from ../lib/supabase)
  • Imported invalidatePublicSlots from ./public to avoid duplicate exports

Benefits

  1. Code Reuse - Single implementation of booking logic shared across all apps
  2. Consistency - All apps use the same booking API and error handling
  3. Maintainability - One place to update booking logic
  4. Flexibility - Custom success callbacks allow different behavior per context
  5. Fixed Bug - Replaced non-existent useCreateTabloWithOwner with working implementation

Verification

All packages pass verification:

turbo typecheck lint --filter=@xtablo/shared --filter=@xtablo/external --filter=@xtablo/main

Results:

  • @xtablo/shared - typecheck ✓ lint ✓
  • @xtablo/main - typecheck ✓ lint ✓
  • @xtablo/external - typecheck ✓ lint ✓

API Contract

Request Payload

{
  event_type_standard_name: string;
  owner_short_id: string;
  event_details: {
    start_date: string;     // YYYY-MM-DD
    start_time: string;     // HH:MM
    end_time: string;       // HH:MM
  };
  user_details: {
    name: string;
    email: string;
  };
}

Response

{
  tablo_id: string;
  hasCreatedAccount: boolean;
  email: string;
}

Usage Examples

Basic Usage (with default navigation)

const { mutateAsync: bookSlot } = useBookSlot(api, session?.access_token);

await bookSlot({
  event_type_standard_name: "consultation",
  owner_short_id: "abc123",
  event_details: { /* ... */ },
  user_details: { /* ... */ },
});
// Default: navigates to /login or /tablos/:id based on hasCreatedAccount

Custom Success Handler (for widgets)

const { mutateAsync: bookSlot } = useBookSlot(api, session?.access_token, (data) => {
  handleCloseModal();
  if (view === "modal") {
    window.parent.postMessage("xtablo:close", "*");
  }
});