medos-sdk 1.1.9 → 1.1.11

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.
Files changed (118) hide show
  1. package/dist/client/MedosClient.d.ts +1 -0
  2. package/dist/client/MedosClient.js +7 -0
  3. package/dist/components/AppointmentCalender.js +22 -8
  4. package/dist/components/AppointmentConfirmationStep.d.ts +4 -0
  5. package/dist/components/AppointmentConfirmationStep.js +50 -52
  6. package/dist/components/AppointmentDateTimeModal.d.ts +4 -0
  7. package/dist/components/AppointmentDateTimeModal.js +216 -165
  8. package/dist/components/AppointmentSummaryStep.d.ts +12 -0
  9. package/dist/components/AppointmentSummaryStep.js +168 -0
  10. package/dist/components/BookingOptionStep.d.ts +14 -0
  11. package/dist/components/BookingOptionStep.js +346 -0
  12. package/dist/components/ContactInformationStep.js +13 -6
  13. package/dist/components/ContactPreferenceStep.js +16 -6
  14. package/dist/components/DoctorSelectModal.d.ts +5 -0
  15. package/dist/components/DoctorSelectModal.js +169 -74
  16. package/dist/components/EnquiryForm.js +84 -72
  17. package/dist/components/Icons/CloseIcon.d.ts +9 -0
  18. package/dist/components/Icons/CloseIcon.js +5 -0
  19. package/dist/components/InquiryDetailsStep.js +11 -6
  20. package/dist/components/PatientDetailsStep.js +17 -12
  21. package/dist/components/PatientSelectionStep.d.ts +12 -0
  22. package/dist/components/PatientSelectionStep.js +254 -0
  23. package/dist/components/PhoneVerificationStep.js +1 -1
  24. package/dist/components/SuccessStep.js +1 -1
  25. package/dist/components/appointment-booking/AppointmentCalender.d.ts +5 -0
  26. package/dist/components/appointment-booking/AppointmentCalender.js +247 -0
  27. package/dist/components/appointment-booking/hooks/index.d.ts +3 -0
  28. package/dist/components/appointment-booking/hooks/index.js +3 -0
  29. package/dist/components/appointment-booking/hooks/useAppointmentFlow.d.ts +8 -0
  30. package/dist/components/appointment-booking/hooks/useAppointmentFlow.js +318 -0
  31. package/dist/components/appointment-booking/hooks/useAppointmentState.d.ts +9 -0
  32. package/dist/components/appointment-booking/hooks/useAppointmentState.js +125 -0
  33. package/dist/components/appointment-booking/hooks/useInitializeAddresses.d.ts +1 -0
  34. package/dist/components/appointment-booking/hooks/useInitializeAddresses.js +55 -0
  35. package/dist/components/appointment-booking/index.d.ts +5 -0
  36. package/dist/components/appointment-booking/index.js +3 -0
  37. package/dist/components/appointment-booking/types.d.ts +291 -0
  38. package/dist/components/appointment-booking/types.js +49 -0
  39. package/dist/components/appointment-modal-styles.d.ts +259 -0
  40. package/dist/components/appointment-modal-styles.js +395 -0
  41. package/dist/components/constant.d.ts +2 -0
  42. package/dist/components/constant.js +15 -0
  43. package/dist/components/custom-calendar.js +20 -11
  44. package/dist/components/styles.js +93 -52
  45. package/dist/components/theme-styles.d.ts +5 -4
  46. package/dist/components/theme-styles.js +221 -125
  47. package/dist/components/types.d.ts +12 -139
  48. package/dist/components/types.js +15 -32
  49. package/dist/components/uiComponents/SelectDropdown.d.ts +1 -1
  50. package/dist/components/uiComponents/SelectDropdown.js +24 -24
  51. package/dist/components/utils.d.ts +3 -0
  52. package/dist/components/utils.js +59 -0
  53. package/dist/components/validation.d.ts +2 -0
  54. package/dist/components/validation.js +41 -0
  55. package/dist/core/theme/index.d.ts +1 -0
  56. package/dist/core/theme/index.js +1 -0
  57. package/dist/core/theme/responsive.d.ts +15 -0
  58. package/dist/core/theme/responsive.js +113 -0
  59. package/dist/core/theme/themes.js +16 -4
  60. package/dist/core/theme/types.d.ts +8 -0
  61. package/dist/enquiry-form/validation.js +1 -1
  62. package/dist/index.d.ts +3 -1
  63. package/dist/index.js +2 -1
  64. package/dist/react/ThemeProvider.d.ts +2 -1
  65. package/dist/react/ThemeProvider.js +49 -10
  66. package/dist/react/index.d.ts +2 -1
  67. package/dist/react/index.js +1 -1
  68. package/dist/services/AppointmentService.d.ts +80 -2
  69. package/dist/services/AppointmentService.js +114 -5
  70. package/dist/services/WorkspaceService.d.ts +58 -3
  71. package/dist/services/WorkspaceService.js +10 -1
  72. package/dist/vanilla/AppointmentCalendarWidget.d.ts +9 -7
  73. package/dist/vanilla/AppointmentCalendarWidget.js +834 -384
  74. package/dist/vanilla/EnquiryFormWidget.d.ts +1 -0
  75. package/dist/vanilla/EnquiryFormWidget.js +25 -43
  76. package/dist/vanilla/client/MedosClient.d.ts +1 -0
  77. package/dist/vanilla/components/AppointmentConfirmationStep.d.ts +4 -0
  78. package/dist/vanilla/components/AppointmentDateTimeModal.d.ts +4 -0
  79. package/dist/vanilla/components/AppointmentSummaryStep.d.ts +12 -0
  80. package/dist/vanilla/components/BookingOptionStep.d.ts +14 -0
  81. package/dist/vanilla/components/DoctorSelectModal.d.ts +5 -0
  82. package/dist/vanilla/components/Icons/CloseIcon.d.ts +9 -0
  83. package/dist/vanilla/components/PatientSelectionStep.d.ts +12 -0
  84. package/dist/vanilla/components/VanillaCalendar.js +33 -18
  85. package/dist/vanilla/components/VanillaIcons.d.ts +5 -0
  86. package/dist/vanilla/components/VanillaIcons.js +92 -0
  87. package/dist/vanilla/components/VanillaSelect.d.ts +3 -0
  88. package/dist/vanilla/components/VanillaSelect.js +93 -5
  89. package/dist/vanilla/components/appointment-booking/AppointmentCalender.d.ts +5 -0
  90. package/dist/vanilla/components/appointment-booking/hooks/index.d.ts +3 -0
  91. package/dist/vanilla/components/appointment-booking/hooks/useAppointmentFlow.d.ts +8 -0
  92. package/dist/vanilla/components/appointment-booking/hooks/useAppointmentState.d.ts +9 -0
  93. package/dist/vanilla/components/appointment-booking/hooks/useInitializeAddresses.d.ts +1 -0
  94. package/dist/vanilla/components/appointment-booking/index.d.ts +5 -0
  95. package/dist/vanilla/components/appointment-booking/types.d.ts +291 -0
  96. package/dist/vanilla/components/appointment-modal-styles.d.ts +259 -0
  97. package/dist/vanilla/components/constant.d.ts +2 -0
  98. package/dist/vanilla/components/theme-styles.d.ts +5 -4
  99. package/dist/vanilla/components/types.d.ts +12 -139
  100. package/dist/vanilla/components/uiComponents/SelectDropdown.d.ts +1 -1
  101. package/dist/vanilla/components/utils.d.ts +3 -0
  102. package/dist/vanilla/components/validation.d.ts +2 -0
  103. package/dist/vanilla/core/theme/index.d.ts +1 -0
  104. package/dist/vanilla/core/theme/responsive.d.ts +15 -0
  105. package/dist/vanilla/core/theme/types.d.ts +8 -0
  106. package/dist/vanilla/enquiry-widget.js +374 -53
  107. package/dist/vanilla/index.d.ts +3 -1
  108. package/dist/vanilla/react/ThemeProvider.d.ts +2 -1
  109. package/dist/vanilla/react/index.d.ts +2 -1
  110. package/dist/vanilla/services/AppointmentService.d.ts +80 -2
  111. package/dist/vanilla/services/WorkspaceService.d.ts +58 -3
  112. package/dist/vanilla/vanilla/AppointmentCalendarWidget.d.ts +9 -7
  113. package/dist/vanilla/vanilla/EnquiryFormWidget.d.ts +1 -0
  114. package/dist/vanilla/vanilla/components/VanillaIcons.d.ts +5 -0
  115. package/dist/vanilla/vanilla/components/VanillaSelect.d.ts +3 -0
  116. package/dist/vanilla/widget.css +833 -207
  117. package/dist/vanilla/widget.js +6463 -5687
  118. package/package.json +1 -1
