medos-sdk 1.1.6 → 1.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AppointmentCalender.js +13 -2
- package/dist/components/AppointmentConfirmationStep.d.ts +24 -0
- package/dist/components/AppointmentConfirmationStep.js +110 -0
- package/dist/components/Icons/SuccessIcon.d.ts +8 -0
- package/dist/components/Icons/SuccessIcon.js +14 -0
- package/dist/components/SuccessStep.js +1 -1
- package/dist/services/EnquiryService.d.ts +1 -1
- package/dist/services/EnquiryService.js +5 -8
- package/dist/services/WorkspaceService.d.ts +9 -0
- package/dist/services/WorkspaceService.js +17 -0
- package/dist/vanilla/AppointmentCalendarWidget.d.ts +12 -0
- package/dist/vanilla/AppointmentCalendarWidget.js +573 -167
- package/dist/vanilla/EnquiryFormWidget.d.ts +3 -0
- package/dist/vanilla/EnquiryFormWidget.js +229 -120
- package/dist/vanilla/components/AppointmentConfirmationStep.d.ts +24 -0
- package/dist/vanilla/components/Icons/SuccessIcon.d.ts +8 -0
- package/dist/vanilla/components/VanillaCalendar.d.ts +32 -0
- package/dist/vanilla/components/VanillaCalendar.js +366 -0
- package/dist/vanilla/components/VanillaIcons.d.ts +17 -0
- package/dist/vanilla/components/VanillaIcons.js +268 -0
- package/dist/vanilla/components/VanillaSelect.d.ts +46 -0
- package/dist/vanilla/components/VanillaSelect.js +523 -0
- package/dist/vanilla/components/index.d.ts +3 -0
- package/dist/vanilla/components/index.js +3 -0
- package/dist/vanilla/components/theme-injector.d.ts +1 -0
- package/dist/vanilla/components/theme-injector.js +447 -0
- package/dist/vanilla/enquiry-widget.js +1445 -128
- package/dist/vanilla/services/EnquiryService.d.ts +1 -1
- package/dist/vanilla/services/WorkspaceService.d.ts +9 -0
- package/dist/vanilla/vanilla/AppointmentCalendarWidget.d.ts +12 -0
- package/dist/vanilla/vanilla/EnquiryFormWidget.d.ts +3 -0
- package/dist/vanilla/vanilla/components/VanillaCalendar.d.ts +32 -0
- package/dist/vanilla/vanilla/components/VanillaIcons.d.ts +17 -0
- package/dist/vanilla/vanilla/components/VanillaSelect.d.ts +46 -0
- package/dist/vanilla/vanilla/components/index.d.ts +3 -0
- package/dist/vanilla/vanilla/components/theme-injector.d.ts +1 -0
- package/dist/vanilla/vanilla/widget.d.ts +2 -0
- package/dist/vanilla/widget.d.ts +2 -0
- package/dist/vanilla/widget.js +2402 -294
- package/package.json +1 -1
|
@@ -1,13 +1,24 @@
|
|
|
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, } from "../components/types";
|
|
4
|
+
import { INITIAL_STATE, COUNTRY_CODES, GENDER_OPTIONS, BLOOD_GROUP_OPTIONS, } from "../components/types";
|
|
5
5
|
import { validatePhoneNumber, validateCountryCode, } from "../components/validation";
|
|
6
6
|
import { formatDateToISO, parsePatientName } from "../components/utils";
|
|
7
|
+
import { VanillaIcons } from "./components/VanillaIcons";
|
|
8
|
+
import { VanillaSelect } from "./components/VanillaSelect";
|
|
9
|
+
import { VanillaCalendar } from "./components/VanillaCalendar";
|
|
10
|
+
import { injectThemedStyles } from "./components/theme-injector";
|
|
7
11
|
class AppointmentCalendarWidget {
|
|
8
12
|
constructor(container, options) {
|
|
9
13
|
this.mounted = true;
|
|
10
14
|
this.doctors = [];
|
|
15
|
+
this.addressSelect = null;
|
|
16
|
+
this.doctorSelect = null;
|
|
17
|
+
this.calendar = null;
|
|
18
|
+
this.countryCodeSelect = null;
|
|
19
|
+
this.genderSelect = null;
|
|
20
|
+
this.bloodGroupSelect = null;
|
|
21
|
+
injectThemedStyles();
|
|
11
22
|
if (typeof container === "string") {
|
|
12
23
|
const el = document.getElementById(container);
|
|
13
24
|
if (!el) {
|
|
@@ -195,8 +206,27 @@ class AppointmentCalendarWidget {
|
|
|
195
206
|
return false;
|
|
196
207
|
return true;
|
|
197
208
|
}
|
|
209
|
+
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);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
198
224
|
async sendOtp() {
|
|
199
225
|
this.setState({ error: null });
|
|
226
|
+
console.log("sendOtp called - Current state:", {
|
|
227
|
+
countryCode: this.state.countryCode,
|
|
228
|
+
patientPhone: this.state.patientPhone,
|
|
229
|
+
});
|
|
200
230
|
if (!this.state.countryCode) {
|
|
201
231
|
this.setState({ error: "Please enter country code." });
|
|
202
232
|
return;
|
|
@@ -220,6 +250,10 @@ class AppointmentCalendarWidget {
|
|
|
220
250
|
this.setState({ otpSending: true });
|
|
221
251
|
this.render();
|
|
222
252
|
try {
|
|
253
|
+
console.log("Sending OTP to:", {
|
|
254
|
+
countryCode: this.state.countryCode,
|
|
255
|
+
phoneNumber: this.state.patientPhone,
|
|
256
|
+
});
|
|
223
257
|
await PatientService.sendPhoneVerificationOtp({
|
|
224
258
|
countryCode: this.state.countryCode,
|
|
225
259
|
phoneNumber: this.state.patientPhone,
|
|
@@ -227,7 +261,10 @@ class AppointmentCalendarWidget {
|
|
|
227
261
|
this.setState({ otpSent: true, error: null });
|
|
228
262
|
}
|
|
229
263
|
catch (e) {
|
|
230
|
-
|
|
264
|
+
console.error("Send OTP error:", e);
|
|
265
|
+
const msg = e?.response?.data?.message ||
|
|
266
|
+
e?.message ||
|
|
267
|
+
"Failed to send OTP. Please try again.";
|
|
231
268
|
this.setState({ error: msg });
|
|
232
269
|
this.options.onError?.(e);
|
|
233
270
|
}
|
|
@@ -251,6 +288,11 @@ class AppointmentCalendarWidget {
|
|
|
251
288
|
this.setState({ otpVerifying: true });
|
|
252
289
|
this.render();
|
|
253
290
|
try {
|
|
291
|
+
console.log("Verifying OTP with:", {
|
|
292
|
+
countryCode: this.state.countryCode,
|
|
293
|
+
phoneNumber: this.state.patientPhone,
|
|
294
|
+
otpCode: this.state.otpCode,
|
|
295
|
+
});
|
|
254
296
|
await PatientService.verifyPhoneVerificationOtp({
|
|
255
297
|
countryCode: this.state.countryCode,
|
|
256
298
|
phoneNumber: this.state.patientPhone,
|
|
@@ -259,7 +301,10 @@ class AppointmentCalendarWidget {
|
|
|
259
301
|
this.setState({ otpVerified: true, error: null });
|
|
260
302
|
}
|
|
261
303
|
catch (e) {
|
|
262
|
-
|
|
304
|
+
console.error("OTP verification error:", e);
|
|
305
|
+
const msg = e?.response?.data?.message ||
|
|
306
|
+
e?.message ||
|
|
307
|
+
"Invalid OTP code. Please try again.";
|
|
263
308
|
this.setState({ error: msg });
|
|
264
309
|
this.options.onError?.(e);
|
|
265
310
|
}
|
|
@@ -382,9 +427,10 @@ class AppointmentCalendarWidget {
|
|
|
382
427
|
this.state.step = Math.max(0, this.state.step - 1);
|
|
383
428
|
this.render();
|
|
384
429
|
}
|
|
385
|
-
reset() {
|
|
430
|
+
async reset() {
|
|
386
431
|
this.state = { ...INITIAL_STATE };
|
|
387
432
|
this.doctors = [];
|
|
433
|
+
await this.loadAddresses();
|
|
388
434
|
this.render();
|
|
389
435
|
}
|
|
390
436
|
setState(updates) {
|
|
@@ -419,6 +465,112 @@ class AppointmentCalendarWidget {
|
|
|
419
465
|
</div>
|
|
420
466
|
`;
|
|
421
467
|
this.attachEventListeners();
|
|
468
|
+
this.initializeCustomComponents();
|
|
469
|
+
}
|
|
470
|
+
initializeCustomComponents() {
|
|
471
|
+
if (this.state.step === 0) {
|
|
472
|
+
const addressContainer = this.container.querySelector("#medos-address-select-container");
|
|
473
|
+
if (addressContainer && this.state.addresses.length > 0) {
|
|
474
|
+
const addressOptions = this.state.addresses.map((a) => ({
|
|
475
|
+
value: a.id.toString(),
|
|
476
|
+
label: a.label || `Address ${a.id}`,
|
|
477
|
+
}));
|
|
478
|
+
this.addressSelect = new VanillaSelect(addressContainer, addressOptions, {
|
|
479
|
+
placeholder: "Select a location",
|
|
480
|
+
onValueChange: (value) => {
|
|
481
|
+
this.handleAddressChange(Number(value) || null);
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
if (this.state.selectedAddress) {
|
|
485
|
+
this.addressSelect.setValue(this.state.selectedAddress.toString());
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
const doctorContainer = this.container.querySelector("#medos-doctor-select-container");
|
|
489
|
+
if (doctorContainer && this.doctors.length > 0) {
|
|
490
|
+
const doctorOptions = this.doctors.map((d) => ({
|
|
491
|
+
value: d.id.toString(),
|
|
492
|
+
label: d.name + (d.specialty ? ` • ${d.specialty}` : ""),
|
|
493
|
+
}));
|
|
494
|
+
this.doctorSelect = new VanillaSelect(doctorContainer, doctorOptions, {
|
|
495
|
+
placeholder: "Select a doctor",
|
|
496
|
+
onValueChange: (value) => {
|
|
497
|
+
this.state.selectedDoctor = Number(value) || null;
|
|
498
|
+
const selectedDoc = this.doctors.find((d) => d.id === this.state.selectedDoctor);
|
|
499
|
+
if (selectedDoc && selectedDoc.consultationCharge) {
|
|
500
|
+
this.state.consultationCharge = selectedDoc.consultationCharge;
|
|
501
|
+
}
|
|
502
|
+
this.render();
|
|
503
|
+
},
|
|
504
|
+
});
|
|
505
|
+
if (this.state.selectedDoctor) {
|
|
506
|
+
this.doctorSelect.setValue(this.state.selectedDoctor.toString());
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (this.state.step === 1) {
|
|
511
|
+
const calendarContainer = this.container.querySelector("#medos-calendar-container");
|
|
512
|
+
if (calendarContainer) {
|
|
513
|
+
this.calendar = new VanillaCalendar(calendarContainer, {
|
|
514
|
+
selectedDate: this.state.selectedDate || undefined,
|
|
515
|
+
pastDisabled: true,
|
|
516
|
+
onSelect: (date) => {
|
|
517
|
+
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
|
+
}
|
|
540
|
+
},
|
|
541
|
+
});
|
|
542
|
+
if (this.state.countryCode) {
|
|
543
|
+
this.countryCodeSelect.setValue(this.state.countryCode);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (this.state.step === 4) {
|
|
548
|
+
const genderContainer = this.container.querySelector("#medos-gender-container");
|
|
549
|
+
if (genderContainer) {
|
|
550
|
+
this.genderSelect = new VanillaSelect(genderContainer, GENDER_OPTIONS, {
|
|
551
|
+
placeholder: "Select gender",
|
|
552
|
+
onValueChange: (value) => {
|
|
553
|
+
this.state.patientGender = value;
|
|
554
|
+
this.updateSubmitButtonState();
|
|
555
|
+
},
|
|
556
|
+
});
|
|
557
|
+
if (this.state.patientGender) {
|
|
558
|
+
this.genderSelect.setValue(this.state.patientGender);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const bloodGroupContainer = this.container.querySelector("#medos-blood-group-container");
|
|
562
|
+
if (bloodGroupContainer) {
|
|
563
|
+
this.bloodGroupSelect = new VanillaSelect(bloodGroupContainer, BLOOD_GROUP_OPTIONS, {
|
|
564
|
+
placeholder: "Select blood group (optional)",
|
|
565
|
+
onValueChange: (value) => {
|
|
566
|
+
this.state.bloodGroup = value;
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
if (this.state.bloodGroup) {
|
|
570
|
+
this.bloodGroupSelect.setValue(this.state.bloodGroup);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
422
574
|
}
|
|
423
575
|
renderStep() {
|
|
424
576
|
switch (this.state.step) {
|
|
@@ -441,72 +593,122 @@ class AppointmentCalendarWidget {
|
|
|
441
593
|
renderStep0() {
|
|
442
594
|
const canProceed = this.canProceedFromMergedStep();
|
|
443
595
|
return `
|
|
444
|
-
<div class="medos-
|
|
445
|
-
<div class="medos-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
<select class="medos-appointment-select" id="medos-address-select">
|
|
454
|
-
<option value="">-- choose address --</option>
|
|
455
|
-
${this.state.addresses
|
|
456
|
-
.map((a) => `<option value="${this.escapeHtml(a.id.toString())}" ${this.state.selectedAddress === a.id ? "selected" : ""}>${this.escapeHtml(a.label || "")}</option>`)
|
|
457
|
-
.join("")}
|
|
458
|
-
</select>
|
|
459
|
-
`}
|
|
596
|
+
<div class="medos-section-card">
|
|
597
|
+
<div class="medos-section-header">
|
|
598
|
+
${VanillaIcons.mapPin(14)}
|
|
599
|
+
<span class="medos-section-title">Location & Doctor</span>
|
|
600
|
+
</div>
|
|
601
|
+
<div class="medos-section-body">
|
|
602
|
+
<div class="medos-form-group">
|
|
603
|
+
<label class="medos-label">Preferred Location <span class="medos-required">*</span></label>
|
|
604
|
+
<div id="medos-address-select-container"></div>
|
|
460
605
|
</div>
|
|
461
|
-
<div>
|
|
462
|
-
<label class="medos-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
${this.doctors
|
|
473
|
-
.map((d) => `<option value="${this.escapeHtml(d.id.toString())}" ${this.state.selectedDoctor === d.id ? "selected" : ""}>${this.escapeHtml(d.name)}${d.specialty
|
|
474
|
-
? ` (${this.escapeHtml(d.specialty)})`
|
|
475
|
-
: ""}</option>`)
|
|
476
|
-
.join("")}
|
|
477
|
-
</select>
|
|
478
|
-
`}
|
|
606
|
+
<div class="medos-form-group">
|
|
607
|
+
<label class="medos-label">Preferred Doctor <span class="medos-required">*</span></label>
|
|
608
|
+
<div id="medos-doctor-select-container"></div>
|
|
609
|
+
</div>
|
|
610
|
+
<div class="medos-form-group">
|
|
611
|
+
<label class="medos-label">Chief Complaint <span class="medos-optional">(optional)</span></label>
|
|
612
|
+
<textarea
|
|
613
|
+
class="medos-textarea"
|
|
614
|
+
id="medos-chief-complaint"
|
|
615
|
+
placeholder="Enter Chief Complaint or Appointment Notes"
|
|
616
|
+
></textarea>
|
|
479
617
|
</div>
|
|
480
|
-
</div>
|
|
481
|
-
<div class="medos-appointment-actions">
|
|
482
|
-
<button class="medos-appointment-btn medos-appointment-btn-secondary" id="medos-btn-cancel">Cancel</button>
|
|
483
|
-
<button class="medos-appointment-btn medos-appointment-btn-primary" id="medos-btn-next" ${!canProceed ? "disabled" : ""} style="opacity: ${canProceed ? 1 : 0.6}">Next</button>
|
|
484
618
|
</div>
|
|
485
619
|
</div>
|
|
620
|
+
<div class="medos-actions">
|
|
621
|
+
<button class="medos-btn medos-btn-primary" id="medos-btn-next" ${!canProceed ? "disabled" : ""}>Next</button>
|
|
622
|
+
</div>
|
|
486
623
|
`;
|
|
487
624
|
}
|
|
488
625
|
renderStep1() {
|
|
489
626
|
const dateStr = formatDateToISO(this.state.selectedDate);
|
|
627
|
+
const selectedDoctor = this.doctors.find((d) => d.id === this.state.selectedDoctor);
|
|
628
|
+
const selectedDoctorData = this.doctors.find((d) => d.id === this.state.selectedDoctor);
|
|
629
|
+
const consultationCharge = selectedDoctorData?.consultationCharge ||
|
|
630
|
+
selectedDoctor?.consultationCharge ||
|
|
631
|
+
this.state.consultationCharge ||
|
|
632
|
+
"N/A";
|
|
490
633
|
return `
|
|
491
|
-
<div class="medos-
|
|
492
|
-
<
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
634
|
+
<div class="medos-section-card">
|
|
635
|
+
<div class="medos-section-header">
|
|
636
|
+
${VanillaIcons.consultationType(14)}
|
|
637
|
+
<span class="medos-section-title">Consultation Type</span>
|
|
638
|
+
</div>
|
|
639
|
+
<div class="medos-section-body">
|
|
640
|
+
<div class="medos-consultation-options">
|
|
641
|
+
<label class="medos-radio-option ${this.state.consultationMode === "OFFLINE" ? "selected" : ""}">
|
|
642
|
+
<input
|
|
643
|
+
type="radio"
|
|
644
|
+
name="consultationMode"
|
|
645
|
+
value="OFFLINE"
|
|
646
|
+
${this.state.consultationMode === "OFFLINE" ? "checked" : ""}
|
|
647
|
+
class="medos-radio-input"
|
|
648
|
+
/>
|
|
649
|
+
<span class="medos-radio-label">In-Person Visit</span>
|
|
650
|
+
</label>
|
|
651
|
+
<label class="medos-radio-option ${this.state.consultationMode === "ONLINE" ? "selected" : ""}">
|
|
652
|
+
<input
|
|
653
|
+
type="radio"
|
|
654
|
+
name="consultationMode"
|
|
655
|
+
value="ONLINE"
|
|
656
|
+
${this.state.consultationMode === "ONLINE" ? "checked" : ""}
|
|
657
|
+
class="medos-radio-input"
|
|
658
|
+
/>
|
|
659
|
+
<span class="medos-radio-label">Online Consultation</span>
|
|
660
|
+
</label>
|
|
661
|
+
</div>
|
|
662
|
+
<div class="medos-consultation-charge">
|
|
663
|
+
<span class="medos-charge-label">Consultation Charge:</span>
|
|
664
|
+
<span class="medos-charge-value">${consultationCharge !== "N/A"
|
|
665
|
+
? "₹" + consultationCharge
|
|
666
|
+
: consultationCharge}</span>
|
|
667
|
+
</div>
|
|
668
|
+
</div>
|
|
669
|
+
</div>
|
|
670
|
+
|
|
671
|
+
<div class="medos-section-card">
|
|
672
|
+
<div class="medos-section-header">
|
|
673
|
+
${VanillaIcons.dateTime(14)}
|
|
674
|
+
<span class="medos-section-title">Select Date</span>
|
|
675
|
+
</div>
|
|
676
|
+
<div class="medos-section-body">
|
|
677
|
+
<div id="medos-calendar-container"></div>
|
|
497
678
|
</div>
|
|
498
679
|
</div>
|
|
680
|
+
|
|
681
|
+
<div class="medos-actions">
|
|
682
|
+
<button class="medos-btn medos-btn-secondary" id="medos-btn-back">Back</button>
|
|
683
|
+
<button class="medos-btn medos-btn-primary" id="medos-btn-next" ${dateStr ? "" : "disabled"}>Next</button>
|
|
684
|
+
</div>
|
|
499
685
|
`;
|
|
500
686
|
}
|
|
501
687
|
renderStep2() {
|
|
688
|
+
const dateDisplay = this.state.selectedDate
|
|
689
|
+
? this.state.selectedDate.toLocaleDateString("en-US", {
|
|
690
|
+
weekday: "long",
|
|
691
|
+
year: "numeric",
|
|
692
|
+
month: "long",
|
|
693
|
+
day: "numeric",
|
|
694
|
+
})
|
|
695
|
+
: "";
|
|
502
696
|
return `
|
|
503
|
-
<div class="medos-
|
|
504
|
-
<
|
|
505
|
-
|
|
506
|
-
|
|
697
|
+
<div class="medos-section-card">
|
|
698
|
+
<div class="medos-section-header">
|
|
699
|
+
${VanillaIcons.clock(14)}
|
|
700
|
+
<span class="medos-section-title">Select Time Slot</span>
|
|
701
|
+
</div>
|
|
702
|
+
<div class="medos-section-body">
|
|
703
|
+
<div class="medos-selected-date-display">
|
|
704
|
+
${VanillaIcons.dateTime(16)}
|
|
705
|
+
<span>${dateDisplay}</span>
|
|
706
|
+
</div>
|
|
707
|
+
${this.state.slots.length === 0
|
|
708
|
+
? '<div class="medos-empty-slots">No slots available for selected date</div>'
|
|
507
709
|
: `
|
|
508
|
-
|
|
509
|
-
|
|
710
|
+
<div class="medos-slots-grid">
|
|
711
|
+
${this.state.slots
|
|
510
712
|
.map((s) => {
|
|
511
713
|
const start = new Date(s.start).toLocaleTimeString([], {
|
|
512
714
|
hour: "2-digit",
|
|
@@ -519,19 +721,28 @@ class AppointmentCalendarWidget {
|
|
|
519
721
|
const selected = this.state.selectedSlot?.start === s.start &&
|
|
520
722
|
this.state.selectedSlot?.end === s.end;
|
|
521
723
|
return `
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
724
|
+
<div class="medos-slot-card ${selected ? "selected" : ""}"
|
|
725
|
+
data-slot-id="${this.escapeHtml(s.id || `${s.start}-${s.end}`)}"
|
|
726
|
+
data-slot-start="${this.escapeHtml(s.start)}"
|
|
727
|
+
data-slot-end="${this.escapeHtml(s.end)}">
|
|
728
|
+
<span class="medos-slot-time">${start}</span>
|
|
729
|
+
<span class="medos-slot-separator">-</span>
|
|
730
|
+
<span class="medos-slot-time">${end}</span>
|
|
731
|
+
${selected
|
|
732
|
+
? `<span class="medos-slot-check">${VanillaIcons.check(14)}</span>`
|
|
733
|
+
: ""}
|
|
734
|
+
</div>
|
|
735
|
+
`;
|
|
526
736
|
})
|
|
527
737
|
.join("")}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
<div class="medos-appointment-actions">
|
|
531
|
-
<button class="medos-appointment-btn medos-appointment-btn-secondary" id="medos-btn-back">Back</button>
|
|
532
|
-
<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>
|
|
738
|
+
</div>
|
|
739
|
+
`}
|
|
533
740
|
</div>
|
|
534
741
|
</div>
|
|
742
|
+
<div class="medos-actions">
|
|
743
|
+
<button class="medos-btn medos-btn-secondary" id="medos-btn-back">Back</button>
|
|
744
|
+
<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>
|
|
745
|
+
</div>
|
|
535
746
|
`;
|
|
536
747
|
}
|
|
537
748
|
renderStep3() {
|
|
@@ -540,47 +751,87 @@ class AppointmentCalendarWidget {
|
|
|
540
751
|
const canSendOtp = countryCodeValid && phoneValid && !this.state.otpSending;
|
|
541
752
|
if (!this.state.otpSent) {
|
|
542
753
|
return `
|
|
543
|
-
<div class="medos-
|
|
544
|
-
<
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
754
|
+
<div class="medos-section-card">
|
|
755
|
+
<div class="medos-section-header">
|
|
756
|
+
${VanillaIcons.phone(14)}
|
|
757
|
+
<span class="medos-section-title">Phone Verification</span>
|
|
758
|
+
</div>
|
|
759
|
+
<div class="medos-section-body">
|
|
760
|
+
<p class="medos-section-description">Please enter your phone number for verification</p>
|
|
761
|
+
<div class="medos-phone-input-row">
|
|
762
|
+
<div class="medos-country-code-wrapper">
|
|
763
|
+
<div id="medos-country-code-container"></div>
|
|
764
|
+
</div>
|
|
765
|
+
<div class="medos-phone-wrapper">
|
|
766
|
+
<input
|
|
767
|
+
type="tel"
|
|
768
|
+
class="medos-input"
|
|
769
|
+
id="medos-phone"
|
|
770
|
+
placeholder="Enter phone number"
|
|
771
|
+
value="${this.escapeHtml(this.state.patientPhone)}"
|
|
772
|
+
maxlength="15"
|
|
773
|
+
/>
|
|
774
|
+
</div>
|
|
775
|
+
</div>
|
|
776
|
+
${this.state.patientPhone && !phoneValid
|
|
777
|
+
? '<div class="medos-validation-error">Phone number should be 7-15 digits</div>'
|
|
553
778
|
: ""}
|
|
554
|
-
<div class="medos-appointment-actions">
|
|
555
|
-
<button class="medos-appointment-btn medos-appointment-btn-secondary" id="medos-btn-back">Back</button>
|
|
556
|
-
<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>
|
|
557
779
|
</div>
|
|
558
780
|
</div>
|
|
781
|
+
<div class="medos-actions">
|
|
782
|
+
<button class="medos-btn medos-btn-secondary" id="medos-btn-back">Back</button>
|
|
783
|
+
<button class="medos-btn medos-btn-primary" id="medos-btn-send-otp" ${canSendOtp ? "" : "disabled"}>${this.state.otpSending ? "Sending..." : "Send OTP"}</button>
|
|
784
|
+
</div>
|
|
559
785
|
`;
|
|
560
786
|
}
|
|
561
787
|
if (this.state.otpVerified) {
|
|
562
788
|
return `
|
|
563
|
-
<div class="medos-
|
|
564
|
-
<div class="medos-
|
|
565
|
-
|
|
566
|
-
<
|
|
567
|
-
|
|
789
|
+
<div class="medos-section-card">
|
|
790
|
+
<div class="medos-section-header">
|
|
791
|
+
${VanillaIcons.phone(14)}
|
|
792
|
+
<span class="medos-section-title">Phone Verification</span>
|
|
793
|
+
</div>
|
|
794
|
+
<div class="medos-section-body">
|
|
795
|
+
<div class="medos-verified-badge">
|
|
796
|
+
${VanillaIcons.successBadge(20)}
|
|
797
|
+
<span>Phone verified successfully</span>
|
|
798
|
+
</div>
|
|
799
|
+
<div class="medos-verified-number">${this.escapeHtml(this.state.countryCode)} ${this.escapeHtml(this.state.patientPhone)}</div>
|
|
568
800
|
</div>
|
|
569
801
|
</div>
|
|
802
|
+
<div class="medos-actions">
|
|
803
|
+
<button class="medos-btn medos-btn-secondary" id="medos-btn-back">Back</button>
|
|
804
|
+
<button class="medos-btn medos-btn-primary" id="medos-btn-next">Continue</button>
|
|
805
|
+
</div>
|
|
570
806
|
`;
|
|
571
807
|
}
|
|
572
808
|
return `
|
|
573
|
-
<div class="medos-
|
|
574
|
-
<
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
<
|
|
580
|
-
|
|
581
|
-
|
|
809
|
+
<div class="medos-section-card">
|
|
810
|
+
<div class="medos-section-header">
|
|
811
|
+
${VanillaIcons.phone(14)}
|
|
812
|
+
<span class="medos-section-title">Enter OTP</span>
|
|
813
|
+
</div>
|
|
814
|
+
<div class="medos-section-body">
|
|
815
|
+
<p class="medos-section-description">Enter the 6-digit code sent to ${this.escapeHtml(this.state.countryCode)} ${this.escapeHtml(this.state.patientPhone)}</p>
|
|
816
|
+
<div class="medos-otp-input-wrapper">
|
|
817
|
+
<input
|
|
818
|
+
type="text"
|
|
819
|
+
class="medos-input medos-otp-input"
|
|
820
|
+
id="medos-otp"
|
|
821
|
+
placeholder="Enter 6-digit OTP"
|
|
822
|
+
value="${this.escapeHtml(this.state.otpCode)}"
|
|
823
|
+
maxlength="6"
|
|
824
|
+
/>
|
|
825
|
+
</div>
|
|
826
|
+
<button class="medos-link-btn" id="medos-btn-change-number">Change phone number</button>
|
|
582
827
|
</div>
|
|
583
828
|
</div>
|
|
829
|
+
<div class="medos-actions">
|
|
830
|
+
<button class="medos-btn medos-btn-secondary" id="medos-btn-resend-otp">Resend OTP</button>
|
|
831
|
+
<button class="medos-btn medos-btn-primary" id="medos-btn-verify-otp" ${this.state.otpCode.length === 6 && !this.state.otpVerifying
|
|
832
|
+
? ""
|
|
833
|
+
: "disabled"}>${this.state.otpVerifying ? "Verifying..." : "Verify OTP"}</button>
|
|
834
|
+
</div>
|
|
584
835
|
`;
|
|
585
836
|
}
|
|
586
837
|
renderStep4() {
|
|
@@ -592,97 +843,211 @@ class AppointmentCalendarWidget {
|
|
|
592
843
|
this.state.patientZipcode &&
|
|
593
844
|
this.state.otpVerified;
|
|
594
845
|
return `
|
|
595
|
-
<div class="medos-
|
|
596
|
-
<
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
<
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
<input type="text" class="medos-appointment-input" id="medos-patient-address" placeholder="Street address, building name, etc." value="${this.escapeHtml(this.state.patientAddress)}" />
|
|
611
|
-
<div class="medos-appointment-form-grid">
|
|
612
|
-
<div>
|
|
613
|
-
<label class="medos-appointment-label">City *</label>
|
|
614
|
-
<input type="text" class="medos-appointment-input" id="medos-patient-city" placeholder="City" value="${this.escapeHtml(this.state.patientCity)}" />
|
|
846
|
+
<div class="medos-section-card">
|
|
847
|
+
<div class="medos-section-header">
|
|
848
|
+
${VanillaIcons.user(14)}
|
|
849
|
+
<span class="medos-section-title">Patient Information</span>
|
|
850
|
+
</div>
|
|
851
|
+
<div class="medos-section-body">
|
|
852
|
+
<div class="medos-form-row">
|
|
853
|
+
<div class="medos-form-group medos-form-group-flex">
|
|
854
|
+
<label class="medos-label">Full Name <span class="medos-required">*</span></label>
|
|
855
|
+
<input type="text" class="medos-input" id="medos-patient-name" placeholder="Enter full name" value="${this.escapeHtml(this.state.patientName)}" />
|
|
856
|
+
</div>
|
|
857
|
+
<div class="medos-form-group medos-form-group-small">
|
|
858
|
+
<label class="medos-label">Age</label>
|
|
859
|
+
<input type="number" class="medos-input" id="medos-patient-age" placeholder="Age" value="${this.escapeHtml(this.state.patientAge)}" />
|
|
860
|
+
</div>
|
|
615
861
|
</div>
|
|
616
|
-
<div>
|
|
617
|
-
<
|
|
618
|
-
|
|
862
|
+
<div class="medos-form-row">
|
|
863
|
+
<div class="medos-form-group medos-form-group-flex">
|
|
864
|
+
<label class="medos-label">Email</label>
|
|
865
|
+
<input type="email" class="medos-input" id="medos-patient-email" placeholder="patient@example.com" value="${this.escapeHtml(this.state.patientEmail)}" />
|
|
866
|
+
</div>
|
|
867
|
+
<div class="medos-form-group medos-form-group-small">
|
|
868
|
+
<label class="medos-label">Gender</label>
|
|
869
|
+
<div id="medos-gender-container"></div>
|
|
870
|
+
</div>
|
|
871
|
+
</div>
|
|
872
|
+
<div class="medos-form-group">
|
|
873
|
+
<label class="medos-label">Blood Group <span class="medos-optional">(optional)</span></label>
|
|
874
|
+
<div id="medos-blood-group-container"></div>
|
|
619
875
|
</div>
|
|
620
876
|
</div>
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
877
|
+
</div>
|
|
878
|
+
|
|
879
|
+
<div class="medos-section-card">
|
|
880
|
+
<div class="medos-section-header">
|
|
881
|
+
${VanillaIcons.mapPin(14)}
|
|
882
|
+
<span class="medos-section-title">Address Details</span>
|
|
883
|
+
</div>
|
|
884
|
+
<div class="medos-section-body">
|
|
885
|
+
<div class="medos-form-group">
|
|
886
|
+
<label class="medos-label">Address Line 1 <span class="medos-required">*</span></label>
|
|
887
|
+
<input type="text" class="medos-input" id="medos-patient-address" placeholder="Street address, building name, etc." value="${this.escapeHtml(this.state.patientAddress)}" />
|
|
625
888
|
</div>
|
|
626
|
-
<div>
|
|
627
|
-
<
|
|
628
|
-
|
|
889
|
+
<div class="medos-form-row">
|
|
890
|
+
<div class="medos-form-group">
|
|
891
|
+
<label class="medos-label">City <span class="medos-required">*</span></label>
|
|
892
|
+
<input type="text" class="medos-input" id="medos-patient-city" placeholder="City" value="${this.escapeHtml(this.state.patientCity)}" />
|
|
893
|
+
</div>
|
|
894
|
+
<div class="medos-form-group">
|
|
895
|
+
<label class="medos-label">State <span class="medos-required">*</span></label>
|
|
896
|
+
<input type="text" class="medos-input" id="medos-patient-state" placeholder="State" value="${this.escapeHtml(this.state.patientState)}" />
|
|
897
|
+
</div>
|
|
898
|
+
</div>
|
|
899
|
+
<div class="medos-form-row">
|
|
900
|
+
<div class="medos-form-group">
|
|
901
|
+
<label class="medos-label">Country <span class="medos-required">*</span></label>
|
|
902
|
+
<input type="text" class="medos-input" id="medos-patient-country" placeholder="Country" value="${this.escapeHtml(this.state.patientCountry)}" />
|
|
903
|
+
</div>
|
|
904
|
+
<div class="medos-form-group">
|
|
905
|
+
<label class="medos-label">Zipcode <span class="medos-required">*</span></label>
|
|
906
|
+
<input type="text" class="medos-input" id="medos-patient-zipcode" placeholder="Zipcode" value="${this.escapeHtml(this.state.patientZipcode)}" />
|
|
907
|
+
</div>
|
|
908
|
+
</div>
|
|
909
|
+
<div class="medos-form-group">
|
|
910
|
+
<label class="medos-label">Landmark <span class="medos-optional">(optional)</span></label>
|
|
911
|
+
<input type="text" class="medos-input" id="medos-patient-landmark" placeholder="Nearby landmark" value="${this.escapeHtml(this.state.patientLandmark)}" />
|
|
629
912
|
</div>
|
|
630
|
-
</div>
|
|
631
|
-
<label class="medos-appointment-label" style="margin-top: 12px">Landmark (Optional)</label>
|
|
632
|
-
<input type="text" class="medos-appointment-input" id="medos-patient-landmark" placeholder="Nearby landmark" value="${this.escapeHtml(this.state.patientLandmark)}" />
|
|
633
|
-
<label class="medos-appointment-label" style="margin-top: 12px">Problem Facing</label>
|
|
634
|
-
<textarea class="medos-appointment-textarea" id="medos-problem-facing" placeholder="Describe the problem you're facing">${this.escapeHtml(this.state.patientName)}</textarea>
|
|
635
|
-
<div class="medos-appointment-actions">
|
|
636
|
-
<button class="medos-appointment-btn medos-appointment-btn-secondary" id="medos-btn-back">Back</button>
|
|
637
|
-
<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>
|
|
638
913
|
</div>
|
|
639
914
|
</div>
|
|
915
|
+
|
|
916
|
+
<div class="medos-actions">
|
|
917
|
+
<button class="medos-btn medos-btn-secondary" id="medos-btn-back">Back</button>
|
|
918
|
+
<button class="medos-btn medos-btn-primary" id="medos-btn-submit" ${canSubmit && !this.state.loading ? "" : "disabled"}>${this.state.loading ? "Booking..." : "Book Appointment"}</button>
|
|
919
|
+
</div>
|
|
640
920
|
`;
|
|
641
921
|
}
|
|
922
|
+
renderSuccessIcon() {
|
|
923
|
+
return `
|
|
924
|
+
<div style="position: relative; display: inline-block;">
|
|
925
|
+
<svg width="64" height="64" viewBox="0 0 41 41" fill="none">
|
|
926
|
+
<path
|
|
927
|
+
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"
|
|
928
|
+
fill="#006E0F"
|
|
929
|
+
/>
|
|
930
|
+
</svg>
|
|
931
|
+
<svg width="16" height="12" viewBox="0 0 16 12" fill="none" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
|
|
932
|
+
<path
|
|
933
|
+
d="M5.472 11.544L0 6.072L1.368 4.704L5.472 8.808L14.28 0L15.648 1.368L5.472 11.544Z"
|
|
934
|
+
fill="white"
|
|
935
|
+
/>
|
|
936
|
+
</svg>
|
|
937
|
+
</div>
|
|
938
|
+
`;
|
|
939
|
+
}
|
|
940
|
+
formatDate(dateStr) {
|
|
941
|
+
try {
|
|
942
|
+
const date = new Date(dateStr);
|
|
943
|
+
return date.toLocaleDateString("en-US", {
|
|
944
|
+
weekday: "long",
|
|
945
|
+
year: "numeric",
|
|
946
|
+
month: "long",
|
|
947
|
+
day: "numeric",
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
catch {
|
|
951
|
+
return dateStr;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
formatTime(timeStr) {
|
|
955
|
+
try {
|
|
956
|
+
const time = new Date(`2000-01-01T${timeStr}`);
|
|
957
|
+
return time.toLocaleTimeString("en-US", {
|
|
958
|
+
hour: "numeric",
|
|
959
|
+
minute: "2-digit",
|
|
960
|
+
hour12: true,
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
catch {
|
|
964
|
+
return timeStr;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
calculateDuration() {
|
|
968
|
+
if (this.state.selectedSlot) {
|
|
969
|
+
const start = new Date(this.state.selectedSlot.start);
|
|
970
|
+
const end = new Date(this.state.selectedSlot.end);
|
|
971
|
+
const diffMs = end.getTime() - start.getTime();
|
|
972
|
+
return Math.round(diffMs / (1000 * 60));
|
|
973
|
+
}
|
|
974
|
+
return 60;
|
|
975
|
+
}
|
|
642
976
|
renderStep5() {
|
|
977
|
+
const duration = this.calculateDuration();
|
|
978
|
+
const selectedAddress = this.state.addresses.find((addr) => addr.id === this.state.selectedAddress);
|
|
979
|
+
const appointmentDate = this.state.selectedDate
|
|
980
|
+
? this.formatDate(formatDateToISO(this.state.selectedDate))
|
|
981
|
+
: "";
|
|
982
|
+
const startTime = this.state.selectedSlot?.start
|
|
983
|
+
? this.formatTime(new Date(this.state.selectedSlot.start).toTimeString().slice(0, 5))
|
|
984
|
+
: "";
|
|
643
985
|
return `
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
986
|
+
<div style="display: flex; flex-direction: column; padding: 0; font-family: Arial, sans-serif; background: #f8f9fa; min-height: 500px;">
|
|
987
|
+
<!-- Header with border -->
|
|
988
|
+
<div style="padding: 20px 24px; font-size: 24px; font-weight: bold; color: #1a365d; border-bottom: 2px solid #e2e8f0; background: white;">
|
|
989
|
+
Appointment Confirmed
|
|
990
|
+
</div>
|
|
991
|
+
|
|
992
|
+
<!-- Main content with border -->
|
|
993
|
+
<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;">
|
|
994
|
+
<!-- Success Title -->
|
|
995
|
+
<h2 style="font-size: 20px; font-weight: 600; color: #006E0F; margin: 0 0 24px 0;">
|
|
996
|
+
Appointment Confirmed
|
|
997
|
+
</h2>
|
|
998
|
+
|
|
999
|
+
<!-- Success Icon -->
|
|
1000
|
+
<div style="margin-bottom: 32px;">
|
|
1001
|
+
${this.renderSuccessIcon()}
|
|
1002
|
+
</div>
|
|
1003
|
+
|
|
1004
|
+
<!-- Appointment Details -->
|
|
1005
|
+
<div style="width: 100%; max-width: 500px; margin-bottom: 32px;">
|
|
1006
|
+
<h3 style="font-size: 18px; font-weight: 600; color: #006E0F; margin-bottom: 24px;">
|
|
1007
|
+
Appointment Details
|
|
1008
|
+
</h3>
|
|
1009
|
+
|
|
1010
|
+
<div style="display: flex; flex-direction: column; gap: 12px; font-size: 16px; line-height: 1.6; color: #4a5568;">
|
|
1011
|
+
<div><strong style="color: #006E0F;">Patient:</strong> ${this.escapeHtml(this.state.patientName || "Patient")}</div>
|
|
1012
|
+
<div><strong style="color: #006E0F;">Visitation Type:</strong> ${this.state.consultationMode === "ONLINE"
|
|
1013
|
+
? "Online Consultation"
|
|
1014
|
+
: "General"}</div>
|
|
1015
|
+
${appointmentDate
|
|
1016
|
+
? `<div><strong style="color: #006E0F;">Date:</strong> ${appointmentDate}</div>`
|
|
1017
|
+
: ""}
|
|
1018
|
+
${startTime
|
|
1019
|
+
? `<div><strong style="color: #006E0F;">Time:</strong> ${startTime}</div>`
|
|
1020
|
+
: ""}
|
|
1021
|
+
<div><strong style="color: #006E0F;">Duration:</strong> ~${duration} minutes</div>
|
|
1022
|
+
${selectedAddress?.label
|
|
1023
|
+
? `<div><strong style="color: #006E0F;">Location:</strong> ${this.escapeHtml(selectedAddress.label)}</div>`
|
|
1024
|
+
: ""}
|
|
1025
|
+
</div>
|
|
649
1026
|
</div>
|
|
650
|
-
|
|
651
|
-
|
|
1027
|
+
|
|
1028
|
+
<!-- Confirmation Message -->
|
|
1029
|
+
<div style="font-size: 16px; font-style: italic; color: #718096; text-align: center; max-width: 600px; line-height: 1.5;">
|
|
1030
|
+
A confirmation email has been sent to the Patient's Email address.
|
|
652
1031
|
</div>
|
|
653
1032
|
</div>
|
|
654
|
-
|
|
1033
|
+
</div>
|
|
1034
|
+
`;
|
|
655
1035
|
}
|
|
656
1036
|
attachEventListeners() {
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const target = e.target;
|
|
661
|
-
this.handleAddressChange(Number(target.value) || null);
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
const doctorSelect = this.container.querySelector("#medos-doctor-select");
|
|
665
|
-
if (doctorSelect) {
|
|
666
|
-
doctorSelect.addEventListener("change", (e) => {
|
|
667
|
-
const target = e.target;
|
|
668
|
-
this.state.selectedDoctor = Number(target.value) || null;
|
|
669
|
-
this.render();
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
const dateInput = this.container.querySelector("#medos-date-input");
|
|
673
|
-
if (dateInput) {
|
|
674
|
-
dateInput.addEventListener("change", (e) => {
|
|
1037
|
+
const consultationRadios = this.container.querySelectorAll('input[name="consultationMode"]');
|
|
1038
|
+
consultationRadios.forEach((radio) => {
|
|
1039
|
+
radio.addEventListener("change", (e) => {
|
|
675
1040
|
const target = e.target;
|
|
676
|
-
this.state.
|
|
1041
|
+
this.state.consultationMode = target.value;
|
|
677
1042
|
this.render();
|
|
678
1043
|
});
|
|
679
|
-
}
|
|
680
|
-
const slotCards = this.container.querySelectorAll(".medos-
|
|
1044
|
+
});
|
|
1045
|
+
const slotCards = this.container.querySelectorAll(".medos-slot-card");
|
|
681
1046
|
slotCards.forEach((card) => {
|
|
682
1047
|
card.addEventListener("click", () => {
|
|
683
|
-
const slotId = card.
|
|
684
|
-
const slotStart = card.
|
|
685
|
-
const slotEnd = card.
|
|
1048
|
+
const slotId = card.dataset.slotId;
|
|
1049
|
+
const slotStart = card.dataset.slotStart;
|
|
1050
|
+
const slotEnd = card.dataset.slotEnd;
|
|
686
1051
|
if (slotStart && slotEnd) {
|
|
687
1052
|
this.state.selectedSlot = {
|
|
688
1053
|
start: slotStart,
|
|
@@ -704,7 +1069,11 @@ class AppointmentCalendarWidget {
|
|
|
704
1069
|
value = value.replace(/[^\d+]/g, "");
|
|
705
1070
|
this.state.countryCode = value;
|
|
706
1071
|
target.value = value;
|
|
707
|
-
this.
|
|
1072
|
+
const sendOtpBtn = this.container.querySelector("#medos-btn-send-otp");
|
|
1073
|
+
if (sendOtpBtn) {
|
|
1074
|
+
const canSendOtp = this.state.countryCode && this.state.patientPhone.length >= 10;
|
|
1075
|
+
sendOtpBtn.disabled = !canSendOtp;
|
|
1076
|
+
}
|
|
708
1077
|
});
|
|
709
1078
|
}
|
|
710
1079
|
const phoneInput = this.container.querySelector("#medos-phone");
|
|
@@ -713,7 +1082,11 @@ class AppointmentCalendarWidget {
|
|
|
713
1082
|
const target = e.target;
|
|
714
1083
|
this.state.patientPhone = target.value.replace(/\D/g, "");
|
|
715
1084
|
target.value = this.state.patientPhone;
|
|
716
|
-
this.
|
|
1085
|
+
const sendOtpBtn = this.container.querySelector("#medos-btn-send-otp");
|
|
1086
|
+
if (sendOtpBtn) {
|
|
1087
|
+
const canSendOtp = this.state.countryCode && this.state.patientPhone.length >= 10;
|
|
1088
|
+
sendOtpBtn.disabled = !canSendOtp;
|
|
1089
|
+
}
|
|
717
1090
|
});
|
|
718
1091
|
}
|
|
719
1092
|
const otpInput = this.container.querySelector("#medos-otp");
|
|
@@ -721,7 +1094,10 @@ class AppointmentCalendarWidget {
|
|
|
721
1094
|
otpInput.addEventListener("input", (e) => {
|
|
722
1095
|
const target = e.target;
|
|
723
1096
|
this.state.otpCode = target.value;
|
|
724
|
-
this.
|
|
1097
|
+
const verifyBtn = this.container.querySelector("#medos-btn-verify-otp");
|
|
1098
|
+
if (verifyBtn) {
|
|
1099
|
+
verifyBtn.disabled = !(this.state.otpCode.length === 6 && !this.state.otpVerifying);
|
|
1100
|
+
}
|
|
725
1101
|
});
|
|
726
1102
|
}
|
|
727
1103
|
const patientNameInput = this.container.querySelector("#medos-patient-name");
|
|
@@ -729,6 +1105,7 @@ class AppointmentCalendarWidget {
|
|
|
729
1105
|
patientNameInput.addEventListener("input", (e) => {
|
|
730
1106
|
const target = e.target;
|
|
731
1107
|
this.state.patientName = target.value;
|
|
1108
|
+
this.updateSubmitButtonState();
|
|
732
1109
|
});
|
|
733
1110
|
}
|
|
734
1111
|
const patientAgeInput = this.container.querySelector("#medos-patient-age");
|
|
@@ -736,6 +1113,7 @@ class AppointmentCalendarWidget {
|
|
|
736
1113
|
patientAgeInput.addEventListener("input", (e) => {
|
|
737
1114
|
const target = e.target;
|
|
738
1115
|
this.state.patientAge = target.value;
|
|
1116
|
+
this.updateSubmitButtonState();
|
|
739
1117
|
});
|
|
740
1118
|
}
|
|
741
1119
|
const patientEmailInput = this.container.querySelector("#medos-patient-email");
|
|
@@ -743,6 +1121,7 @@ class AppointmentCalendarWidget {
|
|
|
743
1121
|
patientEmailInput.addEventListener("input", (e) => {
|
|
744
1122
|
const target = e.target;
|
|
745
1123
|
this.state.patientEmail = target.value;
|
|
1124
|
+
this.updateSubmitButtonState();
|
|
746
1125
|
});
|
|
747
1126
|
}
|
|
748
1127
|
const patientGenderSelect = this.container.querySelector("#medos-patient-gender");
|
|
@@ -757,6 +1136,7 @@ class AppointmentCalendarWidget {
|
|
|
757
1136
|
patientAddressInput.addEventListener("input", (e) => {
|
|
758
1137
|
const target = e.target;
|
|
759
1138
|
this.state.patientAddress = target.value;
|
|
1139
|
+
this.updateSubmitButtonState();
|
|
760
1140
|
});
|
|
761
1141
|
}
|
|
762
1142
|
const patientCityInput = this.container.querySelector("#medos-patient-city");
|
|
@@ -764,6 +1144,7 @@ class AppointmentCalendarWidget {
|
|
|
764
1144
|
patientCityInput.addEventListener("input", (e) => {
|
|
765
1145
|
const target = e.target;
|
|
766
1146
|
this.state.patientCity = target.value;
|
|
1147
|
+
this.updateSubmitButtonState();
|
|
767
1148
|
});
|
|
768
1149
|
}
|
|
769
1150
|
const patientStateInput = this.container.querySelector("#medos-patient-state");
|
|
@@ -771,6 +1152,7 @@ class AppointmentCalendarWidget {
|
|
|
771
1152
|
patientStateInput.addEventListener("input", (e) => {
|
|
772
1153
|
const target = e.target;
|
|
773
1154
|
this.state.patientState = target.value;
|
|
1155
|
+
this.updateSubmitButtonState();
|
|
774
1156
|
});
|
|
775
1157
|
}
|
|
776
1158
|
const patientCountryInput = this.container.querySelector("#medos-patient-country");
|
|
@@ -778,6 +1160,7 @@ class AppointmentCalendarWidget {
|
|
|
778
1160
|
patientCountryInput.addEventListener("input", (e) => {
|
|
779
1161
|
const target = e.target;
|
|
780
1162
|
this.state.patientCountry = target.value;
|
|
1163
|
+
this.updateSubmitButtonState();
|
|
781
1164
|
});
|
|
782
1165
|
}
|
|
783
1166
|
const patientZipcodeInput = this.container.querySelector("#medos-patient-zipcode");
|
|
@@ -785,6 +1168,7 @@ class AppointmentCalendarWidget {
|
|
|
785
1168
|
patientZipcodeInput.addEventListener("input", (e) => {
|
|
786
1169
|
const target = e.target;
|
|
787
1170
|
this.state.patientZipcode = target.value;
|
|
1171
|
+
this.updateSubmitButtonState();
|
|
788
1172
|
});
|
|
789
1173
|
}
|
|
790
1174
|
const patientLandmarkInput = this.container.querySelector("#medos-patient-landmark");
|
|
@@ -796,7 +1180,11 @@ class AppointmentCalendarWidget {
|
|
|
796
1180
|
}
|
|
797
1181
|
const nextBtn = this.container.querySelector("#medos-btn-next");
|
|
798
1182
|
if (nextBtn) {
|
|
799
|
-
nextBtn.addEventListener("click", () =>
|
|
1183
|
+
nextBtn.addEventListener("click", () => {
|
|
1184
|
+
if (!nextBtn.disabled) {
|
|
1185
|
+
this.goToNext();
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
800
1188
|
}
|
|
801
1189
|
const backBtn = this.container.querySelector("#medos-btn-back");
|
|
802
1190
|
if (backBtn) {
|
|
@@ -804,11 +1192,19 @@ class AppointmentCalendarWidget {
|
|
|
804
1192
|
}
|
|
805
1193
|
const sendOtpBtn = this.container.querySelector("#medos-btn-send-otp");
|
|
806
1194
|
if (sendOtpBtn) {
|
|
807
|
-
sendOtpBtn.addEventListener("click", () =>
|
|
1195
|
+
sendOtpBtn.addEventListener("click", () => {
|
|
1196
|
+
if (!sendOtpBtn.disabled) {
|
|
1197
|
+
this.sendOtp();
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
808
1200
|
}
|
|
809
1201
|
const verifyOtpBtn = this.container.querySelector("#medos-btn-verify-otp");
|
|
810
1202
|
if (verifyOtpBtn) {
|
|
811
|
-
verifyOtpBtn.addEventListener("click", () =>
|
|
1203
|
+
verifyOtpBtn.addEventListener("click", () => {
|
|
1204
|
+
if (!verifyOtpBtn.disabled) {
|
|
1205
|
+
this.verifyOtp();
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
812
1208
|
}
|
|
813
1209
|
const changeNumberBtn = this.container.querySelector("#medos-btn-change-number");
|
|
814
1210
|
if (changeNumberBtn) {
|
|
@@ -817,9 +1213,19 @@ class AppointmentCalendarWidget {
|
|
|
817
1213
|
this.render();
|
|
818
1214
|
});
|
|
819
1215
|
}
|
|
1216
|
+
const resendOtpBtn = this.container.querySelector("#medos-btn-resend-otp");
|
|
1217
|
+
if (resendOtpBtn) {
|
|
1218
|
+
resendOtpBtn.addEventListener("click", () => {
|
|
1219
|
+
this.sendOtp();
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
820
1222
|
const submitBtn = this.container.querySelector("#medos-btn-submit");
|
|
821
1223
|
if (submitBtn) {
|
|
822
|
-
submitBtn.addEventListener("click", () =>
|
|
1224
|
+
submitBtn.addEventListener("click", () => {
|
|
1225
|
+
if (!submitBtn.disabled) {
|
|
1226
|
+
this.submitAppointment();
|
|
1227
|
+
}
|
|
1228
|
+
});
|
|
823
1229
|
}
|
|
824
1230
|
const bookAnotherBtn = this.container.querySelector("#medos-btn-book-another");
|
|
825
1231
|
if (bookAnotherBtn) {
|