* Integrate BookingHistory with Bookings V3 design * Enhance booking features by integrating booking audit functionality - Updated the booking page to retrieve user feature statuses for "bookings-v3" and "booking-audit". - Modified BookingDetailsSheet and BookingListContainer components to accept and utilize the new bookingAuditEnabled prop. - Adjusted the BookingHistory display logic to conditionally render based on the bookingAuditEnabled state. - Refactored SegmentedControl to support generic types for better type safety. This change improves the user experience by allowing conditional access to booking audit features based on user permissions. * Refactor BookingDetailsSheet to manage active segment state with Zustand store - Removed local state management for active segment in BookingDetailsSheet and integrated Zustand store for better state synchronization. - Introduced useActiveSegmentFromUrl hook to sync active segment with URL query parameters. - Updated BookingDetailsSheet to handle derived active segment logic based on bookingAuditEnabled state. - Adjusted SegmentedControl to ensure it defaults to "info" when activeSegment is null. This refactor enhances the maintainability and responsiveness of the booking details component. * refactor: rename useBiDirectionalSyncBetweenZustandAndNuqs to useBiDirectionalSyncBetweenStoreAndUrl Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> --------- Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
75 lines
2.8 KiB
TypeScript
75 lines
2.8 KiB
TypeScript
import classNames from "@calcom/ui/classNames";
|
|
|
|
export type SegmentedControlData<T extends string> = T | { value: T; label: string };
|
|
|
|
export interface SegmentedControlProps<T extends string> {
|
|
data: SegmentedControlData<T>[];
|
|
value: T;
|
|
onChange: (value: T) => void;
|
|
disabled?: boolean;
|
|
className?: string;
|
|
"data-testid"?: string;
|
|
}
|
|
|
|
const SegmentedControl = function <T extends string>({
|
|
data,
|
|
value,
|
|
onChange,
|
|
disabled = false,
|
|
className,
|
|
"data-testid": dataTestId,
|
|
...props
|
|
}: SegmentedControlProps<T>) {
|
|
const handleChange = (newValue: T) => {
|
|
if (!disabled) {
|
|
onChange(newValue);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={classNames("max-w-full", className)}
|
|
data-testid={dataTestId}
|
|
{...props}>
|
|
<div
|
|
className="no-scrollbar flex space-x-0.5 overflow-x-scroll rounded-lg bg-subtle p-0.5"
|
|
role="radiogroup">
|
|
{data.map((item, idx) => {
|
|
const itemValue = typeof item === "string" ? item : item.value;
|
|
const itemLabel = typeof item === "string" ? item : item.label;
|
|
const isActive = value === itemValue;
|
|
const inputId = `segmented-control-${itemValue}-${idx}`;
|
|
|
|
return (
|
|
<label
|
|
key={itemValue}
|
|
htmlFor={inputId}
|
|
className={classNames(
|
|
"inline-flex h-fit w-full items-center justify-center whitespace-nowrap rounded-lg p-1 text-md font-medium leading-none transition md:mb-0",
|
|
"not-disabled:hover:shadow-outline-gray-hover not-disabled:active:shadow-outline-gray-active",
|
|
isActive
|
|
? "bg-default text-emphasis shadow-outline-gray-rested ring-inset ring-subtle"
|
|
: "text-subtle",
|
|
disabled ? "pointer-events-none opacity-30" : "cursor-pointer"
|
|
)}>
|
|
<input
|
|
type="radio"
|
|
id={inputId}
|
|
value={itemValue}
|
|
checked={isActive}
|
|
onChange={() => handleChange(itemValue)}
|
|
disabled={disabled}
|
|
className="sr-only"
|
|
aria-checked={isActive}
|
|
/>
|
|
<span>{itemLabel}</span>
|
|
</label>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SegmentedControl;
|