@@ -4,21 +4,17 @@ import { CustomCalendarWithDateSelector } from "./custom-calendar";
4
4
  import { PaymentMethodIcon } from "./Icons/PaymentMethodIcon";
5
5
  import { DateTimeIcon } from "./Icons/Date&TimeIcon";
6
6
  import { ConsultationTypeIcon } from "./Icons/ConsultationType";
7
+ import ChevronLeftIcon from "./Icons/ChevronLeft";
8
+ import ChevronRightIcon from "./Icons/ChevronRight";
7
9
  import { useTheme } from "../react/hooks/useTheme";
8
- const MONTHS = [
9
- "January",
10
- "February",
11
- "March",
12
- "April",
13
- "May",
14
- "June",
15
- "July",
16
- "August",
17
- "September",
18
- "October",
19
- "November",
20
- "December",
21
- ];
10
+ import { useBreakpoint } from "../core/theme/responsive";
11
+ import { MONTHS, WEEKDAYS } from "./constant";
12
+ import { getMobileStyles, getDesktopStyles } from "./appointment-modal-styles";
13
+ const formatDateCard = (date) => ({
14
+ month: MONTHS[date.getMonth()],
15
+ day: date.getDate(),
16
+ weekday: WEEKDAYS[date.getDay()],
17
+ });
22
18
  const getOrdinalSuffix = (day) => {
23
19
  if (day > 3 && day < 21)
24
20
  return "th";
@@ -33,24 +29,171 @@ const getOrdinalSuffix = (day) => {
33
29
  return "th";
34
30
  }
35
31
  };
