@licklist/design 0.63.2 → 0.63.3-dev.1

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 (153) 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/SelectEventProductSet/SelectEventProductSet.d.ts.map +1 -1
  4. package/dist/events/edit-event-modal/component/SelectEventProductSet/SelectEventProductSet.js +1 -1
  5. package/dist/events/edit-event-modal/component/SelectEventProductSet/component/EditEventProductSet.d.ts.map +1 -1
  6. package/dist/events/edit-event-modal/component/SelectEventProductSet/component/EditEventProductSet.js +1 -1
  7. package/dist/iframe/payment/order-items-table/utils/paymentSummary.d.ts.map +1 -1
  8. package/dist/iframe/ryft/RyftPaymentForm.d.ts +4 -1
  9. package/dist/iframe/ryft/RyftPaymentForm.d.ts.map +1 -1
  10. package/dist/iframe/ryft/RyftPaymentForm.js +1 -1
  11. package/dist/iframe/ryft/utils/ryft-form.d.ts +5 -2
  12. package/dist/iframe/ryft/utils/ryft-form.d.ts.map +1 -1
  13. package/dist/iframe/ryft/utils/ryft-form.js +1 -1
  14. package/dist/product-set/control/DateAndRecurrenceInput.d.ts +1 -1
  15. package/dist/product-set/control/DateAndRecurrenceInput.d.ts.map +1 -1
  16. package/dist/product-set/control/DateAndRecurrenceInput.js +1 -1
  17. package/dist/product-set/control/DateInput.js +1 -1
  18. package/dist/product-set/control/ProductSetControl.d.ts +3 -2
  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/form/ProductCategoriesControl.d.ts +3 -1
  22. package/dist/product-set/form/ProductCategoriesControl.d.ts.map +1 -1
  23. package/dist/product-set/form/ProductCategoriesControl.js +1 -1
  24. package/dist/product-set/form/ProductSetForm.d.ts +6 -2
  25. package/dist/product-set/form/ProductSetForm.d.ts.map +1 -1
  26. package/dist/product-set/form/ProductSetForm.js +1 -1
  27. package/dist/product-set/form/ProductsControl.d.ts +3 -1
  28. package/dist/product-set/form/ProductsControl.d.ts.map +1 -1
  29. package/dist/product-set/form/ProductsControl.js +1 -1
  30. package/dist/product-set/form/StepsControl.d.ts +3 -1
  31. package/dist/product-set/form/StepsControl.d.ts.map +1 -1
  32. package/dist/product-set/form/StepsControl.js +1 -1
  33. package/dist/product-set/product/ProductControl.d.ts +1 -0
  34. package/dist/product-set/product/ProductControl.d.ts.map +1 -1
  35. package/dist/product-set/product/ProductControl.js +1 -1
  36. package/dist/product-set/step/StepControl.d.ts +2 -1
  37. package/dist/product-set/step/StepControl.d.ts.map +1 -1
  38. package/dist/product-set/step/StepControl.js +1 -1
  39. package/dist/provider/working-hours-input/WorkingHoursInputDescription.d.ts.map +1 -1
  40. package/dist/provider/working-hours-input/WorkingHoursInputDescription.js +1 -1
  41. package/dist/recurring-date-picker-input/RecurrenceIntervalAndFrequencyInput.d.ts.map +1 -1
  42. package/dist/recurring-date-picker-input/RecurrenceIntervalAndFrequencyInput.js +1 -1
  43. package/dist/recurring-date-picker-input/RecurringDatePickerInput.d.ts +3 -1
  44. package/dist/recurring-date-picker-input/RecurringDatePickerInput.d.ts.map +1 -1
  45. package/dist/recurring-date-picker-input/RecurringDatePickerInput.js +1 -1
  46. package/dist/recurring-date-picker-input/utils.d.ts +5 -0
  47. package/dist/recurring-date-picker-input/utils.d.ts.map +1 -1
  48. package/dist/recurring-date-picker-input/utils.js +1 -1
  49. package/dist/sales/booking/results/components/ResultCard.d.ts.map +1 -1
  50. package/dist/sales/coupon/control/CouponFormControl.d.ts +2 -1
  51. package/dist/sales/coupon/control/CouponFormControl.d.ts.map +1 -1
  52. package/dist/sales/coupon/control/CouponFormControl.js +1 -1
  53. package/dist/sales/coupon/form/CouponFrom.d.ts +2 -2
  54. package/dist/sales/coupon/form/CouponFrom.d.ts.map +1 -1
  55. package/dist/sales/coupon/utils/index.d.ts +7 -0
  56. package/dist/sales/coupon/utils/index.d.ts.map +1 -0
  57. package/dist/sales/coupon/utils/index.js +1 -0
  58. package/dist/setting/admin/AdminSettingForm.d.ts +2 -2
  59. package/dist/setting/admin/AdminSettingForm.d.ts.map +1 -1
  60. package/dist/sortable-tree/SortableTreeItem.d.ts +1 -2
  61. package/dist/sortable-tree/SortableTreeItem.d.ts.map +1 -1
  62. package/dist/sortable-tree/SortableTreeItem.js +1 -1
  63. package/dist/styles/iframe-page/PageBody.scss +0 -1
  64. package/dist/styles/ryft-payment-form/RyftPaymentForm.scss +8 -0
  65. package/dist/styles/sales/BookingResults.scss +1 -1
  66. package/dist/styles/zones/ZoneForm.scss +2 -1
  67. package/dist/typeahead/Typeahead.d.ts +2 -1
  68. package/dist/typeahead/Typeahead.d.ts.map +1 -1
  69. package/dist/typeahead/Typeahead.js +1 -1
  70. package/dist/zone/form/ZoneForm.d.ts +2 -2
  71. package/dist/zone/form/ZoneForm.d.ts.map +1 -1
  72. package/dist/zone/form/ZoneForm.js +1 -1
  73. package/dist/zone/form/components/ZoneControl.d.ts +2 -2
  74. package/dist/zone/form/components/ZoneControl.d.ts.map +1 -1
  75. package/dist/zone/form/components/ZoneControl.js +1 -1
  76. package/dist/zone/form/components/ZoneRecurrencesControl.d.ts +4 -4
  77. package/dist/zone/form/components/ZoneRecurrencesControl.d.ts.map +1 -1
  78. package/dist/zone/form/components/ZoneRecurrencesControl.js +1 -1
  79. package/dist/zone/form/utils/dates.d.ts.map +1 -1
  80. package/package.json +12 -37
  81. package/src/date-time-button/DateTimeButton.stories.tsx +2 -1
  82. package/src/date-time-button/DateTimeButton.tsx +7 -5
  83. package/src/events/edit-event-modal/component/SelectEventProductSet/SelectEventProductSet.tsx +14 -9
  84. package/src/events/edit-event-modal/component/SelectEventProductSet/component/EditEventProductSet.tsx +2 -0
  85. package/src/iframe/payment/order-items-table/utils/paymentSummary.tsx +1 -0
  86. package/src/iframe/ryft/RyftPaymentForm.tsx +11 -2
  87. package/src/iframe/ryft/utils/ryft-form.ts +23 -4
  88. package/src/product-set/control/DateAndRecurrenceInput.tsx +3 -4
  89. package/src/product-set/control/DateInput.tsx +2 -2
  90. package/src/product-set/control/ProductSetControl.tsx +11 -4
  91. package/src/product-set/form/ProductCategoriesControl.tsx +15 -4
  92. package/src/product-set/form/ProductSetForm.tsx +13 -2
  93. package/src/product-set/form/ProductsControl.tsx +93 -77
  94. package/src/product-set/form/StepsControl.tsx +15 -7
  95. package/src/product-set/product/ProductControl.tsx +39 -39
  96. package/src/product-set/step/StepControl.tsx +4 -3
  97. package/src/provider/working-hours-input/WorkingHoursInputDescription.tsx +4 -18
  98. package/src/recurring-date-picker-input/RecurrenceIntervalAndFrequencyInput.tsx +40 -21
  99. package/src/recurring-date-picker-input/RecurringDatePickerInput.stories.tsx +13 -7
  100. package/src/recurring-date-picker-input/RecurringDatePickerInput.tsx +12 -1
  101. package/src/recurring-date-picker-input/utils.ts +90 -1
  102. package/src/sales/booking/results/components/ResultCard.tsx +0 -3
  103. package/src/sales/coupon/control/CouponFormControl.tsx +28 -51
  104. package/src/sales/coupon/form/CouponFrom.tsx +5 -15
  105. package/src/sales/coupon/utils/index.ts +13 -0
  106. package/src/setting/admin/AdminSettingForm.tsx +2 -2
  107. package/src/sortable-tree/SortableTreeItem.tsx +0 -3
  108. package/src/static/switch/BooleanSwitch.tsx +1 -1
  109. package/src/styles/iframe-page/PageBody.scss +0 -1
  110. package/src/styles/ryft-payment-form/RyftPaymentForm.scss +8 -0
  111. package/src/styles/sales/BookingResults.scss +1 -1
  112. package/src/styles/zones/ZoneForm.scss +2 -1
  113. package/src/typeahead/Typeahead.tsx +16 -3
  114. package/src/zone/form/ZoneForm.tsx +3 -2
  115. package/src/zone/form/components/ZoneControl.tsx +3 -3
  116. package/src/zone/form/components/ZoneRecurrencesControl.tsx +7 -5
  117. package/src/zone/form/utils/dates.ts +9 -10
  118. package/jest.config.js +0 -29
  119. package/tests/Auth/Authorizer.test.tsx +0 -194
  120. package/tests/Auth/Layout/UserNavDropDown.test.tsx +0 -43
  121. package/tests/Auth/Layout/UserNavDropDownToggle.test.tsx +0 -33
  122. package/tests/Auth/Login/LoginComponent.test.tsx +0 -246
  123. package/tests/Auth/Login/LoginFormComponent.test.tsx +0 -182
  124. package/tests/Auth/Register/RegisterComponent.test.tsx +0 -285
  125. package/tests/Auth/Register/RegisterFormComponent.test.tsx +0 -170
  126. package/tests/Auth/Settings/Dashboard/IpInput.test.tsx +0 -130
  127. package/tests/Auth/Social/SocialCallbackComponent.test.tsx +0 -133
  128. package/tests/Auth/Social/SocialFormComponent.test.tsx +0 -118
  129. package/tests/FileUpload/FileUpload.test.tsx +0 -42
  130. package/tests/Notification/EmailTemplate.test.tsx +0 -82
  131. package/tests/ProductSet/ProductSetPopover.test.tsx +0 -40
  132. package/tests/Report/Report.test.tsx +0 -48
  133. package/tests/Sales/Coupon.test.tsx +0 -51
  134. package/tests/Sales/SalesAndVIews.test.tsx +0 -63
  135. package/tests/SnippetTemplates/SnippetTemplates.test.tsx +0 -56
  136. package/tests/Table/FilterHelperComponent.test.tsx +0 -88
  137. package/tests/Table/PaginationHelperComponent.test.tsx +0 -109
  138. package/tests/Table/PerPageHelperComponent.test.tsx +0 -34
  139. package/tests/Table/TableHelperComponent.test.tsx +0 -295
  140. package/tests/TipTapEditor/TipTapEditor.test.tsx +0 -28
  141. package/tests/__mock__/hooks/useAuthApi.ts +0 -13
  142. package/tests/__mock__/hooks/useAuthMock.ts +0 -13
  143. package/tests/__mock__/hooks/useFormMock.ts +0 -27
  144. package/tests/__mock__/hooks/useNotificationMock.ts +0 -13
  145. package/tests/__mock__/hooks/useQueryMock.ts +0 -16
  146. package/tests/__mock__/hooks/useSocialApiMock.ts +0 -20
  147. package/tests/__mock__/hooks/useTranslationMock.ts +0 -17
  148. package/tests/__mock__/hooks/useUserApiMock.ts +0 -18
  149. package/tests/__mock__/hooks/useUserMock.ts +0 -13
  150. package/tests/__mock__/styleMock.js +0 -1
  151. package/tests/__mock__/windowMock.ts +0 -5
  152. package/tests/packages/react-query.tsx +0 -28
  153. package/tests/setupTests.ts +0 -10
