Skip to content

Commit b994b18

Browse files
committed
added search-customer component
1 parent c6b93fa commit b994b18

38 files changed

+307
-148
lines changed

src/Client/Logistics.OfficeApp/angular.json

+5-15
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,8 @@
2828
"src/assets"
2929
],
3030
"styles": [
31-
"src/styles/global.scss",
32-
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css",
33-
"node_modules/primeng/resources/primeng.min.css",
34-
"node_modules/primeicons/primeicons.css",
35-
"node_modules/bootstrap-icons/font/bootstrap-icons.css",
36-
"node_modules/bootstrap/dist/css/bootstrap.min.css",
37-
"node_modules/mapbox-gl/dist/mapbox-gl.css"
31+
"node_modules/mapbox-gl/dist/mapbox-gl.css",
32+
"src/styles/global.scss"
3833
],
3934
"scripts": [
4035
"node_modules/chart.js/dist/chart.js"
@@ -109,16 +104,11 @@
109104
"src/assets"
110105
],
111106
"styles": [
112-
"src/styles/global.scss",
113-
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css",
114-
"node_modules/primeng/resources/primeng.min.css",
115-
"node_modules/primeicons/primeicons.css",
116-
"node_modules/bootstrap-icons/font/bootstrap-icons.css",
117-
"node_modules/bootstrap/dist/css/bootstrap.min.css",
118-
"node_modules/mapbox-gl/dist/mapbox-gl.css"
107+
"node_modules/mapbox-gl/dist/mapbox-gl.css",
108+
"src/styles/global.scss"
119109
],
120110
"scripts": [
121-
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
111+
"node_modules/chart.js/dist/chart.js"
122112
]
123113
}
124114
},

src/Client/Logistics.OfficeApp/src/app/core/models/createLoad.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export interface CreateLoad {
1010
distance: number;
1111
assignedDispatcherId: string;
1212
assignedTruckId: string;
13+
customerId: string;
1314
}

src/Client/Logistics.OfficeApp/src/app/core/models/updateLoad.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ export interface UpdateLoad {
1313
distance: number;
1414
assignedDispatcherId: string;
1515
assignedTruckId: string;
16+
customerId?: string;
1617
status?: LoadStatus;
1718
}

src/Client/Logistics.OfficeApp/src/app/core/services/api.service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ export class ApiService {
323323
return `search=${search}&orderBy=${orderBy}&page=${page}&pageSize=${pageSize}`;
324324
}
325325

