Compare commits

..

6 Commits

28 changed files with 918 additions and 549 deletions

View File

@ -39,20 +39,7 @@ const routes: Routes = [
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
),
},
{
path: 'patients',
loadChildren: () => import('./patients/patients.module').then(m => m.PatientsModule),

View File

@ -1 +1,6 @@
<full-calendar [options]="calendarOptions"></full-calendar>
<app-appointment-dialog [selectedDate]="selectedDate"
[appointmentId]="appointmentIdToEdit"[isEditMode]="isEditMode"
[visible]="isModalVisible"*ngIf="isModalVisible" [name]="'Appointment'" (close)="closeDialog()"></app-appointment-dialog>

View File

@ -1,16 +1,59 @@
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { FullCalendarModule } from '@fullcalendar/angular';
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';
@Component({
selector: 'app-appointment-calendar',
templateUrl: './appointment-calendar.component.html',
styleUrl: './appointment-calendar.component.scss'
})
export class AppointmentCalendarComponent {
export class AppointmentCalendarComponent implements OnInit{
appointments: any[] = [];
params: PagingSortResultDto;
selectedDate: string;
isModalVisible: boolean = false;
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 => ({
title: appointment.firstName + ' - ' + appointment.doctor,
date: appointment.dateOfAppointment
})),
dateClick: (arg) => this.handleDateClick(arg)
};
}
calendarOptions: CalendarOptions = {
initialView: 'dayGridMonth',
plugins: [dayGridPlugin, interactionPlugin],
@ -22,6 +65,10 @@ export class AppointmentCalendarComponent {
};
handleDateClick(arg) {
alert('date click! ' + arg.dateStr)
this.selectedDate = arg.dateStr;
this.isModalVisible = true;
}
onModalClose() {
this.isModalVisible = false;
}
}

View File

@ -0,0 +1,372 @@
<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-1 fw-normal">{{ name }}</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">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
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">First Name is required.</span>
<span *ngIf="fnameCtrl.errors?.minlength">Minimum 2 characters required.</span>
<span *ngIf="fnameCtrl.errors?.maxlength">Maximum 30 characters allowed.</span>
</small>
</div>
<div class="field col-md-1"></div>
<div class="field col-md-5">
<label for="lname">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="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">Last Name is required.</span>
<span *ngIf="lnameCtrl.errors?.minlength">Minimum 2 characters required.</span>
<span *ngIf="lnameCtrl.errors?.maxlength">Maximum 30 characters allowed.</span>
</small>
</div>
<div class="field col-md-5">
<label>Gender <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</label>
<p-radioButton
name="gender"
value="2"
[(ngModel)]="appointment.gender"
inputId="female"
required
></p-radioButton>
<label for="female" class="ml-2">Female</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">
Please select a gender.
</small>
</div>
<div class="field col-md-1"></div>
<div class="field col-md-5">
<label for="mobile">Mobile No <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">Mobile number is required.</span>
<span *ngIf="mobileCtrl.errors?.pattern">Enter a valid 10-digit mobile number.</span>
</small>
</div>
<div class="field col-md-11">
<label for="address">Address <span class="text-danger">*</span></label>
<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">Address is required.</span>
<span *ngIf="addressCtrl.errors?.minlength">Minimum 5 characters required.</span>
<span *ngIf="addressCtrl.errors?.maxlength">Maximum 100 characters allowed.</span>
</small>
</div>
<div class="field col-md-5">
<label for="email">Email ID <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">Email address is required.</span>
<span *ngIf="emailCtrl.errors?.email">Enter a valid email address.</span>
</small>
</div>
<div class="field col-md-1"></div>
<div class="field col-md-5">
<label for="dob">Date of Birth <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"
>DOB Required</small
>
</div>
<div class="field col-md-5">
<label for="doctor">Consulting Doctor <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">Date of Appointment <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">Time Of Appointment <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">Injury/Condition</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">Insurance Provider</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">Appointment Status</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">Visit Type</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">Payment Status</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</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"
class="p-button-success"
[disabled]="appointmentForm.invalid"
></button>
<button
pButton
type="button"
label="Cancel"
class="p-button-secondary ml-2"
(click)="onClose()"
></button>
</div>
</div>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
.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 */
}

View File

@ -0,0 +1,23 @@
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();
});
});

View File

