medos-sdk 1.0.1 → 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 (113) hide show
  1. package/README.md +208 -73
  2. package/dist/client/MedosClient.d.ts +1 -1
  3. package/dist/client/MedosClient.js +1 -1
  4. package/dist/components/AppointmentCalender.d.ts +1 -4
  5. package/dist/components/AppointmentCalender.js +282 -486
  6. package/dist/components/AppointmentDateTimeModal.d.ts +14 -0
  7. package/dist/components/AppointmentDateTimeModal.js +206 -0
  8. package/dist/components/ConfigurableCard.d.ts +12 -0
  9. package/dist/components/ConfigurableCard.js +29 -0
  10. package/dist/components/DoctorSelectModal.d.ts +7 -0
  11. package/dist/components/DoctorSelectModal.js +80 -0
  12. package/dist/components/Icons/Check.d.ts +6 -0
  13. package/dist/components/Icons/Check.js +2 -0
  14. package/dist/components/Icons/ChevronDownIcon.d.ts +4 -0
  15. package/dist/components/Icons/ChevronDownIcon.js +2 -0
  16. package/dist/components/Icons/ChevronLeft.d.ts +3 -0
  17. package/dist/components/Icons/ChevronLeft.js +3 -0
  18. package/dist/components/Icons/ChevronRight.d.ts +3 -0
  19. package/dist/components/Icons/ChevronRight.js +3 -0
  20. package/dist/components/Icons/ConfirmationCheck.d.ts +1 -0
  21. package/dist/components/Icons/ConfirmationCheck.js +9 -0
  22. package/dist/components/Icons/ConsultationType.d.ts +1 -0
  23. package/dist/components/Icons/ConsultationType.js +2 -0
  24. package/dist/components/Icons/Date&TimeIcon.d.ts +1 -0
  25. package/dist/components/Icons/Date&TimeIcon.js +2 -0
  26. package/dist/components/Icons/MapIcon.d.ts +1 -0
  27. package/dist/components/Icons/MapIcon.js +2 -0
  28. package/dist/components/Icons/PaymentMethodIcon.d.ts +1 -0
  29. package/dist/components/Icons/PaymentMethodIcon.js +2 -0
  30. package/dist/components/Icons/UserIcon.d.ts +1 -0
  31. package/dist/components/Icons/UserIcon.js +2 -0
  32. package/dist/components/PatientDetailsStep.d.ts +3 -0
  33. package/dist/components/PatientDetailsStep.js +76 -0
  34. package/dist/components/PhoneVerificationStep.d.ts +3 -0
  35. package/dist/components/PhoneVerificationStep.js +39 -0
  36. package/dist/components/SuccessStep.d.ts +3 -0
  37. package/dist/components/SuccessStep.js +17 -0
  38. package/dist/components/custom-calendar.d.ts +5 -0
  39. package/dist/components/custom-calendar.js +153 -0
  40. package/dist/components/styles.d.ts +6 -0
  41. package/dist/components/styles.js +257 -0
  42. package/dist/components/types.d.ts +182 -0
  43. package/dist/components/types.js +55 -0
  44. package/dist/components/ui/select.d.ts +10 -0
  45. package/dist/components/ui/select.js +21 -0
  46. package/dist/components/uiComponents/SelectDropdown.d.ts +41 -0
  47. package/dist/components/uiComponents/SelectDropdown.js +302 -0
  48. package/dist/components/utils.d.ts +5 -0
  49. package/dist/components/utils.js +15 -0
  50. package/dist/components/validation.d.ts +2 -0
  51. package/dist/components/validation.js +7 -0
  52. package/dist/context/TemplateContext.d.ts +12 -0
  53. package/dist/context/TemplateContext.js +19 -0
  54. package/dist/core/index.d.ts +8 -0
  55. package/dist/core/index.js +8 -0
  56. package/dist/lib/templateUtils.d.ts +3 -0
  57. package/dist/lib/templateUtils.js +28 -0
  58. package/dist/react/index.d.ts +2 -0
  59. package/dist/react/index.js +2 -0
  60. package/dist/services/AppointmentService.d.ts +9 -10
  61. package/dist/services/AppointmentService.js +12 -10
  62. package/dist/templates/registry.d.ts +12 -0
  63. package/dist/templates/registry.js +58 -0
  64. package/dist/vanilla/AppointmentCalendarWidget.d.ts +41 -0
  65. package/dist/vanilla/AppointmentCalendarWidget.js +848 -0
  66. package/dist/vanilla/appointment-calendar/provider.d.ts +13 -0
  67. package/dist/vanilla/appointment-calendar/types.d.ts +144 -0
  68. package/dist/vanilla/appointments/provider.d.ts +13 -0
  69. package/dist/vanilla/appointments/types.d.ts +81 -0
  70. package/dist/vanilla/client/MedosClient.d.ts +32 -0
  71. package/dist/vanilla/components/AppointmentCalender.d.ts +3 -0
  72. package/dist/vanilla/components/AppointmentDateTimeModal.d.ts +14 -0
  73. package/dist/vanilla/components/ConfigurableCard.d.ts +12 -0
  74. package/dist/vanilla/components/DoctorSelectModal.d.ts +7 -0
  75. package/dist/vanilla/components/Icons/Check.d.ts +6 -0
  76. package/dist/vanilla/components/Icons/ChevronDownIcon.d.ts +4 -0
  77. package/dist/vanilla/components/Icons/ChevronLeft.d.ts +3 -0
  78. package/dist/vanilla/components/Icons/ChevronRight.d.ts +3 -0
  79. package/dist/vanilla/components/Icons/ConfirmationCheck.d.ts +1 -0
  80. package/dist/vanilla/components/Icons/ConsultationType.d.ts +1 -0
  81. package/dist/vanilla/components/Icons/Date&TimeIcon.d.ts +1 -0
  82. package/dist/vanilla/components/Icons/MapIcon.d.ts +1 -0
  83. package/dist/vanilla/components/Icons/PaymentMethodIcon.d.ts +1 -0
  84. package/dist/vanilla/components/Icons/UserIcon.d.ts +1 -0
  85. package/dist/vanilla/components/PatientDetailsStep.d.ts +3 -0
  86. package/dist/vanilla/components/PhoneVerificationStep.d.ts +3 -0
  87. package/dist/vanilla/components/SuccessStep.d.ts +3 -0
  88. package/dist/vanilla/components/custom-calendar.d.ts +5 -0
  89. package/dist/vanilla/components/styles.d.ts +6 -0
  90. package/dist/vanilla/components/types.d.ts +182 -0
  91. package/dist/vanilla/components/ui/select.d.ts +10 -0
  92. package/dist/vanilla/components/uiComponents/SelectDropdown.d.ts +41 -0
  93. package/dist/vanilla/components/utils.d.ts +5 -0
  94. package/dist/vanilla/components/validation.d.ts +2 -0
  95. package/dist/vanilla/context/TemplateContext.d.ts +12 -0
  96. package/dist/vanilla/core/index.d.ts +8 -0
  97. package/dist/vanilla/index.d.ts +7 -0
  98. package/dist/vanilla/index.js +2 -0
  99. package/dist/vanilla/lib/templateUtils.d.ts +3 -0
  100. package/dist/vanilla/react/index.d.ts +2 -0
  101. package/dist/vanilla/services/AppointmentService.d.ts +85 -0
  102. package/dist/vanilla/services/AuthService.d.ts +6 -0
  103. package/dist/vanilla/services/PatientService.d.ts +14 -0
  104. package/dist/vanilla/templates/alternative.css +13 -0
  105. package/dist/vanilla/templates/default.css +13 -0
  106. package/dist/vanilla/templates/registry.d.ts +12 -0
  107. package/dist/vanilla/vanilla/AppointmentCalendarWidget.d.ts +41 -0
  108. package/dist/vanilla/vanilla/index.d.ts +2 -0
  109. package/dist/vanilla/vanilla/widget.d.ts +10 -0
  110. package/dist/vanilla/widget.css +217 -0
  111. package/dist/vanilla/widget.d.ts +10 -0
  112. package/dist/vanilla/widget.js +5084 -0
  113. package/package.json +40 -5
