From 8958db65c95b5052e86a1dff49b186b81992d5da Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Fri, 16 Jan 2026 08:42:06 +0530 Subject: [PATCH 01/14] fix: amm-1931 session expiry error handling mechanism --- .../core/services/confirmation.service.ts | 2 +- .../core/services/http-interceptor.service.ts | 407 ++++++++++++------ ...urse-mmu-tm-referred-worklist.component.ts | 4 + 3 files changed, 291 insertions(+), 122 deletions(-) diff --git a/src/app/app-modules/core/services/confirmation.service.ts b/src/app/app-modules/core/services/confirmation.service.ts index 761dc63..456246f 100644 --- a/src/app/app-modules/core/services/confirmation.service.ts +++ b/src/app/app-modules/core/services/confirmation.service.ts @@ -11,7 +11,7 @@ import { CommonDialogComponent } from '../components/common-dialog/common-dialog @Injectable() export class ConfirmationService { constructor( - private dialog: MatDialog, + public dialog: MatDialog, @Inject(DOCUMENT) doc: any, ) {} diff --git a/src/app/app-modules/core/services/http-interceptor.service.ts b/src/app/app-modules/core/services/http-interceptor.service.ts index 60b391e..97cfbb6 100644 --- a/src/app/app-modules/core/services/http-interceptor.service.ts +++ b/src/app/app-modules/core/services/http-interceptor.service.ts @@ -9,170 +9,335 @@ import { HttpErrorResponse, HttpHeaders, } from '@angular/common/http'; -import { catchError, tap } from 'rxjs/operators'; +import { catchError, tap, finalize } from 'rxjs/operators'; import { Observable, of } from 'rxjs'; import { Router } from '@angular/router'; import { throwError } from 'rxjs/internal/observable/throwError'; +import { MatDialog } from '@angular/material/dialog'; import { SpinnerService } from './spinner.service'; import { ConfirmationService } from './confirmation.service'; import { environment } from 'src/environments/environment'; import { SessionStorageService } from 'Common-UI/src/registrar/services/session-storage.service'; +/** + * HTTP Interceptor Service + * Handles: + * - Request authorization headers + * - Response success/error handling + * - Session management and timeout + * - Loading spinner management + * - User feedback on errors + */ @Injectable({ providedIn: 'root', }) export class HttpInterceptorService implements HttpInterceptor { - timerRef: any; - currentLanguageSet: any; - donotShowSpinnerUrl = [ + private sessionTimeoutRef: any; + private currentLanguageSet: any; + private readonly SESSION_TIMEOUT_DURATION = 27 * 60 * 1000; // 27 minutes + private readonly SESSION_WARNING_TIME = 1.5 * 60 * 1000; // 1.5 minutes before timeout + private readonly EXCLUDED_SPINNER_URLS = [ environment.syncDownloadProgressUrl, environment.ioturl, ]; + constructor( private spinnerService: SpinnerService, private router: Router, private confirmationService: ConfirmationService, private http: HttpClient, - readonly sessionstorage: SessionStorageService, + readonly sessionStorage: SessionStorageService, + private matDialog: MatDialog, ) {} intercept( req: HttpRequest, next: HttpHandler, ): Observable> { - const key: any = sessionStorage.getItem('key'); - let modifiedReq = req; - const isPlatformFeedback = - req.url && req.url.toLowerCase().includes('/platform-feedback'); + // Add authorization header + const modifiedReq = this.addAuthorizationHeader(req); - if (isPlatformFeedback) { - // For platform-feedback: remove Authorization and force JSON content-type - const headers = req.headers - .delete('Authorization') - .set('Content-Type', 'application/json'); - modifiedReq = req.clone({ headers }); - } else { - if (req.body instanceof FormData) { - modifiedReq = req.clone({ - headers: req.headers.set('Authorization', key || ''), - }); - } else { - if (key !== undefined && key !== null) { - modifiedReq = req.clone({ - headers: req.headers - .set('Authorization', key) - .set('Content-Type', 'application/json'), - }); - } else { - modifiedReq = req.clone({ - headers: req.headers.set('Authorization', ''), - }); - } - } + // Show spinner unless URL is in exclusion list + const shouldShowSpinner = !this.isExcludedUrl(req.url); + if (shouldShowSpinner) { + this.spinnerService.setLoading(true); } + return next.handle(modifiedReq).pipe( - tap((event: HttpEvent) => { - if (req.url !== undefined && !req.url.includes('cti/getAgentState')) - this.spinnerService.setLoading(true); - if (event instanceof HttpResponse) { - console.log(event.body); - this.onSuccess(req.url, event.body); + tap((event: HttpEvent) => this.handleResponse(event)), + catchError((error: HttpErrorResponse) => + this.handleError(error, req.url), + ), + finalize(() => { + if (shouldShowSpinner) { this.spinnerService.setLoading(false); - return event.body; - } - }), - catchError((error: HttpErrorResponse) => { - console.error(error); - this.spinnerService.setLoading(false); - - if(error.status === 401){ - this.sessionstorage.clear(); - this.confirmationService.alert(this.currentLanguageSet.sessionExpiredPleaseLogin, 'error'); - setTimeout(() => this.router.navigate(['/login']), 0); - } else if (error.status === 403) { - this.confirmationService.alert( - this.currentLanguageSet.accessDenied, - 'error', - ); - } else if (error.status === 500) { - this.confirmationService.alert( - this.currentLanguageSet.internaleServerError, - 'error', - ); - } else { - this.confirmationService.alert( - error.message || this.currentLanguageSet.somethingWentWrong, - 'error', - ); } - return throwError(error.error); }), ); } + /** + * Adds authorization header to request + * Special handling for platform-feedback endpoint (no auth required) + */ + private addAuthorizationHeader(req: HttpRequest): HttpRequest { + const authToken = sessionStorage.getItem('key'); + const isPlatformFeedback = req.url + ?.toLowerCase() + .includes('/platform-feedback'); - private onSuccess(url: string, response: any): void { - if (this.timerRef) clearTimeout(this.timerRef); + if (isPlatformFeedback) { + // Remove Authorization for feedback endpoint + return req.clone({ + headers: req.headers + .delete('Authorization') + .set('Content-Type', 'application/json'), + }); + } - if ( - response.statusCode === 5002 && - url.indexOf('user/userAuthenticate') < 0 - ) { - sessionStorage.clear(); - this.sessionstorage.clear(); - setTimeout(() => this.router.navigate(['/login']), 0); - this.confirmationService.alert(response.errorMessage, 'error'); + let headers = req.headers; + + if (req.body instanceof FormData) { + headers = headers.set('Authorization', authToken || ''); } else { - this.startTimer(); + headers = headers + .set('Authorization', authToken || '') + .set('Content-Type', 'application/json'); + } + + return req.clone({ headers }); + } + + /** + * Check if URL is excluded from spinner display + */ + private isExcludedUrl(url: string): boolean { + return ( + url === undefined || + url.includes('cti/getAgentState') || + this.EXCLUDED_SPINNER_URLS.some((excludedUrl) => url.includes(excludedUrl)) + ); + } + + /** + * Handle successful HTTP responses + * Checks for application-level errors in response body + */ + private handleResponse(event: HttpEvent): void { + if (event instanceof HttpResponse) { + const response = event.body; + + // Check for application-level errors (statusCode in body) + if ( + response && + typeof response === 'object' && + response.statusCode === 5002 && + !event.url?.includes('user/userAuthenticate') + ) { + this.handleSessionExpired(); + return; + } + + // Reset session timeout on successful response + this.resetSessionTimeout(); + } + } + + /** + * Handle HTTP errors with appropriate user-facing messages + */ + private handleError( + error: HttpErrorResponse, + url: string, + ): Observable { + const errorMessage = this.getErrorMessage(error); + + // Close all open dialogs before showing error + this.matDialog.closeAll(); + + // Show error based on status code + switch (error.status) { + case 401: + this.handleUnauthorized(); + break; + case 403: + this.handleForbidden(); + break; + case 404: + this.handleNotFound(errorMessage); + break; + case 500: + case 502: + case 503: + case 504: + this.handleServerError(error.status, errorMessage); + break; + default: + this.handleGenericError(errorMessage); + } + + return throwError(() => error); + } + + /** + * Extract error message from HTTP response + * Prioritizes server-provided messages over generic ones + */ + private getErrorMessage(error: HttpErrorResponse): string { + // Try to get message from error response body + if (error.error) { + if (typeof error.error === 'string') { + return error.error; + } + if ( + error.error.message && + typeof error.error.message === 'string' + ) { + return error.error.message; + } + if (error.error.error && typeof error.error.error === 'string') { + return error.error.error; + } } + + // Fallback to HTTP status text + return error.statusText || 'Unknown error occurred'; + } + + /** + * Handle 401 Unauthorized - Session expired + */ + private handleUnauthorized(): void { + this.confirmationService.alert( + this.currentLanguageSet?.sessionExpiredPleaseLogin || + 'Session has expired, please login again.', + 'error', + ); + this.handleSessionExpired(); + } + + /** + * Handle 403 Forbidden - Access denied + */ + private handleForbidden(): void { + this.confirmationService.alert( + this.currentLanguageSet?.accessDenied || + 'Access Denied. You do not have permission to access this resource.', + 'error', + ); + this.handleSessionExpired(); } - startTimer() { - this.timerRef = setTimeout( - () => { - console.log('there', Date()); - - if ( - sessionStorage.getItem('authenticationToken') && - sessionStorage.getItem('isAuthenticated') - ) { - this.confirmationService - .alert( - 'Your session is about to Expire. Do you need more time ? ', - 'sessionTimeOut', - ) - .afterClosed() - .subscribe((result: any) => { - if (result.action === 'continue') { - this.http.post(environment.extendSessionUrl, {}).subscribe( - (res: any) => {}, - (err: any) => {}, - ); - } else if (result.action === 'timeout') { - clearTimeout(this.timerRef); - sessionStorage.clear(); - this.sessionstorage.clear(); - this.confirmationService.alert( - this.currentLanguageSet.sessionExpired, - 'error', - ); - this.router.navigate(['/login']); - } else if (result.action === 'cancel') { - setTimeout(() => { - clearTimeout(this.timerRef); - sessionStorage.clear(); - this.sessionstorage.clear(); - this.confirmationService.alert( - this.currentLanguageSet.sessionExpired, - 'error', - ); - this.router.navigate(['/login']); - }, result.remainingTime * 1000); - } - }); + /** + * Handle 404 Not Found + */ + private handleNotFound(errorMessage: string): void { + this.confirmationService.alert( + this.currentLanguageSet?.notFound || `Resource not found: ${errorMessage}`, + 'error', + ); + } + + /** + * Handle 5xx Server Errors + */ + private handleServerError(status: number, errorMessage: string): void { + const message = + this.currentLanguageSet?.internaleServerError || + `Server error (${status}): ${errorMessage}`; + + this.confirmationService.alert(message, 'error'); + } + + /** + * Handle generic/unknown errors + */ + private handleGenericError(errorMessage: string): void { + const message = + this.currentLanguageSet?.somethingWentWrong || + `Something went wrong: ${errorMessage}`; + + this.confirmationService.alert(message, 'error'); + } + + /** + * Handle session expiration - clear storage and redirect + */ + private handleSessionExpired(): void { + this.clearSessionTimeout(); + sessionStorage.clear(); + this.sessionStorage.clear(); + this.router.navigate(['/login']); + } + + /** + * Reset session timeout on user activity + */ + private resetSessionTimeout(): void { + this.clearSessionTimeout(); + this.startSessionTimeout(); + } + + /** + * Start session timeout timer + * Warns user 1.5 minutes before actual timeout (27 minutes total) + */ + private startSessionTimeout(): void { + this.sessionTimeoutRef = setTimeout(() => { + const isAuthenticated = + sessionStorage.getItem('authenticationToken') && + sessionStorage.getItem('isAuthenticated'); + + if (isAuthenticated) { + this.showSessionTimeoutWarning(); + } + }, this.SESSION_TIMEOUT_DURATION); + } + + /** + * Show session timeout warning dialog + */ + private showSessionTimeoutWarning(): void { + this.confirmationService + .alert( + 'Your session is about to expire. Do you need more time?', + 'sessionTimeOut', + ) + .afterClosed() + .subscribe((result: any) => { + if (!result) return; + + if (result.action === 'continue') { + this.extendSession(); + } else if (result.action === 'timeout' || result.action === 'cancel') { + this.handleSessionExpired(); } + }); + } + + /** + * Extend user session by calling backend endpoint + */ + private extendSession(): void { + this.http.post(environment.extendSessionUrl, {}).subscribe( + (res: any) => { + // Session extended successfully, restart timeout + this.resetSessionTimeout(); + }, + (err: any) => { + // Silently fail - let normal error handling take over + console.warn('Failed to extend session', err); }, - 27 * 60 * 1000, ); } + + /** + * Clear session timeout + */ + private clearSessionTimeout(): void { + if (this.sessionTimeoutRef) { + clearTimeout(this.sessionTimeoutRef); + this.sessionTimeoutRef = null; + } + } } diff --git a/src/app/app-modules/nurse-doctor/nurse-worklist-wrapper/nurse-mmu-tm-referred-worklist/nurse-mmu-tm-referred-worklist.component.ts b/src/app/app-modules/nurse-doctor/nurse-worklist-wrapper/nurse-mmu-tm-referred-worklist/nurse-mmu-tm-referred-worklist.component.ts index e1e07a6..8346b6f 100644 --- a/src/app/app-modules/nurse-doctor/nurse-worklist-wrapper/nurse-mmu-tm-referred-worklist/nurse-mmu-tm-referred-worklist.component.ts +++ b/src/app/app-modules/nurse-doctor/nurse-worklist-wrapper/nurse-mmu-tm-referred-worklist/nurse-mmu-tm-referred-worklist.component.ts @@ -136,6 +136,10 @@ export class NurseMmuTmReferredWorklistComponent this.dataSource.paginator = this.paginator; }, (err: any) => { + if (err?.status == 401) { + // do nothing as http-interceptor will handle + return; + } this.confirmationService.alert(err, 'error'); }, ); From 5a152bce761acc2df336adaa3c978bea7f752e59 Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Tue, 20 Jan 2026 16:06:22 +0530 Subject: [PATCH 02/14] fix: enabling logs for debugging --- src/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.html b/src/index.html index 10bb11e..952efc0 100644 --- a/src/index.html +++ b/src/index.html @@ -4,7 +4,7 @@ Tele-Medicine From 7dcf6141986e603da59a2c20df48222e456f9cab Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Wed, 21 Jan 2026 13:13:25 +0530 Subject: [PATCH 03/14] fix: updating common-ui --- Common-UI | 2 +- src/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Common-UI b/Common-UI index 93c9157..e1a2a37 160000 --- a/Common-UI +++ b/Common-UI @@ -1 +1 @@ -Subproject commit 93c915707165d8ebeefa5c62cc087b08184b261a +Subproject commit e1a2a3734bd86974b3ef687ffac73f80d9ffc5df diff --git a/src/index.html b/src/index.html index 10bb11e..952efc0 100644 --- a/src/index.html +++ b/src/index.html @@ -4,7 +4,7 @@ Tele-Medicine From e49eb19d086a3e9d70567aac0af1e19326968d39 Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Wed, 21 Jan 2026 13:23:31 +0530 Subject: [PATCH 04/14] fix: disabling ES for TM UI --- src/environments/environment.ci.ts.template | 2 +- src/environments/environment.dev.ts | 2 +- src/environments/environment.development.ts | 2 +- src/environments/environment.local.ts | 2 +- src/environments/environment.prod.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/environments/environment.ci.ts.template b/src/environments/environment.ci.ts.template index b390b53..d6321b9 100644 --- a/src/environments/environment.ci.ts.template +++ b/src/environments/environment.ci.ts.template @@ -64,7 +64,7 @@ export const environment = { haemoglobinTest: `Hemoglobin Test`, abhaExtension: `@abdm`, parentAPI: `${TM_API}`, - + isEnableES: false, INVENTORY_URL: `${INVENTORY_UI}/#/redirin?`, fallbackUrl: `/pharmacist/redirfallback`, redirInUrl: `/pharmacist/redirin`, diff --git a/src/environments/environment.dev.ts b/src/environments/environment.dev.ts index 0f6d49e..10b930f 100644 --- a/src/environments/environment.dev.ts +++ b/src/environments/environment.dev.ts @@ -79,7 +79,7 @@ export const environment = { haemoglobinTest: `Haemoglobin Test`, abhaExtension: `@sbx`, parentAPI: `${TM_API}`, - + isEnableES: false, INVENTORY_URL: `${inventoryUI_IP}/inventory/#/redirin?`, fallbackUrl: `/pharmacist/redirfallback`, redirInUrl: `/pharmacist/redirin`, diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts index 80b2f00..6adb03d 100644 --- a/src/environments/environment.development.ts +++ b/src/environments/environment.development.ts @@ -79,7 +79,7 @@ export const environment = { haemoglobinTest: `Haemoglobin Test`, abhaExtension: `@sbx`, parentAPI: `${TM_API}`, - + isEnableES: false, INVENTORY_URL: `${inventoryUI_IP}/inventory/#/redirin?`, fallbackUrl: `/pharmacist/redirfallback`, redirInUrl: `/pharmacist/redirin`, diff --git a/src/environments/environment.local.ts b/src/environments/environment.local.ts index 9cd89e3..fb11270 100644 --- a/src/environments/environment.local.ts +++ b/src/environments/environment.local.ts @@ -85,7 +85,7 @@ export const environment = { encKey: sessionStorageEncKey, parentAPI: `${TM_API}`, - + isEnableES: false, INVENTORY_URL: inventoryUI_IP + ':4201/#/redirin?', fallbackUrl: '/pharmacist/redirfallback', redirInUrl: '/pharmacist/redirin', diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 85a7fcc..80dd9d5 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -80,7 +80,7 @@ export const environment = { haemoglobinTest: `Haemoglobin Test`, abhaExtension: `@abdm`, parentAPI: `${TM_API}`, - + isEnableES: false, INVENTORY_URL: `${inventoryUI_IP}/inventory/#/redirin?`, fallbackUrl: `/pharmacist/redirfallback`, redirInUrl: `/pharmacist/redirin`, From 7004db3cd98fd7b7f92248f3b28a38b063e6a977 Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Wed, 21 Jan 2026 13:36:02 +0530 Subject: [PATCH 05/14] fix: disabling ES for TM UI --- src/environments/environment.ci.ts.template | 2 ++ src/environments/environment.dev.ts | 2 ++ src/environments/environment.development.ts | 2 ++ src/environments/environment.local.ts | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/environments/environment.ci.ts.template b/src/environments/environment.ci.ts.template index d6321b9..48c7f0a 100644 --- a/src/environments/environment.ci.ts.template +++ b/src/environments/environment.ci.ts.template @@ -65,6 +65,8 @@ export const environment = { abhaExtension: `@abdm`, parentAPI: `${TM_API}`, isEnableES: false, + elasticSearchUrl: '', + advanceElasticSearchUrl: '', INVENTORY_URL: `${INVENTORY_UI}/#/redirin?`, fallbackUrl: `/pharmacist/redirfallback`, redirInUrl: `/pharmacist/redirin`, diff --git a/src/environments/environment.dev.ts b/src/environments/environment.dev.ts index 10b930f..a8b786b 100644 --- a/src/environments/environment.dev.ts +++ b/src/environments/environment.dev.ts @@ -80,6 +80,8 @@ export const environment = { abhaExtension: `@sbx`, parentAPI: `${TM_API}`, isEnableES: false, + elasticSearchUrl: '', + advanceElasticSearchUrl: '', INVENTORY_URL: `${inventoryUI_IP}/inventory/#/redirin?`, fallbackUrl: `/pharmacist/redirfallback`, redirInUrl: `/pharmacist/redirin`, diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts index 6adb03d..2233d1e 100644 --- a/src/environments/environment.development.ts +++ b/src/environments/environment.development.ts @@ -80,6 +80,8 @@ export const environment = { abhaExtension: `@sbx`, parentAPI: `${TM_API}`, isEnableES: false, + elasticSearchUrl: '', + advanceElasticSearchUrl: '', INVENTORY_URL: `${inventoryUI_IP}/inventory/#/redirin?`, fallbackUrl: `/pharmacist/redirfallback`, redirInUrl: `/pharmacist/redirin`, diff --git a/src/environments/environment.local.ts b/src/environments/environment.local.ts index fb11270..e46adb2 100644 --- a/src/environments/environment.local.ts +++ b/src/environments/environment.local.ts @@ -86,6 +86,8 @@ export const environment = { parentAPI: `${TM_API}`, isEnableES: false, + elasticSearchUrl: '', + advanceElasticSearchUrl: '', INVENTORY_URL: inventoryUI_IP + ':4201/#/redirin?', fallbackUrl: '/pharmacist/redirfallback', redirInUrl: '/pharmacist/redirin', From 7691510b42952cc0a47577dc8da9b587062f7099 Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Wed, 21 Jan 2026 13:46:09 +0530 Subject: [PATCH 06/14] fix: updating common-ui reference --- Common-UI | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common-UI b/Common-UI index e1a2a37..e9304ae 160000 --- a/Common-UI +++ b/Common-UI @@ -1 +1 @@ -Subproject commit e1a2a3734bd86974b3ef687ffac73f80d9ffc5df +Subproject commit e9304ae4c85709a82608e6f900429a857fc703ba From 77f635a9e96e5bd1a5f579c4dd21f53d7fb62ce3 Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Wed, 21 Jan 2026 17:38:45 +0530 Subject: [PATCH 07/14] fix: disabling console log --- src/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.html b/src/index.html index 952efc0..10bb11e 100644 --- a/src/index.html +++ b/src/index.html @@ -4,7 +4,7 @@ Tele-Medicine From 6feb04f2ef4242555c0bcc23e306a3ec952a44da Mon Sep 17 00:00:00 2001 From: Amoghavarsh <93114621+5Amogh@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:26:03 +0530 Subject: [PATCH 08/14] Bump version from 3.6.0 to 3.6.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6fc0be4..042ca81 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.iemr.tm-ui tm-ui - 3.6.0 + 3.6.1 TM-UI Piramal - tm: Module ui war From 1c59c96e786bb01a1fc56aff6f4a84feaa66a3b5 Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Thu, 22 Jan 2026 16:13:00 +0530 Subject: [PATCH 09/14] fix: amm-2074 issue fix --- src/assets/Assamese.json | 4 +++- src/assets/English.json | 3 ++- src/assets/Hindi.json | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/assets/Assamese.json b/src/assets/Assamese.json index 7fbbd55..f6af624 100644 --- a/src/assets/Assamese.json +++ b/src/assets/Assamese.json @@ -1335,7 +1335,9 @@ "image": "দর্শন চিত্ৰ", "reschedule": "টি.টিৰ বাবে সময়সূচী পুনৰ নির্ধাৰণ কৰক", "cancel": "বাতিল কৰা", - "schedule": "সূচী (চিডিউল)" + "schedule": "সূচী (চিডিউল)", + "accept": "গ্ৰহণ কৰক" + }, "casesheet": { "caseComponent": "প্ৰিন্ট কৰিবলৈ কেচ্‌চিট কম্পোনেন্ট নির্বাচন কৰক", diff --git a/src/assets/English.json b/src/assets/English.json index 6ec9071..0673d7a 100644 --- a/src/assets/English.json +++ b/src/assets/English.json @@ -1345,7 +1345,8 @@ "image": "View Image", "reschedule": "Reschedule TC", "cancel": "Cancel", - "schedule": "Schedule" + "schedule": "Schedule", + "accept": "Accept" }, "casesheet": { "caseComponent": "Select casesheet component for print", diff --git a/src/assets/Hindi.json b/src/assets/Hindi.json index bd4233f..701b20e 100644 --- a/src/assets/Hindi.json +++ b/src/assets/Hindi.json @@ -1337,7 +1337,8 @@ "image": "छवि देखें", "reschedule": "पुनर्निर्धारित टीसी", "cancel": "रद्द करना", - "schedule": "अनुसूची" + "schedule": "अनुसूची", + "accept": "सस्वीकार करें" }, "casesheet": { "caseComponent": "प्रिंट के लिए केसशीट घटक का चयन करें", From c6757355627f1fbf7143996ecd66aacf1d0cff42 Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Thu, 12 Feb 2026 15:43:56 +0530 Subject: [PATCH 10/14] fix: amm-2174 n-1 date issues and habit field refelction issues fixed --- .../nurse-doctor/anc/anc.component.ts | 50 ++++++++++++++ .../personal-history.component.ts | 68 +++++++++++++------ .../nurse-doctor/pnc/pnc.component.ts | 22 +++++- .../shared/services/nurse.service.ts | 58 +++++++++++++--- 4 files changed, 167 insertions(+), 31 deletions(-) diff --git a/src/app/app-modules/nurse-doctor/anc/anc.component.ts b/src/app/app-modules/nurse-doctor/anc/anc.component.ts index 17d68ec..82e3ed3 100644 --- a/src/app/app-modules/nurse-doctor/anc/anc.component.ts +++ b/src/app/app-modules/nurse-doctor/anc/anc.component.ts @@ -134,6 +134,46 @@ export class AncComponent implements OnInit, OnChanges, OnDestroy, DoCheck { visitCode: this.sessionstorage.getItem('visitCode'), }; + const immunizationForm = patientANCForm.get('patientANCImmunizationForm'); + + if (immunizationForm) { + const ttDateFields = [ + 'dateReceivedForTT_1', + 'dateReceivedForTT_2', + 'dateReceivedForTT_3', + ]; + + ttDateFields.forEach((field) => { + const value = immunizationForm.get(field)?.value; + + if (value) { + immunizationForm.patchValue({ + [field]: this.normalizeToUTCMidnight(new Date(value)), + }); + } + }); + } + + const ancDetailsForm = patientANCForm.get('patientANCDetailsForm'); + + if (ancDetailsForm) { + const lmpDateValue = ancDetailsForm.get('lmpDate')?.value; + + if (lmpDateValue) { + ancDetailsForm.patchValue({ + lmpDate: this.normalizeToUTCMidnight(new Date(lmpDateValue)), + }); + } + + const expDelDtValue = ancDetailsForm.get('expDelDt')?.value; + + if (expDelDtValue) { + ancDetailsForm.patchValue({ + expDelDt: this.normalizeToUTCMidnight(new Date(expDelDtValue)), + }); + } + } + this.updateANCDetailsSubs = this.doctorService .updateANCDetails(patientANCForm, temp) .subscribe( @@ -152,6 +192,16 @@ export class AncComponent implements OnInit, OnChanges, OnDestroy, DoCheck { ); } + private normalizeToUTCMidnight(date: Date | null | undefined): string | null { + if (!date) return null; + + const d = new Date(date); + const utcDate = new Date( + Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0) + ); + return utcDate.toISOString(); + } + getHRPDetails() { const beneficiaryRegID = this.sessionstorage.getItem('beneficiaryRegID'); const visitCode = this.sessionstorage.getItem('visitCode'); diff --git a/src/app/app-modules/nurse-doctor/history/general-opd-history/personal-history/personal-history.component.ts b/src/app/app-modules/nurse-doctor/history/general-opd-history/personal-history/personal-history.component.ts index 1817385..89290fb 100644 --- a/src/app/app-modules/nurse-doctor/history/general-opd-history/personal-history/personal-history.component.ts +++ b/src/app/app-modules/nurse-doctor/history/general-opd-history/personal-history/personal-history.component.ts @@ -213,6 +213,15 @@ export class GeneralPersonalHistoryComponent history.data.PersonalHistory ) { this.personalHistoryData = history.data.PersonalHistory; + if ( + this.personalHistoryData && + this.personalHistoryData.riskySexualPracticesStatus !== null + ) { + this.personalHistoryData.riskySexualPracticesStatus = + this.personalHistoryData.riskySexualPracticesStatus == '1' + ? true + : false; + } this.generalPersonalHistoryForm.patchValue(this.personalHistoryData); this.handlePersonalTobaccoHistoryData(); this.handlePersonalAlcoholHistoryData(); @@ -420,44 +429,63 @@ export class GeneralPersonalHistoryComponent const formArray = this.generalPersonalHistoryForm.controls[ 'allergicList' ] as FormArray; + if (this.personalHistoryData && this.personalHistoryData.allergicList) { const temp = this.personalHistoryData.allergicList.slice(); + while (formArray.length > 0) { + formArray.removeAt(0); + } + + for (let i = 0; i < temp.length; i++) { + formArray.push(this.initAllergyList()); + } + + this.allerySelectList = []; + this.previousSelectedAlleryList = []; + for (let i = 0; i < temp.length; i++) { const allergyType = this.allergyMasterData.filter((item) => { return item.allergyType === temp[i].allergyType; }); + if (allergyType.length > 0) temp[i].allergyType = allergyType[0]; - if (this.masterData.AllergicReactionTypes !== undefined) { - temp[i].typeOfAllergicReactions = - this.masterData.AllergicReactionTypes.filter((item: any) => { - let flag = false; - temp[i].typeOfAllergicReactions.forEach((element: any) => { - if (element.name === item.name) flag = true; - }); - return flag; + temp[i].typeOfAllergicReactions = + this.masterData.AllergicReactionTypes.filter((item: any) => { + let flag = false; + temp[i].typeOfAllergicReactions.forEach((element: any) => { + if (element.name === item.name) flag = true; }); - } + return flag; + }); if (temp[i].otherAllergicReaction) temp[i].enableOtherAllergy = true; + const selectedAllergies = temp + .filter((t: any, idx: any) => idx !== i && t.allergyType) + .map((t: any) => t.allergyType.allergyType); + + const availableAllergies = this.allergyMasterData.filter( + (item) => !selectedAllergies.includes(item.allergyType), + ); + + this.allerySelectList.push(availableAllergies.slice()); + if (temp[i].allergyType) { - const k: any = formArray.get('' + i); + this.previousSelectedAlleryList[i] = temp[i].allergyType; + } + + const k: any = formArray.get('' + i); + if (k) { k.patchValue(temp[i]); k.markAsTouched(); - k.markAsDirty(); - this.filterAlleryList(temp[i].allergyType, i); - if ( - k?.get('snomedTerm')?.value !== null && - k?.get('typeOfAllergicReactions')?.value !== null - ) { - k?.get('snomedTerm')?.enable(); - k?.get('typeOfAllergicReactions')?.enable(); + + if (temp[i].allergyType) { + k.get('snomedTerm')?.enable(); + k.get('typeOfAllergicReactions')?.enable(); } } - - if (i + 1 < temp.length) this.addAllergy(); } } } diff --git a/src/app/app-modules/nurse-doctor/pnc/pnc.component.ts b/src/app/app-modules/nurse-doctor/pnc/pnc.component.ts index 13e14f6..49bed08 100644 --- a/src/app/app-modules/nurse-doctor/pnc/pnc.component.ts +++ b/src/app/app-modules/nurse-doctor/pnc/pnc.component.ts @@ -158,13 +158,25 @@ export class PncComponent implements OnInit, DoCheck, OnChanges, OnDestroy { })[0]; } - tempPNCData.dDate = new Date(tempPNCData.dateOfDelivery); + tempPNCData.dDate = this.normalizeToUTCMidnight( + new Date(tempPNCData.dateOfDelivery), + ); const patchPNCdata = Object.assign({}, tempPNCData); this.patientPNCForm.patchValue(tempPNCData); }); } + private normalizeToUTCMidnight(date: Date | null | undefined): string | null { + if (!date) return null; + + const d = new Date(date); + const utcDate = new Date( + Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0) + ); + return utcDate.toISOString(); + } + updatePatientPNC(patientPNCForm: any) { const temp = { beneficiaryRegID: this.sessionstorage.getItem('beneficiaryRegID'), @@ -174,6 +186,14 @@ export class PncComponent implements OnInit, DoCheck, OnChanges, OnDestroy { visitCode: this.sessionstorage.getItem('visitCode'), }; + const dDate = patientPNCForm.get('dDate')?.value; + if (dDate) { + patientPNCForm.patchValue({ + dateOfDelivery: this.normalizeToUTCMidnight(new Date(dDate)), + dDate: this.normalizeToUTCMidnight(new Date(dDate)), + }); + } + this.doctorService.updatePNCDetails(patientPNCForm, temp).subscribe( (res: any) => { if (res.statusCode === 200 && res.data !== null) { diff --git a/src/app/app-modules/nurse-doctor/shared/services/nurse.service.ts b/src/app/app-modules/nurse-doctor/shared/services/nurse.service.ts index 9dcdb4a..b73a8d5 100644 --- a/src/app/app-modules/nurse-doctor/shared/services/nurse.service.ts +++ b/src/app/app-modules/nurse-doctor/shared/services/nurse.service.ts @@ -694,6 +694,17 @@ export class NurseService { }; } + private normalizeToUTCMidnight(date: Date | null | undefined): string | null { + if (!date) return null; + + const d = new Date(date); + + const utcDate = new Date( + Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0) + ); + return utcDate.toISOString(); + } + postANCDetailForm(patientANCForm: any, benVisitID: any) { const detailedANC = JSON.parse( JSON.stringify(patientANCForm.controls.patientANCDetailsForm.value), @@ -702,11 +713,15 @@ export class NurseService { JSON.stringify(patientANCForm.controls.obstetricFormulaForm.value), ); if (detailedANC.lmpDate) { - const lmpDate = new Date(detailedANC.lmpDate); - const adjustedDate = new Date( - lmpDate.getTime() - lmpDate.getTimezoneOffset() * 60000, + detailedANC.lmpDate = this.normalizeToUTCMidnight( + new Date(detailedANC.lmpDate) + ); + } + + if (detailedANC.expDelDt) { + detailedANC.expDelDt = this.normalizeToUTCMidnight( + new Date(detailedANC.expDelDt) ); - detailedANC.lmpDate = adjustedDate.toISOString(); } const combinedANCForm = Object.assign({}, detailedANC, { @@ -726,7 +741,29 @@ export class NurseService { } postANCImmunizationForm(patientANCImmunizationForm: any, benVisitID: any) { - const immunizationForm = Object.assign({}, patientANCImmunizationForm, { + const immunizationFormValue = JSON.parse( + JSON.stringify(patientANCImmunizationForm) + ); + + if (immunizationFormValue.dateReceivedForTT_1) { + immunizationFormValue.dateReceivedForTT_1 = this.normalizeToUTCMidnight( + new Date(immunizationFormValue.dateReceivedForTT_1) + ); + } + + if (immunizationFormValue.dateReceivedForTT_2) { + immunizationFormValue.dateReceivedForTT_2 = this.normalizeToUTCMidnight( + new Date(immunizationFormValue.dateReceivedForTT_2) + ); + } + + if (immunizationFormValue.dateReceivedForTT_3) { + immunizationFormValue.dateReceivedForTT_3 = this.normalizeToUTCMidnight( + new Date(immunizationFormValue.dateReceivedForTT_3) + ); + } + + const immunizationForm = Object.assign({}, immunizationFormValue, { beneficiaryRegID: this.sessionstorage.getItem('beneficiaryRegID'), benVisitID: benVisitID, providerServiceMapID: this.sessionstorage.getItem('providerServiceID'), @@ -1264,11 +1301,7 @@ export class NurseService { if (!temp.lMPDate) { temp.lMPDate = undefined; } else { - const lmpDate = new Date(temp.lMPDate); - const adjustedDate = new Date( - lmpDate.getTime() - lmpDate.getTimezoneOffset() * 60000, - ); - temp.lMPDate = adjustedDate.toISOString(); + temp.lMPDate = this.normalizeToUTCMidnight(new Date(temp.lMPDate)); } const menstrualHistoryData = Object.assign({}, temp, otherDetails); @@ -1894,6 +1927,11 @@ export class NurseService { temp.newBornHealthStatus = temp.newBornHealthStatus.newBornHealthStatus; } + if (temp.dDate) { + temp.dateOfDelivery = this.normalizeToUTCMidnight(new Date(temp.dDate)); + temp.dDate = temp.dateOfDelivery; + } + const patientPNCDetails = Object.assign({}, temp, { beneficiaryRegID: this.sessionstorage.getItem('beneficiaryRegID'), benVisitID: benVisitID, From 452cf789be56c79678317883027c395545d7a887 Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Wed, 18 Feb 2026 07:26:49 +0530 Subject: [PATCH 11/14] fix: amm-2175 prescription validation --- .../workarea/workarea.component.ts | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/app/app-modules/nurse-doctor/workarea/workarea.component.ts b/src/app/app-modules/nurse-doctor/workarea/workarea.component.ts index 27773c0..740fad9 100644 --- a/src/app/app-modules/nurse-doctor/workarea/workarea.component.ts +++ b/src/app/app-modules/nurse-doctor/workarea/workarea.component.ts @@ -2122,6 +2122,34 @@ export class WorkareaComponent } } } + + // Ensure doctor has added at least one prescription + if (this.attendant === 'doctor') { + try { + const drugPrescriptionForm = ( + (caseRecordForm && caseRecordForm.controls + ? caseRecordForm.controls['drugPrescriptionForm'] + : null) + ); + if (drugPrescriptionForm) { + let prescribedDrugs = + drugPrescriptionForm.value && + drugPrescriptionForm.value.prescribedDrugs + ? drugPrescriptionForm.value.prescribedDrugs + : []; + prescribedDrugs = prescribedDrugs.filter((d: any) => !!d.createdBy); + if (!prescribedDrugs || prescribedDrugs.length === 0) { + required.push( + this.current_language_set?.Prescription?.prescriptionRequired || + 'Please add at least one prescription', + ); + } + } + } catch (err) { + console.warn('Error validating prescription presence', err); + } + } + if (required.length) { this.confirmationService.notify( this.current_language_set.alerts.info.belowFields, @@ -2231,7 +2259,7 @@ export class WorkareaComponent !this.schedulerData ) required.push(this.current_language_set.nurseData.scheduleTM); - + if (required.length) { this.confirmationService.notify( this.current_language_set.alerts.info.belowFields, @@ -2680,6 +2708,37 @@ export class WorkareaComponent } } + // For quick consult doctor flow, ensure at least one prescription exists + if (this.attendant === 'doctor') { + try { + const caseRecordForm = ( + this.patientMedicalForm.controls['patientCaseRecordForm'] + ); + const prescription = + caseRecordForm && caseRecordForm.controls + ? caseRecordForm.controls['drugPrescriptionForm'] + : null; + if (prescription) { + let prescribedDrugs = + prescription.value && prescription.value.prescribedDrugs + ? prescription.value.prescribedDrugs + : []; + prescribedDrugs = prescribedDrugs.filter((d: any) => !!d.createdBy); + if (!prescribedDrugs || prescribedDrugs.length === 0) { + required.push( + this.current_language_set?.Prescription?.prescriptionRequired || + 'Please add at least one prescription', + ); + } + } + } catch (err) { + console.warn( + 'Error validating quick consult prescription presence', + err, + ); + } + } + if (required.length) { this.confirmationService.notify( this.current_language_set.alerts.info.belowFields, From b6ca9717e4a8a99a77bbb47f2505f537962730c6 Mon Sep 17 00:00:00 2001 From: SnehaRH Date: Wed, 18 Feb 2026 13:11:59 +0530 Subject: [PATCH 12/14] fix: 2159 changed text from Advance to Advance serarch --- src/assets/English.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assets/English.json b/src/assets/English.json index 0673d7a..b338253 100644 --- a/src/assets/English.json +++ b/src/assets/English.json @@ -105,7 +105,7 @@ "district": "District / Village", "registrationDate": "Registration Date", "image": "Image", - "advanceSearch": "Advance Search", + "advanceSearch": "Advanced Search", "externalSearch": "Higher Health Facility Search", "status": "Status", "visitCategory": "Visit Category / Visit No", @@ -298,7 +298,7 @@ "toTime": "To Time", "Kindlyuploadthefiles": "Kindly upload the files", "clearslots":"Clear Slot", - "advanceBeneficiarySearch": "Advance Beneficiary Search", + "advanceBeneficiarySearch": "Advanced Beneficiary Search", "externalBeneficiarySearch": "External Beneficiary Search", "firstNameisrequired": "First Name is required", "pleaseprovideatleast2character": "Please provide atleast 2 character", From c02d3deb49af9c7798f052443983c6b6d82cdd95 Mon Sep 17 00:00:00 2001 From: SnehaRH Date: Fri, 27 Feb 2026 16:19:01 +0530 Subject: [PATCH 13/14] fix: amm-2192 remove mandatory for prescription --- .../prescription/prescription.component.html | 7 ----- .../workarea/workarea.component.ts | 28 +------------------ 2 files changed, 1 insertion(+), 34 deletions(-) diff --git a/src/app/app-modules/nurse-doctor/case-record/general-case-record/prescription/prescription.component.html b/src/app/app-modules/nurse-doctor/case-record/general-case-record/prescription/prescription.component.html index a018ddb..ecd338d 100644 --- a/src/app/app-modules/nurse-doctor/case-record/general-case-record/prescription/prescription.component.html +++ b/src/app/app-modules/nurse-doctor/case-record/general-case-record/prescription/prescription.component.html @@ -45,7 +45,6 @@ name="form" [(ngModel)]="currentPrescription.formName" (selectionChange)="getFormValueChanged()" - required > {{ item }} @@ -157,7 +152,6 @@ name="unit" [(ngModel)]="currentPrescription.unit" [disabled]="!currentPrescription.drugID" - required > ( - (caseRecordForm && caseRecordForm.controls - ? caseRecordForm.controls['drugPrescriptionForm'] - : null) - ); - if (drugPrescriptionForm) { - let prescribedDrugs = - drugPrescriptionForm.value && - drugPrescriptionForm.value.prescribedDrugs - ? drugPrescriptionForm.value.prescribedDrugs - : []; - prescribedDrugs = prescribedDrugs.filter((d: any) => !!d.createdBy); - if (!prescribedDrugs || prescribedDrugs.length === 0) { - required.push( - this.current_language_set?.Prescription?.prescriptionRequired || - 'Please add at least one prescription', - ); - } - } - } catch (err) { - console.warn('Error validating prescription presence', err); - } - } - + if (required.length) { this.confirmationService.notify( this.current_language_set.alerts.info.belowFields, From d4106b935c29a7387ceba224f71ce23a41c00551 Mon Sep 17 00:00:00 2001 From: SnehaRH <77656297+snehar-nd@users.noreply.github.com> Date: Wed, 18 Mar 2026 20:36:13 +0530 Subject: [PATCH 14/14] fix: removed prescription data is required condition (#130) --- .../quick-consult.component.html | 8 +---- .../workarea/workarea.component.ts | 31 ------------------- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/src/app/app-modules/nurse-doctor/quick-consult/quick-consult.component.html b/src/app/app-modules/nurse-doctor/quick-consult/quick-consult.component.html index 81a8c18..03a6483 100644 --- a/src/app/app-modules/nurse-doctor/quick-consult/quick-consult.component.html +++ b/src/app/app-modules/nurse-doctor/quick-consult/quick-consult.component.html @@ -786,7 +786,7 @@