@ -0,0 +1,215 @@
import { ConfirmationService, ToasterService } from '@abp/ng.theme.shared';
import { HttpClient } from '@angular/common/http';
import {
Component,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
ViewChild,
} 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';
@Component({
selector: 'app-appointment-dialog',
standalone: true,
imports: [
ViewAppointmentRoutingModule,
TableModule,
DialogModule,
FormsModule,
TableModule,
ButtonModule,
DialogModule,
InputTextModule,
CalendarModule,
DropdownModule,
RadioButtonModule,
InputTextareaModule,
ChipModule,
CommonModule,
],
templateUrl: './appointment-dialog.component.html',
styleUrl: './appointment-dialog.component.scss',
})
export class AppointmentDialogComponent implements OnInit,OnChanges {
@Input() visible: boolean = false; // Control modal visibility
@Input() name: string;
@Input() isEditMode: boolean = false; // Determine if it's for edit or create
@Output() save = new EventEmitter<any>(); // Event emitter for saving appointment
@Output() close = new EventEmitter<void>(); // Event emitter for closing the modal
@Input() appointmentId: string; // To accept the appointment ID from the parent
@Input() selectedDate: string;
appointmentsForDate: any[] = [];
loading: boolean = false;
AppointmentDialogTitle: string = '';
AppointmentDialog: boolean = false;
genders = Gender;
Dateofbirth: Date = new Date();
AppointmentDate: Date = new Date();
doctors = [];
doctorOptions = [];
constructor(
private DoctorService: DoctorService,
private AppointmentService: AppointmentService,
private http: HttpClient,
private confirmation: ConfirmationService,
private toaster: ToasterService
) {}
ngOnInit(): void {
debugger;
this.getdoctorlist();
if (!this.isEditMode) {
this.appointment = {
firstName: '',
lastName: '',
email: '',
gender: Gender.Male,
dateOfAppointment: '',
dob: '',
timeOfAppointment: '',
mobile: '',
injuryORContion: '',
note: '',
doctorId: '',
address: '',
appointmentStatus: null,
visitType: null,
paymentStatus: null,
insuranceProvider: '',
};
}
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['appointmentId'] && this.appointmentId) {
// When the appointment ID changes, fetch the appointment details
this.fetchAppointmentData();
}
if (changes['selectedDate'] && this.selectedDate) {
this.fetchAppointmentsForDate();
}
}
fetchAppointmentsForDate() {
// this.AppointmentService.getAppointmentsByDate(this.selectedDate).subscribe((data: any[]) => {
// this.appointmentsForDate = data;
// });
debugger
}
fetchAppointmentData() {
// Fetch data based on appointment ID
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) {
debugger;
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 => {
console.log(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 => {
console.log(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() {
debugger;
this.AppointmentDialog = false;
this.close.emit(); // Emit close event
}
}

View File

@ -0,0 +1,25 @@
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',
loadChildren: () => import('./view-appointment/view-appointment.module').then(m => m.ViewAppointmentModule)
},
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AppointmentRoutingModule { }

View File

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppointmentRoutingModule } from './appointment-routing.module';
import { AppointmentDialogComponent } from './appointment-dialog/appointment-dialog.component';
@NgModule({
declarations: [],
imports: [
CommonModule,
AppointmentRoutingModule,
AppointmentDialogComponent
]
})
export class AppointmentModule { }

View File

@ -1,11 +0,0 @@
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 { }

View File

@ -1 +0,0 @@
<p>book-appointment works!</p>

View File

@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BookAppointmentComponent } from './book-appointment.component';
describe('BookAppointmentComponent', () => {
let component: BookAppointmentComponent;
let fixture: ComponentFixture<BookAppointmentComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [BookAppointmentComponent]
})
.compileComponents();
fixture = TestBed.createComponent(BookAppointmentComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,10 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-book-appointment',
templateUrl: './book-appointment.component.html',
styleUrl: './book-appointment.component.scss'
})
export class BookAppointmentComponent {
}

View File

@ -1,17 +0,0 @@
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 { }

View File

@ -1,11 +0,0 @@
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 { }

View File

@ -1 +0,0 @@
<p>edit-appointment works!</p>

View File

@ -1,23 +0,0 @@
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();
});
});

View File

@ -1,10 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-edit-appointment',
templateUrl: './edit-appointment.component.html',
styleUrl: './edit-appointment.component.scss'
})
export class EditAppointmentComponent {
}

View File

@ -1,17 +0,0 @@
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 { }

View File

