shipping and payment updates

This commit is contained in:
Cyril Joseph 2025-07-14 09:59:26 -03:00
parent 5185cb375d
commit d1bc0eb45a
33 changed files with 957 additions and 179 deletions

View File

@ -36,33 +36,11 @@
</app-travel-plan> </app-travel-plan>
</mat-step> </mat-step>
<!-- Insurance Step --> <!-- Shipping & Payment Step -->
<mat-step *ngIf="applicationType === 'new' || applicationType === 'extend' || applicationType === 'additional'"
[completed]="stepsCompleted.insurance" [editable]="stepsCompleted.applicationDetail">
<ng-template matStepLabel>Insurance</ng-template>
<app-insurance (completed)="onInsuranceSaved($event)" [headerid]="headerid">
</app-insurance>
</mat-step>
<!-- Shipping Step -->
<mat-step [completed]="stepsCompleted.shipping" [editable]="stepsCompleted.applicationDetail"> <mat-step [completed]="stepsCompleted.shipping" [editable]="stepsCompleted.applicationDetail">
<ng-template matStepLabel>Shipping</ng-template> <ng-template matStepLabel>Shipping & Payment</ng-template>
<app-shipping (completed)="onShippingSaved($event)" [headerid]="headerid"> <app-shipping (completed)="onShippingSaved($event)" [headerid]="headerid">
</app-shipping> </app-shipping>
</mat-step> </mat-step>
<!-- Delivery Method Step -->
<mat-step [completed]="stepsCompleted.deliveryMethod" [editable]="stepsCompleted.applicationDetail">
<ng-template matStepLabel>Delivery Method</ng-template>
<app-delivery (completed)="onDeliveryMethodSaved($event)" [headerid]="headerid">
</app-delivery>
</mat-step>
<!-- Payment Step -->
<mat-step [completed]="stepsCompleted.payment" [editable]="stepsCompleted.applicationDetail">
<ng-template matStepLabel>Payment</ng-template>
<app-payment (completed)="onPaymentSaved($event)" [headerid]="headerid">
</app-payment>
</mat-step>
</mat-stepper> </mat-stepper>
</div> </div>

View File

@ -7,18 +7,15 @@ import { ApplicationComponent } from "../application/application.component";
import { HolderComponent } from '../holder/holder.component'; import { HolderComponent } from '../holder/holder.component';
import { GoodsComponent } from '../goods/goods.component'; import { GoodsComponent } from '../goods/goods.component';
import { TravelPlanComponent } from '../travel-plan/travel-plan.component'; import { TravelPlanComponent } from '../travel-plan/travel-plan.component';
import { InsuranceComponent } from '../insurance/insurance.component';
import { PaymentComponent } from '../payment/payment.component';
import { ShippingComponent } from '../shipping/shipping.component'; import { ShippingComponent } from '../shipping/shipping.component';
import { DeliveryComponent } from '../delivery/delivery.component';
import { UserPreferencesService } from '../../core/services/user-preference.service'; import { UserPreferencesService } from '../../core/services/user-preference.service';
import { UserPreferences } from '../../core/models/user-preference'; import { UserPreferences } from '../../core/models/user-preference';
@Component({ @Component({
selector: 'app-add-carnet', selector: 'app-add-carnet',
imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule, imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule,
ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, InsuranceComponent, ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent,
ShippingComponent, DeliveryComponent, PaymentComponent], ShippingComponent],
templateUrl: './add-carnet.component.html', templateUrl: './add-carnet.component.html',
styleUrl: './add-carnet.component.scss' styleUrl: './add-carnet.component.scss'
}) })
@ -36,10 +33,7 @@ export class AddCarnetComponent {
holderSelection: false, holderSelection: false,
goodsSection: false, goodsSection: false,
travelPlan: false, travelPlan: false,
insurance: false, shipping: false
shipping: false,
deliveryMethod: false,
payment: false
}; };
constructor(userPrefenceService: UserPreferencesService) { constructor(userPrefenceService: UserPreferencesService) {
@ -51,7 +45,7 @@ export class AddCarnetComponent {
} }
onApplicationDetailCreated(headerid: number): void { onApplicationDetailCreated(headerid: number): void {
this.headerid = this.headerid; this.headerid = headerid;
this.stepsCompleted.applicationDetail = true; this.stepsCompleted.applicationDetail = true;
this.isLinear = false; // Disable linear mode after application detail is created this.isLinear = false; // Disable linear mode after application detail is created
} }
@ -68,19 +62,7 @@ export class AddCarnetComponent {
this.stepsCompleted.travelPlan = completed; this.stepsCompleted.travelPlan = completed;
} }
onInsuranceSaved(completed: boolean): void {
this.stepsCompleted.insurance = completed;
}
onShippingSaved(completed: boolean): void { onShippingSaved(completed: boolean): void {
this.stepsCompleted.shipping = completed; this.stepsCompleted.shipping = completed;
} }
onDeliveryMethodSaved(completed: boolean): void {
this.stepsCompleted.deliveryMethod = completed;
}
onPaymentSaved(completed: boolean): void {
this.stepsCompleted.payment = completed;
}
} }

View File

