Compare commits

...

2 Commits

Author SHA1 Message Date
Mat Rawi
2d4f9bcad3 Merge branch 'main' of https://git.hzwnrw.my/Insurance/insurance-fe 2025-08-29 11:03:25 +08:00
Mat Rawi
c23fc5c262 Add claims pages 2025-08-29 11:02:24 +08:00
13 changed files with 282 additions and 5 deletions

View File

@@ -1,8 +1,12 @@
import { Routes } from '@angular/router';
import { HomepageComponent } from './features/homepage/homepage.component';
import { InsuranceDetailComponent } from './features/insurance-detail/insurance-detail.component';
import { ClaimComponent } from './features/claim/claim.component';
import { ClaimNewComponent } from './features/claim-new/claim-new.component';
export const routes: Routes = [
{ path: '', component: HomepageComponent },
{ path: 'plan/:id', component: InsuranceDetailComponent }
{ path: 'plan/:id', component: InsuranceDetailComponent },
{ path: 'claims', component: ClaimComponent },
{ path: 'claims/new', component: ClaimNewComponent }
];

View File

@@ -0,0 +1,27 @@
<div class="container mt-4">
<h2>New Claim Submission</h2>
<form [formGroup]="claimForm" (ngSubmit)="onSubmit()">
<div class="mb-2">
<label>Name</label>
<input type="text" class="form-control" formControlName="name" />
</div>
<div class="mb-2">
<label>Policy ID</label>
<input type="text" class="form-control" formControlName="policyID" />
</div>
<div class="mb-2">
<label>Reason</label>
<input type="text" class="form-control" formControlName="claimReason" />
</div>
<div class="mb-2">
<label>Amount</label>
<input type="number" class="form-control" formControlName="claimAmount" />
</div>
<button type="submit" class="btn btn-success" [disabled]="claimForm.invalid">
Submit
</button>
<button type="button" class="btn btn-secondary ms-2" (click)="cancel()">Cancel</button>
</form>
</div>

View File

@@ -0,0 +1,45 @@
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { Router, RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ClaimService } from '../../shared/services/claim.service';
@Component({
selector: 'app-claim-new',
standalone: true,
imports: [CommonModule, ReactiveFormsModule, RouterModule], // ✅ RouterModule is required
templateUrl: './claim-new.component.html',
styleUrls: ['./claim-new.component.css']
})
export class ClaimNewComponent {
claimForm: FormGroup;
constructor(
private fb: FormBuilder,
private claimService: ClaimService,
private router: Router
) {
this.claimForm = this.fb.group({
name: ['', Validators.required],
policyID: ['', Validators.required],
claimReason: ['', Validators.required],
claimAmount: [0, [Validators.required, Validators.min(1)]],
status: ['PENDING']
});
}
// ✅ Submit with alert and redirect
onSubmit(): void {
if (this.claimForm.valid) {
this.claimService.addClaim(this.claimForm.value).subscribe(() => {
alert('✅ Claim submitted successfully!');
setTimeout(() => this.router.navigate(['/claims']));
});
}
}
// ✅ Cancel button redirect
cancel(): void {
this.router.navigate(['/claims']);
}
}

View File

@@ -0,0 +1,69 @@
<div class="container mt-4">
<h2>Claims</h2>
<!-- Add Button -->
<a routerLink="/claims/new" class="btn btn-primary mb-3">
Add Claim
</a>
<!-- Claims Table -->
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Policy ID</th>
<th>Reason</th>
<th>Amount</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let claim of claims">
<td>{{ claim.id }}</td>
<td>{{ claim.name }}</td>
<td>{{ claim.policyID }}</td>
<td>{{ claim.claimReason }}</td>
<td>{{ claim.claimAmount }}</td>
<td>{{ claim.status }}</td>
<td>
<button class="btn btn-sm btn-success me-1"
(click)="updateStatus(claim.id, 'APPROVED')"
[disabled]="claim.status === 'APPROVED'">
Approve
</button>
<button class="btn btn-sm btn-danger"
(click)="updateStatus(claim.id, 'REJECTED')"
[disabled]="claim.status === 'REJECTED'">
Reject
</button>
</td>
</tr>
</tbody>
</table>
<!-- Pagination -->
<div class="d-flex justify-content-center mt-3">
<button class="btn btn-outline-primary me-1"
(click)="prevPage()"
[disabled]="page === 0">
Previous
</button>
<ng-container *ngFor="let p of pageNumbers">
<button class="btn me-1"
[ngClass]="{'btn-primary': page === p, 'btn-outline-primary': page !== p}"
(click)="goToPage(p)">
{{ p + 1 }}
</button>
</ng-container>
<button class="btn btn-outline-primary ms-1"
(click)="nextPage()"
[disabled]="page + 1 >= totalPages">
Next
</button>
</div>
</div>

