medos-sdk 1.0.2 → 1.0.3

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 (94) hide show
  1. package/dist/client/MedosClient.d.ts +1 -1
  2. package/dist/client/MedosClient.js +1 -1
  3. package/dist/components/AppointmentCalender.d.ts +1 -4
  4. package/dist/components/AppointmentCalender.js +282 -530
  5. package/dist/components/AppointmentDateTimeModal.d.ts +14 -0
  6. package/dist/components/AppointmentDateTimeModal.js +206 -0
  7. package/dist/components/ConfigurableCard.d.ts +12 -0
  8. package/dist/components/ConfigurableCard.js +29 -0
  9. package/dist/components/DoctorSelectModal.d.ts +7 -0
  10. package/dist/components/DoctorSelectModal.js +80 -0
  11. package/dist/components/Icons/Check.d.ts +6 -0
  12. package/dist/components/Icons/Check.js +2 -0
  13. package/dist/components/Icons/ChevronDownIcon.d.ts +4 -0
  14. package/dist/components/Icons/ChevronDownIcon.js +2 -0
  15. package/dist/components/Icons/ChevronLeft.d.ts +3 -0
  16. package/dist/components/Icons/ChevronLeft.js +3 -0
  17. package/dist/components/Icons/ChevronRight.d.ts +3 -0
  18. package/dist/components/Icons/ChevronRight.js +3 -0
  19. package/dist/components/Icons/ConfirmationCheck.d.ts +1 -0
  20. package/dist/components/Icons/ConfirmationCheck.js +9 -0
  21. package/dist/components/Icons/ConsultationType.d.ts +1 -0
  22. package/dist/components/Icons/ConsultationType.js +2 -0
  23. package/dist/components/Icons/Date&TimeIcon.d.ts +1 -0
  24. package/dist/components/Icons/Date&TimeIcon.js +2 -0
  25. package/dist/components/Icons/MapIcon.d.ts +1 -0
  26. package/dist/components/Icons/MapIcon.js +2 -0
  27. package/dist/components/Icons/PaymentMethodIcon.d.ts +1 -0
  28. package/dist/components/Icons/PaymentMethodIcon.js +2 -0
  29. package/dist/components/Icons/UserIcon.d.ts +1 -0
  30. package/dist/components/Icons/UserIcon.js +2 -0
  31. package/dist/components/PatientDetailsStep.d.ts +3 -0
  32. package/dist/components/PatientDetailsStep.js +76 -0
  33. package/dist/components/PhoneVerificationStep.d.ts +3 -0
  34. package/dist/components/PhoneVerificationStep.js +39 -0
  35. package/dist/components/SuccessStep.d.ts +3 -0
  36. package/dist/components/SuccessStep.js +17 -0
  37. package/dist/components/custom-calendar.d.ts +5 -0
  38. package/dist/components/custom-calendar.js +153 -0
  39. package/dist/components/styles.d.ts +6 -0
  40. package/dist/components/styles.js +257 -0
  41. package/dist/components/types.d.ts +182 -0
  42. package/dist/components/types.js +55 -0
  43. package/dist/components/ui/select.d.ts +10 -0
  44. package/dist/components/ui/select.js +21 -0
  45. package/dist/components/uiComponents/SelectDropdown.d.ts +41 -0
  46. package/dist/components/uiComponents/SelectDropdown.js +302 -0
  47. package/dist/components/utils.d.ts +5 -0
  48. package/dist/components/utils.js +15 -0
  49. package/dist/components/validation.d.ts +2 -0
  50. package/dist/components/validation.js +7 -0
  51. package/dist/context/TemplateContext.d.ts +12 -0
  52. package/dist/context/TemplateContext.js +19 -0
  53. package/dist/lib/templateUtils.d.ts +3 -0
  54. package/dist/lib/templateUtils.js +28 -0
  55. package/dist/services/AppointmentService.d.ts +4 -5
  56. package/dist/services/AppointmentService.js +12 -10
  57. package/dist/templates/registry.d.ts +12 -0
  58. package/dist/templates/registry.js +58 -0
  59. package/dist/vanilla/AppointmentCalendarWidget.d.ts +2 -34
  60. package/dist/vanilla/AppointmentCalendarWidget.js +264 -273
  61. package/dist/vanilla/client/MedosClient.d.ts +1 -1
  62. package/dist/vanilla/components/AppointmentCalender.d.ts +1 -4
  63. package/dist/vanilla/components/AppointmentDateTimeModal.d.ts +14 -0
  64. package/dist/vanilla/components/ConfigurableCard.d.ts +12 -0
  65. package/dist/vanilla/components/DoctorSelectModal.d.ts +7 -0
  66. package/dist/vanilla/components/Icons/Check.d.ts +6 -0
  67. package/dist/vanilla/components/Icons/ChevronDownIcon.d.ts +4 -0
  68. package/dist/vanilla/components/Icons/ChevronLeft.d.ts +3 -0
  69. package/dist/vanilla/components/Icons/ChevronRight.d.ts +3 -0
  70. package/dist/vanilla/components/Icons/ConfirmationCheck.d.ts +1 -0
  71. package/dist/vanilla/components/Icons/ConsultationType.d.ts +1 -0
  72. package/dist/vanilla/components/Icons/Date&TimeIcon.d.ts +1 -0
  73. package/dist/vanilla/components/Icons/MapIcon.d.ts +1 -0
  74. package/dist/vanilla/components/Icons/PaymentMethodIcon.d.ts +1 -0
  75. package/dist/vanilla/components/Icons/UserIcon.d.ts +1 -0
  76. package/dist/vanilla/components/PatientDetailsStep.d.ts +3 -0
  77. package/dist/vanilla/components/PhoneVerificationStep.d.ts +3 -0
  78. package/dist/vanilla/components/SuccessStep.d.ts +3 -0
  79. package/dist/vanilla/components/custom-calendar.d.ts +5 -0
  80. package/dist/vanilla/components/styles.d.ts +6 -0
  81. package/dist/vanilla/components/types.d.ts +182 -0
  82. package/dist/vanilla/components/ui/select.d.ts +10 -0
  83. package/dist/vanilla/components/uiComponents/SelectDropdown.d.ts +41 -0
  84. package/dist/vanilla/components/utils.d.ts +5 -0
  85. package/dist/vanilla/components/validation.d.ts +2 -0
  86. package/dist/vanilla/context/TemplateContext.d.ts +12 -0
  87. package/dist/vanilla/lib/templateUtils.d.ts +3 -0
  88. package/dist/vanilla/services/AppointmentService.d.ts +4 -5
  89. package/dist/vanilla/templates/alternative.css +13 -0
  90. package/dist/vanilla/templates/default.css +13 -0
  91. package/dist/vanilla/templates/registry.d.ts +12 -0
  92. package/dist/vanilla/vanilla/AppointmentCalendarWidget.d.ts +2 -34
  93. package/dist/vanilla/widget.js +331 -283
  94. package/package.json +9 -4
@@ -3885,7 +3885,7 @@
3885
3885
  };
3886
3886
 
