Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Setting for rescheduling past bookings #18358

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
2 changes: 1 addition & 1 deletion apps/web/components/booking/BookingListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ function BookingListItem(booking: BookingItemProps) {
];

const editBookingActions: ActionType[] = [
...(isBookingInPast
...(isBookingInPast && !booking.eventType.alllowReschedulingPastBookings
anikdhabal marked this conversation as resolved.
Show resolved Hide resolved
? []
: [
{
Expand Down
1 change: 1 addition & 0 deletions apps/web/lib/booking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const getEventTypesFromDB = async (id: number) => {
price: true,
currency: true,
bookingFields: true,
alllowReschedulingPastBookings: true,
disableGuests: true,
timeZone: true,
profile: {
Expand Down
3 changes: 2 additions & 1 deletion apps/web/lib/reschedule/[uid]/getServerSideProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
},
},
slug: true,
alllowReschedulingPastBookings: true,
team: {
select: {
parentId: true,
Expand Down Expand Up @@ -128,7 +129,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
});

const isBookingInPast = booking.endTime && new Date(booking.endTime) < new Date();
if (isBookingInPast) {
if (isBookingInPast && !eventType.alllowReschedulingPastBookings) {
return {
redirect: {
destination: eventUrl,
Expand Down
32 changes: 16 additions & 16 deletions apps/web/modules/bookings/views/bookings-single-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import type { nameObjectSchema } from "@calcom/core/event";
import { getEventName } from "@calcom/core/event";
import type { ConfigType } from "@calcom/dayjs";
import dayjs from "@calcom/dayjs";
import { getOrgFullOrigin } from "@calcom/ee/organizations/lib/orgDomains";
import {
useEmbedNonStylesConfig,
useIsBackgroundTransparent,
Expand Down Expand Up @@ -728,22 +727,23 @@ export default function Success(props: PageProps) {
</span>

<>
{!props.recurringBookings && !isBookingInPast && (
<span className="text-default inline">
<span className="underline" data-testid="reschedule-link">
<Link
href={`/reschedule/${seatReferenceUid || bookingInfo?.uid}${
currentUserEmail
? `?rescheduledBy=${encodeURIComponent(currentUserEmail)}`
: ""
}`}
legacyBehavior>
{t("reschedule")}
</Link>
{!props.recurringBookings &&
(!isBookingInPast || eventType.alllowReschedulingPastBookings) && (
<span className="text-default inline">
<span className="underline" data-testid="reschedule-link">
<Link
href={`/reschedule/${seatReferenceUid || bookingInfo?.uid}${
currentUserEmail
? `?rescheduledBy=${encodeURIComponent(currentUserEmail)}`
: ""
}`}
legacyBehavior>
{t("reschedule")}
</Link>
</span>
<span className="mx-2">{t("or_lowercase")}</span>
</span>
<span className="mx-2">{t("or_lowercase")}</span>
</span>
)}
)}

<button
data-testid="cancel"
Expand Down
2 changes: 2 additions & 0 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -2893,6 +2893,8 @@
"attribute_weight_enabled_description":"By enabling weights, it would be possible to assign higher priority to certain attributes per user. The higher the weight, the higher the priority.",
"routed": "Routed",
"reassigned": "Reassigned",
"allow_rescheduling_past_events": "Allow rescheduling past events",
"allow_rescheduling_past_events_description": "Enabling this option allows for past events to be rescheduled",
"rerouted": "Rerouted",
"salesforce_assigned": "Salesforce assignment",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ export const EventAdvancedTab = ({
const eventTypeColorLocked = shouldLockDisableProps("eventTypeColor");
const lockTimeZoneToggleOnBookingPageLocked = shouldLockDisableProps("lockTimeZoneToggleOnBookingPage");
const multiplePrivateLinksLocked = shouldLockDisableProps("multiplePrivateLinks");
const reschedulingPastBookingsLocked = shouldLockDisableProps("alllowReschedulingPastBookings");
const { isLocked, ...eventNameLocked } = shouldLockDisableProps("eventName");

if (isManagedEventType) {
Expand Down Expand Up @@ -893,6 +894,21 @@ export const EventAdvancedTab = ({
/>
)}
/>
<Controller
name="alllowReschedulingPastBookings"
render={({ field: { value, onChange } }) => (
<SettingsToggle
labelClassName={classNames("text-sm")}
toggleSwitchAtTheEnd={true}
switchContainerClassName={classNames("border-subtle rounded-lg border py-6 px-4 sm:px-6")}
title={t("allow_rescheduling_past_events")}
{...reschedulingPastBookingsLocked}
description={t("allow_rescheduling_past_events_description")}
checked={value}
onCheckedChange={(e) => onChange(e)}
/>
)}
/>
<Controller
name="eventTypeColor"
render={() => (
Expand Down
1 change: 1 addition & 0 deletions packages/lib/defaultEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ const commons = {
seatsShowAttendees: null,
seatsShowAvailabilityCount: null,
onlyShowFirstAvailableSlot: false,
alllowReschedulingPastBookings: false,
id: 0,
hideCalendarNotes: false,
hideCalendarEventDetails: false,
Expand Down
1 change: 1 addition & 0 deletions packages/lib/server/eventTypeSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const eventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
afterEventBuffer: true,
seatsPerTimeSlot: true,
onlyShowFirstAvailableSlot: true,
alllowReschedulingPastBookings: true,
seatsShowAttendees: true,
seatsShowAvailabilityCount: true,
scheduleId: true,
Expand Down
2 changes: 2 additions & 0 deletions packages/lib/server/repository/eventType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ export class EventTypeRepository {
onlyShowFirstAvailableSlot: true,
durationLimits: true,
assignAllTeamMembers: true,
alllowReschedulingPastBookings: true,
assignRRMembersUsingSegment: true,
rrSegmentQueryValue: true,
isRRWeightsEnabled: true,
Expand Down Expand Up @@ -784,6 +785,7 @@ export class EventTypeRepository {
periodStartDate: true,
periodEndDate: true,
onlyShowFirstAvailableSlot: true,
alllowReschedulingPastBookings: true,
periodCountCalendarDays: true,
rescheduleWithSameRoundRobinHost: true,
periodDays: true,
Expand Down
1 change: 1 addition & 0 deletions packages/lib/test/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export const buildEventType = (eventType?: Partial<EventType>): EventType => {
rrSegmentQueryValue: null,
autoTranslateDescriptionEnabled: false,
useEventLevelSelectedCalendars: false,
alllowReschedulingPastBookings: false,
...eventType,
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export const useEventTypeForm = ({
requiresConfirmationForFreeEmail: eventType.requiresConfirmationForFreeEmail,
slotInterval: eventType.slotInterval,
minimumBookingNotice: eventType.minimumBookingNotice,
alllowReschedulingPastBookings: eventType.alllowReschedulingPastBookings,
metadata: eventType.metadata,
hosts: eventType.hosts.sort((a, b) => sortHosts(a, b, eventType.isRRWeightsEnabled)),
successRedirectUrl: eventType.successRedirectUrl || "",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "EventType" ADD COLUMN "alllowReschedulingPastBookings" BOOLEAN NOT NULL DEFAULT false;
anikdhabal marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions packages/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ model EventType {
fieldTranslations EventTypeTranslation[]
maxLeadThreshold Int?
selectedCalendars SelectedCalendar[]
alllowReschedulingPastBookings Boolean @default(false)

/// @zod.custom(imports.eventTypeColor)
eventTypeColor Json?
Expand Down
1 change: 1 addition & 0 deletions packages/prisma/zod-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@ export const allManagedEventTypeProps: { [k in keyof Omit<Prisma.EventTypeSelect
assignAllTeamMembers: true,
isRRWeightsEnabled: true,
eventTypeColor: true,
alllowReschedulingPastBookings: true,
rescheduleWithSameRoundRobinHost: true,
maxLeadThreshold: true,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export async function getBookings({
seatsShowAttendees: true,
seatsShowAvailabilityCount: true,
eventTypeColor: true,
alllowReschedulingPastBookings: true,
schedulingType: true,
length: true,
team: {
Expand Down
1 change: 1 addition & 0 deletions packages/trpc/server/routers/viewer/slots/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ async function getExistingBookings(
seatsPerTimeSlot: true,
requiresConfirmationWillBlockSlot: true,
requiresConfirmation: true,
alllowReschedulingPastBookings: true,
},
},
...(!!eventType?.seatsPerTimeSlot && {
Expand Down
Loading