36
- const formatDate = (date) => {
32
+ const formatDesktopDate = (date) => {
37
33
  const day = date.getDate();
38
34
  const month = MONTHS[date.getMonth()];
39
35
  const year = date.getFullYear();
40
36
  return `${day}${getOrdinalSuffix(day)} ${month} ${year}`;
41
37
  };
42
- export const AppointmentDateTimeModal = ({ onlineFee, offlineFee, slots, onCancel, onContinue, onDateChange, }) => {
38
+ export const AppointmentDateTimeModal = (props) => {
43
39
  const theme = useTheme();
44
- const [mode, setMode] = useState("OFFLINE");
40
+ const breakpoint = useBreakpoint(theme);
41
+ const isMobile = breakpoint === "mobile";
42
+ if (isMobile) {
43
+ return _jsx(MobileAppointmentModal, { ...props });
44
+ }
45
+ return _jsx(DesktopAppointmentModal, { ...props });
46
+ };
47
+ const MobileAppointmentModal = ({ onlineFee, offlineFee, slots, selectedDate: initialSelectedDate, selectedSlot: initialSelectedSlot, consultationMode: initialConsultationMode, onCancel, onContinue, onDateChange, initialStep = "consultation", }) => {
48
+ const theme = useTheme();
49
+ const breakpoint = useBreakpoint(theme);
50
+ const [currentStep, setCurrentStep] = useState(initialStep);
51
+ const [mode, setMode] = useState(initialConsultationMode || "OFFLINE");
45
52
  const [consultationCharge, setConsultationCharge] = useState("");
46
53
  const [paymentMode, setPaymentMode] = useState("CASH");
47
- const [selectedDate, setSelectedDate] = useState(new Date());
48
- const [selectedSlot, setSelectedSlot] = useState(null);
54
+ const [selectedDate, setSelectedDate] = useState(initialSelectedDate || new Date());
55
+ const [selectedSlot, setSelectedSlot] = useState(initialSelectedSlot || null);
56
+ const [currentDateIndex, setCurrentDateIndex] = useState(0);
57
+ useEffect(() => {
58
+ if (!initialConsultationMode && initialStep === "consultation") {
59
+ setCurrentStep("consultation");
60
+ }
61
+ }, [initialConsultationMode, initialStep]);
62
+ const availableDates = useMemo(() => {
63
+ const dates = [];
64
+ const today = new Date();
65
+ for (let i = 0; i < 30; i++) {
66
+ const date = new Date(today);
67
+ date.setDate(today.getDate() + i);
68
+ dates.push(date);
69
+ }
70
+ return dates;
71
+ }, []);
72
+ const visibleCount = 4;
73
+ const visibleDates = useMemo(() => {
74
+ return availableDates.slice(currentDateIndex, currentDateIndex + visibleCount);
75
+ }, [availableDates, currentDateIndex, visibleCount]);
76
+ const canScrollLeft = currentDateIndex > 0;
77
+ const canScrollRight = currentDateIndex + visibleCount < availableDates.length;
78
+ const scrollLeft = () => {
79
+ if (canScrollLeft)
80
+ setCurrentDateIndex(Math.max(0, currentDateIndex - 1));
81
+ };
82
+ const scrollRight = () => {
83
+ if (canScrollRight) {
84
+ const maxIndex = availableDates.length - visibleCount;
85
+ setCurrentDateIndex(Math.min(maxIndex, currentDateIndex + 1));
86
+ }
87
+ };
88
+ useEffect(() => {
89
+ const charge = mode === "ONLINE" && onlineFee
90
+ ? String(onlineFee)
91
+ : mode === "OFFLINE" && offlineFee
92
+ ? String(offlineFee)
93
+ : "";
94
+ setConsultationCharge(charge);
95
+ }, [mode, onlineFee, offlineFee]);
96
+ const handleContinueFromConsultation = () => setCurrentStep("datetime");
97
+ const handleBackFromDateTime = () => setCurrentStep("consultation");
49
98
  const handleDateSelect = useCallback((date) => {
99
+ const dateChanged = date.toDateString() !== selectedDate.toDateString();
50
100
  setSelectedDate(date);
51
- setSelectedSlot(null);
101
+ if (dateChanged)
102
+ setSelectedSlot(null);
52
103
  onDateChange?.(date);
53
- }, [onDateChange]);
104
+ }, [selectedDate, onDateChange]);
105
+ const handleFinalContinue = useCallback(() => {
106
+ if (!selectedSlot)
107
+ return;
108
+ onContinue(mode, selectedDate, selectedSlot, consultationCharge, paymentMode);
109
+ }, [
110
+ mode,
111
+ selectedDate,
112
+ selectedSlot,
113
+ consultationCharge,
114
+ paymentMode,
115
+ onContinue,
116
+ ]);
117
+ const isSlotSelected = useCallback((slot) => selectedSlot?.start === slot.start && selectedSlot?.end === slot.end, [selectedSlot]);
118
+ const STYLES = getMobileStyles(theme, breakpoint);
119
+ if (currentStep === "consultation") {
120
+ return (_jsxs("div", { style: STYLES.container, children: [_jsx("div", { style: STYLES.header, children: _jsx("h2", { style: STYLES.title, children: "New Appointment" }) }), _jsxs("div", { style: STYLES.section, children: [_jsxs("h3", { style: STYLES.sectionTitle, children: ["Consultation Mode ", _jsx("span", { style: STYLES.required, children: "*" })] }), _jsx("div", { style: STYLES.modeContainer, children: ["ONLINE", "OFFLINE"].map((item) => {
121
+ const disabled = (item === "ONLINE" && (!onlineFee || onlineFee <= 0)) ||
122
+ (item === "OFFLINE" && (!offlineFee || offlineFee <= 0));
123
+ return (_jsxs("label", { style: STYLES.modeOption, children: [_jsx("input", { type: "radio", name: "mode", value: item, checked: mode === item, disabled: disabled, onChange: () => !disabled && setMode(item), style: STYLES.radioInput }), item === "ONLINE"
124
+ ? "Online Consultation"
125
+ : "Offline Consultation"] }, item));
126
+ }) })] }), _jsxs("div", { style: STYLES.section, children: [_jsx("h3", { style: STYLES.sectionTitle, children: "Consultation Charges" }), _jsxs("div", { style: STYLES.chargeAmount, children: ["\u20B9", consultationCharge] })] }), _jsxs("div", { style: STYLES.section, children: [_jsxs("h3", { style: STYLES.sectionTitle, children: ["Preferred Mode of Payment ", _jsx("span", { style: STYLES.required, children: "*" })] }), _jsx("div", { style: STYLES.paymentOption, children: mode === "ONLINE" ? "Online Consultation" : "Offline Consultation" })] }), _jsxs("div", { style: STYLES.footer, children: [_jsx("button", { style: STYLES.backButton, onClick: onCancel, children: "Back" }), _jsx("button", { style: STYLES.nextButton, onClick: handleContinueFromConsultation, disabled: !consultationCharge, children: "Next" })] })] }));
127
+ }
128
+ return (_jsxs("div", { style: STYLES.container, children: [_jsx("div", { style: STYLES.header, children: _jsx("h2", { style: STYLES.title, children: "Date & Time" }) }), _jsxs("div", { style: STYLES.dateSection, children: [_jsxs("div", { style: STYLES.monthNavigationContainer, children: [_jsx("button", { onClick: scrollLeft, style: {
129
+ ...STYLES.navButton,
130
+ opacity: canScrollLeft ? 1 : 0.3,
131
+ cursor: canScrollLeft ? "pointer" : "not-allowed",
132
+ }, children: _jsx(ChevronLeftIcon, {}) }), _jsx("div", { style: STYLES.monthYearDisplay, children: selectedDate.toLocaleDateString("en-US", {
133
+ month: "long",
134
+ year: "numeric",
135
+ }) }), _jsx("button", { onClick: scrollRight, style: {
136
+ ...STYLES.navButton,
137
+ opacity: canScrollRight ? 1 : 0.3,
138
+ cursor: canScrollRight ? "pointer" : "not-allowed",
139
+ }, children: _jsx(ChevronRightIcon, {}) })] }), _jsx("div", { style: STYLES.dateCardsContainer, children: visibleDates.map((date, index) => {
140
+ const { month, day, weekday } = formatDateCard(date);
141
+ const isSelected = date.toDateString() === selectedDate.toDateString();
142
+ return (_jsxs("div", { style: {
143
+ ...STYLES.dateCard,
144
+ ...(isSelected ? STYLES.dateCardSelected : {}),
145
+ }, onClick: () => handleDateSelect(date), children: [_jsx("div", { style: STYLES.dateCardWeekday, children: weekday }), _jsx("div", { style: STYLES.dateCardDay, children: day })] }, `${date.getTime()}-${index}`));
146
+ }) })] }), _jsx("div", { style: STYLES.slotsContainer, children: slots.length === 0 ? (_jsx("p", { style: STYLES.noSlots, children: "No available slots" })) : (_jsx("div", { style: STYLES.slotGrid, children: slots.map((slot) => {
147
+ const startTime = new Date(slot.start).toLocaleTimeString([], {
148
+ hour: "2-digit",
149
+ minute: "2-digit",
150
+ });
151
+ const endTime = new Date(slot.end).toLocaleTimeString([], {
152
+ hour: "2-digit",
153
+ minute: "2-digit",
154
+ });
155
+ const selected = isSlotSelected(slot);
156
+ return (_jsxs("button", { onClick: () => setSelectedSlot(slot), onTouchStart: (e) => {
157
+ if (!selected) {
158
+ e.currentTarget.style.transform = "scale(0.98)";
159
+ }
160
+ }, onTouchEnd: (e) => {
161
+ if (!selected) {
162
+ e.currentTarget.style.transform = "scale(1)";
163
+ }
164
+ }, style: {
165
+ ...STYLES.slotButton,
166
+ ...(selected ? STYLES.slotButtonSelected : {}),
167
+ }, children: [startTime, " - ", endTime] }, slot.id));
168
+ }) })) }), _jsxs("div", { style: STYLES.footer, children: [_jsx("button", { style: STYLES.backButton, onClick: handleBackFromDateTime, children: "Back" }), _jsx("button", { style: STYLES.nextButton, onClick: handleFinalContinue, disabled: !selectedSlot, children: "Next" })] })] }));
169
+ };
170
+ const DesktopAppointmentModal = ({ onlineFee, offlineFee, slots, selectedDate: initialSelectedDate, selectedSlot: initialSelectedSlot, consultationMode: initialConsultationMode, onCancel, onContinue, onDateChange, }) => {
171
+ const theme = useTheme();
172
+ const breakpoint = useBreakpoint(theme);
173
+ const [mode, setMode] = useState(initialConsultationMode || "OFFLINE");
174
+ const [consultationCharge, setConsultationCharge] = useState("");
175
+ const [paymentMode, setPaymentMode] = useState("CASH");
176
+ const [selectedDate, setSelectedDate] = useState(initialSelectedDate || new Date());
177
+ const [selectedSlot, setSelectedSlot] = useState(initialSelectedSlot || null);
178
+ useEffect(() => {
179
+ if (initialSelectedDate)
180
+ setSelectedDate(initialSelectedDate);
181
+ }, [initialSelectedDate]);
182
+ useEffect(() => {
183
+ if (initialSelectedSlot)
184
+ setSelectedSlot(initialSelectedSlot);
185
+ }, [initialSelectedSlot]);
186
+ useEffect(() => {
187
+ if (initialConsultationMode)
188
+ setMode(initialConsultationMode);
189
+ }, [initialConsultationMode]);
190
+ const handleDateSelect = useCallback((date) => {
191
+ const dateChanged = date.toDateString() !== selectedDate.toDateString();
192
+ setSelectedDate(date);
193
+ if (dateChanged)
194
+ setSelectedSlot(null);
195
+ onDateChange?.(date);
196
+ }, [selectedDate, onDateChange]);
54
197
  useEffect(() => {
55
198
  const charge = mode === "ONLINE" && onlineFee
56
199
  ? String(onlineFee)
@@ -72,149 +215,57 @@ export const AppointmentDateTimeModal = ({ onlineFee, offlineFee, slots, onCance
72
215
  onContinue,
73
216
  ]);
74
217
  const isSlotSelected = useCallback((slot) => selectedSlot?.start === slot.start && selectedSlot?.end === slot.end, [selectedSlot]);
75
- const formattedDate = useMemo(() => formatDate(selectedDate), [selectedDate]);
218
+ const formattedDate = useMemo(() => formatDesktopDate(selectedDate), [selectedDate]);
76
219
  const isFormValid = selectedDate && selectedSlot && consultationCharge;
77
- const STYLES = getStyles(theme);
78
- return (_jsxs("div", { style: STYLES.modalWrapper, children: [_jsx(ConsultationTypeSection, { mode: mode, onlineFee: onlineFee, offlineFee: offlineFee, onModeChange: setMode, STYLES: STYLES }), _jsx(ChargesSection, { charge: consultationCharge, STYLES: STYLES }), _jsx(DateTimeSection, { selectedDate: selectedDate, formattedDate: formattedDate, slots: slots, selectedSlot: selectedSlot, onDateSelect: handleDateSelect, onSlotSelect: setSelectedSlot, isSlotSelected: isSlotSelected, STYLES: STYLES }), _jsx(FooterSection, { onCancel: onCancel, onContinue: handleContinue, isValid: !!isFormValid, STYLES: STYLES })] }));
79
- };
80
- const ConsultationTypeSection = ({ mode, onlineFee, offlineFee, onModeChange, STYLES }) => (_jsxs("div", { style: STYLES.sectionCard, children: [_jsxs("div", { style: STYLES.sectionHeader, children: [_jsx(ConsultationTypeIcon, {}), _jsx("span", { style: STYLES.sectionTitle, children: "Consultation Type" })] }), _jsxs("div", { style: STYLES.sectionBody, children: [_jsx("p", { style: STYLES.label, children: "Consultation Mode" }), _jsx("div", { style: STYLES.modeContainer, children: ["ONLINE", "OFFLINE"].map((item) => {
81
- const disabled = (item === "ONLINE" && (!onlineFee || onlineFee <= 0)) ||
82
- (item === "OFFLINE" && (!offlineFee || offlineFee <= 0));
83
- return (_jsxs("label", { style: { ...STYLES.radioLabel, opacity: disabled ? 0.5 : 1 }, children: [_jsx("input", { type: "radio", name: "mode", value: item, checked: mode === item, disabled: disabled, onChange: () => !disabled && onModeChange(item), style: STYLES.radioInput }), item === "ONLINE" ? "Online" : "Offline"] }, item));
84
- }) })] })] }));
85
- const ChargesSection = ({ charge, STYLES }) => (_jsxs("div", { style: STYLES.sectionCard, children: [_jsxs("div", { style: STYLES.sectionHeader, children: [_jsx(PaymentMethodIcon, {}), _jsx("span", { style: STYLES.sectionTitle, children: "Charges & Mode of Payment" })] }), _jsx("div", { style: STYLES.sectionBody, children: _jsxs("span", { style: STYLES.rupee, children: ["\u20B9 ", charge] }) })] }));
86
- const DateTimeSection = ({ selectedDate, formattedDate, slots, selectedSlot, onDateSelect, onSlotSelect, isSlotSelected, STYLES, }) => (_jsxs("div", { style: STYLES.sectionCard, children: [_jsxs("div", { style: STYLES.sectionHeader, children: [_jsx(DateTimeIcon, {}), _jsx("span", { style: STYLES.sectionTitle, children: "Date & Time" })] }), _jsx("div", { style: STYLES.sectionBody, children: _jsxs("div", { style: STYLES.dateTimeContainer, children: [_jsx("div", { style: STYLES.calendarBox, children: _jsx(CustomCalendarWithDateSelector, { selectedDate: selectedDate, onSelect: onDateSelect, pastDisabled: true }) }), _jsxs("div", { style: STYLES.timesContainer, children: [_jsxs("p", { style: STYLES.timesLabel, children: ["Available times ", _jsx("br", {}), _jsx("span", { style: STYLES.dateLabel, children: formattedDate })] }), slots.length === 0 ? (_jsx("p", { style: STYLES.noSlots, children: "No available slots" })) : (_jsx("div", { style: STYLES.slotGrid, children: slots.map((slot) => (_jsx(SlotButton, { slot: slot, isSelected: isSlotSelected(slot), onSelect: onSlotSelect }, slot.id))) }))] })] }) })] }));
87
- const SlotButton = ({ slot, isSelected, onSelect }) => {
88
- const startTime = useMemo(() => new Date(slot.start).toLocaleTimeString([], {
89
- hour: "2-digit",
90
- minute: "2-digit",
91
- }), [slot.start]);
92
- const theme = useTheme();
93
- const STYLES = getStyles(theme);
94
- return (_jsx("button", { onClick: () => onSelect(slot), style: {
95
- ...STYLES.slotButton,
96
- background: isSelected
97
- ? theme.colors.secondary
98
- : theme.colors.background,
99
- color: isSelected
100
- ? theme.colors.textOnSecondary
101
- : theme.colors.secondary,
102
- borderColor: theme.colors.secondary,
103
- }, children: startTime }));
220
+ const STYLES = getDesktopStyles(theme, breakpoint);
221
+ return (_jsxs("div", { style: STYLES.modalWrapper, children: [_jsxs("div", { style: STYLES.sectionCard, children: [_jsxs("div", { style: STYLES.sectionHeader, children: [_jsx(ConsultationTypeIcon, {}), _jsx("span", { style: STYLES.sectionTitle, children: "Consultation Type" })] }), _jsxs("div", { style: STYLES.sectionBody, children: [_jsx("p", { style: STYLES.label, children: "Consultation Mode" }), _jsx("div", { style: STYLES.modeContainer, children: ["ONLINE", "OFFLINE"].map((item) => {
222
+ const disabled = (item === "ONLINE" && (!onlineFee || onlineFee <= 0)) ||
223
+ (item === "OFFLINE" && (!offlineFee || offlineFee <= 0));
224
+ return (_jsxs("label", { style: { ...STYLES.radioLabel, opacity: disabled ? 0.5 : 1 }, children: [_jsx("input", { type: "radio", name: "mode", value: item, checked: mode === item, disabled: disabled, onChange: () => !disabled && setMode(item), style: STYLES.radioInput }), item === "ONLINE" ? "Online" : "Offline"] }, item));
225
+ }) })] })] }), _jsxs("div", { style: STYLES.sectionCard, children: [_jsxs("div", { style: STYLES.sectionHeader, children: [_jsx(PaymentMethodIcon, {}), _jsx("span", { style: STYLES.sectionTitle, children: "Charges & Mode of Payment" })] }), _jsx("div", { style: STYLES.sectionBody, children: _jsxs("span", { style: STYLES.rupee, children: ["\u20B9 ", consultationCharge] }) })] }), _jsxs("div", { style: STYLES.sectionCard, children: [_jsxs("div", { style: STYLES.sectionHeader, children: [_jsx(DateTimeIcon, {}), _jsx("span", { style: STYLES.sectionTitle, children: "Date & Time" })] }), _jsx("div", { style: STYLES.sectionBody, children: _jsxs("div", { style: STYLES.dateTimeContainer, children: [_jsx("div", { style: STYLES.calendarBox, children: _jsx(CustomCalendarWithDateSelector, { selectedDate: selectedDate, onSelect: handleDateSelect, pastDisabled: true }) }), _jsxs("div", { style: STYLES.timesContainer, children: [_jsxs("p", { style: STYLES.timesLabel, children: ["Available times ", _jsx("br", {}), _jsx("span", { style: STYLES.dateLabel, children: formattedDate })] }), slots.length === 0 ? (_jsx("p", { style: STYLES.noSlots, children: "No available slots" })) : (_jsx("div", { style: STYLES.slotGrid, children: slots.map((slot) => {
226
+ const selected = isSlotSelected(slot);
227
+ return (_jsxs("button", { onClick: () => setSelectedSlot(slot), onMouseEnter: (e) => {
228
+ if (!selected) {
229
+ e.currentTarget.style.borderColor =
230
+ theme.colors.secondary;
231
+ e.currentTarget.style.backgroundColor = `${theme.colors.secondary}08`;
232
+ e.currentTarget.style.transform =
233
+ "translateY(-2px)";
234
+ e.currentTarget.style.boxShadow = `0 4px 12px ${theme.colors.secondary}15`;
235
+ }
236
+ }, onMouseLeave: (e) => {
237
+ if (!selected) {
238
+ e.currentTarget.style.borderColor =
239
+ theme.colors.border;
240
+ e.currentTarget.style.backgroundColor =
241
+ theme.colors.surface;
242
+ e.currentTarget.style.transform = "translateY(0)";
243
+ e.currentTarget.style.boxShadow =
244
+ "0 1px 3px rgba(0, 0, 0, 0.04)";
245
+ }
246
+ }, style: {
247
+ ...STYLES.slotButton,
248
+ background: selected
249
+ ? theme.colors.secondary
250
+ : theme.colors.surface,
251
+ color: selected
252
+ ? theme.colors.textOnSecondary
253
+ : theme.colors.text,
254
+ borderColor: selected
255
+ ? theme.colors.secondary
256
+ : theme.colors.border,
257
+ boxShadow: selected
258
+ ? `0 4px 12px ${theme.colors.secondary}30, 0 2px 6px ${theme.colors.secondary}20`
259
+ : "0 1px 3px rgba(0, 0, 0, 0.04)",
260
+ transform: selected
261
+ ? "translateY(-1px)"
262
+ : "translateY(0)",
263
+ }, children: [new Date(slot.start).toLocaleTimeString([], {
264
+ hour: "2-digit",
265
+ minute: "2-digit",
266
+ }), " ", "-", " ", new Date(slot.end).toLocaleTimeString([], {
267
+ hour: "2-digit",
268
+ minute: "2-digit",
269
+ })] }, slot.id));
270
+ }) }))] })] }) })] }), _jsxs("div", { style: STYLES.footer, children: [_jsx("button", { style: STYLES.backBtn, onClick: onCancel, children: "Back" }), _jsx("button", { style: { ...STYLES.continueBtn, opacity: isFormValid ? 1 : 0.6 }, onClick: handleContinue, disabled: !isFormValid, children: "Continue" })] })] }));
104
271
  };
105
- const FooterSection = ({ onCancel, onContinue, isValid, STYLES }) => (_jsxs("div", { style: STYLES.footer, children: [_jsx("button", { style: STYLES.backBtn, onClick: onCancel, children: "Back" }), _jsx("button", { style: { ...STYLES.continueBtn, opacity: isValid ? 1 : 0.6 }, onClick: onContinue, disabled: !isValid, children: "Continue" })] }));
106
- const getStyles = (theme) => ({
107
- modalWrapper: {
108
- backgroundColor: theme.colors.background,
109
- borderRadius: theme.radii.lg,
110
- padding: "20px 24px 24px 24px",
111
- maxWidth: 800,
112
- margin: "0 auto",
113
- fontFamily: theme.typography.fontFamily,
114
- color: theme.colors.text,
115
- boxSizing: "border-box",
116
- },
117
- sectionCard: {
118
- border: `1px solid ${theme.colors.border}`,
119
- borderRadius: theme.radii.lg,
120
- marginBottom: 20,
121
- position: "relative",
122
- },
123
- sectionHeader: {
124
- background: theme.colors.primary,
125
- color: theme.colors.textOnPrimary,
126
- borderBottom: `1px solid ${theme.colors.border}`,
127
- padding: "12px 16px",
128
- display: "flex",
129
- alignItems: "center",
130
- gap: 8,
131
- },
132
- sectionTitle: { fontSize: 15, fontWeight: 600 },
133
- sectionBody: { padding: 16, position: "relative" },
134
- label: {
135
- fontSize: 14,
136
- fontWeight: 500,
137
- marginBottom: 4,
138
- display: "block",
139
- },
140
- modeContainer: {
141
- display: "flex",
142
- gap: 24,
143
- marginTop: 4,
144
- },
145
- radioLabel: {
146
- display: "flex",
147
- alignItems: "center",
148
- gap: 8,
149
- cursor: "pointer",
150
- fontSize: 14,
151
- },
152
- radioInput: {
153
- width: 18,
154
- height: 18,
155
- accentColor: theme.colors.secondary,
156
- cursor: "pointer",
157
- },
158
- rupee: {
159
- fontWeight: 600,
160
- fontSize: 16,
161
- marginRight: 6,
162
- },
163
- dateTimeContainer: { display: "flex", gap: 24 },
164
- calendarBox: {
165
- border: `1px solid ${theme.colors.border}`,
166
- borderRadius: theme.radii.lg,
167
- padding: 8,
168
- },
169
- timesContainer: { flexGrow: 1 },
170
- timesLabel: { fontWeight: 500 },
171
- dateLabel: {
172
- color: theme.colors.textSecondary,
173
- fontSize: 13,
174
- },
175
- noSlots: {
176
- color: theme.colors.textSecondary,
177
- fontSize: 14,
178
- marginTop: 8,
179
- },
180
- slotGrid: {
181
- display: "grid",
182
- gridTemplateColumns: "repeat(auto-fit,minmax(100px,1fr))",
183
- gap: 8,
184
- marginTop: 12,
185
- },
186
- slotButton: {
187
- borderRadius: theme.radii.md,
188
- padding: "8px 10px",
189
- border: `1px solid ${theme.colors.secondary}`,
190
- fontSize: 13,
191
- fontWeight: 500,
192
- cursor: "pointer",
193
- transition: "0.2s",
194
- },
195
- footer: {
196
- display: "flex",
197
- justifyContent: "flex-end",
198
- alignItems: "center",
199
- marginTop: 20,
200
- gap: 12,
201
- },
202
- backBtn: {
203
- border: `1px solid ${theme.colors.primary}`,
204
- background: theme.colors.background,
205
- color: theme.colors.primary,
206
- borderRadius: theme.radii.md,
207
- fontWeight: 600,
208
- padding: "8px 20px",
209
- cursor: "pointer",
210
- },
211
- continueBtn: {
212
- background: theme.colors.secondary,
213
- color: theme.colors.textOnSecondary,
214
- border: "none",
215
- borderRadius: theme.radii.md,
216
- fontWeight: 600,
217
- padding: "8px 20px",
218
- cursor: "pointer",
219
- },
220
- });
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import { AppointmentState } from "./appointment-booking/types";
3
+ import { Doctor, AddressItem } from "../services/AppointmentService";
4
+ export interface AppointmentSummaryStepProps {
5
+ state: AppointmentState;
6
+ selectedDoctor: Doctor | null;
7
+ selectedAddress: AddressItem | null;
8
+ onBack: () => void;
9
+ onConfirm: () => void;
10
+ }
11
+ export declare const AppointmentSummaryStep: React.FC<AppointmentSummaryStepProps>;
12
+ export default AppointmentSummaryStep;
@@ -0,0 +1,168 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useTheme } from "../react/hooks/useTheme";
3
+ import { useBreakpoint } from "../core/theme/responsive";
4
+ import { formatDate, formatTime, calculateDuration } from "./utils";
5
+ export const AppointmentSummaryStep = ({ state, selectedDoctor, selectedAddress, onBack, onConfirm, }) => {
6
+ const theme = useTheme();
7
+ const breakpoint = useBreakpoint(theme);
8
+ const styles = getStyles(theme, breakpoint);
9
+ const usingSessionPack = state.bookingOptionType === "session-pack" && state.selectedSessionPack;
10
+ const usingNewPackage = state.bookingOptionType === "explore-packages" && state.selectedNewPackage;
11
+ const getDuration = () => {
12
+ if (state.selectedSlot) {
13
+ return calculateDuration(state.selectedSlot.start, state.selectedSlot.end);
14
+ }
15
+ return 60;
16
+ };
17
+ const getPaymentInfo = () => {
18
+ if (usingSessionPack) {
19
+ return {
20
+ type: "Session Pack",
21
+ name: state.selectedSessionPack?.name || "Session Pack",
22
+ amount: "Prepaid",
23
+ remaining: `${(state.selectedSessionPack?.remainingSessions || 1) - 1} sessions remaining after this appointment`,
24
+ };
25
+ }
26
+ else if (usingNewPackage) {
27
+ return {
28
+ type: "New Package",
29
+ name: state.selectedNewPackage?.name || "Package",
30
+ amount: `₹${state.selectedNewPackage?.price?.toLocaleString() || 0}`,
31
+ remaining: `${state.selectedNewPackage?.totalSessions ||
32
+ state.selectedNewPackage?.sessions ||
33
+ 1} sessions included`,
34
+ };
35
+ }
36
+ else {
37
+ return {
38
+ type: "Single Appointment",
39
+ name: state.consultationMode === "ONLINE"
40
+ ? "Online Consultation"
41
+ : "In-Person Visit",
42
+ amount: state.consultationCharge
43
+ ? `₹${state.consultationCharge}`
44
+ : "Pay at clinic",
45
+ remaining: null,
46
+ };
47
+ }
48
+ };
49
+ const paymentInfo = getPaymentInfo();
50
+ const patient = state.selectedPatient;
51
+ return (_jsxs("div", { style: styles.container, children: [_jsx("h3", { style: styles.title, children: "Appointment Summary" }), _jsx("p", { style: styles.subtitle, children: "Please review your appointment details before confirming" }), _jsxs("div", { style: styles.section, children: [_jsx("h4", { style: styles.sectionTitle, children: "\uD83D\uDC64 Patient Information" }), _jsxs("div", { style: styles.summaryCard, children: [_jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Name" }), _jsx("span", { style: styles.value, children: patient
52
+ ? `${patient.firstName} ${patient.lastName}`
53
+ : state.patientName || "New Patient" })] }), (patient?.email || state.patientEmail) && (_jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Email" }), _jsx("span", { style: styles.value, children: patient?.email || state.patientEmail })] })), _jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Phone" }), _jsxs("span", { style: styles.value, children: [state.countryCode, " ", state.patientPhone] })] }), (patient?.age || state.patientAge) && (_jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Age" }), _jsxs("span", { style: styles.value, children: [patient?.age || state.patientAge, " years"] })] })), (patient?.bloodGroup || state.bloodGroup) && (_jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Blood Group" }), _jsx("span", { style: styles.value, children: patient?.bloodGroup || state.bloodGroup })] }))] })] }), _jsxs("div", { style: styles.section, children: [_jsx("h4", { style: styles.sectionTitle, children: "\uD83D\uDCC5 Appointment Details" }), _jsxs("div", { style: styles.summaryCard, children: [_jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Date" }), _jsx("span", { style: styles.value, children: formatDate(state.selectedDate) })] }), _jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Time" }), _jsx("span", { style: styles.value, children: state.selectedSlot
54
+ ? `${formatTime(state.selectedSlot.start)} - ${formatTime(state.selectedSlot.end)}`
55
+ : "Not selected" })] }), _jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Duration" }), _jsxs("span", { style: styles.value, children: ["~", getDuration(), " minutes"] })] }), _jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Type" }), _jsx("span", { style: styles.value, children: state.consultationMode === "ONLINE"
56
+ ? "Online Consultation"
57
+ : "In-Person Visit" })] })] })] }), _jsxs("div", { style: styles.section, children: [_jsx("h4", { style: styles.sectionTitle, children: "\uD83C\uDFE5 Doctor & Location" }), _jsxs("div", { style: styles.summaryCard, children: [selectedDoctor && (_jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Doctor" }), _jsxs("span", { style: styles.value, children: [selectedDoctor.name, selectedDoctor.specialty && ` • ${selectedDoctor.specialty}`] })] })), selectedAddress && (_jsxs(_Fragment, { children: [_jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Location" }), _jsx("span", { style: styles.value, children: selectedAddress.completeAddress || "Unknown Location" })] }), selectedAddress.address && (_jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Address" }), _jsx("span", { style: styles.value, children: selectedAddress.address })] }))] }))] })] }), _jsxs("div", { style: styles.section, children: [_jsx("h4", { style: styles.sectionTitle, children: "\uD83D\uDCB3 Payment" }), _jsxs("div", { style: styles.summaryCard, children: [_jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: "Booking Type" }), _jsx("span", { style: styles.value, children: paymentInfo.type })] }), _jsxs("div", { style: styles.summaryRow, children: [_jsx("span", { style: styles.label, children: usingSessionPack ? "Pack" : "Service" }), _jsx("span", { style: styles.value, children: paymentInfo.name })] }), _jsxs("div", { style: { ...styles.summaryRow, ...styles.totalRow }, children: [_jsx("span", { style: styles.totalLabel, children: "Amount" }), _jsx("span", { style: styles.totalValue, children: paymentInfo.amount })] }), paymentInfo.remaining && (_jsx("div", { style: styles.remainingInfo, children: paymentInfo.remaining }))] })] }), _jsxs("div", { style: styles.actions, children: [_jsx("button", { style: styles.backBtn, onClick: onBack, children: "Back" }), _jsx("button", { style: styles.confirmBtn, onClick: onConfirm, disabled: state.loading, children: state.loading ? "Confirming..." : "Confirm Appointment" })] })] }));
58
+ };
59
+ const getStyles = (theme, breakpoint) => {
60
+ const isMobile = breakpoint === "mobile";
61
+ return {
62
+ container: {
63
+ display: "flex",
64
+ flexDirection: "column",
65
+ gap: 20,
66
+ },
67
+ title: {
68
+ margin: 0,
69
+ fontSize: isMobile ? 18 : 20,
70
+ fontWeight: 600,
71
+ color: theme.colors.text,
72
+ },
73
+ subtitle: {
74
+ margin: 0,
75
+ fontSize: 14,
76
+ color: theme.colors.textSecondary,
77
+ },
78
+ section: {
79
+ display: "flex",
80
+ flexDirection: "column",
81
+ gap: 12,
82
+ },
83
+ sectionTitle: {
84
+ margin: 0,
85
+ fontSize: 16,
86
+ fontWeight: 600,
87
+ color: theme.colors.text,
88
+ display: "flex",
89
+ alignItems: "center",
90
+ gap: 8,
91
+ },
92
+ summaryCard: {
93
+ display: "flex",
94
+ flexDirection: "column",
95
+ gap: 12,
96
+ padding: 16,
97
+ backgroundColor: theme.colors.backgroundSecondary || "#f9fafb",
98
+ borderRadius: theme.radii.md,
99
+ border: `1px solid ${theme.colors.border}`,
100
+ },
101
+ summaryRow: {
102
+ display: "flex",
103
+ justifyContent: "space-between",
104
+ alignItems: "center",
105
+ },
106
+ label: {
107
+ fontSize: 14,
108
+ color: theme.colors.textSecondary,
109
+ },
110
+ value: {
111
+ fontSize: 14,
112
+ fontWeight: 500,
113
+ color: theme.colors.text,
114
+ textAlign: "right",
115
+ },
116
+ totalRow: {
117
+ paddingTop: 12,
118
+ borderTop: `1px solid ${theme.colors.border}`,
119
+ marginTop: 4,
120
+ },
121
+ totalLabel: {
122
+ fontSize: 16,
123
+ fontWeight: 600,
124
+ color: theme.colors.text,
125
+ },
126
+ totalValue: {
127
+ fontSize: 18,
128
+ fontWeight: 600,
129
+ color: theme.colors.primary,
130
+ },
131
+ remainingInfo: {
132
+ padding: "8px 12px",
133
+ backgroundColor: "#fef3c7",
134
+ color: "#92400e",
135
+ borderRadius: theme.radii.sm,
136
+ fontSize: 13,
137
+ textAlign: "center",
138
+ },
139
+ actions: {
140
+ display: "flex",
141
+ gap: 12,
142
+ marginTop: 24,
143
+ },
144
+ backBtn: {
145
+ flex: 1,
146
+ padding: "14px 24px",
147
+ border: `1px solid ${theme.colors.border}`,
148
+ borderRadius: theme.radii.md,
149
+ backgroundColor: "transparent",
150
+ color: theme.colors.text,
151
+ cursor: "pointer",
152
+ fontSize: 14,
153
+ fontWeight: 500,
154
+ },
155
+ confirmBtn: {
156
+ flex: 1,
157
+ padding: "14px 24px",
158
+ border: "none",
159
+ borderRadius: theme.radii.md,
160
+ backgroundColor: theme.colors.primary,
161
+ color: "#ffffff",
162
+ cursor: "pointer",
163
+ fontSize: 14,
164
+ fontWeight: 600,
165
+ },
166
+ };
167
+ };
168
+ export default AppointmentSummaryStep;
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ import { SessionPack, AvailablePackage } from "./appointment-booking/types";
3
+ export interface BookingOptionStepProps {
4
+ userSessionPacks: SessionPack[];
5
+ availablePackages: AvailablePackage[];
6
+ showPackageExplorer: boolean;
7
+ selectedSessionPack: SessionPack | null;
8
+ selectedNewPackage: AvailablePackage | null;
9
+ onSelectOption: (optionType: "session-pack" | "new-appointment" | "explore-packages", sessionPack?: SessionPack, newPackage?: AvailablePackage) => void;
10
+ onSelectPackage: (pkg: AvailablePackage) => void;
11
+ onBack: () => void;
12
+ }
13
+ export declare const BookingOptionStep: React.FC<BookingOptionStepProps>;
14
+ export default BookingOptionStep;