@licklist/design 0.44.542 → 0.44.544
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/dist/availability-indicator/AvailabilityIndicator.d.ts +5 -4
- package/dist/availability-indicator/AvailabilityIndicator.d.ts.map +1 -1
- package/dist/availability-indicator/AvailabilityIndicator.js +1 -1
- package/dist/calendar/Calendar.d.ts +2 -4
- package/dist/calendar/Calendar.d.ts.map +1 -1
- package/dist/date-time-button/DateTimeButton.d.ts +4 -5
- package/dist/date-time-button/DateTimeButton.d.ts.map +1 -1
- package/dist/date-time-button/DateTimeButton.js +1 -1
- package/dist/events/edit-event-modal/utils/getDefaultProductSet.d.ts.map +1 -1
- package/dist/events/edit-event-modal/utils/getDefaultProductSet.js +1 -1
- package/dist/events/event-card/utils.d.ts +27 -0
- package/dist/events/event-card/utils.d.ts.map +1 -1
- package/dist/events/event-card/utils.js +1 -1
- package/dist/events/event-statistic-modal/EventStatisticModal.d.ts +3 -1
- package/dist/events/event-statistic-modal/EventStatisticModal.d.ts.map +1 -1
- package/dist/events/event-statistic-modal/EventStatisticModal.js +1 -1
- package/dist/events/event-statistic-modal/hooks/useTableData.d.ts +2 -1
- package/dist/events/event-statistic-modal/hooks/useTableData.d.ts.map +1 -1
- package/dist/events/event-statistic-modal/hooks/useTableData.js +1 -1
- package/dist/events/event-statistic-modal/utils/index.d.ts +1 -1
- package/dist/events/event-statistic-modal/utils/index.d.ts.map +1 -1
- package/dist/events/event-statistic-modal/utils/index.js +1 -1
- package/dist/iframe/activity-card/ActivityCard.d.ts +1 -3
- package/dist/iframe/activity-card/ActivityCard.d.ts.map +1 -1
- package/dist/iframe/activity-card/ActivityCard.js +1 -1
- package/dist/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.d.ts +3 -1
- package/dist/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.d.ts.map +1 -1
- package/dist/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.js +1 -1
- package/dist/product-set/control/DateAndRecurrenceInput.d.ts +5 -1
- package/dist/product-set/control/DateAndRecurrenceInput.d.ts.map +1 -1
- package/dist/product-set/control/DateAndRecurrenceInput.js +1 -1
- package/dist/product-set/control/ProductSetControl.d.ts +4 -2
- 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/ProductSetRecurrenceControl.d.ts +2 -1
- package/dist/product-set/control/ProductSetRecurrenceControl.d.ts.map +1 -1
- package/dist/product-set/control/ProductSetRecurrenceControl.js +1 -1
- package/dist/product-set/form/ProductSetForm.d.ts +3 -1
- package/dist/product-set/form/ProductSetForm.d.ts.map +1 -1
- package/dist/product-set/form/ProductSetForm.js +1 -1
- package/dist/product-set/utils/index.d.ts +10 -0
- package/dist/product-set/utils/index.d.ts.map +1 -1
- package/dist/product-set/utils/index.js +1 -1
- package/dist/recurring-date-picker-input/RecurringDatePickerInput.js +1 -1
- package/dist/sales/payment-form/SalePaymentForm.js +1 -1
- package/dist/styles/activity-card/GridActivitiesCard.scss +0 -8
- package/dist/styles/form/CustomCheckbox.scss +1 -0
- package/dist/styles/zones/ZoneForm.scss +3 -0
- package/dist/zone/form/ZoneForm.d.ts +1 -1
- package/dist/zone/form/ZoneForm.d.ts.map +1 -1
- package/dist/zone/form/ZoneForm.js +1 -1
- package/dist/zone/form/components/AvailableTimesControl.d.ts +2 -0
- package/dist/zone/form/components/AvailableTimesControl.d.ts.map +1 -1
- package/dist/zone/form/components/AvailableTimesControl.js +1 -1
- package/dist/zone/form/components/ZoneControl.d.ts +1 -1
- package/dist/zone/form/components/ZoneControl.d.ts.map +1 -1
- package/dist/zone/form/components/ZoneControl.js +1 -1
- package/dist/zone/form/components/ZoneRecurrencesControl.d.ts +1 -1
- package/dist/zone/form/components/ZoneRecurrencesControl.d.ts.map +1 -1
- package/dist/zone/form/components/ZoneRecurrencesControl.js +1 -1
- package/package.json +1 -1
- package/src/availability-indicator/AvailabilityIndicator.tsx +41 -33
- package/src/calendar/Calendar.tsx +2 -3
- package/src/date-time-button/DateTimeButton.tsx +11 -6
- package/src/events/edit-event-modal/component/EditEventForm/EditEventForm.stories.tsx +0 -3
- package/src/events/edit-event-modal/utils/getDefaultProductSet.ts +0 -1
- package/src/events/event-card/utils.ts +71 -17
- package/src/events/event-statistic-modal/EventStatisticModal.tsx +5 -1
- package/src/events/event-statistic-modal/hooks/useTableData.tsx +12 -5
- package/src/events/event-statistic-modal/utils/index.ts +39 -34
- package/src/iframe/activity-card/ActivityCard.stories.tsx +27 -51
- package/src/iframe/activity-card/ActivityCard.tsx +2 -13
- package/src/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.tsx +13 -32
- package/src/product-set/control/DateAndRecurrenceInput.tsx +73 -2
- package/src/product-set/control/ProductSetControl.stories.tsx +0 -2
- package/src/product-set/control/ProductSetControl.tsx +8 -10
- package/src/product-set/control/ProductSetRecurrenceControl.tsx +6 -1
- package/src/product-set/form/ProductSetForm.stories.tsx +6 -6
- package/src/product-set/form/ProductSetForm.tsx +19 -3
- package/src/product-set/utils/index.ts +38 -0
- package/src/styles/activity-card/GridActivitiesCard.scss +0 -8
- package/src/styles/form/CustomCheckbox.scss +1 -0
- package/src/styles/zones/ZoneForm.scss +3 -0
- package/src/zone/form/ZoneForm.tsx +1 -2
- package/src/zone/form/components/AvailableTimesControl.tsx +89 -83
- package/src/zone/form/components/ZoneControl.tsx +2 -5
- package/src/zone/form/components/ZoneRecurrencesControl.tsx +2 -51
|
@@ -4,10 +4,14 @@ import { useIntl } from "react-intl";
|
|
|
4
4
|
import { useTranslation } from "react-i18next";
|
|
5
5
|
import * as Config from "@licklist/core/dist/Config";
|
|
6
6
|
import { EventStatistic } from "@licklist/core/dist/DataMapper/Provider/EventStatisticDataMapper";
|
|
7
|
+
import { Event } from "@licklist/core/dist/DataMapper/Provider/EventDataMapper";
|
|
7
8
|
import { convertEventStatisticToTableData, TRANSLATION_KEYS } from "../utils";
|
|
8
9
|
import { StaticTableData } from "../../../table";
|
|
9
10
|
|
|
10
|
-
export const useTableData = (
|
|
11
|
+
export const useTableData = (
|
|
12
|
+
date: Event["startAt"] | null,
|
|
13
|
+
eventStatistic: EventStatistic | null
|
|
14
|
+
) => {
|
|
11
15
|
const { t } = useTranslation("Design");
|
|
12
16
|
const { formatNumber } = useIntl();
|
|
13
17
|
const [tableRows, setTableRows] = useState<StaticTableData[]>([]);
|
|
@@ -28,14 +32,17 @@ export const useTableData = (eventStatistic: EventStatistic | null) => {
|
|
|
28
32
|
return t(key);
|
|
29
33
|
};
|
|
30
34
|
|
|
31
|
-
const transformStatisticToTableRows = (
|
|
35
|
+
const transformStatisticToTableRows = (
|
|
36
|
+
date: Event["startAt"],
|
|
37
|
+
statistic: EventStatistic | null
|
|
38
|
+
) => {
|
|
32
39
|
if (!statistic) {
|
|
33
40
|
return [];
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
const tableDataRows: StaticTableData[] = [];
|
|
37
44
|
|
|
38
|
-
convertEventStatisticToTableData(statistic).forEach(
|
|
45
|
+
convertEventStatisticToTableData(date, statistic).forEach(
|
|
39
46
|
({ name, quantity, total, isBold, isHeader, key }) => {
|
|
40
47
|
const formattedTotal = !Object.keys(TRANSLATION_KEYS).includes(
|
|
41
48
|
String(total)
|
|
@@ -77,9 +84,9 @@ export const useTableData = (eventStatistic: EventStatistic | null) => {
|
|
|
77
84
|
};
|
|
78
85
|
|
|
79
86
|
useEffect(() => {
|
|
80
|
-
setTableRows(transformStatisticToTableRows(eventStatistic));
|
|
87
|
+
setTableRows(transformStatisticToTableRows(date, eventStatistic));
|
|
81
88
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
82
|
-
}, [eventStatistic]);
|
|
89
|
+
}, [date, eventStatistic]);
|
|
83
90
|
|
|
84
91
|
return tableRows;
|
|
85
92
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { EventStatistic } from "@licklist/core/dist/DataMapper/Provider/EventStatisticDataMapper";
|
|
2
|
+
import { DateTime } from "luxon";
|
|
3
|
+
import { DATE_TIME_FULL_FORMAT } from "@licklist/core/dist/Config";
|
|
2
4
|
|
|
3
5
|
export const TRANSLATION_KEYS = {
|
|
4
6
|
shortQuantity: "shortQuantity",
|
|
@@ -7,6 +9,7 @@ export const TRANSLATION_KEYS = {
|
|
|
7
9
|
};
|
|
8
10
|
|
|
9
11
|
export const convertEventStatisticToTableData = (
|
|
12
|
+
date: string,
|
|
10
13
|
eventStatistic: EventStatistic
|
|
11
14
|
) => {
|
|
12
15
|
let totalAmount = 0;
|
|
@@ -25,49 +28,51 @@ export const convertEventStatisticToTableData = (
|
|
|
25
28
|
total: string | number;
|
|
26
29
|
}[] = [];
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
(
|
|
30
|
-
|
|
31
|
-
({ totalPerCategory, productsSummary = [], categoryName, eventId }) => {
|
|
32
|
-
let quantity = 0;
|
|
31
|
+
const summaryFormattedDate = DateTime.fromISO(date)
|
|
32
|
+
.toUTC()
|
|
33
|
+
.toFormat(DATE_TIME_FULL_FORMAT);
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
Object.values(
|
|
36
|
+
eventStatistic?.productCategorySummary[summaryFormattedDate] ?? {}
|
|
37
|
+
)?.forEach(
|
|
38
|
+
({ totalPerCategory, productsSummary = [], categoryName, eventId }) => {
|
|
39
|
+
let quantity = 0;
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
name: categoryName,
|
|
38
|
-
key: `${eventId}.${categoryName}`,
|
|
39
|
-
isBold: true,
|
|
40
|
-
isHeader: true,
|
|
41
|
-
quantity: TRANSLATION_KEYS.shortQuantity,
|
|
42
|
-
total: TRANSLATION_KEYS.total,
|
|
43
|
-
});
|
|
41
|
+
totalAmount += totalPerCategory || 0;
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
tableDataArray.push({
|
|
44
|
+
name: categoryName,
|
|
45
|
+
key: `${eventId}.${categoryName}`,
|
|
46
|
+
isBold: true,
|
|
47
|
+
isHeader: true,
|
|
48
|
+
quantity: TRANSLATION_KEYS.shortQuantity,
|
|
49
|
+
total: TRANSLATION_KEYS.total,
|
|
50
|
+
});
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
quantity: totalQuantity
|
|
53
|
-
? `${productsSold} / ${totalQuantity}`
|
|
54
|
-
: String(productsSold),
|
|
55
|
-
total: String(total),
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
);
|
|
52
|
+
productsSummary?.forEach(
|
|
53
|
+
({ productsSold, name, totalQuantity, total }) => {
|
|
54
|
+
quantity += productsSold;
|
|
59
55
|
|
|
60
56
|
tableDataArray.push({
|
|
61
|
-
name
|
|
62
|
-
key: `${eventId}.${categoryName}.${
|
|
63
|
-
quantity
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
name,
|
|
58
|
+
key: `${eventId}.${categoryName}.${name}`,
|
|
59
|
+
quantity: totalQuantity
|
|
60
|
+
? `${productsSold} / ${totalQuantity}`
|
|
61
|
+
: String(productsSold),
|
|
62
|
+
total: String(total),
|
|
66
63
|
});
|
|
67
|
-
|
|
68
|
-
totalQuantity += quantity;
|
|
69
64
|
}
|
|
70
65
|
);
|
|
66
|
+
|
|
67
|
+
tableDataArray.push({
|
|
68
|
+
name: TRANSLATION_KEYS.totalPerCategory,
|
|
69
|
+
key: `${eventId}.${categoryName}.${TRANSLATION_KEYS.totalPerCategory}`,
|
|
70
|
+
quantity,
|
|
71
|
+
total: totalPerCategory,
|
|
72
|
+
isBold: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
totalQuantity += quantity;
|
|
71
76
|
}
|
|
72
77
|
);
|
|
73
78
|
|
|
@@ -1,75 +1,51 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Meta, Story } from "@storybook/react";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ActivityCard,
|
|
5
|
+
ActivityCardProps,
|
|
6
|
+
LAYOUT_GRID,
|
|
7
|
+
LAYOUT_LIST,
|
|
8
|
+
} from "./ActivityCard";
|
|
4
9
|
|
|
5
10
|
export default {
|
|
6
11
|
title: "Iframe/ActivityCard",
|
|
7
12
|
component: ActivityCard,
|
|
8
13
|
} as Meta;
|
|
9
14
|
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
};
|
|
15
|
+
export const GridView: Story<ActivityCardProps> = (props) => {
|
|
16
|
+
const [isSelected, setIsSelected] = React.useState(false);
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
imageType: "cover",
|
|
22
|
-
imageableId: 25,
|
|
23
|
-
imageableType: "zone",
|
|
24
|
-
path: "images/original/000/000/000/000/195-0b37b130e22aa2d3.jpeg",
|
|
25
|
-
// eslint-disable-next-line max-len
|
|
26
|
-
url: "https://cdn.lickli.st/doNktZRze1yuz3Uo2OPrLSvXVtUcdUsF/images/original/000/000/000/000/195-0b37b130e22aa2d3.jpeg",
|
|
27
|
-
},
|
|
28
|
-
onSelect: () => null,
|
|
29
|
-
isSelected: false,
|
|
18
|
+
return (
|
|
19
|
+
<ActivityCard
|
|
20
|
+
{...props}
|
|
21
|
+
isSelected={isSelected}
|
|
22
|
+
onSelect={() => setIsSelected(true)}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
30
25
|
};
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
Selected.args = {
|
|
27
|
+
GridView.args = {
|
|
28
|
+
layout: LAYOUT_GRID,
|
|
37
29
|
title: "Clay Pigeons & Axe Thowing",
|
|
38
30
|
duration: "60 mins",
|
|
39
31
|
price: "from £20",
|
|
40
|
-
image: {
|
|
41
|
-
hash: "0b37b130e22aa2d3",
|
|
42
|
-
id: 195,
|
|
43
|
-
imageType: "cover",
|
|
44
|
-
imageableId: 25,
|
|
45
|
-
imageableType: "zone",
|
|
46
|
-
path: "images/original/000/000/000/000/195-0b37b130e22aa2d3.jpeg",
|
|
47
|
-
// eslint-disable-next-line max-len
|
|
48
|
-
url: "https://cdn.lickli.st/doNktZRze1yuz3Uo2OPrLSvXVtUcdUsF/images/original/000/000/000/000/195-0b37b130e22aa2d3.jpeg",
|
|
49
|
-
},
|
|
50
|
-
onSelect: () => null,
|
|
51
|
-
isSelected: true,
|
|
52
32
|
};
|
|
53
33
|
|
|
54
34
|
export const ListView: Story<ActivityCardProps> = (props) => {
|
|
55
|
-
|
|
35
|
+
const [isSelected, setIsSelected] = React.useState(false);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<ActivityCard
|
|
39
|
+
{...props}
|
|
40
|
+
isSelected={isSelected}
|
|
41
|
+
onSelect={() => setIsSelected(true)}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
56
44
|
};
|
|
57
45
|
|
|
58
|
-
|
|
46
|
+
ListView.args = {
|
|
59
47
|
layout: LAYOUT_LIST,
|
|
60
48
|
title: "Clay Pigeons & Axe Thowing",
|
|
61
49
|
duration: "60 mins",
|
|
62
50
|
price: "from £20",
|
|
63
|
-
image: {
|
|
64
|
-
hash: "0b37b130e22aa2d3",
|
|
65
|
-
id: 195,
|
|
66
|
-
imageType: "cover",
|
|
67
|
-
imageableId: 25,
|
|
68
|
-
imageableType: "zone",
|
|
69
|
-
path: "images/original/000/000/000/000/195-0b37b130e22aa2d3.jpeg",
|
|
70
|
-
// eslint-disable-next-line max-len
|
|
71
|
-
url: "https://cdn.lickli.st/doNktZRze1yuz3Uo2OPrLSvXVtUcdUsF/images/original/000/000/000/000/195-0b37b130e22aa2d3.jpeg",
|
|
72
|
-
},
|
|
73
|
-
onSelect: () => null,
|
|
74
|
-
isSelected: false,
|
|
75
51
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { ReactNode } from "react";
|
|
2
2
|
import clsx from "clsx";
|
|
3
|
-
import { Image } from "@licklist/core/dist/DataMapper/Media/ImageDataMapper";
|
|
4
3
|
|
|
5
4
|
export const LAYOUT_GRID = "grid";
|
|
6
5
|
export const LAYOUT_LIST = "list";
|
|
@@ -11,7 +10,6 @@ export type ActivityCardProps = {
|
|
|
11
10
|
title: ReactNode;
|
|
12
11
|
duration: ReactNode;
|
|
13
12
|
price: ReactNode;
|
|
14
|
-
image?: Image | null;
|
|
15
13
|
onSelect: () => void;
|
|
16
14
|
isSelected: boolean;
|
|
17
15
|
layout?: Layout;
|
|
@@ -21,7 +19,7 @@ export const ActivityCard = ({
|
|
|
21
19
|
title,
|
|
22
20
|
duration,
|
|
23
21
|
price,
|
|
24
|
-
image,
|
|
22
|
+
// image,
|
|
25
23
|
onSelect,
|
|
26
24
|
isSelected,
|
|
27
25
|
layout = LAYOUT_GRID,
|
|
@@ -33,10 +31,7 @@ export const ActivityCard = ({
|
|
|
33
31
|
className={clsx("activity-card", isSelected && "active")}
|
|
34
32
|
onClick={onSelect}
|
|
35
33
|
>
|
|
36
|
-
{
|
|
37
|
-
<img className="activity-card-image" alt="" src={image.url} />
|
|
38
|
-
)}
|
|
39
|
-
<div className={clsx("d-flex", "flex-column", !image && "no-image")}>
|
|
34
|
+
<div className={clsx("d-flex", "flex-column")}>
|
|
40
35
|
<div className="activity-card-title">{title}</div>
|
|
41
36
|
|
|
42
37
|
{duration && <div>{duration}</div>}
|
|
@@ -64,12 +59,6 @@ export const ActivityCard = ({
|
|
|
64
59
|
|
|
65
60
|
{price && <div>{price}</div>}
|
|
66
61
|
</div>
|
|
67
|
-
|
|
68
|
-
{image && (
|
|
69
|
-
<div className="image-container">
|
|
70
|
-
<img className="image" src={image.url} alt="" />
|
|
71
|
-
</div>
|
|
72
|
-
)}
|
|
73
62
|
</div>
|
|
74
63
|
<hr className="list-activity-card-hr" />
|
|
75
64
|
</div>
|
|
@@ -3,6 +3,7 @@ import clsx from "clsx";
|
|
|
3
3
|
import { Button } from "react-bootstrap";
|
|
4
4
|
import { useTranslation } from "react-i18next";
|
|
5
5
|
import { FieldValues, RefCallBack, UseFormClearErrors } from "react-hook-form";
|
|
6
|
+
import { Zone } from "@licklist/core/dist/DataMapper/Provider/ZoneDataMapper";
|
|
6
7
|
import { NumberInput } from "../NumberInput";
|
|
7
8
|
import { Product, ProductCategory } from "../../../../../../types";
|
|
8
9
|
|
|
@@ -11,6 +12,7 @@ interface FormOrderItem {
|
|
|
11
12
|
name: string;
|
|
12
13
|
price: number;
|
|
13
14
|
productsCategoryId: number;
|
|
15
|
+
zoneId?: Zone["id"];
|
|
14
16
|
deposit?: number | null;
|
|
15
17
|
quantity: number;
|
|
16
18
|
capacity?: number | null;
|
|
@@ -30,7 +32,7 @@ interface ProductQuantityInputProps {
|
|
|
30
32
|
export const ProductQuantityInput = ({
|
|
31
33
|
product,
|
|
32
34
|
category,
|
|
33
|
-
onChange,
|
|
35
|
+
onChange: _onChange,
|
|
34
36
|
clearErrors,
|
|
35
37
|
refCallback,
|
|
36
38
|
productInfo,
|
|
@@ -39,14 +41,15 @@ export const ProductQuantityInput = ({
|
|
|
39
41
|
}: ProductQuantityInputProps) => {
|
|
40
42
|
const { t } = useTranslation("Design");
|
|
41
43
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
+
const onChange = (quantity: number | null) => {
|
|
45
|
+
_onChange({
|
|
44
46
|
id: product.id,
|
|
45
47
|
name: product.name,
|
|
46
48
|
deposit,
|
|
47
49
|
price: product.price,
|
|
48
50
|
productsCategoryId: category.id,
|
|
49
|
-
|
|
51
|
+
zoneId: category.zone?.id,
|
|
52
|
+
quantity,
|
|
50
53
|
capacity: product?.capacity,
|
|
51
54
|
});
|
|
52
55
|
};
|
|
@@ -58,6 +61,7 @@ export const ProductQuantityInput = ({
|
|
|
58
61
|
</div>
|
|
59
62
|
);
|
|
60
63
|
}
|
|
64
|
+
|
|
61
65
|
if (product?.maxAmount === 1) {
|
|
62
66
|
return (
|
|
63
67
|
<Button
|
|
@@ -69,49 +73,26 @@ export const ProductQuantityInput = ({
|
|
|
69
73
|
}`,
|
|
70
74
|
invalid && "error"
|
|
71
75
|
)}
|
|
72
|
-
onClick={
|
|
76
|
+
onClick={() => onChange(productInfo?.quantity ? 0 : 1)}
|
|
73
77
|
>
|
|
74
78
|
{t(`Design:${productInfo?.quantity ? "unselect" : "select"}`)}
|
|
75
79
|
</Button>
|
|
76
80
|
);
|
|
77
81
|
}
|
|
82
|
+
|
|
78
83
|
return (
|
|
79
84
|
<NumberInput
|
|
80
85
|
ref={refCallback}
|
|
81
86
|
onChange={(val) => {
|
|
82
|
-
onChange(
|
|
83
|
-
id: product.id,
|
|
84
|
-
name: product.name,
|
|
85
|
-
deposit,
|
|
86
|
-
price: product.price,
|
|
87
|
-
productsCategoryId: category.id,
|
|
88
|
-
quantity: val || null,
|
|
89
|
-
capacity: product?.capacity,
|
|
90
|
-
});
|
|
87
|
+
onChange(val || null);
|
|
91
88
|
clearErrors(`${product.id}` as const);
|
|
92
89
|
}}
|
|
93
90
|
onArrowDown={() => {
|
|
94
|
-
onChange(
|
|
95
|
-
id: product.id,
|
|
96
|
-
name: product.name,
|
|
97
|
-
deposit,
|
|
98
|
-
price: product.price,
|
|
99
|
-
productsCategoryId: category.id,
|
|
100
|
-
quantity: productInfo?.quantity - 1 || 0,
|
|
101
|
-
capacity: product?.capacity,
|
|
102
|
-
});
|
|
91
|
+
onChange(productInfo?.quantity - 1 || 0);
|
|
103
92
|
clearErrors(`${product.id}` as const);
|
|
104
93
|
}}
|
|
105
94
|
onArrowUp={() => {
|
|
106
|
-
onChange(
|
|
107
|
-
id: product.id,
|
|
108
|
-
name: product.name,
|
|
109
|
-
deposit,
|
|
110
|
-
price: product.price,
|
|
111
|
-
productsCategoryId: category.id,
|
|
112
|
-
quantity: (productInfo?.quantity ?? 0) + 1,
|
|
113
|
-
capacity: product?.capacity,
|
|
114
|
-
});
|
|
95
|
+
onChange((productInfo?.quantity ?? 0) + 1);
|
|
115
96
|
clearErrors(`${product.id}` as const);
|
|
116
97
|
}}
|
|
117
98
|
min={0}
|
|
@@ -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 = (
|
|
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
|
};
|