feat(admin): admins can create one-off custom appointments (#28463)#28502
feat(admin): admins can create one-off custom appointments (#28463)#28502anmolxlight wants to merge 2 commits intocalcom:mainfrom
Conversation
|
Ryan seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 27cd821e76
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| attendees: z.array(z.object({ | ||
| email: z.string().email(), | ||
| name: z.string(), | ||
| })), |
There was a problem hiding this comment.
Require a primary attendee in createCustomAppointment
If this mutation is called with attendees: [], it creates a booking that violates an existing invariant: several downstream consumers index the first attendee without guarding for empties, e.g. apps/web/components/booking/BookingListItem.tsx:1102-1104 and packages/features/ee/workflows/lib/getiCalEventAsString.ts:52-55. That means an admin-created booking with no attendees can later break booking pages or iCal/reminder generation instead of behaving like a normal appointment.
Useful? React with 👍 / 👎.
| create: input.attendees.map((attendee) => ({ | ||
| name: attendee.name, | ||
| email: attendee.email, | ||
| timeZone: targetUser.timeZone, |
There was a problem hiding this comment.
Store each attendee's own timezone
For custom appointments with attendees outside the host's timezone, this persists every attendee as targetUser.timeZone, so attendee-facing reminders and webhook payloads will be localized incorrectly. We already use attendee.timeZone directly when formatting reminder content (packages/features/ee/workflows/lib/reminders/utils.ts:64-66) and building booking reminder attendees (apps/web/app/api/cron/bookingReminder/route.ts:105-110), so a New York attendee booked for a London host will see the wrong local time. Because the schema only accepts name and email, the caller has no way to provide the correct timezone.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
2 issues found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/web/modules/ee/users/components/UsersTable.tsx">
<violation number="1" location="apps/web/modules/ee/users/components/UsersTable.tsx:417">
P2: Custom appointment creation does not validate that end time is after start time, allowing invalid inverted booking ranges to be submitted and persisted.</violation>
<violation number="2" location="apps/web/modules/ee/users/components/UsersTable.tsx:418">
P2: The attendee timezone is not collected or sent with the mutation. The handler will likely default all attendees to the host's timezone, causing reminders, iCal events, and webhook payloads to show incorrect local times for attendees in different timezones. Add a timezone field to the attendee input (defaulting to the browser's detected timezone if not explicitly provided).</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
6 issues found across 3 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/trpc/server/routers/viewer/admin/createCustomAppointment.schema.ts">
<violation number="1" location="packages/trpc/server/routers/viewer/admin/createCustomAppointment.schema.ts:7">
P2: UTC-only `z.string().datetime()` rejects valid ISO timestamps with numeric offsets, causing avoidable validation failures for client-supplied datetimes.</violation>
<violation number="2" location="packages/trpc/server/routers/viewer/admin/createCustomAppointment.schema.ts:10">
P1: The `attendees` array accepts an empty list. Bookings created with zero attendees will break downstream consumers that index `attendees[0]` without a guard (e.g., booking list rendering, iCal generation, workflow reminders). Add `.min(1)` to require at least one attendee.</violation>
<violation number="3" location="packages/trpc/server/routers/viewer/admin/createCustomAppointment.schema.ts:14">
P2: Missing cross-field validation allows invalid booking intervals where endTime is not after startTime.</violation>
</file>
<file name="packages/trpc/server/routers/viewer/admin/createCustomAppointment.handler.ts">
<violation number="1" location="packages/trpc/server/routers/viewer/admin/createCustomAppointment.handler.ts:52">
P2: Custom appointment creation stores `responses` as empty object, which can break downstream flows expecting `responses.email`/booker data.</violation>
<violation number="2" location="packages/trpc/server/routers/viewer/admin/createCustomAppointment.handler.ts:57">
P2: Every attendee is persisted with the host's timezone (`targetUser.timeZone`) instead of their own. Downstream reminder and webhook logic reads `attendee.timeZone` to localize times, so a New York attendee on a London host will receive incorrectly localized notifications. Add a `timeZone` field to each attendee in the schema and use it here.</violation>
<violation number="3" location="packages/trpc/server/routers/viewer/admin/createCustomAppointment.handler.ts:61">
P2: Per project Prisma guidelines, never use `include` — use `select` instead. `include: { attendees: true }` fetches all attendee columns, risking unnecessary data exposure from the tRPC endpoint. Replace with a `select` clause that explicitly lists only the needed booking and attendee fields.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
packages/trpc/server/routers/viewer/admin/createCustomAppointment.schema.ts
Show resolved
Hide resolved
packages/trpc/server/routers/viewer/admin/createCustomAppointment.handler.ts
Outdated
Show resolved
Hide resolved
packages/trpc/server/routers/viewer/admin/createCustomAppointment.schema.ts
Outdated
Show resolved
Hide resolved
packages/trpc/server/routers/viewer/admin/createCustomAppointment.schema.ts
Show resolved
Hide resolved
packages/trpc/server/routers/viewer/admin/createCustomAppointment.handler.ts
Outdated
Show resolved
Hide resolved
packages/trpc/server/routers/viewer/admin/createCustomAppointment.handler.ts
Outdated
Show resolved
Hide resolved
3e85c55 to
106f732
Compare
romitg2
left a comment
There was a problem hiding this comment.
thanks, we'll remove 'needs approval' label from issue if we decide to add this feature. closing for now.
Fixes #28463. This adds the backend TRPC capability for system admins to create one-off custom appointments directly for users. It bypasses event types and provisions a one-off booking directly.