medos-sdk 1.1.10 → 1.1.12

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 (265) hide show
  1. package/dist/client/MedosClient.d.ts +1 -0
  2. package/dist/client/MedosClient.js +8 -1
  3. package/dist/components/AppointmentCalender.js +19 -22
  4. package/dist/components/AppointmentConfirmationStep.d.ts +1 -0
  5. package/dist/components/AppointmentConfirmationStep.js +34 -42
  6. package/dist/components/AppointmentDateTimeModal.d.ts +1 -0
  7. package/dist/components/AppointmentDateTimeModal.js +201 -168
  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 +10 -4
  13. package/dist/components/ContactPreferenceStep.js +10 -1
  14. package/dist/components/DoctorSelectModal.js +105 -59
  15. package/dist/components/EnquiryForm.js +81 -69
  16. package/dist/components/Icons/CloseIcon.d.ts +9 -0
  17. package/dist/components/Icons/CloseIcon.js +5 -0
  18. package/dist/components/InquiryDetailsStep.js +5 -1
  19. package/dist/components/PatientDetailsStep.js +17 -12
  20. package/dist/components/PatientSelectionStep.d.ts +12 -0
  21. package/dist/components/PatientSelectionStep.js +254 -0
  22. package/dist/components/PhoneVerificationStep.js +1 -1
  23. package/dist/components/SuccessStep.js +1 -1
  24. package/dist/components/appointment-booking/AppointmentCalender.js +145 -67
  25. package/dist/components/appointment-booking/appointment-modal-styles.d.ts +272 -0
  26. package/dist/components/appointment-booking/appointment-modal-styles.js +419 -0
  27. package/dist/components/appointment-booking/components/AppointmentConfirmationStep.d.ts +28 -0
  28. package/dist/components/appointment-booking/components/AppointmentConfirmationStep.js +107 -0
  29. package/dist/components/appointment-booking/components/AppointmentDateTimeModal.d.ts +18 -0
  30. package/dist/components/appointment-booking/components/AppointmentDateTimeModal.js +306 -0
  31. package/dist/components/appointment-booking/components/AppointmentSummaryStep.d.ts +12 -0
  32. package/dist/components/appointment-booking/components/AppointmentSummaryStep.js +194 -0
  33. package/dist/components/appointment-booking/components/BookingOptionStep.d.ts +14 -0
  34. package/dist/components/appointment-booking/components/BookingOptionStep.js +350 -0
  35. package/dist/components/appointment-booking/components/DoctorSelectModal.d.ts +14 -0
  36. package/dist/components/appointment-booking/components/DoctorSelectModal.js +213 -0
  37. package/dist/components/appointment-booking/components/PatientDetailsStep.d.ts +3 -0
  38. package/dist/components/appointment-booking/components/PatientDetailsStep.js +96 -0
  39. package/dist/components/appointment-booking/components/PatientSelectionStep.d.ts +12 -0
  40. package/dist/components/appointment-booking/components/PatientSelectionStep.js +254 -0
  41. package/dist/components/appointment-booking/components/PhoneVerificationStep.d.ts +3 -0
  42. package/dist/components/appointment-booking/components/PhoneVerificationStep.js +49 -0
  43. package/dist/components/appointment-booking/components/SuccessStep.d.ts +5 -0
  44. package/dist/components/appointment-booking/components/SuccessStep.js +9 -0
  45. package/dist/components/appointment-booking/components/index.d.ts +9 -0
  46. package/dist/components/appointment-booking/components/index.js +9 -0
  47. package/dist/components/appointment-booking/hooks/useAppointmentFlow.d.ts +0 -1
  48. package/dist/components/appointment-booking/hooks/useAppointmentFlow.js +111 -25
  49. package/dist/components/appointment-booking/hooks/useAppointmentState.js +32 -0
  50. package/dist/components/appointment-booking/hooks/useInitializeAddresses.js +0 -1
  51. package/dist/components/appointment-booking/index.d.ts +2 -4
  52. package/dist/components/appointment-booking/index.js +2 -2
  53. package/dist/components/appointment-booking/types.d.ts +167 -0
  54. package/dist/components/appointment-booking/types.js +16 -0
  55. package/dist/components/appointment-modal-styles.d.ts +259 -0
  56. package/dist/components/appointment-modal-styles.js +395 -0
  57. package/dist/components/constant.d.ts +2 -0
  58. package/dist/components/constant.js +15 -0
  59. package/dist/components/constants/constant.d.ts +2 -0
  60. package/dist/components/constants/constant.js +15 -0
  61. package/dist/components/constants/index.d.ts +3 -0
  62. package/dist/components/constants/index.js +24 -0
  63. package/dist/components/constants/options.d.ts +13 -0
  64. package/dist/components/constants/options.js +35 -0
  65. package/dist/components/constants/validation.d.ts +6 -0
  66. package/dist/components/constants/validation.js +16 -0
  67. package/dist/components/custom-calendar.js +20 -11
  68. package/dist/components/enquiry-form/EnquiryForm.d.ts +7 -0
  69. package/dist/components/enquiry-form/EnquiryForm.js +238 -0
  70. package/dist/components/enquiry-form/components/ContactInformationStep.d.ts +13 -0
  71. package/dist/components/enquiry-form/components/ContactInformationStep.js +21 -0
  72. package/dist/components/enquiry-form/components/ContactPreferenceStep.d.ts +9 -0
  73. package/dist/components/enquiry-form/components/ContactPreferenceStep.js +20 -0
  74. package/dist/components/enquiry-form/components/InquiryDetailsStep.d.ts +10 -0
  75. package/dist/components/enquiry-form/components/InquiryDetailsStep.js +20 -0
  76. package/dist/components/enquiry-form/components/index.d.ts +3 -0
  77. package/dist/components/enquiry-form/components/index.js +3 -0
  78. package/dist/components/enquiry-form/index.d.ts +2 -0
  79. package/dist/components/enquiry-form/index.js +2 -0
  80. package/dist/components/index.d.ts +7 -0
  81. package/dist/components/index.js +7 -0
  82. package/dist/components/shared/icons/Check.d.ts +6 -0
  83. package/dist/components/shared/icons/Check.js +2 -0
  84. package/dist/components/shared/icons/ChevronDownIcon.d.ts +4 -0
  85. package/dist/components/shared/icons/ChevronDownIcon.js +2 -0
  86. package/dist/components/shared/icons/ChevronLeft.d.ts +3 -0
  87. package/dist/components/shared/icons/ChevronLeft.js +3 -0
  88. package/dist/components/shared/icons/ChevronRight.d.ts +3 -0
  89. package/dist/components/shared/icons/ChevronRight.js +3 -0
  90. package/dist/components/shared/icons/CloseIcon.d.ts +9 -0
  91. package/dist/components/shared/icons/CloseIcon.js +5 -0
  92. package/dist/components/shared/icons/ConfirmationCheck.d.ts +1 -0
  93. package/dist/components/shared/icons/ConfirmationCheck.js +9 -0
  94. package/dist/components/shared/icons/ConsultationType.d.ts +1 -0
  95. package/dist/components/shared/icons/ConsultationType.js +2 -0
  96. package/dist/components/shared/icons/Date&TimeIcon.d.ts +1 -0
  97. package/dist/components/shared/icons/Date&TimeIcon.js +2 -0
  98. package/dist/components/shared/icons/MapIcon.d.ts +1 -0
  99. package/dist/components/shared/icons/MapIcon.js +2 -0
  100. package/dist/components/shared/icons/MedosLogo.d.ts +3 -0
  101. package/dist/components/shared/icons/MedosLogo.js +3 -0
  102. package/dist/components/shared/icons/PaymentMethodIcon.d.ts +1 -0
  103. package/dist/components/shared/icons/PaymentMethodIcon.js +2 -0
  104. package/dist/components/shared/icons/SuccessIcon.d.ts +8 -0
  105. package/dist/components/shared/icons/SuccessIcon.js +14 -0
  106. package/dist/components/shared/icons/UserIcon.d.ts +1 -0
  107. package/dist/components/shared/icons/UserIcon.js +2 -0
  108. package/dist/components/shared/icons/index.d.ts +13 -0
  109. package/dist/components/shared/icons/index.js +13 -0
  110. package/dist/components/shared/index.d.ts +2 -0
  111. package/dist/components/shared/index.js +2 -0
  112. package/dist/components/shared/ui/Calendar.d.ts +5 -0
  113. package/dist/components/shared/ui/Calendar.js +167 -0
  114. package/dist/components/shared/ui/SelectDropdown.d.ts +41 -0
  115. package/dist/components/shared/ui/SelectDropdown.js +301 -0
  116. package/dist/components/shared/ui/index.d.ts +2 -0
  117. package/dist/components/shared/ui/index.js +2 -0
  118. package/dist/components/styles/appointment.d.ts +4 -0
  119. package/dist/components/styles/appointment.js +220 -0
  120. package/dist/components/styles/enquiry.d.ts +2 -0
  121. package/dist/components/styles/enquiry.js +3 -0
  122. package/dist/components/styles/index.d.ts +2 -0
  123. package/dist/components/styles/index.js +2 -0
  124. package/dist/components/styles/shared.d.ts +3 -0
  125. package/dist/components/styles/shared.js +78 -0
  126. package/dist/components/styles.d.ts +1 -6
  127. package/dist/components/styles.js +1 -257
  128. package/dist/components/theme-styles.d.ts +5 -4
  129. package/dist/components/theme-styles.js +239 -125
  130. package/dist/components/types/appointment.d.ts +42 -0
  131. package/dist/components/types/appointment.js +1 -0
  132. package/dist/components/types/common.d.ts +24 -0
  133. package/dist/components/types/common.js +1 -0
  134. package/dist/components/types/enquiry.d.ts +59 -0
  135. package/dist/components/types/enquiry.js +1 -0
  136. package/dist/components/types/index.d.ts +4 -0
  137. package/dist/components/types/index.js +4 -0
  138. package/dist/components/types.d.ts +1 -52
  139. package/dist/components/types.js +1 -23
  140. package/dist/components/utils/date.d.ts +4 -0
  141. package/dist/components/utils/date.js +65 -0
  142. package/dist/components/utils/formatting.d.ts +4 -0
  143. package/dist/components/utils/formatting.js +9 -0
  144. package/dist/components/utils/index.d.ts +3 -0
  145. package/dist/components/utils/index.js +3 -0
  146. package/dist/components/utils/validation.d.ts +4 -0
  147. package/dist/components/utils/validation.js +37 -0
  148. package/dist/components/utils.d.ts +1 -5
  149. package/dist/components/utils.js +1 -15
  150. package/dist/components/validation.d.ts +1 -2
  151. package/dist/components/validation.js +1 -7
  152. package/dist/constants/index.d.ts +1 -1
  153. package/dist/constants/index.js +1 -1
  154. package/dist/core/theme/index.d.ts +1 -0
  155. package/dist/core/theme/index.js +1 -0
  156. package/dist/core/theme/responsive.d.ts +15 -0
  157. package/dist/core/theme/responsive.js +113 -0
  158. package/dist/core/theme/themes.js +16 -4
  159. package/dist/core/theme/types.d.ts +8 -0
  160. package/dist/index.d.ts +4 -2
  161. package/dist/index.js +2 -1
  162. package/dist/react/ThemeProvider.d.ts +2 -1
  163. package/dist/react/ThemeProvider.js +49 -10
  164. package/dist/react/index.d.ts +3 -3
  165. package/dist/react/index.js +1 -1
  166. package/dist/services/AppointmentService.d.ts +80 -2
  167. package/dist/services/AppointmentService.js +131 -5
  168. package/dist/services/AuthService.js +1 -1
  169. package/dist/services/WorkspaceService.d.ts +58 -3
  170. package/dist/services/WorkspaceService.js +10 -1
  171. package/dist/vanilla/AppointmentCalendarWidget.d.ts +17 -7
  172. package/dist/vanilla/AppointmentCalendarWidget.js +1401 -380
  173. package/dist/vanilla/EnquiryFormWidget.d.ts +1 -0
  174. package/dist/vanilla/EnquiryFormWidget.js +25 -43
  175. package/dist/vanilla/client/MedosClient.d.ts +1 -0
  176. package/dist/vanilla/components/AppointmentConfirmationStep.d.ts +1 -0
  177. package/dist/vanilla/components/AppointmentDateTimeModal.d.ts +1 -0
  178. package/dist/vanilla/components/AppointmentSummaryStep.d.ts +12 -0
  179. package/dist/vanilla/components/BookingOptionStep.d.ts +14 -0
  180. package/dist/vanilla/components/Icons/CloseIcon.d.ts +9 -0
  181. package/dist/vanilla/components/PatientSelectionStep.d.ts +12 -0
  182. package/dist/vanilla/components/VanillaCalendar.js +33 -18
  183. package/dist/vanilla/components/VanillaIcons.d.ts +5 -0
  184. package/dist/vanilla/components/VanillaIcons.js +92 -0
  185. package/dist/vanilla/components/VanillaSelect.d.ts +3 -0
  186. package/dist/vanilla/components/VanillaSelect.js +93 -5
  187. package/dist/vanilla/components/appointment-booking/appointment-modal-styles.d.ts +272 -0
  188. package/dist/vanilla/components/appointment-booking/components/AppointmentConfirmationStep.d.ts +28 -0
  189. package/dist/vanilla/components/appointment-booking/components/AppointmentDateTimeModal.d.ts +18 -0
  190. package/dist/vanilla/components/appointment-booking/components/AppointmentSummaryStep.d.ts +12 -0
  191. package/dist/vanilla/components/appointment-booking/components/BookingOptionStep.d.ts +14 -0
  192. package/dist/vanilla/components/appointment-booking/components/DoctorSelectModal.d.ts +14 -0
  193. package/dist/vanilla/components/appointment-booking/components/PatientDetailsStep.d.ts +3 -0
  194. package/dist/vanilla/components/appointment-booking/components/PatientSelectionStep.d.ts +12 -0
  195. package/dist/vanilla/components/appointment-booking/components/PhoneVerificationStep.d.ts +3 -0
  196. package/dist/vanilla/components/appointment-booking/components/SuccessStep.d.ts +5 -0
  197. package/dist/vanilla/components/appointment-booking/components/index.d.ts +9 -0
  198. package/dist/vanilla/components/appointment-booking/hooks/useAppointmentFlow.d.ts +0 -1
  199. package/dist/vanilla/components/appointment-booking/index.d.ts +2 -4
  200. package/dist/vanilla/components/appointment-booking/types.d.ts +167 -0
  201. package/dist/vanilla/components/appointment-modal-styles.d.ts +259 -0
  202. package/dist/vanilla/components/constant.d.ts +2 -0
  203. package/dist/vanilla/components/constants/constant.d.ts +2 -0
  204. package/dist/vanilla/components/constants/index.d.ts +3 -0
  205. package/dist/vanilla/components/constants/options.d.ts +13 -0
  206. package/dist/vanilla/components/constants/validation.d.ts +6 -0
  207. package/dist/vanilla/components/enquiry-form/EnquiryForm.d.ts +7 -0
  208. package/dist/vanilla/components/enquiry-form/components/ContactInformationStep.d.ts +13 -0
  209. package/dist/vanilla/components/enquiry-form/components/ContactPreferenceStep.d.ts +9 -0
  210. package/dist/vanilla/components/enquiry-form/components/InquiryDetailsStep.d.ts +10 -0
  211. package/dist/vanilla/components/enquiry-form/components/index.d.ts +3 -0
  212. package/dist/vanilla/components/enquiry-form/index.d.ts +2 -0
  213. package/dist/vanilla/components/index.d.ts +7 -3
  214. package/dist/vanilla/components/shared/icons/Check.d.ts +6 -0
  215. package/dist/vanilla/components/shared/icons/ChevronDownIcon.d.ts +4 -0
  216. package/dist/vanilla/components/shared/icons/ChevronLeft.d.ts +3 -0
  217. package/dist/vanilla/components/shared/icons/ChevronRight.d.ts +3 -0
  218. package/dist/vanilla/components/shared/icons/CloseIcon.d.ts +9 -0
  219. package/dist/vanilla/components/shared/icons/ConfirmationCheck.d.ts +1 -0
  220. package/dist/vanilla/components/shared/icons/ConsultationType.d.ts +1 -0
  221. package/dist/vanilla/components/shared/icons/Date&TimeIcon.d.ts +1 -0
  222. package/dist/vanilla/components/shared/icons/MapIcon.d.ts +1 -0
  223. package/dist/vanilla/components/shared/icons/MedosLogo.d.ts +3 -0
  224. package/dist/vanilla/components/shared/icons/PaymentMethodIcon.d.ts +1 -0
  225. package/dist/vanilla/components/shared/icons/SuccessIcon.d.ts +8 -0
  226. package/dist/vanilla/components/shared/icons/UserIcon.d.ts +1 -0
  227. package/dist/vanilla/components/shared/icons/index.d.ts +13 -0
  228. package/dist/vanilla/components/shared/index.d.ts +2 -0
  229. package/dist/vanilla/components/shared/ui/Calendar.d.ts +5 -0
  230. package/dist/vanilla/components/shared/ui/SelectDropdown.d.ts +41 -0
  231. package/dist/vanilla/components/shared/ui/index.d.ts +2 -0
  232. package/dist/vanilla/components/styles/appointment.d.ts +4 -0
  233. package/dist/vanilla/components/styles/enquiry.d.ts +2 -0
  234. package/dist/vanilla/components/styles/index.d.ts +2 -0
  235. package/dist/vanilla/components/styles/shared.d.ts +3 -0
  236. package/dist/vanilla/components/styles.d.ts +1 -6
  237. package/dist/vanilla/components/theme-styles.d.ts +5 -4
  238. package/dist/vanilla/components/types/appointment.d.ts +42 -0
  239. package/dist/vanilla/components/types/common.d.ts +24 -0
  240. package/dist/vanilla/components/types/enquiry.d.ts +59 -0
  241. package/dist/vanilla/components/types/index.d.ts +4 -0
  242. package/dist/vanilla/components/types.d.ts +1 -52
  243. package/dist/vanilla/components/utils/date.d.ts +4 -0
  244. package/dist/vanilla/components/utils/formatting.d.ts +4 -0
  245. package/dist/vanilla/components/utils/index.d.ts +3 -0
  246. package/dist/vanilla/components/utils/validation.d.ts +4 -0
  247. package/dist/vanilla/components/utils.d.ts +1 -5
  248. package/dist/vanilla/components/validation.d.ts +1 -2
  249. package/dist/vanilla/constants/index.d.ts +1 -1
  250. package/dist/vanilla/core/theme/index.d.ts +1 -0
  251. package/dist/vanilla/core/theme/responsive.d.ts +15 -0
  252. package/dist/vanilla/core/theme/types.d.ts +8 -0
  253. package/dist/vanilla/enquiry-widget.js +3632 -90
  254. package/dist/vanilla/index.d.ts +4 -2
  255. package/dist/vanilla/react/ThemeProvider.d.ts +2 -1
  256. package/dist/vanilla/react/index.d.ts +3 -3
  257. package/dist/vanilla/services/AppointmentService.d.ts +80 -2
  258. package/dist/vanilla/services/WorkspaceService.d.ts +58 -3
  259. package/dist/vanilla/vanilla/AppointmentCalendarWidget.d.ts +17 -7
  260. package/dist/vanilla/vanilla/EnquiryFormWidget.d.ts +1 -0
  261. package/dist/vanilla/vanilla/components/VanillaIcons.d.ts +5 -0
  262. package/dist/vanilla/vanilla/components/VanillaSelect.d.ts +3 -0
  263. package/dist/vanilla/widget.css +1045 -205
  264. package/dist/vanilla/widget.js +10365 -5737
  265. package/package.json +2 -2
