Compare commits
No commits in common. "AppointmentModule_Modified" and "master" have entirely different histories.
Appointmen
...
master
@ -30,31 +30,6 @@
|
||||
"allowedCommonJsDependencies": ["chart.js", "js-sha256"],
|
||||
"assets": ["src/favicon.ico", "src/assets"],
|
||||
"styles": [
|
||||
{
|
||||
"input": "node_modules/primeng/resources/themes/saga-blue/theme.css",
|
||||
"inject": true,
|
||||
"bundleName": "saga-blue"
|
||||
},
|
||||
{
|
||||
"input": "node_modules/primeflex/themes/primeone-dark.css",
|
||||
"inject": true,
|
||||
"bundleName": "primeone-dark"
|
||||
},
|
||||
{
|
||||
"input": "node_modules/primeng/resources/primeng.min.css",
|
||||
"inject": true,
|
||||
"bundleName": "primeng.min"
|
||||
},
|
||||
{
|
||||
"input": "node_modules/primeicons/primeicons.css",
|
||||
"inject": true,
|
||||
"bundleName": "primeicons"
|
||||
},
|
||||
{
|
||||
"input": "node_modules/primeflex/primeflex.min.css",
|
||||
"inject": true,
|
||||
"bundleName": "primeflex.min"
|
||||
},
|
||||
{
|
||||
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/bootstrap-dim.css",
|
||||
"inject": false,
|
||||
|
86
angular/package-lock.json
generated
86
angular/package-lock.json
generated
@ -26,14 +26,7 @@
|
||||
"@angular/platform-browser": "~18.1.0",
|
||||
"@angular/platform-browser-dynamic": "~18.1.0",
|
||||
"@angular/router": "~18.1.0",
|
||||
"@fullcalendar/angular": "^6.1.16",
|
||||
"@fullcalendar/core": "^6.1.15",
|
||||
"@fullcalendar/daygrid": "^6.1.15",
|
||||
"@fullcalendar/interaction": "^6.1.15",
|
||||
"bootstrap-icons": "~1.8.0",
|
||||
"primeflex": "^3.3.1",
|
||||
"primeicons": "^6.0.1",
|
||||
"primeng": "^17.18.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.14.0"
|
||||
@ -3507,47 +3500,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fullcalendar/angular": {
|
||||
"version": "6.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@fullcalendar/angular/-/angular-6.1.16.tgz",
|
||||
"integrity": "sha512-Qqs0MZPlIDretmWgtOr0H+uiLO6DKeqxqH1Y2oeWufwEbQTaAxhH7mivdTmxL596JkiuJ/1dhCm+v4tVbFb40w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "12 - 19",
|
||||
"@angular/core": "12 - 19",
|
||||
"@fullcalendar/core": "~6.1.15"
|
||||
}
|
||||
},
|
||||
"node_modules/@fullcalendar/core": {
|
||||
"version": "6.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.15.tgz",
|
||||
"integrity": "sha512-BuX7o6ALpLb84cMw1FCB9/cSgF4JbVO894cjJZ6kP74jzbUZNjtwffwRdA+Id8rrLjT30d/7TrkW90k4zbXB5Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"preact": "~10.12.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@fullcalendar/daygrid": {
|
||||
"version": "6.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.15.tgz",
|
||||
"integrity": "sha512-j8tL0HhfiVsdtOCLfzK2J0RtSkiad3BYYemwQKq512cx6btz6ZZ2RNc/hVnIxluuWFyvx5sXZwoeTJsFSFTEFA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@fullcalendar/core": "~6.1.15"
|
||||
}
|
||||
},
|
||||
"node_modules/@fullcalendar/interaction": {
|
||||
"version": "6.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.15.tgz",
|
||||
"integrity": "sha512-DOTSkofizM7QItjgu7W68TvKKvN9PSEEvDJceyMbQDvlXHa7pm/WAVtAc6xSDZ9xmB1QramYoWGLHkCYbTW1rQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@fullcalendar/core": "~6.1.15"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
@ -13025,16 +12977,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.12.1",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
|
||||
"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@ -13073,34 +13015,6 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/primeflex": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.3.1.tgz",
|
||||
"integrity": "sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/primeicons": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/primeicons/-/primeicons-6.0.1.tgz",
|
||||
"integrity": "sha512-KDeO94CbWI4pKsPnYpA1FPjo79EsY9I+M8ywoPBSf9XMXoe/0crjbUK7jcQEDHuc0ZMRIZsxH3TYLv4TUtHmAA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/primeng": {
|
||||
"version": "17.18.0",
|
||||
"resolved": "https://registry.npmjs.org/primeng/-/primeng-17.18.0.tgz",
|
||||
"integrity": "sha512-EcvU/0Ex9QoBR6g6db9fDTCTAmzokW70TV5Oroy2gdvXRr3eqlflnOBoArQsmxTaw1oxSsu68YVj3RvcKYWhTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^17.0.0 || ^18.0.0",
|
||||
"@angular/core": "^17.0.0 || ^18.0.0",
|
||||
"@angular/forms": "^17.0.0 || ^18.0.0",
|
||||
"rxjs": "^6.0.0 || ^7.8.1",
|
||||
"zone.js": "~0.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proc-log": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz",
|
||||
|
@ -20,7 +20,7 @@
|
||||
"@abp/ng.setting-management": "~9.0.2",
|
||||
"@abp/ng.tenant-management": "~9.0.2",
|
||||
"@abp/ng.theme.lepton-x": "~4.0.3",
|
||||
"@abp/ng.theme.shared": "^9.0.4",
|
||||
"@abp/ng.theme.shared": "~9.0.2",
|
||||
"@angular/animations": "~18.1.0",
|
||||
"@angular/common": "~18.1.0",
|
||||
"@angular/compiler": "~18.1.0",
|
||||
@ -30,14 +30,7 @@
|
||||
"@angular/platform-browser": "~18.1.0",
|
||||
"@angular/platform-browser-dynamic": "~18.1.0",
|
||||
"@angular/router": "~18.1.0",
|
||||
"@fullcalendar/angular": "^6.1.16",
|
||||
"@fullcalendar/core": "^6.1.15",
|
||||
"@fullcalendar/daygrid": "^6.1.15",
|
||||
"@fullcalendar/interaction": "^6.1.15",
|
||||
"@swimlane/ngx-datatable": "^20.1.0", "bootstrap-icons": "~1.8.0",
|
||||
"primeflex": "^3.3.1",
|
||||
"primeicons": "^6.0.1",
|
||||
"primeng": "^17.18.0",
|
||||
"bootstrap-icons": "~1.8.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.14.0"
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { CustomUsersComponent } from './modules/custom-identity/custom-users/custom-users.component';
|
||||
import { CustomRolesComponent } from './modules/custom-identity/custom-roles/custom-roles.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -13,12 +11,10 @@ const routes: Routes = [
|
||||
path: 'account',
|
||||
loadChildren: () => import('@abp/ng.account').then(m => m.AccountModule.forLazy()),
|
||||
},
|
||||
{ path: 'identity/users', component: CustomUsersComponent },
|
||||
{ path: 'identity/roles', component: CustomRolesComponent },
|
||||
// {
|
||||
// path: 'identity',
|
||||
// loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),
|
||||
// },
|
||||
{
|
||||
path: 'identity',
|
||||
loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),
|
||||
},
|
||||
{
|
||||
path: 'tenant-management',
|
||||
loadChildren: () =>
|
||||
@ -29,37 +25,12 @@ const routes: Routes = [
|
||||
loadChildren: () =>
|
||||
import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()),
|
||||
},
|
||||
{
|
||||
path: 'appointment/appointment-calendar',
|
||||
loadChildren: () =>
|
||||
import('./appointment/appointment-calendar/appointment-calendar.module').then(
|
||||
m => m.AppointmentCalendarModule
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'appointment/view-appointment',
|
||||
loadChildren: () =>
|
||||
import('./appointment/view-appointment/view-appointment.module').then(
|
||||
m => m.ViewAppointmentModule
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
path: 'patients',
|
||||
loadChildren: () => import('./patients/patients.module').then(m => m.PatientsModule),
|
||||
},
|
||||
{
|
||||
path: 'departments',
|
||||
loadChildren: () => import('./departments/departments.module').then(m => m.DepartmentsModule),
|
||||
},
|
||||
{
|
||||
path: 'doctors',
|
||||
loadChildren: () => import('./doctors/doctors.module').then(m => m.DoctorsModule),
|
||||
},
|
||||
{
|
||||
path: 'rooms',
|
||||
loadChildren: () => import('./room-management/room-management.module').then(m => m.RoomManagementModule),
|
||||
},
|
||||
{ path: 'appointment/appointment-calendar',
|
||||
loadChildren: () =>
|
||||
import('./appointment/appointment-calendar/appointment-calendar.module').then(m => m.AppointmentCalendarModule) },
|
||||
{ path: 'appointment/view-appointment', loadChildren: () => import('./appointment/view-appointment/view-appointment.module').then(m => m.ViewAppointmentModule) },
|
||||
{ path: 'appointment/book-appointment', loadChildren: () => import('./appointment/book-appointment/book-appointment.module').then(m => m.BookAppointmentModule) },
|
||||
{ path: 'appointment/edit-appointment', loadChildren: () => import('./appointment/edit-appointment/edit-appointment.module').then(m => m.EditAppointmentModule) },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -29,11 +29,13 @@ import { AccountLayoutModule } from '@abp/ng.theme.lepton-x/account';
|
||||
AppRoutingModule,
|
||||
CoreModule,
|
||||
ThemeSharedModule,
|
||||
|
||||
|
||||
|
||||
|
||||
InternetConnectionStatusComponent,
|
||||
ThemeLeptonXModule.forRoot(),
|
||||
SideMenuLayoutModule.forRoot(),
|
||||
AccountLayoutModule.forRoot(),
|
||||
ThemeLeptonXModule.forRoot(),
|
||||
SideMenuLayoutModule.forRoot(),
|
||||
AccountLayoutModule.forRoot(),
|
||||
],
|
||||
declarations: [AppComponent],
|
||||
providers: [
|
||||
|
@ -1,6 +1 @@
|
||||
<full-calendar [options]="calendarOptions" style="cursor: pointer;text-decoration:none;"></full-calendar>
|
||||
|
||||
|
||||
<app-appointment-dialog [selectedDate]="selectedDate"
|
||||
[appointmentId]="appointmentIdToEdit"[isEditMode]="isEditMode"
|
||||
[visible]="isModalVisible"*ngIf="isModalVisible" [name]="'Appointment'" (close)="closeDialog()"></app-appointment-dialog>
|
||||
<p>appointment-calendar works!</p>
|
||||
|
@ -1,8 +0,0 @@
|
||||
.fc.fc-theme-standard .fc-view-harness .fc-event.fc-daygrid-block-event {
|
||||
color: #ffffff;
|
||||
background: #2563eb;
|
||||
border-color: #2563eb;
|
||||
}
|
||||
.fc-daygrid-day-number {
|
||||
text-decoration: none !important;
|
||||
}
|
@ -1,109 +1,10 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { CalendarOptions } from '@fullcalendar/core'; // useful for typechecking
|
||||
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||
import interactionPlugin from '@fullcalendar/interaction';
|
||||
import { AppointmentService } from '@proxy/appointments';
|
||||
import { PagingSortResultDto } from '@proxy/dto';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-appointment-calendar',
|
||||
templateUrl: './appointment-calendar.component.html',
|
||||
styleUrl: './appointment-calendar.component.scss',
|
||||
styleUrl: './appointment-calendar.component.scss'
|
||||
})
|
||||
export class AppointmentCalendarComponent implements OnInit {
|
||||
appointments: any[] = [];
|
||||
params: PagingSortResultDto;
|
||||
selectedDate: string;
|
||||
isModalVisible: boolean = false;
|
||||
appointmentIdToEdit: string;
|
||||
isEditMode: boolean = false;
|
||||
export class AppointmentCalendarComponent {
|
||||
|
||||
constructor(private appointmentService: AppointmentService) {}
|
||||
ngOnInit() {
|
||||
this.loadAppointments({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
}
|
||||
loadAppointments(event: any) {
|
||||
let order = event.sortOrder == 1 ? ' asc' : ' desc';
|
||||
this.params = {
|
||||
skipCount: event.first,
|
||||
maxResultCount: event.rows,
|
||||
sorting: event.sortField + order,
|
||||
search: event.globalFilter == null ? '' : event.globalFilter,
|
||||
};
|
||||
this.appointmentService.getAppointmentList(this.params).subscribe(data => {
|
||||
this.appointments = data.items;
|
||||
this.updateCalendarEvents();
|
||||
});
|
||||
}
|
||||
updateCalendarEvents() {
|
||||
this.calendarOptions = {
|
||||
initialView: 'dayGridMonth',
|
||||
plugins: [dayGridPlugin, interactionPlugin],
|
||||
events: this.appointments.map(appointment => ({
|
||||
id: appointment.id,
|
||||
title: `${appointment.firstName} ${appointment.lastName}`,
|
||||
date: this.combineDateTime(appointment.dateOfAppointment, appointment.timeOfAppointment),
|
||||
})),
|
||||
dateClick: arg => this.handleDateClick(arg),
|
||||
eventClick: info => this.onEventClick(info),
|
||||
eventContent: function(arg) {
|
||||
return {
|
||||
html: `<div style="background-color: #3788d8; color: white; padding: 5px;cursor: pointer; border-radius: 5px;">
|
||||
${arg.event.title}
|
||||
</div>`
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
combineDateTime(dateStr: string, timeStr: string): string {
|
||||
if (!timeStr) return dateStr;
|
||||
const date = new Date(dateStr);
|
||||
const [hours, minutes] = timeStr.split(':');
|
||||
date.setHours(parseInt(hours, 10), parseInt(minutes, 10), 0);
|
||||
return date.toISOString();
|
||||
}
|
||||
|
||||
calendarOptions: CalendarOptions = {
|
||||
initialView: 'dayGridMonth',
|
||||
plugins: [dayGridPlugin, interactionPlugin],
|
||||
dateClick: arg => this.handleDateClick(arg),
|
||||
events: [
|
||||
{ title: 'event 1', date: '2025-01-01' },
|
||||
],
|
||||
};
|
||||
closeDialog() {
|
||||
this.isModalVisible = false;
|
||||
this.loadAppointments({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
}
|
||||
handleDateClick(arg) {
|
||||
this.selectedDate = arg.dateStr;
|
||||
this.isModalVisible = true;
|
||||
this.isEditMode = false;
|
||||
|
||||
}
|
||||
onModalClose() {
|
||||
this.isModalVisible = false;
|
||||
this.isEditMode = false;
|
||||
this.appointmentIdToEdit = "";
|
||||
|
||||
}
|
||||
|
||||
onEventClick(info: any) {
|
||||
this.appointmentIdToEdit = info.event.id;
|
||||
this.isEditMode = true;
|
||||
this.isModalVisible = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { FullCalendarModule } from '@fullcalendar/angular';
|
||||
|
||||
import { AppointmentCalendarRoutingModule } from './appointment-calendar-routing.module';
|
||||
import { AppointmentCalendarComponent } from './appointment-calendar.component';
|
||||
import { AppointmentDialogComponent } from '../appointment-dialog/appointment-dialog.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -14,10 +11,7 @@ import { AppointmentDialogComponent } from '../appointment-dialog/appointment-di
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppointmentCalendarRoutingModule,
|
||||
RouterOutlet,
|
||||
FullCalendarModule,
|
||||
AppointmentDialogComponent
|
||||
AppointmentCalendarRoutingModule
|
||||
]
|
||||
})
|
||||
export class AppointmentCalendarModule { }
|
||||
|
@ -1,446 +0,0 @@
|
||||
<div
|
||||
class="modal fade show d-block"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
style="background: rgba(0, 0, 0, 0.5)"
|
||||
*ngIf="visible"
|
||||
aria-label="l('name')"
|
||||
>
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header py-4">
|
||||
<h4 class="text-success mb-0 fs-3 fw-normal">
|
||||
{{ isEditMode ? ('::edit' | abpLocalization) : ('::create' | abpLocalization) }}
|
||||
{{ '::appointmentStatus' | abpLocalization }}
|
||||
</h4>
|
||||
<button
|
||||
tabindex="0"
|
||||
type="button"
|
||||
(click)="onClose()"
|
||||
class="btn-close"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
<form #appointmentForm="ngForm" (ngSubmit)="saveAppointment(appointmentForm)">
|
||||
<div class="p-fluid grid justify-content-center">
|
||||
<div class="field col-md-5">
|
||||
<label for="fname"
|
||||
>{{ '::firstName' | abpLocalization }}<span class="text-danger">*</span></label
|
||||
>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-user"></i>
|
||||
<input
|
||||
autofocus
|
||||
pInputText
|
||||
id="fname"
|
||||
name="fname"
|
||||
[(ngModel)]="appointment.firstName"
|
||||
#fnameCtrl="ngModel"
|
||||
required
|
||||
minlength="2"
|
||||
maxlength="30"
|
||||
[ngClass]="{
|
||||
'is-valid': fnameCtrl.valid && fnameCtrl.touched,
|
||||
'is-invalid': fnameCtrl.invalid && fnameCtrl.touched
|
||||
}"
|
||||
/>
|
||||
<i *ngIf="fnameCtrl.valid && fnameCtrl.touched" class="pi pi-check text-success"></i>
|
||||
<i *ngIf="fnameCtrl.invalid && fnameCtrl.touched" class="pi pi-times text-danger"></i>
|
||||
</span>
|
||||
|
||||
<small class="text-danger" *ngIf="fnameCtrl.invalid && fnameCtrl.touched">
|
||||
<span *ngIf="fnameCtrl.errors?.required">{{
|
||||
'::firstNameRequired' | abpLocalization
|
||||
}}</span>
|
||||
<span *ngIf="fnameCtrl.errors?.minlength">
|
||||
{{
|
||||
'::minLength'
|
||||
| abpLocalization : { length: fnameCtrl.errors?.minlength.requiredLength }
|
||||
}}
|
||||
</span>
|
||||
<span *ngIf="fnameCtrl.errors?.maxlength">{{
|
||||
'::maxLength'
|
||||
| abpLocalization : { length: fnameCtrl.errors?.maxlength.requiredLength }
|
||||
}}</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="lname"
|
||||
>{{ '::lastName' | abpLocalization }}<span class="text-danger">*</span></label
|
||||
>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-user"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="lname"
|
||||
name="lname"
|
||||
[(ngModel)]="appointment.lastName"
|
||||
#lnameCtrl="ngModel"
|
||||
required
|
||||
minlength="2"
|
||||
maxlength="30"
|
||||
[ngClass]="{
|
||||
'is-valid': lnameCtrl.valid && lnameCtrl.touched,
|
||||
'is-invalid': lnameCtrl.invalid && lnameCtrl.touched
|
||||
}"
|
||||
/>
|
||||
<i *ngIf="lnameCtrl.valid && lnameCtrl.touched" class="pi pi-check text-success"></i>
|
||||
<i *ngIf="lnameCtrl.invalid && lnameCtrl.touched" class="pi pi-times text-danger"></i>
|
||||
</span>
|
||||
<small class="text-danger" *ngIf="lnameCtrl.invalid && lnameCtrl.touched">
|
||||
<span *ngIf="lnameCtrl.errors?.required">{{
|
||||
'::lastNameRequired' | abpLocalization
|
||||
}}</span>
|
||||
<span *ngIf="lnameCtrl.errors?.minlength">
|
||||
{{
|
||||
'::minLength'
|
||||
| abpLocalization : { length: lnameCtrl.errors?.minlength.requiredLength }
|
||||
}}
|
||||
</span>
|
||||
<span *ngIf="lnameCtrl.errors?.maxlength">
|
||||
{{
|
||||
'::maxLength'
|
||||
| abpLocalization : { length: lnameCtrl.errors?.maxlength.requiredLength }
|
||||
}}
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label>{{ '::gender' | abpLocalization }} <span class="text-danger">*</span></label>
|
||||
<div class="flex align-items-center p-input-icon-right">
|
||||
<p-radioButton
|
||||
name="gender"
|
||||
value="1"
|
||||
[(ngModel)]="appointment.gender"
|
||||
inputId="male"
|
||||
#genderCtrl="ngModel"
|
||||
required
|
||||
></p-radioButton>
|
||||
<label for="male" class="ml-2 mr-3">{{ '::male' | abpLocalization }}</label>
|
||||
|
||||
<p-radioButton
|
||||
name="gender"
|
||||
value="2"
|
||||
[(ngModel)]="appointment.gender"
|
||||
inputId="female"
|
||||
required
|
||||
></p-radioButton>
|
||||
<label for="female" class="ml-2">{{ '::female' | abpLocalization }}</label>
|
||||
|
||||
<i
|
||||
*ngIf="genderCtrl.valid && genderCtrl.touched"
|
||||
class="pi pi-check text-success ml-2"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="genderCtrl.invalid && genderCtrl.touched"
|
||||
class="pi pi-times text-danger ml-2"
|
||||
></i>
|
||||
</div>
|
||||
<small class="text-danger d-block" *ngIf="genderCtrl.invalid && genderCtrl.touched">
|
||||
{{ '::genderRequired' | abpLocalization }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="mobile"
|
||||
>{{ '::mobileNo' | abpLocalization }} <span class="text-danger">*</span></label
|
||||
>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-phone"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="mobile"
|
||||
name="mobile"
|
||||
[(ngModel)]="appointment.mobile"
|
||||
#mobileCtrl="ngModel"
|
||||
required
|
||||
pattern="^[0-9]{10}$"
|
||||
[ngClass]="{
|
||||
'is-valid': mobileCtrl.valid && mobileCtrl.touched,
|
||||
'is-invalid': mobileCtrl.invalid && mobileCtrl.touched
|
||||
}"
|
||||
/>
|
||||
<i
|
||||
*ngIf="mobileCtrl.valid && mobileCtrl.touched"
|
||||
class="pi pi-check text-success"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="mobileCtrl.invalid && mobileCtrl.touched"
|
||||
class="pi pi-times text-danger"
|
||||
></i>
|
||||
</span>
|
||||
<small class="text-danger d-block" *ngIf="mobileCtrl.invalid && mobileCtrl.touched">
|
||||
<span *ngIf="mobileCtrl.errors?.required">{{
|
||||
'::mobileNoRequired' | abpLocalization
|
||||
}}</span>
|
||||
<span *ngIf="mobileCtrl.errors?.pattern">{{
|
||||
'::mobileNoInvalid' | abpLocalization
|
||||
}}</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-11">
|
||||
{{ '::address' | abpLocalization }} <span class="text-danger">*</span>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-map-marker"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="address"
|
||||
name="address"
|
||||
[(ngModel)]="appointment.address"
|
||||
#addressCtrl="ngModel"
|
||||
required
|
||||
minlength="5"
|
||||
maxlength="100"
|
||||
[ngClass]="{
|
||||
'is-valid': addressCtrl.valid && addressCtrl.touched,
|
||||
'is-invalid': addressCtrl.invalid && addressCtrl.touched
|
||||
}"
|
||||
/>
|
||||
<i
|
||||
*ngIf="addressCtrl.valid && addressCtrl.touched"
|
||||
class="pi pi-check text-success"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="addressCtrl.invalid && addressCtrl.touched"
|
||||
class="pi pi-times text-danger"
|
||||
></i>
|
||||
</span>
|
||||
<small class="text-danger d-block" *ngIf="addressCtrl.invalid && addressCtrl.touched">
|
||||
<span *ngIf="addressCtrl.errors?.required">{{
|
||||
'::addressRequired' | abpLocalization
|
||||
}}</span>
|
||||
<span *ngIf="addressCtrl.errors?.minlength">
|
||||
{{
|
||||
'::minLength'
|
||||
| abpLocalization : { length: addressCtrl.errors?.minlength.requiredLength }
|
||||
}}
|
||||
</span>
|
||||
<span *ngIf="addressCtrl.errors?.maxlength">
|
||||
{{
|
||||
'::maxLength'
|
||||
| abpLocalization : { length: addressCtrl.errors?.maxlength.requiredLength }
|
||||
}}
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="email"
|
||||
>{{ '::emailId' | abpLocalization }} <span class="text-danger">*</span></label
|
||||
>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-envelope"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="email"
|
||||
name="email"
|
||||
[(ngModel)]="appointment.email"
|
||||
#emailCtrl="ngModel"
|
||||
required
|
||||
email
|
||||
[ngClass]="{
|
||||
'is-valid': emailCtrl.valid && emailCtrl.touched,
|
||||
'is-invalid': emailCtrl.invalid && emailCtrl.touched
|
||||
}"
|
||||
/>
|
||||
<i *ngIf="emailCtrl.valid && emailCtrl.touched" class="pi pi-check text-success"></i>
|
||||
<i *ngIf="emailCtrl.invalid && emailCtrl.touched" class="pi pi-times text-danger"></i>
|
||||
</span>
|
||||
<small class="text-danger d-block" *ngIf="emailCtrl.invalid && emailCtrl.touched">
|
||||
<span *ngIf="emailCtrl.errors?.required">{{
|
||||
'::emailRequired' | abpLocalization
|
||||
}}</span>
|
||||
<span *ngIf="emailCtrl.errors?.email">{{ '::emailInvalid' | abpLocalization }}</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="dob"
|
||||
>{{ '::dateOfBirth' | abpLocalization }} <span class="text-danger">*</span></label
|
||||
>
|
||||
<p-calendar
|
||||
id="dob"
|
||||
required
|
||||
name="dob"
|
||||
[(ngModel)]="Dateofbirth"
|
||||
[showIcon]="true"
|
||||
required
|
||||
></p-calendar>
|
||||
<small
|
||||
class="p-error"
|
||||
*ngIf="appointmentForm.controls.dob?.invalid && appointmentForm.controls.dob?.touched"
|
||||
>
|
||||
{{ '::dobRequired' | abpLocalization }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="doctor"
|
||||
>{{ '::consultingDoctor' | abpLocalization }}
|
||||
<span class="text-danger">*</span></label
|
||||
>
|
||||
<p-dropdown
|
||||
id="doctor"
|
||||
name="doctor"
|
||||
[(ngModel)]="appointment.doctorId"
|
||||
[options]="doctorOptions"
|
||||
placeholder="Select Doctor"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
required
|
||||
></p-dropdown>
|
||||
</div>
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="date"
|
||||
>{{ '::dateOfAppointment' | abpLocalization }}
|
||||
<span class="text-danger">*</span></label
|
||||
>
|
||||
<p-calendar
|
||||
id="date"
|
||||
name="date"
|
||||
[(ngModel)]="AppointmentDate"
|
||||
[showIcon]="true"
|
||||
required
|
||||
></p-calendar>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="time"
|
||||
>{{ '::timeOfAppointment' | abpLocalization }}
|
||||
<span class="text-danger">*</span></label
|
||||
>
|
||||
<span class="p-input-icon-left">
|
||||
<i class="pi pi-clock"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="time"
|
||||
name="time"
|
||||
type="time"
|
||||
[(ngModel)]="appointment.timeOfAppointment"
|
||||
required
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="injury">{{ '::injuryCondition' | abpLocalization }}</label>
|
||||
<span class="p-input-icon-left">
|
||||
<i class="pi pi-exclamation-triangle"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="injury"
|
||||
name="injury"
|
||||
[(ngModel)]="appointment.injuryORContion"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="insurance">
|
||||
{{ '::insuranceProvider' | abpLocalization }}
|
||||
</label>
|
||||
<span class="p-input-icon-left">
|
||||
<i class="pi pi-credit-card"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="insurance"
|
||||
name="insuranceProvider"
|
||||
[(ngModel)]="appointment.insuranceProvider"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="status">
|
||||
{{ '::appointmentStatus' | abpLocalization }}
|
||||
</label>
|
||||
<p-dropdown
|
||||
id="status"
|
||||
name="status"
|
||||
[(ngModel)]="appointment.appointmentStatus"
|
||||
[options]="appointmentStatuses"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="Select Status"
|
||||
></p-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="visitType">
|
||||
{{ '::visitType' | abpLocalization }}
|
||||
</label>
|
||||
<p-dropdown
|
||||
id="visitType"
|
||||
name="visitType"
|
||||
[(ngModel)]="appointment.visitType"
|
||||
[options]="visitTypes"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="Select Visit Type"
|
||||
></p-dropdown>
|
||||
</div>
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="paymentStatus">
|
||||
{{ '::paymentStatus' | abpLocalization }}
|
||||
</label>
|
||||
<p-dropdown
|
||||
id="paymentStatus"
|
||||
name="paymentStatus"
|
||||
[(ngModel)]="appointment.paymentStatus"
|
||||
[options]="paymentStatuses"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="Select Payment Status"
|
||||
></p-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="field col-11">
|
||||
<label for="notes">
|
||||
{{ '::notes' | abpLocalization }}
|
||||
</label>
|
||||
<textarea
|
||||
id="notes"
|
||||
name="notes"
|
||||
[(ngModel)]="appointment.note"
|
||||
rows="5"
|
||||
cols="30"
|
||||
pInputTextarea
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="field col-11 flex justify-content-end">
|
||||
<button
|
||||
pButton
|
||||
type="submit"
|
||||
[label]="'::save' | abpLocalization"
|
||||
class="p-button-success"
|
||||
[disabled]="appointmentForm.invalid"
|
||||
></button>
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
[label]="'::cancel' | abpLocalization"
|
||||
class="p-button-secondary ml-2"
|
||||
(click)="onClose()"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,21 +0,0 @@
|
||||
.hide_body{
|
||||
scrollbar-width: auto !important;
|
||||
min-height: 150px;
|
||||
max-height: calc(100vh - 13rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.is-valid {
|
||||
border-color: green !important;
|
||||
}
|
||||
.is-invalid {
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
.p-calendar .p-datepicker {
|
||||
z-index: 1050 !important;
|
||||
}
|
||||
|
||||
.modal {
|
||||
z-index: 1040;
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AppointmentDialogComponent } from './appointment-dialog.component';
|
||||
|
||||
describe('AppointmentDialogComponent', () => {
|
||||
let component: AppointmentDialogComponent;
|
||||
let fixture: ComponentFixture<AppointmentDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AppointmentDialogComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AppointmentDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,187 +0,0 @@
|
||||
import {ToasterService } from '@abp/ng.theme.shared';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { FormsModule, NgForm } from '@angular/forms';
|
||||
import { CreateOrUpdateAppointmentDto } from '@proxy/appoinments/dto';
|
||||
import { Gender, appointmentStatus, visitType, paymentStatus } from '@proxy/global-enum';
|
||||
import { DoctorService } from '@proxy/doctors';
|
||||
import { AppointmentService } from '@proxy/appointments';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CalendarModule } from 'primeng/calendar';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { DialogModule } from 'primeng/dialog';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { RadioButtonModule } from 'primeng/radiobutton';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { ViewAppointmentRoutingModule } from '../view-appointment/view-appointment-routing.module';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'app-appointment-dialog',
|
||||
standalone: true,
|
||||
imports: [
|
||||
ViewAppointmentRoutingModule,
|
||||
TableModule,
|
||||
DialogModule,
|
||||
FormsModule,
|
||||
TableModule,
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
InputTextModule,
|
||||
CalendarModule,
|
||||
DropdownModule,
|
||||
RadioButtonModule,
|
||||
InputTextareaModule,
|
||||
ChipModule,
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
],
|
||||
templateUrl: './appointment-dialog.component.html',
|
||||
styleUrl: './appointment-dialog.component.scss',
|
||||
})
|
||||
export class AppointmentDialogComponent implements OnInit {
|
||||
@Input() visible: boolean = false;
|
||||
@Input() name: string;
|
||||
@Input() isEditMode: boolean = false;
|
||||
@Output() save = new EventEmitter<any>();
|
||||
@Output() close = new EventEmitter<void>();
|
||||
@Input() appointmentId: string;
|
||||
@Input() selectedDate: string;
|
||||
appointmentsForDate: any[] = [];
|
||||
|
||||
loading: boolean = false;
|
||||
AppointmentDialogTitle: string = '';
|
||||
AppointmentDialog: boolean = false;
|
||||
genders = Gender;
|
||||
Dateofbirth: Date = null;
|
||||
AppointmentDate: Date = null;
|
||||
doctors = [];
|
||||
doctorOptions = [];
|
||||
constructor(
|
||||
private DoctorService: DoctorService,
|
||||
private AppointmentService: AppointmentService,
|
||||
private toaster: ToasterService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getdoctorlist();
|
||||
if (!this.isEditMode) {
|
||||
this.appointmentId = '';
|
||||
|
||||
this.appointment = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
gender: Gender.Male,
|
||||
dateOfAppointment: '',
|
||||
dob: '',
|
||||
timeOfAppointment: '',
|
||||
mobile: '',
|
||||
injuryORContion: '',
|
||||
note: '',
|
||||
doctorId: '',
|
||||
address: '',
|
||||
appointmentStatus: null,
|
||||
visitType: null,
|
||||
paymentStatus: null,
|
||||
insuranceProvider: '',
|
||||
};
|
||||
} else {
|
||||
this.fetchAppointmentData();
|
||||
}
|
||||
}
|
||||
|
||||
fetchAppointmentData() {
|
||||
this.AppointmentService.getAppointmentById(this.appointmentId).subscribe(result => {
|
||||
this.appointment = result;
|
||||
this.AppointmentDate = new Date(result.dateOfAppointment);
|
||||
this.Dateofbirth = new Date(result.dob);
|
||||
});
|
||||
}
|
||||
appointment: CreateOrUpdateAppointmentDto = {
|
||||
id: '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
gender: Gender.Male,
|
||||
mobile: '',
|
||||
address: '',
|
||||
dob: '',
|
||||
doctorId: '',
|
||||
dateOfAppointment: '',
|
||||
timeOfAppointment: '',
|
||||
injuryORContion: '',
|
||||
note: '',
|
||||
appointmentStatus: appointmentStatus.Scheduled,
|
||||
visitType: visitType.NewPatient,
|
||||
paymentStatus: paymentStatus.Unpaid,
|
||||
insuranceProvider: '',
|
||||
};
|
||||
|
||||
saveAppointment(form: NgForm) {
|
||||
if (form.invalid) {
|
||||
Object.values(form.controls).forEach(control => control.markAsTouched());
|
||||
return;
|
||||
}
|
||||
this.appointment.dob = this.Dateofbirth.toDateString();
|
||||
this.appointment.dateOfAppointment = this.AppointmentDate.toDateString();
|
||||
if (this.isEditMode) {
|
||||
this.AppointmentService.updateAppointment(this.appointment).subscribe(
|
||||
() => {
|
||||
this.toaster.success('Appointment updated successfully', 'Success');
|
||||
this.AppointmentDialog = false;
|
||||
this.onClose();
|
||||
},
|
||||
error => {
|
||||
this.toaster.error(error, 'Error');
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.AppointmentService.createAppointment(this.appointment).subscribe(
|
||||
() => {
|
||||
this.toaster.success('Appointment created successfully', 'Success');
|
||||
this.AppointmentDialog = false;
|
||||
this.onClose();
|
||||
},
|
||||
error => {
|
||||
this.toaster.error(error, 'Error');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
getdoctorlist() {
|
||||
this.DoctorService.get().subscribe(result => {
|
||||
this.doctors = result;
|
||||
this.doctorOptions = this.doctors.map(doctor => ({
|
||||
label: `Dr. ${doctor.firstName} ${doctor.lastName}`,
|
||||
value: doctor.id,
|
||||
}));
|
||||
});
|
||||
}
|
||||
appointmentStatuses = Object.keys(appointmentStatus)
|
||||
.filter(key => !isNaN(Number(key)))
|
||||
.map(key => ({
|
||||
label: appointmentStatus[key as unknown as keyof typeof appointmentStatus],
|
||||
value: Number(key),
|
||||
}));
|
||||
visitTypes = Object.keys(visitType)
|
||||
.filter(key => !isNaN(Number(key)))
|
||||
.map(key => ({
|
||||
label: visitType[key as unknown as keyof typeof visitType],
|
||||
value: Number(key),
|
||||
}));
|
||||
paymentStatuses = Object.keys(paymentStatus)
|
||||
.filter(key => !isNaN(Number(key)))
|
||||
.map(key => ({
|
||||
label: paymentStatus[key as unknown as keyof typeof paymentStatus],
|
||||
value: Number(key),
|
||||
}));
|
||||
|
||||
onClose() {
|
||||
this.AppointmentDialog = false;
|
||||
this.appointmentId = '';
|
||||
this.close.emit();
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
children: [
|
||||
{
|
||||
path: 'appointment-calendar',
|
||||
loadChildren: () => import('./appointment-calendar/appointment-calendar.module').then(m => m.AppointmentCalendarModule)
|
||||
},
|
||||
{
|
||||
path: 'view-appointment:date',
|
||||
loadChildren: () => import('./view-appointment/view-appointment.module').then(m => m.ViewAppointmentModule)
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppointmentRoutingModule { }
|
@ -1,18 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { AppointmentRoutingModule } from './appointment-routing.module';
|
||||
import { AppointmentDialogComponent } from './appointment-dialog/appointment-dialog.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
AppointmentRoutingModule,
|
||||
AppointmentDialogComponent
|
||||
]
|
||||
})
|
||||
export class AppointmentModule { }
|
@ -0,0 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { BookAppointmentComponent } from './book-appointment.component';
|
||||
|
||||
const routes: Routes = [{ path: '', component: BookAppointmentComponent }];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class BookAppointmentRoutingModule { }
|
@ -0,0 +1 @@
|
||||
<p>book-appointment works!</p>
|
@ -1,18 +1,18 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ShiftManagementComponent } from './shift-management.component';
|
||||
import { BookAppointmentComponent } from './book-appointment.component';
|
||||
|
||||
describe('ShiftManagementComponent', () => {
|
||||
let component: ShiftManagementComponent;
|
||||
let fixture: ComponentFixture<ShiftManagementComponent>;
|
||||
describe('BookAppointmentComponent', () => {
|
||||
let component: BookAppointmentComponent;
|
||||
let fixture: ComponentFixture<BookAppointmentComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ShiftManagementComponent]
|
||||
declarations: [BookAppointmentComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ShiftManagementComponent);
|
||||
fixture = TestBed.createComponent(BookAppointmentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-book-appointment',
|
||||
templateUrl: './book-appointment.component.html',
|
||||
styleUrl: './book-appointment.component.scss'
|
||||
})
|
||||
export class BookAppointmentComponent {
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { BookAppointmentRoutingModule } from './book-appointment-routing.module';
|
||||
import { BookAppointmentComponent } from './book-appointment.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
BookAppointmentComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
BookAppointmentRoutingModule
|
||||
]
|
||||
})
|
||||
export class BookAppointmentModule { }
|
@ -0,0 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { EditAppointmentComponent } from './edit-appointment.component';
|
||||
|
||||
const routes: Routes = [{ path: '', component: EditAppointmentComponent }];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class EditAppointmentRoutingModule { }
|
@ -0,0 +1 @@
|
||||
<p>edit-appointment works!</p>
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EditAppointmentComponent } from './edit-appointment.component';
|
||||
|
||||
describe('EditAppointmentComponent', () => {
|
||||
let component: EditAppointmentComponent;
|
||||
let fixture: ComponentFixture<EditAppointmentComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [EditAppointmentComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(EditAppointmentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit-appointment',
|
||||
templateUrl: './edit-appointment.component.html',
|
||||
styleUrl: './edit-appointment.component.scss'
|
||||
})
|
||||
export class EditAppointmentComponent {
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { EditAppointmentRoutingModule } from './edit-appointment-routing.module';
|
||||
import { EditAppointmentComponent } from './edit-appointment.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
EditAppointmentComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
EditAppointmentRoutingModule
|
||||
]
|
||||
})
|
||||
export class EditAppointmentModule { }
|
@ -1,144 +1 @@
|
||||
<div>
|
||||
<p-table
|
||||
#dt2
|
||||
dataKey="id"
|
||||
[value]="appointments"
|
||||
[paginator]="true"
|
||||
[rows]="10"
|
||||
[totalRecords]="totalRecords"
|
||||
[lazy]="true"
|
||||
(onLazyLoad)="loadappointments($event)"
|
||||
[rowsPerPageOptions]="[10, 20, 50]"
|
||||
[responsiveLayout]="'scroll'"
|
||||
[globalFilterFields]="['id', 'name', 'status']"
|
||||
[filters]="{ global: { value: '', matchMode: 'contains' } }"
|
||||
class="table table-striped"
|
||||
>
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="flex align-items-center justify-content-between mb-3 gap-3">
|
||||
<h4 class="m-0">{{ '::appointmentList' | abpLocalization }}</h4>
|
||||
|
||||
<div class="flex-grow-1 flex justify-content-center">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="pi pi-search"></i></span>
|
||||
<input
|
||||
pInputText
|
||||
type="text"
|
||||
class="form-control"
|
||||
(input)="dt2.filterGlobal($event.target.value, 'contains')"
|
||||
[(ngModel)]="globalFilter"
|
||||
placeholder="{{ '::searchKeyword' | abpLocalization }}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
pButton
|
||||
class="p-button-rounded p-button-secondary ml-2"
|
||||
(click)="loadappointments($event)"
|
||||
[title]="'::refresh' | abpLocalization"
|
||||
|
||||
>
|
||||
<i class="pi pi-refresh"></i>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="createPermission"
|
||||
pButton
|
||||
class="p-button-rounded p-button-success ml-2"
|
||||
(click)="openNewAppointmentDialog()"
|
||||
pTooltip="{{ '::addAppointment' | abpLocalization }}"
|
||||
tooltipPosition="left"
|
||||
>
|
||||
<i class="pi pi-plus-circle"></i>
|
||||
</button>
|
||||
<button
|
||||
pButton
|
||||
class="p-button-rounded p-button-warning ml-2"
|
||||
(click)="exportAppointments()"
|
||||
[title]="'::export' | abpLocalization"
|
||||
|
||||
>
|
||||
<i class="pi pi-download"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th pSortableColumn="firstName">
|
||||
{{ '::name' | abpLocalization }} <p-sortIcon field="firstName" />
|
||||
</th>
|
||||
<th pSortableColumn="doctor">
|
||||
{{ '::doctor' | abpLocalization }} <p-sortIcon field="doctor" />
|
||||
</th>
|
||||
<th pSortableColumn="gender">
|
||||
{{ '::gender' | abpLocalization }} <p-sortIcon field="gender" />
|
||||
</th>
|
||||
<th pSortableColumn="dateOfAppointment">
|
||||
{{ '::date' | abpLocalization }} <p-sortIcon field="dateOfAppointment" />
|
||||
</th>
|
||||
<th pSortableColumn="timeOfAppointment">
|
||||
{{ '::time' | abpLocalization }} <p-sortIcon field="timeOfAppointment" />
|
||||
</th>
|
||||
<th>{{ '::mobileNo' | abpLocalization }}</th>
|
||||
<th>{{ '::email' | abpLocalization }}</th>
|
||||
<th pSortableColumn="appointmentStatus">
|
||||
{{ '::appointmentStatus' | abpLocalization }} <p-sortIcon field="appointmentStatus" />
|
||||
</th>
|
||||
<th pSortableColumn="visitType">
|
||||
{{ '::visitType' | abpLocalization }} <p-sortIcon field="visitType" />
|
||||
</th>
|
||||
<th>{{ '::actions' | abpLocalization }}</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="body" let-appointment>
|
||||
<tr>
|
||||
<td>{{ appointment.firstName }} {{ appointment.lastName }}</td>
|
||||
<td>{{ '::doctorPrefix' | abpLocalization }} {{ appointment.doctor.firstName }} {{ appointment.doctor.lastName }}</td>
|
||||
<td>
|
||||
<span class="badge" [ngClass]="appointment.gender === 1 ? 'bg-primary' : 'bg-pink'">
|
||||
{{ getGenderLabel(appointment.gender) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ appointment.dateOfAppointment | date }}</td>
|
||||
<td>{{ appointment.timeOfAppointment }}</td>
|
||||
<td>{{ appointment.mobile }}</td>
|
||||
<td>{{ appointment.email }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="badge"
|
||||
[ngClass]="
|
||||
appointment.appointmentStatus === 1
|
||||
? 'bg-success'
|
||||
: appointment.appointmentStatus === 2
|
||||
? 'bg-primary'
|
||||
: 'bg-danger'
|
||||
"
|
||||
>
|
||||
{{ getStatusLabel(appointment.appointmentStatus) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge" [ngClass]="appointment.visitType === 1 ? 'bg-success' : 'bg-danger'">
|
||||
{{ getVisitTypeLabel(appointment.visitType) }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="d-flex">
|
||||
<button class="btn btn-warning btn-sm ml-1" (click)="editAppointment(appointment)">
|
||||
<i class="pi pi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger btn-sm ml-1" (click)="deleteAppointment(appointment.id)">
|
||||
<i class="pi pi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
|
||||
<app-appointment-dialog [appointmentId]="appointmentIdToEdit"[isEditMode]="isEditMode"
|
||||
[visible]="isModalVisible"*ngIf="isModalVisible"
|
||||
[name]="'::appointment' | abpLocalization"
|
||||
(close)="closeDialog()"></app-appointment-dialog>
|
||||
<p>view-appointment works!</p>
|
||||
|
@ -1,44 +0,0 @@
|
||||
.is-valid {
|
||||
border-color: green !important;
|
||||
}
|
||||
.is-invalid {
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.male {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.female {
|
||||
background-color: purple;
|
||||
}
|
||||
|
||||
.pdf-icon {
|
||||
color: red;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.bg-pink {
|
||||
background-color: #ff4081 !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.gap-1 {
|
||||
gap: 5px;
|
||||
}
|
@ -1,192 +1,10 @@
|
||||
import { Confirmation, ConfirmationService, ToasterService } from '@abp/ng.theme.shared';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component } from '@angular/core';
|
||||
import { CreateOrUpdateAppointmentDto } from '@proxy/appoinments/dto';
|
||||
import { AppointmentService } from '@proxy/appointments';
|
||||
import { DoctorService } from '@proxy/doctors';
|
||||
import { PagingSortResultDto } from '@proxy/dto';
|
||||
import { appointmentStatus, Gender, paymentStatus, visitType } from '@proxy/global-enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-view-appointment',
|
||||
templateUrl: './view-appointment.component.html',
|
||||
styleUrl: './view-appointment.component.scss',
|
||||
styleUrl: './view-appointment.component.scss'
|
||||
})
|
||||
export class ViewAppointmentComponent {
|
||||
totalRecords: number = 0;
|
||||
appointmentIdToEdit: string;
|
||||
AppointmentDialogTitle: string = '';
|
||||
AppointmentDialog: boolean = false;
|
||||
patients: [];
|
||||
genders = Gender;
|
||||
Dateofbirth: Date = new Date();
|
||||
AppointmentDate: Date = new Date();
|
||||
isModalVisible:boolean=false;
|
||||
appointmentStatuses = Object.keys(appointmentStatus)
|
||||
.filter(key => !isNaN(Number(key)))
|
||||
.map(key => ({
|
||||
label: appointmentStatus[key as unknown as keyof typeof appointmentStatus],
|
||||
value: Number(key),
|
||||
}));
|
||||
visitTypes = Object.keys(visitType)
|
||||
.filter(key => !isNaN(Number(key)))
|
||||
.map(key => ({
|
||||
label: visitType[key as unknown as keyof typeof visitType],
|
||||
value: Number(key),
|
||||
}));
|
||||
paymentStatuses = Object.keys(paymentStatus)
|
||||
.filter(key => !isNaN(Number(key)))
|
||||
.map(key => ({
|
||||
label: paymentStatus[key as unknown as keyof typeof paymentStatus],
|
||||
value: Number(key),
|
||||
}));
|
||||
isEditMode: boolean = false;
|
||||
loading: boolean = false;
|
||||
params: PagingSortResultDto;
|
||||
|
||||
appointment: CreateOrUpdateAppointmentDto = {
|
||||
id: '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
gender: Gender.Male,
|
||||
mobile: '',
|
||||
address: '',
|
||||
dob: '',
|
||||
doctorId: '',
|
||||
dateOfAppointment: '',
|
||||
timeOfAppointment: '',
|
||||
injuryORContion: '',
|
||||
note: '',
|
||||
appointmentStatus: appointmentStatus.Scheduled,
|
||||
visitType: visitType.NewPatient,
|
||||
paymentStatus: paymentStatus.Unpaid,
|
||||
insuranceProvider: '',
|
||||
};
|
||||
|
||||
appointments = [];
|
||||
|
||||
doctors = [];
|
||||
doctorOptions = [];
|
||||
createPermission = true;
|
||||
editPermission = true;
|
||||
deletePermission = true;
|
||||
constructor(
|
||||
private DoctorService: DoctorService,
|
||||
private AppointmentService: AppointmentService,
|
||||
private confirmation: ConfirmationService,
|
||||
private toaster: ToasterService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getdoctorlist();
|
||||
|
||||
this.loadappointments({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
}
|
||||
getGenderLabel(gender: number): string {
|
||||
return Gender[gender] ?? 'Unknown';
|
||||
}
|
||||
getStatusLabel(Status: number): string {
|
||||
return appointmentStatus[Status] ?? 'Unknown';
|
||||
}
|
||||
getVisitTypeLabel(VisitType: number): string {
|
||||
return visitType[VisitType] ?? 'Unknown';
|
||||
}
|
||||
|
||||
loadappointments(event: any) {
|
||||
this.loading = true;
|
||||
let order = event.sortOrder == 1 ? ' asc' : ' desc';
|
||||
event.sortField = event.sortField == undefined ? 'id' : event.sortField;
|
||||
this.params = {
|
||||
skipCount: event.first,
|
||||
maxResultCount: event.rows,
|
||||
sorting: event.sortField + order,
|
||||
search: event.globalFilter == null ? '' : event.globalFilter,
|
||||
};
|
||||
|
||||
this.AppointmentService.getAppointmentList(this.params).subscribe(data => {
|
||||
this.appointments = data.items;
|
||||
this.totalRecords = data.totalCount;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
openNewAppointmentDialog() {
|
||||
this.isModalVisible=true;
|
||||
this.isEditMode = false;
|
||||
}
|
||||
|
||||
|
||||
exportAppointments() {
|
||||
this.AppointmentService.getExportAppointmentRecord().subscribe(result => {
|
||||
const binary = atob(result.fileContent);
|
||||
const len = binary.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
|
||||
const blob = new Blob([bytes], { type: 'application/xlsx' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = result.fileName;
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
}
|
||||
editAppointment(appointment: any) {
|
||||
this.isEditMode = true;
|
||||
this.appointmentIdToEdit = appointment.id;
|
||||
this.isModalVisible=true;
|
||||
}
|
||||
|
||||
deleteAppointment(id: string) {
|
||||
this.confirmation
|
||||
.warn('Do you really want to delete this Appointment?', {
|
||||
key: '::AreYouSure',
|
||||
defaultValue: 'Are you sure?',
|
||||
})
|
||||
.subscribe((status: Confirmation.Status) => {
|
||||
if (status == 'confirm') {
|
||||
this.AppointmentService.deleteAppointmentRecord(id).subscribe(() => {
|
||||
this.toaster.success('Appointment deleted successfully', 'Success');
|
||||
this.loadappointments(this.params);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.isModalVisible = false;
|
||||
this.loadappointments({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
}
|
||||
getdoctorlist() {
|
||||
this.DoctorService.get().subscribe(result => {
|
||||
this.doctors = result;
|
||||
this.doctorOptions = this.doctors.map(doctor => ({
|
||||
label: `Dr. ${doctor.firstName} ${doctor.lastName}`,
|
||||
value: doctor.id,
|
||||
}));
|
||||
});
|
||||
}
|
||||
saveAppointment(appointmentData: any) {
|
||||
this.closeDialog();
|
||||
}
|
||||
}
|
||||
|
@ -3,40 +3,15 @@ import { CommonModule } from '@angular/common';
|
||||
|
||||
import { ViewAppointmentRoutingModule } from './view-appointment-routing.module';
|
||||
import { ViewAppointmentComponent } from './view-appointment.component';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { DialogModule } from 'primeng/dialog';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CalendarModule } from 'primeng/calendar';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { RadioButtonModule } from 'primeng/radiobutton';
|
||||
import { DoctorService } from '@proxy/doctors';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { AppointmentDialogComponent } from "../appointment-dialog/appointment-dialog.component";
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [ViewAppointmentComponent],
|
||||
declarations: [
|
||||
ViewAppointmentComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ViewAppointmentRoutingModule,
|
||||
TableModule,
|
||||
DialogModule,
|
||||
FormsModule,
|
||||
TableModule,
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
InputTextModule,
|
||||
CalendarModule,
|
||||
DropdownModule,
|
||||
RadioButtonModule,
|
||||
InputTextareaModule,
|
||||
ChipModule,
|
||||
SharedModule,
|
||||
AppointmentDialogComponent
|
||||
],
|
||||
providers:[DoctorService]
|
||||
ViewAppointmentRoutingModule
|
||||
]
|
||||
})
|
||||
export class ViewAppointmentModule {}
|
||||
export class ViewAppointmentModule { }
|
||||
|
@ -1,176 +0,0 @@
|
||||
<div
|
||||
class="modal fade show d-block"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
style="background: rgba(0, 0, 0, 0.5)"
|
||||
*ngIf="visible"
|
||||
aria-label="{{ '::department' | abpLocalization }}"
|
||||
>
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header py-4">
|
||||
<h4 class="text-success mb-0 fs-3 fw-normal">
|
||||
{{ isEditMode ? ('::edit' | abpLocalization) : ('::create' | abpLocalization) }}
|
||||
{{ '::department' | abpLocalization }}
|
||||
</h4>
|
||||
<button
|
||||
tabindex="0"
|
||||
type="button"
|
||||
(click)="onClose()"
|
||||
class="btn-close"
|
||||
aria-label="{{ '::close' | abpLocalization }}"
|
||||
></button>
|
||||
</div>
|
||||
<form #departmentForm="ngForm" (ngSubmit)="saveDepartment(departmentForm)">
|
||||
<div class="p-fluid grid justify-content-center">
|
||||
<div class="field col-md-5">
|
||||
<label for="departmentNo">
|
||||
{{ '::departmentNo' | abpLocalization }} <span class="text-danger">*</span>
|
||||
</label>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-hashtag"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="departmentNo"
|
||||
name="departmentNo"
|
||||
[(ngModel)]="department.departmentNo"
|
||||
#departmentNoCtrl="ngModel"
|
||||
required
|
||||
[ngClass]="{
|
||||
'is-valid': departmentNoCtrl.valid && departmentNoCtrl.touched,
|
||||
'is-invalid': departmentNoCtrl.invalid && departmentNoCtrl.touched
|
||||
}"
|
||||
/>
|
||||
<i
|
||||
*ngIf="departmentNoCtrl.valid && departmentNoCtrl.touched"
|
||||
class="pi pi-check text-success"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="departmentNoCtrl.invalid && departmentNoCtrl.touched"
|
||||
class="pi pi-times text-danger"
|
||||
></i>
|
||||
</span>
|
||||
<small class="text-danger" *ngIf="departmentNoCtrl.invalid && departmentNoCtrl.touched">
|
||||
<span *ngIf="departmentNoCtrl.errors?.required">
|
||||
{{ '::departmentNoRequired' | abpLocalization }}
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="departmentName">
|
||||
{{ '::departmentName' | abpLocalization }} <span class="text-danger">*</span>
|
||||
</label>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-building"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="departmentName"
|
||||
name="departmentName"
|
||||
[(ngModel)]="department.departmentName"
|
||||
#departmentNameCtrl="ngModel"
|
||||
required
|
||||
/>
|
||||
</span>
|
||||
<small
|
||||
class="text-danger"
|
||||
*ngIf="departmentNameCtrl.invalid && departmentNameCtrl.touched"
|
||||
>
|
||||
<span *ngIf="departmentNameCtrl.errors?.required">
|
||||
{{ '::departmentNameRequired' | abpLocalization }}
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="departmentDate">
|
||||
{{ '::departmentDate' | abpLocalization }}
|
||||
</label>
|
||||
<p-calendar
|
||||
id="departmentDate"
|
||||
name="departmentDate"
|
||||
[(ngModel)]="Departmentdate"
|
||||
[showIcon]="true"
|
||||
>
|
||||
</p-calendar>
|
||||
</div>
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<div class="field col-md-5">
|
||||
<label for="departmentHead">
|
||||
{{ '::departmentHead' | abpLocalization }}
|
||||
</label>
|
||||
<span class="p-input-icon-left">
|
||||
<i class="pi pi-user"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="departmentHead"
|
||||
name="departmentHead"
|
||||
[(ngModel)]="department.departmentHead"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="field col-md-5">
|
||||
<label>
|
||||
{{ '::status' | abpLocalization }}
|
||||
</label>
|
||||
<div class="flex align-items-center p-input-icon-right">
|
||||
<p-radioButton
|
||||
name="status"
|
||||
value="Active"
|
||||
[(ngModel)]="department.status"
|
||||
inputId="active"
|
||||
></p-radioButton>
|
||||
<label for="active" class="ml-2 mr-3">
|
||||
{{ '::active' | abpLocalization }}
|
||||
</label>
|
||||
<p-radioButton
|
||||
name="status"
|
||||
value="Inactive"
|
||||
[(ngModel)]="department.status"
|
||||
inputId="inactive"
|
||||
></p-radioButton>
|
||||
<label for="inactive" class="ml-2">
|
||||
{{ '::inactive' | abpLocalization }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field col-md-6"></div>
|
||||
|
||||
<div class="field col-11">
|
||||
<label for="description">
|
||||
{{ '::description' | abpLocalization }}
|
||||
</label>
|
||||
<textarea
|
||||
id="description"
|
||||
name="description"
|
||||
[(ngModel)]="department.description"
|
||||
rows="5"
|
||||
cols="30"
|
||||
pInputTextarea
|
||||
>
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<div class="field col-11 flex justify-content-end">
|
||||
<button
|
||||
pButton
|
||||
type="submit"
|
||||
[label]="'::save' | abpLocalization"
|
||||
class="p-button-success"
|
||||
[disabled]="departmentForm.invalid"
|
||||
></button>
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
[label]="'::cancel' | abpLocalization"
|
||||
class="p-button-secondary ml-2"
|
||||
(click)="onClose()"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,21 +0,0 @@
|
||||
.hide_body{
|
||||
scrollbar-width: auto !important;
|
||||
min-height: 150px;
|
||||
max-height: calc(100vh - 13rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.is-valid {
|
||||
border-color: green !important;
|
||||
}
|
||||
.is-invalid {
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
.p-calendar .p-datepicker {
|
||||
z-index: 1050 !important;
|
||||
}
|
||||
|
||||
.modal {
|
||||
z-index: 1040;
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DepartmentDialogComponent } from './department-dialog.component';
|
||||
|
||||
describe('DepartmentDialogComponent', () => {
|
||||
let component: DepartmentDialogComponent;
|
||||
let fixture: ComponentFixture<DepartmentDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DepartmentDialogComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DepartmentDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,107 +0,0 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule, NgForm } from '@angular/forms';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CalendarModule } from 'primeng/calendar';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { DialogModule } from 'primeng/dialog';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { RadioButtonModule } from 'primeng/radiobutton';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { CreateDepartmentDto } from '@proxy/dtos';
|
||||
import { DepartmentService } from '@proxy/departments';
|
||||
import { ToasterService } from '@abp/ng.theme.shared';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'app-department-dialog',
|
||||
standalone: true,
|
||||
imports: [
|
||||
TableModule,
|
||||
DialogModule,
|
||||
FormsModule,
|
||||
TableModule,
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
InputTextModule,
|
||||
CalendarModule,
|
||||
DropdownModule,
|
||||
RadioButtonModule,
|
||||
InputTextareaModule,
|
||||
ChipModule,
|
||||
CommonModule,
|
||||
SharedModule
|
||||
],
|
||||
templateUrl: './department-dialog.component.html',
|
||||
styleUrl: './department-dialog.component.scss',
|
||||
})
|
||||
export class DepartmentDialogComponent implements OnInit {
|
||||
@Input() visible: boolean = false;
|
||||
@Input() name: string;
|
||||
@Input() isEditMode: boolean = false;
|
||||
@Output() save = new EventEmitter<any>();
|
||||
@Output() close = new EventEmitter<void>();
|
||||
@Input() Id: string;
|
||||
DepartmentDialog: boolean;
|
||||
Departmentdate: Date;
|
||||
constructor(
|
||||
private DepartmentService: DepartmentService,
|
||||
private toaster: ToasterService
|
||||
) {}
|
||||
ngOnInit(): void {
|
||||
if(this.isEditMode){
|
||||
this.fetchDepartmentData();
|
||||
}
|
||||
}
|
||||
|
||||
department: CreateDepartmentDto = {
|
||||
departmentNo: '',
|
||||
departmentName: '',
|
||||
departmentDate: '',
|
||||
departmentHead: '',
|
||||
description: '',
|
||||
status: '',
|
||||
};
|
||||
|
||||
fetchDepartmentData() {
|
||||
this.DepartmentService.getDepartmentById(this.Id).subscribe(result => {
|
||||
this.department = result;
|
||||
this.Departmentdate = new Date(result.departmentDate);
|
||||
});
|
||||
}
|
||||
saveDepartment(form: NgForm) {
|
||||
if (form.invalid) {
|
||||
Object.values(form.controls).forEach(control => control.markAsTouched());
|
||||
return;
|
||||
}
|
||||
this.department.departmentDate = this.Departmentdate.toDateString();
|
||||
|
||||
if (this.isEditMode) {
|
||||
this.DepartmentService.updateDepartment(this.department).subscribe(
|
||||
() => {
|
||||
this.toaster.success('Department updated successfully', 'Success');
|
||||
this.onClose();
|
||||
},
|
||||
error => {
|
||||
this.toaster.error(error, 'Error');
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.DepartmentService.createDepartment(this.department).subscribe(
|
||||
() => {
|
||||
this.toaster.success('Department created successfully', 'Success');
|
||||
this.onClose();
|
||||
},
|
||||
error => {
|
||||
this.toaster.error(error, 'Error');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
onClose() {
|
||||
this.Id = '';
|
||||
this.close.emit();
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { DepartmentsComponent } from './departments.component';
|
||||
|
||||
const routes: Routes = [
|
||||
|
||||
{ path: '', component: DepartmentsComponent },
|
||||
|
||||
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class DepartmentsRoutingModule { }
|
@ -1,122 +0,0 @@
|
||||
<div>
|
||||
<p-table
|
||||
#dt2
|
||||
dataKey="id"
|
||||
[value]="departments"
|
||||
[paginator]="true"
|
||||
[rows]="10"
|
||||
[totalRecords]="totalRecords"
|
||||
[lazy]="true"
|
||||
(onLazyLoad)="loaddepartments($event)"
|
||||
[rowsPerPageOptions]="[10, 20, 50]"
|
||||
[responsiveLayout]="'scroll'"
|
||||
[globalFilterFields]="['id', 'name', 'status']"
|
||||
[filters]="{ global: { value: '', matchMode: 'contains' } }"
|
||||
class="table table-striped"
|
||||
>
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="flex align-items-center justify-content-between mb-3 gap-3">
|
||||
<h4 class="m-0">{{ '::departmentList' | abpLocalization }}</h4>
|
||||
<div class="flex-grow-1 flex justify-content-center">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="pi pi-search"></i></span>
|
||||
<input
|
||||
pInputText
|
||||
type="text"
|
||||
class="form-control"
|
||||
(input)="dt2.filterGlobal($event.target.value, 'contains')"
|
||||
[(ngModel)]="globalFilter"
|
||||
placeholder="{{ '::searchKeyword' | abpLocalization }}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
pButton
|
||||
class="p-button-rounded p-button-secondary ml-2"
|
||||
(click)="loaddepartments($event)"
|
||||
[title]="'::refresh' | abpLocalization"
|
||||
|
||||
>
|
||||
<i class="pi pi-refresh"></i>
|
||||
</button>
|
||||
<button
|
||||
pButton
|
||||
class="p-button-rounded p-button-success ml-2"
|
||||
(click)="openNewDepartmentDialog()"
|
||||
pTooltip="{{ '::addDepartment' | abpLocalization }}"
|
||||
|
||||
tooltipPosition="left"
|
||||
>
|
||||
<i class="pi pi-plus-circle"></i>
|
||||
</button>
|
||||
<button
|
||||
pButton
|
||||
class="p-button-rounded p-button-warning ml-2"
|
||||
(click)="exportDepartments()"
|
||||
[title]="'::export' | abpLocalization"
|
||||
|
||||
>
|
||||
<i class="pi pi-download"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th pSortableColumn="departmentNo">
|
||||
{{ '::departmentNumber' | abpLocalization }}
|
||||
<p-sortIcon field="departmentNo" />
|
||||
</th>
|
||||
<th pSortableColumn="departmentName">
|
||||
{{ '::departmentName' | abpLocalization }}
|
||||
<p-sortIcon field="departmentName" />
|
||||
</th>
|
||||
<th pSortableColumn="description">
|
||||
{{ '::description' | abpLocalization }}
|
||||
<p-sortIcon field="description" />
|
||||
</th>
|
||||
<th pSortableColumn="departmentDate">
|
||||
{{ '::date' | abpLocalization }}
|
||||
<p-sortIcon field="departmentDate" />
|
||||
</th>
|
||||
<th pSortableColumn="departmentHead">
|
||||
{{ '::departmentHead' | abpLocalization }}
|
||||
<p-sortIcon field="departmentHead" />
|
||||
</th>
|
||||
<th pSortableColumn="status">
|
||||
{{ '::status' | abpLocalization }}
|
||||
<p-sortIcon field="status" />
|
||||
</th>
|
||||
<th>{{ '::actions' | abpLocalization }}</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-department>
|
||||
<tr>
|
||||
<td>{{ department.departmentNo }}</td>
|
||||
<td>{{ department.departmentName }}</td>
|
||||
<td>{{ department.description }}</td>
|
||||
<td>{{ department.departmentDate | date }}</td>
|
||||
<td>{{ department.departmentHead }}</td>
|
||||
<td>
|
||||
<span class="badge" [ngClass]="department.status === 'Active' ? 'bg-primary' : 'bg-danger'">
|
||||
{{ department.status }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="d-flex">
|
||||
<button class="btn btn-warning btn-sm ml-1" (click)="editDepartment(department)">
|
||||
<i class="pi pi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger btn-sm ml-1" (click)="deletedepartment(department.id)">
|
||||
<i class="pi pi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
|
||||
<app-department-dialog [Id]="DepartmentIdToEdit"[isEditMode]="isEditMode"
|
||||
[visible]="isModalVisible"*ngIf="isModalVisible"
|
||||
[name]="'::department' | abpLocalization"
|
||||
(close)="closeDialog()"></app-department-dialog >
|
@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DepartmentsComponent } from './departments.component';
|
||||
|
||||
describe('DepartmentsComponent', () => {
|
||||
let component: DepartmentsComponent;
|
||||
let fixture: ComponentFixture<DepartmentsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DepartmentsComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DepartmentsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,113 +0,0 @@
|
||||
import { Confirmation, ConfirmationService, ToasterService } from '@abp/ng.theme.shared';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { DepartmentService } from '@proxy/departments';
|
||||
import { PagingSortResultDto } from '@proxy/dto';
|
||||
|
||||
@Component({
|
||||
selector: 'app-departments',
|
||||
templateUrl: './departments.component.html',
|
||||
styleUrl: './departments.component.scss',
|
||||
})
|
||||
export class DepartmentsComponent implements OnInit {
|
||||
totalRecords: number = 0;
|
||||
departments= [];
|
||||
loading: boolean = false;
|
||||
params: PagingSortResultDto;
|
||||
isModalVisible: boolean;
|
||||
isEditMode: boolean = false;
|
||||
DepartmentIdToEdit: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loaddepartments({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
}
|
||||
constructor(
|
||||
private DepartmentService: DepartmentService,
|
||||
private confirmation: ConfirmationService,
|
||||
private toaster: ToasterService
|
||||
|
||||
) {}
|
||||
loaddepartments(event: any) {
|
||||
this.loading = true;
|
||||
let order = event.sortOrder == 1 ? ' asc' : ' desc';
|
||||
event.sortField = event.sortField == undefined ? 'id' : event.sortField;
|
||||
this.params = {
|
||||
skipCount: event.first,
|
||||
maxResultCount: event.rows,
|
||||
sorting: event.sortField + order,
|
||||
search: event.globalFilter == null ? '' : event.globalFilter,
|
||||
};
|
||||
|
||||
this.DepartmentService.getDepartmentList(this.params).subscribe(data => {
|
||||
this.departments = data.items;
|
||||
this.totalRecords = data.totalCount;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
openNewDepartmentDialog() {
|
||||
this.isModalVisible=true;
|
||||
this.isEditMode = false;
|
||||
}
|
||||
editDepartment(Department: any) {
|
||||
this.isEditMode = true;
|
||||
this.DepartmentIdToEdit = Department.id;
|
||||
this.isModalVisible=true;
|
||||
}
|
||||
deletedepartment(id: string) {
|
||||
this.confirmation
|
||||
.warn('Do you really want to delete this Department?', {
|
||||
key: '::AreYouSure',
|
||||
defaultValue: 'Are you sure?',
|
||||
})
|
||||
.subscribe((status: Confirmation.Status) => {
|
||||
if (status == 'confirm') {
|
||||
this.DepartmentService.deleteDepartmentRecord(id).subscribe(() => {
|
||||
this.toaster.success('Deleted successfully', 'Success');
|
||||
this.loaddepartments(this.params);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.isModalVisible = false;
|
||||
this.loaddepartments({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
}
|
||||
|
||||
exportDepartments() {
|
||||
this.DepartmentService.getExportDepartmentRecord().subscribe(result => {
|
||||
const binary = atob(result.fileContent);
|
||||
const len = binary.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
|
||||
const blob = new Blob([bytes], { type: 'application/xlsx' });
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = result.fileName;
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { DepartmentsRoutingModule } from './departments-routing.module';
|
||||
import { DepartmentsComponent } from './departments.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CalendarModule } from 'primeng/calendar';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { DialogModule } from 'primeng/dialog';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { RadioButtonModule } from 'primeng/radiobutton';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { DepartmentDialogComponent } from "./department-dialog.component";
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [DepartmentsComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
DepartmentsRoutingModule,
|
||||
TableModule,
|
||||
DialogModule,
|
||||
FormsModule,
|
||||
TableModule,
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
InputTextModule,
|
||||
CalendarModule,
|
||||
DropdownModule,
|
||||
RadioButtonModule,
|
||||
InputTextareaModule,
|
||||
ChipModule,
|
||||
DepartmentDialogComponent
|
||||
]
|
||||
})
|
||||
export class DepartmentsModule { }
|
@ -1,393 +0,0 @@
|
||||
<div
|
||||
class="modal fade show d-block"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
style="background: rgba(0, 0, 0, 0.5)"
|
||||
*ngIf="visible"
|
||||
aria-label="l('Doctor')"
|
||||
>
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header py-4">
|
||||
<h4 class="text-success mb-0 fs-3 fw-normal">
|
||||
{{ isEditMode ? 'Edit ' : 'Create ' }} Doctor
|
||||
</h4>
|
||||
<button
|
||||
tabindex="0"
|
||||
type="button"
|
||||
(click)="onClose()"
|
||||
class="btn-close"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
<form #doctorForm="ngForm" (ngSubmit)="saveDoctor(doctorForm)">
|
||||
<div class="p-fluid grid justify-content-center">
|
||||
<!-- First Name -->
|
||||
<div class="field col-md-5">
|
||||
<label for="firstName">First Name <span class="text-danger">*</span></label>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-user"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="firstName"
|
||||
name="firstName"
|
||||
[(ngModel)]="doctor.firstName"
|
||||
#firstNameCtrl="ngModel"
|
||||
required
|
||||
minlength="2"
|
||||
maxlength="30"
|
||||
[ngClass]="{
|
||||
'is-valid': firstNameCtrl.valid && firstNameCtrl.touched,
|
||||
'is-invalid': firstNameCtrl.invalid && firstNameCtrl.touched
|
||||
}"
|
||||
/>
|
||||
<i
|
||||
*ngIf="firstNameCtrl.valid && firstNameCtrl.touched"
|
||||
class="pi pi-check text-success"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="firstNameCtrl.invalid && firstNameCtrl.touched"
|
||||
class="pi pi-times text-danger"
|
||||
></i>
|
||||
</span>
|
||||
<small class="text-danger" *ngIf="firstNameCtrl.invalid && firstNameCtrl.touched">
|
||||
<span *ngIf="firstNameCtrl.errors?.required">First Name is required.</span>
|
||||
<span *ngIf="firstNameCtrl.errors?.minlength">Minimum 2 characters required.</span>
|
||||
<span *ngIf="firstNameCtrl.errors?.maxlength">Maximum 30 characters allowed.</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<!-- Last Name -->
|
||||
<div class="field col-md-5">
|
||||
<label for="lastName">Last Name <span class="text-danger">*</span></label>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-user"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="lastName"
|
||||
name="lastName"
|
||||
[(ngModel)]="doctor.lastName"
|
||||
#lastNameCtrl="ngModel"
|
||||
required
|
||||
minlength="2"
|
||||
maxlength="30"
|
||||
[ngClass]="{
|
||||
'is-valid': lastNameCtrl.valid && lastNameCtrl.touched,
|
||||
'is-invalid': lastNameCtrl.invalid && lastNameCtrl.touched
|
||||
}"
|
||||
/>
|
||||
<i
|
||||
*ngIf="lastNameCtrl.valid && lastNameCtrl.touched"
|
||||
class="pi pi-check text-success"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="lastNameCtrl.invalid && lastNameCtrl.touched"
|
||||
class="pi pi-times text-danger"
|
||||
></i>
|
||||
</span>
|
||||
<small class="text-danger" *ngIf="lastNameCtrl.invalid && lastNameCtrl.touched">
|
||||
<span *ngIf="lastNameCtrl.errors?.required">Last Name is required.</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Mobile -->
|
||||
<div class="field col-md-5">
|
||||
<label for="mobile">Mobile <span class="text-danger">*</span></label>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-phone"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="mobile"
|
||||
name="mobile"
|
||||
[(ngModel)]="doctor.mobile"
|
||||
#mobileCtrl="ngModel"
|
||||
required
|
||||
pattern="^[0-9]{10}$"
|
||||
[ngClass]="{
|
||||
'is-valid': mobileCtrl.valid && mobileCtrl.touched,
|
||||
'is-invalid': mobileCtrl.invalid && mobileCtrl.touched
|
||||
}"
|
||||
/>
|
||||
<i
|
||||
*ngIf="mobileCtrl.valid && mobileCtrl.touched"
|
||||
class="pi pi-check text-success"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="mobileCtrl.invalid && mobileCtrl.touched"
|
||||
class="pi pi-times text-danger"
|
||||
></i>
|
||||
</span>
|
||||
<small class="text-danger" *ngIf="mobileCtrl.invalid && mobileCtrl.touched">
|
||||
<span *ngIf="mobileCtrl.errors?.required">Mobile is required.</span>
|
||||
<span *ngIf="mobileCtrl.errors?.pattern">Enter a valid 10-digit number.</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<!-- Email -->
|
||||
<div class="field col-md-5">
|
||||
<label for="email">Email <span class="text-danger">*</span></label>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-envelope"></i>
|
||||
<input
|
||||
pInputText
|
||||
id="email"
|
||||
name="email"
|
||||
[(ngModel)]="doctor.email"
|
||||
#emailCtrl="ngModel"
|
||||
required
|
||||
type="email"
|
||||
pattern="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
|
||||
[ngClass]="{
|
||||
'is-valid': emailCtrl.valid && emailCtrl.touched,
|
||||
'is-invalid': emailCtrl.invalid && emailCtrl.touched
|
||||
}"
|
||||
/>
|
||||
<i *ngIf="emailCtrl.valid && emailCtrl.touched" class="pi pi-check text-success"></i>
|
||||
<i *ngIf="emailCtrl.invalid && emailCtrl.touched" class="pi pi-times text-danger"></i>
|
||||
</span>
|
||||
<small class="text-danger" *ngIf="emailCtrl.invalid && emailCtrl.touched">
|
||||
<span *ngIf="emailCtrl.errors?.required">Email is required.</span>
|
||||
<span *ngIf="emailCtrl.errors?.pattern">Enter a valid email address.</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Specialization -->
|
||||
<div class="field col-md-5">
|
||||
<label for="specialization">Specialization</label>
|
||||
<input
|
||||
pInputText
|
||||
id="specialization"
|
||||
name="specialization"
|
||||
[(ngModel)]="doctor.specialization"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<!-- Experience -->
|
||||
<div class="field col-md-5">
|
||||
<label for="experience">Experience (Years)</label>
|
||||
<input
|
||||
type="number"
|
||||
pInputText
|
||||
id="experience"
|
||||
name="experience"
|
||||
[(ngModel)]="doctor.experience"
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Consultation Fee -->
|
||||
<div class="field col-md-5">
|
||||
<label for="consultationFee">Consultation Fee</label>
|
||||
<input
|
||||
type="number"
|
||||
pInputText
|
||||
id="consultationFee"
|
||||
name="consultationFee"
|
||||
[(ngModel)]="doctor.consultationFee"
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<!-- Clinic Location -->
|
||||
<div class="field col-md-5">
|
||||
<label for="clinicLocation">Clinic Location</label>
|
||||
<input
|
||||
pInputText
|
||||
id="clinicLocation"
|
||||
name="clinicLocation"
|
||||
[(ngModel)]="doctor.clinicLocation"
|
||||
/>
|
||||
</div>
|
||||
<div class="field col-md-5">
|
||||
<label for="dateofbirth">Date Of Birth <span class="text-danger">*</span></label>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-calendar"></i>
|
||||
<p-calendar
|
||||
id="dateofbirth"
|
||||
name="dateofbirth"
|
||||
[(ngModel)]="dateofbirth"
|
||||
#dateofbirthCtrl="ngModel"
|
||||
required
|
||||
[showIcon]="true"
|
||||
dateFormat="dd/mm/yy"
|
||||
[ngClass]="{
|
||||
'is-valid': dateofbirthCtrl.valid && dateofbirthCtrl.touched,
|
||||
'is-invalid': dateofbirthCtrl.invalid && dateofbirthCtrl.touched
|
||||
}"
|
||||
>
|
||||
</p-calendar>
|
||||
<i
|
||||
*ngIf="dateofbirthCtrl.valid && dateofbirthCtrl.touched"
|
||||
class="pi pi-check text-success"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="dateofbirthCtrl.invalid && dateofbirthCtrl.touched"
|
||||
class="pi pi-times text-danger"
|
||||
></i>
|
||||
</span>
|
||||
<small class="text-danger" *ngIf="dateofbirthCtrl.invalid && dateofbirthCtrl.touched">
|
||||
<span *ngIf="dateofbirthCtrl.errors?.required">DOB is required.</span>
|
||||
</small>
|
||||
</div>
|
||||
<div class="field col-md-1"></div>
|
||||
<div class="field col-md-5">
|
||||
<label for="joiningDate">Joining Date <span class="text-danger">*</span></label>
|
||||
<span class="p-input-icon-left p-input-icon-right">
|
||||
<i class="pi pi-calendar"></i>
|
||||
<p-calendar
|
||||
id="joiningDate"
|
||||
name="joiningDate"
|
||||
[(ngModel)]="JoiningDate"
|
||||
#joiningDateCtrl="ngModel"
|
||||
required
|
||||
[showIcon]="true"
|
||||
dateFormat="dd/mm/yy"
|
||||
[ngClass]="{
|
||||
'is-valid': joiningDateCtrl.valid && joiningDateCtrl.touched,
|
||||
'is-invalid': joiningDateCtrl.invalid && joiningDateCtrl.touched
|
||||
}"
|
||||
>
|
||||
</p-calendar>
|
||||
<i
|
||||
*ngIf="joiningDateCtrl.valid && joiningDateCtrl.touched"
|
||||
class="pi pi-check text-success"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="joiningDateCtrl.invalid && joiningDateCtrl.touched"
|
||||
class="pi pi-times text-danger"
|
||||
></i>
|
||||
</span>
|
||||
<small class="text-danger" *ngIf="joiningDateCtrl.invalid && joiningDateCtrl.touched">
|
||||
<span *ngIf="joiningDateCtrl.errors?.required">Joining Date is required.</span>
|
||||
</small>
|
||||
</div>
|
||||
<div class="field col-md-5">
|
||||
<label for="rating">Rating</label><br />
|
||||
<p-rating
|
||||
[(ngModel)]="doctor.rating"
|
||||
[stars]="5"
|
||||
[cancel]="false"
|
||||
name="rating"
|
||||
></p-rating>
|
||||
</div>
|
||||
<div class="field col-md-1"></div>
|
||||
|
||||
<!-- Button to open the popup -->
|
||||
<div class="col-md-5">
|
||||
<div class="button-container">
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
label="Select Availability"
|
||||
icon="pi pi-calendar"
|
||||
(click)="openDialog()"
|
||||
class="p-mr-2"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- #incomplete -->
|
||||
<!-- Need to Be Worked on the popup for schedule the time -->
|
||||
<p-dialog
|
||||
header="Select Days and Times"
|
||||
[(visible)]="displayDialog"
|
||||
modal="true"
|
||||
closable="true"
|
||||
[style]="{ width: '80%', height: '100%' }"
|
||||
[baseZIndex]="10000"
|
||||
styleClass="availability-dialog"
|
||||
>
|
||||
<div class="p-fluid grid">
|
||||
<div class="field col-md-5">
|
||||
<label for="availability">Select Available Days</label>
|
||||
<p-multiSelect
|
||||
[options]="workScheduleOptions"
|
||||
[(ngModel)]="selectedDays"
|
||||
optionLabel="label"
|
||||
placeholder="Select Days"
|
||||
(onChange)="onDaysChange($event)"
|
||||
></p-multiSelect>
|
||||
</div>
|
||||
<div class="field col-md-1"></div>
|
||||
<div class="field col-md-5">
|
||||
<!-- Display time selection for each selected day -->
|
||||
<div *ngIf="selectedDays && selectedDays.length" class="selected-days-container">
|
||||
<h3 class="selected-days-header">Set Time for Selected Days</h3>
|
||||
<div *ngFor="let day of selectedDays" class="day-time-row">
|
||||
<span class="day-label">{{ day.label }}</span>
|
||||
<p-dropdown
|
||||
[options]="timeSlotOptions"
|
||||
[ngModel]="selectedTimes[day.value]"
|
||||
(onChange)="onTimeChange(day.value, $event)"
|
||||
placeholder="Select Time"
|
||||
style="width: 80%"
|
||||
></p-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p-footer>
|
||||
<div class="footer-buttons p-d-flex p-jc-end">
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
label="Save"
|
||||
icon="pi pi-check"
|
||||
(click)="saveSelection()"
|
||||
class="p-button-success p-mr-2"
|
||||
></button
|
||||
>
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
label="Cancel"
|
||||
icon="pi pi-times"
|
||||
(click)="closeDialog()"
|
||||
class="p-button-secondary"
|
||||
></button>
|
||||
</div>
|
||||
</p-footer>
|
||||
</p-dialog>
|
||||
<!-- end -->
|
||||
<div class="field col-11">
|
||||
<label for="description">Description</label>
|
||||
<textarea
|
||||
id="description"
|
||||
name="description"
|
||||
[(ngModel)]="doctor.description"
|
||||
rows="5"
|
||||
cols="30"
|
||||
pInputTextarea
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="field col-11 flex justify-content-end">
|
||||
<button
|
||||
pButton
|
||||
type="submit"
|
||||
label="Save"
|
||||
class="p-button-success"
|
||||
[disabled]="doctorForm.invalid"
|
||||
></button>
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
label="Cancel"
|
||||
class="p-button-secondary ml-2"
|
||||
(click)="onClose()"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,85 +0,0 @@
|
||||
.hide_body{
|
||||
scrollbar-width: auto !important;
|
||||
min-height: 150px;
|
||||
max-height: calc(100vh - 13rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.is-valid {
|
||||
border-color: green !important;
|
||||
}
|
||||
.is-invalid {
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
/* Adjust the z-index of the calendar dropdown */
|
||||
.p-calendar .p-datepicker {
|
||||
z-index: 1050 !important; /* Make sure it's above other elements */
|
||||
}
|
||||
|
||||
/* You can also adjust modal z-index if necessary */
|
||||
.modal {
|
||||
z-index: 1040; /* Set lower z-index to ensure modal is behind the calendar */
|
||||
}
|
||||
.availability-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
.dropdown-item-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 0; /* adjust as needed */
|
||||
}
|
||||
|
||||
.day-label {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.time-options {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.time-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
/* Style for the container below the multi-select */
|
||||
.selected-days-container {
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
/* Header styling */
|
||||
.selected-days-header {
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Each day row */
|
||||
.day-time-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* Fixed width for day label */
|
||||
.day-label {
|
||||
width: 100px;
|
||||
font-weight: 600;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DoctorDialogComponent } from './doctor-dialog.component';
|
||||
|
||||
describe('DoctorDialogComponent', () => {
|
||||
let component: DoctorDialogComponent;
|
||||
let fixture: ComponentFixture<DoctorDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DoctorDialogComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DoctorDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,196 +0,0 @@
|
||||
import { ToasterService } from '@abp/ng.theme.shared';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { FormsModule, NgForm } from '@angular/forms';
|
||||
import { DoctorService } from '@proxy/doctors';
|
||||
import { CreateDoctorDto } from '@proxy/dtos';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CalendarModule } from 'primeng/calendar';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { DialogModule } from 'primeng/dialog';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { RadioButtonModule } from 'primeng/radiobutton';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { RatingModule } from 'primeng/rating';
|
||||
import { MultiSelectModule } from 'primeng/multiselect';
|
||||
import { TimeSlot, WorkSchedule, } from '@proxy/global-enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-doctor-dialog',
|
||||
standalone: true,
|
||||
imports: [
|
||||
TableModule,
|
||||
DialogModule,
|
||||
FormsModule,
|
||||
TableModule,
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
InputTextModule,
|
||||
CalendarModule,
|
||||
DropdownModule,
|
||||
RadioButtonModule,
|
||||
InputTextareaModule,
|
||||
ChipModule,
|
||||
CommonModule,
|
||||
RatingModule,
|
||||
MultiSelectModule,
|
||||
],
|
||||
templateUrl: './doctor-dialog.component.html',
|
||||
styleUrl: './doctor-dialog.component.scss',
|
||||
})
|
||||
export class DoctorDialogComponent implements OnInit {
|
||||
@Input() visible: boolean = false;
|
||||
@Input() name: string;
|
||||
@Input() isEditMode: boolean = false;
|
||||
@Output() save = new EventEmitter<any>();
|
||||
@Output() close = new EventEmitter<void>();
|
||||
@Input() Id: string;
|
||||
doctorDialog: boolean;
|
||||
JoiningDate: Date;
|
||||
dateofbirth: Date;
|
||||
isSchedulePopupVisible = false;
|
||||
selectedDays: { label: string; value: string }[] = [];
|
||||
selectedTimeSlots: { [key: number]: number | null } = {};
|
||||
displayDialog: boolean = false;
|
||||
selectedDay: any = null;
|
||||
selectedTime: any = null;
|
||||
selectedTimes: { [key: number]: number } = {};
|
||||
workScheduleOptions: { label: string; value: number }[] = [];
|
||||
timeSlotOptions: { label: string; value: number }[] = [];
|
||||
|
||||
constructor(private DoctorService: DoctorService, private toaster: ToasterService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.isEditMode) {
|
||||
this.fetchDoctorData();
|
||||
} else {
|
||||
this.doctor = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
gender: '',
|
||||
mobile: '',
|
||||
password: '',
|
||||
designation: '',
|
||||
departmentId: '',
|
||||
address: '',
|
||||
email: '',
|
||||
dob: '',
|
||||
education: '',
|
||||
specialization: '',
|
||||
degree: '',
|
||||
joiningDate: '',
|
||||
experience: 0,
|
||||
consultationFee: 0,
|
||||
availability: null,
|
||||
rating: 0,
|
||||
clinicLocation: '',
|
||||
};
|
||||
}
|
||||
this.initializeWorkScheduleOptions();
|
||||
this.initializeTimeSlotOptions();
|
||||
}
|
||||
|
||||
doctor: CreateDoctorDto = {
|
||||
id: '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
gender: '',
|
||||
mobile: '',
|
||||
password: '',
|
||||
designation: '',
|
||||
departmentId: '',
|
||||
address: '',
|
||||
email: '',
|
||||
dob: '',
|
||||
education: '',
|
||||
specialization: '',
|
||||
degree: '',
|
||||
joiningDate: '',
|
||||
experience: 0,
|
||||
consultationFee: 0,
|
||||
availability: null,
|
||||
rating: 0,
|
||||
clinicLocation: '',
|
||||
};
|
||||
|
||||
openDialog() {
|
||||
this.displayDialog = true;
|
||||
}
|
||||
onDaysChange(event: any) {
|
||||
this.selectedDays=event.value;
|
||||
}
|
||||
onTimeChange(dayValue: number, timeValue: number) {
|
||||
this.selectedTimes[dayValue] = timeValue;
|
||||
}
|
||||
closeDialog() {
|
||||
this.displayDialog = false;
|
||||
}
|
||||
initializeWorkScheduleOptions() {
|
||||
this.workScheduleOptions = Object.keys(WorkSchedule)
|
||||
.filter(key => isNaN(Number(key)))
|
||||
.map(key => {
|
||||
const value = WorkSchedule[key as keyof typeof WorkSchedule];
|
||||
return { label: key, value };
|
||||
});
|
||||
}
|
||||
|
||||
initializeTimeSlotOptions() {
|
||||
this.timeSlotOptions = Object.keys(TimeSlot)
|
||||
.filter(key => isNaN(Number(key)))
|
||||
.map(key => {
|
||||
const value = TimeSlot[key as keyof typeof TimeSlot];
|
||||
return { label: key, value };
|
||||
});
|
||||
}
|
||||
|
||||
saveSelection() {
|
||||
const availability = this.selectedDays.map(day => ({
|
||||
day: day.value,
|
||||
time: this.selectedTimes[day.value] || null,
|
||||
}));
|
||||
this.displayDialog = false;
|
||||
}
|
||||
|
||||
fetchDoctorData() {
|
||||
this.DoctorService.getDoctorById(this.Id).subscribe(result => {
|
||||
this.doctor = result;
|
||||
this.JoiningDate = new Date(result.joiningDate);
|
||||
this.dateofbirth = new Date(result.dob);
|
||||
});
|
||||
}
|
||||
saveDoctor(form: NgForm) {
|
||||
if (form.invalid) {
|
||||
Object.values(form.controls).forEach(control => control.markAsTouched());
|
||||
return;
|
||||
}
|
||||
this.doctor.dob = this.dateofbirth.toDateString();
|
||||
this.doctor.joiningDate = this.JoiningDate.toDateString();
|
||||
if (this.isEditMode) {
|
||||
this.DoctorService.updatDoctor(this.doctor).subscribe(
|
||||
() => {
|
||||
this.toaster.success('Updated Successfully', 'Success');
|
||||
this.onClose();
|
||||
},
|
||||
error => {
|
||||
this.toaster.error(error, 'Error');
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.DoctorService.createDoctor(this.doctor).subscribe(
|
||||
() => {
|
||||
this.toaster.success('created successfully', 'Success');
|
||||
this.onClose();
|
||||
},
|
||||
error => {
|
||||
this.toaster.error(error, 'Error');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
onClose() {
|
||||
this.Id = '';
|
||||
this.close.emit();
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { DoctorsComponent } from './doctors.component';
|
||||
import { ShiftManagementComponent } from './shift-management/shift-management.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: DoctorsComponent },
|
||||
{ path: 'shift-management/:id', component: ShiftManagementComponent },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class DoctorsRoutingModule {}
|
@ -1,119 +0,0 @@
|
||||
<div>
|
||||
<p-table
|
||||
#dt
|
||||
dataKey="id"
|
||||
[value]="doctors"
|
||||
[paginator]="true"
|
||||
[rows]="10"
|
||||
[totalRecords]="totalRecords"
|
||||
[lazy]="true"
|
||||
(onLazyLoad)="loadDoctors($event)"
|
||||
[rowsPerPageOptions]="[10, 20, 50]"
|
||||
[responsiveLayout]="'scroll'"
|
||||
[globalFilterFields]="['id', 'firstName', 'lastName', 'mobile', 'email', 'specialization']"
|
||||
[filters]="{ global: { value: '', matchMode: 'contains' } }"
|
||||
class="table table-striped"
|
||||
>
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="flex align-items-center justify-content-between mb-3 gap-3">
|
||||
<h4 class="m-0">{{ '::doctorList' | abpLocalization }}</h4>
|
||||
<div class="flex-grow-1 flex justify-content-center">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="pi pi-search"></i></span>
|
||||
<input
|
||||
pInputText
|
||||
type="text"
|
||||
class="form-control"
|
||||
(input)="dt.filterGlobal($event.target.value, 'contains')"
|
||||
[(ngModel)]="globalFilter"
|
||||
placeholder="{{ '::searchKeyword' | abpLocalization }}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
pButton
|
||||
class="p-button-rounded p-button-secondary ml-2"
|
||||
(click)="loadDoctors($event)"
|
||||
[title]="'::refresh' | abpLocalization"
|
||||
>
|
||||
<i class="pi pi-refresh"></i>
|
||||
</button>
|
||||
<button
|
||||
pButton
|
||||
class="p-button-rounded p-button-success ml-2"
|
||||
(click)="openNewDoctorDialog()"
|
||||
pTooltip="{{ '::addDoctor' | abpLocalization }}"
|
||||
tooltipPosition="left"
|
||||
>
|
||||
<i class="pi pi-plus-circle"></i>
|
||||
</button>
|
||||
<button pButton class="p-button-rounded p-button-warning ml-2" (click)="exportDoctors()"
|
||||
[title]="'::export' | abpLocalization"
|
||||
>
|
||||
<i class="pi pi-download"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th pSortableColumn="firstName">
|
||||
{{ '::name' | abpLocalization }}
|
||||
<p-sortIcon field="firstName" />
|
||||
</th>
|
||||
<th pSortableColumn="mobile">
|
||||
{{ '::mobile' | abpLocalization }}
|
||||
<p-sortIcon field="mobile" />
|
||||
</th>
|
||||
<th pSortableColumn="email">
|
||||
{{ '::email' | abpLocalization }}
|
||||
<p-sortIcon field="email" />
|
||||
</th>
|
||||
<th pSortableColumn="specialization">
|
||||
{{ '::specialization' | abpLocalization }}
|
||||
<p-sortIcon field="specialization" />
|
||||
</th>
|
||||
<th pSortableColumn="experience">
|
||||
{{ '::experience' | abpLocalization }}
|
||||
<p-sortIcon field="experience" />
|
||||
</th>
|
||||
<th pSortableColumn="rating">
|
||||
{{ '::rating' | abpLocalization }}
|
||||
<p-sortIcon field="rating" />
|
||||
</th>
|
||||
<th>{{ '::actions' | abpLocalization }}</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-doctor>
|
||||
<tr>
|
||||
<td>{{ '::doctorPrefix' | abpLocalization }} {{ doctor.firstName }} {{ doctor.lastName }}</td>
|
||||
<td>{{ doctor.mobile }}</td>
|
||||
<td>{{ doctor.email }}</td>
|
||||
<td>{{ doctor.specialization }}</td>
|
||||
<td>{{ doctor.experience }} {{ '::years' | abpLocalization }}</td>
|
||||
<td>{{ doctor.rating }}</td>
|
||||
<td class="d-flex">
|
||||
<button class="btn btn-warning btn-sm ml-1" (click)="editDoctor(doctor)">
|
||||
<i class="pi pi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger btn-sm ml-1" (click)="deleteDoctor(doctor.id)">
|
||||
<i class="pi pi-trash"></i>
|
||||
</button>
|
||||
<button class="btn btn-warning btn-sm ml-1" (click)="GotoShiftManagement(doctor.id)">
|
||||
<i class="pi pi-arrow-right"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
|
||||
<app-doctor-dialog
|
||||
[Id]="DoctorIdToEdit"
|
||||
[isEditMode]="isEditMode"
|
||||
[visible]="isModalVisible"
|
||||
*ngIf="isModalVisible"
|
||||
[name]="'::doctor' | abpLocalization"
|
||||
(close)="closeDialog()"
|
||||
></app-doctor-dialog>
|
@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DoctorsComponent } from './doctors.component';
|
||||
|
||||
describe('DoctorsComponent', () => {
|
||||
let component: DoctorsComponent;
|
||||
let fixture: ComponentFixture<DoctorsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DoctorsComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DoctorsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,112 +0,0 @@
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { DoctorService } from '@proxy/doctors';
|
||||
import { PagingSortResultDto } from '@proxy/dto';
|
||||
import { workScheduleOptions } from '@proxy/global-enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-doctors',
|
||||
templateUrl: './doctors.component.html',
|
||||
styleUrl: './doctors.component.scss',
|
||||
})
|
||||
export class DoctorsComponent implements OnInit {
|
||||
totalRecords: number = 0;
|
||||
doctors = [];
|
||||
loading: boolean = false;
|
||||
params: PagingSortResultDto;
|
||||
isModalVisible: boolean = false;
|
||||
isEditMode: boolean = false;
|
||||
DoctorIdToEdit: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadDoctors({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
}
|
||||
constructor(
|
||||
private DoctorService: DoctorService,
|
||||
|
||||
private router: Router
|
||||
) {}
|
||||
workSchedule = Object.keys(workScheduleOptions)
|
||||
.filter(key => !isNaN(Number(key)))
|
||||
.map(key => ({
|
||||
label: workScheduleOptions[key as unknown as keyof typeof workScheduleOptions],
|
||||
value: Number(key),
|
||||
}));
|
||||
|
||||
loadDoctors(event: any) {
|
||||
this.loading = true;
|
||||
let order = event.sortOrder == 1 ? ' asc' : ' desc';
|
||||
event.sortField = event.sortField == undefined ? 'id' : event.sortField;
|
||||
this.params = {
|
||||
skipCount: event.first,
|
||||
maxResultCount: event.rows,
|
||||
sorting: event.sortField + order,
|
||||
search: event.globalFilter == null ? '' : event.globalFilter,
|
||||
};
|
||||
|
||||
this.DoctorService.getDoctorList(this.params).subscribe(data => {
|
||||
this.doctors = data.items;
|
||||
this.totalRecords = data.totalCount;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
openNewDoctorDialog() {
|
||||
this.isModalVisible = true;
|
||||
this.isEditMode = false;
|
||||
}
|
||||
editDoctor(Doctor: any) {
|
||||
this.isEditMode = true;
|
||||
this.DoctorIdToEdit = Doctor.id;
|
||||
this.isModalVisible = true;
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.isModalVisible = false;
|
||||
this.loadDoctors({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
}
|
||||
|
||||
exportDoctors() {
|
||||
this.DoctorService.getExportDoctorsRecord().subscribe(result => {
|
||||
const binary = atob(result.fileContent);
|
||||
const len = binary.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
|
||||
const blob = new Blob([bytes], { type: 'application/xlsx' });
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = result.fileName;
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
}
|
||||
GotoShiftManagement(id:string) {
|
||||
this.router.navigate(['/doctors/shift-management', id]);
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { DoctorsRoutingModule } from './doctors-routing.module';
|
||||
import { DoctorsComponent } from './doctors.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CalendarModule } from 'primeng/calendar';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { DialogModule } from 'primeng/dialog';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { RadioButtonModule } from 'primeng/radiobutton';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { DoctorDialogComponent } from './doctor-dialog.component';
|
||||
import { ShiftManagementComponent } from './shift-management/shift-management.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [DoctorsComponent,ShiftManagementComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
DoctorsRoutingModule,
|
||||
TableModule,
|
||||
DialogModule,
|
||||
FormsModule,
|
||||
TableModule,
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
InputTextModule,
|
||||
CalendarModule,
|
||||
DropdownModule,
|
||||
RadioButtonModule,
|
||||
InputTextareaModule,
|
||||
ChipModule,
|
||||
DoctorDialogComponent,
|
||||
SharedModule
|
||||
|
||||
|
||||
|
||||
],
|
||||
})
|
||||
export class DoctorsModule {}
|
@ -1,52 +0,0 @@
|
||||
<div class="card p-4 shift-management">
|
||||
<h4>Shift Management</h4>
|
||||
|
||||
<div class="field">
|
||||
<p-dropdown
|
||||
[options]="doctors"
|
||||
[(ngModel)]="selectedDoctor"
|
||||
placeholder="Select Doctor"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
class="w-full"
|
||||
(onChange)="onDoctorSelect($event.value)"
|
||||
></p-dropdown>
|
||||
</div>
|
||||
|
||||
<div *ngIf="selectedDoctor" class="shift-grid-container">
|
||||
<div class="shift-column-layout">
|
||||
<!-- Column Layout for Days & Time Slots -->
|
||||
<div *ngFor="let day of selectedDays" class="shift-column">
|
||||
<div class="day-header">
|
||||
<strong>{{ day }}</strong>
|
||||
</div>
|
||||
<p-dropdown
|
||||
[options]="timeSlotOptions"
|
||||
[(ngModel)]="selectedTimeSlots[day]"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="Select Time Slot"
|
||||
class="w-full"
|
||||
></p-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="button-container">
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
label="Save Shift"
|
||||
class="p-button-success mt-3"
|
||||
(click)="saveShiftData()"
|
||||
></button>
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
label="Back"
|
||||
class="p-button-primary mt-3"
|
||||
(click)="backToDoctor()"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,66 +0,0 @@
|
||||
.shift-column-layout {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.shift-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
min-width: 150px;
|
||||
max-width: 180px;
|
||||
flex: 1;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.shift-column:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.day-header {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color: #007bff;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
/* Center the button (optional) */
|
||||
.button-container {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
/* Styling for the dialog */
|
||||
.availability-dialog .p-dialog-header {
|
||||
background-color: #1976d2;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.availability-dialog .p-dialog-content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.footer-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Optional: Adjust field spacing */
|
||||
.field {
|
||||
margin-bottom: 1rem;
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
import { ToasterService } from '@abp/ng.theme.shared';
|
||||
import { Component, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { DoctorService } from '@proxy/doctors';
|
||||
import { CreateUpdateShiftManagementDto } from '@proxy/doctors/dto';
|
||||
import { TimeSlot, WorkSchedule } from '@proxy/global-enum';
|
||||
import { ShiftManagementService } from '@proxy/shift-management';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shift-management',
|
||||
templateUrl: './shift-management.component.html',
|
||||
styleUrl: './shift-management.component.scss',
|
||||
})
|
||||
export class ShiftManagementComponent implements OnInit {
|
||||
doctors = [];
|
||||
selectedDoctor: string | null = null;
|
||||
selectedDoctorName = '';
|
||||
selectedDoctorEmail = '';
|
||||
selectedDoctorPhone = '';
|
||||
selectedDoctorSpecialty = '';
|
||||
selectedDays = [];
|
||||
selectedTimeSlots: { [key: string]: TimeSlot } = {};
|
||||
DoctorID: string;
|
||||
constructor(
|
||||
private DoctorService: DoctorService,
|
||||
private shiftService: ShiftManagementService,
|
||||
private toaster: ToasterService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute
|
||||
) {}
|
||||
ngOnInit(): void {
|
||||
this.getdoctorlist();
|
||||
|
||||
setTimeout(() => {
|
||||
this.DoctorID = this.route.snapshot.paramMap.get('id');
|
||||
this.onDoctorSelect(this.DoctorID);
|
||||
}, 10);
|
||||
|
||||
}
|
||||
|
||||
getdoctorlist() {
|
||||
this.DoctorService.get().subscribe(result => {
|
||||
const doctors = result;
|
||||
this.doctors = doctors.map(doctor => ({
|
||||
label: `Dr. ${doctor.firstName} ${doctor.lastName}`,
|
||||
value: doctor.id,
|
||||
availability: doctor.availability,
|
||||
email: doctor.email,
|
||||
phoneNo: doctor.mobile,
|
||||
specialist: doctor.specialization,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
shiftData: CreateUpdateShiftManagementDto = {
|
||||
doctorId: this.selectedDoctor,
|
||||
shifts: {} as Record<WorkSchedule, TimeSlot>,
|
||||
isActive: true,
|
||||
};
|
||||
|
||||
workScheduleOptions = Object.keys(WorkSchedule)
|
||||
.filter(key => isNaN(Number(key)))
|
||||
.map(key => ({
|
||||
label: key.replace(/([A-Z])/g, ' $1').trim(),
|
||||
value: WorkSchedule[key as keyof typeof WorkSchedule],
|
||||
}));
|
||||
|
||||
getTimeRangeLabel(value: TimeSlot): string {
|
||||
const timeRanges = {
|
||||
[TimeSlot.TenToSeven]: { start: 10, end: 19 },
|
||||
[TimeSlot.NineToFive]: { start: 9, end: 17 },
|
||||
[TimeSlot.EightToFour]: { start: 8, end: 16 },
|
||||
[TimeSlot.SevenToFour]: { start: 7, end: 16 },
|
||||
[TimeSlot.SixToThree]: { start: 6, end: 15 },
|
||||
[TimeSlot.TwelveToNine]: { start: 12, end: 21 },
|
||||
[TimeSlot.TenToSix]: { start: 10, end: 18 },
|
||||
[TimeSlot.ElevenToEight]: { start: 11, end: 20 },
|
||||
};
|
||||
|
||||
const { start, end } = timeRanges[value];
|
||||
const formatTime = (hour: number): string => {
|
||||
const period = hour >= 12 ? 'PM' : 'AM';
|
||||
const formattedHour = hour % 12 || 12;
|
||||
return `${formattedHour}:00 ${period}`;
|
||||
};
|
||||
|
||||
return `${formatTime(start)} - ${formatTime(end)}`;
|
||||
}
|
||||
timeSlotOptions = Object.keys(TimeSlot)
|
||||
.filter(key => isNaN(Number(key)))
|
||||
.map(key => ({
|
||||
label: this.getTimeRangeLabel(TimeSlot[key as keyof typeof TimeSlot]),
|
||||
value: TimeSlot[key as keyof typeof TimeSlot],
|
||||
}));
|
||||
|
||||
onDoctorSelect(doctorId: string) {
|
||||
const selectedDoctor = this.doctors.find(doctor => doctor.value === doctorId);
|
||||
|
||||
if (selectedDoctor) {
|
||||
this.selectedDoctorName = selectedDoctor.label;
|
||||
this.selectedDoctorEmail = selectedDoctor.email;
|
||||
this.selectedDoctorPhone = selectedDoctor.phoneNo;
|
||||
this.selectedDoctorSpecialty = selectedDoctor.specialist;
|
||||
this.shiftService.getShiftsByDoctorId(doctorId).subscribe(data => {
|
||||
if (data && data.shifts && Object.keys(data.shifts).length > 0) {
|
||||
this.selectedTimeSlots = {};
|
||||
this.selectedDays = Object.keys(data.shifts).map(key => {
|
||||
return this.daysOfWeek[WorkSchedule[key as keyof typeof WorkSchedule] - 1];
|
||||
});
|
||||
for (const [day, timeSlot] of Object.entries(data.shifts)) {
|
||||
const dayEnumKey = WorkSchedule[day as keyof typeof WorkSchedule];
|
||||
if (dayEnumKey !== undefined) {
|
||||
this.selectedTimeSlots[this.daysOfWeek[dayEnumKey - 1]] = timeSlot;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.selectedDays = this.getDaysFromSchedule(selectedDoctor.availability);
|
||||
this.selectedDays.forEach(day => {
|
||||
this.selectedTimeSlots[day] = TimeSlot.NineToFive;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
daysOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
||||
|
||||
getDaysFromSchedule(scheduleValue: WorkSchedule): string[] {
|
||||
let days: string[] = [];
|
||||
if (scheduleValue >= 1 && scheduleValue <= 7) {
|
||||
days.push(this.daysOfWeek[scheduleValue - 1]);
|
||||
} else {
|
||||
const startDayIndex = (scheduleValue - 8) % 7;
|
||||
const endDayIndex = (startDayIndex + 4) % 7;
|
||||
for (let i = 0; i < 5; i++) {
|
||||
days.push(this.daysOfWeek[(startDayIndex + i) % 7]);
|
||||
}
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
saveShiftData() {
|
||||
if (!this.selectedDoctor) {
|
||||
return;
|
||||
}
|
||||
this.selectedDays;
|
||||
const shiftData: CreateUpdateShiftManagementDto = {
|
||||
doctorId: this.selectedDoctor,
|
||||
shifts: Object.keys(this.selectedTimeSlots).reduce((acc, day) => {
|
||||
const timeSlot = this.selectedTimeSlots[day]; // Get selected time slot
|
||||
const dayEnumKey = WorkSchedule[day as keyof typeof WorkSchedule]; // Convert string to WorkSchedule enum
|
||||
|
||||
if (timeSlot !== undefined && dayEnumKey !== undefined) {
|
||||
acc[dayEnumKey] = timeSlot; // Store correctly in shifts
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<WorkSchedule, TimeSlot>),
|
||||
isActive: true,
|
||||
};
|
||||
|
||||
this.shiftService.createOrUpdateShift(shiftData).subscribe({
|
||||
next: response => {
|
||||
this.toaster.success('Updated Successfully', 'Success');
|
||||
},
|
||||
error: err => {
|
||||
this.toaster.error(err, 'Error');
|
||||
},
|
||||
});
|
||||
}
|
||||
backToDoctor() {
|
||||
this.router.navigate(['/doctors']);
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IdentityModule } from '@abp/ng.identity';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CustomRolesComponent } from './custom-roles/custom-roles.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IdentityModule,
|
||||
TableModule,
|
||||
ButtonModule,
|
||||
CustomRolesComponent
|
||||
],
|
||||
exports:[
|
||||
CustomRolesComponent
|
||||
]
|
||||
})
|
||||
export class CustomIdentityModule { }
|
@ -1,154 +0,0 @@
|
||||
<abp-page [title]="'AbpIdentity::Roles' | abpLocalization" [toolbar]="data.items">
|
||||
<div id="identity-roles-wrapper" class="card">
|
||||
<div class="card-body">
|
||||
<!-- <abp-extensible-table
|
||||
[data]="data.items"
|
||||
[recordsTotal]="data.totalCount"
|
||||
[list]="list"
|
||||
></abp-extensible-table> -->
|
||||
<div class="flex justify-content-between align-items-center mb-3">
|
||||
<button
|
||||
pButton
|
||||
icon="pi pi-plus"
|
||||
label="{{ '::addRoles' | abpLocalization }}"
|
||||
class="p-button-success"
|
||||
(click)="add()"
|
||||
></button>
|
||||
</div>
|
||||
<p-table [value]="data.items" [paginator]="true" [rows]="5" responsiveLayout="scroll">
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th pSortableColumn="name">
|
||||
{{ 'AbpIdentity::RoleName' | abpLocalization }}
|
||||
<p-sortIcon field="name"></p-sortIcon>
|
||||
</th>
|
||||
<th>{{ 'AbpIdentity::Actions' | abpLocalization }}</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="body" let-role>
|
||||
<tr>
|
||||
<td>{{ role.name }}</td>
|
||||
|
||||
<td>
|
||||
<button
|
||||
pButton
|
||||
icon="pi pi-pencil"
|
||||
class="p-button-text p-button-primary"
|
||||
(click)="edit(role.id)"
|
||||
title="{{ 'AbpIdentity::Edit' | abpLocalization }}"
|
||||
></button>
|
||||
<button
|
||||
pButton
|
||||
icon="pi pi-trash"
|
||||
class="p-button-text p-button-danger"
|
||||
(click)="delete(role.id, role.name)"
|
||||
title="{{ 'AbpIdentity::Delete' | abpLocalization }}"
|
||||
></button>
|
||||
<button
|
||||
pButton
|
||||
icon="pi pi-lock"
|
||||
class="p-button-text p-button-success"
|
||||
(click)="openPermissionsModal(role.name)"
|
||||
title="{{ 'AbpIdentity::Permissions' | abpLocalization }}"
|
||||
></button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<abp-modal [(visible)]="isModalVisible" [busy]="modalBusy">
|
||||
<ng-template #abpHeader>
|
||||
<h3>{{ (selected?.id ? 'AbpIdentity::Edit' : 'AbpIdentity::NewRole') | abpLocalization }}</h3>
|
||||
</ng-template>
|
||||
|
||||
<!-- <ng-template #abpBody>
|
||||
<form [formGroup]="form" (ngSubmit)="save()" validateOnSubmit>
|
||||
<abp-extensible-form [selectedRecord]="selected"></abp-extensible-form>
|
||||
</form>
|
||||
</ng-template> -->
|
||||
|
||||
<ng-template #abpBody>
|
||||
<form #roleForm="ngForm" (ngSubmit)="save()" validateOnSubmit>
|
||||
<!-- Name Field -->
|
||||
<div class="form-group">
|
||||
<label for="name">{{ 'AbpIdentity::Name' | abpLocalization }}</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
class="form-control"
|
||||
[(ngModel)]="selected.name"
|
||||
name="name"
|
||||
required
|
||||
#nameCtrl="ngModel"
|
||||
/>
|
||||
<small class="text-danger" *ngIf="nameCtrl.invalid && nameCtrl.touched">
|
||||
{{ 'AbpIdentity::NameIsRequired' | abpLocalization }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Is Default Checkbox -->
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="isDefault"
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
[(ngModel)]="selected.isDefault"
|
||||
name="isDefault"
|
||||
/>
|
||||
<label for="isDefault" class="form-check-label">
|
||||
{{ 'AbpIdentity::Default' | abpLocalization }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Is Public Checkbox -->
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="isPublic"
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
[(ngModel)]="selected.isPublic"
|
||||
name="isPublic"
|
||||
/>
|
||||
<label for="isPublic" class="form-check-label">
|
||||
{{ 'AbpIdentity::Public' | abpLocalization }}
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
<abp-button iconClass="fa fa-check" [disabled]="roleForm.invalid" (click)="save()">{{
|
||||
'AbpIdentity::Save' | abpLocalization
|
||||
}}</abp-button>
|
||||
</form>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #abpFooter>
|
||||
<button type="button" class="btn btn-outline-primary" abpClose>
|
||||
{{ 'AbpIdentity::Cancel' | abpLocalization }}
|
||||
</button>
|
||||
<!-- <abp-button iconClass="fa fa-check" [disabled]="roleForm?.invalid" (click)="save()">{{
|
||||
'AbpIdentity::Save' | abpLocalization
|
||||
}}</abp-button> -->
|
||||
</ng-template>
|
||||
</abp-modal>
|
||||
|
||||
<abp-permission-management
|
||||
#abpPermissionManagement="abpPermissionManagement"
|
||||
*abpReplaceableTemplate="
|
||||
{
|
||||
inputs: {
|
||||
providerName: { value: 'R' },
|
||||
providerKey: { value: providerKey },
|
||||
visible: { value: visiblePermissions, twoWay: true },
|
||||
hideBadges: { value: true }
|
||||
},
|
||||
outputs: { visibleChange: onVisiblePermissionChange },
|
||||
componentKey: permissionManagementKey
|
||||
};
|
||||
let init = initTemplate
|
||||
"
|
||||
(abpInit)="init(abpPermissionManagement)"
|
||||
>
|
||||
</abp-permission-management>
|
||||
</abp-page>
|
@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CustomRolesComponent } from './custom-roles.component';
|
||||
|
||||
describe('CustomRolesComponent', () => {
|
||||
let component: CustomRolesComponent;
|
||||
let fixture: ComponentFixture<CustomRolesComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [CustomRolesComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CustomRolesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,215 +0,0 @@
|
||||
import {
|
||||
ExtensibleModule,
|
||||
EXTENSIONS_IDENTIFIER,
|
||||
FormPropData,
|
||||
generateFormFromProps,
|
||||
} from '@abp/ng.components/extensible';
|
||||
import { PageModule } from '@abp/ng.components/page';
|
||||
import {
|
||||
CoreModule,
|
||||
ListResultDto,
|
||||
ListService,
|
||||
LocalizationModule,
|
||||
PagedAndSortedResultRequestDto,
|
||||
PagedResultDto,
|
||||
} from '@abp/ng.core';
|
||||
import { IdentityRoleCreateDto, IdentityRoleDto, IdentityRoleService, IdentityRoleUpdateDto } from '@abp/ng.identity/proxy';
|
||||
import {
|
||||
ThemeSharedModule,
|
||||
ConfirmationService,
|
||||
ToasterService,
|
||||
Confirmation,
|
||||
} from '@abp/ng.theme.shared';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, inject, Injector, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, FormsModule, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { finalize } from 'rxjs';
|
||||
import { ePermissionManagementComponents } from '@abp/ng.permission-management';
|
||||
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
|
||||
import { eIdentityComponents } from '@abp/ng.identity';
|
||||
import { PermissionManagementModule } from '@abp/ng.permission-management';
|
||||
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { MenuItem } from 'primeng/api';
|
||||
import { Dropdown, DropdownModule } from 'primeng/dropdown';
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'app-custom-roles',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
TableModule,
|
||||
ButtonModule,
|
||||
PageModule,
|
||||
LocalizationModule,
|
||||
ThemeSharedModule,
|
||||
NgxDatatableModule,
|
||||
ExtensibleModule,
|
||||
PermissionManagementModule,
|
||||
CoreModule,
|
||||
NgbNavModule,
|
||||
DropdownModule,
|
||||
SharedModule
|
||||
],
|
||||
templateUrl: './custom-roles.component.html',
|
||||
providers: [
|
||||
ListService,
|
||||
{
|
||||
provide: EXTENSIONS_IDENTIFIER,
|
||||
useValue: eIdentityComponents.Roles,
|
||||
},
|
||||
],
|
||||
styleUrl: './custom-roles.component.scss',
|
||||
})
|
||||
export class CustomRolesComponent implements OnInit {
|
||||
protected readonly list = inject(ListService<PagedAndSortedResultRequestDto>);
|
||||
protected readonly confirmationService = inject(ConfirmationService);
|
||||
protected readonly toasterService = inject(ToasterService);
|
||||
private readonly injector = inject(Injector);
|
||||
protected readonly service = inject(IdentityRoleService);
|
||||
|
||||
data: PagedResultDto<IdentityRoleDto> = { items: [], totalCount: 0 };
|
||||
|
||||
form!: UntypedFormGroup;
|
||||
|
||||
selected?: IdentityRoleDto;
|
||||
|
||||
isModalVisible!: boolean;
|
||||
|
||||
visiblePermissions = false;
|
||||
|
||||
providerKey?: string;
|
||||
|
||||
modalBusy = false;
|
||||
|
||||
permissionManagementKey = ePermissionManagementComponents.PermissionManagement;
|
||||
|
||||
onVisiblePermissionChange = (event: boolean) => {
|
||||
this.visiblePermissions = event;
|
||||
};
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
this.hookToQuery();
|
||||
}
|
||||
|
||||
// buildForm() {
|
||||
// const data = new FormPropData(this.injector, this.selected);
|
||||
// this.form = generateFormFromProps(data);
|
||||
// }
|
||||
buildForm() {
|
||||
this.form = new FormGroup({
|
||||
name: new FormControl(this.selected?.name || '', Validators.required),
|
||||
isDefault: new FormControl(this.selected?.isDefault || false),
|
||||
isPublic: new FormControl(this.selected?.isPublic || false)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
openModal() {
|
||||
this.buildForm();
|
||||
this.isModalVisible = true;
|
||||
}
|
||||
|
||||
add() {
|
||||
debugger
|
||||
// this.selected = {} as IdentityRoleDto;
|
||||
// this.openModal();
|
||||
this.selected = { name: '', isDefault: false, isPublic: false } as IdentityRoleDto;
|
||||
this.isModalVisible = true;
|
||||
}
|
||||
|
||||
edit(id: string) {
|
||||
debugger
|
||||
// this.service.get(id).subscribe(res => {
|
||||
// this.selected = res;
|
||||
// this.openModal();
|
||||
// });
|
||||
this.service.get(id).subscribe(res => {
|
||||
this.selected = res;
|
||||
this.isModalVisible = true;
|
||||
});
|
||||
}
|
||||
|
||||
// save() {
|
||||
// debugger
|
||||
// if (!this.form.valid) return;
|
||||
// this.modalBusy = true;
|
||||
|
||||
// const { id } = this.selected || {};
|
||||
// (id
|
||||
// ? this.service.update(id, { ...this.selected, ...this.form.value })
|
||||
// : this.service.create(this.form.value)
|
||||
// )
|
||||
// .pipe(finalize(() => (this.modalBusy = false)))
|
||||
// .subscribe(() => {
|
||||
// this.isModalVisible = false;
|
||||
// this.toasterService.success('AbpUi::SavedSuccessfully');
|
||||
// this.list.get();
|
||||
// });
|
||||
// }
|
||||
save() {
|
||||
debugger
|
||||
if (!this.selected?.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.modalBusy = true;
|
||||
|
||||
const { id, name, isDefault, isPublic, concurrencyStamp } = this.selected;
|
||||
|
||||
const roleDto = {
|
||||
name,
|
||||
isDefault,
|
||||
isPublic,
|
||||
concurrencyStamp
|
||||
};
|
||||
|
||||
const saveObservable = id
|
||||
? this.service.update(id, roleDto) // Update call
|
||||
: this.service.create(roleDto); // Create call
|
||||
|
||||
saveObservable
|
||||
.pipe(finalize(() => (this.modalBusy = false)))
|
||||
.subscribe(() => {
|
||||
this.isModalVisible = false;
|
||||
this.toasterService.success('AbpUi::SavedSuccessfully');
|
||||
this.list.get();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
delete(id: string, name: string) {
|
||||
this.confirmationService
|
||||
.warn('AbpIdentity::RoleDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', {
|
||||
messageLocalizationParams: [name],
|
||||
})
|
||||
.subscribe((status: Confirmation.Status) => {
|
||||
if (status === Confirmation.Status.confirm) {
|
||||
this.toasterService.success('AbpUi::DeletedSuccessfully');
|
||||
this.service.delete(id).subscribe(() => this.list.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private hookToQuery() {
|
||||
this.list.hookToQuery(query => this.service.getList(query)).subscribe(res => (this.data = res));
|
||||
}
|
||||
|
||||
openPermissionsModal(providerKey: string) {
|
||||
this.providerKey = providerKey;
|
||||
setTimeout(() => {
|
||||
this.visiblePermissions = true;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
sort(data: any) {
|
||||
const { prop, dir } = data.sorts[0];
|
||||
this.list.sortKey = prop;
|
||||
this.list.sortOrder = dir;
|
||||
}
|
||||
}
|
@ -1,355 +0,0 @@
|
||||
<!-- <div>
|
||||
<h3>Total Users: {{ data.totalCount }}</h3>
|
||||
<ul>
|
||||
<li *ngFor="let user of data.items">
|
||||
<strong>{{ user.userName }}</strong> ({{ user.email }})
|
||||
</li>
|
||||
</ul>
|
||||
</div> -->
|
||||
|
||||
<abp-page [title]="'AbpIdentity::Users' | abpLocalization" [toolbar]="data.items">
|
||||
<div id="identity-roles-wrapper" class="card">
|
||||
<div class="card-body">
|
||||
<div id="data-tables-table-filter" class="data-tables-filter mb-3">
|
||||
<div class="flex justify-content-between align-items-center mb-3">
|
||||
<button
|
||||
pButton
|
||||
icon="pi pi-plus"
|
||||
label="{{ '::addUser' | abpLocalization }}"
|
||||
class="p-button-success"
|
||||
(click)="add()"
|
||||
></button>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="search"
|
||||
class="form-control"
|
||||
[placeholder]="'AbpUi::PagerSearch' | abpLocalization"
|
||||
[(ngModel)]="list.filter"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<abp-extensible-table
|
||||
|
||||
[data]="data.items"
|
||||
[recordsTotal]="data.totalCount"
|
||||
[list]="list"
|
||||
|
||||
></abp-extensible-table> -->
|
||||
<p-table
|
||||
[value]="data.items"
|
||||
[tableStyle]="{ 'min-width': '60rem' }"
|
||||
[paginator]="true"
|
||||
[rows]="5"
|
||||
responsiveLayout="scroll"
|
||||
>
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th pSortableColumn="userName">
|
||||
{{ '::username' | abpLocalization }}
|
||||
<p-sortIcon field="userName"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="name">
|
||||
{{ '::firstName' | abpLocalization }}
|
||||
<p-sortIcon field="name"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="surname">
|
||||
{{ '::lastName' | abpLocalization }}
|
||||
<p-sortIcon field="surname"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="email">
|
||||
{{ '::email' | abpLocalization }}
|
||||
<p-sortIcon field="email"></p-sortIcon>
|
||||
</th>
|
||||
<th pSortableColumn="phoneNumber">
|
||||
{{ '::phoneNumber' | abpLocalization }}
|
||||
<p-sortIcon field="phoneNumber"></p-sortIcon>
|
||||
</th>
|
||||
<th>{{ '::status' | abpLocalization }}</th>
|
||||
<th>{{ '::actions' | abpLocalization }}</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="body" let-user>
|
||||
<tr>
|
||||
<td>{{ user.userName }}</td>
|
||||
<td>{{ user.name }}</td>
|
||||
<td>{{ user.surname }}</td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td>{{ user.phoneNumber }}</td>
|
||||
<td>
|
||||
<p-tag
|
||||
[value]="user.isActive ? 'Active' : 'Inactive'"
|
||||
[severity]="user.isActive ? 'success' : 'danger'"
|
||||
></p-tag>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group" dropdown placement="bottom left" container="body">
|
||||
<p-splitButton
|
||||
label="{{ '::actions' | abpLocalization }}"
|
||||
icon="pi pi-cog"
|
||||
class="p-button-sm p-button-primary"
|
||||
[model]="actionItems"
|
||||
(onClick)="editUser(user.id)"
|
||||
>
|
||||
</p-splitButton>
|
||||
</div>
|
||||
<!-- <button
|
||||
pButton
|
||||
icon="pi pi-pencil"
|
||||
class="p-button-text p-button-primary"
|
||||
(click)="edit(user.id)"
|
||||
></button>
|
||||
<button
|
||||
pButton
|
||||
icon="pi pi-trash"
|
||||
class="p-button-text p-button-danger"
|
||||
(click)="delete(user.id, user.userName)"
|
||||
></button>
|
||||
<button
|
||||
pButton
|
||||
icon="pi pi-lock"
|
||||
class="p-button-text p-button-success"
|
||||
(click)="openPermissionsModal(user.id, user.userName)"
|
||||
></button> -->
|
||||
<!-- <div class="btn-group" dropdown placement="bottom left" container="body">
|
||||
<button
|
||||
id="dropdownButton"
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm dropdown-toggle"
|
||||
dropdownToggle
|
||||
aria-controls="dropdownMenu"
|
||||
(click)="toggleDropdown()"
|
||||
>
|
||||
<i class="fa fa-cog"></i>
|
||||
<span class="caret"></span>
|
||||
Actions
|
||||
</button>
|
||||
<ul
|
||||
id="dropdownMenu"
|
||||
#dropdownMenu
|
||||
class="dropdown-menu"
|
||||
role="menu"
|
||||
aria-labelledby="dropdownButton"
|
||||
|
||||
>
|
||||
<li role="menuitem" tabindex="0">
|
||||
<a
|
||||
#firstDropdownItem
|
||||
class="dropdown-item"
|
||||
href="javascript:;"
|
||||
(click)="edit(user.id)"
|
||||
>
|
||||
Edit
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div> -->
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<abp-modal [(visible)]="isModalVisible" [busy]="modalBusy">
|
||||
<ng-template #abpHeader>
|
||||
<h3>
|
||||
{{ (selected?.id ? 'AbpIdentity::EditUser' : 'AbpIdentity::NewUser') | abpLocalization }}
|
||||
</h3>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #abpBody>
|
||||
<form #userForm="ngForm" (ngSubmit)="save(userForm)">
|
||||
<!-- User Information Tab -->
|
||||
<ul ngbNav #nav="ngbNav" class="nav-tabs">
|
||||
<li ngbNavItem>
|
||||
<a ngbNavLink>{{ 'AbpIdentity::UserInformations' | abpLocalization }}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="form-group">
|
||||
<label for="userName">
|
||||
{{ '::userName' | abpLocalization }}
|
||||
</label>
|
||||
<input
|
||||
id="userName"
|
||||
type="text"
|
||||
class="form-control"
|
||||
[(ngModel)]="selected.userName"
|
||||
name="userName"
|
||||
required
|
||||
#userName="ngModel"
|
||||
/>
|
||||
<div *ngIf="userName.invalid && userName.touched" class="text-danger">
|
||||
{{ '::userNameIsRequired' | abpLocalization }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" *ngIf="!selected.id">
|
||||
<label for="password">
|
||||
{{ '::password' | abpLocalization }}
|
||||
</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
class="form-control"
|
||||
[(ngModel)]="password"
|
||||
name="password"
|
||||
required
|
||||
#passwordField="ngModel"
|
||||
/>
|
||||
<div *ngIf="passwordField.invalid && passwordField.touched" class="text-danger">
|
||||
{{ '::passwordIsRequired' | abpLocalization }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">
|
||||
{{ '::firstName' | abpLocalization }}
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
class="form-control"
|
||||
[(ngModel)]="selected.name"
|
||||
name="name"
|
||||
#name="ngModel"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="surname">
|
||||
{{ '::lastName' | abpLocalization }}
|
||||
</label>
|
||||
<input
|
||||
id="surname"
|
||||
type="text"
|
||||
class="form-control"
|
||||
[(ngModel)]="selected.surname"
|
||||
name="surname"
|
||||
#surname="ngModel"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div class="form-group">
|
||||
<label for="email">
|
||||
{{ '::email' | abpLocalization }}
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
class="form-control"
|
||||
[(ngModel)]="selected.email"
|
||||
name="email"
|
||||
required
|
||||
#email="ngModel"
|
||||
/>
|
||||
<div *ngIf="email.invalid && email.touched" class="text-danger">
|
||||
{{ '::validEmailIsRequired' | abpLocalization }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="phoneNumber">
|
||||
{{ '::phoneNumber' | abpLocalization }}
|
||||
</label>
|
||||
<input
|
||||
id="phoneNumber"
|
||||
type="text"
|
||||
class="form-control"
|
||||
[(ngModel)]="selected.phoneNumber"
|
||||
name="phoneNumber"
|
||||
#phoneNumber="ngModel"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="isActive"
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
[(ngModel)]="selected.isActive"
|
||||
name="isActive"
|
||||
/>
|
||||
<label for="isActive" class="form-check-label">
|
||||
{{ '::active' | abpLocalization }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="lockoutEnabled"
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
[(ngModel)]="selected.lockoutEnabled"
|
||||
name="lockoutEnabled"
|
||||
/>
|
||||
<label for="lockoutEnabled" class="form-check-label">
|
||||
{{ '::lockoutEnabled' | abpLocalization }}
|
||||
</label>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li ngbNavItem>
|
||||
<a ngbNavLink>{{ 'AbpIdentity::Roles' | abpLocalization }}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div *ngFor="let role of roles; let i = index">
|
||||
<div class="form-check mb-2">
|
||||
<label class="form-check-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
[(ngModel)]="role.selected"
|
||||
name="role-{{ i }}"
|
||||
/>
|
||||
{{ role.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mt-2 fade-in-top" [ngbNavOutlet]="nav"></div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-primary" [disabled]="userForm.invalid">
|
||||
{{ '::save' | abpLocalization }}
|
||||
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #abpFooter>
|
||||
<button type="button" class="btn btn-outline-primary" abpClose>
|
||||
{{ '::cancel' | abpLocalization }}
|
||||
</button>
|
||||
<!-- <abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">{{
|
||||
'AbpIdentity::Save' | abpLocalization
|
||||
}}</abp-button> -->
|
||||
</ng-template>
|
||||
</abp-modal>
|
||||
|
||||
<abp-permission-management
|
||||
#abpPermissionManagement="abpPermissionManagement"
|
||||
*abpReplaceableTemplate="
|
||||
{
|
||||
inputs: {
|
||||
providerName: { value: 'U' },
|
||||
providerKey: { value: providerKey },
|
||||
visible: { value: visiblePermissions, twoWay: true }
|
||||
},
|
||||
outputs: { visibleChange: onVisiblePermissionChange },
|
||||
componentKey: permissionManagementKey
|
||||
};
|
||||
let init = initTemplate
|
||||
"
|
||||
[entityDisplayName]="entityDisplayName"
|
||||
(abpInit)="init(abpPermissionManagement)"
|
||||
>
|
||||
</abp-permission-management>
|
||||
</abp-page>
|
@ -1,23 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CustomUsersComponent } from './custom-users.component';
|
||||
|
||||
describe('CustomUsersComponent', () => {
|
||||
let component: CustomUsersComponent;
|
||||
let fixture: ComponentFixture<CustomUsersComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [CustomUsersComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CustomUsersComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,396 +0,0 @@
|
||||
import {
|
||||
FormPropData,
|
||||
EXTENSIONS_IDENTIFIER,
|
||||
generateFormFromProps,
|
||||
ExtensibleModule,
|
||||
} from '@abp/ng.components/extensible';
|
||||
import { PageModule } from '@abp/ng.components/page';
|
||||
import {
|
||||
CoreModule,
|
||||
ListResultDto,
|
||||
ListService,
|
||||
LocalizationModule,
|
||||
PagedResultDto,
|
||||
} from '@abp/ng.core';
|
||||
import { eIdentityComponents } from '@abp/ng.identity';
|
||||
import {
|
||||
GetIdentityUsersInput,
|
||||
IdentityRoleDto,
|
||||
IdentityUserDto,
|
||||
IdentityUserService,
|
||||
IdentityUserUpdateDto,
|
||||
} from '@abp/ng.identity/proxy';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
inject,
|
||||
Injector,
|
||||
OnInit,
|
||||
TemplateRef,
|
||||
TrackByFunction,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
AbstractControl,
|
||||
FormsModule,
|
||||
NgForm,
|
||||
UntypedFormArray,
|
||||
UntypedFormBuilder,
|
||||
UntypedFormGroup,
|
||||
} from '@angular/forms';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import {
|
||||
Confirmation,
|
||||
eFormComponets,
|
||||
ThemeSharedModule,
|
||||
ToasterService,
|
||||
ConfirmationService,
|
||||
} from '@abp/ng.theme.shared';
|
||||
import { finalize, switchMap, tap } from 'rxjs';
|
||||
import {
|
||||
ePermissionManagementComponents,
|
||||
PermissionManagementModule,
|
||||
} from '@abp/ng.permission-management';
|
||||
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
|
||||
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { PaginatorModule } from 'primeng/paginator';
|
||||
import { TagModule } from 'primeng/tag'; // For active/inactive status
|
||||
import { SplitButtonModule } from 'primeng/splitbutton';
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'app-custom-users',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
TableModule,
|
||||
ButtonModule,
|
||||
PageModule,
|
||||
LocalizationModule,
|
||||
ThemeSharedModule,
|
||||
NgxDatatableModule,
|
||||
ExtensibleModule,
|
||||
PermissionManagementModule,
|
||||
CoreModule,
|
||||
NgbNavModule,
|
||||
PaginatorModule,
|
||||
TagModule,SplitButtonModule,SharedModule
|
||||
],
|
||||
templateUrl: './custom-users.component.html',
|
||||
providers: [
|
||||
ListService,
|
||||
{
|
||||
provide: EXTENSIONS_IDENTIFIER,
|
||||
useValue: eIdentityComponents.Users,
|
||||
},
|
||||
],
|
||||
styleUrl: './custom-users.component.scss',
|
||||
})
|
||||
export class CustomUsersComponent implements OnInit {
|
||||
protected readonly list = inject(ListService<GetIdentityUsersInput>);
|
||||
protected readonly confirmationService = inject(ConfirmationService);
|
||||
protected readonly service = inject(IdentityUserService);
|
||||
protected readonly toasterService = inject(ToasterService);
|
||||
private readonly fb = inject(UntypedFormBuilder);
|
||||
private readonly injector = inject(Injector);
|
||||
@ViewChild('firstDropdownItem') firstDropdownItem: ElementRef;
|
||||
|
||||
data: PagedResultDto<IdentityUserDto> = { items: [], totalCount: 0 };
|
||||
|
||||
@ViewChild('modalContent', { static: false })
|
||||
modalContent!: TemplateRef<any>;
|
||||
|
||||
form!: UntypedFormGroup;
|
||||
password:string;
|
||||
selected?: IdentityUserDto;
|
||||
|
||||
selectedUserRoles?: IdentityRoleDto[];
|
||||
|
||||
// roles?: IdentityRoleDto[];
|
||||
roles: (IdentityRoleDto & { selected: boolean })[] = []; // Use intersection type to add 'selected'
|
||||
|
||||
visiblePermissions = false;
|
||||
|
||||
providerKey?: string;
|
||||
|
||||
isModalVisible?: boolean;
|
||||
|
||||
modalBusy = false;
|
||||
|
||||
permissionManagementKey = ePermissionManagementComponents.PermissionManagement;
|
||||
|
||||
entityDisplayName: string;
|
||||
|
||||
inputKey = eFormComponets.FormCheckboxComponent;
|
||||
|
||||
trackByFn: TrackByFunction<AbstractControl> = (index, item) => Object.keys(item)[0] || index;
|
||||
isDropdownOpen = false;
|
||||
|
||||
toggleDropdown() {
|
||||
this.isDropdownOpen = !this.isDropdownOpen;
|
||||
|
||||
// After the dropdown is shown, focus the first item in the dropdown
|
||||
setTimeout(() => {
|
||||
if (this.firstDropdownItem) {
|
||||
this.firstDropdownItem.nativeElement.focus();
|
||||
}
|
||||
}, 100); // Delay to ensure dropdown is rendered
|
||||
}
|
||||
|
||||
closeDropdown() {
|
||||
this.isDropdownOpen = false;
|
||||
}
|
||||
actionItems = [
|
||||
{ label: 'Edit', icon: 'pi pi-pencil', command: () => this.edit("") },
|
||||
{ label: 'Delete', icon: 'pi pi-trash', command: () => this.delete("","") },
|
||||
// { label: 'Permissions', icon: 'pi pi-shield', command: () => this.openPermissionsModal("") },
|
||||
];
|
||||
|
||||
|
||||
|
||||
onVisiblePermissionChange = (event: boolean) => {
|
||||
debugger
|
||||
this.visiblePermissions = event;
|
||||
};
|
||||
|
||||
get roleGroups(): UntypedFormGroup[] {
|
||||
return ((this.form.get('roleNames') as UntypedFormArray)?.controls as UntypedFormGroup[]) || [];
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.hookToQuery();
|
||||
}
|
||||
formValid() {
|
||||
return true;
|
||||
}
|
||||
// buildForm() {
|
||||
// debugger
|
||||
// const data = new FormPropData(this.injector, this.selected);
|
||||
// this.form = generateFormFromProps(data);
|
||||
|
||||
// this.service.getAssignableRoles().subscribe(({ items }) => {
|
||||
// this.roles = items;
|
||||
// if (this.roles) {
|
||||
// this.form.addControl(
|
||||
// 'roleNames',
|
||||
// this.fb.array(
|
||||
// this.roles.map(role =>
|
||||
// this.fb.group({
|
||||
// [role.name as string]: [
|
||||
// this.selected?.id
|
||||
// ? !!this.selectedUserRoles?.find(userRole => userRole.id === role.id)
|
||||
// : role.isDefault,
|
||||
// ],
|
||||
// }),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// openModal() {
|
||||
// this.buildForm();
|
||||
// this.isModalVisible = true;
|
||||
// }
|
||||
|
||||
// add() {
|
||||
// debugger
|
||||
// this.selected = {} as IdentityUserDto;
|
||||
// this.selectedUserRoles = [] as IdentityRoleDto[];
|
||||
// this.openModal();
|
||||
// }
|
||||
|
||||
// edit(id: string) {
|
||||
// debugger
|
||||
// this.service
|
||||
// .get(id)
|
||||
// .pipe(
|
||||
// tap(user => (this.selected = user)),
|
||||
// switchMap(() => this.service.getRoles(id)),
|
||||
// )
|
||||
// .subscribe(userRole => {
|
||||
// debugger
|
||||
|
||||
// this.selectedUserRoles = userRole.items || [];
|
||||
// this.openModal();
|
||||
// });
|
||||
// }
|
||||
|
||||
// save() {
|
||||
// if (!this.form.valid || this.modalBusy) return;
|
||||
// this.modalBusy = true;
|
||||
|
||||
// const { roleNames = [] } = this.form.value;
|
||||
// const mappedRoleNames =
|
||||
// roleNames
|
||||
// .filter((role: { [key: string]: any }) => !!role[Object.keys(role)[0]])
|
||||
// .map((role: { [key: string]: any }) => Object.keys(role)[0]) || [];
|
||||
|
||||
// const { id } = this.selected || {};
|
||||
|
||||
// (id
|
||||
// ? this.service.update(id, {
|
||||
// ...this.selected,
|
||||
// ...this.form.value,
|
||||
// roleNames: mappedRoleNames,
|
||||
// })
|
||||
// : this.service.create({ ...this.form.value, roleNames: mappedRoleNames })
|
||||
// )
|
||||
// .pipe(finalize(() => (this.modalBusy = false)))
|
||||
// .subscribe(() => {
|
||||
// this.isModalVisible = false;
|
||||
// this.toasterService.success('AbpUi::SavedSuccessfully');
|
||||
// this.list.get();
|
||||
// });
|
||||
// }
|
||||
buildForm() {
|
||||
this.service.getAssignableRoles().subscribe(({ items }) => {
|
||||
this.roles = items.map(role => ({
|
||||
...role,
|
||||
selected: this.selectedUserRoles.some(userRole => userRole.id === role.id),
|
||||
}));
|
||||
|
||||
// Initialize the selected roles based on existing data for selected user
|
||||
if (this.selected?.id) {
|
||||
this.roles.forEach(role => {
|
||||
role.selected = this.selectedUserRoles.some(userRole => userRole.id === role.id);
|
||||
});
|
||||
} else {
|
||||
// Default values when creating a new user
|
||||
this.roles.forEach(role => {
|
||||
role.selected = role.isDefault;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openModal() {
|
||||
this.loadRoles(); // Load roles when modal opens
|
||||
this.isModalVisible = true;
|
||||
}
|
||||
loadRoles() {
|
||||
this.service.getAssignableRoles().subscribe(({ items }) => {
|
||||
this.roles = items.map(role => ({
|
||||
...role, // spread IdentityRoleDto properties
|
||||
selected: false, // Initially no role is selected
|
||||
}));
|
||||
});
|
||||
}
|
||||
add() {
|
||||
this.selected = {} as IdentityUserDto;
|
||||
this.selectedUserRoles = [] as IdentityRoleDto[];
|
||||
this.password = ''; // Store password separately
|
||||
|
||||
this.openModal();
|
||||
}
|
||||
|
||||
edit(id: string) {
|
||||
this.service
|
||||
.get(id)
|
||||
.pipe(finalize(() => this.buildForm()))
|
||||
.subscribe(user => {
|
||||
this.selected = user;
|
||||
this.service.getRoles(id).subscribe(userRoles => {
|
||||
this.selectedUserRoles = userRoles.items || [];
|
||||
});
|
||||
});
|
||||
this.openModal();
|
||||
}
|
||||
|
||||
// Save user data (either create or update)
|
||||
save(form: NgForm) {
|
||||
debugger;
|
||||
if (this.modalBusy || form.invalid) return;
|
||||
|
||||
this.modalBusy = true;
|
||||
|
||||
// Prepare roleNames array
|
||||
// Prepare roleNames array
|
||||
const selectedRoleNames = this.roles
|
||||
.filter(role => role.selected) // Only include selected roles
|
||||
.map(role => role.name); // Extract role names
|
||||
|
||||
// Create user payload
|
||||
|
||||
const userPayload: any = {
|
||||
userName: this.selected.userName,
|
||||
name: this.selected.name,
|
||||
surname: this.selected.surname,
|
||||
email: this.selected.email,
|
||||
phoneNumber: this.selected.phoneNumber,
|
||||
isActive: this.selected.isActive,
|
||||
lockoutEnabled: this.selected.lockoutEnabled,
|
||||
roleNames: selectedRoleNames, // Include selected roles
|
||||
|
||||
};
|
||||
|
||||
// Only include password if creating a new user
|
||||
if (!this.selected.id && this.password) {
|
||||
userPayload.password = this.password;
|
||||
}
|
||||
// Handle create or update
|
||||
const saveObservable = this.selected.id
|
||||
? this.service.update(this.selected.id, userPayload) // Update if id exists
|
||||
: this.service.create(userPayload); // Create if no id
|
||||
|
||||
saveObservable.pipe(finalize(() => (this.modalBusy = false))).subscribe(() => {
|
||||
this.isModalVisible = false;
|
||||
this.toasterService.success('AbpUi::SavedSuccessfully');
|
||||
this.list.get(); // Reload the list if needed
|
||||
});
|
||||
}
|
||||
|
||||
private getRoleNames() {
|
||||
return this.roles.filter(role => role.selected).map(role => role.name);
|
||||
}
|
||||
|
||||
delete(id: string, userName: string) {
|
||||
this.confirmationService
|
||||
.warn('AbpIdentity::UserDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', {
|
||||
messageLocalizationParams: [userName],
|
||||
})
|
||||
.subscribe((status: Confirmation.Status) => {
|
||||
if (status === Confirmation.Status.confirm) {
|
||||
this.service.delete(id).subscribe(() => {
|
||||
this.toasterService.success('AbpUi::DeletedSuccessfully');
|
||||
this.list.get();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sort(data: any) {
|
||||
const { prop, dir } = data.sorts[0];
|
||||
this.list.sortKey = prop;
|
||||
this.list.sortOrder = dir;
|
||||
}
|
||||
|
||||
// private hookToQuery() {
|
||||
// this.list.hookToQuery(query => this.service.getList(query)).subscribe(res => (this.data = res));
|
||||
// }
|
||||
private hookToQuery() {
|
||||
this.list
|
||||
.hookToQuery(query => {
|
||||
console.log('Query:', query); // Log the query
|
||||
return this.service.getList(query);
|
||||
})
|
||||
.subscribe(res => {
|
||||
debugger;
|
||||
this.data = res;
|
||||
});
|
||||
}
|
||||
|
||||
openPermissionsModal(providerKey: string, entityDisplayName?: string) {
|
||||
debugger;
|
||||
this.providerKey = providerKey;
|
||||
this.entityDisplayName = entityDisplayName;
|
||||
setTimeout(() => {
|
||||
this.visiblePermissions = true;
|
||||
}, 0);
|
||||
}
|
||||
}
|
@ -1,335 +0,0 @@
|
||||
<!-- <div>
|
||||
<p-table #dt2 dataKey="id" [value]="patients" [paginator]="true" [rows]="10" [totalRecords]="totalRecords"
|
||||
[lazy]="true" (onLazyLoad)="loadPatient($event)" [rowsPerPageOptions]="[10, 20, 50]"
|
||||
[responsiveLayout]="'scroll'" [globalFilterFields]="['id', 'name', 'status']"
|
||||
[filters]="{ global: { value: '', matchMode: 'contains' } }" class="table table-striped">
|
||||
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="flex">
|
||||
<h2 class="mr-auto">Patient List</h2>
|
||||
<div>
|
||||
<button *ngIf="createpermission" pButton class="p-button-rounded p-button-success ml-2"
|
||||
(click)="openNewPatientDialog()">
|
||||
<i class="pi pi-plus-circle"></i>
|
||||
</button>
|
||||
<button pButton class="p-button-rounded p-button-warning ml-2" (click)="exportPatient()">
|
||||
<i class="pi pi-download"></i>
|
||||
</button>
|
||||
<p-iconField iconPosition="right" class="ml-2">
|
||||
<p-inputIcon>
|
||||
<i class="pi pi-search"></i>
|
||||
</p-inputIcon>
|
||||
<input pInputText type="text" (input)="dt2.filterGlobal($event.target.value, 'contains')"
|
||||
[(ngModel)]="globalFilter" placeholder="Search keyword" />
|
||||
</p-iconField>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th class="hidden" pSortableColumn="id">Patient ID <p-sortIcon field="id" /></th>
|
||||
<th pSortableColumn="name">Full Name <p-sortIcon field="name" /></th>
|
||||
<th pSortableColumn="gender">Gender <p-sortIcon field="gender" /></th>
|
||||
<th pSortableColumn="admissionDate">Date of Admission <p-sortIcon field="admissionDate" /></th>
|
||||
<th pSortableColumn="bloodGroup">Blood Group <p-sortIcon field="bloodGroup" /></th>
|
||||
<th>Mobile</th>
|
||||
<th>Email</th>
|
||||
<th pSortableColumn="status">Status <p-sortIcon field="status" /></th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="body" let-patient>
|
||||
<tr>
|
||||
<td class="hidden">{{ patient.id }}</td>
|
||||
<td>{{ patient.name }}</td>
|
||||
<td>
|
||||
<span class="badge" [ngClass]="patient.gender === 1 ? 'male' : 'female'">
|
||||
{{ gender[patient.gender] }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ patient.admissionDate | date }}</td>
|
||||
<td>{{ patient.bloodGroup }}</td>
|
||||
<td>{{ patient.mobile }}</td>
|
||||
<td>{{ patient.email }}</td>
|
||||
<td>{{ status[patient.status] }}</td>
|
||||
<td class="d-flex">
|
||||
<button *ngIf="createpermission" pButton class="btn btn-success btn-sm"
|
||||
(click)="addnewrecord(patient.id)">
|
||||
<i class="pi pi-arrow-right"></i>
|
||||
</button>
|
||||
<button *ngIf="editpermission" class="btn btn-warning btn-sm ml-1" (click)="editPatient(patient)"><i
|
||||
class="pi pi-pencil"></i></button>
|
||||
<button *ngIf="deletepermission" class="btn btn-danger btn-sm ml-1"
|
||||
(click)="deletePatient(patient.id)"><i class="pi pi-trash"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="emptymessage">
|
||||
<tr>
|
||||
<td colspan="10">No Patients found.</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
<span>Total Records: {{totalRecords}}</span>
|
||||
</div> -->
|
||||
<div class="container-fluid py-3">
|
||||
<div class="card shadow-sm p-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2 class="mb-0">Patient List</h2>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button *ngIf="createpermission" class="btn btn-success btn-sm" (click)="openNewPatientDialog()">
|
||||
<i class="pi pi-plus-circle"></i>
|
||||
</button>
|
||||
<button class="btn btn-warning btn-sm" (click)="exportPatient()">
|
||||
<i class="pi pi-download"></i>
|
||||
</button>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="pi pi-search"></i></span>
|
||||
<input pInputText type="text" class="form-control"
|
||||
(input)="dt2.filterGlobal($event.target.value, 'contains')" [(ngModel)]="globalFilter"
|
||||
placeholder="Search keyword" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p-table #dt2 dataKey="id" [value]="patients" [paginator]="true" [rows]="10" [totalRecords]="totalRecords"
|
||||
[lazy]="true" (onLazyLoad)="loadPatient($event)" [rowsPerPageOptions]="[10, 20, 50]"
|
||||
styleClass="p-datatable-gridlines" [responsiveLayout]="'scroll'"
|
||||
[globalFilterFields]="['id', 'name', 'status']" [filters]="{ global: { value: '', matchMode: 'contains' } }"
|
||||
class="table table-hover table-responsive-md">
|
||||
|
||||
<ng-template pTemplate="header">
|
||||
<tr class="table-dark">
|
||||
<th class="hidden" pSortableColumn="id">Patient ID <p-sortIcon field="id" /></th>
|
||||
<th pSortableColumn="patientCardId">Card ID <p-sortIcon field="patientCardId" /></th>
|
||||
<th pSortableColumn="name">Full Name <p-sortIcon field="name" /></th>
|
||||
<th pSortableColumn="gender">Gender <p-sortIcon field="gender" /></th>
|
||||
<th pSortableColumn="bloodGroup">Blood Group <p-sortIcon field="bloodGroup" /></th>
|
||||
<th>Mobile</th>
|
||||
<th>Age</th>
|
||||
<th>Email</th>
|
||||
<th pSortableColumn="insuranceProvider">Insurance <p-sortIcon field="insuranceProvider" /></th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="body" let-patient>
|
||||
<tr>
|
||||
<td class="hidden">{{ patient.id }}</td>
|
||||
<td>{{patient.patientCardId}}</td>
|
||||
<td>{{ patient.name }}</td>
|
||||
<td>
|
||||
<span class="badge" [ngClass]="patient.gender === 1 ? 'bg-primary' : 'bg-pink'">
|
||||
{{ gender[patient.gender] }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ patient.bloodGroup }}</td>
|
||||
<td>{{ patient.mobile }}</td>
|
||||
<td>{{patient.age}}</td>
|
||||
<td>{{ patient.email }}</td>
|
||||
<td>{{patient.insuranceProvider}}</td>
|
||||
<td class="d-flex gap-1">
|
||||
<button *ngIf="createpermission" class="btn btn-success btn-sm"
|
||||
(click)="addnewrecord(patient.id)">
|
||||
<i class="pi pi-arrow-right"></i>
|
||||
</button>
|
||||
<button *ngIf="editpermission" class="btn btn-warning btn-sm" (click)="editPatient(patient)"><i
|
||||
class="pi pi-pencil"></i></button>
|
||||
<button *ngIf="deletepermission" class="btn btn-danger btn-sm"
|
||||
(click)="deletePatient(patient.id)"><i class="pi pi-trash"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="emptymessage">
|
||||
<tr>
|
||||
<td colspan="10" class="text-center">No Patients found.</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
<div class="text-end mt-2 fw-bold">Total Records: {{ totalRecords }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p-dialog *ngIf="patientDialog" header="{{patientDialogTitle}}" [(visible)]="patientDialog" [modal]="true"
|
||||
[closable]="true" [style]="{width: '80%', height: 'auto'}" class="modern-dialog">
|
||||
<form #patientrecord="ngForm" (ngSubmit)="savePatient(patientrecord)">
|
||||
<div class="p-fluid grid">
|
||||
<!-- Full Name & Profile Image -->
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="name"><i class="pi pi-user"></i> Full Name</label>
|
||||
<input id="name" name="name" type="text" pInputText [(ngModel)]="selectedPatient.name"
|
||||
placeholder="Enter full patient name" required #name="ngModel" />
|
||||
<small *ngIf="name.invalid && name.touched" class="p-error">Full Name is required</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="age">Age</label>
|
||||
<input id="age" name="age" type="number" pInputText [(ngModel)]="selectedPatient.age"
|
||||
placeholder="Enter age" required min="1" max="120" #age="ngModel" />
|
||||
<small *ngIf="age.invalid && age.touched" class="p-error">
|
||||
Age is required and must be between 1 and 90
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-fluid grid">
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="image"><i class="pi pi-image"></i> Profile Image</label>
|
||||
<input class="d-flex" type="file" id="image" name="image" accept=".jpg,.png"
|
||||
(change)="profileimageupload($event)" />
|
||||
</div>
|
||||
<small *ngIf="error !== ''" class="p-error">{{error}}</small>
|
||||
<div *ngIf="uploadProgress > 0">
|
||||
<p>Uploading... {{ uploadProgress }}%</p>
|
||||
<progress [value]="uploadProgress" max="100"></progress>
|
||||
</div>
|
||||
<small *ngIf="imgpath !== ''">{{imgpath}} <i class="pi pi-image"></i></small>
|
||||
<!-- <div class="col-6">
|
||||
<div class="field">
|
||||
<label for="status">Status</label>
|
||||
<p-dropdown name="status" id="status" [options]="statuslist" [(ngModel)]="selectedstatus"
|
||||
optionLabel="label" optionValue="value" placeholder="Select status"
|
||||
required></p-dropdown>
|
||||
<small *ngIf="selectedstatus === 0" class="p-error">Status is required</small>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="gender"><i class="pi pi-users"></i> Gender</label>
|
||||
<div>
|
||||
<label class="mx-1">
|
||||
<input id="male" type="radio" name="gender" [(ngModel)]="selectedgender" [value]="1"
|
||||
required />
|
||||
Male
|
||||
</label>
|
||||
<label>
|
||||
<input id="female" type="radio" name="gender" [(ngModel)]="selectedgender"
|
||||
[value]="2" />
|
||||
Female
|
||||
</label>
|
||||
</div>
|
||||
<small *ngIf="!selectedgender" class="p-error">Gender is required</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-fluid grid">
|
||||
<!-- Mobile & Address -->
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="mobile"><i class="pi pi-phone"></i> Mobile</label>
|
||||
<input id="mobile" name="mobile" type="text" pInputText [(ngModel)]="selectedPatient.mobile"
|
||||
placeholder="Enter mobile number" maxlength="10" required pattern="[0-9]{10}"
|
||||
#mobile="ngModel" />
|
||||
<small *ngIf="mobile.invalid && mobile.touched" class="p-error">
|
||||
Mobile is required and must be 10 digits
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="address"><i class="pi pi-map-marker"></i> Address</label>
|
||||
<input id="address" name="address" type="text" pInputText [(ngModel)]="selectedPatient.address"
|
||||
placeholder="Enter address" required #address="ngModel" />
|
||||
<small *ngIf="address.invalid && address.touched" class="p-error">Address is
|
||||
required</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="p-fluid grid"> -->
|
||||
<!-- <div class="col-6">
|
||||
<div class="field">
|
||||
<label for="admissionDate"><i class="pi pi-calendar"></i> Admission Date</label>
|
||||
<p-calendar appendTo="body" id="admissionDate" name="admissionDate" [(ngModel)]="selectadmissionDate" showIcon
|
||||
styleClass="small-calendar" required #admissionDate="ngModel"></p-calendar>
|
||||
<small *ngIf="admissionDate.invalid && admissionDate.touched" class="p-error">
|
||||
Admission Date is required
|
||||
</small>
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- </div> -->
|
||||
|
||||
<!-- <div class="p-fluid grid">
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="doctorAssigned"><i class="pi pi-user-md"></i> Doctor Assigned</label>
|
||||
<input id="doctorAssigned" name="doctorAssigned" type="text" pInputText
|
||||
[(ngModel)]="selectedPatient.doctorAssigned" placeholder="Enter assigned doctor" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="treatment"><i class="pi pi-heartbeat"></i> Treatment</label>
|
||||
<input id="treatment" name="treatment" type="text" pInputText
|
||||
[(ngModel)]="selectedPatient.treatment" placeholder="Enter treatment" />
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="p-fluid grid">
|
||||
<!-- Blood Group & Email -->
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="bloodGroup"><i class="pi pi-tint"></i> Blood Group</label>
|
||||
<input id="bloodGroup" name="bloodGroup" type="text" pInputText
|
||||
[(ngModel)]="selectedPatient.bloodGroup" placeholder="Enter blood group" required
|
||||
#bloodGroup="ngModel" />
|
||||
<small *ngIf="bloodGroup.invalid && bloodGroup.touched" class="p-error">Blood Group is
|
||||
required</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="email"><i class="pi pi-envelope"></i> Email</label>
|
||||
<input id="email" name="email" type="email" pInputText [(ngModel)]="selectedPatient.email"
|
||||
placeholder="Enter email address" required email #email="ngModel" />
|
||||
<small *ngIf="email.invalid && email.touched" class="p-error">Enter a valid email
|
||||
address</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Insurance Provider -->
|
||||
<div class="p-fluid grid">
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="insuranceProvider"><i class="pi pi-credit-card"></i> Insurance Provider</label>
|
||||
<input id="insuranceProvider" name="insuranceProvider" type="text" pInputText
|
||||
[(ngModel)]="selectedPatient.insuranceProvider" required
|
||||
placeholder="Enter insurance provider" #insurance="ngModel" />
|
||||
<small *ngIf="insurance.invalid && insurance.touched" class="p-error">
|
||||
Insurance is required.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="patientCardId"><i class="pi pi-credit-card"></i> Card ID</label>
|
||||
<input id="patientCardId" name="patientCardId" type="text" pInputText
|
||||
[(ngModel)]="selectedPatient.patientCardId" required
|
||||
placeholder="Enter patient card Id" #patientCardId="ngModel" />
|
||||
<small *ngIf="patientCardId.invalid && patientCardId.touched" class="p-error">
|
||||
Card Id is required.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<button type="submit" pButton class="btn btn-primary"
|
||||
[disabled]="(patientrecord.invalid || !selectedgender)">
|
||||
<i class="pi pi-check"></i> Save
|
||||
</button>
|
||||
<button pButton class="btn btn-secondary ml-1" (click)="closeDialog()">
|
||||
<i class="pi pi-times"></i> Close
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</p-dialog>
|
||||
</div>
|
@ -1,37 +0,0 @@
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.male {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.female {
|
||||
background-color: purple;
|
||||
}
|
||||
|
||||
.pdf-icon {
|
||||
color: red;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.bg-pink {
|
||||
background-color: #ff4081 !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.gap-1 {
|
||||
gap: 5px;
|
||||
}
|
||||
|
@ -1,259 +0,0 @@
|
||||
import { PermissionService } from '@abp/ng.core';
|
||||
import { Confirmation, ConfirmationService, ToasterService } from '@abp/ng.theme.shared';
|
||||
import { HttpClient, HttpEventType, HttpParams, HttpRequest } from '@angular/common/http';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { PagingSortResultDto } from '@proxy/dto';
|
||||
import { Gender } from '@proxy/global-enum';
|
||||
import { PatientService } from '@proxy/patients';
|
||||
import { PatientDto, CreateUpdatePatientDto } from '@proxy/patients/dto';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
@Component({
|
||||
selector: 'app-all-patients',
|
||||
templateUrl: './all-patients.component.html',
|
||||
styleUrl: './all-patients.component.scss',
|
||||
providers: [PatientService, ConfirmationService, ToasterService, PermissionService],
|
||||
})
|
||||
export class AllPatientsComponent implements OnInit {
|
||||
globalFilter: string = '';
|
||||
patients: PatientDto[];
|
||||
totalRecords: number = 0;
|
||||
loading: boolean = false;
|
||||
patientDialog: boolean = false;
|
||||
selectedPatient: CreateUpdatePatientDto;
|
||||
isEditMode: boolean = false;
|
||||
patientDialogTitle: string = '';
|
||||
params: PagingSortResultDto;
|
||||
gender: any;
|
||||
selectedgender: any = 0;
|
||||
selectadmissionDate: Date = new Date();
|
||||
selectdischargeDate: Date = new Date();
|
||||
createpermission: boolean;
|
||||
editpermission: boolean;
|
||||
deletepermission: boolean;
|
||||
imgpath: string = '';
|
||||
guid: string = '00000000-0000-0000-0000-000000000000';
|
||||
uploadProgress = 0;
|
||||
|
||||
constructor(
|
||||
private patientService: PatientService,
|
||||
private http: HttpClient,
|
||||
private confirmation: ConfirmationService,
|
||||
private toaster: ToasterService,
|
||||
private permissionChecker: PermissionService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.createpermission = this.permissionChecker.getGrantedPolicy(
|
||||
'HospitalManagementSystem.Patient.Create'
|
||||
);
|
||||
this.editpermission = this.permissionChecker.getGrantedPolicy(
|
||||
'HospitalManagementSystem.Patient.Edit'
|
||||
);
|
||||
this.deletepermission = this.permissionChecker.getGrantedPolicy(
|
||||
'HospitalManagementSystem.Patient.Delete'
|
||||
);
|
||||
this.gender = Gender;
|
||||
|
||||
this.resetselectpatient();
|
||||
this.loadPatient({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
}
|
||||
resetselectpatient() {
|
||||
this.selectedPatient = {
|
||||
name: '',
|
||||
gender: Gender.Male,
|
||||
mobile: '',
|
||||
email: '',
|
||||
age: 0,
|
||||
address: '',
|
||||
bloodGroup: '',
|
||||
};
|
||||
this.imgpath = '';
|
||||
this.selectedgender = 0;
|
||||
this.uploadProgress = 0;
|
||||
}
|
||||
|
||||
loadPatient(event: any) {
|
||||
this.loading = true;
|
||||
let order = event.sortOrder == 1 ? ' asc' : ' desc';
|
||||
event.sortField = event.sortField == undefined ? 'id' : event.sortField;
|
||||
this.params = {
|
||||
skipCount: event.first,
|
||||
maxResultCount: event.rows,
|
||||
sorting: event.sortField + order,
|
||||
search: event.globalFilter == null ? '' : event.globalFilter,
|
||||
};
|
||||
|
||||
this.patientService.getPatientList(this.params).subscribe(data => {
|
||||
this.patients = data.items;
|
||||
this.totalRecords = data.totalCount;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
exportPatient() {
|
||||
this.patientService.getExportPatientData().subscribe(result => {
|
||||
const binary = atob(result.fileContent);
|
||||
const len = binary.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
|
||||
const blob = new Blob([bytes], { type: 'application/xlsx' });
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = result.fileName;
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
}
|
||||
|
||||
openNewPatientDialog() {
|
||||
this.patientDialogTitle = 'New Patient';
|
||||
this.resetselectpatient();
|
||||
this.selectedgender = 0;
|
||||
this.patientDialog = true;
|
||||
this.isEditMode = false;
|
||||
}
|
||||
|
||||
addnewrecord(id: any) {
|
||||
this.router.navigate(['/patients/patient-record', id]);
|
||||
}
|
||||
|
||||
profileimageupload(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.files.length > 0) {
|
||||
const tag = 'Image';
|
||||
const formdata = new FormData();
|
||||
formdata.append('file', input.files[0]);
|
||||
this.UploadFileData(tag, formdata);
|
||||
} else {
|
||||
this.uploadProgress = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UploadFileData(tag: string, formdata: FormData) {
|
||||
const req = new HttpRequest(
|
||||
'POST',
|
||||
environment.apis.default.url + '/api/app/shared/upload-file',
|
||||
formdata,
|
||||
{
|
||||
reportProgress: true,
|
||||
responseType: 'text',
|
||||
params: new HttpParams().set('tagName', tag),
|
||||
}
|
||||
);
|
||||
this.http.request(req).subscribe(
|
||||
event => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
this.uploadProgress = Math.round((event.loaded / event.total!) * 100);
|
||||
} else if (event.type === HttpEventType.Response) {
|
||||
this.uploadProgress = 100;
|
||||
this.selectedPatient.imageID = event.body.toString();
|
||||
}
|
||||
},
|
||||
error => {
|
||||
this.uploadProgress = 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
editPatient(Patient: any) {
|
||||
this.resetselectpatient();
|
||||
this.patientDialogTitle = 'Edit Patient';
|
||||
this.patientService.getPatientById(Patient.id).subscribe(result => {
|
||||
this.selectedPatient = result;
|
||||
this.imgpath = result.imagepath != null ? result.imagepath.split('\\')[3] : '';
|
||||
this.selectedgender = this.selectedPatient.gender;
|
||||
});
|
||||
this.patientDialog = true;
|
||||
this.isEditMode = true;
|
||||
}
|
||||
|
||||
deletePatient(id: any) {
|
||||
this.confirmation
|
||||
.warn('Do you really want to delete this patient?', {
|
||||
key: '::AreYouSure',
|
||||
defaultValue: 'Are you sure?',
|
||||
})
|
||||
.subscribe((status: Confirmation.Status) => {
|
||||
// your code here
|
||||
if (status == 'confirm') {
|
||||
this.patientService.deletePatient(id).subscribe(() => {
|
||||
this.toaster.success('Patient deleted successfully', 'Success');
|
||||
this.loadPatient(this.params);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
savePatient(form: NgForm) {
|
||||
if (form.invalid) {
|
||||
Object.values(form.controls).forEach(control => control.markAsTouched());
|
||||
return;
|
||||
}
|
||||
this.selectedPatient.gender = this.selectedgender;
|
||||
this.selectedPatient.imageID = this.selectedPatient.imageID
|
||||
? this.selectedPatient.imageID.replace('"', '').replace('"', '')
|
||||
: this.guid;
|
||||
|
||||
if (this.isEditMode) {
|
||||
this.patientService.updatePatient(this.selectedPatient.id, this.selectedPatient).subscribe(
|
||||
() => {
|
||||
this.toaster.success('Patient updated successfully', 'Success');
|
||||
this.patientDialog = false;
|
||||
this.loadPatient({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
},
|
||||
error => {
|
||||
this.closeDialog();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.patientService.createPatient(this.selectedPatient).subscribe(
|
||||
() => {
|
||||
this.toaster.success('Patient created successfully', 'Success');
|
||||
this.patientDialog = false;
|
||||
this.loadPatient({
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
});
|
||||
},
|
||||
error => {
|
||||
this.closeDialog();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.patientDialog = false;
|
||||
}
|
||||
}
|
@ -1,252 +0,0 @@
|
||||
<div class="patient-container">
|
||||
<div class="patient-card">
|
||||
<div class="patient-image">
|
||||
<img [src]="patientImageUrl" alt="Patient Image">
|
||||
</div>
|
||||
|
||||
<div class="patient-info">
|
||||
<h2 class="patient-name">{{ patientdto?.name }}</h2>
|
||||
<div class="patient-details">
|
||||
<div class="info-item">
|
||||
<strong>Age:</strong> {{ patientdto?.age }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Gender:</strong> {{ gender[patientdto?.gender] }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Mobile:</strong> {{ patientdto?.mobile }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Email:</strong> {{ patientdto?.email }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Blood Group:</strong> {{ patientdto?.bloodGroup }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>Card ID:</strong> {{ patientdto?.patientCardId }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-primary mb-2" (click)="backtopatient()">
|
||||
<i class="pi pi-arrow-left"></i> Back
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<p-table #dt2 [value]="patientrecords" [paginator]="true" [rows]="10" [totalRecords]="totalRecords" [lazy]="true"
|
||||
(onLazyLoad)="loadPatient($event,patientId)" [rowsPerPageOptions]="[10, 20, 50]" [responsiveLayout]="'scroll'"
|
||||
[globalFilterFields]="['id', 'name', 'diagnosis']" class="card shadow-sm">
|
||||
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="flex justify-content-between align-items-center">
|
||||
<h3>{{'::PatientHeader' | abpLocalization}}</h3>
|
||||
<div class="flex gap-2">
|
||||
<button *ngIf="createpermission" pButton class="btn btn-success btn-sm" (click)="openNewPatientDialog()">
|
||||
<i class="pi pi-plus-circle"></i>
|
||||
</button>
|
||||
<button pButton class="btn btn-warning btn-sm" (click)="exportPatient()">
|
||||
<i class="pi pi-download"></i>
|
||||
</button>
|
||||
<span class="p-input-icon-left">
|
||||
<i class="pi pi-search"></i>
|
||||
<input pInputText type="text" [(ngModel)]="globalFilter"
|
||||
(input)="dt2.filterGlobal($event.target.value, 'contains')" placeholder="Search...">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th class="hidden" pSortableColumn="id">Patient ID <p-sortIcon field="id" /></th>
|
||||
<th>Full Name</th>
|
||||
<th>Gender</th>
|
||||
<th>Doctor</th>
|
||||
<th pSortableColumn="dateOfAdmission">Date of Admission <p-sortIcon field="dateOfAdmission" /></th>
|
||||
<th pSortableColumn="dischargeDate">Discharge Date <p-sortIcon field="dischargeDate" /></th>
|
||||
<th pSortableColumn="nextFollowUp">Next Follow-Up <p-sortIcon field="nextFollowUp" /></th>
|
||||
<th pSortableColumn="diagnosis">Diagnosis <p-sortIcon field="diagnosis" /></th>
|
||||
<th>Lab Reports</th>
|
||||
<th>Medications</th>
|
||||
|
||||
<th pSortableColumn="status">Status <p-sortIcon field="status" /></th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="body" let-patientrecord>
|
||||
<tr>
|
||||
<td class="hidden">{{ patientrecord.id }}</td>
|
||||
<td>{{ patientrecord.patients.name }}</td>
|
||||
<td>
|
||||
<span class="badge" [ngClass]="patientrecord.patients.gender === 1 ? 'male' : 'female'">
|
||||
{{ gender[patientrecord.patients.gender] }}
|
||||
</span>
|
||||
</td>
|
||||
<td><span *ngIf="patientrecord.doctorAssigned !== null">{{'Dr. ' + patientrecord.doctorAssigned?.firstName + ' '
|
||||
+
|
||||
patientrecord.doctorAssigned?.lastName}}</span></td>
|
||||
<td>{{ patientrecord.dateOfAdmission | date }}</td>
|
||||
<td>{{ patientrecord.dischargeDate | date }}</td>
|
||||
<td>{{ patientrecord.nextFollowUp | date }}</td>
|
||||
<td>{{ patientrecord.diagnosis }}</td>
|
||||
<td><i class="pi pi-file-pdf text-danger"></i></td>
|
||||
<td><i class="pi pi-file-pdf text-primary"></i></td>
|
||||
<td>
|
||||
<span class="badge"
|
||||
[ngClass]="patientrecord.status === 1 ? 'bg-success' : (patientrecord.status === 2 ? 'bg-primary':'bg-danger')">
|
||||
{{ status[patientrecord.status] }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="flex gap-2">
|
||||
<button *ngIf="editpermission" class="btn btn-warning btn-sm" (click)="editPatient(patientrecord)">
|
||||
<i class="pi pi-pencil"></i>
|
||||
</button>
|
||||
<button *ngIf="deletepermission" class="btn btn-danger btn-sm" (click)="deletePatient(patientrecord.id)">
|
||||
<i class="pi pi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="emptymessage">
|
||||
<tr>
|
||||
<td colspan="9">No Patients found.</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
<div class="mt-2">Total Records: {{totalRecords}}</div>
|
||||
</div>
|
||||
<div>
|
||||
<p-dialog *ngIf="patientDialog" header="{{patientDialogTitle}}" [(visible)]="patientDialog" [modal]="true"
|
||||
[closable]="true" [style]="{width: '70%', height: 'auto'}">
|
||||
<form #patientrecord="ngForm" (ngSubmit)="savePatient(patientrecord)" novalidate>
|
||||
<div class="p-fluid grid">
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="admissionDate"><i class="pi pi-calendar"></i> Admission Date <span
|
||||
class="text-danger">*</span></label>
|
||||
<p-calendar appendTo="body" id="admissionDate" name="admissionDate" [(ngModel)]="selectdateOfAdmission"
|
||||
showIcon required #admissionDate="ngModel"></p-calendar>
|
||||
<small *ngIf="admissionDate.invalid && admissionDate.touched" class="p-error">
|
||||
Admission Date is required.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="nextFollowUp"><i class="pi pi-calendar-plus"></i> Next Follow-Up Date</label>
|
||||
<p-calendar appendTo="body" id="nextFollowUp" name="nextFollowUp" [(ngModel)]="selectnextFollowUp"
|
||||
showIcon></p-calendar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-fluid grid">
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="dischargeDate"><i class="pi pi-calendar"></i> Discharge Date</label>
|
||||
<p-calendar appendTo="body" id="dischargeDate" name="dischargeDate" [(ngModel)]="selectdischargeDate"
|
||||
showIcon></p-calendar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="status">Status <span class="text-danger">*</span></label>
|
||||
<p-dropdown name="status" id="status" [options]="statuslist" [(ngModel)]="selectedstatus"
|
||||
optionLabel="label" optionValue="value" placeholder="Select status" required></p-dropdown>
|
||||
<small *ngIf="selectedstatus === 0" class="p-error">Status is required</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-fluid grid">
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="doctorAssignedID">Consulting Doctor <span class="text-danger">*</span></label>
|
||||
<p-dropdown id="doctorAssignedID" name="doctorAssignedID"
|
||||
[(ngModel)]="selectedPatientRecord.doctorAssignedID" [options]="doctorOptions" placeholder="Select Doctor"
|
||||
optionLabel="label" optionValue="value" required></p-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-fluid">
|
||||
<div class="field">
|
||||
<label for="diagnosis"><i class="pi pi-heartbeat"></i> Diagnosis</label>
|
||||
<textarea style="width: 100%; background-color: #fff; color: black;" id="diagnosis" name="diagnosis"
|
||||
pInputTextarea rows="5" cols="30" [(ngModel)]="selectedPatientRecord.diagnosis"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-fluid">
|
||||
<div class="field">
|
||||
<label for="treatmentPlan"><i class="pi pi-heartbeat"></i> Treatment Plan</label>
|
||||
<textarea style="width: 100%; background-color: #fff; color: black;" id="treatmentPlan" name="treatmentPlan"
|
||||
pInputTextarea rows="5" cols="30" [(ngModel)]="selectedPatientRecord.treatmentPlan"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-fluid">
|
||||
<div class="field">
|
||||
<label for="doctorNotes"><i class="pi pi-heartbeat"></i> Doctor Notes</label>
|
||||
<textarea style="width: 100%; background-color: #fff; color: black;" id="doctorNotes" name="doctorNotes"
|
||||
pInputTextarea rows="5" cols="30" [(ngModel)]="selectedPatientRecord.doctorNotes"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<h3 class="mb-4">Documents</h3>
|
||||
<!-- File Uploads -->
|
||||
<div class="p-fluid grid">
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="labReport"><i class="pi pi-file-pdf"></i> Lab Report</label>
|
||||
<input class="d-flex" type="file" id="labReport" name="labReport" accept=".pdf,.doc,.docx"
|
||||
(change)="handleUpload($event,'Lab-Report')" />
|
||||
<div *ngIf="uploadProgress1 > 0">
|
||||
<p>Uploading... {{ uploadProgress1 }}%</p>
|
||||
<progress [value]="uploadProgress1" max="100"></progress>
|
||||
</div>
|
||||
<small *ngIf="labReportUrlpath!==''">{{labReportUrlpath}} <i class="pi pi-file"></i></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="medications"><i class="pi pi-file-pdf"></i> Medications</label>
|
||||
<input class="d-flex" type="file" id="medications" name="medications" accept=".pdf,.doc,.docx"
|
||||
(change)="handleUpload($event,'Medication')" />
|
||||
<div *ngIf="uploadProgress2 > 0">
|
||||
<p>Uploading... {{ uploadProgress2 }}%</p>
|
||||
<progress [value]="uploadProgress2" max="100"></progress>
|
||||
</div>
|
||||
<small *ngIf="medicationUrlpath!==''">{{medicationUrlpath}} <i class="pi pi-file"></i></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="field">
|
||||
<label for="medicationHistory"><i class="pi pi-file-pdf"></i>Medication History</label>
|
||||
<input class="d-flex" type="file" id="medicationHistory" name="medicationHistory" accept=".pdf,.doc,.docx"
|
||||
(change)="handleUpload($event,'Medication-History')" />
|
||||
<div *ngIf="uploadProgress3 > 0">
|
||||
<p>Uploading... {{ uploadProgress3 }}%</p>
|
||||
<progress [value]="uploadProgress3" max="100"></progress>
|
||||
</div>
|
||||
<small *ngIf="medicationHistoryUrlpath !==''">{{medicationHistoryUrlpath}} <i
|
||||
class="pi pi-file-pdf pdf-icon"></i></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Buttons -->
|
||||
<div class="p-dialog-footer flex justify-content-end">
|
||||
<button type="submit" pButton class="btn btn-primary"
|
||||
[disabled]="patientrecord.invalid || selectedstatus === 0">
|
||||
<i class="pi pi-check"></i> Save
|
||||
</button>
|
||||
<button pButton class="btn btn-secondary ml-2" (click)="closeDialog()">
|
||||
<i class="pi pi-times"></i> Close
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</p-dialog>
|
||||
</div>
|
@ -1,82 +0,0 @@
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.male {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.female {
|
||||
background-color: #ff4081 !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pdf-icon {
|
||||
color: red;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
|
||||
.patient-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.patient-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
||||
border-left: 5px solid #007bff;
|
||||
}
|
||||
|
||||
.patient-image img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 3px solid #007bff;
|
||||
}
|
||||
|
||||
.patient-info {
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.patient-name {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.patient-details {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(150px, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
font-size: 14px;
|
||||
background: #f8f9fa;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
|
@ -1,357 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { PagingSortResultDto } from '@proxy/dto';
|
||||
import { Gender, Status } from '@proxy/global-enum';
|
||||
import { PatientService } from '@proxy/patients';
|
||||
import { CreateUpdatePatientRecordDto, PatientDto, PatientRecordDto } from '@proxy/patients/dto';
|
||||
import { PermissionService } from '@abp/ng.core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { Confirmation, ConfirmationService, ToasterService } from '@abp/ng.theme.shared';
|
||||
import { HttpClient, HttpEventType, HttpParams, HttpRequest } from '@angular/common/http';
|
||||
import { DoctorService } from '@proxy/doctors/doctor.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-patient-record',
|
||||
templateUrl: './patient-record.component.html',
|
||||
styleUrl: './patient-record.component.scss',
|
||||
providers: [
|
||||
PatientService,
|
||||
ConfirmationService,
|
||||
DoctorService,
|
||||
ToasterService,
|
||||
PermissionService,
|
||||
],
|
||||
})
|
||||
export class PatientRecordComponent implements OnInit {
|
||||
globalFilter: string = '';
|
||||
patientrecords: PatientRecordDto[];
|
||||
totalRecords: number = 0;
|
||||
loading: boolean = false;
|
||||
patientDialog: boolean = false;
|
||||
selectedPatientRecord: CreateUpdatePatientRecordDto;
|
||||
patientdto: PatientDto;
|
||||
isEditMode: boolean = false;
|
||||
patientDialogTitle: string = '';
|
||||
params: PagingSortResultDto;
|
||||
status: any;
|
||||
gender: any;
|
||||
statuslist: any;
|
||||
selectdateOfAdmission: Date = new Date();
|
||||
selectnextFollowUp: Date = new Date();
|
||||
selectdischargeDate: Date = new Date();
|
||||
selectedstatus = 0;
|
||||
createpermission: boolean;
|
||||
editpermission: boolean;
|
||||
deletepermission: boolean;
|
||||
patientId: any;
|
||||
patientImageUrl: string;
|
||||
labReportUrlpath: string;
|
||||
medicationHistoryUrlpath: string;
|
||||
medicationUrlpath: string;
|
||||
guid: string = '00000000-0000-0000-0000-000000000000';
|
||||
uploadProgress1 = 0;
|
||||
uploadProgress2 = 0;
|
||||
uploadProgress3 = 0;
|
||||
doctorOptions = [];
|
||||
doctorname = '';
|
||||
|
||||
constructor(
|
||||
private patientService: PatientService,
|
||||
private confirmation: ConfirmationService,
|
||||
private permissionChecker: PermissionService,
|
||||
private DoctorService: DoctorService,
|
||||
private toaster: ToasterService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private http: HttpClient
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.patientId = this.route.snapshot.paramMap.get('id');
|
||||
|
||||
this.createpermission = this.permissionChecker.getGrantedPolicy(
|
||||
'HospitalManagementSystem.Patient.Create'
|
||||
);
|
||||
this.editpermission = this.permissionChecker.getGrantedPolicy(
|
||||
'HospitalManagementSystem.Patient.Edit'
|
||||
);
|
||||
this.deletepermission = this.permissionChecker.getGrantedPolicy(
|
||||
'HospitalManagementSystem.Patient.Delete'
|
||||
);
|
||||
this.status = Status;
|
||||
this.gender = Gender;
|
||||
this.patientService.getStatusDropdown().subscribe(result => {
|
||||
this.statuslist = result;
|
||||
});
|
||||
|
||||
this.resetselectpatient();
|
||||
this.getdoctorlist();
|
||||
}
|
||||
|
||||
resetselectpatient() {
|
||||
this.selectedPatientRecord = {
|
||||
patientId: this.patientId,
|
||||
dateOfAdmission: '',
|
||||
// labReportUrl: '',
|
||||
// medicationUrl: '',
|
||||
// medicationHistoryUrl: '',
|
||||
nextFollowUp: '',
|
||||
diagnosis: '',
|
||||
treatmentPlan: '',
|
||||
doctorNotes: '',
|
||||
status: Status.InTreatment,
|
||||
};
|
||||
this.labReportUrlpath = '';
|
||||
this.medicationUrlpath = '';
|
||||
this.medicationHistoryUrlpath = '';
|
||||
this.selectdateOfAdmission = new Date();
|
||||
this.selectnextFollowUp = new Date();
|
||||
this.selectdischargeDate = new Date();
|
||||
this.selectedstatus = 0;
|
||||
this.uploadProgress1 = 0;
|
||||
this.uploadProgress2 = 0;
|
||||
this.uploadProgress3 = 0;
|
||||
}
|
||||
|
||||
loadPatient(event: any, id: any) {
|
||||
this.selectedPatientRecord.patientId = this.patientId;
|
||||
this.loading = true;
|
||||
let order = event.sortOrder == 1 ? ' asc' : ' desc';
|
||||
event.sortField = event.sortField == undefined ? 'id' : event.sortField;
|
||||
this.params = {
|
||||
skipCount: event.first,
|
||||
maxResultCount: event.rows,
|
||||
sorting: event.sortField + order,
|
||||
search: event.globalFilter == null ? '' : event.globalFilter,
|
||||
};
|
||||
this.patientService.getPatientById(this.patientId).subscribe(result => {
|
||||
this.patientdto = result;
|
||||
this.patientImageUrl =
|
||||
environment.apis.default.url +
|
||||
(result.imagepath !== null ? result.imagepath : '/images/default-profile.png');
|
||||
});
|
||||
this.patientService.getPatientRecordList(this.params, id).subscribe(data => {
|
||||
this.patientrecords = data.items;
|
||||
this.totalRecords = data.totalCount;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
getdoctorlist() {
|
||||
this.DoctorService.get().subscribe(result => {
|
||||
const doctors = result;
|
||||
this.doctorOptions = doctors.map(doctor => ({
|
||||
label: `Dr. ${doctor.firstName} ${doctor.lastName}`, // Combine first and last name
|
||||
value: doctor.id, // Use the ID as the value
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
backtopatient() {
|
||||
this.router.navigate(['/patients/all-patients']);
|
||||
}
|
||||
|
||||
exportPatient() {
|
||||
this.patientService.getExportPatientRecord(this.patientId).subscribe(result => {
|
||||
const binary = atob(result.fileContent);
|
||||
const len = binary.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
|
||||
const blob = new Blob([bytes], { type: 'application/xlsx' });
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = result.fileName;
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
}
|
||||
|
||||
openNewPatientDialog() {
|
||||
this.patientDialogTitle = 'New Patient Record';
|
||||
this.resetselectpatient();
|
||||
this.patientDialog = true;
|
||||
this.isEditMode = false;
|
||||
}
|
||||
|
||||
editPatient(Patient: any) {
|
||||
this.resetselectpatient();
|
||||
this.patientDialogTitle = 'Edit Patient';
|
||||
this.patientService.getPatientRecordById(Patient.id).subscribe(result => {
|
||||
this.selectedPatientRecord = result;
|
||||
this.selectedPatientRecord.patientId = this.patientId;
|
||||
this.selectdateOfAdmission = new Date(this.selectedPatientRecord.dateOfAdmission);
|
||||
this.selectnextFollowUp = new Date(this.selectedPatientRecord.nextFollowUp);
|
||||
this.selectdischargeDate = new Date(this.selectedPatientRecord.dischargeDate);
|
||||
this.selectedstatus = this.selectedPatientRecord.status;
|
||||
this.selectedPatientRecord.doctorAssignedID = result.doctorAssigned.id;
|
||||
this.labReportUrlpath = result.labReportUrl != null ? result.labReportUrl.split('\\')[3] : '';
|
||||
this.medicationUrlpath =
|
||||
result.medicationUrl != null ? result.medicationUrl.split('\\')[3] : '';
|
||||
this.medicationHistoryUrlpath =
|
||||
result.medicationHistoryUrl != null ? result.medicationHistoryUrl.split('\\')[3] : '';
|
||||
});
|
||||
this.patientDialog = true;
|
||||
this.isEditMode = true;
|
||||
}
|
||||
|
||||
deletePatient(id: any) {
|
||||
this.confirmation
|
||||
.warn('Do you really want to delete this record ?', {
|
||||
key: '::AreYouSure',
|
||||
defaultValue: 'Are you sure?',
|
||||
})
|
||||
.subscribe((status: Confirmation.Status) => {
|
||||
// your code here
|
||||
if (status == 'confirm') {
|
||||
this.patientService.deletePatientRecord(id).subscribe(() => {
|
||||
this.loadPatient(this.params, this.patientId);
|
||||
this.toaster.success('Patient deleted successfully', 'Success');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleUpload(event: Event, tag: string): void {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.files.length > 0) {
|
||||
const formdata = new FormData();
|
||||
formdata.append('file', input.files[0]);
|
||||
this.UploadFileData(tag, formdata);
|
||||
} else {
|
||||
this.uploadProgress1 = 0;
|
||||
this.uploadProgress2 = 0;
|
||||
this.uploadProgress3 = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UploadFileData(tag: string, formdata: FormData) {
|
||||
const req = new HttpRequest(
|
||||
'POST',
|
||||
environment.apis.default.url + '/api/app/shared/upload-file',
|
||||
formdata,
|
||||
{
|
||||
reportProgress: true,
|
||||
responseType: 'text',
|
||||
params: new HttpParams().set('tagName', tag),
|
||||
}
|
||||
);
|
||||
this.http.request(req).subscribe(
|
||||
event => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
switch (tag) {
|
||||
case 'Lab-Report':
|
||||
this.uploadProgress1 = Math.round((event.loaded / event.total!) * 100);
|
||||
break;
|
||||
case 'Medication':
|
||||
this.uploadProgress2 = Math.round((event.loaded / event.total!) * 100);
|
||||
break;
|
||||
case 'Medication-History':
|
||||
this.uploadProgress3 = Math.round((event.loaded / event.total!) * 100);
|
||||
break;
|
||||
}
|
||||
} else if (event.type === HttpEventType.Response) {
|
||||
switch (tag) {
|
||||
case 'Lab-Report':
|
||||
this.uploadProgress1 = 100;
|
||||
this.selectedPatientRecord.labReportUrlID = event.body.toString();
|
||||
break;
|
||||
case 'Medication':
|
||||
this.uploadProgress2 = 100;
|
||||
this.selectedPatientRecord.medicationUrlID = event.body.toString();
|
||||
break;
|
||||
case 'Medication-History':
|
||||
this.uploadProgress3 = 100;
|
||||
this.selectedPatientRecord.medicationHistoryUrlID = event.body.toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
error => {
|
||||
this.uploadProgress1 = 100;
|
||||
this.uploadProgress2 = 100;
|
||||
this.uploadProgress3 = 100;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
savePatient(form: NgForm) {
|
||||
this.selectedPatientRecord.patientId = this.patientId;
|
||||
this.selectedPatientRecord.status = this.selectedPatientRecord.status;
|
||||
this.selectedPatientRecord.dateOfAdmission = this.selectdateOfAdmission.toDateString();
|
||||
this.selectedPatientRecord.nextFollowUp = this.selectnextFollowUp.toDateString();
|
||||
this.selectedPatientRecord.dischargeDate = this.selectdischargeDate.toDateString();
|
||||
this.selectedPatientRecord.status = this.selectedstatus;
|
||||
this.selectedPatientRecord.labReportUrlID = this.selectedPatientRecord.labReportUrlID
|
||||
? this.selectedPatientRecord.labReportUrlID.replace('"', '').replace('"', '')
|
||||
: this.guid;
|
||||
this.selectedPatientRecord.medicationUrlID = this.selectedPatientRecord.medicationUrlID
|
||||
? this.selectedPatientRecord.medicationUrlID.replace('"', '').replace('"', '')
|
||||
: this.guid;
|
||||
this.selectedPatientRecord.medicationHistoryUrlID = this.selectedPatientRecord
|
||||
.medicationHistoryUrlID
|
||||
? this.selectedPatientRecord.medicationHistoryUrlID.replace('"', '').replace('"', '')
|
||||
: this.guid;
|
||||
|
||||
if (this.isEditMode) {
|
||||
this.patientService
|
||||
.updatePatientRecord(this.selectedPatientRecord.id, this.selectedPatientRecord)
|
||||
.subscribe(
|
||||
result => {
|
||||
this.toaster.success('Patient updated successfully', 'Success');
|
||||
this.patientDialog = false;
|
||||
|
||||
this.loadPatient(
|
||||
{
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
},
|
||||
this.patientId
|
||||
);
|
||||
},
|
||||
error => {
|
||||
this.closeDialog();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.patientService.createPatientRecord(this.selectedPatientRecord).subscribe(
|
||||
() => {
|
||||
this.toaster.success('Patient created successfully', 'Success');
|
||||
this.patientDialog = false;
|
||||
this.loadPatient(
|
||||
{
|
||||
first: 0,
|
||||
rows: 10,
|
||||
sortField: 'id',
|
||||
sortOrder: 1,
|
||||
globalFilter: null,
|
||||
},
|
||||
this.patientId
|
||||
);
|
||||
},
|
||||
error => {
|
||||
this.closeDialog();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.patientDialog = false;
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { AllPatientsComponent } from './all-patients/all-patients.component';
|
||||
import { PatientRecordComponent } from './patient-record/patient-record.component';
|
||||
import { authGuard, permissionGuard } from '@abp/ng.core';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
children: [
|
||||
{
|
||||
path: 'patient-record/:id',
|
||||
component: PatientRecordComponent,
|
||||
// canActivate: [authGuard, permissionGuard],
|
||||
},
|
||||
{ path: 'all-patients', component: AllPatientsComponent },
|
||||
// { path: 'create-new/:id', component: FaqCreateComponent },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class PatientsRoutingModule {}
|
@ -1,37 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { PatientsRoutingModule } from './patients-routing.module';
|
||||
import { AllPatientsComponent } from './all-patients/all-patients.component';
|
||||
import { PatientRecordComponent } from './patient-record/patient-record.component';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { IconFieldModule } from 'primeng/iconfield';
|
||||
import { InputIconModule } from 'primeng/inputicon';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { PaginatorModule } from 'primeng/paginator';
|
||||
import { DialogModule } from 'primeng/dialog';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { FileUploadModule } from 'primeng/fileupload';
|
||||
import { CalendarModule } from 'primeng/calendar';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AllPatientsComponent, PatientRecordComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
PatientsRoutingModule,
|
||||
TableModule,
|
||||
InputIconModule,
|
||||
IconFieldModule,
|
||||
FormsModule,
|
||||
InputTextModule,
|
||||
PaginatorModule,
|
||||
DialogModule,
|
||||
DropdownModule,
|
||||
FileUploadModule,
|
||||
CalendarModule,
|
||||
SharedModule
|
||||
],
|
||||
})
|
||||
export class PatientsModule {}
|
@ -1,17 +0,0 @@
|
||||
# Proxy Generation Output
|
||||
|
||||
This directory includes the output of the latest proxy generation.
|
||||
The files and folders in it will be overwritten when proxy generation is run again.
|
||||
Therefore, please do not place your own content in this folder.
|
||||
|
||||
In addition, `generate-proxy.json` works like a lock file.
|
||||
It includes information used by the proxy generator, so please do not delete or modify it.
|
||||
|
||||
Finally, the name of the files and folders should not be changed for two reasons:
|
||||
- Proxy generator will keep creating them at those paths and you will have multiple copies of the same content.
|
||||
- ABP Suite generates files which include imports from this folder.
|
||||
|
||||
> **Important Notice:** If you are building a module and are planning to publish to npm,
|
||||
> some of the generated proxies are likely to be exported from public-api.ts file. In such a case,
|
||||
> please make sure you export files directly and not from barrel exports. In other words,
|
||||
> do not include index.ts exports in your public-api.ts exports.
|
@ -1,2 +0,0 @@
|
||||
import * as Services from './services';
|
||||
export { Services };
|
@ -1 +0,0 @@
|
||||
export * from './models';
|
@ -1,8 +0,0 @@
|
||||
|
||||
export interface ListResultDto<T> {
|
||||
items: T[];
|
||||
}
|
||||
|
||||
export interface PagedResultDto<T> extends ListResultDto<T> {
|
||||
totalCount: number;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
import * as Dto from './dto';
|
||||
export { Dto };
|
@ -1,2 +0,0 @@
|
||||
import * as Application from './application';
|
||||
export { Application };
|
@ -1 +0,0 @@
|
||||
export * from './models';
|
@ -1,46 +0,0 @@
|
||||
import type { Gender } from '../../global-enum/gender.enum';
|
||||
import type { appointmentStatus } from '../../global-enum/appointment-status.enum';
|
||||
import type { visitType } from '../../global-enum/visit-type.enum';
|
||||
import type { paymentStatus } from '../../global-enum/payment-status.enum';
|
||||
import type { DoctorDto } from '../../doctors/dto/models';
|
||||
|
||||
export interface AppointmentDto {
|
||||
id?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
gender: Gender;
|
||||
mobile?: string;
|
||||
address?: string;
|
||||
email?: string;
|
||||
dob?: string;
|
||||
doctorId?: string;
|
||||
dateOfAppointment?: string;
|
||||
timeOfAppointment?: string;
|
||||
injuryORContion?: string;
|
||||
note?: string;
|
||||
insuranceProvider?: string;
|
||||
appointmentStatus: appointmentStatus;
|
||||
visitType: visitType;
|
||||
paymentStatus: paymentStatus;
|
||||
doctor: DoctorDto;
|
||||
}
|
||||
|
||||
export interface CreateOrUpdateAppointmentDto {
|
||||
id?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
gender: Gender;
|
||||
mobile?: string;
|
||||
address?: string;
|
||||
email?: string;
|
||||
dob?: string;
|
||||
doctorId?: string;
|
||||
dateOfAppointment?: string;
|
||||
timeOfAppointment?: string;
|
||||
injuryORContion?: string;
|
||||
note?: string;
|
||||
insuranceProvider?: string;
|
||||
appointmentStatus: appointmentStatus;
|
||||
visitType: visitType;
|
||||
paymentStatus: paymentStatus;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
import * as Dto from './dto';
|
||||
export { Dto };
|
@ -1,74 +0,0 @@
|
||||
import { RestService, Rest } from '@abp/ng.core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import type { PagedResultDto } from '../abp/application/services/dto/models';
|
||||
import type { AppointmentDto, CreateOrUpdateAppointmentDto } from '../appoinments/dto/models';
|
||||
import type { FileDownloadDto, PagingSortResultDto } from '../dto/models';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AppointmentService {
|
||||
apiName = 'Default';
|
||||
|
||||
|
||||
createAppointment = (input: CreateOrUpdateAppointmentDto, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, void>({
|
||||
method: 'POST',
|
||||
url: '/api/app/appointment/appointment',
|
||||
body: input,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
deleteAppointmentRecord = (id: string, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, void>({
|
||||
method: 'DELETE',
|
||||
url: `/api/app/appointment/${id}/appointment-record`,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
get = (config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, string>({
|
||||
method: 'GET',
|
||||
responseType: 'text',
|
||||
url: '/api/app/appointment',
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
getAppointmentById = (id: string, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, AppointmentDto>({
|
||||
method: 'GET',
|
||||
url: `/api/app/appointment/${id}/appointment-by-id`,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
getAppointmentList = (input: PagingSortResultDto, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, PagedResultDto<AppointmentDto>>({
|
||||
method: 'GET',
|
||||
url: '/api/app/appointment/appointment-list',
|
||||
params: { search: input.search, sorting: input.sorting, skipCount: input.skipCount, maxResultCount: input.maxResultCount },
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
getExportAppointmentRecord = (config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, FileDownloadDto>({
|
||||
method: 'GET',
|
||||
url: '/api/app/appointment/export-appointment-record',
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
updateAppointment = (input: CreateOrUpdateAppointmentDto, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, void>({
|
||||
method: 'PUT',
|
||||
url: '/api/app/appointment/appointment',
|
||||
body: input,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
constructor(private restService: RestService) {}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from './appointment.service';
|
@ -1,65 +0,0 @@
|
||||
import { RestService, Rest } from '@abp/ng.core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import type { PagedResultDto } from '../abp/application/services/dto/models';
|
||||
import type { FileDownloadDto, PagingSortResultDto } from '../dto/models';
|
||||
import type { CreateDepartmentDto, DepartmentDto } from '../dtos/models';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DepartmentService {
|
||||
apiName = 'Default';
|
||||
|
||||
|
||||
createDepartment = (input: CreateDepartmentDto, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, void>({
|
||||
method: 'POST',
|
||||
url: '/api/app/department/department',
|
||||
body: input,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
deleteDepartmentRecord = (id: string, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, void>({
|
||||
method: 'DELETE',
|
||||
url: `/api/app/department/${id}/department-record`,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
getDepartmentById = (id: string, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, DepartmentDto>({
|
||||
method: 'GET',
|
||||
url: `/api/app/department/${id}/department-by-id`,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
getDepartmentList = (input: PagingSortResultDto, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, PagedResultDto<DepartmentDto>>({
|
||||
method: 'GET',
|
||||
url: '/api/app/department/department-list',
|
||||
params: { search: input.search, sorting: input.sorting, skipCount: input.skipCount, maxResultCount: input.maxResultCount },
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
getExportDepartmentRecord = (config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, FileDownloadDto>({
|
||||
method: 'GET',
|
||||
url: '/api/app/department/export-department-record',
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
updateDepartment = (input: CreateDepartmentDto, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, void>({
|
||||
method: 'PUT',
|
||||
url: '/api/app/department/department',
|
||||
body: input,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
constructor(private restService: RestService) {}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from './department.service';
|
@ -1,74 +0,0 @@
|
||||
import type { DoctorDto } from './dto/models';
|
||||
import { RestService, Rest } from '@abp/ng.core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import type { PagedResultDto } from '../abp/application/services/dto/models';
|
||||
import type { FileDownloadDto, PagingSortResultDto } from '../dto/models';
|
||||
import type { CreateDoctorDto } from '../dtos/models';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DoctorService {
|
||||
apiName = 'Default';
|
||||
|
||||
|
||||
createDoctor = (input: CreateDoctorDto, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, void>({
|
||||
method: 'POST',
|
||||
url: '/api/app/doctor/doctor',
|
||||
body: input,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
deleteDoctorRecord = (id: string, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, void>({
|
||||
method: 'DELETE',
|
||||
url: `/api/app/doctor/${id}/doctor-record`,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
get = (config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, DoctorDto[]>({
|
||||
method: 'GET',
|
||||
url: '/api/app/doctor',
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
getDoctorById = (id: string, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, DoctorDto>({
|
||||
method: 'GET',
|
||||
url: `/api/app/doctor/${id}/doctor-by-id`,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
getDoctorList = (input: PagingSortResultDto, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, PagedResultDto<DoctorDto>>({
|
||||
method: 'GET',
|
||||
url: '/api/app/doctor/doctor-list',
|
||||
params: { search: input.search, sorting: input.sorting, skipCount: input.skipCount, maxResultCount: input.maxResultCount },
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
getExportDoctorsRecord = (config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, FileDownloadDto>({
|
||||
method: 'GET',
|
||||
url: '/api/app/doctor/export-doctors-record',
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
|
||||
updatDoctor = (input: CreateDoctorDto, config?: Partial<Rest.Config>) =>
|
||||
this.restService.request<any, void>({
|
||||
method: 'POST',
|
||||
url: '/api/app/doctor/updat-doctor',
|
||||
body: input,
|
||||
},
|
||||
{ apiName: this.apiName,...config });
|
||||
|
||||
constructor(private restService: RestService) {}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from './models';
|
@ -1,39 +0,0 @@
|
||||
import type { FullAuditedEntity } from '../../volo/abp/domain/entities/auditing/models';
|
||||
import type { WorkSchedule } from '../../global-enum/work-schedule.enum';
|
||||
import type { FullAuditedEntityDto } from '@abp/ng.core';
|
||||
import type { TimeSlot } from '../../global-enum/time-slot.enum';
|
||||
|
||||
export interface DoctorDto extends FullAuditedEntity<string> {
|
||||
id?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
gender?: string;
|
||||
mobile?: string;
|
||||
designation?: string;
|
||||
departmentId?: string;
|
||||
address?: string;
|
||||
email?: string;
|
||||
dob?: string;
|
||||
education?: string;
|
||||
specialization?: string;
|
||||
degree?: string;
|
||||
joiningDate?: string;
|
||||
experience?: number;
|
||||
consultationFee?: number;
|
||||
availability: WorkSchedule[];
|
||||
rating?: number;
|
||||
clinicLocation?: string;
|
||||
shifts: ShiftManagementDto[];
|
||||
}
|
||||
|
||||
export interface ShiftManagementDto extends FullAuditedEntityDto<string> {
|
||||
doctorId?: string;
|
||||
shifts: Record<WorkSchedule, TimeSlot>;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
export interface CreateUpdateShiftManagementDto {
|
||||
doctorId?: string;
|
||||
shifts: Record<WorkSchedule, TimeSlot>;
|
||||
isActive: boolean;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
import * as Dto from './dto';
|
||||
export * from './doctor.service';
|
||||
export { Dto };
|
@ -1 +0,0 @@
|
||||
export * from './models';
|
@ -1,11 +0,0 @@
|
||||
import type { AuditedAggregateRoot } from '../volo/abp/domain/entities/auditing/models';
|
||||
|
||||
export interface EntityDocument extends AuditedAggregateRoot<string> {
|
||||
originalFileName?: string;
|
||||
generatedFileName?: string;
|
||||
fileSize?: string;
|
||||
filePath?: string;
|
||||
fileType?: string;
|
||||
tagName?: string;
|
||||
uploadDate?: string;
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from './models';
|
@ -1,15 +0,0 @@
|
||||
import type { PagedAndSortedResultRequestDto } from '@abp/ng.core';
|
||||
|
||||
export interface DropDownItems {
|
||||
label?: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface FileDownloadDto {
|
||||
fileName?: string;
|
||||
fileContent?: string;
|
||||
}
|
||||
|
||||
export interface PagingSortResultDto extends PagedAndSortedResultRequestDto {
|
||||
search?: string;
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from './models';
|
@ -1,44 +0,0 @@
|
||||
import type { WorkSchedule } from '../global-enum/work-schedule.enum';
|
||||
import type { FullAuditedEntity } from '../volo/abp/domain/entities/auditing/models';
|
||||
|
||||
export interface CreateDepartmentDto {
|
||||
id?: string;
|
||||
departmentNo?: string;
|
||||
departmentName?: string;
|
||||
departmentDate?: string;
|
||||
departmentHead?: string;
|
||||
description?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface CreateDoctorDto {
|
||||
id?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
gender?: string;
|
||||
mobile?: string;
|
||||
password?: string;
|
||||
designation?: string;
|
||||
departmentId?: string;
|
||||
address?: string;
|
||||
email?: string;
|
||||
dob?: string;
|
||||
education?: string;
|
||||
specialization?: string;
|
||||
degree?: string;
|
||||
joiningDate?: string;
|
||||
experience?: number;
|
||||
consultationFee?: number;
|
||||
availability: WorkSchedule[];
|
||||
rating?: number;
|
||||
clinicLocation?: string;
|
||||
}
|
||||
|
||||
export interface DepartmentDto extends FullAuditedEntity<string> {
|
||||
departmentNo?: string;
|
||||
departmentName?: string;
|
||||
departmentDate?: string;
|
||||
departmentHead?: string;
|
||||
status?: string;
|
||||
description?: string;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +0,0 @@
|
||||
import { mapEnumToOptions } from '@abp/ng.core';
|
||||
|
||||
export enum appointmentStatus {
|
||||
Scheduled = 1,
|
||||
Completed = 2,
|
||||
Canceled = 3,
|
||||
}
|
||||
|
||||
export const appointmentStatusOptions = mapEnumToOptions(appointmentStatus);
|
@ -1,8 +0,0 @@
|
||||
import { mapEnumToOptions } from '@abp/ng.core';
|
||||
|
||||
export enum Gender {
|
||||
Male = 1,
|
||||
Female = 2,
|
||||
}
|
||||
|
||||
export const genderOptions = mapEnumToOptions(Gender);
|
@ -1,7 +0,0 @@
|
||||
export * from './appointment-status.enum';
|
||||
export * from './gender.enum';
|
||||
export * from './payment-status.enum';
|
||||
export * from './status.enum';
|
||||
export * from './time-slot.enum';
|
||||
export * from './visit-type.enum';
|
||||
export * from './work-schedule.enum';
|
@ -1,9 +0,0 @@
|
||||
import { mapEnumToOptions } from '@abp/ng.core';
|
||||
|
||||
export enum paymentStatus {
|
||||
Paid = 1,
|
||||
Pending = 2,
|
||||
Unpaid = 3,
|
||||
}
|
||||
|
||||
export const paymentStatusOptions = mapEnumToOptions(paymentStatus);
|
@ -1,9 +0,0 @@
|
||||
import { mapEnumToOptions } from '@abp/ng.core';
|
||||
|
||||
export enum Status {
|
||||
Recovered = 1,
|
||||
Observation = 2,
|
||||
InTreatment = 3,
|
||||
}
|
||||
|
||||
export const statusOptions = mapEnumToOptions(Status);
|
@ -1,14 +0,0 @@
|
||||
import { mapEnumToOptions } from '@abp/ng.core';
|
||||
|
||||
export enum TimeSlot {
|
||||
TenToSeven = 1,
|
||||
NineToFive = 2,
|
||||
EightToFour = 3,
|
||||
SevenToFour = 4,
|
||||
SixToThree = 5,
|
||||
TwelveToNine = 6,
|
||||
TenToSix = 7,
|
||||
ElevenToEight = 8,
|
||||
}
|
||||
|
||||
export const timeSlotOptions = mapEnumToOptions(TimeSlot);
|
@ -1,8 +0,0 @@
|
||||
import { mapEnumToOptions } from '@abp/ng.core';
|
||||
|
||||
export enum visitType {
|
||||
NewPatient = 1,
|
||||
Followup = 2,
|
||||
}
|
||||
|
||||
export const visitTypeOptions = mapEnumToOptions(visitType);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user