mautourco-components 0.2.76 → 0.2.78
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/components/atoms/Chip/Chip.css +32 -1
- package/dist/components/molecules/VehicleSupplement/VehicleSupplement.css +85 -0
- package/dist/components/molecules/VehicleSupplement/VehicleSupplement.d.ts +31 -0
- package/dist/components/molecules/VehicleSupplement/VehicleSupplement.js +101 -0
- package/dist/components/molecules/VehicleSupplement/index.d.ts +2 -0
- package/dist/components/molecules/VehicleSupplement/index.js +1 -0
- package/dist/components/organisms/CarBookingCard/CarBookingCard.css +122 -1
- package/dist/components/organisms/CarBookingCard/CarBookingCard.d.ts +12 -6
- package/dist/components/organisms/CarBookingCard/CarBookingCard.js +87 -10
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
- package/src/components/atoms/Chip/Chip.css +32 -1
- package/src/components/molecules/VehicleSupplement/VehicleSupplement.css +81 -0
- package/src/components/molecules/VehicleSupplement/VehicleSupplement.tsx +206 -0
- package/src/components/molecules/VehicleSupplement/index.ts +2 -0
- package/src/components/organisms/CarBookingCard/CarBookingCard.css +121 -1
- package/src/components/organisms/CarBookingCard/CarBookingCard.tsx +154 -36
|
@@ -162,11 +162,42 @@
|
|
|
162
162
|
opacity: 0.6;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
/* Focus styles - specific to each color */
|
|
166
|
+
.chip--clickable.chip--brand:focus {
|
|
166
167
|
outline: 2px solid var(--chip-color-brand-outline-foreground, #ed4c09);
|
|
167
168
|
outline-offset: 2px;
|
|
168
169
|
}
|
|
169
170
|
|
|
171
|
+
.chip--clickable.chip--accent:focus {
|
|
172
|
+
outline: 2px solid var(--chip-color-accent-outline-foreground, #0f7173);
|
|
173
|
+
outline-offset: 2px;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.chip--clickable.chip--blue:focus {
|
|
177
|
+
outline: 2px solid var(--chip-color-blue-outline-foreground, #2e4780);
|
|
178
|
+
outline-offset: 2px;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.chip--clickable.chip--green:focus {
|
|
182
|
+
outline: 2px solid var(--chip-color-green-outline-foreground, #4a6045);
|
|
183
|
+
outline-offset: 2px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.chip--clickable.chip--yellow:focus {
|
|
187
|
+
outline: 2px solid var(--chip-color-yellow-outline-foreground, #eab308);
|
|
188
|
+
outline-offset: 2px;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.chip--clickable.chip--red:focus {
|
|
192
|
+
outline: 2px solid var(--chip-color-red-outline-foreground, #991b1b);
|
|
193
|
+
outline-offset: 2px;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.chip--clickable.chip--neutral:focus {
|
|
197
|
+
outline: 2px solid var(--chip-color-neutral-outline-foreground, #9ca3af);
|
|
198
|
+
outline-offset: 2px;
|
|
199
|
+
}
|
|
200
|
+
|
|
170
201
|
.chip--outline.chip--black-text {
|
|
171
202
|
color: var(--chip-color-neutral-outline-text, #262626);
|
|
172
203
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Do not edit directly, this file was auto-generated.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
.vehicle-supplement {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: var(--spacing-gap-gap-6, 24px);
|
|
9
|
+
padding: var(--spacing-padding-px-0, 0px);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.vehicle-supplement__content {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
gap: var(--spacing-gap-gap-4, 16px);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.vehicle-supplement__transfer-section {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
gap: var(--spacing-gap-gap-4, 16px);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.vehicle-supplement__transfer-header {
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
height: 44px;
|
|
29
|
+
padding: var(--spacing-gap-gap-0, 0px);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.vehicle-supplement__transfer-title {
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
gap: var(--spacing-gap-gap-2, 8px);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.vehicle-supplement__transfer-label {
|
|
39
|
+
font-family: var(--font-font-family-body, "Satoshi"), "Inter", "Segoe UI", "system-ui", sans-serif;
|
|
40
|
+
font-weight: var(--font-weight-font-bold, 700);
|
|
41
|
+
font-size: var(--font-size-text-base, 16px);
|
|
42
|
+
line-height: calc(var(--font-leading-leading-md, 24) * 1px);
|
|
43
|
+
color: var(--checkbox-color-label-default, #303642);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.vehicle-supplement__supplements-list {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-direction: column;
|
|
49
|
+
gap: var(--spacing-gap-gap-3, 12px);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.vehicle-supplement__supplement-row {
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
justify-content: space-between;
|
|
56
|
+
height: 32px;
|
|
57
|
+
padding: var(--spacing-padding-px-0, 0px);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.vehicle-supplement__supplement-name {
|
|
61
|
+
font-family: var(--font-font-family-body, "Satoshi"), "Inter", "Segoe UI", "system-ui", sans-serif;
|
|
62
|
+
font-weight: var(--font-weight-font-medium, 500);
|
|
63
|
+
font-size: var(--font-size-text-base, 16px);
|
|
64
|
+
line-height: calc(var(--font-leading-leading-md, 24) * 1px);
|
|
65
|
+
color: var(--checkbox-color-label-default, #303642);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.vehicle-supplement__divider {
|
|
69
|
+
width: 100%;
|
|
70
|
+
height: 0;
|
|
71
|
+
border-top: 1px solid var(--color-border-default, #d9d9d9);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.vehicle-supplement__footer {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: flex-end;
|
|
78
|
+
gap: var(--spacing-gap-gap-4, 16px);
|
|
79
|
+
height: 36px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.vehicle-supplement__cancel-button,
|
|
83
|
+
.vehicle-supplement__done-button {
|
|
84
|
+
min-width: 114px;
|
|
85
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TransferDocket } from '../../../types/docket/services.types';
|
|
3
|
+
import './VehicleSupplement.css';
|
|
4
|
+
export interface Supplement {
|
|
5
|
+
supplement_name: string;
|
|
6
|
+
max?: number;
|
|
7
|
+
min?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface SupplementValue {
|
|
10
|
+
transferId: number;
|
|
11
|
+
supplementName: string;
|
|
12
|
+
value: number;
|
|
13
|
+
}
|
|
14
|
+
export interface VehicleSupplementProps {
|
|
15
|
+
/** Array of available supplements */
|
|
16
|
+
supplements: Supplement[];
|
|
17
|
+
/** Array of transfers (Arrival, Inter-Hotel, Departure) */
|
|
18
|
+
transfer: TransferDocket[];
|
|
19
|
+
/** Handler called when supplement values change */
|
|
20
|
+
onChange: (values: SupplementValue[]) => void;
|
|
21
|
+
/** Handler called when Done button is clicked */
|
|
22
|
+
onDone: (values: SupplementValue[]) => void;
|
|
23
|
+
/** Handler called when Clear/Cancel button is clicked */
|
|
24
|
+
onClear: () => void;
|
|
25
|
+
/** Initial values for the supplements (optional) */
|
|
26
|
+
initialValues?: SupplementValue[];
|
|
27
|
+
/** Additional CSS classes */
|
|
28
|
+
className?: string;
|
|
29
|
+
}
|
|
30
|
+
declare const VehicleSupplement: React.FC<VehicleSupplementProps>;
|
|
31
|
+
export default VehicleSupplement;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { useState } from 'react';
|
|
14
|
+
import Button from '../../atoms/Button/Button';
|
|
15
|
+
import Chip from '../../atoms/Chip/Chip';
|
|
16
|
+
import Icon from '../../atoms/Icon/Icon';
|
|
17
|
+
import Stepper from '../Stepper/Stepper';
|
|
18
|
+
import './VehicleSupplement.css';
|
|
19
|
+
var VehicleSupplement = function (_a) {
|
|
20
|
+
var supplements = _a.supplements, transfer = _a.transfer, onChange = _a.onChange, onDone = _a.onDone, onClear = _a.onClear, _b = _a.initialValues, initialValues = _b === void 0 ? [] : _b, _c = _a.className, className = _c === void 0 ? '' : _c;
|
|
21
|
+
// Initialize state for all supplements across all transfers
|
|
22
|
+
var _d = useState(function () {
|
|
23
|
+
// If initial values are provided, use them
|
|
24
|
+
if (initialValues.length > 0) {
|
|
25
|
+
return initialValues;
|
|
26
|
+
}
|
|
27
|
+
// Otherwise, create default values with 0
|
|
28
|
+
var defaultValues = [];
|
|
29
|
+
transfer.forEach(function (t) {
|
|
30
|
+
supplements.forEach(function (s) {
|
|
31
|
+
defaultValues.push({
|
|
32
|
+
transferId: t.IdTransfer,
|
|
33
|
+
supplementName: s.supplement_name,
|
|
34
|
+
value: 0,
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
return defaultValues;
|
|
39
|
+
}), supplementValues = _d[0], setSupplementValues = _d[1];
|
|
40
|
+
var getValue = function (transferId, supplementName) {
|
|
41
|
+
var item = supplementValues.find(function (v) { return v.transferId === transferId && v.supplementName === supplementName; });
|
|
42
|
+
return (item === null || item === void 0 ? void 0 : item.value) || 0;
|
|
43
|
+
};
|
|
44
|
+
var handleSupplementChange = function (transferId, supplementName, newValue) {
|
|
45
|
+
var updatedValues = supplementValues.map(function (v) {
|
|
46
|
+
return v.transferId === transferId && v.supplementName === supplementName
|
|
47
|
+
? __assign(__assign({}, v), { value: newValue }) : v;
|
|
48
|
+
});
|
|
49
|
+
setSupplementValues(updatedValues);
|
|
50
|
+
onChange(updatedValues);
|
|
51
|
+
};
|
|
52
|
+
var getTransferTypeIcon = function (transferType) {
|
|
53
|
+
var type = transferType.toLowerCase();
|
|
54
|
+
if (type.includes('arrival') || type.includes('airport') || type.includes('inbound')) {
|
|
55
|
+
return 'plane-landing-outline';
|
|
56
|
+
}
|
|
57
|
+
if (type.includes('departure') || type.includes('outbound')) {
|
|
58
|
+
return 'plane-takeoff-outline';
|
|
59
|
+
}
|
|
60
|
+
return 'building-2-outline';
|
|
61
|
+
};
|
|
62
|
+
var getTransferTypeLabel = function (transferType) {
|
|
63
|
+
var type = transferType.toLowerCase();
|
|
64
|
+
if (type.includes('arrival') || type.includes('airport') || type.includes('inbound')) {
|
|
65
|
+
return 'Arrival';
|
|
66
|
+
}
|
|
67
|
+
if (type.includes('departure') || type.includes('outbound')) {
|
|
68
|
+
return 'Departure';
|
|
69
|
+
}
|
|
70
|
+
return 'Inter-Hotel';
|
|
71
|
+
};
|
|
72
|
+
var formatDate = function (dateStr) {
|
|
73
|
+
try {
|
|
74
|
+
var date = new Date(dateStr);
|
|
75
|
+
var day = String(date.getDate()).padStart(2, '0');
|
|
76
|
+
var month = String(date.getMonth() + 1).padStart(2, '0');
|
|
77
|
+
var year = date.getFullYear();
|
|
78
|
+
return "".concat(day, "/").concat(month, "/").concat(year);
|
|
79
|
+
}
|
|
80
|
+
catch (_a) {
|
|
81
|
+
return dateStr;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var handleDone = function () {
|
|
85
|
+
onDone(supplementValues);
|
|
86
|
+
};
|
|
87
|
+
var baseClass = 'vehicle-supplement';
|
|
88
|
+
var classes = [baseClass, className].filter(Boolean).join(' ');
|
|
89
|
+
return (_jsxs("div", { className: classes, children: [_jsx("div", { className: "".concat(baseClass, "__content"), children: transfer.map(function (t, index) {
|
|
90
|
+
var isLast = index === transfer.length - 1;
|
|
91
|
+
var typeLabel = getTransferTypeLabel(t.TransferType);
|
|
92
|
+
var iconName = getTransferTypeIcon(t.TransferType);
|
|
93
|
+
return (_jsxs("div", { className: "".concat(baseClass, "__transfer-section"), children: [_jsxs("div", { className: "".concat(baseClass, "__transfer-header"), children: [_jsxs("div", { className: "".concat(baseClass, "__transfer-title"), children: [_jsx(Icon, { name: iconName, size: "sm" }), _jsx("span", { className: "".concat(baseClass, "__transfer-label"), children: typeLabel })] }), _jsx(Chip, { leadingIcon: "calendar", label: formatDate(t.TransferDate), size: "sm", color: "neutral", isBlackText: true })] }), _jsx("div", { className: "".concat(baseClass, "__supplements-list"), children: supplements.map(function (supplement) {
|
|
94
|
+
var currentValue = getValue(t.IdTransfer, supplement.supplement_name);
|
|
95
|
+
return (_jsxs("div", { className: "".concat(baseClass, "__supplement-row"), children: [_jsx("span", { className: "".concat(baseClass, "__supplement-name"), children: supplement.supplement_name }), _jsx(Stepper, { value: currentValue, min: supplement.min || 0, max: supplement.max || 99, onChange: function (value) {
|
|
96
|
+
return handleSupplementChange(t.IdTransfer, supplement.supplement_name, value);
|
|
97
|
+
} })] }, supplement.supplement_name));
|
|
98
|
+
}) }), !isLast && _jsx("div", { className: "".concat(baseClass, "__divider") })] }, t.IdTransfer));
|
|
99
|
+
}) }), _jsxs("div", { className: "".concat(baseClass, "__footer"), children: [_jsx(Button, { variant: "outline-secondary", size: "sm", onClick: onClear, className: "".concat(baseClass, "__cancel-button"), children: "Cancel" }), _jsx(Button, { variant: "secondary", size: "sm", onClick: handleDone, className: "".concat(baseClass, "__done-button"), children: "Done" })] })] }));
|
|
100
|
+
};
|
|
101
|
+
export default VehicleSupplement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './VehicleSupplement';
|
|
@@ -137,12 +137,15 @@
|
|
|
137
137
|
|
|
138
138
|
.car-booking-card__supplement-message {
|
|
139
139
|
margin: 0;
|
|
140
|
-
color: var(--color-messaging-danger-foreground, #a01414);
|
|
141
140
|
white-space: normal;
|
|
142
141
|
text-align: right;
|
|
143
142
|
flex: 1 1 auto;
|
|
144
143
|
}
|
|
145
144
|
|
|
145
|
+
.car-booking-card__supplement-header .car-booking-card__supplement-message--error {
|
|
146
|
+
color: var(--color-messaging-danger-foreground, #a01414);
|
|
147
|
+
}
|
|
148
|
+
|
|
146
149
|
.car-booking-card__section--supplement {
|
|
147
150
|
/* Control spacing precisely: divider -> header != header -> select */
|
|
148
151
|
gap: 0;
|
|
@@ -247,4 +250,122 @@
|
|
|
247
250
|
|
|
248
251
|
.car-booking-card__cta {
|
|
249
252
|
white-space: nowrap;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Supplement dropdown styles */
|
|
256
|
+
.car-booking-card__supplement-dropdown {
|
|
257
|
+
position: relative;
|
|
258
|
+
width: 100%;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.car-booking-card__supplement-trigger {
|
|
262
|
+
width: 100%;
|
|
263
|
+
display: flex;
|
|
264
|
+
align-items: center;
|
|
265
|
+
justify-content: space-between;
|
|
266
|
+
gap: var(--spacing-gap-gap-2, 8px);
|
|
267
|
+
padding: var(--spacing-padding-py-3, 12px) var(--spacing-padding-px-4, 16px);
|
|
268
|
+
background: var(--color-elevation-level-1, #ffffff);
|
|
269
|
+
border: var(--input-border-width-default, 1px) solid var(--color-border-default, #d4d4d4);
|
|
270
|
+
border-radius: var(--border-radius-rounded-xl, 12px);
|
|
271
|
+
font-family: var(--font-font-family-body, "Satoshi"), "Inter", "Segoe UI", "system-ui", sans-serif;
|
|
272
|
+
font-size: var(--font-size-text-base, 16px);
|
|
273
|
+
line-height: calc(var(--font-leading-leading-md, 24) * 1px);
|
|
274
|
+
color: var(--color-text-default, #262626);
|
|
275
|
+
cursor: pointer;
|
|
276
|
+
transition: border-color 0.2s ease, background-color 0.2s ease;
|
|
277
|
+
min-height: 48px;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.car-booking-card__supplement-trigger:hover:not(:disabled) {
|
|
281
|
+
border-color: var(--color-border-active-default, #0f7173);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.car-booking-card__supplement-trigger:focus {
|
|
285
|
+
outline: var(--border-width-focus, 2px) solid var(--color-border-active-default, #0f7173);
|
|
286
|
+
outline-offset: var(--spacing-base-0-5, 2px);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.car-booking-card__supplement-trigger--disabled {
|
|
290
|
+
background: var(--color-elevation-level-2, #f5f5f5);
|
|
291
|
+
cursor: not-allowed;
|
|
292
|
+
opacity: 0.6;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.car-booking-card__supplement-placeholder {
|
|
296
|
+
color: var(--color-text-subtle, #737373);
|
|
297
|
+
font-weight: var(--font-weight-font-regular, 400);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.car-booking-card__supplement-trigger--has-value .car-booking-card__supplement-placeholder {
|
|
301
|
+
color: var(--color-text-default, #262626);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.car-booking-card__supplement-chips {
|
|
305
|
+
display: flex;
|
|
306
|
+
flex-wrap: wrap;
|
|
307
|
+
gap: var(--spacing-gap-gap-2, 8px);
|
|
308
|
+
flex: 1;
|
|
309
|
+
align-items: center;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* Ensure chips are properly interactive within the button */
|
|
313
|
+
.car-booking-card__supplement-chips .chip {
|
|
314
|
+
cursor: pointer;
|
|
315
|
+
transition: opacity 0.15s ease, transform 0.15s ease;
|
|
316
|
+
-webkit-user-select: none;
|
|
317
|
+
user-select: none;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* Hover state for chips - subtle scale and opacity change */
|
|
321
|
+
.car-booking-card__supplement-chips .chip:hover {
|
|
322
|
+
opacity: 0.85;
|
|
323
|
+
transform: translateY(-1px);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* Active/pressed state for chips */
|
|
327
|
+
.car-booking-card__supplement-chips .chip:active {
|
|
328
|
+
opacity: 0.7;
|
|
329
|
+
transform: translateY(0);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/* Make close icon more prominent on chip hover */
|
|
333
|
+
.car-booking-card__supplement-chips .chip:hover .chip__icon--trailing {
|
|
334
|
+
opacity: 1;
|
|
335
|
+
transform: scale(1.1);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* Transition for chip icons */
|
|
339
|
+
.car-booking-card__supplement-chips .chip .chip__icon--trailing {
|
|
340
|
+
transition: transform 0.15s ease, opacity 0.15s ease;
|
|
341
|
+
opacity: 0.8;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.car-booking-card__supplement-icon {
|
|
345
|
+
transition: transform 0.2s ease;
|
|
346
|
+
flex-shrink: 0;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.car-booking-card__supplement-icon--open {
|
|
350
|
+
transform: rotate(180deg);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.car-booking-card__supplement-panel {
|
|
354
|
+
position: absolute;
|
|
355
|
+
top: calc(100% + var(--spacing-gap-gap-2, 8px));
|
|
356
|
+
left: 0;
|
|
357
|
+
right: 0;
|
|
358
|
+
z-index: 10;
|
|
359
|
+
background: var(--color-elevation-level-1, #ffffff);
|
|
360
|
+
border-radius: var(--border-radius-rounded-xl, 12px);
|
|
361
|
+
box-shadow:
|
|
362
|
+
var(--spacing-base-0, 0px) var(--spacing-base-1, 4px)
|
|
363
|
+
var(--backdrop-blur-backdrop-blur, 8px) var(--spacing-base-0, 0px)
|
|
364
|
+
rgba(48, 54, 66, 0.1),
|
|
365
|
+
var(--spacing-base-0, 0px) var(--spacing-base-px, 1px)
|
|
366
|
+
var(--backdrop-blur-backdrop-blur, 8px) var(--spacing-base-0, 0px)
|
|
367
|
+
rgba(48, 54, 66, 0.11);
|
|
368
|
+
padding: var(--spacing-padding-py-6, 24px);
|
|
369
|
+
max-height: 600px;
|
|
370
|
+
overflow-y: auto;
|
|
250
371
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { TransferDocket } from '../../../types/docket/services.types';
|
|
2
3
|
import { ButtonProps } from '../../atoms/Button/Button';
|
|
3
4
|
import { FeatureRowProps } from '../../molecules/FeatureRow/FeatureRow';
|
|
5
|
+
import { Supplement, SupplementValue } from '../../molecules/VehicleSupplement/VehicleSupplement';
|
|
4
6
|
import './CarBookingCard.css';
|
|
5
7
|
export type CarBookingCardSize = 'small' | 'large';
|
|
6
8
|
export type CarBookingCardState = 'default' | 'selected' | 'hover';
|
|
@@ -30,21 +32,25 @@ export interface CarBookingCardProps {
|
|
|
30
32
|
/** Supplement dropdown section */
|
|
31
33
|
/** Whether to render the supplement section (matches Figma variants Supplement=Yes/No/-) */
|
|
32
34
|
showSupplement?: boolean;
|
|
33
|
-
/** Optional message shown
|
|
35
|
+
/** Optional message shown next to the "Supplement" label (Figma: "Supplement is not available for this vehicle.") */
|
|
34
36
|
supplementMessage?: string;
|
|
37
|
+
/** State variant for the supplement message: 'error' applies danger/red styling, 'default' uses normal text color */
|
|
38
|
+
supplementMessageState?: 'default' | 'error';
|
|
35
39
|
supplementLabel?: string;
|
|
36
40
|
supplementPlaceholder?: string;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
/** Array of available supplements */
|
|
42
|
+
supplements?: Supplement[];
|
|
43
|
+
/** Array of transfers for supplement selection */
|
|
44
|
+
transfers?: TransferDocket[];
|
|
45
|
+
/** Handler when supplements are selected */
|
|
46
|
+
onSupplementChange?: (values: SupplementValue[]) => void;
|
|
41
47
|
/** Footer price */
|
|
42
48
|
totalPrice: string;
|
|
43
49
|
totalPriceLabel?: string;
|
|
44
50
|
/** Footer CTA */
|
|
45
51
|
ctaLabel: string;
|
|
46
52
|
ctaButtonProps?: Omit<ButtonProps, 'children'>;
|
|
47
|
-
onCtaClick?:
|
|
53
|
+
onCtaClick?: (event: React.MouseEvent<HTMLButtonElement>, supplements?: SupplementValue[]) => void;
|
|
48
54
|
/** Readonly mode - disables interactions and shows values as text */
|
|
49
55
|
readonly?: boolean;
|
|
50
56
|
className?: string;
|
|
@@ -10,24 +10,95 @@ var __assign = (this && this.__assign) || function () {
|
|
|
10
10
|
return __assign.apply(this, arguments);
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
-
import { useState } from 'react';
|
|
13
|
+
import { useEffect, useRef, useState } from 'react';
|
|
14
14
|
import Button from '../../atoms/Button/Button';
|
|
15
|
+
import Chip from '../../atoms/Chip/Chip';
|
|
15
16
|
import Divider from '../../atoms/Divider/Divider';
|
|
16
|
-
import
|
|
17
|
+
import Icon from '../../atoms/Icon/Icon';
|
|
17
18
|
import { Heading, Text } from '../../atoms/Typography/Typography';
|
|
18
19
|
import FeatureRow from '../../molecules/FeatureRow/FeatureRow';
|
|
20
|
+
import VehicleSupplement from '../../molecules/VehicleSupplement/VehicleSupplement';
|
|
19
21
|
import './CarBookingCard.css';
|
|
20
22
|
var CarBookingCard = function (_a) {
|
|
21
23
|
var _b;
|
|
22
|
-
var imageSrc = _a.imageSrc, title = _a.title, _c = _a.size, size = _c === void 0 ? 'large' : _c, _d = _a.state, state = _d === void 0 ? 'default' : _d, _e = _a.type, type = _e === void 0 ? 'default' : _e, features = _a.features, infoText = _a.infoText, _f = _a.priceTitle, priceTitle = _f === void 0 ? 'Price breakdown' : _f, _g = _a.priceRows, priceRows = _g === void 0 ? [] : _g, showSupplement = _a.showSupplement, supplementMessage = _a.supplementMessage, _h = _a.
|
|
23
|
-
var
|
|
24
|
-
var
|
|
25
|
-
var
|
|
26
|
-
var
|
|
27
|
-
|
|
24
|
+
var imageSrc = _a.imageSrc, title = _a.title, _c = _a.size, size = _c === void 0 ? 'large' : _c, _d = _a.state, state = _d === void 0 ? 'default' : _d, _e = _a.type, type = _e === void 0 ? 'default' : _e, features = _a.features, infoText = _a.infoText, _f = _a.priceTitle, priceTitle = _f === void 0 ? 'Price breakdown' : _f, _g = _a.priceRows, priceRows = _g === void 0 ? [] : _g, showSupplement = _a.showSupplement, supplementMessage = _a.supplementMessage, _h = _a.supplementMessageState, supplementMessageState = _h === void 0 ? 'error' : _h, _j = _a.supplementLabel, supplementLabel = _j === void 0 ? 'Supplement' : _j, _k = _a.supplementPlaceholder, supplementPlaceholder = _k === void 0 ? 'Select a supplement' : _k, _l = _a.supplements, supplements = _l === void 0 ? [] : _l, _m = _a.transfers, transfers = _m === void 0 ? [] : _m, onSupplementChange = _a.onSupplementChange, totalPrice = _a.totalPrice, _o = _a.totalPriceLabel, totalPriceLabel = _o === void 0 ? 'Total price' : _o, ctaLabel = _a.ctaLabel, ctaButtonProps = _a.ctaButtonProps, onCtaClick = _a.onCtaClick, _p = _a.readonly, readonly = _p === void 0 ? false : _p, _q = _a.className, className = _q === void 0 ? '' : _q;
|
|
25
|
+
var _r = useState(state === 'selected'), isSelected = _r[0], setIsSelected = _r[1];
|
|
26
|
+
var _s = useState(false), isHovered = _s[0], setIsHovered = _s[1];
|
|
27
|
+
var _t = useState(false), isSupplementOpen = _t[0], setIsSupplementOpen = _t[1];
|
|
28
|
+
var _u = useState([]), selectedSupplements = _u[0], setSelectedSupplements = _u[1];
|
|
29
|
+
var dropdownRef = useRef(null);
|
|
30
|
+
var panelRef = useRef(null);
|
|
31
|
+
var resolvedShowSupplement = showSupplement !== null && showSupplement !== void 0 ? showSupplement : Boolean(supplements.length > 0 && transfers.length > 0);
|
|
32
|
+
var hasSupplementsSelected = selectedSupplements.some(function (s) { return s.value > 0; });
|
|
33
|
+
// Close dropdown when clicking outside
|
|
34
|
+
useEffect(function () {
|
|
35
|
+
var handleClickOutside = function (event) {
|
|
36
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
37
|
+
setIsSupplementOpen(false);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
if (isSupplementOpen) {
|
|
41
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
42
|
+
}
|
|
43
|
+
return function () {
|
|
44
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
45
|
+
};
|
|
46
|
+
}, [isSupplementOpen]);
|
|
47
|
+
// Auto-scroll to panel when opened
|
|
48
|
+
useEffect(function () {
|
|
49
|
+
if (isSupplementOpen && panelRef.current) {
|
|
50
|
+
// Small delay to ensure the panel is rendered
|
|
51
|
+
setTimeout(function () {
|
|
52
|
+
var _a;
|
|
53
|
+
(_a = panelRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({
|
|
54
|
+
behavior: 'smooth',
|
|
55
|
+
block: 'nearest',
|
|
56
|
+
});
|
|
57
|
+
}, 100);
|
|
58
|
+
}
|
|
59
|
+
}, [isSupplementOpen]);
|
|
60
|
+
var getSupplementSummary = function () {
|
|
61
|
+
var summary = [];
|
|
62
|
+
var grouped = selectedSupplements.reduce(function (acc, item) {
|
|
63
|
+
if (item.value > 0) {
|
|
64
|
+
if (!acc[item.supplementName]) {
|
|
65
|
+
acc[item.supplementName] = 0;
|
|
66
|
+
}
|
|
67
|
+
acc[item.supplementName] += item.value;
|
|
68
|
+
}
|
|
69
|
+
return acc;
|
|
70
|
+
}, {});
|
|
71
|
+
Object.entries(grouped).forEach(function (_a) {
|
|
72
|
+
var name = _a[0], count = _a[1];
|
|
73
|
+
summary.push("".concat(name, " x").concat(count));
|
|
74
|
+
});
|
|
75
|
+
return summary;
|
|
76
|
+
};
|
|
77
|
+
var handleSupplementDone = function (values) {
|
|
78
|
+
setSelectedSupplements(values);
|
|
79
|
+
setIsSupplementOpen(false);
|
|
80
|
+
onSupplementChange === null || onSupplementChange === void 0 ? void 0 : onSupplementChange(values);
|
|
81
|
+
};
|
|
82
|
+
var handleSupplementClear = function () {
|
|
83
|
+
setIsSupplementOpen(false);
|
|
84
|
+
};
|
|
85
|
+
var handleSupplementToggle = function () {
|
|
86
|
+
if (!supplementMessage) {
|
|
87
|
+
setIsSupplementOpen(!isSupplementOpen);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var handleRemoveSupplement = function (supplementName) {
|
|
91
|
+
var updatedValues = selectedSupplements.map(function (s) {
|
|
92
|
+
return s.supplementName === supplementName ? __assign(__assign({}, s), { value: 0 }) : s;
|
|
93
|
+
});
|
|
94
|
+
setSelectedSupplements(updatedValues);
|
|
95
|
+
onSupplementChange === null || onSupplementChange === void 0 ? void 0 : onSupplementChange(updatedValues);
|
|
96
|
+
};
|
|
97
|
+
// Handle CTA click: toggle between "Add to quote" and "Selected" and pass supplement data
|
|
28
98
|
var handleCtaClick = function (e) {
|
|
29
99
|
setIsSelected(!isSelected);
|
|
30
|
-
|
|
100
|
+
// Pass the selected supplements to the parent component
|
|
101
|
+
onCtaClick === null || onCtaClick === void 0 ? void 0 : onCtaClick(e, selectedSupplements.length > 0 ? selectedSupplements : undefined);
|
|
31
102
|
};
|
|
32
103
|
// Determine button state based on selection and hover
|
|
33
104
|
var shouldShowRemove = isSelected && isHovered;
|
|
@@ -71,6 +142,12 @@ var CarBookingCard = function (_a) {
|
|
|
71
142
|
return (_jsxs("article", { className: classes, children: [_jsx("div", { className: "car-booking-card__image-wrap", children: _jsx("img", { className: "car-booking-card__image", src: imageSrc, alt: "" }) }), _jsx("div", { className: "car-booking-card__active-divider" }), _jsxs("div", { className: "car-booking-card__body", children: [_jsxs("section", { className: "car-booking-card__section car-booking-card__section--content", children: [_jsxs("div", { className: "car-booking-card__title", children: [_jsx("span", { className: "car-booking-card__title-marker", "aria-hidden": "true" }), _jsx(Heading, { level: 4, variant: "bold", color: "accent", className: "car-booking-card__title-text", children: title })] }), _jsx("div", { className: "car-booking-card__features", children: features.map(function (feature, idx) {
|
|
72
143
|
var _a;
|
|
73
144
|
return (_jsx(FeatureRow, __assign({}, feature, { className: "car-booking-card__feature-row ".concat((_a = feature.className) !== null && _a !== void 0 ? _a : '').trim() }), "".concat(feature.label, "-").concat(idx)));
|
|
74
|
-
}) }), infoText && !readonly && (_jsxs("div", { className: "car-booking-card__info", children: [_jsx("span", { className: "car-booking-card__info-icon", "aria-hidden": "true", children: "i" }), _jsx(Text, { size: "sm", leading: "5", variant: "regular", color: "default", className: "car-booking-card__info-text", children: infoText })] }))] }), resolvedShowSupplement && !readonly && (_jsxs("section", { className: "car-booking-card__section car-booking-card__section--supplement", children: [_jsx(Divider, { variant: "dashed", className: "car-booking-card__dashed-divider" }), _jsxs("div", { className: "car-booking-card__supplement-header", children: [_jsx(Text, { as: "h3", size: "md", variant: "bold", leading: "none", color: "default", className: "car-booking-card__section-title", children: supplementLabel }), supplementMessage && (_jsx(Text, { as: "p", size: "sm", leading: "5", variant: "regular", className: "car-booking-card__supplement-message"
|
|
145
|
+
}) }), infoText && !readonly && (_jsxs("div", { className: "car-booking-card__info", children: [_jsx("span", { className: "car-booking-card__info-icon", "aria-hidden": "true", children: "i" }), _jsx(Text, { size: "sm", leading: "5", variant: "regular", color: "default", className: "car-booking-card__info-text", children: infoText })] }))] }), resolvedShowSupplement && !readonly && (_jsxs("section", { className: "car-booking-card__section car-booking-card__section--supplement", children: [_jsx(Divider, { variant: "dashed", className: "car-booking-card__dashed-divider" }), _jsxs("div", { className: "car-booking-card__supplement-header", children: [_jsx(Text, { as: "h3", size: "md", variant: "bold", leading: "none", color: "default", className: "car-booking-card__section-title", children: supplementLabel }), supplementMessage && (_jsx(Text, { as: "p", size: "sm", leading: "5", variant: "regular", className: "car-booking-card__supplement-message ".concat(supplementMessageState === 'error' ? 'car-booking-card__supplement-message--error' : '').trim(), children: supplementMessage }))] }), _jsxs("div", { className: "car-booking-card__supplement-dropdown", ref: dropdownRef, children: [_jsxs("button", { type: "button", className: "car-booking-card__supplement-trigger ".concat(hasSupplementsSelected ? 'car-booking-card__supplement-trigger--has-value' : '', " ").concat(supplementMessage ? 'car-booking-card__supplement-trigger--disabled' : ''), onClick: handleSupplementToggle, disabled: Boolean(supplementMessage), children: [hasSupplementsSelected ? (_jsx("div", { className: "car-booking-card__supplement-chips", children: getSupplementSummary().map(function (summary, idx) {
|
|
146
|
+
var name = summary.split(' x')[0];
|
|
147
|
+
return (_jsx(Chip, { label: summary, size: "sm", color: "accent", trailingIcon: "close", onClick: function (e) {
|
|
148
|
+
e.stopPropagation();
|
|
149
|
+
handleRemoveSupplement(name);
|
|
150
|
+
} }, idx));
|
|
151
|
+
}) })) : (_jsx("span", { className: "car-booking-card__supplement-placeholder", children: supplementPlaceholder })), _jsx(Icon, { name: "chevron-down", size: "sm", className: "car-booking-card__supplement-icon ".concat(isSupplementOpen ? 'car-booking-card__supplement-icon--open' : '') })] }), isSupplementOpen && (_jsx("div", { className: "car-booking-card__supplement-panel", ref: panelRef, children: _jsx(VehicleSupplement, { supplements: supplements, transfer: transfers, initialValues: selectedSupplements, onChange: function () { }, onDone: handleSupplementDone, onClear: handleSupplementClear }) }))] })] })), priceRows.length > 0 && !readonly && (_jsxs("section", { className: "car-booking-card__section car-booking-card__section--price", children: [_jsx(Divider, { variant: "dashed", className: "car-booking-card__dashed-divider" }), _jsxs("div", { className: "car-booking-card__price-content", children: [_jsx(Text, { as: "h3", size: "md", variant: "bold", leading: "none", color: "default", className: "car-booking-card__section-title", children: priceTitle }), _jsx("div", { className: "car-booking-card__price-rows", children: priceRows.map(function (row, idx) { return (_jsxs("div", { className: "car-booking-card__price-row", children: [_jsx(Text, { size: "sm", leading: "5", variant: "regular", color: "subtle", className: "car-booking-card__price-label", children: row.label }), _jsx(Text, { size: "sm", leading: "5", variant: "bold", color: "subtle", className: "car-booking-card__price-value", children: row.value })] }, "".concat(row.label, "-").concat(idx))); }) })] }), _jsx(Divider, { variant: "dashed", className: "car-booking-card__dashed-divider" })] })), !readonly && _jsxs("footer", { className: "car-booking-card__footer", children: [_jsxs("div", { className: "car-booking-card__total", children: [_jsx(Text, { size: "base", variant: "bold", color: "accent", className: "car-booking-card__total-price", children: totalPrice }), _jsx(Text, { size: "sm", variant: "regular", color: "subtle", className: "car-booking-card__total-label", children: totalPriceLabel })] }), !readonly && (_jsx("div", { onMouseEnter: function () { return setIsHovered(true); }, onMouseLeave: function () { return setIsHovered(false); }, className: "car-booking-card__cta-wrapper", children: _jsx(Button, __assign({}, resolvedCtaButtonProps, { onClick: handleCtaClick, className: "car-booking-card__cta ".concat((_b = resolvedCtaButtonProps === null || resolvedCtaButtonProps === void 0 ? void 0 : resolvedCtaButtonProps.className) !== null && _b !== void 0 ? _b : '').trim(), children: resolvedCtaLabel })) }))] })] })] }));
|
|
75
152
|
};
|
|
76
153
|
export default CarBookingCard;
|
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ export { ServiceLanguages } from './components/molecules/ServiceLanguages/Servic
|
|
|
39
39
|
export { default as ServiceSelector } from './components/molecules/ServiceSelector/ServiceSelector';
|
|
40
40
|
export { default as Stepper } from './components/molecules/Stepper/Stepper';
|
|
41
41
|
export { default as TextWithIcon } from './components/molecules/TextWithIcon/TextWithIcon';
|
|
42
|
+
export { default as VehicleSupplement } from './components/molecules/VehicleSupplement/VehicleSupplement';
|
|
43
|
+
export type { VehicleSupplementProps, Supplement, SupplementValue } from './components/molecules/VehicleSupplement';
|
|
42
44
|
export { default as TimelineItem } from './components/molecules/TimelineItem/TimelineItem';
|
|
43
45
|
export { default as Toast } from './components/molecules/Toast/Toast';
|
|
44
46
|
export * from './components/molecules/TooltipDisplay/TooltipDisplay';
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,7 @@ export { ServiceLanguages } from './components/molecules/ServiceLanguages/Servic
|
|
|
41
41
|
export { default as ServiceSelector } from './components/molecules/ServiceSelector/ServiceSelector';
|
|
42
42
|
export { default as Stepper } from './components/molecules/Stepper/Stepper';
|
|
43
43
|
export { default as TextWithIcon } from './components/molecules/TextWithIcon/TextWithIcon';
|
|
44
|
+
export { default as VehicleSupplement } from './components/molecules/VehicleSupplement/VehicleSupplement';
|
|
44
45
|
export { default as TimelineItem } from './components/molecules/TimelineItem/TimelineItem';
|
|
45
46
|
export { default as Toast } from './components/molecules/Toast/Toast';
|
|
46
47
|
export * from './components/molecules/TooltipDisplay/TooltipDisplay';
|