mautourco-components 0.2.161 → 0.2.162
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/SelectedValue/SelectedValue.d.ts +3 -0
- package/dist/components/atoms/SelectedValue/SelectedValue.js +3 -5
- package/dist/components/organisms/DialogSaveQuotation/DialogSaveQuotation.css +104 -0
- package/dist/components/organisms/DialogSaveQuotation/DialogSaveQuotation.d.ts +36 -0
- package/dist/components/organisms/DialogSaveQuotation/DialogSaveQuotation.js +97 -0
- package/dist/components/organisms/DialogSaveQuotation/index.d.ts +2 -0
- package/dist/components/organisms/DialogSaveQuotation/index.js +1 -0
- package/dist/components/organisms/DialogSaveQuotation/save-quotation-schema.d.ts +8 -0
- package/dist/components/organisms/DialogSaveQuotation/save-quotation-schema.js +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/styles/components/selected-value.css +24 -18
- package/package.json +1 -1
- package/src/components/atoms/SelectedValue/SelectedValue.tsx +7 -5
- package/src/components/organisms/DialogSaveQuotation/DialogSaveQuotation.css +100 -0
- package/src/components/organisms/DialogSaveQuotation/DialogSaveQuotation.tsx +249 -0
- package/src/components/organisms/DialogSaveQuotation/index.ts +6 -0
- package/src/components/organisms/DialogSaveQuotation/save-quotation-schema.ts +10 -0
- package/src/styles/components/selected-value.css +21 -18
|
@@ -3,7 +3,10 @@ interface SelectedValueProps {
|
|
|
3
3
|
value: string;
|
|
4
4
|
className?: string;
|
|
5
5
|
iconSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
6
|
+
/** Chip size - controls padding and gap (Figma: chip/spacing/lg = 16px, 4px, 4px) */
|
|
6
7
|
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
8
|
+
/** Text size - defaults to same as size. Use 'md' for Figma body-md (16px, leading-24) */
|
|
9
|
+
textSize?: 'xs' | 'sm' | 'md' | 'lg';
|
|
7
10
|
variant?: 'filled' | 'text';
|
|
8
11
|
color?: 'accent' | 'neutral';
|
|
9
12
|
onRemove?: () => void;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import Icon from '../Icon/Icon';
|
|
3
3
|
import { Text } from '../Typography/Typography';
|
|
4
4
|
var SelectedValue = function (_a) {
|
|
5
|
-
var value = _a.value, _b = _a.className, className = _b === void 0 ? '' : _b, _c = _a.iconSize, iconSize = _c === void 0 ? 'xs' : _c, _d = _a.size, size = _d === void 0 ? 'md' : _d, _e = _a.variant, variant = _e === void 0 ? 'filled' : _e, _f = _a.color, color = _f === void 0 ? 'accent' : _f, onRemove = _a.onRemove, onSelect = _a.onSelect;
|
|
5
|
+
var value = _a.value, _b = _a.className, className = _b === void 0 ? '' : _b, _c = _a.iconSize, iconSize = _c === void 0 ? 'xs' : _c, _d = _a.size, size = _d === void 0 ? 'md' : _d, textSize = _a.textSize, _e = _a.variant, variant = _e === void 0 ? 'filled' : _e, _f = _a.color, color = _f === void 0 ? 'accent' : _f, onRemove = _a.onRemove, onSelect = _a.onSelect;
|
|
6
6
|
var handleRemove = function (event) {
|
|
7
7
|
event.stopPropagation();
|
|
8
8
|
if (onRemove) {
|
|
@@ -10,9 +10,7 @@ var SelectedValue = function (_a) {
|
|
|
10
10
|
}
|
|
11
11
|
};
|
|
12
12
|
var classes = "selected-value selected-value--".concat(size, " selected-value--").concat(color, " selected-value--").concat(variant, " ").concat(className, " ").concat(onSelect ? 'cursor-pointer' : '').trim();
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
return (_jsxs("div", { className: classes, onClick: onSelect, children: [_jsx(Text, { size: getTextSize(), variant: "medium", leading: "4", className: "selected-value__text", children: value }), variant !== 'text' && onRemove && (_jsx("button", { type: "button", className: "selected-value__remove", onClick: handleRemove, "aria-label": "Remove ".concat(value), children: _jsx(Icon, { name: "close", size: iconSize, className: "selected-value__remove-icon" }) }))] }));
|
|
13
|
+
var effectiveTextSize = textSize !== null && textSize !== void 0 ? textSize : size;
|
|
14
|
+
return (_jsxs("div", { className: classes, onClick: onSelect, children: [_jsx(Text, { size: effectiveTextSize, variant: "medium", leading: "6", className: "selected-value__text", children: value }), variant !== 'text' && onRemove && (_jsx("button", { type: "button", className: "selected-value__remove", onClick: handleRemove, "aria-label": "Remove ".concat(value), children: _jsx(Icon, { name: "close", size: iconSize, className: "selected-value__remove-icon" }) }))] }));
|
|
17
15
|
};
|
|
18
16
|
export default SelectedValue;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Do not edit directly, this file was auto-generated.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/* ===== DialogSaveQuotation - Tokens from Figma ===== */
|
|
6
|
+
/* Uses CSS variables from tokens.css */
|
|
7
|
+
|
|
8
|
+
.dialog-save-quotation {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: var(--spacing-gap-gap-6, 24px);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* Selected quotes section - tags + select all */
|
|
15
|
+
.dialog-save-quotation__quotes-section {
|
|
16
|
+
display: flex;
|
|
17
|
+
padding: var(--spacing-gap-gap-4, 16px) 0 var(--spacing-gap-gap-2, 8px) 0;
|
|
18
|
+
justify-content: space-between;
|
|
19
|
+
align-items: flex-start;
|
|
20
|
+
gap: var(--spacing-gap-gap-4, 16px);
|
|
21
|
+
flex-wrap: wrap;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.dialog-save-quotation__quotes-list {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-wrap: wrap;
|
|
27
|
+
gap: var(--spacing-gap-gap-2, 8px);
|
|
28
|
+
flex: 1;
|
|
29
|
+
min-width: 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.dialog-save-quotation__select-all {
|
|
33
|
+
flex-shrink: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* All quotations section header */
|
|
37
|
+
.dialog-save-quotation__section-title {
|
|
38
|
+
font-family:
|
|
39
|
+
var(--font-font-family-body), 'Satoshi', 'Inter', 'Segoe UI', system-ui, sans-serif;
|
|
40
|
+
font-size: var(--font-size-text-base, 16px);
|
|
41
|
+
font-weight: var(--font-weight-font-bold, 700);
|
|
42
|
+
line-height: var(--font-leading-leading-md, 24px);
|
|
43
|
+
color: var(--color-text-default);
|
|
44
|
+
margin: 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Form fields grid */
|
|
48
|
+
.dialog-save-quotation__form {
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
gap: var(--spacing-gap-gap-6, 24px);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.dialog-save-quotation__form-row {
|
|
55
|
+
display: grid;
|
|
56
|
+
grid-template-columns: repeat(3, 1fr);
|
|
57
|
+
gap: var(--spacing-gap-gap-4, 16px);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.dialog-save-quotation__form-row--single {
|
|
61
|
+
grid-template-columns: 1fr;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@media (max-width: 768px) {
|
|
65
|
+
.dialog-save-quotation__form-row {
|
|
66
|
+
grid-template-columns: 1fr;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Form field */
|
|
71
|
+
.dialog-save-quotation__field {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
gap: var(--spacing-gap-gap-2, 8px);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.dialog-save-quotation__label {
|
|
78
|
+
font-family:
|
|
79
|
+
var(--font-font-family-body), 'Satoshi', 'Inter', 'Segoe UI', system-ui, sans-serif;
|
|
80
|
+
font-size: var(--font-size-text-sm, 14px);
|
|
81
|
+
font-weight: var(--font-weight-font-medium, 500);
|
|
82
|
+
line-height: var(--font-leading-leading-sm, 20px);
|
|
83
|
+
color: var(--color-text-default);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.dialog-save-quotation__label--required::after {
|
|
87
|
+
content: ' *';
|
|
88
|
+
color: var(--color-text-state-error);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Footer - buttons full width, side by side */
|
|
92
|
+
.dialog-save-quotation__footer {
|
|
93
|
+
display: flex;
|
|
94
|
+
flex-direction: row;
|
|
95
|
+
align-items: stretch;
|
|
96
|
+
gap: var(--spacing-gap-gap-4, 16px);
|
|
97
|
+
padding: var(--spacing-gap-gap-2, 8px) 0 var(--spacing-gap-gap-4, 16px) 0;
|
|
98
|
+
width: 100%;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.dialog-save-quotation__footer > * {
|
|
102
|
+
flex: 1;
|
|
103
|
+
min-width: 0;
|
|
104
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SaveQuotationSchema } from './save-quotation-schema';
|
|
2
|
+
import './DialogSaveQuotation.css';
|
|
3
|
+
export interface SaveQuotationQuote {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DialogSaveQuotationProps {
|
|
8
|
+
/** Whether the dialog is open */
|
|
9
|
+
open: boolean;
|
|
10
|
+
/** Callback to control dialog open state */
|
|
11
|
+
setOpen: (open: boolean) => void;
|
|
12
|
+
/** List of quotations to save (displayed as removable chips) */
|
|
13
|
+
quotations: SaveQuotationQuote[];
|
|
14
|
+
/** Callback when form is submitted */
|
|
15
|
+
onSubmit: (data: {
|
|
16
|
+
selectedQuotationIds: string[];
|
|
17
|
+
formData: SaveQuotationSchema;
|
|
18
|
+
}) => void;
|
|
19
|
+
/** Optional default values for the form */
|
|
20
|
+
defaultValues?: Partial<SaveQuotationSchema>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* DialogSaveQuotation - Modal to save quotations with agent and quotation details.
|
|
24
|
+
* Based on Figma "Save quotation" design with tokens.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* <DialogSaveQuotation
|
|
29
|
+
* open={open}
|
|
30
|
+
* setOpen={setOpen}
|
|
31
|
+
* quotations={[{ id: '1', name: 'Quote 1' }]}
|
|
32
|
+
* onSubmit={(data) => console.log(data)}
|
|
33
|
+
* />
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function DialogSaveQuotation(props: DialogSaveQuotationProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
2
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
3
|
+
if (ar || !(i in from)) {
|
|
4
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
5
|
+
ar[i] = from[i];
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
9
|
+
};
|
|
10
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
12
|
+
import Button from '../../atoms/Button/Button';
|
|
13
|
+
import Checkbox from '../../atoms/Checkbox/Checkbox';
|
|
14
|
+
import Input from '../../atoms/Inputs/Input/Input';
|
|
15
|
+
import SelectedValue from '../../atoms/SelectedValue/SelectedValue';
|
|
16
|
+
import { DialogBookingConfirm } from '../DialogBookingConfirm';
|
|
17
|
+
import { saveQuotationSchema, } from './save-quotation-schema';
|
|
18
|
+
import './DialogSaveQuotation.css';
|
|
19
|
+
/**
|
|
20
|
+
* DialogSaveQuotation - Modal to save quotations with agent and quotation details.
|
|
21
|
+
* Based on Figma "Save quotation" design with tokens.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <DialogSaveQuotation
|
|
26
|
+
* open={open}
|
|
27
|
+
* setOpen={setOpen}
|
|
28
|
+
* quotations={[{ id: '1', name: 'Quote 1' }]}
|
|
29
|
+
* onSubmit={(data) => console.log(data)}
|
|
30
|
+
* />
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function DialogSaveQuotation(props) {
|
|
34
|
+
var _a, _b, _c, _d;
|
|
35
|
+
var open = props.open, setOpen = props.setOpen, quotations = props.quotations, onSubmit = props.onSubmit, defaultValues = props.defaultValues;
|
|
36
|
+
var _e = useState(quotations), remainingQuotations = _e[0], setRemainingQuotations = _e[1];
|
|
37
|
+
var _f = useState([]), selectedQuotationIds = _f[0], setSelectedQuotationIds = _f[1];
|
|
38
|
+
var _g = useState(false), isSelectAll = _g[0], setIsSelectAll = _g[1];
|
|
39
|
+
var _h = useState((_a = defaultValues === null || defaultValues === void 0 ? void 0 : defaultValues.agentForename) !== null && _a !== void 0 ? _a : ''), agentForename = _h[0], setAgentForename = _h[1];
|
|
40
|
+
var _j = useState((_b = defaultValues === null || defaultValues === void 0 ? void 0 : defaultValues.agentLastname) !== null && _b !== void 0 ? _b : ''), agentLastname = _j[0], setAgentLastname = _j[1];
|
|
41
|
+
var _k = useState((_c = defaultValues === null || defaultValues === void 0 ? void 0 : defaultValues.agency) !== null && _c !== void 0 ? _c : ''), agency = _k[0], setAgency = _k[1];
|
|
42
|
+
var _l = useState((_d = defaultValues === null || defaultValues === void 0 ? void 0 : defaultValues.quotationName) !== null && _d !== void 0 ? _d : ''), quotationName = _l[0], setQuotationName = _l[1];
|
|
43
|
+
var remainingQuotationsFiltered = useMemo(function () {
|
|
44
|
+
return remainingQuotations.filter(function (q) { return selectedQuotationIds.includes(q.id); });
|
|
45
|
+
}, [remainingQuotations, selectedQuotationIds]);
|
|
46
|
+
useEffect(function () {
|
|
47
|
+
setRemainingQuotations(quotations);
|
|
48
|
+
setSelectedQuotationIds(quotations.map(function (q) { return q.id; }));
|
|
49
|
+
}, [quotations]);
|
|
50
|
+
useEffect(function () {
|
|
51
|
+
setIsSelectAll(remainingQuotations.length > 0 &&
|
|
52
|
+
selectedQuotationIds.length === remainingQuotations.length);
|
|
53
|
+
}, [selectedQuotationIds, remainingQuotations]);
|
|
54
|
+
var handleRemoveQuotation = function (id) {
|
|
55
|
+
if (remainingQuotations.length > 1) {
|
|
56
|
+
setRemainingQuotations(function (prev) { return prev.filter(function (q) { return q.id !== id; }); });
|
|
57
|
+
setSelectedQuotationIds(function (prev) { return prev.filter(function (quoteId) { return quoteId !== id; }); });
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var handleSelectQuotation = function (id) {
|
|
61
|
+
if (remainingQuotations.length > 1) {
|
|
62
|
+
setSelectedQuotationIds(function (prev) {
|
|
63
|
+
if (prev.includes(id)) {
|
|
64
|
+
return prev.length > 1 ? prev.filter(function (q) { return q !== id; }) : prev;
|
|
65
|
+
}
|
|
66
|
+
return __spreadArray(__spreadArray([], prev, true), [id], false);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var handleSelectAll = function () {
|
|
71
|
+
if (isSelectAll) {
|
|
72
|
+
setSelectedQuotationIds(remainingQuotations.length > 1 ? [remainingQuotations[0].id] : []);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
setSelectedQuotationIds(remainingQuotations.map(function (q) { return q.id; }));
|
|
76
|
+
}
|
|
77
|
+
setIsSelectAll(!isSelectAll);
|
|
78
|
+
};
|
|
79
|
+
var handleSubmit = function () {
|
|
80
|
+
var formData = {
|
|
81
|
+
agentForename: agentForename,
|
|
82
|
+
agentLastname: agentLastname,
|
|
83
|
+
agency: agency,
|
|
84
|
+
quotationName: quotationName,
|
|
85
|
+
};
|
|
86
|
+
var result = saveQuotationSchema.safeParse(formData);
|
|
87
|
+
if (!result.success) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
onSubmit({
|
|
91
|
+
selectedQuotationIds: remainingQuotationsFiltered.map(function (q) { return q.id; }),
|
|
92
|
+
formData: result.data,
|
|
93
|
+
});
|
|
94
|
+
setOpen(false);
|
|
95
|
+
};
|
|
96
|
+
return (_jsx(DialogBookingConfirm, { open: open, setOpen: setOpen, title: "Save quotation", className: "!max-w-[800px]", closeOnOverlayClick: true, children: _jsxs("div", { className: "dialog-save-quotation", children: [_jsxs("div", { className: "dialog-save-quotation__quotes-section", children: [_jsx("div", { className: "dialog-save-quotation__quotes-list", children: remainingQuotations.map(function (quote) { return (_jsx(SelectedValue, { value: quote.name, color: selectedQuotationIds.includes(quote.id) ? 'accent' : 'neutral', onSelect: function () { return handleSelectQuotation(quote.id); }, onRemove: function () { return handleRemoveQuotation(quote.id); }, size: "lg", textSize: "md" }, quote.id)); }) }), _jsx("div", { className: "dialog-save-quotation__select-all", children: _jsx(Checkbox, { label: "Select all quotations", checked: isSelectAll || remainingQuotations.length === 1, disabled: remainingQuotations.length === 1, onChange: handleSelectAll, labelPosition: "leading" }) })] }), _jsx("h3", { className: "dialog-save-quotation__section-title", children: "All quotations" }), _jsxs("div", { className: "dialog-save-quotation__form", children: [_jsxs("div", { className: "dialog-save-quotation__form-row", children: [_jsxs("div", { className: "dialog-save-quotation__field", children: [_jsx("label", { htmlFor: "agent-forename", className: "dialog-save-quotation__label dialog-save-quotation__label--required", children: "Agent Forename" }), _jsx(Input, { id: "agent-forename", value: agentForename, onChange: function (e) { return setAgentForename(e.target.value); }, placeholder: "Mautourco" })] }), _jsxs("div", { className: "dialog-save-quotation__field", children: [_jsx("label", { htmlFor: "agent-lastname", className: "dialog-save-quotation__label dialog-save-quotation__label--required", children: "Agent Lastname" }), _jsx(Input, { id: "agent-lastname", value: agentLastname, onChange: function (e) { return setAgentLastname(e.target.value); }, placeholder: "B2B" })] }), _jsxs("div", { className: "dialog-save-quotation__field", children: [_jsx("label", { htmlFor: "agency", className: "dialog-save-quotation__label", children: "Agency" }), _jsx(Input, { id: "agency", value: agency, onChange: function (e) { return setAgency(e.target.value); }, placeholder: "Mautourco Ltd" })] })] }), _jsx("div", { className: "dialog-save-quotation__form-row dialog-save-quotation__form-row--single", children: _jsxs("div", { className: "dialog-save-quotation__field", children: [_jsx("label", { htmlFor: "quotation-name", className: "dialog-save-quotation__label dialog-save-quotation__label--required", children: "Quotation name" }), _jsx(Input, { id: "quotation-name", value: quotationName, onChange: function (e) { return setQuotationName(e.target.value); }, placeholder: "Insert client's name" })] }) })] }), _jsxs("div", { className: "dialog-save-quotation__footer", children: [_jsx(Button, { variant: "outline-secondary", size: "sm", onClick: function () { return setOpen(false); }, children: "Cancel" }), _jsx(Button, { variant: "secondary", size: "sm", onClick: handleSubmit, children: "Save quotation" })] })] }) }));
|
|
97
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DialogSaveQuotation, } from './DialogSaveQuotation';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
export declare const saveQuotationSchema: z.ZodObject<{
|
|
3
|
+
agentForename: z.ZodString;
|
|
4
|
+
agentLastname: z.ZodString;
|
|
5
|
+
agency: z.ZodOptional<z.ZodString>;
|
|
6
|
+
quotationName: z.ZodString;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
export type SaveQuotationSchema = z.infer<typeof saveQuotationSchema>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
export var saveQuotationSchema = z.object({
|
|
3
|
+
agentForename: z.string().min(1, 'Agent forename is required'),
|
|
4
|
+
agentLastname: z.string().min(1, 'Agent lastname is required'),
|
|
5
|
+
agency: z.string().optional(),
|
|
6
|
+
quotationName: z.string().min(1, 'Quotation name is required'),
|
|
7
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -65,6 +65,7 @@ export * from './components/organisms/DialogDeleteConfirm';
|
|
|
65
65
|
export { DialogDeleteConfirm } from './components/organisms/DialogDeleteConfirm/DialogDeleteConfirm';
|
|
66
66
|
export { DialogQuoteRename } from './components/organisms/DialogQuoteRename/DialogQuoteRename';
|
|
67
67
|
export * from './components/organisms/DialogSendingMail';
|
|
68
|
+
export * from './components/organisms/DialogSaveQuotation';
|
|
68
69
|
export { default as Docket } from './components/organisms/Docket/Docket';
|
|
69
70
|
export { DocketAccordion, type DocketAccordionProps, } from './components/organisms/DocketAccordion';
|
|
70
71
|
export { Footer } from './components/organisms/Footer/Footer';
|
package/dist/index.js
CHANGED
|
@@ -67,6 +67,7 @@ export * from './components/organisms/DialogDeleteConfirm';
|
|
|
67
67
|
export { DialogDeleteConfirm } from './components/organisms/DialogDeleteConfirm/DialogDeleteConfirm';
|
|
68
68
|
export { DialogQuoteRename } from './components/organisms/DialogQuoteRename/DialogQuoteRename';
|
|
69
69
|
export * from './components/organisms/DialogSendingMail';
|
|
70
|
+
export * from './components/organisms/DialogSaveQuotation';
|
|
70
71
|
export { default as Docket } from './components/organisms/Docket/Docket';
|
|
71
72
|
export { DocketAccordion, } from './components/organisms/DocketAccordion';
|
|
72
73
|
export { Footer } from './components/organisms/Footer/Footer';
|
|
@@ -4,19 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
/* SelectedValue Component Styles */
|
|
6
6
|
|
|
7
|
+
/* Base uses sm as default; tokens: chip-spacing-*-* from Figma */
|
|
8
|
+
|
|
7
9
|
.selected-value {
|
|
8
10
|
display: inline-flex;
|
|
9
11
|
align-items: center;
|
|
10
|
-
gap: var(--chip-spacing-gap,
|
|
11
|
-
padding: var(--chip-spacing-sm-padding-y,
|
|
12
|
-
border-radius: var(--chip-border-radius-pill,
|
|
12
|
+
gap: var(--chip-spacing-sm-gap, 4px);
|
|
13
|
+
padding: var(--chip-spacing-sm-padding-y, 4px) var(--chip-spacing-sm-padding-x, 8px);
|
|
14
|
+
border-radius: var(--chip-border-radius-pill, 9999px);
|
|
13
15
|
/* Typography is now handled by the Text component */
|
|
14
16
|
cursor: default;
|
|
15
17
|
-webkit-user-select: none;
|
|
16
18
|
user-select: none;
|
|
17
19
|
max-width: 200px;
|
|
18
20
|
width: auto;
|
|
19
|
-
opacity: var(--opacity-opacity-100);
|
|
21
|
+
opacity: var(--opacity-opacity-100, 1);
|
|
20
22
|
/* Variant: text-only (no background, no padding) */
|
|
21
23
|
.selected-value--text {
|
|
22
24
|
background: transparent;
|
|
@@ -63,32 +65,36 @@
|
|
|
63
65
|
/* Size variants */
|
|
64
66
|
|
|
65
67
|
.selected-value--xs {
|
|
66
|
-
padding: var(--chip-spacing-xs-padding-y,
|
|
67
|
-
var(--chip-spacing-xs-padding-x,
|
|
68
|
-
gap: var(--chip-spacing-xs-gap,
|
|
68
|
+
padding: var(--chip-spacing-xs-padding-y, 2px)
|
|
69
|
+
var(--chip-spacing-xs-padding-x, 6px);
|
|
70
|
+
gap: var(--chip-spacing-xs-gap, 2px);
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
.selected-value--sm {
|
|
72
|
-
padding: var(--chip-spacing-sm-padding-y,
|
|
73
|
-
var(--chip-spacing-sm-padding-x,
|
|
74
|
-
gap: var(--chip-spacing-sm-gap,
|
|
74
|
+
padding: var(--chip-spacing-sm-padding-y, 4px)
|
|
75
|
+
var(--chip-spacing-sm-padding-x, 8px);
|
|
76
|
+
gap: var(--chip-spacing-sm-gap, 4px);
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
.selected-value--md {
|
|
78
|
-
padding: var(--chip-spacing-md-padding-y,
|
|
79
|
-
var(--chip-spacing-md-padding-x,
|
|
80
|
-
gap: var(--chip-spacing-md-gap,
|
|
80
|
+
padding: var(--chip-spacing-md-padding-y, 4px)
|
|
81
|
+
var(--chip-spacing-md-padding-x, 12px);
|
|
82
|
+
gap: var(--chip-spacing-md-gap, 4px);
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
/* Figma chip/lg: padding-x 16px, padding-y 4px, gap 4px */
|
|
86
|
+
|
|
83
87
|
.selected-value--lg {
|
|
84
|
-
padding: var(--chip-spacing-lg-padding-y,
|
|
85
|
-
var(--chip-spacing-lg-padding-x,
|
|
86
|
-
gap: var(--chip-spacing-lg-gap,
|
|
88
|
+
padding: var(--chip-spacing-lg-padding-y, 4px)
|
|
89
|
+
var(--chip-spacing-lg-padding-x, 16px);
|
|
90
|
+
gap: var(--chip-spacing-lg-gap, 4px);
|
|
87
91
|
}
|
|
88
92
|
|
|
93
|
+
/* Figma chip/color/accent/filled: bg #0f7173, fg #ffffff */
|
|
94
|
+
|
|
89
95
|
.selected-value--accent {
|
|
90
|
-
background: var(--color-atoll-green-800);
|
|
91
|
-
color: var(--color-white);
|
|
96
|
+
background: var(--chip-color-accent-filled-background, var(--color-atoll-green-800));
|
|
97
|
+
color: var(--chip-color-accent-filled-foreground, var(--color-white));
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
.selected-value--neutral {
|
package/package.json
CHANGED
|
@@ -6,7 +6,10 @@ interface SelectedValueProps {
|
|
|
6
6
|
value: string;
|
|
7
7
|
className?: string;
|
|
8
8
|
iconSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
9
|
+
/** Chip size - controls padding and gap (Figma: chip/spacing/lg = 16px, 4px, 4px) */
|
|
9
10
|
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
11
|
+
/** Text size - defaults to same as size. Use 'md' for Figma body-md (16px, leading-24) */
|
|
12
|
+
textSize?: 'xs' | 'sm' | 'md' | 'lg';
|
|
10
13
|
variant?: 'filled' | 'text';
|
|
11
14
|
color?: 'accent' | 'neutral';
|
|
12
15
|
onRemove?: () => void;
|
|
@@ -18,6 +21,7 @@ const SelectedValue: React.FC<SelectedValueProps> = ({
|
|
|
18
21
|
className = '',
|
|
19
22
|
iconSize = 'xs',
|
|
20
23
|
size = 'md',
|
|
24
|
+
textSize,
|
|
21
25
|
variant = 'filled',
|
|
22
26
|
color = 'accent',
|
|
23
27
|
onRemove,
|
|
@@ -33,16 +37,14 @@ const SelectedValue: React.FC<SelectedValueProps> = ({
|
|
|
33
37
|
const classes =
|
|
34
38
|
`selected-value selected-value--${size} selected-value--${color} selected-value--${variant} ${className} ${onSelect ? 'cursor-pointer' : ''}`.trim();
|
|
35
39
|
|
|
36
|
-
const
|
|
37
|
-
return size; // Utilise directement la taille passée en prop
|
|
38
|
-
};
|
|
40
|
+
const effectiveTextSize = textSize ?? size;
|
|
39
41
|
|
|
40
42
|
return (
|
|
41
43
|
<div className={classes} onClick={onSelect}>
|
|
42
44
|
<Text
|
|
43
|
-
size={
|
|
45
|
+
size={effectiveTextSize}
|
|
44
46
|
variant="medium"
|
|
45
|
-
leading="
|
|
47
|
+
leading="6"
|
|
46
48
|
className="selected-value__text">
|
|
47
49
|
{value}
|
|
48
50
|
</Text>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* ===== DialogSaveQuotation - Tokens from Figma ===== */
|
|
2
|
+
/* Uses CSS variables from tokens.css */
|
|
3
|
+
|
|
4
|
+
.dialog-save-quotation {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
gap: var(--spacing-gap-gap-6, 24px);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* Selected quotes section - tags + select all */
|
|
11
|
+
.dialog-save-quotation__quotes-section {
|
|
12
|
+
display: flex;
|
|
13
|
+
padding: var(--spacing-gap-gap-4, 16px) 0 var(--spacing-gap-gap-2, 8px) 0;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
align-items: flex-start;
|
|
16
|
+
gap: var(--spacing-gap-gap-4, 16px);
|
|
17
|
+
flex-wrap: wrap;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.dialog-save-quotation__quotes-list {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-wrap: wrap;
|
|
23
|
+
gap: var(--spacing-gap-gap-2, 8px);
|
|
24
|
+
flex: 1;
|
|
25
|
+
min-width: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.dialog-save-quotation__select-all {
|
|
29
|
+
flex-shrink: 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* All quotations section header */
|
|
33
|
+
.dialog-save-quotation__section-title {
|
|
34
|
+
font-family:
|
|
35
|
+
var(--font-font-family-body), 'Satoshi', 'Inter', 'Segoe UI', system-ui, sans-serif;
|
|
36
|
+
font-size: var(--font-size-text-base, 16px);
|
|
37
|
+
font-weight: var(--font-weight-font-bold, 700);
|
|
38
|
+
line-height: var(--font-leading-leading-md, 24px);
|
|
39
|
+
color: var(--color-text-default);
|
|
40
|
+
margin: 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Form fields grid */
|
|
44
|
+
.dialog-save-quotation__form {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
gap: var(--spacing-gap-gap-6, 24px);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.dialog-save-quotation__form-row {
|
|
51
|
+
display: grid;
|
|
52
|
+
grid-template-columns: repeat(3, 1fr);
|
|
53
|
+
gap: var(--spacing-gap-gap-4, 16px);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.dialog-save-quotation__form-row--single {
|
|
57
|
+
grid-template-columns: 1fr;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@media (max-width: 768px) {
|
|
61
|
+
.dialog-save-quotation__form-row {
|
|
62
|
+
grid-template-columns: 1fr;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Form field */
|
|
67
|
+
.dialog-save-quotation__field {
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
gap: var(--spacing-gap-gap-2, 8px);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.dialog-save-quotation__label {
|
|
74
|
+
font-family:
|
|
75
|
+
var(--font-font-family-body), 'Satoshi', 'Inter', 'Segoe UI', system-ui, sans-serif;
|
|
76
|
+
font-size: var(--font-size-text-sm, 14px);
|
|
77
|
+
font-weight: var(--font-weight-font-medium, 500);
|
|
78
|
+
line-height: var(--font-leading-leading-sm, 20px);
|
|
79
|
+
color: var(--color-text-default);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.dialog-save-quotation__label--required::after {
|
|
83
|
+
content: ' *';
|
|
84
|
+
color: var(--color-text-state-error);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Footer - buttons full width, side by side */
|
|
88
|
+
.dialog-save-quotation__footer {
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-direction: row;
|
|
91
|
+
align-items: stretch;
|
|
92
|
+
gap: var(--spacing-gap-gap-4, 16px);
|
|
93
|
+
padding: var(--spacing-gap-gap-2, 8px) 0 var(--spacing-gap-gap-4, 16px) 0;
|
|
94
|
+
width: 100%;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.dialog-save-quotation__footer > * {
|
|
98
|
+
flex: 1;
|
|
99
|
+
min-width: 0;
|
|
100
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import Button from '../../atoms/Button/Button';
|
|
3
|
+
import Checkbox from '../../atoms/Checkbox/Checkbox';
|
|
4
|
+
import Input from '../../atoms/Inputs/Input/Input';
|
|
5
|
+
import SelectedValue from '../../atoms/SelectedValue/SelectedValue';
|
|
6
|
+
import { DialogBookingConfirm } from '../DialogBookingConfirm';
|
|
7
|
+
import {
|
|
8
|
+
saveQuotationSchema,
|
|
9
|
+
SaveQuotationSchema,
|
|
10
|
+
} from './save-quotation-schema';
|
|
11
|
+
import './DialogSaveQuotation.css';
|
|
12
|
+
|
|
13
|
+
export interface SaveQuotationQuote {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DialogSaveQuotationProps {
|
|
19
|
+
/** Whether the dialog is open */
|
|
20
|
+
open: boolean;
|
|
21
|
+
/** Callback to control dialog open state */
|
|
22
|
+
setOpen: (open: boolean) => void;
|
|
23
|
+
/** List of quotations to save (displayed as removable chips) */
|
|
24
|
+
quotations: SaveQuotationQuote[];
|
|
25
|
+
/** Callback when form is submitted */
|
|
26
|
+
onSubmit: (data: {
|
|
27
|
+
selectedQuotationIds: string[];
|
|
28
|
+
formData: SaveQuotationSchema;
|
|
29
|
+
}) => void;
|
|
30
|
+
/** Optional default values for the form */
|
|
31
|
+
defaultValues?: Partial<SaveQuotationSchema>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* DialogSaveQuotation - Modal to save quotations with agent and quotation details.
|
|
36
|
+
* Based on Figma "Save quotation" design with tokens.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```tsx
|
|
40
|
+
* <DialogSaveQuotation
|
|
41
|
+
* open={open}
|
|
42
|
+
* setOpen={setOpen}
|
|
43
|
+
* quotations={[{ id: '1', name: 'Quote 1' }]}
|
|
44
|
+
* onSubmit={(data) => console.log(data)}
|
|
45
|
+
* />
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export function DialogSaveQuotation(props: DialogSaveQuotationProps) {
|
|
49
|
+
const {
|
|
50
|
+
open,
|
|
51
|
+
setOpen,
|
|
52
|
+
quotations,
|
|
53
|
+
onSubmit,
|
|
54
|
+
defaultValues,
|
|
55
|
+
} = props;
|
|
56
|
+
|
|
57
|
+
const [remainingQuotations, setRemainingQuotations] =
|
|
58
|
+
useState<SaveQuotationQuote[]>(quotations);
|
|
59
|
+
const [selectedQuotationIds, setSelectedQuotationIds] = useState<string[]>([]);
|
|
60
|
+
const [isSelectAll, setIsSelectAll] = useState(false);
|
|
61
|
+
|
|
62
|
+
const [agentForename, setAgentForename] = useState(
|
|
63
|
+
defaultValues?.agentForename ?? ''
|
|
64
|
+
);
|
|
65
|
+
const [agentLastname, setAgentLastname] = useState(
|
|
66
|
+
defaultValues?.agentLastname ?? ''
|
|
67
|
+
);
|
|
68
|
+
const [agency, setAgency] = useState(defaultValues?.agency ?? '');
|
|
69
|
+
const [quotationName, setQuotationName] = useState(
|
|
70
|
+
defaultValues?.quotationName ?? ''
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const remainingQuotationsFiltered = useMemo(
|
|
74
|
+
() =>
|
|
75
|
+
remainingQuotations.filter((q) => selectedQuotationIds.includes(q.id)),
|
|
76
|
+
[remainingQuotations, selectedQuotationIds]
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
setRemainingQuotations(quotations);
|
|
81
|
+
setSelectedQuotationIds(quotations.map((q) => q.id));
|
|
82
|
+
}, [quotations]);
|
|
83
|
+
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
setIsSelectAll(
|
|
86
|
+
remainingQuotations.length > 0 &&
|
|
87
|
+
selectedQuotationIds.length === remainingQuotations.length
|
|
88
|
+
);
|
|
89
|
+
}, [selectedQuotationIds, remainingQuotations]);
|
|
90
|
+
|
|
91
|
+
const handleRemoveQuotation = (id: string) => {
|
|
92
|
+
if (remainingQuotations.length > 1) {
|
|
93
|
+
setRemainingQuotations((prev) => prev.filter((q) => q.id !== id));
|
|
94
|
+
setSelectedQuotationIds((prev) => prev.filter((quoteId) => quoteId !== id));
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handleSelectQuotation = (id: string) => {
|
|
99
|
+
if (remainingQuotations.length > 1) {
|
|
100
|
+
setSelectedQuotationIds((prev) => {
|
|
101
|
+
if (prev.includes(id)) {
|
|
102
|
+
return prev.length > 1 ? prev.filter((q) => q !== id) : prev;
|
|
103
|
+
}
|
|
104
|
+
return [...prev, id];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const handleSelectAll = () => {
|
|
110
|
+
if (isSelectAll) {
|
|
111
|
+
setSelectedQuotationIds(remainingQuotations.length > 1 ? [remainingQuotations[0].id] : []);
|
|
112
|
+
} else {
|
|
113
|
+
setSelectedQuotationIds(remainingQuotations.map((q) => q.id));
|
|
114
|
+
}
|
|
115
|
+
setIsSelectAll(!isSelectAll);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const handleSubmit = () => {
|
|
119
|
+
const formData: SaveQuotationSchema = {
|
|
120
|
+
agentForename,
|
|
121
|
+
agentLastname,
|
|
122
|
+
agency,
|
|
123
|
+
quotationName,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const result = saveQuotationSchema.safeParse(formData);
|
|
127
|
+
if (!result.success) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
onSubmit({
|
|
132
|
+
selectedQuotationIds: remainingQuotationsFiltered.map((q) => q.id),
|
|
133
|
+
formData: result.data,
|
|
134
|
+
});
|
|
135
|
+
setOpen(false);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<DialogBookingConfirm
|
|
140
|
+
open={open}
|
|
141
|
+
setOpen={setOpen}
|
|
142
|
+
title="Save quotation"
|
|
143
|
+
className="!max-w-[800px]"
|
|
144
|
+
closeOnOverlayClick>
|
|
145
|
+
<div className="dialog-save-quotation">
|
|
146
|
+
{/* Quotes section: chips + select all */}
|
|
147
|
+
<div className="dialog-save-quotation__quotes-section">
|
|
148
|
+
<div className="dialog-save-quotation__quotes-list">
|
|
149
|
+
{remainingQuotations.map((quote) => (
|
|
150
|
+
<SelectedValue
|
|
151
|
+
key={quote.id}
|
|
152
|
+
value={quote.name}
|
|
153
|
+
color={selectedQuotationIds.includes(quote.id) ? 'accent' : 'neutral'}
|
|
154
|
+
onSelect={() => handleSelectQuotation(quote.id)}
|
|
155
|
+
onRemove={() => handleRemoveQuotation(quote.id)}
|
|
156
|
+
size="lg"
|
|
157
|
+
textSize="md"
|
|
158
|
+
/>
|
|
159
|
+
))}
|
|
160
|
+
</div>
|
|
161
|
+
<div className="dialog-save-quotation__select-all">
|
|
162
|
+
<Checkbox
|
|
163
|
+
label="Select all quotations"
|
|
164
|
+
checked={isSelectAll || remainingQuotations.length === 1}
|
|
165
|
+
disabled={remainingQuotations.length === 1}
|
|
166
|
+
onChange={handleSelectAll}
|
|
167
|
+
labelPosition="leading"
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
{/* All quotations form */}
|
|
173
|
+
<h3 className="dialog-save-quotation__section-title">All quotations</h3>
|
|
174
|
+
|
|
175
|
+
<div className="dialog-save-quotation__form">
|
|
176
|
+
<div className="dialog-save-quotation__form-row">
|
|
177
|
+
<div className="dialog-save-quotation__field">
|
|
178
|
+
<label
|
|
179
|
+
htmlFor="agent-forename"
|
|
180
|
+
className="dialog-save-quotation__label dialog-save-quotation__label--required">
|
|
181
|
+
Agent Forename
|
|
182
|
+
</label>
|
|
183
|
+
<Input
|
|
184
|
+
id="agent-forename"
|
|
185
|
+
value={agentForename}
|
|
186
|
+
onChange={(e) => setAgentForename(e.target.value)}
|
|
187
|
+
placeholder="Mautourco"
|
|
188
|
+
/>
|
|
189
|
+
</div>
|
|
190
|
+
<div className="dialog-save-quotation__field">
|
|
191
|
+
<label
|
|
192
|
+
htmlFor="agent-lastname"
|
|
193
|
+
className="dialog-save-quotation__label dialog-save-quotation__label--required">
|
|
194
|
+
Agent Lastname
|
|
195
|
+
</label>
|
|
196
|
+
<Input
|
|
197
|
+
id="agent-lastname"
|
|
198
|
+
value={agentLastname}
|
|
199
|
+
onChange={(e) => setAgentLastname(e.target.value)}
|
|
200
|
+
placeholder="B2B"
|
|
201
|
+
/>
|
|
202
|
+
</div>
|
|
203
|
+
<div className="dialog-save-quotation__field">
|
|
204
|
+
<label
|
|
205
|
+
htmlFor="agency"
|
|
206
|
+
className="dialog-save-quotation__label">
|
|
207
|
+
Agency
|
|
208
|
+
</label>
|
|
209
|
+
<Input
|
|
210
|
+
id="agency"
|
|
211
|
+
value={agency}
|
|
212
|
+
onChange={(e) => setAgency(e.target.value)}
|
|
213
|
+
placeholder="Mautourco Ltd"
|
|
214
|
+
/>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<div className="dialog-save-quotation__form-row dialog-save-quotation__form-row--single">
|
|
219
|
+
<div className="dialog-save-quotation__field">
|
|
220
|
+
<label
|
|
221
|
+
htmlFor="quotation-name"
|
|
222
|
+
className="dialog-save-quotation__label dialog-save-quotation__label--required">
|
|
223
|
+
Quotation name
|
|
224
|
+
</label>
|
|
225
|
+
<Input
|
|
226
|
+
id="quotation-name"
|
|
227
|
+
value={quotationName}
|
|
228
|
+
onChange={(e) => setQuotationName(e.target.value)}
|
|
229
|
+
placeholder="Insert client's name"
|
|
230
|
+
/>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<div className="dialog-save-quotation__footer">
|
|
236
|
+
<Button
|
|
237
|
+
variant="outline-secondary"
|
|
238
|
+
size="sm"
|
|
239
|
+
onClick={() => setOpen(false)}>
|
|
240
|
+
Cancel
|
|
241
|
+
</Button>
|
|
242
|
+
<Button variant="secondary" size="sm" onClick={handleSubmit}>
|
|
243
|
+
Save quotation
|
|
244
|
+
</Button>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
</DialogBookingConfirm>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
|
|
3
|
+
export const saveQuotationSchema = z.object({
|
|
4
|
+
agentForename: z.string().min(1, 'Agent forename is required'),
|
|
5
|
+
agentLastname: z.string().min(1, 'Agent lastname is required'),
|
|
6
|
+
agency: z.string().optional(),
|
|
7
|
+
quotationName: z.string().min(1, 'Quotation name is required'),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export type SaveQuotationSchema = z.infer<typeof saveQuotationSchema>;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/* SelectedValue Component Styles */
|
|
2
|
+
/* Base uses sm as default; tokens: chip-spacing-*-* from Figma */
|
|
2
3
|
.selected-value {
|
|
3
4
|
display: inline-flex;
|
|
4
5
|
align-items: center;
|
|
5
|
-
gap: var(--chip-spacing-gap,
|
|
6
|
-
padding: var(--chip-spacing-sm-padding-y,
|
|
7
|
-
border-radius: var(--chip-border-radius-pill,
|
|
6
|
+
gap: var(--chip-spacing-sm-gap, 4px);
|
|
7
|
+
padding: var(--chip-spacing-sm-padding-y, 4px) var(--chip-spacing-sm-padding-x, 8px);
|
|
8
|
+
border-radius: var(--chip-border-radius-pill, 9999px);
|
|
8
9
|
/* Typography is now handled by the Text component */
|
|
9
10
|
cursor: default;
|
|
10
11
|
user-select: none;
|
|
11
12
|
max-width: 200px;
|
|
12
13
|
width: auto;
|
|
13
|
-
opacity: var(--opacity-opacity-100);
|
|
14
|
+
opacity: var(--opacity-opacity-100, 1);
|
|
14
15
|
/* Variant: text-only (no background, no padding) */
|
|
15
16
|
.selected-value--text {
|
|
16
17
|
background: transparent;
|
|
@@ -56,32 +57,34 @@
|
|
|
56
57
|
|
|
57
58
|
/* Size variants */
|
|
58
59
|
.selected-value--xs {
|
|
59
|
-
padding: var(--chip-spacing-xs-padding-y,
|
|
60
|
-
var(--chip-spacing-xs-padding-x,
|
|
61
|
-
gap: var(--chip-spacing-xs-gap,
|
|
60
|
+
padding: var(--chip-spacing-xs-padding-y, 2px)
|
|
61
|
+
var(--chip-spacing-xs-padding-x, 6px);
|
|
62
|
+
gap: var(--chip-spacing-xs-gap, 2px);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
.selected-value--sm {
|
|
65
|
-
padding: var(--chip-spacing-sm-padding-y,
|
|
66
|
-
var(--chip-spacing-sm-padding-x,
|
|
67
|
-
gap: var(--chip-spacing-sm-gap,
|
|
66
|
+
padding: var(--chip-spacing-sm-padding-y, 4px)
|
|
67
|
+
var(--chip-spacing-sm-padding-x, 8px);
|
|
68
|
+
gap: var(--chip-spacing-sm-gap, 4px);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
.selected-value--md {
|
|
71
|
-
padding: var(--chip-spacing-md-padding-y,
|
|
72
|
-
var(--chip-spacing-md-padding-x,
|
|
73
|
-
gap: var(--chip-spacing-md-gap,
|
|
72
|
+
padding: var(--chip-spacing-md-padding-y, 4px)
|
|
73
|
+
var(--chip-spacing-md-padding-x, 12px);
|
|
74
|
+
gap: var(--chip-spacing-md-gap, 4px);
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
/* Figma chip/lg: padding-x 16px, padding-y 4px, gap 4px */
|
|
76
78
|
.selected-value--lg {
|
|
77
|
-
padding: var(--chip-spacing-lg-padding-y,
|
|
78
|
-
var(--chip-spacing-lg-padding-x,
|
|
79
|
-
gap: var(--chip-spacing-lg-gap,
|
|
79
|
+
padding: var(--chip-spacing-lg-padding-y, 4px)
|
|
80
|
+
var(--chip-spacing-lg-padding-x, 16px);
|
|
81
|
+
gap: var(--chip-spacing-lg-gap, 4px);
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
/* Figma chip/color/accent/filled: bg #0f7173, fg #ffffff */
|
|
82
85
|
.selected-value--accent {
|
|
83
|
-
background: var(--color-atoll-green-800);
|
|
84
|
-
color: var(--color-white);
|
|
86
|
+
background: var(--chip-color-accent-filled-background, var(--color-atoll-green-800));
|
|
87
|
+
color: var(--chip-color-accent-filled-foreground, var(--color-white));
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
.selected-value--neutral {
|