@ -17,21 +17,21 @@
<ng-template pTemplate="caption">
<div class="flex align-items-center justify-content-between mb-3 gap-3">
<!-- Left: Title -->
<h2 class="m-0">Appointment List</h2>
<h4 class="m-0">Appointment List</h4>
<!-- Center: Search Bar with Icon Inside -->
<div class="flex-grow-1 flex justify-content-center">
<span class="p-input-icon-left w-100">
<i class="pi pi-search"></i>
<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 Appointments"
(input)="dt2.filterGlobal(globalFilter, 'contains')"
class="w-full"
placeholder="Search keyword"
/>
</span>
</div>
</div>
<div>
<button
@ -51,7 +51,11 @@
>
<i class="pi pi-plus-circle"></i>
</button>
<button pButton class="p-button-rounded p-button-warning ml-2" (click)="exportAppointments()">
<button
pButton
class="p-button-rounded p-button-warning ml-2"
(click)="exportAppointments()"
>
<i class="pi pi-download"></i>
</button>
</div>
@ -67,7 +71,9 @@
<th pSortableColumn="timeOfAppointment">Time<p-sortIcon field="timeOfAppointment" /></th>
<th>Mobile No</th>
<th>Email</th>
<th pSortableColumn="appointmentStatus">Appointment Status<p-sortIcon field="appointmentStatus" /></th>
<th pSortableColumn="appointmentStatus">
Appointment Status<p-sortIcon field="appointmentStatus" />
</th>
<th pSortableColumn="visitType">Visit Type<p-sortIcon field="visitType" /></th>
<th>Actions</th>
</tr>
@ -78,296 +84,45 @@
<td>{{ appointment.firstName }} {{ appointment.lastName }}</td>
<td>{{ appointment.doctor }}</td>
<td>
<p-chip
[severity]="appointment.gender === 1 ? 'info' : 'danger'"
[styleClass]="'px-2 py-1 text-sm font-medium'"
>
<span class="badge" [ngClass]="appointment.gender === 1 ? 'bg-primary' : 'bg-pink'">
{{ getGenderLabel(appointment.gender) }}
</p-chip>
</span>
</td>
<td>{{ appointment.dateOfAppointment | date }}</td>
<td>{{ appointment.timeOfAppointment }}</td>
<td>{{ appointment.mobile }}</td>
<td>{{ appointment.email }}</td>
<td>
<p-chip
[severity]="appointment.appointmentStatus === 1 ? 'info' : 'danger'"
[styleClass]="'px-2 py-1 text-sm font-medium'"
<span
class="badge"
[ngClass]="
appointment.appointmentStatus === 1
? 'bg-success'
: appointment.appointmentStatus === 2
? 'bg-primary'
: 'bg-danger'
"
>
{{ getStatusLabel(appointment.appointmentStatus) }}
</p-chip>
</span>
</td>
<td>
<p-chip
[severity]="appointment.visitType === 1 ? 'info' : 'danger'"
[styleClass]="'px-2 py-1 text-sm font-medium'"
>
<span class="badge" [ngClass]="appointment.visitType === 1 ? 'bg-success' : 'bg-danger'">
{{ getVisitTypeLabel(appointment.visitType) }}
</p-chip>
</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>
<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>
<p-dialog
header="{{ isEditMode ? 'Edit Appointment' : 'New Appointment' }}"
[(visible)]="appointmentDialog"
[modal]="true"
[closable]="true"
[style]="{ width: '80%' }"
>
<form #appointmentForm="ngForm" (ngSubmit)="saveAppointment(appointmentForm)">
<div class="p-fluid grid justify-content-center">
<div class="field col-md-5">
<label for="fname">First Name <span class="text-danger">*</span></label>
<span class="p-input-icon-left">
<i class="pi pi-user"></i>
<input pInputText id="fname" name="fname" [(ngModel)]="appointment.firstName" required />
</span>
</div>
<div class="field col-md-1"></div>
<div class="field col-md-5">
<label for="lname">Last Name <span class="text-danger">*</span></label>
<span class="p-input-icon-left">
<i class="pi pi-user"></i>
<input pInputText id="lname" name="lname" [(ngModel)]="appointment.lastName" required />
</span>
</div>
<div class="field col-md-5">
<label>Gender <span class="text-danger">*</span></label>
<div class="flex align-items-center">
<p-radioButton
name="gender"
value="1"
[(ngModel)]="appointment.gender"
inputId="male"
required
></p-radioButton>
<label for="male" class="ml-2 mr-3">Male</label>
<p-radioButton
name="gender"
value="2"
[(ngModel)]="appointment.gender"
inputId="female"
required
></p-radioButton>
<label for="female" class="ml-2">Female</label>
</div>
</div>
<div class="field col-md-1"></div>
<div class="field col-md-5">
<label for="mobile">Mobile No <span class="text-danger">*</span></label>
<span class="p-input-icon-left">
<i class="pi pi-phone"></i>
<input pInputText id="mobile" name="mobile" [(ngModel)]="appointment.mobile" required />
</span>
<small
class="p-error"
*ngIf="
appointmentForm.controls.mobile?.invalid && appointmentForm.controls.mobile?.touched
"
>Mobile number Required</small
>
</div>
<div class="field col-md-11">
<label for="mobile">Address <span class="text-danger">*</span></label>
<span class="p-input-icon-left">
<i class="pi pi-map-marker"></i>
<input
pInputText
id="Address"
name="Address"
[(ngModel)]="appointment.address"
required
/>
</span>
</div>
<div class="field col-md-5">
<label for="email">Email ID <span class="text-danger">*</span></label>
<span class="p-input-icon-left">
<i class="pi pi-envelope"></i>
<input pInputText id="email" name="email" [(ngModel)]="appointment.email" required />
</span>
<small
class="p-error"
*ngIf="appointmentForm.controls.email?.invalid && appointmentForm.controls.email?.touched"
>Email address Required</small
>
</div>
<div class="field col-md-1"></div>
<div class="field col-md-5">
<label for="dob">Date of Birth <span class="text-danger">*</span></label>
<p-calendar
id="dob"
required
name="dob"
[(ngModel)]="Dateofbirth"
[showIcon]="true"
appendTo="body"
required
></p-calendar>
<small
class="p-error"
*ngIf="appointmentForm.controls.dob?.invalid && appointmentForm.controls.dob?.touched"
>DOB Required</small
>
</div>
<div class="field col-md-5">
<label for="doctor">Consulting Doctor <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">Date of Appointment <span class="text-danger">*</span></label>
<p-calendar
id="date"
name="date"
[(ngModel)]="AppointmentDate"
[showIcon]="true"
appendTo="body"
required
></p-calendar>
</div>
<div class="field col-md-5">
<label for="time">Time Of Appointment <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">Injury/Condition</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">Insurance Provider</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">Appointment Status</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">Visit Type</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">Payment Status</label>
<p-dropdown
id="paymentStatus"
name="paymentStatus"
[(ngModel)]="appointment.paymentStatus"
[options]="paymentStatuses"
optionLabel="label"
optionValue="value"
placeholder="Select Payment Status"
></p-dropdown>
</div>
<!-- Notes (Full Width) -->
<div class="field col-11">
<label for="notes">Notes</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"
class="p-button-success"
[disabled]="appointmentForm.invalid"
></button>
<button
pButton
type="button"
label="Cancel"
class="p-button-secondary ml-2"
(click)="closeDialog()"
></button>
</div>
</div>
</form>
</p-dialog>
<app-appointment-dialog [appointmentId]="appointmentIdToEdit"[isEditMode]="isEditMode"
[visible]="isModalVisible"*ngIf="isModalVisible" [name]="'Appointment'" (close)="closeDialog()"></app-appointment-dialog>