@@ -1,570 +1,366 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState } from "react";
3
- import { AppointmentService, } from "../services/AppointmentService";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useCallback, useReducer } from "react";
3
+ import { AppointmentService } from "../services/AppointmentService";
4
4
  import { PatientService } from "../services/PatientService";
5
+ import { AppointmentDateTimeModal } from "./AppointmentDateTimeModal";
6
+ import { DoctorSelectModal } from "./DoctorSelectModal";
7
+ import { PhoneVerificationStep } from "./PhoneVerificationStep";
8
+ import { PatientDetailsStep } from "./PatientDetailsStep";
9
+ import { SuccessStep } from "./SuccessStep";
10
+ import { INITIAL_STATE, } from "./types";
11
+ import { CONTAINER_STYLES } from "./styles";
12
+ import { validatePhoneNumber, validateCountryCode } from "./validation";
13
+ import { formatDateToISO, parsePatientName } from "./utils";
14
+ const appointmentReducer = (state, action) => {
15
+ switch (action.type) {
16
+ case "SET_STEP":
17
+ return { ...state, step: action.payload };
18
+ case "SET_LOADING":
19
+ return { ...state, loading: action.payload };
20
+ case "SET_ERROR":
21
+ return { ...state, error: action.payload };
22
+ case "SET_WORKSPACE": {
23
+ const doctorMap = {};
24
+ action.payload.addresses.forEach((addr) => {
25
+ doctorMap[addr.id] = addr.doctors || [];
26
+ });
27
+ return {
28
+ ...state,
29
+ workspaceId: action.payload.id,
30
+ addresses: action.payload.addresses,
31
+ addressDoctorsMap: doctorMap,
32
+ };
33
+ }
34
+ case "SET_SELECTED_ADDRESS":
35
+ return { ...state, selectedAddress: action.payload };
36
+ case "SET_SELECTED_DOCTOR":
37
+ return { ...state, selectedDoctor: action.payload };
38
+ case "SET_SELECTED_DATE":
39
+ return { ...state, selectedDate: action.payload, selectedSlot: null };
40
+ case "SET_SLOTS":
41
+ return { ...state, slots: action.payload };
42
+ case "SET_SELECTED_SLOT":
43
+ return { ...state, selectedSlot: action.payload };
44
+ case "SET_CONSULTATION_MODE":
45
+ return { ...state, consultationMode: action.payload };
46
+ case "SET_CONSULTATION_CHARGE":
47
+ return { ...state, consultationCharge: action.payload };
48
+ case "SET_PATIENT_NAME":
49
+ return { ...state, patientName: action.payload };
50
+ case "SET_PATIENT_AGE":
51
+ return { ...state, patientAge: action.payload };
52
+ case "SET_PATIENT_EMAIL":
53
+ return { ...state, patientEmail: action.payload };
54
+ case "SET_PATIENT_GENDER":
55
+ return { ...state, patientGender: action.payload };
56
+ case "SET_BLOOD_GROUP":
57
+ return { ...state, bloodGroup: action.payload };
58
+ case "SET_PATIENT_ADDRESS":
59
+ return { ...state, patientAddress: action.payload };
60
+ case "SET_PATIENT_CITY":
61
+ return { ...state, patientCity: action.payload };
62
+ case "SET_PATIENT_STATE":
63
+ return { ...state, patientState: action.payload };
64
+ case "SET_PATIENT_COUNTRY":
65
+ return { ...state, patientCountry: action.payload };
66
+ case "SET_PATIENT_ZIPCODE":
67
+ return { ...state, patientZipcode: action.payload };
68
+ case "SET_PATIENT_LANDMARK":
69
+ return { ...state, patientLandmark: action.payload };
70
+ case "SET_COUNTRY_CODE":
71
+ return { ...state, countryCode: action.payload };
72
+ case "SET_PATIENT_PHONE":
73
+ return { ...state, patientPhone: action.payload };
74
+ case "SET_OTP_CODE":
75
+ return { ...state, otpCode: action.payload };
76
+ case "SET_OTP_SENT":
77
+ return { ...state, otpSent: action.payload };
78
+ case "SET_OTP_VERIFIED":
79
+ return { ...state, otpVerified: action.payload };
80
+ case "SET_OTP_SENDING":
81
+ return { ...state, otpSending: action.payload };
82
+ case "SET_OTP_VERIFYING":
83
+ return { ...state, otpVerifying: action.payload };
84
+ case "RESET_FORM":
85
+ return INITIAL_STATE;
86
+ default:
87
+ return state;
88
+ }
89
+ };
5
90
  export const AppointmentCalender = ({ onError, }) => {
6
- const [step, setStep] = useState(0);
7
- const [addresses, setAddresses] = useState([]);
8
- const [addressDoctorsMap, setAddressDoctorsMap] = useState({});
9
- const [selectedAddress, setSelectedAddress] = useState(null);
10
- const [workspaceId, setWorkspaceId] = useState(null);
11
- const [doctors, setDoctors] = useState([]);
12
- const [selectedDoctor, setSelectedDoctor] = useState(null);
13
- const [date, setDate] = useState("");
14
- const [slots, setSlots] = useState([]);
15
- const [selectedSlot, setSelectedSlot] = useState(null);
16
- const [loading, setLoading] = useState(false);
17
- const [error, setError] = useState(null);
18
- const [patientName, setPatientName] = useState("");
19
- const [patientAge, setPatientAge] = useState("");
20
- const [patientAddress, setPatientAddress] = useState("");
21
- const [patientEmail, setPatientEmail] = useState("");
22
- const [patientGender, setPatientGender] = useState("");
23
- const [problemFacing, setProblemFacing] = useState("");
24
- const [consultationCharge, setConsultationCharge] = useState("");
25
- const [countryCode, setCountryCode] = useState("+91");
26
- const [patientPhone, setPatientPhone] = useState("");
27
- const [otpCode, setOtpCode] = useState("");
28
- const [otpSent, setOtpSent] = useState(false);
29
- const [otpVerified, setOtpVerified] = useState(false);
30
- const [otpSending, setOtpSending] = useState(false);
31
- const [otpVerifying, setOtpVerifying] = useState(false);
32
- useEffect(() => {
91
+ const [state, dispatch] = useReducer(appointmentReducer, INITIAL_STATE);
92
+ const handleDateChange = useCallback(async (date) => {
93
+ dispatch({ type: "SET_SELECTED_DATE", payload: date });
94
+ if (!state.workspaceId ||
95
+ !state.selectedAddress ||
96
+ !state.selectedDoctor) {
97
+ dispatch({ type: "SET_SLOTS", payload: [] });
98
+ return;
99
+ }
33
100
  let mounted = true;
34
- (async () => {
35
- setLoading(true);
36
- setError(null);
37
- try {
38
- let fetchedAddresses = [];
39
- try {
40
- const addrResp = await AppointmentService.getAddresses();
41
- if (addrResp && Array.isArray(addrResp.addresses)) {
42
- fetchedAddresses = addrResp.addresses;
43
- if (addrResp.workspaceId && mounted) {
44
- setWorkspaceId(addrResp.workspaceId);
45
- }
46
- }
47
- }
48
- catch (e) {
49
- if (!mounted)
50
- return;
51
- throw e;
52
- }
53
- if (mounted && fetchedAddresses.length > 0) {
54
- const addrMap = {};
55
- const mappedAddrs = fetchedAddresses.map((a, idx) => {
56
- const id = String(a.id ?? idx);
57
- const label = a.completeAddress ?? a.label ?? a.address ?? `Address ${idx + 1}`;
58
- const docs = Array.isArray(a.doctors)
59
- ? a.doctors
60
- : [];
61
- addrMap[id] = docs || [];
62
- return { id, label };
63
- });
64
- setAddresses(mappedAddrs);
65
- setAddressDoctorsMap(addrMap);
66
- const anyDoctorsExist = Object.values(addrMap).some((arr) => Array.isArray(arr) && arr.length > 0);
67
- if (mappedAddrs.length === 1) {
68
- const only = mappedAddrs[0];
69
- setSelectedAddress(only.id);
70
- const docsForAddr = addrMap[only.id] || [];
71
- if (docsForAddr.length > 0) {
72
- setDoctors(docsForAddr);
73
- if (docsForAddr.length === 1) {
74
- setSelectedDoctor(docsForAddr[0].id);
75
- setStep(1);
76
- }
77
- else {
78
- setStep(0);
79
- }
80
- }
81
- else {
82
- if (anyDoctorsExist) {
83
- setError("No doctors at this address. Please choose a different address.");
84
- setDoctors([]);
85
- setStep(0);
86
- }
87
- else {
88
- setError("No doctors available for the selected location(s).");
89
- setDoctors([]);
90
- setStep(0);
91
- }
92
- }
93
- }
94
- else {
95
- setStep(0);
96
- }
97
- setLoading(false);
98
- return;
99
- }
100
- if (mounted) {
101
- setError("No addresses or doctors available.");
102
- setAddresses([]);
103
- setAddressDoctorsMap({});
104
- setDoctors([]);
105
- setStep(0);
106
- }
101
+ dispatch({ type: "SET_LOADING", payload: true });
102
+ dispatch({ type: "SET_ERROR", payload: null });
103
+ try {
104
+ const dateStr = formatDateToISO(date);
105
+ const fetchedSlots = await AppointmentService.fetchSlots(state.workspaceId, state.selectedAddress, state.selectedDoctor, dateStr);
106
+ if (mounted) {
107
+ dispatch({ type: "SET_SLOTS", payload: fetchedSlots || [] });
107
108
  }
108
- catch (e) {
109
- if (!mounted)
110
- return;
111
- const msg = e.message || "Failed to load addresses";
112
- setError(msg);
109
+ }
110
+ catch (e) {
111
+ if (mounted) {
112
+ const msg = e.message || "Failed to load slots";
113
+ dispatch({ type: "SET_ERROR", payload: msg });
113
114
  onError?.(e);
114
115
  }
115
- finally {
116
- if (mounted)
117
- setLoading(false);
118
- }
119
- })();
116
+ }
117
+ finally {
118
+ if (mounted)
119
+ dispatch({ type: "SET_LOADING", payload: false });
120
+ }
120
121
  return () => {
121
122
  mounted = false;
122
123
  };
123
- }, []);
124
+ }, [state.workspaceId, state.selectedAddress, state.selectedDoctor, onError]);
124
125
  useEffect(() => {
125
126
  let mounted = true;
126
127
  (async () => {
127
- if (!selectedAddress) {
128
- setDoctors([]);
129
- setSelectedDoctor(null);
130
- return;
131
- }
132
- setLoading(true);
133
- setError(null);
128
+ dispatch({ type: "SET_LOADING", payload: true });
129
+ dispatch({ type: "SET_ERROR", payload: null });
134
130
  try {
135
- const docsForAddr = addressDoctorsMap[selectedAddress] ?? [];
136
- if (docsForAddr.length > 0) {
137
- if (mounted) {
138
- setDoctors(docsForAddr);
139
- if (docsForAddr.length === 1) {
140
- setSelectedDoctor(docsForAddr[0].id);
141
- setStep(1);
142
- }
143
- else {
144
- setSelectedDoctor(null);
131
+ const response = await AppointmentService.getAddresses();
132
+ if (!mounted)
133
+ return;
134
+ if (response.workspaceId && response.addresses?.length > 0) {
135
+ dispatch({
136
+ type: "SET_WORKSPACE",
137
+ payload: {
138
+ id: response.workspaceId,
139
+ addresses: response.addresses,
140
+ },
141
+ });
142
+ if (response.addresses.length === 1) {
143
+ const address = response.addresses[0];
144
+ dispatch({ type: "SET_SELECTED_ADDRESS", payload: address.id });
145
+ if (address.doctors?.length === 1) {
146
+ dispatch({
147
+ type: "SET_SELECTED_DOCTOR",
148
+ payload: address.doctors[0].id,
149
+ });
150
+ dispatch({ type: "SET_STEP", payload: 1 });
145
151
  }
146
152
  }
147
153
  }
148
154
  else {
149
- const otherHasDoctors = Object.entries(addressDoctorsMap).some(([key, docs]) => key !== selectedAddress && Array.isArray(docs) && docs.length > 0);
150
- if (mounted) {
151
- setDoctors([]);
152
- setSelectedDoctor(null);
153
- if (otherHasDoctors) {
154
- setError("No doctors at this address. Please select a different address.");
155
- }
156
- else {
157
- setError("No doctors available for the selected location(s).");
158
- }
159
- }
155
+ dispatch({
156
+ type: "SET_ERROR",
157
+ payload: "No addresses or doctors available.",
158
+ });
160
159
  }
161
160
  }
162
161
  catch (e) {
163
162
  if (!mounted)
164
163
  return;
165
- setError(e.message || "Failed to load doctors for address");
166
- }
167
- finally {
168
- if (mounted)
169
- setLoading(false);
170
- }
171
- })();
172
- return () => {
173
- mounted = false;
174
- };
175
- }, [selectedAddress, addressDoctorsMap]);
176
- useEffect(() => {
177
- if (!workspaceId || !selectedAddress || !selectedDoctor || !date) {
178
- setSlots([]);
179
- setSelectedSlot(null);
180
- return;
181
- }
182
- let mounted = true;
183
- setLoading(true);
184
- setError(null);
185
- (async () => {
186
- try {
187
- const s = await AppointmentService.fetchSlots(workspaceId, selectedAddress, selectedDoctor, date);
188
- if (!mounted)
189
- return;
190
- setSlots(s || []);
191
- }
192
- catch (e) {
193
- if (!mounted)
194
- return;
195
- setError(e.message || "Failed to load slots");
164
+ const msg = e.message || "Failed to load addresses";
165
+ dispatch({ type: "SET_ERROR", payload: msg });
166
+ onError?.(e);
196
167
  }
197
168
  finally {
198
169
  if (mounted)
199
- setLoading(false);
170
+ dispatch({ type: "SET_LOADING", payload: false });
200
171
  }
201
172
  })();
202
173
  return () => {
203
174
  mounted = false;
204
175
  };
205
- }, [workspaceId, selectedAddress, selectedDoctor, date]);
206
- const goToNext = () => {
207
- if (step === 0) {
208
- if (addresses.length > 1 && !selectedAddress)
209
- return;
210
- if (doctors.length > 1 && !selectedDoctor)
211
- return;
212
- setStep(1);
213
- return;
176
+ }, [onError]);
177
+ const goBack = useCallback(() => {
178
+ if (state.step === 3) {
179
+ dispatch({ type: "SET_OTP_SENT", payload: false });
180
+ dispatch({ type: "SET_OTP_CODE", payload: "" });
181
+ dispatch({ type: "SET_OTP_VERIFIED", payload: false });
214
182
  }
215
- setStep((s) => Math.min(5, s + 1));
216
- };
217
- const goBack = () => {
218
- setStep((s) => Math.max(0, s - 1));
219
- };
220
- const validatePhoneNumber = (phone) => {
221
- const cleaned = phone.replace(/\D/g, "");
222
- return cleaned.length >= 7 && cleaned.length <= 15;
223
- };
224
- const validateCountryCode = (code) => {
225
- return /^\+[1-9]\d{0,3}$/.test(code);
226
- };
227
- const canProceedFromMergedStep = (() => {
228
- if (addresses.length === 0 || doctors.length === 0)
229
- return false;
230
- if (addresses.length > 1 && !selectedAddress)
231
- return false;
232
- if (doctors.length > 1 && !selectedDoctor)
233
- return false;
234
- return true;
235
- })();
236
- const sendOtp = async () => {
237
- setError(null);
238
- if (!countryCode) {
239
- setError("Please enter country code.");
183
+ dispatch({ type: "SET_STEP", payload: Math.max(0, state.step - 1) });
184
+ }, [state.step]);
185
+ const goToNext = useCallback(() => {
186
+ dispatch({ type: "SET_STEP", payload: Math.min(5, state.step + 1) });
187
+ }, [state.step]);
188
+ const sendOtp = useCallback(async () => {
189
+ dispatch({ type: "SET_ERROR", payload: null });
190
+ if (!state.countryCode) {
191
+ dispatch({ type: "SET_ERROR", payload: "Please enter country code." });
240
192
  return;
241
193
  }
242
- if (!validateCountryCode(countryCode)) {
243
- setError("Please enter a valid country code (e.g., +91, +1).");
194
+ if (!validateCountryCode(state.countryCode)) {
195
+ dispatch({
196
+ type: "SET_ERROR",
197
+ payload: "Please enter a valid country code (e.g., +91, +1).",
198
+ });
244
199
  return;
245
200
  }
246
- if (!patientPhone) {
247
- setError("Please enter phone number.");
201
+ if (!state.patientPhone) {
202
+ dispatch({ type: "SET_ERROR", payload: "Please enter phone number." });
248
203
  return;
249
204
  }
250
- if (!validatePhoneNumber(patientPhone)) {
251
- setError("Please enter a valid phone number (7-15 digits).");
205
+ if (!validatePhoneNumber(state.patientPhone)) {
206
+ dispatch({
207
+ type: "SET_ERROR",
208
+ payload: "Please enter a valid phone number (7-15 digits).",
209
+ });
252
210
  return;
253
211
  }
254
- setOtpSending(true);
212
+ dispatch({ type: "SET_OTP_SENDING", payload: true });
255
213
  try {
256
214
  await PatientService.sendPhoneVerificationOtp({
257
- countryCode,
258
- phoneNumber: patientPhone,
215
+ countryCode: state.countryCode,
216
+ phoneNumber: state.patientPhone,
259
217
  });
260
- setOtpSent(true);
261
- setError(null);
218
+ dispatch({ type: "SET_OTP_SENT", payload: true });
219
+ dispatch({ type: "SET_ERROR", payload: null });
262
220
  }
263
221
  catch (e) {
264
222
  const msg = e.message || "Failed to send OTP";
265
- setError(msg);
223
+ dispatch({ type: "SET_ERROR", payload: msg });
266
224
  onError?.(e);
267
225
  }
268
226
  finally {
269
- setOtpSending(false);
227
+ dispatch({ type: "SET_OTP_SENDING", payload: false });
270
228
  }
271
- };
272
- const verifyOtp = async () => {
273
- setError(null);
274
- if (!countryCode || !patientPhone || !otpCode) {
275
- setError("Please enter all required fields.");
229
+ }, [state.countryCode, state.patientPhone, onError]);
230
+ const verifyOtp = useCallback(async () => {
231
+ dispatch({ type: "SET_ERROR", payload: null });
232
+ if (!state.countryCode || !state.patientPhone || !state.otpCode) {
233
+ dispatch({
234
+ type: "SET_ERROR",
235
+ payload: "Please enter all required fields.",
236
+ });
276
237
  return;
277
238
  }
278
- if (otpCode.length !== 6) {
279
- setError("Please enter a 6-digit OTP code.");
239
+ if (state.otpCode.length !== 6) {
240
+ dispatch({
241
+ type: "SET_ERROR",
242
+ payload: "Please enter a 6-digit OTP code.",
243
+ });
280
244
  return;
281
245
  }
282
- setOtpVerifying(true);
246
+ dispatch({ type: "SET_OTP_VERIFYING", payload: true });
283
247
  try {
284
248
  await PatientService.verifyPhoneVerificationOtp({
285
- countryCode,
286
- phoneNumber: patientPhone,
287
- otpCode,
249
+ countryCode: state.countryCode,
250
+ phoneNumber: state.patientPhone,
251
+ otpCode: state.otpCode,
288
252
  });
289
- setOtpVerified(true);
290
- setError(null);
253
+ dispatch({ type: "SET_OTP_VERIFIED", payload: true });
254
+ dispatch({ type: "SET_ERROR", payload: null });
291
255
  }
292
256
  catch (e) {
293
257
  const msg = e.message || "Invalid OTP code";
294
- setError(msg);
258
+ dispatch({ type: "SET_ERROR", payload: msg });
295
259
  onError?.(e);
296
260
  }
297
261
  finally {
298
- setOtpVerifying(false);
262
+ dispatch({ type: "SET_OTP_VERIFYING", payload: false });
299
263
  }
300
- };
301
- const submitAppointment = async () => {
302
- setError(null);
303
- if (!selectedDoctor ||
304
- !selectedSlot ||
305
- !workspaceId ||
306
- !selectedAddress ||
307
- !patientAddress) {
308
- setError("Please ensure all selections are complete.");
264
+ }, [state.countryCode, state.patientPhone, state.otpCode, onError]);
265
+ const submitAppointment = useCallback(async () => {
266
+ dispatch({ type: "SET_ERROR", payload: null });
267
+ if (!state.selectedDoctor ||
268
+ !state.selectedSlot ||
269
+ !state.workspaceId ||
270
+ !state.selectedAddress) {
271
+ dispatch({
272
+ type: "SET_ERROR",
273
+ payload: "Please ensure all required fields are complete.",
274
+ });
309
275
  return;
310
276
  }
311
- if (!otpVerified) {
312
- setError("Please verify your phone number first.");
277
+ if (!state.otpVerified) {
278
+ dispatch({
279
+ type: "SET_ERROR",
280
+ payload: "Please verify your phone number first.",
281
+ });
313
282
  return;
314
283
  }
315
- setLoading(true);
284
+ if (!state.patientName ||
285
+ !state.patientAge ||
286
+ !state.patientEmail ||
287
+ !state.patientGender ||
288
+ !state.bloodGroup) {
289
+ dispatch({
290
+ type: "SET_ERROR",
291
+ payload: "Please fill in all patient details.",
292
+ });
293
+ return;
294
+ }
295
+ if (!state.patientAddress ||
296
+ !state.patientCity ||
297
+ !state.patientState ||
298
+ !state.patientCountry ||
299
+ !state.patientZipcode) {
300
+ dispatch({
301
+ type: "SET_ERROR",
302
+ payload: "Please fill in all address fields.",
303
+ });
304
+ return;
305
+ }
306
+ dispatch({ type: "SET_LOADING", payload: true });
316
307
  try {
317
- const nameParts = (patientName || "Patient").trim().split(/\s+/);
318
- const firstName = nameParts[0] || "Patient";
319
- const lastName = nameParts.slice(1).join(" ") || "";
320
- const startDate = new Date(selectedSlot.start);
321
- const endDate = new Date(selectedSlot.end);
322
- const appointmentDate = startDate.toISOString().split("T")[0];
323
- const formatTime = (date) => {
324
- const hours = String(date.getHours()).padStart(2, "0");
325
- const minutes = String(date.getMinutes()).padStart(2, "0");
326
- return `${hours}:${minutes}`;
327
- };
328
- const fromDateTimeTs = formatTime(startDate);
329
- const toDateTimeTs = formatTime(endDate);
330
- const patientAddressPayload = {
331
- addressLine1: patientAddress,
332
- };
308
+ const { firstName, lastName } = parsePatientName(state.patientName);
309
+ const appointmentDate = formatDateToISO(state.selectedDate);
333
310
  await AppointmentService.createAppointment({
334
- workspaceId: workspaceId,
335
- workspaceAddressId: selectedAddress,
336
- doctorId: selectedDoctor,
337
- mode: "OFFLINE",
338
- appointmentDate,
339
- fromDateTimeTs,
340
- toDateTimeTs,
341
- consultationCharge: consultationCharge || "0",
311
+ workspaceId: state.workspaceId,
312
+ workspaceAddressId: state.selectedAddress,
313
+ doctorId: state.selectedDoctor,
314
+ mode: state.consultationMode,
315
+ appointmentDate: appointmentDate,
316
+ fromDateTimeTs: state.selectedSlot.start,
317
+ toDateTimeTs: state.selectedSlot.end,
318
+ consultationCharge: state.consultationCharge || "0",
342
319
  type: "CONSULTATION",
343
320
  source: "SDK_POWERED_WEBSITE",
344
321
  patientPayload: {
345
322
  firstName,
346
323
  lastName,
347
- email: patientEmail || undefined,
348
- countryCode,
349
- phoneNumber: patientPhone,
350
- age: patientAge ? parseInt(patientAge, 10) : undefined,
351
- gender: patientGender
352
- ? patientGender.toUpperCase()
353
- : undefined,
324
+ email: state.patientEmail,
325
+ countryCode: state.countryCode,
326
+ phoneNumber: state.patientPhone,
327
+ age: parseInt(state.patientAge, 10),
328
+ gender: state.patientGender.toUpperCase(),
329
+ },
330
+ patientAddress: {
331
+ addressLine1: state.patientAddress,
332
+ city: state.patientCity,
333
+ state: state.patientState,
334
+ country: state.patientCountry,
335
+ zipcode: state.patientZipcode,
336
+ landmark: state.patientLandmark || undefined,
354
337
  },
355
- patientAddress: patientAddressPayload,
356
338
  });
357
- setStep(5);
339
+ dispatch({ type: "SET_STEP", payload: 5 });
358
340
  }
359
341
  catch (e) {
360
342
  const msg = e.message || "Failed to create appointment";
361
- setError(msg);
343
+ dispatch({ type: "SET_ERROR", payload: msg });
362
344
  onError?.(e);
363
345
  }
364
346
  finally {
365
- setLoading(false);
347
+ dispatch({ type: "SET_LOADING", payload: false });
366
348
  }
367
- };
368
- const styles = {
369
- container: {
370
- display: "flex",
371
- justifyContent: "center",
372
- padding: 20,
373
- fontFamily: "'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial",
374
- background: "#f6f8fa",
375
- },
376
- card: {
377
- width: "100%",
378
- maxWidth: 720,
379
- background: "#fff",
380
- borderRadius: 12,
381
- boxShadow: "0 8px 24px rgba(16,24,40,0.08)",
382
- padding: 24,
383
- boxSizing: "border-box",
384
- },
385
- header: {
386
- display: "flex",
387
- alignItems: "center",
388
- justifyContent: "space-between",
389
- marginBottom: 16,
390
- },
391
- title: { margin: 0, fontSize: 20, fontWeight: 600 },
392
- stepper: {
393
- display: "flex",
394
- gap: 8,
395
- alignItems: "center",
396
- },
397
- stepPill: (active = false) => ({
398
- padding: "6px 10px",
399
- borderRadius: 999,
400
- background: active ? "#0b79f7" : "#eef2ff",
401
- color: active ? "#fff" : "#333",
402
- fontSize: 12,
403
- fontWeight: 600,
404
- }),
405
- section: { marginTop: 12 },
406
- label: {
407
- display: "block",
408
- fontSize: 13,
409
- marginBottom: 6,
410
- color: "#374151",
411
- },
412
- input: {
413
- width: "100%",
414
- padding: "10px 12px",
415
- borderRadius: 8,
416
- border: "1px solid #e6e9ef",
417
- outline: "none",
418
- fontSize: 14,
419
- boxSizing: "border-box",
420
- },
421
- select: {
422
- width: "100%",
423
- padding: "10px 12px",
424
- borderRadius: 8,
425
- border: "1px solid #e6e9ef",
426
- background: "#fff",
427
- fontSize: 14,
428
- },
429
- actions: {
430
- display: "flex",
431
- gap: 8,
432
- marginTop: 16,
433
- justifyContent: "flex-end",
434
- },
435
- primaryBtn: {
436
- background: "#0b79f7",
437
- color: "#fff",
438
- border: "none",
439
- padding: "10px 14px",
440
- borderRadius: 8,
441
- cursor: "pointer",
442
- fontWeight: 600,
443
- },
444
- secondaryBtn: {
445
- background: "#fff",
446
- color: "#0b254a",
447
- border: "1px solid #e6e9ef",
448
- padding: "10px 14px",
449
- borderRadius: 8,
450
- cursor: "pointer",
451
- },
452
- slotGrid: {
453
- display: "grid",
454
- gridTemplateColumns: "repeat(auto-fit,minmax(140px,1fr))",
455
- gap: 12,
456
- marginTop: 12,
457
- },
458
- slotCard: (selected = false) => ({
459
- padding: 12,
460
- borderRadius: 10,
461
- border: selected ? "2px solid #0b79f7" : "1px solid #e6e9ef",
462
- background: selected
463
- ? "linear-gradient(180deg,#f0f6ff,#e9f2ff)"
464
- : "#fff",
465
- cursor: "pointer",
466
- textAlign: "center",
467
- }),
468
- smallMuted: { fontSize: 12, color: "#6b7280" },
469
- successCard: {
470
- padding: 20,
471
- borderRadius: 12,
472
- background: "linear-gradient(90deg,#ecfdf5,#eff6ff)",
473
- textAlign: "center",
474
- },
475
- };
476
- return (_jsx("div", { style: styles.container, children: _jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.header, children: [_jsx("h2", { style: styles.title, children: "Book Appointment" }), _jsxs("div", { style: styles.stepper, children: [_jsx("div", { style: styles.stepPill(step === 0), children: "1 Address" }), _jsx("div", { style: styles.stepPill(step === 1), children: "2 Date" }), _jsx("div", { style: styles.stepPill(step === 2), children: "3 Slot" }), _jsx("div", { style: styles.stepPill(step === 3), children: "4 Phone" }), _jsx("div", { style: styles.stepPill(step === 4), children: "5 Details" })] })] }), loading && _jsx("div", { style: { marginBottom: 12 }, children: "Loading..." }), error && (_jsx("div", { style: {
477
- marginBottom: 12,
478
- color: "#ef4444",
479
- fontWeight: 600,
480
- }, children: error })), step === 0 && (_jsxs("div", { style: styles.section, children: [_jsxs("div", { style: {
481
- display: "grid",
482
- gridTemplateColumns: "1fr 1fr",
483
- gap: 16,
484
- }, children: [_jsxs("div", { children: [_jsx("label", { style: styles.label, children: "Address" }), addresses.length === 0 ? (_jsx("div", { style: styles.smallMuted, children: "No addresses available" })) : addresses.length === 1 ? (_jsx("div", { style: { ...styles.smallMuted, fontWeight: 600 }, children: `${addresses[0].label}` })) : (_jsxs("select", { style: styles.select, value: selectedAddress || "", onChange: (e) => {
485
- setSelectedAddress(e.target.value || null);
486
- setError(null);
487
- }, children: [_jsx("option", { value: "", children: "-- choose address --" }), addresses.map((a) => (_jsx("option", { value: a.id, children: a.label }, a.id)))] }))] }), _jsxs("div", { children: [_jsx("label", { style: styles.label, children: "Doctor" }), doctors.length === 0 ? (_jsx("div", { style: styles.smallMuted, children: "No doctors available" })) : doctors.length === 1 ? (_jsx("div", { style: { ...styles.smallMuted, fontWeight: 600 }, children: `${doctors[0].name} ${doctors[0].specialty ? `• ${doctors[0].specialty}` : ""}` })) : (_jsxs("select", { style: styles.select, value: selectedDoctor || "", onChange: (e) => setSelectedDoctor(e.target.value), children: [_jsx("option", { value: "", children: "-- choose doctor --" }), doctors.map((d) => (_jsxs("option", { value: d.id, children: [d.name, " ", d.specialty ? `(${d.specialty})` : ""] }, d.id)))] }))] })] }), _jsxs("div", { style: styles.actions, children: [_jsx("button", { style: styles.secondaryBtn, onClick: () => {
488
- }, children: "Cancel" }), _jsx("button", { style: {
489
- ...styles.primaryBtn,
490
- opacity: canProceedFromMergedStep ? 1 : 0.6,
491
- }, disabled: !canProceedFromMergedStep, onClick: goToNext, children: "Next" })] })] })), step === 1 && (_jsxs("div", { style: styles.section, children: [_jsx("label", { style: styles.label, children: "Select Date" }), _jsx("input", { style: styles.input, type: "date", value: date, onChange: (e) => setDate(e.target.value) }), _jsxs("div", { style: styles.actions, children: [_jsx("button", { style: styles.secondaryBtn, onClick: goBack, children: "Back" }), _jsx("button", { style: { ...styles.primaryBtn, opacity: date ? 1 : 0.6 }, disabled: !date, onClick: () => setStep(2), children: "Next" })] })] })), step === 2 && (_jsxs("div", { style: styles.section, children: [_jsx("label", { style: styles.label, children: "Choose Time Slot" }), slots.length === 0 ? (_jsx("div", { style: styles.smallMuted, children: "No slots available for selected date" })) : (_jsx("div", { style: styles.slotGrid, children: slots.map((s) => {
492
- const start = new Date(s.start).toLocaleTimeString([], {
493
- hour: "2-digit",
494
- minute: "2-digit",
495
- });
496
- const end = new Date(s.end).toLocaleTimeString([], {
497
- hour: "2-digit",
498
- minute: "2-digit",
499
- });
500
- const selected = selectedSlot?.start === s.start &&
501
- selectedSlot?.end === s.end;
502
- return (_jsxs("div", { style: styles.slotCard(selected), onClick: () => setSelectedSlot(s), children: [_jsx("div", { style: { fontWeight: 700 }, children: `${start} — ${end}` }), _jsx("div", { style: styles.smallMuted })] }, s.id ?? `${s.start}-${s.end}`));
503
- }) })), _jsxs("div", { style: styles.actions, children: [_jsx("button", { style: styles.secondaryBtn, onClick: goBack, children: "Back" }), _jsx("button", { style: {
504
- ...styles.primaryBtn,
505
- opacity: selectedSlot ? 1 : 0.6,
506
- }, disabled: !selectedSlot, onClick: () => setStep(3), children: "Next" })] })] })), step === 3 && (_jsxs("div", { style: styles.section, children: [_jsx("label", { style: styles.label, children: "Country Code" }), _jsx("input", { style: styles.input, placeholder: "+91", value: countryCode, onChange: (e) => {
507
- let value = e.target.value;
508
- if (value && !value.startsWith("+")) {
509
- value = "+" + value;
510
- }
511
- value = value.replace(/[^\d+]/g, "");
512
- setCountryCode(value);
513
- } }), countryCode && !validateCountryCode(countryCode) && (_jsx("div", { style: { marginTop: 4, fontSize: 12, color: "#ef4444" }, children: "Please enter a valid country code (e.g., +91, +1)" })), _jsx("label", { style: { ...styles.label, marginTop: 12 }, children: "Phone Number" }), _jsx("input", { style: styles.input, type: "tel", placeholder: "9311840587", value: patientPhone, onChange: (e) => {
514
- const value = e.target.value.replace(/\D/g, "");
515
- setPatientPhone(value);
516
- }, disabled: otpSent, maxLength: 15 }), patientPhone && !validatePhoneNumber(patientPhone) && (_jsx("div", { style: { marginTop: 4, fontSize: 12, color: "#ef4444" }, children: "Phone number should be 7-15 digits" })), !otpSent ? (_jsxs("div", { style: styles.actions, children: [_jsx("button", { style: styles.secondaryBtn, onClick: goBack, children: "Back" }), _jsx("button", { style: {
517
- ...styles.primaryBtn,
518
- opacity: countryCode && patientPhone ? 1 : 0.6,
519
- }, disabled: !countryCode ||
520
- !patientPhone ||
521
- otpSending ||
522
- !validateCountryCode(countryCode) ||
523
- !validatePhoneNumber(patientPhone), onClick: sendOtp, children: otpSending ? "Sending..." : "Send OTP" })] })) : (_jsx(_Fragment, { children: otpVerified ? (_jsxs(_Fragment, { children: [_jsx("div", { style: {
524
- marginTop: 12,
525
- padding: 12,
526
- borderRadius: 8,
527
- background: "#ecfdf5",
528
- color: "#10b981",
529
- fontWeight: 600,
530
- }, children: "\u2713 Phone verified successfully" }), _jsxs("div", { style: styles.actions, children: [_jsx("button", { style: styles.secondaryBtn, onClick: goBack, children: "Back" }), _jsx("button", { style: styles.primaryBtn, onClick: goToNext, children: "Continue to Details" })] })] })) : (_jsxs(_Fragment, { children: [_jsx("label", { style: { ...styles.label, marginTop: 12 }, children: "Enter OTP" }), _jsx("input", { style: styles.input, type: "text", placeholder: "Enter 6-digit OTP", value: otpCode, onChange: (e) => setOtpCode(e.target.value), maxLength: 6 }), _jsxs("div", { style: {
531
- marginTop: 8,
532
- fontSize: 12,
533
- color: "#6b7280",
534
- }, children: ["OTP sent to ", countryCode, " ", patientPhone] }), _jsxs("div", { style: styles.actions, children: [_jsx("button", { style: styles.secondaryBtn, onClick: () => {
535
- setOtpSent(false);
536
- setOtpCode("");
537
- setOtpVerified(false);
538
- }, children: "Change Number" }), _jsx("button", { style: {
539
- ...styles.primaryBtn,
540
- opacity: otpCode.length === 6 ? 1 : 0.6,
541
- }, disabled: otpCode.length !== 6 || otpVerifying, onClick: verifyOtp, children: otpVerifying ? "Verifying..." : "Verify OTP" })] })] })) }))] })), step === 4 && (_jsxs("div", { style: styles.section, children: [_jsx("label", { style: styles.label, children: "Patient Name" }), _jsx("input", { style: styles.input, placeholder: "Full name", value: patientName, onChange: (e) => setPatientName(e.target.value) }), _jsx("label", { style: { ...styles.label, marginTop: 12 }, children: "Age" }), _jsx("input", { style: styles.input, type: "number", placeholder: "Age", value: patientAge, onChange: (e) => setPatientAge(e.target.value) }), _jsx("label", { style: { ...styles.label, marginTop: 12 }, children: "Email (Optional)" }), _jsx("input", { style: styles.input, type: "email", placeholder: "patient@example.com", value: patientEmail, onChange: (e) => setPatientEmail(e.target.value) }), _jsx("label", { style: { ...styles.label, marginTop: 12 }, children: "Gender (Optional)" }), _jsxs("select", { style: styles.select, value: patientGender, onChange: (e) => setPatientGender(e.target.value), children: [_jsx("option", { value: "", children: "-- Select Gender --" }), _jsx("option", { value: "MALE", children: "Male" }), _jsx("option", { value: "FEMALE", children: "Female" }), _jsx("option", { value: "OTHER", children: "Other" })] }), _jsx("label", { style: { ...styles.label, marginTop: 12 }, children: "Address" }), _jsx("textarea", { style: { ...styles.input, minHeight: 80, resize: "vertical" }, placeholder: "Enter your address", value: patientAddress, onChange: (e) => setPatientAddress(e.target.value) }), _jsx("label", { style: { ...styles.label, marginTop: 12 }, children: "Problem Facing" }), _jsx("textarea", { style: { ...styles.input, minHeight: 80, resize: "vertical" }, placeholder: "Describe the problem you're facing", value: problemFacing, onChange: (e) => setProblemFacing(e.target.value) }), _jsxs("div", { style: styles.actions, children: [_jsx("button", { style: styles.secondaryBtn, onClick: goBack, children: "Back" }), _jsx("button", { style: {
542
- ...styles.primaryBtn,
543
- opacity: patientName && otpVerified ? 1 : 0.6,
544
- }, disabled: !patientName || !otpVerified || loading, onClick: submitAppointment, children: loading ? "Booking..." : "Book Appointment" })] })] })), step === 5 && (_jsxs("div", { style: { ...styles.section, textAlign: "center" }, children: [_jsxs("div", { style: styles.successCard, children: [_jsx("div", { style: {
545
- fontSize: 32,
546
- color: "#10b981",
547
- marginBottom: 8,
548
- }, children: "\u2713" }), _jsx("div", { style: { fontWeight: 700, marginBottom: 6 }, children: "Appointment Confirmed" }), _jsxs("div", { style: styles.smallMuted, children: ["Thank you, ", patientName || "Patient", ". Your appointment is confirmed."] })] }), _jsx("div", { style: {
549
- marginTop: 14,
550
- display: "flex",
551
- justifyContent: "center",
552
- }, children: _jsx("button", { style: { ...styles.primaryBtn, width: 160 }, onClick: () => {
553
- setStep(0);
554
- setSelectedSlot(null);
555
- setSelectedDoctor(null);
556
- setDate("");
557
- setPatientName("");
558
- setPatientAge("");
559
- setPatientAddress("");
560
- setPatientEmail("");
561
- setPatientGender("");
562
- setProblemFacing("");
563
- setConsultationCharge("");
564
- setCountryCode("+91");
565
- setPatientPhone("");
566
- setOtpCode("");
567
- setOtpSent(false);
568
- setOtpVerified(false);
569
- }, children: "Book Another" }) })] }))] }) }));
349
+ }, [state, onError]);
350
+ const resetForm = useCallback(() => {
351
+ dispatch({ type: "RESET_FORM" });
352
+ }, []);
353
+ const handleDoctorSelect = useCallback((addrId, docId) => {
354
+ dispatch({ type: "SET_SELECTED_ADDRESS", payload: addrId });
355
+ dispatch({ type: "SET_SELECTED_DOCTOR", payload: docId });
356
+ dispatch({ type: "SET_STEP", payload: 1 });
357
+ }, []);
358
+ const handleDateTimeModalContinue = useCallback((mode, date, slot, charge) => {
359
+ dispatch({ type: "SET_CONSULTATION_MODE", payload: mode });
360
+ dispatch({ type: "SET_SELECTED_DATE", payload: date });
361
+ dispatch({ type: "SET_SELECTED_SLOT", payload: slot });
362
+ dispatch({ type: "SET_CONSULTATION_CHARGE", payload: charge });
363
+ dispatch({ type: "SET_STEP", payload: 3 });
364
+ }, []);
365
+ return (_jsx("div", { style: CONTAINER_STYLES.container, children: _jsxs("div", { style: CONTAINER_STYLES.card, children: [_jsx("div", { style: CONTAINER_STYLES.header, children: _jsx("h2", { style: CONTAINER_STYLES.title, children: "Book Appointment" }) }), state.loading && _jsx("div", { style: { marginBottom: 12 }, children: "Loading..." }), state.error && (_jsx("div", { style: CONTAINER_STYLES.errorMessage, children: state.error })), state.step === 0 && (_jsx(DoctorSelectModal, { onCancel: () => dispatch({ type: "SET_STEP", payload: 0 }), onContinue: handleDoctorSelect })), state.step === 1 && (_jsx(AppointmentDateTimeModal, { onlineFee: 500, offlineFee: 300, slots: state.slots, onCancel: goBack, onDateChange: handleDateChange, onContinue: handleDateTimeModalContinue })), state.step === 3 && (_jsx(PhoneVerificationStep, { state: state, dispatch: dispatch, onSendOtp: sendOtp, onVerifyOtp: verifyOtp, onBack: goBack, onContinue: goToNext })), state.step === 4 && (_jsx(PatientDetailsStep, { state: state, dispatch: dispatch, onBack: goBack, onSubmit: submitAppointment })), state.step === 5 && _jsx(SuccessStep, { state: state, onReset: resetForm })] }) }));
570
366
  };