@licklist/design 0.63.0 → 0.63.1-dev.0

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.
Files changed (163) hide show
  1. package/bitbucket-pipelines.yml +0 -8
  2. package/dist/date-time-button/DateTimeButton.js +1 -1
  3. package/dist/events/edit-event-modal/component/EditEventForm/EditEventForm.d.ts +3 -1
  4. package/dist/events/edit-event-modal/component/EditEventForm/EditEventForm.d.ts.map +1 -1
  5. package/dist/events/edit-event-modal/component/EditEventForm/EditEventForm.js +1 -1
  6. package/dist/events/edit-event-modal/component/SelectEventProductSet/SelectEventProductSet.d.ts +3 -1
  7. package/dist/events/edit-event-modal/component/SelectEventProductSet/SelectEventProductSet.d.ts.map +1 -1
  8. package/dist/events/edit-event-modal/component/SelectEventProductSet/SelectEventProductSet.js +1 -1
  9. package/dist/events/edit-event-modal/component/SelectEventProductSet/component/EditEventProductSet.d.ts +3 -1
  10. package/dist/events/edit-event-modal/component/SelectEventProductSet/component/EditEventProductSet.d.ts.map +1 -1
  11. package/dist/events/edit-event-modal/component/SelectEventProductSet/component/EditEventProductSet.js +1 -1
  12. package/dist/iframe/payment/order-items-table/utils/paymentSummary.js +1 -1
  13. package/dist/product-set/control/DateAndRecurrenceInput.d.ts +1 -5
  14. package/dist/product-set/control/DateAndRecurrenceInput.d.ts.map +1 -1
  15. package/dist/product-set/control/DateAndRecurrenceInput.js +1 -1
  16. package/dist/product-set/control/DateInput.d.ts.map +1 -1
  17. package/dist/product-set/control/DateInput.js +1 -1
  18. package/dist/product-set/control/ProductSetControl.d.ts +4 -3
  19. package/dist/product-set/control/ProductSetControl.d.ts.map +1 -1
  20. package/dist/product-set/control/ProductSetControl.js +1 -1
  21. package/dist/product-set/control/utils.d.ts +5 -0
  22. package/dist/product-set/control/utils.d.ts.map +1 -0
  23. package/dist/product-set/control/utils.js +1 -0
  24. package/dist/product-set/form/ProductCategoriesControl.d.ts +2 -1
  25. package/dist/product-set/form/ProductCategoriesControl.d.ts.map +1 -1
  26. package/dist/product-set/form/ProductCategoriesControl.js +1 -1
  27. package/dist/product-set/form/ProductSetForm.d.ts +4 -1
  28. package/dist/product-set/form/ProductSetForm.d.ts.map +1 -1
  29. package/dist/product-set/form/ProductsControl.d.ts +2 -1
  30. package/dist/product-set/form/ProductsControl.d.ts.map +1 -1
  31. package/dist/product-set/form/ProductsControl.js +1 -1
  32. package/dist/product-set/form/StepsControl.d.ts +2 -1
  33. package/dist/product-set/form/StepsControl.d.ts.map +1 -1
  34. package/dist/product-set/form/StepsControl.js +1 -1
  35. package/dist/product-set/hooks/useSortableTreeFunctions.d.ts.map +1 -1
  36. package/dist/product-set/product/ProductControl.d.ts +1 -0
  37. package/dist/product-set/product/ProductControl.d.ts.map +1 -1
  38. package/dist/product-set/product/ProductControl.js +1 -1
  39. package/dist/product-set/product/fixed-duration-fields/FixedDurationOptions.d.ts.map +1 -1
  40. package/dist/product-set/step/StepControl.d.ts +2 -1
  41. package/dist/product-set/step/StepControl.d.ts.map +1 -1
  42. package/dist/product-set/step/StepControl.js +1 -1
  43. package/dist/provider/working-hours-input/WorkingHoursInputDescription.d.ts.map +1 -1
  44. package/dist/provider/working-hours-input/WorkingHoursInputDescription.js +1 -1
  45. package/dist/recurring-date-picker-input/RecurrenceAndFrequencyInput.d.ts.map +1 -1
  46. package/dist/recurring-date-picker-input/RecurringDatePickerInput.d.ts +3 -1
  47. package/dist/recurring-date-picker-input/RecurringDatePickerInput.d.ts.map +1 -1
  48. package/dist/recurring-date-picker-input/RecurringDatePickerInput.js +1 -1
  49. package/dist/recurring-date-picker-input/utils.d.ts +6 -0
  50. package/dist/recurring-date-picker-input/utils.d.ts.map +1 -1
  51. package/dist/recurring-date-picker-input/utils.js +1 -1
  52. package/dist/sales/booking/results/components/ResultCard.d.ts.map +1 -1
  53. package/dist/sales/booking/results/components/ResultCard.js +1 -1
  54. package/dist/sales/coupon/control/CouponFormControl.d.ts +2 -1
  55. package/dist/sales/coupon/control/CouponFormControl.d.ts.map +1 -1
  56. package/dist/sales/coupon/control/CouponFormControl.js +1 -1
  57. package/dist/sales/coupon/form/CouponFrom.d.ts +2 -2
  58. package/dist/sales/coupon/form/CouponFrom.d.ts.map +1 -1
  59. package/dist/sales/coupon/utils/index.d.ts +7 -0
  60. package/dist/sales/coupon/utils/index.d.ts.map +1 -0
  61. package/dist/sales/coupon/utils/index.js +1 -0
  62. package/dist/setting/admin/AdminSettingForm.d.ts +2 -2
  63. package/dist/setting/admin/AdminSettingForm.d.ts.map +1 -1
  64. package/dist/setting/dashboard/DashboardSettingForm.d.ts +1 -0
  65. package/dist/setting/dashboard/DashboardSettingForm.d.ts.map +1 -1
  66. package/dist/setting/dashboard/DashboardSettingForm.js +1 -1
  67. package/dist/sortable-tree/SortableTreeItem.d.ts +1 -2
  68. package/dist/sortable-tree/SortableTreeItem.d.ts.map +1 -1
  69. package/dist/sortable-tree/SortableTreeItem.js +1 -1
  70. package/dist/styles/sales/BookingResults.scss +1 -1
  71. package/dist/typeahead/Typeahead.d.ts +2 -1
  72. package/dist/typeahead/Typeahead.d.ts.map +1 -1
  73. package/dist/typeahead/Typeahead.js +1 -1
  74. package/dist/zone/form/ZoneForm.d.ts +2 -2
  75. package/dist/zone/form/ZoneForm.d.ts.map +1 -1
  76. package/dist/zone/form/ZoneForm.js +1 -1
  77. package/dist/zone/form/components/AvailableTimesControl.d.ts +0 -2
  78. package/dist/zone/form/components/AvailableTimesControl.d.ts.map +1 -1
  79. package/dist/zone/form/components/ZoneControl.d.ts +2 -2
  80. package/dist/zone/form/components/ZoneControl.d.ts.map +1 -1
  81. package/dist/zone/form/components/ZoneControl.js +1 -1
  82. package/dist/zone/form/components/ZoneRecurrencesControl.d.ts +4 -4
  83. package/dist/zone/form/components/ZoneRecurrencesControl.d.ts.map +1 -1
  84. package/dist/zone/form/components/ZoneRecurrencesControl.js +1 -1
  85. package/dist/zone/form/utils/dates.d.ts.map +1 -1
  86. package/package.json +10 -35
  87. package/src/date-time-button/DateTimeButton.stories.tsx +2 -1
  88. package/src/date-time-button/DateTimeButton.tsx +7 -5
  89. package/src/events/edit-event-modal/component/EditEventForm/EditEventForm.tsx +4 -0
  90. package/src/events/edit-event-modal/component/SelectEventProductSet/SelectEventProductSet.tsx +18 -9
  91. package/src/events/edit-event-modal/component/SelectEventProductSet/component/EditEventProductSet.tsx +5 -0
  92. package/src/iframe/payment/order-items-table/utils/paymentSummary.tsx +6 -6
  93. package/src/product-set/control/DateAndRecurrenceInput.tsx +48 -14
  94. package/src/product-set/control/DateInput.tsx +2 -4
  95. package/src/product-set/control/ProductSetControl.stories.tsx +1 -1
  96. package/src/product-set/control/ProductSetControl.tsx +12 -11
  97. package/src/product-set/control/utils.ts +25 -0
  98. package/src/product-set/form/ProductCategoriesControl.tsx +8 -4
  99. package/src/product-set/form/ProductSetForm.stories.tsx +1 -2
  100. package/src/product-set/form/ProductSetForm.tsx +5 -1
  101. package/src/product-set/form/ProductsControl.tsx +87 -77
  102. package/src/product-set/form/StepsControl.tsx +11 -6
  103. package/src/product-set/hooks/useSortableTreeFunctions.ts +2 -0
  104. package/src/product-set/product/ProductControl.tsx +39 -39
  105. package/src/product-set/product/fixed-duration-fields/FixedDurationOptions.tsx +0 -2
  106. package/src/product-set/step/StepControl.tsx +4 -3
  107. package/src/provider/working-hours-input/WorkingHoursInputDescription.tsx +4 -18
  108. package/src/recurring-date-picker-input/RecurrenceAndFrequencyInput.tsx +0 -1
  109. package/src/recurring-date-picker-input/RecurringDatePickerInput.stories.tsx +13 -7
  110. package/src/recurring-date-picker-input/RecurringDatePickerInput.tsx +12 -1
  111. package/src/recurring-date-picker-input/utils.ts +82 -1
  112. package/src/sales/booking/results/BookingResults.stories.tsx +3 -2
  113. package/src/sales/booking/results/components/ResultCard.tsx +2 -5
  114. package/src/sales/coupon/control/CouponFormControl.tsx +28 -51
  115. package/src/sales/coupon/form/CouponFrom.tsx +5 -15
  116. package/src/sales/coupon/utils/index.ts +13 -0
  117. package/src/setting/admin/AdminSettingForm.tsx +2 -2
  118. package/src/setting/dashboard/DashboardSettingForm.tsx +13 -0
  119. package/src/sortable-tree/SortableTreeItem.tsx +1 -4
  120. package/src/static/switch/BooleanSwitch.tsx +1 -1
  121. package/src/styles/sales/BookingResults.scss +1 -1
  122. package/src/typeahead/Typeahead.tsx +16 -3
  123. package/src/zone/form/ZoneForm.tsx +3 -2
  124. package/src/zone/form/components/AvailableTimesControl.tsx +0 -2
  125. package/src/zone/form/components/ZoneControl.tsx +3 -3
  126. package/src/zone/form/components/ZoneRecurrencesControl.tsx +7 -5
  127. package/src/zone/form/utils/dates.ts +9 -10
  128. package/jest.config.js +0 -29
  129. package/tests/Auth/Authorizer.test.tsx +0 -194
  130. package/tests/Auth/Layout/UserNavDropDown.test.tsx +0 -43
  131. package/tests/Auth/Layout/UserNavDropDownToggle.test.tsx +0 -33
  132. package/tests/Auth/Login/LoginComponent.test.tsx +0 -246
  133. package/tests/Auth/Login/LoginFormComponent.test.tsx +0 -182
  134. package/tests/Auth/Register/RegisterComponent.test.tsx +0 -285
  135. package/tests/Auth/Register/RegisterFormComponent.test.tsx +0 -170
  136. package/tests/Auth/Settings/Dashboard/IpInput.test.tsx +0 -130
  137. package/tests/Auth/Social/SocialCallbackComponent.test.tsx +0 -133
  138. package/tests/Auth/Social/SocialFormComponent.test.tsx +0 -118
  139. package/tests/FileUpload/FileUpload.test.tsx +0 -42
  140. package/tests/Notification/EmailTemplate.test.tsx +0 -82
  141. package/tests/ProductSet/ProductSetPopover.test.tsx +0 -40
  142. package/tests/Report/Report.test.tsx +0 -48
  143. package/tests/Sales/Coupon.test.tsx +0 -51
  144. package/tests/Sales/SalesAndVIews.test.tsx +0 -63
  145. package/tests/SnippetTemplates/SnippetTemplates.test.tsx +0 -56
  146. package/tests/Table/FilterHelperComponent.test.tsx +0 -88
  147. package/tests/Table/PaginationHelperComponent.test.tsx +0 -109
  148. package/tests/Table/PerPageHelperComponent.test.tsx +0 -34
  149. package/tests/Table/TableHelperComponent.test.tsx +0 -295
  150. package/tests/TipTapEditor/TipTapEditor.test.tsx +0 -28
  151. package/tests/__mock__/hooks/useAuthApi.ts +0 -13
  152. package/tests/__mock__/hooks/useAuthMock.ts +0 -13
  153. package/tests/__mock__/hooks/useFormMock.ts +0 -27
  154. package/tests/__mock__/hooks/useNotificationMock.ts +0 -13
  155. package/tests/__mock__/hooks/useQueryMock.ts +0 -16
  156. package/tests/__mock__/hooks/useSocialApiMock.ts +0 -20
  157. package/tests/__mock__/hooks/useTranslationMock.ts +0 -17
  158. package/tests/__mock__/hooks/useUserApiMock.ts +0 -18
  159. package/tests/__mock__/hooks/useUserMock.ts +0 -13
  160. package/tests/__mock__/styleMock.js +0 -1
  161. package/tests/__mock__/windowMock.ts +0 -5
  162. package/tests/packages/react-query.tsx +0 -28
  163. package/tests/setupTests.ts +0 -10
