* feat: companion-android-ui-upgrade version 1 * recurrings and unconfirmed booking filter and page implementation * add badge and links to event type list page * address cubics comments * feat(companion): unify dropdown menu for Android and extension (#26486) * feat(companion): unify dropdown menu for Android and extension - Merge Android-specific dropdown implementations into base component files - EventTypeListItem: Add DropdownMenu with Preview, Copy link, Edit, Duplicate, Delete actions - BookingListItem: Add DropdownMenu with booking actions (reschedule, edit location, add guests, etc.) - RecurringBookingListItem: Add DropdownMenu with recurring booking actions - AvailabilityListItem: Add DropdownMenu with Set as Default, Duplicate, Delete actions - BookingDetailScreen: Add DropdownMenu in header for Android with AlertDialog for cancel confirmation - Delete all .android.tsx files as implementations are now unified * fix(companion): fix typecheck errors after dropdown unification - Remove unused props from EventTypeListItem.ios.tsx (copiedEventTypeId, handleEventTypeLongPress) - Remove unused onActionsPress prop from BookingListItem.ios.tsx - Remove copiedEventTypeId and handleEventTypeLongPress props from index.ios.tsx and index.tsx - Remove onLongPress and onActionsPress props from BookingListScreen.tsx - Remove handleScheduleLongPress, setSelectedSchedule, setShowActionsModal props from AvailabilityListScreen.tsx - Fix BookingDetailScreen.tsx to use correct action property names (reschedule.visible instead of canReschedule, etc.) * fix(companion): remove unused code after dropdown unification - Remove unused handleEventTypeLongPress function and ActionSheetIOS import from index.ios.tsx - Remove unused copiedEventTypeId state, handleEventTypeLongPress function, and ActionSheetIOS import from index.tsx - Prefix unused setSelectedBooking with underscore in BookingListScreen.tsx - Remove unused handleScheduleLongPress function and ActionSheetIOS import from AvailabilityListScreen.tsx * feat(companion): unify booking filter UI for Android and extension - Remove SegmentedControl from web/extension booking list page - Use Header dropdown for booking status filter on both Android and web - Use unified event type filter dropdown for both platforms - Remove unused showFilterModal state and related code - Pass filterOptions, activeFilter, and onFilterChange to Header for all platforms * fix(companion): add header padding for web/extension to prevent button clipping - Add headerLeftContainerStyle and headerRightContainerStyle with 12px padding for web platform - Applied to root Stack and all nested tab Stack layouts - Fixes issue where header buttons were touching edges and getting chopped off on extension/web - Android remains unaffected as the fix is web-only * fix(companion): add HeaderButtonWrapper for web-only header padding - Create HeaderButtonWrapper component that adds 12px margin on web only - Wrap all header buttons with HeaderButtonWrapper to prevent clipping - Revert invalid screenOptions changes that caused typecheck errors - Apply fix to all screens with native header buttons: - reschedule.tsx, edit-location.tsx, add-guests.tsx - mark-no-show.tsx, view-recordings.tsx, meeting-session-details.tsx - event-type-detail.tsx, BookingDetailScreen.tsx, profile-sheet.tsx - edit-availability-day.tsx, edit-availability-name.tsx, edit-availability-override.tsx * update more ui-ux * address cubics comments * address cubics comments & open event type list page first for inttial render of app
139 lines
3.8 KiB
TypeScript
139 lines
3.8 KiB
TypeScript
import { useCallback, useMemo, useState } from "react";
|
|
import type { NativeSyntheticEvent } from "react-native";
|
|
import type { BookingFilters } from "./useBookings";
|
|
|
|
export type BookingFilter = "upcoming" | "unconfirmed" | "recurring" | "past" | "cancelled";
|
|
|
|
export interface BookingFilterOption {
|
|
key: BookingFilter;
|
|
label: string;
|
|
}
|
|
|
|
const FILTER_OPTIONS: BookingFilterOption[] = [
|
|
{ key: "upcoming", label: "Upcoming" },
|
|
{ key: "unconfirmed", label: "Unconfirmed" },
|
|
{ key: "recurring", label: "Recurring" },
|
|
{ key: "past", label: "Past" },
|
|
{ key: "cancelled", label: "Cancelled" },
|
|
];
|
|
|
|
/**
|
|
* Hook to manage active booking filter state and provide filter-related utilities.
|
|
*
|
|
* Features:
|
|
* - Manages active filter state
|
|
* - Provides filter options and labels for UI components
|
|
* - Converts filter selection to API-compatible parameters
|
|
* - Handles segmented control integration
|
|
* - Supports change callbacks for dependent filter cleanup
|
|
*
|
|
* @param initialFilter - Initial filter value (defaults to "upcoming")
|
|
* @param onFilterChange - Optional callback invoked when filter changes
|
|
* @returns Object containing filter state, handlers, and computed values
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const {
|
|
* activeFilter,
|
|
* filterLabels,
|
|
* activeIndex,
|
|
* filterParams,
|
|
* handleFilterChange,
|
|
* handleSegmentChange,
|
|
* } = useActiveBookingFilter("upcoming", () => {
|
|
* // Clear dependent filters like search query, event type filter, etc.
|
|
* setSearchQuery("");
|
|
* setSelectedEventTypeId(null);
|
|
* });
|
|
*
|
|
* // Use with React Query
|
|
* const { data: bookings } = useBookings(filterParams);
|
|
*
|
|
* // Use with SegmentedControl
|
|
* <SegmentedControl
|
|
* values={filterLabels}
|
|
* selectedIndex={activeIndex}
|
|
* onChange={handleSegmentChange}
|
|
* />
|
|
* ```
|
|
*/
|
|
export function useActiveBookingFilter(
|
|
initialFilter: BookingFilter = "upcoming",
|
|
onFilterChange?: (filter: BookingFilter) => void
|
|
) {
|
|
const [activeFilter, setActiveFilter] = useState<BookingFilter>(initialFilter);
|
|
|
|
/**
|
|
* Converts the active filter to API-compatible query parameters
|
|
*/
|
|
const filterParams = useMemo<BookingFilters>(() => {
|
|
switch (activeFilter) {
|
|
case "upcoming":
|
|
return { status: ["upcoming"], limit: 50 };
|
|
case "unconfirmed":
|
|
return { status: ["unconfirmed"], limit: 50 };
|
|
case "recurring":
|
|
return { status: ["recurring"], limit: 100 };
|
|
case "past":
|
|
return { status: ["past"], limit: 100 };
|
|
case "cancelled":
|
|
return { status: ["cancelled"], limit: 100 };
|
|
default:
|
|
return { status: ["upcoming"], limit: 50 };
|
|
}
|
|
}, [activeFilter]);
|
|
|
|
/**
|
|
* Array of filter labels for UI components (e.g., SegmentedControl)
|
|
*/
|
|
const filterLabels = useMemo(() => FILTER_OPTIONS.map((option) => option.label), []);
|
|
|
|
/**
|
|
* Current active filter index for UI components
|
|
*/
|
|
const activeIndex = useMemo(
|
|
() => FILTER_OPTIONS.findIndex((option) => option.key === activeFilter),
|
|
[activeFilter]
|
|
);
|
|
|
|
/**
|
|
* Changes the active filter and invokes the optional callback
|
|
*/
|
|
const handleFilterChange = useCallback(
|
|
(filter: BookingFilter) => {
|
|
setActiveFilter(filter);
|
|
onFilterChange?.(filter);
|
|
},
|
|
[onFilterChange]
|
|
);
|
|
|
|
/**
|
|
* Handler for React Native SegmentedControl onChange event
|
|
*/
|
|
const handleSegmentChange = useCallback(
|
|
(event: NativeSyntheticEvent<{ selectedSegmentIndex: number }>) => {
|
|
const { selectedSegmentIndex } = event.nativeEvent;
|
|
const selectedFilter = FILTER_OPTIONS[selectedSegmentIndex];
|
|
if (selectedFilter) {
|
|
handleFilterChange(selectedFilter.key);
|
|
}
|
|
},
|
|
[handleFilterChange]
|
|
);
|
|
|
|
return {
|
|
// State
|
|
activeFilter,
|
|
|
|
// Computed values
|
|
filterOptions: FILTER_OPTIONS,
|
|
filterLabels,
|
|
activeIndex,
|
|
filterParams,
|
|
|
|
// Handlers
|
|
handleFilterChange,
|
|
handleSegmentChange,
|
|
};
|
|
}
|