@licklist/design 0.62.3 → 0.63.0-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 (150) 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/iframe/ryft/RyftPaymentForm.d.ts.map +1 -1
  14. package/dist/iframe/ryft/RyftPaymentForm.js +1 -1
  15. package/dist/iframe/ryft/utils/ryft-form.d.ts.map +1 -1
  16. package/dist/iframe/ryft/utils/ryft-form.js +1 -1
  17. package/dist/product-set/control/DateAndRecurrenceInput.d.ts +1 -5
  18. package/dist/product-set/control/DateAndRecurrenceInput.d.ts.map +1 -1
  19. package/dist/product-set/control/DateAndRecurrenceInput.js +1 -1
  20. package/dist/product-set/control/DateInput.d.ts.map +1 -1
  21. package/dist/product-set/control/DateInput.js +1 -1
  22. package/dist/product-set/control/ProductSetControl.d.ts +4 -3
  23. package/dist/product-set/control/ProductSetControl.d.ts.map +1 -1
  24. package/dist/product-set/control/utils.d.ts +5 -0
  25. package/dist/product-set/control/utils.d.ts.map +1 -0
  26. package/dist/product-set/control/utils.js +1 -0
  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/hooks/useSortableTreeFunctions.d.ts.map +1 -1
  30. package/dist/product-set/product/ProductControl.d.ts.map +1 -1
  31. package/dist/product-set/product/ProductControl.js +1 -1
  32. package/dist/product-set/product/fixed-duration-fields/FixedDurationOptions.d.ts.map +1 -1
  33. package/dist/provider/working-hours-input/WorkingHoursInputDescription.d.ts.map +1 -1
  34. package/dist/provider/working-hours-input/WorkingHoursInputDescription.js +1 -1
  35. package/dist/recurring-date-picker-input/RecurrenceAndFrequencyInput.d.ts.map +1 -1
  36. package/dist/recurring-date-picker-input/RecurringDatePickerInput.d.ts +3 -1
  37. package/dist/recurring-date-picker-input/RecurringDatePickerInput.d.ts.map +1 -1
  38. package/dist/recurring-date-picker-input/RecurringDatePickerInput.js +1 -1
  39. package/dist/recurring-date-picker-input/utils.d.ts +12 -0
  40. package/dist/recurring-date-picker-input/utils.d.ts.map +1 -1
  41. package/dist/recurring-date-picker-input/utils.js +1 -1
  42. package/dist/sales/booking/results/components/ResultCard.d.ts.map +1 -1
  43. package/dist/sales/booking/results/components/ResultCard.js +1 -1
  44. package/dist/sales/coupon/control/CouponFormControl.d.ts +2 -1
  45. package/dist/sales/coupon/control/CouponFormControl.d.ts.map +1 -1
  46. package/dist/sales/coupon/control/CouponFormControl.js +1 -1
  47. package/dist/sales/coupon/form/CouponFrom.d.ts +2 -2
  48. package/dist/sales/coupon/form/CouponFrom.d.ts.map +1 -1
  49. package/dist/sales/coupon/utils/index.d.ts +7 -0
  50. package/dist/sales/coupon/utils/index.d.ts.map +1 -0
  51. package/dist/sales/coupon/utils/index.js +1 -0
  52. package/dist/setting/admin/AdminSettingForm.d.ts +2 -2
  53. package/dist/setting/admin/AdminSettingForm.d.ts.map +1 -1
  54. package/dist/setting/dashboard/DashboardSettingForm.d.ts +1 -0
  55. package/dist/setting/dashboard/DashboardSettingForm.d.ts.map +1 -1
  56. package/dist/setting/dashboard/DashboardSettingForm.js +1 -1
  57. package/dist/styles/ryft-payment-form/RyftPaymentForm.scss +99 -113
  58. package/dist/styles/sales/BookingResults.scss +1 -1
  59. package/dist/typeahead/Typeahead.d.ts +2 -1
  60. package/dist/typeahead/Typeahead.d.ts.map +1 -1
  61. package/dist/typeahead/Typeahead.js +1 -1
  62. package/dist/zone/form/ZoneForm.d.ts +2 -2
  63. package/dist/zone/form/ZoneForm.d.ts.map +1 -1
  64. package/dist/zone/form/ZoneForm.js +1 -1
  65. package/dist/zone/form/components/AvailableTimesControl.d.ts +0 -2
  66. package/dist/zone/form/components/AvailableTimesControl.d.ts.map +1 -1
  67. package/dist/zone/form/components/ZoneControl.d.ts +2 -2
  68. package/dist/zone/form/components/ZoneControl.d.ts.map +1 -1
  69. package/dist/zone/form/components/ZoneControl.js +1 -1
  70. package/dist/zone/form/components/ZoneRecurrencesControl.d.ts +4 -4
  71. package/dist/zone/form/components/ZoneRecurrencesControl.d.ts.map +1 -1
  72. package/dist/zone/form/components/ZoneRecurrencesControl.js +1 -1
  73. package/dist/zone/form/utils/dates.d.ts.map +1 -1
  74. package/package.json +10 -35
  75. package/src/date-time-button/DateTimeButton.stories.tsx +2 -1
  76. package/src/date-time-button/DateTimeButton.tsx +7 -5
  77. package/src/events/edit-event-modal/component/EditEventForm/EditEventForm.tsx +4 -0
  78. package/src/events/edit-event-modal/component/SelectEventProductSet/SelectEventProductSet.tsx +10 -8
  79. package/src/events/edit-event-modal/component/SelectEventProductSet/component/EditEventProductSet.tsx +4 -0
  80. package/src/iframe/payment/order-items-table/utils/paymentSummary.tsx +6 -6
  81. package/src/iframe/ryft/RyftPaymentForm.tsx +12 -4
  82. package/src/iframe/ryft/utils/ryft-form.ts +8 -4
  83. package/src/product-set/control/DateAndRecurrenceInput.tsx +48 -14
  84. package/src/product-set/control/DateInput.tsx +2 -4
  85. package/src/product-set/control/ProductSetControl.stories.tsx +1 -1
  86. package/src/product-set/control/ProductSetControl.tsx +5 -8
  87. package/src/product-set/control/utils.ts +25 -0
  88. package/src/product-set/form/ProductCategoriesControl.tsx +1 -1
  89. package/src/product-set/form/ProductSetForm.stories.tsx +1 -2
  90. package/src/product-set/form/ProductSetForm.tsx +5 -1
  91. package/src/product-set/hooks/useSortableTreeFunctions.ts +2 -0
  92. package/src/product-set/product/ProductControl.tsx +38 -38
  93. package/src/product-set/product/fixed-duration-fields/FixedDurationOptions.tsx +0 -2
  94. package/src/provider/working-hours-input/WorkingHoursInputDescription.tsx +4 -18
  95. package/src/recurring-date-picker-input/RecurrenceAndFrequencyInput.tsx +0 -1
  96. package/src/recurring-date-picker-input/RecurringDatePickerInput.tsx +11 -1
  97. package/src/recurring-date-picker-input/utils.ts +77 -0
  98. package/src/sales/booking/results/BookingResults.stories.tsx +3 -2
  99. package/src/sales/booking/results/components/ResultCard.tsx +2 -5
  100. package/src/sales/coupon/control/CouponFormControl.tsx +28 -51
  101. package/src/sales/coupon/form/CouponFrom.tsx +5 -15
  102. package/src/sales/coupon/utils/index.ts +13 -0
  103. package/src/setting/admin/AdminSettingForm.tsx +2 -2
  104. package/src/setting/dashboard/DashboardSettingForm.tsx +13 -0
  105. package/src/sortable-tree/SortableTreeItem.tsx +1 -1
  106. package/src/static/switch/BooleanSwitch.tsx +1 -1
  107. package/src/styles/ryft-payment-form/RyftPaymentForm.scss +99 -113
  108. package/src/styles/sales/BookingResults.scss +1 -1
  109. package/src/typeahead/Typeahead.tsx +16 -3
  110. package/src/zone/form/ZoneForm.tsx +3 -2
  111. package/src/zone/form/components/AvailableTimesControl.tsx +0 -2
  112. package/src/zone/form/components/ZoneControl.tsx +3 -3
  113. package/src/zone/form/components/ZoneRecurrencesControl.tsx +7 -5
  114. package/src/zone/form/utils/dates.ts +9 -10
  115. package/jest.config.js +0 -29
  116. package/tests/Auth/Authorizer.test.tsx +0 -194
  117. package/tests/Auth/Layout/UserNavDropDown.test.tsx +0 -43
  118. package/tests/Auth/Layout/UserNavDropDownToggle.test.tsx +0 -33
  119. package/tests/Auth/Login/LoginComponent.test.tsx +0 -246
  120. package/tests/Auth/Login/LoginFormComponent.test.tsx +0 -182
  121. package/tests/Auth/Register/RegisterComponent.test.tsx +0 -285
  122. package/tests/Auth/Register/RegisterFormComponent.test.tsx +0 -170
  123. package/tests/Auth/Settings/Dashboard/IpInput.test.tsx +0 -130
  124. package/tests/Auth/Social/SocialCallbackComponent.test.tsx +0 -133
  125. package/tests/Auth/Social/SocialFormComponent.test.tsx +0 -118
  126. package/tests/FileUpload/FileUpload.test.tsx +0 -42
  127. package/tests/Notification/EmailTemplate.test.tsx +0 -82
  128. package/tests/ProductSet/ProductSetPopover.test.tsx +0 -40
  129. package/tests/Report/Report.test.tsx +0 -48
  130. package/tests/Sales/Coupon.test.tsx +0 -51
  131. package/tests/Sales/SalesAndVIews.test.tsx +0 -63
  132. package/tests/SnippetTemplates/SnippetTemplates.test.tsx +0 -56
  133. package/tests/Table/FilterHelperComponent.test.tsx +0 -88
  134. package/tests/Table/PaginationHelperComponent.test.tsx +0 -109
  135. package/tests/Table/PerPageHelperComponent.test.tsx +0 -34
  136. package/tests/Table/TableHelperComponent.test.tsx +0 -295
  137. package/tests/TipTapEditor/TipTapEditor.test.tsx +0 -28
  138. package/tests/__mock__/hooks/useAuthApi.ts +0 -13
  139. package/tests/__mock__/hooks/useAuthMock.ts +0 -13
  140. package/tests/__mock__/hooks/useFormMock.ts +0 -27
  141. package/tests/__mock__/hooks/useNotificationMock.ts +0 -13
  142. package/tests/__mock__/hooks/useQueryMock.ts +0 -16
  143. package/tests/__mock__/hooks/useSocialApiMock.ts +0 -20
  144. package/tests/__mock__/hooks/useTranslationMock.ts +0 -17
  145. package/tests/__mock__/hooks/useUserApiMock.ts +0 -18
  146. package/tests/__mock__/hooks/useUserMock.ts +0 -13
  147. package/tests/__mock__/styleMock.js +0 -1
  148. package/tests/__mock__/windowMock.ts +0 -5
  149. package/tests/packages/react-query.tsx +0 -28
  150. package/tests/setupTests.ts +0 -10
