Allows setting the event frequency to other than event length (#1349)

This commit is contained in:
Alex van Andel 2021-12-19 13:11:31 +01:00 committed by GitHub
parent 38f762f7b2
commit cbf528c33e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 70 additions and 4 deletions

View File

@ -16,6 +16,7 @@ type AvailableTimesProps = {
minimumBookingNotice: number;
eventTypeId: number;
eventLength: number;
slotInterval: number | null;
date: Dayjs;
users: {
username: string | null;
@ -27,6 +28,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
date,
eventLength,
eventTypeId,
slotInterval,
minimumBookingNotice,
timeFormat,
users,
@ -38,6 +40,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
const { slots, loading, error } = useSlots({
date,
slotInterval,
eventLength,
schedulingType,
users,

View File

@ -220,6 +220,7 @@ const AvailabilityPage = ({ profile, eventType, workingHours }: Props) => {
timeFormat={timeFormat}
minimumBookingNotice={eventType.minimumBookingNotice}
eventTypeId={eventType.id}
slotInterval={eventType.slotInterval}
eventLength={eventType.length}
date={selectedDate}
users={eventType.users}

View File

@ -23,6 +23,7 @@ type Slot = {
};
type UseSlotsProps = {
slotInterval: number | null;
eventLength: number;
eventTypeId: number;
minimumBookingNotice?: number;
@ -32,7 +33,7 @@ type UseSlotsProps = {
};
export const useSlots = (props: UseSlotsProps) => {
const { eventLength, minimumBookingNotice = 0, date, users, eventTypeId } = props;
const { slotInterval, eventLength, minimumBookingNotice = 0, date, users, eventTypeId } = props;
const [slots, setSlots] = useState<Slot[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<Error | null>(null);
@ -102,7 +103,7 @@ export const useSlots = (props: UseSlotsProps) => {
const handleAvailableSlots = async (res: Response) => {
const responseBody: AvailabilityUserResponse = await res.json();
const times = getSlots({
frequency: eventLength,
frequency: slotInterval || eventLength,
inviteeDate: date,
workingHours: responseBody.workingHours,
minimumBookingNotice,

View File

@ -12,6 +12,7 @@ export type AdvancedOptions = {
requiresConfirmation?: boolean;
disableGuests?: boolean;
minimumBookingNotice?: number;
slotInterval?: number | null;
price?: number;
currency?: string;
schedulingType?: SchedulingType;

View File

@ -44,6 +44,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
schedulingType: true,
minimumBookingNotice: true,
timeZone: true,
slotInterval: true,
users: {
select: {
avatar: true,

View File

@ -132,6 +132,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
req.body.minimumBookingNotice || req.body.minimumBookingNotice === 0
? parseInt(req.body.minimumBookingNotice, 10)
: undefined,
slotInterval: req.body.slotInterval,
price: req.body.price,
currency: req.body.currency,
};

View File

@ -275,6 +275,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
periodDaysType: string;
periodDates: { startDate: Date; endDate: Date };
minimumBookingNotice: number;
slotInterval: number | null;
}>({
defaultValues: {
locations: eventType.locations || [],
@ -512,6 +513,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
advancedPayload.periodStartDate = values.periodDates.startDate || undefined;
advancedPayload.periodEndDate = values.periodDates.endDate || undefined;
advancedPayload.minimumBookingNotice = values.minimumBookingNotice;
advancedPayload.slotInterval = values.slotInterval;
// prettier-ignore
advancedPayload.price =
!requirePayment ? undefined :
@ -849,6 +851,53 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
)}
/>
<div className="items-center block sm:flex">
<div className="mb-4 min-w-48 sm:mb-0">
<label htmlFor="eventName" className="flex text-sm font-medium text-neutral-700">
{t("slot_interval")}
</label>
</div>
<div className="w-full">
<div className="relative mt-1 rounded-sm shadow-sm">
<Controller
name="slotInterval"
control={formMethods.control}
render={() => {
const slotIntervalOptions = [
{
label: t("slot_interval_default"),
value: -1,
},
...[5, 10, 15, 20, 30, 45, 60].map((minutes) => ({
label: minutes + " " + t("minutes"),
value: minutes,
})),
];
return (
<Select
isSearchable={false}
classNamePrefix="react-select"
className="flex-1 block w-full min-w-0 border border-gray-300 rounded-sm react-select-container focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
onChange={(val) => {
formMethods.setValue(
"slotInterval",
val && (val.value || 0) > 0 ? val.value : null
);
}}
defaultValue={
slotIntervalOptions.find(
(option) => option.value === eventType.slotInterval
) || slotIntervalOptions[0]
}
options={slotIntervalOptions}
/>
);
}}
/>
</div>
</div>
</div>
<div className="block sm:flex">
<div className="mb-4 min-w-48 sm:mb-0">
<label
@ -1324,6 +1373,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
requiresConfirmation: true,
disableGuests: true,
minimumBookingNotice: true,
slotInterval: true,
team: {
select: {
slug: true,

View File

@ -63,6 +63,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
price: true,
currency: true,
timeZone: true,
slotInterval: true,
},
},
},

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "EventType" ADD COLUMN "slotInterval" INTEGER;

View File

@ -53,6 +53,7 @@ model EventType {
Schedule Schedule[]
price Int @default(0)
currency String @default("usd")
slotInterval Int?
@@unique([userId, slug])
}

View File

@ -541,6 +541,8 @@
"new_event_type_to_book_description": "Create a new event type for people to book times with.",
"length": "Length",
"minimum_booking_notice": "Minimum booking notice",
"slot_interval": "Time-slot intervals",
"slot_interval_default": "Use event length (default)",
"delete_event_type_description": "Are you sure you want to delete this event type? Anyone who you&apos;ve shared this link with will no longer be able to book using it.",
"delete_event_type": "Delete Event Type",
"confirm_delete_event_type": "Yes, delete event type",
@ -562,4 +564,4 @@
"not_installed": "Not installed",
"error_password_mismatch": "Passwords don't match.",
"error_required_field": "This field is required."
}
}

View File

@ -431,6 +431,8 @@
"add_new_event_type": "Een nieuw type afspraak toevoegen",
"new_event_type_to_book_description": "Maak een nieuw afspraak type voor bezoekers om tijden mee te reserveren.",
"length": "Lengte",
"slot_interval": "Tijdslot intervallen",
"slot_interval_default": "Gebruik evenement lengte (standaard)",
"minimum_booking_notice": "Minimum vooruitboeken",
"delete_event_type_description": "Weet u zeker dat u dit evenement wilt verwijderen? Iedereen met wie u deze link heeft gedeeld zal hem niet meer kunnen gebruiken om te boeken.",
"delete_event_type": "Verwijder Evenement",
@ -439,4 +441,4 @@
"settings": "Instellingen",
"next_step": "Stap overslaan",
"prev_step": "Vorige stap"
}
}