@licklist/design 0.58.5 → 0.58.6-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.
- package/bitbucket-pipelines.yml +0 -8
- package/dist/iframe/event/event-card/IframeEventCard.d.ts +2 -1
- package/dist/iframe/event/event-card/IframeEventCard.d.ts.map +1 -1
- package/dist/iframe/event/event-card/IframeEventCard.js +1 -1
- package/dist/iframe/order-process/components/CategoryProduct/components/NumberInput/NumberInput.js +1 -1
- package/dist/iframe/page/components/PageBody/components/LeftBlock/LeftBlock.d.ts.map +1 -1
- package/dist/iframe/page/components/PageBody/components/LeftBlock/LeftBlock.js +1 -1
- package/dist/iframe/payment/order-items-table/utils/paymentSummary.js +1 -1
- package/dist/index.js +1 -1
- package/dist/product-set/card/ProductSetCard.d.ts +2 -1
- package/dist/product-set/card/ProductSetCard.d.ts.map +1 -1
- package/dist/product-set/card/ProductSetCard.js +1 -1
- package/dist/product-set/control/DateInput.d.ts +17 -0
- package/dist/product-set/control/DateInput.d.ts.map +1 -0
- package/dist/product-set/control/DateInput.js +1 -0
- package/dist/product-set/control/ProductSetControl.d.ts +2 -1
- package/dist/product-set/control/ProductSetControl.d.ts.map +1 -1
- package/dist/product-set/control/ProductSetControl.js +1 -1
- package/dist/product-set/control/ProductSetRecurrenceOverridesControl.d.ts +11 -0
- package/dist/product-set/control/ProductSetRecurrenceOverridesControl.d.ts.map +1 -0
- package/dist/product-set/control/ProductSetRecurrenceOverridesControl.js +1 -0
- package/dist/product-set/control/TutorialGifCard.d.ts +2 -1
- package/dist/product-set/control/TutorialGifCard.d.ts.map +1 -1
- package/dist/product-set/control/TutorialGifCard.js +1 -1
- package/dist/product-set/form/ProductCategoriesControl.d.ts +2 -1
- package/dist/product-set/form/ProductCategoriesControl.d.ts.map +1 -1
- package/dist/product-set/form/ProductCategoriesControl.js +1 -1
- package/dist/product-set/form/ProductSetForm.d.ts +7 -2
- package/dist/product-set/form/ProductSetForm.d.ts.map +1 -1
- package/dist/product-set/form/ProductSetForm.js +1 -1
- package/dist/product-set/form/ProductsControl.d.ts +2 -1
- package/dist/product-set/form/ProductsControl.d.ts.map +1 -1
- package/dist/product-set/form/ProductsControl.js +1 -1
- package/dist/product-set/form/StepsControl.d.ts +2 -1
- package/dist/product-set/form/StepsControl.d.ts.map +1 -1
- package/dist/product-set/form/StepsControl.js +1 -1
- package/dist/product-set/form/SubProductsControl.d.ts +2 -1
- package/dist/product-set/form/SubProductsControl.d.ts.map +1 -1
- package/dist/product-set/form/SubProductsControl.js +1 -1
- package/dist/product-set/hooks/useSortableTreeFunctions.d.ts +2 -1
- package/dist/product-set/hooks/useSortableTreeFunctions.d.ts.map +1 -1
- package/dist/product-set/hooks/useSortableTreeFunctions.js +1 -1
- package/dist/product-set/item/CreateProductSetItem.d.ts +2 -1
- package/dist/product-set/item/CreateProductSetItem.d.ts.map +1 -1
- package/dist/product-set/item/CreateProductSetItem.js +1 -1
- package/dist/product-set/product/ProductControl.d.ts +2 -1
- package/dist/product-set/product/ProductControl.d.ts.map +1 -1
- package/dist/product-set/product/ProductControl.js +1 -1
- package/dist/product-set/product/fixed-duration-fields/FixedDurationOptions.d.ts +4 -2
- package/dist/product-set/product/fixed-duration-fields/FixedDurationOptions.d.ts.map +1 -1
- package/dist/product-set/product/fixed-duration-fields/FixedDurationOptions.js +1 -1
- package/dist/product-set/product/quantity/ProductQuantityControl.d.ts +1 -0
- package/dist/product-set/product/quantity/ProductQuantityControl.d.ts.map +1 -1
- package/dist/product-set/product/quantity/ProductQuantityControl.js +1 -1
- package/dist/product-set/product-category/ProductCategoryControl.d.ts +2 -1
- package/dist/product-set/product-category/ProductCategoryControl.d.ts.map +1 -1
- package/dist/product-set/product-category/ProductCategoryControl.js +1 -1
- package/dist/recurring-date-picker-input/DatePickerInput.d.ts +19 -0
- package/dist/recurring-date-picker-input/DatePickerInput.d.ts.map +1 -0
- package/dist/recurring-date-picker-input/DatePickerInput.js +1 -0
- package/dist/recurring-date-picker-input/RecurrenceAndFrequencyInput.d.ts +15 -0
- package/dist/recurring-date-picker-input/RecurrenceAndFrequencyInput.d.ts.map +1 -0
- package/dist/recurring-date-picker-input/RecurrenceAndFrequencyInput.js +1 -0
- package/dist/recurring-date-picker-input/RecurringDatePickerInput.d.ts +2 -0
- package/dist/recurring-date-picker-input/RecurringDatePickerInput.d.ts.map +1 -1
- package/dist/recurring-date-picker-input/RecurringDatePickerInput.js +1 -1
- package/dist/sales/booking/results/components/ResultCard.d.ts.map +1 -1
- package/dist/sales/booking/results/components/ResultCard.js +1 -1
- package/dist/sortable-list/SortableList.d.ts +2 -1
- package/dist/sortable-list/SortableList.d.ts.map +1 -1
- package/dist/sortable-list/SortableList.js +1 -1
- package/dist/sortable-tree/SortableTreeItem.d.ts +3 -1
- package/dist/sortable-tree/SortableTreeItem.d.ts.map +1 -1
- package/dist/sortable-tree/SortableTreeItem.js +1 -1
- package/dist/static/manual-date-picker/ManualDatePicker.js +1 -1
- package/dist/static/manual-date-picker/constants/index.d.ts +4 -1
- package/dist/static/manual-date-picker/constants/index.d.ts.map +1 -1
- package/dist/static/manual-date-picker/constants/index.js +1 -1
- package/dist/static/manual-date-picker/utils/index.d.ts +4 -0
- package/dist/static/manual-date-picker/utils/index.d.ts.map +1 -1
- package/dist/static/manual-date-picker/utils/index.js +1 -1
- package/dist/styles/availability-indicator/AvailabilityIndicator.scss +10 -0
- package/dist/styles/iframe-events/Card.scss +24 -8
- package/dist/styles/iframe-events/PoweredBy.scss +2 -2
- package/dist/styles/iframe-order-process/IframeOrderProcess.scss +0 -2
- package/dist/styles/iframe-page/Page.scss +1 -0
- package/dist/styles/iframe-page/PageBody.scss +32 -11
- package/dist/styles/iframe-page/PageHeader.scss +41 -39
- package/dist/styles/product-set/EditVenueMapSetModal.scss +1 -1
- package/dist/styles/sales/BookingResults.scss +1 -1
- package/package.json +10 -33
- package/src/calendar/Calendar.stories.tsx +9 -2
- package/src/iframe/event/event-card/IframeEventCard.stories.tsx +1 -0
- package/src/iframe/event/event-card/IframeEventCard.tsx +7 -8
- package/src/iframe/order-process/components/CategoryProduct/components/NumberInput/NumberInput.tsx +1 -1
- package/src/iframe/page/components/PageBody/components/LeftBlock/LeftBlock.tsx +3 -1
- package/src/iframe/payment/order-items-table/utils/paymentSummary.tsx +6 -6
- package/src/product-set/card/ProductSetCard.tsx +10 -1
- package/src/product-set/control/DateInput.tsx +309 -0
- package/src/product-set/control/ProductSetControl.tsx +46 -24
- package/src/product-set/control/ProductSetRecurrenceOverridesControl.tsx +63 -0
- package/src/product-set/control/TutorialGifCard.tsx +11 -3
- package/src/product-set/form/ProductCategoriesControl.tsx +12 -1
- package/src/product-set/form/ProductSetForm.tsx +10 -1
- package/src/product-set/form/ProductsControl.tsx +10 -0
- package/src/product-set/form/StepsControl.tsx +8 -2
- package/src/product-set/form/SubProductsControl.tsx +3 -0
- package/src/product-set/hooks/useSortableTreeFunctions.ts +6 -0
- package/src/product-set/item/CreateProductSetItem.tsx +3 -0
- package/src/product-set/product/ProductControl.tsx +23 -12
- package/src/product-set/product/fixed-duration-fields/FixedDurationOptions.tsx +8 -2
- package/src/product-set/product/quantity/ProductQuantityControl.tsx +4 -3
- package/src/product-set/product-category/ProductCategoryControl.tsx +12 -8
- package/src/recurring-date-picker-input/DatePickerInput.tsx +93 -0
- package/src/recurring-date-picker-input/RecurrenceAndFrequencyInput.tsx +136 -0
- package/src/recurring-date-picker-input/RecurringDatePickerInput.tsx +4 -1
- package/src/sales/booking/results/BookingResults.stories.tsx +3 -2
- package/src/sales/booking/results/components/ResultCard.tsx +2 -5
- package/src/sortable-list/SortableList.tsx +3 -0
- package/src/sortable-tree/SortableTreeItem.tsx +6 -0
- package/src/static/manual-date-picker/ManualDatePicker.tsx +3 -3
- package/src/static/manual-date-picker/constants/index.ts +6 -2
- package/src/static/manual-date-picker/utils/index.ts +11 -0
- package/src/static/switch/BooleanSwitch.tsx +1 -1
- package/src/styles/availability-indicator/AvailabilityIndicator.scss +10 -0
- package/src/styles/iframe-events/Card.scss +24 -8
- package/src/styles/iframe-events/PoweredBy.scss +2 -2
- package/src/styles/iframe-order-process/IframeOrderProcess.scss +0 -2
- package/src/styles/iframe-page/Page.scss +1 -0
- package/src/styles/iframe-page/PageBody.scss +32 -11
- package/src/styles/iframe-page/PageHeader.scss +41 -39
- package/src/styles/product-set/EditVenueMapSetModal.scss +1 -1
- package/src/styles/sales/BookingResults.scss +1 -1
- package/jest.config.js +0 -29
- package/tests/Auth/Authorizer.test.tsx +0 -194
- package/tests/Auth/Layout/UserNavDropDown.test.tsx +0 -43
- package/tests/Auth/Layout/UserNavDropDownToggle.test.tsx +0 -33
- package/tests/Auth/Login/LoginComponent.test.tsx +0 -246
- package/tests/Auth/Login/LoginFormComponent.test.tsx +0 -182
- package/tests/Auth/Register/RegisterComponent.test.tsx +0 -285
- package/tests/Auth/Register/RegisterFormComponent.test.tsx +0 -170
- package/tests/Auth/Settings/Dashboard/IpInput.test.tsx +0 -130
- package/tests/Auth/Social/SocialCallbackComponent.test.tsx +0 -133
- package/tests/Auth/Social/SocialFormComponent.test.tsx +0 -118
- package/tests/FileUpload/FileUpload.test.tsx +0 -42
- package/tests/Notification/EmailTemplate.test.tsx +0 -82
- package/tests/ProductSet/ProductSetPopover.test.tsx +0 -40
- package/tests/Report/Report.test.tsx +0 -48
- package/tests/Sales/Coupon.test.tsx +0 -51
- package/tests/Sales/SalesAndVIews.test.tsx +0 -63
- package/tests/SnippetTemplates/SnippetTemplates.test.tsx +0 -56
- package/tests/Table/FilterHelperComponent.test.tsx +0 -88
- package/tests/Table/PaginationHelperComponent.test.tsx +0 -109
- package/tests/Table/PerPageHelperComponent.test.tsx +0 -34
- package/tests/Table/TableHelperComponent.test.tsx +0 -295
- package/tests/TipTapEditor/TipTapEditor.test.tsx +0 -28
- package/tests/__mock__/hooks/useAuthApi.ts +0 -13
- package/tests/__mock__/hooks/useAuthMock.ts +0 -13
- package/tests/__mock__/hooks/useFormMock.ts +0 -27
- package/tests/__mock__/hooks/useNotificationMock.ts +0 -13
- package/tests/__mock__/hooks/useQueryMock.ts +0 -16
- package/tests/__mock__/hooks/useSocialApiMock.ts +0 -20
- package/tests/__mock__/hooks/useTranslationMock.ts +0 -17
- package/tests/__mock__/hooks/useUserApiMock.ts +0 -18
- package/tests/__mock__/hooks/useUserMock.ts +0 -13
- package/tests/__mock__/styleMock.js +0 -1
- package/tests/__mock__/windowMock.ts +0 -5
- package/tests/packages/react-query.tsx +0 -28
- package/tests/setupTests.ts +0 -10
|
@@ -29,6 +29,7 @@ interface IframeEventCardProps {
|
|
|
29
29
|
titleId?: string;
|
|
30
30
|
descriptionId?: string;
|
|
31
31
|
imageClassName?: string;
|
|
32
|
+
showMoreInfo?: boolean;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export function IframeEventCard({
|
|
@@ -46,6 +47,7 @@ export function IframeEventCard({
|
|
|
46
47
|
titleId,
|
|
47
48
|
descriptionId,
|
|
48
49
|
imageClassName,
|
|
50
|
+
showMoreInfo,
|
|
49
51
|
}: IframeEventCardProps) {
|
|
50
52
|
const { formatNumber } = useIntl();
|
|
51
53
|
const { t } = useTranslation("Design");
|
|
@@ -70,6 +72,7 @@ export function IframeEventCard({
|
|
|
70
72
|
)}
|
|
71
73
|
onClick={goToDetails ? onClick : undefined}
|
|
72
74
|
>
|
|
75
|
+
{showMoreInfo && <div className="iframe-event-card__info-icon">i</div>}
|
|
73
76
|
{imageSrc && (
|
|
74
77
|
<Card.Img
|
|
75
78
|
className={imageClassName ?? "card-image"}
|
|
@@ -91,12 +94,8 @@ export function IframeEventCard({
|
|
|
91
94
|
<div className="icon-wrapper">
|
|
92
95
|
<CalendarSvg />
|
|
93
96
|
</div>
|
|
94
|
-
<p className="
|
|
95
|
-
|
|
96
|
-
</p>
|
|
97
|
-
<p className="iframe-event-card__bold m-0 grid-view-hided">
|
|
98
|
-
{shortDate}
|
|
99
|
-
</p>
|
|
97
|
+
<p className="m-0 list-view-hided">{date}</p>
|
|
98
|
+
<p className="m-0 grid-view-hided">{shortDate}</p>
|
|
100
99
|
</div>
|
|
101
100
|
</Card.Text>
|
|
102
101
|
|
|
@@ -106,7 +105,7 @@ export function IframeEventCard({
|
|
|
106
105
|
<div className="icon-wrapper">
|
|
107
106
|
<ClockSvg />
|
|
108
107
|
</div>
|
|
109
|
-
<p className="
|
|
108
|
+
<p className="m-0">{type}</p>
|
|
110
109
|
</div>
|
|
111
110
|
</Card.Text>
|
|
112
111
|
)}
|
|
@@ -117,7 +116,7 @@ export function IframeEventCard({
|
|
|
117
116
|
<div className="icon-wrapper">
|
|
118
117
|
<TicketSvg />
|
|
119
118
|
</div>
|
|
120
|
-
<p className="
|
|
119
|
+
<p className="m-0">
|
|
121
120
|
{t("from")}{" "}
|
|
122
121
|
{formatNumber(cost, { style: "currency", currency: "GBP" })}
|
|
123
122
|
</p>
|
package/src/iframe/order-process/components/CategoryProduct/components/NumberInput/NumberInput.tsx
CHANGED
|
@@ -90,7 +90,7 @@ export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
|
|
90
90
|
type="number"
|
|
91
91
|
onChange={(e) => handleChangeValue(Number(e.target.value))}
|
|
92
92
|
value={value}
|
|
93
|
-
min={
|
|
93
|
+
min={0}
|
|
94
94
|
max={max}
|
|
95
95
|
defaultValue={defaultValue}
|
|
96
96
|
placeholder={placeholder}
|
|
@@ -63,7 +63,7 @@ const processedPaymentSummary = ({
|
|
|
63
63
|
const total = calculateTotalPrice(order, externalPaymentDetail);
|
|
64
64
|
|
|
65
65
|
const totalDiscount = calculateTotalDiscount(order.payments);
|
|
66
|
-
|
|
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:
|
|
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:
|
|
87
|
+
price: order.paidAmount,
|
|
88
88
|
});
|
|
89
|
-
if (
|
|
89
|
+
if (order.totalToPayAmount <= 0) {
|
|
90
90
|
return summaryItems;
|
|
91
91
|
}
|
|
92
92
|
summaryItems = [...summaryItems, ...externalPaymentDetail];
|
|
93
93
|
|
|
94
|
-
if (
|
|
94
|
+
if (order.remainingToPay) {
|
|
95
95
|
summaryItems.push({
|
|
96
96
|
translateKey: REMAINING_TO_PAY,
|
|
97
|
-
price:
|
|
97
|
+
price: order.remainingToPay,
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
return summaryItems;
|
|
@@ -18,6 +18,7 @@ export interface ProductSetCardProps extends HasPermissionProp {
|
|
|
18
18
|
onCopy: () => void;
|
|
19
19
|
onRemove: () => void;
|
|
20
20
|
className?: string;
|
|
21
|
+
override?: number;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export function ProductSetCard({
|
|
@@ -28,6 +29,7 @@ export function ProductSetCard({
|
|
|
28
29
|
onRemove,
|
|
29
30
|
className = "",
|
|
30
31
|
hasPermission = true,
|
|
32
|
+
override = 0,
|
|
31
33
|
}: ProductSetCardProps) {
|
|
32
34
|
const { t } = useTranslation("Design");
|
|
33
35
|
const popoverId = useId();
|
|
@@ -44,7 +46,14 @@ export function ProductSetCard({
|
|
|
44
46
|
>
|
|
45
47
|
<div className={productSetCardClasses}>
|
|
46
48
|
<div className="product-set-card-link">
|
|
47
|
-
|
|
49
|
+
<div className="d-flex flex-column">
|
|
50
|
+
<div>
|
|
51
|
+
{name} {rrule && `(${RRule.fromString(rrule).toText()})`}
|
|
52
|
+
</div>
|
|
53
|
+
<div className="h6 bold ml-1">
|
|
54
|
+
{!!override && t("overridesAmount", { amount: override })}
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
48
57
|
</div>
|
|
49
58
|
<div className="d-flex">
|
|
50
59
|
<div className="product-set-card-menu-eye-container">
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import React, { useRef, useState } from "react";
|
|
2
|
+
import { Modifier } from "@popperjs/core";
|
|
3
|
+
import { useId } from "@react-aria/utils";
|
|
4
|
+
import { Form, OverlayTrigger, Popover } from "react-bootstrap";
|
|
5
|
+
import { useFieldArray, useFormContext } from "react-hook-form";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { useClickAway } from "react-use";
|
|
8
|
+
import { TIMEZONE, TIME_FORMAT } from "@licklist/core/dist/Config/Date";
|
|
9
|
+
import { ProductSetRecurrence } from "@licklist/core/dist/DataMapper/Product/ProductSetRecurrenceDataMapper";
|
|
10
|
+
import { DateTime } from "luxon";
|
|
11
|
+
import RRule, { Frequency } from "rrule";
|
|
12
|
+
import { WorkHour } from "@licklist/core/dist/DataMapper/Provider/WorkHourDataMapper";
|
|
13
|
+
|
|
14
|
+
import { DatePickerInput } from "../../recurring-date-picker-input/DatePickerInput";
|
|
15
|
+
import { CONFIRM_MODAL_ACTIONS } from "../../modals/confirmation/ConfirmModal";
|
|
16
|
+
import { Icon } from "../../static";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
AvailableTimesControl,
|
|
20
|
+
AvailableTimesControlRef,
|
|
21
|
+
} from "../../zone/form/components/AvailableTimesControl";
|
|
22
|
+
import { RecurringDatePickerInputValues } from "../../recurring-date-picker-input/RecurringDatePickerInput";
|
|
23
|
+
import { ProductSetRecurrenceOverridesControl } from "./ProductSetRecurrenceOverridesControl";
|
|
24
|
+
|
|
25
|
+
export interface DateAndRecurrenceInputValues {
|
|
26
|
+
menuRecurrences?: Partial<ProductSetRecurrence>[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface DateAndRecurrenceInputProps {
|
|
30
|
+
isEventEditProductSet?: boolean;
|
|
31
|
+
workHours?: WorkHour[];
|
|
32
|
+
providerHasBookingManagement: boolean;
|
|
33
|
+
isLoading?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const DateInput = ({
|
|
37
|
+
isEventEditProductSet,
|
|
38
|
+
isLoading,
|
|
39
|
+
workHours,
|
|
40
|
+
providerHasBookingManagement,
|
|
41
|
+
}: DateAndRecurrenceInputProps) => {
|
|
42
|
+
const {
|
|
43
|
+
control,
|
|
44
|
+
register,
|
|
45
|
+
formState: { errors },
|
|
46
|
+
getValues,
|
|
47
|
+
setValue,
|
|
48
|
+
clearErrors,
|
|
49
|
+
} = useFormContext<DateAndRecurrenceInputValues>();
|
|
50
|
+
|
|
51
|
+
const { fields, append, update } = useFieldArray({
|
|
52
|
+
name: "menuRecurrences",
|
|
53
|
+
control,
|
|
54
|
+
keyName: "_id",
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const [isDatePopoverVisible, setIsDatePopoverVisible] = useState(false);
|
|
58
|
+
const availableTimesFormRef = useRef<AvailableTimesControlRef>();
|
|
59
|
+
const [editState, setEditState] = useState<{
|
|
60
|
+
index: number;
|
|
61
|
+
values: Partial<ProductSetRecurrence>;
|
|
62
|
+
}>({
|
|
63
|
+
index: null,
|
|
64
|
+
values: null,
|
|
65
|
+
});
|
|
66
|
+
const clearEditState = () => setEditState({ index: null, values: null });
|
|
67
|
+
|
|
68
|
+
const popoverRef = useRef<HTMLDivElement>();
|
|
69
|
+
|
|
70
|
+
useClickAway(popoverRef, (event) => {
|
|
71
|
+
const isModal = CONFIRM_MODAL_ACTIONS.includes(
|
|
72
|
+
(event.target as HTMLElement)?.attributes?.getNamedItem("data-id")?.value
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (isModal) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setIsDatePopoverVisible(false);
|
|
80
|
+
clearEditState();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const popoverId = useId()!;
|
|
84
|
+
|
|
85
|
+
const handleOnEdit = (next: Partial<ProductSetRecurrence>, index) => {
|
|
86
|
+
clearEditState();
|
|
87
|
+
|
|
88
|
+
// Hotfix for an issue with Popover close & open actions
|
|
89
|
+
setTimeout(() => {
|
|
90
|
+
setEditState({ index, values: next });
|
|
91
|
+
setIsDatePopoverVisible(() => true);
|
|
92
|
+
}, 100);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const { t } = useTranslation("Design");
|
|
96
|
+
|
|
97
|
+
// in this method we should fulfill the form with a proper data
|
|
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
|
+
|
|
129
|
+
const rrule = RRule.optionsToString({
|
|
130
|
+
dtstart: DateTime.fromISO(next.startDate).toJSDate(),
|
|
131
|
+
until: DateTime.fromISO(next.startDate).toJSDate(),
|
|
132
|
+
byweekday: [],
|
|
133
|
+
freq: Frequency.DAILY,
|
|
134
|
+
tzid: TIMEZONE,
|
|
135
|
+
}).replace("RRULE:", "");
|
|
136
|
+
|
|
137
|
+
const availableTime =
|
|
138
|
+
availableTimesFormRef?.current?.getValues()?.availableTimes || null;
|
|
139
|
+
const values = {
|
|
140
|
+
rrule,
|
|
141
|
+
startDate: next.startDate,
|
|
142
|
+
endDate: next.startDate,
|
|
143
|
+
startTime: next.startTime,
|
|
144
|
+
endTime: next.endTime,
|
|
145
|
+
availableTimes: availableTime,
|
|
146
|
+
} as Partial<ProductSetRecurrence>;
|
|
147
|
+
|
|
148
|
+
if (editState.values?.id) {
|
|
149
|
+
values.id = editState.values?.id;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (editState.index !== null) {
|
|
153
|
+
update(editState.index, values);
|
|
154
|
+
} else {
|
|
155
|
+
append(values);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
setIsDatePopoverVisible(() => false);
|
|
159
|
+
clearEditState();
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const handleOnAdd = () => {
|
|
163
|
+
if (fields.length >= 1) return;
|
|
164
|
+
clearEditState();
|
|
165
|
+
setIsDatePopoverVisible((prevValue) => !prevValue);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const handleDelete = () => {
|
|
169
|
+
const removeIndex = editState.index;
|
|
170
|
+
|
|
171
|
+
setIsDatePopoverVisible(false);
|
|
172
|
+
clearEditState();
|
|
173
|
+
|
|
174
|
+
setTimeout(() => {
|
|
175
|
+
const { menuRecurrences = [] } = getValues();
|
|
176
|
+
|
|
177
|
+
// @TODO looks like workaround
|
|
178
|
+
// Need recheck other places with remove method.
|
|
179
|
+
// or update react-hook-form version.
|
|
180
|
+
// Remove method from useFormFieldArray throw
|
|
181
|
+
// errors while try to remove element by index.
|
|
182
|
+
// So we remove them manually
|
|
183
|
+
setValue(
|
|
184
|
+
"menuRecurrences",
|
|
185
|
+
menuRecurrences.filter(
|
|
186
|
+
(_currentMenuRecurrence, index) => index !== removeIndex
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
}, 100);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<Form.Group>
|
|
194
|
+
<Form.Label>
|
|
195
|
+
{t(
|
|
196
|
+
isEventEditProductSet
|
|
197
|
+
? "whenOverridesAvailable"
|
|
198
|
+
: "productSetAvailable"
|
|
199
|
+
)}
|
|
200
|
+
</Form.Label>
|
|
201
|
+
|
|
202
|
+
<OverlayTrigger
|
|
203
|
+
show={isDatePopoverVisible}
|
|
204
|
+
overlay={
|
|
205
|
+
<Popover id={popoverId} className="rounded">
|
|
206
|
+
<Popover.Content className="shadow-lg rounded" ref={popoverRef}>
|
|
207
|
+
<DatePickerInput
|
|
208
|
+
defaultValues={editState.values}
|
|
209
|
+
onChange={handleRecurringDateChange}
|
|
210
|
+
onDelete={handleDelete}
|
|
211
|
+
>
|
|
212
|
+
{providerHasBookingManagement && (
|
|
213
|
+
<AvailableTimesControl
|
|
214
|
+
workHours={workHours}
|
|
215
|
+
isLoading={isLoading}
|
|
216
|
+
ref={availableTimesFormRef}
|
|
217
|
+
defaultValues={[]}
|
|
218
|
+
errorMessage={
|
|
219
|
+
errors?.menuRecurrences?.[`${editState?.index}`]
|
|
220
|
+
?.availableTimes?.message
|
|
221
|
+
}
|
|
222
|
+
clearErrorMessage={() =>
|
|
223
|
+
clearErrors(
|
|
224
|
+
`menuRecurrences.${editState?.index}.availableTimes`
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
/>
|
|
228
|
+
)}
|
|
229
|
+
</DatePickerInput>
|
|
230
|
+
</Popover.Content>
|
|
231
|
+
</Popover>
|
|
232
|
+
}
|
|
233
|
+
trigger="click"
|
|
234
|
+
placement="bottom"
|
|
235
|
+
popperConfig={{ modifiers: [sameWidthPopperModifier] }}
|
|
236
|
+
>
|
|
237
|
+
<div className="product-set-recurrences">
|
|
238
|
+
{fields.map((menuRecurrence, index) => (
|
|
239
|
+
<ProductSetRecurrenceOverridesControl
|
|
240
|
+
key={menuRecurrence._id}
|
|
241
|
+
menuRecurrence={menuRecurrence}
|
|
242
|
+
onEdit={() => handleOnEdit(menuRecurrence, index)}
|
|
243
|
+
errorMessage={
|
|
244
|
+
errors?.menuRecurrences?.[`${index}`]?.availableTimes?.message
|
|
245
|
+
}
|
|
246
|
+
/>
|
|
247
|
+
))}
|
|
248
|
+
<div
|
|
249
|
+
className="product-set-recurrence-icon-wrapper"
|
|
250
|
+
role="button"
|
|
251
|
+
tabIndex={0}
|
|
252
|
+
onClick={handleOnAdd}
|
|
253
|
+
onKeyDown={handleOnAdd}
|
|
254
|
+
>
|
|
255
|
+
<Icon
|
|
256
|
+
type="plus-circle"
|
|
257
|
+
height="1rem"
|
|
258
|
+
className="product-set-recurrence-icon-add mr-2"
|
|
259
|
+
/>
|
|
260
|
+
{t("addDates")}
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<Form.Control
|
|
264
|
+
type="hidden"
|
|
265
|
+
{...register("menuRecurrences", {
|
|
266
|
+
required: {
|
|
267
|
+
value: true,
|
|
268
|
+
message: t("Validation:fieldMinNumber", {
|
|
269
|
+
attribute: t("dates"),
|
|
270
|
+
min: 1,
|
|
271
|
+
}),
|
|
272
|
+
},
|
|
273
|
+
})}
|
|
274
|
+
/>
|
|
275
|
+
|
|
276
|
+
<div className="manual-form-error">
|
|
277
|
+
{errors.menuRecurrences?.message}
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
</OverlayTrigger>
|
|
281
|
+
</Form.Group>
|
|
282
|
+
);
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
export const sameWidthPopperModifier: Modifier<
|
|
286
|
+
"sameWidth",
|
|
287
|
+
Record<string, unknown>
|
|
288
|
+
> = {
|
|
289
|
+
name: "sameWidth",
|
|
290
|
+
enabled: true,
|
|
291
|
+
phase: "beforeWrite",
|
|
292
|
+
requires: ["computeStyles"],
|
|
293
|
+
fn({ state }) {
|
|
294
|
+
const style = {
|
|
295
|
+
width: `${state.rects.reference.width + 4}px`,
|
|
296
|
+
marginLeft: "-2px",
|
|
297
|
+
maxWidth: "unset",
|
|
298
|
+
};
|
|
299
|
+
Object.assign(state.styles.popper, style);
|
|
300
|
+
},
|
|
301
|
+
effect({ state }) {
|
|
302
|
+
const { width } = state.elements.reference.getBoundingClientRect();
|
|
303
|
+
const style = {
|
|
304
|
+
width: `${width}px`,
|
|
305
|
+
maxWidth: "unset",
|
|
306
|
+
};
|
|
307
|
+
Object.assign(state.elements.popper.style, style);
|
|
308
|
+
},
|
|
309
|
+
};
|
|
@@ -34,6 +34,7 @@ import TutorialGifCard from "./TutorialGifCard";
|
|
|
34
34
|
import { Step } from "../types";
|
|
35
35
|
import { StepsControl } from "../form/StepsControl";
|
|
36
36
|
import { Typeahead } from "../../typeahead";
|
|
37
|
+
import { DateInput } from "./DateInput";
|
|
37
38
|
|
|
38
39
|
const OPERATIONAL_COST_TITLES = {
|
|
39
40
|
[OPERATIONAL_COST_PROVIDER]: "operationalCostProvider",
|
|
@@ -86,6 +87,7 @@ export interface ProductSetControlProps {
|
|
|
86
87
|
showSmsTemplate?: boolean;
|
|
87
88
|
workHours?: WorkHour[];
|
|
88
89
|
providerHasBookingManagement?: boolean;
|
|
90
|
+
isOverrides?: boolean;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
export function ProductSetControl({
|
|
@@ -101,6 +103,7 @@ export function ProductSetControl({
|
|
|
101
103
|
showSmsTemplate,
|
|
102
104
|
workHours,
|
|
103
105
|
providerHasBookingManagement = false,
|
|
106
|
+
isOverrides = false,
|
|
104
107
|
}: ProductSetControlProps) {
|
|
105
108
|
const { t } = useTranslation(["Design", "Validation", "Notification"]);
|
|
106
109
|
const {
|
|
@@ -118,6 +121,9 @@ export function ProductSetControl({
|
|
|
118
121
|
const termsAndConditionsId = useId();
|
|
119
122
|
const relyOnPeopleTypeId = useId();
|
|
120
123
|
|
|
124
|
+
const showSmsTemplateSelector = showSmsTemplate && !isOverrides;
|
|
125
|
+
const showEmailTemplateSelector = showEmailTemplate && !isOverrides;
|
|
126
|
+
|
|
121
127
|
return (
|
|
122
128
|
<Row
|
|
123
129
|
className={`product-set-form ${
|
|
@@ -159,20 +165,30 @@ export function ProductSetControl({
|
|
|
159
165
|
</Form.Control.Feedback>
|
|
160
166
|
</Form.Group>
|
|
161
167
|
|
|
162
|
-
{
|
|
163
|
-
<
|
|
164
|
-
isEventEditProductSet={isEventEditProductSet}
|
|
168
|
+
{isOverrides ? (
|
|
169
|
+
<DateInput
|
|
165
170
|
workHours={workHours}
|
|
166
171
|
isLoading={isLoading}
|
|
167
172
|
providerHasBookingManagement={providerHasBookingManagement}
|
|
168
173
|
/>
|
|
174
|
+
) : (
|
|
175
|
+
<>
|
|
176
|
+
{isCreateAction && (
|
|
177
|
+
<DateAndRecurrenceInput
|
|
178
|
+
isEventEditProductSet={isEventEditProductSet}
|
|
179
|
+
workHours={workHours}
|
|
180
|
+
isLoading={isLoading}
|
|
181
|
+
providerHasBookingManagement={providerHasBookingManagement}
|
|
182
|
+
/>
|
|
183
|
+
)}
|
|
184
|
+
</>
|
|
169
185
|
)}
|
|
170
186
|
|
|
171
187
|
<div className="divider" />
|
|
172
188
|
|
|
173
|
-
<TutorialGifCard />
|
|
189
|
+
<TutorialGifCard isOverrides={isOverrides} />
|
|
174
190
|
|
|
175
|
-
<StepsControl isLoading={isLoading} />
|
|
191
|
+
<StepsControl isLoading={isLoading} isOverrides={isOverrides} />
|
|
176
192
|
</Col>
|
|
177
193
|
<Col md={6} sm={12}>
|
|
178
194
|
<div className="second-column">
|
|
@@ -212,6 +228,7 @@ export function ProductSetControl({
|
|
|
212
228
|
{...register("operationalCost", { required: true })}
|
|
213
229
|
as="select"
|
|
214
230
|
defaultValue={OPERATIONAL_COST_PROVIDER}
|
|
231
|
+
disabled={isOverrides}
|
|
215
232
|
isInvalid={HookFormService.isInvalid("operationalCost", errors)}
|
|
216
233
|
>
|
|
217
234
|
{Object.keys(OPERATIONAL_COST_TYPES).map((operationalCost) => (
|
|
@@ -241,6 +258,7 @@ export function ProductSetControl({
|
|
|
241
258
|
{...register("relyOnPeopleType")}
|
|
242
259
|
as="select"
|
|
243
260
|
defaultValue={null}
|
|
261
|
+
disabled={isOverrides}
|
|
244
262
|
isInvalid={HookFormService.isInvalid("relyOnPeopleType", errors)}
|
|
245
263
|
>
|
|
246
264
|
<option value="">{t("notRequired")} </option>
|
|
@@ -268,7 +286,7 @@ export function ProductSetControl({
|
|
|
268
286
|
{...register("termsAndConditions")}
|
|
269
287
|
as="textarea"
|
|
270
288
|
rows={2}
|
|
271
|
-
disabled={isLoading}
|
|
289
|
+
disabled={isLoading || isOverrides}
|
|
272
290
|
isInvalid={HookFormService.isInvalid(
|
|
273
291
|
"termsAndConditions",
|
|
274
292
|
errors
|
|
@@ -283,30 +301,32 @@ export function ProductSetControl({
|
|
|
283
301
|
<Form.Label className="mt-4">
|
|
284
302
|
{t("Design:emailTemplate")}
|
|
285
303
|
</Form.Label>
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
304
|
+
<>
|
|
305
|
+
{showEmailTemplateSelector ? (
|
|
306
|
+
<Typeahead
|
|
307
|
+
name="emailTemplates"
|
|
308
|
+
options={emailTemplates}
|
|
309
|
+
isMultipleChoise
|
|
310
|
+
placeholder={t("Design:choose")}
|
|
311
|
+
noOptionsMessage={t("Design:noActiveTemplates", {
|
|
312
|
+
notification: t("Design:email"),
|
|
313
|
+
})}
|
|
314
|
+
/>
|
|
315
|
+
) : (
|
|
316
|
+
<WarningMessage
|
|
317
|
+
message={t("Design:noActiveTemplates", {
|
|
318
|
+
notification: t("Design:email"),
|
|
319
|
+
})}
|
|
320
|
+
/>
|
|
321
|
+
)}
|
|
322
|
+
</>
|
|
303
323
|
|
|
304
324
|
<Form.Label
|
|
305
325
|
className={clsx(emailTemplates.length > 0 ? "mt-4" : "mt-1")}
|
|
306
326
|
>
|
|
307
327
|
{t("Design:smsTemplate")}
|
|
308
328
|
</Form.Label>
|
|
309
|
-
{
|
|
329
|
+
{showSmsTemplateSelector ? (
|
|
310
330
|
<Typeahead
|
|
311
331
|
name="smsTemplates"
|
|
312
332
|
options={smsTemplates}
|
|
@@ -339,6 +359,7 @@ export function ProductSetControl({
|
|
|
339
359
|
onChange={field.onChange}
|
|
340
360
|
value={fieldValue}
|
|
341
361
|
as="select"
|
|
362
|
+
disabled={isOverrides}
|
|
342
363
|
isInvalid={HookFormService.isInvalid(
|
|
343
364
|
"fieldSetId",
|
|
344
365
|
errors
|
|
@@ -375,6 +396,7 @@ export function ProductSetControl({
|
|
|
375
396
|
}) as string;
|
|
376
397
|
},
|
|
377
398
|
})}
|
|
399
|
+
disabled={isOverrides}
|
|
378
400
|
isInvalid={HookFormService.isInvalid("thankYouPageUrl", errors)}
|
|
379
401
|
/>
|
|
380
402
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useIntl } from "react-intl";
|
|
3
|
+
import { ProductSetRecurrence } from "@licklist/core/dist/DataMapper/Product/ProductSetRecurrenceDataMapper";
|
|
4
|
+
|
|
5
|
+
import { Icon } from "../../static";
|
|
6
|
+
|
|
7
|
+
export interface ProductSetRecurrenceControlProps {
|
|
8
|
+
menuRecurrence?: { _id: string } & Partial<ProductSetRecurrence>;
|
|
9
|
+
onEdit?: (onEdit: Partial<ProductSetRecurrence>) => void;
|
|
10
|
+
errorMessage?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const ProductSetRecurrenceOverridesControl = ({
|
|
14
|
+
menuRecurrence,
|
|
15
|
+
onEdit,
|
|
16
|
+
errorMessage,
|
|
17
|
+
}: ProductSetRecurrenceControlProps) => {
|
|
18
|
+
const { formatDate } = useIntl();
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="sortable-tree-root flex-column" role="button" tabIndex={0}>
|
|
22
|
+
<div
|
|
23
|
+
key={menuRecurrence.rrule}
|
|
24
|
+
className="product-set-recurrence d-flex flex-column mr-0 mb-3 p-3 px-4 flex-grow-1"
|
|
25
|
+
>
|
|
26
|
+
<span className="d-flex flex-row align-items-center">
|
|
27
|
+
<Icon
|
|
28
|
+
type="calendar"
|
|
29
|
+
height="1rem"
|
|
30
|
+
className="product-set-recurrence-icon-calendar"
|
|
31
|
+
/>
|
|
32
|
+
<span className="product-set-recurrence-title d-flex flex-grow-1">
|
|
33
|
+
{formatDate(menuRecurrence.startDate, {
|
|
34
|
+
dateStyle: "medium",
|
|
35
|
+
})}{" "}
|
|
36
|
+
</span>
|
|
37
|
+
|
|
38
|
+
<div
|
|
39
|
+
className="d-inline-flex"
|
|
40
|
+
role="button"
|
|
41
|
+
tabIndex={0}
|
|
42
|
+
onClick={() => onEdit(menuRecurrence)}
|
|
43
|
+
onKeyDown={() => onEdit(menuRecurrence)}
|
|
44
|
+
>
|
|
45
|
+
<Icon
|
|
46
|
+
type="ellipsis-h"
|
|
47
|
+
height="1rem"
|
|
48
|
+
className="product-set-recurrence-icon-more"
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
</span>
|
|
52
|
+
<span className="product-set-recurrence-description">
|
|
53
|
+
{formatDate(menuRecurrence.startDate, {
|
|
54
|
+
dateStyle: "medium",
|
|
55
|
+
})}{" "}
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
<div className={`manual-form-error ${!errorMessage && "d-none"}`}>
|
|
59
|
+
{errorMessage}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
};
|