View File

@ -1,3 +1,11 @@
.is-valid {
border-color: green !important;
}
.is-invalid {
border-color: red !important;
}
.table-header {
display: flex;
justify-content: space-between;
@ -13,3 +21,24 @@
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;
}

View File

@ -15,14 +15,15 @@ import { appointmentStatus, Gender, paymentStatus, visitType } from '@proxy/glob
})
export class ViewAppointmentComponent {
totalRecords: number = 0;
appointmentIdToEdit: string;
AppointmentDialogTitle: string = '';
AppointmentDialog: boolean = false;
patients: [];
appointmentDialog = false;
genders = Gender;
Dateofbirth: Date = new Date();
AppointmentDate: Date = new Date();
isModalVisible:boolean=false;
appointmentStatuses = Object.keys(appointmentStatus)
.filter(key => !isNaN(Number(key)))
.map(key => ({
@ -122,26 +123,27 @@ export class ViewAppointmentComponent {
});
}
openNewAppointmentDialog() {
this.isModalVisible=true;
this.isEditMode = false;
this.appointmentDialog = true;
this.appointment = {
firstName: '',
lastName: '',
email: '',
gender: Gender.Male,
dateOfAppointment: '',
dob: '',
timeOfAppointment: '',
mobile: '',
injuryORContion: '',
note: '',
doctorId: '',
address: '',
appointmentStatus: null,
visitType: null,
paymentStatus: null,
insuranceProvider: '',
};
//this.AppointmentDialog = true;
// this.appointment = {
// firstName: '',
// lastName: '',
// email: '',
// gender: Gender.Male,
// dateOfAppointment: '',
// dob: '',
// timeOfAppointment: '',
// mobile: '',
// injuryORContion: '',
// note: '',
// doctorId: '',
// address: '',
// appointmentStatus: null,
// visitType: null,
// paymentStatus: null,
// insuranceProvider: '',
// };
}
@ -174,14 +176,17 @@ export class ViewAppointmentComponent {
editAppointment(appointment: any) {
debugger;
this.isEditMode = true;
this.appointmentDialog = true;
this.appointmentIdToEdit = appointment.id;
this.isModalVisible=true;
this.AppointmentService.getAppointmentById(appointment.id).subscribe(result => {
debugger;
this.appointment = result;
this.AppointmentDate = new Date(result.dateOfAppointment);
this.Dateofbirth = new Date(result.dob);
});
// this.AppointmentDialog = true;
// this.isModalVisible=true;
// this.AppointmentService.getAppointmentById(appointment.id).subscribe(result => {
// debugger;
// this.appointment = result;
// this.AppointmentDate = new Date(result.dateOfAppointment);
// this.Dateofbirth = new Date(result.dob);
// });
}
deleteAppointment(id: string) {
@ -200,58 +205,17 @@ export class ViewAppointmentComponent {
}
});
}
saveAppointment(form: NgForm) {
debugger;
console.log(form.controls);
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.loadappointments({
first: 0,
rows: 10,
sortField: 'id',
sortOrder: 1,
globalFilter: null,
});
this.closeDialog();
},
error => {
console.log(error);
this.toaster.error(error, 'Error');
}
);
} else {
this.AppointmentService.createAppointment(this.appointment).subscribe(
() => {
this.toaster.success('Appointment created successfully', 'Success');
this.AppointmentDialog = false;
this.loadappointments({
first: 0,
rows: 10,
sortField: 'id',
sortOrder: 1,
globalFilter: null,
});
this.closeDialog();
},
error => {
console.log(error);
this.toaster.error(error.error.error.message, 'Error');
}
);
}
}
closeDialog() {
this.appointmentDialog = false;
debugger
this.isModalVisible = false;
this.loadappointments({
first: 0,
rows: 10,
sortField: 'id',
sortOrder: 1,
globalFilter: null,
});
}
getdoctorlist() {
this.DoctorService.get().subscribe(result => {
@ -263,4 +227,9 @@ export class ViewAppointmentComponent {
}));
});
}
saveAppointment(appointmentData: any) {
// Save appointment logic here
console.log('Appointment saved:', appointmentData);
this.closeDialog(); // Close the dialog after saving
}
}

