Skip to content

Commit 420ae6a

Browse files
authored
Enhance calBookings mapping and schema: Added fallback for reschedulingReason, updated eventType and meetingUrl to support null values, and improved MeetingDetails component with URL validation. (#69)
1 parent 2c72d2b commit 420ae6a

File tree

4 files changed

+62
-37
lines changed

4 files changed

+62
-37
lines changed

lib/dto/calBookings.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ export const mapCalBookingToMeetingRecord = (booking: CalBooking): MeetingRecord
162162
}))
163163
: [];
164164
const normalizedGuests = Array.isArray(booking?.guests) ? booking.guests : [];
165+
166+
// Extract rescheduleReason from bookingFieldsResponses if not at top level
167+
const bookingFields = (booking.bookingFieldsResponses as Record<string, unknown> | undefined) ?? {};
168+
const rescheduleReasonFromFields = typeof bookingFields.rescheduleReason === "string"
169+
? bookingFields.rescheduleReason
170+
: undefined;
171+
172+
// Prefer top-level reschedulingReason, fallback to rescheduleReason from bookingFieldsResponses
173+
const reschedulingReason = booking.reschedulingReason ?? rescheduleReasonFromFields;
165174

166175
return {
167176
id: typeof booking.id === "number" ? booking.id : 0,
@@ -172,35 +181,31 @@ export const mapCalBookingToMeetingRecord = (booking: CalBooking): MeetingRecord
172181
status: typeof booking.status === "string" ? booking.status : "pending",
173182
cancellationReason: booking.cancellationReason ?? undefined,
174183
cancelledByEmail: booking.cancelledByEmail ?? undefined,
175-
reschedulingReason: booking.reschedulingReason ?? undefined,
184+
reschedulingReason: reschedulingReason ?? undefined,
176185
rescheduledByEmail: booking.rescheduledByEmail ?? undefined,
177186
rescheduledFromUid: booking.rescheduledFromUid ?? undefined,
178187
rescheduledToUid: booking.rescheduledToUid ?? undefined,
179188
start: booking.start ?? "",
180189
end: booking.end ?? "",
181190
duration: booking.duration ?? 0,
182-
eventTypeId: booking.eventTypeId ?? (typeof eventType?.id === "number" ? eventType.id : 0),
183-
eventType: {
184-
id:
185-
typeof eventType?.id === "number"
186-
? eventType.id
187-
: typeof booking.eventTypeId === "number"
188-
? booking.eventTypeId
189-
: 0,
190-
slug: typeof eventType?.slug === "string" ? eventType.slug : "",
191-
},
191+
eventTypeId: booking.eventTypeId ?? null,
192+
eventType: eventType
193+
? {
194+
id: typeof eventType.id === "number" ? eventType.id : 0,
195+
slug: typeof eventType.slug === "string" ? eventType.slug : "",
196+
}
197+
: null,
192198
meetingUrl: booking.meetingUrl ?? "",
193199
location: booking.location ?? "",
194200
absentHost: booking.absentHost ?? false,
195201
createdAt: booking.createdAt ?? "",
196-
updatedAt: booking.updatedAt ?? "",
202+
updatedAt: booking.updatedAt ?? null,
197203
metadata: (booking.metadata as Record<string, unknown> | undefined) ?? {},
198-
rating: booking.rating ?? 0,
199-
icsUid: booking.icsUid ?? "",
204+
rating: booking.rating ?? null,
205+
icsUid: booking.icsUid ?? null,
200206
attendees: normalizedAttendees,
201207
guests: normalizedGuests,
202-
bookingFieldsResponses:
203-
(booking.bookingFieldsResponses as Record<string, unknown> | undefined) ?? {},
208+
bookingFieldsResponses: bookingFields,
204209
};
205210
};
206211

lib/schemas/calBookings.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,18 +127,20 @@ export const calBookingSchema = z.object({
127127
start: isoDateStringSchema,
128128
end: isoDateStringSchema,
129129
duration: z.number().nonnegative(),
130-
eventTypeId: z.number(),
131-
eventType: z.object({
132-
id: z.number(),
133-
slug: z.string(),
134-
}),
135-
meetingUrl: z.string().url(),
130+
eventTypeId: z.number().nullable(),
131+
eventType: z
132+
.object({
133+
id: z.number(),
134+
slug: z.string(),
135+
})
136+
.nullable(),
137+
meetingUrl: z.string(),
136138
location: z.string().nullable().optional(),
137139
absentHost: z.boolean().optional(),
138140
createdAt: isoDateStringSchema,
139-
updatedAt: isoDateStringSchema,
141+
updatedAt: isoDateStringSchema.nullable(),
140142
metadata: z.record(z.string(), z.unknown()).optional(),
141-
rating: z.number().optional(),
143+
rating: z.number().nullable().optional(),
142144
icsUid: z.string().nullable().optional(),
143145
attendees: z.array(bookingAttendeeSchema).optional(),
144146
guests: z.array(z.string().email()).optional(),

lib/types/meeting.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,16 @@ export interface MeetingItem {
4747
readonly start: string;
4848
readonly end: string;
4949
readonly duration: number;
50-
readonly eventTypeId: number;
51-
readonly eventType: MeetingEventType;
50+
readonly eventTypeId: number | null;
51+
readonly eventType: MeetingEventType | null;
5252
readonly meetingUrl: string;
5353
readonly location: string;
5454
readonly absentHost: boolean;
5555
readonly createdAt: string;
56-
readonly updatedAt: string;
56+
readonly updatedAt: string | null;
5757
readonly metadata: Record<string, unknown>;
58-
readonly rating: number;
59-
readonly icsUid: string;
58+
readonly rating: number | null;
59+
readonly icsUid: string | null;
6060
readonly attendees: readonly MeetingAttendee[];
6161
readonly guests: readonly string[];
6262
readonly bookingFieldsResponses: Record<string, unknown>;

modules/dashboard/MeetingDetails.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,21 @@ export function MeetingDetails({ meeting }: MeetingDetailsProps) {
3131
if (url.includes('meet.google.com')) return 'Google Meet';
3232
if (url.includes('zoom.us')) return 'Zoom';
3333
if (url.includes('app.cal.com')) return 'Cal.com Video';
34+
if (url.includes('integrations:google:meet')) return 'Google Meet';
35+
if (url.includes('integrations:zoom')) return 'Zoom';
36+
if (url.includes('integrations:daily')) return 'Cal.com Video';
3437
return 'Other';
3538
};
3639

40+
const isValidUrl = (url: string) => {
41+
try {
42+
new URL(url);
43+
return true;
44+
} catch {
45+
return false;
46+
}
47+
};
48+
3749
return (
3850
<div className="space-y-6">
3951
{/* Title and Status */}
@@ -129,14 +141,20 @@ export function MeetingDetails({ meeting }: MeetingDetailsProps) {
129141
<div className="flex-1">
130142
<div className="text-sm">Platform</div>
131143
<div className="text-muted-foreground text-sm">{getPlatformName(meeting.meetingUrl)}</div>
132-
<a
133-
href={meeting.meetingUrl}
134-
target="_blank"
135-
rel="noopener noreferrer"
136-
className="text-xs text-primary hover:underline break-all mt-1 block"
137-
>
138-
{meeting.meetingUrl}
139-
</a>
144+
{isValidUrl(meeting.meetingUrl) ? (
145+
<a
146+
href={meeting.meetingUrl}
147+
target="_blank"
148+
rel="noopener noreferrer"
149+
className="text-xs text-primary hover:underline break-all mt-1 block"
150+
>
151+
{meeting.meetingUrl}
152+
</a>
153+
) : (
154+
<div className="text-xs text-muted-foreground break-all mt-1">
155+
{meeting.meetingUrl}
156+
</div>
157+
)}
140158
</div>
141159
</div>
142160

0 commit comments

Comments
 (0)