@@ -1,8 +1,9 @@
1
1
  import { AppointmentService, } from "../services/AppointmentService";
2
2
  import { PatientService } from "../services/PatientService";
3
3
  import { MedosClient } from "../client/MedosClient";
4
- import { INITIAL_STATE, COUNTRY_CODES, GENDER_OPTIONS, BLOOD_GROUP_OPTIONS, } from "../components/types";
5
- import { validatePhoneNumber, validateCountryCode, } from "../components/validation";
4
+ import { INITIAL_STATE, } from "../components/appointment-booking/types";
5
+ import { COUNTRY_CODES, GENDER_OPTIONS, BLOOD_GROUP_OPTIONS, mapBloodGroupToApi, } from "../components/constants/options";
6
+ import { validatePhoneNumber, validateCountryCode, validateDateOfBirth, validateBloodGroup, } from "../components/validation";
6
7
  import { formatDateToISO, parsePatientName } from "../components/utils";
7
8
  import { VanillaIcons } from "./components/VanillaIcons";
8
9
  import { VanillaSelect } from "./components/VanillaSelect";
@@ -207,18 +208,58 @@ class AppointmentCalendarWidget {
207
208
  return true;
208
209
  }
209
210
  updateSubmitButtonState() {
210
- const submitBtn = this.container.querySelector("#medos-btn-submit");
211
- if (submitBtn) {
212
- const canSubmit = this.state.patientName &&
213
- this.state.patientAddress &&
214
- this.state.patientCity &&
215
- this.state.patientState &&
216
- this.state.patientCountry &&
217
- this.state.patientZipcode &&
218
- this.state.patientEmail &&
219
- this.state.patientAge &&
220
- this.state.otpVerified;
221
- submitBtn.disabled = !(canSubmit && !this.state.loading);
211
+ try {
212
+ const submitBtn = this.container.querySelector("#medos-btn-submit");
213
+ if (submitBtn) {
214
+ const canSubmit = this.state.patientName &&
215
+ this.state.patientAddress &&
216
+ this.state.patientCity &&
217
+ this.state.patientState &&
218
+ this.state.patientCountry &&
219
+ this.state.patientZipcode &&
220
+ this.state.patientAge &&
221
+ this.state.patientGender &&
222
+ this.state.otpVerified &&
223
+ (!(this.state.patientDob && this.state.patientDob.trim() !== "") ||
224
+ this.isValidDateOfBirth(this.state.patientDob)) &&
225
+ (!(this.state.bloodGroup && this.state.bloodGroup.trim() !== "") ||
226
+ this.isValidBloodGroup(this.state.bloodGroup));
227
+ submitBtn.disabled = !(canSubmit && !this.state.loading);
228
+ }
229
+ }
230
+ catch (error) {
231
+ console.error("Error updating submit button state:", error);
232
+ const submitBtn = this.container.querySelector("#medos-btn-submit");
233
+ if (submitBtn) {
234
+ submitBtn.disabled = true;
235
+ }
236
+ }
237
+ }
238
+ updatePatientDetailsButtonState() {
239
+ try {
240
+ const continueBtn = this.container.querySelector("#medos-btn-continue");
241
+ if (continueBtn) {
242
+ const isFormValid = this.state.patientName &&
243
+ this.state.patientAge &&
244
+ this.state.patientGender &&
245
+ this.state.patientAddress &&
246
+ this.state.patientCity &&
247
+ this.state.patientState &&
248
+ this.state.patientCountry &&
249
+ this.state.patientZipcode &&
250
+ (!(this.state.patientDob && this.state.patientDob.trim() !== "") ||
251
+ this.isValidDateOfBirth(this.state.patientDob)) &&
252
+ (!(this.state.bloodGroup && this.state.bloodGroup.trim() !== "") ||
253
+ this.isValidBloodGroup(this.state.bloodGroup));
254
+ continueBtn.disabled = !isFormValid;
255
+ }
256
+ }
257
+ catch (error) {
258
+ console.error("Error updating patient details button state:", error);
259
+ const continueBtn = this.container.querySelector("#medos-btn-continue");
260
+ if (continueBtn) {
261
+ continueBtn.disabled = true;
262
+ }
222
263
  }
223
264
  }
224
265
  async sendOtp() {
@@ -319,6 +360,9 @@ class AppointmentCalendarWidget {
319
360
  !this.state.selectedSlot ||
320
361
  !this.state.workspaceId ||
321
362
  !this.state.selectedAddress ||
363
+ !this.state.patientName ||
364
+ !this.state.patientAge ||
365
+ !this.state.patientGender ||
322
366
  !this.state.patientAddress ||
323
367
  !this.state.patientCity ||
324
368
  !this.state.patientState ||
@@ -333,6 +377,40 @@ class AppointmentCalendarWidget {
333
377
  this.setState({ error: "Please verify your phone number first." });
334
378
  return;
335
379
  }
380
+ if (this.state.patientDob && this.state.patientDob.trim() !== "") {
381
+ if (!this.isValidDateOfBirth(this.state.patientDob)) {
382
+ const dobValue = this.state.patientDob;
383
+ const today = new Date();
384
+ const inputDate = new Date(dobValue);
385
+ if (inputDate > today) {
386
+ this.displayFieldValidationError("dateOfBirth", "future");
387
+ }
388
+ else if (!/^\d{4}-\d{2}-\d{2}$/.test(dobValue)) {
389
+ this.displayFieldValidationError("dateOfBirth", "format");
390
+ }
391
+ else {
392
+ this.displayFieldValidationError("dateOfBirth", "invalid");
393
+ }
394
+ return;
395
+ }
396
+ }
397
+ if (this.state.bloodGroup && this.state.bloodGroup.trim() !== "") {
398
+ if (!this.isValidBloodGroup(this.state.bloodGroup)) {
399
+ this.displayFieldValidationError("bloodGroup", "invalid");
400
+ return;
401
+ }
402
+ try {
403
+ const mappedValue = this.safeMapBloodGroupToApi(this.state.bloodGroup);
404
+ if (mappedValue === "UNKNOWN" && this.state.bloodGroup !== "UNKNOWN") {
405
+ this.displayFieldValidationError("bloodGroup", "mapping");
406
+ return;
407
+ }
408
+ }
409
+ catch (error) {
410
+ this.displayFieldValidationError("bloodGroup", "mapping");
411
+ return;
412
+ }
413
+ }
336
414
  this.setState({ loading: true });
337
415
  this.render();
338
416
  try {
@@ -359,11 +437,11 @@ class AppointmentCalendarWidget {
359
437
  workspaceId: this.state.workspaceId,
360
438
  workspaceAddressId: this.state.selectedAddress,
361
439
  doctorId: this.state.selectedDoctor,
362
- mode: "OFFLINE",
440
+ mode: this.state.consultationMode || "OFFLINE",
363
441
  appointmentDate,
364
442
  fromDateTimeTs,
365
443
  toDateTimeTs,
366
- consultationCharge: this.state.consultationCharge || "0",
444
+ consultationCharge: this.state.consultationMode === "ONLINE" ? "500" : "300",
367
445
  type: "CONSULTATION",
368
446
  source: "SDK_POWERED_WEBSITE",
369
447
  patientPayload: {
@@ -378,10 +456,12 @@ class AppointmentCalendarWidget {
378
456
  gender: this.state.patientGender
379
457
  ? this.state.patientGender.toUpperCase()
380
458
  : undefined,
459
+ dob: this.safeFormatDateOfBirth(this.state.patientDob),
460
+ bloodGroup: this.safeMapBloodGroupToApi(this.state.bloodGroup),
381
461
  },
382
462
  patientAddress: patientAddressPayload,
383
463
  });
384
- this.state.step = 5;
464
+ this.state.step = 7;
385
465
  this.options.onSuccess?.();
386
466
  }
387
467
  catch (e) {
@@ -396,34 +476,112 @@ class AppointmentCalendarWidget {
396
476
  }
397
477
  goToNext() {
398
478
  if (this.state.step === 0) {
479
+ if (!this.state.otpVerified)
480
+ return;
481
+ this.state.step = 1;
482
+ this.render();
483
+ return;
484
+ }
485
+ if (this.state.step === 1) {
486
+ if (!this.state.bookingOptionType)
487
+ return;
488
+ if (this.state.showPackageExplorer) {
489
+ if (this.state.selectedNewPackage) {
490
+ this.state.showPackageExplorer = false;
491
+ this.state.step = 2;
492
+ this.render();
493
+ }
494
+ return;
495
+ }
496
+ this.state.step = 2;
497
+ this.render();
498
+ return;
499
+ }
500
+ if (this.state.step === 2) {
399
501
  if (this.state.addresses.length > 1 && !this.state.selectedAddress)
400
502
  return;
401
503
  if (this.doctors.length > 1 && !this.state.selectedDoctor)
402
504
  return;
505
+ this.state.step = 3;
506
+ const dateStr = formatDateToISO(this.state.selectedDate);
507
+ if (dateStr) {
508
+ this.loadSlots();
509
+ }
510
+ else {
511
+ this.render();
512
+ }
513
+ return;
514
+ }
515
+ if (this.state.step === 3) {
516
+ const dateStr = formatDateToISO(this.state.selectedDate);
517
+ if (!dateStr || !this.state.selectedSlot)
518
+ return;
519
+ this.state.step = 4;
520
+ this.render();
521
+ return;
522
+ }
523
+ if (this.state.step === 4) {
524
+ if (!this.state.selectedPatient)
525
+ return;
526
+ this.state.step = 5;
527
+ this.render();
528
+ return;
529
+ }
530
+ if (this.state.step === 5) {
531
+ const isFormValid = this.state.patientName &&
532
+ this.state.patientAge &&
533
+ this.state.patientGender &&
534
+ this.state.patientAddress &&
535
+ this.state.patientCity &&
536
+ this.state.patientState &&
537
+ this.state.patientCountry &&
538
+ this.state.patientZipcode;
539
+ if (!isFormValid)
540
+ return;
541
+ this.state.step = 6;
542
+ this.render();
543
+ return;
544
+ }
545
+ this.state.step = Math.min(7, this.state.step + 1);
546
+ this.render();
547
+ }
548
+ goBack() {
549
+ if (this.state.step === 1) {
550
+ if (this.state.showPackageExplorer) {
551
+ this.state.showPackageExplorer = false;
552
+ this.state.selectedNewPackage = null;
553
+ this.render();
554
+ return;
555
+ }
556
+ this.state.step = 0;
557
+ this.render();
558
+ return;
559
+ }
560
+ if (this.state.step === 2) {
403
561
  this.state.step = 1;
404
562
  this.render();
405
563
  return;
406
564
  }
407
- const dateStr = formatDateToISO(this.state.selectedDate);
408
- if (this.state.step === 1 && dateStr) {
565
+ if (this.state.step === 3) {
409
566
  this.state.step = 2;
410
- this.loadSlots();
567
+ this.render();
411
568
  return;
412
569
  }
413
- if (this.state.step === 2 && this.state.selectedSlot) {
570
+ if (this.state.step === 4) {
414
571
  this.state.step = 3;
415
572
  this.render();
416
573
  return;
417
574
  }
418
- if (this.state.step === 3 && this.state.otpVerified) {
575
+ if (this.state.step === 5) {
419
576
  this.state.step = 4;
420
577
  this.render();
421
578
  return;
422
579
  }
423
- this.state.step = Math.min(5, this.state.step + 1);
424
- this.render();
425
- }
426
- goBack() {
580
+ if (this.state.step === 6) {
581
+ this.state.step = 5;
582
+ this.render();
583
+ return;
584
+ }
427
585
  this.state.step = Math.max(0, this.state.step - 1);
428
586
  this.render();
429
587
  }
@@ -434,7 +592,25 @@ class AppointmentCalendarWidget {
434
592
  this.render();
435
593
  }
436
594
  setState(updates) {
437
- this.state = { ...this.state, ...updates };
595
+ try {
596
+ const safeUpdates = { ...updates };
597
+ if ("bloodGroup" in safeUpdates && safeUpdates.bloodGroup === undefined) {
598
+ safeUpdates.bloodGroup = "";
599
+ }
600
+ if ("patientDob" in safeUpdates && safeUpdates.patientDob === undefined) {
601
+ safeUpdates.patientDob = "";
602
+ }
603
+ this.state = { ...this.state, ...safeUpdates };
604
+ if (safeUpdates.bloodGroup !== undefined ||
605
+ safeUpdates.patientDob !== undefined) {
606
+ this.updatePatientDetailsButtonState();
607
+ this.updateSubmitButtonState();
608
+ }
609
+ }
610
+ catch (error) {
611
+ console.error("Error updating state:", error);
612
+ this.state = { ...this.state, ...updates };
613
+ }
438
614
  }
439
615
  render() {
440
616
  if (!this.mounted)
@@ -444,13 +620,6 @@ class AppointmentCalendarWidget {
444
620
  <div class="medos-appointment-card">
445
621
  <div class="medos-appointment-header">
446
622
  <h2 class="medos-appointment-title">Book Appointment</h2>
447
- <div class="medos-appointment-stepper">
448
- <div class="medos-appointment-step-pill ${this.state.step === 0 ? "active" : ""}">1 Address</div>
449
- <div class="medos-appointment-step-pill ${this.state.step === 1 ? "active" : ""}">2 Date</div>
450
- <div class="medos-appointment-step-pill ${this.state.step === 2 ? "active" : ""}">3 Slot</div>
451
- <div class="medos-appointment-step-pill ${this.state.step === 3 ? "active" : ""}">4 Phone</div>
452
- <div class="medos-appointment-step-pill ${this.state.step === 4 ? "active" : ""}">5 Details</div>
453
- </div>
454
623
  </div>
455
624
 
456
625
  ${this.state.loading
@@ -469,6 +638,30 @@ class AppointmentCalendarWidget {
469
638
  }
470
639
  initializeCustomComponents() {
471
640
  if (this.state.step === 0) {
641
+ const countryCodeContainer = this.container.querySelector("#medos-country-code-container");
642
+ if (countryCodeContainer) {
643
+ const countryOptions = COUNTRY_CODES.map((c) => ({
644
+ value: c.code,
645
+ label: c.label,
646
+ }));
647
+ this.countryCodeSelect = new VanillaSelect(countryCodeContainer, countryOptions, {
648
+ placeholder: "Country",
649
+ onValueChange: (value) => {
650
+ this.state.countryCode = value;
651
+ const sendOtpBtn = this.container.querySelector("#medos-btn-send-otp");
652
+ if (sendOtpBtn) {
653
+ const canSendOtp = this.state.countryCode &&
654
+ this.state.patientPhone.length >= 10;
655
+ sendOtpBtn.disabled = !canSendOtp;
656
+ }
657
+ },
658
+ });
659
+ if (this.state.countryCode) {
660
+ this.countryCodeSelect.setValue(this.state.countryCode);
661
+ }
662
+ }
663
+ }
664
+ if (this.state.step === 2 && !this.state.showPackageExplorer) {
472
665
  const addressContainer = this.container.querySelector("#medos-address-select-container");
473
666
  if (addressContainer && this.state.addresses.length > 0) {
474
667
  const addressOptions = this.state.addresses.map((a) => ({
@@ -496,7 +689,7 @@ class AppointmentCalendarWidget {
496
689
  onValueChange: (value) => {
497
690
  this.state.selectedDoctor = Number(value) || null;
498
691
  const selectedDoc = this.doctors.find((d) => d.id === this.state.selectedDoctor);
499
- if (selectedDoc && selectedDoc.consultationCharge) {
692
+ if (selectedDoc?.consultationCharge) {
500
693
  this.state.consultationCharge = selectedDoc.consultationCharge;
501
694
  }
502
695
  this.render();
@@ -507,7 +700,7 @@ class AppointmentCalendarWidget {
507
700
  }
508
701
  }
509
702
  }
510
- if (this.state.step === 1) {
703
+ if (this.state.step === 3) {
511
704
  const calendarContainer = this.container.querySelector("#medos-calendar-container");
512
705
  if (calendarContainer) {
513
706
  this.calendar = new VanillaCalendar(calendarContainer, {
@@ -515,55 +708,59 @@ class AppointmentCalendarWidget {
515
708
  pastDisabled: true,
516
709
  onSelect: (date) => {
517
710
  this.state.selectedDate = date;
518
- this.render();
519
- },
520
- });
521
- }
522
- }
523
- if (this.state.step === 3) {
524
- const countryCodeContainer = this.container.querySelector("#medos-country-code-container");
525
- if (countryCodeContainer) {
526
- const countryOptions = COUNTRY_CODES.map((c) => ({
527
- value: c.code,
528
- label: c.label,
529
- }));
530
- this.countryCodeSelect = new VanillaSelect(countryCodeContainer, countryOptions, {
531
- placeholder: "Country",
532
- onValueChange: (value) => {
533
- this.state.countryCode = value;
534
- const sendOtpBtn = this.container.querySelector("#medos-btn-send-otp");
535
- if (sendOtpBtn) {
536
- const canSendOtp = this.state.countryCode &&
537
- this.state.patientPhone.length >= 10;
538
- sendOtpBtn.disabled = !canSendOtp;
539
- }
711
+ this.loadSlots();
540
712
  },
541
713
  });
542
- if (this.state.countryCode) {
543
- this.countryCodeSelect.setValue(this.state.countryCode);
544
- }
545
714
  }
546
715
  }
547
- if (this.state.step === 4) {
548
- const genderContainer = this.container.querySelector("#medos-gender-container");
716
+ if (this.state.step === 5) {
717
+ const genderContainer = this.container.querySelector("#medos-gender-select-container");
549
718
  if (genderContainer) {
550
- this.genderSelect = new VanillaSelect(genderContainer, GENDER_OPTIONS, {
719
+ const genderOptions = GENDER_OPTIONS.map((g) => ({
720
+ value: g.value,
721
+ label: g.label,
722
+ }));
723
+ this.genderSelect = new VanillaSelect(genderContainer, genderOptions, {
551
724
  placeholder: "Select gender",
552
725
  onValueChange: (value) => {
553
726
  this.state.patientGender = value;
554
- this.updateSubmitButtonState();
727
+ this.updatePatientDetailsButtonState();
555
728
  },
556
729
  });
557
730
  if (this.state.patientGender) {
558
731
  this.genderSelect.setValue(this.state.patientGender);
559
732
  }
560
733
  }
561
- const bloodGroupContainer = this.container.querySelector("#medos-blood-group-container");
734
+ const bloodGroupContainer = this.container.querySelector("#medos-blood-group-select-container");
562
735
  if (bloodGroupContainer) {
563
- this.bloodGroupSelect = new VanillaSelect(bloodGroupContainer, BLOOD_GROUP_OPTIONS, {
736
+ const bloodGroupOptions = BLOOD_GROUP_OPTIONS.map((bg) => ({
737
+ value: bg.value,
738
+ label: bg.label,
739
+ }));
740
+ this.bloodGroupSelect = new VanillaSelect(bloodGroupContainer, bloodGroupOptions, {
564
741
  placeholder: "Select blood group (optional)",
565
742
  onValueChange: (value) => {
566
- this.state.bloodGroup = value;
743
+ this.setState({ error: null });
744
+ try {
745
+ if (value && value.trim() !== "") {
746
+ if (!this.isValidBloodGroup(value)) {
747
+ this.displayFieldValidationError("bloodGroup", "invalid");
748
+ return;
749
+ }
750
+ const mappedValue = this.safeMapBloodGroupToApi(value);
751
+ if (mappedValue === "UNKNOWN" && value !== "UNKNOWN") {
752
+ this.displayFieldValidationError("bloodGroup", "mapping");
753
+ return;
754
+ }
755
+ }
756
+ this.setState({ bloodGroup: value });
757
+ this.updatePatientDetailsButtonState();
758
+ this.updateSubmitButtonState();
759
+ }
760
+ catch (error) {
761
+ console.error("Blood group selection error:", error);
762
+ this.displayFieldValidationError("bloodGroup", "mapping");
763
+ }
567
764
  },
568
765
  });
569
766
  if (this.state.bloodGroup) {
@@ -575,147 +772,357 @@ class AppointmentCalendarWidget {
575
772
  renderStep() {
576
773
  switch (this.state.step) {
577
774
  case 0:
578
- return this.renderStep0();
775
+ return this.renderPhoneVerificationStep();
579
776
  case 1:
580
- return this.renderStep1();
777
+ return this.renderBookingOptionStep();
581
778
  case 2:
582
- return this.renderStep2();
779
+ return this.state.showPackageExplorer
780
+ ? this.renderPackageExplorerStep()
781
+ : this.renderLocationDoctorStep();
583
782
  case 3:
584
- return this.renderStep3();
783
+ return this.renderNewAppointmentStep();
585
784
  case 4:
586
- return this.renderStep4();
785
+ return this.renderPatientSelectionStep();
587
786
  case 5:
588
- return this.renderStep5();
787
+ return this.renderPatientDetailsStep();
788
+ case 6:
789
+ return this.renderAppointmentSummaryStep();
790
+ case 7:
791
+ return this.renderSuccessStep();
589
792
  default:
590
793
  return "";
591
794
  }
592
795
  }
593
- renderStep0() {
594
- const canProceed = this.canProceedFromMergedStep();
595
- return `
596
- <div style="border:1px solid #e5e7eb;border-radius:12px;margin-bottom:24px;background:#fff;">
597
- <div style="background:#F9FAFB;padding:16px 20px;display:flex;align-items:center;gap:12px;border-bottom:1px solid #E5E7EB;">
598
- ${VanillaIcons.mapPin(18)}
599
- <h3 style="font-size:18px;font-weight:600;margin:0;">Location & Doctor</h3>
600
- </div>
601
- <div style="padding:24px;">
602
- <div style="margin-bottom:20px;">
603
- <label style="display:block;font-size:13px;margin-bottom:6px;color:#374151;">
604
- Preferred Location <span style="color:#EF4444">*</span>
605
- </label>
606
- <div id="medos-address-select-container"></div>
796
+ renderPhoneVerificationStep() {
797
+ const countryCodeValid = this.state.countryCode && validateCountryCode(this.state.countryCode);
798
+ const phoneValid = this.state.patientPhone && validatePhoneNumber(this.state.patientPhone);
799
+ const canSendOtp = countryCodeValid && phoneValid && !this.state.otpSending;
800
+ if (!this.state.otpSent) {
801
+ return `
802
+ <div class="medos-section-card">
803
+ <div class="medos-section-header">
804
+ ${VanillaIcons.phone(14)}
805
+ <span class="medos-section-title">Search Patient</span>
607
806
  </div>
608
- <div style="margin-bottom:20px;">
609
- <label style="display:block;font-size:13px;margin-bottom:6px;color:#374151;">
610
- Preferred Doctor <span style="color:#EF4444">*</span>
611
- </label>
612
- <div id="medos-doctor-select-container"></div>
807
+ <div class="medos-section-body">
808
+ <p class="medos-section-description">Enter Phone Number</p>
809
+ <div class="medos-phone-input-row">
810
+ <div class="medos-country-code-wrapper">
811
+ <div id="medos-country-code-container"></div>
812
+ </div>
813
+ <div class="medos-phone-wrapper">
814
+ <input
815
+ type="tel"
816
+ class="medos-input"
817
+ id="medos-phone"
818
+ placeholder="XXXXXXXXXX"
819
+ value="${this.escapeHtml(this.state.patientPhone)}"
820
+ maxlength="15"
821
+ />
822
+ </div>
823
+ </div>
824
+ ${this.state.patientPhone && !phoneValid
825
+ ? '<div class="medos-validation-error">Phone number should be 7-15 digits</div>'
826
+ : ""}
613
827
  </div>
614
- <div style="margin-bottom:20px;">
615
- <label style="display:block;font-size:13px;margin-bottom:6px;color:#374151;">
616
- Chief Complaint <span style="color:#6B7280">(optional)</span>
617
- </label>
618
- <textarea
619
- style="width:100%;padding:10px 12px;border-radius:8px;border:1px solid #e6e9ef;font-size:14px;box-sizing:border-box;"
620
- class="medos-textarea"
621
- id="medos-chief-complaint"
622
- placeholder="Enter Chief Complaint or Appointment Notes"
623
- ></textarea>
828
+ </div>
829
+ <div class="medos-actions">
830
+ <button class="medos-btn medos-btn-secondary" id="medos-btn-cancel">Cancel</button>
831
+ <button class="medos-btn medos-btn-primary" id="medos-btn-send-otp" ${canSendOtp ? "" : "disabled"}>${this.state.otpSending ? "Sending..." : "Continue"}</button>
832
+ </div>
833
+ `;
834
+ }
835
+ if (this.state.otpVerified) {
836
+ return `
837
+ <div class="medos-section-card">
838
+ <div class="medos-section-header">
839
+ ${VanillaIcons.phone(14)}
840
+ <span class="medos-section-title">Search Patient</span>
841
+ </div>
842
+ <div class="medos-section-body">
843
+ <div class="medos-verified-badge">
844
+ ${VanillaIcons.successBadge(20)}
845
+ <span>Phone verified successfully</span>
846
+ </div>
847
+ <div class="medos-verified-number">${this.escapeHtml(this.state.countryCode)} ${this.escapeHtml(this.state.patientPhone)}</div>
624
848
  </div>
625
849
  </div>
626
- </div>
627
- <div style="display:flex;gap:8px;margin-top:16px;justify-content:flex-end;">
628
- <button style="background:#218838;color:#fff;border:none;padding:10px 14px;border-radius:8px;cursor:pointer;font-weight:600;${!canProceed ? "opacity:0.6;" : ""}" id="medos-btn-next" ${!canProceed ? "disabled" : ""}>Next</button>
629
- </div>
630
- `;
631
- }
632
- renderStep1() {
633
- const dateStr = formatDateToISO(this.state.selectedDate);
634
- const selectedDoctor = this.doctors.find((d) => d.id === this.state.selectedDoctor);
635
- const selectedDoctorData = this.doctors.find((d) => d.id === this.state.selectedDoctor);
636
- const consultationCharge = selectedDoctorData?.consultationCharge ||
637
- selectedDoctor?.consultationCharge ||
638
- this.state.consultationCharge ||
639
- "N/A";
850
+ <div class="medos-actions">
851
+ <button class="medos-btn medos-btn-secondary" id="medos-btn-cancel">Cancel</button>
852
+ <button class="medos-btn medos-btn-primary" id="medos-btn-next">Continue</button>
853
+ </div>
854
+ `;
855
+ }
640
856
  return `
641
857
  <div class="medos-section-card">
642
858
  <div class="medos-section-header">
643
- ${VanillaIcons.consultationType(14)}
644
- <span class="medos-section-title">Consultation Type</span>
859
+ ${VanillaIcons.phone(14)}
860
+ <span class="medos-section-title">Enter OTP</span>
645
861
  </div>
646
862
  <div class="medos-section-body">
647
- <div class="medos-consultation-options">
648
- <label class="medos-radio-option ${this.state.consultationMode === "OFFLINE" ? "selected" : ""}">
649
- <input
650
- type="radio"
651
- name="consultationMode"
652
- value="OFFLINE"
653
- ${this.state.consultationMode === "OFFLINE" ? "checked" : ""}
654
- class="medos-radio-input"
655
- />
656
- <span class="medos-radio-label">In-Person Visit</span>
657
- </label>
658
- <label class="medos-radio-option ${this.state.consultationMode === "ONLINE" ? "selected" : ""}">
659
- <input
660
- type="radio"
661
- name="consultationMode"
662
- value="ONLINE"
663
- ${this.state.consultationMode === "ONLINE" ? "checked" : ""}
664
- class="medos-radio-input"
665
- />
666
- <span class="medos-radio-label">Online Consultation</span>
667
- </label>
863
+ <p class="medos-section-description">Enter the 6-digit code sent to ${this.escapeHtml(this.state.countryCode)} ${this.escapeHtml(this.state.patientPhone)}</p>
864
+ <div class="medos-otp-input-wrapper">
865
+ <input
866
+ type="text"
867
+ class="medos-input medos-otp-input"
868
+ id="medos-otp"
869
+ placeholder="Enter 6-digit OTP"
870
+ value="${this.escapeHtml(this.state.otpCode)}"
871
+ maxlength="6"
872
+ />
668
873
  </div>
669
- <div class="medos-consultation-charge">
670
- <span class="medos-charge-label">Consultation Charge:</span>
671
- <span class="medos-charge-value">${consultationCharge !== "N/A"
672
- ? "" + consultationCharge
673
- : consultationCharge}</span>
874
+ <button class="medos-link-btn" id="medos-btn-change-number">Change phone number</button>
875
+ </div>
876
+ </div>
877
+ <div class="medos-actions">
878
+ <button class="medos-btn medos-btn-secondary" id="medos-btn-resend-otp">Resend OTP</button>
879
+ <button class="medos-btn medos-btn-primary" id="medos-btn-verify-otp" ${this.state.otpCode.length === 6 && !this.state.otpVerifying
880
+ ? ""
881
+ : "disabled"}>${this.state.otpVerifying ? "Verifying..." : "Verify OTP"}</button>
882
+ </div>
883
+ `;
884
+ }
885
+ renderBookingOptionStep() {
886
+ const hasActivePacks = this.state.userSessionPacks.length > 0;
887
+ const activePack = this.state.userSessionPacks.find((p) => p.remainingSessions > 0);
888
+ return `
889
+ <div class="medos-section-card">
890
+ <div class="medos-section-header">
891
+ ${VanillaIcons.calendar(14)}
892
+ <span class="medos-section-title">Choose Booking Option</span>
893
+ </div>
894
+ <div class="medos-section-body">
895
+ <p class="medos-section-description">Select a package to explore on offer</p>
896
+
897
+ ${hasActivePacks && activePack
898
+ ? `
899
+ <div class="medos-active-session-packs">
900
+ <label class="medos-form-label">YOUR ACTIVE SESSION PACKS</label>
901
+ <div class="medos-session-pack-card ${this.state.bookingOptionType === "session-pack"
902
+ ? "selected"
903
+ : ""}" data-pack-id="${activePack.id}">
904
+ <div class="medos-pack-header">
905
+ <span class="medos-pack-name">${this.escapeHtml(activePack.name)}</span>
906
+ <span class="medos-pack-badge">${activePack.remainingSessions} sessions</span>
907
+ </div>
908
+ <div class="medos-pack-details">
909
+ ${activePack.remainingSessions} of ${activePack.totalSessions} sessions • <a href="#" class="medos-link">view details</a>
910
+ </div>
911
+ </div>
912
+ </div>
913
+ `
914
+ : ""}
915
+
916
+ <div class="medos-other-options">
917
+ <label class="medos-form-label">OTHER OPTIONS</label>
918
+
919
+ <div class="medos-option-cards">
920
+ <div class="medos-option-card ${this.state.bookingOptionType === "new-appointment"
921
+ ? "selected"
922
+ : ""}" data-option="new-appointment">
923
+ <div class="medos-option-icon">${VanillaIcons.calendar(24)}</div>
924
+ <div class="medos-option-content">
925
+ <span class="medos-option-title">Standard Consultation</span>
926
+ <span class="medos-option-subtitle">Pay per appointment</span>
927
+ </div>
928
+ </div>
929
+
930
+ <div class="medos-option-card ${this.state.bookingOptionType === "explore-packages"
931
+ ? "selected"
932
+ : ""}" data-option="explore-packages">
933
+ <div class="medos-option-icon">${VanillaIcons.giftBox(24)}</div>
934
+ <div class="medos-option-content">
935
+ <span class="medos-option-title">Session Packs</span>
936
+ <span class="medos-option-subtitle">Explore packs with discounts and offers</span>
937
+ </div>
938
+ </div>
939
+ </div>
674
940
  </div>
675
941
  </div>
676
942
  </div>
677
-
943
+ <div class="medos-actions">
944
+ <button class="medos-btn medos-btn-back" id="medos-btn-back">${VanillaIcons.arrowLeft(14)} Back</button>
945
+ <button class="medos-btn medos-btn-secondary" id="medos-btn-cancel">Cancel</button>
946
+ <button class="medos-btn medos-btn-primary" id="medos-btn-continue">Continue</button>
947
+ </div>
948
+ `;
949
+ }
950
+ renderPackageExplorerStep() {
951
+ const packages = this.state.availablePackages.length > 0
952
+ ? this.state.availablePackages
953
+ : [
954
+ {
955
+ id: 1,
956
+ name: "Silver Package",
957
+ description: "• 3 consultations (In clinic & Online appointments)\n• Can redeem for 6 months after\n purchase",
958
+ totalSessions: 3,
959
+ price: 5000,
960
+ validityDays: 180,
961
+ applicableOnline: true,
962
+ applicableOffline: true,
963
+ },
964
+ {
965
+ id: 2,
966
+ name: "Gold Package",
967
+ description: "• 5 consultations (In clinic & Online appointments)\n• Can redeem for 12 months after\n purchase",
968
+ totalSessions: 5,
969
+ price: 8000,
970
+ validityDays: 365,
971
+ applicableOnline: true,
972
+ applicableOffline: true,
973
+ },
974
+ ];
975
+ return `
678
976
  <div class="medos-section-card">
679
977
  <div class="medos-section-header">
680
- ${VanillaIcons.dateTime(14)}
681
- <span class="medos-section-title">Select Date</span>
978
+ ${VanillaIcons.giftBox(14)}
979
+ <span class="medos-section-title">Explore Session Packs</span>
682
980
  </div>
683
981
  <div class="medos-section-body">
684
- <div id="medos-calendar-container"></div>
982
+ <p class="medos-section-description">Select a package to explore on offer</p>
983
+
984
+ <div class="medos-available-packages">
985
+ <label class="medos-form-label">AVAILABLE PACKAGES</label>
986
+
987
+ ${packages
988
+ .map((pkg, index) => `
989
+ <div class="medos-package-card ${this.state.selectedNewPackage?.id === pkg.id ? "selected" : ""}" data-package-id="${pkg.id}">
990
+ <div class="medos-package-radio">
991
+ <input type="radio" name="package" value="${pkg.id}" ${this.state.selectedNewPackage?.id === pkg.id ? "checked" : ""} />
992
+ </div>
993
+ <div class="medos-package-content">
994
+ <div class="medos-package-name">${this.escapeHtml(pkg.name)}</div>
995
+ <div class="medos-package-description">${this.escapeHtml(pkg.description || "").replace(/\n/g, "<br>")}</div>
996
+ </div>
997
+ <div class="medos-package-price">₹ ${pkg.price.toLocaleString()}</div>
998
+ </div>
999
+ `)
1000
+ .join("")}
1001
+ </div>
1002
+
1003
+ <div class="medos-package-note">
1004
+ <a href="#" class="medos-link">View full terms and conditions</a>
1005
+ </div>
685
1006
  </div>
686
1007
  </div>
687
-
688
1008
  <div class="medos-actions">
689
- <button class="medos-btn medos-btn-secondary" id="medos-btn-back">Back</button>
690
- <button class="medos-btn medos-btn-primary" id="medos-btn-next" ${dateStr ? "" : "disabled"}>Next</button>
1009
+ <button class="medos-btn medos-btn-back" id="medos-btn-back">${VanillaIcons.arrowLeft(14)} Back</button>
1010
+ <button class="medos-btn medos-btn-secondary" id="medos-btn-cancel">Cancel</button>
1011
+ <button class="medos-btn medos-btn-primary" id="medos-btn-continue" ${this.state.selectedNewPackage ? "" : "disabled"}>Continue</button>
691
1012
  </div>
692
1013
  `;
693
1014
  }
694
- renderStep2() {
1015
+ renderLocationDoctorStep() {
1016
+ const canProceed = this.canProceedFromMergedStep();
1017
+ return `
1018
+ <div style="border:1px solid #e5e7eb;border-radius:12px;margin-bottom:24px;background:#fff;">
1019
+ <div style="background:#F9FAFB;padding:16px 20px;display:flex;align-items:center;gap:12px;border-bottom:1px solid #E5E7EB;">
1020
+ ${VanillaIcons.mapPin(18)}
1021
+ <h3 style="font-size:18px;font-weight:600;margin:0;">Location & Doctor</h3>
1022
+ </div>
1023
+ <div style="padding:24px;">
1024
+ <div style="margin-bottom:20px;">
1025
+ <label style="display:block;font-size:13px;margin-bottom:6px;color:#374151;">
1026
+ Preferred Location <span style="color:#EF4444">*</span>
1027
+ </label>
1028
+ <div id="medos-address-select-container"></div>
1029
+ </div>
1030
+ <div style="margin-bottom:20px;">
1031
+ <label style="display:block;font-size:13px;margin-bottom:6px;color:#374151;">
1032
+ Preferred Doctor <span style="color:#EF4444">*</span>
1033
+ </label>
1034
+ <div id="medos-doctor-select-container"></div>
1035
+ </div>
1036
+ <div style="margin-bottom:20px;">
1037
+ <label style="display:block;font-size:13px;margin-bottom:6px;color:#374151;">
1038
+ Chief Complaint <span style="color:#6B7280">(optional)</span>
1039
+ </label>
1040
+ <textarea
1041
+ style="width:100%;padding:10px 12px;border-radius:8px;border:1px solid #e6e9ef;font-size:14px;box-sizing:border-box;"
1042
+ class="medos-textarea"
1043
+ id="medos-chief-complaint"
1044
+ placeholder="Enter Chief Complaint or Appointment Notes"
1045
+ ></textarea>
1046
+ </div>
1047
+ </div>
1048
+ </div>
1049
+ <div style="display:flex;gap:8px;margin-top:16px;justify-content:flex-end;">
1050
+ <button class="medos-btn medos-btn-back" id="medos-btn-back">${VanillaIcons.arrowLeft(14)} Back</button>
1051
+ <button style="background:#218838;color:#fff;border:none;padding:10px 14px;border-radius:8px;cursor:pointer;font-weight:600;${canProceed ? "" : "opacity:0.6;"}" id="medos-btn-next" ${canProceed ? "" : "disabled"}>Continue</button>
1052
+ </div>
1053
+ `;
1054
+ }
1055
+ renderNewAppointmentStep() {
1056
+ const dateStr = formatDateToISO(this.state.selectedDate);
1057
+ const consultationCharge = this.state.consultationMode === "ONLINE" ? "500" : "300";
695
1058
  const dateDisplay = this.state.selectedDate
696
1059
  ? this.state.selectedDate.toLocaleDateString("en-US", {
697
- weekday: "long",
698
1060
  year: "numeric",
699
1061
  month: "long",
700
1062
  day: "numeric",
701
1063
  })
702
1064
  : "";
1065
+ const usingSessionPack = this.state.bookingOptionType === "session-pack" &&
1066
+ this.state.selectedSessionPack;
1067
+ const sessionInfo = usingSessionPack
1068
+ ? `
1069
+ <div class="medos-session-info">
1070
+ <span class="medos-session-label">Sessions:</span>
1071
+ <span class="medos-session-value">${this.state.selectedSessionPack?.remainingSessions} appointments now remaining</span>
1072
+ </div>
1073
+ `
1074
+ : "";
703
1075
  return `
704
1076
  <div class="medos-section-card">
705
1077
  <div class="medos-section-header">
706
- ${VanillaIcons.clock(14)}
707
- <span class="medos-section-title">Select Time Slot</span>
1078
+ ${VanillaIcons.consultationType(14)}
1079
+ <span class="medos-section-title">Consultation Type</span>
708
1080
  </div>
709
1081
  <div class="medos-section-body">
710
- <div class="medos-selected-date-display">
711
- ${VanillaIcons.dateTime(16)}
712
- <span>${dateDisplay}</span>
1082
+ <label class="medos-form-label">Consultation Mode</label>
1083
+ <div class="medos-consultation-options">
1084
+ <label class="medos-radio-option ${this.state.consultationMode === "OFFLINE" ? "selected" : ""}">
1085
+ <input
1086
+ type="radio"
1087
+ name="consultationMode"
1088
+ value="OFFLINE"
1089
+ ${this.state.consultationMode === "OFFLINE" ? "checked" : ""}
1090
+ class="medos-radio-input"
1091
+ />
1092
+ <span class="medos-radio-label">Online</span>
1093
+ </label>
1094
+ <label class="medos-radio-option ${this.state.consultationMode === "ONLINE" ? "selected" : ""}">
1095
+ <input
1096
+ type="radio"
1097
+ name="consultationMode"
1098
+ value="ONLINE"
1099
+ ${this.state.consultationMode === "ONLINE" ? "checked" : ""}
1100
+ class="medos-radio-input"
1101
+ />
1102
+ <span class="medos-radio-label">Offline</span>
1103
+ </label>
1104
+ </div>
1105
+ </div>
1106
+ </div>
1107
+
1108
+ <div class="medos-section-card">
1109
+ <div class="medos-section-header">
1110
+ ${VanillaIcons.dateTime(14)}
1111
+ <span class="medos-section-title">Date & Time</span>
1112
+ </div>
1113
+ <div class="medos-section-body medos-datetime-layout">
1114
+ <div class="medos-calendar-section">
1115
+ <label class="medos-form-label">Available Dates</label>
1116
+ <div class="medos-date-display">${dateDisplay}</div>
1117
+ <div id="medos-calendar-container"></div>
713
1118
  </div>
714
- ${this.state.slots.length === 0
715
- ? '<div class="medos-empty-slots">No slots available for selected date</div>'
1119
+ <div class="medos-slots-section">
1120
+ <label class="medos-form-label">Available Slots</label>
1121
+ ${this.state.slots.length === 0
1122
+ ? '<div class="medos-empty-slots">Select a date to see available slots</div>'
716
1123
  : `
717
- <div class="medos-slots-grid">
718
- ${this.state.slots
1124
+ <div class="medos-slots-grid-compact">
1125
+ ${this.state.slots
719
1126
  .map((s) => {
720
1127
  const start = new Date(s.start).toLocaleTimeString([], {
721
1128
  hour: "2-digit",
@@ -728,128 +1135,164 @@ class AppointmentCalendarWidget {
728
1135
  const selected = this.state.selectedSlot?.start === s.start &&
729
1136
  this.state.selectedSlot?.end === s.end;
730
1137
  return `
731
- <div class="medos-slot-card ${selected ? "selected" : ""}"
732
- data-slot-id="${this.escapeHtml(s.id || `${s.start}-${s.end}`)}"
733
- data-slot-start="${this.escapeHtml(s.start)}"
734
- data-slot-end="${this.escapeHtml(s.end)}">
735
- <span class="medos-slot-time">${start}</span>
736
- <span class="medos-slot-separator">-</span>
737
- <span class="medos-slot-time">${end}</span>
738
- ${selected
739
- ? `<span class="medos-slot-check">${VanillaIcons.check(14)}</span>`
740
- : ""}
741
- </div>
742
- `;
1138
+ <div class="medos-slot-btn ${selected ? "selected" : ""}"
1139
+ data-slot-id="${this.escapeHtml(s.id || `${s.start}-${s.end}`)}"
1140
+ data-slot-start="${this.escapeHtml(s.start)}"
1141
+ data-slot-end="${this.escapeHtml(s.end)}">
1142
+ ${start}
1143
+ </div>
1144
+ `;
743
1145
  })
744
1146
  .join("")}
745
- </div>
746
- `}
1147
+ </div>
1148
+ `}
1149
+ </div>
747
1150
  </div>
748
1151
  </div>
1152
+
749
1153
  <div class="medos-actions">
750
- <button class="medos-btn medos-btn-secondary" id="medos-btn-back">Back</button>
751
- <button class="medos-btn medos-btn-primary" id="medos-btn-next" ${this.state.selectedSlot ? "" : "disabled"} style="opacity: ${this.state.selectedSlot ? 1 : 0.6}">Next</button>
1154
+ <button class="medos-btn medos-btn-back" id="medos-btn-back">${VanillaIcons.arrowLeft(14)} Back</button>
1155
+ <button class="medos-btn medos-btn-secondary" id="medos-btn-cancel">Cancel</button>
1156
+ <button class="medos-btn medos-btn-primary" id="medos-btn-continue" ${this.state.selectedSlot ? "" : "disabled"}>Continue</button>
752
1157
  </div>
753
1158
  `;
754
1159
  }
755
- renderStep3() {
756
- const countryCodeValid = this.state.countryCode && validateCountryCode(this.state.countryCode);
757
- const phoneValid = this.state.patientPhone && validatePhoneNumber(this.state.patientPhone);
758
- const canSendOtp = countryCodeValid && phoneValid && !this.state.otpSending;
759
- if (!this.state.otpSent) {
760
- return `
761
- <div class="medos-section-card">
762
- <div class="medos-section-header">
763
- ${VanillaIcons.phone(14)}
764
- <span class="medos-section-title">Phone Verification</span>
765
- </div>
766
- <div class="medos-section-body">
767
- <p class="medos-section-description">Please enter your phone number for verification</p>
768
- <div class="medos-phone-input-row">
769
- <div class="medos-country-code-wrapper">
770
- <div id="medos-country-code-container"></div>
771
- </div>
772
- <div class="medos-phone-wrapper">
773
- <input
774
- type="tel"
775
- class="medos-input"
776
- id="medos-phone"
777
- placeholder="Enter phone number"
778
- value="${this.escapeHtml(this.state.patientPhone)}"
779
- maxlength="15"
780
- />
781
- </div>
782
- </div>
783
- ${this.state.patientPhone && !phoneValid
784
- ? '<div class="medos-validation-error">Phone number should be 7-15 digits</div>'
785
- : ""}
786
- </div>
787
- </div>
788
- <div class="medos-actions">
789
- <button class="medos-btn medos-btn-secondary" id="medos-btn-back">Back</button>
790
- <button class="medos-btn medos-btn-primary" id="medos-btn-send-otp" ${canSendOtp ? "" : "disabled"}>${this.state.otpSending ? "Sending..." : "Send OTP"}</button>
791
- </div>
792
- `;
793
- }
794
- if (this.state.otpVerified) {
795
- return `
796
- <div class="medos-section-card">
797
- <div class="medos-section-header">
798
- ${VanillaIcons.phone(14)}
799
- <span class="medos-section-title">Phone Verification</span>
800
- </div>
801
- <div class="medos-section-body">
802
- <div class="medos-verified-badge">
803
- ${VanillaIcons.successBadge(20)}
804
- <span>Phone verified successfully</span>
805
- </div>
806
- <div class="medos-verified-number">${this.escapeHtml(this.state.countryCode)} ${this.escapeHtml(this.state.patientPhone)}</div>
807
- </div>
808
- </div>
809
- <div class="medos-actions">
810
- <button class="medos-btn medos-btn-secondary" id="medos-btn-back">Back</button>
811
- <button class="medos-btn medos-btn-primary" id="medos-btn-next">Continue</button>
812
- </div>
813
- `;
814
- }
1160
+ renderPatientSelectionStep() {
1161
+ const patients = this.state.verifiedPatients.length > 0
1162
+ ? this.state.verifiedPatients
1163
+ : [
1164
+ {
1165
+ id: 1,
1166
+ firstName: "Mumma",
1167
+ lastName: "Bear",
1168
+ email: "mumma@example.com",
1169
+ countryCode: "+91",
1170
+ phoneNumber: this.state.patientPhone,
1171
+ dob: "1970-01-01",
1172
+ age: 55,
1173
+ gender: "FEMALE",
1174
+ bloodGroup: "O+",
1175
+ mrn: "MRN001",
1176
+ address: {
1177
+ id: 1,
1178
+ completeAddress: "123 Main St",
1179
+ addressLine1: "123 Main St",
1180
+ addressLine2: "",
1181
+ city: "Mumbai",
1182
+ state: "Maharashtra",
1183
+ country: "India",
1184
+ zipcode: "400001",
1185
+ landmark: "",
1186
+ phoneNumber: this.state.patientPhone,
1187
+ latitude: 0,
1188
+ longitude: 0,
1189
+ },
1190
+ },
1191
+ {
1192
+ id: 2,
1193
+ firstName: "Papa",
1194
+ lastName: "Bear",
1195
+ email: "papa@example.com",
1196
+ countryCode: "+91",
1197
+ phoneNumber: this.state.patientPhone,
1198
+ dob: "1968-01-01",
1199
+ age: 57,
1200
+ gender: "MALE",
1201
+ bloodGroup: "B+",
1202
+ mrn: "MRN002",
1203
+ address: {
1204
+ id: 2,
1205
+ completeAddress: "123 Main St",
1206
+ addressLine1: "123 Main St",
1207
+ addressLine2: "",
1208
+ city: "Mumbai",
1209
+ state: "Maharashtra",
1210
+ country: "India",
1211
+ zipcode: "400001",
1212
+ landmark: "",
1213
+ phoneNumber: this.state.patientPhone,
1214
+ latitude: 0,
1215
+ longitude: 0,
1216
+ },
1217
+ },
1218
+ {
1219
+ id: 3,
1220
+ firstName: "Monty",
1221
+ lastName: "Bear",
1222
+ email: "monty@example.com",
1223
+ countryCode: "+91",
1224
+ phoneNumber: this.state.patientPhone,
1225
+ dob: "1990-01-01",
1226
+ age: 35,
1227
+ gender: "MALE",
1228
+ bloodGroup: "A+",
1229
+ mrn: "MRN003",
1230
+ address: {
1231
+ id: 3,
1232
+ completeAddress: "123 Main St",
1233
+ addressLine1: "123 Main St",
1234
+ addressLine2: "",
1235
+ city: "Mumbai",
1236
+ state: "Maharashtra",
1237
+ country: "India",
1238
+ zipcode: "400001",
1239
+ landmark: "",
1240
+ phoneNumber: this.state.patientPhone,
1241
+ latitude: 0,
1242
+ longitude: 0,
1243
+ },
1244
+ },
1245
+ ];
815
1246
  return `
816
1247
  <div class="medos-section-card">
817
1248
  <div class="medos-section-header">
818
- ${VanillaIcons.phone(14)}
819
- <span class="medos-section-title">Enter OTP</span>
1249
+ ${VanillaIcons.users(14)}
1250
+ <span class="medos-section-title">Select Patient</span>
820
1251
  </div>
821
1252
  <div class="medos-section-body">
822
- <p class="medos-section-description">Enter the 6-digit code sent to ${this.escapeHtml(this.state.countryCode)} ${this.escapeHtml(this.state.patientPhone)}</p>
823
- <div class="medos-otp-input-wrapper">
824
- <input
825
- type="text"
826
- class="medos-input medos-otp-input"
827
- id="medos-otp"
828
- placeholder="Enter 6-digit OTP"
829
- value="${this.escapeHtml(this.state.otpCode)}"
830
- maxlength="6"
831
- />
1253
+ <div class="medos-patient-list">
1254
+ ${patients
1255
+ .map((patient) => `
1256
+ <div class="medos-patient-card ${this.state.selectedPatient?.id === patient.id ? "selected" : ""}" data-patient-id="${patient.id}">
1257
+ <div class="medos-patient-radio">
1258
+ <input type="radio" name="patient" value="${patient.id}" ${this.state.selectedPatient?.id === patient.id ? "checked" : ""} />
1259
+ </div>
1260
+ <div class="medos-patient-avatar">
1261
+ ${patient.firstName.charAt(0)}${patient.lastName.charAt(0)}
1262
+ </div>
1263
+ <div class="medos-patient-info">
1264
+ <div class="medos-patient-name">${this.escapeHtml(patient.firstName)} ${this.escapeHtml(patient.lastName)}</div>
1265
+ <div class="medos-patient-details">${this.escapeHtml(patient.countryCode)} ${this.escapeHtml(patient.phoneNumber)}</div>
1266
+ </div>
1267
+ <div class="medos-patient-select">select</div>
1268
+ </div>
1269
+ `)
1270
+ .join("")}
832
1271
  </div>
833
- <button class="medos-link-btn" id="medos-btn-change-number">Change phone number</button>
1272
+
1273
+ <button class="medos-add-patient-btn" id="medos-btn-add-patient">
1274
+ ${VanillaIcons.plus(16)} New Patient
1275
+ </button>
834
1276
  </div>
835
1277
  </div>
836
1278
  <div class="medos-actions">
837
- <button class="medos-btn medos-btn-secondary" id="medos-btn-resend-otp">Resend OTP</button>
838
- <button class="medos-btn medos-btn-primary" id="medos-btn-verify-otp" ${this.state.otpCode.length === 6 && !this.state.otpVerifying
839
- ? ""
840
- : "disabled"}>${this.state.otpVerifying ? "Verifying..." : "Verify OTP"}</button>
1279
+ <button class="medos-btn medos-btn-back" id="medos-btn-back">${VanillaIcons.arrowLeft(14)} Back</button>
1280
+ <button class="medos-btn medos-btn-secondary" id="medos-btn-cancel">Cancel</button>
1281
+ <button class="medos-btn medos-btn-primary" id="medos-btn-continue" ${this.state.selectedPatient ? "" : "disabled"}>Continue</button>
841
1282
  </div>
842
1283
  `;
843
1284
  }
844
- renderStep4() {
845
- const canSubmit = this.state.patientName &&
1285
+ renderPatientDetailsStep() {
1286
+ const isFormValid = this.state.patientName &&
1287
+ this.state.patientAge &&
1288
+ this.state.patientGender &&
846
1289
  this.state.patientAddress &&
847
1290
  this.state.patientCity &&
848
1291
  this.state.patientState &&
849
1292
  this.state.patientCountry &&
850
- this.state.patientZipcode &&
851
- this.state.otpVerified;
1293
+ this.state.patientZipcode;
852
1294
  return `
1295
+ <!-- Patient Information Section -->
853
1296
  <div class="medos-section-card">
854
1297
  <div class="medos-section-header">
855
1298
  ${VanillaIcons.user(14)}
@@ -857,92 +1300,384 @@ class AppointmentCalendarWidget {
857
1300
  </div>
858
1301
  <div class="medos-section-body">
859
1302
  <div class="medos-form-row">
860
- <div class="medos-form-group medos-form-group-flex">
861
- <label class="medos-label">Full Name <span class="medos-required">*</span></label>
862
- <input type="text" class="medos-input" id="medos-patient-name" placeholder="Enter full name" value="${this.escapeHtml(this.state.patientName)}" />
1303
+ <div class="medos-form-field">
1304
+ <label class="medos-form-label">
1305
+ First Name <span style="color: #EF4444;">*</span>
1306
+ </label>
1307
+ <input
1308
+ type="text"
1309
+ class="medos-appointment-input"
1310
+ id="medos-patient-first-name"
1311
+ placeholder="Jane"
1312
+ value="${this.escapeHtml(this.state.patientName.split(" ")[0] || "")}"
1313
+ />
1314
+ </div>
1315
+ <div class="medos-form-field">
1316
+ <label class="medos-form-label">
1317
+ Last Name <span style="color: #EF4444;">*</span>
1318
+ </label>
1319
+ <input
1320
+ type="text"
1321
+ class="medos-appointment-input"
1322
+ id="medos-patient-last-name"
1323
+ placeholder="Doe"
1324
+ value="${this.escapeHtml(this.state.patientName.split(" ").slice(1).join(" ") || "")}"
1325
+ />
1326
+ </div>
1327
+ </div>
1328
+
1329
+ <div class="medos-form-row">
1330
+ <div class="medos-form-field">
1331
+ <label class="medos-form-label">
1332
+ Age <span style="color: #EF4444;">*</span>
1333
+ </label>
1334
+ <input
1335
+ type="number"
1336
+ class="medos-appointment-input"
1337
+ id="medos-patient-age"
1338
+ placeholder="25"
1339
+ min="0"
1340
+ max="120"
1341
+ value="${this.escapeHtml(this.state.patientAge)}"
1342
+ />
863
1343
  </div>
864
- <div class="medos-form-group medos-form-group-small">
865
- <label class="medos-label">Age</label>
866
- <input type="number" class="medos-input" id="medos-patient-age" placeholder="Age" value="${this.escapeHtml(this.state.patientAge)}" />
1344
+ <div class="medos-form-field">
1345
+ <label class="medos-form-label">Email</label>
1346
+ <input
1347
+ type="email"
1348
+ class="medos-appointment-input"
1349
+ id="medos-patient-email"
1350
+ placeholder="jane@example.com (optional)"
1351
+ value="${this.escapeHtml(this.state.patientEmail)}"
1352
+ />
867
1353
  </div>
868
1354
  </div>
1355
+
869
1356
  <div class="medos-form-row">
870
- <div class="medos-form-group medos-form-group-flex">
871
- <label class="medos-label">Email</label>
872
- <input type="email" class="medos-input" id="medos-patient-email" placeholder="patient@example.com" value="${this.escapeHtml(this.state.patientEmail)}" />
1357
+ <div class="medos-form-field">
1358
+ <label class="medos-form-label">
1359
+ Gender <span style="color: #EF4444;">*</span>
1360
+ </label>
1361
+ <div id="medos-gender-select-container"></div>
873
1362
  </div>
874
- <div class="medos-form-group medos-form-group-small">
875
- <label class="medos-label">Gender</label>
876
- <div id="medos-gender-container"></div>
1363
+ <div class="medos-form-field">
1364
+ <label class="medos-form-label">Blood Group</label>
1365
+ <div id="medos-blood-group-select-container"></div>
877
1366
  </div>
878
1367
  </div>
879
- <div class="medos-form-group">
880
- <label class="medos-label">Blood Group <span class="medos-optional">(optional)</span></label>
881
- <div id="medos-blood-group-container"></div>
1368
+
1369
+ <div class="medos-form-row">
1370
+ <div class="medos-form-field">
1371
+ <label class="medos-form-label">Date of Birth</label>
1372
+ <input
1373
+ type="date"
1374
+ class="medos-appointment-input"
1375
+ id="medos-patient-dob"
1376
+ value="${this.escapeHtml(this.state.patientDob)}"
1377
+ />
1378
+ ${this.state.patientDob &&
1379
+ this.state.patientDob.trim() !== "" &&
1380
+ !this.isValidDateOfBirth(this.state.patientDob)
1381
+ ? this.getDateOfBirthErrorMessage(this.state.patientDob)
1382
+ : ""}
1383
+ </div>
882
1384
  </div>
883
1385
  </div>
884
1386
  </div>
885
1387
 
1388
+ <!-- Address Information Section -->
886
1389
  <div class="medos-section-card">
887
1390
  <div class="medos-section-header">
888
1391
  ${VanillaIcons.mapPin(14)}
889
- <span class="medos-section-title">Address Details</span>
1392
+ <span class="medos-section-title">Address Information</span>
890
1393
  </div>
891
1394
  <div class="medos-section-body">
892
- <div class="medos-form-group">
893
- <label class="medos-label">Address Line 1 <span class="medos-required">*</span></label>
894
- <input type="text" class="medos-input" id="medos-patient-address" placeholder="Street address, building name, etc." value="${this.escapeHtml(this.state.patientAddress)}" />
1395
+ <div class="medos-form-field">
1396
+ <label class="medos-form-label">
1397
+ Address <span style="color: #EF4444;">*</span>
1398
+ </label>
1399
+ <input
1400
+ type="text"
1401
+ class="medos-appointment-input"
1402
+ id="medos-patient-address"
1403
+ placeholder="123 Main Street"
1404
+ value="${this.escapeHtml(this.state.patientAddress)}"
1405
+ />
895
1406
  </div>
1407
+
896
1408
  <div class="medos-form-row">
897
- <div class="medos-form-group">
898
- <label class="medos-label">City <span class="medos-required">*</span></label>
899
- <input type="text" class="medos-input" id="medos-patient-city" placeholder="City" value="${this.escapeHtml(this.state.patientCity)}" />
1409
+ <div class="medos-form-field">
1410
+ <label class="medos-form-label">
1411
+ City <span style="color: #EF4444;">*</span>
1412
+ </label>
1413
+ <input
1414
+ type="text"
1415
+ class="medos-appointment-input"
1416
+ id="medos-patient-city"
1417
+ placeholder="New York"
1418
+ value="${this.escapeHtml(this.state.patientCity)}"
1419
+ />
900
1420
  </div>
901
- <div class="medos-form-group">
902
- <label class="medos-label">State <span class="medos-required">*</span></label>
903
- <input type="text" class="medos-input" id="medos-patient-state" placeholder="State" value="${this.escapeHtml(this.state.patientState)}" />
1421
+ <div class="medos-form-field">
1422
+ <label class="medos-form-label">
1423
+ State <span style="color: #EF4444;">*</span>
1424
+ </label>
1425
+ <input
1426
+ type="text"
1427
+ class="medos-appointment-input"
1428
+ id="medos-patient-state"
1429
+ placeholder="NY"
1430
+ value="${this.escapeHtml(this.state.patientState)}"
1431
+ />
904
1432
  </div>
905
1433
  </div>
1434
+
906
1435
  <div class="medos-form-row">
907
- <div class="medos-form-group">
908
- <label class="medos-label">Country <span class="medos-required">*</span></label>
909
- <input type="text" class="medos-input" id="medos-patient-country" placeholder="Country" value="${this.escapeHtml(this.state.patientCountry)}" />
1436
+ <div class="medos-form-field">
1437
+ <label class="medos-form-label">
1438
+ Country <span style="color: #EF4444;">*</span>
1439
+ </label>
1440
+ <input
1441
+ type="text"
1442
+ class="medos-appointment-input"
1443
+ id="medos-patient-country"
1444
+ placeholder="United States"
1445
+ value="${this.escapeHtml(this.state.patientCountry)}"
1446
+ />
910
1447
  </div>
911
- <div class="medos-form-group">
912
- <label class="medos-label">Zipcode <span class="medos-required">*</span></label>
913
- <input type="text" class="medos-input" id="medos-patient-zipcode" placeholder="Zipcode" value="${this.escapeHtml(this.state.patientZipcode)}" />
1448
+ <div class="medos-form-field">
1449
+ <label class="medos-form-label">
1450
+ Zipcode <span style="color: #EF4444;">*</span>
1451
+ </label>
1452
+ <input
1453
+ type="text"
1454
+ class="medos-appointment-input"
1455
+ id="medos-patient-zipcode"
1456
+ placeholder="10001"
1457
+ value="${this.escapeHtml(this.state.patientZipcode)}"
1458
+ />
914
1459
  </div>
915
1460
  </div>
916
- <div class="medos-form-group">
917
- <label class="medos-label">Landmark <span class="medos-optional">(optional)</span></label>
918
- <input type="text" class="medos-input" id="medos-patient-landmark" placeholder="Nearby landmark" value="${this.escapeHtml(this.state.patientLandmark)}" />
1461
+
1462
+ <div class="medos-form-field" style="margin-top: 20px;">
1463
+ <label class="medos-form-label">Landmark</label>
1464
+ <input
1465
+ type="text"
1466
+ class="medos-appointment-input"
1467
+ id="medos-patient-landmark"
1468
+ placeholder="Near Central Park"
1469
+ value="${this.escapeHtml(this.state.patientLandmark)}"
1470
+ />
919
1471
  </div>
920
1472
  </div>
921
1473
  </div>
922
1474
 
923
1475
  <div class="medos-actions">
924
- <button class="medos-btn medos-btn-secondary" id="medos-btn-back">Back</button>
925
- <button class="medos-btn medos-btn-primary" id="medos-btn-submit" ${canSubmit && !this.state.loading ? "" : "disabled"}>${this.state.loading ? "Booking..." : "Book Appointment"}</button>
1476
+ <button class="medos-btn medos-btn-back" id="medos-btn-back">${VanillaIcons.arrowLeft(14)} Back</button>
1477
+ <button class="medos-btn medos-btn-secondary" id="medos-btn-cancel">Cancel</button>
1478
+ <button class="medos-btn medos-btn-primary" id="medos-btn-continue" ${isFormValid ? "" : "disabled"}>Continue</button>
1479
+ </div>
1480
+ `;
1481
+ }
1482
+ renderAppointmentSummaryStep() {
1483
+ const selectedDoctor = this.doctors.find((d) => d.id === this.state.selectedDoctor);
1484
+ const selectedAddress = this.state.addresses.find((addr) => addr.id === this.state.selectedAddress);
1485
+ const appointmentDate = this.state.selectedDate
1486
+ ? this.state.selectedDate.toLocaleDateString("en-US", {
1487
+ weekday: "short",
1488
+ year: "numeric",
1489
+ month: "short",
1490
+ day: "numeric",
1491
+ })
1492
+ : "";
1493
+ const startTime = this.state.selectedSlot?.start
1494
+ ? new Date(this.state.selectedSlot.start).toLocaleTimeString([], {
1495
+ hour: "2-digit",
1496
+ minute: "2-digit",
1497
+ })
1498
+ : "";
1499
+ const usingSessionPack = this.state.bookingOptionType === "session-pack" &&
1500
+ this.state.selectedSessionPack;
1501
+ const usingNewPackage = this.state.bookingOptionType === "explore-packages" &&
1502
+ this.state.selectedNewPackage;
1503
+ const consultationCharge = this.state.consultationMode === "ONLINE" ? 500 : 300;
1504
+ let totalAmount = consultationCharge;
1505
+ let sessionInfo = "";
1506
+ let dateOfExpiry = "";
1507
+ if (usingSessionPack) {
1508
+ totalAmount = 0;
1509
+ sessionInfo = `${this.state.selectedSessionPack?.remainingSessions} appointments now remaining`;
1510
+ dateOfExpiry = this.state.selectedSessionPack?.expiryDate || "";
1511
+ }
1512
+ else if (usingNewPackage) {
1513
+ totalAmount = this.state.selectedNewPackage?.price || 0;
1514
+ sessionInfo = `${this.state.selectedNewPackage?.totalSessions} appointments after purchase`;
1515
+ const validityDays = this.state.selectedNewPackage?.validityDays || 365;
1516
+ const expiryDate = new Date();
1517
+ expiryDate.setDate(expiryDate.getDate() + validityDays);
1518
+ dateOfExpiry = expiryDate.toLocaleDateString("en-US", {
1519
+ day: "numeric",
1520
+ month: "long",
1521
+ year: "numeric",
1522
+ });
1523
+ }
1524
+ const patientName = this.state.selectedPatient
1525
+ ? `${this.state.selectedPatient.firstName} ${this.state.selectedPatient.lastName}`
1526
+ : this.state.patientName || "Patient";
1527
+ return `
1528
+ <div class="medos-appointment-summary">
1529
+ <div class="medos-summary-header">
1530
+ <h3>Appointment Summary</h3>
1531
+ <button class="medos-close-btn" id="medos-btn-close">&times;</button>
1532
+ </div>
1533
+
1534
+ <div class="medos-summary-section">
1535
+ <label class="medos-form-label">REVIEW APPOINTMENT DETAILS</label>
1536
+
1537
+ <div class="medos-summary-grid">
1538
+ <div class="medos-summary-row">
1539
+ <span class="medos-summary-label">Patient:</span>
1540
+ <span class="medos-summary-value">${this.escapeHtml(patientName)}</span>
1541
+ </div>
1542
+ ${this.state.patientAge
1543
+ ? `<div class="medos-summary-row">
1544
+ <span class="medos-summary-label">Age:</span>
1545
+ <span class="medos-summary-value">${this.escapeHtml(this.state.patientAge)} years</span>
1546
+ </div>`
1547
+ : ""}
1548
+ ${this.state.bloodGroup
1549
+ ? `<div class="medos-summary-row">
1550
+ <span class="medos-summary-label">Blood Group:</span>
1551
+ <span class="medos-summary-value">${this.escapeHtml(this.state.bloodGroup)}</span>
1552
+ </div>`
1553
+ : ""}
1554
+ <div class="medos-summary-row">
1555
+ <span class="medos-summary-label">Doctor:</span>
1556
+ <span class="medos-summary-value">${selectedDoctor
1557
+ ? this.escapeHtml(selectedDoctor.name)
1558
+ : "Not selected"}</span>
1559
+ </div>
1560
+ ${selectedAddress
1561
+ ? `<div class="medos-summary-row">
1562
+ <span class="medos-summary-label">Location:</span>
1563
+ <span class="medos-summary-value">${this.escapeHtml(selectedAddress.label || "Unknown Location")}</span>
1564
+ </div>`
1565
+ : ""}
1566
+ <div class="medos-summary-row">
1567
+ <span class="medos-summary-label">Date & Time:</span>
1568
+ <span class="medos-summary-value">${appointmentDate}${startTime ? `, ${startTime}` : ""}</span>
1569
+ </div>
1570
+ <div class="medos-summary-row">
1571
+ <span class="medos-summary-label">Appointment Type:</span>
1572
+ <span class="medos-summary-value">${this.state.consultationMode === "ONLINE"
1573
+ ? "In Clinic Consultation"
1574
+ : "Online Consultation"}</span>
1575
+ </div>
1576
+ <div class="medos-summary-row">
1577
+ <span class="medos-summary-label">Service:</span>
1578
+ <span class="medos-summary-value">${usingSessionPack
1579
+ ? this.escapeHtml(this.state.selectedSessionPack?.name || "Session Pack")
1580
+ : usingNewPackage
1581
+ ? this.escapeHtml(this.state.selectedNewPackage?.name || "Package")
1582
+ : "Standard Consultation"}</span>
1583
+ </div>
1584
+ <div class="medos-summary-row">
1585
+ <span class="medos-summary-label">Sessions:</span>
1586
+ <span class="medos-summary-value">${sessionInfo || "1 consultation"}</span>
1587
+ </div>
1588
+ ${usingSessionPack || usingNewPackage
1589
+ ? `
1590
+ <div class="medos-summary-row">
1591
+ <span class="medos-summary-label">Date of Purchase:</span>
1592
+ <span class="medos-summary-value">${usingSessionPack
1593
+ ? this.state.selectedSessionPack?.purchaseDate ||
1594
+ "Previously"
1595
+ : "Today"}</span>
1596
+ </div>
1597
+ <div class="medos-summary-row">
1598
+ <span class="medos-summary-label">Date of Expiry:</span>
1599
+ <span class="medos-summary-value medos-expiry">${dateOfExpiry}</span>
1600
+ </div>
1601
+ `
1602
+ : ""}
1603
+ </div>
1604
+ </div>
1605
+
1606
+ <div class="medos-summary-total">
1607
+ <div class="medos-total-row">
1608
+ <span class="medos-total-label">Total Amount</span>
1609
+ <span class="medos-total-note">${usingSessionPack ? "Using credits from bundle sessions." : ""}</span>
1610
+ </div>
1611
+ <div class="medos-total-amount">₹${totalAmount.toLocaleString()}</div>
1612
+ </div>
1613
+
1614
+ <div class="medos-actions">
1615
+ <button class="medos-btn medos-btn-back" id="medos-btn-back">${VanillaIcons.arrowLeft(14)} Back</button>
1616
+ <button class="medos-btn medos-btn-secondary" id="medos-btn-cancel">Cancel</button>
1617
+ <button class="medos-btn medos-btn-primary" id="medos-btn-confirm">Confirm Book</button>
1618
+ </div>
926
1619
  </div>
927
1620
  `;
928
1621
  }
929
- renderSuccessIcon() {
1622
+ renderSuccessStep() {
1623
+ const duration = this.calculateDuration();
1624
+ const selectedDoctor = this.doctors.find((d) => d.id === this.state.selectedDoctor);
1625
+ const selectedAddress = this.state.addresses.find((addr) => addr.id === this.state.selectedAddress);
1626
+ const appointmentDate = this.state.selectedDate
1627
+ ? this.formatDate(formatDateToISO(this.state.selectedDate))
1628
+ : "";
1629
+ const startTime = this.state.selectedSlot?.start
1630
+ ? this.formatTime(new Date(this.state.selectedSlot.start).toTimeString().slice(0, 5))
1631
+ : "";
1632
+ const patientName = this.state.selectedPatient
1633
+ ? `${this.state.selectedPatient.firstName} ${this.state.selectedPatient.lastName}`
1634
+ : this.state.patientName || "Patient";
1635
+ const usingSessionPack = this.state.bookingOptionType === "session-pack" &&
1636
+ this.state.selectedSessionPack;
930
1637
  return `
931
- <div style="position: relative; display: inline-block;">
932
- <svg width="64" height="64" viewBox="0 0 41 41" fill="none">
933
- <path
934
- d="M31.1309 4.90254C32.388 4.98797 33.0166 5.03069 33.5247 5.25288C34.2598 5.57438 34.8467 6.16126 35.1682 6.8964C35.3904 7.40445 35.4331 8.03302 35.5185 9.29016L35.7135 12.159C35.748 12.6674 35.7653 12.9217 35.8206 13.1645C35.9004 13.5154 36.0391 13.8503 36.2308 14.1549C36.3634 14.3657 36.531 14.5576 36.8661 14.9416L38.7568 17.108C39.5853 18.0574 39.9996 18.532 40.2017 19.0484C40.4942 19.7955 40.4942 20.6255 40.2017 21.3727C39.9996 21.889 39.5853 22.3637 38.7568 23.313L36.8661 25.4795C36.531 25.8634 36.3634 26.0554 36.2308 26.2662C36.0391 26.5708 35.9004 26.9056 35.8206 27.2566C35.7653 27.4994 35.748 27.7536 35.7135 28.2621L35.5185 31.1309C35.4331 32.388 35.3904 33.0166 35.1682 33.5247C34.8467 34.2598 34.2598 34.8467 33.5247 35.1682C33.0166 35.3904 32.388 35.4331 31.1309 35.5185L28.2621 35.7135C27.7536 35.748 27.4994 35.7653 27.2566 35.8206C26.9056 35.9004 26.5708 36.0391 26.2662 36.2308C26.0554 36.3634 25.8634 36.531 25.4795 36.8661L23.313 38.7568C22.3637 39.5853 21.889 39.9996 21.3727 40.2017C20.6255 40.4942 19.7955 40.4942 19.0484 40.2017C18.532 39.9996 18.0574 39.5853 17.108 38.7568L14.9416 36.8661C14.5576 36.531 14.3657 36.3634 14.1549 36.2308C13.8503 36.0391 13.5154 35.9004 13.1645 35.8206C12.9217 35.7653 12.6674 35.748 12.159 35.7135L9.29016 35.5185C8.03302 35.4331 7.40445 35.3904 6.8964 35.1682C6.16126 34.8467 5.57438 34.2598 5.25288 33.5247C5.03069 33.0166 4.98797 32.388 4.90254 31.1309L4.70759 28.2621C4.67304 27.7536 4.65576 27.4994 4.60049 27.2566C4.52063 26.9056 4.38193 26.5708 4.19028 26.2662C4.05764 26.0554 3.89009 25.8634 3.555 25.4795L1.66428 23.313C0.83576 22.3637 0.421499 21.889 0.219363 21.3727C-0.073121 20.6255 -0.0731209 19.7955 0.219363 19.0484C0.421499 18.532 0.83576 18.0574 1.66428 17.108L3.555 14.9416C3.89009 14.5576 4.05764 14.3657 4.19027 14.1549C4.38193 13.8503 4.52063 13.5154 4.60049 13.1645C4.65576 12.9217 4.67304 12.6674 4.70759 12.159L4.90254 9.29016C4.98797 8.03302 5.03069 7.40445 5.25288 6.8964C5.57438 6.16126 6.16126 5.57438 6.8964 5.25288C7.40445 5.03069 8.03302 4.98797 9.29016 4.90254L12.159 4.70759C12.6674 4.67304 12.9217 4.65577 13.1645 4.6005C13.5154 4.52063 13.8503 4.38193 14.1549 4.19028C14.3657 4.05764 14.5576 3.89009 14.9416 3.555L17.108 1.66428C18.0574 0.83576 18.532 0.421499 19.0484 0.219363C19.7955 -0.073121 20.6255 -0.073121 21.3727 0.219363C21.889 0.421499 22.3637 0.83576 23.313 1.66428L25.4795 3.555C25.8634 3.89009 26.0554 4.05764 26.2662 4.19028C26.5708 4.38193 26.9056 4.52063 27.2566 4.6005C27.4994 4.65577 27.7536 4.67304 28.2621 4.70759L31.1309 4.90254Z"
935
- fill="#006E0F"
936
- />
937
- </svg>
938
- <svg width="16" height="12" viewBox="0 0 16 12" fill="none" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
939
- <path
940
- d="M5.472 11.544L0 6.072L1.368 4.704L5.472 8.808L14.28 0L15.648 1.368L5.472 11.544Z"
941
- fill="white"
942
- />
943
- </svg>
944
- </div>
945
- `;
1638
+ <div class="medos-success-container">
1639
+ <div class="medos-success-header">
1640
+ <h2>Appointment Confirmed</h2>
1641
+ </div>
1642
+
1643
+ <div class="medos-success-content">
1644
+ <div class="medos-success-badge">
1645
+ ${VanillaIcons.successBadge(64)}
1646
+ </div>
1647
+
1648
+ <h3 class="medos-success-title">Appointment Confirmed</h3>
1649
+
1650
+ <div class="medos-success-details">
1651
+ <h4>Appointment Details</h4>
1652
+ <div class="medos-details-text">
1653
+ <p><strong>Patient:</strong> ${this.escapeHtml(patientName)}</p>
1654
+ <p><strong>Type:</strong> ${this.state.consultationMode === "ONLINE"
1655
+ ? "In Clinic Consultation"
1656
+ : "Online"}</p>
1657
+ <p><strong>Date:</strong> ${appointmentDate}</p>
1658
+ <p><strong>Time:</strong> ${startTime}</p>
1659
+ <p><strong>Duration:</strong> ~${duration} minutes</p>
1660
+ ${selectedAddress?.label
1661
+ ? `<p><strong>Location:</strong> ${this.escapeHtml(selectedAddress.label)}</p>`
1662
+ : ""}
1663
+ </div>
1664
+ </div>
1665
+
1666
+ <p class="medos-confirmation-message">
1667
+ A confirmation message has been sent to the registered Phone Number.
1668
+ </p>
1669
+
1670
+ ${usingSessionPack
1671
+ ? `
1672
+ <div class="medos-session-remaining">
1673
+ <span class="medos-remaining-label">Sessions:</span>
1674
+ <span class="medos-remaining-value">${(this.state.selectedSessionPack?.remainingSessions || 1) - 1} appointments now remaining</span>
1675
+ </div>
1676
+ `
1677
+ : ""}
1678
+ </div>
1679
+ </div>
1680
+ `;
946
1681
  }
947
1682
  formatDate(dateStr) {
948
1683
  try {
@@ -980,66 +1715,6 @@ class AppointmentCalendarWidget {
980
1715
  }
981
1716
  return 60;
982
1717
  }
983
- renderStep5() {
984
- const duration = this.calculateDuration();
985
- const selectedAddress = this.state.addresses.find((addr) => addr.id === this.state.selectedAddress);
986
- const appointmentDate = this.state.selectedDate
987
- ? this.formatDate(formatDateToISO(this.state.selectedDate))
988
- : "";
989
- const startTime = this.state.selectedSlot?.start
990
- ? this.formatTime(new Date(this.state.selectedSlot.start).toTimeString().slice(0, 5))
991
- : "";
992
- return `
993
- <div style="display: flex; flex-direction: column; padding: 0; font-family: Arial, sans-serif; background: #f8f9fa; min-height: 500px;">
994
- <!-- Header with border -->
995
- <div style="padding: 20px 24px; font-size: 24px; font-weight: bold; color: #1a365d; border-bottom: 2px solid #e2e8f0; background: white;">
996
- Appointment Confirmed
997
- </div>
998
-
999
- <!-- Main content with border -->
1000
- <div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 24px; border: 2px solid #e2e8f0; border-top: none; background: white; text-align: center;">
1001
- <!-- Success Title -->
1002
- <h2 style="font-size: 20px; font-weight: 600; color: #006E0F; margin: 0 0 24px 0;">
1003
- Appointment Confirmed
1004
- </h2>
1005
-
1006
- <!-- Success Icon -->
1007
- <div style="margin-bottom: 32px;">
1008
- ${this.renderSuccessIcon()}
1009
- </div>
1010
-
1011
- <!-- Appointment Details -->
1012
- <div style="width: 100%; max-width: 500px; margin-bottom: 32px;">
1013
- <h3 style="font-size: 18px; font-weight: 600; color: #006E0F; margin-bottom: 24px;">
1014
- Appointment Details
1015
- </h3>
1016
-
1017
- <div style="display: flex; flex-direction: column; gap: 12px; font-size: 16px; line-height: 1.6; color: #4a5568;">
1018
- <div><strong style="color: #006E0F;">Patient:</strong> ${this.escapeHtml(this.state.patientName || "Patient")}</div>
1019
- <div><strong style="color: #006E0F;">Visitation Type:</strong> ${this.state.consultationMode === "ONLINE"
1020
- ? "Online Consultation"
1021
- : "General"}</div>
1022
- ${appointmentDate
1023
- ? `<div><strong style="color: #006E0F;">Date:</strong> ${appointmentDate}</div>`
1024
- : ""}
1025
- ${startTime
1026
- ? `<div><strong style="color: #006E0F;">Time:</strong> ${startTime}</div>`
1027
- : ""}
1028
- <div><strong style="color: #006E0F;">Duration:</strong> ~${duration} minutes</div>
1029
- ${selectedAddress?.label
1030
- ? `<div><strong style="color: #006E0F;">Location:</strong> ${this.escapeHtml(selectedAddress.label)}</div>`
1031
- : ""}
1032
- </div>
1033
- </div>
1034
-
1035
- <!-- Confirmation Message -->
1036
- <div style="font-size: 16px; font-style: italic; color: #718096; text-align: center; max-width: 600px; line-height: 1.5;">
1037
- A confirmation email has been sent to the Patient's Email address.
1038
- </div>
1039
- </div>
1040
- </div>
1041
- `;
1042
- }
1043
1718
  attachEventListeners() {
1044
1719
  const consultationRadios = this.container.querySelectorAll('input[name="consultationMode"]');
1045
1720
  consultationRadios.forEach((radio) => {
@@ -1049,7 +1724,7 @@ class AppointmentCalendarWidget {
1049
1724
  this.render();
1050
1725
  });
1051
1726
  });
1052
- const slotCards = this.container.querySelectorAll(".medos-slot-card");
1727
+ const slotCards = this.container.querySelectorAll(".medos-slot-card, .medos-slot-btn");
1053
1728
  slotCards.forEach((card) => {
1054
1729
  card.addEventListener("click", () => {
1055
1730
  const slotId = card.dataset.slotId;
@@ -1121,6 +1796,7 @@ class AppointmentCalendarWidget {
1121
1796
  const target = e.target;
1122
1797
  this.state.patientAge = target.value;
1123
1798
  this.updateSubmitButtonState();
1799
+ this.updatePatientDetailsButtonState();
1124
1800
  });
1125
1801
  }
1126
1802
  const patientEmailInput = this.container.querySelector("#medos-patient-email");
@@ -1131,19 +1807,13 @@ class AppointmentCalendarWidget {
1131
1807
  this.updateSubmitButtonState();
1132
1808
  });
1133
1809
  }
1134
- const patientGenderSelect = this.container.querySelector("#medos-patient-gender");
1135
- if (patientGenderSelect) {
1136
- patientGenderSelect.addEventListener("change", (e) => {
1137
- const target = e.target;
1138
- this.state.patientGender = target.value;
1139
- });
1140
- }
1141
1810
  const patientAddressInput = this.container.querySelector("#medos-patient-address");
1142
1811
  if (patientAddressInput) {
1143
1812
  patientAddressInput.addEventListener("input", (e) => {
1144
1813
  const target = e.target;
1145
1814
  this.state.patientAddress = target.value;
1146
1815
  this.updateSubmitButtonState();
1816
+ this.updatePatientDetailsButtonState();
1147
1817
  });
1148
1818
  }
1149
1819
  const patientCityInput = this.container.querySelector("#medos-patient-city");
@@ -1152,6 +1822,7 @@ class AppointmentCalendarWidget {
1152
1822
  const target = e.target;
1153
1823
  this.state.patientCity = target.value;
1154
1824
  this.updateSubmitButtonState();
1825
+ this.updatePatientDetailsButtonState();
1155
1826
  });
1156
1827
  }
1157
1828
  const patientStateInput = this.container.querySelector("#medos-patient-state");
@@ -1160,6 +1831,7 @@ class AppointmentCalendarWidget {
1160
1831
  const target = e.target;
1161
1832
  this.state.patientState = target.value;
1162
1833
  this.updateSubmitButtonState();
1834
+ this.updatePatientDetailsButtonState();
1163
1835
  });
1164
1836
  }
1165
1837
  const patientCountryInput = this.container.querySelector("#medos-patient-country");
@@ -1168,6 +1840,7 @@ class AppointmentCalendarWidget {
1168
1840
  const target = e.target;
1169
1841
  this.state.patientCountry = target.value;
1170
1842
  this.updateSubmitButtonState();
1843
+ this.updatePatientDetailsButtonState();
1171
1844
  });
1172
1845
  }
1173
1846
  const patientZipcodeInput = this.container.querySelector("#medos-patient-zipcode");
@@ -1176,6 +1849,7 @@ class AppointmentCalendarWidget {
1176
1849
  const target = e.target;
1177
1850
  this.state.patientZipcode = target.value;
1178
1851
  this.updateSubmitButtonState();
1852
+ this.updatePatientDetailsButtonState();
1179
1853
  });
1180
1854
  }
1181
1855
  const patientLandmarkInput = this.container.querySelector("#medos-patient-landmark");
@@ -1185,6 +1859,151 @@ class AppointmentCalendarWidget {
1185
1859
  this.state.patientLandmark = target.value;
1186
1860
  });
1187
1861
  }
1862
+ const patientFirstNameInput = this.container.querySelector("#medos-patient-first-name");
1863
+ if (patientFirstNameInput) {
1864
+ patientFirstNameInput.addEventListener("input", (e) => {
1865
+ const target = e.target;
1866
+ const lastName = this.state.patientName.split(" ").slice(1).join(" ") || "";
1867
+ this.state.patientName = `${target.value} ${lastName}`.trim();
1868
+ this.updatePatientDetailsButtonState();
1869
+ });
1870
+ }
1871
+ const patientLastNameInput = this.container.querySelector("#medos-patient-last-name");
1872
+ if (patientLastNameInput) {
1873
+ patientLastNameInput.addEventListener("input", (e) => {
1874
+ const target = e.target;
1875
+ const firstName = this.state.patientName.split(" ")[0] || "";
1876
+ this.state.patientName = `${firstName} ${target.value}`.trim();
1877
+ this.updatePatientDetailsButtonState();
1878
+ });
1879
+ }
1880
+ const patientDobInput = this.container.querySelector("#medos-patient-dob");
1881
+ if (patientDobInput) {
1882
+ patientDobInput.addEventListener("input", (e) => {
1883
+ const target = e.target;
1884
+ const dobValue = target.value;
1885
+ this.setState({ patientDob: dobValue });
1886
+ this.setState({ error: null });
1887
+ if (dobValue && dobValue.trim() !== "") {
1888
+ try {
1889
+ if (!this.isValidDateOfBirth(dobValue)) {
1890
+ target.classList.add("medos-input-error");
1891
+ const today = new Date();
1892
+ const inputDate = new Date(dobValue);
1893
+ if (inputDate > today) {
1894
+ target.title = "Date of birth cannot be in the future";
1895
+ }
1896
+ else if (!/^\d{4}-\d{2}-\d{2}$/.test(dobValue)) {
1897
+ target.title = "Please use YYYY-MM-DD format";
1898
+ }
1899
+ else {
1900
+ target.title = "Please enter a valid date";
1901
+ }
1902
+ }
1903
+ else {
1904
+ target.classList.remove("medos-input-error");
1905
+ target.title = "";
1906
+ }
1907
+ }
1908
+ catch (error) {
1909
+ console.warn("Date validation error:", error);
1910
+ target.classList.add("medos-input-error");
1911
+ target.title = "Date validation failed";
1912
+ }
1913
+ }
1914
+ else {
1915
+ target.classList.remove("medos-input-error");
1916
+ target.title = "";
1917
+ }
1918
+ this.updatePatientDetailsButtonState();
1919
+ this.updateSubmitButtonState();
1920
+ });
1921
+ patientDobInput.addEventListener("blur", (e) => {
1922
+ const target = e.target;
1923
+ const dobValue = target.value;
1924
+ if (dobValue && dobValue.trim() !== "") {
1925
+ try {
1926
+ if (!this.isValidDateOfBirth(dobValue)) {
1927
+ target.classList.add("medos-input-error");
1928
+ const today = new Date();
1929
+ const inputDate = new Date(dobValue);
1930
+ if (inputDate > today) {
1931
+ this.displayFieldValidationError("dateOfBirth", "future");
1932
+ }
1933
+ else if (!/^\d{4}-\d{2}-\d{2}$/.test(dobValue)) {
1934
+ this.displayFieldValidationError("dateOfBirth", "format");
1935
+ }
1936
+ else {
1937
+ this.displayFieldValidationError("dateOfBirth", "invalid");
1938
+ }
1939
+ }
1940
+ }
1941
+ catch (error) {
1942
+ console.warn("Date validation error on blur:", error);
1943
+ this.displayFieldValidationError("dateOfBirth", "invalid");
1944
+ }
1945
+ }
1946
+ });
1947
+ }
1948
+ const optionCards = this.container.querySelectorAll(".medos-option-card");
1949
+ optionCards.forEach((card) => {
1950
+ card.addEventListener("click", () => {
1951
+ const option = card.dataset.option;
1952
+ if (option === "new-appointment" ||
1953
+ option === "session-pack" ||
1954
+ option === "explore-packages") {
1955
+ this.state.bookingOptionType = option;
1956
+ this.render();
1957
+ }
1958
+ });
1959
+ });
1960
+ const packCards = this.container.querySelectorAll(".medos-session-pack-card");
1961
+ packCards.forEach((card) => {
1962
+ card.addEventListener("click", () => {
1963
+ const packId = Number.parseInt(card.dataset.packId || "0", 10);
1964
+ const pack = this.state.userSessionPacks.find((p) => p.id === packId);
1965
+ if (pack) {
1966
+ this.state.selectedSessionPack = pack;
1967
+ this.state.bookingOptionType = "session-pack";
1968
+ this.render();
1969
+ }
1970
+ });
1971
+ });
1972
+ const packageCards = this.container.querySelectorAll(".medos-package-card");
1973
+ packageCards.forEach((card) => {
1974
+ card.addEventListener("click", () => {
1975
+ const packageId = parseInt(card.dataset.packageId || "0", 10);
1976
+ const pkg = this.state.availablePackages.length > 0
1977
+ ? this.state.availablePackages.find((p) => p.id === packageId)
1978
+ : {
1979
+ id: packageId,
1980
+ name: packageId === 1 ? "Silver Package" : "Gold Package",
1981
+ totalSessions: packageId === 1 ? 3 : 5,
1982
+ price: packageId === 1 ? 5000 : 8000,
1983
+ validityDays: packageId === 1 ? 180 : 365,
1984
+ applicableOnline: true,
1985
+ applicableOffline: true,
1986
+ };
1987
+ if (pkg) {
1988
+ this.state.selectedNewPackage = pkg;
1989
+ this.render();
1990
+ }
1991
+ });
1992
+ });
1993
+ const patientCards = this.container.querySelectorAll(".medos-patient-card");
1994
+ patientCards.forEach((card) => {
1995
+ card.addEventListener("click", () => {
1996
+ const patientId = parseInt(card.dataset.patientId || "0", 10);
1997
+ const patients = this.state.verifiedPatients.length > 0
1998
+ ? this.state.verifiedPatients
1999
+ : this.getPlaceholderPatients();
2000
+ const patient = patients.find((p) => p.id === patientId);
2001
+ if (patient) {
2002
+ this.state.selectedPatient = patient;
2003
+ this.render();
2004
+ }
2005
+ });
2006
+ });
1188
2007
  const nextBtn = this.container.querySelector("#medos-btn-next");
1189
2008
  if (nextBtn) {
1190
2009
  nextBtn.addEventListener("click", () => {
@@ -1193,10 +2012,22 @@ class AppointmentCalendarWidget {
1193
2012
  }
1194
2013
  });
1195
2014
  }
2015
+ const continueBtn = this.container.querySelector("#medos-btn-continue");
2016
+ if (continueBtn) {
2017
+ continueBtn.addEventListener("click", () => {
2018
+ if (!continueBtn.disabled) {
2019
+ this.goToNext();
2020
+ }
2021
+ });
2022
+ }
1196
2023
  const backBtn = this.container.querySelector("#medos-btn-back");
1197
2024
  if (backBtn) {
1198
2025
  backBtn.addEventListener("click", () => this.goBack());
1199
2026
  }
2027
+ const cancelBtn = this.container.querySelector("#medos-btn-cancel");
2028
+ if (cancelBtn) {
2029
+ cancelBtn.addEventListener("click", () => this.reset());
2030
+ }
1200
2031
  const sendOtpBtn = this.container.querySelector("#medos-btn-send-otp");
1201
2032
  if (sendOtpBtn) {
1202
2033
  sendOtpBtn.addEventListener("click", () => {
@@ -1226,6 +2057,14 @@ class AppointmentCalendarWidget {
1226
2057
  this.sendOtp();
1227
2058
  });
1228
2059
  }
2060
+ const confirmBtn = this.container.querySelector("#medos-btn-confirm");
2061
+ if (confirmBtn) {
2062
+ confirmBtn.addEventListener("click", () => {
2063
+ if (!confirmBtn.disabled) {
2064
+ this.submitAppointment();
2065
+ }
2066
+ });
2067
+ }
1229
2068
  const submitBtn = this.container.querySelector("#medos-btn-submit");
1230
2069
  if (submitBtn) {
1231
2070
  submitBtn.addEventListener("click", () => {
@@ -1238,12 +2077,194 @@ class AppointmentCalendarWidget {
1238
2077
  if (bookAnotherBtn) {
1239
2078
  bookAnotherBtn.addEventListener("click", () => this.reset());
1240
2079
  }
2080
+ const addPatientBtn = this.container.querySelector("#medos-btn-add-patient");
2081
+ if (addPatientBtn) {
2082
+ addPatientBtn.addEventListener("click", () => {
2083
+ this.state.selectedPatient = null;
2084
+ this.state.useExistingPatient = false;
2085
+ this.goToNext();
2086
+ });
2087
+ }
2088
+ }
2089
+ getPlaceholderPatients() {
2090
+ return [
2091
+ {
2092
+ id: 1,
2093
+ firstName: "Mumma",
2094
+ lastName: "Bear",
2095
+ email: "mumma@example.com",
2096
+ countryCode: "+91",
2097
+ phoneNumber: this.state.patientPhone,
2098
+ dob: "1970-01-01",
2099
+ age: 55,
2100
+ gender: "FEMALE",
2101
+ bloodGroup: "O+",
2102
+ mrn: "MRN001",
2103
+ address: {
2104
+ id: 1,
2105
+ completeAddress: "123 Main St",
2106
+ addressLine1: "123 Main St",
2107
+ addressLine2: "",
2108
+ city: "Mumbai",
2109
+ state: "Maharashtra",
2110
+ country: "India",
2111
+ zipcode: "400001",
2112
+ landmark: "",
2113
+ phoneNumber: this.state.patientPhone,
2114
+ latitude: 0,
2115
+ longitude: 0,
2116
+ },
2117
+ },
2118
+ {
2119
+ id: 2,
2120
+ firstName: "Papa",
2121
+ lastName: "Bear",
2122
+ email: "papa@example.com",
2123
+ countryCode: "+91",
2124
+ phoneNumber: this.state.patientPhone,
2125
+ dob: "1968-01-01",
2126
+ age: 57,
2127
+ gender: "MALE",
2128
+ bloodGroup: "B+",
2129
+ mrn: "MRN002",
2130
+ address: {
2131
+ id: 2,
2132
+ completeAddress: "123 Main St",
2133
+ addressLine1: "123 Main St",
2134
+ addressLine2: "",
2135
+ city: "Mumbai",
2136
+ state: "Maharashtra",
2137
+ country: "India",
2138
+ zipcode: "400001",
2139
+ landmark: "",
2140
+ phoneNumber: this.state.patientPhone,
2141
+ latitude: 0,
2142
+ longitude: 0,
2143
+ },
2144
+ },
2145
+ ];
1241
2146
  }
1242
2147
  escapeHtml(text) {
1243
2148
  const div = document.createElement("div");
1244
2149
  div.textContent = text;
1245
2150
  return div.innerHTML;
1246
2151
  }
2152
+ isValidDateOfBirth(dob) {
2153
+ try {
2154
+ return validateDateOfBirth(dob);
2155
+ }
2156
+ catch (error) {
2157
+ console.warn("Date of birth validation error:", error);
2158
+ return false;
2159
+ }
2160
+ }
2161
+ isValidBloodGroup(bloodGroup) {
2162
+ try {
2163
+ return validateBloodGroup(bloodGroup);
2164
+ }
2165
+ catch (error) {
2166
+ console.warn("Blood group validation error:", error);
2167
+ return false;
2168
+ }
2169
+ }
2170
+ safeMapBloodGroupToApi(bloodGroup) {
2171
+ try {
2172
+ if (!bloodGroup) {
2173
+ return "UNKNOWN";
2174
+ }
2175
+ if (!this.isValidBloodGroup(bloodGroup)) {
2176
+ console.warn(`Invalid blood group provided: ${bloodGroup}, using UNKNOWN`);
2177
+ return "UNKNOWN";
2178
+ }
2179
+ const mapped = mapBloodGroupToApi(bloodGroup);
2180
+ if (!mapped || mapped === "") {
2181
+ console.warn(`Blood group mapping failed for: ${bloodGroup}, using UNKNOWN`);
2182
+ return "UNKNOWN";
2183
+ }
2184
+ return mapped;
2185
+ }
2186
+ catch (error) {
2187
+ console.error("Blood group mapping error:", error);
2188
+ return "UNKNOWN";
2189
+ }
2190
+ }
2191
+ safeFormatDateOfBirth(dob) {
2192
+ try {
2193
+ if (!dob) {
2194
+ return undefined;
2195
+ }
2196
+ if (!this.isValidDateOfBirth(dob)) {
2197
+ console.warn(`Invalid date of birth provided: ${dob}, excluding from payload`);
2198
+ return undefined;
2199
+ }
2200
+ return dob;
2201
+ }
2202
+ catch (error) {
2203
+ console.error("Date of birth formatting error:", error);
2204
+ return undefined;
2205
+ }
2206
+ }
2207
+ getDateOfBirthErrorMessage(dobValue) {
2208
+ try {
2209
+ const today = new Date();
2210
+ const inputDate = new Date(dobValue);
2211
+ if (inputDate > today) {
2212
+ return '<div class="medos-validation-error">Date of birth cannot be in the future</div>';
2213
+ }
2214
+ else if (!/^\d{4}-\d{2}-\d{2}$/.test(dobValue)) {
2215
+ return '<div class="medos-validation-error">Please use YYYY-MM-DD format</div>';
2216
+ }
2217
+ else {
2218
+ return '<div class="medos-validation-error">Please enter a valid date of birth</div>';
2219
+ }
2220
+ }
2221
+ catch (error) {
2222
+ console.warn("Error generating date of birth error message:", error);
2223
+ return '<div class="medos-validation-error">Please enter a valid date of birth</div>';
2224
+ }
2225
+ }
2226
+ displayFieldValidationError(fieldName, errorType) {
2227
+ let errorMessage = "";
2228
+ switch (fieldName) {
2229
+ case "bloodGroup":
2230
+ switch (errorType) {
2231
+ case "invalid":
2232
+ errorMessage =
2233
+ "Please select a valid blood group from the dropdown or leave it empty.";
2234
+ break;
2235
+ case "mapping":
2236
+ errorMessage =
2237
+ "Blood group selection failed. Please try selecting again or leave it empty.";
2238
+ break;
2239
+ default:
2240
+ errorMessage =
2241
+ "Blood group validation failed. Please check your selection.";
2242
+ }
2243
+ break;
2244
+ case "dateOfBirth":
2245
+ switch (errorType) {
2246
+ case "invalid":
2247
+ errorMessage =
2248
+ "Please enter a valid date of birth in YYYY-MM-DD format or leave it empty.";
2249
+ break;
2250
+ case "future":
2251
+ errorMessage =
2252
+ "Date of birth cannot be in the future. Please enter a valid past date.";
2253
+ break;
2254
+ case "format":
2255
+ errorMessage =
2256
+ "Please enter date of birth in the correct format (YYYY-MM-DD).";
2257
+ break;
2258
+ default:
2259
+ errorMessage =
2260
+ "Date of birth validation failed. Please check the date format.";
2261
+ }
2262
+ break;
2263
+ default:
2264
+ errorMessage = `Validation failed for ${fieldName}. Please check your input.`;
2265
+ }
2266
+ this.setState({ error: errorMessage });
2267
+ }
1247
2268
  destroy() {
1248
2269
  this.mounted = false;
1249
2270
  this.container.innerHTML = "";