Commit 6dad94e7 authored by Sandeep Sagar Panjala's avatar Sandeep Sagar Panjala

initial commit

parent b1304615
import { Component, OnInit } from "@angular/core";
import { Title } from "@angular/platform-browser";
import { NavigationEnd, NavigationStart, Router, Event as RouterEvent } from "@angular/router";
import { NgbDatepickerConfig, NgbTooltipConfig, } from "@ng-bootstrap/ng-bootstrap";
import { AppData, IdentityService } from "@shared/services";
@Component({
selector: "app-root",
template: `
<ngx-loading-bar [color]="'#3283f6'"></ngx-loading-bar>
<router-outlet></router-outlet>
`
})
export class AppComponent implements OnInit {
constructor(
datePickerConfig: NgbDatepickerConfig,
toolTipConfig: NgbTooltipConfig,
private readonly router: Router,
private readonly titleService: Title,
private readonly identityService: IdentityService,
private readonly appData: AppData,
) {
datePickerConfig.outsideDays = "collapsed";
datePickerConfig.navigation = "select";
toolTipConfig.container = "body";
}
ngOnInit() {
this.router.events.subscribe((event: RouterEvent) => {
if (event instanceof NavigationStart) {
$("body,html").animate({ scrollTop: 0 });
}
if (event instanceof NavigationEnd) {
let root = this.router.routerState.snapshot.root;
if (this.router.url.indexOf("/encounter/") > -1) {
$("#titleEncounter").show();
$("#titleInternalMedicine").hide();
$("#titlePatient").hide();
$("#navAppointments").addClass("selected");
$("#navPatients").removeClass("selected");
}
else if (this.router.url.indexOf("/internal-medicine/") > -1 || this.router.url.indexOf("/behavioral-health/") > -1) {
$("#titleEncounter").hide();
$("#titlePatient").hide();
$("#titleInternalMedicine").show();
$("#navAppointments").addClass("selected");
$("#navPatients").removeClass("selected");
}
else if (this.router.url.indexOf("/patient/") > -1) {
$("#titleEncounter").hide();
$("#titleInternalMedicine").hide();
$("#titlePatient").show();
$("#navAppointments").removeClass("selected");
$("#navPatients").addClass("selected");
} else {
$("#titleEncounter").hide();
$("#titleInternalMedicine").hide();
$("#titlePatient").hide();
$("#navAppointments").removeClass("selected");
$("#navPatients").removeClass("selected");
}
while (root) {
if (root.children && root.children.length) {
root = root.children[0];
} else if (root.data && root.data["title"]) {
let showSidebar: boolean;
try {
showSidebar = root.parent.data["showSidebar"] || this.router.url.indexOf("/internal-medicine/") > -1;
} catch (e) {
showSidebar = false;
}
$("body").attr("data-sidebar-size", (showSidebar || this.router.url.indexOf("app") === -1) ? "default" : "condensed");
this.titleService.setTitle(root.data["title"] + " | Careaxes");
return;
} else {
return;
}
}
}
});
this.identityService.get().subscribe(userAccount => {
if (userAccount) {
this.appData.setAccount(userAccount);
}
});
}
}
\ No newline at end of file
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
export interface IAppConfig {
maintenance: boolean;
baseURI: string;
baseWeb: string;
env: string;
envTitle: string;
version: string;
hostingUrl: string;
signalR: string;
}
@Injectable()
export class AppConfig {
static settings: IAppConfig;
constructor(private readonly http: HttpClient) {
}
load() {
const url = location.origin + location.pathname;
return this.http.get<IAppConfig>(url + "assets/settings.json", { headers: { ignoreLoadingBar: "", "Auth": "False" } })
.subscribe({
next: (response: IAppConfig) => {
localStorage.removeItem("settings");
AppConfig.settings = response;
sessionStorage.setItem("settingsUrls", JSON.stringify(response));
localStorage.setItem("settings", JSON.stringify(response));
},
error: () => {
AppConfig.settings = { maintenance: false, baseURI: "", env: "", envTitle: "", version: "", baseWeb: "", hostingUrl: "", signalR: "" };
}
});
}
getURI(base: string, endPoint: string): string {
const localSettings = localStorage.getItem("settings");
const localSettingsFromSession = sessionStorage.getItem("settingsUrls");
if (localSettings) {
const setting = (JSON.parse(localSettings) as IAppConfig);
return setting.baseURI + base + "/" + endPoint;
}
else if (localSettingsFromSession) {
const setting = (JSON.parse(localSettingsFromSession) as IAppConfig);
return setting.baseURI + base + "/" + endPoint;
} else {
return AppConfig.settings.baseURI + base + "/" + endPoint;
}
}
returnUrl() {
return AppConfig.settings.baseURI;
}
}
\ No newline at end of file
import { Injectable } from "@angular/core";
import { BehaviorSubject, Observable } from "rxjs";
import { IUserAccount } from "@shared/models";
@Injectable()
export class AppData {
private accountSource = new BehaviorSubject(null);
userAccount: Observable<IUserAccount> = this.accountSource.asObservable();
setAccount(userAccount: IUserAccount) {
this.accountSource.next(userAccount);
}
}
\ No newline at end of file
import { DatePipe } from '@angular/common';
import { APP_INITIALIZER, NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppComponent } from "@app/app.component";
import { AppRoutingModule, routeGuards, routePages } from "@app/app.routing.module";
import { AvatarImageComponent, SymptomsViewComponent } from "@shared/components";
import { AppConfig, AppData } from "@shared/services";
import { SharedModule } from "@shared/shared.module";
import { OTPWidget } from "@shared/widgets";
import { LayoutComponent } from './areas/layout/layout.component';
const widgets = [OTPWidget];
const components = [AvatarImageComponent, SymptomsViewComponent];
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
SharedModule.forRoot(),
AppRoutingModule
],
declarations: [
LayoutComponent,
AppComponent,
widgets,
components,
routePages
],
providers: [
AppData,
AppConfig,
routeGuards,
DatePipe,
{
provide: APP_INITIALIZER,
useFactory: (config: AppConfig) => () => config.load(),
deps: [AppConfig],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {
}
\ No newline at end of file
import { ForgotPasswordPage } from "@account/forgot-password/forgot-password.page";
import { LoginPage } from "@account/login/login.page";
import { AccountsPage } from "@admin/accounts/accounts.page";
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { NotFoundPage } from "@error/not-found/not-found.page";
import { ServerErrorPage } from "@error/server-error/server-error.page";
import { AccessGuard, AuthGuard, DeactivateGuard, TelemedicineGuard } from "@shared/guards";
import { LayoutComponent } from "./areas/layout/layout.component";
// STARTING PAGES
const startingRoutes = [
{ path: "login", canActivate: [AccessGuard], component: LoginPage, data: { title: "Login", anonymous: true } },
{ path: "forgot-password", canActivate: [AccessGuard], component: ForgotPasswordPage, data: { title: "Forgot Password", anonymous: true } }
]
const startingRouteComponents = [LoginPage, ForgotPasswordPage];
// ERROR PAGES
const errorRoutes = [
{ path: "not-found", component: NotFoundPage, data: { title: "Not Found" } },
{ path: "server-error", component: ServerErrorPage, data: { title: "Server Error" } },
]
const errorRouteComponents = [NotFoundPage, ServerErrorPage];
// HIDDEN PAGES
const hiddenPages = [
{ path: "accounts", component: AccountsPage, data: { title: "Accounts", } }
]
const hiddenPagesComponents = [AccountsPage];
const routes: Routes = [
{ path: "", redirectTo: "login", pathMatch: "full" },
...startingRoutes,
...errorRoutes,
{
path: "app",
component: LayoutComponent,
canActivate: [AuthGuard],
canActivateChild: [AuthGuard],
children: [
{ path: "", redirectTo: "dashboard", pathMatch: "full" },
...hiddenPages
]
},
{ path: "**", redirectTo: "not-found" },
];
// ----- ROUTING MODULE -----
@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true, enableTracing: false, onSameUrlNavigation: "reload" })],
exports: [RouterModule]
})
export class AppRoutingModule { }
export const routePages = [
...startingRouteComponents,
...errorRouteComponents,
...hiddenPagesComponents
];
export const routeGuards = [AccessGuard, AuthGuard, DeactivateGuard, TelemedicineGuard];
\ No newline at end of file
<div class="auth-fluid">
<div class="auth-fluid-right text-center"></div>
<div class="auth-fluid-form-box">
<div class="align-items-center d-flex h-100">
<div class="card-body">
<div class="auth-brand text-center text-lg-left">
<div class="auth-logo">
<a href="javascript:;" class="logo logo-dark text-center">
<span class="logo-lg">
<img src="assets/images/careaxesLogo.png" alt="Careaxes" height="25">
</span>
</a>
<a href="javascript:;" class="logo logo-light text-center">
<span class="logo-lg">
<!--<img src="assets/images/logo-white.png" alt="Careaxes" height="25">-->
</span>
</a>
</div>
</div>
<ng-container *ngIf="successMessage">
<div class="d-flex align-content-center align-items-center justify-content-center flex-column">
<div class="icon--success mb-4">
<svg xmlns="http://www.w3.org/2000/svg" width="72" height="72">
<g fill="none" stroke="#3283f6" stroke-width="2">
<circle cx="36" cy="36" r="35" style="stroke-dasharray: 240px, 240px; stroke-dashoffset: 480px;"></circle>
<path d="M17.417,37.778l9.93,9.909l25.444-25.393" style="stroke-dasharray: 50px, 50px; stroke-dashoffset: 0;"></path>
</g>
</svg>
</div>
<h4 class="mt-0 mb-4 font-22 text-left">Password Changed!</h4>
<div class="d-flex align-items-center justify-content-center flex-column">
<p class="mb-0 text-center">Your password has been changed successfully.</p>
<a role="button" href="javascript:;" routerLink="/login" class="btn btn-primary btn-sm mt-4"><i class="fe-arrow-left mr-1"></i>Go to Login</a>
</div>
</div>
</ng-container>
<ng-container *ngIf="!successMessage">
<form [formGroup]="forgotForm" (ngSubmit)="onSubmit()">
<ng-container *ngIf="step === 1">
<h4 class="mt-0 font-22">Forgot your password?</h4>
<p class="text-muted mb-4 font-15">Don't worry! We can help to reset your password.</p>
<div class="alert d-flex align-items-center mb-3 alert-warning" role="alert">
<i class="mdi mdi-alert-circle-outline mr-2"></i> Please enter your registered mobile number or email address below to reset your password.
</div>
<div class="form-group mb-4" [ngClass]="{ 'is-invalid': submitted && (form.username.errors || form.countryId.errors) }">
<label class="mb-1">Email/Mobile number</label>
<div class="input-group mb-0">
<div class="input-group-prepend" *ngIf="form.type.value && form.type.value === 'M'">
<select tabindex="1" class="form-control country-control" formControlName="countryId">
<option selected *ngIf="loadingCountries">...</option>
<option *ngFor="let item of countries" [textContent]="'+' + item.optionalText" [ngValue]="item.id"></option>
</select>
</div>
<input type="text" block [class.border-left-0]="form.type.value && form.type.value === 'M'" [attr.maxlength]="form.type.value && form.type.value === 'M' ? 10 : 100" tabindex="2" formControlName="username" autocomplete="nope" class="form-control" placeholder="Enter mobile number or email address" [ngClass]="{ 'is-invalid': (submitted && form.username.errors) }" />
</div>
</div>
<button type="submit" [disabled]="submitting" class="btn btn-primary btn-block">
<span *ngIf="submitting">
<span class="spinner-border spinner-border-sm mr-1" role="status" aria-hidden="true"></span>
Please wait..
</span>
<span *ngIf="!submitting">Submit</span>
</button>
</ng-container>
<ng-container *ngIf="step === 2">
<h4 class="mt-0 font-22">Forgot your password?</h4>
<p class="text-muted mb-4 font-15">Don't worry! We can help to reset your password.</p>
<otp [model]="getModel()" [otp]="otp" [otpExpiresIn]="otpExpiresIn" (validateOTPEmitter)="onValidateOTP($event)"></otp>
</ng-container>
<ng-container *ngIf="step === 3">
<h4 class="mt-0 font-22">Reset your password</h4>
<p class="text-muted mb-4 font-15">Enter new password to continue.</p>
<div class="form-group mb-3">
<div class="media">
<div class="avatar-sm mr-2">
<span class="avatar-title rounded-circle font-12 font-weight-bold text-white bg-warning" [textContent]="form.fullName.value | initials"></span>
</div>
<div class="media-body">
<h5 class="mb-0 mt-0 font-weight-normal" [textContent]="form.fullName.value"></h5>
<span *ngIf="form.type.value && form.type.value === 'M'" class="d-block text-mute font-13" [textContent]="'(+' + countryCode + ') ' + form.username.value"></span>
<span *ngIf="form.type.value && form.type.value === 'E'" class="d-block text-mute font-13" [textContent]="form.username.value"></span>
</div>
</div>
</div>
<div class="form-group mb-3">
<label class="mb-1">New Password</label>
<div class="input-group mb-0">
<input type="password" formControlName="password" block autocomplete="nope" class="form-control" [ngClass]="{ 'is-invalid': submitted && form.password.errors }" placeholder="Your new password" />
<div class="input-group-append cursor-pointer" password>
<div class="input-group-text"><span class="password-eye"></span></div>
</div>
</div>
<div *ngIf="submitted && form.password.errors" class="invalid-feedback show-must">
<div *ngIf="form.password.errors.minLength">
Password must contain minimum of 4 characters.
</div>
</div>
</div>
<div class="form-group mb-4">
<label class="mb-1">Re-enter Password</label>
<div class="input-group mb-0">
<input type="password" formControlName="confirmPassword" block autocomplete="nope" class="form-control" [ngClass]="{ 'is-invalid': submitted && form.confirmPassword.errors }" placeholder="Your password again" />
<div class="input-group-append cursor-pointer" password>
<div class="input-group-text"><span class="password-eye"></span></div>
</div>
</div>
<div *ngIf="submitted && form.confirmPassword.errors" class="invalid-feedback show-must">
<div *ngIf="form.confirmPassword.errors.minLength || form.confirmPassword.errors.notEquivalent">
Passwords are not matched
</div>
</div>
</div>
<button type="submit" [disabled]="submitting" class="btn btn-primary btn-block">
<span *ngIf="submitting">
<span class="spinner-border spinner-border-sm mr-1" role="status" aria-hidden="true"></span>
Please wait..
</span>
<span *ngIf="!submitting">Submit</span>
</button>
</ng-container>
</form>
</ng-container>
<footer class="footer footer-alt" *ngIf="!successMessage">
<p>Remember your password? <a routerLink="/login" href="javascript:;" class="ml-1"><b>Login</b></a></p>
</footer>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { HttpErrorResponse } from "@angular/common/http";
import { Component, OnDestroy, OnInit, ViewEncapsulation } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { ApiResources, UtilHelper } from "@shared/helpers";
import { IResource, OTPResponse, Page } from "@shared/models";
import { HttpService, NotifyService, ResourceService } from "@shared/services";
import { CompareValidator, EmailValidator, MobileValidator } from "@shared/validators";
import { finalize, takeUntil } from "rxjs/operators";
class ForgotRequest {
type: string;
username: string;
countryId: number;
password: string;
accountTypes: Array<number>;
accountId: number;
fullName: string;
}
@Component({
templateUrl: "./forgot-password.html",
encapsulation: ViewEncapsulation.None
})
export class ForgotPasswordPage implements OnInit, OnDestroy {
page: Page;
step: number;
loadingCountries: boolean;
countries: Array<IResource>;
forgotForm: FormGroup;
otp: string;
otpExpiresIn: number;
successMessage: string;
submitting: boolean;
submitted: boolean;
constructor(
private readonly httpService: HttpService,
private readonly notifyService: NotifyService,
private readonly resourceService: ResourceService,
private readonly formBuilder: FormBuilder
) {
this.page = new Page();
this.buildForm();
}
private buildForm() {
this.step = 1;
this.forgotForm = this.formBuilder.group({
type: [null],
accountId: [0],
fullName: [null],
countryId: [null, [Validators.required]],
username: ["", [Validators.required]],
password: "",
confirmPassword: ""
}, { validator: CompareValidator.compare("password", "confirmPassword") });
this.forgotForm.get("countryId").valueChanges.subscribe(() => { this.forgotForm.get("username").updateValueAndValidity(); });
this.forgotForm.get("username").valueChanges.subscribe((username: string) => {
const typeControl = this.forgotForm.get("type");
const usernameControl = this.forgotForm.get("username");
if (username) {
const type = /^[0-9]+$/.test(username) ? "M" : "E";
if (type === "M") {
usernameControl.setValidators([Validators.required, MobileValidator.isValid]);
} else {
usernameControl.setValidators([Validators.required, EmailValidator.isValid]);
}
typeControl.patchValue(type);
} else {
typeControl.patchValue(null);
usernameControl.setValidators([Validators.required]);
}
});
}
private fetchCountries() {
this.loadingCountries = true;
this.resourceService.countries()
.pipe(finalize(() => { this.loadingCountries = false }))
.pipe(takeUntil(this.page.unSubscribe))
.subscribe((response: Array<IResource>) => {
this.countries = response;
this.forgotForm.get("countryId").patchValue(response[0].id);
});
}
getModel() {
return {
username: this.forgotForm.get("username").value,
countryCode: this.forgotForm.get("type").value === "M" ? this.countries.find(m => m.id === this.forgotForm.get("countryId").value).optionalText : "",
countryId: this.forgotForm.get("type").value === "M" ? this.forgotForm.get("countryId").value : 0
}
}
get countryCode() {
return this.forgotForm.get("countryId").value ? this.countries.find(m => m.id === this.forgotForm.get("countryId").value).optionalText : "";
}
get form() {
return this.forgotForm.controls;
}
onSubmit() {
this.submitted = true;
if (!this.forgotForm.valid) {
this.notifyService.warningToast("PLEASE ENTER VALID EMAILId/ MOBILE NUMBER")
return;
}
if (this.step === 3 && (this.forgotForm.controls.password.errors || this.forgotForm.controls.confirmPassword.errors)) {
return;
}
const model = UtilHelper.clone(this.forgotForm.getRawValue()) as ForgotRequest;
model.username = model.type && model.type === "M" ? model.countryId + ":" + model.username : model.username;
//model.accountTypes = [Role.SuperAdmin, Role.Administrator, Role.Support, Role.Accountant, Role.SymptomCreator, Role.SymptomManager];
delete model.type;
delete model.countryId;
delete model.fullName;
this.successMessage = undefined;
this.submitting = true;
this.httpService
.post<string | OTPResponse>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.forgotPassword), model, false)
.pipe(finalize(() => { this.submitted = false, this.submitting = false }))
.pipe(takeUntil(this.page.unSubscribe))
.subscribe({
next: (response: (string | OTPResponse)) => {
if (typeof response === "object") {
const otpResponse = response as OTPResponse;
if (otpResponse.error) {
this.notifyService.warning(otpResponse.errorDescription);
} else {
this.otp = otpResponse.otp;
this.otpExpiresIn = otpResponse.otpExpiresIn;
this.forgotForm.get("accountId").patchValue(otpResponse.accountId);
this.forgotForm.get("fullName").patchValue(otpResponse.fullName);
this.step = 2;
}
} else {
this.successMessage = response as string;
}
},
error: (error: HttpErrorResponse) => {
const errorMessage = UtilHelper.handleError(error);
if (errorMessage) {
this.notifyService.warning(errorMessage);
} else {
this.notifyService.defaultError();
}
}
});
}
onValidateOTP(validated: boolean) {
if (validated) {
this.step = 3;
const passwordControl = this.forgotForm.get("password");
passwordControl.patchValue(null);
passwordControl.setValidators([Validators.required, Validators.minLength(4)]);
passwordControl.updateValueAndValidity();
const confirmPasswordControl = this.forgotForm.get("confirmPassword");
confirmPasswordControl.patchValue(null);
confirmPasswordControl.setValidators([Validators.required, Validators.minLength(4)]);
confirmPasswordControl.updateValueAndValidity();
}
}
ngOnInit() {
this.fetchCountries();
}
ngOnDestroy() {
this.page.unsubscribeAll();
}
}
\ No newline at end of file
<style>
html,
body {
height: 100%;
margin: 0px;
}
</style>
<div class="flex-container">
<div class="div1"></div>
<div class="div2 align-items-center d-flex div2 justify-content-center">
<div class="w-100 p-4">
<div>
<div class="auth-logo">
<a href="javascript:;" class="logo logo-dark">
<span class="logo-lg">
<img [src]="logoBasics && logoBasics.imageUrl ? logoBasics.imageUrl : 'assets/images/fernandez.png'" [hidden]="loading" alt="Careaxes" width="55" />
</span>
</a>
<a href="javascript:;" class="logo logo-light">
<span class="logo-lg">
<!--<img src="assets/images/fernandez.png" alt="Careaxes" width="200">-->
<img [src]="logoBasics && logoBasics.imageUrl ? logoBasics.imageUrl : 'assets/images/fernandez.png'" [hidden]="loading" alt="Careaxes" width="200" />
</span>
</a>
</div>
</div>
<div *ngIf="env !== 'live'" class="mb-4">
<div class="badge badge- mb-1" [ngClass]="{'badge-success': env === 'uat', 'badge-blue': env === 'qa proactive', 'badge-danger': env === 'qa reactive', 'badge-warning': env === 'local'}">
<h3 class="m-0 font-22 text-white">
<span [textContent]="env" class="mr-1 text-uppercase"></span>Environment
</h3>
</div>
<h6 class="m-0 text-secondary">
<i class="mdi mid-laptop mr-1"></i><span [textContent]="envTitle" class="mr-1 text-uppercase"></span>
</h6>
</div>
<div>
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<h4 class="mt-0 font-22">Welcome Back :)</h4>
<p class="font-15">Sign in to access your account.</p>
<div class="form-group mb-3" [ngClass]="{ 'is-invalid': submitted && (form.username.errors || form.countryId.errors) }">
<div class="d-flex justify-content-between">
<label class="mb-1">Username</label>
<div class="text-primary" *ngIf="loadingLocationMap">
loading ...
</div>
</div>
<div class="input-group mb-0">
<div class="input-group-prepend" *ngIf="form.type.value && form.type.value === 'M'">
<select class="form-control country-control" formControlName="countryId">
<option *ngFor="let item of countries" [textContent]="'+' + item.optionalText" [ngValue]="item.id"></option>
</select>
</div>
<input tabindex="0" type="text" block [class.border-left-0]="form.type.value && form.type.value === 'M'" [attr.maxlength]="form.type.value && form.type.value === 'M' ? 10 : 100" formControlName="username" autocomplete="nope" class="form-control" [ngClass]="{ 'is-invalid': (submitted && form.username.errors) }" placeholder="Enter mobile number or email address" />
</div>
</div>
<div class="form-group">
<label class="mb-1">Password</label>
<div class="input-group password-group mb-0">
<input tabindex="0" type="password" block formControlName="password" maxlength="20" autocomplete="nope" class="form-control" [ngClass]="{ 'is-invalid': (submitted && form.password.errors) }" placeholder="Enter password" />
<div class="input-group-append cursor-pointer" password>
<div class="input-group-text">
<span class="password-eye"></span>
</div>
</div>
</div>
</div>
<div class="form-group mb-3" *ngIf="locationDropDown">
<label class="mb-1">Location</label>
<div class="input-group password-group mb-0">
<select class="form-control" tabindex="0" formControlName="locationId" [ngClass]="{ 'is-invalid': (submitted && form.locationId.errors) }">
<option selected hidden [ngValue]="null">
Select Location
</option>
<option *ngFor="let location of locations" [ngValue]="location.id" [textContent]="location.name"></option>
</select>
</div>
</div>
<a href="javascript:;" tabindex="0" routerLink="/forgot-password" class="float-right anchorFocus mb-1 onHoverUnderline"><small>Forgot your password?</small></a>
<button type="submit" [disabled]="submitting" tabindex="0" class="btn btn-primary btn-block">
<span *ngIf="submitting">
<span class="spinner-border spinner-border-sm mr-1" role="status" aria-hidden="true"></span>
Please wait..
</span>
<span *ngIf="!submitting">Sign In</span>
</button>
<a href="javascript:;" tabindex="0" routerLink="/queue" class="float-right anchorFocus mt-3 onHoverUnderline">Queue Management</a>
</form>
</div>
</div>
</div>
</div>
\ No newline at end of file
import { HttpErrorResponse } from "@angular/common/http";
import { Component, OnDestroy, OnInit, ViewEncapsulation } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { Router } from "@angular/router";
import { Setting } from "@shared/entities";
import { ApiResources, UtilHelper } from "@shared/helpers";
import { IResource, IUserAccount, Page } from "@shared/models";
import { AppConfig, AppData, HttpService, IdentityService, MenuService, NotifyService, ResourceService } from "@shared/services";
import { EmailValidator, MobileValidator } from "@shared/validators";
import { Subscription } from "rxjs";
import { debounceTime, finalize, takeUntil } from "rxjs/operators";
class LoginRequest {
type: string;
username: string;
countryId: number;
password: string;
accountTypes: Array<number>;
deviceId: string;
deviceToken: string;
deviceType: string;
locationId: number;
}
@Component({
templateUrl: "./login.html",
encapsulation: ViewEncapsulation.None
})
export class LoginPage implements OnInit, OnDestroy {
page: Page;
loadingCountries: boolean;
countries: Array<IResource>;
locations: Array<IResource>;
locationMap: Array<IResource>;
loadingLocationMap: boolean;
locationDropDown: boolean;
env: string;
envTitle: string;
loginForm: FormGroup;
submitting: boolean;
submitted: boolean;
logoBasics: Setting;
logoSubscription: Subscription;
loadingSettings: boolean;
isTheme: boolean;
themeColor: string;
loading: boolean;
constructor(
private readonly httpService: HttpService,
private readonly router: Router,
private readonly formBuilder: FormBuilder,
private readonly identityService: IdentityService,
private readonly menuService: MenuService,
private readonly resourceService: ResourceService,
private readonly notifyService: NotifyService,
private readonly appData: AppData
) {
this.locations = new Array<IResource>();
this.countries = new Array<IResource>();
this.buildForm();
this.env = AppConfig.settings.env.toLowerCase();
this.envTitle = AppConfig.settings.envTitle;
this.page = new Page();
this.locationDropDown = false;
this.logoBasics = new Setting();
}
getLogoImage = () => {
this.loading = true;
this.httpService
.get<Array<Setting>>(ApiResources.getURI(ApiResources.setting.base, ApiResources.setting.fetch), { type: "Logo", active: true })
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => { this.loading = false }))
.subscribe({
next: (response: Array<Setting>) => {
if (response && response.length > 0) {
this.logoBasics = response[0];
if (UtilHelper.isEmpty(response[0].imageUrl)) {
response[0].imageUrl = `${ApiResources.getURI(ApiResources.resources.base, ApiResources.resources.getProfileImage)}?imagePath=${response[0].imageUrl}`;
}
}
},
error: () => {
this.logoBasics = new Setting();
}
});
}
private fetchCountries() {
this.loadingCountries = true;
this.resourceService.countries()
.pipe(finalize(() => { this.loadingCountries = false }))
.pipe(takeUntil(this.page.unSubscribe))
.subscribe((response: Array<IResource>) => {
this.countries = response;
this.loginForm.get("countryId").patchValue(response[0].id);
});
}
private fetchLocationMap(type: string, value: string) {
this.loadingLocationMap = true;
this.resourceService.locationAccountDynamic(type, value)
.pipe(finalize(() => { this.loadingLocationMap = false }))
.pipe(takeUntil(this.page.unSubscribe))
.subscribe((response: Array<IResource>) => {
this.setAccountLocation(response)
});
}
private buildForm() {
this.loginForm = this.formBuilder.group({
type: [null],
countryId: [null, [Validators.required]],
locationId: [null, [Validators.required]],
username: ["", [Validators.required]],
password: ["", [Validators.required]]
});
this.loginForm.get("countryId").valueChanges.subscribe(() => {
this.loginForm.get("username").updateValueAndValidity();
});
this.loginForm.get("username").valueChanges.pipe(debounceTime(300)).subscribe((username: string) => {
const typeControl = this.loginForm.get("type");
const usernameControl = this.loginForm.get("username");
if (username) {
let type = /^[0-9]+$/.test(username) ? "M" : "E";
if (type === "M") {
usernameControl.setValidators([Validators.required, MobileValidator.isValid]);
} else {
if (username.includes("@")) {
usernameControl.setValidators([Validators.required, EmailValidator.isValid]);
}
else {
usernameControl.setValidators([Validators.nullValidator])
}
}
typeControl.patchValue(type);
if (type === "E") {
type = username.includes("@") ? "E" : "U";
}
this.fetchLocationMap(type, username);
} else {
this.locationDropDown = false;
typeControl.patchValue(null);
usernameControl.setValidators([Validators.required]);
}
});
}
get form() { return this.loginForm.controls; }
onSubmit() {
this.submitted = true;
if (!this.loginForm.valid) {
this.notifyService.warningToast("PLEASE ENTER VALID CREDENTIALS")
return;
}
const model = UtilHelper.clone(this.loginForm.getRawValue()) as LoginRequest;
model.username = model.type && model.type === "M" ? model.countryId + ":" + model.username : model.username;
model.deviceType = "Web";
model.deviceId = Math.random().toString(36).substring(2) + Date.now().toString(36);
model.deviceToken = Math.random().toString(36).substring(2) + Date.now().toString(36);
delete model.type;
delete model.countryId;
this.submitting = true;
this.httpService
.post<IUserAccount>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.authenticate), model, false)
.pipe(takeUntil(this.page.unSubscribe))
.subscribe(
(account: IUserAccount) => {
account.deviceId = model.deviceId;
this.identityService.signIn(account)
.pipe(takeUntil(this.page.unSubscribe))
.subscribe(() => {
this.menuService.fetch(account.accountId, account.roleId, () => {
this.appData.setAccount(account);
//this.appData.setPrint()
// this.router.navigate([this.menuService.getDefaultRoute]);
this.router.navigate(['app/accounts']);
});
}, () => {
this.submitting = false;
this.notifyService.defaultError();
});
},
(error: HttpErrorResponse) => {
this.submitting = false;
const errorMessage = UtilHelper.handleError(error);
if (errorMessage) {
this.notifyService.warningToast(errorMessage);
} else {
this.notifyService.defaultError();
}
}
);
}
setAccountLocation = (found: Array<IResource>) => {
if (found && found.length) {
this.loginForm.patchValue({
locationId: found[0].id
});
this.locations = found;
this.locationDropDown = found.length > 1;
} else {
this.locationDropDown = false;
}
}
private getSettingsLayoutCommon() {
this.loadingSettings = true;
this.httpService.get<Array<Setting>>(ApiResources.getURI(ApiResources.setting.base, ApiResources.setting.fetch), { name: "Theme Color" })
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => this.loadingSettings = false))
.subscribe((response: Array<Setting>) => {
if (response.length > 0) {
this.isTheme = response[0].active;
this.themeColor = response[0].value;
}
this.onToggleTheme(this.themeColor);
}), () => { this.onToggleTheme(this.themeColor); };
}
onToggleTheme(color: string) {
switch (color) {
case "#827d77 #fec32b #f5f6f9 #f86262":
this.yellowTheme();
break;
default:
this.blueTheme();
}
}
yellowTheme() {
const head = document.getElementsByTagName('head')[0];
const fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
const fileName = location.origin + location.pathname + "assets/css/yellow-theme.css";
fileref.setAttribute("href", fileName);
fileref.media = 'all';
head.appendChild(fileref);
}
blueTheme() {
const head = document.getElementsByTagName('head')[0];
const fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
const fileName = location.origin + location.pathname + "assets/css/blue-theme.css";
fileref.setAttribute("href", fileName);
fileref.media = 'all';
head.appendChild(fileref);
}
ngOnInit() {
this.getSettingsLayoutCommon();
this.getLogoImage();
$("body").addClass("auth-fluid-pages pb-0");
this.locationDropDown = false;
this.fetchCountries();
}
ngOnDestroy() {
$("body").removeClass("auth-fluid-pages pb-0");
this.page.unsubscribeAll();
}
}
\ No newline at end of file
<div class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="page-title-box">
<div class="page-title-right">
<button type="button" class="btn btn-secondary btn-sm ml-1" (click)="onShowFilters()"><i class="fe-filter mr-1"></i> Filters</button>
</div>
<h4 class="page-title">Accounts</h4>
</div>
</div>
</div>
<div class="row">
<div class="col-12 p-0">
<div class="card-box mb-1 p-1">
<table class="table table-borderless table-sm mb-0">
<tr style="font-size:12px;" class="myFlex">
<td class="flex-grow-1">
<div class="form-group mb-0">
<label class="mb-1 font-15">Full name</label>
<input type="text" class="form-control form-control-sm" (keyup.enter)="onApplyFilters()" autocomplete="nope" [(ngModel)]="filters.options.fullName" block maxlength="152" placeholder="Enter full name" />
</div>
</td>
<td>
<div class="form-group">
<label class="mb-1 font-15">Email address</label>
<input type="text" class="form-control form-control-sm" (keyup.enter)="onApplyFilters()" autocomplete="nope" [(ngModel)]="filters.options.email" block maxlength="100" placeholder="Enter email address" />
</div>
</td>
<td class="flex-grow-1">
<div class="form-group">
<label class="mb-1 font-15">Mobile number</label>
<input type="text" class="form-control form-control-sm" (keyup.enter)="onApplyFilters()" autocomplete="nope" [(ngModel)]="filters.options.mobile" numbersOnly block maxlength="10" placeholder="Enter mobile number" />
</div>
</td>
<td class="flex-grow-1">
<div class="form-group">
<label class="mb-1 font-15">Role</label>
<select class="form-control custom-select form-control-sm" (keyup.enter)="onApplyFilters()" autocomplete="nope" [(ngModel)]="filters.options.roleId">
<option [ngValue]="null" *ngIf="!loadingRoles">All</option>
<option selected *ngIf="loadingRoles">Loading...</option>
<option *ngFor="let item of roles" [textContent]="item.value" [ngValue]="item.id"></option>
</select>
</div>
</td>
<td style="width: 12%;" class="flex-grow-1">
<div class="d-flex justify-content-center xsMarTop">
<button type="button" (click)="onApplyFilters()" class="btn btn-sm btn-outline-primary">Search</button>
<button type="button" (click)="onResetFilters()" class="btn btn-sm btn-outline-danger ml-1">Reset</button>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="row" *ngIf="loading">
<div class="col-12">
<div class="d-flex align-items-center">
<span class="spinner-grow text-warning" role="status" aria-hidden="true"></span>
<span class="ml-2">Please wait while loading Accounts ...</span>
</div>
</div>
</div>
<div class="row" *ngIf="!loading && (!users || !users.length)">
<div class="col-12 mh-400">
<no-data [applied]="filters.applied" [title]="'Accounts'"></no-data>
</div>
</div>
<div class="row" *ngIf="!loading && users && users.length">
<div class="col-12">
<div class="card mb-0">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-centered table-nowrap table-sm table-striped table-borderless mb-0">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th>User Name</th>
<th>Mobile Number</th>
<th>Email Address</th>
<th>Status</th>
<th>Last Login Date</th>
<th class="text-right" style="width: 85px">Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of users; let i = index; trackBy: trackBy.account">
<td>
<div class="media">
<div class="avatar-sm mr-2">
<img *ngIf="item.thumbnailUrl" [src]="item.thumbnailUrl" [alt]="item.fullName" class="img-fluid rounded-circle">
<span *ngIf="!item.thumbnailUrl" class="avatar-title rounded-circle font-11 font-weight-bold text-white" avatar-bg [index]="i" [textContent]="item.fullName | initials"></span>
</div>
<div class="media-body">
<h5 class="mb-0 mt-0 font-weight-normal text-uppercase" [textContent]="item.fullName"></h5>
</div>
</div>
</td>
<td><span class="badge bg-soft-info" [textContent]="item.roleName"></span></td>
<td>
<div class="media-body">
<h5 class="mb-0 mt-0 font-weight-normal text-uppercase" [textContent]="item.userName"></h5>
</div>
</td>
<td *ngIf="item.mobile" [textContent]="'(+' + item.countryCode + ') ' + item.mobile"></td>
<td *ngIf="!item.mobile" [textContent]="'No mobile '"></td>
<td [textContent]="item.email ? item.email : 'No email'"></td>
<td>
<span class="badge badge-soft-success" *ngIf="item.status === 'A'">Active</span>
<span class="badge badge-soft-danger" *ngIf="item.status === 'D'">Inactive</span>
<span class="badge badge-soft-danger" *ngIf="item.status === 'L'">Locked</span>
</td>
<td>
<span [textContent]="item.lastLoginDate | utcToLocal:false"></span>
<small class="ml-1" [textContent]="item.lastLoginDate | utcToLocal:false:'hh:mm a'"></small>
</td>
<td>
<div class="d-flex align-items-center justify-content-end">
<a href="javascript:;" *ngIf="item.active" class="action-icon text-primary mr-1" placement="left" ngbTooltip="Modify Location" (click)="onOpenLocationModel(item,templateModifyLocation)"><i class="mdi mdi-crosshairs-gps"></i></a>
<a href="javascript:;" class="action-icon text-primary mr-1" placement="left" ngbTooltip="Change Password" (click)="onOpenChangePassword(item,templateChangePassword)"><i class="mdi mdi-account-key-outline"></i></a>
<a href="javascript:;" *ngIf="item.active" class="action-icon text-primary mr-1" placement="left" ngbTooltip="Inactivate" (click)="onModifyStatus(item.accountId, false)"><i class="fe-user-x"></i></a>
<a href="javascript:;" *ngIf="!item.active" class="action-icon text-primary mr-1" placement="left" ngbTooltip="Activate" (click)="onModifyStatus(item.accountId, true)"><i class="fe-user-check"></i></a>
<a href="javascript:;" *ngIf="!item.isLocked" class="action-icon text-primary mr-1" placement="left" ngbTooltip="Lock" (click)="onModifyLockedStatus(item.accountId, true)"><i class="fe-lock"></i></a>
<a href="javascript:;" *ngIf="item.isLocked" class="action-icon text-primary" placement="left" ngbTooltip="Unlock" (click)="onModifyLockedStatus(item.accountId, false)"><i class="fe-unlock"></i></a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<nav class="d-flex align-items-center justify-content-between p-2" *ngIf="pagination.totalPages > 1">
<p class="mb-0 font-13">
<span class="text-dark">Page <span [textContent]="pagination.currentPage"></span> of <span [textContent]="pagination.totalPages"></span></span>
<span class="ml-1">
<span>(Showing <span [textContent]="pagination.currentItems - users.length + 1"></span> - <span [textContent]="pagination.currentItems"></span> of <span [textContent]="pagination.totalItems"></span> Users)</span>
</span>
</p>
<ngb-pagination class="pagination justify-content-end" [maxSize]="5" [rotate]="true" [ellipses]="true" [(page)]="pagination.pageIndex" [pageSize]="pagination.pageSize" (pageChange)="onNextPage()" [collectionSize]="pagination.totalItems">
<ng-template ngbPaginationPrevious><i class="fe-arrow-left"></i></ng-template>
<ng-template ngbPaginationNext><i class="fe-arrow-right"></i></ng-template>
</ngb-pagination>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="right-bar">
<div data-simplebar class="h-100">
<div class="filters-header">
<h4>Filter Options</h4>
<a href="javascript:;" (click)="onCloseFilters()"><i class="fe-x"></i></a>
</div>
<div class="filters-body">
<div class="form-group position-relative">
<label class="mb-1 font-13">Last logged date</label>
<input class="form-control date-picker form-control-sm" type="text" block placeholder="Select last loggin date" [(ngModel)]="filters.options.lastLoginDate" readonly autocomplete="nope" ngbDatepicker #fromDate="ngbDatepicker" (click)="fromDate.toggle(); $event.stopPropagation();">
</div>
<div class="form-group">
<label class="mb-1 font-13">Country</label>
<select class="form-control custom-select form-control-sm" (keyup.enter)="onApplyFilters()" autocomplete="nope" [(ngModel)]="filters.options.countryId">
<option [ngValue]="null" *ngIf="!loadingCountries">All</option>
<option selected *ngIf="loadingCountries">Loading...</option>
<option *ngFor="let item of countries" [textContent]="item.value" [ngValue]="item.id"></option>
</select>
</div>
<div class="form-group">
<label class="mb-1 font-13">Status</label>
<select class="form-control custom-select form-control-sm" (keyup.enter)="onApplyFilters()" autocomplete="nope" [(ngModel)]="filters.options.status">
<option [ngValue]="null">All</option>
<option [ngValue]="'A'">Active</option>
<option [ngValue]="'D'">Inactive</option>
<option [ngValue]="'L'">Locked</option>
</select>
</div>
</div>
<div class="filters-footer">
<button type="button" (click)="onResetFilters()" class="btn btn-sm btn-light">Reset</button>
<button type="button" (click)="onApplyFilters()" class="btn btn-sm btn-primary ml-1">Search</button>
</div>
</div>
</div>
<ng-template #templateChangePassword>
<form [formGroup]="changePasswordForm" (ngSubmit)="onChangePassword()">
<div class="modal-header">
<h4 class="modal-title"><i class="fe-log-in mr-1"></i>Change Password for {{selectedAccount.fullName}}</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" (click)="onCloseModal();">×</button>
</div>
<div class="modal-body">
<div class="form-group mb-3">
<label class="mb-1">New Password</label>
<input type="password" formControlName="password" block autocomplete="nope" class="form-control" [ngClass]="{ 'is-invalid': submitted && forms.passwordForm.password.errors }" (keypress)="space($event)" placeholder="Your new password" />
<div *ngIf="submitted && forms.passwordForm.password.errors" class="invalid-feedback">
<!--<div *ngIf="forms.passwordForm.password.errors">
Password must contain minimum of 4 characters.
</div>-->
<div *ngIf="forms.passwordForm.password.errors">
Password must contain minimum of 1 Specialcharacter, LowerCAse aplhabet, Uppercase Alphabet and Number.
</div>
</div>
</div>
<div class="form-group">
<label class="mb-1">Re-enter Password</label>
<div class="input-group mb-0">
<input type="password" formControlName="confirmPassword" block autocomplete="nope" class="form-control" [ngClass]="{ 'is-invalid': submitted && forms.passwordForm.confirmPassword.errors }" (keypress)="space($event)" placeholder="Your new password again" />
<div class="input-group-append cursor-pointer" password><div class="input-group-text"><span class="password-eye"></span></div></div>
</div>
<div *ngIf="submitted && forms.passwordForm.confirmPassword.errors" class="invalid-feedback show-must">
<!--<div *ngIf="forms.passwordForm.confirmPassword.errors.minLength && forms.passwordForm.confirmPassword.errors.notEquivalent">
Passwords are not matched
</div>-->
<div *ngIf="forms.passwordForm.confirmPassword.errors.minLength || forms.passwordForm.confirmPassword.errors.notEquivalent ">Password were not matched</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-light mr-1" (click)="onCloseModal();">Cancel</button>
<button type="submit" [disabled]="submitting" class="btn btn-sm btn-primary">
<span *ngIf="submitting">
<span class="spinner-border spinner-border-sm mr-1" role="status" aria-hidden="true"></span>
Please wait..
</span>
<span *ngIf="!submitting">Submit</span>
</button>
</div>
</form>
</ng-template>
<ng-template #templateModifyLocation>
<form [formGroup]="locationForm" (ngSubmit)="onModifyLocation()">
<div class="modal-header">
<h4 class="modal-title"><i class="fe-log-in mr-1"></i>Location for {{selectedAccount.fullName}}</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" (click)="onCloseModal();">×</button>
</div>
<div class="modal-body">
<div class="row mb-1">
<div class="col-md-12">
<div class="form-group">
<label class="mb-1">Locations <code>*</code></label>
<ng-select class="ng-select-sm text-uppercase" [items]="locations"
bindLabel="name"
bindValue="id"
autocomplete="nope"
placeholder="Select locations"
[ngClass]="{ 'is-invalid': submitted && forms.locationForm.locationIds.errors }"
[multiple]="true"
formControlName="locationIds">
<ng-template ng-notfound-tmp let-searchTerm="searchTerm">
<div class="ng-option disabled">
No locations found for '{{searchTerm}}'
</div>
</ng-template>
</ng-select>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-light mr-1" (click)="onCloseModal();">Cancel</button>
<button type="submit" [disabled]="submitting" class="btn btn-sm btn-primary">
<span *ngIf="submitting">
<span class="spinner-border spinner-border-sm mr-1" role="status" aria-hidden="true"></span>
Please wait..
</span>
<span *ngIf="!submitting">Submit</span>
</button>
</div>
</form>
</ng-template>
\ No newline at end of file
import { HttpErrorResponse } from "@angular/common/http";
import { Component, OnDestroy, OnInit, TemplateRef } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { NgbModal, NgbModalRef } from "@ng-bootstrap/ng-bootstrap";
import { Account } from "@shared/entities";
import { ApiResources, DateHelper, UtilHelper } from "@shared/helpers";
import { IResource, IUserAccount, Page, Pagination } from "@shared/models";
import { AppData, HttpService, NotifyService, ResourceService } from "@shared/services";
import { CompareValidator } from "@shared/validators";
import { finalize, takeUntil } from "rxjs/operators";
class TrackBy {
account(_: number, item: Account) {
return item.accountId;
}
resource(_: number, item: IResource) {
return item.id;
}
}
class FilterOptions {
fullName: string = null;
email: string = null;
mobile: string = null;
roleId: number = null;
countryId: number = null;
active: boolean = null;
status: string = null;
lastLoginDate: Date = null;
}
class Filters {
options: FilterOptions;
applied: boolean;
constructor() {
this.init();
}
init() {
this.options = new FilterOptions();
this.applied = undefined;
}
}
@Component({
templateUrl: "./accounts.html"
})
export class AccountsPage implements OnInit, OnDestroy {
page: Page;
today = DateHelper.ngbToday;
minDate = DateHelper.minDate;
roles: Array<IResource>;
loadingRoles: boolean;
modalRef: NgbModalRef;
filters: Filters;
trackBy: TrackBy;
pagination: Pagination;
loading: boolean;
countries: Array<IResource>;
loadingCountries: boolean;
users: Array<Account>;
user: Account;
loadingUsers: boolean;
modifying: boolean;
modifyingContent: string;
changePasswordForm: FormGroup;
selectedAccount: Account;
submitted: boolean;
submitting: boolean;
locations: Array<IResource>;
loadingLocation: boolean;
locationForm: FormGroup;
constructor(
private readonly appData: AppData,
private readonly resourceService: ResourceService,
private readonly notifyService: NotifyService,
private readonly httpService: HttpService,
private readonly formBuilder: FormBuilder,
private readonly modalService: NgbModal
) {
this.loading = true;
this.page = new Page();
this.filters = new Filters();
this.trackBy = new TrackBy();
this.users = new Array<Account>();
this.roles = new Array<IResource>();
this.initPagination();
this.selectedAccount = new Account();
this.locations = new Array<IResource>();
}
private fetchLocations() {
this.loadingLocation = true;
this.resourceService.locations()
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => this.loadingLocation = false))
.subscribe((response: Array<IResource>) => {
this.locations = response;
});
}
private buildChangePasswordForm() {
this.changePasswordForm = this.formBuilder.group({
accountId: this.selectedAccount.accountId,
/*password: ["", [Validators.required, Validators.minLength(4)]],*/
password: ["", [Validators.required, Validators.pattern('(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&].{5,}')]],
confirmPassword: ["", [Validators.required, Validators.minLength(6)]]
}, { validator: CompareValidator.compare("password", "confirmPassword") });
}
private buildLocationForm() {
this.locationForm = this.formBuilder.group({
locationIds: [null, [Validators.required]],
accountId: [null, [Validators.required]]
});
const locationIds = new Array<number>();
if (UtilHelper.isEmpty(this.selectedAccount.locationIdentifiers)) {
this.selectedAccount.locationIdentifiers.split(",").forEach((id) => { locationIds.push(+id) });
}
this.locationForm.patchValue({
locationIds: locationIds,
accountId: this.selectedAccount.accountId
});
}
get forms() {
return {
passwordForm: this.changePasswordForm ? this.changePasswordForm.controls : {},
locationForm: this.locationForm ? this.locationForm.controls : {}
}
}
onChangePassword() {
this.submitted = true;
if (!this.changePasswordForm.valid) {
return;
}
if (this.changePasswordForm.controls.password.errors || this.changePasswordForm.controls.confirmPassword.errors) {
return;
}
this.submitting = true;
const request = this.changePasswordForm.getRawValue();
request["locationId"] = this.page.userAccount.locationId;
this.httpService.put<string>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.updatePassword), request)
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => this.submitted = this.submitting = false))
.subscribe({
next: (response: string) => {
if (response) {
this.notifyService.successToast(response);
}
this.onCloseModal();
}, error: (error: HttpErrorResponse) => {
const errorMessage = UtilHelper.handleError(error);
if (errorMessage) {
this.notifyService.warningToast("Error occurred while changing the password.");
} else {
this.notifyService.defaultError();
}
}
});
}
onModifyLocation() {
this.submitted = true;
if (this.locationForm.invalid) {
this.notifyService.infoToast("Please fill the form.");
return;
}
const request = this.locationForm.getRawValue();
this.submitting = true;
this.httpService.post<string>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.modifyLocation), request)
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => this.submitted = this.submitting = false))
.subscribe({
next: (response: string) => {
if (response) {
this.notifyService.successToast(response);
}
this.fetchAccounts();
this.onCloseModal();
}, error: (error: HttpErrorResponse) => {
const errorMessage = UtilHelper.handleError(error);
if (errorMessage) {
this.notifyService.warningToast("Error occurred while updating location.");
} else {
this.notifyService.defaultError();
}
}
});
}
private initPagination() {
this.pagination = new Pagination();
this.pagination.pageIndex = 1;
this.pagination.pageSize = 15;
}
private fetchRoles() {
this.loadingRoles = true;
this.resourceService.roles()
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => this.loadingRoles = false))
.subscribe((response: Array<IResource>) => {
this.roles = response;
});
}
private fetchCountries() {
this.loadingCountries = true;
this.resourceService.countries()
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => this.loadingCountries = false))
.subscribe((response: Array<IResource>) => {
this.countries = response;
});
}
private fetchAccounts() {
this.loading = true;
const request = Object.assign(UtilHelper.clone(this.filters.options), UtilHelper.clone(this.pagination));
this.httpService.post<Array<Account>>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.fetch), request)
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => this.loading = false))
.subscribe((response: Array<Account>) => {
this.users = response;
UtilHelper.applyPagination(this.users, this.pagination);
}, (error: HttpErrorResponse) => {
const errorMessage = UtilHelper.handleError(error);
if (errorMessage) {
this.notifyService.warning("Error occurred while fetching users.");
} else {
this.notifyService.defaultError();
}
});
}
onModifyStatus(id: number, status: boolean) {
this.user = this.users.find(m => m.accountId === id);
this.notifyService.confirm(`Do you really want to ${status ? "enable" : "disable"} this selected account?`, () => {
this.modifying = true;
this.modifyingContent = `${status ? "enabling" : "disabling"} the selected account`;
this.httpService
.put<string>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.modifyStatus), { accountId: this.user.accountId, active: status, modifiedBy: this.page.userAccount.accountId })
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => { this.modifying = false; this.user = undefined }))
.subscribe({
next: (response: string) => {
this.fetchAccounts();
this.notifyService.success(response);
}, error: (error: HttpErrorResponse) => {
const errorMessage = UtilHelper.handleError(error);
if (errorMessage) {
this.notifyService.warning(errorMessage);
} else {
this.notifyService.defaultError();
}
}
});
});
}
onModifyLockedStatus(id: number, status: boolean) {
this.user = this.users.find(m => m.accountId === id);
this.notifyService.confirm(`Do you really want to ${status ? "lock" : "unlock"} this selected account?`, () => {
this.modifying = true;
this.modifyingContent = `${status ? "lock" : "unlock"} the selected account`;
this.httpService
.put<string>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.lockStatus), { accountId: this.user.accountId, isLocked: status, modifiedBy: this.page.userAccount.accountId })
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => { this.modifying = false; this.user = undefined }))
.subscribe({
next: (response: string) => {
this.fetchAccounts();
this.notifyService.success(response);
}, error: (error: HttpErrorResponse) => {
const errorMessage = UtilHelper.handleError(error);
if (errorMessage) {
this.notifyService.warning(errorMessage);
} else {
this.notifyService.defaultError();
}
}
});
});
}
onNextPage() {
this.fetchAccounts();
$("body,html").animate({ scrollTop: 0 });
}
onShowFilters() {
$("body").addClass("right-bar-enabled");
}
onApplyFilters() {
this.filters.applied = UtilHelper.isFiltersApplied(this.filters.options);
this.initPagination();
this.loading = true;
this.fetchAccounts();
this.onCloseFilters();
}
onResetFilters() {
this.filters.init();
this.loading = true;
this.onCloseFilters();
this.fetchAccounts();
}
onCloseFilters() {
$("body").removeClass("right-bar-enabled");
}
onOpenLocationModel(account: Account, content: TemplateRef<any>) {
this.selectedAccount = account;
this.buildLocationForm();
this.onOpenModel(content);
}
onOpenChangePassword(account: Account, content: TemplateRef<any>) {
this.selectedAccount = account;
this.buildChangePasswordForm();
this.onOpenModel(content);
}
onOpenModel(content: TemplateRef<any>) {
this.modalRef = this.modalService.open(content, {
backdrop: "static",
keyboard: false,
centered: false,
size: "lg",
windowClass: "custom-modal effect-scale"
});
}
onCloseModal() {
try {
this.modalRef.close();
this.modalRef = undefined;
} catch (e) {
// ignored;
}
this.submitted = this.submitting = false;
this.selectedAccount = new Account();
}
ngOnInit() {
this.appData.userAccount
.pipe(takeUntil(this.page.unSubscribe))
.subscribe((userAccount: IUserAccount) => {
if (userAccount) {
this.page.userAccount = userAccount;
this.fetchRoles();
this.fetchCountries();
this.fetchAccounts();
this.fetchLocations();
} else {
this.page.userAccount = undefined;
}
});
}
ngOnDestroy() {
this.page.unsubscribeAll();
}
space(event) {
console.log(event);
if (event.charcode === 32) {
event.preventdefault();
}
}
}
\ No newline at end of file
<div class="error-container bg-pattern">
<div class="error-box">
<img src="assets/images/logo.png" alt="Careaxes" height="25">
<div class="error-text-box">
<svg viewBox="0 0 600 200">
<symbol id="s-text">
<text text-anchor="middle" x="50%" y="50%" dy=".35em">404!</text>
</symbol>
<use class="text" xlink:href="#s-text"></use>
<use class="text" xlink:href="#s-text"></use>
<use class="text" xlink:href="#s-text"></use>
<use class="text" xlink:href="#s-text"></use>
<use class="text" xlink:href="#s-text"></use>
</svg>
</div>
<div class="text-center">
<h3 class="mt-0 mb-2">Whoops! Page not found </h3>
<p class="font-15 mb-3">
We're sorry, but the page you were looking for doesn't exist. You may have mistyped the address or the page may have moved.
</p>
<a href="javascript:;" routerLink="/app/dashboard" class="btn btn-primary waves-effect waves-light">Back to Dashboard</a>
</div>
</div>
</div>
\ No newline at end of file
import { Component, ViewEncapsulation } from "@angular/core";
@Component({
templateUrl: "./not-found.html",
encapsulation: ViewEncapsulation.None
})
export class NotFoundPage {
}
\ No newline at end of file
<div class="error-container bg-pattern bg-error-light">
<div class="error-box">
<img src="assets/images/logo.png" alt="Careaxes" height="25">
<div class="error-text-box">
<svg viewBox="0 0 600 200">
<symbol id="s-text">
<text text-anchor="middle" x="50%" y="50%" dy=".35em">500!</text>
</symbol>
<use class="text" xlink:href="#s-text"></use>
<use class="text" xlink:href="#s-text"></use>
<use class="text" xlink:href="#s-text"></use>
<use class="text" xlink:href="#s-text"></use>
<use class="text" xlink:href="#s-text"></use>
</svg>
</div>
<div class="text-center">
<h3 class="mt-0 mb-2">Oh no! Internal server error </h3>
<p class="font-15 mb-3">
We're sorry, there was an error please try again later. The server encountered an internal server error and was unable to complete your request.
</p>
<a href="javascript:;" routerLink="/app/dashboard" class="btn btn-danger waves-effect waves-light">Back to Dashboard</a>
</div>
</div>
</div>
\ No newline at end of file
import { Component, ViewEncapsulation } from "@angular/core";
@Component({
templateUrl: "./server-error.html",
encapsulation: ViewEncapsulation.None
})
export class ServerErrorPage {
}
\ No newline at end of file
import { Component, OnDestroy, OnInit, TemplateRef, HostListener, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { Router, NavigationEnd } from "@angular/router";
import { NgbModal, NgbModalRef, NgbTypeaheadSelectItemEvent } from "@ng-bootstrap/ng-bootstrap";
import { Observable, of, Subject, Subscription } from "rxjs";
import { takeUntil, finalize, debounceTime, distinctUntilChanged, tap, switchMap, catchError } from "rxjs/operators";
import { ApiResources, DateHelper, UtilHelper, LinqHelper } from "@shared/helpers";
//import { ApiResources, DateHelper, LocalStorageHelper, UtilHelper } from "@shared/helpers";
import { IUserAccount, Page, IResource, IUser, IMenuModel, HttpErrorType, ICategoryMenuModel, GenericResponse, GenericStatus, IMenuPatientModel, Pagination } from "@shared/models";
import { AppData, appUrls, HttpService, ResourceService, IdentityService, NotifyService, MenuService, HttpErrorService, SettingService, CommunicationService, JitsiService } from "@shared/services";
import { CompareValidator } from "@shared/validators";
import { Role, MenuType } from "@shared/enums";
import { AuditLog, Notification, ICubicle, Setting } from "@shared/entities";
import { NgSelectComponent } from "@ng-select/ng-select";
//import { DomSanitizer } from "@angular/platform-browser";
const CUBICLE_PAGE_SIZE = 5;
class Settings {
providerId: number;
theme: string = "light";
enableEmailAlerts: boolean;
enableSMSAlerts: boolean;
enableDesktopNotifications: boolean;
enableMobileNotifications: boolean;
}
class MenuHolder {
menuName: string;
menuUrl: string;
mainPage: string;
subMenus: Array<IMenuModel>;
}
//const firebaseConfig = {
// apiKey: "AIzaSyD7BsM-9nNjTtceWLBBcmeB8rF57slhwmc",
// authDomain: "airopd-patient.firebaseapp.com",
// databaseURL: "https://airopd-patient.firebaseio.com",
// projectId: "airopd-patient",
// storageBucket: "airopd-patient.appspot.com",
// messagingSenderId: "604641314293",
// appId: "1:604641314293:web:e58639265aaa9c88f38cbc",
// measurementId: "G-88RPR98C1W",
// vapidKey: "BIPwrSGkeNlw-mUtBlFzlIMgA9N-Rks0Ay25viPjnGmJ03ZywrhtWGYysfpLOPBou8UIZvbg-7AE1rKhPa-9KFY"
//};
@Component({
templateUrl: "./layout.html"
})
export class LayoutComponent implements OnInit, OnDestroy {
@ViewChild("templateModify", { static: true }) templateModify: TemplateRef<any>;
@ViewChild("templateCubicle", { static: false }) templateCubicle: TemplateRef<any>;
today = DateHelper.ngbToday;
minDate = DateHelper.minDate;
page: Page;
role = Role;
menuType = MenuType;
httpErrorType = HttpErrorType;
selectedPatient: IResource;
searching = false;
searchFailed = false;
auditlog: AuditLog;
modalRef: NgbModalRef;
changePasswordForm: FormGroup;
submitting: boolean;
submitted: boolean;
settings: Settings;
userForm: FormGroup;
user: IUser;
userLoading: boolean;
loading: boolean;
webVersion: string;
menus: Array<IMenuModel>;
allMenus: Array<IMenuModel>;
allMenuAndSubMenu: Array<IMenuPatientModel>;
menuHolder: MenuHolder;
isHovering: boolean;
defaultMenu: string;
logCount: number = 0;
isHitMenu: boolean;
env: string;
envTitle: string;
activeRoute: string;
logoSubscription: Subscription;
logoBasics: Setting;//
imageType: string;
showTicket: boolean;
notifications: Array<Notification>;
assignedCubicles: Array<ICubicle>;
unassignedCubicles: Array<ICubicle>;
notificationCount: number;
publicIp: string;
privateIp: string;
cubiclesLoading: boolean;
defaultImgUrl: string = 'assets/images/broken.png';
url: string;
loadingSettings: boolean;
isTheme: boolean;
themeColor: any;
providerId: number;
providersInput = new Subject<string>();
providers: Observable<Array<IResource>>;
loadingProviders: boolean;
isAssigningDoctor: boolean;
currentAssignedCubicles: Array<ICubicle>;
currentUnAssignedCubicles: Array<ICubicle>;
unAssignedCubiclespagination: Pagination;
assignedCubiclespagination: Pagination;
unAssignedCubiclespaginationFlag: boolean = true;
sideSubMenu: any = document.getElementsByClassName("sidebar-main-menu");
constructor(
private readonly identityService: IdentityService,
private readonly appData: AppData,
private readonly router: Router,
private readonly modalService: NgbModal,
private readonly notifyService: NotifyService,
private readonly resourceService: ResourceService,
private readonly httpService: HttpService,
private readonly menuService: MenuService,
private readonly formBuilder: FormBuilder,
public httpErrorService: HttpErrorService,
public settingService: SettingService,
public communicationService: CommunicationService,
public readonly jitsiService: JitsiService,
//private sanitizer: DomSanitizer
) {
this.setUpMenu();
this.page = new Page();
this.auditlog = new AuditLog();
this.settings = new Settings();//
this.logoBasics = new Setting();
this.assignedCubicles = new Array<ICubicle>();
this.unassignedCubicles = new Array<ICubicle>();
this.communicationService.createConnection(() => {
this.communicationService.startConnection("QueueManagement");
});
this.unAssignedCubiclesinitPagination();
this.assignedCubiclesinitPagination();
}
private unAssignedCubiclesinitPagination() {
this.unAssignedCubiclespagination = new Pagination();
this.unAssignedCubiclespagination.pageIndex = 1;
this.unAssignedCubiclespagination.pageSize = 10;
}
private assignedCubiclesinitPagination() {
this.assignedCubiclespagination = new Pagination();
this.assignedCubiclespagination.pageIndex = 1;
this.assignedCubiclespagination.pageSize = 10;
}
safe = (url: any) => {
if (url) {
return `${ApiResources.getURI(ApiResources.resources.base, ApiResources.resources.getProfileImage)}?imagePath=${url}`;
}
return this.defaultImgUrl;
}
private onFetchPublicIp() {
fetch('https://jsonip.com', { mode: 'cors' })
.then((resp) => resp.json())
.then((ip) => {
this.publicIp = ip.ip;
this.jitsiService.ipAddress = ip.ip;
});
}
private onReadPrivateIp() {
// this.privateIp = document.getElementById("ip").innerHTML;
}
onNotificationToggle() {
if (!this.showTicket) {
this.showTicket = true;
this.fetchNotifications();
} else {
this.showTicket = false;
}
}
onNavigate(item: Notification) {
this.showTicket = false;
if (UtilHelper.isEmpty(item.redirectionLink) && UtilHelper.isEmpty(item.referenceId)) {
this.router.navigateByUrl(`${item.redirectionLink}/${item.encryptedReferenceId}`);
}
}
setUpMenu = () => {
this.defaultMenu = this.menuService.getDefaultRoute;
this.menus = this.menuService.menus(MenuType.MainMenu);
this.allMenus = this.menuService.menus().filter(x => x.mainPage);
this.allMenus = this.menuService.menus().filter(x => (x.url || '').toLowerCase().indexOf(':id'.toLowerCase()) === -1 && x.mainPage !== null && (x.menuTypeId !== 4))
this.allMenuAndSubMenu = this.menuService.menus();
this.menuHolder = new MenuHolder();
this.menuHolder.subMenus = new Array<IMenuModel>();
this.setMenuMarkers();
}
@HostListener("window:scroll", ["$event"])
onScroll(event) {
const $toTop = $(".btn-scroll-top");
if ($(event.currentTarget).scrollTop() !== 0) {
$toTop.fadeIn();
} else {
$toTop.fadeOut();
}
}
scrollToTop() {
$("body,html").animate(({ scrollTop: 0 }) as any, 500);
}
onToggleMenu(event: Event) {
event.preventDefault();
const sidebarSize = $("body").attr("data-sidebar-size");
if ($("window").width() >= 993) {
if (sidebarSize === "condensed") {
//self.changeSize(defaultSidebarSize);
}
//else {
// //self.changeSize('condensed');
//}
} else {
//self.changeSize(defaultSidebarSize);
$("body").toggleClass("sidebar-enable");
}
}
sos() {
var t = document.getElementsByTagName("body")[0];
if (t.classList) {
t.classList.remove("sidebar-enable");
}
}
dontLeave() {
var newidebarSize = document.getElementsByTagName("body")[0];
if ($("window").width() >= 993) {
} else {
//self.changeSize(defaultSidebarSize);
newidebarSize.classList.add("sidebar-enable");
}
}
formatMatches = (item: IResource) => item.value || "";
formatMenuMatches = (item: IMenuModel) => item.displayName || "";
search = (text$: Observable<string>) =>
text$.pipe(
debounceTime(100),
distinctUntilChanged(),
tap(() => this.searching = true),
switchMap((term: string) =>
term.length < 2 ? of([]) : this.resourceService
.patients(term)
.pipe(
tap(() => this.searchFailed = false),
catchError(() => {
this.searchFailed = true;
return of([]);
})
)
),
tap(() => this.searching = false)
);
menuSearch = (text$: Observable<string>) =>
text$.pipe(
distinctUntilChanged(),
tap(() => this.searching = true),
switchMap((term: string) => {
return of((this.allMenus.filter(x => (x.mainPage || '').toLowerCase().indexOf(term.toLowerCase()) !== -1 ||
(x.subPage || '').toLowerCase().indexOf(term.toLowerCase()) !== -1 || (x.displayName || '').toLowerCase().indexOf(term.toLowerCase()) !== -1)) || []);
}),
tap(() => this.searching = false)
);
onSelectPatient(event: NgbTypeaheadSelectItemEvent) {
if (event.item && (event.item as IResource).encryptedId) {
this.router.navigate([]).then(() => { location.assign(`${location.pathname}#/app/patient/${event.item.encryptedId}`); });
} else {
event.preventDefault();
}
}
onSelectMenu(event: NgbTypeaheadSelectItemEvent) {
if (event.item && (event.item as IMenuModel).url) {
this.router.navigate([event.item.url]);
} else {
event.preventDefault();
}
}
private buildUserForm() {
this.userForm = this.formBuilder.group({
userId: 0,
firstName: [null, [Validators.required]],
lastName: [null, [Validators.required]],
middleName: [null],
gender: [null],
roleId: [null, [Validators.required]],
dateOfBirth: [null],
email: [null, [Validators.required]],
mobile: [null, [Validators.required]],
countryName: [null, [Validators.required]],
base64ProfileImage: [null],
profileImageUrl: [null],
providerLocationId: [{ value: null, disabled: true }, [Validators.required]],
provider: [{ value: null, disabled: true }, [Validators.required]]
});
}
private updateForm() {
this.userForm.patchValue({
userId: this.user.userId,
firstName: this.user.firstName,
lastName: this.user.lastName,
middleName: this.user.middleName,
email: this.user.email,
mobile: this.user.mobile,
countryName: this.user.countryName,
roleId: this.user.roleId,
providerLocationId: this.user.providerLocationId,
gender: this.user.gender,
dateOfBirth: this.user.dateOfBirth,
profileImageUrl: this.user.profileImageUrl
});
}
get profileForm() { return this.userForm.controls; }
onProfile() {
if (this.page.userAccount.roleId === Role.Provider) {
this.router.navigateByUrl(`app/providers/${this.page.userAccount.encryptedReferenceId}`);
} else {
this.buildUserForm();
this.fetchUsersProfile();
this.modalRef = this.modalService.open(this.templateModify, {
backdrop: "static",
keyboard: false,
centered: true,
size: "lg",
windowClass: "custom-modal effect-scale"
});
}
}
private fetchUsersProfile() {
this.userLoading = true;
const request = {
encryptedUserId: this.page.userAccount.encryptedReferenceId
};
this.httpService.post(ApiResources.getURI(ApiResources.users.base, ApiResources.users.find), request)
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => {
this.userLoading = false;
}))
.subscribe((response: IUser) => {
this.user = response;
this.updateForm();
}, () => {
this.user = null;
this.onCloseModal();
this.notifyService.error("Error Occurred while getting your Profile.");
});
}
onProfileSubmit() {
this.submitted = true;
if (!this.userForm.valid) {
return;
}
this.submitting = true;
const request = Object.assign(UtilHelper.clone(this.userForm.getRawValue()));
request.modifiedBy = this.page.userAccount.accountId;
this.httpService.put(ApiResources.getURI(ApiResources.users.base, ApiResources.users.update), request)
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => {
this.submitting = false;
this.submitted = false;
this.onCloseModal();
}))
.subscribe((response: string) => {
this.notifyService.success(response);
},
() => {
this.notifyService.error("Error occurred while updating profile.");
});
}
onOpenModal(content: TemplateRef<any>) {
this.buildForm();
this.modalRef = this.modalService.open(content, {
backdrop: "static",
keyboard: false,
centered: true,
windowClass: "custom-modal change-password-modal effect-scale"
});
}
onCloseModal() {
try {
this.modalRef.close();
this.modalRef = undefined;
} catch (e) {
// ignored;
}
this.submitted = undefined;
this.submitting = undefined;
this.unAssignedCubiclespaginationFlag = true;
}
onShowSettings() {
$("body").addClass("right-bar-enabled");
}
onCloseSettings() {
$("body").removeClass("right-bar-enabled");
}
onApplySettings() {
this.httpService.post(ApiResources.getURI(ApiResources.providers.base, ApiResources.providers.modifySettings), this.settings)
.pipe(takeUntil(this.page.unSubscribe))
.subscribe(
(response: Settings) => {
this.settings = response;
$("body").removeClass("right-bar-enabled");
},
() => {
this.notifyService.defaultError();
}
);
}
onCancelSettings() {
$("body").removeClass("right-bar-enabled");
}
onSubmit() {
this.submitted = true;
if (!this.changePasswordForm.valid || (this.changePasswordForm.controls.password.errors || this.changePasswordForm.controls.confirmPassword.errors)) {
return;
}
this.submitting = true;
this.httpService
.put<string>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.updatePassword), this.changePasswordForm.value)
.pipe(
finalize(() => {
this.submitted = false;
this.submitting = false;
})
)
.pipe(takeUntil(this.page.unSubscribe))
.subscribe(
(message: string) => {
this.notifyService.success(message);
this.onCloseModal();
this.onLogout();
},
() => {
this.notifyService.defaultError();
}
);
}
private buildForm() {
this.changePasswordForm = this.formBuilder.group({
accountId: [this.page.userAccount.accountId],
password: ["", [Validators.required, Validators.minLength(4)]],
confirmPassword: ["", [Validators.required, Validators.minLength(4)]],
locationId: this.page.userAccount.locationId
}, { validator: CompareValidator.compare("password", "confirmPassword") });
}
get form() { return this.changePasswordForm.controls; }
onLogout(isHardReload: boolean = false) {
this.auditlog.AccountId = this.page.userAccount.accountId;
this.auditlog.roleName = this.page.userAccount.roleName;
this.auditlog.fullName = this.page.userAccount.fullName;
this.auditlog.logDescription = "<b>" + this.page.userAccount.fullName + "</b>" + "(" + this.page.userAccount.roleName + ")" + " has been logout.";
this.auditlog.logTypeName = "Account";
this.auditlog.logTypeId = 8;
this.auditlog.logFrom = this.page.userAccount.roleId;
try {
this.communicationService.stopConnection();
} catch (e) {
console.log(e);
}
this.identityService.signOut()
.pipe(takeUntil(this.page.unSubscribe))
.subscribe(() => {
this.auditlogService(this.auditlog);
//LocalStorageHelper.DeleteAll();
this.appData.setAccount(null);
delete localStorage["ProviderVideoCallingSession"];
this.menuService.removeFromLocal();
if (isHardReload) {
this.router.navigateByUrl(appUrls.login).then(() => {
location.reload();
});
} else {
this.router.navigateByUrl(appUrls.login);
}
});
}
auditlogService(request: AuditLog) {
this.httpService.post(ApiResources.getURI(ApiResources.auditlog.base, ApiResources.auditlog.insert), request)
.pipe(takeUntil(this.page.unSubscribe))
.subscribe((response) => {
console.log(response);
}, () => {
});
}
getWebVersion() {
this.httpService
.get(ApiResources.getURI(ApiResources.application.base, ApiResources.application.version))
.pipe(takeUntil(this.page.unSubscribe))
.subscribe(
(response: any) => {
this.webVersion = response["version"];
},
() => {
}
);
}
ngOnInit() {
this.logoSubscription = this.settingService.get.subscribe((value: boolean) => {
if (value) {
this.getLogoImage();
}
});
this.appData.userAccount
.pipe(takeUntil(this.page.unSubscribe))
.subscribe((userAccount: IUserAccount) => {
if (!userAccount)
return;
this.page.userAccount = userAccount ? userAccount : undefined;
this.onFetchPublicIp();
this.fetchProviders();
// this.onReadPrivateIp();
this.getLogoImage();
this.getSettingsLayoutCommon();
if (this.menuService.isHitMenu) {
this.menuService.fetch(this.page.userAccount.accountId, this.page.userAccount.roleId, () => {
this.setUpMenu();
});
}
this.getWebVersion();
this.onAssignConsultantDoctor({
accountId: this.page.userAccount.accountId,
providerId: this.page.userAccount.roleId == 3 ? this.page.userAccount.referenceId : undefined
} as ICubicle, () => {
this.fetchCubicles(true);
})
// this.requestPermission();
});
this.router.events.pipe(takeUntil(this.page.unSubscribe)).subscribe(event => {
if (event instanceof NavigationEnd) {
this.activeRoute = event.url;
}
});
// let interval = setInterval(() => {
// let appConfigUrl = { ...AppConfig.settings };
// if (UtilHelper.isEmpty(appConfigUrl["hostingUrl"])) {
// this.url = appConfigUrl["hostingUrl"];
// clearInterval(interval);
// }
// }, 10);
}
onProviderChange(event: any) {
if (event) {
this.onAssignConsultantDoctor({
accountId: this.page.userAccount.accountId,
providerId: event.id
} as ICubicle)
}
}
private fetchProviders() {
this.providers = this.providersInput.pipe(
debounceTime(500),
distinctUntilChanged(),
switchMap((term: string) =>
term && term.length >= 2
? this.resourceService.consultatDoctors(term).pipe(
tap(() => this.loadingProviders = true),
catchError(() => { return of([]) }),
finalize(() => this.loadingProviders = false)
)
: of([])
)
);
}
openCubicleModal() {
this.modalRef = this.modalService.open(this.templateCubicle, {
backdrop: "static",
keyboard: false,
centered: true,
size: "lg",
windowClass: "custom-modal effect-scale"
});
}
fetchCubicles = (isBypass: boolean = false) => {
if (!isBypass) {
this.openCubicleModal();
}
setTimeout(() => {
this.cubiclesLoading = true
const request = {
pageIndex: 1,
pageSize: 100,
locationId: this.page.userAccount.locationId
};
this.httpService.post(ApiResources.getURI(ApiResources.cubicles.base, ApiResources.cubicles.fetch), request)
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => this.cubiclesLoading = false))
.subscribe(
(response: GenericResponse) => {
if (response.status === GenericStatus[GenericStatus.Success]) {
this.refineCubicles(response.data);
}
},
);
})
}
refineCubicles = (cubiclesData: any) => {
const records = (cubiclesData as Array<ICubicle>).map(x => ({ ...x, providerName: x.providerName ? x.providerName.toLowerCase() : null }));
this.assignedCubicles = records.filter(x => x.providerId);
this.currentAssignedCubicles = records.filter(x => x.providerId).slice(0, CUBICLE_PAGE_SIZE);
if (this.currentAssignedCubicles.length > 0) {
UtilHelper.applyPagination(this.currentAssignedCubicles, this.assignedCubiclespagination);
}
this.unassignedCubicles = records.filter(x => !x.providerId);
this.currentUnAssignedCubicles = records.filter(x => !x.providerId).slice(0, CUBICLE_PAGE_SIZE);
if (this.currentUnAssignedCubicles.length > 0) {
UtilHelper.applyPagination(this.currentUnAssignedCubicles, this.unAssignedCubiclespagination);
}
this.settingService.cubicles = this.assignedCubicles.filter(x => x.accountId == this.page.userAccount.accountId);
}
onAssignedPageChange = (index: number) => {
const startIndex = index * CUBICLE_PAGE_SIZE;
this.currentAssignedCubicles = LinqHelper.cloneDeepArray(this.assignedCubicles.slice(startIndex, startIndex + CUBICLE_PAGE_SIZE));
}
onUnAssignedPageChange = (index: number) => {
const startIndex = index * CUBICLE_PAGE_SIZE;
this.currentUnAssignedCubicles = LinqHelper.cloneDeepArray(this.unassignedCubicles.slice(startIndex, startIndex + CUBICLE_PAGE_SIZE))
}
onUnAssignedCubicleSearch = (id: any) => {
if (id) {
this.unAssignedCubiclespaginationFlag = false;
this.currentUnAssignedCubicles = LinqHelper.cloneDeepArray(this.unassignedCubicles.filter(x => x.cubicleId === id.cubicleId));
}
if (id === undefined) {
this.unAssignedCubiclespaginationFlag = true;
this.currentUnAssignedCubicles = this.unassignedCubicles.filter(x => !x.providerId).slice(0, CUBICLE_PAGE_SIZE);
}
}
onAssignConsultantDoctor = (item: ICubicle, callback?: Function) => {
this.isAssigningDoctor = true;
const request = {
accountId: item.accountId,
providerId: item.providerId
}
this.httpService.post(ApiResources.getURI(ApiResources.cubicles.base, ApiResources.cubicles.assignConsultantDoctor), request)
.pipe(takeUntil(this.page.unSubscribe))
.subscribe(
(response: GenericResponse) => {
this.isAssigningDoctor = false;
if (response.status === GenericStatus[GenericStatus.Success]) {
this.settingService.selectedConsultantDoctor = response.data;
if (this.settingService.selectedConsultantDoctor && this.settingService.selectedConsultantDoctor.value) {
this.settingService.selectedConsultantDoctor.value = this.settingService.selectedConsultantDoctor.value.toLowerCase();
}
if (callback) {
callback();
}
}
},
);
}
clearConsultantDoctor = (component: NgSelectComponent) => {
this.isAssigningDoctor = true;
const request = {
accountId: this.page.userAccount.accountId
}
this.httpService.post(ApiResources.getURI(ApiResources.cubicles.base, ApiResources.cubicles.unassignConsultantDoctor), request)
.pipe(takeUntil(this.page.unSubscribe))
.subscribe(
(response: GenericResponse) => {
this.isAssigningDoctor = false;
if (response.status === GenericStatus[GenericStatus.Success]) {
component && component.clearModel();
this.settingService.selectedConsultantDoctor = null;
}
},
);
}
onAssign = (item: ICubicle) => {
if (item.loading) return;
if (!this.settingService.selectedConsultantDoctor && this.page.userAccount.roleId !== 3) {
this.notifyService.warning("Please select Consultant doctor.");
return;
}
if (this.settingService.cubicles.length) {
this.notifyService.warning("Only 1 room can be assigned");
return;
}
item.loading = true;
const request = {
cubicleId: item.cubicleId,
accountId: this.page.userAccount.accountId,
}
this.httpService.post(ApiResources.getURI(ApiResources.cubicles.base, ApiResources.cubicles.assign), request)
.pipe(takeUntil(this.page.unSubscribe))
.subscribe(
(response: GenericResponse) => {
item.loading = false;
if (response.status === GenericStatus[GenericStatus.Success]) {
item.providerId = this.settingService.selectedConsultantDoctor.id;
item.accountId = this.page.userAccount.accountId;
item.assignedName = this.page.userAccount.fullName.toLowerCase();
item.providerName = this.settingService.selectedConsultantDoctor.value.toLowerCase();
this.refineCubicles([...this.assignedCubicles, ...this.unassignedCubicles])
}
if (response.status === GenericStatus[GenericStatus.Info]) {
this.notifyService.warning("Please select consultant doctor");
}
if (response.status === GenericStatus[GenericStatus.Warning]) {
this.notifyService.warning("Room already assigned, Only 1 room can be assigned");
}
},
);
}
onUnassign = (item: ICubicle) => {
if (item.loading) return;
item.loading = true;
const request = {
cubicleId: item.cubicleId,
providerId: null,
accountId: item.accountId
}
this.httpService.post(ApiResources.getURI(ApiResources.cubicles.base, ApiResources.cubicles.unassign), request)
.pipe(takeUntil(this.page.unSubscribe))
.subscribe(
(response: GenericResponse) => {
item.loading = false;
if (response.status === GenericStatus[GenericStatus.Success]) {
item.providerId = null;
item.accountId = null;
item.assignedName = null;
this.refineCubicles([...this.assignedCubicles, ...this.unassignedCubicles])
}
},
);
}
fetchNotifications() {
const request = {
pageSize: 5,
pageIndex: 1
};
this.loading = true;
this.httpService.post<Array<Notification>>(ApiResources.getURI(ApiResources.webNotification.base, ApiResources.webNotification.fetch), request)
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => this.loading = false))
.subscribe((response: Array<Notification>) => {
//response.forEach(x => {
// x["newThumbnailUrl"] = x.patientThumbnailUrl ? this.safe(x.patientThumbnailUrl) : null;
//})
this.notifications = response;
this.notificationCount = this.notifications.length > 0 ? this.notifications[0].totalItems : 0;
}, () => {
this.notifications = new Array<Notification>();
});
}
getLogoImage = () => {
this.loading = true;
this.httpService
.get<Array<Setting>>(ApiResources.getURI(ApiResources.setting.base, ApiResources.setting.fetch), { type: "Logo", active: true })
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => { this.loading = false }))
.subscribe(
(response: Array<Setting>) => {
if (response && response.length > 0) {
this.logoBasics = response[0];
if (UtilHelper.isEmpty(response[0].imageUrl)) {
response[0].imageUrl = `${ApiResources.getURI(ApiResources.resources.base, ApiResources.resources.getProfileImage)}?imagePath=${response[0].imageUrl}`;
}
}
},
() => {
this.logoBasics = new Setting();
}
);
}
ngOnDestroy() {
try {
this.modalService.dismissAll();
} catch (e) {
// ignored;
}
this.showTicket = false;
this.onCloseSettings();
this.page.unSubscribe.next(null);
this.page.unSubscribe.complete();
}
setMenuMarkers = () => {
this.menus.forEach(m => {
m.hasMarker = this.menuService.menus(MenuType.SubMenu, m.mainPage).length > 0;
})
}
enter = (menu: IMenuModel) => {
this.showTicket = false;
this.menus.forEach(x => { x.isHovering = false; });
if (menu.hasMarker) {
menu.isHovering = true;
}
this.isHovering = true;
this.menuHolder.menuName = menu.displayName + " Navigation";
this.menuHolder.mainPage = menu.mainPage;
this.menuHolder.menuUrl = menu.url;
var allSubMenus = this.menuService.menus(MenuType.SubMenu, menu.mainPage);
var onlySubMenus = allSubMenus.filter(x => x.menuTypeId === MenuType.SubMenu ||
(x.menuTypeId === MenuType.CategoryMenu && x.category && x.category === x.displayName));
onlySubMenus.filter(x => x.menuTypeId === MenuType.CategoryMenu).forEach(x => {
x.categoryMenus = allSubMenus.filter(y => y.menuTypeId === MenuType.CategoryMenu && y.category === x.category && y.displayName !== x.displayName)
.map((y, i) => ({
id: i,
subPage: y.subPage,
displayName: y.displayName,
iconClasses: y.iconClasses,
url: y.url,
priority: y.priority
} as ICategoryMenuModel));
});
this.menuHolder.subMenus = onlySubMenus;
setTimeout(() => {
var v = document.getElementById("submenu");
if (v && v.classList) {
v.classList.add("d-block");
}
}, 100);
}
leaveMenu = () => {
if (!this.menuHolder.subMenus.length) {
this.isHovering = false;
this.menus.forEach(x => { x.isHovering = false; });
}
}
leave = () => {
this.isHovering = false;
this.menuHolder.subMenus = new Array<IMenuModel>();
this.menus.forEach(x => { x.isHovering = false; });
}
private getSettingsLayoutCommon() {
this.loadingSettings = true;
this.httpService.get<Array<Setting>>(ApiResources.getURI(ApiResources.setting.base, ApiResources.setting.fetch), { name: "Theme Color" })
.pipe(takeUntil(this.page.unSubscribe))
.pipe(finalize(() => this.loadingSettings = false))
.subscribe((response: Array<Setting>) => {
if (response.length > 0) {
this.isTheme = response[0].active;
this.themeColor = response[0].value;
}
this.onToggleTheme(this.themeColor);
}, () => {
this.onToggleTheme(this.themeColor);
});
}
onToggleTheme(color: string) {
switch (color) {
case "#827d77 #fec32b #f5f6f9 #f86262":
this.yellowTheme();
break;
default:
this.blueTheme();
}
}
//private getSettingsLayoutYellow() {
// debugger;
// this.loadingSettings = true;
// this.httpService.get<Array<Setting>>(ApiResources.getURI(ApiResources.setting.base, ApiResources.setting.fetch), { name: "Yellow Theme" })
// .pipe(takeUntil(this.page.unSubscribe))
// .pipe(finalize(() => this.loadingSettings = false))
// .subscribe((response: Array<Setting>) => {
// debugger;
// if (response.length > 0) {
// this.isYellowTheme = response[0].active;
// console.log("from yellow" + this.isYellowTheme);
// if (this.isYellowTheme) {
// this.yellowTheme();
// }
// }
// });
//}
yellowTheme() {
var head = document.getElementsByTagName('head')[0];
var fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
var fileName = location.origin + location.pathname + "assets/css/yellow-theme.css";
fileref.setAttribute("href", fileName);
fileref.media = 'all';
head.appendChild(fileref);
}
blueTheme() {
var head = document.getElementsByTagName('head')[0];
var fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
var fileName = location.origin + location.pathname + "assets/css/blue-theme.css";
fileref.setAttribute("href", fileName);
fileref.media = 'all';
head.appendChild(fileref);
}
onToggleSubMenu(event: Event, threeDot: HTMLElement) {
event.preventDefault();
const sideSubMenu = document.getElementsByClassName("sidebar-main-menu")[0].classList;
if ($("window").width() >= 99) {
} else {
if (threeDot.classList.contains("d-none")) {
threeDot.classList.toggle("text-success");
}
threeDot.classList.toggle("text-danger");
sideSubMenu.toggle("addedLeft");
}
}
}
\ No newline at end of file
<style>
.popOverCustom {
height: 500px !important;
width: 600px !important;
}
</style>
<div id="wrapper">
<div class="navbar-custom" (mouseenter)="leave()">
<div class="container-fluid" style="line-height: 70px">
<ul class="list-unstyled topnav-menu m-0">
<li class="d-lg-none">
<button
class="button-menu-mobile waves-effect waves-light"
(click)="onToggleMenu($event)"
>
<i class="fe-menu"></i>
</button>
</li>
</ul>
<div
style="line-height: 16px; top: 50px; left: 12px"
class="position-absolute d-lg-none"
[ngClass]="{ 'd-none': sideSubMenu.length ==0 }"
>
<a
aria-expanded="true"
class="text-success"
aria-haspopup="true"
data-toggle="dropdown"
href="javascript:;"
id="menuThreeDot"
#threedot
(click)="onToggleSubMenu($event,threedot)"
title="Actions"
><i class="mdi mdi-dots-horizontal mdi-36px"></i
></a>
</div>
<ul class="list-unstyled topnav-menu float-left mb-0">
<li class="dropdown d-none d-lg-inline-block">
<div class="app-search app-search-custom">
<div class="app-search-box">
<div class="input-group menu-search-p">
<input
type="text"
class="form-control menu-search"
block
autocomplete="off"
[ngbTypeahead]="menuSearch"
[resultFormatter]="formatMenuMatches"
[resultTemplate]="tempalteMenuSearchResults"
[inputFormatter]="formatMenuMatches"
(selectItem)="onSelectMenu($event)"
placeholder="Search Pages"
/>
<div class="input-group-append">
<a
class="btn bg-primary ml-0"
title="Patients"
[routerLink]="['/app/patient-services']"
>
<i class="fe-file-text"></i>
</a>
</div>
</div>
</div>
</div>
</li>
<li></li>
<li
class="dropdown d-none d-md-inline-block header-info"
*ngIf="settingService.cubicles.length"
>
<ng-container *ngIf="settingService.cubicles.length">
<ng-container *ngFor="let item of settingService.cubicles">
<span class="mr-2 ml-2 header-info-div">
<i class="fe-box mr-2"></i>
<span class="text-capitalize" [textContent]="item.name"></span>
</span>
</ng-container>
</ng-container>
</li>
<li
class="dropdown d-none d-md-inline-block header-info"
*ngIf="settingService.selectedConsultantDoctor && page.userAccount.roleId !== 3"
>
<ng-container *ngIf="settingService.cubicles.length">
<ng-container *ngFor="let item of settingService.cubicles">
<span class="header-info-div">
<i class="fe-user mr-2"></i>
<span
class="text-capitalize"
[textContent]="settingService.selectedConsultantDoctor.value"
></span>
</span>
</ng-container>
</ng-container>
</li>
</ul>
<ul class="list-unstyled topnav-menu float-right mb-0">
<!--<li class="dropdown d-none d-lg-inline-block" *ngIf="publicIp">
<span class="mr-2">
Public:<span class="ml-1" [textContent]="publicIp"></span>
</span>
</li>
<li class="dropdown d-none d-lg-inline-block" *ngIf="privateIp">
<span class="mr-2">
Private:<span class="ml-1" [textContent]="privateIp"></span>
</span>
</li>-->
<li
class="dropdown d-none d-md-inline-block"
*ngIf="page && page.userAccount && page.userAccount.locationName"
>
<span class="mr-2">
<i
class="mdi mdi-checkbox-blank-circle text-danger"
*ngIf="communicationService && communicationService.hubConnection && communicationService.hubConnection.state && communicationService.hubConnection.state !== 'Connected'"
></i>
<i
class="mdi mdi-checkbox-blank-circle text-success"
*ngIf="communicationService && communicationService.hubConnection && communicationService.hubConnection.state && communicationService.hubConnection.state === 'Connected'"
></i>
<!-- <span
class="ml-2"
*ngIf="communicationService && communicationService.hubConnection && communicationService.hubConnection.state"
[textContent]="communicationService.hubConnection.state"
></span> -->
</span>
</li>
<li
class="dropdown d-none d-md-inline-block"
*ngIf="page && page.userAccount && page.userAccount.locationName"
>
<span class="mr-2">
<i class="fe-map-pin mr-2"></i>
<span [textContent]="page.userAccount.locationName"></span>
</span>
</li>
<li class="dropdown">
<div class="app-search">
<div class="app-search-box">
<div class="input-group patient-search">
<input
type="text"
class="form-control"
block
#searchPatient
autocomplete="off"
[ngbTypeahead]="search"
[resultFormatter]="formatMatches"
[resultTemplate]="tempalteSearchResults"
[inputFormatter]="formatMatches"
(focus)="searchPatient.value = ''"
(selectItem)="onSelectPatient($event)"
placeholder="Enter Name/Mobile/UMR No"
/>
<div class="input-group-append">
<button
*ngIf="searching"
class="btn bg-primary"
type="button"
style="cursor: default !important"
>
<span
class="spinner-border text-white spinner-border-sm"
></span>
</button>
<a
*ngIf="!searching"
class="btn bg-primary ml-0"
title="Patients"
[routerLink]="['/app/patients']"
>
<i class="fe-users"></i>
</a>
</div>
</div>
</div>
</div>
</li>
<li class="dropdown d-none d-lg-inline-block">
<full-screen></full-screen>
</li>
<!--<li class="dropdown notification-list">
<a class="nav-link dropdown-toggle waves-effect waves-light" (click)="onNotificationToggle()" href="javascript:;" role="button" aria-haspopup="false" aria-expanded="false">
<i class="fe-bell noti-icon"></i>
<span class="badge badge-danger rounded-circle noti-icon-badge" [textContent]="notificationCount > 99 ? '99+' : notificationCount"></span>
</a>
<div class="dropdown-menu dropdown-menu-right dropdown-lg" [ngClass]="{'show':showTicket}">
<div class="dropdown-item noti-title">
<h5 class="m-0">
Notifications
</h5>
</div>
<div class="noti-scroll">
<div class="dropdown-item notify-item" style="line-height:initial;" *ngIf="!loading && notifications && notifications.length === 0">
<div class="notify-icon">
<div class="avatar-sm rounded-circle bg-info text-hover">
<i class="fe-info font-33 avatar-title text-white"></i>
</div>
</div>
<p class="notify-details">Information</p>
<p class="text-muted mb-0 user-msg">There is nothing to show you right now.</p>
</div>
<div class="dropdown-item notify-item active" style="line-height:initial;" *ngIf="loading">
<div class="notify-icon">
<div class="avatar-sm rounded-circle bg-info text-hover">
<span style="vertical-align:middle !important" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
</div>
</div>
<p class="notify-details">Information</p>
<p class="text-muted mb-0 user-msg">Please wait while loading..</p>
</div>
<div class="dropdown-item" *ngFor="let notification of notifications;index as i;" (click)="onNavigate(notification)">
<a href="javascript:void(0);" class="dropdown-item notify-item active" style="line-height:initial; padding-top:8px; padding-bottom:8px;">
<div class="notify-icon m-1">
<img *ngIf="notification.patientThumbnailUrl" [src]="safe(notification.patientThumbnailUrl)" [alt]="notification.patientName" class="img-fluid rounded-circle">
<span *ngIf="!notification.patientThumbnailUrl" class="avatar-title rounded-circle bg-info font-24 text-white"
[textContent]="notification.patientName | initials"></span>
</div>
<span class="notify-details m-0" [textContent]="notification.patientName"></span>
<span *ngIf="notification.patientAge"><span>(<span [textContent]="notification.patientAge"></span>yrs)</span></span>
<p class="text-muted mb-0 user-msg">
<small>
<span class="badge bg-primary text-white font-weight-lighter" style="padding:3px;" [textContent]="notification.moduleType"></span>
<span class="d-block font-weight-bold"> {{notification.message}}</span>
</small>
</p>
</a>
</div>
</div>
<a [routerLink]="['/app/web-notification']" (click)="showTicket = false" style="line-height:initial;" class="dropdown-item text-center text-primary notify-item notify-all pb-1">
View all
<i class="fe-arrow-right"></i>
</a>
</div>
</li>-->
<li
class="dropdown notification-list"
*ngIf="page && page.userAccount && page.userAccount.accountId > 0"
>
<a
class="nav-link dropdown-toggle nav-user mr-0 waves-effect waves-light"
(click)="onLogout()"
data-toggle="dropdown"
href="javascript:;"
role="button"
aria-haspopup="false"
aria-expanded="false"
>
<avatar-img
*ngIf="!page.userAccount.thumbnailUrl"
src="assets/images/doctor_male.jpg"
[alt]="page.userAccount.fullName"
cssClass="rounded-circle myicon"
></avatar-img>
<avatar-img
*ngIf="page.userAccount.thumbnailUrl"
[src]="page.userAccount.thumbnailUrl"
[alt]="page.userAccount.fullName"
cssClass="rounded-circle myicon"
></avatar-img>
<span class="pro-user-name ml-1">
<span [textContent]="page.userAccount.fullName"></span
><i class="mdi mdi-chevron-down ml-2 mr-1"></i>
</span>
</a>
<div
class="dropdown-menu dropdown-menu-right profile-dropdown"
style="
position: absolute;
will-change: transform;
top: 0px;
left: 0px;
transform: translate3d(24px, 65px, 0px);
line-height: 20px;
"
>
<div class="dropdown-header noti-title">
<h6 class="m-0">
Welcome <span>{{page.userAccount.roleName}}</span>!
</h6>
</div>
<a
routerLinkActive="active"
href="javascript:;"
class="dropdown-item notify-item"
(click)="onProfile()"
>
<i class="fe-user"></i>
<span>Profile</span>
</a>
<a
href="javascript:;"
class="dropdown-item notify-item"
(click)="fetchCubicles()"
>
<i class="fe-box"></i>
<span>Rooms</span>
</a>
<a
href="javascript:;"
class="dropdown-item notify-item"
(click)="onOpenModal(templateChangePassword)"
>
<i class="fe-lock"></i>
<span>Change Password</span>
</a>
<div class="dropdown-divider"></div>
<a
href="javascript:;"
class="dropdown-item notify-item"
(click)="onLogout()"
>
<i class="fe-log-out"></i>
<span>Logout</span>
</a>
</div>
</li>
</ul>
<!--<div class="logo-box">
<a href="javascript:;" class="logo logo-dark text-center">
<span class="logo-sm">
<img src="assets/images/icon.png" alt="Careaxes" height="22">
</span>
<span class="logo-lg">
<img src="assets/images/logo.png" alt="Careaxes" height="25">
</span>
</a>
</div>-->
<div class="clearfix"></div>
</div>
</div>
<div
class="left-side-menu"
onmouseleave="sos()"
[ngClass]="{ 'w-345': menuHolder.subMenus.length }"
>
<div class="h-100">
<div>
<div class="sidebar-icon-menu h-100">
<a
href="javascript:;"
[routerLink]="defaultMenu"
class="logo position-fixed"
>
<span>
<img
[src]="logoBasics && logoBasics.imageUrl ? logoBasics.imageUrl : 'assets/images/logo-careaxes.png'"
[hidden]="loading"
alt="Careaxes"
width="35"
/>
</span>
</a>
<nav
class="nav flex-column"
(mouseleave)="leaveMenu()"
[ngClass]="{'layout-main-menu-active' : !isHovering, 'layout-main-menu-inactive': isHovering}"
>
<div
(mouseenter)="enter(menu)"
routerLinkActive="menu-active"
class="pt-1 pb-1 menu-border"
[ngClass]="{'inactive-highlight' : isHovering && menuHolder.mainPage === menu.mainPage}"
*ngFor="let menu of menus; let i = index"
>
<a
href="javascript:;"
class="nav-link m-auto position-relative"
[routerLink]="menu.url"
>
<span *ngIf="menu.hasMarker" class="menu-marker">
<i
class="mdi"
[ngClass]="{'mdi-menu-right': !menu.isHovering, 'mdi-menu-down': menu.isHovering}"
></i>
</span>
<div><i [class]="menu.iconClasses"></i></div>
<div
class="text-white white-space-break very-small text-center text-capitalize"
[textContent]="menu.displayName"
></div>
</a>
</div>
</nav>
</div>
<div class="clearfix"></div>
</div>
</div>
<ng-container *ngIf="menuHolder.subMenus.length">
<div
(mouseenter)="dontLeave()"
id="submenu"
(mouseleave)="leave();sos();"
class="layout-sidebar-main-menu d-block mt-1 saideNavMt"
style="
background-size: 200%;
background-repeat: no-repeat;
z-index: 10000;
border-radius: 0 10px 10px 0;
"
[ngClass]="menuHolder.subMenus.length ? 'fade-in':'fade-out'"
>
<div id="two-col-menu" class="h-100">
<div class="twocolumn-menu-item d-block h-100">
<h5 class="menu-title text-white text-truncate">
<i class="mdi mdi-apps mdi-infor text-white font-18 mr-1"></i
><span [textContent]="menuHolder.menuName"></span>
</h5>
<div class="overflow-auto" style="height: calc(100% - 52px)">
<ul class="nav flex-column encounter-menu pb-5">
<ng-container *ngFor="let menu of menuHolder.subMenus">
<ng-container *ngIf="menuType.SubMenu === menu.menuTypeId">
<li class="nav-item w-100">
<a
class="text-color text-truncate dropdown-item position-relative"
(click)="leave();sos();"
[routerLink]="menu.url"
[class.active]="activeRoute === menu.url"
href="javascript:;"
>
<i
*ngIf="menu.iconClasses"
[class]="'mr-1 ' + menu.iconClasses"
></i>
<span
class="text-capitalize"
[textContent]="menu.displayName"
></span>
</a>
</li>
</ng-container>
<ng-container
*ngIf="menuType.CategoryMenu === menu.menuTypeId"
>
<li class="nav-item category-menu w-100">
<a
[href]="'#sidebarMultilevel' + '_' + menu.id"
data-toggle="collapse"
class="text-color dropdown-item text-truncate position-relative"
aria-expanded="true"
>
<i [class]="'mr-1 ' + menu.iconClasses"></i>
<span [textContent]="menu.displayName"></span>
<span class="menu-arrow"></span>
</a>
<div
class="collapse show"
[id]="'sidebarMultilevel' + '_' + menu.id"
>
<ul class="nav-second-level1">
<li
class="nav-item"
*ngFor="let item of menu.categoryMenus"
>
<a
class="text-color dropdown-item"
(click)="leave();sos();"
[routerLink]="item.url"
[class.active]="activeRoute === item.url"
href="javascript:;"
>
<i [class]="'mr-1 ' + item.iconClasses"></i>
<span
class="text-capitalize"
[textContent]="item.displayName"
></span>
</a>
</li>
</ul>
</div>
</li>
</ng-container>
</ng-container>
</ul>
</div>
</div>
</div>
</div>
</ng-container>
</div>
<div class="content-page" id="content-page">
<!-- <telemedicine-widget *ngIf="page.userAccount && page.userAccount.allowVideoCall"></telemedicine-widget> -->
<session-timeout></session-timeout>
<idle-timeout></idle-timeout>
<router-outlet></router-outlet>
<button
class="btn btn-secondary btn-scroll-top btn-sm"
style="z-index: 1000000"
(click)="scrollToTop()"
>
<i class="fe-arrow-up"></i>
</button>
<footer class="footer">
<div class="container-fluid">
<div class="d-flex heading1 justify-content-between p-1">
<div>
<script>
document.write(new Date().getFullYear());
</script>
&copy; Careaxes by
<a target="_blank" href="https://sujainfo.com/">Suja Info</a>
</div>
<div>
<span
*ngIf="webVersion"
[textContent]="'version : '+webVersion"
></span>
<span
ngbPopover="{{url}}"
triggers="mouseenter:mouseleave"
popoverTitle="Pointing URL"
class="ml-1"
>
<i class="mdi mdi-information"></i>
</span>
</div>
<div
*ngIf="page && page.userAccount && page.userAccount.lastLoginDate"
>
<!--<div class="text-md-right d-none d-sm-block">
Last Logged In <a href="javascript:;" [textContent]="page.userAccount.lastLoginDate | utcToLocal"></a>
</div>-->
<div class="text-md-right d-sm-block">
Last Logged In
<a
href="javascript:;"
[textContent]="page.userAccount.lastLoginDate | date:'dd MMMM y , h:mm a'"
></a>
</div>
</div>
</div>
</div>
</footer>
</div>
</div>
<div id="overlay" class="rightbar-overlay"></div>
<ng-template #templateChangePassword>
<form [formGroup]="changePasswordForm" (ngSubmit)="onSubmit()">
<div class="modal-header">
<h4 class="modal-title"><i class="fe-lock mr-1"></i>Change Password</h4>
<button
type="button"
class="close"
data-dismiss="modal"
aria-hidden="true"
(click)="onCloseModal();"
>
×
</button>
</div>
<div class="modal-body">
<div
class="alert alert-light bg-light text-dark font-13 border-0"
role="alert"
>
<span>Password must contain minimum of 4 characters.</span>
</div>
<div class="form-group mb-3">
<label class="mb-1">New Password</label>
<input
type="password"
formControlName="password"
block
autocomplete="nope"
class="form-control"
[ngClass]="{ 'is-invalid': submitted && form.password.errors }"
placeholder="Your new password"
/>
<div *ngIf="submitted && form.password.errors" class="invalid-feedback">
<div *ngIf="form.password.errors.minLength">Invalid password</div>
</div>
</div>
<div class="form-group">
<label class="mb-1">Re-enter Password</label>
<div class="input-group mb-0">
<input
type="password"
formControlName="confirmPassword"
block
autocomplete="nope"
class="form-control"
[ngClass]="{ 'is-invalid': submitted && form.confirmPassword.errors }"
placeholder="Your new password again"
/>
<div class="input-group-append cursor-pointer" password>
<div class="input-group-text">
<span class="password-eye"></span>
</div>
</div>
</div>
<div
*ngIf="submitted && form.confirmPassword.errors"
class="invalid-feedback show-must"
>
<div
*ngIf="form.confirmPassword.errors.minLength || form.confirmPassword.errors.notEquivalent"
>
Passwords are not matched
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-sm btn-light mr-1"
(click)="onCloseModal();"
>
Cancel
</button>
<button
type="submit"
[disabled]="submitting"
class="btn btn-sm btn-primary"
>
<span *ngIf="submitting">
<span
class="spinner-border spinner-border-sm mr-1"
role="status"
aria-hidden="true"
></span>
Please wait..
</span>
<span *ngIf="!submitting">Submit</span>
</button>
</div>
</form>
</ng-template>
<ng-template
#tempalteSearchResults
let-item="result"
let-term="term"
let-i="index"
>
<div class="media" style="line-height: normal !important">
<div class="avatar-xs mr-2">
<!--<avatar-img *ngIf="item.optionalText" [src]="item.optionalText" [alt]="item.value" cssClass="img-fluid rounded-circle"></avatar-img>-->
<!--<span *ngIf="!item.optionalText" class="avatar-title rounded-circle font-10 bg-soft-warning font-weight-bold text-warning" [textContent]="item.value | initials"></span>-->
<span
class="avatar-title rounded-circle font-10 bg-soft-primary font-weight-bold text-primary"
[textContent]="item.value | initials"
></span>
</div>
<div class="media-body">
<ngb-highlight
highlightClass="text-primary font-weight-bold"
[result]="item.value"
[term]="term"
></ngb-highlight
><br />
<ngb-highlight
highlightClass="text-primary font-weight-bold"
[result]="item.optionalText2 + ' / ' + item.optionalText1"
[term]="term"
></ngb-highlight>
</div>
</div>
</ng-template>
<ng-template
#tempalteMenuSearchResults
let-item="result"
let-term="term"
let-i="index"
>
<div class="media" style="line-height: normal !important">
<div class="avatar-xs mr-2">
<span
class="avatar-title rounded-circle font-15 bg-soft-primary font-weight-bold text-primary"
>
<i *ngIf="item.iconClasses" [class]="item.iconClasses"></i>
</span>
</div>
<div class="media-body">
<ngb-highlight
highlightClass="text-primary font-weight-bold"
[result]="item.subPage ? (item.mainPage + ' / ' + item.subPage) : item.mainPage"
[term]="term"
></ngb-highlight
><br />
<ngb-highlight
highlightClass="text-primary font-weight-bold"
[result]="item.displayName"
[term]="term"
></ngb-highlight>
</div>
</div>
</ng-template>
<ng-template #templateModify>
<div class="modal-header">
<h4 class="modal-title">
<i class="mdi mdi-account-box-outline mr-1"></i>User Profile
</h4>
<button
type="button"
class="close"
data-dismiss="modal"
aria-hidden="true"
(click)="onCloseModal();"
>
×
</button>
</div>
<form [formGroup]="userForm" (ngSubmit)="onProfileSubmit()">
<div class="modal-body">
<div *ngIf="userLoading">
<span
class="spinner-border spinner-border-sm mr-1"
role="status"
aria-hidden="true"
></span>
Please wait loading while we load user profile.
</div>
<div class="row" *ngIf="!userLoading">
<div class="col-md-12">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>First Name <code>*</code></label>
<input
type="text"
class="form-control"
formControlName="firstName"
placeholder="Enter first name"
[ngClass]="{ 'is-invalid': submitted && profileForm.firstName.invalid }"
/>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Last Name <code>*</code></label>
<input
type="text"
class="form-control"
formControlName="lastName"
placeholder="Enter last name"
[ngClass]="{ 'is-invalid': submitted && profileForm.lastName.invalid }"
/>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="mb-1">Role <code>*</code></label>
<select
class="form-control custom-select"
formControlName="roleId"
[ngClass]="{ 'is-invalid': submitted && profileForm.roleId.errors }"
>
<option [ngValue]="null">Select</option>
<option [ngValue]="2">Administrator</option>
<option [ngValue]="5">Support</option>
<option [ngValue]="6">Nurse</option>
<option [ngValue]="7">Receptionist</option>
<option [ngValue]="8">Accountant</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="mb-1">Email address <code>*</code></label>
<input
type="text"
class="form-control"
readonly
formControlName="email"
maxlength="150"
block
autocomplete="nope"
[ngClass]="{ 'is-invalid': submitted && profileForm.email.errors }"
placeholder="Enter email address"
/>
</div>
</div>
</div>
<div class="row mb-1">
<div class="col-md-6">
<div class="form-group mb-0">
<label class="mb-1">Mobile number <code>*</code></label>
<input
type="text"
numbersOnly
readonly
class="form-control"
formControlName="mobile"
maxlength="10"
block
autocomplete="nope"
[ngClass]="{ 'is-invalid': submitted && profileForm.mobile.errors }"
placeholder="Enter mobile number"
/>
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-0">
<label class="mb-1">Country <code>*</code></label>
<input
type="text"
readonly
formControlName="countryName"
class="form-control"
/>
</div>
</div>
</div>
<div class="row mb-1">
<div class="col-md-6">
<div class="form-group mb-0">
<label class="mb-1">Gender</label>
<select
class="form-control custom-select"
formControlName="gender"
>
<option [ngValue]="null">Select</option>
<option [ngValue]="'M'">Male</option>
<option [ngValue]="'F'">Female</option>
<option [ngValue]="'O'">Others</option>
</select>
</div>
</div>
<div class="col-md-6 mb-0">
<div class="form-group position-relative">
<label class="mb-1">Date Of Birth</label>
<input
class="form-control date-picker"
type="text"
placeholder="Select"
formControlName="dateOfBirth"
readonly
autocomplete="nope"
ngbDatepicker
#dateOfBirth="ngbDatepicker"
[maxDate]="today"
[minDate]="minDate"
(click)="dateOfBirth.toggle(); $event.stopPropagation();"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-light btn-sm mr-1"
(click)="onCloseModal();"
>
Cancel
</button>
<button
type="submit"
[disabled]="submitting"
class="btn btn-primary btn-sm"
>
<span *ngIf="submitting">
<span
class="spinner-border spinner-border-sm mr-1"
role="status"
aria-hidden="true"
></span>
Please wait..
</span>
<span *ngIf="!submitting">Submit</span>
</button>
</div>
</form>
</ng-template>
<ng-template #templateCubicle>
<div class="modal-header">
<h4 class="modal-title"><i class="fe-box mr-1"></i>Select Room</h4>
<button
type="button"
class="close"
data-dismiss="modal"
aria-hidden="true"
(click)="onCloseModal();"
>
×
</button>
</div>
<div class="modal-body">
<div
*ngIf="cubiclesLoading"
class="d-flex justify-content-center align-items-center"
>
<span class="spinner-grow spinner-grow-lg"></span> please wait while
loading cubicles
</div>
<div
class="d-flex justify-content-center"
*ngIf="page.userAccount.roleId !== 3 && !cubiclesLoading"
>
<div class="form-group text-center">
<label class="mb-1">Select Consultant Doctor</label>
<div
*ngIf="settingService.selectedConsultantDoctor && !isAssigningDoctor"
>
<span
class="text-capitalize font-weight-bold mr-2"
[textContent]="settingService.selectedConsultantDoctor.value"
></span>
<span
(click)="clearConsultantDoctor(select3)"
class="cursor-pointer text-danger"
>
<i class="mdi mdi-close"></i>
</span>
</div>
<ng-container *ngIf="isAssigningDoctor">
<div class="d-flex justify-contant-center align-items-center my-2">
<span class="spinner-grow spinner-grow-lg"></span> please wait while
assigning/un assigning doctor
</div>
</ng-container>
<div
[ngClass]="{'d-block': !settingService.selectedConsultantDoctor, 'd-none': settingService.selectedConsultantDoctor}"
>
<ng-select
[items]="providers | async"
#select3
[loading]="loadingProviders"
bindLabel="value"
bindValue="id"
[typeahead]="providersInput"
typeToSearchText="Search provider"
[virtualScroll]="true"
placeholder="Enter provider name"
(change)="onProviderChange($event)"
class="cubible-search"
[(ngModel)]="providerId"
>
<ng-template ng-notfound-tmp let-searchTerm="searchTerm">
<div class="ng-option disabled" *ngIf="searchTerm">
No provider found for '{{ searchTerm }}'
</div>
<div class="ng-option disabled" *ngIf="!searchTerm">
Search consultant doctor
</div>
</ng-template>
<ng-template ng-option-tmp let-item="item" let-i="index">
<div class="media">
<div class="avatar-xs mr-1">
<!--<img *ngIf="item.optionalText"
[src]="item.optionalText"
[alt]="item.value"
class="img-fluid rounded-circle" />-->
<span
*ngIf="!item.optionalText"
class="avatar-title rounded-circle font-11 font-weight-bold text-white"
avatar-bg
[index]="i"
[textContent]="item.value | initials"
></span>
</div>
<div class="media-body">
<h5
class="mb-0 mt-0 font-14 font-weight-normal"
[textContent]="item.value"
></h5>
<h6
class="mb-0 mt-0 font-15 font-weight-normal"
[textContent]="item.optionalText2"
></h6>
<h6
class="mb-0 mt-0 font-15 font-weight-normal"
[textContent]="item.optionalText1"
></h6>
</div>
</div>
</ng-template>
<ng-template ng-label-tmp let-item="item">
<div class="media">
<div class="avatar-xs mr-1">
<!--<img *ngIf="item.optionalText"
[src]="item.optionalText"
[alt]="item.value"
class="img-fluid rounded-circle" />-->
<span
*ngIf="!item.optionalText"
class="avatar-title rounded-circle font-11 font-weight-bold bg-soft-primary text-primary"
[textContent]="item.value | initials"
></span>
</div>
<div class="media-body">
<h5
class="mb-0 mt-0 font-14 font-weight-normal"
[textContent]="item.value"
></h5>
</div>
</div>
</ng-template>
</ng-select>
</div>
</div>
</div>
<div *ngIf="!cubiclesLoading" class="table-responsive">
<table class="table table-sm table-bordered table-striped">
<tbody>
<tr *ngIf="currentAssignedCubicles.length" class="table-primary">
<th>Assigned Room</th>
<th class="text-right">Assigned To</th>
<th class="text-right">Action</th>
</tr>
<tr *ngFor="let item of currentAssignedCubicles">
<td [textContent]="item.name"></td>
<td class="text-right" [textContent]="item.assignedName"></td>
<td width="150" class="text-right">
<button
class="btn btn-sm mb-0 btn-danger"
*ngIf="(page.userAccount.roleId === 1 || page.userAccount.roleId === 5 || page.userAccount.roleId === 2 || page.userAccount.accountId === item.accountId) && !item.loading"
(click)="onUnassign(item)"
>
<i class="mdi mdi-close mr-1"></i>
<span *ngIf="!item.loading">Remove</span>
<span *ngIf="item.loading">loading...</span>
</button>
</td>
</tr>
<tr *ngIf="currentAssignedCubicles.length > 0">
<td colspan="3">
<nav class="d-flex align-items-center justify-content-between">
<p class="mb-0 font-13"></p>
<ngb-pagination
class="pagination justify-content-end"
[maxSize]="5"
[rotate]="true"
[ellipses]="true"
[(page)]="assignedCubiclespagination.pageIndex"
[pageSize]="assignedCubiclespagination.pageSize"
(pageChange)="onAssignedPageChange($event)"
[collectionSize]="currentAssignedCubicles.length"
>
<ng-template ngbPaginationPrevious
><i class="fe-arrow-left"></i
></ng-template>
<ng-template ngbPaginationNext
><i class="fe-arrow-right"></i
></ng-template>
</ngb-pagination>
</nav>
</td>
</tr>
<tr *ngIf="currentUnAssignedCubicles.length" class="table-primary">
<th colspan="3">
<div class="d-flex justify-content-between align-items-center">
<div>Unassigned Rooms</div>
<div>
<ng-select
class="ng-select-sm text-uppercase"
[items]="unassignedCubicles"
bindLabel="name"
bindValue="cubicleId"
autocomplete="nope"
(change)="onUnAssignedCubicleSearch($event)"
placeholder="Search unassigned cubilces"
>
<ng-template ng-notfound-tmp let-searchTerm="searchTerm">
<div class="ng-option disabled">
No unassigned cubicles found for '{{searchTerm}}'
</div>
</ng-template>
</ng-select>
</div>
</div>
</th>
</tr>
<tr *ngFor="let item of currentUnAssignedCubicles">
<td [textContent]="item.name"></td>
<td colspan="2" class="text-right">
<button
class="btn btn-sm mb-0 btn-success"
(click)="onAssign(item)"
>
<i class="mdi mdi-check mr-1"></i>
<span *ngIf="!item.loading">Select</span>
<span *ngIf="item.loading">loading...</span>
</button>
</td>
</tr>
<tr
*ngIf="currentUnAssignedCubicles.length > 0 && unAssignedCubiclespaginationFlag"
>
<td colspan="3">
<nav class="d-flex align-items-center justify-content-between">
<p class="mb-0 font-13"></p>
<ngb-pagination
class="pagination justify-content-end"
[maxSize]="5"
[rotate]="false"
[ellipses]="false"
[(page)]="unAssignedCubiclespagination.pageIndex"
[pageSize]="unAssignedCubiclespagination.pageSize"
(pageChange)="onUnAssignedPageChange($event)"
[collectionSize]="unAssignedCubiclespagination.totalItems"
>
<ng-template ngbPaginationPrevious
><i class="fe-arrow-left"></i
></ng-template>
<ng-template ngbPaginationNext
><i class="fe-arrow-right"></i
></ng-template>
</ngb-pagination>
</nav>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-light btn-sm mr-1"
(click)="onCloseModal();"
>
Close
</button>
</div>
</ng-template>
import { Component, Input, AfterViewChecked, ChangeDetectorRef } from "@angular/core";
import * as moment from "moment";
@Component({
selector: "available-days",
template: `&nbsp;<span [innerHtml]="value"></span>`
})
export class AvailableDaysComponent implements AfterViewChecked {
@Input()
value: string;
constructor(private readonly cdRef: ChangeDetectorRef) { }
ngAfterViewChecked() {
if (this.value) {
const dayName = moment().format("d");
let value = this.value;
value = value.replace(/,/g, ", ");
value = value.replace(dayName, `<b class='text-primary'>${dayName}</b>`);
value = value.replace("1", "Mo");
value = value.replace("2", "Tu");
value = value.replace("3", "We");
value = value.replace("4", "Th");
value = value.replace("5", "Fr");
value = value.replace("6", "Sa");
value = value.replace("7", "Su");
this.value = value;
this.cdRef.detectChanges();
}
}
}
\ No newline at end of file
import { Component, Input, ViewEncapsulation } from "@angular/core";
@Component({
selector: "avatar-img",
encapsulation: ViewEncapsulation.None,
template: `
<img [hidden]="loading" (load)="onImageLoad()" (error)="onImageError()" [alt]="alt" [class]="cssClass" [src]="src" />
<div *ngIf="loading" class="ph-item ph-loading m-0 ph-avatar-loading">
<div class="ph-avatar mb-0 h-100"></div>
</div>
`
})
export class AvatarImageComponent {
@Input() src: string;
@Input() alt: string;
@Input() cssClass: string;
bindingSrc: string;
loading: boolean;
constructor() {
this.loading = true;
}
onImageLoad() {
this.loading = false;
}
onImageError() {
this.loading = false;
this.src = location.origin + location.pathname + "assets/images/broken.png";
}
}
\ No newline at end of file
import { DOCUMENT } from "@angular/common";
import { Component, Inject } from "@angular/core";
@Component({
selector: "full-screen",
template: `
<a class="nav-link dropdown-toggle waves-effect waves-light" [hidden]="maximized" (click)="open();" data-toggle="fullscreen" href="javascript:;">
<i class="fe-maximize noti-icon"></i>
</a>
<a class="nav-link dropdown-toggle waves-effect waves-light" [hidden]="!maximized" (click)="close();" data-toggle="fullscreen" href="javascript:;">
<i class="fe-minimize noti-icon"></i>
</a>
`
})
export class FullScreenComponent {
elem: any;
maximized: boolean;
constructor(@Inject(DOCUMENT) private readonly document: any) {
this.elem = document.documentElement;
this.maximized = false;
}
private makeFullScreenOpen() {
this.maximized = true;
}
private makeFullScreenClose() {
this.maximized = false;
}
open() {
if (this.elem.requestFullscreen) {
this.makeFullScreenOpen();
this.elem.requestFullscreen();
} else if (this.elem.mozRequestFullScreen) {
this.makeFullScreenOpen();
this.elem.mozRequestFullScreen();
} else if (this.elem.webkitRequestFullscreen) {
this.makeFullScreenOpen();
this.elem.webkitRequestFullscreen();
} else if (this.elem.msRequestFullscreen) {
this.makeFullScreenOpen();
this.elem.msRequestFullscreen();
}
}
close() {
if (this.document.exitFullscreen) {
this.makeFullScreenClose();
this.document.exitFullscreen();
} else if (this.document.mozCancelFullScreen) {
this.makeFullScreenClose();
this.document.mozCancelFullScreen();
} else if (this.document.webkitExitFullscreen) {
this.makeFullScreenClose();
this.document.webkitExitFullscreen();
} else if (this.document.msExitFullscreen) {
this.makeFullScreenClose();
this.document.msExitFullscreen();
}
}
}
\ No newline at end of file
import { Component, OnDestroy } from "@angular/core";
import { Router } from "@angular/router";
import { NgbModal, NgbModalRef } from "@ng-bootstrap/ng-bootstrap";
import { AutoResume, DEFAULT_INTERRUPTSOURCES, Idle } from "@ng-idle/core";
import { AppData, IdentityService, appUrls } from "@shared/services";
import { ProgressBarModalComponent } from "@shared/components/progress-bar-modal.component";
@Component({
selector: "idle-timeout",
template: ""
})
export class IdleComponent implements OnDestroy {
timeoutIn = 60;
modalRef: NgbModalRef;
idleTimeout: any;
idleStart: any;
constructor(
private readonly idle: Idle,
private readonly router: Router,
private readonly appData: AppData,
private readonly identityService: IdentityService,
private readonly modalService: NgbModal
) {
idle.setIdle(1200);
idle.setTimeout(this.timeoutIn);
idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);
idle.setAutoResume(AutoResume.notIdle);
this.idle.onTimeout.subscribe(() => {
this.modalRef.close("logout");
});
this.idle.onIdleStart.subscribe(() => {
if (this.modalService.hasOpenModals()) {
this.modalService.dismissAll();
}
this.openModal();
});
this.idle.onTimeoutWarning.subscribe((countdown: number) => {
this.modalRef.componentInstance.countdown = countdown;
});
this.idle.watch();
}
openModal() {
this.modalRef = this.modalService.open(ProgressBarModalComponent, {
backdrop: "static",
keyboard: false,
centered: true,
windowClass: "custom-modal effect-scale idle-timeout"
});
this.modalRef.componentInstance.countdown = this.timeoutIn;
this.modalRef.result.then((result: any) => {
if (result === "logout") {
this.identityService.signOut()
.subscribe(() => {
this.appData.setAccount(null);
delete localStorage["ProviderVideoCallingSession"];
localStorage.removeItem("menus");
this.router.navigateByUrl(appUrls.login);
});
}
if (result === "stay") {
this.idle.watch();
}
});
}
ngOnDestroy() {
this.idle.stop();
this.idle.onTimeout.observers.length = 0;
this.idle.onIdleStart.observers.length = 0;
this.idle.onTimeoutWarning.observers.length = 0;
try {
this.modalService.dismissAll();
} catch (e) {
// ignored;
}
}
}
\ No newline at end of file
export * from "./available-days.component";
export * from "./avatar-image.component";
export * from "./full-screen.component";
export * from "./idle-timeout.component";
export * from "./no-data/no-data.component";
export * from "./progress-bar-modal.component";
export * from "./session-timeout.component";
export * from "./symptoms-view.component";
<div class="{{free ? 'no-data' : 'no-data position-absolute'}}">
<img src="assets/images/no-data.png" alt="No data"/>
<h4 class="title">No {{title}} found {{applied ? 'based on your filters criteria.' : '.'}}</h4>
<p class="sub-title">There is no data to show you right now.</p>
</div>
\ No newline at end of file
import { Component, Input } from "@angular/core";
@Component({
selector: "no-data",
templateUrl: "./no-data.component.html",
})
export class NoDataComponent {
@Input()
title: string;
@Input()
applied: boolean;
@Input()
free: boolean;
}
\ No newline at end of file
import { Component, Input } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
@Component({
selector: "progressbar-modal-comp",
template: `
<div class="modal-header">
<h5 class="modal-title">Idle timeout</h5>
</div>
<div class="modal-body">
<div class="row">
<div class="col-12">
<h4>
You're being timed out due to inactivity.<br />
Please choose to stay logged in or to log off.<br />
Otherwise you will be logged off automatically.
</h4>
</div>
<div class="col-12">
<div class="text-danger">
<p> Your session expires in </p>
</div>
</div>
<div class="col-12">
<div class="d-flex justify-content-center">
<div class="">
<div class="rounded-circle" style="width: 100px; height: 100px; background-color: #ff5959; color: white; text-align: center;">
<span style="margin-top: 29px !important;position: absolute; margin-left: -20px;font-size: 26px;">
<span [textContent]="countdown + 's'"></span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light btn-xs mr-1" (click)="logout()">Logout</button>
<button type="button" class="btn btn-primary btn-xs" (click)="stayLoggedIn()">Stay Logged In</button>
</div>
`
})
export class ProgressBarModalComponent {
@Input() countdown: number;
constructor(public activeModal: NgbActiveModal) {
}
stayLoggedIn() {
this.activeModal.close("stay");
}
logout() {
this.activeModal.close("logout");
}
}
import { Component, OnDestroy, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { AppData, appUrls, IdentityService } from "@shared/services";
import { interval, Subscription } from "rxjs";
@Component({
selector: "session-timeout",
template: ""
})
export class SessionTimeoutComponent implements OnInit, OnDestroy {
session: Subscription;
constructor(
private readonly router: Router,
private readonly identityService: IdentityService,
private readonly appData: AppData
) {
}
checkSession() {
this.identityService.get().subscribe(account => {
if (!account) {
this.router.navigateByUrl(appUrls.login);
this.appData.setAccount(null);
}
});
}
ngOnInit() {
this.session = interval(90000).subscribe(() => {
this.checkSession();
});
}
ngOnDestroy() {
this.session.unsubscribe();
}
}
\ No newline at end of file
import { Component, Input } from "@angular/core";
@Component({
selector: "symptoms-view",
template: '<ul>' +
'<li *ngFor="let node of symptomQuestions" (click)="checkFunction(node)">{{node.ChatBotMsg[0]}}' +
'<ul>' +
'<li *ngFor="let option of node.UserResponseOptions">{{option.Option}}' +
'<symptoms-view [symptomQuestions]="option.IfSelected"></symptoms-view>' +
'</li>' +
'</ul>' +
'</li>' +
'</ul>'
})
export class SymptomsViewComponent {
@Input() symptomQuestions: any;
@Input() checkFunction: any;
}
\ No newline at end of file
import { Directive, HostListener, Input, ElementRef } from '@angular/core';
@Directive({
selector: '[allowNumeric]'
})
export class AllowNumericDirective {
constructor(private el: ElementRef) { }
@Input() allowMultiLine = false;
@Input() allowNegative = false;
@Input() allowDecimal = true;
@Input() maxLength = 0;
regex: RegExp;
@HostListener('keypress', ['$event'])
onKeyPress(event: KeyboardEvent) {
this.validate(event, event.key === 'Enter' ? '\n' : event.key);
}
@HostListener('paste', ['$event'])
onPaste(event: Event) {
const pastedText = (<any>window).clipboardData && (<any>window).clipboardData.getData('Text') // If IE, use window
|| <ClipboardEvent>event && (<ClipboardEvent>event).clipboardData.getData('text/plain'); // Non-IE browsers
this.validate(event, pastedText);
}
@HostListener('cut', ['$event'])
onCut(event: Event) {
this.validate(event, '');
}
validate(event: Event, text: string) {
const txtInput = this.el.nativeElement;
const newValue = (txtInput.value.substring(0, txtInput.selectionStart)
+ text + txtInput.value.substring(txtInput.selectionEnd));
if (!this.regex) {
this.regex = <RegExp>eval('/^'
+ (this.allowNegative ? '-?' : '')
+ (this.allowDecimal ? '((\\d+\\.?)|(\\.?))\\d*' : '\\d*')
+ '$/g');
}
const lines = this.allowMultiLine ? newValue.split('\n') : [newValue];
for (const line of lines) {
const lineText = line.replace('\r', '');
if (this.maxLength && lineText.length > this.maxLength || !lineText.match(this.regex)) {
event.preventDefault();
return;
}
}
}
}
\ No newline at end of file
import { Directive, HostListener } from "@angular/core";
import { KeyCodesHelper, RegExHelper } from "@shared/helpers";
@Directive({
selector: "[alphaNumeric]"
})
export class AlphaNumericOnlyDirective {
@HostListener("keydown", ["$event"])
onKeyDown(e: KeyboardEvent) {
const keys = KeyCodesHelper.default.concat(KeyCodesHelper.alphabets).concat(KeyCodesHelper.numbers);
if (keys.indexOf(e.keyCode) === -1) {
event.preventDefault();
}
if (KeyCodesHelper.specialCharacters.indexOf(e.key) > -1) {
event.preventDefault();
return;
}
if (KeyCodesHelper.defaultNames.indexOf(e.key) === -1) {
return;
}
if (RegExHelper.alphaNumeric.test(e.key))
return;
else
event.preventDefault();
}
}
\ No newline at end of file
import { Directive, HostListener, ElementRef } from "@angular/core";
@Directive({
selector: "[autoResize]"
})
export class AutoResizeDirective {
@HostListener("input", ["$event.target"])
onInput() {
this.adjust();
}
constructor(public element: ElementRef) {
}
ngAfterContentChecked(): void {
this.adjust();
}
adjust(): void {
this.element.nativeElement.style.overflow = "hidden";
this.element.nativeElement.style.height = "auto";
this.element.nativeElement.style.height = this.element.nativeElement.scrollHeight + "px";
}
}
\ No newline at end of file
import { AfterContentInit, Directive, ElementRef, OnChanges, OnDestroy, SimpleChanges } from "@angular/core";
const baseTimerDelay = 10;
@Directive({
selector: "[autofocus]",
inputs: [
"shouldFocusElement: autofocus",
"timerDelay: autofocusDelay"
]
})
export class AutofocusDirective implements AfterContentInit, OnChanges, OnDestroy {
shouldFocusElement: any;
timerDelay: number;
private readonly elementRef: ElementRef;
private timer: any;
// I initialize the autofocus directive.
constructor(elementRef: ElementRef) {
this.elementRef = elementRef;
this.shouldFocusElement = "";
this.timer = null;
this.timerDelay = baseTimerDelay;
}
// ---
// PUBLIC METHODS.
// ---
// I get called once after the contents have been fully initialized.
ngAfterContentInit(): void {
// Because this directive can act on the stand-only "autofocus" attribute or
// the more specialized "autofocus" property, we need to check to see if the
// "shouldFocusElement" input property is the empty string. This will signify
// that the focus it not being data-driven and should be performed automatically.
if (this.shouldFocusElement === "") {
this.startFocusWorkflow();
}
}
// I get called every time the input bindings are updated.
ngOnChanges(changes: SimpleChanges): void {
// If the timer delay is being passed-in as a string (ie., someone is using
// attribute-input syntax, not property-input syntax), let's coalesce the
// attribute to a numeric value so that our type-annotations are consistent.
// ReSharper disable once TsResolvedFromInaccessibleModule
if (changes.timerDelay && (typeof (this.timerDelay) !== "number")) {
// If the coalesce fails, just fall-back to a sane value.
if (isNaN(this.timerDelay = +this.timerDelay)) {
this.timerDelay = baseTimerDelay;
}
}
// If the focus input is being data-driven, then we either need to start the
// focus work flow if focus is required; or, clear any existing work flow if focus
// is no longer required (so that we don't steal focus from another element).
// ReSharper disable once TsResolvedFromInaccessibleModule
if (changes.shouldFocusElement) {
(this.shouldFocusElement)
? this.startFocusWorkflow()
: this.stopFocusWorkflow()
;
}
}
// I get called once when the directive is being unmounted.
ngOnDestroy(): void {
this.stopFocusWorkflow();
}
// ---
// PRIVATE METHODS.
// ---
// I start the timer-based work flow that will focus the current element.
private startFocusWorkflow(): void {
// If there is already a timer running for this element, just let it play out -
// resetting it at this point will only push-out the time at which the focus is
// applied to the element.
if (this.timer) {
return;
}
this.timer = setTimeout(
(): void => {
this.timer = null;
// ReSharper disable once TsResolvedFromInaccessibleModule
this.elementRef.nativeElement.focus();
},
this.timerDelay
);
}
// I stop the timer-based work flow, preventing focus from taking place.
private stopFocusWorkflow(): void {
clearTimeout(this.timer);
this.timer = null;
}
}
\ No newline at end of file
import { Directive, OnInit, Input, ElementRef } from "@angular/core";
@Directive({
selector: "[avatar-bg]"
})
export class AvatarBgDirective implements OnInit {
@Input() index: number;
backgrounds: Array<string> = [
"bg-primary",
"bg-secondary",
"bg-success",
"bg-danger",
"bg-warning",
"bg-info",
"bg-pink",
"bg-blue"
];
constructor(private readonly el: ElementRef) { }
ngOnInit() {
this.el.nativeElement.classList.add(this.backgrounds[this.index % this.backgrounds.length]);
}
}
\ No newline at end of file
import { Directive/*, HostListener*/ } from "@angular/core";
@Directive({
selector: "[block]"
})
export class BlockCopyPasteDirective {
//@HostListener("paste", ["$event"]) blockPaste(e: KeyboardEvent) {
// e.preventDefault();
//}
//@HostListener("copy", ["$event"]) blockCopy(e: KeyboardEvent) {
// e.preventDefault();
//}
//@HostListener("cut", ["$event"]) blockCut(e: KeyboardEvent) {
// e.preventDefault();
//}
}
\ No newline at end of file
import { Directive, Input, forwardRef, HostListener, ElementRef, Renderer2 } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
@Directive({
selector: "input[type=checkbox][trueFalseValue]",
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TrueFalseValueDirective),
multi: true
}
]
})
export class TrueFalseValueDirective implements ControlValueAccessor {
private propagateChange(_: any) { }
@Input() trueValue = true;
@Input() falseValue = false;
functions: any;
disable: boolean;
constructor(private readonly elementRef: ElementRef, private readonly renderer: Renderer2) { }
@HostListener("change", ["$event"])
onHostChange(ev) {
this.propagateChange(ev.target.checked ? this.trueValue : this.falseValue);
}
writeValue(obj: any): void {
if (obj === this.trueValue) {
this.renderer.setProperty(this.elementRef.nativeElement, "checked", true);
} else {
this.renderer.setProperty(this.elementRef.nativeElement, "checked", false);
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void { this.functions = fn; }
setDisabledState?(isDisabled: boolean): void { this.disable = isDisabled; }
}
\ No newline at end of file
import { Directive, HostListener } from "@angular/core";
import { KeyCodesHelper, RegExHelper } from "@shared/helpers";
@Directive({
selector: "[decimalsOnly]"
})
export class DecimalsOnlyDirective {
@HostListener("keydown", ["$event"])
onKeyDown(e: KeyboardEvent) {
const keys = KeyCodesHelper.default.concat(KeyCodesHelper.numbers).concat(KeyCodesHelper.dots);
if (keys.indexOf(e.keyCode) === -1) {
event.preventDefault();
}
if (KeyCodesHelper.defaultNames.indexOf(e.key) === -1) {
return;
}
if (RegExHelper.decimalsOnly.test(e.key))
return;
else
event.preventDefault();
}
}
\ No newline at end of file
export * from "./alpha-numeric-only.directive";
export * from "./autofocus.directive";
export * from "./block.directive";
export * from "./decimals-only.directive";
export * from "./numbers-only.directive";
export * from "./text-only.directive";
export * from "./title-only.directive";
export * from "./auto-resize.directive";
export * from "./password.directive";
export * from "./avatar-bg.directive";
export * from "./numeric.directive";
export * from "./paste-only.directive";
export * from "./checkbox.directive";
export * from "./trimspace.directive";
export * from "./allow-numeric.directive";
export * from "./menu-button.directive";
\ No newline at end of file
import { Directive, Input, OnInit, TemplateRef, ViewContainerRef } from "@angular/core";
import { MenuService } from "../services";
@Directive({
selector: "[menuButton]"
})
export class MenuButtonDirective implements OnInit {
@Input() menuButton: string;
constructor(
private view: ViewContainerRef,
private template: TemplateRef<any>,
private menuService: MenuService
) {
}
ngOnInit(): void {
if(this.menuService.isMenuButtonAllowed(this.menuButton)) {
this.view.createEmbeddedView(this.template);
} else {
this.view.clear();
}
}
}
\ No newline at end of file
import { Directive, HostListener } from "@angular/core";
import { KeyCodesHelper, RegExHelper } from "@shared/helpers";
@Directive({
selector: "[numbersOnly]"
})
export class NumbersOnlyDirective {
@HostListener("keydown", ["$event"])
onKeyDown(e: KeyboardEvent) {
const keys = KeyCodesHelper.default.concat(KeyCodesHelper.numbers);
if (keys.indexOf(e.keyCode) === -1) {
event.preventDefault();
return;
}
if (KeyCodesHelper.specialCharacters.indexOf(e.key) > -1) {
event.preventDefault();
return;
}
if (KeyCodesHelper.defaultNames.indexOf(e.key) === -1) {
return;
}
if (RegExHelper.numbersOnly.test(e.key))
return;
event.preventDefault();
}
}
\ No newline at end of file
import { Directive, ElementRef, HostListener, Input } from "@angular/core";
@Directive({
selector: "[numeric]"
})
export class NumericDirective {
@Input() decimals = 0;
private check(value: string, decimals: number) {
if (decimals <= 0) {
return String(value).match(new RegExp(/^\d+$/));
} else {
const regExpString = `^\\s*((\\d+(\\.\\d{0,${decimals}})?)|((\\d*(\\.\\d{1,${decimals}}))))\\s*$`;
return String(value).match(new RegExp(regExpString));
}
}
private specialKeys = [
"Backspace", "Tab", "End", "Home", "ArrowLeft", "ArrowRight", "Delete"
];
constructor(private readonly el: ElementRef) { }
@HostListener("keydown", ["$event"])
onKeyDown(event: KeyboardEvent) {
if (this.specialKeys.indexOf(event.key) !== -1) {
return;
}
const current: string = this.el.nativeElement.value;
const next = current.concat(event.key);
if (next && !this.check(next, this.decimals)) {
event.preventDefault();
}
}
}
\ No newline at end of file
import { Directive, HostListener, ElementRef } from "@angular/core";
@Directive({
selector: "[password]"
})
export class PasswordDirective {
@HostListener("click") onClick(){
if (!$(this.element.nativeElement).hasClass("show-password")) {
$(this.element.nativeElement).siblings("input").attr("type", "text");
$(this.element.nativeElement).addClass("show-password");
} else {
$(this.element.nativeElement).siblings("input").attr("type", "password");
$(this.element.nativeElement).removeClass("show-password");
}
}
constructor(public element: ElementRef) {
}
}
\ No newline at end of file
import { Directive, HostListener } from "@angular/core";
//import { KeyCodesHelper, RegExHelper } from "@shared/helpers";
@Directive({
selector: "[pasteOnly]"
})
export class PasteOnlyDirective {
@HostListener('paste', ['$event']) onPaste(event: ClipboardEvent) {
const dataToPaste = event.clipboardData.getData('text');
console.log(dataToPaste);
// Validate 'dataToPaste' against the regex
}
//@HostListener("keydown", ["$event"])
//handleKeyboardEvent(event: KeyboardEvent) {
// console.log(event);
// let x = event.keyCode;
// if (x === 65) {
// console.log('Escape!');
// }
//}
//onKeyDown(e: KeyboardEvent) {
//
// let x = e.keyCode;
// if (x === 17) {
// console.log('Escape!');
// return;
// }
// const keys = KeyCodesHelper.default.concat(KeyCodesHelper.numbers);
// if (keys.indexOf(e.keyCode) === -1) {
// event.preventDefault();
// return;
// }
// if (KeyCodesHelper.specialCharacters.indexOf(e.key) > -1) {
// event.preventDefault();
// return;
// }
// if (KeyCodesHelper.defaultNames.indexOf(e.key) === -1) {
// return;
// }
// if (RegExHelper.numbersOnly.test(e.key))
// return;
// event.preventDefault();
//}
}
\ No newline at end of file
import { Directive, HostListener } from "@angular/core";
import { KeyCodesHelper, RegExHelper } from "@shared/helpers";
@Directive({
selector: "[textOnly]"
})
export class TextOnlyDirective {
@HostListener("keydown", ["$event"])
onKeyDown(e: KeyboardEvent) {
const keys = KeyCodesHelper.default.concat(KeyCodesHelper.alphabets);
if (keys.indexOf(e.keyCode) === -1) {
event.preventDefault();
return;
}
if (KeyCodesHelper.specialCharacters.indexOf(e.key) > -1) {
event.preventDefault();
return;
}
if (RegExHelper.textOnly.test(e.key))
return;
else
event.preventDefault();
}
}
\ No newline at end of file
import { Directive, HostListener } from "@angular/core";
import { KeyCodesHelper, RegExHelper } from "@shared/helpers";
@Directive({
selector: "[titleOnly]"
})
export class TitleOnlyDirective {
@HostListener("keydown", ["$event"])
onKeyDown(e: KeyboardEvent) {
const keys = KeyCodesHelper.default.concat(KeyCodesHelper.alphabets).concat(KeyCodesHelper.specialChars);
if (keys.indexOf(e.keyCode) === -1) {
event.preventDefault();
}
if (RegExHelper.titleOnly.test(e.key))
return;
else
event.preventDefault();
}
}
\ No newline at end of file
import { Directive, forwardRef, HostListener } from '@angular/core';
import { DefaultValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
const TRIM_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TrimSpaceDirective),
multi: true,
};
/**
* The trim accessor for writing trimmed value and listening to changes that is
* used by the {@link NgModel}, {@link FormControlDirective}, and
* {@link FormControlName} directives.
*/
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector
selector: "[trimSpace]",
providers: [TRIM_VALUE_ACCESSOR],
})
export class TrimSpaceDirective extends DefaultValueAccessor {
@HostListener('input', ['$event.target.value'])
ngOnChange = (val: string) => {
this.onChange(val.trim());
};
@HostListener('blur', ['$event.target.value'])
applyTrim(val: string) {
this.writeValue(val.trim());
}
override writeValue(value: any): void {
if (typeof value === 'string') {
value = value.trim();
}
super.writeValue(value);
}
}
\ No newline at end of file
export class Account {
accountId: number;
referenceId: number;
roleId: number;
roleName: string;
fullName: string;
email: string;
mobile: string;
countryId: number;
countryName: string;
countryCode: string;
lastLoginDate?: Date;
createdDate?: Date;
active?: boolean;
isLocked?: boolean;
thumbnailUrl: string;
totalItems: number;
status: string;
locationIdentifiers: string;
userName: string;
}
\ No newline at end of file
export class AdmissionTransferRequest {
admissionTransferRequestId: number;
fullName: string;
chargeCategoryName: string;
unitName: string;
createdByName: string;
modifiedByName: string;
modifiedDate: Date;
createdDate: Date;
doctorUnitMasterId: number;
admissionId: number;
active: boolean;
approvedByName: string;
approvedBy: number;
approvedDate: Date;
admissionChangeRequestTypeId: number;
chargeCategories: string;
charges: Array<string>;
changeRequestType: string;
}
\ No newline at end of file
export class Admission {
admissionId: number;
encryptedAdmissionId: string;
departmentId: number;
providerId: number;
providerName: string;
providerGender: string;
providerAge?: number;
providerThumbnailUrl: string;
practiceName: string;
patientId: number;
encryptedPatientId: string;
patientFamilyId?: number;
patientAccountId: number;
patientName: string;
patientAge?: number;
patientGender: string;
patientMobile: string;
patientEmail: string;
floorName: string;
patientThumbnailUrl: string;
patientCountryId: number;
patientCountryCode: string;
visitType: string;
roomId: number;
bedId: number;
wardId: number;
bedNumber: string;
roomName: string;
wardName: string;
surgeryTypeId: number;
isMaternity: string;
attendantName: string;
attendantRelationWithPatient: string;
attendantContactNo: string;
admissionDate: Date;
admissionTime: object;
admissionTimeString: string;
patientType: string;
admissionNo: string;
isDischarged: boolean;
dischargeDate: Date;
dischargeTime: object;
dischargeTimeString: string;
admissionNotes: string;
couponId: number;
amount: number;
discount: number;
currencySymbol: string;
total: number;
status: string;
active: boolean;
expired: boolean;
createdBy: number;
createdDate: Date;
totalItems: number;
encounterType: string;
reason: string;
paidAmount: number;
finalAmount: number;
paymentStatus: string;
isFinalBill: boolean;
umrNo: string;
cancelling: boolean;
pendingMedicines: number;
progressReportId: number;
medicationInfo: MedicationInfo;
followUpDaysForIp?: number;
editExpectedDischargeDate: boolean;
expectedDischargeDate: Date;
expectedDischargeDateView: any;
isUpdatingDischargeDate: boolean;
// dischargedBy: number;
dischargedByRole: string;
dischargedByName: string;
visitTypeId: number;
patientPriorityId: number;
locationId: number;
locationName: string;
serviceOrder: any;
bedStatusId: number;
bedStatusName: string;
floorId: number;
swap: boolean;
insuranceCompanyId: number;
admissionPayTypeId: number;
caseTypeId: number;
referralDoctorId: number;
tpaId: number;
patientOrganization: string;
bedAssociatedData: string;
surrogacyData? :SurrogacyInfo;
emergencyInfo: EmergencyInformation;
nriData: nriInfo;
chargeCatgoryId: number;
chargeCategoryName: string;
doctorUnitMasterId: number;
packageModuleId?: number;
admissionPackageId?: number;
admissionBedChangeRequestId?: number;
requestComments?: number;
chargeCategoryNames?: number;
currentRoomId?: number;
currentRoomName: string;
breakfast: object;
lunch: object;
dinner: object;
unitName: string;
}
class MedicationContentViewModel {
count: number
}
class MedicationViewModel {
missed: MedicationContentViewModel;
pending: MedicationContentViewModel;
next: MedicationContentViewModel;
}
export class MedicationInfo {
admissionId: number;
medications: MedicationViewModel;
labs: MedicationContentViewModel;
notes: MedicationContentViewModel;
}
export class SurrogacyInfo {
geneticMotherName?: string;
age?: number;
geneticFatherName?: string;
address?: string;
phoneNo?: number;
bloodGroup?: string;
rhType?: string;
fromDate?: Date;
toDate?: Date;
}
export class nriInfo {
isNri?: boolean;
memberIdcardNo?: string;
unit?: number;
policyNo?: string;
cardHolder?: string;
serviceNo?: string;
refDate?: Date;
rank?: number;
refSerialNo?: string;
insuranceCode?: string;
organisation?: string;
insuranceCo?: string;
panCardNo?: string;
passportNo?: string;
ppAddress?: string;
phoneNo?: number;
ppIssuePlace?: string;
afterDischarge?: string;
}
export class EmergencyInformation {
emergencyContactNo?: number;
emergencyTypeId?: string;
}
export class Ambulance {
ambulanceId: number;
ambulanceNo: string;
assignedNo: string;
locationId: number;
active: boolean;
createdBy: number;
createdDate: Date;
modifiedBy?: number;
modifiedDate?: Date;
createdByName: string;
createdByRole: string;
modifiedByName: string;
modifiedByRole: string;
}
\ No newline at end of file
export class AppointmentTypes {
appointmentTypeId: number;
name: string;
createdBy: number;
createdDate: Date;
modifiedBy?: number;
modifiedDate?: Date;
rowColor: string;
createdByName: string;
modifiedByName: string;
}
\ No newline at end of file
import { DragDropModule } from '@angular/cdk/drag-drop';
import { CommonModule } from "@angular/common";
import { HttpClientModule } from "@angular/common/http";
import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { NgbDateAdapter, NgbDateParserFormatter, NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { NgIdleModule } from "@ng-idle/core";
import { NgSelectModule } from "@ng-select/ng-select";
import { LoadingBarModule } from "@ngx-loading-bar/core";
import { LoadingBarRouterModule } from "@ngx-loading-bar/router";
import { DateParserFormatter, NgbUTCStringAdapter } from "@shared/formatters";
import { MomentModule } from "ngx-moment";
import { ToastrModule } from "ngx-toastr";
@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
LoadingBarRouterModule,
LoadingBarModule,
NgbModule,
NgSelectModule,
NgIdleModule.forRoot(),
MomentModule.forRoot(),
ToastrModule.forRoot({
timeOut: 2000,
positionClass: "toast-bottom-right",
preventDuplicates: true,
closeButton: true,
autoDismiss: true,
progressBar: true,
progressAnimation: "increasing",
}),
DragDropModule
],
providers: [
{ provide: NgbDateAdapter, useClass: NgbUTCStringAdapter },
{ provide: NgbDateParserFormatter, useClass: DateParserFormatter }
],
exports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
LoadingBarRouterModule,
LoadingBarModule,
NgbModule,
NgSelectModule,
NgIdleModule,
MomentModule,
ToastrModule,
DragDropModule
]
})
export class SharedCommonModule {
}
\ No newline at end of file
import { NgModule } from "@angular/core";
import { AvailableDaysComponent, FullScreenComponent, IdleComponent, NoDataComponent, ProgressBarModalComponent, SessionTimeoutComponent } from "@shared/components";
import { AllowNumericDirective, AlphaNumericOnlyDirective, AutoResizeDirective, AutofocusDirective, AvatarBgDirective, BlockCopyPasteDirective, DecimalsOnlyDirective, MenuButtonDirective, NumbersOnlyDirective, NumericDirective, PasswordDirective, PasteOnlyDirective, TextOnlyDirective, TitleOnlyDirective, TrimSpaceDirective, TrueFalseValueDirective } from "@shared/directives";
import { AgePipe, FormatBytesPipe, FromArrayPipe, GenderPipe, InitialsPipe, MaritalStatusPipe, MinuteSecondsPipe, SafePipe, SearchPipe, SortFormArrayPipe, SortFormPipe, TitlePipe, ToArrayPipe, UTCToLocalPipe } from "@shared/pipes";
const components = [SessionTimeoutComponent, NoDataComponent, FullScreenComponent, IdleComponent, ProgressBarModalComponent, AvailableDaysComponent];
const directives = [AlphaNumericOnlyDirective, AutofocusDirective, BlockCopyPasteDirective, DecimalsOnlyDirective, NumericDirective, AvatarBgDirective, NumbersOnlyDirective, PasswordDirective, TextOnlyDirective, TitleOnlyDirective, AutoResizeDirective, PasteOnlyDirective, TrueFalseValueDirective, TrimSpaceDirective, AllowNumericDirective, MenuButtonDirective];
const pipes = [SafePipe, InitialsPipe, TitlePipe, FormatBytesPipe, UTCToLocalPipe, SortFormArrayPipe, GenderPipe, MaritalStatusPipe, FromArrayPipe, ToArrayPipe, MinuteSecondsPipe, SearchPipe, SortFormPipe, AgePipe];
@NgModule({
declarations: [components, directives, pipes],
exports: [components, directives, pipes]
})
export class SharedLibsModule {
}
\ No newline at end of file
import { HTTP_INTERCEPTORS } from "@angular/common/http";
import { ModuleWithProviders, NgModule } from "@angular/core";
import { RequestInterceptor } from "@shared/interceptors";
import { AppointmentRefreshService, AppointmentToggleService, BannerService, BillNotificationService, CommunicationService, DashboardService, FinalBillService, HttpErrorService, HttpService, IconService, IdentityService, JitsiService, MenuService, NotifyService, PrintOptionService, PrintService, QueueService, RedirectAppointmentService, ResourceService, SettingService, TimelineToggleService, ValidatorService, VideoLinkService } from "@shared/services";
import { SharedCommonModule } from "@shared/shared.common.module";
import { SharedLibsModule } from "@shared/shared.libs.module";
import { SharedService } from "./services/shared.service";
@NgModule({
imports: [SharedCommonModule, SharedLibsModule],
exports: [SharedCommonModule, SharedLibsModule]
})
export class SharedModule {
static forRoot(): ModuleWithProviders<SharedModule> {
return {
ngModule: SharedModule,
providers: [
TimelineToggleService, CommunicationService, VideoLinkService, AppointmentToggleService, IdentityService, NotifyService, HttpService, ResourceService, JitsiService, QueueService, ValidatorService, FinalBillService, MenuService, PrintService, HttpErrorService, DashboardService, SettingService, AppointmentRefreshService, RedirectAppointmentService, PrintOptionService, BannerService, IconService, SharedService, BillNotificationService,
{
provide: HTTP_INTERCEPTORS,
useClass: RequestInterceptor,
multi: true
}
]
};
}
}
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment