mautourco-components 0.2.142 → 0.2.144
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/molecules/LanguageSelector/LanguageSelector.css +120 -0
- package/dist/components/molecules/LanguageSelector/LanguageSelector.d.ts +11 -0
- package/dist/components/molecules/LanguageSelector/LanguageSelector.js +28 -0
- package/dist/components/molecules/TextWithIcon/TextWithIcon.js +1 -1
- package/dist/components/organisms/Docket/Docket.d.ts +31 -6
- package/dist/components/organisms/Docket/Docket.js +33 -13
- package/dist/components/organisms/Docket/DocketEmptyState.d.ts +14 -3
- package/dist/components/organisms/Docket/DocketEmptyState.js +7 -2
- package/dist/components/organisms/Docket/DocketFooter.d.ts +24 -0
- package/dist/components/organisms/Docket/DocketFooter.js +11 -3
- package/dist/components/organisms/DocketAccordion/DocketAccordion.d.ts +10 -1
- package/dist/components/organisms/DocketAccordion/DocketAccordion.js +2 -2
- package/dist/hooks/useMobile.d.ts +2 -1
- package/dist/hooks/useMobile.js +1 -0
- package/dist/styles/components/organism/docket.css +14 -20
- package/package.json +1 -1
- package/src/components/molecules/LanguageSelector/LanguageSelector.css +83 -0
- package/src/components/molecules/LanguageSelector/LanguageSelector.tsx +102 -0
- package/src/components/molecules/TextWithIcon/TextWithIcon.tsx +1 -1
- package/src/components/organisms/Docket/Docket.tsx +106 -24
- package/src/components/organisms/Docket/DocketEmptyState.tsx +29 -7
- package/src/components/organisms/Docket/DocketFooter.tsx +82 -26
- package/src/components/organisms/DocketAccordion/DocketAccordion.tsx +12 -2
- package/src/styles/components/organism/docket.css +13 -20
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Do not edit directly, this file was auto-generated.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/* Language Selector - Trigger */
|
|
6
|
+
.language-selector__trigger {
|
|
7
|
+
display: flex;
|
|
8
|
+
cursor: pointer;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
gap: 0.5rem;
|
|
12
|
+
padding-left: 1rem;
|
|
13
|
+
padding-right: 1rem;
|
|
14
|
+
height: 44px;
|
|
15
|
+
border: solid 1px var(--color-tuna-500);
|
|
16
|
+
border-radius: 12px;
|
|
17
|
+
transition: all 0.5s ease;
|
|
18
|
+
&:hover {
|
|
19
|
+
border-color: #262626;
|
|
20
|
+
}
|
|
21
|
+
&.language-selector__trigger--with-text {
|
|
22
|
+
border-color: #262626;
|
|
23
|
+
color: #262626;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.language-selector__placeholder {
|
|
28
|
+
color: var(--color-tuna-500);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.language-selector__trigger-icon {
|
|
32
|
+
flex-shrink: 0;
|
|
33
|
+
color: var(--color-neutral-800);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.language-selector__trigger-text {
|
|
37
|
+
font-size: 0.875rem;
|
|
38
|
+
line-height: 1.25rem;
|
|
39
|
+
font-weight: 500;
|
|
40
|
+
color: var(--color-neutral-800);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.language-selector__trigger-chevron {
|
|
44
|
+
flex-shrink: 0;
|
|
45
|
+
color: var(--color-neutral-800);
|
|
46
|
+
transition-property: transform;
|
|
47
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
48
|
+
transition-duration: 200ms;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.language-selector__trigger[data-state='open'] .language-selector__trigger-chevron {
|
|
52
|
+
transform: rotate(180deg);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Popover */
|
|
56
|
+
.language-selector__popover {
|
|
57
|
+
--tw-bg-opacity: 1;
|
|
58
|
+
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
|
|
59
|
+
padding: var(--spacing-padding-px-2);
|
|
60
|
+
border-radius: var(--border-radius-rounded-xl);
|
|
61
|
+
min-width: 8rem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Popover Content */
|
|
65
|
+
.language-selector__content {
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
--tw-bg-opacity: 1;
|
|
69
|
+
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
|
|
70
|
+
padding-top: 0.25rem;
|
|
71
|
+
padding-bottom: 0.25rem;
|
|
72
|
+
min-width: 8rem;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.language-selector__option {
|
|
76
|
+
display: flex;
|
|
77
|
+
width: 100%;
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
align-items: center;
|
|
80
|
+
column-gap: 0.5rem;
|
|
81
|
+
height: 2.25rem;
|
|
82
|
+
padding-inline: var(--spacing-padding-px-3);
|
|
83
|
+
border-radius: var(--border-radius-rounded-md);
|
|
84
|
+
font-size: var(--typography-font-size-sm);
|
|
85
|
+
font-weight: var(--typography-font-weight-medium);
|
|
86
|
+
color: var(--color-neutral-800);
|
|
87
|
+
transition: background-color 0.2s ease;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.language-selector__option:hover {
|
|
91
|
+
background-color: var(--color-neutral-100);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.language-selector__option--selected {
|
|
95
|
+
background-color: var(--color-neutral-100);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.language-selector__selected-language {
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
column-gap: 0.5rem;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.language-selector__flag {
|
|
105
|
+
display: flex;
|
|
106
|
+
align-items: center;
|
|
107
|
+
justify-content: center;
|
|
108
|
+
height: 1rem;
|
|
109
|
+
flex-shrink: 0;
|
|
110
|
+
flex-grow: 0;
|
|
111
|
+
flex-basis: 1rem;
|
|
112
|
+
overflow: hidden;
|
|
113
|
+
border-radius: 9999px;
|
|
114
|
+
.fi {
|
|
115
|
+
width: 1rem;
|
|
116
|
+
}
|
|
117
|
+
.fi {
|
|
118
|
+
flex: 0 0 1.333333em;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import 'flag-icons/css/flag-icons.min.css';
|
|
2
|
+
import './LanguageSelector.css';
|
|
3
|
+
export declare const defaultLanguages: string[];
|
|
4
|
+
export interface LanguageSelectorProps {
|
|
5
|
+
languages?: string[];
|
|
6
|
+
defaultLanguage?: string;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
onSelectLanguage?: (language: string) => void;
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
export default function LanguageSelector(props: LanguageSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../../../lib/utils';
|
|
3
|
+
import 'flag-icons/css/flag-icons.min.css';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
import Icon from '../../atoms/Icon/Icon';
|
|
6
|
+
import { Text } from '../../atoms/Typography/Typography';
|
|
7
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover';
|
|
8
|
+
import { languagesMap } from '../ServiceLanguages/constant';
|
|
9
|
+
import './LanguageSelector.css';
|
|
10
|
+
export var defaultLanguages = ['English', 'Français', 'German', 'Italian'];
|
|
11
|
+
export default function LanguageSelector(props) {
|
|
12
|
+
var _a = props.languages, languages = _a === void 0 ? defaultLanguages : _a, defaultLanguage = props.defaultLanguage, _b = props.placeholder, placeholder = _b === void 0 ? 'Prefered language' : _b, label = props.label, onSelectLanguage = props.onSelectLanguage;
|
|
13
|
+
var _c = useState(false), open = _c[0], setOpen = _c[1];
|
|
14
|
+
var _d = useState(defaultLanguage), selectedLanguage = _d[0], setSelectedLanguage = _d[1];
|
|
15
|
+
var handleSelectLanguage = function (language) {
|
|
16
|
+
setSelectedLanguage(language);
|
|
17
|
+
onSelectLanguage === null || onSelectLanguage === void 0 ? void 0 : onSelectLanguage(language);
|
|
18
|
+
setOpen(false);
|
|
19
|
+
};
|
|
20
|
+
useEffect(function () {
|
|
21
|
+
setSelectedLanguage(defaultLanguage);
|
|
22
|
+
}, [defaultLanguage]);
|
|
23
|
+
return (_jsxs("div", { children: [label && (_jsx("label", { className: "mb-2", children: _jsx(Text, { size: "sm", as: "span", children: label }) })), _jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: cn('language-selector__trigger', {
|
|
24
|
+
'language-selector__trigger--with-text': !!selectedLanguage,
|
|
25
|
+
}), children: [selectedLanguage ? (_jsxs("span", { className: "language-selector__selected-language", children: [_jsx("span", { className: "language-selector__flag", children: _jsx("i", { className: "fi fi-".concat(languagesMap[selectedLanguage.toLowerCase()]) }) }), selectedLanguage] })) : (_jsx("span", { className: "language-selector__placeholder", children: placeholder })), _jsx(Icon, { name: "chevron-down", size: "sm", className: "language-selector__trigger-chevron" })] }) }), _jsx(PopoverContent, { className: "language-selector__popover", align: "end", side: "bottom", sideOffset: 8, children: _jsx("div", { className: "language-selector__content", children: languages.map(function (language) { return (_jsxs("div", { className: cn('language-selector__option', {
|
|
26
|
+
'language-selector__option--selected': selectedLanguage === language,
|
|
27
|
+
}), onClick: function () { return handleSelectLanguage(language); }, children: [_jsx("span", { className: "language-selector__flag", children: _jsx("i", { className: "fi fi-".concat(languagesMap[language.toLowerCase()]) }) }), language] }, language)); }) }) })] })] }));
|
|
28
|
+
}
|
|
@@ -15,6 +15,6 @@ import { Text, } from '../../atoms/Typography/Typography';
|
|
|
15
15
|
*/
|
|
16
16
|
function TextWithIcon(props) {
|
|
17
17
|
var icon = props.icon, children = props.children, _a = props.iconSize, iconSize = _a === void 0 ? 'sm' : _a, _b = props.textSize, textSize = _b === void 0 ? 'sm' : _b, _c = props.color, color = _c === void 0 ? 'default' : _c, _d = props.textLeading, textLeading = _d === void 0 ? '5' : _d, _e = props.textVariant, textVariant = _e === void 0 ? 'medium' : _e;
|
|
18
|
-
return (_jsxs("div", { className: cn('flex gap-x-2', color === 'yellow' && 'text-[var(--color-yellow-600)]', color === 'accent' && 'text-[var(--color-text-accent)]'), children: [icon && (_jsx("span", { className: "relative top-[2px]", children: _jsx(Icon, { name: icon, size: iconSize }) })), _jsx(Text, { variant: textVariant, size: textSize, className: "flex items-center gap-x-2", color: color, leading: textLeading, as: "div", children: children })] }));
|
|
18
|
+
return (_jsxs("div", { className: cn('flex gap-x-2 items-center', color === 'yellow' && 'text-[var(--color-yellow-600)]', color === 'accent' && 'text-[var(--color-text-accent)]'), children: [icon && (_jsx("span", { className: "relative top-[2px]", children: _jsx(Icon, { name: icon, size: iconSize }) })), _jsx(Text, { variant: textVariant, size: textSize, className: "flex items-center gap-x-2", color: color, leading: textLeading, as: "div", children: children })] }));
|
|
19
19
|
}
|
|
20
20
|
export default TextWithIcon;
|
|
@@ -3,14 +3,23 @@ import React from 'react';
|
|
|
3
3
|
import '../../../styles/components/organism/docket.css';
|
|
4
4
|
import { DocketService } from '../../../types/docket/services.types';
|
|
5
5
|
import { ActionDropdownItem } from '../../molecules/ActionDropdown/ActionDropdown';
|
|
6
|
+
import { DocketFooterLabelsType } from './DocketFooter';
|
|
6
7
|
export { DocketCollapsedHeader } from './DocketCollapsedHeader';
|
|
7
8
|
export type { DocketCollapsedHeaderProps } from './DocketCollapsedHeader';
|
|
8
9
|
export { DocketEmptyState } from './DocketEmptyState';
|
|
9
|
-
export type { DocketEmptyStateProps } from './DocketEmptyState';
|
|
10
|
+
export type { DocketEmptyStateLabelsType, DocketEmptyStateProps, } from './DocketEmptyState';
|
|
10
11
|
export { DocketFooter } from './DocketFooter';
|
|
11
12
|
export type { DocketFooterProps } from './DocketFooter';
|
|
12
13
|
export { DocketHeader } from './DocketHeader';
|
|
13
14
|
export type { DocketHeaderProps } from './DocketHeader';
|
|
15
|
+
export type DocketLabelsType = {
|
|
16
|
+
manageQuotation: string;
|
|
17
|
+
close: string;
|
|
18
|
+
/** Bold part of the empty state message */
|
|
19
|
+
emptyStateBold?: string;
|
|
20
|
+
/** Regular part of the empty state message */
|
|
21
|
+
emptyStateRegular?: string;
|
|
22
|
+
} & DocketFooterLabelsType;
|
|
14
23
|
export interface DocketProps {
|
|
15
24
|
/**
|
|
16
25
|
* Array of nested Docket objects
|
|
@@ -89,10 +98,6 @@ export interface DocketProps {
|
|
|
89
98
|
* Optional click handler
|
|
90
99
|
*/
|
|
91
100
|
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
92
|
-
/**
|
|
93
|
-
* Optional data attributes for testing or tracking
|
|
94
|
-
*/
|
|
95
|
-
'data-testid'?: string;
|
|
96
101
|
/**
|
|
97
102
|
* Remove mode - replaces price chips with remove buttons in all service dockets
|
|
98
103
|
*/
|
|
@@ -102,11 +107,31 @@ export interface DocketProps {
|
|
|
102
107
|
* Receives the service index and service data
|
|
103
108
|
*/
|
|
104
109
|
onServiceRemove?: (index: number, service: DocketService) => void;
|
|
110
|
+
/**
|
|
111
|
+
* Button done mode - replaces the "Book now" button with a "Done" button
|
|
112
|
+
*/
|
|
113
|
+
doneButtonMode?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Handler for the "Done" button click
|
|
116
|
+
*/
|
|
117
|
+
onDoneClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
118
|
+
/**
|
|
119
|
+
* Labels for the docket
|
|
120
|
+
*/
|
|
121
|
+
labels?: DocketLabelsType;
|
|
122
|
+
/**
|
|
123
|
+
* Maximum number of proposals
|
|
124
|
+
*/
|
|
125
|
+
maxProposals?: number;
|
|
126
|
+
/**
|
|
127
|
+
* URL of the empty state illustration image (when there are no proposals or no services)
|
|
128
|
+
*/
|
|
129
|
+
emptyStateImageUrl?: string;
|
|
105
130
|
}
|
|
106
131
|
/**
|
|
107
132
|
* Docket is a container component for displaying quotation/docket information.
|
|
108
133
|
* It provides a structured layout for travel booking summaries with sections
|
|
109
134
|
* for accommodation, services, pricing, and actions.
|
|
110
135
|
*/
|
|
111
|
-
export declare
|
|
136
|
+
export declare function Docket({ proposals, activeProposal: initialActiveProposal, onProposalSelect, title, moreOptions, accordionMoreOptions, onMoreOptionsClick, onAddNewQuoteClick, onCompareClick, onViewClick, onSaveClick, onBookNowClick, className, containerClassName, onClick, removeMode, onServiceRemove, doneButtonMode, onDoneClick, maxProposals, emptyStateImageUrl, labels, }: DocketProps): React.ReactElement;
|
|
112
137
|
export default Docket;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Breakpoint } from '../../../hooks/useMobile';
|
|
3
4
|
import '../../../styles/components/organism/docket.css';
|
|
4
5
|
import Icon from '../../atoms/Icon/Icon';
|
|
5
6
|
import { AccomodationDocket } from '../../molecules/AccomodationDocket/AccomodationDocket';
|
|
@@ -22,14 +23,25 @@ export { DocketHeader } from './DocketHeader';
|
|
|
22
23
|
* It provides a structured layout for travel booking summaries with sections
|
|
23
24
|
* for accommodation, services, pricing, and actions.
|
|
24
25
|
*/
|
|
25
|
-
export
|
|
26
|
-
var proposals = _a.proposals, _b = _a.activeProposal, initialActiveProposal = _b === void 0 ? 0 : _b, onProposalSelect = _a.onProposalSelect, _c = _a.title, title = _c === void 0 ? 'Your quotation' : _c, moreOptions = _a.moreOptions, accordionMoreOptions = _a.accordionMoreOptions, onMoreOptionsClick = _a.onMoreOptionsClick, onAddNewQuoteClick = _a.onAddNewQuoteClick, onCompareClick = _a.onCompareClick, onViewClick = _a.onViewClick, onSaveClick = _a.onSaveClick, onBookNowClick = _a.onBookNowClick, _d = _a.className, className = _d === void 0 ? '' : _d, _e = _a.containerClassName, containerClassName = _e === void 0 ? '' : _e, onClick = _a.onClick,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
export function Docket(_a) {
|
|
27
|
+
var proposals = _a.proposals, _b = _a.activeProposal, initialActiveProposal = _b === void 0 ? 0 : _b, onProposalSelect = _a.onProposalSelect, _c = _a.title, title = _c === void 0 ? 'Your quotation' : _c, moreOptions = _a.moreOptions, accordionMoreOptions = _a.accordionMoreOptions, onMoreOptionsClick = _a.onMoreOptionsClick, onAddNewQuoteClick = _a.onAddNewQuoteClick, onCompareClick = _a.onCompareClick, onViewClick = _a.onViewClick, onSaveClick = _a.onSaveClick, onBookNowClick = _a.onBookNowClick, _d = _a.className, className = _d === void 0 ? '' : _d, _e = _a.containerClassName, containerClassName = _e === void 0 ? '' : _e, onClick = _a.onClick, _f = _a.removeMode, removeMode = _f === void 0 ? false : _f, onServiceRemove = _a.onServiceRemove, _g = _a.doneButtonMode, doneButtonMode = _g === void 0 ? false : _g, onDoneClick = _a.onDoneClick, _h = _a.maxProposals, maxProposals = _h === void 0 ? 5 : _h, emptyStateImageUrl = _a.emptyStateImageUrl, _j = _a.labels, labels = _j === void 0 ? {
|
|
28
|
+
manageQuotation: 'Manage quotation',
|
|
29
|
+
close: 'Close',
|
|
30
|
+
addNewQuote: 'Add new quote',
|
|
31
|
+
compare: 'Compare',
|
|
32
|
+
view: 'View',
|
|
33
|
+
save: 'Save',
|
|
34
|
+
bookNow: 'Book now',
|
|
35
|
+
done: 'Done',
|
|
36
|
+
emptyStateBold: 'Start by adding a service here—',
|
|
37
|
+
emptyStateRegular: "you'll be able to create and compare multiple quotations.",
|
|
38
|
+
} : _j;
|
|
39
|
+
var _k = useState(false), isOpen = _k[0], setIsOpen = _k[1];
|
|
40
|
+
var _l = useState(false), isMobile = _l[0], setIsMobile = _l[1];
|
|
41
|
+
var _m = useState(initialActiveProposal), expandedProposalIndex = _m[0], setExpandedProposalIndex = _m[1];
|
|
30
42
|
useEffect(function () {
|
|
31
43
|
var checkMobile = function () {
|
|
32
|
-
setIsMobile(window.innerWidth <
|
|
44
|
+
setIsMobile(window.innerWidth < Breakpoint.LG); // Desktop S breakpoint (1440px)
|
|
33
45
|
};
|
|
34
46
|
checkMobile();
|
|
35
47
|
window.addEventListener('resize', checkMobile);
|
|
@@ -80,32 +92,40 @@ export var Docket = function (_a) {
|
|
|
80
92
|
};
|
|
81
93
|
var renderContent = function () {
|
|
82
94
|
if (!proposals || proposals.length === 0) {
|
|
83
|
-
return _jsx(DocketEmptyState, {
|
|
95
|
+
return (_jsx(DocketEmptyState, { imageUrl: emptyStateImageUrl, labels: (labels === null || labels === void 0 ? void 0 : labels.emptyStateBold) != null && (labels === null || labels === void 0 ? void 0 : labels.emptyStateRegular) != null
|
|
96
|
+
? { bold: labels.emptyStateBold, regular: labels.emptyStateRegular }
|
|
97
|
+
: undefined }));
|
|
84
98
|
}
|
|
85
99
|
// If there are multiple proposals, render each in a DocketAccordion
|
|
86
100
|
if (proposals.length > 1) {
|
|
87
|
-
return (_jsx("div", { className: "docket__proposals", children: proposals.map(function (proposal, index) { return (_jsx(DocketAccordion, { proposal: proposal, removeMode: removeMode && index === expandedProposalIndex, onServiceRemove: onServiceRemove, expanded: index === expandedProposalIndex, onExpandedChange: function (isExpanded) { return handleProposalToggle(index, isExpanded); }, moreOptions: accordionMoreOptions
|
|
101
|
+
return (_jsx("div", { className: "docket__proposals", children: proposals.map(function (proposal, index) { return (_jsx(DocketAccordion, { proposal: proposal, removeMode: removeMode && index === expandedProposalIndex, onServiceRemove: onServiceRemove, expanded: index === expandedProposalIndex, onExpandedChange: function (isExpanded) { return handleProposalToggle(index, isExpanded); }, moreOptions: accordionMoreOptions, manageQuotationLabel: labels === null || labels === void 0 ? void 0 : labels.manageQuotation, emptyStateImageUrl: emptyStateImageUrl, emptyStateLabels: (labels === null || labels === void 0 ? void 0 : labels.emptyStateBold) != null && (labels === null || labels === void 0 ? void 0 : labels.emptyStateRegular) != null
|
|
102
|
+
? { bold: labels.emptyStateBold, regular: labels.emptyStateRegular }
|
|
103
|
+
: undefined }, "proposal-".concat(proposal.ProposalDetail.QuoteId))); }) }));
|
|
88
104
|
}
|
|
89
105
|
// If there's only one proposal, render services directly
|
|
90
106
|
var currentProposal = proposals[0];
|
|
91
107
|
var allServices = currentProposal.Services || [];
|
|
92
108
|
if (allServices.length === 0) {
|
|
93
|
-
return _jsx(DocketEmptyState, {
|
|
109
|
+
return (_jsx(DocketEmptyState, { imageUrl: emptyStateImageUrl, labels: (labels === null || labels === void 0 ? void 0 : labels.emptyStateBold) != null && (labels === null || labels === void 0 ? void 0 : labels.emptyStateRegular) != null
|
|
110
|
+
? { bold: labels.emptyStateBold, regular: labels.emptyStateRegular }
|
|
111
|
+
: undefined }));
|
|
94
112
|
}
|
|
95
113
|
var renderedServices = allServices
|
|
96
114
|
.map(function (service, index) { return renderService(service, index); })
|
|
97
115
|
.filter(Boolean);
|
|
98
116
|
if (renderedServices.length === 0) {
|
|
99
|
-
return _jsx(DocketEmptyState, {
|
|
117
|
+
return (_jsx(DocketEmptyState, { imageUrl: emptyStateImageUrl, labels: (labels === null || labels === void 0 ? void 0 : labels.emptyStateBold) != null && (labels === null || labels === void 0 ? void 0 : labels.emptyStateRegular) != null
|
|
118
|
+
? { bold: labels.emptyStateBold, regular: labels.emptyStateRegular }
|
|
119
|
+
: undefined }));
|
|
100
120
|
}
|
|
101
121
|
return _jsx("div", { className: "docket__services", children: renderedServices });
|
|
102
122
|
};
|
|
103
123
|
// Collect all prices from the current proposal (only for single proposal view)
|
|
104
124
|
var allPrices = proposals && proposals.length === 1 ? proposals[0].Prices || [] : [];
|
|
105
|
-
var renderDocketContent = function () { return (_jsxs("div", { className: classes, onClick: onClick,
|
|
125
|
+
var renderDocketContent = function () { return (_jsxs("div", { className: classes, onClick: onClick, children: [_jsx(DocketHeader, { title: title, moreOptions: moreOptions, onMoreOptionsClick: onMoreOptionsClick }), _jsx("div", { className: "docket__content", children: renderContent() }), allPrices.length > 0 && proposals && proposals.length === 1 && (_jsxs("div", { className: "docket__prices-section", children: [_jsx(Icon, { name: "line", size: "lg" }), _jsx(DocketPrices, { prices: allPrices }), _jsx(Icon, { name: "line", size: "lg" })] })), _jsx(DocketFooter, { onAddNewQuoteClick: onAddNewQuoteClick, onCompareClick: onCompareClick, showCompareButton: !!(proposals === null || proposals === void 0 ? void 0 : proposals.length), onViewClick: onViewClick, onSaveClick: onSaveClick, onBookNowClick: onBookNowClick, doneButtonMode: doneButtonMode, onDoneClick: onDoneClick, labels: labels, disableAddNewQuoteButton: proposals ? proposals.length >= maxProposals : false })] })); };
|
|
106
126
|
var containerClasses = containerClassName
|
|
107
127
|
? "docket__container ".concat(containerClassName)
|
|
108
128
|
: 'docket__container';
|
|
109
|
-
return (_jsxs("div", { className: containerClasses, children: [isMobile && !isOpen && (_jsx(DocketCollapsedHeader, { title: title, onClick: handleToggle })), isMobile && isOpen && (_jsxs("div", { className: "docket__mobile-wrapper", children: [_jsx("
|
|
110
|
-
}
|
|
129
|
+
return (_jsxs("div", { className: containerClasses, children: [isMobile && !isOpen && (_jsx(DocketCollapsedHeader, { title: title, onClick: handleToggle })), isMobile && isOpen && (_jsxs("div", { className: "docket__mobile-wrapper", children: [_jsx("button", { className: "docket__close-header", onClick: handleClose, children: _jsxs("div", { className: "docket__close-header-content", children: [_jsx("h2", { className: "docket__close-header-text", children: labels === null || labels === void 0 ? void 0 : labels.close }), _jsx("div", { className: "docket__close-button", "aria-label": "Close docket", children: _jsx(Icon, { name: "close", size: "lg" }) })] }) }), renderDocketContent()] })), !isMobile && renderDocketContent()] }));
|
|
130
|
+
}
|
|
111
131
|
export default Docket;
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
export type DocketEmptyStateLabelsType = {
|
|
3
|
+
/** Bold part of the empty state message */
|
|
4
|
+
bold: string;
|
|
5
|
+
/** Regular part of the empty state message */
|
|
6
|
+
regular: string;
|
|
7
|
+
};
|
|
2
8
|
export interface DocketEmptyStateProps {
|
|
9
|
+
/**
|
|
10
|
+
* URL of the empty state illustration image
|
|
11
|
+
*/
|
|
12
|
+
imageUrl?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Labels for the empty state text (bold and regular parts)
|
|
15
|
+
*/
|
|
16
|
+
labels?: DocketEmptyStateLabelsType;
|
|
3
17
|
/**
|
|
4
18
|
* Additional CSS classes
|
|
5
19
|
*/
|
|
6
20
|
className?: string;
|
|
7
21
|
}
|
|
8
|
-
/**
|
|
9
|
-
* Empty state component for docket when no content is provided
|
|
10
|
-
*/
|
|
11
22
|
export declare const DocketEmptyState: React.FC<DocketEmptyStateProps>;
|
|
12
23
|
export default DocketEmptyState;
|
|
@@ -2,8 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
/**
|
|
3
3
|
* Empty state component for docket when no content is provided
|
|
4
4
|
*/
|
|
5
|
+
var DEFAULT_EMPTY_STATE_IMAGE = '/images/docket-empty-illustration.svg';
|
|
6
|
+
var DEFAULT_EMPTY_STATE_LABELS = {
|
|
7
|
+
bold: 'Start by adding a service here—',
|
|
8
|
+
regular: "you'll be able to create and compare multiple quotations.",
|
|
9
|
+
};
|
|
5
10
|
export var DocketEmptyState = function (_a) {
|
|
6
|
-
var _b = _a.className, className =
|
|
7
|
-
return (_jsx("div", { className: "docket__empty-state ".concat(className), children: _jsxs("div", { className: "docket__empty-state-content", children: [_jsx("div", { className: "docket__empty-state-illustration", children: _jsx("img", { src:
|
|
11
|
+
var _b = _a.imageUrl, imageUrl = _b === void 0 ? DEFAULT_EMPTY_STATE_IMAGE : _b, _c = _a.labels, labels = _c === void 0 ? DEFAULT_EMPTY_STATE_LABELS : _c, _d = _a.className, className = _d === void 0 ? '' : _d;
|
|
12
|
+
return (_jsx("div", { className: "docket__empty-state ".concat(className), children: _jsxs("div", { className: "docket__empty-state-content", children: [_jsx("div", { className: "docket__empty-state-illustration", children: _jsx("img", { src: imageUrl, alt: "Empty docket illustration", className: "docket__empty-state-image" }) }), _jsxs("p", { className: "docket__empty-state-text", children: [_jsx("span", { className: "docket__empty-state-text-bold", children: labels.bold }), _jsx("span", { className: "docket__empty-state-text-regular", children: labels.regular })] })] }) }));
|
|
8
13
|
};
|
|
9
14
|
export default DocketEmptyState;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
export type DocketFooterLabelsType = {
|
|
3
|
+
addNewQuote: string;
|
|
4
|
+
compare: string;
|
|
5
|
+
view: string;
|
|
6
|
+
save: string;
|
|
7
|
+
bookNow: string;
|
|
8
|
+
done: string;
|
|
9
|
+
};
|
|
2
10
|
export interface DocketFooterProps {
|
|
3
11
|
/**
|
|
4
12
|
* Handler for "Add new quote" button click
|
|
@@ -28,6 +36,22 @@ export interface DocketFooterProps {
|
|
|
28
36
|
* Additional CSS classes
|
|
29
37
|
*/
|
|
30
38
|
className?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Button done mode - replaces the "Book now" button with a "Done" button
|
|
41
|
+
*/
|
|
42
|
+
doneButtonMode?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Handler for the "Done" button click
|
|
45
|
+
*/
|
|
46
|
+
onDoneClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Disable the "Add new quote" button
|
|
49
|
+
*/
|
|
50
|
+
disableAddNewQuoteButton?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Labels for the buttons
|
|
53
|
+
*/
|
|
54
|
+
labels?: DocketFooterLabelsType;
|
|
31
55
|
}
|
|
32
56
|
/**
|
|
33
57
|
* DocketFooter component for action buttons
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import Button from '../../atoms/Button/Button';
|
|
3
3
|
/**
|
|
4
4
|
* DocketFooter component for action buttons
|
|
5
5
|
*/
|
|
6
6
|
export var DocketFooter = function (_a) {
|
|
7
|
-
var onAddNewQuoteClick = _a.onAddNewQuoteClick, onCompareClick = _a.onCompareClick, _b = _a.showCompareButton, showCompareButton = _b === void 0 ? false : _b, onViewClick = _a.onViewClick, onSaveClick = _a.onSaveClick, onBookNowClick = _a.onBookNowClick, _c = _a.className, className = _c === void 0 ? '' : _c
|
|
8
|
-
|
|
7
|
+
var onAddNewQuoteClick = _a.onAddNewQuoteClick, onCompareClick = _a.onCompareClick, _b = _a.showCompareButton, showCompareButton = _b === void 0 ? false : _b, onViewClick = _a.onViewClick, onSaveClick = _a.onSaveClick, onBookNowClick = _a.onBookNowClick, _c = _a.className, className = _c === void 0 ? '' : _c, _d = _a.doneButtonMode, doneButtonMode = _d === void 0 ? false : _d, onDoneClick = _a.onDoneClick, _e = _a.disableAddNewQuoteButton, disableAddNewQuoteButton = _e === void 0 ? false : _e, _f = _a.labels, labels = _f === void 0 ? {
|
|
8
|
+
addNewQuote: 'Add new quote',
|
|
9
|
+
compare: 'Compare',
|
|
10
|
+
view: 'View',
|
|
11
|
+
save: 'Save',
|
|
12
|
+
bookNow: 'Book now',
|
|
13
|
+
done: 'Done',
|
|
14
|
+
} : _f;
|
|
15
|
+
var isDoneMode = doneButtonMode && onDoneClick;
|
|
16
|
+
return (_jsxs("div", { className: "docket__footer ".concat(className), children: [!isDoneMode && (onAddNewQuoteClick || onCompareClick) && (_jsxs("div", { className: "docket__footer-top-buttons", children: [onAddNewQuoteClick && (_jsx(Button, { variant: "outline-secondary", size: "sm", leadingIcon: disableAddNewQuoteButton ? undefined : 'plus', trailingIcon: disableAddNewQuoteButton ? 'info' : undefined, disabled: disableAddNewQuoteButton, onClick: onAddNewQuoteClick, className: "docket__footer-button ".concat(!showCompareButton ? 'docket__footer-button--full' : ''), children: labels.addNewQuote })), showCompareButton && onCompareClick && (_jsx(Button, { variant: "outline-secondary", size: "sm", onClick: onCompareClick, leadingIcon: "document", className: "docket__footer-button", children: labels.compare }))] })), _jsx("div", { className: "docket__footer-actions", children: isDoneMode ? (_jsx(Button, { variant: "secondary", size: "sm", onClick: onDoneClick, className: "docket__footer-button docket__footer-button--primary docket__footer-button--half", children: labels.done })) : (_jsxs(_Fragment, { children: [onViewClick && (_jsx(Button, { variant: "outline-secondary", size: "sm", onClick: onViewClick, className: "docket__footer-button", children: _jsx("span", { className: "docket__footer-view-text", children: labels.view }) })), onSaveClick && (_jsx(Button, { variant: "outline-secondary", size: "sm", onClick: onSaveClick, className: "docket__footer-button", children: labels.save })), onBookNowClick && (_jsx(Button, { variant: "secondary", size: "sm", onClick: onBookNowClick, className: "docket__footer-button docket__footer-button--primary", children: labels.bookNow }))] })) })] }));
|
|
9
17
|
};
|
|
10
18
|
export default DocketFooter;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ProposalSorted } from '@/src/types/docket/docket.types';
|
|
2
2
|
import { DocketService } from '@/src/types/docket/services.types';
|
|
3
3
|
import { ActionDropdownItem } from '../../molecules/ActionDropdown/ActionDropdown';
|
|
4
|
+
import { type DocketEmptyStateLabelsType } from '../Docket/Docket';
|
|
4
5
|
export interface DocketAccordionProps {
|
|
5
6
|
proposal: ProposalSorted;
|
|
6
7
|
removeMode?: boolean;
|
|
@@ -10,6 +11,14 @@ export interface DocketAccordionProps {
|
|
|
10
11
|
expanded?: boolean;
|
|
11
12
|
onExpandedChange?: (expanded: boolean) => void;
|
|
12
13
|
moreOptions?: ActionDropdownItem[];
|
|
14
|
+
/**
|
|
15
|
+
* URL of the empty state illustration image (when the proposal has no services)
|
|
16
|
+
*/
|
|
17
|
+
emptyStateImageUrl?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Labels for the empty state (bold and regular text) when the proposal has no services
|
|
20
|
+
*/
|
|
21
|
+
emptyStateLabels?: DocketEmptyStateLabelsType;
|
|
13
22
|
}
|
|
14
23
|
/**
|
|
15
24
|
* DocketAccordion component displays a single quotation in an accordion format.
|
|
@@ -23,4 +32,4 @@ export interface DocketAccordionProps {
|
|
|
23
32
|
* defaultExpanded={false}
|
|
24
33
|
* />
|
|
25
34
|
*/
|
|
26
|
-
export default function DocketAccordion({ proposal, removeMode, onServiceRemove, defaultExpanded, expanded, onExpandedChange, moreOptions, manageQuotationLabel, }: DocketAccordionProps): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
export default function DocketAccordion({ proposal, removeMode, onServiceRemove, defaultExpanded, expanded, onExpandedChange, moreOptions, manageQuotationLabel, emptyStateImageUrl, emptyStateLabels, }: DocketAccordionProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -22,7 +22,7 @@ import { DocketEmptyState } from '../Docket/Docket';
|
|
|
22
22
|
* />
|
|
23
23
|
*/
|
|
24
24
|
export default function DocketAccordion(_a) {
|
|
25
|
-
var proposal = _a.proposal, _b = _a.removeMode, removeMode = _b === void 0 ? false : _b, onServiceRemove = _a.onServiceRemove, _c = _a.defaultExpanded, defaultExpanded = _c === void 0 ? false : _c, expanded = _a.expanded, onExpandedChange = _a.onExpandedChange, _d = _a.moreOptions, moreOptions = _d === void 0 ? [] : _d, _e = _a.manageQuotationLabel, manageQuotationLabel = _e === void 0 ? 'Manage quotation' : _e;
|
|
25
|
+
var proposal = _a.proposal, _b = _a.removeMode, removeMode = _b === void 0 ? false : _b, onServiceRemove = _a.onServiceRemove, _c = _a.defaultExpanded, defaultExpanded = _c === void 0 ? false : _c, expanded = _a.expanded, onExpandedChange = _a.onExpandedChange, _d = _a.moreOptions, moreOptions = _d === void 0 ? [] : _d, _e = _a.manageQuotationLabel, manageQuotationLabel = _e === void 0 ? 'Manage quotation' : _e, emptyStateImageUrl = _a.emptyStateImageUrl, emptyStateLabels = _a.emptyStateLabels;
|
|
26
26
|
var allServices = proposal.Services || [];
|
|
27
27
|
var renderAccordionTitle = function (isExpanded) {
|
|
28
28
|
var _a;
|
|
@@ -52,5 +52,5 @@ export default function DocketAccordion(_a) {
|
|
|
52
52
|
transform: open ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
53
53
|
transition: 'transform 0.2s ease-in-out',
|
|
54
54
|
} })] })); } }), _jsx(Icon, { name: "line", size: "md" }), _jsx(DocketPrices, { prices: proposal.Prices, className: "docket-accordion__footer-price-list" })] }));
|
|
55
|
-
return (_jsx(Accordion, { title: proposal.ProposalDetail.Label, renderTitle: renderAccordionTitle, classNameExpanded: "docket-accordion__expanded", defaultExpanded: defaultExpanded, expanded: expanded, onExpandedChange: onExpandedChange, footer: (allServices === null || allServices === void 0 ? void 0 : allServices.length) > 0 ? renderAccordionFooter : undefined, children: _jsx("div", { className: "docket-accordion__content", children: allServices.length === 0 ? (_jsx(DocketEmptyState, {})) : (_jsx("div", { className: "docket-accordion__services", children: allServices.map(function (service, index) { return renderService(service, index); }) })) }) }));
|
|
55
|
+
return (_jsx(Accordion, { title: proposal.ProposalDetail.Label, renderTitle: renderAccordionTitle, classNameExpanded: "docket-accordion__expanded", defaultExpanded: defaultExpanded, expanded: expanded, onExpandedChange: onExpandedChange, footer: (allServices === null || allServices === void 0 ? void 0 : allServices.length) > 0 ? renderAccordionFooter : undefined, children: _jsx("div", { className: "docket-accordion__content", children: allServices.length === 0 ? (_jsx(DocketEmptyState, { imageUrl: emptyStateImageUrl, labels: emptyStateLabels })) : (_jsx("div", { className: "docket-accordion__services", children: allServices.map(function (service, index) { return renderService(service, index); }) })) }) }));
|
|
56
56
|
}
|
package/dist/hooks/useMobile.js
CHANGED
|
@@ -4,6 +4,7 @@ export var Breakpoint;
|
|
|
4
4
|
Breakpoint[Breakpoint["SM"] = 768] = "SM";
|
|
5
5
|
Breakpoint[Breakpoint["MD"] = 1024] = "MD";
|
|
6
6
|
Breakpoint[Breakpoint["LG"] = 1280] = "LG";
|
|
7
|
+
Breakpoint[Breakpoint["XL"] = 1440] = "XL";
|
|
7
8
|
})(Breakpoint || (Breakpoint = {}));
|
|
8
9
|
export function useMobile(breakpoint) {
|
|
9
10
|
if (breakpoint === void 0) { breakpoint = Breakpoint.SM; }
|
|
@@ -252,24 +252,6 @@
|
|
|
252
252
|
width: 100%;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
.docket__footer-view-link {
|
|
256
|
-
display: inline-flex;
|
|
257
|
-
align-items: center;
|
|
258
|
-
gap: var(--component-button-spacing-ghost-sm-gap, 6px);
|
|
259
|
-
background: none;
|
|
260
|
-
border: none;
|
|
261
|
-
padding: var(--component-button-spacing-ghost-sm-padding-y, 0px)
|
|
262
|
-
var(--component-button-spacing-ghost-sm-padding-x, 0px);
|
|
263
|
-
cursor: pointer;
|
|
264
|
-
font-family: var(--font-font-family-body, 'Satoshi', sans-serif);
|
|
265
|
-
font-size: var(--font-size-text-sm, 14px);
|
|
266
|
-
font-weight: var(--font-weight-font-medium, 500);
|
|
267
|
-
line-height: var(--font-leading-leading-sm, 20px);
|
|
268
|
-
color: var(--color-button-ghost-foreground-default, #78716c);
|
|
269
|
-
text-decoration: none;
|
|
270
|
-
transition: color 0.2s ease;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
255
|
.docket__footer-view-text {
|
|
274
256
|
display: inline-block;
|
|
275
257
|
}
|
|
@@ -300,6 +282,16 @@
|
|
|
300
282
|
flex: 2;
|
|
301
283
|
}
|
|
302
284
|
|
|
285
|
+
.docket__footer-actions .docket__footer-button--half {
|
|
286
|
+
flex: 0 0 50%;
|
|
287
|
+
max-width: 50%;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.docket__footer-button:disabled {
|
|
291
|
+
border: var(--button-border-width-outline-default, 1px) solid
|
|
292
|
+
var(--button-color-outline-secondary-border-disabled, #a3a3a3) !important;
|
|
293
|
+
}
|
|
294
|
+
|
|
303
295
|
/* Collapsed header for mobile/tablet */
|
|
304
296
|
|
|
305
297
|
.docket__collapsed-header {
|
|
@@ -407,7 +399,7 @@
|
|
|
407
399
|
|
|
408
400
|
/* Responsive behavior */
|
|
409
401
|
|
|
410
|
-
@media (max-width:
|
|
402
|
+
@media (max-width: 1439px) {
|
|
411
403
|
.docket {
|
|
412
404
|
width: 384px;
|
|
413
405
|
}
|
|
@@ -451,7 +443,9 @@
|
|
|
451
443
|
}
|
|
452
444
|
}
|
|
453
445
|
|
|
454
|
-
|
|
446
|
+
/* Desktop and up */
|
|
447
|
+
|
|
448
|
+
@media (min-width: 1440px) {
|
|
455
449
|
.docket__collapsed-header,
|
|
456
450
|
.docket__close-header {
|
|
457
451
|
display: none;
|