pih-appointment-widget 0.0.38 → 0.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -70
- package/babel.config.js +5 -5
- package/dist/App.js +10 -9
- package/dist/components/AppointmentPage.js +87 -62
- package/dist/components/ICD10Assistant.js +43 -46
- package/dist/doctor-appointments-widget.umd.js +115 -119
- package/dist/doctor-appointments-widget.umd.min.js +1 -1
- package/dist/hooks/useClipboard.js +3 -3
- package/dist/pih-appointment-widget.umd.js +185 -191
- package/dist/pih-appointment-widget.umd.min.js +1 -1
- package/dist/services/appointmentService.js +20 -20
- package/dist/services/httpService.js +14 -18
- package/dist/services/icdService.js +21 -23
- package/package.json +67 -67
- package/public/index.html +43 -43
- package/public/manifest.json +25 -25
- package/public/robots.txt +3 -3
- package/rollup.config.js +43 -43
- package/src/App.js +50 -50
- package/src/Example.js +14 -14
- package/src/assets/icons/icdIcons.js +23 -23
- package/src/components/AppointmentPage.js +2502 -2498
- package/src/components/ICD10Assistant.jsx +923 -923
- package/src/constants/apiConfig.js +29 -29
- package/src/hooks/useClipboard.js +35 -35
- package/src/index.js +6 -6
- package/src/services/appointmentService.js +92 -92
- package/src/services/httpService.js +103 -103
- package/src/services/icdService.js +76 -76
package/README.md
CHANGED
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
# Getting Started with Create React App
|
|
2
|
-
|
|
3
|
-
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
|
4
|
-
|
|
5
|
-
## Available Scripts
|
|
6
|
-
|
|
7
|
-
In the project directory, you can run:
|
|
8
|
-
|
|
9
|
-
### `npm start`
|
|
10
|
-
|
|
11
|
-
Runs the app in the development mode.\
|
|
12
|
-
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
|
13
|
-
|
|
14
|
-
The page will reload when you make changes.\
|
|
15
|
-
You may also see any lint errors in the console.
|
|
16
|
-
|
|
17
|
-
### `npm test`
|
|
18
|
-
|
|
19
|
-
Launches the test runner in the interactive watch mode.\
|
|
20
|
-
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
|
21
|
-
|
|
22
|
-
### `npm run build`
|
|
23
|
-
|
|
24
|
-
Builds the app for production to the `build` folder.\
|
|
25
|
-
It correctly bundles React in production mode and optimizes the build for the best performance.
|
|
26
|
-
|
|
27
|
-
The build is minified and the filenames include the hashes.\
|
|
28
|
-
Your app is ready to be deployed!
|
|
29
|
-
|
|
30
|
-
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
|
31
|
-
|
|
32
|
-
### `npm run eject`
|
|
33
|
-
|
|
34
|
-
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
|
35
|
-
|
|
36
|
-
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
|
37
|
-
|
|
38
|
-
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
|
39
|
-
|
|
40
|
-
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
|
41
|
-
|
|
42
|
-
## Learn More
|
|
43
|
-
|
|
44
|
-
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
|
45
|
-
|
|
46
|
-
To learn React, check out the [React documentation](https://reactjs.org/).
|
|
47
|
-
|
|
48
|
-
### Code Splitting
|
|
49
|
-
|
|
50
|
-
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
|
51
|
-
|
|
52
|
-
### Analyzing the Bundle Size
|
|
53
|
-
|
|
54
|
-
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
|
55
|
-
|
|
56
|
-
### Making a Progressive Web App
|
|
57
|
-
|
|
58
|
-
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
|
59
|
-
|
|
60
|
-
### Advanced Configuration
|
|
61
|
-
|
|
62
|
-
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
|
63
|
-
|
|
64
|
-
### Deployment
|
|
65
|
-
|
|
66
|
-
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
|
67
|
-
|
|
68
|
-
### `npm run build` fails to minify
|
|
69
|
-
|
|
70
|
-
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
|
1
|
+
# Getting Started with Create React App
|
|
2
|
+
|
|
3
|
+
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
|
4
|
+
|
|
5
|
+
## Available Scripts
|
|
6
|
+
|
|
7
|
+
In the project directory, you can run:
|
|
8
|
+
|
|
9
|
+
### `npm start`
|
|
10
|
+
|
|
11
|
+
Runs the app in the development mode.\
|
|
12
|
+
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
|
13
|
+
|
|
14
|
+
The page will reload when you make changes.\
|
|
15
|
+
You may also see any lint errors in the console.
|
|
16
|
+
|
|
17
|
+
### `npm test`
|
|
18
|
+
|
|
19
|
+
Launches the test runner in the interactive watch mode.\
|
|
20
|
+
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
|
21
|
+
|
|
22
|
+
### `npm run build`
|
|
23
|
+
|
|
24
|
+
Builds the app for production to the `build` folder.\
|
|
25
|
+
It correctly bundles React in production mode and optimizes the build for the best performance.
|
|
26
|
+
|
|
27
|
+
The build is minified and the filenames include the hashes.\
|
|
28
|
+
Your app is ready to be deployed!
|
|
29
|
+
|
|
30
|
+
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
|
31
|
+
|
|
32
|
+
### `npm run eject`
|
|
33
|
+
|
|
34
|
+
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
|
35
|
+
|
|
36
|
+
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
|
37
|
+
|
|
38
|
+
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
|
39
|
+
|
|
40
|
+
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
|
41
|
+
|
|
42
|
+
## Learn More
|
|
43
|
+
|
|
44
|
+
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
|
45
|
+
|
|
46
|
+
To learn React, check out the [React documentation](https://reactjs.org/).
|
|
47
|
+
|
|
48
|
+
### Code Splitting
|
|
49
|
+
|
|
50
|
+
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
|
51
|
+
|
|
52
|
+
### Analyzing the Bundle Size
|
|
53
|
+
|
|
54
|
+
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
|
55
|
+
|
|
56
|
+
### Making a Progressive Web App
|
|
57
|
+
|
|
58
|
+
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
|
59
|
+
|
|
60
|
+
### Advanced Configuration
|
|
61
|
+
|
|
62
|
+
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
|
63
|
+
|
|
64
|
+
### Deployment
|
|
65
|
+
|
|
66
|
+
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
|
67
|
+
|
|
68
|
+
### `npm run build` fails to minify
|
|
69
|
+
|
|
70
|
+
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
package/babel.config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
presets: [
|
|
3
|
-
'@babel/preset-env',
|
|
4
|
-
'@babel/preset-react'
|
|
5
|
-
],
|
|
1
|
+
module.exports = {
|
|
2
|
+
presets: [
|
|
3
|
+
'@babel/preset-env',
|
|
4
|
+
'@babel/preset-react'
|
|
5
|
+
],
|
|
6
6
|
};
|
package/dist/App.js
CHANGED
|
@@ -26,20 +26,21 @@ var _react = _interopRequireDefault(require("react"));
|
|
|
26
26
|
var _reactDom = _interopRequireDefault(require("react-dom"));
|
|
27
27
|
var _AppointmentPage = _interopRequireWildcard(require("./components/AppointmentPage.js"));
|
|
28
28
|
var _ICD10Assistant = _interopRequireDefault(require("./components/ICD10Assistant.jsx"));
|
|
29
|
-
function
|
|
29
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
30
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
30
31
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
31
32
|
let bookingWidgetInstance = null;
|
|
32
33
|
|
|
33
34
|
// SDK for embedding appointment widget
|
|
34
35
|
const BookingSDK = {
|
|
35
|
-
/**
|
|
36
|
-
* Show appointment widget with configuration
|
|
37
|
-
* @param {object} config - Configuration object
|
|
38
|
-
* @param {string} config.apiBaseUrl - Base URL for API
|
|
39
|
-
* @param {string} config.hospitalId - Hospital ID
|
|
40
|
-
* @param {number} config.doctorId - Doctor ID
|
|
41
|
-
* @param {string} config.joinCallUrl - Video call URL
|
|
42
|
-
* @param {function} onAction - Optional callback for actions
|
|
36
|
+
/**
|
|
37
|
+
* Show appointment widget with configuration
|
|
38
|
+
* @param {object} config - Configuration object
|
|
39
|
+
* @param {string} config.apiBaseUrl - Base URL for API
|
|
40
|
+
* @param {string} config.hospitalId - Hospital ID
|
|
41
|
+
* @param {number} config.doctorId - Doctor ID
|
|
42
|
+
* @param {string} config.joinCallUrl - Video call URL
|
|
43
|
+
* @param {function} onAction - Optional callback for actions
|
|
43
44
|
*/
|
|
44
45
|
showWidget: (config, onAction) => {
|
|
45
46
|
if (!bookingWidgetInstance) {
|
|
@@ -8,10 +8,8 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
8
8
|
var _appointmentService = require("../services/appointmentService");
|
|
9
9
|
var _httpService = require("../services/httpService");
|
|
10
10
|
var _apiConfig = require("../constants/apiConfig");
|
|
11
|
-
function
|
|
12
|
-
function
|
|
13
|
-
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
14
|
-
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
11
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
12
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
15
13
|
// Decode JWT payload (no verification; for display only). Returns {} on invalid/missing.
|
|
16
14
|
function getJwtPayload(token) {
|
|
17
15
|
if (!token || typeof token !== "string") return {};
|
|
@@ -21,7 +19,7 @@ function getJwtPayload(token) {
|
|
|
21
19
|
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
22
20
|
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
23
21
|
return JSON.parse(atob(padded));
|
|
24
|
-
} catch
|
|
22
|
+
} catch {
|
|
25
23
|
return {};
|
|
26
24
|
}
|
|
27
25
|
}
|
|
@@ -41,32 +39,26 @@ const PIH_APPOINTMENT_WIDGET_DATA_ATTR = "data-pih-widget";
|
|
|
41
39
|
|
|
42
40
|
// Extract from SSO login response (not from appToken JWT — these are only in login response)
|
|
43
41
|
function extractAppToken(response) {
|
|
44
|
-
var _ref, _ref2, _ref3, _response$data$access, _response$data, _response$data2;
|
|
45
42
|
if (!response || response.err) return null;
|
|
46
|
-
return
|
|
43
|
+
return response.data?.access_token ?? response.data?.token ?? response.token ?? response.accessToken ?? null;
|
|
47
44
|
}
|
|
48
45
|
function extractDoctorIdFromLoginResponse(response) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const id = (_ref4 = (_response$data$doctor = response.data.doctor_id) !== null && _response$data$doctor !== void 0 ? _response$data$doctor : response.data.doctorId) !== null && _ref4 !== void 0 ? _ref4 : null;
|
|
46
|
+
if (!response?.data) return null;
|
|
47
|
+
const id = response.data.doctor_id ?? response.data.doctorId ?? null;
|
|
52
48
|
console.log(id, 'extractDoctorIdFromLoginResponse -> id');
|
|
53
49
|
return id != null ? String(id) : null;
|
|
54
50
|
}
|
|
55
51
|
function extractUserNameFromLoginResponse(response) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const name = (_ref5 = (_ref6 = (_response$data$name = response.data.name) !== null && _response$data$name !== void 0 ? _response$data$name : response.data.doctor_name) !== null && _ref6 !== void 0 ? _ref6 : response.data.userName) !== null && _ref5 !== void 0 ? _ref5 : null;
|
|
52
|
+
if (!response?.data) return null;
|
|
53
|
+
const name = response.data.name ?? response.data.doctor_name ?? response.data.userName ?? null;
|
|
59
54
|
return name != null ? String(name) : null;
|
|
60
55
|
}
|
|
61
56
|
|
|
62
57
|
// Error boundary so a render error never shows a blank screen
|
|
63
58
|
class AppointmentErrorBoundary extends _react.Component {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
hasError: false
|
|
68
|
-
});
|
|
69
|
-
}
|
|
59
|
+
state = {
|
|
60
|
+
hasError: false
|
|
61
|
+
};
|
|
70
62
|
static getDerivedStateFromError() {
|
|
71
63
|
return {
|
|
72
64
|
hasError: true
|
|
@@ -123,21 +115,21 @@ class AppointmentErrorBoundary extends _react.Component {
|
|
|
123
115
|
}
|
|
124
116
|
|
|
125
117
|
// SDK Component - accepts configuration from parent app
|
|
126
|
-
const AppointmentPage =
|
|
118
|
+
const AppointmentPage = _ref => {
|
|
127
119
|
let {
|
|
128
120
|
config = {}
|
|
129
|
-
} =
|
|
121
|
+
} = _ref;
|
|
130
122
|
const [storedApiBaseUrl, setStoredApiBaseUrl] = (0, _react.useState)(() => {
|
|
131
123
|
try {
|
|
132
124
|
return typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE_KEY_API_BASE_URL) : null;
|
|
133
|
-
} catch
|
|
125
|
+
} catch {
|
|
134
126
|
return null;
|
|
135
127
|
}
|
|
136
128
|
});
|
|
137
129
|
const [storedHospitalId, setStoredHospitalId] = (0, _react.useState)(() => {
|
|
138
130
|
try {
|
|
139
131
|
return typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE_KEY_HOSPITAL_ID) : null;
|
|
140
|
-
} catch
|
|
132
|
+
} catch {
|
|
141
133
|
return null;
|
|
142
134
|
}
|
|
143
135
|
});
|
|
@@ -148,14 +140,14 @@ const AppointmentPage = _ref7 => {
|
|
|
148
140
|
const [storedIdToken, setStoredIdToken] = (0, _react.useState)(() => {
|
|
149
141
|
try {
|
|
150
142
|
return typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE_KEY_ID_TOKEN) : null;
|
|
151
|
-
} catch
|
|
143
|
+
} catch {
|
|
152
144
|
return null;
|
|
153
145
|
}
|
|
154
146
|
});
|
|
155
147
|
const [storedEmail, setStoredEmail] = (0, _react.useState)(() => {
|
|
156
148
|
try {
|
|
157
149
|
return typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE_KEY_EMAIL) : null;
|
|
158
|
-
} catch
|
|
150
|
+
} catch {
|
|
159
151
|
return null;
|
|
160
152
|
}
|
|
161
153
|
});
|
|
@@ -170,12 +162,14 @@ const AppointmentPage = _ref7 => {
|
|
|
170
162
|
if (token && String(token).trim()) {
|
|
171
163
|
const prevToken = localStorage.getItem(STORAGE_KEY_ID_TOKEN);
|
|
172
164
|
if (prevToken !== token) {
|
|
173
|
-
// New token from parent (e.g. Flutter re-login) — clear
|
|
174
|
-
//
|
|
165
|
+
// New token from parent (e.g. Flutter re-login) — clear stored credentials.
|
|
166
|
+
// Only increment refreshLoginTrigger when there was an existing appToken to evict;
|
|
167
|
+
// if appToken is already absent, the SSO effect will fire naturally via !appToken.
|
|
168
|
+
const hadAppToken = !!localStorage.getItem(STORAGE_KEY_APP_TOKEN);
|
|
175
169
|
setAppToken(null);
|
|
176
170
|
setDoctorIdFromLogin(null);
|
|
177
171
|
setUserName(null);
|
|
178
|
-
setRefreshLoginTrigger(t => t + 1);
|
|
172
|
+
if (hadAppToken) setRefreshLoginTrigger(t => t + 1);
|
|
179
173
|
localStorage.removeItem(STORAGE_KEY_APP_TOKEN);
|
|
180
174
|
localStorage.removeItem(STORAGE_KEY_DOCTOR_ID);
|
|
181
175
|
localStorage.removeItem(STORAGE_KEY_USER_NAME);
|
|
@@ -200,21 +194,21 @@ const AppointmentPage = _ref7 => {
|
|
|
200
194
|
const [appToken, setAppToken] = (0, _react.useState)(() => {
|
|
201
195
|
try {
|
|
202
196
|
return typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE_KEY_APP_TOKEN) : null;
|
|
203
|
-
} catch
|
|
197
|
+
} catch {
|
|
204
198
|
return null;
|
|
205
199
|
}
|
|
206
200
|
});
|
|
207
201
|
const [doctorIdFromLogin, setDoctorIdFromLogin] = (0, _react.useState)(() => {
|
|
208
202
|
try {
|
|
209
203
|
return typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE_KEY_DOCTOR_ID) : null;
|
|
210
|
-
} catch
|
|
204
|
+
} catch {
|
|
211
205
|
return null;
|
|
212
206
|
}
|
|
213
207
|
});
|
|
214
208
|
const [userName, setUserName] = (0, _react.useState)(() => {
|
|
215
209
|
try {
|
|
216
210
|
return typeof localStorage !== "undefined" ? localStorage.getItem(STORAGE_KEY_USER_NAME) : null;
|
|
217
|
-
} catch
|
|
211
|
+
} catch {
|
|
218
212
|
return null;
|
|
219
213
|
}
|
|
220
214
|
});
|
|
@@ -225,7 +219,7 @@ const AppointmentPage = _ref7 => {
|
|
|
225
219
|
const hasId = localStorage.getItem(STORAGE_KEY_ID_TOKEN);
|
|
226
220
|
const hasEmail = localStorage.getItem(STORAGE_KEY_EMAIL);
|
|
227
221
|
return !hasApp && !!(hasId && hasEmail);
|
|
228
|
-
} catch
|
|
222
|
+
} catch {
|
|
229
223
|
return false;
|
|
230
224
|
}
|
|
231
225
|
});
|
|
@@ -287,7 +281,7 @@ const AppointmentPage = _ref7 => {
|
|
|
287
281
|
const y = date.getFullYear();
|
|
288
282
|
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
289
283
|
const d = String(date.getDate()).padStart(2, "0");
|
|
290
|
-
return
|
|
284
|
+
return `${y}-${m}-${d}`;
|
|
291
285
|
};
|
|
292
286
|
const getDateRange = option => {
|
|
293
287
|
const today = new Date();
|
|
@@ -318,8 +312,8 @@ const AppointmentPage = _ref7 => {
|
|
|
318
312
|
to = formatLocalDate(lastOfMonth);
|
|
319
313
|
break;
|
|
320
314
|
case "currentYear":
|
|
321
|
-
from =
|
|
322
|
-
to =
|
|
315
|
+
from = `${today.getFullYear()}-01-01`;
|
|
316
|
+
to = `${today.getFullYear()}-12-31`;
|
|
323
317
|
break;
|
|
324
318
|
default:
|
|
325
319
|
from = to = getTodayDate();
|
|
@@ -344,7 +338,7 @@ const AppointmentPage = _ref7 => {
|
|
|
344
338
|
case "currentYear":
|
|
345
339
|
return "Current Year";
|
|
346
340
|
case "custom":
|
|
347
|
-
return isMobile ? "Custom" :
|
|
341
|
+
return isMobile ? "Custom" : `${fromDate} to ${toDate}`;
|
|
348
342
|
default:
|
|
349
343
|
return "Today";
|
|
350
344
|
}
|
|
@@ -429,12 +423,12 @@ const AppointmentPage = _ref7 => {
|
|
|
429
423
|
|
|
430
424
|
// Helper to get unique identifier from appointment
|
|
431
425
|
const getAppointmentId = appointment => {
|
|
432
|
-
return
|
|
426
|
+
return appointment?.id || appointment?._id || appointment?.appointmentId || appointment?.patientId || JSON.stringify(appointment);
|
|
433
427
|
};
|
|
434
428
|
|
|
435
429
|
// Generate avatar with first letter if no image
|
|
436
430
|
const getPatientAvatar = appointment => {
|
|
437
|
-
if (appointment
|
|
431
|
+
if (appointment?.image) {
|
|
438
432
|
return appointment.image;
|
|
439
433
|
}
|
|
440
434
|
// Return null to use the letter avatar component
|
|
@@ -555,8 +549,8 @@ const AppointmentPage = _ref7 => {
|
|
|
555
549
|
// Returns true if the current time is within 2 hours after the appointment's scheduled slot time.
|
|
556
550
|
// Handles date format: "Sun, 15 Mar 2026" and time format: "12:15 PM"
|
|
557
551
|
const isWithinJoinWindow = appointment => {
|
|
558
|
-
const dateStr =
|
|
559
|
-
const timeStr =
|
|
552
|
+
const dateStr = appointment?.date || appointment?.appointmentDate;
|
|
553
|
+
const timeStr = appointment?.time || appointment?.appointmentTime;
|
|
560
554
|
if (!dateStr || !timeStr) return false;
|
|
561
555
|
try {
|
|
562
556
|
// Parse "12:15 PM" or "9:30 AM" into 24-hour h/m values
|
|
@@ -576,11 +570,11 @@ const AppointmentPage = _ref7 => {
|
|
|
576
570
|
}
|
|
577
571
|
if (isNaN(h) || isNaN(m)) return false;
|
|
578
572
|
// "Sun, 15 Mar 2026 12:15:00" — space-separated; JS parses RFC-like date strings correctly
|
|
579
|
-
const appointmentDate = new Date(
|
|
573
|
+
const appointmentDate = new Date(`${dateStr} ${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:00`);
|
|
580
574
|
if (isNaN(appointmentDate.getTime())) return false;
|
|
581
575
|
const windowEnd = new Date(appointmentDate.getTime() + 2 * 60 * 60 * 1000);
|
|
582
576
|
return new Date() <= windowEnd;
|
|
583
|
-
} catch
|
|
577
|
+
} catch {
|
|
584
578
|
return false;
|
|
585
579
|
}
|
|
586
580
|
};
|
|
@@ -591,7 +585,6 @@ const AppointmentPage = _ref7 => {
|
|
|
591
585
|
setCallLoading(true);
|
|
592
586
|
setCallError(null);
|
|
593
587
|
try {
|
|
594
|
-
var _response$data3;
|
|
595
588
|
const callConfig = {
|
|
596
589
|
apiBaseUrl,
|
|
597
590
|
hospitalId,
|
|
@@ -619,7 +612,7 @@ const AppointmentPage = _ref7 => {
|
|
|
619
612
|
setCallError("Session expired. Re-authenticating...");
|
|
620
613
|
return;
|
|
621
614
|
}
|
|
622
|
-
if (response.err || !
|
|
615
|
+
if (response.err || !response.data?.token) {
|
|
623
616
|
setCallError(String(response.err || "Failed to initiate call"));
|
|
624
617
|
return;
|
|
625
618
|
}
|
|
@@ -842,6 +835,7 @@ const AppointmentPage = _ref7 => {
|
|
|
842
835
|
setAppToken(null);
|
|
843
836
|
setDoctorIdFromLogin(null);
|
|
844
837
|
setUserName(null);
|
|
838
|
+
setRefreshLoginTrigger(0); // reset trigger even on failure to prevent infinite retry loops
|
|
845
839
|
try {
|
|
846
840
|
if (typeof localStorage !== "undefined") {
|
|
847
841
|
localStorage.removeItem(STORAGE_KEY_APP_TOKEN);
|
|
@@ -858,6 +852,7 @@ const AppointmentPage = _ref7 => {
|
|
|
858
852
|
setUserName(name);
|
|
859
853
|
setTokenError(null);
|
|
860
854
|
setRedirectToHome(false);
|
|
855
|
+
setRefreshLoginTrigger(0); // reset one-shot trigger so SSO doesn't re-fire on next dep change
|
|
861
856
|
try {
|
|
862
857
|
if (typeof localStorage !== "undefined") {
|
|
863
858
|
localStorage.setItem(STORAGE_KEY_APP_TOKEN, token);
|
|
@@ -868,7 +863,7 @@ const AppointmentPage = _ref7 => {
|
|
|
868
863
|
}
|
|
869
864
|
}).catch(err => {
|
|
870
865
|
if (!cancelled) {
|
|
871
|
-
setTokenError(
|
|
866
|
+
setTokenError(err?.message || "Authentication failed. Please try again.");
|
|
872
867
|
setAppToken(null);
|
|
873
868
|
}
|
|
874
869
|
}).finally(() => {
|
|
@@ -900,7 +895,32 @@ const AppointmentPage = _ref7 => {
|
|
|
900
895
|
|
|
901
896
|
// Add responsive styles and animations
|
|
902
897
|
const style = document.createElement("style");
|
|
903
|
-
style.innerHTML =
|
|
898
|
+
style.innerHTML = `
|
|
899
|
+
@keyframes spin {
|
|
900
|
+
0% { transform: rotate(0deg); }
|
|
901
|
+
100% { transform: rotate(360deg); }
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
@media (max-width: 768px) {
|
|
905
|
+
.appointments-grid {
|
|
906
|
+
grid-template-columns: 1.5fr 1fr 0.8fr !important;
|
|
907
|
+
}
|
|
908
|
+
.appointments-header-grid {
|
|
909
|
+
grid-template-columns: 1.5fr 1fr 0.8fr !important;
|
|
910
|
+
}
|
|
911
|
+
.hide-on-mobile {
|
|
912
|
+
display: none !important;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
@media (max-width: 480px) {
|
|
916
|
+
.appointments-header-grid {
|
|
917
|
+
font-size: 10px !important;
|
|
918
|
+
}
|
|
919
|
+
.appointments-grid {
|
|
920
|
+
font-size: 11px !important;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
`;
|
|
904
924
|
document.head.appendChild(style);
|
|
905
925
|
|
|
906
926
|
// Handle window resize
|
|
@@ -953,7 +973,7 @@ const AppointmentPage = _ref7 => {
|
|
|
953
973
|
justifyContent: "center",
|
|
954
974
|
padding: "24px"
|
|
955
975
|
}
|
|
956
|
-
}, /*#__PURE__*/_react.default.createElement("style", null,
|
|
976
|
+
}, /*#__PURE__*/_react.default.createElement("style", null, `@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }`), /*#__PURE__*/_react.default.createElement("div", {
|
|
957
977
|
style: {
|
|
958
978
|
width: "40px",
|
|
959
979
|
height: "40px",
|
|
@@ -1712,7 +1732,7 @@ const AppointmentPage = _ref7 => {
|
|
|
1712
1732
|
style: {
|
|
1713
1733
|
fontSize: "13px"
|
|
1714
1734
|
}
|
|
1715
|
-
}, searchQuery ?
|
|
1735
|
+
}, searchQuery ? `No appointments found for "${searchQuery}"` : "No appointments found"), searchQuery && /*#__PURE__*/_react.default.createElement("button", {
|
|
1716
1736
|
onClick: () => setSearchQuery(""),
|
|
1717
1737
|
style: {
|
|
1718
1738
|
marginTop: "12px",
|
|
@@ -1991,7 +2011,7 @@ const AppointmentPage = _ref7 => {
|
|
|
1991
2011
|
fontWeight: "700",
|
|
1992
2012
|
fontSize: isMobile ? "12px" : "13px"
|
|
1993
2013
|
}
|
|
1994
|
-
},
|
|
2014
|
+
}, selectedAppointment?.specialisation || selectedAppointment?.speciality || "N/A")), /*#__PURE__*/_react.default.createElement("div", {
|
|
1995
2015
|
style: {
|
|
1996
2016
|
textAlign: "right"
|
|
1997
2017
|
}
|
|
@@ -2006,7 +2026,7 @@ const AppointmentPage = _ref7 => {
|
|
|
2006
2026
|
fontWeight: "700",
|
|
2007
2027
|
fontSize: isMobile ? "12px" : "13px"
|
|
2008
2028
|
}
|
|
2009
|
-
},
|
|
2029
|
+
}, selectedAppointment?.type || selectedAppointment?.appointmentType || "Online"))), /*#__PURE__*/_react.default.createElement("div", {
|
|
2010
2030
|
style: {
|
|
2011
2031
|
display: "flex",
|
|
2012
2032
|
justifyContent: "space-between"
|
|
@@ -2026,7 +2046,7 @@ const AppointmentPage = _ref7 => {
|
|
|
2026
2046
|
fontWeight: "700",
|
|
2027
2047
|
fontSize: isMobile ? "12px" : "13px"
|
|
2028
2048
|
}
|
|
2029
|
-
},
|
|
2049
|
+
}, selectedAppointment?.date || selectedAppointment?.appointmentDate || "N/A")), /*#__PURE__*/_react.default.createElement("div", {
|
|
2030
2050
|
style: {
|
|
2031
2051
|
textAlign: "right"
|
|
2032
2052
|
}
|
|
@@ -2041,7 +2061,7 @@ const AppointmentPage = _ref7 => {
|
|
|
2041
2061
|
fontWeight: "700",
|
|
2042
2062
|
fontSize: isMobile ? "12px" : "13px"
|
|
2043
2063
|
}
|
|
2044
|
-
},
|
|
2064
|
+
}, selectedAppointment?.time || selectedAppointment?.appointmentTime || "N/A"))), /*#__PURE__*/_react.default.createElement("div", {
|
|
2045
2065
|
style: {
|
|
2046
2066
|
display: "flex",
|
|
2047
2067
|
justifyContent: "space-between"
|
|
@@ -2061,7 +2081,7 @@ const AppointmentPage = _ref7 => {
|
|
|
2061
2081
|
fontWeight: "700",
|
|
2062
2082
|
fontSize: isMobile ? "12px" : "13px"
|
|
2063
2083
|
}
|
|
2064
|
-
},
|
|
2084
|
+
}, selectedAppointment?.doctor || selectedAppointment?.doctorName || "N/A"))), /*#__PURE__*/_react.default.createElement("div", {
|
|
2065
2085
|
style: {
|
|
2066
2086
|
display: "flex",
|
|
2067
2087
|
justifyContent: "space-between"
|
|
@@ -2081,7 +2101,7 @@ const AppointmentPage = _ref7 => {
|
|
|
2081
2101
|
fontWeight: "700",
|
|
2082
2102
|
fontSize: isMobile ? "12px" : "13px"
|
|
2083
2103
|
}
|
|
2084
|
-
},
|
|
2104
|
+
}, selectedAppointment?.hospital || selectedAppointment?.hospitalName || "N/A"))), /*#__PURE__*/_react.default.createElement("div", {
|
|
2085
2105
|
style: {
|
|
2086
2106
|
display: "flex",
|
|
2087
2107
|
justifyContent: "space-between"
|
|
@@ -2102,7 +2122,7 @@ const AppointmentPage = _ref7 => {
|
|
|
2102
2122
|
fontSize: isMobile ? "11px" : "12px",
|
|
2103
2123
|
lineHeight: "1.4"
|
|
2104
2124
|
}
|
|
2105
|
-
},
|
|
2125
|
+
}, selectedAppointment?.reason || selectedAppointment?.reasonForAppointment || "No reason provided"))), (activeTab === "upcoming" || activeTab === "completed" && isWithinJoinWindow(selectedAppointment)) && /*#__PURE__*/_react.default.createElement("div", {
|
|
2106
2126
|
style: {
|
|
2107
2127
|
display: "flex",
|
|
2108
2128
|
flexDirection: isMobile ? "column" : "row",
|
|
@@ -2155,12 +2175,12 @@ const AppointmentPage = _ref7 => {
|
|
|
2155
2175
|
}, "Select an appointment to view details"))))))), showPipVideo && /*#__PURE__*/_react.default.createElement("div", {
|
|
2156
2176
|
style: {
|
|
2157
2177
|
position: "fixed",
|
|
2158
|
-
left: isPipFullscreen ? "0" : isMobile ? "10px" :
|
|
2159
|
-
top: isPipFullscreen ? "0" : isMobile ? "70px" :
|
|
2178
|
+
left: isPipFullscreen ? "0" : isMobile ? "10px" : `${pipPosition.x}px`,
|
|
2179
|
+
top: isPipFullscreen ? "0" : isMobile ? "70px" : `${pipPosition.y}px`,
|
|
2160
2180
|
right: isPipFullscreen ? "0" : isMobile ? "10px" : "auto",
|
|
2161
2181
|
bottom: isPipFullscreen ? "0" : "auto",
|
|
2162
|
-
width: isPipFullscreen ? "100vw" : isMobile ? "calc(100vw - 20px)" : isPipMinimized ? "350px" :
|
|
2163
|
-
height: isPipFullscreen ? "100vh" : isPipMinimized ? "auto" : isMobile ? "300px" :
|
|
2182
|
+
width: isPipFullscreen ? "100vw" : isMobile ? "calc(100vw - 20px)" : isPipMinimized ? "350px" : `${pipSize.width}px`,
|
|
2183
|
+
height: isPipFullscreen ? "100vh" : isPipMinimized ? "auto" : isMobile ? "300px" : `${pipSize.height}px`,
|
|
2164
2184
|
background: "#FFFFFF",
|
|
2165
2185
|
borderRadius: isPipFullscreen ? "0" : "8px",
|
|
2166
2186
|
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.3)",
|
|
@@ -2211,7 +2231,7 @@ const AppointmentPage = _ref7 => {
|
|
|
2211
2231
|
textOverflow: "ellipsis",
|
|
2212
2232
|
whiteSpace: "nowrap"
|
|
2213
2233
|
}
|
|
2214
|
-
}, "Video Call - ",
|
|
2234
|
+
}, "Video Call - ", selectedAppointment?.patientName || "Patient")), /*#__PURE__*/_react.default.createElement("div", {
|
|
2215
2235
|
style: {
|
|
2216
2236
|
display: "flex",
|
|
2217
2237
|
gap: isMobile ? "4px" : "6px",
|
|
@@ -2340,7 +2360,7 @@ const AppointmentPage = _ref7 => {
|
|
|
2340
2360
|
src: (() => {
|
|
2341
2361
|
if (!callToken) return "";
|
|
2342
2362
|
const base = String(joinCallUrlBase || "");
|
|
2343
|
-
return
|
|
2363
|
+
return `${base}${callToken}`;
|
|
2344
2364
|
})(),
|
|
2345
2365
|
style: {
|
|
2346
2366
|
width: "100%",
|
|
@@ -2411,7 +2431,12 @@ const AppointmentPage = _ref7 => {
|
|
|
2411
2431
|
background: "transparent",
|
|
2412
2432
|
zIndex: 10001
|
|
2413
2433
|
}
|
|
2414
|
-
})), /*#__PURE__*/_react.default.createElement("style", null,
|
|
2434
|
+
})), /*#__PURE__*/_react.default.createElement("style", null, `
|
|
2435
|
+
@keyframes pulse {
|
|
2436
|
+
0%, 100% { opacity: 1; }
|
|
2437
|
+
50% { opacity: 0.5; }
|
|
2438
|
+
}
|
|
2439
|
+
`))), showAuthError && /*#__PURE__*/_react.default.createElement("div", {
|
|
2415
2440
|
style: {
|
|
2416
2441
|
position: "fixed",
|
|
2417
2442
|
inset: 0,
|