4.9 KiB
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
useBookSlothook with improved API signature - Added optional
onSuccesscallback 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.tsxapps/external/src/EmbeddedBookingPage.tsx
Changes:
- Removed non-existent
useCreateTabloWithOwnerimport - Added
useBookSlotimport from@xtablo/shared/hooks/book - Refactored booking logic to use the booking slot API endpoint instead of direct tablo creation
- Updated both logged-in and non-logged-in user flows
- 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(forinvalidatePublicSlots) - 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
queryClientfrom../lib/api(not from../lib/supabase) - Imported
invalidatePublicSlotsfrom./publicto avoid duplicate exports
Benefits
- Code Reuse - Single implementation of booking logic shared across all apps
- Consistency - All apps use the same booking API and error handling
- Maintainability - One place to update booking logic
- Flexibility - Custom success callbacks allow different behavior per context
- Fixed Bug - Replaced non-existent
useCreateTabloWithOwnerwith 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", "*");
}
});