@ -102,7 +102,7 @@ export class ApplicationComponent {
this.applicationDetailService.createApplicationDetails(applicationDetailData).subscribe({ this.applicationDetailService.createApplicationDetails(applicationDetailData).subscribe({
next: (applicationData: any) => { next: (applicationData: any) => {
this.notificationService.showSuccess(`Application details added successfully`); this.notificationService.showSuccess(`Application details added successfully`);
this.headerIdCreated.emit(applicationData.HEADERID); this.headerIdCreated.emit(+applicationData.HEADERID);
this.applicationDetailsForm.get('name')?.disable(); this.applicationDetailsForm.get('name')?.disable();
this.disableSaveButton = true; this.disableSaveButton = true;
this.isLoading = false; this.isLoading = false;

View File

@ -1,13 +0,0 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-delivery',
imports: [],
templateUrl: './delivery.component.html',
styleUrl: './delivery.component.scss'
})
export class DeliveryComponent {
@Input() headerid: number = 0;
@Output() completed = new EventEmitter<boolean>();
}

View File

@ -23,7 +23,7 @@
<mat-step *ngIf="applicationType === 'new'" [completed]="stepsCompleted.goodsSection" <mat-step *ngIf="applicationType === 'new'" [completed]="stepsCompleted.goodsSection"
[editable]="stepsCompleted.applicationDetail"> [editable]="stepsCompleted.applicationDetail">
<ng-template matStepLabel>Goods Section</ng-template> <ng-template matStepLabel>Goods Section</ng-template>
<app-goods (completed)="onGoodsSectionSaved($event)" [headerid]="headerid" <app-goods (completed)="onGoodsSectionSaved($event)" [headerid]="headerid" [isEditMode]="isEditMode"
[userPreferences]="userPreferences"> [userPreferences]="userPreferences">
</app-goods> </app-goods>
</mat-step> </mat-step>
@ -36,33 +36,11 @@
</app-travel-plan> </app-travel-plan>
</mat-step> </mat-step>
<!-- Insurance Step --> <!-- Shipping & Payment Step -->
<mat-step *ngIf="applicationType === 'new' || applicationType === 'extend' || applicationType === 'additional'"
[completed]="stepsCompleted.insurance" [editable]="stepsCompleted.applicationDetail">
<ng-template matStepLabel>Insurance</ng-template>
<app-insurance (completed)="onInsuranceSaved($event)" [headerid]="headerid">
</app-insurance>
</mat-step>
<!-- Shipping Step -->
<mat-step [completed]="stepsCompleted.shipping" [editable]="stepsCompleted.applicationDetail"> <mat-step [completed]="stepsCompleted.shipping" [editable]="stepsCompleted.applicationDetail">
<ng-template matStepLabel>Shipping</ng-template> <ng-template matStepLabel>Shipping & Payment</ng-template>
<app-shipping (completed)="onShippingSaved($event)" [headerid]="headerid"> <app-shipping (completed)="onShippingSaved($event)" [headerid]="headerid">
</app-shipping> </app-shipping>
</mat-step> </mat-step>
<!-- Delivery Method Step -->
<mat-step [completed]="stepsCompleted.deliveryMethod" [editable]="stepsCompleted.applicationDetail">
<ng-template matStepLabel>Delivery Method</ng-template>
<app-delivery (completed)="onDeliveryMethodSaved($event)" [headerid]="headerid">
</app-delivery>
</mat-step>
<!-- Payment Step -->
<mat-step [completed]="stepsCompleted.payment" [editable]="stepsCompleted.applicationDetail">
<ng-template matStepLabel>Payment</ng-template>
<app-payment (completed)="onPaymentSaved($event)" [headerid]="headerid">
</app-payment>
</mat-step>
</mat-stepper> </mat-stepper>
</div> </div>

View File

@ -7,9 +7,6 @@ import { StepperSelectionEvent } from '@angular/cdk/stepper';
import { GoodsComponent } from '../goods/goods.component'; import { GoodsComponent } from '../goods/goods.component';
import { HolderComponent } from '../holder/holder.component'; import { HolderComponent } from '../holder/holder.component';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { DeliveryComponent } from '../delivery/delivery.component';
import { InsuranceComponent } from '../insurance/insurance.component';
import { PaymentComponent } from '../payment/payment.component';
import { ShippingComponent } from '../shipping/shipping.component'; import { ShippingComponent } from '../shipping/shipping.component';
import { TravelPlanComponent } from '../travel-plan/travel-plan.component'; import { TravelPlanComponent } from '../travel-plan/travel-plan.component';
import { UserPreferences } from '../../core/models/user-preference'; import { UserPreferences } from '../../core/models/user-preference';
@ -18,8 +15,7 @@ import { UserPreferencesService } from '../../core/services/user-preference.serv
@Component({ @Component({
selector: 'app-edit-carnet', selector: 'app-edit-carnet',
imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule, imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule,
ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, InsuranceComponent, ApplicationComponent, HolderComponent, GoodsComponent, TravelPlanComponent, ShippingComponent],
ShippingComponent, DeliveryComponent, PaymentComponent],
templateUrl: './edit-carnet.component.html', templateUrl: './edit-carnet.component.html',
styleUrl: './edit-carnet.component.scss' styleUrl: './edit-carnet.component.scss'
}) })
@ -37,10 +33,7 @@ export class EditCarnetComponent {
holderSelection: false, holderSelection: false,
goodsSection: false, goodsSection: false,
travelPlan: false, travelPlan: false,
insurance: false, shipping: false
shipping: false,
deliveryMethod: false,
payment: false
}; };
private route = inject(ActivatedRoute); private route = inject(ActivatedRoute);
@ -70,19 +63,7 @@ export class EditCarnetComponent {
this.stepsCompleted.travelPlan = completed; this.stepsCompleted.travelPlan = completed;
} }
onInsuranceSaved(completed: boolean): void {
this.stepsCompleted.insurance = completed;
}
onShippingSaved(completed: boolean): void { onShippingSaved(completed: boolean): void {
this.stepsCompleted.shipping = completed; this.stepsCompleted.shipping = completed;
} }
onDeliveryMethodSaved(completed: boolean): void {
this.stepsCompleted.deliveryMethod = completed;
}
onPaymentSaved(completed: boolean): void {
this.stepsCompleted.payment = completed;
}
} }

View File

@ -89,7 +89,7 @@
<!-- Value Column --> <!-- Value Column -->
<ng-container matColumnDef="value"> <ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Value</th> <th mat-header-cell *matHeaderCellDef mat-sort-header>Value</th>
<td mat-cell *matCellDef="let item">{{ formatDecimalDisplay(item.value, 2) }}</td> <td mat-cell *matCellDef="let item">{{ formatDecimalDisplay(item.value, 2) | currency}}</td>
</ng-container> </ng-container>
<!-- Country of Origin Column --> <!-- Country of Origin Column -->

View File

