Add, remove, edit attendee reminders
parent
1816139295
commit
ddf9323908
|
@ -1,4 +1,8 @@
|
|||
import { EventTypeAttendeeReminder, EventTypeAttendeeReminderMethod } from "@prisma/client";
|
||||
import {
|
||||
EventTypeAttendeeReminder,
|
||||
EventTypeAttendeeReminderMethod,
|
||||
EventTypeAttendeeReminderTimeUnit,
|
||||
} from "@prisma/client";
|
||||
import React, { FC } from "react";
|
||||
import { Controller, SubmitHandler, useForm, useWatch } from "react-hook-form";
|
||||
import Select from "react-select";
|
||||
|
@ -9,30 +13,38 @@ import Button from "@components/ui/Button";
|
|||
|
||||
interface OptionTypeBase {
|
||||
label: string;
|
||||
value: EventTypeAttendeeReminder;
|
||||
value: EventTypeAttendeeReminderMethod | EventTypeAttendeeReminderTimeUnit;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
onSubmit: SubmitHandler<IFormInput>;
|
||||
onCancel: () => void;
|
||||
selectedCustomInput?: EventTypeAttendeeReminder;
|
||||
selectedAttendeeReminder?: EventTypeAttendeeReminder;
|
||||
}
|
||||
|
||||
type IFormInput = EventTypeAttendeeReminder;
|
||||
|
||||
const AttendeeReminderTypeForm: FC<Props> = (props) => {
|
||||
const { t } = useLocale();
|
||||
const inputOptions: OptionTypeBase[] = [
|
||||
const methodOptions: OptionTypeBase[] = [
|
||||
{ value: EventTypeAttendeeReminderMethod.EMAIL, label: t("email") },
|
||||
{ value: EventTypeAttendeeReminderMethod.SMS, label: t("SMS") },
|
||||
];
|
||||
const { selectedCustomInput } = props;
|
||||
const defaultValues = selectedCustomInput || { type: inputOptions[0].value };
|
||||
const timeUnitOptions: OptionTypeBase[] = [
|
||||
{ value: EventTypeAttendeeReminderTimeUnit.MINUTES, label: t("minutes") },
|
||||
{ value: EventTypeAttendeeReminderTimeUnit.HOURS, label: t("hours") },
|
||||
{ value: EventTypeAttendeeReminderTimeUnit.DAYS, label: t("days") },
|
||||
];
|
||||
const { selectedAttendeeReminder } = props;
|
||||
const defaultValues = selectedAttendeeReminder || { type: methodOptions[0].value };
|
||||
const { register, control, handleSubmit } = useForm<IFormInput>({
|
||||
defaultValues,
|
||||
});
|
||||
const selectedInputType = useWatch({ name: "type", control });
|
||||
const selectedInputOption = inputOptions.find((e) => selectedInputType === e.value)!;
|
||||
const selectedMethod = useWatch({ name: "method", control });
|
||||
const selectedMethodOption = methodOptions.find((e) => selectedMethod === e.value)!;
|
||||
|
||||
const selectedTimeUnit = useWatch({ name: "timeUnit", control });
|
||||
const selectedTimeUnitOption = timeUnitOptions.find((e) => selectedTimeUnit === e.value)!;
|
||||
|
||||
const onCancel = () => {
|
||||
props.onCancel();
|
||||
|
@ -42,82 +54,56 @@ const AttendeeReminderTypeForm: FC<Props> = (props) => {
|
|||
<form onSubmit={handleSubmit(props.onSubmit)}>
|
||||
<div className="mb-2">
|
||||
<label htmlFor="type" className="block text-sm font-medium text-gray-700">
|
||||
{t("input_type")}
|
||||
{t("communication_method")}
|
||||
</label>
|
||||
<Controller
|
||||
name="type"
|
||||
name="method"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
id="type"
|
||||
defaultValue={selectedInputOption}
|
||||
options={inputOptions}
|
||||
defaultValue={selectedMethodOption}
|
||||
options={methodOptions}
|
||||
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}
|
||||
value={selectedMethodOption}
|
||||
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">
|
||||
{/* TODO Align these vertically */}
|
||||
<label htmlFor="type" className="block text-sm font-medium text-gray-700">
|
||||
{t("when_to_send")}
|
||||
</label>
|
||||
<div className="middle mb-2 flex items-center justify-center text-sm">
|
||||
<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")}
|
||||
type="number"
|
||||
className="focus:border-primary-500 focus:ring-primary-500 block w-12 rounded-sm border-gray-300 shadow-sm [appearance:textfield] ltr:mr-2 rtl:ml-2 sm:text-sm"
|
||||
placeholder="30"
|
||||
defaultValue={selectedAttendeeReminder?.time}
|
||||
{...register("time", { required: true })}
|
||||
/>
|
||||
<Controller
|
||||
name="timeUnit"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
id="timeUnit"
|
||||
defaultValue={selectedTimeUnitOption}
|
||||
options={timeUnitOptions}
|
||||
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={selectedTimeUnitOption}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<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")}
|
||||
|
|
|
@ -313,6 +313,12 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
setCustomInputs([...customInputs]);
|
||||
};
|
||||
|
||||
const removeReminder = (index: number) => {
|
||||
formMethods.getValues("attendeeReminders").splice(index, 1);
|
||||
attendeeReminders.splice(index, 1);
|
||||
setAttendeeReminders([...attendeeReminders]);
|
||||
};
|
||||
|
||||
const schedulingTypeOptions: {
|
||||
value: SchedulingType;
|
||||
label: string;
|
||||
|
@ -1366,7 +1372,51 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
render={() => (
|
||||
<div className="w-full">
|
||||
{/* TODO sort reminders based on time from event */}
|
||||
<ul className="mt-1"></ul>
|
||||
<ul className="mt-1">
|
||||
{attendeeReminders.map(
|
||||
(attendeeReminder: EventTypeAttendeeReminder, idx: number) => (
|
||||
<li key={idx} className="bg-secondary-50 mb-2 border p-2">
|
||||
<div className="flex justify-between">
|
||||
<div className="w-0 flex-1">
|
||||
<div className="truncate">
|
||||
<span
|
||||
className="text-sm ltr:ml-2 rtl:mr-2"
|
||||
title={`${t("communication_method")}: ${
|
||||
attendeeReminder.method
|
||||
}`}>
|
||||
{t("communication_method")}: {attendeeReminder.method}
|
||||
</span>
|
||||
</div>
|
||||
{attendeeReminder.time && attendeeReminder.timeUnit && (
|
||||
<div className="truncate">
|
||||
<span
|
||||
className="text-sm ltr:ml-2 rtl:mr-2"
|
||||
title={`${t("when")}: ${attendeeReminder.time}`}>
|
||||
{t("when")}: {attendeeReminder.time}{" "}
|
||||
{attendeeReminder.timeUnit} {t("before_event")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setSelectedAttendeeReminder(attendeeReminder);
|
||||
setSelectedAttendeeReminderModalOpen(true);
|
||||
}}
|
||||
color="minimal"
|
||||
type="button">
|
||||
{t("edit")}
|
||||
</Button>
|
||||
<button type="button" onClick={() => removeReminder(idx)}>
|
||||
<XIcon className="h-6 w-6 border-l-2 pl-1 hover:text-red-500 " />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
@ -1700,43 +1750,39 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
</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")}
|
||||
{t("add_new_attendee_reminder")}
|
||||
</h3>
|
||||
<div>
|
||||
<p className="text-sm text-gray-400">
|
||||
{t("this_input_will_shown_booking_this_event")}
|
||||
</p>
|
||||
<p className="text-sm text-gray-400">{t("attendee_reminder_description")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AttendeeReminderTypeForm
|
||||
selectedCustomInput={selectedAttendeeReminder}
|
||||
selectedAttendeeReminder={selectedAttendeeReminder}
|
||||
onSubmit={(values) => {
|
||||
const customInput: EventTypeAttendeeReminder = {
|
||||
const attendeeReminder: EventTypeAttendeeReminder = {
|
||||
id: -1,
|
||||
eventTypeId: -1,
|
||||
label: values.label,
|
||||
placeholder: values.placeholder,
|
||||
required: values.required,
|
||||
type: values.type,
|
||||
method: values.method,
|
||||
timeUnit: values.timeUnit,
|
||||
time: values.time,
|
||||
};
|
||||
|
||||
if (selectedCustomInput) {
|
||||
selectedCustomInput.label = customInput.label;
|
||||
selectedCustomInput.placeholder = customInput.placeholder;
|
||||
selectedCustomInput.required = customInput.required;
|
||||
selectedCustomInput.type = customInput.type;
|
||||
if (selectedAttendeeReminder) {
|
||||
selectedAttendeeReminder.method = attendeeReminder.method;
|
||||
selectedAttendeeReminder.timeUnit = attendeeReminder.timeUnit;
|
||||
selectedAttendeeReminder.time = attendeeReminder.time;
|
||||
} else {
|
||||
setCustomInputs(customInputs.concat(customInput));
|
||||
setAttendeeReminders(attendeeReminders.concat(attendeeReminder));
|
||||
formMethods.setValue(
|
||||
"customInputs",
|
||||
formMethods.getValues("customInputs").concat(customInput)
|
||||
"attendeeReminders",
|
||||
formMethods.getValues("attendeeReminders").concat(attendeeReminder)
|
||||
);
|
||||
}
|
||||
setSelectedCustomInputModalOpen(false);
|
||||
setSelectedAttendeeReminderModalOpen(false);
|
||||
}}
|
||||
onCancel={() => {
|
||||
setSelectedCustomInputModalOpen(false);
|
||||
setSelectedAttendeeReminderModalOpen(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -673,5 +673,10 @@
|
|||
"12_hour": "12 hour",
|
||||
"24_hour": "24 hour",
|
||||
"attendee_reminders": "Attendee Reminders",
|
||||
"sms": "SMS"
|
||||
"sms": "SMS",
|
||||
"add_new_attendee_reminder": "Add new attendee reminder",
|
||||
"attendee_reminder_description": "A reminder will be sent to attendees before the event",
|
||||
"communication_method": "Reminder Method",
|
||||
"when_to_send": "When to send the reminder",
|
||||
"days": "Days"
|
||||
}
|
||||
|
|
|
@ -309,12 +309,19 @@ enum EventTypeAttendeeReminderMethod {
|
|||
SMS @map("sms")
|
||||
}
|
||||
|
||||
enum EventTypeAttendeeReminderTimeUnit {
|
||||
MINUTES @map("minutes")
|
||||
HOURS @map("hours")
|
||||
DAYS @map("days")
|
||||
}
|
||||
|
||||
model EventTypeAttendeeReminder {
|
||||
id Int @id @default(autoincrement())
|
||||
eventTypeId Int
|
||||
eventType EventType @relation(fields: [eventTypeId], references: [id])
|
||||
method String
|
||||
secondsBeforeEvent Int
|
||||
id Int @id @default(autoincrement())
|
||||
eventTypeId Int
|
||||
eventType EventType @relation(fields: [eventTypeId], references: [id])
|
||||
method EventTypeAttendeeReminderMethod
|
||||
timeUnit EventTypeAttendeeReminderTimeUnit
|
||||
time Int
|
||||
}
|
||||
|
||||
model ResetPasswordRequest {
|
||||
|
|
Loading…
Reference in New Issue