Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions booking-app/app/api/bookings/edit/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
BookingOrigin,
BookingStatusLabel,
} from "@/components/src/types";
import { getSecondaryContactName } from "@/components/src/utils/formatters";
import { callXStateTransitionAPI } from "@/components/src/server/db";
import { getStatusFromXState } from "@/components/src/utils/statusFromXState";
import { shouldUseXState } from "@/components/src/utils/tenantUtils";
Expand Down Expand Up @@ -109,6 +110,7 @@ async function sendEditNotificationEmails(
contents: {
...formattedContents,
requestNumber: existingContents.requestNumber + "",
secondaryContactName: getSecondaryContactName(userEventInputs as any),
},
targetEmail: recipient,
status: BookingStatusLabel.REQUESTED,
Expand Down
10 changes: 6 additions & 4 deletions booking-app/app/api/bookings/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
BookingStatusLabel,
RoomSetting,
} from "@/components/src/types";
import { getSecondaryContactName } from "@/components/src/utils/formatters";
import {
logServerBookingChange,
serverGetNextSequentialId,
Expand Down Expand Up @@ -191,14 +192,14 @@ const buildBookingContents = (
return {
...data,
roomId: selectedRoomIds,
startDate: startDateObj.toLocaleDateString(),
startTime: startDateObj.toLocaleTimeString([], {
startDate: startDateObj.toLocaleDateString("en-US"),
startTime: startDateObj.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
hour12: true,
}),
endDate: endDateObj.toLocaleDateString(),
endTime: endDateObj.toLocaleTimeString([], {
endDate: endDateObj.toLocaleDateString("en-US"),
endTime: endDateObj.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
hour12: true,
Expand Down Expand Up @@ -343,6 +344,7 @@ async function handleBookingApprovalEmails(
contents: {
...contentsAsStrings,
requestNumber: contents.requestNumber + "",
secondaryContactName: getSecondaryContactName(contents),
},
targetEmail: recipient,
status: BookingStatusLabel.REQUESTED,
Expand Down
8 changes: 7 additions & 1 deletion booking-app/app/templates/booking_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,13 @@
<tr>
<td>Secondary Contact Name</td>
<td>
{{#if contents.secondaryName}}{{ contents.secondaryName
{{#if contents.secondaryContactName}}{{ contents.secondaryContactName }}{{else}}none{{/if}}
</td>
</tr>
<tr>
<td>Secondary Contact Email</td>
<td>
{{#if contents.secondaryEmail}}{{ contents.secondaryEmail
}}{{else}}none{{/if}}
</td>
</tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ export function BookingFormDropdown(props: DropdownInputs) {
interface TextFieldProps extends Props {
description?: React.ReactNode;
pattern?: ValidationRule<RegExp>;
// Custom validation function for the field value
// Note: For optional fields, this will only be called if the field has a non-empty value
// If you need to validate empty values for optional fields, handle that case explicitly
validate?: any;
containerSx?: any;
fieldSx?: any;
Expand Down Expand Up @@ -154,10 +157,23 @@ export function BookingFormTextField(props: TextFieldProps) {
rules={{
required: required && `${label} is required`,
validate: (value) => {
if (!required) return true;
const isNotEmpty = value?.trim().length > 0;
const isValid = validate(value);
return (isNotEmpty && isValid) || `${label} is required`;
// If field is not required and empty, allow it
if (!required && (!value || value.trim().length === 0)) return true;

// Check for whitespace-only values
if (value && typeof value === "string" && value.trim().length === 0) {
return `${label} cannot be empty whitespace`;
}

// For required fields, check if value is valid
if (required) {
const isNotEmpty = value?.trim().length > 0;
const isValid = validate(value);
return (isNotEmpty && isValid) || `${label} is required`;
}

// For optional fields with a value, run custom validation
return validate(value);
},
Comment on lines 159 to 177
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validate function will be called with the value even for optional fields with values (line 173), but the validate parameter defaults to returning true (line 147). This means if a custom validation function expects a non-empty value but receives an empty string for an optional field, it should handle that case. Consider adding a comment to clarify that custom validate functions for optional fields should handle empty values, or ensure empty values aren't passed to custom validators for optional fields.

Copilot uses AI. Check for mistakes.
pattern,
}}
Expand All @@ -183,6 +199,7 @@ export function BookingFormTextField(props: TextFieldProps) {

interface SwitchProps extends Props {
description?: React.ReactElement;
disabled?: boolean;
}

export function BookingFormSwitch(props: SwitchProps) {
Expand All @@ -193,6 +210,7 @@ export function BookingFormSwitch(props: SwitchProps) {
required = true,
control,
trigger,
disabled = false,
} = props;

const desc =
Expand Down Expand Up @@ -221,6 +239,7 @@ export function BookingFormSwitch(props: SwitchProps) {
field.onChange(e.target.checked ? "yes" : "no")
}
onBlur={() => trigger(id)}
disabled={disabled}
/>
}
/>
Expand Down
Loading
Loading