medos-sdk 1.1.8 → 1.1.10

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 (52) hide show
  1. package/dist/components/AppointmentCalender.js +44 -27
  2. package/dist/components/AppointmentConfirmationStep.d.ts +3 -0
  3. package/dist/components/AppointmentConfirmationStep.js +26 -20
  4. package/dist/components/AppointmentDateTimeModal.d.ts +3 -0
  5. package/dist/components/AppointmentDateTimeModal.js +24 -6
  6. package/dist/components/ContactInformationStep.js +6 -5
  7. package/dist/components/ContactPreferenceStep.js +6 -5
  8. package/dist/components/DoctorSelectModal.d.ts +5 -0
  9. package/dist/components/DoctorSelectModal.js +65 -16
  10. package/dist/components/EnquiryForm.js +4 -4
  11. package/dist/components/InquiryDetailsStep.js +7 -6
  12. package/dist/components/appointment-booking/AppointmentCalender.d.ts +5 -0
  13. package/dist/components/appointment-booking/AppointmentCalender.js +107 -0
  14. package/dist/components/appointment-booking/hooks/index.d.ts +3 -0
  15. package/dist/components/appointment-booking/hooks/index.js +3 -0
  16. package/dist/components/appointment-booking/hooks/useAppointmentFlow.d.ts +9 -0
  17. package/dist/components/appointment-booking/hooks/useAppointmentFlow.js +233 -0
  18. package/dist/components/appointment-booking/hooks/useAppointmentState.d.ts +9 -0
  19. package/dist/components/appointment-booking/hooks/useAppointmentState.js +93 -0
  20. package/dist/components/appointment-booking/hooks/useInitializeAddresses.d.ts +1 -0
  21. package/dist/components/appointment-booking/hooks/useInitializeAddresses.js +56 -0
  22. package/dist/components/appointment-booking/index.d.ts +5 -0
  23. package/dist/components/appointment-booking/index.js +3 -0
  24. package/dist/components/appointment-booking/types.d.ts +128 -0
  25. package/dist/components/appointment-booking/types.js +33 -0
  26. package/dist/components/types.d.ts +10 -139
  27. package/dist/components/types.js +1 -33
  28. package/dist/components/uiComponents/SelectDropdown.d.ts +1 -1
  29. package/dist/components/uiComponents/SelectDropdown.js +24 -24
  30. package/dist/enquiry-form/validation.js +1 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +1 -1
  33. package/dist/react/index.d.ts +2 -1
  34. package/dist/react/index.js +1 -1
  35. package/dist/vanilla/AppointmentCalendarWidget.js +20 -13
  36. package/dist/vanilla/components/AppointmentConfirmationStep.d.ts +3 -0
  37. package/dist/vanilla/components/AppointmentDateTimeModal.d.ts +3 -0
  38. package/dist/vanilla/components/DoctorSelectModal.d.ts +5 -0
  39. package/dist/vanilla/components/appointment-booking/AppointmentCalender.d.ts +5 -0
  40. package/dist/vanilla/components/appointment-booking/hooks/index.d.ts +3 -0
  41. package/dist/vanilla/components/appointment-booking/hooks/useAppointmentFlow.d.ts +9 -0
  42. package/dist/vanilla/components/appointment-booking/hooks/useAppointmentState.d.ts +9 -0
  43. package/dist/vanilla/components/appointment-booking/hooks/useInitializeAddresses.d.ts +1 -0
  44. package/dist/vanilla/components/appointment-booking/index.d.ts +5 -0
  45. package/dist/vanilla/components/appointment-booking/types.d.ts +128 -0
  46. package/dist/vanilla/components/types.d.ts +10 -139
  47. package/dist/vanilla/components/uiComponents/SelectDropdown.d.ts +1 -1
  48. package/dist/vanilla/enquiry-widget.js +1 -1
  49. package/dist/vanilla/index.d.ts +1 -1
  50. package/dist/vanilla/react/index.d.ts +2 -1
  51. package/dist/vanilla/widget.js +44 -36
  52. package/package.json +1 -1
@@ -0,0 +1,107 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AppointmentDateTimeModal } from "../AppointmentDateTimeModal";
3
+ import { DoctorSelectModal } from "../DoctorSelectModal";
4
+ import { PhoneVerificationStep } from "../PhoneVerificationStep";
5
+ import { PatientDetailsStep } from "../PatientDetailsStep";
6
+ import AppointmentConfirmationStep from "../AppointmentConfirmationStep";
7
+ import { useTheme } from "../../react/hooks/useTheme";
8
+ import MedosLogo from "../Icons/MedosLogo";
9
+ import { useAppointmentState, useAppointmentFlow, useInitializeAddresses, } from "./hooks";
10
+ import { formatDateToISO } from "../utils";
11
+ export const AppointmentCalender = ({ onError, }) => {
12
+ const { state, dispatch, setStep, resetForm } = useAppointmentState();
13
+ const { goBack, goToNext, handleDateChange, sendOtp, verifyOtp, submitAppointment, } = useAppointmentFlow(state, dispatch, onError);
14
+ useInitializeAddresses(dispatch, onError);
15
+ const handleDoctorSelect = (addrId, docId) => {
16
+ dispatch({ type: "SET_SELECTED_ADDRESS", payload: addrId });
17
+ dispatch({ type: "SET_SELECTED_DOCTOR", payload: docId });
18
+ setStep(1);
19
+ };
20
+ const handleDateTimeModalContinue = (mode, date, slot, charge) => {
21
+ dispatch({ type: "SET_CONSULTATION_MODE", payload: mode });
22
+ dispatch({ type: "SET_SELECTED_DATE", payload: date });
23
+ dispatch({ type: "SET_SELECTED_SLOT", payload: slot });
24
+ dispatch({ type: "SET_CONSULTATION_CHARGE", payload: charge });
25
+ setStep(3);
26
+ };
27
+ const theme = useTheme();
28
+ const styles = getStyles(theme);
29
+ return (_jsx("div", { style: styles.container, children: _jsxs("div", { style: styles.card, children: [_jsx("div", { style: styles.header, children: _jsx("h2", { style: styles.title, children: "Book Appointment" }) }), _jsx("hr", {}), _jsxs("div", { style: styles.content, children: [state.loading && _jsx("div", { style: { marginBottom: 12 }, children: "Loading..." }), state.error && _jsx("div", { style: styles.errorMessage, children: state.error }), state.step === 0 && (_jsx(DoctorSelectModal, { addresses: state.addresses, addressDoctorsMap: state.addressDoctorsMap, selectedAddressId: state.selectedAddress, selectedDoctorId: state.selectedDoctor, onCancel: () => setStep(0), onContinue: handleDoctorSelect })), state.step === 1 && (_jsx(AppointmentDateTimeModal, { onlineFee: 500, offlineFee: 300, slots: state.slots, selectedDate: state.selectedDate, selectedSlot: state.selectedSlot, consultationMode: state.consultationMode, 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(AppointmentConfirmationStep, { appointment: {
30
+ patientName: state.patientName,
31
+ visitationType: state.consultationMode === "ONLINE"
32
+ ? "Online Consultation"
33
+ : "In-Person Visit",
34
+ appointmentDate: formatDateToISO(state.selectedDate),
35
+ fromTime: state.selectedSlot?.start
36
+ ? new Date(state.selectedSlot.start)
37
+ .toTimeString()
38
+ .slice(0, 5)
39
+ : "",
40
+ toTime: state.selectedSlot?.end
41
+ ? new Date(state.selectedSlot.end).toTimeString().slice(0, 5)
42
+ : "",
43
+ location: state.addresses.find((addr) => addr.id === state.selectedAddress)?.label || "Clinic",
44
+ mode: state.consultationMode,
45
+ paymentMode: "Cash",
46
+ }, patient: {
47
+ patientName: state.patientName,
48
+ }, selectedDoctor: state.selectedAddress && state.selectedDoctor
49
+ ? state.addressDoctorsMap[state.selectedAddress]?.find((doc) => doc.id === state.selectedDoctor) || null
50
+ : null, selectedAddress: state.selectedAddress
51
+ ? state.addresses.find((addr) => addr.id === state.selectedAddress) || null
52
+ : null, onClose: resetForm })), _jsxs("div", { style: styles.branding, children: [_jsx("span", { style: styles.poweredBy, children: "Powered by" }), _jsx("a", { href: "https://medos.one", target: "_blank", rel: "noopener noreferrer", children: _jsx(MedosLogo, { style: { height: 50, width: "auto" } }) })] })] })] }) }));
53
+ };
54
+ const getStyles = (theme) => ({
55
+ container: {
56
+ display: "flex",
57
+ justifyContent: "center",
58
+ padding: 20,
59
+ fontFamily: theme.typography.fontFamily,
60
+ background: theme.colors.background,
61
+ },
62
+ card: {
63
+ width: "100%",
64
+ maxWidth: 840,
65
+ background: theme.colors.surface,
66
+ borderRadius: theme.radii.lg,
67
+ boxShadow: theme.shadows.lg,
68
+ overflow: "hidden",
69
+ boxSizing: "border-box",
70
+ },
71
+ header: {
72
+ padding: "20px 24px",
73
+ },
74
+ title: {
75
+ margin: 0,
76
+ fontSize: theme.typography.fontSizeXl,
77
+ fontWeight: theme.typography.fontWeightSemibold,
78
+ },
79
+ content: {
80
+ padding: 24,
81
+ },
82
+ errorMessage: {
83
+ marginBottom: 12,
84
+ color: theme.colors.error,
85
+ fontWeight: 600,
86
+ padding: "12px",
87
+ background: theme.colors.errorBackground,
88
+ borderRadius: theme.radii.md,
89
+ border: `1px solid ${theme.colors.errorBorder}`,
90
+ fontSize: theme.typography.fontSizeSm,
91
+ },
92
+ branding: {
93
+ display: "flex",
94
+ alignItems: "center",
95
+ justifyContent: "center",
96
+ gap: 8,
97
+ marginTop: 24,
98
+ paddingTop: 16,
99
+ borderTop: `1px solid ${theme.colors.border}`,
100
+ opacity: 0.8,
101
+ },
102
+ poweredBy: {
103
+ fontSize: 12,
104
+ color: theme.colors.textSecondary,
105
+ fontWeight: 500,
106
+ },
107
+ });
@@ -0,0 +1,3 @@
1
+ export { useAppointmentState } from "./useAppointmentState";
2
+ export { useAppointmentFlow } from "./useAppointmentFlow";
3
+ export { useInitializeAddresses } from "./useInitializeAddresses";
@@ -0,0 +1,3 @@
1
+ export { useAppointmentState } from "./useAppointmentState";
2
+ export { useAppointmentFlow } from "./useAppointmentFlow";
3
+ export { useInitializeAddresses } from "./useInitializeAddresses";
@@ -0,0 +1,9 @@
1
+ import { AppointmentState } from "../types";
2
+ export declare const useAppointmentFlow: (state: AppointmentState, dispatch: React.Dispatch<any>, onError?: (error: Error) => void) => {
3
+ goBack: () => void;
4
+ goToNext: () => void;
5
+ handleDateChange: (date: Date) => Promise<(() => void) | undefined>;
6
+ sendOtp: () => Promise<void>;
7
+ verifyOtp: () => Promise<void>;
8
+ submitAppointment: () => Promise<void>;
9
+ };
@@ -0,0 +1,233 @@
1
+ import { useCallback } from "react";
2
+ import { AppointmentService } from "../../../services/AppointmentService";
3
+ import { PatientService } from "../../../services/PatientService";
4
+ import { validatePhoneNumber, validateCountryCode, } from "../../../components/validation";
5
+ import { formatDateToISO, parsePatientName } from "../../../components/utils";
6
+ export const useAppointmentFlow = (state, dispatch, onError) => {
7
+ const goBack = useCallback(() => {
8
+ if (state.step === 3) {
9
+ dispatch({ type: "SET_OTP_SENT", payload: false });
10
+ dispatch({ type: "SET_OTP_CODE", payload: "" });
11
+ dispatch({ type: "SET_OTP_VERIFIED", payload: false });
12
+ dispatch({ type: "SET_STEP", payload: 1 });
13
+ }
14
+ else if (state.step === 4) {
15
+ dispatch({ type: "SET_STEP", payload: 3 });
16
+ }
17
+ else {
18
+ dispatch({ type: "SET_STEP", payload: Math.max(0, state.step - 1) });
19
+ }
20
+ }, [state.step, dispatch]);
21
+ const goToNext = useCallback(() => {
22
+ dispatch({ type: "SET_STEP", payload: Math.min(5, state.step + 1) });
23
+ }, [state.step, dispatch]);
24
+ const handleDateChange = useCallback(async (date) => {
25
+ dispatch({ type: "SET_SELECTED_DATE", payload: date });
26
+ if (!state.workspaceId ||
27
+ !state.selectedAddress ||
28
+ !state.selectedDoctor) {
29
+ dispatch({ type: "SET_SLOTS", payload: [] });
30
+ return;
31
+ }
32
+ let mounted = true;
33
+ dispatch({ type: "SET_LOADING", payload: true });
34
+ dispatch({ type: "SET_ERROR", payload: null });
35
+ try {
36
+ const dateStr = formatDateToISO(date);
37
+ const fetchedSlots = await AppointmentService.fetchSlots(state.workspaceId, state.selectedAddress, state.selectedDoctor, dateStr);
38
+ if (mounted) {
39
+ dispatch({ type: "SET_SLOTS", payload: fetchedSlots || [] });
40
+ }
41
+ }
42
+ catch (e) {
43
+ if (mounted) {
44
+ const msg = e.message || "Failed to load slots";
45
+ dispatch({ type: "SET_ERROR", payload: msg });
46
+ onError?.(e);
47
+ }
48
+ }
49
+ finally {
50
+ if (mounted)
51
+ dispatch({ type: "SET_LOADING", payload: false });
52
+ }
53
+ return () => {
54
+ mounted = false;
55
+ };
56
+ }, [
57
+ state.workspaceId,
58
+ state.selectedAddress,
59
+ state.selectedDoctor,
60
+ dispatch,
61
+ onError,
62
+ ]);
63
+ const sendOtp = useCallback(async () => {
64
+ dispatch({ type: "SET_ERROR", payload: null });
65
+ if (!state.countryCode) {
66
+ dispatch({ type: "SET_ERROR", payload: "Please enter country code." });
67
+ return;
68
+ }
69
+ if (!validateCountryCode(state.countryCode)) {
70
+ dispatch({
71
+ type: "SET_ERROR",
72
+ payload: "Please enter a valid country code (e.g., +91, +1).",
73
+ });
74
+ return;
75
+ }
76
+ if (!state.patientPhone) {
77
+ dispatch({ type: "SET_ERROR", payload: "Please enter phone number." });
78
+ return;
79
+ }
80
+ if (!validatePhoneNumber(state.patientPhone)) {
81
+ dispatch({
82
+ type: "SET_ERROR",
83
+ payload: "Please enter a valid phone number (7-15 digits).",
84
+ });
85
+ return;
86
+ }
87
+ dispatch({ type: "SET_OTP_SENDING", payload: true });
88
+ try {
89
+ await PatientService.sendPhoneVerificationOtp({
90
+ countryCode: state.countryCode,
91
+ phoneNumber: state.patientPhone,
92
+ });
93
+ dispatch({ type: "SET_OTP_SENT", payload: true });
94
+ dispatch({ type: "SET_ERROR", payload: null });
95
+ }
96
+ catch (e) {
97
+ const msg = e.message || "Failed to send OTP";
98
+ dispatch({ type: "SET_ERROR", payload: msg });
99
+ onError?.(e);
100
+ }
101
+ finally {
102
+ dispatch({ type: "SET_OTP_SENDING", payload: false });
103
+ }
104
+ }, [state.countryCode, state.patientPhone, dispatch, onError]);
105
+ const verifyOtp = useCallback(async () => {
106
+ dispatch({ type: "SET_ERROR", payload: null });
107
+ if (!state.countryCode || !state.patientPhone || !state.otpCode) {
108
+ dispatch({
109
+ type: "SET_ERROR",
110
+ payload: "Please enter all required fields.",
111
+ });
112
+ return;
113
+ }
114
+ if (state.otpCode.length !== 6) {
115
+ dispatch({
116
+ type: "SET_ERROR",
117
+ payload: "Please enter a 6-digit OTP code.",
118
+ });
119
+ return;
120
+ }
121
+ dispatch({ type: "SET_OTP_VERIFYING", payload: true });
122
+ try {
123
+ await PatientService.verifyPhoneVerificationOtp({
124
+ countryCode: state.countryCode,
125
+ phoneNumber: state.patientPhone,
126
+ otpCode: state.otpCode,
127
+ });
128
+ dispatch({ type: "SET_OTP_VERIFIED", payload: true });
129
+ dispatch({ type: "SET_ERROR", payload: null });
130
+ }
131
+ catch (e) {
132
+ const msg = e.message || "Invalid OTP code";
133
+ dispatch({ type: "SET_ERROR", payload: msg });
134
+ onError?.(e);
135
+ }
136
+ finally {
137
+ dispatch({ type: "SET_OTP_VERIFYING", payload: false });
138
+ }
139
+ }, [state.countryCode, state.patientPhone, state.otpCode, dispatch, onError]);
140
+ const submitAppointment = useCallback(async () => {
141
+ dispatch({ type: "SET_ERROR", payload: null });
142
+ if (!state.selectedDoctor ||
143
+ !state.selectedSlot ||
144
+ !state.workspaceId ||
145
+ !state.selectedAddress) {
146
+ dispatch({
147
+ type: "SET_ERROR",
148
+ payload: "Please ensure all required fields are complete.",
149
+ });
150
+ return;
151
+ }
152
+ if (!state.otpVerified) {
153
+ dispatch({
154
+ type: "SET_ERROR",
155
+ payload: "Please verify your phone number first.",
156
+ });
157
+ return;
158
+ }
159
+ if (!state.patientName ||
160
+ !state.patientAge ||
161
+ !state.patientEmail ||
162
+ !state.patientGender ||
163
+ !state.bloodGroup) {
164
+ dispatch({
165
+ type: "SET_ERROR",
166
+ payload: "Please fill in all patient details.",
167
+ });
168
+ return;
169
+ }
170
+ if (!state.patientAddress ||
171
+ !state.patientCity ||
172
+ !state.patientState ||
173
+ !state.patientCountry ||
174
+ !state.patientZipcode) {
175
+ dispatch({
176
+ type: "SET_ERROR",
177
+ payload: "Please fill in all address fields.",
178
+ });
179
+ return;
180
+ }
181
+ dispatch({ type: "SET_LOADING", payload: true });
182
+ try {
183
+ const { firstName, lastName } = parsePatientName(state.patientName);
184
+ const appointmentDate = formatDateToISO(state.selectedDate);
185
+ await AppointmentService.createAppointment({
186
+ workspaceId: state.workspaceId,
187
+ workspaceAddressId: state.selectedAddress,
188
+ doctorId: state.selectedDoctor,
189
+ mode: state.consultationMode,
190
+ appointmentDate: appointmentDate,
191
+ fromDateTimeTs: state.selectedSlot.start,
192
+ toDateTimeTs: state.selectedSlot.end,
193
+ consultationCharge: state.consultationCharge || "0",
194
+ type: "CONSULTATION",
195
+ source: "SDK_POWERED_WEBSITE",
196
+ patientPayload: {
197
+ firstName,
198
+ lastName,
199
+ email: state.patientEmail,
200
+ countryCode: state.countryCode,
201
+ phoneNumber: state.patientPhone,
202
+ age: parseInt(state.patientAge, 10),
203
+ gender: state.patientGender.toUpperCase(),
204
+ },
205
+ patientAddress: {
206
+ addressLine1: state.patientAddress,
207
+ city: state.patientCity,
208
+ state: state.patientState,
209
+ country: state.patientCountry,
210
+ zipcode: state.patientZipcode,
211
+ landmark: state.patientLandmark || undefined,
212
+ },
213
+ });
214
+ dispatch({ type: "SET_STEP", payload: 5 });
215
+ }
216
+ catch (e) {
217
+ const msg = e.message || "Failed to create appointment";
218
+ dispatch({ type: "SET_ERROR", payload: msg });
219
+ onError?.(e);
220
+ }
221
+ finally {
222
+ dispatch({ type: "SET_LOADING", payload: false });
223
+ }
224
+ }, [state, dispatch, onError]);
225
+ return {
226
+ goBack,
227
+ goToNext,
228
+ handleDateChange,
229
+ sendOtp,
230
+ verifyOtp,
231
+ submitAppointment,
232
+ };
233
+ };
@@ -0,0 +1,9 @@
1
+ import { AppointmentState, AppointmentAction } from "../types";
2
+ export declare const useAppointmentState: () => {
3
+ state: AppointmentState;
4
+ dispatch: import("react").Dispatch<AppointmentAction>;
5
+ setStep: (step: number) => void;
6
+ setError: (error: string | null) => void;
7
+ setLoading: (loading: boolean) => void;
8
+ resetForm: () => void;
9
+ };
@@ -0,0 +1,93 @@
1
+ import { useReducer, useCallback } from "react";
2
+ import { INITIAL_STATE } from "../types";
3
+ const appointmentReducer = (state, action) => {
4
+ switch (action.type) {
5
+ case "SET_STEP":
6
+ return { ...state, step: action.payload };
7
+ case "SET_LOADING":
8
+ return { ...state, loading: action.payload };
9
+ case "SET_ERROR":
10
+ return { ...state, error: action.payload };
11
+ case "SET_WORKSPACE": {
12
+ const doctorMap = {};
13
+ action.payload.addresses.forEach((addr) => {
14
+ doctorMap[addr.id] = addr.doctors || [];
15
+ });
16
+ return {
17
+ ...state,
18
+ workspaceId: action.payload.id,
19
+ addresses: action.payload.addresses,
20
+ addressDoctorsMap: doctorMap,
21
+ };
22
+ }
23
+ case "SET_SELECTED_ADDRESS":
24
+ return { ...state, selectedAddress: action.payload };
25
+ case "SET_SELECTED_DOCTOR":
26
+ return { ...state, selectedDoctor: action.payload };
27
+ case "SET_SELECTED_DATE":
28
+ return { ...state, selectedDate: action.payload, selectedSlot: null };
29
+ case "SET_SLOTS":
30
+ return { ...state, slots: action.payload };
31
+ case "SET_SELECTED_SLOT":
32
+ return { ...state, selectedSlot: action.payload };
33
+ case "SET_CONSULTATION_MODE":
34
+ return { ...state, consultationMode: action.payload };
35
+ case "SET_CONSULTATION_CHARGE":
36
+ return { ...state, consultationCharge: action.payload };
37
+ case "SET_PATIENT_NAME":
38
+ return { ...state, patientName: action.payload };
39
+ case "SET_PATIENT_AGE":
40
+ return { ...state, patientAge: action.payload };
41
+ case "SET_PATIENT_EMAIL":
42
+ return { ...state, patientEmail: action.payload };
43
+ case "SET_PATIENT_GENDER":
44
+ return { ...state, patientGender: action.payload };
45
+ case "SET_BLOOD_GROUP":
46
+ return { ...state, bloodGroup: action.payload };
47
+ case "SET_PATIENT_ADDRESS":
48
+ return { ...state, patientAddress: action.payload };
49
+ case "SET_PATIENT_CITY":
50
+ return { ...state, patientCity: action.payload };
51
+ case "SET_PATIENT_STATE":
52
+ return { ...state, patientState: action.payload };
53
+ case "SET_PATIENT_COUNTRY":
54
+ return { ...state, patientCountry: action.payload };
55
+ case "SET_PATIENT_ZIPCODE":
56
+ return { ...state, patientZipcode: action.payload };
57
+ case "SET_PATIENT_LANDMARK":
58
+ return { ...state, patientLandmark: action.payload };
59
+ case "SET_COUNTRY_CODE":
60
+ return { ...state, countryCode: action.payload };
61
+ case "SET_PATIENT_PHONE":
62
+ return { ...state, patientPhone: action.payload };
63
+ case "SET_OTP_CODE":
64
+ return { ...state, otpCode: action.payload };
65
+ case "SET_OTP_SENT":
66
+ return { ...state, otpSent: action.payload };
67
+ case "SET_OTP_VERIFIED":
68
+ return { ...state, otpVerified: action.payload };
69
+ case "SET_OTP_SENDING":
70
+ return { ...state, otpSending: action.payload };
71
+ case "SET_OTP_VERIFYING":
72
+ return { ...state, otpVerifying: action.payload };
73
+ case "RESET_FORM":
74
+ return INITIAL_STATE;
75
+ default:
76
+ return state;
77
+ }
78
+ };
79
+ export const useAppointmentState = () => {
80
+ const [state, dispatch] = useReducer(appointmentReducer, INITIAL_STATE);
81
+ const setStep = useCallback((step) => dispatch({ type: "SET_STEP", payload: step }), []);
82
+ const setError = useCallback((error) => dispatch({ type: "SET_ERROR", payload: error }), []);
83
+ const setLoading = useCallback((loading) => dispatch({ type: "SET_LOADING", payload: loading }), []);
84
+ const resetForm = useCallback(() => dispatch({ type: "RESET_FORM" }), []);
85
+ return {
86
+ state,
87
+ dispatch,
88
+ setStep,
89
+ setError,
90
+ setLoading,
91
+ resetForm,
92
+ };
93
+ };
@@ -0,0 +1 @@
1
+ export declare const useInitializeAddresses: (dispatch: React.Dispatch<any>, onError?: (error: Error) => void) => void;
@@ -0,0 +1,56 @@
1
+ import { useEffect } from "react";
2
+ import { AppointmentService } from "../../../services/AppointmentService";
3
+ export const useInitializeAddresses = (dispatch, onError) => {
4
+ useEffect(() => {
5
+ let mounted = true;
6
+ (async () => {
7
+ dispatch({ type: "SET_LOADING", payload: true });
8
+ dispatch({ type: "SET_ERROR", payload: null });
9
+ try {
10
+ const response = await AppointmentService.getAddresses();
11
+ if (!mounted)
12
+ return;
13
+ if (response.workspaceId && response.addresses?.length > 0) {
14
+ dispatch({
15
+ type: "SET_WORKSPACE",
16
+ payload: {
17
+ id: response.workspaceId,
18
+ addresses: response.addresses,
19
+ },
20
+ });
21
+ if (response.addresses.length === 1) {
22
+ const address = response.addresses[0];
23
+ dispatch({ type: "SET_SELECTED_ADDRESS", payload: address.id });
24
+ if (address.doctors?.length === 1) {
25
+ dispatch({
26
+ type: "SET_SELECTED_DOCTOR",
27
+ payload: address.doctors[0].id,
28
+ });
29
+ dispatch({ type: "SET_STEP", payload: 1 });
30
+ }
31
+ }
32
+ }
33
+ else {
34
+ dispatch({
35
+ type: "SET_ERROR",
36
+ payload: "No addresses or doctors available.",
37
+ });
38
+ }
39
+ }
40
+ catch (e) {
41
+ if (!mounted)
42
+ return;
43
+ const msg = e.message || "Failed to load addresses";
44
+ dispatch({ type: "SET_ERROR", payload: msg });
45
+ onError?.(e);
46
+ }
47
+ finally {
48
+ if (mounted)
49
+ dispatch({ type: "SET_LOADING", payload: false });
50
+ }
51
+ })();
52
+ return () => {
53
+ mounted = false;
54
+ };
55
+ }, [dispatch, onError]);
56
+ };
@@ -0,0 +1,5 @@
1
+ export { AppointmentCalender } from "./AppointmentCalender";
2
+ export type { AppointmentCalenderProps } from "./AppointmentCalender";
3
+ export { useAppointmentState, useAppointmentFlow, useInitializeAddresses, } from "./hooks";
4
+ export type { AppointmentState, AppointmentAction } from "./types";
5
+ export { INITIAL_STATE } from "./types";
@@ -0,0 +1,3 @@
1
+ export { AppointmentCalender } from "./AppointmentCalender";
2
+ export { useAppointmentState, useAppointmentFlow, useInitializeAddresses, } from "./hooks";
3
+ export { INITIAL_STATE } from "./types";
@@ -0,0 +1,128 @@
1
+ import { Doctor, Slot, AddressItem } from "../../services/AppointmentService";
2
+ export interface AppointmentState {
3
+ step: number;
4
+ loading: boolean;
5
+ error: string | null;
6
+ workspaceId: number | null;
7
+ addresses: AddressItem[];
8
+ addressDoctorsMap: Record<number, Doctor[]>;
9
+ selectedAddress: number | null;
10
+ selectedDoctor: number | null;
11
+ selectedDate: Date;
12
+ slots: Slot[];
13
+ selectedSlot: Slot | null;
14
+ consultationMode: "ONLINE" | "OFFLINE";
15
+ consultationCharge: string;
16
+ patientName: string;
17
+ patientAge: string;
18
+ patientEmail: string;
19
+ patientGender: string;
20
+ bloodGroup: string;
21
+ patientAddress: string;
22
+ patientCity: string;
23
+ patientState: string;
24
+ patientCountry: string;
25
+ patientZipcode: string;
26
+ patientLandmark: string;
27
+ countryCode: string;
28
+ patientPhone: string;
29
+ otpCode: string;
30
+ otpSent: boolean;
31
+ otpVerified: boolean;
32
+ otpSending: boolean;
33
+ otpVerifying: boolean;
34
+ }
35
+ export type AppointmentAction = {
36
+ type: "SET_STEP";
37
+ payload: number;
38
+ } | {
39
+ type: "SET_LOADING";
40
+ payload: boolean;
41
+ } | {
42
+ type: "SET_ERROR";
43
+ payload: string | null;
44
+ } | {
45
+ type: "SET_WORKSPACE";
46
+ payload: {
47
+ id: number;
48
+ addresses: AddressItem[];
49
+ };
50
+ } | {
51
+ type: "SET_SELECTED_ADDRESS";
52
+ payload: number | null;
53
+ } | {
54
+ type: "SET_SELECTED_DOCTOR";
55
+ payload: number | null;
56
+ } | {
57
+ type: "SET_SELECTED_DATE";
58
+ payload: Date;
59
+ } | {
60
+ type: "SET_SLOTS";
61
+ payload: Slot[];
62
+ } | {
63
+ type: "SET_SELECTED_SLOT";
64
+ payload: Slot | null;
65
+ } | {
66
+ type: "SET_CONSULTATION_MODE";
67
+ payload: "ONLINE" | "OFFLINE";
68
+ } | {
69
+ type: "SET_CONSULTATION_CHARGE";
70
+ payload: string;
71
+ } | {
72
+ type: "SET_PATIENT_NAME";
73
+ payload: string;
74
+ } | {
75
+ type: "SET_PATIENT_AGE";
76
+ payload: string;
77
+ } | {
78
+ type: "SET_PATIENT_EMAIL";
79
+ payload: string;
80
+ } | {
81
+ type: "SET_PATIENT_GENDER";
82
+ payload: string;
83
+ } | {
84
+ type: "SET_BLOOD_GROUP";
85
+ payload: string;
86
+ } | {
87
+ type: "SET_PATIENT_ADDRESS";
88
+ payload: string;
89
+ } | {
90
+ type: "SET_PATIENT_CITY";
91
+ payload: string;
92
+ } | {
93
+ type: "SET_PATIENT_STATE";
94
+ payload: string;
95
+ } | {
96
+ type: "SET_PATIENT_COUNTRY";
97
+ payload: string;
98
+ } | {
99
+ type: "SET_PATIENT_ZIPCODE";
100
+ payload: string;
101
+ } | {
102
+ type: "SET_PATIENT_LANDMARK";
103
+ payload: string;
104
+ } | {
105
+ type: "SET_COUNTRY_CODE";
106
+ payload: string;
107
+ } | {
108
+ type: "SET_PATIENT_PHONE";
109
+ payload: string;
110
+ } | {
111
+ type: "SET_OTP_CODE";
112
+ payload: string;
113
+ } | {
114
+ type: "SET_OTP_SENT";
115
+ payload: boolean;
116
+ } | {
117
+ type: "SET_OTP_VERIFIED";
118
+ payload: boolean;
119
+ } | {
120
+ type: "SET_OTP_SENDING";
121
+ payload: boolean;
122
+ } | {
123
+ type: "SET_OTP_VERIFYING";
124
+ payload: boolean;
125
+ } | {
126
+ type: "RESET_FORM";
127
+ };
128
+ export declare const INITIAL_STATE: AppointmentState;