@@ -5,6 +5,7 @@ import { useId } from "@react-aria/utils";
5
5
  import { Controller, useFormContext } from "react-hook-form";
6
6
  import { HasPermissionProp } from "@licklist/plugins/dist/types/permission/Permission";
7
7
  import { ProductSet } from "@licklist/core/dist/DataMapper/Product/ProductSetDataMapper";
8
+ import { WorkHour } from "@licklist/core/dist/DataMapper/Provider/WorkHourDataMapper";
8
9
  import { ConfirmModal } from "../../../../modals/confirmation/ConfirmModal";
9
10
  import { EditEventFormValues } from "../EditEventForm/EditEventForm";
10
11
  import { EditEventProductSet } from "./component/EditEventProductSet";
@@ -15,6 +16,7 @@ interface SelectEventProductSetProps extends HasPermissionProp {
15
16
  setShowEditProductSet: (value: boolean) => void;
16
17
  createProductSet: (props: EditEventFormValues) => void;
17
18
  setEditProductSetId: (value: string) => void;
19
+ workHours: WorkHour[] | undefined;
18
20
  }
19
21
 
20
22
  export const SelectEventProductSet = ({
@@ -23,6 +25,7 @@ export const SelectEventProductSet = ({
23
25
  setEditProductSetId,
24
26
  showEditProductSet,
25
27
  setShowEditProductSet,
28
+ workHours,
26
29
  }: SelectEventProductSetProps) => {
27
30
  const { t } = useTranslation(["Design"]);
28
31
  const form = useFormContext<EditEventFormValues>();
@@ -199,14 +202,13 @@ export const SelectEventProductSet = ({
199
202
  </Form.Group>
200
203
  </div>
201
204
  {showEditProductSet && productSetId && (
202
- <>
203
- <EditEventProductSet
204
- onSubmit={onSubmit}
205
- defaultValues={
206
- productSetId === editedProductSet?.id && editedProductSet
207
- }
208
- />
209
- </>
205
+ <EditEventProductSet
206
+ onSubmit={onSubmit}
207
+ defaultValues={
208
+ productSetId === editedProductSet?.id && editedProductSet
209
+ }
210
+ workHours={workHours}
211
+ />
210
212
  )}
211
213
  </>
212
214
  );
@@ -5,6 +5,7 @@ import { isEqual } from "lodash";
5
5
  import { useParams } from "react-router-dom";
6
6
  import { useEventEditProductSetContext } from "@licklist/plugins/dist/context/event/EventEditProductSetContext";
7
7
  import { useTranslation } from "react-i18next";
8
+ import { WorkHour } from "@licklist/core/dist/DataMapper/Provider/WorkHourDataMapper";
8
9
  import { BlockLoader } from "../../../../../static/loader/BlockLoader";
9
10
  import { ProductSetControl } from "../../../../../product-set";
10
11
  import { ProductSetContextProvider } from "../../../../../product-set/form/context";
@@ -22,11 +23,13 @@ export interface EventDates {
22
23
  interface EditEventProductSetProps {
23
24
  onSubmit: (value: EditEventFormValues["editedProductSet"]) => void;
24
25
  defaultValues?: EditEventFormValues["editedProductSet"];
26
+ workHours: WorkHour[] | undefined;
25
27
  }
26
28
 
27
29
  export const EditEventProductSet = ({
28
30
  onSubmit,
29
31
  defaultValues,
32
+ workHours,
30
33
  }: EditEventProductSetProps) => {
31
34
  const { companyId } = useParams<{
32
35
  companyId: string;
@@ -124,6 +127,7 @@ export const EditEventProductSet = ({
124
127
  companyName={selectedCompany?.title}
125
128
  fee={feeInPercent}
126
129
  isEventEditProductSet
130
+ workHours={workHours}
127
131
  />
128
132
  <Button
129
133
  disabled={isLoading}
@@ -63,7 +63,7 @@ const processedPaymentSummary = ({
63
63
  const total = calculateTotalPrice(order, externalPaymentDetail);
64
64
 
65
65
  const totalDiscount = calculateTotalDiscount(order.payments);
66
- const totalWithDiscount = total - Number(totalDiscount);
66
+
67
67
  let summaryItems: SummaryItem[] = [
68
68
  {
69
69
  translateKey: AMOUNT_TOTAL,
@@ -74,7 +74,7 @@ const processedPaymentSummary = ({
74
74
  if (totalDiscount) {
75
75
  summaryItems.push({
76
76
  translateKey: AMOUNT_WITH_DISCOUNT,
77
- price: totalWithDiscount < 0 ? 0 : totalWithDiscount,
77
+ price: order.totalToPayAmount,
78
78
  });
79
79
  }
80
80
 
@@ -84,17 +84,17 @@ const processedPaymentSummary = ({
84
84
 
85
85
  summaryItems.push({
86
86
  translateKey: TOTAL_PAID,
87
- price: totalWithDiscount <= 0 ? 0 : order.totalAmount,
87
+ price: order.paidAmount,
88
88
  });
89
- if (totalWithDiscount <= 0) {
89
+ if (order.totalToPayAmount <= 0) {
90
90
  return summaryItems;
91
91
  }
92
92
  summaryItems = [...summaryItems, ...externalPaymentDetail];
93
93
 
94
- if (totalWithDiscount > order.totalAmount) {
94
+ if (order.remainingToPay) {
95
95
  summaryItems.push({
96
96
  translateKey: REMAINING_TO_PAY,
97
- price: totalWithDiscount - order.totalAmount,
97
+ price: order.remainingToPay,
98
98
  });
99
99
  }
100
100
  return summaryItems;
@@ -123,18 +123,26 @@ export const RyftPaymentForm = ({
123
123
  };
124
124
 
125
125
  useEffect(
126
- () =>
126
+ () => {
127
+ if (
128
+ !formRef.current?.childNodes?.length ||
129
+ formRef.current?.childNodes?.length < 3 ||
130
+ !accountId.data?.account_id
131
+ ) {
132
+ return;
133
+ }
127
134
  injectComponentsInRyftForm({
128
135
  t,
129
136
  additionalPaymentMethodsEnabled:
130
137
  providerPaymentSettings?.additionalPaymentMethodsEnabled,
131
- }),
138
+ });
139
+ },
132
140
  // eslint-disable-next-line react-hooks/exhaustive-deps
133
141
  [formRef.current?.childNodes?.length]
134
142
  );
135
143
 
136
144
  useEffect(() => {
137
- if (!accountId.data || accountId.isError) return;
145
+ if (!accountId.data?.account_id || accountId.isError) return;
138
146
 
139
147
  initRyft(
140
148
  () => {
@@ -148,7 +156,7 @@ export const RyftPaymentForm = ({
148
156
  );
149
157
 
150
158
  // eslint-disable-next-line react-hooks/exhaustive-deps
151
- }, [accountId.data]);
159
+ }, [accountId.data?.account_id]);
152
160
 
153
161
  if (accountId.isLoading || accountId.isFetching || isCreateLoading)
154
162
  return (
@@ -8,13 +8,12 @@ export const injectComponentsInRyftForm = ({
8
8
  additionalPaymentMethodsEnabled?: boolean;
9
9
  }) => {
10
10
  const cardContainer = document.getElementById("ryft-pay-iframe");
11
- const oldCardDividerContainer = document.getElementById(
12
- "card-title-container"
13
- );
14
11
 
15
- if (!cardContainer || oldCardDividerContainer) {
12
+ if (!cardContainer) {
16
13
  return;
17
14
  }
15
+ // Removing of the old title container
16
+ document.getElementById("card-title-container")?.remove();
18
17
 
19
18
  const cardTitle = document.createElement("div");
20
19
  const cardText = document.createElement("div");
@@ -32,6 +31,10 @@ export const injectComponentsInRyftForm = ({
32
31
  return;
33
32
  }
34
33
 
34
+ // Removing of the old mobile title container and divider container
35
+ document.getElementById("mobile-pay-title")?.remove();
36
+ document.getElementById("mobile-pay-divider-container")?.remove();
37
+
35
38
  // Adding title and divider for apple and google pay button
36
39
  const mobilePayTitle = document.createElement("div");
37
40
  const dividerText = document.createElement("div");
@@ -39,6 +42,7 @@ export const injectComponentsInRyftForm = ({
39
42
  const container = document.createElement("div");
40
43
  mobilePayTitle.className = "mobile-pay-title";
41
44
  mobilePayTitle.innerText = t("Design:expressCheckoutWith");
45
+ mobilePayTitle.id = "mobile-pay-title";
42
46
  dividerText.id = "mobile-pay-divider-text";
43
47
  dividerText.innerText = "or";
44
48
  divider.id = "mobile-pay-divider";
@@ -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
  }
@@ -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];
@@ -307,7 +307,7 @@ export function ProductCategoriesControl({
307
307
  title={t("addCategory")}
308
308
  isOverride={isOverrides}
309
309
  onClick={() => {
310
- if (isOverrides) return;
310
+ if (isOverrides) return;
311
311
  setIsSelectCategoryVisible(true);
312
312
  }}
313
313
  />
@@ -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;
@@ -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
 
@@ -7,14 +7,17 @@ import {
7
7
  QUANTITY_TYPE_LIST_DTO,
8
8
  QUANTITY_TYPE_RECHARGING,
9
9
  } from "@licklist/core/dist/DataMapper/Product/ProductCategoryDataMapper";
10
- import { ProductType } from "@licklist/core/dist/DataMapper/Product/ProductDataMapper";
10
+ import {
11
+ ProductType,
12
+ PRODUCT_DEfAULT_COLORS,
13
+ } from "@licklist/core/dist/DataMapper/Product/ProductDataMapper";
11
14
  import HookFormService from "@licklist/plugins/dist/services/Form/HookFormService";
15
+ import clsx from "clsx";
12
16
  import {
13
17
  FieldNamePrefixPath,
14
18
  FormValues,
15
19
  } from "@licklist/plugins/dist/types/services/Form/hook-form-service";
16
20
  import { useId } from "@react-aria/utils";
17
- import clsx from "clsx";
18
21
  import React, {
19
22
  ChangeEvent,
20
23
  useCallback,
@@ -36,7 +39,7 @@ import {
36
39
  } from "react-hook-form";
37
40
  import { useTranslation } from "react-i18next";
38
41
  import { useImages } from "@licklist/plugins/dist/hooks/Media/useImages";
39
- import { PRODUCT_DEfAULT_COLORS } from "@licklist/core/dist/DataMapper/Product/ProductDataMapper";
42
+
40
43
  import {
41
44
  Image,
42
45
  IMAGE_TYPE_IMAGE,
@@ -146,7 +149,7 @@ export function ProductControl<T extends FormValues>({
146
149
  clearErrors,
147
150
  } = useFormContext<T>();
148
151
 
149
- const { setLoading } = useContext(ProductSetLoadingContext);
152
+ const { setLoading, productGroupList } = useContext(ProductSetLoadingContext);
150
153
  const { t } = useTranslation(["Design", "Validation", "ProductSet"]);
151
154
  const [expanded, setExpanded] = useState(false);
152
155
  const [initialImages, setInitialImages] = useState<Image[] | null>(null);
@@ -159,8 +162,8 @@ export function ProductControl<T extends FormValues>({
159
162
 
160
163
  const advancedId = useId();
161
164
  const nameId = useId();
162
- // @TODO: no need for v1 release
163
- // const productGroupId = useId();
165
+
166
+ const productGroupId = useId();
164
167
  const descriptionId = useId();
165
168
  const termsAndConditionsId = useId();
166
169
  const isAvailableId = useId();
@@ -400,6 +403,35 @@ export function ProductControl<T extends FormValues>({
400
403
  isRequired={false}
401
404
  defaultColors={PRODUCT_DEfAULT_COLORS}
402
405
  />
406
+
407
+ <Form.Group controlId={productGroupId}>
408
+ <Form.Label>{t("productGroup")}</Form.Label>
409
+ <Form.Control
410
+ as="select"
411
+ {...register(`${fieldNamePrefix}.productGroupId` as Path<T>)}
412
+ isInvalid={HookFormService.isInvalid<T>(
413
+ `${fieldNamePrefix}.productGroupId` as Path<T>,
414
+ errors
415
+ )}
416
+ disabled={isLoading}
417
+ defaultValue=""
418
+ >
419
+ <option value="">{t("Design:selectProductGroup")}</option>
420
+ {productGroupList.map((productGroup) => {
421
+ return (
422
+ <option value={productGroup.id} key={productGroup.id}>
423
+ {productGroup.value}
424
+ </option>
425
+ );
426
+ })}
427
+ </Form.Control>
428
+ <Form.Control.Feedback type="invalid">
429
+ {HookFormService.getErrors<T>(
430
+ `${fieldNamePrefix}.productGroupId` as Path<T>,
431
+ errors
432
+ )}
433
+ </Form.Control.Feedback>
434
+ </Form.Group>
403
435
  </Col>
404
436
  </Row>
405
437
 
@@ -672,38 +704,6 @@ export function ProductControl<T extends FormValues>({
672
704
  </Collapse>
673
705
 
674
706
  <Row>
675
- {/* @TODO: no need for v1 release
676
- <Col xs={12} sm={6}>
677
- <Form.Group controlId={productGroupId}>
678
- <Form.Label>{t("productGroup")}</Form.Label>
679
- <Form.Control
680
- as="select"
681
- {...register(`${fieldNamePrefix}.productGroupId` as Path<T>)}
682
- isInvalid={HookFormService.isInvalid<T>(
683
- `${fieldNamePrefix}.productGroupId` as Path<T>,
684
- errors
685
- )}
686
- disabled={isLoading}
687
- defaultValue=""
688
- >
689
- <option value="">{t("Design:selectProductGroup")}</option>
690
- {productGroupList.map((productGroup) => {
691
- return (
692
- <option value={productGroup.id} key={productGroup.id}>
693
- {productGroup.value}
694
- </option>
695
- );
696
- })}
697
- </Form.Control>
698
- <Form.Control.Feedback type="invalid">
699
- {HookFormService.getErrors<T>(
700
- `${fieldNamePrefix}.productGroupId` as Path<T>,
701
- errors
702
- )}
703
- </Form.Control.Feedback>
704
- </Form.Group>
705
- </Col> */}
706
-
707
707
  <Col xs={12}>
708
708
  {hasBookingManagement && (
709
709
  <ProductBookingManagementControl<T>
@@ -5,8 +5,6 @@ import { FieldNamePrefixPath } from "@licklist/plugins/dist/types/services/Form/
5
5
  import { useWatch } from "react-hook-form";
6
6
  import { FormNumberInput } from "../../../static";
7
7
 
8
-
9
-
10
8
  interface FixedDurationOptionsProps<T> extends FieldNamePrefixPath<T> {
11
9
  isOverrides?: boolean;
12
10
  }
@@ -1,9 +1,7 @@
1
- import { DateTime } from "luxon";
2
1
  import React from "react";
3
2
  import { Col, Row } from "react-bootstrap";
4
3
  import { useFormContext } from "react-hook-form";
5
4
  import { useTranslation } from "react-i18next";
6
- import { TIME_FORMAT } from "@licklist/core/dist/Config";
7
5
  import { WorkingHoursInputValues } from ".";
8
6
  import { useWeekdays } from "./utils";
9
7
 
@@ -25,22 +23,10 @@ export function WorkingHoursInputDescription() {
25
23
  <Row key={i}>
26
24
  <Col xs="auto">{weekdays[i]}:</Col>
27
25
  <Col>
28
- {weekday?.end
29
- ? weekday.start
30
- ? t("timeInterval", {
31
- start: DateTime.fromISO(weekday.start).toFormat(
32
- TIME_FORMAT
33
- ),
34
- end: DateTime.fromISO(weekday.start).toFormat(
35
- TIME_FORMAT
36
- ),
37
- })
38
- : t("timeFrom", {
39
- start: DateTime.fromISO(weekday.start).toFormat(
40
- TIME_FORMAT
41
- ),
42
- })
43
- : null}
26
+ {t("timeInterval", {
27
+ start: weekday.start,
28
+ end: weekday.end,
29
+ })}
44
30
  </Col>
45
31
  </Row>
46
32
  );
@@ -72,7 +72,6 @@ function RecurrenceAndFrequencyInput({
72
72
  </Form.Control.Feedback>
73
73
  </Form.Group>
74
74
  </Col>
75
-
76
75
  </Row>
77
76
 
78
77
  <Row>