326+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
326327
private handleError(responseData: any): Observable<any> {
327328
const errorMessage = responseData.error?.error ?? responseData.error;
328329

src/Client/Logistics.OfficeApp/src/app/pages/dashboard/components/truck-stats-table/truck-stats-table.component.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ <h4 class="ps-3 py-2">Truck statistics for
6767
<button pButton
6868
class="p-button-raised"
6969
label="View details"
70-
[routerLink]="['/truck/details', truckStats.truckId]">
70+
[routerLink]="['/trucks/details', truckStats.truckId]">
7171
</button>
7272
<button pButton
7373
class="p-button-raised ms-2"

src/Client/Logistics.OfficeApp/src/app/pages/load/add-load/add-load.component.html

+8-7
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ <h1>Add a new load</h1>
66
<p-card>
77
<div class="row">
88
<div class="col-12 col-md-6">
9-
<p-progressSpinner *ngIf="isBusy"></p-progressSpinner>
10-
<form [formGroup]="form" (ngSubmit)="submit()">
9+
<p-progressSpinner *ngIf="isLoading"></p-progressSpinner>
10+
<form [formGroup]="form" (ngSubmit)="createLoad()">
1111
<div class="mb-3">
1212
<label for="name" class="form-label">Name</label>
1313
<input id="name" formControlName="name" type="text" class="form-control" placeholder="Load name" />
1414
</div>
15+
<div class="mb-3">
16+
<label for="customer" class="form-label">Customer</label>
17+
<app-search-customer [(selectedCustomer)]="selectedCustomer"></app-search-customer>
18+
</div>
1519
<div class="mb-3">
1620
<label for="orgAddress" class="form-label">Origin Address</label>
1721
<app-address-autocomplete field="orgAddress"
@@ -52,17 +56,14 @@ <h1>Add a new load</h1>
5256
</div>
5357
<div class="mb-3">
5458
<label for="assignedTruck" class="form-label">Assigned Truck</label>
55-
<p-autoComplete formControlName="assignedTruck" styleClass="w-100" inputStyleClass="form-control"
56-
placeholder="Type a driver name or truck number" field="driversName" [minLength]="2" [suggestions]="suggestedDrivers"
57-
(completeMethod)="searchTruck($event)">
58-
</p-autoComplete>
59+
<app-search-truck [(selectedTruck)]="selectedTruck"></app-search-truck>
5960
</div>
6061
<div>
6162
<button pButton
6263
type="submit"
6364
class="p-button-raised mt-3"
6465
icon="bi bi-pencil-square"
65-
[disabled]="isBusy">
66+
[disabled]="isLoading">
6667
Add
6768
</button>
6869
<button pButton

src/Client/Logistics.OfficeApp/src/app/pages/load/add-load/add-load.component.ts

+57-37
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
12
import {Component, OnInit, ViewEncapsulation} from '@angular/core';
23
import {NgIf} from '@angular/common';
34
import {FormControl, FormGroup, Validators, FormsModule, ReactiveFormsModule} from '@angular/forms';
@@ -11,7 +12,7 @@ import {ProgressSpinnerModule} from 'primeng/progressspinner';
1112
import {AppConfig} from '@configs';
1213
import {AuthService} from '@core/auth';
1314
import {LoadStatus} from '@core/enums';
14-
import {CreateLoad} from '@core/models';
15+
import {CreateLoad, Customer} from '@core/models';
1516
import {ApiService, ToastService} from '@core/services';
1617
import {DistanceConverter} from '@shared/utils';
1718
import {
@@ -20,7 +21,7 @@ import {
2021
RouteChangedEvent,
2122
SelectedAddressEvent,
2223
} from '@shared/components';
23-
import {TruckData, TruckHelper} from '../shared';
24+
import {SearchCustomerComponent, SearchTruckComponent, TruckData} from '../components';
2425

2526

2627
@Component({
@@ -42,27 +43,33 @@ import {TruckData, TruckHelper} from '../shared';
4243
RouterLink,
4344
AddressAutocompleteComponent,
4445
DirectionsMapComponent,
46+
SearchCustomerComponent,
47+
SearchTruckComponent,
4548
],
4649
})
4750
export class AddLoadComponent implements OnInit {
4851
public readonly accessToken: string;
4952
private distanceMeters: number;
5053

51-
public isBusy: boolean;
54+
public isLoading: boolean;
5255
public form: FormGroup;
53-
public suggestedDrivers: TruckData[];
56+
public selectedTruck: TruckData | null;
57+
public selectedCustomer: Customer | null;
58+
public suggestedCustomers: Customer[];
5459
public originCoords?: [number, number] | null;
5560
public destinationCoords?: [number, number] | null;
5661

5762
constructor(
58-
private authService: AuthService,
59-
private apiService: ApiService,
60-
private toastService: ToastService,
61-
private router: Router)
63+
private readonly authService: AuthService,
64+
private readonly apiService: ApiService,
65+
private readonly toastService: ToastService,
66+
private readonly router: Router)
6267
{
6368
this.accessToken = AppConfig.mapboxToken;
64-
this.isBusy = false;
65-
this.suggestedDrivers = [];
69+
this.isLoading = false;
70+
this.selectedTruck = null;
71+
this.selectedCustomer = null;
72+
this.suggestedCustomers = [];
6673
this.distanceMeters = 0;
6774

6875
this.form = new FormGroup({
@@ -76,7 +83,6 @@ export class AddLoadComponent implements OnInit {
7683
distance: new FormControl(0, Validators.required),
7784
dispatcherName: new FormControl('', Validators.required),
7885
dispatcherId: new FormControl('', Validators.required),
79-
assignedTruck: new FormControl('', Validators.required),
8086
status: new FormControl(LoadStatus.Dispatched, Validators.required),
8187
});
8288
}
@@ -85,21 +91,6 @@ export class AddLoadComponent implements OnInit {
8591
this.fetchCurrentDispatcher();
8692
}
8793

88-
searchTruck(event: any) {
89-
TruckHelper.findTruckDrivers(this.apiService, event.query).subscribe((drivers) => this.suggestedDrivers = drivers);
90-
}
91-
92-
submit() {
93-
const assignedTruck = this.form.value.assignedTruck;
94-
95-
if (!assignedTruck) {
96-
this.toastService.showError('Select a truck');
97-
return;
98-
}
99-
100-
this.createLoad();
101-
}
102-
10394
updateOrigin(eventData: SelectedAddressEvent) {
10495
this.originCoords = eventData.center;
10596
this.form.patchValue({
@@ -120,9 +111,12 @@ export class AddLoadComponent implements OnInit {
120111
this.form.patchValue({distance: distanceMiles});
121112
}
122113

123-
private createLoad() {
124-
this.isBusy = true;
114+
createLoad() {
115+
if (!this.isValid()) {
116+
return;
117+
}
125118

119+
this.isLoading = true;
126120
const command: CreateLoad = {
127121
name: this.form.value.name,
128122
originAddress: this.form.value.orgAddress,
@@ -134,18 +128,44 @@ export class AddLoadComponent implements OnInit {
134128
deliveryCost: this.form.value.deliveryCost,
135129
distance: this.distanceMeters,
136130
assignedDispatcherId: this.form.value.dispatcherId,
137-
assignedTruckId: this.form.value.assignedTruck.truckId,
131+
assignedTruckId: this.selectedTruck!.truckId,
132+
customerId: this.form.value.customer.id
138133
};
134+
139135

140136
this.apiService.createLoad(command)
141-
.subscribe((result) => {
142-
if (result.isSuccess) {
143-
this.toastService.showSuccess('A new load has been created successfully');
144-
this.router.navigateByUrl('/load/list');
145-
}
146-
147-
this.isBusy = false;
148-
});
137+
.subscribe((result) => {
138+
if (result.isSuccess) {
139+
this.toastService.showSuccess('A new load has been created successfully');
140+
this.router.navigateByUrl('/loads');
141+
}
142+
143+
this.isLoading = false;
144+
});
145+
}
146+
147+
private isValid(): boolean {
148+
if (!this.selectedTruck) {
149+
this.toastService.showError('Please select the truck');
150+
return false;
151+
}
152+
153+
if (!this.selectedCustomer) {
154+
this.toastService.showError('Please select the customer');
155+
return false;
156+
}
157+
158+
if (!this.form.value.orgAddress) {
159+
this.toastService.showError('Please select the origin address');
160+
return false;
161+
}
162+
163+
if (!this.form.value.dstAddress) {
164+
this.toastService.showError('Please select the destination address');
165+
return false;
166+
}
167+
168+
return true;
149169
}
150170

151171
private fetchCurrentDispatcher() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './search-customer/search-customer.component';
2+
export * from './search-truck/search-truck.component';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<p-autoComplete #autoCompleteInput
2+
[suggestions]="suggestedCustomers"
3+
styleClass="w-100" inputStyleClass="form-control"
4+
(completeMethod)="searchCustomer($event)"
5+
[minLength]="2"
6+
[forceSelection]="true"
7+
field="name"
8+
(onSelect)="changeSelectedCustomer">
9+
</p-autoComplete>
10+
11+
<!-- <p-overlayPanel #emptyResultsPanel [showCloseIcon]="true" [appendTo]="autoCompleteInput">
12+
<div>
13+
No results found. Would you like to add a new customer with name "<span>{{searchQuery}}</span>" ?
14+
<button pButton class="p-button-text" (click)="addCustomer()">Add</button>
15+
</div>
16+
</p-overlayPanel> -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import {Component, EventEmitter, Input, Output} from '@angular/core';
2+
import {CommonModule} from '@angular/common';
3+
import {AutoCompleteModule} from 'primeng/autocomplete';
4+
import {ApiService} from '@core/services';
5+
import {Customer} from '@core/models';
6+
7+
8+
@Component({
9+
selector: 'app-search-customer',
10+
standalone: true,
11+
templateUrl: './search-customer.component.html',
12+
styleUrls: [],
13+
imports: [
14+
CommonModule,
15+
AutoCompleteModule,
16+
],
17+
})
18+
export class SearchCustomerComponent {
19+
public suggestedCustomers: Customer[] = [];
20+
21+
@Input() selectedCustomer: Customer | null = null;
22+
@Output() selectedCustomerChange = new EventEmitter<Customer>();
23+
24+
constructor(private readonly apiService: ApiService){
25+
}
26+
27+
searchCustomer(event: {query: string}) {
28+
this.apiService.getCustomers({search: event.query}).subscribe((result) => {
29+
if (result.data && result.data.length) {
30+
this.suggestedCustomers = result.data;
31+
}
32+
});
33+
}
34+
35+
changeSelectedCustomer(event: Customer) {
36+
this.selectedCustomerChange.emit(event);
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<p-autoComplete [(ngModel)]="selectedTruck"
2+
styleClass="w-100" inputStyleClass="form-control"
3+
placeholder="Type a driver name or truck number"
4+
field="driversName"
5+
[minLength]="2"
6+
[suggestions]="suggestedTrucks"
7+
[forceSelection]="true"
8+
(completeMethod)="searchTruck($event)"
9+
(onSelect)="changeSelectedTruck($event)">
10+
</p-autoComplete>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {Component, EventEmitter, Input, Output} from '@angular/core';
2+
import {CommonModule} from '@angular/common';
3+
import {FormsModule} from '@angular/forms';
4+
import {AutoCompleteModule} from 'primeng/autocomplete';
5+
import {ApiService} from '@core/services';
6+
import {TruckHelper} from '@pages/load/shared';
7+
8+
9+
@Component({
10+
selector: 'app-search-truck',
11+
standalone: true,
12+
templateUrl: './search-truck.component.html',
13+
styleUrls: [],
14+
imports: [
15+
CommonModule,
16+
AutoCompleteModule,
17+
FormsModule,
18+
],
19+
})
20+
export class SearchTruckComponent {
21+
public suggestedTrucks: TruckData[] = [];
22+
@Input() selectedTruck: TruckData | null = null;
23+
@Output() selectedTruckChange = new EventEmitter<TruckData>();
24+
25+
constructor(private readonly apiService: ApiService) {
26+
}
27+
28+
searchTruck(event: {query: string}) {
29+
this.apiService.getTruckDrivers({search: event.query}).subscribe((result) => {
30+
if (!result.data) {
31+
return;
32+
}
33+
34+
this.suggestedTrucks = result.data.map((truckDriver) => ({
35+
truckId: truckDriver.truck.id,
36+
driversName: TruckHelper.formatDriversName(truckDriver.truck.truckNumber, truckDriver.drivers.map((i) => i.fullName)),
37+
}));
38+
});
39+
}
40+
41+
changeSelectedTruck(event: TruckData) {
42+
this.selectedTruckChange.emit(event);
43+
}
44+
}
45+
46+
export interface TruckData {
47+
driversName: string,
48+
truckId: string;
49+
}

0 commit comments

Comments
 (0)