Commit dc3fe90a authored by Heber Cordova's avatar Heber Cordova

feat: added styles and interfaces of booking manager

parent 493f1d49
......@@ -45,6 +45,37 @@
"email": "ncordova@gmail.com",
"password": "hebeer123",
"id": 6
},
{
"name": "Heber",
"lastname": "Cordova",
"detail": "",
"email": "correo@correo.com",
"password": "correo123",
"id": 7
}
],
"bookings": [
{
"id": 1,
"passengerId": 1,
"typeTicket": "Economico",
"class": "Economico",
"status": "Pendiente"
},
{
"id": 2,
"passengerId": 2,
"typeTicket": "Economico",
"class": "Economico",
"status": "Pendiente"
},
{
"id": 3,
"passengerId": 3,
"typeTicket": "Ejecutivo",
"class": "Ejecutivo",
"status": "Pendiente"
}
],
"typeDocuments": [
......@@ -64,7 +95,7 @@
"passengers": [
{
"name": "Michael",
"secondName": "asd",
"secondName": "",
"lastname": "Johnson",
"country": "USA",
"city": "Anytown",
......@@ -75,28 +106,28 @@
"id": 1
},
{
"id": 2,
"name": "Jennifer",
"secondName": "asd",
"secondName": "",
"lastname": "Williams",
"country": "Canada",
"city": "Somewhere",
"address": "456 Elm Avenue",
"phone": "+987654321",
"email": "jennifer.williams@example.com",
"address": "456 Elm Avenue",
"city": "Somewhere",
"country": "Canada",
"password": "123456"
"password": "123456",
"id": 2
},
{
"id": 3,
"name": "Sarah",
"secondName": "ads",
"secondName": "",
"lastname": "Brown",
"country": "UKS",
"city": "Everytown",
"address": "789 Oak Road",
"phone": "+1122334455",
"email": "sarah.brown@example.com",
"address": "789 Oak Road",
"city": "Everytown",
"country": "UK",
"password": "123456"
"password": "123456",
"id": 3
},
{
"name": "Heber",
......@@ -325,5 +356,33 @@
"id": 3,
"name": "Anulado"
}
],
"ticketTypes": [
{
"id": 1,
"name": "Economico"
},
{
"id": 2,
"name": "Ejecutivo"
},
{
"id": 3,
"name": "Primera Clase"
}
],
"ticketClasses": [
{
"id": 1,
"name": "Economico"
},
{
"id": 2,
"name": "Ejecutivo"
},
{
"id": 3,
"name": "Primera Clase"
}
]
}
\ No newline at end of file
......@@ -4,6 +4,7 @@ import { AgentHomePageComponent } from './pages/home-page/home-page.component';
import { PassengersComponent } from './pages/passengers/passengers.component';
import { ReservationsComponent } from './pages/reservations/reservations.component';
import { PassengerAddComponent } from './pages/passenger-add/passenger-add.component';
import { ReservationAddComponent } from './pages/reservation-add/reservation-add.component';
const routes: Routes = [
{
......@@ -30,7 +31,12 @@ const routes: Routes = [
path: 'reservations',
title: 'Reservas',
component: ReservationsComponent
}
},
{
path: 'reservations/new',
title: 'Reservas',
component: ReservationAddComponent
}
]
},
];
......
......@@ -8,13 +8,21 @@ import { ReservationsComponent } from './pages/reservations/reservations.compone
import { PassengerAddComponent } from './pages/passenger-add/passenger-add.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { ReservationAddComponent } from './pages/reservation-add/reservation-add.component';
import { BaseInformationComponent } from './components/base-information/base-information.component';
import { FlightInformationComponent } from './components/flight-information/flight-information.component';
import { SummaryInformationComponent } from './components/summary-information/summary-information.component';
@NgModule({
declarations: [
AgentHomePageComponent,
PassengersComponent,
ReservationsComponent,
PassengerAddComponent
PassengerAddComponent,
ReservationAddComponent,
BaseInformationComponent,
FlightInformationComponent,
SummaryInformationComponent
],
imports: [
CommonModule,
......
.field label {
font-size: 12px;
color: #ccc;
}
.field-select {
width: 100%;
outline: none;
border: 0;
border-bottom: 1px solid #ccc;
font-size: 12px;
color: #8f8e8e;
}
.base__fields, .search__fields {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.field-passenger {
grid-column: 1 / 3;
}
<div class="base__fields">
<div class="field field-passenger">
<shared-input-selector name="Pasajeros" [value]="passengerSelectedName" [tableModel]="passengerModel" [tableData]="passengers" (onSelect)="onSelectedPassenger($event)"></shared-input-selector>
</div>
<div class="field">
<label for="field-ticket">Tipo de ticket *</label>
<select class="field-select" name="name" id="field-ticket">
<option *ngFor="let type of ticketTypes" [value]="type.id">{{ type.name }}</option>
</select>
</div>
<div class="field">
<label for="field-class">Clase de vuelo *</label>
<select class="field-select" name="name" id="field-class">
<option *ngFor="let class of flightClass" [value]="class.id">{{ class.name }}</option>
</select>
</div>
</div>
import { Component, OnInit } from '@angular/core';
import { Passenger } from '../../interfaces/passenger.interface';
import { TicketType } from '../../interfaces/ticket-type.interface';
import { FlightClass } from '../../interfaces/flight-class.interface';
import { TableModel } from 'src/app/shared/interfaces/table-model.interface';
import { PassengerService } from '../../services/passenger.service';
import { TicketTypeService } from '../../services/ticket-type.service';
import { TicketClassService } from '../../services/ticket-class.service';
@Component({
selector: 'agent-base-information',
templateUrl: './base-information.component.html',
styleUrls: ['./base-information.component.css']
})
export class BaseInformationComponent implements OnInit {
public passengerModel: TableModel[] = [
{
name: 'id',
title: '#'
},
{
name: 'name',
title: 'Nombre'
},
{
name: 'secondName',
title: 'Segundo Nombre'
},
{
name: 'lastname',
title: 'Apellido'
},
{
name: 'email',
title: 'Correo'
},
{
name: 'phone',
title: 'Teléfono'
}
];
constructor(private passengersService: PassengerService,
private ticketTypesService: TicketTypeService,
private ticketClassService: TicketClassService) { }
ngOnInit(): void {
this.passengersService.getAll()
.subscribe(passengers => this.passengers = passengers);
this.ticketTypesService.getAll()
.subscribe(ticketTypes => this.ticketTypes = ticketTypes);
this.ticketClassService.getAll()
.subscribe(flightClass => this.flightClass = flightClass);
}
public passengers: Passenger[] = [];
public ticketTypes: TicketType[] = [];
public flightClass: FlightClass[] = [];
public passengerSelected?: Passenger;
get passengerSelectedName(): string {
return this.passengerSelected ? `${this.passengerSelected.name} ${this.passengerSelected.lastname}` : '';
}
onSelectedPassenger(passenger: Passenger): void {
this.passengerSelected = passenger;
}
}
/* Input */
.field {
position: relative;
padding-top: 12px;
}
.field input {
width: 100%;
border: none;
border-bottom: 1px solid #ccc;
border-radius: 0;
font-size: 12px;
outline: none;
color: #8f8e8e;
}
.field input:focus {
border-bottom: 1px solid #3f51b5;
}
.field label {
position: absolute;
top: 0px;
left: 2px;
pointer-events: none;
transition: 0.2s;
font-size: 12px;
}
.field input:focus + label {
top: 0;
left: 2px;
font-size: 12px;
color: #3f51b5;
}
.field input + label {
top: 0;
left: 2px;
font-size: 12px;
color: #ccc;
}
.search__fields {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.field-date-start {
grid-column: 1 / 3;
}
/* Form Group [search] */
.form-group {
padding: 20px;
margin: 10px 40px;
border: 1px solid #e6e6e6;
position: relative;
}
.form-group::before {
position: absolute;
content: 'Buscador';
padding: 5px;
top: -15px;
left: 17px;
background-color: white;
font-size: 12px;
}
.search__actions {
padding-top: 16px;
display: flex;
justify-content: end;
}
.btn-filtrar {
background-color: #1ab394;
border: #1ab394;
font-size: 12px;
color: white;
width: 100px;
}
<div class="d-flex flex-column">
<form class="form-group flex-fill">
<div class="search__fields">
<div class="field field-airport-origin">
<shared-input-selector
name="Aeropuerto origen"
[tableModel]="airportModel"
[tableData]="airports"
(onSelect)="onSelectAirportOrigin($event)"
[value]="airportValueNameOrigin">
</shared-input-selector>
</div>
<div class="field field-airport-destination">
<shared-input-selector
name="Aeropuerto destino"
[tableModel]="airportModel"
[tableData]="airports"
(onSelect)="onSelectAirportDestination($event)"
[value]="airportValueNameDestination">
</shared-input-selector>
</div>
<div class="field field-date-start">
<input type="date" name="name" id="field-date-start">
<label for="field-name">Fecha partida *</label>
</div>
</div>
<div class="search__actions">
<button class="btn btn-filtrar">Filtrar</button>
</div>
</form>
<shared-table-search [tableModel]="flightModel" [tableData]="[]"></shared-table-search>
</div>
import { Component, Input, OnInit } from '@angular/core';
import { TableModel } from 'src/app/shared/interfaces/table-model.interface';
import { AirportsService } from '../../services/airports.service';
import { Airport } from '../../interfaces/airport.interface';
@Component({
selector: 'agent-flight-information',
templateUrl: './flight-information.component.html',
styleUrls: ['./flight-information.component.css']
})
export class FlightInformationComponent implements OnInit {
@Input()
public flightModel: TableModel[] = [];
public airportModel: TableModel[] = [
{
name: 'id',
title: '#'
},
{
name: 'name',
title: 'Nombre'
},
{
name: 'location',
title: 'Ubicacion'
},
{
name: 'code',
title: 'Codigo'
}
];
public airports: Airport[] = [];
public airportOrigin?: Airport;
public airportDestination?: Airport;
constructor(private airpotsService: AirportsService) { }
ngOnInit(): void {
this.airpotsService.getAll()
.subscribe(airports => this.airports = airports);
}
get airportValueNameOrigin(): string {
return this.airportOrigin?.name || '';
}
get airportValueNameDestination(): string {
return this.airportDestination?.name || '';
}
onSelectAirportOrigin(airport: Airport): void {
this.airportOrigin = airport;
}
onSelectAirportDestination(airport: Airport): void {
this.airportDestination = airport;
}
}
/* Section resumen */
.base__header, .flight__header, .detail__header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 18px;
padding-bottom: 8px;
border-bottom: 1px solid #e6e6e6;
}
.base__header-title, .flight__header-title, .detail__header-title {
font-size: 14px;
font-weight: 700;
margin-bottom: 0;
}
.base__header-btn, .flight__header-btn {
font-size: 12px;
background-color: white;
border: 1px solid #8f8e8e;
color: #8f8e8e;
border-radius: 4px;
cursor: pointer;
}
.base__content, .flight__content, .detail__content {
margin-top: 10px;
margin-bottom: 10px;
}
.base__content-item, .flight__content-item {
display: grid;
grid-template-columns: 200px 200px;
column-gap: 40px;
font-size: 12px;
}
.content__item-label, .content__item-label {
text-align: end;
}
<div class="resumen">
<section class="base">
<div class="base__header">
<h3 class="base__header-title">Información base</h3>
<button class="base__header-btn">
<span class="bi bi-pencil-square"></span>
<span class="ms-1">Editar</span>
</button>
</div>
<div class="base__content">
<div class="base__content-item">
<span class="content__item-label">Pasajero</span>
<span class="content__item-value">Juan Perez</span>
</div>
<div class="base__content-item">
<span class="content__item-label">Tipo de ticket</span>
<span class="content__item-value">Economico</span>
</div>
<div class="base__content-item">
<span class="content__item-label">Clase de vuelo</span>
<span class="content__item-value">Economico</span>
</div>
</div>
</section>
<section class="flight">
<div class="flight__header">
<h3 class="flight__header-title">Selección de vuelo</h3>
<button class="flight__header-btn">
<span class="bi bi-pencil-square"></span>
<span class="ms-1">Editar</span>
</button>
</div>
<div class="flight__content">
<div class="flight__content-item">
<span class="content__item-label">Origen</span>
<span class="content__item_value">Aeropuerto de Los Angeles</span>
</div>
<div class="flight__content-item">
<span class="content__item-label">Destino</span>
<span class="content__item_value">Aeropuerto de Miami</span>
</div>
<div class="flight__content-item">
<span class="content__item-label">Fecha de partida</span>
<span class="content__item_value">12/12/2021</span>
</div>
<div class="flight__content-item">
<span class="content__item-label">Hora de partida</span>
<span class="content__item_value">12:00</span>
</div>
<div class="flight__content-item">
<span class="content__item-label">Fecha de llegada</span>
<span class="content__item_value">16:00</span>
</div>
<div class="flight__content-item">
<span class="content__item-label">Aerolinea</span>
<span class="content__item_value">American Airlines</span>
</div>
<div class="flight__content-item">
<span class="content__item-label">Tipo de vuelo</span>
<span class="content__item_value">Directo</span>
</div>
<div class="flight__content-item">
<span class="content__item-label">Precio</span>
<span class="content__item_value">200 USD</span>
</div>
</div>
</section>
<section class="detail">
<div class="detail__header">
<h3 class="detail__header-title">Detalle de vuelo</h3>
</div>
<div class="detail__content">
<shared-table-search [tableModel]="flightModel"></shared-table-search>
</div>
</section>
</div>
import { Component, Input } from '@angular/core';
import { TableModel } from 'src/app/shared/interfaces/table-model.interface';
@Component({
selector: 'agent-summary-information',
templateUrl: './summary-information.component.html',
styleUrls: ['./summary-information.component.css']
})
export class SummaryInformationComponent {
@Input()
public flightModel: TableModel[] = [];
}
export interface Airport {
id: number;
name: string;
location: string;
code: string;
}
export interface Booking {
id: number;
passengerId: number;
typeTicket: string;
class: string;
status: string;
}
export interface FlightClass {
id: number;
name: string;
}
export interface PassengerTable {
id: number;
name: string;
lastname: string;
country: string;
city: string;
}
export interface TicketType {
id: number;
name: string;
}
import { Component, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { PassengerService } from '../../services/passenger-service.service';
import { PassengerService } from '../../services/passenger.service';
import { EMPTY_PASSENGER, Passenger } from '../../interfaces/passenger.interface';
import { PASSENGER_VALIDATORS, getPassengerValidatorLenght } from '../../validators/passenger.validator';
import { Location } from '@angular/common';
......
import { Component, OnInit } from '@angular/core';
import { Passenger } from '../../interfaces/passenger.interface';
import { TableModel } from 'src/app/shared/interfaces/table-model.interface';
import { PassengerTable } from '../../interfaces/passenger-table.interface';
import { PassengerService } from '../../services/passenger-service.service';
import { PassengerService } from '../../services/passenger.service';
import { ResultDelete } from 'src/app/shared/interfaces/result-delete.interface';
@Component({
......
/* Input */
.field {
position: relative;
padding-top: 12px;
}
.field input {
width: 100%;
border: none;
border-bottom: 1px solid #ccc;
border-radius: 0;
font-size: 12px;
outline: none;
color: #8f8e8e;
}
.field input:focus {
border-bottom: 1px solid #3f51b5;
}
.field label {
position: absolute;
top: 0px;
left: 2px;
pointer-events: none;
transition: 0.2s;
font-size: 12px;
}
.field input:focus + label {
top: 0;
left: 2px;
font-size: 12px;
color: #3f51b5;
}
.field input + label {
top: 0;
left: 2px;
font-size: 12px;
color: #ccc;
}
/* Buttons */
.btn-back, .btn-next, .btn-save {
border: 1px solid #6f6f6f;
background-color: white;
font-size: 12px;
padding: 15px 10px;
border-radius: 4px;
color: #6f6f6f;
width: 100px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.btn-back .bi, .btn-next .bi, .btn-save .bi {
margin-right: 4px;
}
.btn-save {
background-color: #1ab394;
border: #1ab394;
font-size: 12px;
}
.stepper__content-actions {
display: flex;
justify-content: space-between;
margin-top: 20px;
padding-top: 8px;
border-top: 1px solid #e6e6e6;
}
<shared-navigation></shared-navigation>
<shared-mail-box
icon="bi-bookmarks-fill"
message="Este gestor permite realizar la configuración de vuelos"
title="Reservas"
[btnBack]="true">
<shared-stepper [steps]="steps" [selectedStepIndex]="stepIndex">
<div class="p-4" [ngSwitch]="stepIndex">
<div *ngSwitchCase="0">
<agent-base-information></agent-base-information>
</div>
<div *ngSwitchCase="1">
<agent-flight-information [flightModel]="flightModel"></agent-flight-information>
</div>
<div *ngSwitchCase="2">
<agent-summary-information [flightModel]="flightModel"></agent-summary-information>
</div>
</div>
</shared-stepper>
<div class="stepper__content-actions d-flex">
<button *ngIf="!isFirstStep()" (click)="prevStep()" class="btn-back">
<i class="bi bi-chevron-left"></i>
<span>Anterior</span>
</button>
<div class="flex-fill"></div>
<button [ngClass]="['btn', isLastStep() ? 'btn-save' : 'btn-next']" (click)="nextStep()">
<i *ngIf="isLastStep()" class="bi bi-floppy"></i>
<span>{{ isLastStep() ? 'Guardar' : 'Siguiente' }}</span>
<i *ngIf="!isLastStep()" class="bi bi-chevron-right"></i>
</button>
</div>
</shared-mail-box>
import { Component, OnInit } from '@angular/core';
import { Step } from 'src/app/shared/interfaces/step.interface';
import { TableModel } from 'src/app/shared/interfaces/table-model.interface';
import { PassengerService } from '../../services/passenger.service';
import { Passenger } from '../../interfaces/passenger.interface';
import { TicketType } from '../../interfaces/ticket-type.interface';
import { FlightClass } from '../../interfaces/flight-class.interface';
@Component({
selector: 'app-reservation-add',
templateUrl: './reservation-add.component.html',
styleUrls: ['./reservation-add.component.css']
})
export class ReservationAddComponent implements OnInit{
public steps: Step[] = [
{
id: 1,
title: 'Información base',
icon: 'bi-person',
detail: "Información general de la reserva"
},
{
id: 2,
title: 'Selección de vuelo',
icon: 'bi-airplane-fill',
detail: "Información general de la reserva"
},
{
id: 3,
title: 'Resumen',
icon: 'bi-check',
detail: "Valida la configuración"
}
];
public flightModel: TableModel[] = [
{
name: 'id',
title: '#'
},
{
name: 'aeroline',
title: 'Aerolinea'
},
{
name: 'aircraftType',
title: 'Tipo de avión'
},
{
name: 'flightType',
title: 'Tipo de vuelo'
},
{
name: 'hourStart',
title: 'Hora de inicio'
},
{
name: 'hourEnd',
title: 'Hora de llegada'
},
{
name: 'price',
title: 'Precio'
}
];
public passengers: Passenger[] = [];
constructor(private passengersService: PassengerService) { }
ngOnInit(): void {
this.passengersService.getAll()
.subscribe(passengers => this.passengers = passengers);
}
public stepIndex: number = 0;
nextStep(): void {
if (this.stepIndex === this.steps.length) return;
this.stepIndex++;
}
prevStep(): void {
if (this.stepIndex === 0) return;
this.stepIndex--;
}
isFirstStep(): boolean {
return this.stepIndex === 0;
}
isLastStep(): boolean {
return this.stepIndex === this.steps.length - 1;
}
}
......@@ -3,6 +3,12 @@
title="Reservas"
icon="bi-bookmarks-fill"
message="Este gestor permite realizar reservas de vuelos">
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sunt iste unde, alias fuga animi natus earum consequuntur quasi omnis sint voluptatem, minus ex! Corporis doloremque voluptate eos ullam, neque quae!</p>
<shared-table
[tableData]="tableData"
[tableModel]="tableModel"
(onReloadData)="getAll()"
(onDialogResult)="onDelete($event)"
newRouter="new"
editRouter="edit">
</shared-table>
</shared-mail-box>
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { TableModel } from 'src/app/shared/interfaces/table-model.interface';
import { BookingService } from '../../services/booking.service';
import { Booking } from '../../interfaces/booking.interface';
import { ResultDelete } from 'src/app/shared/interfaces/result-delete.interface';
@Component({
selector: 'app-reservations',
templateUrl: './reservations.component.html',
styleUrls: ['./reservations.component.css']
})
export class ReservationsComponent {
export class ReservationsComponent implements OnInit{
tableData: Booking[] = [];
tableModel: TableModel[] = [
{
name: 'id',
title: '#'
},
{
name: 'passengerId',
title: 'Pasajero'
},
{
name: 'typeTicket',
title: 'Tipo de Tiquete'
},
{
name: 'class',
title: 'Clase'
},
{
name: 'status',
title: 'Estado'
}
];
constructor(private bookingService: BookingService) { }
ngOnInit(): void {
this.getAll();
}
getAll(): void {
this.bookingService.getAll()
.subscribe(bookings => this.tableData = bookings);
}
onDelete(result: ResultDelete): void {
if (!result.result) return;
this.bookingService.delete(result.id)
.subscribe(() => this.getAll());
}
}
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Airport } from '../interfaces/airport.interface';
@Injectable({providedIn: 'root'})
export class AirportsService {
constructor(private http: HttpClient) { }
private BASE_URL: string = 'http://localhost:3000/airports';
getAll(): Observable<Airport[]> {
return this.http.get<Airport[]>(this.BASE_URL);
}
}
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Booking } from '../interfaces/booking.interface';
@Injectable({providedIn: 'root'})
export class BookingService {
constructor(private httpClient: HttpClient) { }
private BASE_URL: string = 'http://localhost:3000/bookings';
getAll(): Observable<Booking[]> {
return this.httpClient.get<Booking[]>(this.BASE_URL);
}
getById(id: number): Observable<Booking> {
return this.httpClient.get<Booking>(`${this.BASE_URL}/${id}`);
}
save(booking: Booking): Observable<Booking> {
return this.httpClient.post<Booking>(this.BASE_URL, booking);
}
edit(id: number, booking: Booking): Observable<Booking> {
return this.httpClient.put<Booking>(`${this.BASE_URL}/${id}`, booking);
}
delete(id: number): Observable<Booking> {
return this.httpClient.delete<Booking>(`${this.BASE_URL}/${id}`);
}
}
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { FlightClass } from '../interfaces/flight-class.interface';
@Injectable({providedIn: 'root'})
export class TicketClassService {
constructor(private http: HttpClient) { }
private BASE_URL: string = 'http://localhost:3000/ticketClasses';
getAll(): Observable<FlightClass[]> {
return this.http.get<FlightClass[]>(this.BASE_URL);
}
}
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { TicketType } from '../interfaces/ticket-type.interface';
import { Observable } from 'rxjs';
@Injectable({providedIn: 'root'})
export class TicketTypeService {
constructor(private http: HttpClient) { }
private BASE_URL: string = 'http://localhost:3000/ticketTypes';
getAll(): Observable<TicketType[]> {
return this.http.get<TicketType[]>(this.BASE_URL);
}
}
......@@ -13,4 +13,4 @@
.body {
font-family: var(--font-family-sans-serif);
}
\ No newline at end of file
}
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Officia, tempora possimus. Repellendus pariatur quisquam illo, eius qui harum quasi tempore aspernatur debitis hic assumenda ex dolorem ullam quod vel temporibus.</p>
<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>
......@@ -2,22 +2,20 @@
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.2);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.dialog__content {
background-color: white;
max-width: 600px;
max-height: 90vh;
width: 100%;
height: auto;
max-width: 70vw;
max-height: 80vh;
border-radius: 5px;
padding: 30px;
}
......@@ -28,7 +26,7 @@
}
.dialog__icon, .dialog__title {
font-size: 24px;
font-size: 16px;
margin-bottom: 0;
margin-right: 5px;
}
......@@ -37,3 +35,7 @@
display: flex;
justify-content: space-between;
}
.btn-close {
font-size: 10px;
}
<div class="dialog" *ngIf="isActivated">
<div class="dialog__content">
<div class="dialog__content-title">
<span class="dialog__icon bi" [ngClass]="icon"></span>
<h3 class="dialog__title">{{ title }}</h3>
<div class="d-flex justify-content-between align-items-center">
<div class="dialog__content-title">
<img width="60" class="img-thumbnail" src="./assets/byte-banner.png" alt="Banner Byte">
<h3 class="dialog__title ms-2">{{ title }}</h3>
</div>
<button (click)="closeDialog()" class="btn btn-close"></button>
</div>
<hr>
<ng-content></ng-content>
......
import { Component, EventEmitter, Input } from '@angular/core';
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'shared-dialog',
......@@ -13,11 +13,13 @@ export class DialogComponent {
@Input()
public icon: string = 'bi-house-door';
public closeDialog: EventEmitter<void> = new EventEmitter<void>();
@Output()
public onCloseDialog: EventEmitter<void> = new EventEmitter<void>();
public isActivated: boolean = true;
@Input()
public isActivated: boolean = false;
onCloseDialog(): void {
this.closeDialog.emit();
closeDialog(): void {
this.onCloseDialog.emit();
}
}
.input__container {
position: relative;
display: flex;
padding-top: 20px;
}
.input__container button {
position: absolute;
border: 0;
background-color: transparent;
right: 0;
top: 20px;
font-size: 12px;
}
.input__container label {
position: absolute;
top: 0;
left: 2px;
font-size: 12px;
color: #ccc;
}
.input__container input {
width: 100%;
border: 0;
border-bottom: 1px solid #ccc;
outline: none;
font-size: 12px;
color: #8f8e8e;
}
<div class="input__container">
<input [value]="value" [readOnly]="true" type="text" [id]="name">
<label [for]="name">{{ name }} *</label>
<button (click)="openDialog()" type="submit">
<i class="bi bi-search"></i>
</button>
</div>
<shared-dialog [isActivated]="isActivated" title="Buscador" (onCloseDialog)="closeDialog()">
<shared-table-search [tableModel]="tableModel" [tableData]="tableData" (onSelect)="selectItem($event)"></shared-table-search>
</shared-dialog>
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { TableModel } from '../../interfaces/table-model.interface';
@Component({
selector: 'shared-input-selector',
templateUrl: './input-selector.component.html',
styleUrls: ['./input-selector.component.css']
})
export class InputSelectorComponent {
@Input()
public isActivated: boolean = false;
@Input()
public tableModel: TableModel[] = []
@Input()
public tableData: any[] = [];
@Input()
public value: string = '';
@Input()
public name: string = '';
@Output()
public onSelect: EventEmitter<any> = new EventEmitter<any>();
closeDialog(): void {
this.isActivated = false;
}
openDialog(): void {
this.isActivated = true;
}
selectItem(item: any): void {
this.onSelect.emit(item);
this.closeDialog();
}
}
/* Stepper Navigation */
.stepper__navigation {
display: flex;
justify-content: center;
margin: 15px 30px;
z-index: 100;
}
.stepper__navigation-item {
display: flex;
flex-direction: column;
align-items: center;
max-width: 300px;
width: 100%;
text-align: center;
position: relative;
z-index: 2;
cursor: pointer;
padding: 25px 0;
border-radius: 8px;
}
.stepper__navigation-item.active {
background-color: rgba(158, 169, 233, 0.4);
border: 1px solid rgba(158, 169, 233, 0.5);
}
.stepper__navigation-item.active .navigation__item-icon span {
background-color: #3f51b5;
}
.stepper__navigation-item.active .navigation__item-detail .item__detail-title {
color: black;
}
.stepper__navigation-item.active .navigation__item-detail .item__detail-description {
color: black;
}
.stepper__navigation-item:first-child::before {
content: '';
width: 38%;
height: 1px;
background-color: #ccc;
position: absolute;
z-index: 1;
top: 38px;
right: 0;
}
.stepper__navigation-item:last-child::before {
content: '';
width: 38%;
height: 1px;
background-color: #ccc;
position: absolute;
z-index: 1;
top: 38px;
left: 0;
}
.stepper__navigation-item:not(:first-child, :last-child)::before {
content: '';
width: 38%;
height: 1px;
background-color: #ccc;
position: absolute;
z-index: 1;
top: 38px;
left: 0;
}
.stepper__navigation-item:not(:first-child, :last-child)::after {
content: '';
width: 38%;
height: 1px;
background-color: #ccc;
position: absolute;
z-index: 1;
top: 38px;
right: 0;
}
.navigation__item-icon {
z-index: 3;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.navigation__item-icon span {
border-radius: 50%;
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
background-color: #6f6f6f;
color: white;
font-size: 16px;
font-weight: 500;
}
.navigation__item-detail {
padding-top: 8px;
display: flex;
flex-direction: column;
}
.navigation__item-detail .item__detail-title {
font-size: 14px;
color: #6f6f6f;
line-height: 14px;
font-weight: 700;
}
.navigation__item-detail .item__detail-description {
font-size: 12px;
font-weight: 400;
color: #6f6f6f;
line-height: 12px;
}
/* Stepper Content */
.stepper__content {
margin-top: 1px solid #e6e6e6;
}
.stepper__content-title {
background-color: #f0efef;
padding: 8px 12px;
font-size: 12px;
color: black;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border: 1px solid #e6e6e6;
border-bottom: 0;
}
.stepper__content-container {
background-color: white;
border: 1px solid #e6e6e6;
}
.stepper__content-actions {
padding: 8px 0;
display: flex;
margin-top: 20px;
border-top: 1px solid #e6e6e6;
}
.stepper__content-actions.one-child {
justify-content: end;
}
.stepper__content-actions.two-child {
justify-content: space-between;
}
/* Buttons */
.btn-back, .btn-next {
border: 1px solid #6f6f6f;
background-color: white;
font-size: 12px;
padding: 15px 10px;
border-radius: 4px;
color: #6f6f6f;
width: 100px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.btn-back .bi {
margin-right: 4px;
}
.btn-next .bi {
margin-left: 4px;
}
<div class="stepper">
<div class="stepper__navigation">
<div *ngFor="let step of steps; let i = index;" [ngClass]="['stepper__navigation-item', isActiveStep(i)]">
<div class="navigation__item-icon">
<span [ngClass]="['bi', step.icon]"></span>
</div>
<div class="navigation__item-detail">
<span class="item__detail-title">{{ step.title }}</span>
<span class="item__detail-description">{{ step.detail }}</span>
</div>
</div>
</div>
<div class="stepper__content">
<div class="stepper__content-title">
<span>{{ nameStep }}</span>
</div>
<div class="stepper__content-container">
<ng-content></ng-content>
</div>
</div>
</div>
import { Component, Input, OnInit } from '@angular/core';
import { Step } from '../../interfaces/step.interface';
@Component({
selector: 'shared-stepper',
templateUrl: './stepper.component.html',
styleUrls: ['./stepper.component.css']
})
export class StepperComponent implements OnInit{
@Input()
public selectedStepIndex: number = 0;
@Input()
public steps: Step[] = [];
isActiveStep(id: number): string {
return this.selectedStepIndex === id ? 'active' : '';
}
get nameStep(): string {
return this.steps[this.selectedStepIndex!].title;
}
ngOnInit(): void {
this.steps.length > 0 ? this.selectedStepIndex = 0 : null;
}
nextStep(): void {
if (this.selectedStepIndex === this.steps.length - 1) return;
this.selectedStepIndex!++;
}
prevStep(): void {
if (this.selectedStepIndex === 0) return;
this.selectedStepIndex!--;
}
}
.pagination {
padding-left: 15px;
padding-right: 20px;
padding-top: 10px;
padding-bottom: 10px;
color: #676a6c;
}
.btn-reload, .btn-left, .btn-right {
color: #676a6c;
border-radius: 50%;
height: 40px;
width: 40px;
}
.btn-reload i {
font-size: 16px;
font-weight: 700;
}
.select-pagination {
background-color: white;
border: none;
border-bottom: 1px solid #676a6c;
color: #676a6c;
width: 60px;
}
.btn:disabled {
border: none;
}
<div class="pagination w-auto d-flex align-items-center">
<div class="flex-fill">
<button (click)="onEmitReloadData()" class="btn btn-reload">
<i class="bi bi-arrow-clockwise"></i>
</button>
</div>
<div class="d-flex align-items-center gap-4">
<div class="d-flex">
<p class="m-0 pe-2">Resultados por pagina: </p>
<select (change)="getItemsPerPage(selectItems.value)" [value]="itemsPerPage" class="select-pagination" #selectItems>
<option *ngFor="let item of itemsPerPage" [value]="item">{{ item }}</option>
</select>
</div>
<div class="d-flex">
<p class="m-0">{{ initItem }} - {{ endItem }} de {{ this.tableData.length }}</p>
</div>
<div class="d-flex">
<button [disabled]="page === 1" class="btn btn-right" (click)="prevPage()">
<i class="bi bi-chevron-left"></i>
</button>
<button [disabled]="page === totalPage" class="btn btn-left" (click)="nextPage()">
<i class="bi bi-chevron-right"></i>
</button>
</div>
</div>
</div>
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { TablePagination } from '../../interfaces/table-pagination.interface';
import { ThisReceiver } from '@angular/compiler';
@Component({
selector: 'shared-table-paginator',
templateUrl: './table-paginator.component.html',
styleUrls: ['./table-paginator.component.css']
})
export class TablePaginatorComponent {
@Output()
public onReloadData: EventEmitter<void> = new EventEmitter();
@Output()
public onChangePagination: EventEmitter<TablePagination> = new EventEmitter<TablePagination>();
@Input()
public tableData: any[] = [];
public numberOfItemsPerPage: number = 5;
public itemsPerPage: number[] = [5, 10, 15];
public page: number = 1;
get totalPage(): number {
return this.tableData.length >= this.numberOfItemsPerPage? Math.ceil(this.tableData.length / this.numberOfItemsPerPage) : 1;
}
ngOnInit(): void {
this.onChangePagination.emit({init: this.initItem, end: this.endItem});
}
get initItem() {
return this.tableData.length == 0? 0 : this.totalPage >= 1 || this.tableData.length == 0? ((this.page - 1) * this.numberOfItemsPerPage) + 1 : 0;
}
get endItem() {
return this.totalPage >= 1? this.page != this.totalPage? this.page * this.numberOfItemsPerPage: this.tableData.length : 0;
}
nextPage(): void {
if (this.page === this.totalPage) return;
this.page += 1;
this.onChangePagination.emit({init: this.initItem, end: this.endItem});
}
prevPage(): void {
if (this.page === 1) return;
this.page -= 1;
this.onChangePagination.emit({init: this.initItem, end: this.endItem});
}
getItemsPerPage(value: string): void {
this.numberOfItemsPerPage = parseInt(value);
}
onEmitReloadData(): void {
this.onReloadData.emit();
}
}
.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: scroll;
box-shadow: 0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f;
}
<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" class="table-item">{{ data[item['name']] }}</td>
<td class="table-item">
<button (click)="onSelectRow(data)" class="btn-select">
<i class="bi bi-plus-lg"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { TableModel } from '../../interfaces/table-model.interface';
@Component({
selector: 'shared-table-search',
templateUrl: './table-search.component.html',
styleUrls: ['./table-search.component.css']
})
export class TableSearchComponent {
@Input()
public tableData: any[] = [];
@Input()
public tableModel: TableModel[] = [];
@Output()
public onSelect: EventEmitter<any[]> = new EventEmitter<any[]>();
onSelectRow(row: any): void {
this.onSelect.emit(row);
}
}
export interface Step {
id: number;
icon: string;
title: string;
detail: string;
}
export interface TablePagination {
init: number;
end: number;
}
......@@ -11,6 +11,10 @@ import { FooterComponent } from './components/footer/footer.component';
import { DeleteDialogComponent } from './components/delete-dialog/delete-dialog.component';
import { DialogComponent } from './components/dialog/dialog.component';
import { InputValidatorComponent } from './components/input-validator/input-validator.component';
import { StepperComponent } from './components/stepper/stepper.component';
import { InputSelectorComponent } from './components/input-selector/input-selector.component';
import { TableSearchComponent } from './components/table-search/table-search.component';
import { TablePaginatorComponent } from './components/table-paginator/table-paginator.component';
......@@ -25,7 +29,11 @@ import { InputValidatorComponent } from './components/input-validator/input-vali
FooterComponent,
DeleteDialogComponent,
DialogComponent,
InputValidatorComponent
InputValidatorComponent,
StepperComponent,
InputSelectorComponent,
TableSearchComponent,
TablePaginatorComponent
],
imports: [
CommonModule,
......@@ -40,7 +48,11 @@ import { InputValidatorComponent } from './components/input-validator/input-vali
FooterComponent,
DeleteDialogComponent,
DialogComponent,
InputValidatorComponent
InputValidatorComponent,
StepperComponent,
InputSelectorComponent,
TablePaginatorComponent,
TableSearchComponent
]
})
export class SharedModule { }
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