3887
3887
  class MedosClient {
3888
- static async init({ apiKey, baseURL = "https://api-dev.medapi.in/v1", }) {
3888
+ static async init({ apiKey, baseURL = "https://api.medos.one", }) {
3889
3889
  if (!apiKey) {
3890
3890
  throw new Error("MedosClient.init() requires 'apiKey'");
3891
3891
  }
@@ -4072,18 +4072,19 @@
4072
4072
  .map((ad) => {
4073
4073
  const addr = ad.address;
4074
4074
  const doctors = (ad.doctors || []).map((d) => ({
4075
- id: String(d.id ?? ""),
4076
- name: `${d.firstName || ""} ${d.lastName || ""}`.trim() || "Doctor",
4077
- email: d.email,
4078
- gender: d.gender,
4079
- countryCode: d.countryCode,
4080
- phoneNumber: d.phoneNumber,
4081
- dob: d.dob,
4082
- platform: d.platform,
4083
- isKycCompleted: d.isKycCompleted,
4075
+ id: Number(d.user.id),
4076
+ name: `${d.user.firstName || ""} ${d.user.lastName || ""}`.trim() ||
4077
+ "Doctor",
4078
+ email: d.user.email,
4079
+ gender: d.user.gender,
4080
+ countryCode: d.user.countryCode,
4081
+ phoneNumber: d.user.phoneNumber,
4082
+ dob: d.user.dob,
4083
+ platform: d.user.platform,
4084
+ isKycCompleted: d.user.isKycCompleted,
4084
4085
  }));
4085
4086
  return {
4086
- id: String(addr.id ?? ""),
4087
+ id: Number(addr.id),
4087
4088
  completeAddress: addr.completeAddress,
4088
4089
  addressLine1: addr.addressLine1,
4089
4090
  addressLine2: addr.addressLine2,
@@ -4109,6 +4110,7 @@
4109
4110
  },
4110
4111
  async fetchSlots(workspaceId, addressId, doctorId, appointmentDate) {
4111
4112
  const client = await MedosClient.ensureInitialized();
4113
+ console.log("fetching slots", workspaceId, addressId, doctorId, appointmentDate);
4112
4114
  const res = await client.get(`/appointments/available-slots`, {
4113
4115
  params: {
4114
4116
  workspaceId,
@@ -4169,40 +4171,68 @@
4169
4171
  },
4170
4172
  };
4171
4173
 
4174
+ const INITIAL_STATE = {
4175
+ step: 0,
4176
+ loading: false,
4177
+ error: null,
4178
+ workspaceId: null,
4179
+ addresses: [],
4180
+ addressDoctorsMap: {},
4181
+ selectedAddress: null,
4182
+ selectedDoctor: null,
4183
+ selectedDate: new Date(),
4184
+ slots: [],
4185
+ selectedSlot: null,
4186
+ consultationMode: "OFFLINE",
4187
+ consultationCharge: "",
4188
+ patientName: "",
4189
+ patientAge: "",
4190
+ patientEmail: "",
4191
+ patientGender: "",
4192
+ bloodGroup: "",
4193
+ patientAddress: "",
4194
+ patientCity: "",
4195
+ patientState: "",
4196
+ patientCountry: "",
4197
+ patientZipcode: "",
4198
+ patientLandmark: "",
4199
+ countryCode: "+91",
4200
+ patientPhone: "",
4201
+ otpCode: "",
4202
+ otpSent: false,
4203
+ otpVerified: false,
4204
+ otpSending: false,
4205
+ otpVerifying: false,
4206
+ };
4207
+
4208
+ const validatePhoneNumber = (phone) => {
4209
+ const cleaned = phone.replace(/\D/g, "");
4210
+ return cleaned.length >= 7 && cleaned.length <= 15;
4211
+ };
4212
+ const validateCountryCode = (code) => {
4213
+ return /^\+[1-9]\d{0,3}$/.test(code);
4214
+ };
4215
+
4216
+ const formatDateToISO = (date) => {
4217
+ const year = String(date.getFullYear()).padStart(4, "0");
4218
+ const month = String(date.getMonth() + 1).padStart(2, "0");
4219
+ const day = String(date.getDate()).padStart(2, "0");
4220
+ return `${year}-${month}-${day}`;
4221
+ };
4222
+ const parsePatientName = (fullName) => {
4223
+ const nameParts = fullName.trim().split(/\s+/);
4224
+ const firstName = nameParts[0] || "Patient";
4225
+ const lastName = nameParts.slice(1).join(" ") || "";
4226
+ return {
4227
+ firstName,
4228
+ lastName,
4229
+ };
4230
+ };
4231
+
4172
4232
  class AppointmentCalendarWidget {
4173
4233
  constructor(container, options) {
4174
4234
  this.mounted = true;
4175
- this.step = 0;
4176
- this.addresses = [];
4177
- this.addressDoctorsMap = {};
4178
- this.selectedAddress = null;
4179
- this.workspaceId = null;
4180
4235
  this.doctors = [];
4181
- this.selectedDoctor = null;
4182
- this.date = "";
4183
- this.slots = [];
4184
- this.selectedSlot = null;
4185
- this.loading = false;
4186
- this.error = null;
4187
- this.patientName = "";
4188
- this.patientAge = "";
4189
- this.patientAddress = "";
4190
- this.patientCity = "";
4191
- this.patientState = "";
4192
- this.patientCountry = "";
4193
- this.patientZipcode = "";
4194
- this.patientLandmark = "";
4195
- this.patientEmail = "";
4196
- this.patientGender = "";
4197
- this.problemFacing = "";
4198
- this.consultationCharge = "";
4199
- this.countryCode = "+91";
4200
- this.patientPhone = "";
4201
- this.otpCode = "";
4202
- this.otpSent = false;
4203
- this.otpVerified = false;
4204
- this.otpSending = false;
4205
- this.otpVerifying = false;
4206
4236
  if (typeof container === "string") {
4207
4237
  const el = document.getElementById(container);
4208
4238
  if (!el) {
@@ -4214,6 +4244,7 @@
4214
4244
  this.container = container;
4215
4245
  }
4216
4246
  this.options = options;
4247
+ this.state = { ...INITIAL_STATE };
4217
4248
  this.init();
4218
4249
  }
4219
4250
  async init() {
@@ -4236,19 +4267,18 @@
4236
4267
  this.render();
4237
4268
  }
4238
4269
  async loadAddresses() {
4239
- this.setLoading(true);
4240
- this.setError(null);
4270
+ this.setState({ loading: true, error: null });
4241
4271
  try {
4242
4272
  const addrResp = await AppointmentService.getAddresses();
4243
4273
  if (addrResp && Array.isArray(addrResp.addresses)) {
4244
4274
  const fetchedAddresses = addrResp.addresses;
4245
4275
  if (addrResp.workspaceId) {
4246
- this.workspaceId = addrResp.workspaceId;
4276
+ this.state.workspaceId = addrResp.workspaceId;
4247
4277
  }
4248
4278
  if (fetchedAddresses.length > 0) {
4249
4279
  const addrMap = {};
4250
4280
  const mappedAddrs = fetchedAddresses.map((a, idx) => {
4251
- const id = String(a.id ?? idx);
4281
+ const id = Number(a.id ?? idx);
4252
4282
  const label = a.completeAddress ?? a.label ?? a.address ?? `Address ${idx + 1}`;
4253
4283
  const docs = Array.isArray(a.doctors)
4254
4284
  ? a.doctors
@@ -4256,233 +4286,242 @@
4256
4286
  addrMap[id] = docs || [];
4257
4287
  return { id, label };
4258
4288
  });
4259
- this.addresses = mappedAddrs;
4260
- this.addressDoctorsMap = addrMap;
4289
+ this.state.addresses = mappedAddrs;
4290
+ this.state.addressDoctorsMap = addrMap;
4261
4291
  const anyDoctorsExist = Object.values(addrMap).some((arr) => Array.isArray(arr) && arr.length > 0);
4262
4292
  if (mappedAddrs.length === 1) {
4263
4293
  const only = mappedAddrs[0];
4264
- this.selectedAddress = only.id;
4294
+ this.state.selectedAddress = only.id;
4265
4295
  const docsForAddr = addrMap[only.id] || [];
4266
4296
  if (docsForAddr.length > 0) {
4267
4297
  this.doctors = docsForAddr;
4268
4298
  if (docsForAddr.length === 1) {
4269
- this.selectedDoctor = docsForAddr[0].id;
4270
- this.step = 1;
4299
+ this.state.selectedDoctor = docsForAddr[0].id;
4300
+ this.state.step = 1;
4271
4301
  }
4272
4302
  else {
4273
- this.step = 0;
4303
+ this.state.step = 0;
4274
4304
  }
4275
4305
  }
4276
4306
  else {
4277
4307
  if (anyDoctorsExist) {
4278
- this.setError("No doctors at this address. Please choose a different address.");
4308
+ this.setState({
4309
+ error: "No doctors at this address. Please choose a different address.",
4310
+ });
4279
4311
  this.doctors = [];
4280
- this.step = 0;
4312
+ this.state.step = 0;
4281
4313
  }
4282
4314
  else {
4283
- this.setError("No doctors available for the selected location(s).");
4315
+ this.setState({
4316
+ error: "No doctors available for the selected location(s).",
4317
+ });
4284
4318
  this.doctors = [];
4285
- this.step = 0;
4319
+ this.state.step = 0;
4286
4320
  }
4287
4321
  }
4288
4322
  }
4289
4323
  else {
4290
- this.step = 0;
4324
+ this.state.step = 0;
4291
4325
  }
4292
4326
  }
4293
4327
  else {
4294
- this.setError("No addresses or doctors available.");
4295
- this.addresses = [];
4296
- this.addressDoctorsMap = {};
4328
+ this.setState({ error: "No addresses or doctors available." });
4329
+ this.state.addresses = [];
4330
+ this.state.addressDoctorsMap = {};
4297
4331
  this.doctors = [];
4298
- this.step = 0;
4332
+ this.state.step = 0;
4299
4333
  }
4300
4334
  }
4301
4335
  }
4302
4336
  catch (e) {
4303
4337
  const msg = e.message || "Failed to load addresses";
4304
- this.setError(msg);
4338
+ this.setState({ error: msg });
4305
4339
  this.options.onError?.(e);
4306
4340
  }
4307
4341
  finally {
4308
- this.setLoading(false);
4342
+ this.setState({ loading: false });
4309
4343
  }
4310
4344
  }
4311
4345
  async handleAddressChange(addressId) {
4312
- this.selectedAddress = addressId;
4346
+ this.state.selectedAddress = addressId;
4313
4347
  if (!addressId) {
4314
4348
  this.doctors = [];
4315
- this.selectedDoctor = null;
4349
+ this.state.selectedDoctor = null;
4316
4350
  this.render();
4317
4351
  return;
4318
4352
  }
4319
- this.setLoading(true);
4320
- this.setError(null);
4353
+ this.setState({ loading: true, error: null });
4321
4354
  try {
4322
- const docsForAddr = this.addressDoctorsMap[addressId] ?? [];
4355
+ const docsForAddr = this.state.addressDoctorsMap[addressId] ?? [];
4323
4356
  if (docsForAddr.length > 0) {
4324
4357
  this.doctors = docsForAddr;
4325
4358
  if (docsForAddr.length === 1) {
4326
- this.selectedDoctor = docsForAddr[0].id;
4327
- this.step = 1;
4359
+ this.state.selectedDoctor = docsForAddr[0].id;
4360
+ this.state.step = 1;
4328
4361
  }
4329
4362
  else {
4330
- this.selectedDoctor = null;
4363
+ this.state.selectedDoctor = null;
4331
4364
  }
4332
4365
  }
4333
4366
  else {
4334
- const otherHasDoctors = Object.entries(this.addressDoctorsMap).some(([key, docs]) => key !== addressId && Array.isArray(docs) && docs.length > 0);
4367
+ const otherHasDoctors = Object.entries(this.state.addressDoctorsMap).some(([key, docs]) => Number(key) !== addressId && Array.isArray(docs) && docs.length > 0);
4335
4368
  this.doctors = [];
4336
- this.selectedDoctor = null;
4369
+ this.state.selectedDoctor = null;
4337
4370
  if (otherHasDoctors) {
4338
- this.setError("No doctors at this address. Please select a different address.");
4371
+ this.setState({
4372
+ error: "No doctors at this address. Please select a different address.",
4373
+ });
4339
4374
  }
4340
4375
  else {
4341
- this.setError("No doctors available for the selected location(s).");
4376
+ this.setState({
4377
+ error: "No doctors available for the selected location(s).",
4378
+ });
4342
4379
  }
4343
4380
  }
4344
4381
  }
4345
4382
  catch (e) {
4346
- this.setError(e.message || "Failed to load doctors for address");
4383
+ this.setState({
4384
+ error: e.message || "Failed to load doctors for address",
4385
+ });
4347
4386
  }
4348
4387
  finally {
4349
- this.setLoading(false);
4388
+ this.setState({ loading: false });
4350
4389
  this.render();
4351
4390
  }
4352
4391
  }
4353
4392
  async loadSlots() {
4354
- if (!this.workspaceId || !this.selectedAddress || !this.selectedDoctor || !this.date) {
4355
- this.slots = [];
4356
- this.selectedSlot = null;
4393
+ const dateStr = formatDateToISO(this.state.selectedDate);
4394
+ if (!this.state.workspaceId ||
4395
+ !this.state.selectedAddress ||
4396
+ !this.state.selectedDoctor ||
4397
+ !dateStr) {
4398
+ this.state.slots = [];
4399
+ this.state.selectedSlot = null;
4357
4400
  this.render();
4358
4401
  return;
4359
4402
  }
4360
- this.setLoading(true);
4361
- this.setError(null);
4403
+ this.setState({ loading: true, error: null });
4362
4404
  try {
4363
- const s = await AppointmentService.fetchSlots(this.workspaceId, this.selectedAddress, this.selectedDoctor, this.date);
4364
- this.slots = s || [];
4405
+ const s = await AppointmentService.fetchSlots(this.state.workspaceId, this.state.selectedAddress, this.state.selectedDoctor, dateStr);
4406
+ this.state.slots = s || [];
4365
4407
  }
4366
4408
  catch (e) {
4367
- this.setError(e.message || "Failed to load slots");
4409
+ this.setState({ error: e.message || "Failed to load slots" });
4368
4410
  }
4369
4411
  finally {
4370
- this.setLoading(false);
4412
+ this.setState({ loading: false });
4371
4413
  this.render();
4372
4414
  }
4373
4415
  }
4374
- validatePhoneNumber(phone) {
4375
- const cleaned = phone.replace(/\D/g, "");
4376
- return cleaned.length >= 7 && cleaned.length <= 15;
4377
- }
4378
- validateCountryCode(code) {
4379
- return /^\+[1-9]\d{0,3}$/.test(code);
4380
- }
4381
4416
  canProceedFromMergedStep() {
4382
- if (this.addresses.length === 0 || this.doctors.length === 0)
4417
+ if (this.state.addresses.length === 0 || this.doctors.length === 0)
4383
4418
  return false;
4384
- if (this.addresses.length > 1 && !this.selectedAddress)
4419
+ if (this.state.addresses.length > 1 && !this.state.selectedAddress)
4385
4420
  return false;
4386
- if (this.doctors.length > 1 && !this.selectedDoctor)
4421
+ if (this.doctors.length > 1 && !this.state.selectedDoctor)
4387
4422
  return false;
4388
4423
  return true;
4389
4424
  }
4390
4425
  async sendOtp() {
4391
- this.setError(null);
4392
- if (!this.countryCode) {
4393
- this.setError("Please enter country code.");
4426
+ this.setState({ error: null });
4427
+ if (!this.state.countryCode) {
4428
+ this.setState({ error: "Please enter country code." });
4394
4429
  return;
4395
4430
  }
4396
- if (!this.validateCountryCode(this.countryCode)) {
4397
- this.setError("Please enter a valid country code (e.g., +91, +1).");
4431
+ if (!validateCountryCode(this.state.countryCode)) {
4432
+ this.setState({
4433
+ error: "Please enter a valid country code (e.g., +91, +1).",
4434
+ });
4398
4435
  return;
4399
4436
  }
4400
- if (!this.patientPhone) {
4401
- this.setError("Please enter phone number.");
4437
+ if (!this.state.patientPhone) {
4438
+ this.setState({ error: "Please enter phone number." });
4402
4439
  return;
4403
4440
  }
4404
- if (!this.validatePhoneNumber(this.patientPhone)) {
4405
- this.setError("Please enter a valid phone number (7-15 digits).");
4441
+ if (!validatePhoneNumber(this.state.patientPhone)) {
4442
+ this.setState({
4443
+ error: "Please enter a valid phone number (7-15 digits).",
4444
+ });
4406
4445
  return;
4407
4446
  }
4408
- this.otpSending = true;
4447
+ this.setState({ otpSending: true });
4409
4448
  this.render();
4410
4449
  try {
4411
4450
  await PatientService.sendPhoneVerificationOtp({
4412
- countryCode: this.countryCode,
4413
- phoneNumber: this.patientPhone,
4451
+ countryCode: this.state.countryCode,
4452
+ phoneNumber: this.state.patientPhone,
4414
4453
  });
4415
- this.otpSent = true;
4416
- this.setError(null);
4454
+ this.setState({ otpSent: true, error: null });
4417
4455
  }
4418
4456
  catch (e) {
4419
4457
  const msg = e.message || "Failed to send OTP";
4420
- this.setError(msg);
4458
+ this.setState({ error: msg });
4421
4459
  this.options.onError?.(e);
4422
4460
  }
4423
4461
  finally {
4424
- this.otpSending = false;
4462
+ this.setState({ otpSending: false });
4425
4463
  this.render();
4426
4464
  }
4427
4465
  }
4428
4466
  async verifyOtp() {
4429
- this.setError(null);
4430
- if (!this.countryCode || !this.patientPhone || !this.otpCode) {
4431
- this.setError("Please enter all required fields.");
4467
+ this.setState({ error: null });
4468
+ if (!this.state.countryCode ||
4469
+ !this.state.patientPhone ||
4470
+ !this.state.otpCode) {
4471
+ this.setState({ error: "Please enter all required fields." });
4432
4472
  return;
4433
4473
  }
4434
- if (this.otpCode.length !== 6) {
4435
- this.setError("Please enter a 6-digit OTP code.");
4474
+ if (this.state.otpCode.length !== 6) {
4475
+ this.setState({ error: "Please enter a 6-digit OTP code." });
4436
4476
  return;
4437
4477
  }
4438
- this.otpVerifying = true;
4478
+ this.setState({ otpVerifying: true });
4439
4479
  this.render();
4440
4480
  try {
4441
4481
  await PatientService.verifyPhoneVerificationOtp({
4442
- countryCode: this.countryCode,
4443
- phoneNumber: this.patientPhone,
4444
- otpCode: this.otpCode,
4482
+ countryCode: this.state.countryCode,
4483
+ phoneNumber: this.state.patientPhone,
4484
+ otpCode: this.state.otpCode,
4445
4485
  });
4446
- this.otpVerified = true;
4447
- this.setError(null);
4486
+ this.setState({ otpVerified: true, error: null });
4448
4487
  }
4449
4488
  catch (e) {
4450
4489
  const msg = e.message || "Invalid OTP code";
4451
- this.setError(msg);
4490
+ this.setState({ error: msg });
4452
4491
  this.options.onError?.(e);
4453
4492
  }
4454
4493
  finally {
4455
- this.otpVerifying = false;
4494
+ this.setState({ otpVerifying: false });
4456
4495
  this.render();
4457
4496
  }
4458
4497
  }
4459
4498
  async submitAppointment() {
4460
- this.setError(null);
4461
- if (!this.selectedDoctor ||
4462
- !this.selectedSlot ||
4463
- !this.workspaceId ||
4464
- !this.selectedAddress ||
4465
- !this.patientAddress ||
4466
- !this.patientCity ||
4467
- !this.patientState ||
4468
- !this.patientCountry ||
4469
- !this.patientZipcode) {
4470
- this.setError("Please ensure all required fields are complete.");
4499
+ this.setState({ error: null });
4500
+ if (!this.state.selectedDoctor ||
4501
+ !this.state.selectedSlot ||
4502
+ !this.state.workspaceId ||
4503
+ !this.state.selectedAddress ||
4504
+ !this.state.patientAddress ||
4505
+ !this.state.patientCity ||
4506
+ !this.state.patientState ||
4507
+ !this.state.patientCountry ||
4508
+ !this.state.patientZipcode) {
4509
+ this.setState({
4510
+ error: "Please ensure all required fields are complete.",
4511
+ });
4471
4512
  return;
4472
4513
  }
4473
- if (!this.otpVerified) {
4474
- this.setError("Please verify your phone number first.");
4514
+ if (!this.state.otpVerified) {
4515
+ this.setState({ error: "Please verify your phone number first." });
4475
4516
  return;
4476
4517
  }
4477
- this.setLoading(true);
4518
+ this.setState({ loading: true });
4478
4519
  this.render();
4479
4520
  try {
4480
- const nameParts = (this.patientName || "Patient").trim().split(/\s+/);
4481
- const firstName = nameParts[0] || "Patient";
4482
- const lastName = nameParts.slice(1).join(" ") || "";
4483
- const startDate = new Date(this.selectedSlot.start);
4484
- const endDate = new Date(this.selectedSlot.end);
4485
- const appointmentDate = startDate.toISOString().split("T")[0];
4521
+ const { firstName, lastName } = parsePatientName(this.state.patientName || "Patient");
4522
+ const startDate = new Date(this.state.selectedSlot.start);
4523
+ const endDate = new Date(this.state.selectedSlot.end);
4524
+ const appointmentDate = formatDateToISO(startDate);
4486
4525
  const formatTime = (date) => {
4487
4526
  const hours = String(date.getHours()).padStart(2, "0");
4488
4527
  const minutes = String(date.getMinutes()).padStart(2, "0");
@@ -4491,112 +4530,92 @@
4491
4530
  const fromDateTimeTs = formatTime(startDate);
4492
4531
  const toDateTimeTs = formatTime(endDate);
4493
4532
  const patientAddressPayload = {
4494
- addressLine1: this.patientAddress,
4495
- city: this.patientCity,
4496
- state: this.patientState,
4497
- country: this.patientCountry,
4498
- zipcode: this.patientZipcode,
4499
- landmark: this.patientLandmark || undefined,
4533
+ addressLine1: this.state.patientAddress,
4534
+ city: this.state.patientCity,
4535
+ state: this.state.patientState,
4536
+ country: this.state.patientCountry,
4537
+ zipcode: this.state.patientZipcode,
4538
+ landmark: this.state.patientLandmark || undefined,
4500
4539
  };
4501
4540
  await AppointmentService.createAppointment({
4502
- workspaceId: this.workspaceId,
4503
- workspaceAddressId: this.selectedAddress,
4504
- doctorId: this.selectedDoctor,
4541
+ workspaceId: this.state.workspaceId,
4542
+ workspaceAddressId: this.state.selectedAddress,
4543
+ doctorId: this.state.selectedDoctor,
4505
4544
  mode: "OFFLINE",
4506
4545
  appointmentDate,
4507
4546
  fromDateTimeTs,
4508
4547
  toDateTimeTs,
4509
- consultationCharge: this.consultationCharge || "0",
4548
+ consultationCharge: this.state.consultationCharge || "0",
4510
4549
  type: "CONSULTATION",
4511
4550
  source: "SDK_POWERED_WEBSITE",
4512
4551
  patientPayload: {
4513
4552
  firstName,
4514
4553
  lastName,
4515
- email: this.patientEmail || undefined,
4516
- countryCode: this.countryCode,
4517
- phoneNumber: this.patientPhone,
4518
- age: this.patientAge ? parseInt(this.patientAge, 10) : undefined,
4519
- gender: this.patientGender
4520
- ? this.patientGender.toUpperCase()
4554
+ email: this.state.patientEmail || undefined,
4555
+ countryCode: this.state.countryCode,
4556
+ phoneNumber: this.state.patientPhone,
4557
+ age: this.state.patientAge
4558
+ ? parseInt(this.state.patientAge, 10)
4559
+ : undefined,
4560
+ gender: this.state.patientGender
4561
+ ? this.state.patientGender.toUpperCase()
4521
4562
  : undefined,
4522
4563
  },
4523
4564
  patientAddress: patientAddressPayload,
4524
4565
  });
4525
- this.step = 5;
4566
+ this.state.step = 5;
4526
4567
  this.options.onSuccess?.();
4527
4568
  }
4528
4569
  catch (e) {
4529
4570
  const msg = e.message || "Failed to create appointment";
4530
- this.setError(msg);
4571
+ this.setState({ error: msg });
4531
4572
  this.options.onError?.(e);
4532
4573
  }
4533
4574
  finally {
4534
- this.setLoading(false);
4575
+ this.setState({ loading: false });
4535
4576
  this.render();
4536
4577
  }
4537
4578
  }
4538
4579
  goToNext() {
4539
- if (this.step === 0) {
4540
- if (this.addresses.length > 1 && !this.selectedAddress)
4580
+ if (this.state.step === 0) {
4581
+ if (this.state.addresses.length > 1 && !this.state.selectedAddress)
4541
4582
  return;
4542
- if (this.doctors.length > 1 && !this.selectedDoctor)
4583
+ if (this.doctors.length > 1 && !this.state.selectedDoctor)
4543
4584
  return;
4544
- this.step = 1;
4585
+ this.state.step = 1;
4545
4586
  this.render();
4546
4587
  return;
4547
4588
  }
4548
- if (this.step === 1 && this.date) {
4549
- this.step = 2;
4589
+ const dateStr = formatDateToISO(this.state.selectedDate);
4590
+ if (this.state.step === 1 && dateStr) {
4591
+ this.state.step = 2;
4550
4592
  this.loadSlots();
4551
4593
  return;
4552
4594
  }
4553
- if (this.step === 2 && this.selectedSlot) {
4554
- this.step = 3;
4595
+ if (this.state.step === 2 && this.state.selectedSlot) {
4596
+ this.state.step = 3;
4555
4597
  this.render();
4556
4598
  return;
4557
4599
  }
4558
- if (this.step === 3 && this.otpVerified) {
4559
- this.step = 4;
4600
+ if (this.state.step === 3 && this.state.otpVerified) {
4601
+ this.state.step = 4;
4560
4602
  this.render();
4561
4603
  return;
4562
4604
  }
4563
- this.step = Math.min(5, this.step + 1);
4605
+ this.state.step = Math.min(5, this.state.step + 1);
4564
4606
  this.render();
4565
4607
  }
4566
4608
  goBack() {
4567
- this.step = Math.max(0, this.step - 1);
4609
+ this.state.step = Math.max(0, this.state.step - 1);
4568
4610
  this.render();
4569
4611
  }
4570
4612
  reset() {
4571
- this.step = 0;
4572
- this.selectedSlot = null;
4573
- this.selectedDoctor = null;
4574
- this.date = "";
4575
- this.patientName = "";
4576
- this.patientAge = "";
4577
- this.patientAddress = "";
4578
- this.patientCity = "";
4579
- this.patientState = "";
4580
- this.patientCountry = "";
4581
- this.patientZipcode = "";
4582
- this.patientLandmark = "";
4583
- this.patientEmail = "";
4584
- this.patientGender = "";
4585
- this.problemFacing = "";
4586
- this.consultationCharge = "";
4587
- this.countryCode = "+91";
4588
- this.patientPhone = "";
4589
- this.otpCode = "";
4590
- this.otpSent = false;
4591
- this.otpVerified = false;
4592
- this.slots = [];
4613
+ this.state = { ...INITIAL_STATE };
4614
+ this.doctors = [];
4593
4615
  this.render();
4594
4616
  }
4595
- setLoading(loading) {
4596
- this.loading = loading;
4597
- }
4598
- setError(error) {
4599
- this.error = error;
4617
+ setState(updates) {
4618
+ this.state = { ...this.state, ...updates };
4600
4619
  }
4601
4620
  render() {
4602
4621
  if (!this.mounted)
@@ -4607,16 +4626,20 @@
4607
4626
  <div class="medos-appointment-header">
4608
4627
  <h2 class="medos-appointment-title">Book Appointment</h2>
4609
4628
  <div class="medos-appointment-stepper">
4610
- <div class="medos-appointment-step-pill ${this.step === 0 ? "active" : ""}">1 Address</div>
4611
- <div class="medos-appointment-step-pill ${this.step === 1 ? "active" : ""}">2 Date</div>
4612
- <div class="medos-appointment-step-pill ${this.step === 2 ? "active" : ""}">3 Slot</div>
4613
- <div class="medos-appointment-step-pill ${this.step === 3 ? "active" : ""}">4 Phone</div>
4614
- <div class="medos-appointment-step-pill ${this.step === 4 ? "active" : ""}">5 Details</div>
4629
+ <div class="medos-appointment-step-pill ${this.state.step === 0 ? "active" : ""}">1 Address</div>
4630
+ <div class="medos-appointment-step-pill ${this.state.step === 1 ? "active" : ""}">2 Date</div>
4631
+ <div class="medos-appointment-step-pill ${this.state.step === 2 ? "active" : ""}">3 Slot</div>
4632
+ <div class="medos-appointment-step-pill ${this.state.step === 3 ? "active" : ""}">4 Phone</div>
4633
+ <div class="medos-appointment-step-pill ${this.state.step === 4 ? "active" : ""}">5 Details</div>
4615
4634
  </div>
4616
4635
  </div>
4617
4636
 
4618
- ${this.loading ? '<div class="medos-appointment-loading">Loading...</div>' : ""}
4619
- ${this.error ? `<div class="medos-appointment-error">${this.escapeHtml(this.error)}</div>` : ""}
4637
+ ${this.state.loading
4638
+ ? '<div class="medos-appointment-loading">Loading...</div>'
4639
+ : ""}
4640
+ ${this.state.error
4641
+ ? `<div class="medos-appointment-error">${this.escapeHtml(this.state.error)}</div>`
4642
+ : ""}
4620
4643
 
4621
4644
  ${this.renderStep()}
4622
4645
  </div>
@@ -4625,7 +4648,7 @@
4625
4648
  this.attachEventListeners();
4626
4649
  }
4627
4650
  renderStep() {
4628
- switch (this.step) {
4651
+ switch (this.state.step) {
4629
4652
  case 0:
4630
4653
  return this.renderStep0();
4631
4654
  case 1:
@@ -4649,14 +4672,16 @@
4649
4672
  <div class="medos-appointment-form-grid-2col">
4650
4673
  <div>
4651
4674
  <label class="medos-appointment-label">Address</label>
4652
- ${this.addresses.length === 0
4675
+ ${this.state.addresses.length === 0
4653
4676
  ? '<div class="medos-appointment-small-muted">No addresses available</div>'
4654
- : this.addresses.length === 1
4655
- ? `<div class="medos-appointment-small-muted" style="font-weight: 600">${this.escapeHtml(this.addresses[0].label)}</div>`
4677
+ : this.state.addresses.length === 1
4678
+ ? `<div class="medos-appointment-small-muted" style="font-weight: 600">${this.escapeHtml(this.state.addresses[0].label || "")}</div>`
4656
4679
  : `
4657
4680
  <select class="medos-appointment-select" id="medos-address-select">
4658
4681
  <option value="">-- choose address --</option>
4659
- ${this.addresses.map(a => `<option value="${this.escapeHtml(a.id)}" ${this.selectedAddress === a.id ? "selected" : ""}>${this.escapeHtml(a.label)}</option>`).join("")}
4682
+ ${this.state.addresses
4683
+ .map((a) => `<option value="${this.escapeHtml(a.id.toString())}" ${this.state.selectedAddress === a.id ? "selected" : ""}>${this.escapeHtml(a.label || "")}</option>`)
4684
+ .join("")}
4660
4685
  </select>
4661
4686
  `}
4662
4687
  </div>
@@ -4665,11 +4690,17 @@
4665
4690
  ${this.doctors.length === 0
4666
4691
  ? '<div class="medos-appointment-small-muted">No doctors available</div>'
4667
4692
  : this.doctors.length === 1
4668
- ? `<div class="medos-appointment-small-muted" style="font-weight: 600">${this.escapeHtml(this.doctors[0].name)}${this.doctors[0].specialty ? ` • ${this.escapeHtml(this.doctors[0].specialty)}` : ""}</div>`
4693
+ ? `<div class="medos-appointment-small-muted" style="font-weight: 600">${this.escapeHtml(this.doctors[0].name)}${this.doctors[0].specialty
4694
+ ? ` • ${this.escapeHtml(this.doctors[0].specialty)}`
4695
+ : ""}</div>`
4669
4696
  : `
4670
4697
  <select class="medos-appointment-select" id="medos-doctor-select">
4671
4698
  <option value="">-- choose doctor --</option>
4672
- ${this.doctors.map(d => `<option value="${this.escapeHtml(d.id)}" ${this.selectedDoctor === d.id ? "selected" : ""}>${this.escapeHtml(d.name)}${d.specialty ? ` (${this.escapeHtml(d.specialty)})` : ""}</option>`).join("")}
4699
+ ${this.doctors
4700
+ .map((d) => `<option value="${this.escapeHtml(d.id.toString())}" ${this.state.selectedDoctor === d.id ? "selected" : ""}>${this.escapeHtml(d.name)}${d.specialty
4701
+ ? ` (${this.escapeHtml(d.specialty)})`
4702
+ : ""}</option>`)
4703
+ .join("")}
4673
4704
  </select>
4674
4705
  `}
4675
4706
  </div>
@@ -4682,13 +4713,14 @@
4682
4713
  `;
4683
4714
  }
4684
4715
  renderStep1() {
4716
+ const dateStr = formatDateToISO(this.state.selectedDate);
4685
4717
  return `
4686
4718
  <div class="medos-appointment-section">
4687
4719
  <label class="medos-appointment-label">Select Date</label>
4688
- <input type="date" class="medos-appointment-input" id="medos-date-input" value="${this.escapeHtml(this.date)}" />
4720
+ <input type="date" class="medos-appointment-input" id="medos-date-input" value="${this.escapeHtml(dateStr)}" />
4689
4721
  <div class="medos-appointment-actions">
4690
4722
  <button class="medos-appointment-btn medos-appointment-btn-secondary" id="medos-btn-back">Back</button>
4691
- <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-next" ${!this.date ? "disabled" : ""} style="opacity: ${this.date ? 1 : 0.6}">Next</button>
4723
+ <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-next" ${!dateStr ? "disabled" : ""} style="opacity: ${dateStr ? 1 : 0.6}">Next</button>
4692
4724
  </div>
4693
4725
  </div>
4694
4726
  `;
@@ -4697,50 +4729,63 @@
4697
4729
  return `
4698
4730
  <div class="medos-appointment-section">
4699
4731
  <label class="medos-appointment-label">Choose Time Slot</label>
4700
- ${this.slots.length === 0
4732
+ ${this.state.slots.length === 0
4701
4733
  ? '<div class="medos-appointment-small-muted">No slots available for selected date</div>'
4702
4734
  : `
4703
4735
  <div class="medos-appointment-slot-grid">
4704
- ${this.slots.map(s => {
4705
- const start = new Date(s.start).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
4706
- const end = new Date(s.end).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
4707
- const selected = this.selectedSlot?.start === s.start && this.selectedSlot?.end === s.end;
4736
+ ${this.state.slots
4737
+ .map((s) => {
4738
+ const start = new Date(s.start).toLocaleTimeString([], {
4739
+ hour: "2-digit",
4740
+ minute: "2-digit",
4741
+ });
4742
+ const end = new Date(s.end).toLocaleTimeString([], {
4743
+ hour: "2-digit",
4744
+ minute: "2-digit",
4745
+ });
4746
+ const selected = this.state.selectedSlot?.start === s.start &&
4747
+ this.state.selectedSlot?.end === s.end;
4708
4748
  return `
4709
4749
  <div class="medos-appointment-slot-card ${selected ? "selected" : ""}" data-slot-id="${this.escapeHtml(s.id || `${s.start}-${s.end}`)}" data-slot-start="${this.escapeHtml(s.start)}" data-slot-end="${this.escapeHtml(s.end)}">
4710
4750
  <div class="medos-appointment-slot-time">${start} — ${end}</div>
4711
4751
  </div>
4712
4752
  `;
4713
- }).join("")}
4753
+ })
4754
+ .join("")}
4714
4755
  </div>
4715
4756
  `}
4716
4757
  <div class="medos-appointment-actions">
4717
4758
  <button class="medos-appointment-btn medos-appointment-btn-secondary" id="medos-btn-back">Back</button>
4718
- <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-next" ${!this.selectedSlot ? "disabled" : ""} style="opacity: ${this.selectedSlot ? 1 : 0.6}">Next</button>
4759
+ <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-next" ${!this.state.selectedSlot ? "disabled" : ""} style="opacity: ${this.state.selectedSlot ? 1 : 0.6}">Next</button>
4719
4760
  </div>
4720
4761
  </div>
4721
4762
  `;
4722
4763
  }
4723
4764
  renderStep3() {
4724
- const countryCodeValid = this.countryCode && this.validateCountryCode(this.countryCode);
4725
- const phoneValid = this.patientPhone && this.validatePhoneNumber(this.patientPhone);
4726
- const canSendOtp = countryCodeValid && phoneValid && !this.otpSending;
4727
- if (!this.otpSent) {
4765
+ const countryCodeValid = this.state.countryCode && validateCountryCode(this.state.countryCode);
4766
+ const phoneValid = this.state.patientPhone && validatePhoneNumber(this.state.patientPhone);
4767
+ const canSendOtp = countryCodeValid && phoneValid && !this.state.otpSending;
4768
+ if (!this.state.otpSent) {
4728
4769
  return `
4729
4770
  <div class="medos-appointment-section">
4730
4771
  <label class="medos-appointment-label">Country Code</label>
4731
- <input type="text" class="medos-appointment-input" id="medos-country-code" placeholder="+91" value="${this.escapeHtml(this.countryCode)}" />
4732
- ${this.countryCode && !countryCodeValid ? '<div class="medos-appointment-validation-error">Please enter a valid country code (e.g., +91, +1)</div>' : ""}
4772
+ <input type="text" class="medos-appointment-input" id="medos-country-code" placeholder="+91" value="${this.escapeHtml(this.state.countryCode)}" />
4773
+ ${this.state.countryCode && !countryCodeValid
4774
+ ? '<div class="medos-appointment-validation-error">Please enter a valid country code (e.g., +91, +1)</div>'
4775
+ : ""}
4733
4776
  <label class="medos-appointment-label" style="margin-top: 12px">Phone Number</label>
4734
- <input type="tel" class="medos-appointment-input" id="medos-phone" placeholder="9311840587" value="${this.escapeHtml(this.patientPhone)}" maxlength="15" />
4735
- ${this.patientPhone && !phoneValid ? '<div class="medos-appointment-validation-error">Phone number should be 7-15 digits</div>' : ""}
4777
+ <input type="tel" class="medos-appointment-input" id="medos-phone" placeholder="9311840587" value="${this.escapeHtml(this.state.patientPhone)}" maxlength="15" />
4778
+ ${this.state.patientPhone && !phoneValid
4779
+ ? '<div class="medos-appointment-validation-error">Phone number should be 7-15 digits</div>'
4780
+ : ""}
4736
4781
  <div class="medos-appointment-actions">
4737
4782
  <button class="medos-appointment-btn medos-appointment-btn-secondary" id="medos-btn-back">Back</button>
4738
- <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-send-otp" ${!canSendOtp ? "disabled" : ""} style="opacity: ${canSendOtp ? 1 : 0.6}">${this.otpSending ? "Sending..." : "Send OTP"}</button>
4783
+ <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-send-otp" ${!canSendOtp ? "disabled" : ""} style="opacity: ${canSendOtp ? 1 : 0.6}">${this.state.otpSending ? "Sending..." : "Send OTP"}</button>
4739
4784
  </div>
4740
4785
  </div>
4741
4786
  `;
4742
4787
  }
4743
- if (this.otpVerified) {
4788
+ if (this.state.otpVerified) {
4744
4789
  return `
4745
4790
  <div class="medos-appointment-section">
4746
4791
  <div class="medos-appointment-verified">✓ Phone verified successfully</div>
@@ -4754,61 +4799,69 @@
4754
4799
  return `
4755
4800
  <div class="medos-appointment-section">
4756
4801
  <label class="medos-appointment-label">Enter OTP</label>
4757
- <input type="text" class="medos-appointment-input" id="medos-otp" placeholder="Enter 6-digit OTP" value="${this.escapeHtml(this.otpCode)}" maxlength="6" />
4758
- <div class="medos-appointment-otp-info">OTP sent to ${this.escapeHtml(this.countryCode)} ${this.escapeHtml(this.patientPhone)}</div>
4802
+ <input type="text" class="medos-appointment-input" id="medos-otp" placeholder="Enter 6-digit OTP" value="${this.escapeHtml(this.state.otpCode)}" maxlength="6" />
4803
+ <div class="medos-appointment-otp-info">OTP sent to ${this.escapeHtml(this.state.countryCode)} ${this.escapeHtml(this.state.patientPhone)}</div>
4759
4804
  <div class="medos-appointment-actions">
4760
4805
  <button class="medos-appointment-btn medos-appointment-btn-secondary" id="medos-btn-change-number">Change Number</button>
4761
- <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-verify-otp" ${this.otpCode.length !== 6 || this.otpVerifying ? "disabled" : ""} style="opacity: ${this.otpCode.length === 6 && !this.otpVerifying ? 1 : 0.6}">${this.otpVerifying ? "Verifying..." : "Verify OTP"}</button>
4806
+ <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-verify-otp" ${this.state.otpCode.length !== 6 || this.state.otpVerifying
4807
+ ? "disabled"
4808
+ : ""} style="opacity: ${this.state.otpCode.length === 6 && !this.state.otpVerifying ? 1 : 0.6}">${this.state.otpVerifying ? "Verifying..." : "Verify OTP"}</button>
4762
4809
  </div>
4763
4810
  </div>
4764
4811
  `;
4765
4812
  }
4766
4813
  renderStep4() {
4767
- const canSubmit = this.patientName && this.patientAddress && this.patientCity && this.patientState && this.patientCountry && this.patientZipcode && this.otpVerified;
4814
+ const canSubmit = this.state.patientName &&
4815
+ this.state.patientAddress &&
4816
+ this.state.patientCity &&
4817
+ this.state.patientState &&
4818
+ this.state.patientCountry &&
4819
+ this.state.patientZipcode &&
4820
+ this.state.otpVerified;
4768
4821
  return `
4769
4822
  <div class="medos-appointment-section">
4770
4823
  <label class="medos-appointment-label">Patient Name</label>
4771
- <input type="text" class="medos-appointment-input" id="medos-patient-name" placeholder="Full name" value="${this.escapeHtml(this.patientName)}" />
4824
+ <input type="text" class="medos-appointment-input" id="medos-patient-name" placeholder="Full name" value="${this.escapeHtml(this.state.patientName)}" />
4772
4825
  <label class="medos-appointment-label" style="margin-top: 12px">Age</label>
4773
- <input type="number" class="medos-appointment-input" id="medos-patient-age" placeholder="Age" value="${this.escapeHtml(this.patientAge)}" />
4826
+ <input type="number" class="medos-appointment-input" id="medos-patient-age" placeholder="Age" value="${this.escapeHtml(this.state.patientAge)}" />
4774
4827
  <label class="medos-appointment-label" style="margin-top: 12px">Email (Optional)</label>
4775
- <input type="email" class="medos-appointment-input" id="medos-patient-email" placeholder="patient@example.com" value="${this.escapeHtml(this.patientEmail)}" />
4828
+ <input type="email" class="medos-appointment-input" id="medos-patient-email" placeholder="patient@example.com" value="${this.escapeHtml(this.state.patientEmail)}" />
4776
4829
  <label class="medos-appointment-label" style="margin-top: 12px">Gender (Optional)</label>
4777
4830
  <select class="medos-appointment-select" id="medos-patient-gender">
4778
4831
  <option value="">-- Select Gender --</option>
4779
- <option value="MALE" ${this.patientGender === "MALE" ? "selected" : ""}>Male</option>
4780
- <option value="FEMALE" ${this.patientGender === "FEMALE" ? "selected" : ""}>Female</option>
4781
- <option value="OTHER" ${this.patientGender === "OTHER" ? "selected" : ""}>Other</option>
4832
+ <option value="MALE" ${this.state.patientGender === "MALE" ? "selected" : ""}>Male</option>
4833
+ <option value="FEMALE" ${this.state.patientGender === "FEMALE" ? "selected" : ""}>Female</option>
4834
+ <option value="OTHER" ${this.state.patientGender === "OTHER" ? "selected" : ""}>Other</option>
4782
4835
  </select>
4783
4836
  <label class="medos-appointment-label" style="margin-top: 12px">Address Line 1 *</label>
4784
- <input type="text" class="medos-appointment-input" id="medos-patient-address" placeholder="Street address, building name, etc." value="${this.escapeHtml(this.patientAddress)}" />
4837
+ <input type="text" class="medos-appointment-input" id="medos-patient-address" placeholder="Street address, building name, etc." value="${this.escapeHtml(this.state.patientAddress)}" />
4785
4838
  <div class="medos-appointment-form-grid">
4786
4839
  <div>
4787
4840
  <label class="medos-appointment-label">City *</label>
4788
- <input type="text" class="medos-appointment-input" id="medos-patient-city" placeholder="City" value="${this.escapeHtml(this.patientCity)}" />
4841
+ <input type="text" class="medos-appointment-input" id="medos-patient-city" placeholder="City" value="${this.escapeHtml(this.state.patientCity)}" />
4789
4842
  </div>
4790
4843
  <div>
4791
4844
  <label class="medos-appointment-label">State *</label>
4792
- <input type="text" class="medos-appointment-input" id="medos-patient-state" placeholder="State" value="${this.escapeHtml(this.patientState)}" />
4845
+ <input type="text" class="medos-appointment-input" id="medos-patient-state" placeholder="State" value="${this.escapeHtml(this.state.patientState)}" />
4793
4846
  </div>
4794
4847
  </div>
4795
4848
  <div class="medos-appointment-form-grid">
4796
4849
  <div>
4797
4850
  <label class="medos-appointment-label">Country *</label>
4798
- <input type="text" class="medos-appointment-input" id="medos-patient-country" placeholder="Country" value="${this.escapeHtml(this.patientCountry)}" />
4851
+ <input type="text" class="medos-appointment-input" id="medos-patient-country" placeholder="Country" value="${this.escapeHtml(this.state.patientCountry)}" />
4799
4852
  </div>
4800
4853
  <div>
4801
4854
  <label class="medos-appointment-label">Zipcode *</label>
4802
- <input type="text" class="medos-appointment-input" id="medos-patient-zipcode" placeholder="Zipcode" value="${this.escapeHtml(this.patientZipcode)}" />
4855
+ <input type="text" class="medos-appointment-input" id="medos-patient-zipcode" placeholder="Zipcode" value="${this.escapeHtml(this.state.patientZipcode)}" />
4803
4856
  </div>
4804
4857
  </div>
4805
4858
  <label class="medos-appointment-label" style="margin-top: 12px">Landmark (Optional)</label>
4806
- <input type="text" class="medos-appointment-input" id="medos-patient-landmark" placeholder="Nearby landmark" value="${this.escapeHtml(this.patientLandmark)}" />
4859
+ <input type="text" class="medos-appointment-input" id="medos-patient-landmark" placeholder="Nearby landmark" value="${this.escapeHtml(this.state.patientLandmark)}" />
4807
4860
  <label class="medos-appointment-label" style="margin-top: 12px">Problem Facing</label>
4808
- <textarea class="medos-appointment-textarea" id="medos-problem-facing" placeholder="Describe the problem you're facing">${this.escapeHtml(this.problemFacing)}</textarea>
4861
+ <textarea class="medos-appointment-textarea" id="medos-problem-facing" placeholder="Describe the problem you're facing">${this.escapeHtml(this.state.patientName)}</textarea>
4809
4862
  <div class="medos-appointment-actions">
4810
4863
  <button class="medos-appointment-btn medos-appointment-btn-secondary" id="medos-btn-back">Back</button>
4811
- <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-submit" ${!canSubmit || this.loading ? "disabled" : ""} style="opacity: ${canSubmit && !this.loading ? 1 : 0.6}">${this.loading ? "Booking..." : "Book Appointment"}</button>
4864
+ <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-submit" ${!canSubmit || this.state.loading ? "disabled" : ""} style="opacity: ${canSubmit && !this.state.loading ? 1 : 0.6}">${this.state.loading ? "Booking..." : "Book Appointment"}</button>
4812
4865
  </div>
4813
4866
  </div>
4814
4867
  `;
@@ -4819,7 +4872,7 @@
4819
4872
  <div class="medos-appointment-success-card">
4820
4873
  <div class="medos-appointment-success-icon">✓</div>
4821
4874
  <div class="medos-appointment-success-title">Appointment Confirmed</div>
4822
- <div class="medos-appointment-small-muted">Thank you, ${this.escapeHtml(this.patientName || "Patient")}. Your appointment is confirmed.</div>
4875
+ <div class="medos-appointment-small-muted">Thank you, ${this.escapeHtml(this.state.patientName || "Patient")}. Your appointment is confirmed.</div>
4823
4876
  </div>
4824
4877
  <div style="margin-top: 14px; display: flex; justify-content: center">
4825
4878
  <button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-book-another" style="width: 160px">Book Another</button>
@@ -4832,14 +4885,14 @@
4832
4885
  if (addressSelect) {
4833
4886
  addressSelect.addEventListener("change", (e) => {
4834
4887
  const target = e.target;
4835
- this.handleAddressChange(target.value || null);
4888
+ this.handleAddressChange(Number(target.value) || null);
4836
4889
  });
4837
4890
  }
4838
4891
  const doctorSelect = this.container.querySelector("#medos-doctor-select");
4839
4892
  if (doctorSelect) {
4840
4893
  doctorSelect.addEventListener("change", (e) => {
4841
4894
  const target = e.target;
4842
- this.selectedDoctor = target.value || null;
4895
+ this.state.selectedDoctor = Number(target.value) || null;
4843
4896
  this.render();
4844
4897
  });
4845
4898
  }
@@ -4847,18 +4900,22 @@
4847
4900
  if (dateInput) {
4848
4901
  dateInput.addEventListener("change", (e) => {
4849
4902
  const target = e.target;
4850
- this.date = target.value;
4903
+ this.state.selectedDate = new Date(target.value);
4851
4904
  this.render();
4852
4905
  });
4853
4906
  }
4854
4907
  const slotCards = this.container.querySelectorAll(".medos-appointment-slot-card");
4855
- slotCards.forEach(card => {
4908
+ slotCards.forEach((card) => {
4856
4909
  card.addEventListener("click", () => {
4857
4910
  const slotId = card.getAttribute("data-slot-id");
4858
4911
  const slotStart = card.getAttribute("data-slot-start");
4859
4912
  const slotEnd = card.getAttribute("data-slot-end");
4860
4913
  if (slotStart && slotEnd) {
4861
- this.selectedSlot = { start: slotStart, end: slotEnd, id: slotId || undefined };
4914
+ this.state.selectedSlot = {
4915
+ start: slotStart,
4916
+ end: slotEnd,
4917
+ id: slotId || undefined,
4918
+ };
4862
4919
  this.render();
4863
4920
  }
4864
4921
  });
@@ -4872,7 +4929,7 @@
4872
4929
  value = "+" + value;
4873
4930
  }
4874
4931
  value = value.replace(/[^\d+]/g, "");
4875
- this.countryCode = value;
4932
+ this.state.countryCode = value;
4876
4933
  target.value = value;
4877
4934
  this.render();
4878
4935
  });
@@ -4881,8 +4938,8 @@
4881
4938
  if (phoneInput) {
4882
4939
  phoneInput.addEventListener("input", (e) => {
4883
4940
  const target = e.target;
4884
- this.patientPhone = target.value.replace(/\D/g, "");
4885
- target.value = this.patientPhone;
4941
+ this.state.patientPhone = target.value.replace(/\D/g, "");
4942
+ target.value = this.state.patientPhone;
4886
4943
  this.render();
4887
4944
  });
4888
4945
  }
@@ -4890,7 +4947,7 @@
4890
4947
  if (otpInput) {
4891
4948
  otpInput.addEventListener("input", (e) => {
4892
4949
  const target = e.target;
4893
- this.otpCode = target.value;
4950
+ this.state.otpCode = target.value;
4894
4951
  this.render();
4895
4952
  });
4896
4953
  }
@@ -4898,77 +4955,70 @@
4898
4955
  if (patientNameInput) {
4899
4956
  patientNameInput.addEventListener("input", (e) => {
4900
4957
  const target = e.target;
4901
- this.patientName = target.value;
4958
+ this.state.patientName = target.value;
4902
4959
  });
4903
4960
  }
4904
4961
  const patientAgeInput = this.container.querySelector("#medos-patient-age");
4905
4962
  if (patientAgeInput) {
4906
4963
  patientAgeInput.addEventListener("input", (e) => {
4907
4964
  const target = e.target;
4908
- this.patientAge = target.value;
4965
+ this.state.patientAge = target.value;
4909
4966
  });
4910
4967
  }
4911
4968
  const patientEmailInput = this.container.querySelector("#medos-patient-email");
4912
4969
  if (patientEmailInput) {
4913
4970
  patientEmailInput.addEventListener("input", (e) => {
4914
4971
  const target = e.target;
4915
- this.patientEmail = target.value;
4972
+ this.state.patientEmail = target.value;
4916
4973
  });
4917
4974
  }
4918
4975
  const patientGenderSelect = this.container.querySelector("#medos-patient-gender");
4919
4976
  if (patientGenderSelect) {
4920
4977
  patientGenderSelect.addEventListener("change", (e) => {
4921
4978
  const target = e.target;
4922
- this.patientGender = target.value;
4979
+ this.state.patientGender = target.value;
4923
4980
  });
4924
4981
  }
4925
4982
  const patientAddressInput = this.container.querySelector("#medos-patient-address");
4926
4983
  if (patientAddressInput) {
4927
4984
  patientAddressInput.addEventListener("input", (e) => {
4928
4985
  const target = e.target;
4929
- this.patientAddress = target.value;
4986
+ this.state.patientAddress = target.value;
4930
4987
  });
4931
4988
  }
4932
4989
  const patientCityInput = this.container.querySelector("#medos-patient-city");
4933
4990
  if (patientCityInput) {
4934
4991
  patientCityInput.addEventListener("input", (e) => {
4935
4992
  const target = e.target;
4936
- this.patientCity = target.value;
4993
+ this.state.patientCity = target.value;
4937
4994
  });
4938
4995
  }
4939
4996
  const patientStateInput = this.container.querySelector("#medos-patient-state");
4940
4997
  if (patientStateInput) {
4941
4998
  patientStateInput.addEventListener("input", (e) => {
4942
4999
  const target = e.target;
4943
- this.patientState = target.value;
5000
+ this.state.patientState = target.value;
4944
5001
  });
4945
5002
  }
4946
5003
  const patientCountryInput = this.container.querySelector("#medos-patient-country");
4947
5004
  if (patientCountryInput) {
4948
5005
  patientCountryInput.addEventListener("input", (e) => {
4949
5006
  const target = e.target;
4950
- this.patientCountry = target.value;
5007
+ this.state.patientCountry = target.value;
4951
5008
  });
4952
5009
  }
4953
5010
  const patientZipcodeInput = this.container.querySelector("#medos-patient-zipcode");
4954
5011
  if (patientZipcodeInput) {
4955
5012
  patientZipcodeInput.addEventListener("input", (e) => {
4956
5013
  const target = e.target;
4957
- this.patientZipcode = target.value;
5014
+ this.state.patientZipcode = target.value;
4958
5015
  });
4959
5016
  }
4960
5017
  const patientLandmarkInput = this.container.querySelector("#medos-patient-landmark");
4961
5018
  if (patientLandmarkInput) {
4962
5019
  patientLandmarkInput.addEventListener("input", (e) => {
4963
5020
  const target = e.target;
4964
- this.patientLandmark = target.value;
4965
- });
4966
- }
4967
- const problemFacingInput = this.container.querySelector("#medos-problem-facing");
4968
- if (problemFacingInput) {
4969
- problemFacingInput.addEventListener("input", (e) => {
4970
- const target = e.target;
4971
- this.problemFacing = target.value;
5021
+ this.state.patientLandmark = target.value;
4972
5022
  });
4973
5023
  }
4974
5024
  const nextBtn = this.container.querySelector("#medos-btn-next");
@@ -4990,9 +5040,7 @@
4990
5040
  const changeNumberBtn = this.container.querySelector("#medos-btn-change-number");
4991
5041
  if (changeNumberBtn) {
4992
5042
  changeNumberBtn.addEventListener("click", () => {
4993
- this.otpSent = false;
4994
- this.otpCode = "";
4995
- this.otpVerified = false;
5043
+ this.setState({ otpSent: false, otpCode: "", otpVerified: false });
4996
5044
  this.render();
4997
5045
  });
4998
5046
  }