View File

@ -14,6 +14,7 @@ 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";
@NgModule({
declarations: [ViewAppointmentComponent],
@ -31,8 +32,8 @@ import { ChipModule } from 'primeng/chip';
DropdownModule,
RadioButtonModule,
InputTextareaModule,
ChipModule
ChipModule,
AppointmentDialogComponent
],
providers:[DoctorService]
})

View File

@ -1462,14 +1462,36 @@
"allowAnonymous": false,
"implementFrom": "HospitalManagementSystem.Patients.PatientAppService"
},
"GetExportPatientRecordAsync": {
"uniqueName": "GetExportPatientRecordAsync",
"GetExportPatientRecordAsyncByPatientId": {
"uniqueName": "GetExportPatientRecordAsyncByPatientId",
"name": "GetExportPatientRecordAsync",
"httpMethod": "GET",
"url": "api/app/patient/export-patient-record",
"url": "api/app/patient/export-patient-record/{patientId}",
"supportedVersions": [],
"parametersOnMethod": [],
"parameters": [],
"parametersOnMethod": [
{
"name": "patientId",
"typeAsString": "System.Guid, System.Private.CoreLib",
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "patientId",
"name": "patientId",
"jsonName": null,
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "HospitalManagementSystem.Dto.FileDownloadDto",
"typeSimple": "HospitalManagementSystem.Dto.FileDownloadDto"
@ -5609,6 +5631,18 @@
"enumValues": null,
"genericArguments": null,
"properties": [
{
"name": "Id",
"jsonName": null,
"type": "System.Guid",
"typeSimple": "string",
"isRequired": false,
"minLength": null,
"maxLength": null,
"minimum": null,
"maximum": null,
"regex": null
},
{
"name": "FirstName",
"jsonName": null,

View File

@ -36,20 +36,7 @@ function configureRoutes(routesService: RoutesService) {
iconClass: 'fas fa-clock',
order: 103,
},
{
path: '/appointment/book-appointment',
name: 'Book Appointment',
parentName: 'Appointments',
iconClass: 'fas fa-clock',
order: 104,
},
{
path: '/appointment/edit-appointment',
name: 'Edit Appointment',
parentName: 'Appointments',
iconClass: 'fas fa-clock',
order: 105,
},
{
path: '/patients',
name: 'Patients',

View File

@ -34,12 +34,13 @@ namespace HospitalManagementSystem.Appointments
private readonly IRepository<Doctor, Guid> _doctorRepository;
private readonly IWebHostEnvironment _env;
public AppointmentAppService(ICurrentUser currentUser, ICurrentTenant currentTenant, IRepository<Appointment, Guid> appointmentsRepository, IRepository<Doctor, Guid> doctorRepository)
public AppointmentAppService(ICurrentUser currentUser, ICurrentTenant currentTenant, IRepository<Appointment, Guid> appointmentsRepository, IRepository<Doctor, Guid> doctorRepository, IWebHostEnvironment env)
{
_currentUser = currentUser;
_currentTenant = currentTenant;
_appointmentsRepository = appointmentsRepository;
_doctorRepository = doctorRepository;
_env = env;
}
public async Task<string> GetAsync()
{
@ -163,15 +164,19 @@ namespace HospitalManagementSystem.Appointments
for (int i = 0; i < Appointmentrecord.Count; i++)
{
//worksheet.Cell(i + 2, 1).Value = Appointmentrecord[i].Patients.Name;
//worksheet.Cell(i + 2, 2).Value = Appointmentrecord[i].Patients.Gender.ToString();
//worksheet.Cell(i + 2, 3).Value = Appointmentrecord[i].DateOfAdmission.ToShortDateString();
//worksheet.Cell(i + 2, 4).Value = Appointmentrecord[i].Diagnosis;
//worksheet.Cell(i + 2, 5).Value = Appointmentrecord[i].NextFollowUp?.ToShortDateString();
//worksheet.Cell(i + 2, 6).Value = Appointmentrecord[i].InsuranceProvider;
//worksheet.Cell(i + 2, 7).Value = Appointmentrecord[i].InsuranceProvider;
//worksheet.Cell(i + 2, 8).Value = Appointmentrecord[i].InsuranceProvider;
//worksheet.Cell(i + 2, 9).Value = Appointmentrecord[i].Status.ToString();
worksheet.Cell(i + 2, 1).Value = Appointmentrecord[i].FirstName + "" + Appointmentrecord[i].LastName;
worksheet.Cell(i + 2, 2).Value = Appointmentrecord[i].Email;
worksheet.Cell(i + 2, 3).Value = Appointmentrecord[i].Gender.ToString();
worksheet.Cell(i + 2, 4).Value = Appointmentrecord[i].DateOfAppointment?.ToShortDateString();
worksheet.Cell(i + 2, 5).Value = Appointmentrecord[i].TimeOfAppointment;
worksheet.Cell(i + 2, 6).Value = Appointmentrecord[i].Mobile;
worksheet.Cell(i + 2, 7).Value = "";
worksheet.Cell(i + 2, 8).Value = Appointmentrecord[i].InjuryORContion;
worksheet.Cell(i + 2, 9).Value = Appointmentrecord[i].AppointmentStatus.ToString();
worksheet.Cell(i + 2, 10).Value = Appointmentrecord[i].VisitType.ToString();
worksheet.Cell(i + 2, 11).Value = Appointmentrecord[i].PaymentStatus.ToString();
worksheet.Cell(i + 2, 12).Value = Appointmentrecord[i].InsuranceProvider;
worksheet.Cell(i + 2, 13).Value = Appointmentrecord[i].Note;
}
worksheet.Columns().AdjustToContents();