@@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from "react";
2
2
  import { Modifier } from "@popperjs/core";
3
3
  import { useId } from "@react-aria/utils";
4
4
  import { Form, OverlayTrigger, Popover } from "react-bootstrap";
5
- import { useFieldArray, useFormContext } from "react-hook-form";
5
+ import { useFieldArray, useFormContext, useWatch } from "react-hook-form";
6
6
  import { useTranslation } from "react-i18next";
7
7
  import { useClickAway } from "react-use";
8
8
  import { DateTime } from "luxon";
@@ -14,7 +14,6 @@ import {
14
14
  SortableContext,
15
15
  verticalListSortingStrategy,
16
16
  } from "@dnd-kit/sortable";
17
-
18
17
  import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
19
18
  import { CONFIRM_MODAL_ACTIONS } from "../../modals/confirmation/ConfirmModal";
20
19
  import { Icon } from "../../static";
@@ -27,16 +26,15 @@ import {
27
26
  AvailableTimesControl,
28
27
  AvailableTimesControlRef,
29
28
  } from "../../zone/form/components/AvailableTimesControl";
30
-
31
- // eslint-disable-next-line max-len
32
-
33
- export interface DateAndRecurrenceInputValues {
34
- menuRecurrences?: Partial<ProductSetRecurrence>[];
35
- }
29
+ import { ProductSetControlValues } from "..";
30
+ import {
31
+ getProductWithSmallestDuration,
32
+ getLatestAvailableDateTime,
33
+ } from "./utils";
36
34
 