View File

@@ -0,0 +1,64 @@
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Claim, ClaimService, PageResponse } from '../../shared/services/claim.service';
import { RouterModule } from '@angular/router';
@Component({
selector: 'app-claim',
standalone: true,
imports: [CommonModule, RouterModule],
templateUrl: './claim.component.html',
styleUrls: ['./claim.component.css']
})
export class ClaimComponent implements OnInit {
claims: Claim[] = [];
page = 0;
size = 5;
totalElements = 0;
constructor(private claimService: ClaimService) {}
ngOnInit(): void {
this.loadClaims();
}
loadClaims(): void {
this.claimService.getClaims(this.page, this.size).subscribe((res: PageResponse<Claim>) => {
this.claims = res.content;
this.totalElements = res.totalElements;
});
}
nextPage(): void {
if ((this.page + 1) * this.size < this.totalElements) {
this.page++;
this.loadClaims();
}
}
prevPage(): void {
if (this.page > 0) {
this.page--;
this.loadClaims();
}
}
get totalPages(): number {
return Math.ceil(this.totalElements / this.size);
}
get pageNumbers(): number[] {
return Array.from({ length: this.totalPages }, (_, i) => i);
}
goToPage(page: number): void {
this.page = page;
this.loadClaims();
}
updateStatus(id: number, status: string): void {
this.claimService.updateStatus(id, status).subscribe(() => {
this.loadClaims();
});
}
}

View File

@@ -28,4 +28,11 @@
<div *ngIf="!loading && !errorMessage && cards.length === 0" class="text-center text-muted fs-5 mt-4">
No insurances found.
</div>
<div class="text-center mb-4">
<button class="btn btn-success btn-lg" (click)="navigateToClaims()">
Claims
</button>
</div>
</div>

View File

@@ -59,4 +59,9 @@ export class HomepageComponent implements OnInit {
navigate(link: string): void {
this.router.navigate([link]);
}
navigateToClaims(): void {
this.router.navigate(['/claims']); // ✅ redirect to claim page
}
}

View File

@@ -0,0 +1,55 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { environment } from '../../../environments/environment';
export interface Claim {
id: number;
name: string;
policyID: string;
claimReason: string;
claimAmount: number;
status: string;
}
export interface PageResponse<T> {
content: T[];
totalElements: number;
totalPages: number;
size: number;
number: number;
}
@Injectable({
providedIn: 'root'
})
export class ClaimService {
private apiUrl = `${environment.apiUrl}/api/claim`;
constructor(private http: HttpClient) {}
// ✅ Get pageable claims
getClaims(page: number, size: number): Observable<PageResponse<Claim>> {
let params = new HttpParams().set('page', page).set('size', size);
return this.http.get<PageResponse<Claim>>(this.apiUrl, { params })
.pipe(catchError(this.handleError));
}
// ✅ Create a new claim
addClaim(claim: Claim): Observable<Claim> {
return this.http.post<Claim>(`${this.apiUrl}/add`, claim)
.pipe(catchError(this.handleError));
}
// ✅ Update status (Approve / Reject)
updateStatus(id: number, status: string): Observable<Claim> {
return this.http.put<Claim>(`${this.apiUrl}/${id}/status?status=${status}`, {})
.pipe(catchError(this.handleError));
}
private handleError(error: HttpErrorResponse) {
console.error('An error occurred:', error.message);
return throwError(() => new Error('Something went wrong; please try again later.'));
}
}

View File

@@ -27,16 +27,17 @@ export interface InsuranceDetail {
providedIn: 'root'
})
export class InsuranceService {
private apiUrl = environment.apiUrl;
private apiUrl = `${environment.apiUrl}/api/insurance`;
constructor(private http: HttpClient) {}
getInsurances(): Observable<Insurance[]> {
return this.http.post<Insurance[]>(`${this.apiUrl}/all`, {}).pipe(
return this.http.get<Insurance[]>(`${this.apiUrl}/all`).pipe(
catchError(this.handleError)
);
}
getInsuranceById(id: number | string): Observable<InsuranceDetail> {
return this.http.get<InsuranceDetail>(`${this.apiUrl}/detail/${id}`)
.pipe(catchError(this.handleError));

View File

@@ -1,4 +1,4 @@
export const environment = {
production: true,
apiUrl: 'https://insurance.hzwnrw.my/api/insurance'
apiUrl: 'https://insurance.hzwnrw.my'
};

View File

@@ -1,4 +1,4 @@
export const environment = {
production: false,
apiUrl: 'http://localhost:1204/api/insurance'
apiUrl: 'http://localhost:1204'
};