@licklist/design 0.44.541 → 0.44.543

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