Booking Limit UI
parent
8ab9f28534
commit
58c61e0620
|
@ -37,7 +37,7 @@ import { StripeData } from "@calcom/stripe/server";
|
|||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogContent, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
import Switch from "@calcom/ui/Switch";
|
||||
import { Form } from "@calcom/ui/form/fields";
|
||||
import { Form, Input } from "@calcom/ui/form/fields";
|
||||
|
||||
import { QueryCell } from "@lib/QueryCell";
|
||||
import { asStringOrThrow, asStringOrUndefined } from "@lib/asStringOrNull";
|
||||
|
@ -45,7 +45,7 @@ import { getSession } from "@lib/auth";
|
|||
import { HttpError } from "@lib/core/http/error";
|
||||
import { isSuccessRedirectAvailable } from "@lib/isSuccessRedirectAvailable";
|
||||
import { LocationType } from "@lib/location";
|
||||
import prisma from "@lib/prisma";
|
||||
import prisma, { BookingPeriodFrequency } from "@lib/prisma";
|
||||
import { slugify } from "@lib/slugify";
|
||||
import { trpc } from "@lib/trpc";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
@ -92,6 +92,8 @@ type OptionTypeBase = {
|
|||
disabled?: boolean;
|
||||
};
|
||||
|
||||
type BookingLimitType = Record<BookingPeriodFrequency, number> | null;
|
||||
|
||||
const SuccessRedirectEdit = <T extends UseFormReturn<any, any>>({
|
||||
eventType,
|
||||
formMethods,
|
||||
|
@ -277,8 +279,8 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
const [periodTypeLimitsVisible, setPeriodTypeLimitsVisible] = useState<boolean>(
|
||||
periodType?.type !== "UNLIMITED"
|
||||
);
|
||||
const [limitBookingFrequencyVisible, setLimitBookingFrequencyVisible] = useState<boolea>(
|
||||
eventType.bookingPeriodLimit.length > 0
|
||||
const [limitBookingFrequencyVisible, setLimitBookingFrequencyVisible] = useState<boolean>(
|
||||
!!eventType.bookingPeriodLimit
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -513,10 +515,10 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
successRedirectUrl: string;
|
||||
giphyThankYouPage: string;
|
||||
bookingFrequency: {
|
||||
day: number;
|
||||
week: number;
|
||||
month: number;
|
||||
year: number;
|
||||
DAY: number;
|
||||
WEEK: number;
|
||||
MONTH: number;
|
||||
YEAR: number;
|
||||
};
|
||||
}>({
|
||||
defaultValues: {
|
||||
|
@ -526,6 +528,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
startDate: periodDates.startDate,
|
||||
endDate: periodDates.endDate,
|
||||
},
|
||||
bookingFrequency: eventType.bookingPeriodLimit as BookingPeriodFrequency,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -1621,7 +1624,74 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="block sm:flex">
|
||||
<div className="min-w-48 mb-4 sm:mb-0"></div>{" "}
|
||||
<Controller
|
||||
name="bookingFrequency"
|
||||
control={formMethods.control}
|
||||
render={() => (
|
||||
<div className="block w-full">
|
||||
<CheckboxField
|
||||
id="bookingFrequncyToggle"
|
||||
name="bookingFrequncyToggle"
|
||||
description={t("limit_booking_frequency")}
|
||||
descriptionAsLabel
|
||||
defaultChecked={limitBookingFrequencyVisible}
|
||||
onChange={(e) => {
|
||||
setLimitBookingFrequencyVisible(e?.target.checked);
|
||||
}}
|
||||
/>
|
||||
{limitBookingFrequencyVisible && (
|
||||
<div className="mt-3 flex flex-col space-y-3 bg-gray-100 p-4">
|
||||
{Object.entries(formMethods.getValues("bookingFrequency")).map(
|
||||
([key, value], index) => (
|
||||
<div className="flex items-center " key={key}>
|
||||
<input
|
||||
type="number"
|
||||
className="block w-16 rounded-sm border-gray-300 shadow-sm [appearance:textfield] ltr:mr-2 rtl:ml-2 sm:text-sm"
|
||||
placeholder="30"
|
||||
defaultValue={value || 0}
|
||||
/>
|
||||
<p className="text-sm text-gray-700">per</p>
|
||||
<select
|
||||
id={`periodBookingSelect-${index}`}
|
||||
className="ml-2 block w-24 rounded-sm border-gray-300 py-2 pl-3 pr-10 text-base focus:outline-none sm:text-sm"
|
||||
defaultValue={key}>
|
||||
<option value="YEAR">{t("period_label_year")}</option>
|
||||
<option value="MONTH">{t("period_label_month")}</option>
|
||||
<option value="WEEK">{t("period_label_week")}</option>
|
||||
<option value="DAY">{t("period_label_day")}</option>
|
||||
</select>
|
||||
<Button color="secondary" className="ml-2 ">
|
||||
-
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
<Button
|
||||
color="minimal"
|
||||
className="w-32"
|
||||
type="button"
|
||||
StartIcon={PlusIcon}
|
||||
onClick={() => {
|
||||
const values = formMethods.getValues("bookingFrequency");
|
||||
let notInUse: BookingPeriodFrequency;
|
||||
["DAY", "WEEK", "MONTH", "YEAR"].forEach((period) => {
|
||||
if (!(period in values)) {
|
||||
notInUse = period;
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (!notInUse) showToast("Cannot add more frequencies", "error");
|
||||
}}>
|
||||
Add Limit
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<hr className="my-2 border-neutral-200" />
|
||||
|
||||
<div className="block items-center sm:flex">
|
||||
|
@ -2116,6 +2186,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
bookingPeriodLimit: {
|
||||
select: {
|
||||
id: true,
|
||||
limit: true,
|
||||
period: true,
|
||||
},
|
||||
},
|
||||
|
@ -2189,10 +2260,23 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
const availability = getAvailability(eventType.availability) || [];
|
||||
availability.sort((a, b) => a.startTime - b.startTime);
|
||||
|
||||
// Format from array of limits to an object that looks like e.g. {'DAY':1,'WEEK':3}
|
||||
// Makes it easier to parse for the UI
|
||||
const bookingPeriodLimit =
|
||||
eventType.bookingPeriodLimit.length > 0
|
||||
? eventType.bookingPeriodLimit.reduce(
|
||||
(obj, item) => Object.assign(obj, { [item.period]: item.limit }),
|
||||
{}
|
||||
)
|
||||
: null;
|
||||
|
||||
console.log(bookingPeriodLimit);
|
||||
|
||||
const eventTypeObject = Object.assign({}, eventType, {
|
||||
periodStartDate: eventType.periodStartDate?.toString() ?? null,
|
||||
periodEndDate: eventType.periodEndDate?.toString() ?? null,
|
||||
availability,
|
||||
bookingPeriodLimit: bookingPeriodLimit as BookingLimitType,
|
||||
});
|
||||
|
||||
const teamMembers = eventTypeObject.team
|
||||
|
|
|
@ -783,5 +783,10 @@
|
|||
"go_to_app_store": "Go to App Store",
|
||||
"calendar_error": "Something went wrong, try reconnecting your calendar with all necessary permissions",
|
||||
"buffers_and_limts" : "Buffers & Limits",
|
||||
"limit_future_booking_checkbox":"Limit how far in the future people can book a time"
|
||||
"limit_future_booking_checkbox":"Limit how far in the future people can book a time",
|
||||
"limit_booking_frequency":"Limit booking frequency",
|
||||
"period_label_day":"day",
|
||||
"period_label_week":"week",
|
||||
"period_label_month":"month",
|
||||
"period_label_year":"year"
|
||||
}
|
||||
|
|
|
@ -88,6 +88,14 @@ const EventTypeUpdateInput = _EventTypeModel
|
|||
users: z.array(stringOrNumber).optional(),
|
||||
schedule: z.number().optional(),
|
||||
hashedLink: z.boolean(),
|
||||
bookingFrequency: z
|
||||
.object({
|
||||
DAY: z.number().optional(),
|
||||
WEEK: z.number().optional(),
|
||||
MONTH: z.number().optional(),
|
||||
YEAR: z.number().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.partial()
|
||||
.merge(
|
||||
|
|
|
@ -18,3 +18,5 @@ CREATE UNIQUE INDEX "BookingPeriodLimit_eventTypeId_period_key" ON "BookingPerio
|
|||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "BookingPeriodLimit" ADD CONSTRAINT "BookingPeriodLimit_eventTypeId_fkey" FOREIGN KEY ("eventTypeId") REFERENCES "EventType"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "BookingPeriodLimit" ADD COLUMN "limit" INTEGER NOT NULL;
|
|
@ -71,7 +71,7 @@ model EventType {
|
|||
slotInterval Int?
|
||||
metadata Json?
|
||||
successRedirectUrl String?
|
||||
bookingPeriodLimit BookingPeriodLimit[]
|
||||
bookingPeriodLimit BookingPeriodLimit[]
|
||||
|
||||
@@unique([userId, slug])
|
||||
@@unique([teamId, slug])
|
||||
|
@ -485,6 +485,7 @@ model BookingPeriodLimit {
|
|||
period BookingPeriodFrequency
|
||||
eventType EventType @relation(fields: [eventTypeId], references: [id])
|
||||
eventTypeId Int
|
||||
limit Int
|
||||
|
||||
@@unique([eventTypeId,period])
|
||||
@@unique([eventTypeId, period])
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue