diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 7c6d99f..4b99dc2 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -19,6 +19,7 @@ import { ApplianceRegistrationModule } from './pages/appliance-registration/appl import { AlertConfirmationModule } from './pages/components/alert-confirmation/alert-confirmation.module'; import { ProductModule } from './pages/product/product.module'; import { ModalProductModule } from './pages/components/modal-product/modal-product.module'; +import { PaymentModule } from './pages/payment/payment.module'; export function HttpLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http); @@ -33,6 +34,7 @@ export function HttpLoaderFactory(http: HttpClient) { HomeModule, DashboardModule, ProductModule, + PaymentModule, ReactiveFormsModule, HttpClientModule, TranslateModule.forRoot({ diff --git a/src/app/constants/routes.constants.ts b/src/app/constants/routes.constants.ts index 117de20..e2aa545 100644 --- a/src/app/constants/routes.constants.ts +++ b/src/app/constants/routes.constants.ts @@ -5,6 +5,5 @@ export const RoutesConstants = { dashboard: '/pages/:userId/home', form: '/pages/:userId/form', product: '/pages/:userId/product', - root: '/', + payment: '/pages/:userId/payment/:id', }; - diff --git a/src/app/models/payment.ts b/src/app/models/payment.ts new file mode 100644 index 0000000..af219a2 --- /dev/null +++ b/src/app/models/payment.ts @@ -0,0 +1,11 @@ +export interface Payment { + user_id: number; + full_name: string; + email: string; + telephone_number: string; + card_type: string; + card_number: string; + security_code: string; + amount_payable: number; + product_id: number; +} diff --git a/src/app/pages/components/menu/menu.component.html b/src/app/pages/components/menu/menu.component.html index d6975a7..76f0251 100644 --- a/src/app/pages/components/menu/menu.component.html +++ b/src/app/pages/components/menu/menu.component.html @@ -22,7 +22,6 @@ translate }} {{ 'header.seguimiento_productos' | translate }} - {{ 'header.gestion_pagos' | translate }} @@ -70,4 +69,3 @@ - diff --git a/src/app/pages/components/menu/menu.component.ts b/src/app/pages/components/menu/menu.component.ts index 9bec36d..a979fec 100644 --- a/src/app/pages/components/menu/menu.component.ts +++ b/src/app/pages/components/menu/menu.component.ts @@ -24,6 +24,7 @@ export class MenuComponent { currentLanguage: 'en' | 'es' = 'es'; languages = LanguageConstants; userId: string | null = localStorage.getItem('userId'); + paymentId: string | undefined; constructor(private translationService: TranslationService) { this.avatar = ImageConstants.avatar; @@ -31,7 +32,7 @@ export class MenuComponent { this.userLastName = localStorage.getItem('userLastName'); } - toggleMenu() : void{ + toggleMenu(): void { this.showMenu = !this.showMenu; } @@ -50,8 +51,8 @@ export class MenuComponent { this.router.navigate(['']); } - changeLanguage(language: 'en' | 'es', event: Event) : void{ - event.preventDefault(); + changeLanguage(language: 'en' | 'es', event: Event): void { + event.preventDefault(); this.translationService.setLanguage(language); this.currentLanguage = language; this.closeMenu(); diff --git a/src/app/pages/components/modal-product/modal-product.component.html b/src/app/pages/components/modal-product/modal-product.component.html index dbf1bc6..ae524d3 100644 --- a/src/app/pages/components/modal-product/modal-product.component.html +++ b/src/app/pages/components/modal-product/modal-product.component.html @@ -18,7 +18,8 @@
- +

{{ product.appliance_type }}

@@ -64,7 +65,7 @@

{{ 'us

-
diff --git a/src/app/pages/components/modal-product/modal-product.component.ts b/src/app/pages/components/modal-product/modal-product.component.ts index ab71ce4..63576f8 100644 --- a/src/app/pages/components/modal-product/modal-product.component.ts +++ b/src/app/pages/components/modal-product/modal-product.component.ts @@ -1,14 +1,16 @@ -import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, inject, OnInit, Output } from '@angular/core'; import { Product } from 'src/app/models/product'; import { ProductModalService } from '../../services/product-modal.service'; import { User } from 'src/app/models/user'; +import { Router } from '@angular/router'; @Component({ selector: 'app-modal-product', templateUrl: './modal-product.component.html', - styleUrls: ['./modal-product.component.css'] + styleUrls: ['./modal-product.component.css'], }) export class ModalProductComponent implements OnInit { + private readonly router: Router = inject(Router); @Output() alertClosed = new EventEmitter(); product: Product | null = null; user: User | null = null; @@ -17,13 +19,10 @@ export class ModalProductComponent implements OnInit { constructor(private productModalService: ProductModalService) {} ngOnInit(): void { - this.productModalService.currentProduct.subscribe(product => { + this.productModalService.currentProduct.subscribe((product) => { this.product = product; - }); - this.productModalService.currentUser.subscribe(user => { - this.user = user; - if (this.user && this.product) { - this.isOpenModal = true; + if (this.product) { + localStorage.setItem('currentProduct', JSON.stringify(this.product)); } }); } @@ -33,4 +32,8 @@ export class ModalProductComponent implements OnInit { this.isOpenModal = false; this.productModalService.closeModal(); } + + navigateToPayment(userId: number, productId: number): void { + this.router.navigate([`/pages/${userId}/payment/${productId}`]); + } } diff --git a/src/app/pages/pages-routing.module.ts b/src/app/pages/pages-routing.module.ts index c67a5d0..015b9e2 100644 --- a/src/app/pages/pages-routing.module.ts +++ b/src/app/pages/pages-routing.module.ts @@ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router'; import { HomeComponent } from './home/home.component'; import { ApplianceRegistrationComponent } from './appliance-registration/appliance-registration.component'; import { ProductComponent } from './product/product.component'; +import { PaymentComponent } from './payment/payment.component'; const routes: Routes = [ { @@ -17,7 +18,10 @@ const routes: Routes = [ path: 'product', component: ProductComponent, }, - + { + path: 'payment/:id', + component: PaymentComponent, + }, ]; @NgModule({ diff --git a/src/app/pages/pages.module.ts b/src/app/pages/pages.module.ts index f191f0a..99c342c 100644 --- a/src/app/pages/pages.module.ts +++ b/src/app/pages/pages.module.ts @@ -5,7 +5,6 @@ import { PagesRoutingModule } from './pages-routing.module'; import { FormProductModule } from './components/form-product/form-product.module'; import { TranslateModule } from '@ngx-translate/core'; -import { ModalProductComponent } from './components/modal-product/modal-product.component'; @NgModule({ declarations: [], diff --git a/src/app/pages/payment/payment.component.css b/src/app/pages/payment/payment.component.css index e69de29..9a20654 100644 --- a/src/app/pages/payment/payment.component.css +++ b/src/app/pages/payment/payment.component.css @@ -0,0 +1,81 @@ +body { + background: linear-gradient(to right, rgba(0, 0, 255, 0.5), rgba(255, 255, 0, 0.5)); + margin: 0; + font-family: Arial, sans-serif; +} + + +/* Contenedor de formulario y ticket */ +.flex { + display: flex; + } + + /* Formulario */ + .form-container, .ticket { + padding: 16px; + background: #fff; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + } + + /* Estilos del ticket */ + .ticket { + border: 1px solid #000; + border-radius: 4px; + padding: 16px; + width: 100%; + background-color: #fff; + font-family: 'Courier New', Courier, monospace; + box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); + } + + .ticket-header { + border-bottom: 2px dashed #000; + padding-bottom: 8px; + margin-bottom: 16px; + } + + .ticket-title { + font-size: 18px; + margin: 0; + text-align: center; + } + + .ticket-date { + font-size: 12px; + color: #666; + text-align: center; + } + + .ticket-body { + margin-bottom: 16px; + } + + .ticket-body p { + margin: 4px 0; + } + + .ticket-footer { + text-align: center; + font-size: 12px; + color: #666; + border-top: 2px dashed #000; + padding-top: 8px; + } + + /* Estilo del contenedor del ticket para centrar y dar margen */ +.ticket-container { + display: flex; + justify-content: center; + align-items: center; + padding: 20px; + border-radius: 12px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + background-color: #ffffff; + max-width: 400px; + margin: auto; +} + + + + diff --git a/src/app/pages/payment/payment.component.html b/src/app/pages/payment/payment.component.html index d9cf5ff..b7cd77c 100644 --- a/src/app/pages/payment/payment.component.html +++ b/src/app/pages/payment/payment.component.html @@ -1 +1,155 @@ -

payment works!

+ + +
+
+ +
+
+
+
+
+

{{ 'contact_sales' | translate }} +

+

{{ 'contact_sales_description' | translate }}

+
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+
+ +
+
+
+
+

{{ 'service_ticket_title' | translate }}

+

{{ 'service_ticket_date' | translate }}

+
+
+

{{ 'client' | translate }}: {{ product?.user?.first_name }} + {{ product?.user?.last_name }}

+

{{ 'email_label' | translate }}: {{ product?.user?.email + }}

+

{{ 'phone' | translate }}: {{ product?.user?.phone_number + }}

+

{{ 'collection_address' | translate }}: {{ + product?.collection_address }}

+

{{ 'service_type' | translate }}: {{ product?.service_type + }}

+

{{ 'problem_details' | translate }}: {{ + product?.problem_details }}

+
+
+

{{ 'payment_ticket_title' | translate }}

+

{{ 'payment_ticket_date' | translate }}: {{ currentDate }}

+
+
+

{{ 'payment_form_full_name' | translate }}: {{ + paymentForm.get('full_name')?.value || 'N/A' }}

+

{{ 'payment_form_email' | translate }}: {{ + paymentForm.get('email')?.value || 'N/A' }}

+

{{ 'payment_form_phone' | translate }}: {{ + paymentForm.get('telephone_number')?.value || 'N/A' }}

+

{{ 'payment_form_card' | translate }}: {{ + paymentForm.get('card_number')?.value || 'N/A' }}

+

{{ 'payment_form_amount' | translate }}: {{ + paymentForm.get('amount_payable')?.value || '0.00' }}

+
+
+

{{ 'thank_you' | translate }}

+
+
+
+
+
+
+ +
+
+ diff --git a/src/app/pages/payment/payment.component.spec.ts b/src/app/pages/payment/payment.component.spec.ts deleted file mode 100644 index 3068378..0000000 --- a/src/app/pages/payment/payment.component.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PaymentComponent } from './payment.component'; - -describe('PaymentComponent', () => { - let component: PaymentComponent; - let fixture: ComponentFixture; - - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [PaymentComponent] - }); - fixture = TestBed.createComponent(PaymentComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/pages/payment/payment.component.ts b/src/app/pages/payment/payment.component.ts index 0f3a3af..0a282a7 100644 --- a/src/app/pages/payment/payment.component.ts +++ b/src/app/pages/payment/payment.component.ts @@ -1,10 +1,106 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { PaymentService } from '../services/payment.service'; +import { Payment } from 'src/app/models/payment'; +import { ActivatedRoute } from '@angular/router'; +import { Product } from 'src/app/models/product'; @Component({ selector: 'app-payment', templateUrl: './payment.component.html', - styleUrls: ['./payment.component.css'] + styleUrls: ['./payment.component.css'], }) export class PaymentComponent { + private readonly fb: FormBuilder = inject(FormBuilder); + paymentForm: FormGroup; + userId: number | undefined; + productId: number | undefined; + product: Product | null = null; + currentDate: string = new Date().toLocaleDateString(); + constructor( + private paymentService: PaymentService, + private route: ActivatedRoute + ) { + this.getid(); + this.paymentForm = this.buildForm(); + this.getProductFromLocalStorage(); + + this.paymentForm.valueChanges.subscribe((values) => { + this.updateTicket(); + }); + } + + private buildForm(): FormGroup { + return this.fb.group({ + user_id: [this.userId, Validators.required], + full_name: ['', [Validators.required, Validators.minLength(2)]], + email: ['', [Validators.required, Validators.email]], + telephone_number: [ + '', + [Validators.required, Validators.pattern('^[0-9]{10,15}$')], + ], + card_type: ['', Validators.required], + card_number: [ + '', + [Validators.required, Validators.pattern('^[0-9]{16}$')], + ], + security_code: [ + '', + [Validators.required, Validators.pattern('^[0-9]{3,4}$')], + ], + amount_payable: [ + '', + [Validators.required, Validators.pattern('^[0-9]+(.[0-9]{1,2})?$')], + ], + product_id: [this.productId, Validators.required], + }); + } + + private getProductFromLocalStorage(): void { + const storedProduct = localStorage.getItem('currentProduct'); + if (storedProduct) { + try { + this.product = JSON.parse(storedProduct) as Product; + } catch (error) { + console.error('Error parsing stored product:', error); + } + } + } + + private updateTicket(): void { + this.currentDate = new Date().toLocaleDateString(); + } + + getid() { + return this.route.paramMap.subscribe((params) => { + const userIdParam = params.get('userId'); + const productIdParam = params.get('id'); + + this.userId = userIdParam ? +userIdParam : undefined; + this.productId = productIdParam ? +productIdParam : undefined; + + if (!isNaN(this.userId!) && !isNaN(this.productId!)) { + this.paymentForm.patchValue({ + user_id: this.userId, + product_id: this.productId, + }); + } + }); + } + + onSubmit(): void { + if (this.paymentForm.valid) { + const paymentData: Payment = this.paymentForm.value; + this.paymentService.registerPayment(paymentData).subscribe((response) => { + if (response) { + alert('Pago registrado correctamente'); + } else { + alert('Error al registrar el pago'); + } + }); + } else { + alert('Por favor, completa todos los campos correctamente.'); + } + } } diff --git a/src/app/pages/payment/payment.module.ts b/src/app/pages/payment/payment.module.ts index e5966a0..452e2bb 100644 --- a/src/app/pages/payment/payment.module.ts +++ b/src/app/pages/payment/payment.module.ts @@ -2,13 +2,24 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { PaymentRoutingModule } from './payment-routing.module'; +import { PaymentComponent } from './payment.component'; +import { MenuModule } from '../components/menu/menu.module'; +import { FooterModule } from '../components/footer/footer.module'; +import { TranslateModule } from '@ngx-translate/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { HttpClientModule } from '@angular/common/http'; @NgModule({ - declarations: [], + declarations: [PaymentComponent], imports: [ CommonModule, - PaymentRoutingModule + PaymentRoutingModule, + MenuModule, + FooterModule, + TranslateModule, + ReactiveFormsModule, + HttpClientModule ] }) export class PaymentModule { } diff --git a/src/app/pages/services/payment.service.ts b/src/app/pages/services/payment.service.ts new file mode 100644 index 0000000..0a0dd32 --- /dev/null +++ b/src/app/pages/services/payment.service.ts @@ -0,0 +1,18 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { Payment } from 'src/app/models/payment'; +import { environment } from 'src/environments/environment'; + +@Injectable({ + providedIn: 'root', +}) +export class PaymentService { + private apiUrl = `${environment.apiUrl}/payments`; + + constructor(private http: HttpClient) {} + + registerPayment(cardDetails: Payment): Observable { + return this.http.post(this.apiUrl, cardDetails); + } +} diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 56741ed..212b7ec 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -234,9 +234,49 @@ "PAY_NOW": "Pay now", "damagedApplianceImageAlt": "Image of the damaged appliance", "applianceTypeAlt": "Type of appliance", - "INVALID_APPLICATION_DATE": "Invalid application date.", + "INVALID_APPLICATION_DATE": "Invalid application date.", "INVALID_SERVICE_TYPE": "Invalid service type.", "SERVICE_COMPLETED": "The repair/maintenance is complete.", "TIMER_FORMAT": "{{days}}d {{hours}}h {{minutes}}m {{seconds}}s", - "DATE_INVALID": "Invalid application date." + "DATE_INVALID": "Invalid application date.", + "contact_sales": "Make a Payment", + "contact_sales_description": "Complete your payment details below to finalize your purchase.", + "card_number": "Card Number", + "card_number_placeholder": "Enter card number", + "card_type": "Card Type", + "card_type_placeholder": "Enter card type", + "security_code": "Security Code (CVV)", + "security_code_placeholder": "CVV", + "full_name": "Full Name", + "full_name_placeholder": "Enter your full name", + "email_placeholder": "Enter your email", + "telephone_number": "Phone Number", + "telephone_number_placeholder": "Enter your phone number", + "amount_payable": "Amount Payable", + "amount_payable_placeholder": "Enter amount", + "privacy_policy": "By selecting this, you agree to our", + "privacy_policy_link": "privacy policy", + "submit_button": "Make Payment", + "service_ticket_title": "Service Ticket - {{appliance_type}}", + "service_ticket_date": "Date: {{application_date}}", + "client": "Client", + "email_label": "Email", + "collection_address": "Collection Address", + "service_type": "Service Type", + "problem_details": "Problem Details", + "damaged_appliance_image": "Damaged Appliance Image", + "payment_ticket_title": "Payment Ticket", + "payment_ticket_date": "Date: {{currentDate}}", + "payment_form_full_name": "Full Name", + "payment_form_email": "Email", + "payment_form_phone": "Phone", + "payment_form_card": "Card", + "payment_form_amount": "Amount to Pay", + "thank_you": "Thank you for your purchase.", + "CARD_TYPE": "Tipo de tarjeta", + "SELECT_CARD_TYPE": "Seleccione el tipo de tarjeta", + "VISA": "Visa", + "MASTERCARD": "MasterCard", + "AMERICAN_EXPRESS": "American Express", + "DISCOVER": "Discover" } diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index fa0a758..799cca0 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -232,12 +232,51 @@ "email": "Correo Electrónico", "phone": "Teléfono", "address": "Dirección", - "PAY_NOW": "Pagar Ahora", + "PAY_NOW": "Pagar Ahora", "damagedApplianceImageAlt": "Imagen del electrodoméstico dañado", "applianceTypeAlt": "Tipo de electrodoméstico", - "INVALID_APPLICATION_DATE": "Fecha de aplicación no válida.", + "INVALID_APPLICATION_DATE": "Fecha de aplicación no válida.", "INVALID_SERVICE_TYPE": "Tipo de servicio no válido.", "SERVICE_COMPLETED": "La reparación/mantenimiento está completo.", "TIMER_FORMAT": "{{days}}d {{hours}}h {{minutes}}m {{seconds}}s", - "DATE_INVALID": "Fecha de aplicación no válida." + "DATE_INVALID": "Fecha de aplicación no válida.", + "contact_sales": "Realizar Pago", + "contact_sales_description": "Complete los detalles de pago a continuación para finalizar su compra.", + "card_number": "Número de tarjeta", + "card_number_placeholder": "Ingrese el número de tarjeta", + "CARD_TYPE": "Tipo de tarjeta", + "SELECT_CARD_TYPE": "Seleccione el tipo de tarjeta", + "VISA": "Visa", + "MASTERCARD": "MasterCard", + "AMERICAN_EXPRESS": "American Express", + "DISCOVER": "Discover", + "card_type_placeholder": "Ingrese el tipo de tarjeta", + "security_code": "Código de seguridad (CVV)", + "security_code_placeholder": "CVV", + "full_name": "Nombre completo", + "full_name_placeholder": "Ingrese su nombre completo", + "email_placeholder": "Ingrese su email", + "telephone_number": "Número de teléfono", + "telephone_number_placeholder": "Ingrese su número de teléfono", + "amount_payable": "Monto a pagar", + "amount_payable_placeholder": "Ingrese el monto", + "privacy_policy": "Al seleccionar esto, aceptas nuestra", + "privacy_policy_link": "política de privacidad", + "submit_button": "Realizar Pago", + "service_ticket_title": "Ticket de Servicio - {{appliance_type}}", + "service_ticket_date": "Fecha: {{application_date}}", + "client": "Cliente", + "email_label": "Email", + "collection_address": "Dirección de Recolección", + "service_type": "Tipo de Servicio", + "problem_details": "Detalles del Problema", + "damaged_appliance_image": "Imagen del Electrodoméstico Dañado", + "payment_ticket_title": "Ticket de Pago", + "payment_ticket_date": "Fecha", + "payment_form_full_name": "Nombre Completo", + "payment_form_email": "Email", + "payment_form_phone": "Teléfono", + "payment_form_card": "Tarjeta", + "payment_form_amount": "Monto a Pagar", + "thank_you": "Gracias por su compra." }