@@ -2,8 +2,18 @@ import React, { useEffect } from "react";
2
2
  import { DateTime } from "luxon";
3
3
  import { Col, Form, Row } from "react-bootstrap";
4
4
  import { useTranslation } from "react-i18next";
5
- import { RegisterOptions, useFormContext } from "react-hook-form";
6
- import { DATE_FORMAT } from "@licklist/core/dist/Config";
5
+ import {
6
+ RegisterOptions,
7
+ useFormContext,
8
+ useWatch,
9
+ Validate,
10
+ } from "react-hook-form";
11
+ import {
12
+ DATE_FORMAT,
13
+ TIME_FORMAT,
14
+ TIME_FULL_FORMAT,
15
+ } from "@licklist/core/dist/Config";
16
+ import { fromFormat } from "@licklist/plugins/dist/utils/dateTime";
7
17
  import { RecurringDatePickerInputValues } from "./RecurringDatePickerInput";
8
18
 
9
19
  interface RecurrenceIntervalAndFrequencyInputProps {
@@ -23,6 +33,8 @@ interface RecurrenceIntervalAndFrequencyInputProps {
23
33
  >;
24
34
  }
25
35
 
36
+ const fromTimeFullFormat = fromFormat(TIME_FULL_FORMAT);
37
+
26
38
  function RecurrenceIntervalAndFrequencyInput({
27
39
  disabled = false,
28
40
  minDate,
@@ -36,16 +48,20 @@ function RecurrenceIntervalAndFrequencyInput({
36
48
  const { t } = useTranslation(["Design", "Notification", "App"]);
37
49
 
38
50
  const {
39
- getValues,
40
51
  formState: { errors },
41
52
  register,
42
53
  trigger,
54
+ control,
43
55
  } = useFormContext<RecurringDatePickerInputValues>();
44
56
 
45
- const startDate = getValues("startDate");
46
- const endDate = getValues("endDate");
47
- const startTime = getValues("startTime");
48
- const endTime = getValues("endTime");
57
+ const startDate = useWatch({ control, name: "startDate" });
58
+ const endDate = useWatch({ control, name: "endDate" });
59
+
60
+ const _startTime = useWatch({ control, name: "startTime" });
61
+ const startTime = fromTimeFullFormat(_startTime).toFormat(TIME_FORMAT);
62
+
63
+ const _endTime = useWatch({ control, name: "endTime" });
64
+ const endTime = fromTimeFullFormat(_endTime).toFormat(TIME_FORMAT);
49
65
 
50
66
  useEffect(() => {
51
67
  if (!startDate || !endDate) {
@@ -149,22 +165,25 @@ function RecurrenceIntervalAndFrequencyInput({
149
165
  type="time"
150
166
  {...register("endTime", {
151
167
  required: false,
152
- validate: (time) => {
153
- if (!startTime || !time) {
154
- return true;
155
- }
156
- const currentStartTime = DateTime.fromISO(startTime);
157
- const currentEndTime = DateTime.fromISO(time);
168
+ ...endTimeRules,
169
+ validate: {
170
+ external: endTimeRules.validate as Validate<string>,
171
+ time: (time) => {
172
+ if (!startTime || !time) {
173
+ return true;
174
+ }
175
+ const currentStartTime = DateTime.fromISO(startTime);
176
+ const currentEndTime = DateTime.fromISO(time);
158
177
 
159
- return currentEndTime?.diff(currentStartTime, ["minutes"])
160
- ?.minutes >= 0
161
- ? true
162
- : `${t(`Validation:fieldLaterDate`, {
163
- attribute: t("Design:endTimeSmall"),
164
- min: t("Design:startTimeSmall"),
165
- })}`;
178
+ return currentEndTime?.diff(currentStartTime, ["minutes"])
179
+ ?.minutes >= 0
180
+ ? true
181
+ : `${t(`Validation:fieldLaterDate`, {
182
+ attribute: t("Design:endTimeSmall"),
183
+ min: t("Design:startTimeSmall"),
184
+ })}`;
185
+ },
166
186
  },
167
- ...endTimeRules,
168
187
  })}
169
188
  placeholder="hh:mm"
170
189
  defaultValue={endTime}
@@ -1,22 +1,19 @@
1
1
  import React from "react";
2
2
  import { Meta, Story } from "@storybook/react";
3
3
  import { Card } from "react-bootstrap";
4
+ import { DateTime } from "luxon";
5
+ import { DATE_FORMAT, TIME_FORMAT } from "@licklist/core/dist/Config/Date";
4
6
  import {
5
7
  RecurringDatePickerInput,
6
8
  RecurringDatePickerInputProps,
7
9
  } from "./RecurringDatePickerInput";
8
- import { DateTime } from "luxon";
9
- import { DATE_FORMAT, TIME_FORMAT } from "@licklist/core/dist/Config/Date";
10
10
 
11
11
  export default {
12
12
  title: "RecurringDatePickerInput",
13
13
  component: RecurringDatePickerInput,
14
14
  } as Meta;
15
15
 
16
- interface DefaultRecurringDatePickerInputProps
17
- extends RecurringDatePickerInputProps {}
18
-
19
- export const Default: Story<DefaultRecurringDatePickerInputProps> = (args) => {
16
+ export const Default: Story<RecurringDatePickerInputProps> = (args) => {
20
17
  return (
21
18
  <Card style={{ maxWidth: "max-content" }}>
22
19
  <Card.Body>
@@ -25,7 +22,7 @@ export const Default: Story<DefaultRecurringDatePickerInputProps> = (args) => {
25
22
  </Card>
26
23
  );
27
24
  };
28
- export const Valid: Story<DefaultRecurringDatePickerInputProps> = (args) => {
25
+ export const Valid: Story<RecurringDatePickerInputProps> = (args) => {
29
26
  return (
30
27
  <Card style={{ maxWidth: "max-content" }}>
31
28
  <Card.Body>
@@ -36,6 +33,15 @@ export const Valid: Story<DefaultRecurringDatePickerInputProps> = (args) => {
36
33
  };
37
34
  Default.args = {
38
35
  onChange: (data) => console.log("onSubmit", data),
36
+ workHours: [
37
+ { start: "20:00", end: "04:00", day: 0, description: "" },
38
+ { start: "20:00", end: "04:00", day: 1, description: "" },
39
+ { start: "20:00", end: "04:00", day: 2, description: "" },
40
+ { start: "20:00", end: "12:00", day: 3, description: "" },
41
+ { start: "20:00", end: "23:00", day: 4, description: "" },
42
+ { start: "08:00", end: "12:00", day: 5, description: "" },
43
+ { start: "10:00", end: "16:00", day: 6, description: "" },
44
+ ],
39
45
  };
40
46
  Valid.args = {
41
47
  onChange: (data) => console.log("onSubmit", data),
@@ -12,9 +12,14 @@ import {
12
12
  import { usePreviousValue } from "@licklist/plugins/dist/hooks/Value/usePreviousValue";
13
13
  import { FormProvider, useForm } from "react-hook-form";
14
14
  import { Form } from "react-bootstrap";
15
+ import { WorkHour } from "@licklist/core/dist/DataMapper/Provider/WorkHourDataMapper";
15
16
  import RecurrenceIntervalAndFrequencyInput from "./RecurrenceIntervalAndFrequencyInput";
16
17
  import RecurrenceWeekdaysInput from "./RecurrenceWeekdaysInput";
17
- import { parseAndValidateRRule, SupportedFrequency } from "./utils";
18
+ import {
19
+ parseAndValidateRRule,
20
+ SupportedFrequency,
21
+ useWorkHoursValidation,
22
+ } from "./utils";
18
23
  import { ConfirmModal } from "../modals";
19
24
  import { DeleteFieldButton } from "../product-set/elements";
20
25
 
@@ -26,6 +31,7 @@ export interface RecurringDatePickerInputProps {
26
31
  initialFrequency?: Frequency;
27
32
  setInitialStartDateAfterSelect?: boolean;
28
33
  minDate?: string;
34
+ workHours: WorkHour[] | undefined;
29
35
  }
30
36
 
31
37
  export interface RecurringDatePickerInputValues {
@@ -51,6 +57,7 @@ export function RecurringDatePickerInput({
51
57
  setInitialStartDateAfterSelect = false,
52
58
  children,
53
59
  minDate,
60
+ workHours,
54
61
  }: PropsWithChildren<RecurringDatePickerInputProps>) {
55
62
  const { t } = useTranslation(["Design"]);
56
63
 
@@ -84,6 +91,8 @@ export function RecurringDatePickerInput({
84
91
  byWeekDay,
85
92
  });
86
93
 
94
+ const validator = useWorkHoursValidation(byWeekDay, workHours);
95
+
87
96
  const onSubmit = (nextState: RecurringDatePickerInputValues) => {
88
97
  const end = getDateTimeObject(endDate, endTime || "23:59:59");
89
98
 
@@ -188,6 +197,8 @@ export function RecurringDatePickerInput({
188
197
  <RecurrenceIntervalAndFrequencyInput
189
198
  disabled={disabled}
190
199
  minDate={minDate}
200
+ startTimeRules={validator}
201
+ endTimeRules={validator}
191
202
  />
192
203
 
193
204
  {children}
@@ -1,4 +1,9 @@
1
- import { DateTime } from "luxon";
1
+ import { TIME_FORMAT, TIME_FULL_FORMAT } from "@licklist/core/dist/Config";
2
+ import { WorkHour } from "@licklist/core/dist/DataMapper/Provider/WorkHourDataMapper";
3
+ import { fromFormat } from "@licklist/plugins/dist/utils/dateTime";
4
+ import { DateTime, Interval } from "luxon";
5
+ import { Validate } from "react-hook-form";
6
+ import { useTranslation } from "react-i18next";
2
7
  import RRule, { Frequency, Weekday } from "rrule";
3
8
 
4
9
  export const getWeekdayForFrequency = ({
@@ -11,6 +16,7 @@ export const getWeekdayForFrequency = ({
11
16
  const parsedDate = DateTime.fromISO(date);
12
17
  return new Weekday(
13
18
  parsedDate.weekday - 1,
19
+ // eslint-disable-next-line no-nested-ternary
14
20
  frequency !== Frequency.MONTHLY
15
21
  ? undefined
16
22
  : parsedDate.day + 7 > parsedDate.daysInMonth
@@ -109,3 +115,86 @@ export const parseAndValidateRRule = ({
109
115
  until: options.until,
110
116
  } as ParsedRRuleOptions;
111
117
  };
118
+
119
+ export const useWorkHoursValidation = (
120
+ byWeekDay: Weekday[] = [],
121
+ workHours: WorkHour[] | undefined
122
+ ): {
123
+ validate?: Validate<string>;
124
+ } => {
125
+ const { t } = useTranslation("Validation");
126
+ if (!workHours) return {};
127
+
128
+ const fromTimeFormat = fromFormat(TIME_FORMAT);
129
+ const fromTimeFullFormat = fromFormat(TIME_FULL_FORMAT);
130
+
131
+ // map every day to array of intervals describing workhours for that day
132
+ let intervalsByDay = workHours.map((workhour, index, self) => {
133
+ const res: Interval[] = [];
134
+
135
+ const startTime = fromTimeFormat(workhour.start);
136
+ const endTime = fromTimeFormat(workhour.end);
137
+
138
+ if (endTime > startTime) {
139
+ res.push(Interval.fromDateTimes(startTime, endTime));
140
+ } else {
141
+ // if end is before start, then it means that end is on the next day
142
+ // so count only to the end of current day
143
+ res.push(Interval.fromDateTimes(startTime, startTime.endOf("day")));
144
+ }
145
+
146
+ const prevIndex = index === 0 ? self.length - 1 : index - 1;
147
+
148
+ const prevStartTime = fromTimeFormat(self[prevIndex].start);
149
+ const prevEndTime = fromTimeFormat(self[prevIndex].end);
150
+
151
+ // if prevStartTime > prevEndTime then it means that prevEndTime is on the current day
152
+ // so we need to add interval from start of current day to prevEndTime
153
+ if (prevStartTime > prevEndTime) {
154
+ res.push(Interval.fromDateTimes(prevEndTime.startOf("day"), prevEndTime));
155
+ }
156
+
157
+ return res;
158
+ });
159
+
160
+ // if length === 0 or length === 7 then all workhours are selected, no need to filter
161
+ if (![0, 7].includes(byWeekDay.length)) {
162
+ intervalsByDay = intervalsByDay.filter((_, index) =>
163
+ byWeekDay.find(({ weekday }) => weekday === index)
164
+ );
165
+ }
166
+
167
+ return {
168
+ validate: (_time: string) => {
169
+ // workaround for issue with form values
170
+ // default values and entered values have different formats
171
+ const time = fromTimeFormat(_time).isValid
172
+ ? fromTimeFormat(_time)
173
+ : fromTimeFullFormat(_time);
174
+
175
+ const notFittingIntervalsByDay = intervalsByDay.reduce<Interval[][]>(
176
+ (acc, intervalsByDay) => {
177
+ if (!intervalsByDay.some((interval) => interval.contains(time))) {
178
+ acc.push(intervalsByDay);
179
+ }
180
+ return acc;
181
+ },
182
+ []
183
+ );
184
+
185
+ const notFittingIntervals = notFittingIntervalsByDay
186
+ .flat()
187
+ .filter((interval) => !interval.contains(time));
188
+
189
+ const attribute = notFittingIntervals
190
+ .map((interval) => interval.toFormat("HH:mm"))
191
+ .join(", ");
192
+
193
+ if (notFittingIntervalsByDay.length !== 0) {
194
+ return t("outsideWorkingHours", { attribute }) as string;
195
+ }
196
+
197
+ return true;
198
+ },
199
+ };
200
+ };
@@ -12,7 +12,6 @@ import { TIME_FORMAT } from "@licklist/core/dist/Config";
12
12
  import * as Config from "@licklist/core/dist/Config";
13
13
  import { formatDateStringForEvent } from "@licklist/plugins/dist/utils/formatDate";
14
14
  import { getOrderSourceTitle } from "@licklist/plugins/dist/utils/sourceTitle";
15
-
16
15
  import Icon from "../../../../static/Icon";
17
16
 
18
17
  export type ResultCardProps = {
@@ -22,8 +21,6 @@ export type ResultCardProps = {
22
21
  onCardClick?: (id: number) => void;
23
22
  };
24
23
 
25
- // @TODO integrate when API will be available
26
- // activity and staff keys
27
24
  export const ResultCard = ({
28
25
  order,
29
26
  className,
@@ -21,12 +21,14 @@ import couponCodeRules from "@licklist/plugins/dist/validation/Rules/couponCodeR
21
21
  // import { useId } from "@react-aria/utils";
22
22
 
23
23
  import HTMLInputDateElement from "../../../types/static/HTMLInputDateElement";
24
+ import { Typeahead, TypeaheadOptions } from "../../../typeahead";
25
+ import { convertToTypeaheadOptions } from "../utils";
24
26
 
25
27
  export interface CouponFormValues {
26
28
  name?: string;
27
29
  code: string;
28
30
  discountType: CouponType;
29
- productGroupIds: string[];
31
+ productGroupIds: Array<TypeaheadOptions | ProductGroup>;
30
32
  discount: number;
31
33
  totalType: CouponTotalType;
32
34
  startFrom: string;
@@ -58,9 +60,9 @@ export const CouponFormControl = ({
58
60
  watch,
59
61
  } = useFormContext<CouponFormValues>();
60
62
  const { t } = useTranslation(["Design", "Notification", "App"]);
61
- const [selectedProductGroups, setSelectedProductGroups] = useState<string[]>(
62
- []
63
- );
63
+ const [selectedProductGroups, setSelectedProductGroups] = useState<
64
+ TypeaheadOptions[]
65
+ >([]);
64
66
  /* TODO: Show Recurrent date, when reccurent date bugs are fixed */
65
67
  // const rrule = watch("rrule");
66
68
  // const [recurrent, setRecurrent] = useState(Boolean(rrule));
@@ -85,15 +87,20 @@ export const CouponFormControl = ({
85
87
  [COUPON_TOTAL_TYPE_TOTAL]: t("Design:depositTotal"),
86
88
  };
87
89
 
88
- // Set pre-selected Product Groups
89
90
  useEffect(() => {
90
- setSelectedProductGroups(getValues("productGroupIds"));
91
+ const defaultProductGroup = getValues("productGroupIds");
92
+
93
+ setSelectedProductGroups(
94
+ convertToTypeaheadOptions(defaultProductGroup as ProductGroup[])
95
+ );
91
96
  }, [getValues]);
92
97
 
93
98
  useEffect(() => {
94
99
  setValue("productGroupIds", selectedProductGroups);
95
100
  }, [getValues, setValue, selectedProductGroups]);
96
101
 
102
+ const productGroupList = convertToTypeaheadOptions(productGroups);
103
+
97
104
  /* TODO: Show Recurrent date, when reccurent date bugs are fixed */
98
105
  // useEffect(() => {
99
106
  // if (!recurrent && rrule) {
@@ -251,51 +258,21 @@ export const CouponFormControl = ({
251
258
  </Form.Group>
252
259
  </Col>
253
260
  </Row>
254
- {productGroups && productGroups?.length > 0 && (
255
- <Row>
256
- <Col md={12}>
257
- <h5 className="mt-4">{t("Design:productGroups")}</h5>
258
- <Form.Group className="d-flex flex-wrap justify-content-between mt-4 mb-5">
259
- {productGroups?.map((productGroup) => {
260
- const isGroupSelected =
261
- getValues("productGroupIds")?.includes(
262
- String(productGroup.id)
263
- ) ?? false;
264
-
265
- return (
266
- <Form.Check
267
- key={productGroup.id}
268
- label={productGroup.name}
269
- defaultChecked={isGroupSelected}
270
- disabled={isLoading}
271
- onChange={(e) => {
272
- if (e.target.checked) {
273
- setSelectedProductGroups((prevGroups) =>
274
- Array.from(
275
- new Set([...prevGroups, String(productGroup.id)])
276
- )
277
- );
278
- } else {
279
- setSelectedProductGroups((prevGroups) => {
280
- return prevGroups.filter(
281
- (id) => String(productGroup.id) !== id
282
- );
283
- });
284
- }
285
- }}
286
- />
287
- );
288
- })}
289
- <Form.Control
290
- {...register("productGroupIds")}
291
- hidden
292
- value={selectedProductGroups}
293
- disabled={isLoading}
294
- />
295
- </Form.Group>
296
- </Col>
297
- </Row>
298
- )}
261
+ <Row>
262
+ <Col>
263
+ <Form.Group className="mb-3">
264
+ <Form.Label>{t("Design:productGroups")}</Form.Label>
265
+ <Typeahead
266
+ name="productGroupIds"
267
+ options={productGroupList}
268
+ isMultipleChoise
269
+ isCouponForm
270
+ placeholder={t("Design:choose")}
271
+ noOptionsMessage={t("Design:noSelectedProductGroups")}
272
+ />
273
+ </Form.Group>
274
+ </Col>
275
+ </Row>
299
276
 
300
277
  {/* TODO: Show Recurrent date, when reccurent date bugs are fixed */}
301
278
  {/* {startFromValue && (
@@ -1,5 +1,8 @@
1
1
  import React, { useEffect } from "react";
2
- import { ProductGroup } from "@licklist/core/dist/DataMapper/Product/ProductGroupDataMapper";
2
+ import {
3
+ ProductGroup,
4
+ ProductGroupDTO,
5
+ } from "@licklist/core/dist/DataMapper/Product/ProductGroupDataMapper";
3
6
  import {
4
7
  CouponTotalType,
5
8
  CouponType,
@@ -16,7 +19,7 @@ export interface CouponFormValues {
16
19
  name?: string;
17
20
  code: string;
18
21
  discountType: CouponType;
19
- productGroupIds: string[];
22
+ productGroupIds: ProductGroupDTO[];
20
23
  discount: number;
21
24
  totalType: CouponTotalType;
22
25
  startFrom: string;
@@ -54,19 +57,6 @@ export const CouponForm = ({
54
57
  const { setError } = form;
55
58
  const { t } = useTranslation(["Design", "Notification", "App"]);
56
59
 
57
- // @TODO: dont need for v1 release
58
- // const [selectedProductGroups, setSelectedProductGroups] = useState<string[]>(
59
- // []
60
- // );
61
- // Set pre-selected Product Groups
62
- // useEffect(() => {
63
- // setSelectedProductGroups(getValues("productGroupIds"));
64
- // }, [getValues]);
65
-
66
- // useEffect(() => {
67
- // setValue("productGroupIds", selectedProductGroups);
68
- // }, [getValues, setValue, selectedProductGroups]);
69
-
70
60
  useEffect(() => {
71
61
  FormErrorService.handleServerErrors(serverErrors, setError);
72
62
  }, [serverErrors, setError]);
@@ -0,0 +1,13 @@
1
+ import { ProductGroup } from "@licklist/core/dist/DataMapper/Product/ProductGroupDataMapper";
2
+
3
+ export const convertToTypeaheadOptions = (productGroups?: ProductGroup[]) => {
4
+ if (!productGroups || !productGroups.length) {
5
+ return [];
6
+ }
7
+
8
+ return productGroups.map(({ id, name }: ProductGroup) => ({
9
+ id: Number(id),
10
+ value: name,
11
+ label: name,
12
+ }));
13
+ };
@@ -11,7 +11,7 @@ import Row from "react-bootstrap/Row";
11
11
  import Col from "react-bootstrap/Col";
12
12
  import { Link } from "react-router-dom";
13
13
  import HookFormService from "@licklist/plugins/dist/services/Form/HookFormService";
14
- import { ProviderType } from "@licklist/core/dist/DataMapper/Provider/ProvidableDataMapper";
14
+ import { ProvidableType } from "@licklist/core/dist/DataMapper/Provider/ProvidableDataMapper";
15
15
  import Icon from "../../static/Icon";
16
16
  import { PaymentFeeForm, PaymentFeeFormFieldValues } from "./PaymentFeeForm";
17
17
  import { Currency } from "../../types/currency";
@@ -33,7 +33,7 @@ export interface AdminSettingFormProps extends HasPermissionProp {
33
33
  providerMetadata: {
34
34
  country?: string;
35
35
  providableId?: number;
36
- providerType?: ProviderType;
36
+ providerType?: ProvidableType;
37
37
  };
38
38
  }
39
39
 
@@ -53,7 +53,6 @@ export interface SortableTreeItemProps {
53
53
  };
54
54
  setIsExpanded?: () => void;
55
55
  isOverride?: boolean;
56
- isProduct?: boolean;
57
56
  }
58
57
 
59
58
  export function SortableTreeItem({
@@ -81,7 +80,6 @@ export function SortableTreeItem({
81
80
  secondaryBadge,
82
81
  setIsExpanded,
83
82
  isOverride,
84
- isProduct = false,
85
83
  }: SortableTreeItemProps) {
86
84
  const [expanded, setExpanded] = useState(isExpanded);
87
85
  const [isModalVisible, setIsModalVisible] = useState(isNewAdded);
@@ -152,7 +150,6 @@ export function SortableTreeItem({
152
150
  };
153
151
 
154
152
  const onEdit = (e) => {
155
- if (isOverride && !isProduct) return;
156
153
  e.stopPropagation();
157
154
  if (edit && !isModalVisible) edit();
158
155
  setIsModalVisible(true);
@@ -23,7 +23,7 @@ export function BooleanSwitch({
23
23
  const { t } = useTranslation("Design");
24
24
 
25
25
  const options: SelectItem[] = [
26
- { id: 1, value: firstValueTitle ?? t("yes") },
26
+ { id: 1, value: firstValueTitle ?? t("yes") },
27
27
  { id: 0, value: secondValueTitle ?? t("no") },
28
28
  ];
29
29
 
@@ -65,7 +65,6 @@
65
65
  }
66
66
 
67
67
  @include media-breakpoint-down(sm) {
68
- height: 100%;
69
68
  max-height: calc(100svh - 30rem);
70
69
  }
71
70
 
@@ -135,6 +135,14 @@
135
135
  margin-bottom: 0.5rem;
136
136
  }
137
137
  }
138
+ .ryft-pay-grid {
139
+ display: flex;
140
+ flex-direction: column;
141
+ .apple-pay-button {
142
+ height: 2.5rem;
143
+ margin-top: 1rem;
144
+ }
145
+ }
138
146
  .mobile-pay-title {
139
147
  display: block;
140
148
  font-weight: 600;
@@ -5,7 +5,7 @@
5
5
  flex-wrap: wrap;
6
6
 
7
7
  .result-card {
8
- height: 14.25rem;
8
+ height: 14rem;
9
9
  border: 1px solid $gray-400;
10
10
  max-width: calc(50% - 0.75rem);
11
11
  flex: 0 0 calc(50% - 0.75rem);
@@ -1,4 +1,5 @@
1
1
  .zone-available-times {
2
+ overscroll-behavior: contain;
2
3
  display: flex;
3
4
  flex-wrap: wrap;
4
5
  gap: 1.5rem;
@@ -7,4 +8,4 @@
7
8
  justify-content: start;
8
9
  width: 95%;
9
10
  overflow: scroll;
10
- }
11
+ }
@@ -16,6 +16,7 @@ export interface TypeaheadProps {
16
16
  isMultipleChoise?: boolean;
17
17
  noOptionsMessage: string;
18
18
  isInvalid?: boolean;
19
+ isCouponForm?: boolean;
19
20
  }
20
21
 
21
22
  function Typeahead({
@@ -24,13 +25,25 @@ function Typeahead({
24
25
  isRequired = false,
25
26
  isMultipleChoise,
26
27
  name = "",
28
+ isCouponForm = false,
27
29
  noOptionsMessage = "",
28
30
  isInvalid,
29
31
  }: TypeaheadProps) {
30
32
  const { control } = useFormContext();
31
33
 
32
- const getErrorStyle = (): CSSObjectWithLabel =>
33
- isInvalid ? { borderColor: "red" } : {};
34
+ const getStyle = (): CSSObjectWithLabel => {
35
+ const couponFormStyle = isCouponForm
36
+ ? {
37
+ borderRadius: "1rem",
38
+ minHeight: "3rem",
39
+ }
40
+ : {};
41
+ return isInvalid
42
+ ? { borderColor: "red", ...couponFormStyle }
43
+ : {
44
+ ...couponFormStyle,
45
+ };
46
+ };
34
47
 
35
48
  return (
36
49
  <>
@@ -46,7 +59,7 @@ function Typeahead({
46
59
  styles={{
47
60
  control: (base) => ({
48
61
  ...base,
49
- ...getErrorStyle(),
62
+ ...getStyle(),
50
63
  }),
51
64
  }}
52
65
  onChange={(value) => {
@@ -13,7 +13,7 @@ export type ZoneFormProps = {
13
13
  onSubmit: (values: Partial<Zone>) => void;
14
14
  serverErrors?: ServerError;
15
15
  isLoading?: boolean;
16
- workHours?: WorkHour[];
16
+ workHours: WorkHour[] | undefined;
17
17
  };
18
18
 
19
19
  export type ZoneFormValues = Partial<Zone> & {
@@ -25,6 +25,7 @@ export const ZoneForm = ({
25
25
  onSubmit,
26
26
  serverErrors,
27
27
  isLoading = false,
28
+ workHours,
28
29
  }: ZoneFormProps) => {
29
30
  const { t } = useTranslation("Design");
30
31
  const form = useForm<ZoneFormValues>({
@@ -54,7 +55,7 @@ export const ZoneForm = ({
54
55
  return (
55
56
  <FormProvider {...form}>
56
57
  <Form onSubmit={handleSubmit(onFormSubmit)}>
57
- <ZoneControl isLoading={isLoading} />
58
+ <ZoneControl isLoading={isLoading} workHours={workHours} />
58
59
  <Row>
59
60
  <Col md={6} sm={12} />
60
61
  <Col