Compare commits
9 Commits
8b3d3409f8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c20f2353c3 | ||
|
|
0b15276ad6 | ||
|
|
c7ba402747 | ||
|
|
fa53c8aaf8 | ||
|
|
2d4f9bcad3 | ||
|
|
c23fc5c262 | ||
|
|
a565ea27a9 | ||
| 77745c23b4 | |||
| fa35aa6c10 |
@@ -1,10 +1,14 @@
|
||||
# Build stage
|
||||
FROM node:22.12 AS build
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build -- --configuration production
|
||||
|
||||
# Serve stage
|
||||
FROM nginx:alpine
|
||||
COPY --from=build /app/dist/insurance-fe/browser /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
17
nginx.conf
Normal file
17
nginx.conf
Normal file
@@ -0,0 +1,17 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name insurance.hzwnrw.my;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
client_max_body_size 10M;
|
||||
}
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "insurance-fe",
|
||||
"version": "0.0.14",
|
||||
"version": "0.0.17",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "insurance-fe",
|
||||
"version": "0.0.14",
|
||||
"version": "0.0.17",
|
||||
"dependencies": {
|
||||
"@angular/cdk": "^20.2.0",
|
||||
"@angular/common": "^20.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "insurance-fe",
|
||||
"version": "0.0.14",
|
||||
"version": "0.0.17",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
|
||||
@@ -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 }
|
||||
];
|
||||
|
||||
0
src/app/features/claim-new/claim-new.component.css
Normal file
0
src/app/features/claim-new/claim-new.component.css
Normal file
27
src/app/features/claim-new/claim-new.component.html
Normal file
27
src/app/features/claim-new/claim-new.component.html
Normal 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>
|
||||
45
src/app/features/claim-new/claim-new.component.ts
Normal file
45
src/app/features/claim-new/claim-new.component.ts
Normal 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],
|
||||
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']);
|
||||
}
|
||||
}
|
||||
0
src/app/features/claim/claim.component.css
Normal file
0
src/app/features/claim/claim.component.css
Normal file
69
src/app/features/claim/claim.component.html
Normal file
69
src/app/features/claim/claim.component.html
Normal 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>
|
||||
64
src/app/features/claim/claim.component.ts
Normal file
64
src/app/features/claim/claim.component.ts
Normal 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
58
src/app/shared/services/claim.service.ts
Normal file
58
src/app/shared/services/claim.service.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
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>> {
|
||||
const params = new HttpParams()
|
||||
.set('page', page.toString())
|
||||
.set('size', size.toString());
|
||||
|
||||
return this.http.get<PageResponse<Claim>>(`${this.apiUrl}/all`, { 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.'));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const environment = {
|
||||
production: true,
|
||||
apiUrl: 'https://insurance.hzwnrw.my/api/insurance'
|
||||
apiUrl: 'https://insurance.hzwnrw.my'
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const environment = {
|
||||
production: false,
|
||||
apiUrl: 'http://localhost:1204/api/insurance'
|
||||
apiUrl: 'http://localhost:1204'
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user