37
35
  interface DateAndRecurrenceInputProps {
38
36
  isEventEditProductSet?: boolean;
39
- workHours?: WorkHour[];
37
+ workHours: WorkHour[] | undefined;
40
38
  providerHasBookingManagement: boolean;
41
39
  isLoading?: boolean;
42
40
  }
@@ -54,7 +52,9 @@ export const DateAndRecurrenceInput = ({
54
52
  getValues,
55
53
  setValue,
56
54
  clearErrors,
57
- } = useFormContext<DateAndRecurrenceInputValues>();
55
+ } = useFormContext<ProductSetControlValues>();
56
+
57
+ const steps = useWatch({ control, name: "steps" });
58
58
 
59
59
  const [availableTimes, setAvailableTimes] = useState<string[]>([]);
60
60
  const availableTimesFormRef = useRef<AvailableTimesControlRef>();
@@ -90,7 +90,7 @@ export const DateAndRecurrenceInput = ({
90
90
  clearEditState();
91
91
  });
92
92
 
93
- const popoverId = useId()!;
93
+ const popoverId = useId();
94
94
 
95
95
  const { t } = useTranslation("Design");
96
96
 
@@ -101,11 +101,13 @@ export const DateAndRecurrenceInput = ({
101
101
  const isAvailableTimesFormValid =
102
102
  await availableTimesFormRef.current?.trigger();
103
103
 
104
+ const { availableTimes } = availableTimesFormRef.current.getValues();
105
+
104
106
  if (next?.startTime && next?.endTime && isAvailableTimesFormValid) {
105
107
  const startTime = DateTime.fromISO(next.startTime);
106
108
  const endTime = DateTime.fromISO(next.endTime);
107
- const { availableTimes } = availableTimesFormRef.current.getValues();
108
- const validAvalilableTimes = availableTimes.every((time) => {
109
+
110
+ const areAvailableTimesValid = availableTimes.every((time) => {
109
111
  const currentAvailableTime = DateTime.fromFormat(time, TIME_FORMAT);
110
112
 
111
113
  return (
@@ -113,7 +115,8 @@ export const DateAndRecurrenceInput = ({
113
115
  currentAvailableTime.diff(endTime, "minutes").minutes <= 0
114
116
  );
115
117
  });
116
- if (!validAvalilableTimes) {
118
+
119
+ if (!areAvailableTimesValid) {
117
120
  availableTimesFormRef.current.setError("availableTimes", {
118
121
  message: t("Validation:fieldTimeBetween", {
119
122
  attribute: t("Design:startTimesSmall"),
@@ -126,6 +129,36 @@ export const DateAndRecurrenceInput = ({
126
129
  }
127
130
  }
128
131
 
132
+ // this check is required to prevent user from adding available time
133
+ // less than the lowest duration product, e.g. if lowest duration product
134
+ // is 1 hour and user tries to add time 15 minutes before end time,
135
+ // such time can never be booked
136
+ const lowestDurationProduct = getProductWithSmallestDuration(steps);
137
+
138
+ if (lowestDurationProduct) {
139
+ const endTime = next?.endTime
140
+ ? DateTime.fromFormat(next?.endTime, TIME_FORMAT)
141
+ : // this is to allow user to add 23:00 if end is at 23:59
142
+ DateTime.fromFormat("23:59", TIME_FORMAT).plus({ minutes: 1 });
143
+
144
+ const latestAvailableTime = getLatestAvailableDateTime(availableTimes);
145
+
146
+ const latestAllowedTime = endTime.minus({
147
+ minutes: lowestDurationProduct.duration,
148
+ });
149
+
150
+ if (latestAvailableTime > latestAllowedTime) {
151
+ availableTimesFormRef.current.setError("availableTimes", {
152
+ message: t("Validation:fieldTimeBefore", {
153
+ attribute: t("Design:timeLowercase"),
154
+ time: latestAllowedTime.toFormat(TIME_FORMAT),
155
+ }),
156
+ });
157
+
158
+ return;
159
+ }
160
+ }
161
+
129
162
  const availableTime =
130
163
  availableTimesFormRef?.current?.getValues()?.availableTimes || null;
131
164
 
@@ -228,6 +261,7 @@ export const DateAndRecurrenceInput = ({
228
261
  defaultValues={editState.values}
229
262
  onChange={handleRecurringDateChange}
230
263
  onDelete={handleDelete}
264
+ workHours={workHours}
231
265
  >
232
266
  {providerHasBookingManagement && (
233
267
  <AvailableTimesControl
@@ -5,9 +5,8 @@ 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 { TIMEZONE } from "@licklist/core/dist/Config/Date";
8
+ import { UTC_TIMEZONE, TIME_FORMAT } from "@licklist/core/dist/Config/Date";
9
9
  import { ProductSetRecurrence } from "@licklist/core/dist/DataMapper/Product/ProductSetRecurrenceDataMapper";
10
- import { TIME_FORMAT } from "@licklist/core/dist/Config/Date";
11
10
  import { DateTime } from "luxon";
12
11
  import RRule, { Frequency } from "rrule";
13
12
  import { WorkHour } from "@licklist/core/dist/DataMapper/Provider/WorkHourDataMapper";
@@ -27,7 +26,6 @@ import {
27
26
  import { ProductSetRecurrenceOverridesControl } from "./ProductSetRecurrenceOverridesControl";
28
27
  import { MAX_QUANTITY_RECURRENCE_DATE_IN_OVERRIDE } from "../product/constants";
29
28
 
30
-
31
29
  export interface DateAndRecurrenceInputValues {
32
30
  menuRecurrences?: Partial<ProductSetRecurrence>[];
33
31
  }
@@ -138,7 +136,7 @@ export const DateInput = ({
138
136
  until: (start as DateTime).toJSDate(),
139
137
  byweekday: [],
140
138
  freq: Frequency.DAILY,
141
- tzid: TIMEZONE,
139
+ tzid: UTC_TIMEZONE,
142
140
  interval: 1,
143
141
  }).replace("RRULE:", "");
144
142
 
@@ -2,12 +2,12 @@ import { Meta, Story } from "@storybook/react";
2
2
  import React from "react";
3
3
  import Form from "react-bootstrap/Form";
4
4
  import { FormProvider, useForm } from "react-hook-form";
5
+ import { OPERATIONAL_COST_CUSTOMER } from "@licklist/core/dist/DataMapper/Product/ProductSetDataMapper";
5
6
  import {
6
7
  ProductSetControl,
7
8
  ProductSetControlProps,
8
9
  ProductSetControlValues,
9
10
  } from "./ProductSetControl";
10
- import { OPERATIONAL_COST_CUSTOMER } from "@licklist/core/dist/DataMapper/Product/ProductSetDataMapper";
11
11
 
12
12
  export default {
13
13
  title: "Product Set/Control",
@@ -22,14 +22,10 @@ import { SmsTemplate } from "@licklist/core/dist/DataMapper/Notification/SmsTemp
22
22
  import HookFormService from "@licklist/plugins/dist/services/Form/HookFormService";
23
23
  import { ruleForUrlWithProtocol } from "@licklist/plugins/dist/validation/Rules/urlRule";
24
24
  import { WorkHour } from "@licklist/core/dist/DataMapper/Provider/WorkHourDataMapper";
25
+ import { ProductSetRecurrence } from "@licklist/core/dist/DataMapper/Product/ProductSetRecurrenceDataMapper";
25
26
  import { WarningMessage } from "../../static";
26
27
  import { SelectItem } from "../../types/generic/SelectItem";
27
- import {
28
- DateAndRecurrenceInput,
29
- // TODO: Show Date Component, when reccurent date bugs are fixed
30
- // DateAndRecurrenceInput,
31
- DateAndRecurrenceInputValues,
32
- } from "./DateAndRecurrenceInput";
28
+ import { DateAndRecurrenceInput } from "./DateAndRecurrenceInput";
33
29
  import TutorialGifCard from "./TutorialGifCard";
34
30
  import { Step } from "../types";
35
31
  import { StepsControl } from "../form/StepsControl";
@@ -53,7 +49,7 @@ export interface TemplateItem {
53
49
  label: string;
54
50
  }
55
51
 
56
- export interface ProductSetControlValues extends DateAndRecurrenceInputValues {
52
+ export interface ProductSetControlValues {
57
53
  name: string;
58
54
  type: ProductSetType;
59
55
  termsAndConditions: string;
@@ -65,6 +61,7 @@ export interface ProductSetControlValues extends DateAndRecurrenceInputValues {
65
61
  steps: Step[];
66
62
  emailTemplates?: TemplateItem[];
67
63
  smsTemplates?: TemplateItem[];
64
+ menuRecurrences?: Partial<ProductSetRecurrence>[];
68
65
  }
69
66
 
70
67
  export interface ProductSetControlShared {
@@ -85,7 +82,7 @@ export interface ProductSetControlProps {
85
82
  fieldSets?: FieldSet[];
86
83
  showEmailTemplate?: boolean;
87
84
  showSmsTemplate?: boolean;
88
- workHours?: WorkHour[];
85
+ workHours: WorkHour[] | undefined;
89
86
  providerHasBookingManagement?: boolean;
90
87
  isOverrides?: boolean;
91
88
  }
@@ -123,7 +120,7 @@ export function ProductSetControl({
123
120
 
124
121
  const showSmsTemplateSelector = showSmsTemplate && !isOverrides;
125
122
  const showEmailTemplateSelector = showEmailTemplate && !isOverrides;
126
-
123
+ const showDateSelector = !isEventEditProductSet && isOverrides;
127
124
  return (
128
125
  <Row
129
126
  className={`product-set-form ${
@@ -165,7 +162,7 @@ export function ProductSetControl({
165
162
  </Form.Control.Feedback>
166
163
  </Form.Group>
167
164
 
168
- {isOverrides ? (
165
+ {showDateSelector ? (
169
166
  <DateInput
170
167
  workHours={workHours}
171
168
  isLoading={isLoading}
@@ -188,7 +185,11 @@ export function ProductSetControl({
188
185
 
189
186
  <TutorialGifCard isOverrides={isOverrides} />
190
187
 
191
- <StepsControl isLoading={isLoading} isOverrides={isOverrides} />
188
+ <StepsControl
189
+ isLoading={isLoading}
190
+ isEventEditProductSet={isEventEditProductSet}
191
+ isOverrides={isOverrides}
192
+ />
192
193
  </Col>
193
194
  <Col md={6} sm={12}>
194
195
  <div className="second-column">
@@ -0,0 +1,25 @@
1
+ import { TIME_FORMAT } from "@licklist/core/dist/Config";
2
+ import { dateTimesSortFn } from "@licklist/plugins/dist/utils/dateTime";
3
+ import { extractObjectWithLowestKey } from "@licklist/plugins/dist/utils/extractObjectWithLowestKey";
4
+ import { DateTime } from "luxon";
5
+ import { Product, Step } from "../types";
6
+
7
+ const getProductsFromSteps = (steps: Step[]): Product[] =>
8
+ steps.reduce(
9
+ (acc, step) =>
10
+ acc.concat(
11
+ step.productCategories.reduce(
12
+ (acc, productCategory) => acc.concat(productCategory.products),
13
+ []
14
+ )
15
+ ),
16
+ []
17
+ );
18
+
19
+ export const getProductWithSmallestDuration = (steps: Step[]): Product =>
20
+ extractObjectWithLowestKey(getProductsFromSteps(steps), "duration");
21
+
22
+ export const getLatestAvailableDateTime = (array: string[]): DateTime =>
23
+ array
24
+ .map((time) => DateTime.fromFormat(time, TIME_FORMAT))
25
+ .sort(dateTimesSortFn("desc"))[0];
@@ -36,6 +36,7 @@ import { useSortableTreeFunctions } from "../hooks/useSortableTreeFunctions";
36
36
  interface ProductCategoriesControlProps extends WithIsLoading {
37
37
  stepIndex: number;
38
38
  isOverrides?: boolean;
39
+ isEventEditProductSet?: boolean;
39
40
  }
40
41
 
41
42
  const getCategoryDefaultValue = (
@@ -67,6 +68,7 @@ export function ProductCategoriesControl({
67
68
  isLoading,
68
69
  stepIndex,
69
70
  isOverrides,
71
+ isEventEditProductSet,
70
72
  }: ProductCategoriesControlProps) {
71
73
  const { t } = useTranslation("Design");
72
74
  const {
@@ -214,6 +216,10 @@ export function ProductCategoriesControl({
214
216
  ? String(productCategory.id)
215
217
  : productCategory.uniqueId;
216
218
 
219
+ const isOverridesCategory = productCategory.products.some((product) =>
220
+ isEventEditProductSet ? !!product.id : !!product.originalProductId
221
+ );
222
+
217
223
  const onSetIsExpanded = () => {
218
224
  setIsExpanded(productCategoryId);
219
225
  };
@@ -269,7 +275,7 @@ export function ProductCategoriesControl({
269
275
  categoryName={value}
270
276
  stepIndex={stepIndex}
271
277
  productCategoryIndex={index}
272
- isOverride={isOverrides}
278
+ isOverride={isOverrides && isOverridesCategory}
273
279
  />
274
280
  }
275
281
  isOverride={isOverrides}
@@ -296,6 +302,7 @@ export function ProductCategoriesControl({
296
302
  productCategoryIndex={index}
297
303
  categoryType={productCategory.type}
298
304
  isOverrides={isOverrides}
305
+ isEventEditProductSet={isEventEditProductSet}
299
306
  />
300
307
  </SortableTree.Item>
301
308
  )}
@@ -305,9 +312,7 @@ export function ProductCategoriesControl({
305
312
  </SortableTree>
306
313
  <CreateProductSetItem
307
314
  title={t("addCategory")}
308
- isOverride={isOverrides}
309
315
  onClick={() => {
310
- if (isOverrides) return;
311
316
  setIsSelectCategoryVisible(true);
312
317
  }}
313
318
  />
@@ -316,7 +321,6 @@ export function ProductCategoriesControl({
316
321
  isVisible={isSelectCategoryVisible}
317
322
  onHide={() => setIsSelectCategoryVisible(false)}
318
323
  onCategorySelect={(categoryType) => {
319
- if (isOverrides) return;
320
324
  append(getCategoryDefaultValue(categoryType, fields.length));
321
325
  setShowCategoryModal(true);
322
326
  if (
@@ -63,9 +63,9 @@ Default.args = {
63
63
  sort: 100,
64
64
  type: "standard",
65
65
  updatedAt: "2023-05-08T15:55:28.000+03:00",
66
+ defaultDuration: 30,
66
67
  zoneRecurrences: [
67
68
  {
68
- availableTimes: ["10:05", "10:25", "10:45"],
69
69
  endDate: "2022-02-22",
70
70
  endTime: "22:00:00",
71
71
  id: 1,
@@ -90,7 +90,6 @@ Default.args = {
90
90
  updatedAt: "2023-05-08T15:55:28.000+03:00",
91
91
  zoneRecurrences: [
92
92
  {
93
- availableTimes: ["10:05", "10:25", "10:45"],
94
93
  endDate: "2022-02-22",
95
94
  endTime: "22:00:00",
96
95
  id: 1,
@@ -26,12 +26,16 @@ import { checkAvailableTimesErrors, getFilteredTemplates } from "../utils";
26
26
  export interface WithIsLoading {
27
27
  isLoading: boolean;
28
28
  }
29
+ export interface WithIdOptional {
30
+ id?: number;
31
+ }
32
+
29
33
  export interface WithId {
30
34
  id: number;
31
35
  }
32
36
  export interface ProductSetFormValues
33
37
  extends FormValues,
34
- WithId,
38
+ WithIdOptional,
35
39
  ProductSetControlValues {
36
40
  steps: Step[];
37
41
  isOverrides?: boolean;
@@ -35,6 +35,7 @@ interface ProductsControlProps extends WithIsLoading {
35
35
  categoryType: CategoryType;
36
36
  zones?: Zone[];
37
37
  isOverrides?: boolean;
38
+ isEventEditProductSet?: boolean;
38
39
  }
39
40
 
40
41
  const getDefaultProductValue = (sort: number): Product => ({
@@ -57,6 +58,7 @@ const getDefaultProductValue = (sort: number): Product => ({
57
58
  quantitySelector: 1,
58
59
  hasSpecialNotes: false,
59
60
  weight: 0,
61
+ originalProductId: null,
60
62
  type: DEFAULT_PRODUCT_TYPE,
61
63
  tierId: Number(uniqueId()),
62
64
  zoneId: null,
@@ -79,6 +81,7 @@ export function ProductsControl({
79
81
  categoryType,
80
82
  zones,
81
83
  isOverrides,
84
+ isEventEditProductSet,
82
85
  }: ProductsControlProps) {
83
86
  const { t } = useTranslation("Design");
84
87
  const form = useFormContext<ProductSetFormValues>();
@@ -203,90 +206,97 @@ export function ProductsControl({
203
206
  onDragEnd={handleDragEnd}
204
207
  sensors={[mouseSensor]}
205
208
  >
206
- {fields.map((product, index) => (
207
- <Controller
208
- key={product._id}
209
- control={control}
210
- name={`${productControlFieldName}.${index}.name` as const}
211
- rules={{
212
- required: t("Validation:fieldRequired", {
213
- attribute: t("name"),
214
- }) as string,
215
- }}
216
- render={({ field: { value, onChange } }) => (
217
- <SortableTree.Item
218
- key={`product-${product._id}`}
219
- id={String(product._id)}
220
- isExpanded={isFirstProductAdded}
221
- isInvalid={!!categoryProductErrors}
222
- title={value}
223
- isOverride={isOverrides}
224
- badge={<Badge className="product-badge">{t("product")}</Badge>}
225
- isIconInHeader={false}
226
- cancelChanges={() => cancelChanges(index)}
227
- edit={() => edit(index)}
228
- isProduct
229
- secondaryBadge={getBadgeConfig(categoryType, t(categoryType))}
230
- subTitle={
231
- <div className="product-set-badges-container">
232
- <div className="product-set-subtitle-dot product-set-subtitle-product-dot" />
233
- <span>
234
- {`£${prices[index]} ${t("each")} - ${t("qty")}:${
235
- !isUnlimitedQuantities[index]
236
- ? ` ${quantities[index]}`
237
- : t(" unlimited")
238
- } ${
239
- !isUnlimitedQuantities[index]
240
- ? ` - ${t("possibleRevenue")} £${
241
- prices[index] * quantities[index]
242
- }`
243
- : ""
244
- } `}
245
- </span>
246
- </div>
247
- }
248
- modalLabel={t("addNewProduct")}
249
- modalClass={ProductSetModalClasses.product}
250
- isNewAdded={showProductModal}
251
- body={
252
- <ProductControl<ProductSetFormValues>
209
+ {fields.map((product, index) => {
210
+ const isOverridesProductSet = isEventEditProductSet
211
+ ? !!product.id
212
+ : isOverrides && !!product.originalProductId;
213
+
214
+ return (
215
+ <Controller
216
+ key={product._id}
217
+ control={control}
218
+ name={`${productControlFieldName}.${index}.name` as const}
219
+ rules={{
220
+ required: t("Validation:fieldRequired", {
221
+ attribute: t("name"),
222
+ }) as string,
223
+ }}
224
+ render={({ field: { value, onChange } }) => (
225
+ <SortableTree.Item
226
+ key={`product-${product._id}`}
227
+ id={String(product._id)}
228
+ isExpanded={isFirstProductAdded}
229
+ isInvalid={!!categoryProductErrors}
230
+ title={value}
231
+ isOverride={isOverrides}
232
+ badge={
233
+ <Badge className="product-badge">{t("product")}</Badge>
234
+ }
235
+ isIconInHeader={false}
236
+ cancelChanges={() => cancelChanges(index)}
237
+ edit={() => edit(index)}
238
+ secondaryBadge={getBadgeConfig(categoryType, t(categoryType))}
239
+ subTitle={
240
+ <div className="product-set-badges-container">
241
+ <div className="product-set-subtitle-dot product-set-subtitle-product-dot" />
242
+ <span>
243
+ {`£${prices[index]} ${t("each")} - ${t("qty")}:${
244
+ !isUnlimitedQuantities[index]
245
+ ? ` ${quantities[index]}`
246
+ : t(" unlimited")
247
+ } ${
248
+ !isUnlimitedQuantities[index]
249
+ ? ` - ${t("possibleRevenue")} £${
250
+ prices[index] * quantities[index]
251
+ }`
252
+ : ""
253
+ } `}
254
+ </span>
255
+ </div>
256
+ }
257
+ modalLabel={t("addNewProduct")}
258
+ modalClass={ProductSetModalClasses.product}
259
+ isNewAdded={showProductModal}
260
+ body={
261
+ <ProductControl<ProductSetFormValues>
262
+ isLoading={isLoading}
263
+ quantityType={quantityType}
264
+ allowDeposits={allowDeposits}
265
+ hasBookingManagement={false}
266
+ fieldNamePrefix={
267
+ `${productControlFieldName}.${index}` as const
268
+ }
269
+ productName={value}
270
+ onProductNameChange={onChange}
271
+ hasTicket={hasTicket}
272
+ categoryType={categoryType}
273
+ zoneDuration={catergoryZone?.defaultDuration}
274
+ isOverrides={isOverridesProductSet}
275
+ />
276
+ }
277
+ onDelete={() => onProductRemove(index)}
278
+ validate={() =>
279
+ trigger(`${productControlFieldName}.${index}`)
280
+ }
281
+ saveValidField={saveValidField}
282
+ // preItem={<Popover className="d-none d-sm-block" />}
283
+ >
284
+ <SubProductsControl
253
285
  isLoading={isLoading}
254
286
  quantityType={quantityType}
255
- allowDeposits={allowDeposits}
256
- hasBookingManagement={false}
257
- fieldNamePrefix={
258
- `${productControlFieldName}.${index}` as const
259
- }
260
- productName={value}
261
- onProductNameChange={onChange}
262
- hasTicket={hasTicket}
263
- categoryType={categoryType}
264
- zoneDuration={catergoryZone?.defaultDuration}
265
- isOverrides={isOverrides}
287
+ stepIndex={stepIndex}
288
+ productCategoryIndex={productCategoryIndex}
289
+ productIndex={index}
266
290
  />
267
- }
268
- onDelete={() => onProductRemove(index)}
269
- validate={() => trigger(`${productControlFieldName}.${index}`)}
270
- saveValidField={saveValidField}
271
- // preItem={<Popover className="d-none d-sm-block" />}
272
- >
273
- <SubProductsControl
274
- isLoading={isLoading}
275
- quantityType={quantityType}
276
- stepIndex={stepIndex}
277
- productCategoryIndex={productCategoryIndex}
278
- productIndex={index}
279
- />
280
- </SortableTree.Item>
281
- )}
282
- />
283
- ))}
291
+ </SortableTree.Item>
292
+ )}
293
+ />
294
+ );
295
+ })}
284
296
  </SortableTree>
285
297
  <CreateProductSetItem
286
298
  title={t("addProduct")}
287
- isOverride={isOverrides}
288
299
  onClick={() => {
289
- if (isOverrides) return;
290
300
  append(getDefaultProductValue(fields.length));
291
301
  setShowProductModal(true);
292
302
 
@@ -19,9 +19,14 @@ import { ProductSetFormValues } from "./ProductSetForm";
19
19
  interface StepsControlProps {
20
20
  isLoading: boolean;
21
21
  isOverrides?: boolean;
22
+ isEventEditProductSet?: boolean;
22
23
  }
23
24
 
24
- export function StepsControl({ isLoading, isOverrides }: StepsControlProps) {
25
+ export function StepsControl({
26
+ isLoading,
27
+ isOverrides,
28
+ isEventEditProductSet,
29
+ }: StepsControlProps) {
25
30
  const form = useFormContext<ProductSetFormValues>();
26
31
  const { t } = useTranslation("Design");
27
32
  const [showStepModal, setShowStepModal] = useState(false);
@@ -68,8 +73,8 @@ export function StepsControl({ isLoading, isOverrides }: StepsControlProps) {
68
73
  <>
69
74
  {fields.map((step, index) => {
70
75
  const stepFieldName = `steps.${index}` as const;
71
-
72
76
  const stepData = getValues(stepFieldName);
77
+ const isOverrideStep = !!step.id;
73
78
 
74
79
  return (
75
80
  <Controller
@@ -92,9 +97,9 @@ export function StepsControl({ isLoading, isOverrides }: StepsControlProps) {
92
97
  modalLabel={t("addNewStep")}
93
98
  modalClass={ProductSetModalClasses.step}
94
99
  isNewAdded={showStepModal}
95
- edit={() => !isOverrides && edit(index)}
100
+ edit={() => edit(index)}
96
101
  cancelChanges={() => cancelChanges(index)}
97
- isOverride={isOverrides}
102
+ isOverride={isOverrideStep}
98
103
  subTitle={
99
104
  <div className="product-set-badges-container">
100
105
  <div className="product-set-subtitle-dot product-set-subtitle-step-dot" />
@@ -110,6 +115,7 @@ export function StepsControl({ isLoading, isOverrides }: StepsControlProps) {
110
115
  stepIndex={index}
111
116
  stepName={value}
112
117
  onStepNameChange={onChange}
118
+ isOverrides={isOverrideStep}
113
119
  />
114
120
  }
115
121
  onDelete={() => remove(index)}
@@ -140,6 +146,7 @@ export function StepsControl({ isLoading, isOverrides }: StepsControlProps) {
140
146
  isLoading={isLoading}
141
147
  stepIndex={index}
142
148
  isOverrides={isOverrides}
149
+ isEventEditProductSet={isEventEditProductSet}
143
150
  />
144
151
  </SortableTree.Item>
145
152
  )}
@@ -149,9 +156,7 @@ export function StepsControl({ isLoading, isOverrides }: StepsControlProps) {
149
156
 
150
157
  <CreateProductSetItem
151
158
  title={t("addStep")}
152
- isOverride={isOverrides}
153
159
  onClick={() => {
154
- if (isOverrides) return;
155
160
  append(getDefaultStepsValues());
156
161
  setShowStepModal(true);
157
162
  if (errors.steps?.type === HookFormService.manualErrorType) {
@@ -24,8 +24,10 @@ export const useSortableTreeFunctions = ({
24
24
  const cancelChanges = (index: number) => {
25
25
  if (isOverrides) return;
26
26
  if (!previousValue) {
27
+ // eslint-disable-next-line consistent-return
27
28
  return remove(index);
28
29
  }
30
+ // eslint-disable-next-line consistent-return
29
31
  return setValue(`${fieldName}.${index}` as const, previousValue);
30
32
  };
31
33