Add event reminder modal & section
parent
74e2e8ba58
commit
1816139295
|
@ -0,0 +1,131 @@
|
|||
import { EventTypeAttendeeReminder, EventTypeAttendeeReminderMethod } from "@prisma/client";
|
||||
import React, { FC } from "react";
|
||||
import { Controller, SubmitHandler, useForm, useWatch } from "react-hook-form";
|
||||
import Select from "react-select";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
|
||||
import Button from "@components/ui/Button";
|
||||
|
||||
interface OptionTypeBase {
|
||||
label: string;
|
||||
value: EventTypeAttendeeReminder;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
onSubmit: SubmitHandler<IFormInput>;
|
||||
onCancel: () => void;
|
||||
selectedCustomInput?: EventTypeAttendeeReminder;
|
||||
}
|
||||
|
||||
type IFormInput = EventTypeAttendeeReminder;
|
||||
|
||||
const AttendeeReminderTypeForm: FC<Props> = (props) => {
|
||||
const { t } = useLocale();
|
||||
const inputOptions: OptionTypeBase[] = [
|
||||
{ value: EventTypeAttendeeReminderMethod.EMAIL, label: t("email") },
|
||||
{ value: EventTypeAttendeeReminderMethod.SMS, label: t("SMS") },
|
||||
];
|
||||
const { selectedCustomInput } = props;
|
||||
const defaultValues = selectedCustomInput || { type: inputOptions[0].value };
|
||||
const { register, control, handleSubmit } = useForm<IFormInput>({
|
||||
defaultValues,
|
||||
});
|
||||
const selectedInputType = useWatch({ name: "type", control });
|
||||
const selectedInputOption = inputOptions.find((e) => selectedInputType === e.value)!;
|
||||
|
||||
const onCancel = () => {
|
||||
props.onCancel();
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(props.onSubmit)}>
|
||||
<div className="mb-2">
|
||||
<label htmlFor="type" className="block text-sm font-medium text-gray-700">
|
||||
{t("input_type")}
|
||||
</label>
|
||||
<Controller
|
||||
name="type"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
id="type"
|
||||
defaultValue={selectedInputOption}
|
||||
options={inputOptions}
|
||||
isSearchable={false}
|
||||
className="focus:border-primary-500 focus:ring-primary-500 mt-1 mb-2 block w-full min-w-0 flex-1 rounded-none rounded-r-md border-gray-300 sm:text-sm"
|
||||
onChange={(option) => option && field.onChange(option.value)}
|
||||
value={selectedInputOption}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<label htmlFor="label" className="block text-sm font-medium text-gray-700">
|
||||
{t("label")}
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<input
|
||||
type="text"
|
||||
id="label"
|
||||
required
|
||||
className="focus:border-primary-500 focus:ring-primary-500 block w-full rounded-sm border-gray-300 shadow-sm sm:text-sm"
|
||||
defaultValue={selectedCustomInput?.label}
|
||||
{...register("label", { required: true })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{(selectedInputType === EventTypeCustomInputType.TEXT ||
|
||||
selectedInputType === EventTypeCustomInputType.TEXTLONG) && (
|
||||
<div className="mb-2">
|
||||
<label htmlFor="placeholder" className="block text-sm font-medium text-gray-700">
|
||||
{t("placeholder")}
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<input
|
||||
type="text"
|
||||
id="placeholder"
|
||||
className="focus:border-primary-500 focus:ring-primary-500 block w-full rounded-sm border-gray-300 shadow-sm sm:text-sm"
|
||||
defaultValue={selectedCustomInput?.placeholder}
|
||||
{...register("placeholder")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex h-5 items-center">
|
||||
<input
|
||||
id="required"
|
||||
type="checkbox"
|
||||
className="text-primary-600 focus:ring-primary-500 h-4 w-4 rounded border-gray-300 ltr:mr-2 rtl:ml-2"
|
||||
defaultChecked={selectedCustomInput?.required ?? true}
|
||||
{...register("required")}
|
||||
/>
|
||||
<label htmlFor="required" className="block text-sm font-medium text-gray-700">
|
||||
{t("is_required")}
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
type="hidden"
|
||||
id="eventTypeId"
|
||||
value={selectedCustomInput?.eventTypeId || -1}
|
||||
{...register("eventTypeId", { valueAsNumber: true })}
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
id="id"
|
||||
value={selectedCustomInput?.id || -1}
|
||||
{...register("id", { valueAsNumber: true })}
|
||||
/>
|
||||
<div className="mt-5 flex space-x-2 sm:mt-4">
|
||||
<Button onClick={onCancel} type="button" color="secondary" className="ltr:mr-2">
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button type="submit">{t("save")}</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default AttendeeReminderTypeForm;
|
|
@ -13,7 +13,14 @@ import {
|
|||
UsersIcon,
|
||||
} from "@heroicons/react/solid";
|
||||
import { MembershipRole } from "@prisma/client";
|
||||
import { Availability, EventTypeCustomInput, PeriodType, Prisma, SchedulingType } from "@prisma/client";
|
||||
import {
|
||||
Availability,
|
||||
EventTypeCustomInput,
|
||||
EventTypeAttendeeReminder,
|
||||
PeriodType,
|
||||
Prisma,
|
||||
SchedulingType,
|
||||
} from "@prisma/client";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
||||
import * as RadioGroup from "@radix-ui/react-radio-group";
|
||||
import dayjs from "dayjs";
|
||||
|
@ -48,6 +55,7 @@ import Loader from "@components/Loader";
|
|||
import Shell from "@components/Shell";
|
||||
import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent";
|
||||
import { Form } from "@components/form/fields";
|
||||
import AttendeeReminderTypeForm from "@components/pages/eventtypes/AttendeeReminderTypeForm";
|
||||
import CustomInputTypeForm from "@components/pages/eventtypes/CustomInputTypeForm";
|
||||
import Button from "@components/ui/Button";
|
||||
import InfoBadge from "@components/ui/InfoBadge";
|
||||
|
@ -173,6 +181,13 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
const [customInputs, setCustomInputs] = useState<EventTypeCustomInput[]>(
|
||||
eventType.customInputs.sort((a, b) => a.id - b.id) || []
|
||||
);
|
||||
// prettier-ignore
|
||||
const [selectedAttendeeReminder, setSelectedAttendeeReminder] = useState<EventTypeAttendeeReminder | undefined>(undefined);
|
||||
const [selectedAttendeeReminderModalOpen, setSelectedAttendeeReminderModalOpen] = useState(false);
|
||||
const [attendeeReminders, setAttendeeReminders] = useState<EventTypeAttendeeReminder[]>(
|
||||
eventType.attendeeReminders.sort((a, b) => a.id - b.id) || []
|
||||
);
|
||||
|
||||
const [tokensList, setTokensList] = useState<Array<Token>>([]);
|
||||
|
||||
const periodType =
|
||||
|
@ -371,6 +386,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
integration: string;
|
||||
externalId: string;
|
||||
};
|
||||
attendeeReminders: EventTypeAttendeeReminder[];
|
||||
}>({
|
||||
defaultValues: {
|
||||
locations: eventType.locations || [],
|
||||
|
@ -1334,6 +1350,40 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="border-neutral-200" />
|
||||
<div className="block sm:flex">
|
||||
<div className="min-w-48 mb-4 sm:mb-0">
|
||||
<label
|
||||
htmlFor="attendeeReminders"
|
||||
className="flex text-sm font-medium text-neutral-700">
|
||||
{t("attendee_reminders")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Controller
|
||||
name="attendeeReminders"
|
||||
control={formMethods.control}
|
||||
render={() => (
|
||||
<div className="w-full">
|
||||
{/* TODO sort reminders based on time from event */}
|
||||
<ul className="mt-1"></ul>
|
||||
|
||||
<Button
|
||||
onClick={() => {
|
||||
setSelectedAttendeeReminder(undefined);
|
||||
setSelectedAttendeeReminderModalOpen(true);
|
||||
}}
|
||||
color="secondary"
|
||||
type="button"
|
||||
StartIcon={PlusIcon}>
|
||||
{t("add_input")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{hasPaymentIntegration && (
|
||||
<>
|
||||
<hr className="border-neutral-200" />
|
||||
|
@ -1634,6 +1684,66 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
</Dialog>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="attendeeReminders"
|
||||
control={formMethods.control}
|
||||
defaultValue={eventType.attendeeReminders.sort((a, b) => a.id - b.id) || []}
|
||||
render={() => (
|
||||
<Dialog
|
||||
open={selectedAttendeeReminderModalOpen}
|
||||
onOpenChange={setSelectedAttendeeReminderModalOpen}>
|
||||
<DialogContent asChild>
|
||||
<div className="inline-block transform rounded-sm bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6 sm:align-middle">
|
||||
<div className="mb-4 sm:flex sm:items-start">
|
||||
<div className="bg-secondary-100 mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full sm:mx-0 sm:h-10 sm:w-10">
|
||||
<PlusIcon className="text-primary-600 h-6 w-6" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title">
|
||||
{t("add_new_custom_input_field")}
|
||||
</h3>
|
||||
<div>
|
||||
<p className="text-sm text-gray-400">
|
||||
{t("this_input_will_shown_booking_this_event")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AttendeeReminderTypeForm
|
||||
selectedCustomInput={selectedAttendeeReminder}
|
||||
onSubmit={(values) => {
|
||||
const customInput: EventTypeAttendeeReminder = {
|
||||
id: -1,
|
||||
eventTypeId: -1,
|
||||
label: values.label,
|
||||
placeholder: values.placeholder,
|
||||
required: values.required,
|
||||
type: values.type,
|
||||
};
|
||||
|
||||
if (selectedCustomInput) {
|
||||
selectedCustomInput.label = customInput.label;
|
||||
selectedCustomInput.placeholder = customInput.placeholder;
|
||||
selectedCustomInput.required = customInput.required;
|
||||
selectedCustomInput.type = customInput.type;
|
||||
} else {
|
||||
setCustomInputs(customInputs.concat(customInput));
|
||||
formMethods.setValue(
|
||||
"customInputs",
|
||||
formMethods.getValues("customInputs").concat(customInput)
|
||||
);
|
||||
}
|
||||
setSelectedCustomInputModalOpen(false);
|
||||
}}
|
||||
onCancel={() => {
|
||||
setSelectedCustomInputModalOpen(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
/>
|
||||
{isAdmin && (
|
||||
<WebhookListContainer
|
||||
title={t("team_webhooks")}
|
||||
|
@ -1748,6 +1858,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
price: true,
|
||||
currency: true,
|
||||
destinationCalendar: true,
|
||||
attendeeReminders: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -671,5 +671,7 @@
|
|||
"example_name": "John Doe",
|
||||
"time_format": "Time format",
|
||||
"12_hour": "12 hour",
|
||||
"24_hour": "24 hour"
|
||||
"24_hour": "24 hour",
|
||||
"attendee_reminders": "Attendee Reminders",
|
||||
"sms": "SMS"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue