Commit f4d7aaa0 authored by Heber Cordova's avatar Heber Cordova

feat: finished flight configuration

parent f8dffe3a
{ {
"agents": [ "agents": [
{ {
"id": 1,
"name": "John", "name": "John",
"lastname": "Doe", "lastname": "Doe",
"detail": "",
"email": "john.doe@gmail.com", "email": "john.doe@gmail.com",
"cellphone": "+51965456934", "password": "heber12345",
"dateCreated": "10/11/2023", "id": 1
"typeDocumentId": 1,
"identificationDocument": "12345678"
}, },
{ {
"id": 2, "id": 2,
...@@ -281,66 +279,117 @@ ...@@ -281,66 +279,117 @@
"code": "LA" "code": "LA"
} }
], ],
"typeAircrafts": [ "flights": [
{
"id": 1,
"airline": "American Airlines",
"airlineId": 1,
"airportOrigin": "John F. Kennedy International Airport",
"airportOriginId": 2,
"airportDestination": "Jorge Chavez International Airport",
"airportDestinationId": 1,
"dateStart": "2023-10-22T07:00",
"dateEnd": "2023-10-22T12:00",
"aircraftType": "Boeing 737",
"aircraftTypeId": 1,
"scales": [
{
"id": 1,
"airportOrigin": "John F. Kennedy International Airport",
"airportOriginId": 2,
"airportDestination": "Jorge Chavez International Airport",
"airportDestinationId": 1,
"dateStart": "2023-10-22T07:00",
"dateEnd": "2023-10-22T12:00"
}
],
"costs": [
{
"id": 1,
"dateStart": "2023-10-01",
"dateEnd": "2023-10-30",
"aircraftType": "Boeing 737",
"aircraftTypeId": 1,
"cost": "1200"
}
]
},
{
"id": 2,
"airline": "LATAM Airlines",
"airlineId": 9,
"airportOrigin": "Jorge Chavez International Airport",
"airportOriginId": 1,
"airportDestination": "Dubai International Airport",
"airportDestinationId": 7,
"dateStart": "2023-10-23T07:00",
"dateEnd": "2023-10-24T07:00",
"aircraftType": "Boeing 737",
"aircraftTypeId": 1,
"scales": [
{
"id": 1,
"airportOrigin": "Jorge Chavez International Airport",
"airportOriginId": 1,
"airportDestination": "John F. Kennedy International Airport",
"airportDestinationId": 2,
"dateStart": "2023-10-23T07:00",
"dateEnd": "2023-10-23T12:00"
},
{
"id": 2,
"airportOrigin": "John F. Kennedy International Airport",
"airportOriginId": 2,
"airportDestination": "Dubai International Airport",
"airportDestinationId": 7,
"dateStart": "2023-10-23T01:00",
"dateEnd": "2023-10-24T07:00"
}
],
"costs": [
{
"id": 1,
"dateStart": "2023-10-01",
"dateEnd": "2023-10-31",
"aircraftType": "Boeing 737",
"aircraftTypeId": 1,
"cost": "4000"
},
{
"id": 2,
"dateStart": "2023-11-01",
"dateEnd": "2023-11-30",
"aircraftType": "Boeing 737",
"aircraftTypeId": 1,
"cost": "5000"
}
]
}
],
"aircrafts": [
{ {
"id": 1, "id": 1,
"name": "Boeing 737", "name": "Boeing 737",
"manufacturer": "Boeing", "manufacturer": "Boeing",
"model": "737-800", "model": "737-800"
"capacity": 189,
"range": "3,850 miles",
"cabin_configuration": {
"economy_class": 150,
"business_class": 20,
"first_class": 19
},
"max_speed": "Mach 0.82",
"description": "The Boeing 737-800 is a popular narrow-body jet with a seating capacity of 189 passengers. It's known for its fuel efficiency and reliability."
}, },
{ {
"id": 2, "id": 2,
"name": "Airbus A320", "name": "Airbus A320",
"manufacturer": "Airbus", "manufacturer": "Airbus",
"model": "A320-200", "model": "A320-200"
"capacity": 150,
"range": "3,300 miles",
"cabin_configuration": {
"economy_class": 140,
"business_class": 10,
"first_class": 0
},
"max_speed": "Mach 0.78",
"description": "The Airbus A320-200 is a versatile and widely used single-aisle aircraft. It can accommodate up to 150 passengers."
}, },
{ {
"id": 3, "id": 3,
"name": "Boeing 787 Dreamliner", "name": "Boeing 787 Dreamliner",
"manufacturer": "Boeing", "manufacturer": "Boeing",
"model": "787-9", "model": "787-9"
"capacity": 290,
"range": "7,530 miles",
"cabin_configuration": {
"economy_class": 240,
"business_class": 30,
"first_class": 20
},
"max_speed": "Mach 0.85",
"description": "The Boeing 787-9 Dreamliner is a long-range, wide-body aircraft with an emphasis on passenger comfort and fuel efficiency."
}, },
{ {
"id": 4, "id": 4,
"name": "Embraer E175", "name": "Embraer E175",
"manufacturer": "Embraer", "manufacturer": "Embraer",
"model": "E175", "model": "E175"
"capacity": 76,
"range": "2,400 miles",
"cabin_configuration": {
"economy_class": 76,
"business_class": 0,
"first_class": 0
},
"max_speed": "Mach 0.82",
"description": "The Embraer E175 is a regional jet designed for shorter flights with a capacity of 76 passengers."
} }
], ],
"paymentStatus": [ "paymentStatus": [
......
export interface ReservationDetail {
id: number;
origin: string;
destination: string;
date: string;
status: string;
cost: number;
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<shared-sidebar [toggle]="toggleSidebar" [menuItems]="menuItems"></shared-sidebar> <shared-sidebar [toggle]="toggleSidebar" [menuItems]="menuItems"></shared-sidebar>
</div> </div>
<div class="w-100"> <div class="w-100">
<shared-navigation-user></shared-navigation-user> <shared-navigation-user (toggleSidebarEvent)="toggleSidebarEvent()"></shared-navigation-user>
<shared-navigation (toggleSidebarEvent)="toggleSidebarEvent()"></shared-navigation> <shared-navigation (toggleSidebarEvent)="toggleSidebarEvent()"></shared-navigation>
<router-outlet></router-outlet> <router-outlet></router-outlet>
<shared-footer></shared-footer> <shared-footer></shared-footer>
......
.pay_fields {
display: grid;
grid-template-columns: repeat(2, 1fr);
row-gap: 5px;
column-gap: 10px;
}
.pay_actions {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 10px;
margin-top: 20px;
border-top: 1px solid gray;
}
.pay__field {
display: flex;
flex-direction: column;
position: relative;
padding-top: 15px;
box-sizing: border-box;
height: 38.8px;
}
.pay__field label {
position: absolute;
font-size: 12px;
}
.pay__field input {
outline: none;
border: 0;
border-bottom: 1px solid #ccc;
font-size: 12px;
}
input:focus + label {
top: 0;
left: 0;
color: #3f51b5;
font-size: 10px;
}
.pay__field input:focus {
border-bottom: 3px solid #3f51b5;
}
.pay__field .label-top {
top: 0;
left: 0;
font-size: 10px;
}
.pay__card, .pay__owner {
grid-column: 1 / 3;
}
.btn-back {
border: 1px solid #ccc;
color: #ccc;
font-size: 12px;
}
.btn-pay {
border: 1px solid #198754;
background-color: #198754;
color: white;
font-size: 12px;
}
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Molestias dolore quis optio dicta suscipit illum. Facere culpa delectus, beatae iure minima voluptatibus numquam odio amet deserunt temporibus eum consectetur modi?</p> <shared-navigation></shared-navigation>
<shared-mail-box
title="Mis reservas"
message="Este mantenimiento te permite visualizar tus reservas"
icon="bi-people">
<shared-table-custom
[tableModel]="tableModel"
[tableData]="tableData"
[tableAction]="actions"
[onClickItem]="onClickItem">
</shared-table-custom>
<shared-dialog
(onCloseDialog)="onTogleDialogPay()"
[isActivated]="openDialogPay"
title="Realizar pago">
<form [formGroup]="formGroup" class="pay">
<div class="pay_fields">
<shared-input-text
[control]="cardControl"
class="pay__card"
label="Numero de targeta"
[minLength]="3"
[maxLength]="30">
</shared-input-text>
<shared-input-text
[control]="expirationControl"
class="pay__expiration"
label="Caducidad"
type="month"
[minLength]="3"
[maxLength]="30">
</shared-input-text>
<shared-input-text
[control]="cvvControl"
class="pay__cvv"
type="number"
label="CVV"
[minLength]="3"
[maxLength]="30">
</shared-input-text>
<shared-input-text
[control]="ownerControl"
class="pay__owner"
label="Nombre del titular"
[minLength]="3"
[maxLength]="30">
</shared-input-text>
</div>
<div class="pay_actions">
<button (click)="onTogleDialogPay()" class="btn btn-back">
Cancelar
</button>
<button class="btn btn-pay" (click)="onSave()">
Pagar: 120 USD
</button>
</div>
</form>
</shared-dialog>
</shared-mail-box>
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { TableModel } from 'src/app/shared/interfaces/table-model.interface';
import { ReservationDetail } from '../../interfaces/reservation-detail.interface';
import { TableAction } from 'src/app/shared/interfaces/table-action.interface';
import { Router } from '@angular/router';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({ @Component({
selector: 'passenger-my-reservations', selector: 'passenger-my-reservations',
...@@ -7,4 +12,99 @@ import { Component } from '@angular/core'; ...@@ -7,4 +12,99 @@ import { Component } from '@angular/core';
}) })
export class MyReservationsComponent { export class MyReservationsComponent {
public actions: TableAction[] = [
{
title: 'Editar',
icon: 'bi-pencil-square',
action: (item) => { console.log(item); }
},
{
title: 'Eliminar',
icon: 'bi-trash',
action: (item) => { console.log(item); }
},
{
title: "Pagar",
icon: "bi-cash",
action: () => { this.onTogleDialogPay(); }
}
];
public tableModel: TableModel[] = [
{
name: 'id',
title: '#',
},
{
name: 'origin',
title: 'Origen',
},
{
name: 'destination',
title: 'Destino',
},
{
name: 'date',
title: 'Fecha',
},
{
name: 'status',
title: 'Estado',
},
{
name: 'cost',
title: 'Costo',
}
]
public tableData: ReservationDetail[] = [
{
id: 1,
origin: 'Bogotá',
destination: 'Medellín',
date: '2021-10-10',
status: 'Activo',
cost: 1500
},
{
id: 2,
origin: 'Bogotá',
destination: 'Medellín',
date: '2021-10-10',
status: 'Activo',
cost: 1500
}
];
cardControl = new FormControl<string>('', [Validators.required]);
expirationControl = new FormControl<string>('', [Validators.required]);
cvvControl = new FormControl<string>('', [Validators.required]);
ownerControl = new FormControl<string>('', [Validators.required]);
formGroup = new FormGroup({
card: this.cardControl,
expiration: this.expirationControl,
cvv: this.cvvControl,
owner: this.ownerControl,
});
onSave(): void {
console.log(this.formGroup.value);
}
// formPay = new FormGroup<string>({
// name: new FormControl<string>('', [Validators.required]),
// });
public openDialogPay: boolean = false;
constructor(private router: Router) {}
onClickItem(item: ReservationDetail): void {
this.router.navigate(['/passenger/reservations', item.id]);
}
onTogleDialogPay(): void {
this.openDialogPay = !this.openDialogPay;
}
} }
...@@ -4,6 +4,7 @@ import { SharedModule } from '../shared/shared.module'; ...@@ -4,6 +4,7 @@ import { SharedModule } from '../shared/shared.module';
import { PassengerRoutingModule } from './passenger-rounting.module'; import { PassengerRoutingModule } from './passenger-rounting.module';
import { HomePageComponent } from './pages/home-page/home-page.component'; import { HomePageComponent } from './pages/home-page/home-page.component';
import { MyReservationsComponent } from './pages/my-reservations/my-reservations.component'; import { MyReservationsComponent } from './pages/my-reservations/my-reservations.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@NgModule({ @NgModule({
...@@ -14,7 +15,9 @@ import { MyReservationsComponent } from './pages/my-reservations/my-reservations ...@@ -14,7 +15,9 @@ import { MyReservationsComponent } from './pages/my-reservations/my-reservations
imports: [ imports: [
CommonModule, CommonModule,
SharedModule, SharedModule,
PassengerRoutingModule PassengerRoutingModule,
FormsModule,
ReactiveFormsModule
], ],
exports: [ exports: [
PassengerRoutingModule PassengerRoutingModule
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
.dialog__content { .dialog__content {
background-color: white; background-color: white;
width: 40%;
max-width: 70vw; max-width: 70vw;
max-height: 80vh; max-height: 80vh;
border-radius: 5px; border-radius: 5px;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.field { .field {
position: relative; position: relative;
padding-top: 12px; padding-top: 10px;
} }
.field input { .field input {
...@@ -13,16 +13,17 @@ ...@@ -13,16 +13,17 @@
font-size: 12px; font-size: 12px;
outline: none; outline: none;
color: #8f8e8e; color: #8f8e8e;
padding-left: 4px;
} }
.field input:focus { .field input:focus {
border-bottom: 1px solid #3f51b5; border-bottom: 3px solid #3f51b5;
} }
.field label { .field label {
position: absolute; position: absolute;
top: 0px; top: 0px;
left: 2px; left: 4px;
pointer-events: none; pointer-events: none;
transition: 0.2s; transition: 0.2s;
font-size: 12px; font-size: 12px;
...@@ -30,14 +31,23 @@ ...@@ -30,14 +31,23 @@
.field input:focus + label { .field input:focus + label {
top: 0; top: 0;
left: 2px; left: 4px;
font-size: 12px; font-size: 12px;
color: #3f51b5; color: #3f51b5;
} }
.field input + label { .field input + label {
top: 0; top: 0;
left: 2px; left: 4px;
font-size: 12px; font-size: 12px;
color: #ccc; color: #ccc;
} }
.box-errors {
line-height: 12px;
}
.errors {
font-size: 10px;
margin-top: -5px;
}
<div class="field field-date-start"> <div class="field field-date-start">
<input [value]="value" type="date" name="name" id="field-date-start"> <input [formControl]="control" [type]="type" name="name" id="field-date-start">
<label for="field-name">{{ name }} *</label> <label for="field-name">{{ name }} *</label>
</div> </div>
<div class="box-errors">
<span *ngIf="isInputValid()" class="text-danger errors">{{ getFieldError() }}</span>
</div>
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({ @Component({
selector: 'shared-input-calendar', selector: 'shared-input-calendar',
...@@ -12,4 +13,28 @@ export class InputCalendarComponent { ...@@ -12,4 +13,28 @@ export class InputCalendarComponent {
@Input() @Input()
public value: string = ''; public value: string = '';
@Input()
public type: string = 'date';
@Input()
public control: FormControl<string | null> = new FormControl<string | null>(null);
isInputValid(): boolean | null {
return this.control? this.control?.errors && this.control?.touched : null;
}
getFieldError(): string | null {
const errors = this.control?.errors || {};
for (const key of Object.keys(errors)) {
switch (key) {
case 'required':
return 'Este campo es requerido';
case 'isValid':
return 'Fecha partida debe ser mayor a la fecha de llegada';
}
}
return null;
}
} }
.field { .pay__field {
display: flex;
flex-direction: column;
position: relative; position: relative;
padding-top: 12px; padding-top: 15px;
box-sizing: border-box;
} }
.field input { .pay__field label {
width: 100%; position: absolute;
border: none; left: 4px;
border-bottom: 1px solid #ccc;
border-radius: 0;
font-size: 12px; font-size: 12px;
color: #ccc;
pointer-events: none;
transition: 0.4s;
}
.pay__field select {
outline: none; outline: none;
border: 0;
border-bottom: 1px solid #ccc;
font-size: 12px;
color: #8f8e8e; color: #8f8e8e;
} }
.field input:focus { select:focus + label {
border-bottom: 1px solid #3f51b5; top: 0;
left: 4px;
color: #3f51b5;
font-size: 12px;
} }
.field label { .pay__field select:focus {
position: absolute; border-bottom: 3px solid #3f51b5;
top: 0px;
left: 2px;
pointer-events: none;
transition: 0.2s;
font-size: 12px;
color: #ccc;
} }
.field input:focus + label { .pay__field .label-top {
top: 0; top: 0;
left: 2px; left: 4px;
font-size: 12px; font-size: 12px;
color: #3f51b5;
} }
.field input + label { .box-errors {
top: 0; line-height: 10px;
left: 2px;
font-size: 12px;
color: #ccc;
} }
.field-select { .errors {
width: 100%; font-size: 10px;
outline: none;
border: 0;
border-bottom: 1px solid #ccc;
font-size: 12px;
color: #8f8e8e;
} }
<div class="field field-date-start"> <div class="field__container">
<select class="field-select" type="date" name="name" id="field-date-start"> <div class="pay__field pay__card">
<option value="1"></option> <select [formControl]="control" [value]="control.value" [ngClass]="['field-select']" id="field-date-start">
</select> <option *ngFor="let item of data" [value]="item.value">{{ item.text }}</option>
<label for="field-name">{{ name }} *</label> </select>
<label [ngClass]="[control.value ? 'label-top' : '']" for="field-name">{{ name }} *</label>
</div>
<div class="box-errors">
<span *ngIf="isInputValid()" class="text-danger errors">{{ getFieldError() }}</span>
</div>
</div> </div>
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { DropdownElement } from '../../interfaces/dropdown-element-interface';
import { FormControl } from '@angular/forms';
@Component({ @Component({
selector: 'shared-input-dropdown', selector: 'shared-input-dropdown',
...@@ -8,5 +10,29 @@ import { Component, Input } from '@angular/core'; ...@@ -8,5 +10,29 @@ import { Component, Input } from '@angular/core';
export class InputDropdownComponent { export class InputDropdownComponent {
@Input() @Input()
public name: string = ''; public name: string | null = null;
@Input()
public data: DropdownElement[] = []
@Input()
public control: FormControl<string | null> = new FormControl<string | null>(null);
isInputValid(): boolean | null {
return this.control? this.control?.errors && this.control?.touched : null;
}
getFieldError(): string | null {
const errors = this.control?.errors || {};
for (const key of Object.keys(errors)) {
switch (key) {
case 'required':
return 'Este campo es requerido';
case 'isValid':
return 'El aeropuerto de partida no puede ser igual al de destino';
}
}
return null;
}
} }
.input__container { .input__container {
position: relative; position: relative;
display: flex; display: flex;
padding-top: 20px; padding-top: 12px;
} }
.input__container button { .input__container button {
...@@ -9,16 +9,18 @@ ...@@ -9,16 +9,18 @@
border: 0; border: 0;
background-color: transparent; background-color: transparent;
right: 0; right: 0;
top: 20px; bottom: 0;
font-size: 12px; font-size: 12px;
} }
.input__container label { .input__container label {
position: absolute; position: absolute;
top: 0; bottom: 1px;
left: 2px; left: 4px;
font-size: 12px; font-size: 12px;
color: #ccc; color: #ccc;
pointer-events: none;
transition: 0.4s;
} }
.input__container input { .input__container input {
...@@ -28,4 +30,18 @@ ...@@ -28,4 +30,18 @@
outline: none; outline: none;
font-size: 12px; font-size: 12px;
color: #8f8e8e; color: #8f8e8e;
padding-left: 4px;
}
input:focus + label {
top: 0;
left: 4px;
font-size: 12px;
color: #3f51b5;
}
.input__container .label-top {
top: 0;
left: 4px;
font-size: 12px;
} }
<div class="input__container"> <div class="input__container">
<input [value]="value" [readOnly]="true" type="text" [id]="name"> <input (focus)="isActivated = true" [readOnly]="true" [value]="value" type="text" [id]="name" #input>
<label [for]="name">{{ name }} *</label> <label [ngClass]="[input.value.length > 0 ? 'label-top' : '']" [for]="name">{{ name }} *</label>
<button (click)="openDialog()" type="submit"> <button (click)="openDialog()" type="submit">
<i class="bi bi-search"></i> <i class="bi bi-search"></i>
</button> </button>
</div> </div>
<div class="box-errors">
<span *ngIf="isInputValid()" class="text-danger errors">{{ getFieldError() }}</span>
</div>
<shared-dialog [isActivated]="isActivated" title="Buscador" (onCloseDialog)="closeDialog()"> <shared-dialog [isActivated]="isActivated" title="Buscador" (onCloseDialog)="closeDialog()">
<shared-table-search [tableModel]="tableModel" [tableData]="tableData" (onSelect)="selectItem($event)"></shared-table-search> <shared-table-search [tableModel]="tableModel" [tableData]="tableData" (onSelect)="selectItem($event)"></shared-table-search>
</shared-dialog> </shared-dialog>
...@@ -18,7 +18,7 @@ export class InputSelectorComponent { ...@@ -18,7 +18,7 @@ export class InputSelectorComponent {
public tableData: any[] = []; public tableData: any[] = [];
@Input() @Input()
public value: string = ''; public value: string | null = null;
@Input() @Input()
public name: string = ''; public name: string = '';
...@@ -26,7 +26,6 @@ export class InputSelectorComponent { ...@@ -26,7 +26,6 @@ export class InputSelectorComponent {
@Output() @Output()
public onSelect: EventEmitter<any> = new EventEmitter<any>(); public onSelect: EventEmitter<any> = new EventEmitter<any>();
closeDialog(): void { closeDialog(): void {
this.isActivated = false; this.isActivated = false;
} }
...@@ -39,4 +38,12 @@ export class InputSelectorComponent { ...@@ -39,4 +38,12 @@ export class InputSelectorComponent {
this.onSelect.emit(item); this.onSelect.emit(item);
this.closeDialog(); this.closeDialog();
} }
isInputValid(): boolean | null {
return this.value ? true : false;
}
getFieldError(): string | null {
return this.isInputValid() ? null : 'Este campo es requerido';
}
} }
.pay_fields {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.pay_actions {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 10px;
margin-top: 20px;
border-top: 1px solid gray;
}
.pay__field {
display: flex;
flex-direction: column;
position: relative;
padding-top: 15px;
box-sizing: border-box;
height: 38.8px;
}
.pay__field label {
position: absolute;
left: 4px;
font-size: 12px;
color: #ccc;
pointer-events: none;
transition: 0.4s;
}
.pay__field input {
outline: none;
border: 0;
border-bottom: 1px solid #ccc;
font-size: 12px;
padding-left: 4px;
color: #8f8e8e;
}
input:focus + label {
top: 0;
left: 4px;
color: #3f51b5;
font-size: 12px;
}
.pay__field input:focus {
border-bottom: 3px solid #3f51b5;
}
.pay__field .label-top {
top: 0;
left: 4px;
font-size: 12px;
}
.pay__card, .pay__owner {
grid-column: 1 / 3;
}
.input__validation {
display: flex;
}
.input__validation span {
font-size: 10px;
}
.field__container {
height: 53.8px;
}
.pay__field input::-webkit {
display: none;
}
<div class="field__container">
<div class="pay__field pay__card">
<input [formControl]="control!" [type]="type" [name]="name" [id]="name" #input>
<label [ngClass]="input.value.length > 0 ? 'label-top' : ''" [for]="name">{{ label }}</label>
</div>
<div [ngClass]="['input__validation', isInputValid() && input.value.length > 0 ? 'justify-content-between' : input.value.length > 0 ? 'justify-content-end' : 'justify-content-start']">
<span *ngIf="isInputValid()" class="text-danger">{{ getFieldError() }}</span>
<span *ngIf="input.value.length > 0">{{ control?.value?.length}} / {{ maxLength }}</span>
</div>
</div>
import { Component, Input, OnInit } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
@Component({
selector: 'shared-input-text',
templateUrl: './input-text.component.html',
styleUrls: ['./input-text.component.css']
})
export class InputTextComponent implements OnInit {
@Input()
public label: string = '';
@Input()
public name: string = '';
@Input()
public type: string = 'text';
@Input()
public maxLength?: number;
@Input()
public minLength?: number;
@Input()
public control?: FormControl<string | null>;
ngOnInit(): void {
if (!this.maxLength) this.maxLength = 999;
if (!this.minLength) this.minLength = 999;
}
isInputValid(): boolean | null {
return this.control? this.control?.errors && this.control?.touched : null;
}
getFieldError(): string | null {
const errors = this.control?.errors || {};
for (const key of Object.keys(errors)) {
switch (key) {
case 'required':
return 'Este campo es requerido';
case 'email':
return 'El email no es valido';
case 'minlength':
return `El minimo de caracteres es ${errors[key].requiredLength}`;
case 'maxlength':
return `El maximo de caracteres es ${errors[key].requiredLength}`;
}
}
return null;
}
}
.text-error { .text-error {
font-size: 12px; font-size: 10px;
color: red; color: red;
} }
......
...@@ -25,6 +25,11 @@ ...@@ -25,6 +25,11 @@
background-color: white; background-color: white;
} }
.panel__navigation-header-title {
font-size: 12px;
color: #ccc;
}
.panel__content { .panel__content {
border: 1px solid #e0e0e0; border: 1px solid #e0e0e0;
padding: 8px 16px; padding: 8px 16px;
......
<div class="panel__navigation"> <div class="panel__navigation">
<div class="panel__navigation-headers"> <div class="panel__navigation-headers">
<div *ngFor="let item of panelItems"> <div *ngFor="let item of panelItems">
<div class="panel__navigation-header" (click)="onChangePanelItem(item)" [ngClass]="[item.id === panelItemActive?.id ? 'active' : '']"> <div [ngClass]="['panel__navigation-header', activeItem(item)]" *ngIf="item.isVisible" (click)="onChangePanelItem(item)">
<span class="panel__navigation-header-title">{{ item.title }}</span> <span class="panel__navigation-header-title">{{ item.title }}</span>
</div> </div>
</div> </div>
......
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { PanelItem } from 'src/app/supervisor/interfaces/panel-item.interface'; import { PanelItem } from 'src/app/supervisor/interfaces/panel-item.interface';
@Component({ @Component({
...@@ -6,22 +6,21 @@ import { PanelItem } from 'src/app/supervisor/interfaces/panel-item.interface'; ...@@ -6,22 +6,21 @@ import { PanelItem } from 'src/app/supervisor/interfaces/panel-item.interface';
templateUrl: './panel-menu.component.html', templateUrl: './panel-menu.component.html',
styleUrls: ['./panel-menu.component.css'] styleUrls: ['./panel-menu.component.css']
}) })
export class PanelMenuComponent implements OnInit { export class PanelMenuComponent {
ngOnInit(): void {
this.panelItems.length > 0 ? this.panelItemActive = this.panelItems[0] : null;
}
@Input() @Input()
public panelItems: PanelItem[] = []; public panelItems: PanelItem[] = [];
@Input()
public panelItemActive?: PanelItem;
@Output() @Output()
public onChangeItemActive: EventEmitter<PanelItem> = new EventEmitter(); public onChangeItemActive: EventEmitter<PanelItem> = new EventEmitter();
onChangePanelItem(item: PanelItem) { onChangePanelItem(item: PanelItem) {
this.panelItems.forEach((item: PanelItem) => item.isActive = false);
item.isActive = true;
this.onChangeItemActive.emit(item); this.onChangeItemActive.emit(item);
} }
activeItem(item: PanelItem): string {
return item.isActive ? 'active' : '';
}
} }
.btn-select {
background-color: white;
border: 1px solid #676a6c;
color: #676a6c;
border-radius: 4px;
width: 24px;
height: 24px;
padding: 0;
}
.table-header {
font-size: 12px;
}
.table-header th {
color: #676a6c;
font-weight: 600;
}
.table-body {
font-size: 14px;
}
.table-item {
padding: 10px 25px;
}
.table-body td {
color: #676a6c;
font-weight: 400;
}
.content {
max-height: 450px;
height: 100%;
width: 100%;
overflow: hidden;
box-shadow: 0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;
}
/* Actions */
.btn-action-table {
background-color: white;
color: #676a6c;
width: auto;
border-radius: 4px;
border: 1px solid #e4e4e4;
font-weight: 200;
padding-left: 5px;
padding-right: 5px;
height: 24px;
font-size: 10px;
display: flex;
align-items: center;
}
.btn-action-table span {
color: #676a6c;
}
.btn-action-table i {
margin-right: 6px;
}
<div class="d-flex gap-2">
<div class="content">
<shared-table-filter></shared-table-filter>
<div class="table-container mt-2">
<table class="table table-striped mb-0">
<thead class="pt-4" >
<tr class="table-header">
<th *ngFor="let item of tableModel" class="table-item">{{ item['title'] }}</th>
<th class="table-item"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tableData; let i = index" class="table-body">
<td *ngFor="let item of tableModel" (click)="onClickItem(data)" class="table-item">{{ data[item['name']] }}</td>
<td class="table-item">
<div class="d-flex justify-content-end">
<button *ngFor="let action of tableAction" (click)="action.action(data)" class="btn btn-action-table">
<i [ngClass]="['bi', action.icon]"></i>
<span>{{ action.title }}</span>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="more-actions">
<ng-content select=".more-actions"></ng-content>
</div>
</div>
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { TableModel } from '../../interfaces/table-model.interface';
import { TableAction } from '../../interfaces/table-action.interface';
import { Router } from '@angular/router';
@Component({
selector: 'shared-table-custom',
templateUrl: './table-custom.component.html',
styleUrls: ['./table-custom.component.css']
})
export class TableCustomComponent {
@Input()
public tableAction: TableAction[] = [];
@Input()
public onClickItem: (item: any) => void = (item) => { console.log(item); };
@Input()
public tableData: any[] = [];
@Input()
public tableModel: TableModel[] = [];
@Output()
public onSelect: EventEmitter<any[]> = new EventEmitter<any[]>();
constructor(private router: Router) { }
onSelectRow(row: any): void {
this.onSelect.emit(row);
}
}
export interface DropdownElement {
text: string;
value: string;
}
export interface TableAction {
title: string;
icon: string;
action: (row: any) => void;
}
...@@ -18,6 +18,9 @@ import { TablePaginatorComponent } from './components/table-paginator/table-pagi ...@@ -18,6 +18,9 @@ import { TablePaginatorComponent } from './components/table-paginator/table-pagi
import { InputCalendarComponent } from './components/input-calendar/input-calendar.component'; import { InputCalendarComponent } from './components/input-calendar/input-calendar.component';
import { InputDropdownComponent } from './components/input-dropdown/input-dropdown.component'; import { InputDropdownComponent } from './components/input-dropdown/input-dropdown.component';
import { PanelMenuComponent } from './components/panel-menu/panel-menu.component'; import { PanelMenuComponent } from './components/panel-menu/panel-menu.component';
import { TableCustomComponent } from './components/table-custom/table-custom.component';
import { InputTextComponent } from './components/input-text/input-text.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
...@@ -39,11 +42,15 @@ import { PanelMenuComponent } from './components/panel-menu/panel-menu.component ...@@ -39,11 +42,15 @@ import { PanelMenuComponent } from './components/panel-menu/panel-menu.component
TablePaginatorComponent, TablePaginatorComponent,
InputCalendarComponent, InputCalendarComponent,
InputDropdownComponent, InputDropdownComponent,
PanelMenuComponent PanelMenuComponent,
TableCustomComponent,
InputTextComponent,
], ],
imports: [ imports: [
CommonModule, CommonModule,
RouterModule, RouterModule,
FormsModule,
ReactiveFormsModule
], ],
exports: [ exports: [
SidebarComponent, SidebarComponent,
...@@ -61,7 +68,9 @@ import { PanelMenuComponent } from './components/panel-menu/panel-menu.component ...@@ -61,7 +68,9 @@ import { PanelMenuComponent } from './components/panel-menu/panel-menu.component
TableSearchComponent, TableSearchComponent,
InputCalendarComponent, InputCalendarComponent,
InputDropdownComponent, InputDropdownComponent,
PanelMenuComponent PanelMenuComponent,
TableCustomComponent,
InputTextComponent
] ]
}) })
export class SharedModule { } export class SharedModule { }
export interface Aircraft {
id: number;
name: string;
model: string;
manufacturer: string;
}
export interface CostFlight {
id: number;
dateStart: string;
dateEnd: string;
aircraftType: string;
aircraftTypeId: number;
cost: number;
}
import { CostFlight } from "./cost-flight.interface";
import { ScaleFlight } from "./scale.interface";
export interface Flight { export interface Flight {
id: number; id: number;
airline: string;
airlineId: number;
aircraftType: string;
aircraftTypeId: number;
airportOrigin: string; airportOrigin: string;
airportOriginId: number;
airportDestination: string; airportDestination: string;
airportDestinationId: number;
dateStart: string; dateStart: string;
dateEnd: string; dateEnd: string;
scales: ScaleFlight[];
costs: CostFlight[];
} }
export interface PanelItem { export interface PanelItem {
id: number; name: string;
title: string; title: string;
isActive: boolean;
isVisible: boolean;
} }
export interface ScaleFlight {
id: number;
airportOrigin: string;
airportOriginId: number;
airportDestination: string;
airportDestinationId: number;
dateStart: string;
dateEnd: string;
}
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
pointer-events: none; pointer-events: none;
transition: 0.2s; transition: 0.2s;
font-size: 12px; font-size: 12px;
color: #ccc;
} }
.field input:focus + label { .field input:focus + label {
...@@ -59,7 +60,7 @@ ...@@ -59,7 +60,7 @@
font-weight: 500; font-weight: 500;
padding-bottom: 8px; padding-bottom: 8px;
margin-bottom: 16px; margin-bottom: 16px;
border-bottom: 1px solid #8f8e8e; border-bottom: 1px solid #ccc;
} }
.general__fields, .credentials__fields { .general__fields, .credentials__fields {
......
.section { .section {
margin-top: 20px; margin-top: 20px;
margin-bottom: 15px; margin-bottom: 25px;
} }
.section__title { .section__title {
padding-bottom: 10px; padding-bottom: 10px;
margin-bottom: 20px; margin-bottom: 10px;
font-size: 14px; font-size: 14px;
border-bottom: 1px solid #e0e0e0; border-bottom: 1px solid #e0e0e0;
} }
...@@ -15,3 +15,57 @@ ...@@ -15,3 +15,57 @@
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
gap: 20px; gap: 20px;
} }
.btn-add {
border: 1px solid #e0e0e0;
border-radius: 50%;
width: 42px;
height: 42px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 0 5px 0 #0000001f;
}
.btn-save {
background-color: #1ab394;
border: 1px solid #1ab394;
color: white;
font-size: 12px;
margin-bottom: 10px;
}
/* Configuracion de escalas */
.fields {
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: 16px;
}
.actions {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 10px;
margin-top: 30px;
border-top: 1px solid #e0e0e0;
}
.btn-cancel {
border: 1px solid #ccc;
color: #ccc;
font-size: 12px;
}
.btn-accept {
border: 1px solid #1ab394;
background-color: #1ab394;
color: white;
font-size: 12px;
}
.flight__type, .flight__optional{
grid-column: 1 / 3;
}
...@@ -5,37 +5,96 @@ ...@@ -5,37 +5,96 @@
icon="bi-airplane" icon="bi-airplane"
[btnBack]="true"> [btnBack]="true">
<!-- General Information -->
<section class="section"> <section class="section">
<h4 class="section__title">Información general</h4> <h4 class="section__title">Información general</h4>
<div class="section__fields"> <div class="row gap-2">
<shared-input-selector
name="Aeropuerto origen"
[tableModel]="flightModel"
[tableData]="airports">
</shared-input-selector>
<shared-input-selector
name="Aeropuerto destino"
[tableModel]="flightModel"
[tableData]="airports">
</shared-input-selector>
<shared-input-calendar name="Fecha de partida"></shared-input-calendar>
<shared-input-calendar name="Fecha de llegada"></shared-input-calendar>
<shared-input-selector <shared-input-selector
class="col-12"
name="Aerolinea" name="Aerolinea"
(onSelect)="onSelectAirline($event)"
[value]="airlineValue"
[tableModel]="airlineModel" [tableModel]="airlineModel"
[tableData]="airlines"> [tableData]="airlines">
</shared-input-selector> </shared-input-selector>
<shared-input-dropdown name="Tipo de aeronave"></shared-input-dropdown> <shared-input-dropdown
name="Tipo de avion"
class="col-12"
[control]="aircraftTypeIdControl"
[data]="aircrafts">
</shared-input-dropdown>
</div> </div>
</section> </section>
<section class="section"> <section class="section">
<h4 class="section__title">Configuración de vuelo</h4> <h4 class="section__title">Configuración de vuelo</h4>
<shared-panel-menu [panelItems]="panelItems" [panelItemActive]="panelItemActive" (onChangeItemActive)="onChangeItemActive($event)">
<div [ngSwitch]="panelItemActive.id"> <shared-panel-menu
<div *ngSwitchCase="1">Escalas</div> [panelItems]="panelMenu"
<div *ngSwitchCase="2">Costos</div> (onChangeItemActive)="onchangeItemActive($event)">
<div [ngSwitch]="panelMenuActive.name">
<div *ngSwitchCase="'scales'">
<shared-table-custom
[tableModel]="scaleModel"
[tableData]="scales">
<div class="more-actions">
<button (click)="onTogleDialogScale()" class="btn btn-add">
<i class="bi bi-plus"></i>
</button>
</div>
</shared-table-custom>
<shared-dialog
title="Configurar escala"
[isActivated]="openDialogScale"
(onCloseDialog)="onTogleDialogScale()">
<div class="fields">
<shared-input-dropdown name="Aeropuero de partida" [control]="airportIdOriginControl" [data]="airports"></shared-input-dropdown>
<shared-input-dropdown name="Aeropuero de llegada" [control]="airportIdDestinationControl" [data]="airports"></shared-input-dropdown>
<shared-input-calendar name="Fecha de partida" type="datetime-local" [control]="dateStartControl"></shared-input-calendar>
<shared-input-calendar name="Fecha de llegada" type="datetime-local" [control]="dateEndControl"></shared-input-calendar>
</div>
<div class="actions">
<button (click)="onTogleDialogScale()" class="btn btn-cancel">Cancelar</button>
<button [disabled]="scalesForm.invalid" (click)="onSaveScale()" class="btn btn-accept">Aceptar</button>
</div>
</shared-dialog>
</div>
<div *ngSwitchCase="'costs'">
<shared-table-custom
[tableModel]="costModel"
[tableData]="costs">
<div class="more-actions">
<button (click)="onTogleDialogCost()" class="btn btn-add">
<i class="bi bi-plus"></i>
</button>
</div>
</shared-table-custom>
<shared-dialog
title="Configurar costos"
(onCloseDialog)="onTogleDialogCost()"
[isActivated]="openDialogCost">
<div class="fields">
<shared-input-calendar name="Fecha de inicio" type="date" [control]="dateStartCostControl"></shared-input-calendar>
<shared-input-calendar name="Fecha de fin" type="date" [control]="dateEndCostControl"></shared-input-calendar>
<shared-input-dropdown name="Tipo de aeronave" [data]="aircrafts" [control]="aircraftIdControl"></shared-input-dropdown>
<shared-input-text label="Costo *" type="number" [control]="costControl"></shared-input-text>
</div>
<div class="actions">
<button (click)="onTogleDialogCost()" class="btn btn-cancel">Cancelar</button>
<button [disabled]="costsForm.invalid" (click)="onSaveCost()" class="btn btn-accept">Aceptar</button>
</div>
</shared-dialog>
</div>
</div> </div>
</shared-panel-menu> </shared-panel-menu>
</section> </section>
<div class="actions d-flex justify-content-end">
<button [disabled]="isInvalidFlightConfiguration()" (click)="onSaveFlightConfig()" class="btn btn-save">
<i class="bi bi-save me-1"></i>
<span>Guardar</span>
</button>
</div>
</shared-mail-box> </shared-mail-box>
import { Component } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ResultDelete } from 'src/app/shared/interfaces/result-delete.interface'; import { ResultDelete } from 'src/app/shared/interfaces/result-delete.interface';
import { TableModel } from 'src/app/shared/interfaces/table-model.interface'; import { TableModel } from 'src/app/shared/interfaces/table-model.interface';
import { Flight } from '../../interfaces/flight.interface'; import { Flight } from '../../interfaces/flight.interface';
import { FlightService } from '../../services/flight.service';
@Component({ @Component({
selector: 'app-flight-config', selector: 'app-flight-config',
templateUrl: './flight-config.component.html', templateUrl: './flight-config.component.html',
styleUrls: ['./flight-config.component.css'] styleUrls: ['./flight-config.component.css']
}) })
export class FlightConfigComponent { export class FlightConfigComponent implements OnInit {
tableData: Flight[] = [ tableData: Flight[] = [];
{
id: 1,
airportOrigin: 'Aeropuerto 1',
airportDestination: 'Aeropuerto 2',
dateStart: '2021-10-10',
dateEnd: '2021-10-10'
},
{
id: 2,
airportOrigin: 'Aeropuerto 1',
airportDestination: 'Aeropuerto 2',
dateStart: '2021-10-10',
dateEnd: '2021-10-10'
}
];
tableModel: TableModel[] = [ tableModel: TableModel[] = [
{ {
name: 'id', name: 'id',
...@@ -49,8 +35,17 @@ export class FlightConfigComponent { ...@@ -49,8 +35,17 @@ export class FlightConfigComponent {
} }
]; ];
getFlights(): void { constructor(private flightService: FlightService) {}
ngOnInit(): void {
this.getFlights();
}
getFlights(): void {
this.flightService.getAll()
.subscribe(flights => {
this.tableData = flights;
});
} }
onDeleteAgent(result: ResultDelete): void { onDeleteAgent(result: ResultDelete): void {
......
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Aircraft } from '../interfaces/aircraft.interface';
@Injectable({providedIn: 'root'})
export class AircraftService {
constructor(private http: HttpClient) { }
URL_BASE: string = 'http://localhost:3000/aircrafts';
getAll(): Observable<Aircraft[]> {
return this.http.get<Aircraft[]>(this.URL_BASE);
}
}
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Flight } from '../interfaces/flight.interface';
@Injectable({providedIn: 'root'}) @Injectable({providedIn: 'root'})
export class FlightService { export class FlightService {
constructor(private httpClient: HttpClient) { } constructor(private http: HttpClient) { }
URL_BASE: string = 'http://localhost:3000/flights';
getAll(): Observable<Flight[]> {
return this.http.get<Flight[]>(this.URL_BASE);
}
searchFlight(origin: string, destination: string, dateStart: string): Observable<Flight[]> {
return this.http.get<Flight[]>(`${this.URL_BASE}?airportOrigin_like=${origin}&airportDestination_like=${destination}&dateStart_like=${dateStart}`);
}
getById(id: number): Observable<Flight> {
return this.http.get<Flight>(`${this.URL_BASE}/${id}`);
}
save(flight: Flight): Observable<Flight> {
return this.http.post<Flight>(this.URL_BASE, flight);
}
update(flight: Flight): Observable<Flight> {
return this.http.put<Flight>(`${this.URL_BASE}/${flight.id}`, flight);
}
delete(id: number): Observable<Flight> {
return this.http.delete<Flight>(`${this.URL_BASE}/${id}`);
}
} }
...@@ -17,6 +17,11 @@ const routes: Routes = [ ...@@ -17,6 +17,11 @@ const routes: Routes = [
title: 'Configuración de vuelos', title: 'Configuración de vuelos',
component: FlightAddComponent component: FlightAddComponent
}, },
{
path: 'flight-config/edit/:id',
title: 'Configuración de vuelos',
component: FlightAddComponent
},
{ {
path: 'agents', path: 'agents',
title: 'Agentes', title: 'Agentes',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment