medos-sdk 1.0.2 → 1.0.3

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