chore(deps): update dependencies and add version constraints (#26328)
* chore(deps): update dependencies and add version constraints - Update direct dependencies: body-parser, @nestjs/swagger, react-use, trigger.dev - Add resolutions for consistent dependency versions across the monorepo - Add packageExtensions to ensure compatible transitive dependency versions * fix: upgrade rollup resolution from 3.29.5 to 4.22.4 Rollup 3.x is incompatible with vite 5.x which requires rollup ^4.20.0. The previous resolution caused CI failures due to missing ./parseAst export * fix: update axios resolution to 1.13.2 Align resolution with direct dependency in apps/api/v2. Both versions include the fix, but 1.13.2 is newer and avoids an unnecessary downgrade
This commit is contained in:
1 parent
6bdb08317d
commit
40dd546429
19 files changed
+794
-834
No files matched your search
@@ -54,14 +54,14 @@
|
||||
"@nestjs/jwt": "10.2.0",
|
||||
"@nestjs/passport": "10.0.3",
|
||||
"@nestjs/platform-express": "10.4.20",
|
||||
"@nestjs/swagger": "7.3.0",
|
||||
"@nestjs/swagger": "7.4.2",
|
||||
"@nestjs/throttler": "6.2.1",
|
||||
"@sentry/nestjs": "9.46.0",
|
||||
"@sentry/node": "9.46.0",
|
||||
"@sentry/profiling-node": "9.46.0",
|
||||
"@snyk/protect": "latest",
|
||||
"axios": "1.13.2",
|
||||
"body-parser": "1.20.2",
|
||||
"body-parser": "1.20.3",
|
||||
"bull": "4.15.1",
|
||||
"class-transformer": "0.5.1",
|
||||
"class-validator": "0.14.3",
|
||||
|
||||
@@ -94,7 +94,8 @@ export default function Availability() {
|
||||
<Stack.Header
|
||||
style={{ backgroundColor: "transparent", shadowColor: "transparent" }}
|
||||
blurEffect={isLiquidGlassAvailable() ? undefined : "light"}
|
||||
hidden={Platform.OS === "android"}>
|
||||
hidden={Platform.OS === "android"}
|
||||
>
|
||||
<Stack.Header.Title large>Availability</Stack.Header.Title>
|
||||
<Stack.Header.Right>
|
||||
{/* New Menu */}
|
||||
|
||||
@@ -218,7 +218,8 @@ export default function EventTypesIOS() {
|
||||
});
|
||||
},
|
||||
onError: (duplicateError) => {
|
||||
const message = duplicateError instanceof Error ? duplicateError.message : String(duplicateError);
|
||||
const message =
|
||||
duplicateError instanceof Error ? duplicateError.message : String(duplicateError);
|
||||
console.error("Failed to duplicate event type", message);
|
||||
if (__DEV__) {
|
||||
const stack = duplicateError instanceof Error ? duplicateError.stack : undefined;
|
||||
@@ -281,13 +282,18 @@ export default function EventTypesIOS() {
|
||||
id: newEventType.id.toString(),
|
||||
title: newEventType.title,
|
||||
description: newEventType.description || "",
|
||||
duration: (newEventType.lengthInMinutes || newEventType.length || 15).toString(),
|
||||
duration: (
|
||||
newEventType.lengthInMinutes ||
|
||||
newEventType.length ||
|
||||
15
|
||||
).toString(),
|
||||
slug: newEventType.slug || "",
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: (createError) => {
|
||||
const message = createError instanceof Error ? createError.message : String(createError);
|
||||
const message =
|
||||
createError instanceof Error ? createError.message : String(createError);
|
||||
console.error("Failed to create event type", message);
|
||||
if (__DEV__) {
|
||||
const stack = createError instanceof Error ? createError.stack : undefined;
|
||||
@@ -373,7 +379,8 @@ export default function EventTypesIOS() {
|
||||
{/* iOS Native Header with Glass UI */}
|
||||
<Stack.Header
|
||||
style={{ backgroundColor: "transparent", shadowColor: "transparent" }}
|
||||
blurEffect={isLiquidGlassAvailable() ? undefined : "light"}>
|
||||
blurEffect={isLiquidGlassAvailable() ? undefined : "light"}
|
||||
>
|
||||
<Stack.Header.Title large>Event Types</Stack.Header.Title>
|
||||
|
||||
<Stack.Header.Right>
|
||||
@@ -385,12 +392,14 @@ export default function EventTypesIOS() {
|
||||
<Stack.Header.Menu title="Sort by">
|
||||
<Stack.Header.MenuAction
|
||||
icon="textformat.abc"
|
||||
onPress={() => handleSortByOption("alphabetical")}>
|
||||
onPress={() => handleSortByOption("alphabetical")}
|
||||
>
|
||||
Alphabetical
|
||||
</Stack.Header.MenuAction>
|
||||
<Stack.Header.MenuAction
|
||||
icon="calendar.badge.clock"
|
||||
onPress={() => handleSortByOption("newest")}>
|
||||
onPress={() => handleSortByOption("newest")}
|
||||
>
|
||||
Newest First
|
||||
</Stack.Header.MenuAction>
|
||||
<Stack.Header.MenuAction icon="clock" onPress={() => handleSortByOption("duration")}>
|
||||
@@ -400,13 +409,19 @@ export default function EventTypesIOS() {
|
||||
|
||||
{/* Filter Submenu - opens as separate submenu */}
|
||||
<Stack.Header.Menu title="Filter">
|
||||
<Stack.Header.MenuAction icon="checkmark.circle" onPress={() => handleFilterOption("all")}>
|
||||
<Stack.Header.MenuAction
|
||||
icon="checkmark.circle"
|
||||
onPress={() => handleFilterOption("all")}
|
||||
>
|
||||
All Event Types
|
||||
</Stack.Header.MenuAction>
|
||||
<Stack.Header.MenuAction icon="eye" onPress={() => handleFilterOption("active")}>
|
||||
Active Only
|
||||
</Stack.Header.MenuAction>
|
||||
<Stack.Header.MenuAction icon="dollarsign.circle" onPress={() => handleFilterOption("paid")}>
|
||||
<Stack.Header.MenuAction
|
||||
icon="dollarsign.circle"
|
||||
onPress={() => handleFilterOption("paid")}
|
||||
>
|
||||
Paid Events
|
||||
</Stack.Header.MenuAction>
|
||||
</Stack.Header.Menu>
|
||||
@@ -444,7 +459,8 @@ export default function EventTypesIOS() {
|
||||
contentContainerStyle={{ paddingBottom: 120 }}
|
||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentInsetAdjustmentBehavior="automatic">
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
>
|
||||
{filteredEventTypes.length === 0 && searchQuery.trim() !== "" ? (
|
||||
<View className="flex-1 items-center justify-center bg-gray-50 p-5 pt-20">
|
||||
<EmptyScreen
|
||||
@@ -482,7 +498,8 @@ export default function EventTypesIOS() {
|
||||
<Host matchContents>
|
||||
<ContextMenu
|
||||
modifiers={[buttonStyle(isLiquidGlassAvailable() ? "glass" : "bordered"), padding()]}
|
||||
activationMethod="singlePress">
|
||||
activationMethod="singlePress"
|
||||
>
|
||||
<ContextMenu.Items>
|
||||
<Button systemImage="link" onPress={handleOpenCreateModal} label="New Event Type" />
|
||||
</ContextMenu.Items>
|
||||
|
||||
@@ -88,7 +88,8 @@ export default function More() {
|
||||
{/* iOS Native Header with Glass UI */}
|
||||
<Stack.Header
|
||||
style={{ backgroundColor: "transparent", shadowColor: "transparent" }}
|
||||
blurEffect={isLiquidGlassAvailable() ? undefined : "light"}>
|
||||
blurEffect={isLiquidGlassAvailable() ? undefined : "light"}
|
||||
>
|
||||
<Stack.Header.Title large>More</Stack.Header.Title>
|
||||
<Stack.Header.Right>
|
||||
{userProfile?.avatarUrl ? (
|
||||
@@ -113,7 +114,8 @@ export default function More() {
|
||||
style={{ backgroundColor: "#f8f9fa" }}
|
||||
contentContainerStyle={{ padding: 16, paddingBottom: 120 }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentInsetAdjustmentBehavior="automatic">
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
>
|
||||
<View className="overflow-hidden rounded-lg border border-[#E5E5EA] bg-white">
|
||||
{menuItems.map((item, index) => (
|
||||
<TouchableOpacity
|
||||
@@ -121,7 +123,8 @@ export default function More() {
|
||||
onPress={item.onPress}
|
||||
className={`flex-row items-center justify-between bg-white px-5 py-5 active:bg-[#F8F9FA] ${
|
||||
index < menuItems.length - 1 ? "border-b border-[#E5E5EA]" : ""
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<View className="flex-1 flex-row items-center">
|
||||
<Ionicons name={item.icon} size={20} color="#333" />
|
||||
<Text className="ml-3 text-base font-semibold text-[#333]">{item.name}</Text>
|
||||
@@ -139,7 +142,8 @@ export default function More() {
|
||||
<View className="mt-6 overflow-hidden rounded-lg border border-[#E5E5EA] bg-white">
|
||||
<TouchableOpacity
|
||||
onPress={handleSignOut}
|
||||
className="flex-row items-center justify-center bg-white px-5 py-4 active:bg-red-50">
|
||||
className="flex-row items-center justify-center bg-white px-5 py-4 active:bg-red-50"
|
||||
>
|
||||
<Ionicons name="log-out-outline" size={20} color="#800000" />
|
||||
<Text className="ml-2 text-base font-medium text-[#800000]">Sign Out</Text>
|
||||
</TouchableOpacity>
|
||||
@@ -149,7 +153,10 @@ export default function More() {
|
||||
<Text className="mt-6 px-1 text-center text-xs text-gray-400">
|
||||
The companion app is an extension of the web application.{"\n"}
|
||||
For advanced features, visit{" "}
|
||||
<Text className="text-gray-800" onPress={() => openInAppBrowser("https://app.cal.com", "Cal.com")}>
|
||||
<Text
|
||||
className="text-gray-800"
|
||||
onPress={() => openInAppBrowser("https://app.cal.com", "Cal.com")}
|
||||
>
|
||||
app.cal.com
|
||||
</Text>
|
||||
</Text>
|
||||
|
||||
+51
-15
@@ -21,13 +21,18 @@ function RootLayoutContent() {
|
||||
headerLargeTitle: false,
|
||||
title: Platform.OS === "ios" ? "You" : "Profile",
|
||||
presentation:
|
||||
Platform.OS === "ios" ? (isLiquidGlassAvailable() ? "formSheet" : "modal") : "containedModal",
|
||||
Platform.OS === "ios"
|
||||
? isLiquidGlassAvailable()
|
||||
? "formSheet"
|
||||
: "modal"
|
||||
: "containedModal",
|
||||
// iOS-specific sheet options (ignored on Android)
|
||||
sheetGrabberVisible: Platform.OS === "ios",
|
||||
sheetAllowedDetents: Platform.OS === "ios" ? [0.6, 0.9] : undefined,
|
||||
sheetInitialDetentIndex: Platform.OS === "ios" ? 0 : undefined,
|
||||
contentStyle: {
|
||||
backgroundColor: Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#FFFFFF",
|
||||
backgroundColor:
|
||||
Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#FFFFFF",
|
||||
},
|
||||
headerStyle: {
|
||||
backgroundColor: Platform.OS === "ios" ? "transparent" : "#FFFFFF",
|
||||
@@ -43,13 +48,18 @@ function RootLayoutContent() {
|
||||
headerLargeTitle: false,
|
||||
title: "Session Details",
|
||||
presentation:
|
||||
Platform.OS === "ios" ? (isLiquidGlassAvailable() ? "formSheet" : "modal") : "containedModal",
|
||||
Platform.OS === "ios"
|
||||
? isLiquidGlassAvailable()
|
||||
? "formSheet"
|
||||
: "modal"
|
||||
: "containedModal",
|
||||
// iOS-specific sheet options (ignored on Android)
|
||||
sheetGrabberVisible: Platform.OS === "ios",
|
||||
sheetAllowedDetents: Platform.OS === "ios" ? [0.7, 0.9] : undefined,
|
||||
sheetInitialDetentIndex: Platform.OS === "ios" ? 0 : undefined,
|
||||
contentStyle: {
|
||||
backgroundColor: Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
backgroundColor:
|
||||
Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
},
|
||||
headerStyle: {
|
||||
backgroundColor: Platform.OS === "ios" ? "transparent" : "#F2F2F7",
|
||||
@@ -65,12 +75,17 @@ function RootLayoutContent() {
|
||||
headerLargeTitle: false,
|
||||
title: "Edit Location",
|
||||
presentation:
|
||||
Platform.OS === "ios" ? (isLiquidGlassAvailable() ? "formSheet" : "modal") : "containedModal",
|
||||
Platform.OS === "ios"
|
||||
? isLiquidGlassAvailable()
|
||||
? "formSheet"
|
||||
: "modal"
|
||||
: "containedModal",
|
||||
sheetGrabberVisible: Platform.OS === "ios",
|
||||
sheetAllowedDetents: Platform.OS === "ios" ? [0.5, 0.7] : undefined,
|
||||
sheetInitialDetentIndex: Platform.OS === "ios" ? 0 : undefined,
|
||||
contentStyle: {
|
||||
backgroundColor: Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
backgroundColor:
|
||||
Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
},
|
||||
headerStyle: {
|
||||
backgroundColor: Platform.OS === "ios" ? "transparent" : "#F2F2F7",
|
||||
@@ -86,12 +101,17 @@ function RootLayoutContent() {
|
||||
headerLargeTitle: false,
|
||||
title: "Mark No-Show",
|
||||
presentation:
|
||||
Platform.OS === "ios" ? (isLiquidGlassAvailable() ? "formSheet" : "modal") : "containedModal",
|
||||
Platform.OS === "ios"
|
||||
? isLiquidGlassAvailable()
|
||||
? "formSheet"
|
||||
: "modal"
|
||||
: "containedModal",
|
||||
sheetGrabberVisible: Platform.OS === "ios",
|
||||
sheetAllowedDetents: Platform.OS === "ios" ? [0.4, 0.6] : undefined,
|
||||
sheetInitialDetentIndex: Platform.OS === "ios" ? 0 : undefined,
|
||||
contentStyle: {
|
||||
backgroundColor: Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
backgroundColor:
|
||||
Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
},
|
||||
headerStyle: {
|
||||
backgroundColor: Platform.OS === "ios" ? "transparent" : "#F2F2F7",
|
||||
@@ -107,12 +127,17 @@ function RootLayoutContent() {
|
||||
headerLargeTitle: false,
|
||||
title: "Recordings",
|
||||
presentation:
|
||||
Platform.OS === "ios" ? (isLiquidGlassAvailable() ? "formSheet" : "modal") : "containedModal",
|
||||
Platform.OS === "ios"
|
||||
? isLiquidGlassAvailable()
|
||||
? "formSheet"
|
||||
: "modal"
|
||||
: "containedModal",
|
||||
sheetGrabberVisible: Platform.OS === "ios",
|
||||
sheetAllowedDetents: Platform.OS === "ios" ? [0.7, 0.9] : undefined,
|
||||
sheetInitialDetentIndex: Platform.OS === "ios" ? 0 : undefined,
|
||||
contentStyle: {
|
||||
backgroundColor: Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
backgroundColor:
|
||||
Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
},
|
||||
headerStyle: {
|
||||
backgroundColor: Platform.OS === "ios" ? "transparent" : "#F2F2F7",
|
||||
@@ -128,12 +153,17 @@ function RootLayoutContent() {
|
||||
headerLargeTitle: false,
|
||||
title: "Add Guests",
|
||||
presentation:
|
||||
Platform.OS === "ios" ? (isLiquidGlassAvailable() ? "formSheet" : "modal") : "containedModal",
|
||||
Platform.OS === "ios"
|
||||
? isLiquidGlassAvailable()
|
||||
? "formSheet"
|
||||
: "modal"
|
||||
: "containedModal",
|
||||
sheetGrabberVisible: Platform.OS === "ios",
|
||||
sheetAllowedDetents: Platform.OS === "ios" ? [0.6, 0.8] : undefined,
|
||||
sheetInitialDetentIndex: Platform.OS === "ios" ? 0 : undefined,
|
||||
contentStyle: {
|
||||
backgroundColor: Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
backgroundColor:
|
||||
Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
},
|
||||
headerStyle: {
|
||||
backgroundColor: Platform.OS === "ios" ? "transparent" : "#F2F2F7",
|
||||
@@ -149,12 +179,17 @@ function RootLayoutContent() {
|
||||
headerLargeTitle: false,
|
||||
title: "Reschedule",
|
||||
presentation:
|
||||
Platform.OS === "ios" ? (isLiquidGlassAvailable() ? "formSheet" : "modal") : "containedModal",
|
||||
Platform.OS === "ios"
|
||||
? isLiquidGlassAvailable()
|
||||
? "formSheet"
|
||||
: "modal"
|
||||
: "containedModal",
|
||||
sheetGrabberVisible: Platform.OS === "ios",
|
||||
sheetAllowedDetents: Platform.OS === "ios" ? [0.8, 0.95] : undefined,
|
||||
sheetInitialDetentIndex: Platform.OS === "ios" ? 0 : undefined,
|
||||
contentStyle: {
|
||||
backgroundColor: Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
backgroundColor:
|
||||
Platform.OS === "ios" && isLiquidGlassAvailable() ? "transparent" : "#F2F2F7",
|
||||
},
|
||||
headerStyle: {
|
||||
backgroundColor: Platform.OS === "ios" ? "transparent" : "#F2F2F7",
|
||||
@@ -185,7 +220,8 @@ function RootLayoutContent() {
|
||||
return (
|
||||
<View
|
||||
style={[containerStyle, Platform.OS === "web" && { pointerEvents: "auto" as const }]}
|
||||
className={containerClass}>
|
||||
className={containerClass}
|
||||
>
|
||||
<StatusBar barStyle="dark-content" backgroundColor="#ffffff" />
|
||||
{content}
|
||||
<NetworkStatusBanner />
|
||||
|
||||
@@ -194,11 +194,15 @@ export const BookingListScreen: React.FC<BookingListScreenProps> = ({
|
||||
case "upcoming":
|
||||
case "unconfirmed":
|
||||
// Sort by start time ascending
|
||||
return sorted.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime());
|
||||
return sorted.sort(
|
||||
(a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()
|
||||
);
|
||||
case "past":
|
||||
case "cancelled":
|
||||
// Sort by start time descending (latest first)
|
||||
return sorted.sort((a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime());
|
||||
return sorted.sort(
|
||||
(a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime()
|
||||
);
|
||||
default:
|
||||
return sorted;
|
||||
}
|
||||
@@ -327,10 +331,17 @@ export const BookingListScreen: React.FC<BookingListScreenProps> = ({
|
||||
contentContainerStyle={{
|
||||
padding: 20,
|
||||
}}
|
||||
refreshControl={<RefreshControl refreshing={isManualRefreshing} onRefresh={manualRefresh} />}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={isManualRefreshing} onRefresh={manualRefresh} />
|
||||
}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
style={{ backgroundColor: "white" }}>
|
||||
<EmptyScreen icon={emptyState.icon} headline={emptyState.title} description={emptyState.text} />
|
||||
style={{ backgroundColor: "white" }}
|
||||
>
|
||||
<EmptyScreen
|
||||
icon={emptyState.icon}
|
||||
headline={emptyState.title}
|
||||
description={emptyState.text}
|
||||
/>
|
||||
</ScrollView>
|
||||
</View>
|
||||
</Activity>
|
||||
@@ -342,9 +353,12 @@ export const BookingListScreen: React.FC<BookingListScreenProps> = ({
|
||||
contentContainerStyle={{
|
||||
padding: 20,
|
||||
}}
|
||||
refreshControl={<RefreshControl refreshing={isManualRefreshing} onRefresh={manualRefresh} />}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={isManualRefreshing} onRefresh={manualRefresh} />
|
||||
}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
style={{ backgroundColor: "white" }}>
|
||||
style={{ backgroundColor: "white" }}
|
||||
>
|
||||
<EmptyScreen
|
||||
icon="search-outline"
|
||||
headline={`No results found for "${searchQuery}"`}
|
||||
@@ -362,7 +376,9 @@ export const BookingListScreen: React.FC<BookingListScreenProps> = ({
|
||||
keyExtractor={(item) => item.key}
|
||||
renderItem={renderListItem}
|
||||
contentContainerStyle={{ paddingBottom: 90 }}
|
||||
refreshControl={<RefreshControl refreshing={isManualRefreshing} onRefresh={manualRefresh} />}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={isManualRefreshing} onRefresh={manualRefresh} />
|
||||
}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
style={{ backgroundColor: "white" }}
|
||||
@@ -377,7 +393,9 @@ export const BookingListScreen: React.FC<BookingListScreenProps> = ({
|
||||
keyExtractor={(item) => item.key}
|
||||
renderItem={renderListItem}
|
||||
contentContainerStyle={{ paddingBottom: 90 }}
|
||||
refreshControl={<RefreshControl refreshing={isManualRefreshing} onRefresh={manualRefresh} />}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={isManualRefreshing} onRefresh={manualRefresh} />
|
||||
}
|
||||
showsVerticalScrollIndicator={false}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -132,7 +132,8 @@ export const AddGuestsScreen = forwardRef<AddGuestsScreenHandle, AddGuestsScreen
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
className={`flex-1 ${backgroundStyle}`}>
|
||||
className={`flex-1 ${backgroundStyle}`}
|
||||
>
|
||||
<ScrollView
|
||||
className="flex-1"
|
||||
contentContainerStyle={{
|
||||
@@ -140,7 +141,8 @@ export const AddGuestsScreen = forwardRef<AddGuestsScreenHandle, AddGuestsScreen
|
||||
paddingBottom: insets.bottom + 16,
|
||||
}}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
showsVerticalScrollIndicator={!transparentBackground}>
|
||||
showsVerticalScrollIndicator={!transparentBackground}
|
||||
>
|
||||
{/* Email input */}
|
||||
{transparentBackground && (
|
||||
<Text className="mb-2 px-1 text-[13px] font-medium text-gray-500">Email *</Text>
|
||||
@@ -148,7 +150,8 @@ export const AddGuestsScreen = forwardRef<AddGuestsScreenHandle, AddGuestsScreen
|
||||
<View
|
||||
className={`mb-3 overflow-hidden rounded-xl px-4 py-3 ${
|
||||
transparentBackground ? "border border-gray-300/40 bg-white/60" : "bg-white"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
{!transparentBackground && (
|
||||
<Text className="mb-1.5 text-[13px] font-medium text-gray-500">Email *</Text>
|
||||
)}
|
||||
@@ -172,7 +175,8 @@ export const AddGuestsScreen = forwardRef<AddGuestsScreenHandle, AddGuestsScreen
|
||||
<View
|
||||
className={`mb-4 overflow-hidden rounded-xl px-4 py-3 ${
|
||||
transparentBackground ? "border border-gray-300/40 bg-white/60" : "bg-white"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
{!transparentBackground && (
|
||||
<Text className="mb-1.5 text-[13px] font-medium text-gray-500">Name (optional)</Text>
|
||||
)}
|
||||
@@ -193,7 +197,8 @@ export const AddGuestsScreen = forwardRef<AddGuestsScreenHandle, AddGuestsScreen
|
||||
}`}
|
||||
onPress={handleAddGuest}
|
||||
activeOpacity={0.7}
|
||||
disabled={isSaving}>
|
||||
disabled={isSaving}
|
||||
>
|
||||
<Ionicons name="add-circle" size={22} color="#007AFF" />
|
||||
<Text className="ml-2 text-[17px] font-medium text-[#007AFF]">Add</Text>
|
||||
</TouchableOpacity>
|
||||
@@ -207,24 +212,31 @@ export const AddGuestsScreen = forwardRef<AddGuestsScreenHandle, AddGuestsScreen
|
||||
<View
|
||||
className={`overflow-hidden rounded-xl ${
|
||||
transparentBackground ? "border border-gray-300/40 bg-white/60" : "bg-white"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
{guests.map((guest, index) => (
|
||||
<View
|
||||
key={guest.email}
|
||||
className={`flex-row items-center px-4 py-3 ${
|
||||
index < guests.length - 1 ? "border-b border-gray-100/50" : ""
|
||||
}`}>
|
||||
<View className={`mr-3 h-10 w-10 items-center justify-center rounded-full ${pillStyle}`}>
|
||||
}`}
|
||||
>
|
||||
<View
|
||||
className={`mr-3 h-10 w-10 items-center justify-center rounded-full ${pillStyle}`}
|
||||
>
|
||||
<Ionicons name="person" size={20} color="#6B7280" />
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
<Text className="text-[17px] text-[#000]">{guest.email}</Text>
|
||||
{guest.name && <Text className="mt-0.5 text-[15px] text-gray-500">{guest.name}</Text>}
|
||||
{guest.name && (
|
||||
<Text className="mt-0.5 text-[15px] text-gray-500">{guest.name}</Text>
|
||||
)}
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
onPress={() => handleRemoveGuest(index)}
|
||||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||
disabled={isSaving}>
|
||||
disabled={isSaving}
|
||||
>
|
||||
<Ionicons name="close-circle-outline" size={24} color="#800020" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
@@ -76,7 +76,10 @@ export interface BookingDetailScreenProps {
|
||||
}) => void;
|
||||
}
|
||||
|
||||
export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreenProps): React.JSX.Element {
|
||||
export function BookingDetailScreen({
|
||||
uid,
|
||||
onActionsReady,
|
||||
}: BookingDetailScreenProps): React.JSX.Element {
|
||||
const router = useRouter();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
@@ -357,13 +360,15 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
paddingTop: 16,
|
||||
paddingBottom: insets.bottom + 100,
|
||||
}}
|
||||
showsVerticalScrollIndicator={false}>
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
{/* Title Section - iOS Calendar Style */}
|
||||
<View className="mb-8">
|
||||
{/* Meeting Title */}
|
||||
<Text
|
||||
className="mb-4 text-[26px] font-semibold leading-tight text-black"
|
||||
style={{ letterSpacing: -0.3 }}>
|
||||
style={{ letterSpacing: -0.3 }}
|
||||
>
|
||||
{booking.title}
|
||||
</Text>
|
||||
|
||||
@@ -376,7 +381,9 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
)}
|
||||
{durationFormatted && (
|
||||
<View className="rounded-full bg-[#E5E5EA] px-2.5 py-1">
|
||||
<Text className="text-[13px] font-medium text-[#636366]">{durationFormatted}</Text>
|
||||
<Text className="text-[13px] font-medium text-[#636366]">
|
||||
{durationFormatted}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
@@ -396,7 +403,8 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
<View className="mb-4 overflow-hidden rounded-xl bg-white">
|
||||
<AppPressable
|
||||
className="flex-row items-center justify-between px-4 py-3.5"
|
||||
onPress={() => setParticipantsExpanded(!participantsExpanded)}>
|
||||
onPress={() => setParticipantsExpanded(!participantsExpanded)}
|
||||
>
|
||||
<Text className="text-[17px] text-black">Participants</Text>
|
||||
<View className="flex-row items-center">
|
||||
<Text className="mr-1 text-[17px] text-[#8E8E93]">{totalParticipants}</Text>
|
||||
@@ -418,7 +426,8 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
key={host.email || `host-${index}`}
|
||||
className={`flex-row items-center py-2 ${
|
||||
index > 0 ? "border-t border-[#E5E5EA]" : ""
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<Ionicons name="star" size={18} color="#FFD60A" />
|
||||
<Text className="ml-2.5 flex-1 text-[15px] text-black" numberOfLines={1}>
|
||||
{host.name || host.email || "Host"}
|
||||
@@ -443,8 +452,11 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
<View
|
||||
key={attendee.email}
|
||||
className={`flex-row items-center py-2 ${
|
||||
index > 0 || booking.hosts?.length || booking.user ? "border-t border-[#E5E5EA]" : ""
|
||||
}`}>
|
||||
index > 0 || booking.hosts?.length || booking.user
|
||||
? "border-t border-[#E5E5EA]"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<Ionicons name={statusIcon.name} size={20} color={statusIcon.color} />
|
||||
<Text className="ml-2.5 flex-1 text-[15px] text-black" numberOfLines={1}>
|
||||
{attendee.name || attendee.email}
|
||||
@@ -466,7 +478,8 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
index > 0 || booking.attendees?.length || booking.hosts?.length || booking.user
|
||||
? "border-t border-[#E5E5EA]"
|
||||
: ""
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<Ionicons name="person-outline" size={20} color="#8E8E93" />
|
||||
<Text className="ml-2.5 flex-1 text-[15px] text-[#8E8E93]" numberOfLines={1}>
|
||||
{guestEmail}
|
||||
@@ -494,21 +507,23 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
"additionalNotes",
|
||||
];
|
||||
|
||||
const displayableEntries = Object.entries(booking.bookingFieldsResponses).filter(([key, value]) => {
|
||||
if (excludedKeys.includes(key)) return false;
|
||||
const displayableEntries = Object.entries(booking.bookingFieldsResponses).filter(
|
||||
([key, value]) => {
|
||||
if (excludedKeys.includes(key)) return false;
|
||||
|
||||
if (value === null || value === undefined || value === "") return false;
|
||||
if (value === null || value === undefined || value === "") return false;
|
||||
|
||||
if (Array.isArray(value) && value.length === 0) return false;
|
||||
if (Array.isArray(value) && value.length === 0) return false;
|
||||
|
||||
if (typeof value === "object" && !Array.isArray(value) && value !== null) {
|
||||
const obj = value as Record<string, unknown>;
|
||||
if ("value" in obj && "optionValue" in obj) return false;
|
||||
if (Object.keys(obj).length === 0) return false;
|
||||
if (typeof value === "object" && !Array.isArray(value) && value !== null) {
|
||||
const obj = value as Record<string, unknown>;
|
||||
if ("value" in obj && "optionValue" in obj) return false;
|
||||
if (Object.keys(obj).length === 0) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
);
|
||||
|
||||
if (displayableEntries.length === 0) return null;
|
||||
|
||||
@@ -538,7 +553,10 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
.trim();
|
||||
|
||||
return (
|
||||
<View key={key} className={`py-2 ${index > 0 ? "border-t border-[#E5E5EA]" : ""}`}>
|
||||
<View
|
||||
key={key}
|
||||
className={`py-2 ${index > 0 ? "border-t border-[#E5E5EA]" : ""}`}
|
||||
>
|
||||
<Text className="mb-0.5 text-[13px] text-[#8E8E93]">{displayKey}</Text>
|
||||
<Text className="text-[17px] text-black">{displayValue}</Text>
|
||||
</View>
|
||||
@@ -583,7 +601,8 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
params: { uid: fromUid },
|
||||
});
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Text className="text-[17px] text-black">Rescheduled from</Text>
|
||||
<View className="flex-row items-center">
|
||||
<Text className="mr-1 text-[15px] text-[#007AFF]">View original</Text>
|
||||
@@ -603,7 +622,8 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
params: { uid },
|
||||
});
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Text className="text-[17px] text-black">Rescheduled to</Text>
|
||||
<View className="flex-row items-center">
|
||||
<Text className="mr-1 text-[15px] text-[#007AFF]">View new</Text>
|
||||
@@ -618,7 +638,8 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
booking.rescheduledFromUid || booking.rescheduledToUid || booking.fromReschedule
|
||||
? "border-t border-[#E5E5EA]"
|
||||
: ""
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<Text className="mb-0.5 text-[13px] text-[#8E8E93]">Reason</Text>
|
||||
<Text className="text-[17px] text-black">{booking.reschedulingReason}</Text>
|
||||
</View>
|
||||
@@ -644,7 +665,9 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
)}
|
||||
|
||||
{booking.cancelledByEmail && (
|
||||
<View className={`py-2 ${booking.cancellationReason ? "border-t border-[#E5E5EA]" : ""}`}>
|
||||
<View
|
||||
className={`py-2 ${booking.cancellationReason ? "border-t border-[#E5E5EA]" : ""}`}
|
||||
>
|
||||
<Text className="mb-0.5 text-[13px] text-[#8E8E93]">Cancelled by</Text>
|
||||
<Text className="text-[17px] text-black">{booking.cancelledByEmail}</Text>
|
||||
</View>
|
||||
@@ -656,7 +679,8 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
booking.cancellationReason || booking.cancelledByEmail
|
||||
? "border-t border-[#E5E5EA]"
|
||||
: ""
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<Ionicons name="warning" size={18} color="#FF9500" />
|
||||
<Text className="ml-2 text-[17px] text-black">Host was absent</Text>
|
||||
</View>
|
||||
|
||||
@@ -446,7 +446,8 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
headerRight: () => (
|
||||
<AppPressable
|
||||
className="h-10 w-10 items-center justify-center rounded-full"
|
||||
onPress={() => setShowActionsModal(true)}>
|
||||
onPress={() => setShowActionsModal(true)}
|
||||
>
|
||||
<Ionicons name="ellipsis-horizontal" size={24} color="#000" />
|
||||
</AppPressable>
|
||||
),
|
||||
@@ -478,7 +479,9 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
<View className="mb-1 flex-row flex-wrap items-center">
|
||||
<Text className="text-base font-medium text-[#333]">{booking.user.name}</Text>
|
||||
<Text className="text-base font-medium text-[#333]">
|
||||
{booking.user.name}
|
||||
</Text>
|
||||
<View className="ml-2 rounded bg-[#007AFF] px-2 py-0.5">
|
||||
<Text className="text-xs font-medium text-white">host</Text>
|
||||
</View>
|
||||
@@ -490,7 +493,8 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
booking.hosts.map((host, hostIndex) => (
|
||||
<View
|
||||
key={host.email ?? host.name}
|
||||
className={`flex-row items-start ${hostIndex > 0 ? "mt-4" : ""}`}>
|
||||
className={`flex-row items-start ${hostIndex > 0 ? "mt-4" : ""}`}
|
||||
>
|
||||
<View className="mr-3 h-12 w-12 items-center justify-center rounded-full bg-black">
|
||||
<Text className="text-base font-semibold text-white">
|
||||
{getInitials(host.name || "Host")}
|
||||
@@ -498,7 +502,9 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
<View className="mb-1 flex-row flex-wrap items-center">
|
||||
<Text className="text-base font-medium text-[#333]">{host.name || "Host"}</Text>
|
||||
<Text className="text-base font-medium text-[#333]">
|
||||
{host.name || "Host"}
|
||||
</Text>
|
||||
<View className="ml-2 rounded bg-[#007AFF] px-2 py-0.5">
|
||||
<Text className="text-xs font-medium text-white">host</Text>
|
||||
</View>
|
||||
@@ -517,11 +523,15 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
(attendee as { noShow?: boolean; absent?: boolean }).noShow === true ||
|
||||
(attendee as { noShow?: boolean; absent?: boolean }).absent === true;
|
||||
return (
|
||||
<View key={attendee.email} className={`flex-row items-start ${index > 0 ? "mt-4" : ""}`}>
|
||||
<View
|
||||
key={attendee.email}
|
||||
className={`flex-row items-start ${index > 0 ? "mt-4" : ""}`}
|
||||
>
|
||||
<View
|
||||
className={`mr-3 h-12 w-12 items-center justify-center rounded-full ${
|
||||
isNoShow ? "bg-[#DC2626]" : "bg-black"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<Text className="text-base font-semibold text-white">
|
||||
{getInitials(attendee.name)}
|
||||
</Text>
|
||||
@@ -531,13 +541,16 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
<Text
|
||||
className={`text-base font-medium ${
|
||||
isNoShow ? "text-[#DC2626]" : "text-[#333]"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
{attendee.name}
|
||||
</Text>
|
||||
{isNoShow && (
|
||||
<View className="ml-2 flex-row items-center rounded-full bg-[#FEE2E2] px-2 py-0.5">
|
||||
<Ionicons name="eye-off" size={12} color="#DC2626" />
|
||||
<Text className="ml-1 text-xs font-medium text-[#DC2626]">No-show</Text>
|
||||
<Text className="ml-1 text-xs font-medium text-[#DC2626]">
|
||||
No-show
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
@@ -557,7 +570,10 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
<View className="mb-2 rounded-2xl bg-white p-6">
|
||||
<Text className="mb-4 text-base font-medium text-[#666]">Where</Text>
|
||||
{locationProvider.url ? (
|
||||
<AppPressable onPress={handleJoinMeeting} className="flex-row flex-wrap items-center">
|
||||
<AppPressable
|
||||
onPress={handleJoinMeeting}
|
||||
className="flex-row flex-wrap items-center"
|
||||
>
|
||||
{locationProvider.iconUrl ? (
|
||||
<SvgImage
|
||||
uri={locationProvider.iconUrl}
|
||||
@@ -588,7 +604,8 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
) : null}
|
||||
|
||||
{/* Recurring Event Section */}
|
||||
{booking.recurringEventId || (booking as { recurringBookingUid?: string }).recurringBookingUid ? (
|
||||
{booking.recurringEventId ||
|
||||
(booking as { recurringBookingUid?: string }).recurringBookingUid ? (
|
||||
<View className="mb-2 rounded-2xl bg-white p-6">
|
||||
<Text className="mb-2 text-base font-medium text-[#666]">Recurring Event</Text>
|
||||
<Text className="text-base text-[#666]">Every 2 weeks for 6 occurrences</Text>
|
||||
@@ -607,11 +624,19 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
{locationProvider?.url ? (
|
||||
<AppPressable
|
||||
onPress={handleJoinMeeting}
|
||||
className="mb-2 flex-row items-center justify-center rounded-lg bg-black px-6 py-4">
|
||||
className="mb-2 flex-row items-center justify-center rounded-lg bg-black px-6 py-4"
|
||||
>
|
||||
{locationProvider.iconUrl ? (
|
||||
<SvgImage uri={locationProvider.iconUrl} width={20} height={20} style={{ marginRight: 8 }} />
|
||||
<SvgImage
|
||||
uri={locationProvider.iconUrl}
|
||||
width={20}
|
||||
height={20}
|
||||
style={{ marginRight: 8 }}
|
||||
/>
|
||||
) : null}
|
||||
<Text className="text-base font-semibold text-white">Join {locationProvider.label}</Text>
|
||||
<Text className="text-base font-semibold text-white">
|
||||
Join {locationProvider.label}
|
||||
</Text>
|
||||
</AppPressable>
|
||||
) : null}
|
||||
</ScrollView>
|
||||
@@ -639,7 +664,9 @@ export function BookingDetailScreen({ uid, onActionsReady }: BookingDetailScreen
|
||||
<View className="absolute inset-0 items-center justify-center bg-black/50">
|
||||
<View className="rounded-2xl bg-white px-8 py-6">
|
||||
<ActivityIndicator size="large" color="#000" />
|
||||
<Text className="mt-3 text-base font-medium text-gray-700">Cancelling booking...</Text>
|
||||
<Text className="mt-3 text-base font-medium text-gray-700">
|
||||
Cancelling booking...
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
@@ -69,7 +69,11 @@ const detectLocationType = (location: string): LocationTypeId => {
|
||||
if (!location) return "link";
|
||||
|
||||
// Check if it's a URL
|
||||
if (location.startsWith("http://") || location.startsWith("https://") || location.startsWith("www.")) {
|
||||
if (
|
||||
location.startsWith("http://") ||
|
||||
location.startsWith("https://") ||
|
||||
location.startsWith("www.")
|
||||
) {
|
||||
return "link";
|
||||
}
|
||||
|
||||
@@ -84,7 +88,10 @@ const detectLocationType = (location: string): LocationTypeId => {
|
||||
};
|
||||
|
||||
export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocationScreenProps>(
|
||||
function EditLocationScreen({ booking, onSuccess, onSavingChange, transparentBackground = false }, ref) {
|
||||
function EditLocationScreen(
|
||||
{ booking, onSuccess, onSavingChange, transparentBackground = false },
|
||||
ref
|
||||
) {
|
||||
const insets = useSafeAreaInsets();
|
||||
const backgroundStyle = transparentBackground ? "bg-transparent" : "bg-[#F2F2F7]";
|
||||
const [selectedType, setSelectedType] = useState<LocationTypeId>("link");
|
||||
@@ -155,7 +162,9 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
setIsSaving(true);
|
||||
try {
|
||||
await CalComAPIService.updateLocationV2(booking.uid, locationPayload);
|
||||
Alert.alert("Success", "Location updated successfully", [{ text: "OK", onPress: onSuccess }]);
|
||||
Alert.alert("Success", "Location updated successfully", [
|
||||
{ text: "OK", onPress: onSuccess },
|
||||
]);
|
||||
setIsSaving(false);
|
||||
} catch (error) {
|
||||
safeLogError("[EditLocationScreen] Failed to update location:", error);
|
||||
@@ -190,7 +199,8 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
paddingBottom: insets.bottom + 16,
|
||||
}}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
showsVerticalScrollIndicator={!transparentBackground}>
|
||||
showsVerticalScrollIndicator={!transparentBackground}
|
||||
>
|
||||
{transparentBackground ? (
|
||||
<>
|
||||
{/* Location Type Selector with Native Context Menu - Glass UI */}
|
||||
@@ -199,15 +209,23 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
<Ionicons name={selectedTypeConfig.iconName} size={20} color="#007AFF" />
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
<Text className="text-[17px] font-medium text-[#000]">{selectedTypeConfig.label}</Text>
|
||||
<Text className="mt-0.5 text-[13px] text-gray-500">{selectedTypeConfig.description}</Text>
|
||||
<Text className="text-[17px] font-medium text-[#000]">
|
||||
{selectedTypeConfig.label}
|
||||
</Text>
|
||||
<Text className="mt-0.5 text-[13px] text-gray-500">
|
||||
{selectedTypeConfig.description}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Native iOS Context Menu Button */}
|
||||
<Host matchContents>
|
||||
<ContextMenu
|
||||
modifiers={[buttonStyle(isLiquidGlassAvailable() ? "glass" : "bordered"), padding()]}
|
||||
activationMethod="singlePress">
|
||||
modifiers={[
|
||||
buttonStyle(isLiquidGlassAvailable() ? "glass" : "bordered"),
|
||||
padding(),
|
||||
]}
|
||||
activationMethod="singlePress"
|
||||
>
|
||||
<ContextMenu.Items>
|
||||
<Button
|
||||
systemImage={LOCATION_TYPES.link.sfSymbol}
|
||||
@@ -251,7 +269,9 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
onChangeText={setInputValue}
|
||||
keyboardType={selectedTypeConfig.keyboardType}
|
||||
multiline={selectedTypeConfig.inputType === "multiline"}
|
||||
textAlignVertical={selectedTypeConfig.inputType === "multiline" ? "top" : "center"}
|
||||
textAlignVertical={
|
||||
selectedTypeConfig.inputType === "multiline" ? "top" : "center"
|
||||
}
|
||||
autoCapitalize={selectedType === "link" ? "none" : "sentences"}
|
||||
autoCorrect={selectedType !== "link" && selectedType !== "phone"}
|
||||
editable={!isSaving}
|
||||
@@ -284,15 +304,23 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
<Ionicons name={selectedTypeConfig.iconName} size={20} color="#007AFF" />
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
<Text className="text-[17px] font-medium text-[#000]">{selectedTypeConfig.label}</Text>
|
||||
<Text className="mt-0.5 text-[13px] text-gray-500">{selectedTypeConfig.description}</Text>
|
||||
<Text className="text-[17px] font-medium text-[#000]">
|
||||
{selectedTypeConfig.label}
|
||||
</Text>
|
||||
<Text className="mt-0.5 text-[13px] text-gray-500">
|
||||
{selectedTypeConfig.description}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Native iOS Context Menu Button */}
|
||||
<Host matchContents>
|
||||
<ContextMenu
|
||||
modifiers={[buttonStyle(isLiquidGlassAvailable() ? "glass" : "bordered"), padding()]}
|
||||
activationMethod="singlePress">
|
||||
modifiers={[
|
||||
buttonStyle(isLiquidGlassAvailable() ? "glass" : "bordered"),
|
||||
padding(),
|
||||
]}
|
||||
activationMethod="singlePress"
|
||||
>
|
||||
<ContextMenu.Items>
|
||||
<Button
|
||||
systemImage={LOCATION_TYPES.link.sfSymbol}
|
||||
@@ -339,7 +367,9 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
onChangeText={setInputValue}
|
||||
keyboardType={selectedTypeConfig.keyboardType}
|
||||
multiline={selectedTypeConfig.inputType === "multiline"}
|
||||
textAlignVertical={selectedTypeConfig.inputType === "multiline" ? "top" : "center"}
|
||||
textAlignVertical={
|
||||
selectedTypeConfig.inputType === "multiline" ? "top" : "center"
|
||||
}
|
||||
autoCapitalize={selectedType === "link" ? "none" : "sentences"}
|
||||
autoCorrect={selectedType !== "link" && selectedType !== "phone"}
|
||||
editable={!isSaving}
|
||||
|
||||
@@ -86,7 +86,11 @@ export interface EditLocationScreenHandle {
|
||||
const detectLocationType = (location: string): LocationTypeId => {
|
||||
if (!location) return "link";
|
||||
|
||||
if (location.startsWith("http://") || location.startsWith("https://") || location.startsWith("www.")) {
|
||||
if (
|
||||
location.startsWith("http://") ||
|
||||
location.startsWith("https://") ||
|
||||
location.startsWith("www.")
|
||||
) {
|
||||
return "link";
|
||||
}
|
||||
|
||||
@@ -99,7 +103,10 @@ const detectLocationType = (location: string): LocationTypeId => {
|
||||
};
|
||||
|
||||
export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocationScreenProps>(
|
||||
function EditLocationScreen({ booking, onSuccess, onSavingChange, transparentBackground = false }, ref) {
|
||||
function EditLocationScreen(
|
||||
{ booking, onSuccess, onSavingChange, transparentBackground = false },
|
||||
ref
|
||||
) {
|
||||
const insets = useSafeAreaInsets();
|
||||
const backgroundStyle = transparentBackground ? "bg-transparent" : "bg-[#F2F2F7]";
|
||||
const pillStyle = transparentBackground ? "bg-[#E8E8ED]/50" : "bg-[#E8E8ED]";
|
||||
@@ -160,7 +167,9 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
setIsSaving(true);
|
||||
try {
|
||||
await CalComAPIService.updateLocationV2(booking.uid, locationPayload);
|
||||
Alert.alert("Success", "Location updated successfully", [{ text: "OK", onPress: onSuccess }]);
|
||||
Alert.alert("Success", "Location updated successfully", [
|
||||
{ text: "OK", onPress: onSuccess },
|
||||
]);
|
||||
setIsSaving(false);
|
||||
} catch (error) {
|
||||
safeLogError("[EditLocationScreen] Failed to update location:", error);
|
||||
@@ -189,7 +198,8 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
<>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
className={`flex-1 ${backgroundStyle}`}>
|
||||
className={`flex-1 ${backgroundStyle}`}
|
||||
>
|
||||
<ScrollView
|
||||
className="flex-1"
|
||||
contentContainerStyle={{
|
||||
@@ -197,11 +207,14 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
paddingBottom: insets.bottom + 16,
|
||||
}}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
showsVerticalScrollIndicator={!transparentBackground}>
|
||||
showsVerticalScrollIndicator={!transparentBackground}
|
||||
>
|
||||
{/* Current Location Info - hidden in transparent mode */}
|
||||
{!transparentBackground && booking.location && (
|
||||
<View className="mb-4 flex-row items-start rounded-xl bg-white p-4">
|
||||
<View className={`mr-3 h-10 w-10 items-center justify-center rounded-full ${pillStyle}`}>
|
||||
<View
|
||||
className={`mr-3 h-10 w-10 items-center justify-center rounded-full ${pillStyle}`}
|
||||
>
|
||||
<Ionicons name="location" size={20} color="#6B7280" />
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
@@ -227,16 +240,22 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
}
|
||||
onPress={() => setShowTypePicker(true)}
|
||||
disabled={isSaving}
|
||||
activeOpacity={0.7}>
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View
|
||||
className={`mr-3 h-10 w-10 items-center justify-center rounded-lg ${
|
||||
transparentBackground ? "bg-[#007AFF]/20" : "bg-[#007AFF]/15"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<Ionicons name={selectedTypeConfig.iconName} size={22} color="#007AFF" />
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
<Text className="text-[17px] font-medium text-[#000]">{selectedTypeConfig.label}</Text>
|
||||
<Text className="mt-0.5 text-[13px] text-gray-500">{selectedTypeConfig.description}</Text>
|
||||
<Text className="text-[17px] font-medium text-[#000]">
|
||||
{selectedTypeConfig.label}
|
||||
</Text>
|
||||
<Text className="mt-0.5 text-[13px] text-gray-500">
|
||||
{selectedTypeConfig.description}
|
||||
</Text>
|
||||
</View>
|
||||
<Ionicons name="chevron-down" size={20} color="#9CA3AF" />
|
||||
</TouchableOpacity>
|
||||
@@ -252,7 +271,8 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
transparentBackground
|
||||
? "mb-4 overflow-hidden rounded-xl border border-gray-300/40 bg-white/60"
|
||||
: "mb-4 overflow-hidden rounded-xl bg-white"
|
||||
}>
|
||||
}
|
||||
>
|
||||
<TextInput
|
||||
className={`px-4 py-3 text-[17px] text-[#000] ${
|
||||
selectedTypeConfig.inputType === "multiline" ? "min-h-[100px]" : "h-[50px]"
|
||||
@@ -277,14 +297,17 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
visible={showTypePicker}
|
||||
transparent
|
||||
animationType="fade"
|
||||
onRequestClose={() => setShowTypePicker(false)}>
|
||||
onRequestClose={() => setShowTypePicker(false)}
|
||||
>
|
||||
<TouchableOpacity
|
||||
className="flex-1 items-center justify-center bg-black/50"
|
||||
activeOpacity={1}
|
||||
onPress={() => setShowTypePicker(false)}>
|
||||
onPress={() => setShowTypePicker(false)}
|
||||
>
|
||||
<TouchableOpacity
|
||||
activeOpacity={1}
|
||||
className="max-h-[70%] w-[320px] overflow-hidden rounded-2xl bg-white">
|
||||
className="max-h-[70%] w-[320px] overflow-hidden rounded-2xl bg-white"
|
||||
>
|
||||
<View className="border-b border-gray-200 px-4 py-3">
|
||||
<Text className="text-center text-[17px] font-semibold text-[#000]">
|
||||
Select Location Type
|
||||
@@ -304,18 +327,25 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
setInputValue("");
|
||||
}
|
||||
setShowTypePicker(false);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<View
|
||||
className={`mr-3 h-10 w-10 items-center justify-center rounded-lg ${
|
||||
isSelected ? "bg-[#007AFF]/20" : "bg-[#F2F2F7]"
|
||||
}`}>
|
||||
<Ionicons name={item.iconName} size={22} color={isSelected ? "#007AFF" : "#6B7280"} />
|
||||
}`}
|
||||
>
|
||||
<Ionicons
|
||||
name={item.iconName}
|
||||
size={22}
|
||||
color={isSelected ? "#007AFF" : "#6B7280"}
|
||||
/>
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
<Text
|
||||
className={`text-[17px] ${
|
||||
isSelected ? "font-semibold text-[#007AFF]" : "text-[#000]"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
{item.label}
|
||||
</Text>
|
||||
<Text className="mt-0.5 text-[13px] text-gray-500">{item.description}</Text>
|
||||
@@ -327,7 +357,8 @@ export const EditLocationScreen = forwardRef<EditLocationScreenHandle, EditLocat
|
||||
/>
|
||||
<TouchableOpacity
|
||||
className="border-t border-gray-200 py-3"
|
||||
onPress={() => setShowTypePicker(false)}>
|
||||
onPress={() => setShowTypePicker(false)}
|
||||
>
|
||||
<Text className="text-center text-[17px] font-semibold text-[#007AFF]">Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -145,7 +145,11 @@ export function MarkNoShowScreen({
|
||||
const isLast = index === safeAttendees.length - 1;
|
||||
|
||||
// For transparent mode (iOS glass UI), use consistent styling regardless of no-show state
|
||||
const itemBgStyle = transparentBackground ? "bg-white/60" : isNoShow ? "bg-[#FEF2F2]" : "bg-white";
|
||||
const itemBgStyle = transparentBackground
|
||||
? "bg-white/60"
|
||||
: isNoShow
|
||||
? "bg-[#FEF2F2]"
|
||||
: "bg-white";
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
@@ -158,15 +162,18 @@ export function MarkNoShowScreen({
|
||||
} ${itemBgStyle}`}
|
||||
onPress={() => handleMarkNoShow(item)}
|
||||
disabled={isProcessing}
|
||||
activeOpacity={0.7}>
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View
|
||||
className={`mr-3 h-12 w-12 items-center justify-center rounded-full ${
|
||||
transparentBackground ? "bg-[#000]" : isNoShow ? "bg-[#FECACA]" : pillStyle
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<Text
|
||||
className={`text-[16px] font-semibold ${
|
||||
transparentBackground ? "text-white" : isNoShow ? "text-[#DC2626]" : "text-gray-600"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
{getInitials(item.name)}
|
||||
</Text>
|
||||
</View>
|
||||
@@ -186,15 +193,20 @@ export function MarkNoShowScreen({
|
||||
<View className="flex-row items-center">
|
||||
<View
|
||||
className={`flex-row items-center rounded-full px-3.5 py-2 ${
|
||||
isNoShow ? "border border-[#16A34A] bg-white" : "border border-[#FEE2E2] bg-[#FEE2E2]"
|
||||
}`}>
|
||||
isNoShow
|
||||
? "border border-[#16A34A] bg-white"
|
||||
: "border border-[#FEE2E2] bg-[#FEE2E2]"
|
||||
}`}
|
||||
>
|
||||
<Ionicons
|
||||
name={isNoShow ? "eye" : "eye-off"}
|
||||
size={15}
|
||||
color={isNoShow ? "#16A34A" : "#DC2626"}
|
||||
style={{ marginRight: 5 }}
|
||||
/>
|
||||
<Text className={`text-[14px] font-semibold ${isNoShow ? "text-[#16A34A]" : "text-[#DC2626]"}`}>
|
||||
<Text
|
||||
className={`text-[14px] font-semibold ${isNoShow ? "text-[#16A34A]" : "text-[#DC2626]"}`}
|
||||
>
|
||||
{isNoShow ? "Unmark" : "Mark"}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -57,12 +57,14 @@ export function MeetingSessionDetailsScreen({
|
||||
<View
|
||||
className={`mb-4 overflow-hidden rounded-xl ${
|
||||
transparentBackground ? "border border-gray-300/40 bg-white/60" : "bg-white"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
{/* Session header */}
|
||||
<View
|
||||
className={`flex-row items-center justify-between p-4 ${
|
||||
transparentBackground ? "" : "border-b border-gray-100"
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
<View className="flex-row items-center">
|
||||
<View className={`mr-3 h-10 w-10 items-center justify-center rounded-full ${pillStyle}`}>
|
||||
<Ionicons name="videocam" size={20} color="#6B7280" />
|
||||
@@ -73,7 +75,9 @@ export function MeetingSessionDetailsScreen({
|
||||
</View>
|
||||
{item.duration && (
|
||||
<View className={`rounded-full px-3 py-1 ${pillStyle}`}>
|
||||
<Text className="text-[13px] font-medium text-gray-600">{formatDuration(item.duration)}</Text>
|
||||
<Text className="text-[13px] font-medium text-gray-600">
|
||||
{formatDuration(item.duration)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
@@ -82,18 +86,24 @@ export function MeetingSessionDetailsScreen({
|
||||
<View className="p-4">
|
||||
<View className="mb-2 flex-row items-center">
|
||||
<Ionicons name="time-outline" size={18} color="#8E8E93" />
|
||||
<Text className="ml-2 text-[15px] text-gray-600">Started: {formatDate(item.startTime)}</Text>
|
||||
<Text className="ml-2 text-[15px] text-gray-600">
|
||||
Started: {formatDate(item.startTime)}
|
||||
</Text>
|
||||
</View>
|
||||
{item.endTime && (
|
||||
<View className="mb-2 flex-row items-center">
|
||||
<Ionicons name="time-outline" size={18} color="#8E8E93" />
|
||||
<Text className="ml-2 text-[15px] text-gray-600">Ended: {formatTime(item.endTime)}</Text>
|
||||
<Text className="ml-2 text-[15px] text-gray-600">
|
||||
Ended: {formatTime(item.endTime)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
{item.maxParticipants && (
|
||||
<View className="flex-row items-center">
|
||||
<Ionicons name="people-outline" size={18} color="#8E8E93" />
|
||||
<Text className="ml-2 text-[15px] text-gray-600">Max participants: {item.maxParticipants}</Text>
|
||||
<Text className="ml-2 text-[15px] text-gray-600">
|
||||
Max participants: {item.maxParticipants}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
@@ -109,8 +119,11 @@ export function MeetingSessionDetailsScreen({
|
||||
key={participant.id || pIndex}
|
||||
className={`mb-2 flex-row items-center p-3 ${
|
||||
transparentBackground ? "" : "rounded-xl bg-[#F2F2F7]"
|
||||
}`}>
|
||||
<View className={`mr-3 h-9 w-9 items-center justify-center rounded-full ${pillStyle}`}>
|
||||
}`}
|
||||
>
|
||||
<View
|
||||
className={`mr-3 h-9 w-9 items-center justify-center rounded-full ${pillStyle}`}
|
||||
>
|
||||
<Ionicons name="person" size={18} color="#6B7280" />
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
|
||||
@@ -59,7 +59,8 @@ export function ViewRecordingsScreen({
|
||||
transparentBackground ? "border border-gray-300/40 bg-white/60" : "bg-white"
|
||||
}`}
|
||||
onPress={() => handleOpenRecording(item)}
|
||||
activeOpacity={0.7}>
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View className="flex-row items-center p-4">
|
||||
<View className={`mr-3 h-10 w-10 items-center justify-center rounded-full ${pillStyle}`}>
|
||||
<Ionicons name="videocam" size={20} color="#6B7280" />
|
||||
@@ -84,7 +85,9 @@ export function ViewRecordingsScreen({
|
||||
<View className={`mb-4 h-16 w-16 items-center justify-center rounded-full ${pillStyle}`}>
|
||||
<Ionicons name="videocam-off" size={32} color="#8E8E93" />
|
||||
</View>
|
||||
<Text className="text-center text-[17px] font-semibold text-[#000]">No recordings available</Text>
|
||||
<Text className="text-center text-[17px] font-semibold text-[#000]">
|
||||
No recordings available
|
||||
</Text>
|
||||
<Text className="mt-2 text-center text-[15px] leading-5 text-gray-500">
|
||||
Recordings may take some time to process after the meeting ends.
|
||||
</Text>
|
||||
|
||||
+27
-1
@@ -130,7 +130,33 @@
|
||||
"dayjs@^1.8.29": "patch:dayjs@npm%3A1.11.4#./.yarn/patches/dayjs-npm-1.11.4-97921cd375.patch",
|
||||
"import-in-the-middle": "1.13.1",
|
||||
"react@19.2.0": "19.2.2",
|
||||
"react@19.2.1": "19.2.2"
|
||||
"react@19.2.1": "19.2.2",
|
||||
"tar-fs": "2.1.4",
|
||||
"rollup": "4.22.4",
|
||||
"jpeg-js": "0.4.4",
|
||||
"validator": "13.15.22",
|
||||
"form-data": "4.0.4",
|
||||
"axios": "1.13.2",
|
||||
"jws": "4.0.1",
|
||||
"jsonwebtoken": "9.0.0"
|
||||
},
|
||||
"packageExtensions": {
|
||||
"@boxyhq/saml-jackson@*": {
|
||||
"dependencies": {
|
||||
"typeorm": "0.3.27",
|
||||
"node-forge": "1.3.2"
|
||||
}
|
||||
},
|
||||
"lingo.dev@*": {
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "1.24.0"
|
||||
}
|
||||
},
|
||||
"ink@3.2.0": {
|
||||
"dependencies": {
|
||||
"ws": "7.5.10"
|
||||
}
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">=7.0.0",
|
||||
|
||||
@@ -45,6 +45,6 @@
|
||||
"devDependencies": {
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@types/web-push": "3.6.3",
|
||||
"trigger.dev": "4.1.0"
|
||||
"trigger.dev": "4.3.1"
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@
|
||||
"clsx": "2.1.1",
|
||||
"dompurify": "3.3.1",
|
||||
"marked": "15.0.6",
|
||||
"react-use": "17.5.0",
|
||||
"react-use": "17.6.0",
|
||||
"tailwind-merge": "1.13.2",
|
||||
"tailwindcss": "4.1.17"
|
||||
},
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"@calcom/platform-constants": "workspace:*",
|
||||
"@calcom/platform-enums": "workspace:*",
|
||||
"@nestjs/common": "10.4.20",
|
||||
"@nestjs/swagger": "7.3.0",
|
||||
"@nestjs/swagger": "7.4.2",
|
||||
"class-transformer": "0.5.1",
|
||||
"class-validator": "0.14.3",
|
||||
"libphonenumber-js": "1.10.49",
|
||||
|
||||
Reference in new issue
Block a user