2023-07-13 20:40:16 +00:00
|
|
|
import type { Prisma, WorkflowReminder } from "@prisma/client";
|
2023-02-27 20:45:40 +00:00
|
|
|
import type { NextApiRequest } from "next";
|
2022-10-20 23:28:02 +00:00
|
|
|
|
2023-02-08 20:36:22 +00:00
|
|
|
import appStore from "@calcom/app-store";
|
2022-10-20 23:28:02 +00:00
|
|
|
import { getCalendar } from "@calcom/app-store/_utils/getCalendar";
|
|
|
|
import { FAKE_DAILY_CREDENTIAL } from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
|
|
|
|
import { DailyLocationType } from "@calcom/app-store/locations";
|
2023-03-14 04:19:05 +00:00
|
|
|
import { deleteMeeting, updateMeeting } from "@calcom/core/videoClient";
|
2022-10-20 23:28:02 +00:00
|
|
|
import dayjs from "@calcom/dayjs";
|
2023-03-14 04:19:05 +00:00
|
|
|
import { sendCancelledEmails, sendCancelledSeatEmails } from "@calcom/emails";
|
2023-04-04 04:59:09 +00:00
|
|
|
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
|
2022-10-20 23:28:02 +00:00
|
|
|
import { deleteScheduledEmailReminder } from "@calcom/features/ee/workflows/lib/reminders/emailReminderManager";
|
|
|
|
import { sendCancelledReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
|
|
|
|
import { deleteScheduledSMSReminder } from "@calcom/features/ee/workflows/lib/reminders/smsReminderManager";
|
2023-07-11 15:48:44 +00:00
|
|
|
import { deleteScheduledWhatsappReminder } from "@calcom/features/ee/workflows/lib/reminders/whatsappReminderManager";
|
2022-10-20 23:28:02 +00:00
|
|
|
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
|
2023-08-30 23:17:42 +00:00
|
|
|
import { cancelScheduledJobs } from "@calcom/features/webhooks/lib/scheduleTrigger";
|
2023-02-27 20:45:40 +00:00
|
|
|
import type { EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
|
|
|
|
import sendPayload from "@calcom/features/webhooks/lib/sendPayload";
|
2022-10-20 23:28:02 +00:00
|
|
|
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
|
2023-07-25 17:05:02 +00:00
|
|
|
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
|
2022-10-20 23:28:02 +00:00
|
|
|
import { HttpError } from "@calcom/lib/http-error";
|
2023-05-11 07:14:32 +00:00
|
|
|
import logger from "@calcom/lib/logger";
|
2023-02-08 20:36:22 +00:00
|
|
|
import { handleRefundError } from "@calcom/lib/payment/handleRefundError";
|
2022-10-20 23:28:02 +00:00
|
|
|
import { getTranslation } from "@calcom/lib/server/i18n";
|
2023-07-19 14:30:37 +00:00
|
|
|
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
2022-10-20 23:28:02 +00:00
|
|
|
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
2023-09-14 16:53:58 +00:00
|
|
|
import { BookingStatus, MembershipRole, WebhookTriggerEvents, WorkflowMethods } from "@calcom/prisma/enums";
|
|
|
|
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
2022-10-20 23:28:02 +00:00
|
|
|
import { schemaBookingCancelParams } from "@calcom/prisma/zod-utils";
|
|
|
|
import type { CalendarEvent } from "@calcom/types/Calendar";
|
feat: Enable Apps for Teams & Orgs [CAL-1782] (#9337)
* Initial commit
* Adding feature flag
* Add schema relation for teams and credentials
* feat: Orgs Schema Changing `scopedMembers` to `orgUsers` (#9209)
* Change scopedMembers to orgMembers
* Change to orgUsers
* Create getUserAdminTeams function & tRPC endpoint
* Get user admin teams on app store page
* Create UserAdminTeams type
* Add user query to getUserAdminTeams
* Letting duplicate slugs for teams to support orgs
* Covering null on unique clauses
* Add dropdown to install button on app store
* Supporting having the orgId in the session cookie
* On app page, only show dropdown if there are teams
* Add teamId to OAuth state
* Create team credential for OAuth flow
* Create team credential for GCal
* Add create user or team credential for Stripe
* Create webex credentials for users or teams
* Fix type error on useAddAppMutation
* Hubspot create credential on user or team
* Zoho create create credential for user or team
* Zoom create credentials on user or team
* Salesforce create credential on user or teams
* OAuth create credentials for user or teams
* Revert Outlook changes
* Revert GCal changes
* Default app instal, create credential on user or team
* Add teamId to credential creation
* Disable installing for teams for calendars
* Include teams when querying installed apps
* Render team credentials on installed page
* Uninstall team apps
* Type fix on app card
* Add input to include user in teams query
* Add dropdown to install app page for user or team
* Type fixes on category page
* Install app from eventType page to user or team
* Render user and team apps on event type app page
* feat: organization event type filter (#9253)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Missing changes to support orgs schema changes
* Render user and team apps on event type app page
* Add credentialOwner to eventTypeAppCard types
* Type fixes
* Create hook to check if app is enabled
* Clean up console.logs
* Fix useIsAppEnabled by returning not an array
* Convert event type apps to useIsAppEnabled
* Abstract credential owner type
* Remove console.logs
* On installed app page, show apps if only team credential is installed
* Clean up commented lines
* Handle installing app to just an team event from event type page
* Fix early return when creating team app credential
* Zoom add state to callback
* Get team location credentials and save credential id to location
* feat: Onboarding process to create an organization (#9184)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* feedback
* Making sure we check requestedSlug now
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Type fix
* Grab team location credentials
* Add isInstalled to eventType apps query
* feat: [CAL-1816] Organization subdomain support (#9345)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* Covering users and subteams, excluding non-org users
* Unpublished teams shows correctly
* Create subdomain in Vercel
* feedback
* Renaming Vercel env vars
* Vercel domain check before creation
* Supporting cal-staging.com
* Change to have vercel detect it
* vercel domain check data message error
* Remove check domain
* Making sure we check requestedSlug now
* Feedback and unneeded code
* Reverting unneeded changes
* Unneeded changes
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Vercel subdomain creation in PROD only
* Enable payment apps for team credentials
* Fix for team-user apps for event types
* Fix layout and add teamId to app card
* Disable apps on managed event types
* Add managed event type fields to event type apps
* Include organizations in query
* Change createAppCredential to createOAuthAppCredential
* Show app installed on teams
* Making sure we let localhost still work
* UI show installed for which team
* Type fixes
* For team events move use host location to top
* Add around to appStore
* New team event types organizer default conf app
* Fix app card bug
* Clean up
* Search for teamId or userId when deleting credential
* Type fixes
* Type fixes
* Type fixes
* Type fixes
* Address feedback
* Feedback
* Type check fixes
* feat: Organization branding in side menu (#9279)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Org branding provider used in shell sidebar
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Using org avatar (temp)
* Not showing org logo if not set
* User onboarding with org branding (slug)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* feedback
* Feedback
* Org public profile
* Public profiles for team event types
* Added setup profile alert
* Using org avatar on subteams avatar
* Making sure we show the set up profile on org only
* Profile username availability rely on org hook
* Update apps/web/pages/team/[slug].tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Update apps/web/pages/team/[slug].tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* feat: Organization support for event types page (#9449)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Org branding provider used in shell sidebar
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Using org avatar (temp)
* Not showing org logo if not set
* User onboarding with org branding (slug)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* feedback
* Feedback
* Org public profile
* Public profiles for team event types
* Added setup profile alert
* Using org avatar on subteams avatar
* Processing orgs and children as profile options
* Reverting change not belonging to this PR
* Making sure we show the set up profile on org only
* Removing console.log
* Comparing memberships to choose the highest one
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Type errors
* Refactor and type fixes
* Update orgDomains.ts
* Feedback
* Reverting
* NIT
* Address feedback
* fix issue getting org slug from domain
* Improving orgDomains util
* Host comes with port
* Update useRouterQuery.ts
* Fix app card bug
* Fix schema
* Type fixes
* Revert changes to location apps
* Remove console.log
* Fix app store test
* Handle install app dropdown
* Add CalendarApp to `getCalendar`
* Add PaymentApp type fix
* Payment type fix
* Type fixes
* Match with main
* Change type to account for team
* Fix app count for team events
* Type fixes
* More type fixes
* Type fix?
* Fix the type fix
* Remove UserAdminTeams empty array union
* Type fix
* Type fix
* Type fix
* Uses type predicates
* Use teamId. Fixes installation for teams after user installation
* Fix Team Events not working
* Get embed for org events working
* Fix rewrites
* Address feedback
* Type fix
* Fixes
* Add useAppContextWithSchema in useIsAppEnabled
* Type fix for apps using useIsAppEnabled
* Integrations.handler change credentialIds to userCredentialIds
* Remove apps endpoint
* Add LockedIcon and disabled props to event type app context
* Type fixes
* Type fix
* Type fixes
* Show team installed apps for members
* Type fix
* Reverting findFirst
* Revert findFirst
* Avoid a possible 500
* Fix missing tanslation
* Avoid possible 500
* Undo default app for teams
* Type fix
* Fix test
* Update package.json
* feat: Fix invite bug - added tests (#9945)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* chore: Button Component Tidy up (#9888)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
* feat: Make Team Private
## What does this PR do?
Fixes https://github.com/calcom/cal.com/issues/8974
1) When user is admin
<img width="1440" alt="Screenshot 2023-07-03 at 6 45 50 PM" src="https://github.com/calcom/cal.com/assets/53316345/ce15158f-d278-4f1a-ba2e-8b63e4274793">
2) When user is not admin and team is private
<img width="1440" alt="Screenshot 2023-07-03 at 6 47 15 PM" src="https://github.com/calcom/cal.com/assets/53316345/ce23560e-690a-4c42-a76d-49691260aa4d">
3)
<img width="1440" alt="Screenshot 2023-07-03 at 6 51 56 PM" src="https://github.com/calcom/cal.com/assets/53316345/13af38f8-5618-4dae-b359-b24dc91e4eb4">
## Type of change
<!-- Please delete bullets that are not relevant. -->
- New feature (non-breaking change which adds functionality)
## How should this be tested?
1) go to Team members page and turn on switch Make Team Private.
Now after making the team private only admin would be able to see all the members list in the settings. There will not be a button to Book a team member instead on the team page like before.
## Mandatory Tasks
- [ ] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-06 16:48:39 +00:00
|
|
|
import type { IAbstractPaymentService, PaymentApp } from "@calcom/types/PaymentService";
|
2022-10-20 23:28:02 +00:00
|
|
|
|
2023-03-14 04:19:05 +00:00
|
|
|
async function getBookingToDelete(id: number | undefined, uid: string | undefined) {
|
|
|
|
return await prisma.booking.findUnique({
|
2022-10-20 23:28:02 +00:00
|
|
|
where: {
|
|
|
|
id,
|
|
|
|
uid,
|
|
|
|
},
|
|
|
|
select: {
|
|
|
|
...bookingMinimalSelect,
|
|
|
|
recurringEventId: true,
|
|
|
|
userId: true,
|
|
|
|
user: {
|
|
|
|
select: {
|
|
|
|
id: true,
|
2023-09-14 16:53:58 +00:00
|
|
|
credentials: { select: credentialForCalendarServiceSelect }, // Not leaking at the moment, be careful with
|
2022-10-20 23:28:02 +00:00
|
|
|
email: true,
|
|
|
|
timeZone: true,
|
2023-07-19 14:30:37 +00:00
|
|
|
timeFormat: true,
|
2022-10-20 23:28:02 +00:00
|
|
|
name: true,
|
|
|
|
destinationCalendar: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
location: true,
|
|
|
|
references: {
|
|
|
|
select: {
|
|
|
|
uid: true,
|
|
|
|
type: true,
|
|
|
|
externalCalendarId: true,
|
|
|
|
credentialId: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
payment: true,
|
|
|
|
paid: true,
|
|
|
|
eventType: {
|
|
|
|
select: {
|
2023-04-18 10:08:09 +00:00
|
|
|
slug: true,
|
2023-08-10 18:52:36 +00:00
|
|
|
owner: {
|
|
|
|
select: {
|
|
|
|
id: true,
|
|
|
|
hideBranding: true,
|
|
|
|
},
|
|
|
|
},
|
2023-02-08 20:36:22 +00:00
|
|
|
teamId: true,
|
2022-10-20 23:28:02 +00:00
|
|
|
recurringEvent: true,
|
|
|
|
title: true,
|
2023-06-07 21:54:00 +00:00
|
|
|
eventName: true,
|
2022-10-20 23:28:02 +00:00
|
|
|
description: true,
|
|
|
|
requiresConfirmation: true,
|
|
|
|
price: true,
|
|
|
|
currency: true,
|
|
|
|
length: true,
|
2023-03-14 04:19:05 +00:00
|
|
|
seatsPerTimeSlot: true,
|
2023-04-04 04:59:09 +00:00
|
|
|
bookingFields: true,
|
2023-03-30 23:45:48 +00:00
|
|
|
seatsShowAttendees: true,
|
2023-02-27 20:45:40 +00:00
|
|
|
hosts: {
|
|
|
|
select: {
|
|
|
|
user: true,
|
|
|
|
},
|
|
|
|
},
|
2022-10-20 23:28:02 +00:00
|
|
|
workflows: {
|
|
|
|
include: {
|
|
|
|
workflow: {
|
|
|
|
include: {
|
|
|
|
steps: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2023-07-25 17:05:02 +00:00
|
|
|
parentId: true,
|
2022-10-20 23:28:02 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
uid: true,
|
|
|
|
eventTypeId: true,
|
|
|
|
destinationCalendar: true,
|
|
|
|
smsReminderNumber: true,
|
|
|
|
workflowReminders: true,
|
|
|
|
scheduledJobs: true,
|
2023-03-14 04:19:05 +00:00
|
|
|
seatsReferences: true,
|
2023-04-04 04:59:09 +00:00
|
|
|
responses: true,
|
2022-10-20 23:28:02 +00:00
|
|
|
},
|
|
|
|
});
|
2023-03-14 04:19:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type CustomRequest = NextApiRequest & {
|
|
|
|
userId?: number;
|
|
|
|
bookingToDelete?: Awaited<ReturnType<typeof getBookingToDelete>>;
|
|
|
|
};
|
|
|
|
|
|
|
|
async function handler(req: CustomRequest) {
|
|
|
|
const { id, uid, allRemainingBookings, cancellationReason, seatReferenceUid } =
|
|
|
|
schemaBookingCancelParams.parse(req.body);
|
|
|
|
req.bookingToDelete = await getBookingToDelete(id, uid);
|
|
|
|
const { bookingToDelete, userId } = req;
|
2022-10-20 23:28:02 +00:00
|
|
|
|
|
|
|
if (!bookingToDelete || !bookingToDelete.user) {
|
|
|
|
throw new HttpError({ statusCode: 400, message: "Booking not found" });
|
|
|
|
}
|
|
|
|
|
|
|
|
if (userId !== bookingToDelete.user?.id && bookingToDelete.startTime < new Date()) {
|
|
|
|
throw new HttpError({ statusCode: 400, message: "Cannot cancel past events" });
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bookingToDelete.userId) {
|
|
|
|
throw new HttpError({ statusCode: 400, message: "User not found" });
|
|
|
|
}
|
|
|
|
|
2023-10-06 16:17:15 +00:00
|
|
|
// If the booking is a seated event and there is no seatReferenceUid we should validate that logged in user is host
|
|
|
|
if (bookingToDelete.eventType?.seatsPerTimeSlot && !seatReferenceUid) {
|
|
|
|
const userIsHost = bookingToDelete.eventType.hosts.find((host) => {
|
|
|
|
if (host.user.id === userId) return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
const userIsOwnerOfEventType = bookingToDelete.eventType.owner?.id === userId;
|
|
|
|
|
|
|
|
if (!userIsHost && !userIsOwnerOfEventType) {
|
|
|
|
throw new HttpError({ statusCode: 401, message: "User not a host of this event" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-13 20:40:16 +00:00
|
|
|
// get webhooks
|
|
|
|
const eventTrigger: WebhookTriggerEvents = "BOOKING_CANCELLED";
|
|
|
|
|
2023-07-25 17:05:02 +00:00
|
|
|
const teamId = await getTeamIdFromEventType({
|
|
|
|
eventType: {
|
|
|
|
team: { id: bookingToDelete.eventType?.teamId ?? null },
|
|
|
|
parentId: bookingToDelete?.eventType?.parentId ?? null,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2023-08-26 00:27:05 +00:00
|
|
|
const triggerForUser = !teamId || (teamId && bookingToDelete.eventType?.parentId);
|
|
|
|
|
2023-07-13 20:40:16 +00:00
|
|
|
const subscriberOptions = {
|
2023-08-26 00:27:05 +00:00
|
|
|
userId: triggerForUser ? bookingToDelete.userId : null,
|
2023-07-13 20:40:16 +00:00
|
|
|
eventTypeId: bookingToDelete.eventTypeId as number,
|
|
|
|
triggerEvent: eventTrigger,
|
2023-07-25 17:05:02 +00:00
|
|
|
teamId,
|
2023-07-13 20:40:16 +00:00
|
|
|
};
|
|
|
|
const eventTypeInfo: EventTypeInfo = {
|
|
|
|
eventTitle: bookingToDelete?.eventType?.title || null,
|
|
|
|
eventDescription: bookingToDelete?.eventType?.description || null,
|
|
|
|
requiresConfirmation: bookingToDelete?.eventType?.requiresConfirmation || null,
|
|
|
|
price: bookingToDelete?.eventType?.price || null,
|
|
|
|
currency: bookingToDelete?.eventType?.currency || null,
|
|
|
|
length: bookingToDelete?.eventType?.length || null,
|
|
|
|
};
|
|
|
|
|
|
|
|
const webhooks = await getWebhooks(subscriberOptions);
|
2023-03-14 04:19:05 +00:00
|
|
|
|
2022-10-20 23:28:02 +00:00
|
|
|
const organizer = await prisma.user.findFirstOrThrow({
|
|
|
|
where: {
|
|
|
|
id: bookingToDelete.userId,
|
|
|
|
},
|
|
|
|
select: {
|
|
|
|
name: true,
|
|
|
|
email: true,
|
|
|
|
timeZone: true,
|
2023-07-19 14:30:37 +00:00
|
|
|
timeFormat: true,
|
2022-10-20 23:28:02 +00:00
|
|
|
locale: true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2023-02-27 20:45:40 +00:00
|
|
|
const teamMembersPromises = [];
|
|
|
|
const attendeesListPromises = [];
|
|
|
|
const hostsPresent = !!bookingToDelete.eventType?.hosts;
|
|
|
|
|
|
|
|
for (const attendee of bookingToDelete.attendees) {
|
|
|
|
const attendeeObject = {
|
2022-10-20 23:28:02 +00:00
|
|
|
name: attendee.name,
|
|
|
|
email: attendee.email,
|
|
|
|
timeZone: attendee.timeZone,
|
|
|
|
language: {
|
|
|
|
translate: await getTranslation(attendee.locale ?? "en", "common"),
|
|
|
|
locale: attendee.locale ?? "en",
|
|
|
|
},
|
|
|
|
};
|
2023-02-27 20:45:40 +00:00
|
|
|
|
|
|
|
// Check for the presence of hosts to determine if it is a team event type
|
|
|
|
if (hostsPresent) {
|
|
|
|
// If the attendee is a host then they are a team member
|
|
|
|
const teamMember = bookingToDelete.eventType?.hosts.some((host) => host.user.email === attendee.email);
|
|
|
|
if (teamMember) {
|
|
|
|
teamMembersPromises.push(attendeeObject);
|
|
|
|
// If not then they are an attendee
|
|
|
|
} else {
|
|
|
|
attendeesListPromises.push(attendeeObject);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
attendeesListPromises.push(attendeeObject);
|
|
|
|
}
|
|
|
|
}
|
2022-10-20 23:28:02 +00:00
|
|
|
|
|
|
|
const attendeesList = await Promise.all(attendeesListPromises);
|
2023-02-27 20:45:40 +00:00
|
|
|
const teamMembers = await Promise.all(teamMembersPromises);
|
2022-10-20 23:28:02 +00:00
|
|
|
const tOrganizer = await getTranslation(organizer.locale ?? "en", "common");
|
|
|
|
|
|
|
|
const evt: CalendarEvent = {
|
|
|
|
title: bookingToDelete?.title,
|
|
|
|
type: (bookingToDelete?.eventType?.title as string) || bookingToDelete?.title,
|
|
|
|
description: bookingToDelete?.description || "",
|
|
|
|
customInputs: isPrismaObjOrUndefined(bookingToDelete.customInputs),
|
2023-04-04 04:59:09 +00:00
|
|
|
...getCalEventResponses({
|
|
|
|
bookingFields: bookingToDelete.eventType?.bookingFields ?? null,
|
|
|
|
booking: bookingToDelete,
|
|
|
|
}),
|
2022-10-20 23:28:02 +00:00
|
|
|
startTime: bookingToDelete?.startTime ? dayjs(bookingToDelete.startTime).format() : "",
|
|
|
|
endTime: bookingToDelete?.endTime ? dayjs(bookingToDelete.endTime).format() : "",
|
|
|
|
organizer: {
|
|
|
|
email: organizer.email,
|
|
|
|
name: organizer.name ?? "Nameless",
|
|
|
|
timeZone: organizer.timeZone,
|
2023-07-19 14:30:37 +00:00
|
|
|
timeFormat: getTimeFormatStringFromUserTimeFormat(organizer.timeFormat),
|
2022-10-20 23:28:02 +00:00
|
|
|
language: { translate: tOrganizer, locale: organizer.locale ?? "en" },
|
|
|
|
},
|
|
|
|
attendees: attendeesList,
|
|
|
|
uid: bookingToDelete?.uid,
|
|
|
|
/* Include recurringEvent information only when cancelling all bookings */
|
|
|
|
recurringEvent: allRemainingBookings
|
|
|
|
? parseRecurringEvent(bookingToDelete.eventType?.recurringEvent)
|
|
|
|
: undefined,
|
|
|
|
location: bookingToDelete?.location,
|
2023-08-31 17:47:02 +00:00
|
|
|
destinationCalendar: bookingToDelete?.destinationCalendar
|
|
|
|
? [bookingToDelete?.destinationCalendar]
|
|
|
|
: bookingToDelete?.user.destinationCalendar
|
|
|
|
? [bookingToDelete?.user.destinationCalendar]
|
|
|
|
: [],
|
2022-10-20 23:28:02 +00:00
|
|
|
cancellationReason: cancellationReason,
|
2023-02-27 20:45:40 +00:00
|
|
|
...(teamMembers && { team: { name: "", members: teamMembers } }),
|
2023-03-30 23:45:48 +00:00
|
|
|
seatsPerTimeSlot: bookingToDelete.eventType?.seatsPerTimeSlot,
|
|
|
|
seatsShowAttendees: bookingToDelete.eventType?.seatsShowAttendees,
|
2022-10-20 23:28:02 +00:00
|
|
|
};
|
2023-02-27 20:45:40 +00:00
|
|
|
|
2023-07-13 20:40:16 +00:00
|
|
|
const dataForWebhooks = { evt, webhooks, eventTypeInfo };
|
|
|
|
|
|
|
|
// If it's just an attendee of a booking then just remove them from that booking
|
|
|
|
const result = await handleSeatedEventCancellation(req, dataForWebhooks);
|
|
|
|
if (result) return { success: true };
|
|
|
|
|
2023-03-14 04:19:05 +00:00
|
|
|
// If it's just an attendee of a booking then just remove them from that booking
|
|
|
|
if (seatReferenceUid && bookingToDelete.attendees.length > 1) {
|
|
|
|
const seatReference = bookingToDelete.seatsReferences.find(
|
|
|
|
(reference) => reference.referenceUid === seatReferenceUid
|
|
|
|
);
|
|
|
|
|
|
|
|
const attendee = bookingToDelete.attendees.find((attendee) => attendee.id === seatReference?.attendeeId);
|
|
|
|
|
|
|
|
if (!seatReference || !attendee)
|
|
|
|
throw new HttpError({ statusCode: 400, message: "User not a part of this booking" });
|
|
|
|
|
|
|
|
await prisma.attendee.delete({
|
|
|
|
where: {
|
|
|
|
id: seatReference.attendeeId,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
req.statusCode = 200;
|
|
|
|
return { message: "No longer attending event" };
|
|
|
|
}
|
|
|
|
|
2022-10-20 23:28:02 +00:00
|
|
|
const promises = webhooks.map((webhook) =>
|
|
|
|
sendPayload(webhook.secret, eventTrigger, new Date().toISOString(), webhook, {
|
|
|
|
...evt,
|
|
|
|
...eventTypeInfo,
|
|
|
|
status: "CANCELLED",
|
2022-12-01 19:51:59 +00:00
|
|
|
smsReminderNumber: bookingToDelete.smsReminderNumber || undefined,
|
2022-10-20 23:28:02 +00:00
|
|
|
}).catch((e) => {
|
|
|
|
console.error(`Error executing webhook for event: ${eventTrigger}, URL: ${webhook.subscriberUrl}`, e);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
|
|
|
//Workflows - schedule reminders
|
|
|
|
if (bookingToDelete.eventType?.workflows) {
|
2023-04-13 19:03:08 +00:00
|
|
|
await sendCancelledReminders({
|
|
|
|
workflows: bookingToDelete.eventType?.workflows,
|
|
|
|
smsReminderNumber: bookingToDelete.smsReminderNumber,
|
2023-04-18 10:08:09 +00:00
|
|
|
evt: {
|
|
|
|
...evt,
|
|
|
|
...{ eventType: { slug: bookingToDelete.eventType.slug } },
|
|
|
|
},
|
|
|
|
hideBranding: !!bookingToDelete.eventType.owner?.hideBranding,
|
2023-09-21 06:22:05 +00:00
|
|
|
eventTypeRequiresConfirmation: bookingToDelete.eventType.requiresConfirmation,
|
2023-04-13 19:03:08 +00:00
|
|
|
});
|
2022-10-20 23:28:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let updatedBookings: {
|
|
|
|
uid: string;
|
|
|
|
workflowReminders: WorkflowReminder[];
|
|
|
|
scheduledJobs: string[];
|
2023-01-10 21:09:28 +00:00
|
|
|
references: {
|
|
|
|
type: string;
|
|
|
|
credentialId: number | null;
|
|
|
|
uid: string;
|
|
|
|
externalCalendarId: string | null;
|
|
|
|
}[];
|
|
|
|
startTime: Date;
|
|
|
|
endTime: Date;
|
2022-10-20 23:28:02 +00:00
|
|
|
}[] = [];
|
|
|
|
|
|
|
|
// by cancelling first, and blocking whilst doing so; we can ensure a cancel
|
|
|
|
// action always succeeds even if subsequent integrations fail cancellation.
|
|
|
|
if (bookingToDelete.eventType?.recurringEvent && bookingToDelete.recurringEventId && allRemainingBookings) {
|
|
|
|
const recurringEventId = bookingToDelete.recurringEventId;
|
|
|
|
// Proceed to mark as cancelled all remaining recurring events instances (greater than or equal to right now)
|
|
|
|
await prisma.booking.updateMany({
|
|
|
|
where: {
|
|
|
|
recurringEventId,
|
|
|
|
startTime: {
|
|
|
|
gte: new Date(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
status: BookingStatus.CANCELLED,
|
|
|
|
cancellationReason: cancellationReason,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
const allUpdatedBookings = await prisma.booking.findMany({
|
|
|
|
where: {
|
|
|
|
recurringEventId: bookingToDelete.recurringEventId,
|
|
|
|
startTime: {
|
|
|
|
gte: new Date(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
select: {
|
2023-01-10 21:09:28 +00:00
|
|
|
startTime: true,
|
|
|
|
endTime: true,
|
|
|
|
references: {
|
|
|
|
select: {
|
|
|
|
uid: true,
|
|
|
|
type: true,
|
|
|
|
externalCalendarId: true,
|
|
|
|
credentialId: true,
|
|
|
|
},
|
|
|
|
},
|
2022-10-20 23:28:02 +00:00
|
|
|
workflowReminders: true,
|
|
|
|
uid: true,
|
|
|
|
scheduledJobs: true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
updatedBookings = updatedBookings.concat(allUpdatedBookings);
|
|
|
|
} else {
|
2023-03-14 04:19:05 +00:00
|
|
|
if (bookingToDelete?.eventType?.seatsPerTimeSlot) {
|
|
|
|
await prisma.attendee.deleteMany({
|
|
|
|
where: {
|
|
|
|
bookingId: bookingToDelete.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2023-03-20 18:30:00 +00:00
|
|
|
|
|
|
|
const where: Prisma.BookingWhereUniqueInput = uid ? { uid } : { id };
|
|
|
|
|
2022-10-20 23:28:02 +00:00
|
|
|
const updatedBooking = await prisma.booking.update({
|
2023-03-20 18:30:00 +00:00
|
|
|
where,
|
2022-10-20 23:28:02 +00:00
|
|
|
data: {
|
|
|
|
status: BookingStatus.CANCELLED,
|
|
|
|
cancellationReason: cancellationReason,
|
|
|
|
},
|
|
|
|
select: {
|
2023-01-10 21:09:28 +00:00
|
|
|
startTime: true,
|
|
|
|
endTime: true,
|
|
|
|
references: {
|
|
|
|
select: {
|
|
|
|
uid: true,
|
|
|
|
type: true,
|
|
|
|
externalCalendarId: true,
|
|
|
|
credentialId: true,
|
|
|
|
},
|
|
|
|
},
|
2022-10-20 23:28:02 +00:00
|
|
|
workflowReminders: true,
|
|
|
|
uid: true,
|
|
|
|
scheduledJobs: true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
updatedBookings.push(updatedBooking);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** TODO: Remove this without breaking functionality */
|
|
|
|
if (bookingToDelete.location === DailyLocationType) {
|
feat: Enable Apps for Teams & Orgs [CAL-1782] (#9337)
* Initial commit
* Adding feature flag
* Add schema relation for teams and credentials
* feat: Orgs Schema Changing `scopedMembers` to `orgUsers` (#9209)
* Change scopedMembers to orgMembers
* Change to orgUsers
* Create getUserAdminTeams function & tRPC endpoint
* Get user admin teams on app store page
* Create UserAdminTeams type
* Add user query to getUserAdminTeams
* Letting duplicate slugs for teams to support orgs
* Covering null on unique clauses
* Add dropdown to install button on app store
* Supporting having the orgId in the session cookie
* On app page, only show dropdown if there are teams
* Add teamId to OAuth state
* Create team credential for OAuth flow
* Create team credential for GCal
* Add create user or team credential for Stripe
* Create webex credentials for users or teams
* Fix type error on useAddAppMutation
* Hubspot create credential on user or team
* Zoho create create credential for user or team
* Zoom create credentials on user or team
* Salesforce create credential on user or teams
* OAuth create credentials for user or teams
* Revert Outlook changes
* Revert GCal changes
* Default app instal, create credential on user or team
* Add teamId to credential creation
* Disable installing for teams for calendars
* Include teams when querying installed apps
* Render team credentials on installed page
* Uninstall team apps
* Type fix on app card
* Add input to include user in teams query
* Add dropdown to install app page for user or team
* Type fixes on category page
* Install app from eventType page to user or team
* Render user and team apps on event type app page
* feat: organization event type filter (#9253)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Missing changes to support orgs schema changes
* Render user and team apps on event type app page
* Add credentialOwner to eventTypeAppCard types
* Type fixes
* Create hook to check if app is enabled
* Clean up console.logs
* Fix useIsAppEnabled by returning not an array
* Convert event type apps to useIsAppEnabled
* Abstract credential owner type
* Remove console.logs
* On installed app page, show apps if only team credential is installed
* Clean up commented lines
* Handle installing app to just an team event from event type page
* Fix early return when creating team app credential
* Zoom add state to callback
* Get team location credentials and save credential id to location
* feat: Onboarding process to create an organization (#9184)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* feedback
* Making sure we check requestedSlug now
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Type fix
* Grab team location credentials
* Add isInstalled to eventType apps query
* feat: [CAL-1816] Organization subdomain support (#9345)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* Covering users and subteams, excluding non-org users
* Unpublished teams shows correctly
* Create subdomain in Vercel
* feedback
* Renaming Vercel env vars
* Vercel domain check before creation
* Supporting cal-staging.com
* Change to have vercel detect it
* vercel domain check data message error
* Remove check domain
* Making sure we check requestedSlug now
* Feedback and unneeded code
* Reverting unneeded changes
* Unneeded changes
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Vercel subdomain creation in PROD only
* Enable payment apps for team credentials
* Fix for team-user apps for event types
* Fix layout and add teamId to app card
* Disable apps on managed event types
* Add managed event type fields to event type apps
* Include organizations in query
* Change createAppCredential to createOAuthAppCredential
* Show app installed on teams
* Making sure we let localhost still work
* UI show installed for which team
* Type fixes
* For team events move use host location to top
* Add around to appStore
* New team event types organizer default conf app
* Fix app card bug
* Clean up
* Search for teamId or userId when deleting credential
* Type fixes
* Type fixes
* Type fixes
* Type fixes
* Address feedback
* Feedback
* Type check fixes
* feat: Organization branding in side menu (#9279)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Org branding provider used in shell sidebar
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Using org avatar (temp)
* Not showing org logo if not set
* User onboarding with org branding (slug)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* feedback
* Feedback
* Org public profile
* Public profiles for team event types
* Added setup profile alert
* Using org avatar on subteams avatar
* Making sure we show the set up profile on org only
* Profile username availability rely on org hook
* Update apps/web/pages/team/[slug].tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Update apps/web/pages/team/[slug].tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* feat: Organization support for event types page (#9449)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Org branding provider used in shell sidebar
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Using org avatar (temp)
* Not showing org logo if not set
* User onboarding with org branding (slug)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* feedback
* Feedback
* Org public profile
* Public profiles for team event types
* Added setup profile alert
* Using org avatar on subteams avatar
* Processing orgs and children as profile options
* Reverting change not belonging to this PR
* Making sure we show the set up profile on org only
* Removing console.log
* Comparing memberships to choose the highest one
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Type errors
* Refactor and type fixes
* Update orgDomains.ts
* Feedback
* Reverting
* NIT
* Address feedback
* fix issue getting org slug from domain
* Improving orgDomains util
* Host comes with port
* Update useRouterQuery.ts
* Fix app card bug
* Fix schema
* Type fixes
* Revert changes to location apps
* Remove console.log
* Fix app store test
* Handle install app dropdown
* Add CalendarApp to `getCalendar`
* Add PaymentApp type fix
* Payment type fix
* Type fixes
* Match with main
* Change type to account for team
* Fix app count for team events
* Type fixes
* More type fixes
* Type fix?
* Fix the type fix
* Remove UserAdminTeams empty array union
* Type fix
* Type fix
* Type fix
* Uses type predicates
* Use teamId. Fixes installation for teams after user installation
* Fix Team Events not working
* Get embed for org events working
* Fix rewrites
* Address feedback
* Type fix
* Fixes
* Add useAppContextWithSchema in useIsAppEnabled
* Type fix for apps using useIsAppEnabled
* Integrations.handler change credentialIds to userCredentialIds
* Remove apps endpoint
* Add LockedIcon and disabled props to event type app context
* Type fixes
* Type fix
* Type fixes
* Show team installed apps for members
* Type fix
* Reverting findFirst
* Revert findFirst
* Avoid a possible 500
* Fix missing tanslation
* Avoid possible 500
* Undo default app for teams
* Type fix
* Fix test
* Update package.json
* feat: Fix invite bug - added tests (#9945)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* chore: Button Component Tidy up (#9888)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
* feat: Make Team Private
## What does this PR do?
Fixes https://github.com/calcom/cal.com/issues/8974
1) When user is admin
<img width="1440" alt="Screenshot 2023-07-03 at 6 45 50 PM" src="https://github.com/calcom/cal.com/assets/53316345/ce15158f-d278-4f1a-ba2e-8b63e4274793">
2) When user is not admin and team is private
<img width="1440" alt="Screenshot 2023-07-03 at 6 47 15 PM" src="https://github.com/calcom/cal.com/assets/53316345/ce23560e-690a-4c42-a76d-49691260aa4d">
3)
<img width="1440" alt="Screenshot 2023-07-03 at 6 51 56 PM" src="https://github.com/calcom/cal.com/assets/53316345/13af38f8-5618-4dae-b359-b24dc91e4eb4">
## Type of change
<!-- Please delete bullets that are not relevant. -->
- New feature (non-breaking change which adds functionality)
## How should this be tested?
1) go to Team members page and turn on switch Make Team Private.
Now after making the team private only admin would be able to see all the members list in the settings. There will not be a button to Book a team member instead on the team page like before.
## Mandatory Tasks
- [ ] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-06 16:48:39 +00:00
|
|
|
bookingToDelete.user.credentials.push({
|
|
|
|
...FAKE_DAILY_CREDENTIAL,
|
|
|
|
teamId: bookingToDelete.eventType?.teamId || null,
|
|
|
|
});
|
2022-10-20 23:28:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const apiDeletes = [];
|
|
|
|
|
2023-08-31 17:47:02 +00:00
|
|
|
const bookingCalendarReference = bookingToDelete.references.filter((reference) =>
|
2022-10-20 23:28:02 +00:00
|
|
|
reference.type.includes("_calendar")
|
|
|
|
);
|
|
|
|
|
2023-08-31 17:47:02 +00:00
|
|
|
if (bookingCalendarReference.length > 0) {
|
|
|
|
for (const reference of bookingCalendarReference) {
|
|
|
|
const { credentialId, uid, externalCalendarId } = reference;
|
|
|
|
// If the booking calendar reference contains a credentialId
|
|
|
|
if (credentialId) {
|
|
|
|
// Find the correct calendar credential under user credentials
|
|
|
|
let calendarCredential = bookingToDelete.user.credentials.find(
|
|
|
|
(credential) => credential.id === credentialId
|
|
|
|
);
|
|
|
|
if (!calendarCredential) {
|
|
|
|
// get credential from DB
|
|
|
|
const foundCalendarCredential = await prisma.credential.findUnique({
|
|
|
|
where: {
|
|
|
|
id: credentialId,
|
|
|
|
},
|
2023-09-14 16:53:58 +00:00
|
|
|
select: credentialForCalendarServiceSelect,
|
2023-08-31 17:47:02 +00:00
|
|
|
});
|
|
|
|
if (foundCalendarCredential) {
|
|
|
|
calendarCredential = foundCalendarCredential;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (calendarCredential) {
|
|
|
|
const calendar = await getCalendar(calendarCredential);
|
|
|
|
if (
|
|
|
|
bookingToDelete.eventType?.recurringEvent &&
|
|
|
|
bookingToDelete.recurringEventId &&
|
|
|
|
allRemainingBookings
|
|
|
|
) {
|
|
|
|
const promises = bookingToDelete.user.credentials
|
|
|
|
.filter((credential) => credential.type.endsWith("_calendar"))
|
|
|
|
.map(async (credential) => {
|
|
|
|
const calendar = await getCalendar(credential);
|
|
|
|
for (const updBooking of updatedBookings) {
|
|
|
|
const bookingRef = updBooking.references.find((ref) => ref.type.includes("_calendar"));
|
|
|
|
if (bookingRef) {
|
|
|
|
const { uid, externalCalendarId } = bookingRef;
|
|
|
|
const deletedEvent = await calendar?.deleteEvent(uid, evt, externalCalendarId);
|
|
|
|
apiDeletes.push(deletedEvent);
|
|
|
|
}
|
2023-01-10 21:09:28 +00:00
|
|
|
}
|
2023-08-31 17:47:02 +00:00
|
|
|
});
|
|
|
|
try {
|
|
|
|
await Promise.all(promises);
|
|
|
|
} catch (error) {
|
|
|
|
if (error instanceof Error) {
|
|
|
|
logger.error(error.message);
|
2023-01-13 18:29:42 +00:00
|
|
|
}
|
2023-08-26 00:43:10 +00:00
|
|
|
}
|
2023-08-31 17:47:02 +00:00
|
|
|
} else {
|
|
|
|
apiDeletes.push(calendar?.deleteEvent(uid, evt, externalCalendarId) as Promise<unknown>);
|
2023-08-26 00:43:10 +00:00
|
|
|
}
|
2023-08-31 17:47:02 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// For bookings made before the refactor we go through the old behavior of running through each calendar credential
|
|
|
|
const calendarCredentials = bookingToDelete.user.credentials.filter((credential) =>
|
|
|
|
credential.type.endsWith("_calendar")
|
|
|
|
);
|
|
|
|
for (const credential of calendarCredentials) {
|
|
|
|
const calendar = await getCalendar(credential);
|
2023-01-10 21:09:28 +00:00
|
|
|
apiDeletes.push(calendar?.deleteEvent(uid, evt, externalCalendarId) as Promise<unknown>);
|
|
|
|
}
|
2022-10-20 23:28:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const bookingVideoReference = bookingToDelete.references.find((reference) =>
|
|
|
|
reference.type.includes("_video")
|
|
|
|
);
|
|
|
|
|
|
|
|
// If the video reference has a credentialId find the specific credential
|
|
|
|
if (bookingVideoReference && bookingVideoReference.credentialId) {
|
|
|
|
const { credentialId, uid } = bookingVideoReference;
|
|
|
|
if (credentialId) {
|
|
|
|
const videoCredential = bookingToDelete.user.credentials.find(
|
|
|
|
(credential) => credential.id === credentialId
|
|
|
|
);
|
|
|
|
|
|
|
|
if (videoCredential) {
|
2023-05-11 07:14:32 +00:00
|
|
|
logger.debug("videoCredential inside cancel booking handler", videoCredential);
|
2022-10-20 23:28:02 +00:00
|
|
|
apiDeletes.push(deleteMeeting(videoCredential, uid));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Avoiding taking care of recurrence for now as Payments are not supported with Recurring Events at the moment
|
|
|
|
if (bookingToDelete && bookingToDelete.paid) {
|
|
|
|
const evt: CalendarEvent = {
|
|
|
|
type: bookingToDelete?.eventType?.title as string,
|
|
|
|
title: bookingToDelete.title,
|
|
|
|
description: bookingToDelete.description ?? "",
|
|
|
|
customInputs: isPrismaObjOrUndefined(bookingToDelete.customInputs),
|
2023-04-04 04:59:09 +00:00
|
|
|
...getCalEventResponses({
|
|
|
|
booking: bookingToDelete,
|
|
|
|
bookingFields: bookingToDelete.eventType?.bookingFields ?? null,
|
|
|
|
}),
|
2022-10-20 23:28:02 +00:00
|
|
|
startTime: bookingToDelete.startTime.toISOString(),
|
|
|
|
endTime: bookingToDelete.endTime.toISOString(),
|
|
|
|
organizer: {
|
|
|
|
email: bookingToDelete.user?.email ?? "dev@calendso.com",
|
|
|
|
name: bookingToDelete.user?.name ?? "no user",
|
|
|
|
timeZone: bookingToDelete.user?.timeZone ?? "",
|
2023-07-19 14:30:37 +00:00
|
|
|
timeFormat: getTimeFormatStringFromUserTimeFormat(organizer.timeFormat),
|
2022-10-20 23:28:02 +00:00
|
|
|
language: { translate: tOrganizer, locale: organizer.locale ?? "en" },
|
|
|
|
},
|
|
|
|
attendees: attendeesList,
|
|
|
|
location: bookingToDelete.location ?? "",
|
|
|
|
uid: bookingToDelete.uid ?? "",
|
2023-08-31 17:47:02 +00:00
|
|
|
destinationCalendar: bookingToDelete?.destinationCalendar
|
|
|
|
? [bookingToDelete?.destinationCalendar]
|
|
|
|
: bookingToDelete?.user.destinationCalendar
|
|
|
|
? [bookingToDelete?.user.destinationCalendar]
|
|
|
|
: [],
|
2022-10-20 23:28:02 +00:00
|
|
|
};
|
2023-02-08 20:36:22 +00:00
|
|
|
|
|
|
|
const successPayment = bookingToDelete.payment.find((payment) => payment.success);
|
2023-08-11 23:29:48 +00:00
|
|
|
if (!successPayment?.externalId) {
|
2023-02-08 20:36:22 +00:00
|
|
|
throw new Error("Cannot reject a booking without a successful payment");
|
|
|
|
}
|
|
|
|
|
|
|
|
let eventTypeOwnerId;
|
|
|
|
if (bookingToDelete.eventType?.owner) {
|
|
|
|
eventTypeOwnerId = bookingToDelete.eventType.owner.id;
|
|
|
|
} else if (bookingToDelete.eventType?.teamId) {
|
|
|
|
const teamOwner = await prisma.membership.findFirst({
|
|
|
|
where: {
|
|
|
|
teamId: bookingToDelete.eventType.teamId,
|
|
|
|
role: MembershipRole.OWNER,
|
|
|
|
},
|
|
|
|
select: {
|
|
|
|
userId: true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
eventTypeOwnerId = teamOwner?.userId;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!eventTypeOwnerId) {
|
|
|
|
throw new Error("Event Type owner not found for obtaining payment app credentials");
|
|
|
|
}
|
|
|
|
|
|
|
|
const paymentAppCredentials = await prisma.credential.findMany({
|
|
|
|
where: {
|
|
|
|
userId: eventTypeOwnerId,
|
|
|
|
appId: successPayment.appId,
|
|
|
|
},
|
|
|
|
select: {
|
|
|
|
key: true,
|
|
|
|
appId: true,
|
|
|
|
app: {
|
|
|
|
select: {
|
|
|
|
categories: true,
|
|
|
|
dirName: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const paymentAppCredential = paymentAppCredentials.find((credential) => {
|
|
|
|
return credential.appId === successPayment.appId;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!paymentAppCredential) {
|
|
|
|
throw new Error("Payment app credentials not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Posible to refactor TODO:
|
feat: Enable Apps for Teams & Orgs [CAL-1782] (#9337)
* Initial commit
* Adding feature flag
* Add schema relation for teams and credentials
* feat: Orgs Schema Changing `scopedMembers` to `orgUsers` (#9209)
* Change scopedMembers to orgMembers
* Change to orgUsers
* Create getUserAdminTeams function & tRPC endpoint
* Get user admin teams on app store page
* Create UserAdminTeams type
* Add user query to getUserAdminTeams
* Letting duplicate slugs for teams to support orgs
* Covering null on unique clauses
* Add dropdown to install button on app store
* Supporting having the orgId in the session cookie
* On app page, only show dropdown if there are teams
* Add teamId to OAuth state
* Create team credential for OAuth flow
* Create team credential for GCal
* Add create user or team credential for Stripe
* Create webex credentials for users or teams
* Fix type error on useAddAppMutation
* Hubspot create credential on user or team
* Zoho create create credential for user or team
* Zoom create credentials on user or team
* Salesforce create credential on user or teams
* OAuth create credentials for user or teams
* Revert Outlook changes
* Revert GCal changes
* Default app instal, create credential on user or team
* Add teamId to credential creation
* Disable installing for teams for calendars
* Include teams when querying installed apps
* Render team credentials on installed page
* Uninstall team apps
* Type fix on app card
* Add input to include user in teams query
* Add dropdown to install app page for user or team
* Type fixes on category page
* Install app from eventType page to user or team
* Render user and team apps on event type app page
* feat: organization event type filter (#9253)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
* Missing changes to support orgs schema changes
* Render user and team apps on event type app page
* Add credentialOwner to eventTypeAppCard types
* Type fixes
* Create hook to check if app is enabled
* Clean up console.logs
* Fix useIsAppEnabled by returning not an array
* Convert event type apps to useIsAppEnabled
* Abstract credential owner type
* Remove console.logs
* On installed app page, show apps if only team credential is installed
* Clean up commented lines
* Handle installing app to just an team event from event type page
* Fix early return when creating team app credential
* Zoom add state to callback
* Get team location credentials and save credential id to location
* feat: Onboarding process to create an organization (#9184)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* feedback
* Making sure we check requestedSlug now
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Type fix
* Grab team location credentials
* Add isInstalled to eventType apps query
* feat: [CAL-1816] Organization subdomain support (#9345)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* Covering users and subteams, excluding non-org users
* Unpublished teams shows correctly
* Create subdomain in Vercel
* feedback
* Renaming Vercel env vars
* Vercel domain check before creation
* Supporting cal-staging.com
* Change to have vercel detect it
* vercel domain check data message error
* Remove check domain
* Making sure we check requestedSlug now
* Feedback and unneeded code
* Reverting unneeded changes
* Unneeded changes
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Vercel subdomain creation in PROD only
* Enable payment apps for team credentials
* Fix for team-user apps for event types
* Fix layout and add teamId to app card
* Disable apps on managed event types
* Add managed event type fields to event type apps
* Include organizations in query
* Change createAppCredential to createOAuthAppCredential
* Show app installed on teams
* Making sure we let localhost still work
* UI show installed for which team
* Type fixes
* For team events move use host location to top
* Add around to appStore
* New team event types organizer default conf app
* Fix app card bug
* Clean up
* Search for teamId or userId when deleting credential
* Type fixes
* Type fixes
* Type fixes
* Type fixes
* Address feedback
* Feedback
* Type check fixes
* feat: Organization branding in side menu (#9279)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Org branding provider used in shell sidebar
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Using org avatar (temp)
* Not showing org logo if not set
* User onboarding with org branding (slug)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* feedback
* Feedback
* Org public profile
* Public profiles for team event types
* Added setup profile alert
* Using org avatar on subteams avatar
* Making sure we show the set up profile on org only
* Profile username availability rely on org hook
* Update apps/web/pages/team/[slug].tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Update apps/web/pages/team/[slug].tsx
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* feat: Organization support for event types page (#9449)
* Desktop first banner, mobile pending
* Removing dead code and img
* WIP
* Adds Email verification template+translations for organizations (#9202)
* First step done
* Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding
* Step 2 done, avatar not working
* Covering null on unique clauses
* Onboarding admins step
* Last step to create teams
* Moving change password handler, improving verifying code flow
* Clearing error before submitting
* Reverting email testing api changes
* Reverting having the banner for now
* Consistent exported components
* Remove unneeded files from banner
* Removing uneeded code
* Fixing avatar selector
* Org branding provider used in shell sidebar
* Using meta component for head/descr
* Missing i18n strings
* Feedback
* Making an org avatar (temp)
* Using org avatar (temp)
* Not showing org logo if not set
* User onboarding with org branding (slug)
* Check for subteams slug clashes with usernames
* Fixing create teams onsuccess
* feedback
* Feedback
* Org public profile
* Public profiles for team event types
* Added setup profile alert
* Using org avatar on subteams avatar
* Processing orgs and children as profile options
* Reverting change not belonging to this PR
* Making sure we show the set up profile on org only
* Removing console.log
* Comparing memberships to choose the highest one
---------
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* Type errors
* Refactor and type fixes
* Update orgDomains.ts
* Feedback
* Reverting
* NIT
* Address feedback
* fix issue getting org slug from domain
* Improving orgDomains util
* Host comes with port
* Update useRouterQuery.ts
* Fix app card bug
* Fix schema
* Type fixes
* Revert changes to location apps
* Remove console.log
* Fix app store test
* Handle install app dropdown
* Add CalendarApp to `getCalendar`
* Add PaymentApp type fix
* Payment type fix
* Type fixes
* Match with main
* Change type to account for team
* Fix app count for team events
* Type fixes
* More type fixes
* Type fix?
* Fix the type fix
* Remove UserAdminTeams empty array union
* Type fix
* Type fix
* Type fix
* Uses type predicates
* Use teamId. Fixes installation for teams after user installation
* Fix Team Events not working
* Get embed for org events working
* Fix rewrites
* Address feedback
* Type fix
* Fixes
* Add useAppContextWithSchema in useIsAppEnabled
* Type fix for apps using useIsAppEnabled
* Integrations.handler change credentialIds to userCredentialIds
* Remove apps endpoint
* Add LockedIcon and disabled props to event type app context
* Type fixes
* Type fix
* Type fixes
* Show team installed apps for members
* Type fix
* Reverting findFirst
* Revert findFirst
* Avoid a possible 500
* Fix missing tanslation
* Avoid possible 500
* Undo default app for teams
* Type fix
* Fix test
* Update package.json
* feat: Fix invite bug - added tests (#9945)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* chore: Button Component Tidy up (#9888)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
* feat: Make Team Private
## What does this PR do?
Fixes https://github.com/calcom/cal.com/issues/8974
1) When user is admin
<img width="1440" alt="Screenshot 2023-07-03 at 6 45 50 PM" src="https://github.com/calcom/cal.com/assets/53316345/ce15158f-d278-4f1a-ba2e-8b63e4274793">
2) When user is not admin and team is private
<img width="1440" alt="Screenshot 2023-07-03 at 6 47 15 PM" src="https://github.com/calcom/cal.com/assets/53316345/ce23560e-690a-4c42-a76d-49691260aa4d">
3)
<img width="1440" alt="Screenshot 2023-07-03 at 6 51 56 PM" src="https://github.com/calcom/cal.com/assets/53316345/13af38f8-5618-4dae-b359-b24dc91e4eb4">
## Type of change
<!-- Please delete bullets that are not relevant. -->
- New feature (non-breaking change which adds functionality)
## How should this be tested?
1) go to Team members page and turn on switch Make Team Private.
Now after making the team private only admin would be able to see all the members list in the settings. There will not be a button to Book a team member instead on the team page like before.
## Mandatory Tasks
- [ ] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.
---------
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Leo Giovanetti <hello@leog.me>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2023-07-06 16:48:39 +00:00
|
|
|
const paymentApp = (await appStore[
|
|
|
|
paymentAppCredential?.app?.dirName as keyof typeof appStore
|
|
|
|
]()) as PaymentApp;
|
|
|
|
if (!paymentApp?.lib?.PaymentService) {
|
2023-02-08 20:36:22 +00:00
|
|
|
console.warn(`payment App service of type ${paymentApp} is not implemented`);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-05-09 19:27:05 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
const PaymentService = paymentApp.lib.PaymentService as unknown as any;
|
|
|
|
const paymentInstance = new PaymentService(paymentAppCredential) as IAbstractPaymentService;
|
|
|
|
|
2023-02-08 20:36:22 +00:00
|
|
|
try {
|
|
|
|
await paymentInstance.refund(successPayment.id);
|
|
|
|
} catch (error) {
|
|
|
|
await handleRefundError({
|
|
|
|
event: evt,
|
|
|
|
reason: error?.toString() || "unknown",
|
|
|
|
paymentId: successPayment.externalId,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-10-20 23:28:02 +00:00
|
|
|
await prisma.booking.update({
|
|
|
|
where: {
|
|
|
|
id: bookingToDelete.id,
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
status: BookingStatus.REJECTED,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// We skip the deletion of the event, because that would also delete the payment reference, which we should keep
|
2023-06-20 00:10:47 +00:00
|
|
|
try {
|
|
|
|
await apiDeletes;
|
|
|
|
} catch (error) {
|
|
|
|
console.error("Error deleting event", error);
|
|
|
|
}
|
2022-10-20 23:28:02 +00:00
|
|
|
req.statusCode = 200;
|
|
|
|
return { message: "Booking successfully cancelled." };
|
|
|
|
}
|
|
|
|
|
|
|
|
const bookingReferenceDeletes = prisma.bookingReference.deleteMany({
|
|
|
|
where: {
|
|
|
|
bookingId: bookingToDelete.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// delete scheduled jobs of cancelled bookings
|
2023-08-26 00:43:10 +00:00
|
|
|
// FIXME: async calls into ether
|
2022-10-20 23:28:02 +00:00
|
|
|
updatedBookings.forEach((booking) => {
|
|
|
|
cancelScheduledJobs(booking);
|
|
|
|
});
|
|
|
|
|
2023-02-20 17:40:08 +00:00
|
|
|
//Workflows - cancel all reminders for cancelled bookings
|
2023-08-26 00:43:10 +00:00
|
|
|
// FIXME: async calls into ether
|
2022-10-20 23:28:02 +00:00
|
|
|
updatedBookings.forEach((booking) => {
|
|
|
|
booking.workflowReminders.forEach((reminder) => {
|
2023-02-20 17:40:08 +00:00
|
|
|
if (reminder.method === WorkflowMethods.EMAIL) {
|
|
|
|
deleteScheduledEmailReminder(reminder.id, reminder.referenceId);
|
|
|
|
} else if (reminder.method === WorkflowMethods.SMS) {
|
|
|
|
deleteScheduledSMSReminder(reminder.id, reminder.referenceId);
|
2023-07-11 15:48:44 +00:00
|
|
|
} else if (reminder.method === WorkflowMethods.WHATSAPP) {
|
|
|
|
deleteScheduledWhatsappReminder(reminder.id, reminder.referenceId);
|
2022-10-20 23:28:02 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-05-22 23:15:06 +00:00
|
|
|
const prismaPromises: Promise<unknown>[] = [bookingReferenceDeletes];
|
2023-01-10 21:09:28 +00:00
|
|
|
|
2023-06-20 00:10:47 +00:00
|
|
|
try {
|
2023-08-26 00:43:10 +00:00
|
|
|
const settled = await Promise.allSettled(prismaPromises.concat(apiDeletes));
|
|
|
|
const rejected = settled.filter(({ status }) => status === "rejected") as PromiseRejectedResult[];
|
|
|
|
if (rejected.length) {
|
|
|
|
throw new Error(`Reasons: ${rejected.map(({ reason }) => reason)}`);
|
|
|
|
}
|
2022-10-20 23:28:02 +00:00
|
|
|
|
2023-08-26 00:43:10 +00:00
|
|
|
// TODO: if emails fail try to requeue them
|
2023-06-20 00:10:47 +00:00
|
|
|
await sendCancelledEmails(evt, { eventName: bookingToDelete?.eventType?.eventName });
|
|
|
|
} catch (error) {
|
|
|
|
console.error("Error deleting event", error);
|
|
|
|
}
|
2022-10-20 23:28:02 +00:00
|
|
|
req.statusCode = 200;
|
|
|
|
return { message: "Booking successfully cancelled." };
|
|
|
|
}
|
|
|
|
|
2023-07-13 20:40:16 +00:00
|
|
|
async function handleSeatedEventCancellation(
|
|
|
|
req: CustomRequest,
|
|
|
|
dataForWebhooks: {
|
|
|
|
webhooks: {
|
|
|
|
id: string;
|
|
|
|
subscriberUrl: string;
|
|
|
|
payloadTemplate: string | null;
|
|
|
|
appId: string | null;
|
|
|
|
secret: string | null;
|
|
|
|
}[];
|
|
|
|
evt: CalendarEvent;
|
|
|
|
eventTypeInfo: EventTypeInfo;
|
|
|
|
}
|
|
|
|
) {
|
2023-03-14 04:19:05 +00:00
|
|
|
const { seatReferenceUid } = schemaBookingCancelParams.parse(req.body);
|
2023-07-13 20:40:16 +00:00
|
|
|
const { webhooks, evt, eventTypeInfo } = dataForWebhooks;
|
2023-03-14 04:19:05 +00:00
|
|
|
if (!seatReferenceUid) return;
|
2023-07-13 20:40:16 +00:00
|
|
|
const bookingToDelete = req.bookingToDelete;
|
|
|
|
if (!bookingToDelete?.attendees.length || bookingToDelete.attendees.length < 2) return;
|
|
|
|
|
|
|
|
if (!bookingToDelete.userId) {
|
|
|
|
throw new HttpError({ statusCode: 400, message: "User not found" });
|
|
|
|
}
|
2023-03-14 04:19:05 +00:00
|
|
|
|
2023-07-13 20:40:16 +00:00
|
|
|
const seatReference = bookingToDelete.seatsReferences.find(
|
2023-03-14 04:19:05 +00:00
|
|
|
(reference) => reference.referenceUid === seatReferenceUid
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!seatReference) throw new HttpError({ statusCode: 400, message: "User not a part of this booking" });
|
|
|
|
|
|
|
|
await Promise.all([
|
|
|
|
prisma.bookingSeat.delete({
|
|
|
|
where: {
|
|
|
|
referenceUid: seatReferenceUid,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
prisma.attendee.delete({
|
|
|
|
where: {
|
|
|
|
id: seatReference.attendeeId,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
]);
|
|
|
|
req.statusCode = 200;
|
2023-07-13 20:40:16 +00:00
|
|
|
|
|
|
|
const attendee = bookingToDelete?.attendees.find((attendee) => attendee.id === seatReference.attendeeId);
|
|
|
|
|
2023-08-04 13:47:16 +00:00
|
|
|
if (attendee) {
|
|
|
|
/* If there are references then we should update them as well */
|
|
|
|
|
|
|
|
const integrationsToUpdate = [];
|
|
|
|
|
|
|
|
for (const reference of bookingToDelete.references) {
|
|
|
|
if (reference.credentialId) {
|
|
|
|
const credential = await prisma.credential.findUnique({
|
|
|
|
where: {
|
|
|
|
id: reference.credentialId,
|
|
|
|
},
|
2023-09-14 16:53:58 +00:00
|
|
|
select: credentialForCalendarServiceSelect,
|
2023-08-04 13:47:16 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if (credential) {
|
|
|
|
const updatedEvt = {
|
|
|
|
...evt,
|
|
|
|
attendees: evt.attendees.filter((evtAttendee) => attendee.email !== evtAttendee.email),
|
|
|
|
};
|
|
|
|
if (reference.type.includes("_video")) {
|
2023-09-14 16:53:58 +00:00
|
|
|
integrationsToUpdate.push(updateMeeting(credential, updatedEvt, reference));
|
2023-08-04 13:47:16 +00:00
|
|
|
}
|
|
|
|
if (reference.type.includes("_calendar")) {
|
|
|
|
const calendar = await getCalendar(credential);
|
|
|
|
if (calendar) {
|
|
|
|
integrationsToUpdate.push(
|
|
|
|
calendar?.updateEvent(reference.uid, updatedEvt, reference.externalCalendarId)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
await Promise.all(integrationsToUpdate);
|
|
|
|
} catch (error) {
|
|
|
|
// Shouldn't stop code execution if integrations fail
|
|
|
|
// as integrations was already updated
|
|
|
|
}
|
|
|
|
|
|
|
|
const tAttendees = await getTranslation(attendee.locale ?? "en", "common");
|
|
|
|
|
|
|
|
await sendCancelledSeatEmails(evt, {
|
|
|
|
...attendee,
|
|
|
|
language: { translate: tAttendees, locale: attendee.locale ?? "en" },
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-07-13 20:40:16 +00:00
|
|
|
evt.attendees = attendee
|
|
|
|
? [
|
|
|
|
{
|
|
|
|
...attendee,
|
|
|
|
language: {
|
|
|
|
translate: await getTranslation(attendee.locale ?? "en", "common"),
|
|
|
|
locale: attendee.locale ?? "en",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
: [];
|
|
|
|
|
|
|
|
const promises = webhooks.map((webhook) =>
|
|
|
|
sendPayload(webhook.secret, WebhookTriggerEvents.BOOKING_CANCELLED, new Date().toISOString(), webhook, {
|
|
|
|
...evt,
|
|
|
|
...eventTypeInfo,
|
|
|
|
status: "CANCELLED",
|
|
|
|
smsReminderNumber: bookingToDelete.smsReminderNumber || undefined,
|
|
|
|
}).catch((e) => {
|
|
|
|
console.error(
|
|
|
|
`Error executing webhook for event: ${WebhookTriggerEvents.BOOKING_CANCELLED}, URL: ${webhook.subscriberUrl}`,
|
|
|
|
e
|
|
|
|
);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
2023-08-01 14:13:28 +00:00
|
|
|
const workflowRemindersForAttendee = bookingToDelete?.workflowReminders.filter(
|
|
|
|
(reminder) => reminder.seatReferenceId === seatReferenceUid
|
|
|
|
);
|
|
|
|
|
|
|
|
if (workflowRemindersForAttendee && workflowRemindersForAttendee.length !== 0) {
|
|
|
|
const deletionPromises = workflowRemindersForAttendee.map((reminder) => {
|
|
|
|
if (reminder.method === WorkflowMethods.EMAIL) {
|
|
|
|
return deleteScheduledEmailReminder(reminder.id, reminder.referenceId);
|
|
|
|
} else if (reminder.method === WorkflowMethods.SMS) {
|
|
|
|
return deleteScheduledSMSReminder(reminder.id, reminder.referenceId);
|
|
|
|
} else if (reminder.method === WorkflowMethods.WHATSAPP) {
|
|
|
|
return deleteScheduledWhatsappReminder(reminder.id, reminder.referenceId);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
await Promise.allSettled(deletionPromises);
|
|
|
|
}
|
|
|
|
|
2023-03-14 04:19:05 +00:00
|
|
|
return { success: true };
|
|
|
|
}
|
|
|
|
|
2022-10-20 23:28:02 +00:00
|
|
|
export default handler;
|