@licklist/design 0.44.541 → 0.44.543
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/availability-indicator/AvailabilityIndicator.d.ts +5 -4
- package/dist/availability-indicator/AvailabilityIndicator.d.ts.map +1 -1
- package/dist/availability-indicator/AvailabilityIndicator.js +1 -1
- package/dist/calendar/Calendar.d.ts +2 -4
- package/dist/calendar/Calendar.d.ts.map +1 -1
- package/dist/date-time-button/DateTimeButton.d.ts +4 -5
- package/dist/date-time-button/DateTimeButton.d.ts.map +1 -1
- package/dist/date-time-button/DateTimeButton.js +1 -1
- package/dist/events/edit-event-modal/utils/getDefaultProductSet.d.ts.map +1 -1
- package/dist/events/edit-event-modal/utils/getDefaultProductSet.js +1 -1
- package/dist/iframe/activity-card/ActivityCard.d.ts +1 -3
- package/dist/iframe/activity-card/ActivityCard.d.ts.map +1 -1
- package/dist/iframe/activity-card/ActivityCard.js +1 -1
- package/dist/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.d.ts +3 -1
- package/dist/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.d.ts.map +1 -1
- package/dist/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.js +1 -1
- package/dist/iframe/page/components/PageBody/components/LeftBlock/LeftBlock.d.ts.map +1 -1
- package/dist/iframe/page/components/PageBody/components/LeftBlock/LeftBlock.js +1 -1
- package/dist/product-set/control/DateAndRecurrenceInput.d.ts +5 -1
- package/dist/product-set/control/DateAndRecurrenceInput.d.ts.map +1 -1
- package/dist/product-set/control/DateAndRecurrenceInput.js +1 -1
- package/dist/product-set/control/ProductSetControl.d.ts +4 -2
- package/dist/product-set/control/ProductSetControl.d.ts.map +1 -1
- package/dist/product-set/control/ProductSetControl.js +1 -1
- package/dist/product-set/control/ProductSetRecurrenceControl.d.ts +2 -1
- package/dist/product-set/control/ProductSetRecurrenceControl.d.ts.map +1 -1
- package/dist/product-set/control/ProductSetRecurrenceControl.js +1 -1
- package/dist/product-set/form/ProductSetForm.d.ts +3 -1
- package/dist/product-set/form/ProductSetForm.d.ts.map +1 -1
- package/dist/product-set/form/ProductSetForm.js +1 -1
- package/dist/product-set/utils/index.d.ts +10 -0
- package/dist/product-set/utils/index.d.ts.map +1 -1
- package/dist/product-set/utils/index.js +1 -1
- package/dist/recurring-date-picker-input/RecurringDatePickerInput.js +1 -1
- package/dist/sales/payment-form/SalePaymentForm.js +1 -1
- package/dist/styles/activity-card/GridActivitiesCard.scss +0 -8
- package/dist/styles/form/CustomCheckbox.scss +1 -0
- package/dist/styles/zones/ZoneForm.scss +3 -0
- package/dist/zone/form/ZoneForm.d.ts +1 -1
- package/dist/zone/form/ZoneForm.d.ts.map +1 -1
- package/dist/zone/form/ZoneForm.js +1 -1
- package/dist/zone/form/components/AvailableTimesControl.d.ts +2 -0
- package/dist/zone/form/components/AvailableTimesControl.d.ts.map +1 -1
- package/dist/zone/form/components/AvailableTimesControl.js +1 -1
- package/dist/zone/form/components/ZoneControl.d.ts +1 -1
- package/dist/zone/form/components/ZoneControl.d.ts.map +1 -1
- package/dist/zone/form/components/ZoneControl.js +1 -1
- package/dist/zone/form/components/ZoneRecurrencesControl.d.ts +1 -1
- package/dist/zone/form/components/ZoneRecurrencesControl.d.ts.map +1 -1
- package/dist/zone/form/components/ZoneRecurrencesControl.js +1 -1
- package/package.json +1 -1
- package/src/availability-indicator/AvailabilityIndicator.tsx +41 -33
- package/src/calendar/Calendar.tsx +2 -3
- package/src/date-time-button/DateTimeButton.tsx +11 -6
- package/src/events/edit-event-modal/component/EditEventForm/EditEventForm.stories.tsx +0 -3
- package/src/events/edit-event-modal/utils/getDefaultProductSet.ts +0 -1
- package/src/iframe/activity-card/ActivityCard.stories.tsx +27 -51
- package/src/iframe/activity-card/ActivityCard.tsx +2 -13
- package/src/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.tsx +13 -32
- package/src/iframe/page/components/PageBody/components/LeftBlock/LeftBlock.tsx +12 -10
- package/src/product-set/control/DateAndRecurrenceInput.tsx +73 -2
- package/src/product-set/control/ProductSetControl.stories.tsx +0 -2
- package/src/product-set/control/ProductSetControl.tsx +8 -10
- package/src/product-set/control/ProductSetRecurrenceControl.tsx +6 -1
- package/src/product-set/form/ProductSetForm.stories.tsx +6 -6
- package/src/product-set/form/ProductSetForm.tsx +19 -3
- package/src/product-set/utils/index.ts +38 -0
- package/src/styles/activity-card/GridActivitiesCard.scss +0 -8
- package/src/styles/form/CustomCheckbox.scss +1 -0
- package/src/styles/zones/ZoneForm.scss +3 -0
- package/src/zone/form/ZoneForm.tsx +1 -2
- package/src/zone/form/components/AvailableTimesControl.tsx +89 -83
- package/src/zone/form/components/ZoneControl.tsx +2 -5
- package/src/zone/form/components/ZoneRecurrencesControl.tsx +2 -51
|
@@ -5,8 +5,11 @@ import { Form, OverlayTrigger, Popover } from "react-bootstrap";
|
|
|
5
5
|
import { useFieldArray, useFormContext } from "react-hook-form";
|
|
6
6
|
import { useTranslation } from "react-i18next";
|
|
7
7
|
import { useClickAway } from "react-use";
|
|
8
|
+
import { DateTime } from "luxon";
|
|
9
|
+
import { WorkHour } from "@licklist/core/dist/DataMapper/Provider/WorkHourDataMapper";
|
|
8
10
|
import { ProductSetRecurrence } from "@licklist/core/dist/DataMapper/Product/ProductSetRecurrenceDataMapper";
|
|
9
11
|
import { DndContext } from "@dnd-kit/core";
|
|
12
|
+
import { TIME_FORMAT } from "@licklist/core/dist/Config/Date";
|
|
10
13
|
import {
|
|
11
14
|
SortableContext,
|
|
12
15
|
verticalListSortingStrategy,
|
|
@@ -20,6 +23,11 @@ import {
|
|
|
20
23
|
RecurringDatePickerInputValues,
|
|
21
24
|
} from "../../recurring-date-picker-input/RecurringDatePickerInput";
|
|
22
25
|
import { ProductSetRecurrenceControl } from "./ProductSetRecurrenceControl";
|
|
26
|
+
import {
|
|
27
|
+
AvailableTimesControl,
|
|
28
|
+
AvailableTimesControlRef,
|
|
29
|
+
} from "../../zone/form/components/AvailableTimesControl";
|
|
30
|
+
|
|
23
31
|
// eslint-disable-next-line max-len
|
|
24
32
|
|
|
25
33
|
export interface DateAndRecurrenceInputValues {
|
|
@@ -28,10 +36,16 @@ export interface DateAndRecurrenceInputValues {
|
|
|
28
36
|
|
|
29
37
|
interface DateAndRecurrenceInputProps {
|
|
30
38
|
isEventEditProductSet?: boolean;
|
|
39
|
+
workHours?: WorkHour[];
|
|
40
|
+
providerHasBookingManagement: boolean;
|
|
41
|
+
isLoading?: boolean;
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
export const DateAndRecurrenceInput = ({
|
|
34
45
|
isEventEditProductSet,
|
|
46
|
+
providerHasBookingManagement,
|
|
47
|
+
workHours,
|
|
48
|
+
isLoading,
|
|
35
49
|
}: DateAndRecurrenceInputProps) => {
|
|
36
50
|
const {
|
|
37
51
|
control,
|
|
@@ -42,6 +56,9 @@ export const DateAndRecurrenceInput = ({
|
|
|
42
56
|
clearErrors,
|
|
43
57
|
} = useFormContext<DateAndRecurrenceInputValues>();
|
|
44
58
|
|
|
59
|
+
const [availableTimes, setAvailableTimes] = useState<string[]>([]);
|
|
60
|
+
const availableTimesFormRef = useRef<AvailableTimesControlRef>();
|
|
61
|
+
|
|
45
62
|
const { fields, append, update, move } = useFieldArray({
|
|
46
63
|
name: "menuRecurrences",
|
|
47
64
|
control,
|
|
@@ -78,13 +95,44 @@ export const DateAndRecurrenceInput = ({
|
|
|
78
95
|
const { t } = useTranslation("Design");
|
|
79
96
|
|
|
80
97
|
// in this method we should fulfill the form with a proper data
|
|
81
|
-
const handleRecurringDateChange = (
|
|
98
|
+
const handleRecurringDateChange = async (
|
|
99
|
+
next: RecurringDatePickerInputValues
|
|
100
|
+
) => {
|
|
101
|
+
const isAvailableTimesFormValid =
|
|
102
|
+
await availableTimesFormRef.current?.trigger();
|
|
103
|
+
|
|
104
|
+
if (next?.startTime && next?.endTime && isAvailableTimesFormValid) {
|
|
105
|
+
const startTime = DateTime.fromISO(next.startTime);
|
|
106
|
+
const endTime = DateTime.fromISO(next.endTime);
|
|
107
|
+
const { availableTimes } = availableTimesFormRef.current.getValues();
|
|
108
|
+
const validAvalilableTimes = availableTimes.every((time) => {
|
|
109
|
+
const currentAvailableTime = DateTime.fromFormat(time, TIME_FORMAT);
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
currentAvailableTime.diff(startTime, "minutes").minutes >= 0 &&
|
|
113
|
+
currentAvailableTime.diff(endTime, "minutes").minutes <= 0
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
if (!validAvalilableTimes) {
|
|
117
|
+
availableTimesFormRef.current.setError("availableTimes", {
|
|
118
|
+
message: t("Validation:fieldTimeBetween", {
|
|
119
|
+
attribute: t("Design:startTimesSmall"),
|
|
120
|
+
min: startTime.toFormat(TIME_FORMAT),
|
|
121
|
+
max: endTime.toFormat(TIME_FORMAT),
|
|
122
|
+
}),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
82
129
|
const values = {
|
|
83
130
|
rrule: next.rrule,
|
|
84
131
|
startDate: next.startDate,
|
|
85
132
|
endDate: next.endDate,
|
|
86
133
|
startTime: next.startTime,
|
|
87
134
|
endTime: next.endTime,
|
|
135
|
+
...availableTimesFormRef?.current?.getValues(),
|
|
88
136
|
} as Partial<ProductSetRecurrence>;
|
|
89
137
|
|
|
90
138
|
if (editState.values?.id) {
|
|
@@ -107,6 +155,7 @@ export const DateAndRecurrenceInput = ({
|
|
|
107
155
|
// Hotfix for an issue with Popover close & open actions
|
|
108
156
|
setTimeout(() => {
|
|
109
157
|
setEditState({ index, values: next });
|
|
158
|
+
setAvailableTimes(next.availableTimes || []);
|
|
110
159
|
setIsDatePopoverVisible(() => true);
|
|
111
160
|
}, 100);
|
|
112
161
|
};
|
|
@@ -176,7 +225,25 @@ export const DateAndRecurrenceInput = ({
|
|
|
176
225
|
defaultValues={editState.values}
|
|
177
226
|
onChange={handleRecurringDateChange}
|
|
178
227
|
onDelete={handleDelete}
|
|
179
|
-
|
|
228
|
+
>
|
|
229
|
+
{providerHasBookingManagement && (
|
|
230
|
+
<AvailableTimesControl
|
|
231
|
+
workHours={workHours}
|
|
232
|
+
isLoading={isLoading}
|
|
233
|
+
defaultValues={availableTimes}
|
|
234
|
+
ref={availableTimesFormRef}
|
|
235
|
+
errorMessage={
|
|
236
|
+
errors?.menuRecurrences?.[`${editState?.index}`]
|
|
237
|
+
?.availableTimes?.message
|
|
238
|
+
}
|
|
239
|
+
clearErrorMessage={() =>
|
|
240
|
+
clearErrors(
|
|
241
|
+
`menuRecurrences.${editState?.index}.availableTimes`
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
/>
|
|
245
|
+
)}
|
|
246
|
+
</RecurringDatePickerInput>
|
|
180
247
|
</Popover.Content>
|
|
181
248
|
</Popover>
|
|
182
249
|
}
|
|
@@ -201,6 +268,10 @@ export const DateAndRecurrenceInput = ({
|
|
|
201
268
|
key={menuRecurrence._id}
|
|
202
269
|
menuRecurrence={menuRecurrence}
|
|
203
270
|
onEdit={() => handleOnEdit(menuRecurrence, index)}
|
|
271
|
+
errorMessage={
|
|
272
|
+
errors?.menuRecurrences?.[`${index}`]?.availableTimes
|
|
273
|
+
?.message
|
|
274
|
+
}
|
|
204
275
|
/>
|
|
205
276
|
))}
|
|
206
277
|
</SortableContext>
|
|
@@ -67,7 +67,6 @@ Empty.args = {
|
|
|
67
67
|
type: "sale",
|
|
68
68
|
termsAndConditions: "",
|
|
69
69
|
operationalCost: OPERATIONAL_COST_CUSTOMER,
|
|
70
|
-
isDefault: false,
|
|
71
70
|
menuRecurrence: {
|
|
72
71
|
id: 38,
|
|
73
72
|
menuId: 257,
|
|
@@ -87,7 +86,6 @@ Default.args = {
|
|
|
87
86
|
type: "sale",
|
|
88
87
|
termsAndConditions: "",
|
|
89
88
|
operationalCost: OPERATIONAL_COST_CUSTOMER,
|
|
90
|
-
isDefault: true,
|
|
91
89
|
menuRecurrence: {
|
|
92
90
|
id: 38,
|
|
93
91
|
menuId: 257,
|
|
@@ -21,6 +21,7 @@ import { EmailTemplate } from "@licklist/core/dist/DataMapper/Notification/Email
|
|
|
21
21
|
import { SmsTemplate } from "@licklist/core/dist/DataMapper/Notification/SmsTemplateDataMapper";
|
|
22
22
|
import HookFormService from "@licklist/plugins/dist/services/Form/HookFormService";
|
|
23
23
|
import { ruleForUrlWithProtocol } from "@licklist/plugins/dist/validation/Rules/urlRule";
|
|
24
|
+
import { WorkHour } from "@licklist/core/dist/DataMapper/Provider/WorkHourDataMapper";
|
|
24
25
|
import { WarningMessage } from "../../static";
|
|
25
26
|
import { SelectItem } from "../../types/generic/SelectItem";
|
|
26
27
|
import {
|
|
@@ -56,7 +57,6 @@ export interface ProductSetControlValues extends DateAndRecurrenceInputValues {
|
|
|
56
57
|
type: ProductSetType;
|
|
57
58
|
termsAndConditions: string;
|
|
58
59
|
thankYouPageUrl: string;
|
|
59
|
-
isDefault: boolean;
|
|
60
60
|
operationalCost: OperationalCostType;
|
|
61
61
|
productGroups?: SelectItem[];
|
|
62
62
|
relyOnPeopleType?: RelyOnPeopleType | null;
|
|
@@ -84,6 +84,8 @@ export interface ProductSetControlProps {
|
|
|
84
84
|
fieldSets?: FieldSet[];
|
|
85
85
|
showEmailTemplate?: boolean;
|
|
86
86
|
showSmsTemplate?: boolean;
|
|
87
|
+
workHours?: WorkHour[];
|
|
88
|
+
providerHasBookingManagement?: boolean;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
export function ProductSetControl({
|
|
@@ -97,6 +99,8 @@ export function ProductSetControl({
|
|
|
97
99
|
isCreateAction,
|
|
98
100
|
showEmailTemplate,
|
|
99
101
|
showSmsTemplate,
|
|
102
|
+
workHours,
|
|
103
|
+
providerHasBookingManagement = false,
|
|
100
104
|
}: ProductSetControlProps) {
|
|
101
105
|
const { t } = useTranslation(["Design", "Validation", "Notification"]);
|
|
102
106
|
const {
|
|
@@ -158,6 +162,9 @@ export function ProductSetControl({
|
|
|
158
162
|
{isCreateAction && (
|
|
159
163
|
<DateAndRecurrenceInput
|
|
160
164
|
isEventEditProductSet={isEventEditProductSet}
|
|
165
|
+
workHours={workHours}
|
|
166
|
+
isLoading={isLoading}
|
|
167
|
+
providerHasBookingManagement={providerHasBookingManagement}
|
|
161
168
|
/>
|
|
162
169
|
)}
|
|
163
170
|
|
|
@@ -189,15 +196,6 @@ export function ProductSetControl({
|
|
|
189
196
|
</Form.Group>
|
|
190
197
|
)}
|
|
191
198
|
/> */}
|
|
192
|
-
{/* <Form.Check
|
|
193
|
-
{...register("isDefault")}
|
|
194
|
-
id="isDefaultId"
|
|
195
|
-
name="isDefault"
|
|
196
|
-
label={t("Design:setAsDraft")}
|
|
197
|
-
custom
|
|
198
|
-
className="is-default-checkbox"
|
|
199
|
-
disabled={isLoading}
|
|
200
|
-
/> */}
|
|
201
199
|
|
|
202
200
|
<Form.Group>
|
|
203
201
|
<Form.Label className="mb-0">
|
|
@@ -11,11 +11,13 @@ import { Icon } from "../../static";
|
|
|
11
11
|
export interface ProductSetRecurrenceControlProps {
|
|
12
12
|
menuRecurrence?: { _id: string } & Partial<ProductSetRecurrence>;
|
|
13
13
|
onEdit?: (onEdit: Partial<ProductSetRecurrence>) => void;
|
|
14
|
+
errorMessage?: string;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export const ProductSetRecurrenceControl = ({
|
|
17
18
|
menuRecurrence,
|
|
18
19
|
onEdit,
|
|
20
|
+
errorMessage,
|
|
19
21
|
}: ProductSetRecurrenceControlProps) => {
|
|
20
22
|
const { formatDate } = useIntl();
|
|
21
23
|
const { t } = useTranslation("Design");
|
|
@@ -30,7 +32,7 @@ export const ProductSetRecurrenceControl = ({
|
|
|
30
32
|
|
|
31
33
|
return (
|
|
32
34
|
<div
|
|
33
|
-
className="sortable-tree-root"
|
|
35
|
+
className="sortable-tree-root flex-column"
|
|
34
36
|
style={{
|
|
35
37
|
transform: CSS.Translate.toString(transform),
|
|
36
38
|
transition,
|
|
@@ -84,6 +86,9 @@ export const ProductSetRecurrenceControl = ({
|
|
|
84
86
|
{getHumanReadableDate(menuRecurrence)}
|
|
85
87
|
</span>
|
|
86
88
|
</div>
|
|
89
|
+
<div className={`manual-form-error ${!errorMessage && "d-none"}`}>
|
|
90
|
+
{errorMessage}
|
|
91
|
+
</div>
|
|
87
92
|
</div>
|
|
88
93
|
);
|
|
89
94
|
};
|
|
@@ -23,13 +23,17 @@ export default {
|
|
|
23
23
|
export const Default: Story<ProductSetFormProps> = (args) => {
|
|
24
24
|
const providerHasMap = boolean("providerHasMap", true);
|
|
25
25
|
|
|
26
|
-
return
|
|
26
|
+
return (
|
|
27
|
+
<ProductSetForm {...args} providerHasMap={providerHasMap} isCreateAction />
|
|
28
|
+
);
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
export const Empty: Story<ProductSetFormProps> = (args) => {
|
|
30
32
|
const providerHasMap = boolean("providerHasMap", true);
|
|
31
33
|
|
|
32
|
-
return
|
|
34
|
+
return (
|
|
35
|
+
<ProductSetForm {...args} providerHasMap={providerHasMap} isCreateAction />
|
|
36
|
+
);
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
export const Valid: Story<ProductSetFormProps> = (args) => {
|
|
@@ -103,7 +107,6 @@ Default.args = {
|
|
|
103
107
|
name: "Example",
|
|
104
108
|
type: "sale",
|
|
105
109
|
termsAndConditions: "",
|
|
106
|
-
isDefault: true,
|
|
107
110
|
operationalCost: OPERATIONAL_COST_CUSTOMER,
|
|
108
111
|
relyOnPeopleType: RELY_ON_PEOPLE_PRODUCT_QUANTITY,
|
|
109
112
|
steps: [
|
|
@@ -848,7 +851,6 @@ Empty.args = {
|
|
|
848
851
|
name: "Example",
|
|
849
852
|
type: "sale",
|
|
850
853
|
termsAndConditions: "",
|
|
851
|
-
isDefault: true,
|
|
852
854
|
operationalCost: OPERATIONAL_COST_PROVIDER,
|
|
853
855
|
steps: [],
|
|
854
856
|
},
|
|
@@ -861,7 +863,6 @@ ServerErrors.args = {
|
|
|
861
863
|
name: "",
|
|
862
864
|
type: "sale",
|
|
863
865
|
termsAndConditions: "",
|
|
864
|
-
isDefault: true,
|
|
865
866
|
operationalCost: OPERATIONAL_COST_PROVIDER,
|
|
866
867
|
steps: [],
|
|
867
868
|
},
|
|
@@ -875,7 +876,6 @@ Valid.args = {
|
|
|
875
876
|
name: "Example",
|
|
876
877
|
type: "sale",
|
|
877
878
|
termsAndConditions: "",
|
|
878
|
-
isDefault: true,
|
|
879
879
|
operationalCost: OPERATIONAL_COST_CUSTOMER,
|
|
880
880
|
menuRecurrences: [
|
|
881
881
|
{
|
|
@@ -11,6 +11,7 @@ import { isEqual } from "lodash";
|
|
|
11
11
|
// eslint-disable-next-line max-len
|
|
12
12
|
import { ProviderBookingManagementSetting } from "@licklist/core/dist/DataMapper/Provider/ProviderBookingManagementSettingDataMapper";
|
|
13
13
|
import { Zone } from "@licklist/core/dist/DataMapper/Provider/ZoneDataMapper";
|
|
14
|
+
import { WorkHour } from "@licklist/core/dist/DataMapper/Provider/WorkHourDataMapper";
|
|
14
15
|
import { Dialog, useDialogContext } from "../../modals/dialog";
|
|
15
16
|
import {
|
|
16
17
|
ProductSetControl,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
import { Step } from "../types";
|
|
21
22
|
import { ProductSetContextProvider, ProductSetLoadingContext } from "./context";
|
|
22
23
|
import { SelectItem } from "../../types/generic/SelectItem";
|
|
23
|
-
import { getFilteredTemplates } from "../utils";
|
|
24
|
+
import { checkAvailableTimesErrors, getFilteredTemplates } from "../utils";
|
|
24
25
|
|
|
25
26
|
export interface WithIsLoading {
|
|
26
27
|
isLoading: boolean;
|
|
@@ -49,6 +50,7 @@ export interface ProductSetFormProps
|
|
|
49
50
|
providerHasBookingManagement?: ProviderBookingManagementSetting["hasBookingManagement"];
|
|
50
51
|
zones?: Zone[] | null;
|
|
51
52
|
isCreateAction?: boolean;
|
|
53
|
+
workHours?: WorkHour[];
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
export function ProductSetForm({
|
|
@@ -64,6 +66,7 @@ export function ProductSetForm({
|
|
|
64
66
|
providerHasBookingManagement,
|
|
65
67
|
zones,
|
|
66
68
|
isCreateAction,
|
|
69
|
+
workHours,
|
|
67
70
|
...shared
|
|
68
71
|
}: ProductSetFormProps) {
|
|
69
72
|
const { t } = useTranslation("Design");
|
|
@@ -83,6 +86,15 @@ export function ProductSetForm({
|
|
|
83
86
|
const { setError, reset } = form;
|
|
84
87
|
const formValues = form.watch();
|
|
85
88
|
|
|
89
|
+
const submitHandler = (onSubmit: (values: ProductSetFormValues) => void) => {
|
|
90
|
+
return (values: ProductSetFormValues) => {
|
|
91
|
+
if (checkAvailableTimesErrors({ values, setError, t })) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
onSubmit(values);
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
|
|
86
98
|
useEffect(() => {
|
|
87
99
|
if (!defaultValues || isEqual(defaultValues, formValues)) {
|
|
88
100
|
return;
|
|
@@ -127,7 +139,7 @@ export function ProductSetForm({
|
|
|
127
139
|
/>
|
|
128
140
|
|
|
129
141
|
<FormProvider {...form}>
|
|
130
|
-
<Form onSubmit={form.handleSubmit(onSubmitAndRedirect)}>
|
|
142
|
+
<Form onSubmit={form.handleSubmit(submitHandler(onSubmitAndRedirect))}>
|
|
131
143
|
<ProductSetLoadingContext.Consumer>
|
|
132
144
|
{({ isLoading: contextIsLoading }) => {
|
|
133
145
|
return (
|
|
@@ -147,6 +159,8 @@ export function ProductSetForm({
|
|
|
147
159
|
isLoading={isLoading}
|
|
148
160
|
isCreateAction={isCreateAction}
|
|
149
161
|
companyName={companyName}
|
|
162
|
+
workHours={workHours}
|
|
163
|
+
providerHasBookingManagement={providerHasBookingManagement}
|
|
150
164
|
/>
|
|
151
165
|
<Row>
|
|
152
166
|
<Col md={6} sm={12} />
|
|
@@ -158,7 +172,9 @@ export function ProductSetForm({
|
|
|
158
172
|
{onSubmitNoRedirect && (
|
|
159
173
|
<Button
|
|
160
174
|
className="product-set-save-btn mt-5 ml-5 d-no dne d-md-block"
|
|
161
|
-
onClick={form.handleSubmit(
|
|
175
|
+
onClick={form.handleSubmit(
|
|
176
|
+
submitHandler(onSubmitNoRedirect)
|
|
177
|
+
)}
|
|
162
178
|
disabled={
|
|
163
179
|
isLoading || contextIsLoading || !formValues.name
|
|
164
180
|
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { EmailTemplate } from "@licklist/core/dist/DataMapper/Notification/EmailTemplateDataMapper";
|
|
2
2
|
import { SmsTemplate } from "@licklist/core/dist/DataMapper/Notification/SmsTemplateDataMapper";
|
|
3
|
+
import { TFunction } from "i18next";
|
|
4
|
+
import { UseFormSetError } from "react-hook-form";
|
|
3
5
|
import { TemplateItem } from "../control/ProductSetControl";
|
|
6
|
+
import { ProductSetFormValues } from "../form/ProductSetForm";
|
|
7
|
+
|
|
8
|
+
interface CheckAvailableTimesErrors {
|
|
9
|
+
values: ProductSetFormValues;
|
|
10
|
+
setError: UseFormSetError<ProductSetFormValues>;
|
|
11
|
+
t: TFunction;
|
|
12
|
+
}
|
|
4
13
|
|
|
5
14
|
export const moveArrayElements = <T>(
|
|
6
15
|
array: T[],
|
|
@@ -42,3 +51,32 @@ export const getFilteredTemplates = (
|
|
|
42
51
|
label: template?.notification?.name,
|
|
43
52
|
}));
|
|
44
53
|
};
|
|
54
|
+
|
|
55
|
+
export const checkAvailableTimesErrors = ({
|
|
56
|
+
values,
|
|
57
|
+
setError,
|
|
58
|
+
t,
|
|
59
|
+
}: CheckAvailableTimesErrors) => {
|
|
60
|
+
let hasError = false;
|
|
61
|
+
|
|
62
|
+
const hasCategoryWithZone = values?.steps?.some(
|
|
63
|
+
(step) => !!step?.productCategories.some((category) => !!category?.zoneId)
|
|
64
|
+
);
|
|
65
|
+
if (!hasCategoryWithZone) {
|
|
66
|
+
return hasError;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
values?.menuRecurrences?.forEach((recurrence, index: number) => {
|
|
70
|
+
if (!recurrence?.availableTimes?.length) {
|
|
71
|
+
hasError = true;
|
|
72
|
+
setError(`menuRecurrences.${index}.availableTimes` as const, {
|
|
73
|
+
type: "validate",
|
|
74
|
+
message: t("Validation:fieldMinNumber", {
|
|
75
|
+
attribute: t("startTimes"),
|
|
76
|
+
min: 1,
|
|
77
|
+
}),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return hasError;
|
|
82
|
+
};
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
color: $snippet-elements-body-color;
|
|
7
7
|
border-radius: 0.5rem;
|
|
8
8
|
padding: 0.375rem;
|
|
9
|
-
min-height: 12.5rem;
|
|
10
9
|
width: 11.5rem;
|
|
11
10
|
word-wrap: break-word;
|
|
12
11
|
background-color: #fff;
|
|
@@ -21,13 +20,6 @@
|
|
|
21
20
|
border: 2px solid #0e8be1;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
&-image {
|
|
25
|
-
width: 100%;
|
|
26
|
-
height: 6.3rem;
|
|
27
|
-
margin-bottom: 0.5rem;
|
|
28
|
-
border-radius: 0.3rem;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
23
|
&-title {
|
|
32
24
|
font-weight: 600;
|
|
33
25
|
line-height: 1.25rem;
|
|
@@ -25,7 +25,6 @@ export const ZoneForm = ({
|
|
|
25
25
|
onSubmit,
|
|
26
26
|
serverErrors,
|
|
27
27
|
isLoading = false,
|
|
28
|
-
workHours = [],
|
|
29
28
|
}: ZoneFormProps) => {
|
|
30
29
|
const { t } = useTranslation("Design");
|
|
31
30
|
const form = useForm<ZoneFormValues>({
|
|
@@ -55,7 +54,7 @@ export const ZoneForm = ({
|
|
|
55
54
|
return (
|
|
56
55
|
<FormProvider {...form}>
|
|
57
56
|
<Form onSubmit={handleSubmit(onFormSubmit)}>
|
|
58
|
-
<ZoneControl isLoading={isLoading}
|
|
57
|
+
<ZoneControl isLoading={isLoading} />
|
|
59
58
|
<Row>
|
|
60
59
|
<Col md={6} sm={12} />
|
|
61
60
|
<Col
|
|
@@ -23,6 +23,8 @@ type AvailableTimesControlProps = {
|
|
|
23
23
|
defaultValues?: string[];
|
|
24
24
|
startTime?: string;
|
|
25
25
|
endTime?: string;
|
|
26
|
+
errorMessage?: string;
|
|
27
|
+
clearErrorMessage: () => void;
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
type AvailableTimesForm = { availableTimes: string[] };
|
|
@@ -30,91 +32,95 @@ type AvailableTimesForm = { availableTimes: string[] };
|
|
|
30
32
|
export const AvailableTimesControl = forwardRef<
|
|
31
33
|
AvailableTimesControlRef,
|
|
32
34
|
AvailableTimesControlProps
|
|
33
|
-
>(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
() => getCheckboxesByWorkHours(workHours),
|
|
52
|
-
[workHours]
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
useImperativeHandle(ref, () => {
|
|
56
|
-
return {
|
|
35
|
+
>(
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
isLoading,
|
|
39
|
+
workHours,
|
|
40
|
+
defaultValues = [],
|
|
41
|
+
errorMessage,
|
|
42
|
+
clearErrorMessage,
|
|
43
|
+
},
|
|
44
|
+
ref
|
|
45
|
+
) => {
|
|
46
|
+
const nameId = useId();
|
|
47
|
+
const { t } = useTranslation(["Design", "Validation"]);
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
register,
|
|
51
|
+
formState: { errors },
|
|
52
|
+
reset,
|
|
57
53
|
trigger,
|
|
58
54
|
getValues,
|
|
55
|
+
setValue,
|
|
56
|
+
watch,
|
|
59
57
|
setError,
|
|
58
|
+
} = useForm<AvailableTimesForm>({ mode: "onChange" });
|
|
59
|
+
|
|
60
|
+
const availableTimes = watch("availableTimes") || [];
|
|
61
|
+
|
|
62
|
+
const checkboxes = useMemo(
|
|
63
|
+
() => getCheckboxesByWorkHours(workHours),
|
|
64
|
+
[workHours]
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
useImperativeHandle(ref, () => {
|
|
68
|
+
return {
|
|
69
|
+
trigger,
|
|
70
|
+
getValues,
|
|
71
|
+
setError,
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const onChange = (option: string) => {
|
|
76
|
+
const isAlreadyChecked = availableTimes.includes(option);
|
|
77
|
+
if (errorMessage) {
|
|
78
|
+
clearErrorMessage();
|
|
79
|
+
}
|
|
80
|
+
if (isAlreadyChecked) {
|
|
81
|
+
setValue(
|
|
82
|
+
"availableTimes",
|
|
83
|
+
availableTimes.filter((value) => value !== option)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
setValue("availableTimes", [...availableTimes, option]);
|
|
60
90
|
};
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
const onChange = (option: string) => {
|
|
64
|
-
const isAlreadyChecked = availableTimes.includes(option);
|
|
65
|
-
|
|
66
|
-
if (isAlreadyChecked) {
|
|
67
|
-
setValue(
|
|
68
|
-
"availableTimes",
|
|
69
|
-
availableTimes.filter((value) => value !== option)
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
setValue("availableTimes", [...availableTimes, option]);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
if (!defaultValues) return;
|
|
80
|
-
|
|
81
|
-
reset({ availableTimes: defaultValues });
|
|
82
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
83
|
-
}, [defaultValues]);
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<Form.Group controlId={nameId}>
|
|
87
|
-
<Form.Label>{t("Design:startTimes")}</Form.Label>
|
|
88
|
-
<div className="zone-available-times">
|
|
89
|
-
{checkboxes.map((option) => {
|
|
90
|
-
return (
|
|
91
|
-
<Form.Check key={option} inline custom id={option}>
|
|
92
|
-
<Form.Check.Input
|
|
93
|
-
type="checkbox"
|
|
94
|
-
value={option}
|
|
95
|
-
checked={availableTimes.includes(option)}
|
|
96
|
-
onChange={() => onChange(option)}
|
|
97
|
-
disabled={isLoading}
|
|
98
|
-
/>
|
|
99
|
-
<Form.Check.Label>{option}</Form.Check.Label>
|
|
100
|
-
</Form.Check>
|
|
101
|
-
);
|
|
102
|
-
})}
|
|
103
|
-
</div>
|
|
104
91
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (!defaultValues) return;
|
|
94
|
+
|
|
95
|
+
reset({ availableTimes: defaultValues });
|
|
96
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
97
|
+
}, [defaultValues]);
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<Form.Group controlId={nameId}>
|
|
101
|
+
<Form.Label>{t("Design:startTimes")}</Form.Label>
|
|
102
|
+
<div className="zone-available-times">
|
|
103
|
+
{checkboxes.map((option) => {
|
|
104
|
+
return (
|
|
105
|
+
<Form.Check key={option} inline custom id={option}>
|
|
106
|
+
<Form.Check.Input
|
|
107
|
+
type="checkbox"
|
|
108
|
+
value={option}
|
|
109
|
+
checked={availableTimes.includes(option)}
|
|
110
|
+
onChange={() => onChange(option)}
|
|
111
|
+
disabled={isLoading}
|
|
112
|
+
/>
|
|
113
|
+
<Form.Check.Label>{option}</Form.Check.Label>
|
|
114
|
+
</Form.Check>
|
|
115
|
+
);
|
|
116
|
+
})}
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<Form.Control type="hidden" {...register("availableTimes")} />
|
|
120
|
+
<div className="manual-form-error">
|
|
121
|
+
{errors.availableTimes?.message || errorMessage}
|
|
122
|
+
</div>
|
|
123
|
+
</Form.Group>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
);
|
|
@@ -14,15 +14,12 @@ export type ZoneControlProps = {
|
|
|
14
14
|
workHours?: WorkHour[];
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
export const ZoneControl = ({
|
|
18
|
-
isLoading,
|
|
19
|
-
workHours = [],
|
|
20
|
-
}: ZoneControlProps) => {
|
|
17
|
+
export const ZoneControl = ({ isLoading }: ZoneControlProps) => {
|
|
21
18
|
return (
|
|
22
19
|
<Row>
|
|
23
20
|
<Col md={6} sm={12} className="pr-5">
|
|
24
21
|
<NameControl isLoading={isLoading} />
|
|
25
|
-
<ZoneRecurrencesControl
|
|
22
|
+
<ZoneRecurrencesControl />
|
|
26
23
|
<hr />
|
|
27
24
|
<Row>
|
|
28
25
|
<Col md={6} sm={6}>
|