@ -29,6 +29,7 @@ import { Goods, GoodsItem } from '../../core/models/carnet/goods';
export class GoodsComponent { export class GoodsComponent {
@Input() headerid: number = 0; @Input() headerid: number = 0;
@Input() userPreferences: UserPreferences = {}; @Input() userPreferences: UserPreferences = {};
@Input() isEditMode: boolean = false;
@Output() completed = new EventEmitter<boolean>(); @Output() completed = new EventEmitter<boolean>();
@ViewChild(MatPaginator) paginator!: MatPaginator; @ViewChild(MatPaginator) paginator!: MatPaginator;
@ -85,10 +86,6 @@ export class GoodsComponent {
value: [0, [Validators.required, Validators.min(1), Validators.pattern(/^\d+(\.\d{1,2})?$/)]], value: [0, [Validators.required, Validators.min(1), Validators.pattern(/^\d+(\.\d{1,2})?$/)]],
countryOfOrigin: ['', Validators.required] countryOfOrigin: ['', Validators.required]
}); });
this.goodsForm.valueChanges.subscribe(() => {
this.completed.emit(this.goodsForm.valid && this.dataSource.data.length > 0);
});
} }
ngAfterViewInit() { ngAfterViewInit() {
@ -117,6 +114,7 @@ export class GoodsComponent {
}); });
this.loadGoodsItems(); this.loadGoodsItems();
this.completed.emit(this.goodsForm.valid && this.dataSource.data.length > 0);
} }
} }
@ -314,10 +312,10 @@ export class GoodsComponent {
return; return;
} }
items.map(item => { // items.map(item => {
item.countryOfOrigin = this.getCountryValue(item.countryOfOrigin); // item.countryOfOrigin = this.getCountryValue(item.countryOfOrigin);
item.unitOfMeasure = this.getUnitOfMeasureValue(item.unitOfMeasure); // item.unitOfMeasure = this.getUnitOfMeasureValue(item.unitOfMeasure);
}); // });
this.goodsService.addGoodsItem(this.headerid, items).subscribe({ this.goodsService.addGoodsItem(this.headerid, items).subscribe({
next: () => { next: () => {
@ -395,7 +393,7 @@ export class GoodsComponent {
this.goodsService.saveGoodsData(this.headerid, formData).subscribe({ this.goodsService.saveGoodsData(this.headerid, formData).subscribe({
next: () => { next: () => {
this.notificationService.showSuccess('Goods information saved successfully'); this.notificationService.showSuccess('Goods information saved successfully');
this.completed.emit(true); this.completed.emit(this.goodsForm.valid && this.dataSource.data.length > 0);
}, },
error: (error) => { error: (error) => {
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to save goods information'); let errorMessage = this.errorHandler.handleApiError(error, 'Failed to save goods information');

View File

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

View File

@ -1,12 +0,0 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-insurance',
imports: [],
templateUrl: './insurance.component.html',
styleUrl: './insurance.component.scss'
})
export class InsuranceComponent {
@Input() headerid: number = 0;
@Output() completed = new EventEmitter<boolean>();
}

View File

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

View File

@ -1,12 +0,0 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-payment',
imports: [],
templateUrl: './payment.component.html',
styleUrl: './payment.component.scss'
})
export class PaymentComponent {
@Input() headerid: number = 0;
@Output() completed = new EventEmitter<boolean>();
}

View File

@ -1 +1,302 @@
<p>shipping works!</p> <div class="shipping-container">
<form [formGroup]="shippingForm" (ngSubmit)="onSubmit()">
<!-- Insurance Section -->
<div class="section">
<h3>Insurance & Bond</h3>
<div class="checkbox-group">
<mat-checkbox labelPosition="before" formControlName="needsBond">Do you need Bond from
us?</mat-checkbox>
<mat-checkbox labelPosition="before" formControlName="needsInsurance">Do you need insurance for your
goods?</mat-checkbox>
<mat-checkbox labelPosition="before" formControlName="needsLostDocProtection">Do you need Lost document
protection?</mat-checkbox>
</div>
</div>
<!-- Shipping Section -->
<div class="section">
<h3>Ship To</h3>
<mat-radio-group formControlName="shipTo" class="radio-group" (change)="onShipToChange($event)">
<mat-radio-button value="preparer">Preparer</mat-radio-button>
<mat-radio-button value="holder">Holder</mat-radio-button>
<mat-radio-button value="thirdParty">3rd Party</mat-radio-button>
</mat-radio-group>
<mat-card appearance="outlined" *ngIf="!showAddressForm && !showContactForm">
<mat-card-content>
<p>{{getAddressLabel()}}</p>
</mat-card-content>
<mat-card-actions>
<button mat-button (click)="editAddressForm()">Edit</button>
</mat-card-actions>
</mat-card>
<div *ngIf="showAddressForm" class="address-form" formGroupName="address">
<h4>Shipping Address</h4>
<!-- Address Information -->
<div class="form-row">
<mat-form-field appearance="outline" class="address1">
<mat-label>Address Line 1</mat-label>
<input matInput formControlName="address1" required>
<mat-error *ngIf="shippingForm.get('address.address1')?.errors?.['required']">
Address is required
</mat-error>
<mat-error *ngIf="shippingForm.get('address.address1')?.errors?.['maxlength']">
Maximum 100 characters allowed
</mat-error>
</mat-form-field>
</div>
<div class="form-row">
<mat-form-field appearance="outline" class="address2">
<mat-label>Address Line 2 (Optional)</mat-label>
<input matInput formControlName="address2">
<mat-error *ngIf="shippingForm.get('address.address2')?.errors?.['maxlength']">
Maximum 100 characters allowed
</mat-error>
</mat-form-field>
</div>
<!-- Location Information -->
<div class="form-row">
<mat-form-field appearance="outline" class="city">
<mat-label>City</mat-label>
<input matInput formControlName="city" required>
<mat-error *ngIf="shippingForm.get('address.city')?.errors?.['required']">
City is required
</mat-error>
<mat-error *ngIf="shippingForm.get('address.city')?.errors?.['maxlength']">
Maximum 50 characters allowed
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline" class="country">
<mat-label>Country</mat-label>
<mat-select formControlName="country" required
(selectionChange)="onCountryChange($event.value)">
<mat-option *ngFor="let country of countries" [value]="country.value">
{{ country.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="shippingForm.get('address.country')?.errors?.['required']">
Country is required
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline" class="state">
<mat-label>State/Province</mat-label>
<mat-select formControlName="state" required>
<mat-option *ngFor="let state of states" [value]="state.value">
{{ state.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="shippingForm.get('address.state')?.errors?.['required']">
State is required
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline" class="zip">
<mat-label>ZIP/Postal Code</mat-label>
<input matInput formControlName="zip" required>
<mat-error *ngIf="shippingForm.get('address.zip')?.errors?.['required']">
ZIP/Postal code is required
</mat-error>
<mat-error
*ngIf="shippingForm.get('address.country')?.value === 'US' && shippingForm.get('address.zip')?.touched && shippingForm.get('address.zip')?.errors?.['invalidUSZip']">
Please enter a valid 5-digit US ZIP code
</mat-error>
<mat-error
*ngIf="shippingForm.get('address.country')?.value === 'CA' && shippingForm.get('address.zip')?.touched && shippingForm.get('address.zip')?.errors?.['invalidCanadaPostal']">
Please enter a valid postal code (e.g., A1B2C3)
</mat-error>
</mat-form-field>
</div>
<div class="form-actions" *ngIf="showAddressForm && shippingForm.get('shipTo')?.value !== 'thirdParty'">
<button mat-button type="button" (click)="cancelEditAddressForm()">Cancel</button>
</div>
</div>
<div *ngIf="showContactForm" class="contact-form" formGroupName="contact">
<h4>Contact Information</h4>
<div class="form-row">
<mat-form-field appearance="outline">
<mat-label>First Name</mat-label>
<input matInput formControlName="firstName" required>
<mat-icon matSuffix>person</mat-icon>
<mat-error *ngIf="shippingForm.get('contact.firstName')?.errors?.['required']">
First name is required
</mat-error>
<mat-error *ngIf="shippingForm.get('contact.firstName')?.errors?.['maxlength']">
Maximum 50 characters allowed
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline" class="small-field">
<mat-label>Middle Initial</mat-label>
<input matInput formControlName="middleInitial" maxlength="1">
<mat-error *ngIf="shippingForm.get('contact.middleInitial')?.errors?.['maxlength']">
Only 1 character allowed
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Last Name</mat-label>
<input matInput formControlName="lastName" required>
<mat-icon matSuffix>person</mat-icon>
<mat-error *ngIf="shippingForm.get('contact.lastName')?.errors?.['required']">
Last name is required
</mat-error>
<mat-error *ngIf="shippingForm.get('contact.lastName')?.errors?.['maxlength']">
Maximum 50 characters allowed
</mat-error>
</mat-form-field>
</div>
<div class="form-row">
<mat-form-field appearance="outline">
<mat-label>Title</mat-label>
<input matInput formControlName="title" required>
<mat-icon matSuffix>work</mat-icon>
<mat-error *ngIf="shippingForm.get('contact.title')?.errors?.['required']">
Title is required
</mat-error>
<mat-error *ngIf="shippingForm.get('contact.title')?.errors?.['maxlength']">
Maximum 100 characters allowed
</mat-error>
</mat-form-field>
</div>
<div class="form-row">
<mat-form-field appearance="outline">
<mat-label>Phone</mat-label>
<input matInput formControlName="phone" required>
<mat-icon matSuffix>phone</mat-icon>
<mat-error *ngIf="shippingForm.get('contact.phone')?.errors?.['required']">
Phone is required
</mat-error>
<mat-error *ngIf="shippingForm.get('contact.phone')?.errors?.['pattern']">
Please enter a valid phone number (10-15 digits)
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Mobile</mat-label>
<input matInput formControlName="mobile">
<mat-icon matSuffix>smartphone</mat-icon>
<mat-error *ngIf="shippingForm.get('contact.mobile')?.errors?.['required']">
Mobile is required
</mat-error>
<mat-error *ngIf="shippingForm.get('contact.mobile')?.errors?.['pattern']">
Please enter a valid mobile number (10-15 digits)
</mat-error>
</mat-form-field>
</div>
<div class="form-row">
<mat-form-field appearance="outline">
<mat-label>Fax</mat-label>
<input matInput formControlName="fax">
<mat-icon matSuffix>fax</mat-icon>
<mat-error *ngIf="shippingForm.get('contact.fax')?.errors?.['pattern']">
Please enter a valid fax number (10-15 digits)
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Email</mat-label>
<input matInput formControlName="email" required>
<mat-icon matSuffix>email</mat-icon>
<mat-error *ngIf="shippingForm.get('contact.email')?.errors?.['required']">
Email is required
</mat-error>
<mat-error *ngIf="shippingForm.get('contact.email')?.errors?.['email']">
Please enter a valid email address
</mat-error>
<mat-error *ngIf="shippingForm.get('contact.email')?.errors?.['maxlength']">
Maximum 100 characters allowed
</mat-error>
</mat-form-field>
</div>
<div class="form-row">
<mat-form-field appearance="outline" class="full-width">
<mat-label>Reference Number</mat-label>
<input matInput formControlName="refNumber">
</mat-form-field>
</div>
<div class="form-row">
<mat-form-field appearance="outline" class="full-width">
<mat-label>Notes</mat-label>
<textarea matInput formControlName="notes" rows="2"></textarea>
</mat-form-field>
</div>
</div>
</div>
<!-- Delivery Section -->
<div class="section">
<h3>Delivery Method</h3>
<div class="form-row">
<mat-form-field appearance="outline">
<mat-label>Delivery Type</mat-label>
<mat-select formControlName="deliveryType" required>
<mat-option *ngFor="let deliveryType of deliveryTypes" [value]="deliveryType.value">
{{ deliveryType.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="shippingForm.get('deliveryType')?.errors?.['required']">
Delivery Type is required
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Delivery Method</mat-label>
<mat-select formControlName="deliveryMethod" required>
<mat-option *ngFor="let deliveryMethod of deliveryMethods" [value]="deliveryMethod.value">
{{ deliveryMethod.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="shippingForm.get('deliveryMethod')?.errors?.['required']">
Delivery Method is required
</mat-error>
</mat-form-field>
</div>
<div *ngIf="shippingForm.get('deliveryMethod')?.value === 'customerCourier'" class="form-row">
<mat-form-field appearance="outline" class="full-width">
<mat-label>Courier Account Number</mat-label>
<input matInput formControlName="courierAccount">
<mat-error>Required when using customer courier</mat-error>
</mat-form-field>
</div>
</div>
<!-- Payment Section -->
<div class="section">
<h3>Payment Method</h3>
<div class="form-row">
<mat-form-field appearance="outline">
<mat-label>Payment Method</mat-label>
<mat-select formControlName="paymentMethod" required>
<mat-option *ngFor="let paymentType of paymentTypes" [value]="paymentType.value">
{{ paymentType.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="shippingForm.get('paymentMethod')?.errors?.['required']">
Payment Method is required
</mat-error>
</mat-form-field>
</div>
<!-- <div class="form-row">
<mat-form-field appearance="outline" class="full-width">
<mat-label>Payment Notes</mat-label>
<textarea matInput formControlName="notes" rows="2"></textarea>
</mat-form-field>
</div> -->
</div>
<div class="form-actions">
<button mat-raised-button color="primary" type="submit" [disabled]="shippingForm.invalid || isLoading">
<span *ngIf="!isLoading">Save</span>
<mat-spinner *ngIf="isLoading" diameter="24"></mat-spinner>
</button>
</div>
</form>
</div>

View File

@ -0,0 +1,76 @@
.shipping-container {
padding: 16px;
.section {
margin-bottom: 24px;
padding: 16px;
border: 1px solid #e0e0e0;
border-radius: 4px;
h3 {
margin: 0px 0px 16px 0px;
color: var(--mat-sys-primary);
font-weight: 500;
}
h4 {
margin: 16px 0 8px;
color: var(--mat-sys-primary);
font-weight: 500;
}
.mat-mdc-card-content:first-child {
padding: 0 16px;
}
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.radio-group {
display: flex;
gap: 16px;
margin-bottom: 16px;
}
.form-row {
display: flex;
gap: 16px;
mat-form-field {
flex: 1;
margin-bottom: 6px;
&.full-width {
flex: 100%;
}
}
.small-field {
max-width: 120px;
}
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 16px;
margin-top: 16px;
}
}
@media (max-width: 768px) {
.shipping-container {
.form-row {
flex-direction: column;
gap: 8px;
mat-form-field {
width: 100%;
}
}
}
}

View File

@ -1,12 +1,376 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { CommonModule } from '@angular/common';
import { Component, EventEmitter, inject, Input, Output } from '@angular/core';
import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { NotificationService } from '../../core/services/common/notification.service';
import { AngularMaterialModule } from '../../shared/module/angular-material.module';
import { ShippingService } from '../../core/services/carnet/shipping.service';
import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service';
import { CommonService } from '../../core/services/common/common.service';
import { Shipping } from '../../core/models/carnet/shipping';
import { Country } from '../../core/models/country';
import { State } from '../../core/models/state';
import { ZipCodeValidator } from '../../shared/validators/zipcode-validator';
import { Subject, takeUntil } from 'rxjs';
import { DeliveryType } from '../../core/models/delivery-type';
import { DeliveryMethod } from '../../core/models/delivery-method';
import { PaymentType } from '../../core/models/payment-type';
@Component({ @Component({
selector: 'app-shipping', selector: 'app-shipping',
imports: [], imports: [AngularMaterialModule, CommonModule, ReactiveFormsModule],
templateUrl: './shipping.component.html', templateUrl: './shipping.component.html',
styleUrl: './shipping.component.scss' styleUrl: './shipping.component.scss'
}) })
export class ShippingComponent { export class ShippingComponent {
@Input() headerid: number = 0; @Input() headerid: number = 0;
@Input() isEditMode = false;
@Output() completed = new EventEmitter<boolean>(); @Output() completed = new EventEmitter<boolean>();
private fb = inject(FormBuilder);
private shippingService = inject(ShippingService);
private notificationService = inject(NotificationService);
private errorHandler = inject(ApiErrorHandlerService);
private commonService = inject(CommonService);
shippingForm: FormGroup;
isLoading = false;
showAddressForm = false;
showContactForm = false;
countriesHasStates = ['US', 'CA', 'MX'];
countries: Country[] = [];
states: State[] = [];
deliveryTypes: DeliveryType[] = [];
deliveryMethods: DeliveryMethod[] = [];
paymentTypes: PaymentType[] = [];
private destroy$ = new Subject<void>();
// preparer contact and address mock data
preparerContact = {
firstName: 'John',
lastName: 'Doe',
middleInitial: 'A',
title: 'Mr.',
phone: '1234567890',
mobile: '0987654321',
fax: '1234567890',
email: 'j@doe.com',
}
preparerAddress = {
address1: '123 Main St',
address2: 'Suite 100',
city: 'Anytown',
state: 'CA',
zip: '12345',
country: 'US'
};
holderAddress = {
address1: '456 Holder St',
address2: 'Apt 200',
city: 'Othertown',
state: 'NY',
zip: '67890',
country: 'US'
};
holderContact = {
firstName: 'Jane',
lastName: 'Smith',
middleInitial: 'B',
title: 'Ms.',
phone: '2345678901',
mobile: '1098765432',
fax: '2345678901',
email: 'jan@sm.cm'
};
constructor() {
this.shippingForm = this.fb.group({
needsBond: [false],
needsInsurance: [false],
needsLostDocProtection: [false],
shipTo: ['preparer', Validators.required],
address: this.fb.group({
address1: ['', [Validators.required, Validators.maxLength(100)]],
address2: ['', [Validators.maxLength(100)]],
city: ['', [Validators.required, Validators.maxLength(50)]],
state: ['', Validators.required],
country: ['', Validators.required],
zip: ['', [Validators.required, ZipCodeValidator('country')]],
}),
contact: this.fb.group({
firstName: ['', [Validators.required, Validators.maxLength(50)]],
lastName: ['', [Validators.required, Validators.maxLength(50)]],
middleInitial: ['', [Validators.maxLength(1)]],
title: ['', [Validators.required, Validators.maxLength(100)]],
phone: ['', [Validators.required, Validators.pattern(/^[0-9]{10,15}$/)]],
mobile: ['', [Validators.required, Validators.pattern(/^[0-9]{10,15}$/)]],
fax: ['', [Validators.pattern(/^[0-9]{10,15}$/)]],
email: ['', [Validators.required, Validators.email, Validators.maxLength(100)]],
refNumber: [''],
notes: ['']
}),
deliveryType: ['', Validators.required],
deliveryMethod: ['', Validators.required],
courierAccount: [''],
paymentMethod: ['', Validators.required],
notes: ['']
});
// Update form validation based on selections
// this.shippingForm.get('shipTo')?.valueChanges.subscribe(value => {
// this.updateShippingValidation(value);
// });
this.shippingForm.get('deliveryMethod')?.valueChanges.subscribe(value => {
const courierControl = this.shippingForm.get('courierAccount');
if (value === 'customerCourier') {
courierControl?.setValidators([Validators.required]);
} else {
courierControl?.clearValidators();
}
courierControl?.updateValueAndValidity();
});
this.shippingForm.valueChanges.subscribe(() => {
this.completed.emit(this.shippingForm.valid);
});
}
ngOnInit(): void {
this.loadCountries();
this.loadDeliveryTypes();
this.loadDeliveryMethods();
this.loadPaymentTypes();
if (this.headerid && this.isEditMode) {
this.loadShippingData();
}
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
onCountryChange(country: string): void {
this.shippingForm.get('address.state')?.reset();
if (country) {
this.loadStates(country);
}
this.shippingForm.get('address.zip')?.updateValueAndValidity();
}
loadCountries(): void {
this.commonService.getCountries(0)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (countries) => {
this.countries = countries;
},
error: (error) => {
console.error('Failed to load countries', error);
this.isLoading = false;
}
});
}
loadDeliveryTypes(): void {
this.commonService.getDeliveryTypes(0)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (deliveryTypes) => {
this.deliveryTypes = deliveryTypes;
},
error: (error) => {
console.error('Failed to load delivery types', error);
this.isLoading = false;
}
});
}
loadDeliveryMethods(): void {
this.commonService.getDeliveryMethods(0)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (deliveryMethods) => {
this.deliveryMethods = deliveryMethods;
},
error: (error) => {
console.error('Failed to load delivery methods', error);
this.isLoading = false;
}
});
}
loadPaymentTypes(): void {
this.commonService.getPaymentTypes(0)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (paymentTypes) => {
this.paymentTypes = paymentTypes;
},
error: (error) => {
console.error('Failed to load payment types', error);
this.isLoading = false;
}
});
}
loadStates(country: string): void {
this.isLoading = true;
country = this.countriesHasStates.includes(country) ? country : 'FN';
this.commonService.getStates(country, 0)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (states) => {
this.states = states;
const stateControl = this.shippingForm.get('contact.state');
if (this.countriesHasStates.includes(country)) {
stateControl?.enable();
} else {
stateControl?.disable();
stateControl?.setValue('FN');
}
this.isLoading = false;
},
error: (error) => {
console.error('Failed to load states', error);
this.isLoading = false;
}
});
}
loadShippingData(): void {
this.isLoading = true;
this.shippingService.getShippingData(this.headerid).subscribe({
next: (data: Shipping) => {
this.patchShippingData(data);
this.isLoading = false;
},
error: (error: any) => {
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to load shipping and payments data');
this.notificationService.showError(errorMessage);
this.isLoading = false;
}
});
}
patchShippingData(shipping: Shipping): void {
this.shippingForm.patchValue({
needsBond: shipping.needsBond,
needsInsurance: shipping.needsInsurance,
needsLostDocProtection: shipping.needsLostDocProtection,
shipTo: shipping.shipTo,
address: shipping.address,
contact: shipping.contact,
deliveryType: shipping.deliveryType,
deliveryMethod: shipping.deliveryMethod,
courierAccount: shipping.courierAccount,
paymentMethod: shipping.paymentMethod,
notes: shipping.notes
});
if (shipping.address?.country) {
this.loadStates(shipping.address?.country);
}
}
updateShippingValidation(shipTo: string): void {
// const addressGroup = this.shippingForm.get('address') as FormGroup;
// const contactGroup = this.shippingForm.get('contact') as FormGroup;
// if (shipTo === 'thirdParty') {
// Object.keys(addressGroup.controls).forEach(key => {
// addressGroup.get(key)?.setValidators(Validators.required);
// });
// Object.keys(contactGroup.controls).forEach(key => {
// if (key !== 'middleInitial' && key !== 'fax' && key !== 'notes' && key !== 'refNumber') {
// contactGroup.get(key)?.setValidators(Validators.required);
// }
// });
// } else {
// Object.keys(addressGroup.controls).forEach(key => {
// addressGroup.get(key)?.clearValidators();
// });
// Object.keys(contactGroup.controls).forEach(key => {
// contactGroup.get(key)?.clearValidators();
// });
// }
// addressGroup.updateValueAndValidity();
// contactGroup.updateValueAndValidity();
}
onSubmit(): void {
if (this.shippingForm.invalid) {
this.shippingForm.markAllAsTouched();
return;
}
this.isLoading = true;
const shippingData = this.shippingForm.value;
this.shippingService.saveShippingDetails(this.headerid, shippingData).subscribe({
next: () => {
this.notificationService.showSuccess('Shipping & payments information saved successfully');
this.completed.emit(true);
this.isLoading = false;
},
error: (error) => {
let errorMessage = this.errorHandler.handleApiError(error, 'Failed to save shipping and payment information');
this.notificationService.showError(errorMessage);
this.isLoading = false;
}
});
}
getAddressLabel(): string {
let shipTo = this.shippingForm.get('shipTo')?.value;
if (shipTo === 'preparer') {
return `${this.preparerContact.firstName} ${this.preparerContact.lastName}, ${this.preparerAddress.address1}, ${this.preparerAddress.city}, ${this.preparerAddress.state}, ${this.preparerAddress.zip}, ${this.preparerAddress.country}`;
}
if (shipTo === 'holder') {
return `${this.holderContact.firstName} ${this.holderContact.lastName}, ${this.holderAddress.address1}, ${this.holderAddress.city}, ${this.holderAddress.state}, ${this.holderAddress.zip}, ${this.holderAddress.country}`;
}
return '';
}
editAddressForm(): void {
this.showAddressForm = true;
let shipTo = this.shippingForm.get('shipTo')?.value;
if (shipTo === 'preparer') {
this.shippingForm.get('address')?.patchValue(this.preparerAddress);
this.loadStates(this.preparerAddress.country);
} else if (shipTo === 'holder') {
this.shippingForm.get('address')?.patchValue(this.holderAddress);
this.loadStates(this.preparerAddress.country);
}
}
cancelEditAddressForm(): void {
this.showAddressForm = false;
this.shippingForm.get('address')?.reset();
}
onShipToChange(event: any): void {
const shipTo = event.value;
this.showAddressForm = false;
this.showContactForm = false;
if (shipTo === 'thirdParty') {
this.shippingForm.get('contact')?.reset();
this.shippingForm.get('address')?.reset();
this.showAddressForm = true;
this.showContactForm = true;
}
}
} }

View File

@ -1,2 +0,0 @@
export interface Delivery {
}

View File

@ -1,2 +0,0 @@
export interface Insurance {
}

View File

@ -1,2 +0,0 @@
export interface Payment {
}

View File

@ -0,0 +1,8 @@
export interface ShippingAddress {
address1: string;
address2?: string | null;
city: string;
state: string;
country: string;
zip: string;
}

View File

@ -0,0 +1,12 @@
export interface ShippingContact {
firstName: string;
lastName: string;
middleInitial?: string | null;
title: string;
phone: string;
mobile: string;
fax?: string | null;
email: string;
refNumber?: string | null;
notes?: string | null;
}

View File

@ -1,2 +1,23 @@
import { ShippingAddress } from "./shipping-address";
import { ShippingContact } from "./shipping-contact";
export interface Shipping { export interface Shipping {
// Insurance details
needsBond: boolean;
needsInsurance: boolean;
needsLostDocProtection: boolean;
// Shipping details
shipTo: string;
address?: ShippingAddress;
contact?: ShippingContact;
// Delivery details
deliveryType: string;
deliveryMethod: string;
courierAccount?: string;
// Payment details
paymentMethod: string;
notes?: string;
} }

View File

@ -0,0 +1,5 @@
export interface DeliveryMethod {
name: string;
id: string;
value: string;
}

View File

@ -0,0 +1,5 @@
export interface PaymentType {
name: string;
id: string;
value: string;
}

View File

@ -1,9 +0,0 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class DeliveryService {
constructor() { }
}

View File

@ -1,9 +0,0 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class PaymentService {
constructor() { }
}

View File

@ -1,9 +1,103 @@
import { Injectable } from '@angular/core'; import { inject, Injectable } from '@angular/core';
import { environment } from '../../../../environments/environment';
import { HttpClient } from '@angular/common/http';
import { filter, map, Observable } from 'rxjs';
import { UserService } from '../common/user.service';
import { Shipping } from '../../models/carnet/shipping';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class ShippingService { export class ShippingService {
private apiUrl = environment.apiUrl;
private apiDb = environment.apiDb;
constructor() { } private http = inject(HttpClient);
private userService = inject(UserService);
getShippingData(headerid: number): Shipping | any {
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetShipPaymentDetailstoEdit/${this.userService.getUserSpid()}/${this.userService.getUser()}/${headerid}`).pipe(
filter(response => response.length > 0),
map(response => this.mapToShippingData(response?.[0])));
}
private mapToShippingData(shippingDetails: any): Shipping {
let shippingData: Shipping = {
shipTo: shippingDetails.SHIPADDRTYPE,
deliveryType: shippingDetails.DELIVERYTYPE,
deliveryMethod: shippingDetails.DELIVERYMETHOD,
courierAccount: shippingDetails.CUSTCOURIERNO,
paymentMethod: shippingDetails.PAYMENTMETHOD,
notes: shippingDetails.NOTES,
needsBond: shippingDetails.INSPROTECTION === 'Y',
needsInsurance: shippingDetails.LDIPROTECTION === 'Y',
needsLostDocProtection: shippingDetails.LDIPROTECTION === 'Y'
};
if (shippingDetails.address) {
shippingData.address = {
address1: shippingDetails.ADDRESS1,
address2: shippingDetails.ADDRESS2,
city: shippingDetails.CITY,
state: shippingDetails.STATE,
zip: shippingDetails.ZIP,
country: shippingDetails.COUNTRY
}
}
if (shippingDetails.contact) {
shippingData.contact = {
firstName: shippingDetails.FIRSTNAME,
lastName: shippingDetails.LASTNAME,
middleInitial: shippingDetails.MIDDLEINITIAL,
title: shippingDetails.TITLE,
phone: shippingDetails.PHONENO,
mobile: shippingDetails.MOBILENO,
fax: shippingDetails.FAXNO,
email: shippingDetails.EMAILADDRESS,
refNumber: shippingDetails.REFNO,
}
}
return shippingData;
}
saveShippingDetails(headerid: number, shippingData: Shipping): Observable<any> {
const shippingDetails = {
P_HEADERID: headerid,
P_SHIPTOTYPE: shippingData.shipTo,
P_DELIVERYTYPE: shippingData.deliveryType,
P_DELIVERYMETHOD: shippingData.deliveryMethod,
P_CUSTCOURIERNO: shippingData.courierAccount,
P_PAYMENTMETHOD: shippingData.paymentMethod,
P_NOTES: shippingData.notes,
P_FORMOFSECURITY: shippingData.needsBond ? 'Y' : 'N',
P_INSPROTECTION: shippingData.needsInsurance ? 'Y' : 'N',
P_LDIPROTECTION: shippingData.needsLostDocProtection ? 'Y' : 'N',
address1: shippingData.address?.address1 || '',
address2: shippingData.address?.address2 || null,
city: shippingData.address?.city || '',
state: shippingData.address?.state || '',
zip: shippingData.address?.zip || '',
country: shippingData.address?.country || '',
firstName: shippingData.contact?.firstName || '',
lastName: shippingData.contact?.lastName || '',
middleInitial: shippingData.contact?.middleInitial || null,
title: shippingData.contact?.title || '',
phone: shippingData.contact?.phone || '',
mobile: shippingData.contact?.mobile || '',
fax: shippingData.contact?.fax || null,
emailAddress: shippingData.contact?.email || '',
refNo: shippingData.contact?.refNumber || '',
P_USERID: this.userService.getUser()
}
return this.http.patch(`${this.apiUrl}/${this.apiDb}/UpdateShippingDetails`, shippingDetails);
}
} }

View File

@ -13,6 +13,8 @@ import { CargoSurety } from '../../models/cargo-surety';
import { CarnetStatus } from '../../models/carnet-status'; import { CarnetStatus } from '../../models/carnet-status';
import { Country } from '../../models/country'; import { Country } from '../../models/country';
import { UnitOfMeasure } from '../../models/unitofmeasure'; import { UnitOfMeasure } from '../../models/unitofmeasure';
import { DeliveryMethod } from '../../models/delivery-method';
import { PaymentType } from '../../models/payment-type';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -156,7 +158,30 @@ export class CommonService {
name: item.PARAMDESC, name: item.PARAMDESC,
id: item.PARAMID, id: item.PARAMID,
value: item.PARAMVALUE, value: item.PARAMVALUE,
color: item.ADDLPARAMVALUE1, }))
)
);
}
getDeliveryMethods(_spid: number = 0): Observable<DeliveryMethod[]> {
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetParamValues?P_PARAMTYPE=007&P_SPID=0`).pipe(
map((response) =>
response.map((item) => ({
name: item.PARAMDESC,
id: item.PARAMID,
value: item.PARAMVALUE,
}))
)
);
}
getPaymentTypes(_spid: number = 0): Observable<PaymentType[]> {
return this.http.get<any[]>(`${this.apiUrl}/${this.apiDb}/GetParamValues?P_PARAMTYPE=008&P_SPID=0`).pipe(
map((response) =>
response.map((item) => ({
name: item.PARAMDESC,
id: item.PARAMID,
value: item.PARAMVALUE,
})) }))
) )
); );

View File

@ -11,7 +11,7 @@
<div class="questions-section"> <div class="questions-section">
<div class="question-row"> <div class="question-row">
<mat-radio-group (change)="newCarnet($event)"> <mat-radio-group (change)="newCarnet($event)">
<mat-label>Do you need a new carnet?</mat-label> <mat-label class="question">Do you need a new carnet?</mat-label>
<mat-radio-button [value]="true">Yes</mat-radio-button> <mat-radio-button [value]="true">Yes</mat-radio-button>
<mat-radio-button [value]="false">No</mat-radio-button> <mat-radio-button [value]="false">No</mat-radio-button>
</mat-radio-group> </mat-radio-group>
@ -19,7 +19,7 @@
<div class="question-row"> <div class="question-row">
<mat-radio-group (change)="additionalSets($event)"> <mat-radio-group (change)="additionalSets($event)">
<mat-label>Do you need additional sets?</mat-label> <mat-label class="question">Do you need additional sets?</mat-label>
<mat-radio-button [value]="true">Yes</mat-radio-button> <mat-radio-button [value]="true">Yes</mat-radio-button>
<mat-radio-button [value]="false">No</mat-radio-button> <mat-radio-button [value]="false">No</mat-radio-button>
</mat-radio-group> </mat-radio-group>
@ -27,7 +27,8 @@
<div class="question-row"> <div class="question-row">
<mat-radio-group (change)="duplicateCarnet($event)"> <mat-radio-group (change)="duplicateCarnet($event)">
<mat-label>Did you lose your carnet? Are you looking for duplicates?</mat-label> <mat-label class="question">Did you lose your carnet? Are you looking for
duplicates?</mat-label>
<mat-radio-button [value]="true">Yes</mat-radio-button> <mat-radio-button [value]="true">Yes</mat-radio-button>
<mat-radio-button [value]="false">No</mat-radio-button> <mat-radio-button [value]="false">No</mat-radio-button>
</mat-radio-group> </mat-radio-group>
@ -35,7 +36,7 @@
<div class="question-row"> <div class="question-row">
<mat-radio-group (change)="extendCarnet($event)"> <mat-radio-group (change)="extendCarnet($event)">
<mat-label>Do you need to extend the carnet?</mat-label> <mat-label class="question">Do you need to extend the carnet?</mat-label>
<mat-radio-button [value]="true">Yes</mat-radio-button> <mat-radio-button [value]="true">Yes</mat-radio-button>
<mat-radio-button [value]="false">No</mat-radio-button> <mat-radio-button [value]="false">No</mat-radio-button>
</mat-radio-group> </mat-radio-group>

View File

@ -21,9 +21,12 @@
.questions-section { .questions-section {
flex: 0 0 70%; flex: 0 0 70%;
padding: 24px 0px; padding: 12px 16px;
margin-bottom: 24px; margin-bottom: 46px;
margin-left: 1rem; margin-left: 1rem;
margin-top: 24px;
background: var(--mat-sys-surface);
border-radius: 4px;
.question-row { .question-row {
margin-bottom: 1rem; margin-bottom: 1rem;
@ -32,6 +35,15 @@
display: flex; display: flex;
gap: 8px; gap: 8px;
.question {
color: var(--mat-sys-on-surface);
font-family: var(--mat-sys-body-medium-font);
line-height: var(--mat-sys-body-medium-line-height);
font-size: var(--mat-sys-body-medium-size);
letter-spacing: var(--mat-sys-body-medium-tracking);
font-weight: var(--mat-sys-medium-font-weight);
}
mat-radio-button { mat-radio-button {
margin-top: -5px; margin-top: -5px;
} }