| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- import { Component, OnInit, OnDestroy } from '@angular/core';
- import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
- import { CommonModule } from '@angular/common';
- import { Router, RouterModule } from '@angular/router';
- import { ExtractionService } from '../../services/extraction.service';
- import { SessionService, User } from '../../services/session.service';
- import { ExtractionResponse, ClaimSubmission } from '../../services/extraction';
- import { LucideAngularModule, ShieldCheck, AlertCircle, CheckCircle, ArrowLeft, Eye, EyeOff, Wand2 } from 'lucide-angular';
- import { Subscription } from 'rxjs';
- @Component({
- selector: 'app-claim-form',
- standalone: true,
- imports: [CommonModule, ReactiveFormsModule, LucideAngularModule, RouterModule],
- templateUrl: './claim-form.component.html',
- styleUrls: ['./claim-form.component.css']
- })
- export class ClaimFormComponent implements OnInit, OnDestroy {
- claimForm: FormGroup;
- currentUser: User | null = null;
- previewUrl: string | null = null;
- selectedFile: File | null = null;
- extractionResponse: ExtractionResponse | null = null;
- isLoading = false;
- isSubmitting = false;
- isDragging = false;
- showRawJson = false;
- private amountSpentSub: Subscription | null = null;
- readonly ShieldCheck = ShieldCheck;
- readonly AlertCircle = AlertCircle;
- readonly CheckCircle = CheckCircle;
- readonly ArrowLeft = ArrowLeft;
- readonly Eye = Eye;
- readonly EyeOff = EyeOff;
- readonly Wand2 = Wand2;
- errorMessage: string | null = null;
- constructor(
- private fb: FormBuilder,
- private extractionService: ExtractionService,
- private sessionService: SessionService,
- private router: Router
- ) {
- this.claimForm = this.fb.group({
- provider_name: ['', Validators.required],
- visit_date: ['', Validators.required],
- amount_spent: ['', [Validators.required, Validators.min(0.01)]],
- amount_claimed: [{ value: 0, disabled: true }],
- currency: ['MYR']
- });
- }
- ngOnInit(): void {
- this.currentUser = this.sessionService.getCurrentUser();
- if (!this.currentUser) {
- this.router.navigate(['/']);
- return;
- }
- this.amountSpentSub = this.claimForm.get('amount_spent')?.valueChanges.subscribe(() => {
- this.calculateClaimable();
- }) || null;
- }
- ngOnDestroy(): void {
- if (this.amountSpentSub) {
- this.amountSpentSub.unsubscribe();
- }
- }
- calculateClaimable(): void {
- if (!this.currentUser) return;
-
- const spent = Number(this.claimForm.get('amount_spent')?.value) || 0;
- const remaining = this.currentUser.medical_allowance;
- const claimable = Math.min(spent, remaining);
-
- this.claimForm.patchValue({ amount_claimed: claimable }, { emitEvent: false });
- }
- onDragOver(event: DragEvent): void {
- event.preventDefault();
- event.stopPropagation();
- this.isDragging = true;
- }
- onDragLeave(event: DragEvent): void {
- event.preventDefault();
- event.stopPropagation();
- this.isDragging = false;
- }
- onDrop(event: DragEvent): void {
- event.preventDefault();
- event.stopPropagation();
- this.isDragging = false;
-
- const files = event.dataTransfer?.files;
- if (files && files.length > 0) {
- this.handleFileSelection(files[0]);
- }
- }
- onFileSelected(event: any): void {
- const file: File = event.target.files[0];
- if (file) {
- this.handleFileSelection(file);
- }
- }
- private handleFileSelection(file: File): void {
- if (!this.currentUser) return;
- this.selectedFile = file;
- this.previewUrl = URL.createObjectURL(file);
- this.extractionResponse = null; // reset if existing
- this.errorMessage = null;
- }
- autoFillWithAI(): void {
- if (!this.currentUser || !this.selectedFile) return;
- this.isLoading = true;
- this.errorMessage = null;
-
- this.extractionService.extractData(this.selectedFile, this.currentUser.name, this.currentUser.department).subscribe({
- next: (response) => {
- this.extractionResponse = response;
- this.claimForm.patchValue({
- provider_name: response.provider_name,
- visit_date: response.visit_date,
- amount_spent: response.total_amount,
- currency: response.currency || 'MYR'
- });
- // The valueChanges subscription will trigger calculateClaimable automatically
- this.isLoading = false;
- },
- error: (err: Error) => {
- console.error('Extraction failed', err);
- this.errorMessage = err.message;
- this.isLoading = false;
- }
- });
- }
- onSubmit(): void {
- if (this.claimForm.invalid || !this.currentUser) return;
- this.isSubmitting = true;
- const formData = this.claimForm.getRawValue();
-
- const submissionData: ClaimSubmission = {
- provider_name: formData.provider_name,
- visit_date: formData.visit_date,
- amount_spent: Number(formData.amount_spent),
- currency: formData.currency,
- extraction_data: this.extractionResponse
- };
- this.extractionService.submitClaim(submissionData, this.currentUser.id).subscribe({
- next: () => {
- this.isSubmitting = false;
- this.router.navigate(['/']);
- },
- error: (err) => {
- console.error('Submission failed', err);
- this.errorMessage = 'Failed to submit claim. Please try again.';
- this.isSubmitting = false;
- }
- });
- }
- get rawJson(): string {
- return JSON.stringify(this.extractionResponse, null, 2);
- }
- }
|