name="form" [(ngModel)]="tempform" (selectionChange)="getFormValueChanged()" - required + > [(ngModel)]="tempDrugName" (keyup)="filterMedicine(tempDrugName)" (blur)="reEnterMedicine()" - required [matAutocomplete]="autoGroup" /> name="dose" [(ngModel)]="currentPrescription.dose" [disabled]="!currentPrescription.drugID" - required > name="frequency" [(ngModel)]="currentPrescription.frequency" [disabled]="!currentPrescription.drugID" - required > name="duration" [(ngModel)]="currentPrescription.duration" [disabled]="!currentPrescription.drugID" - required > name="unit" [(ngModel)]="currentPrescription.unit" [disabled]="!currentPrescription.drugID" - required > name="quantity" [(ngModel)]="currentPrescription.qtyPrescribed" [disabled]="!currentPrescription.drugID" - required > ( - this.patientMedicalForm.controls['patientCaseRecordForm'] - ); - const prescription = - caseRecordForm && caseRecordForm.controls - ? caseRecordForm.controls['drugPrescriptionForm'] - : null; - if (prescription) { - let prescribedDrugs = - prescription.value && prescription.value.prescribedDrugs - ? prescription.value.prescribedDrugs - : []; - prescribedDrugs = prescribedDrugs.filter((d: any) => !!d.createdBy); - if (!prescribedDrugs || prescribedDrugs.length === 0) { - required.push( - this.current_language_set?.Prescription?.prescriptionRequired || - 'Please add at least one prescription', - ); - } - } - } catch (err) { - console.warn( - 'Error validating quick consult prescription presence', - err, - ); - } - } - if (required.length) { this.confirmationService.notify( this.current_language_set.alerts.info.belowFields,