kupos-ui-components-lib 9.10.0 → 9.10.2
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/KuposUIComponent.d.ts +14 -1
- package/dist/KuposUIComponent.js +3 -0
- package/dist/components/ServiceItem/ServiceItemDesktop.d.ts +1 -1
- package/dist/components/ServiceItem/ServiceItemDesktop.js +2 -20
- package/dist/components/ServiceItem/ServiceItemMobile.d.ts +1 -1
- package/dist/components/ServiceItem/ServiceItemMobile.js +2 -2
- package/dist/components/ServiceItem/mobileTypes.d.ts +0 -2
- package/dist/components/ServiceItem/types.d.ts +0 -7
- package/dist/components/Survey/SurveyDesktop.js +17 -16
- package/dist/components/Survey/SurveyMobile.js +17 -16
- package/dist/index.d.ts +5 -1
- package/dist/index.js +9 -1
- package/dist/styles.css +32 -6
- package/dist/ui/SeatSection/SeatSection.d.ts +1 -7
- package/dist/ui/SeatSection/SeatSection.js +9 -38
- package/dist/ui/mobileweb/DateTimeSectionMobile.d.ts +1 -2
- package/dist/ui/mobileweb/DateTimeSectionMobile.js +6 -12
- package/dist/ui/mobileweb/SeatSectionMobile.d.ts +1 -2
- package/dist/ui/mobileweb/SeatSectionMobile.js +14 -21
- package/dist/ui/mobileweb/ServiceBadgesMobile.js +2 -2
- package/dist/utils/CommonService.d.ts +1 -1
- package/dist/utils/CommonService.js +1 -5
- package/package.json +1 -1
- package/src/KuposUIComponent.tsx +22 -1
- package/src/assets/images/anims/service_list/succes_anim.json +1 -0
- package/src/components/ServiceItem/ServiceItemDesktop.tsx +0 -43
- package/src/components/ServiceItem/ServiceItemMobile.tsx +1 -3
- package/src/components/ServiceItem/mobileTypes.ts +26 -32
- package/src/components/ServiceItem/types.ts +0 -12
- package/src/components/Survey/ResponsiveSurvey.tsx +14 -0
- package/src/components/Survey/SurveyDesktop.tsx +121 -0
- package/src/components/Survey/SurveyMobile.tsx +125 -0
- package/src/components/Survey/index.ts +5 -0
- package/src/components/Survey/types.ts +22 -0
- package/src/index.ts +23 -0
- package/src/ui/BottomSheet/BottomSheet.tsx +131 -0
- package/src/ui/BottomSheet/index.ts +2 -0
- package/src/ui/Modal/Modal.tsx +92 -0
- package/src/ui/Modal/ModalHeader.tsx +58 -0
- package/src/ui/Modal/index.ts +4 -0
- package/src/ui/SeatSection/SeatSection.tsx +18 -86
- package/src/ui/Survey/FeedbackBanner.tsx +36 -0
- package/src/ui/Survey/FeedbackTextarea.tsx +84 -0
- package/src/ui/Survey/HeartIcon.tsx +18 -0
- package/src/ui/Survey/ScoreButtons.tsx +91 -0
- package/src/ui/Survey/SurveyFooter.tsx +145 -0
- package/src/ui/Survey/SurveyHeader.tsx +72 -0
- package/src/ui/Survey/ThankYouCard.tsx +100 -0
- package/src/ui/Survey/constants.ts +59 -0
- package/src/ui/Survey/index.ts +9 -0
- package/src/ui/mobileweb/DateTimeSectionMobile.tsx +35 -44
- package/src/ui/mobileweb/SeatSectionMobile.tsx +11 -26
- package/src/ui/mobileweb/ServiceBadgesMobile.tsx +2 -2
- package/src/utils/CommonService.ts +1 -7
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SurveyProps } from "./types";
|
|
3
|
+
import { BottomSheet } from "../../ui/BottomSheet";
|
|
4
|
+
import {
|
|
5
|
+
ThankYouCard,
|
|
6
|
+
SurveyHeader,
|
|
7
|
+
ScoreButtons,
|
|
8
|
+
FeedbackBanner,
|
|
9
|
+
FeedbackTextarea,
|
|
10
|
+
SurveyFooter,
|
|
11
|
+
getFeedbackConfig,
|
|
12
|
+
} from "../../ui/Survey";
|
|
13
|
+
import KuposButton from "../../ui/KuposButton/KuposButton";
|
|
14
|
+
|
|
15
|
+
const SurveyMobile = ({
|
|
16
|
+
isOpen,
|
|
17
|
+
isSubmitted,
|
|
18
|
+
selectedScore,
|
|
19
|
+
onScoreChange,
|
|
20
|
+
feedback = "",
|
|
21
|
+
onFeedbackChange,
|
|
22
|
+
onClose,
|
|
23
|
+
onSkip,
|
|
24
|
+
onSubmit,
|
|
25
|
+
colors,
|
|
26
|
+
icons,
|
|
27
|
+
isLoading,
|
|
28
|
+
}: SurveyProps) => {
|
|
29
|
+
if (!isOpen) return null;
|
|
30
|
+
|
|
31
|
+
const config = getFeedbackConfig(selectedScore);
|
|
32
|
+
|
|
33
|
+
const handleSubmit = () => {
|
|
34
|
+
if (selectedScore != null) {
|
|
35
|
+
onSubmit?.(selectedScore, feedback);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<BottomSheet
|
|
41
|
+
isOpen={isOpen ?? false}
|
|
42
|
+
onClose={onClose}
|
|
43
|
+
showHandle={true}
|
|
44
|
+
showBackdrop={false}
|
|
45
|
+
blurBackground={true}
|
|
46
|
+
blurAmount="2px"
|
|
47
|
+
padding={isSubmitted ? 0 : "0 20px 32px"}
|
|
48
|
+
>
|
|
49
|
+
{/* {isSubmitted ? (
|
|
50
|
+
<ThankYouCard onClose={onClose} titleFontSize="1.35rem" />
|
|
51
|
+
) : ( */}
|
|
52
|
+
<>
|
|
53
|
+
{/* <SurveyHeader onClose={onClose} /> */}
|
|
54
|
+
{/* Close Button */}
|
|
55
|
+
<button
|
|
56
|
+
onClick={onClose}
|
|
57
|
+
aria-label="Close survey"
|
|
58
|
+
className="absolute top-[15px] right-[25px] bg-transparent border-none cursor-pointer text-[22px] text-gray-400 flex items-center justify-center p-1 z-10 transition-colors duration-200 hover:text-gray-600"
|
|
59
|
+
>
|
|
60
|
+
<img
|
|
61
|
+
src={icons.closeIcon}
|
|
62
|
+
alt="Close"
|
|
63
|
+
className="w-[16px] h-[16px] block"
|
|
64
|
+
/>
|
|
65
|
+
</button>
|
|
66
|
+
|
|
67
|
+
{/* Centered Illustration */}
|
|
68
|
+
{icons?.surveyIcon && (
|
|
69
|
+
<div className="flex justify-center mb-3 mt-2">
|
|
70
|
+
<img
|
|
71
|
+
src={icons.surveyIcon}
|
|
72
|
+
alt="Survey Illustration"
|
|
73
|
+
className="w-[90px] h-[90px] block"
|
|
74
|
+
/>
|
|
75
|
+
</div>
|
|
76
|
+
)}
|
|
77
|
+
|
|
78
|
+
{/* Centered Title */}
|
|
79
|
+
<h2 className="text-[18px] bold-text leading-[1.25] text-center mt-4 mb-2">
|
|
80
|
+
Ayúdanos a mejorar
|
|
81
|
+
</h2>
|
|
82
|
+
|
|
83
|
+
{/* Centered Subtitle */}
|
|
84
|
+
<p className="text-[13.33px] text-center leading-[1.4] mb-6 max-w-[460px] mx-auto">
|
|
85
|
+
Basándote en tu experiencia de compra.
|
|
86
|
+
<br />
|
|
87
|
+
¿Nos recomendarías a un amigo?
|
|
88
|
+
</p>
|
|
89
|
+
|
|
90
|
+
<ScoreButtons
|
|
91
|
+
selectedScore={selectedScore}
|
|
92
|
+
onScoreChange={onScoreChange}
|
|
93
|
+
buttonHeight={44}
|
|
94
|
+
fontSize={13}
|
|
95
|
+
gap={6}
|
|
96
|
+
colors={colors}
|
|
97
|
+
/>
|
|
98
|
+
|
|
99
|
+
{/* <FeedbackBanner config={config} /> */}
|
|
100
|
+
|
|
101
|
+
<FeedbackTextarea
|
|
102
|
+
config={config}
|
|
103
|
+
feedback={feedback}
|
|
104
|
+
onFeedbackChange={onFeedbackChange}
|
|
105
|
+
/>
|
|
106
|
+
|
|
107
|
+
<div className="flex justify-center mt-[20px]">
|
|
108
|
+
<div className="w-[180px]">
|
|
109
|
+
<KuposButton
|
|
110
|
+
isSoldOut={selectedScore == null}
|
|
111
|
+
isLoading={isLoading || false}
|
|
112
|
+
buttonColor={"#FF8E43"}
|
|
113
|
+
buyLabel="Enviar"
|
|
114
|
+
soldOutLabel="Enviar"
|
|
115
|
+
onClick={handleSubmit}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</>
|
|
120
|
+
{/* )} */}
|
|
121
|
+
</BottomSheet>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export default SurveyMobile;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface SurveyProps {
|
|
2
|
+
variant?: "mobile" | "desktop";
|
|
3
|
+
isOpen?: boolean;
|
|
4
|
+
selectedScore?: number | null;
|
|
5
|
+
onScoreChange?: (score: number) => void;
|
|
6
|
+
feedback?: string;
|
|
7
|
+
onFeedbackChange?: (text: string) => void;
|
|
8
|
+
onClose?: () => void;
|
|
9
|
+
onSkip?: () => void;
|
|
10
|
+
onSubmit?: (score: number, feedback: string) => void;
|
|
11
|
+
isSubmitted?: boolean;
|
|
12
|
+
isLoading?: boolean;
|
|
13
|
+
colors?: {
|
|
14
|
+
secondaryColor?: string;
|
|
15
|
+
tertiaryColor?: string;
|
|
16
|
+
primaryColor?: string;
|
|
17
|
+
};
|
|
18
|
+
icons?: {
|
|
19
|
+
surveyIcon?: string;
|
|
20
|
+
closeIcon?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,16 @@ import {
|
|
|
25
25
|
ResponsiveFilterBar,
|
|
26
26
|
} from "./components/FilterBar";
|
|
27
27
|
|
|
28
|
+
// Import Survey components
|
|
29
|
+
import {
|
|
30
|
+
SurveyDesktop,
|
|
31
|
+
SurveyMobile,
|
|
32
|
+
ResponsiveSurvey,
|
|
33
|
+
} from "./components/Survey";
|
|
34
|
+
|
|
35
|
+
// Import Modal components
|
|
36
|
+
import { Modal, ModalHeader } from "./ui/Modal";
|
|
37
|
+
|
|
28
38
|
export {
|
|
29
39
|
ServiceItemDesktop,
|
|
30
40
|
ServiceItemMobile,
|
|
@@ -46,6 +56,16 @@ export {
|
|
|
46
56
|
FilterBarDesktop,
|
|
47
57
|
FilterBarMobile,
|
|
48
58
|
ResponsiveFilterBar,
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
//Survey components
|
|
62
|
+
SurveyDesktop,
|
|
63
|
+
SurveyMobile,
|
|
64
|
+
ResponsiveSurvey,
|
|
65
|
+
|
|
66
|
+
// Modal components
|
|
67
|
+
Modal,
|
|
68
|
+
ModalHeader,
|
|
49
69
|
};
|
|
50
70
|
|
|
51
71
|
// Also export types
|
|
@@ -54,3 +74,6 @@ export type { MobileServiceItemProps } from "./components/ServiceItem/mobileType
|
|
|
54
74
|
export type { PaymentSideBarProps } from "./components/PaymentSideBar/types";
|
|
55
75
|
export type { ServiceListProps } from "./components/ServiceList/types";
|
|
56
76
|
export type { FilterBarProps } from "./components/FilterBar/tyoes";
|
|
77
|
+
export type { SurveyProps } from "./components/Survey/types";
|
|
78
|
+
export type { ModalProps, ModalVariant, ModalSize, ModalHeaderProps } from "./ui/Modal";
|
|
79
|
+
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ReactDOM from "react-dom";
|
|
3
|
+
|
|
4
|
+
export interface BottomSheetProps {
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
onClose?: () => void;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
showHandle?: boolean;
|
|
9
|
+
showBackdrop?: boolean;
|
|
10
|
+
backdropColor?: string;
|
|
11
|
+
blurBackground?: boolean;
|
|
12
|
+
blurAmount?: string;
|
|
13
|
+
closeOnBackdrop?: boolean;
|
|
14
|
+
padding?: string | number;
|
|
15
|
+
maxHeight?: string;
|
|
16
|
+
borderRadius?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const STYLE_ID = "__kupos_bottom_sheet_style__";
|
|
20
|
+
|
|
21
|
+
const injectStyle = () => {
|
|
22
|
+
if (typeof document === "undefined") return;
|
|
23
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
24
|
+
const style = document.createElement("style");
|
|
25
|
+
style.id = STYLE_ID;
|
|
26
|
+
style.textContent = `
|
|
27
|
+
@keyframes __ks_slideUp {
|
|
28
|
+
from { transform: translateY(100%); }
|
|
29
|
+
to { transform: translateY(0); }
|
|
30
|
+
}
|
|
31
|
+
@keyframes __ks_fadeIn {
|
|
32
|
+
from { opacity: 0; }
|
|
33
|
+
to { opacity: 1; }
|
|
34
|
+
}
|
|
35
|
+
.__ks_sheet {
|
|
36
|
+
animation: __ks_slideUp 0.38s cubic-bezier(0.32, 0.72, 0, 1) both;
|
|
37
|
+
}
|
|
38
|
+
.__ks_backdrop {
|
|
39
|
+
animation: __ks_fadeIn 0.3s ease both;
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
document.head.appendChild(style);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const BottomSheet = ({
|
|
46
|
+
isOpen,
|
|
47
|
+
onClose,
|
|
48
|
+
children,
|
|
49
|
+
showHandle = true,
|
|
50
|
+
showBackdrop = false,
|
|
51
|
+
backdropColor = "rgba(0,0,0,0.45)",
|
|
52
|
+
blurBackground = false,
|
|
53
|
+
blurAmount = "6px",
|
|
54
|
+
closeOnBackdrop = true,
|
|
55
|
+
padding = "20px 20px 32px",
|
|
56
|
+
maxHeight = "92vh",
|
|
57
|
+
borderRadius = "24px 24px 0 0",
|
|
58
|
+
}: BottomSheetProps) => {
|
|
59
|
+
if (!isOpen) return null;
|
|
60
|
+
|
|
61
|
+
injectStyle();
|
|
62
|
+
|
|
63
|
+
const sheet = (
|
|
64
|
+
<>
|
|
65
|
+
{(showBackdrop || blurBackground) && (
|
|
66
|
+
<div
|
|
67
|
+
className="__ks_backdrop"
|
|
68
|
+
onClick={closeOnBackdrop ? onClose : undefined}
|
|
69
|
+
style={{
|
|
70
|
+
position: "fixed",
|
|
71
|
+
top: 0,
|
|
72
|
+
left: 0,
|
|
73
|
+
right: 0,
|
|
74
|
+
bottom: 0,
|
|
75
|
+
zIndex: 9998,
|
|
76
|
+
backgroundColor: showBackdrop ? backdropColor : "rgba(0,0,0,0.4)",
|
|
77
|
+
backdropFilter: blurBackground ? `blur(${blurAmount})` : undefined,
|
|
78
|
+
WebkitBackdropFilter: blurBackground
|
|
79
|
+
? `blur(${blurAmount})`
|
|
80
|
+
: undefined,
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
|
|
85
|
+
<div
|
|
86
|
+
className="__ks_sheet"
|
|
87
|
+
style={{
|
|
88
|
+
position: "fixed",
|
|
89
|
+
left: 0,
|
|
90
|
+
right: 0,
|
|
91
|
+
bottom: 0,
|
|
92
|
+
zIndex: 9999,
|
|
93
|
+
background: "#FFFFFF",
|
|
94
|
+
maxHeight,
|
|
95
|
+
borderRadius,
|
|
96
|
+
overflowY: "auto",
|
|
97
|
+
boxSizing: "border-box" as const,
|
|
98
|
+
boxShadow: "0 -8px 40px rgba(0,0,0,0.15)",
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
{showHandle && (
|
|
102
|
+
<div
|
|
103
|
+
style={{
|
|
104
|
+
display: "flex",
|
|
105
|
+
justifyContent: "center",
|
|
106
|
+
padding: "12px 0 4px",
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
<div
|
|
110
|
+
style={{
|
|
111
|
+
width: 40,
|
|
112
|
+
height: 4,
|
|
113
|
+
borderRadius: 999,
|
|
114
|
+
backgroundColor: "#E5E7EB",
|
|
115
|
+
}}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
)}
|
|
119
|
+
|
|
120
|
+
<div style={{ padding }}>{children}</div>
|
|
121
|
+
</div>
|
|
122
|
+
</>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
if (typeof document !== "undefined") {
|
|
126
|
+
return ReactDOM.createPortal(sheet, document.body) as React.ReactElement;
|
|
127
|
+
}
|
|
128
|
+
return sheet;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export default BottomSheet;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ReactDOM from "react-dom";
|
|
3
|
+
|
|
4
|
+
export type ModalVariant = "center" | "bottom-sheet";
|
|
5
|
+
export type ModalSize = "sm" | "md" | "lg" | "xl" | "full";
|
|
6
|
+
|
|
7
|
+
export interface ModalProps {
|
|
8
|
+
isOpen: boolean;
|
|
9
|
+
onClose?: () => void;
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
variant?: ModalVariant;
|
|
12
|
+
size?: ModalSize;
|
|
13
|
+
closeOnBackdrop?: boolean;
|
|
14
|
+
padding?: string | number;
|
|
15
|
+
borderRadius?: string | number;
|
|
16
|
+
backdropColor?: string;
|
|
17
|
+
maxWidth?: number | string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const SIZE_MAP: Record<ModalSize, number | string> = {
|
|
21
|
+
sm: 400,
|
|
22
|
+
md: 520,
|
|
23
|
+
lg: 640,
|
|
24
|
+
xl: 800,
|
|
25
|
+
full: "100%",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const Modal = ({
|
|
29
|
+
isOpen,
|
|
30
|
+
onClose,
|
|
31
|
+
children,
|
|
32
|
+
variant = "center",
|
|
33
|
+
size = "md",
|
|
34
|
+
closeOnBackdrop = true,
|
|
35
|
+
padding = "28px 32px 24px",
|
|
36
|
+
borderRadius,
|
|
37
|
+
backdropColor = "rgba(0,0,0,0.45)",
|
|
38
|
+
maxWidth,
|
|
39
|
+
}: ModalProps) => {
|
|
40
|
+
if (!isOpen) return null;
|
|
41
|
+
|
|
42
|
+
const resolvedMaxWidth = maxWidth ?? SIZE_MAP[size];
|
|
43
|
+
const resolvedRadius =
|
|
44
|
+
borderRadius ?? (variant === "bottom-sheet" ? "24px 24px 0 0" : 24);
|
|
45
|
+
|
|
46
|
+
const overlayStyle: React.CSSProperties = {
|
|
47
|
+
position: "fixed",
|
|
48
|
+
top: 0,
|
|
49
|
+
left: 0,
|
|
50
|
+
right: 0,
|
|
51
|
+
bottom: 0,
|
|
52
|
+
zIndex: 9999,
|
|
53
|
+
display: "flex",
|
|
54
|
+
alignItems: variant === "bottom-sheet" ? "flex-end" : "center",
|
|
55
|
+
justifyContent: "center",
|
|
56
|
+
padding: variant === "bottom-sheet" ? 0 : 16,
|
|
57
|
+
backgroundColor: backdropColor,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const cardStyle: React.CSSProperties = {
|
|
61
|
+
background: "#FFFFFF",
|
|
62
|
+
width: "100%",
|
|
63
|
+
maxWidth: resolvedMaxWidth,
|
|
64
|
+
maxHeight: variant === "bottom-sheet" ? "92vh" : undefined,
|
|
65
|
+
borderRadius: resolvedRadius,
|
|
66
|
+
padding,
|
|
67
|
+
position: "relative",
|
|
68
|
+
boxSizing: "border-box",
|
|
69
|
+
overflowY: variant === "bottom-sheet" ? "auto" : undefined,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const modal = (
|
|
73
|
+
<div
|
|
74
|
+
style={overlayStyle}
|
|
75
|
+
onClick={closeOnBackdrop ? onClose : undefined}
|
|
76
|
+
>
|
|
77
|
+
<div
|
|
78
|
+
style={cardStyle}
|
|
79
|
+
onClick={(e) => e.stopPropagation()}
|
|
80
|
+
>
|
|
81
|
+
{children}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (typeof document !== "undefined") {
|
|
87
|
+
return ReactDOM.createPortal(modal, document.body) as React.ReactElement;
|
|
88
|
+
}
|
|
89
|
+
return modal;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default Modal;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export interface ModalHeaderProps {
|
|
4
|
+
title?: React.ReactNode;
|
|
5
|
+
onClose?: () => void;
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const ModalHeader = ({ title, onClose, children }: ModalHeaderProps) => (
|
|
10
|
+
<div
|
|
11
|
+
style={{
|
|
12
|
+
display: "flex",
|
|
13
|
+
alignItems: "center",
|
|
14
|
+
justifyContent: "space-between",
|
|
15
|
+
marginBottom: 20,
|
|
16
|
+
}}
|
|
17
|
+
>
|
|
18
|
+
<div style={{ flex: 1 }}>
|
|
19
|
+
{title && (
|
|
20
|
+
<span
|
|
21
|
+
style={{
|
|
22
|
+
fontSize: "1rem",
|
|
23
|
+
fontWeight: 700,
|
|
24
|
+
color: "#111827",
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
{title}
|
|
28
|
+
</span>
|
|
29
|
+
)}
|
|
30
|
+
{children}
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
{onClose && (
|
|
34
|
+
<button
|
|
35
|
+
onClick={onClose}
|
|
36
|
+
style={{
|
|
37
|
+
width: 36,
|
|
38
|
+
height: 36,
|
|
39
|
+
borderRadius: "50%",
|
|
40
|
+
background: "#F3F4F6",
|
|
41
|
+
border: "none",
|
|
42
|
+
cursor: "pointer",
|
|
43
|
+
fontSize: 14,
|
|
44
|
+
color: "#6B7280",
|
|
45
|
+
display: "flex",
|
|
46
|
+
alignItems: "center",
|
|
47
|
+
justifyContent: "center",
|
|
48
|
+
flexShrink: 0,
|
|
49
|
+
marginLeft: 12,
|
|
50
|
+
}}
|
|
51
|
+
>
|
|
52
|
+
✕
|
|
53
|
+
</button>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
export default ModalHeader;
|
|
@@ -7,8 +7,6 @@ interface SeatType {
|
|
|
7
7
|
label: string;
|
|
8
8
|
fare: number;
|
|
9
9
|
key: any;
|
|
10
|
-
apiSeatType?: string;
|
|
11
|
-
api_seat_type?: string;
|
|
12
10
|
}
|
|
13
11
|
|
|
14
12
|
interface SeatSectionProps {
|
|
@@ -23,15 +21,6 @@ interface SeatSectionProps {
|
|
|
23
21
|
serviceItem?: any;
|
|
24
22
|
renderIcon?: (iconKey: string, size?: string) => React.ReactNode;
|
|
25
23
|
discountSeatPriceColor?: string;
|
|
26
|
-
isTrain?: boolean;
|
|
27
|
-
selectedSeatKey?: any;
|
|
28
|
-
onSeatSelect?: (
|
|
29
|
-
key: any,
|
|
30
|
-
price: number,
|
|
31
|
-
seatKey: string,
|
|
32
|
-
apiSeatType?: string,
|
|
33
|
-
) => void;
|
|
34
|
-
topLabelColor?: string;
|
|
35
24
|
}
|
|
36
25
|
|
|
37
26
|
function getAllSeatTypes(seatTypes: SeatType[]) {
|
|
@@ -42,8 +31,6 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
|
|
|
42
31
|
let seatTypesWithPrices = seatTypes.filter(Boolean).map((val) => ({
|
|
43
32
|
label: val?.label,
|
|
44
33
|
price: val?.fare,
|
|
45
|
-
key: val?.key,
|
|
46
|
-
apiSeatType: val?.apiSeatType || val?.api_seat_type,
|
|
47
34
|
}));
|
|
48
35
|
|
|
49
36
|
seatTypesWithPrices.sort((a, b) => a.price - b.price);
|
|
@@ -51,7 +38,7 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
|
|
|
51
38
|
return seatTypesWithPrices;
|
|
52
39
|
}
|
|
53
40
|
|
|
54
|
-
function getSortedSeatTypes(seatTypes: SeatType[]
|
|
41
|
+
function getSortedSeatTypes(seatTypes: SeatType[]) {
|
|
55
42
|
if (!seatTypes?.length) {
|
|
56
43
|
return [{ label: "Salon cama", price: 0 }];
|
|
57
44
|
}
|
|
@@ -65,9 +52,7 @@ function getSortedSeatTypes(seatTypes: SeatType[], isTrain: any) {
|
|
|
65
52
|
seatTypesWithPrices[2] = seatTypesWithPrices[premiumIndex];
|
|
66
53
|
}
|
|
67
54
|
|
|
68
|
-
|
|
69
|
-
seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
|
|
70
|
-
}
|
|
55
|
+
seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
|
|
71
56
|
|
|
72
57
|
const seenPrices = new Set<number>();
|
|
73
58
|
seatTypesWithPrices = seatTypesWithPrices.filter((seat) => {
|
|
@@ -112,18 +97,14 @@ function SeatSection({
|
|
|
112
97
|
priceColor,
|
|
113
98
|
currencySign,
|
|
114
99
|
removeDuplicateSeats,
|
|
115
|
-
selectedSeatKey,
|
|
116
|
-
onSeatSelect,
|
|
117
100
|
isPeru,
|
|
118
101
|
serviceItem,
|
|
119
102
|
renderIcon,
|
|
120
103
|
dpSeatColor,
|
|
121
104
|
discountSeatPriceColor,
|
|
122
|
-
isTrain,
|
|
123
|
-
topLabelColor,
|
|
124
105
|
}: SeatSectionProps): React.ReactElement {
|
|
125
106
|
const uniqueSeats = getUniqueSeats(seatTypes);
|
|
126
|
-
const sortedSeatTypes = getSortedSeatTypes(seatTypes
|
|
107
|
+
const sortedSeatTypes = getSortedSeatTypes(seatTypes);
|
|
127
108
|
const numberOfSeats = getNumberOfSeats(seatTypes);
|
|
128
109
|
const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
|
|
129
110
|
|
|
@@ -135,71 +116,22 @@ function SeatSection({
|
|
|
135
116
|
const renderSeatNames = () => {
|
|
136
117
|
const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
|
|
137
118
|
|
|
138
|
-
return seats.map((val, key: number) =>
|
|
139
|
-
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
? () =>
|
|
146
|
-
val.label === selectedSeatKey
|
|
147
|
-
? onSeatSelect?.(null, 0, "", "")
|
|
148
|
-
: onSeatSelect?.(
|
|
149
|
-
val.label,
|
|
150
|
-
val.price,
|
|
151
|
-
val.key,
|
|
152
|
-
(val as any).apiSeatType,
|
|
153
|
-
)
|
|
154
|
-
: undefined
|
|
155
|
-
}
|
|
119
|
+
return seats.map((val, key: number) =>
|
|
120
|
+
SEAT_EXCEPTIONS.includes(val.label) ? null : (
|
|
121
|
+
<span
|
|
122
|
+
key={key}
|
|
123
|
+
className={`flex items-center justify-between text-[13.33px] ${
|
|
124
|
+
isSoldOut ? "text-[#c0c0c0]" : ""
|
|
125
|
+
}`}
|
|
156
126
|
>
|
|
157
|
-
{
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
marginRight: "10px",
|
|
166
|
-
display: "flex",
|
|
167
|
-
alignItems: "center",
|
|
168
|
-
justifyContent: "center",
|
|
169
|
-
}}
|
|
170
|
-
>
|
|
171
|
-
{val.label === selectedSeatKey && (
|
|
172
|
-
<div
|
|
173
|
-
style={{
|
|
174
|
-
backgroundColor: topLabelColor,
|
|
175
|
-
borderRadius: "50%",
|
|
176
|
-
width: "7px",
|
|
177
|
-
height: "7px",
|
|
178
|
-
}}
|
|
179
|
-
/>
|
|
180
|
-
)}
|
|
181
|
-
</div>
|
|
182
|
-
)}
|
|
183
|
-
<span
|
|
184
|
-
key={key}
|
|
185
|
-
className={`flex items-center justify-between text-[13.33px] ${
|
|
186
|
-
isSoldOut ? "text-[#c0c0c0]" : ""
|
|
187
|
-
}`}
|
|
188
|
-
>
|
|
189
|
-
{typeof val.label === "string" || typeof val.label === "number"
|
|
190
|
-
? removeDuplicateSeats && isPeru
|
|
191
|
-
? CommonService.truncateSeatLabel(val.label)
|
|
192
|
-
: isTrain
|
|
193
|
-
? CommonService.truncateSeatLabel(
|
|
194
|
-
CommonService.capitalize(String(val.label)),
|
|
195
|
-
8,
|
|
196
|
-
)
|
|
197
|
-
: val.label
|
|
198
|
-
: null}
|
|
199
|
-
</span>
|
|
200
|
-
</div>
|
|
201
|
-
);
|
|
202
|
-
});
|
|
127
|
+
{typeof val.label === "string" || typeof val.label === "number"
|
|
128
|
+
? removeDuplicateSeats && isPeru
|
|
129
|
+
? CommonService.truncateSeatLabel(val.label)
|
|
130
|
+
: val.label
|
|
131
|
+
: null}
|
|
132
|
+
</span>
|
|
133
|
+
),
|
|
134
|
+
);
|
|
203
135
|
};
|
|
204
136
|
|
|
205
137
|
const renderSeatPrices = () => {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { FeedbackConfig } from "./constants";
|
|
3
|
+
|
|
4
|
+
interface FeedbackBannerProps {
|
|
5
|
+
config: FeedbackConfig | null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const FeedbackBanner = ({ config }: FeedbackBannerProps) => {
|
|
9
|
+
if (!config) return null;
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
style={{
|
|
13
|
+
display: "flex",
|
|
14
|
+
alignItems: "center",
|
|
15
|
+
gap: 12,
|
|
16
|
+
borderRadius: 20,
|
|
17
|
+
padding: "12px 16px",
|
|
18
|
+
marginTop: 16,
|
|
19
|
+
backgroundColor: config.bannerBg,
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
<span style={{ fontSize: 22 }}>{config.emoji}</span>
|
|
23
|
+
<span
|
|
24
|
+
style={{
|
|
25
|
+
fontWeight: 600,
|
|
26
|
+
fontSize: "13.33px",
|
|
27
|
+
color: config.bannerText,
|
|
28
|
+
}}
|
|
29
|
+
>
|
|
30
|
+
{config.message}
|
|
31
|
+
</span>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default FeedbackBanner;
|