Добавьте файлы проекта.
This commit is contained in:
parent
1eda2f54b5
commit
ab5883356b
32
Airport.Server/Airport.Server.csproj
Normal file
32
Airport.Server/Airport.Server.csproj
Normal file
@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<SpaRoot>..\airport.client</SpaRoot>
|
||||
<SpaProxyLaunchCommand>npm run dev</SpaProxyLaunchCommand>
|
||||
<SpaProxyServerUrl>https://localhost:5173</SpaProxyServerUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.31" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaProxy">
|
||||
<Version>6.*-*</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.20">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.18" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\airport.client\airport.client.esproj">
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
172
Airport.Server/AirportContext.cs
Normal file
172
Airport.Server/AirportContext.cs
Normal file
@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Airport.Server.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Airport.Server;
|
||||
|
||||
public partial class AirportContext : DbContext
|
||||
{
|
||||
public AirportContext()
|
||||
{
|
||||
}
|
||||
|
||||
public AirportContext(DbContextOptions<AirportContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual DbSet<Airplane> Airplanes { get; set; }
|
||||
|
||||
public virtual DbSet<Customer> Customers { get; set; }
|
||||
|
||||
public virtual DbSet<Flight> Flights { get; set; }
|
||||
|
||||
public virtual DbSet<Seatreserve> Seatreserves { get; set; }
|
||||
|
||||
public virtual DbSet<Ticket> Tickets { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
|
||||
=> optionsBuilder.UseNpgsql("Host=localhost;Port=5432;Database=Airport;Username=postgres;Password=postgres");
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Airplane>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("airplane_pkey");
|
||||
|
||||
entity.ToTable("airplane");
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.NameModel)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("name_model");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Customer>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("customer_pkey");
|
||||
|
||||
entity.ToTable("customer");
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.Address)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("address");
|
||||
entity.Property(e => e.Benefit)
|
||||
.HasDefaultValueSql("'Отсутствует'::character varying")
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("benefit");
|
||||
entity.Property(e => e.Login)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("login");
|
||||
entity.Property(e => e.Name)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("name");
|
||||
entity.Property(e => e.Password)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("password");
|
||||
entity.Property(e => e.Patronymic)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("patronymic");
|
||||
entity.Property(e => e.PhoneNumber)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("phone_number");
|
||||
entity.Property(e => e.Surname)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("surname");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Flight>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("flight_pkey");
|
||||
|
||||
entity.ToTable("flight");
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.ArrivalDate)
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("arrival_date");
|
||||
entity.Property(e => e.ArrivalPlace)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("arrival_place");
|
||||
entity.Property(e => e.CompanyName)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("company_name");
|
||||
entity.Property(e => e.DepartureDate)
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("departure_date");
|
||||
entity.Property(e => e.DeparturePlace)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("departure_place");
|
||||
entity.Property(e => e.PlaneId).HasColumnName("plane_id");
|
||||
entity.Property(e => e.StateFlight)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("state_flight");
|
||||
entity.Property(e => e.TariffPlan)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("tariff_plan");
|
||||
|
||||
entity.HasOne(d => d.Plane).WithMany(p => p.Flights)
|
||||
.HasForeignKey(d => d.PlaneId)
|
||||
.HasConstraintName("flight_plane_id_fkey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Seatreserve>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("seatreserve_pkey");
|
||||
|
||||
entity.ToTable("seatreserve");
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.FlightId).HasColumnName("flight_id");
|
||||
entity.Property(e => e.Pozition)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("pozition");
|
||||
entity.Property(e => e.TicketId).HasColumnName("ticket_id");
|
||||
|
||||
entity.HasOne(d => d.Flight).WithMany(p => p.Seatreserves)
|
||||
.HasForeignKey(d => d.FlightId)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("seatreserve_flight_id_fkey");
|
||||
|
||||
entity.HasOne(d => d.Ticket).WithMany(p => p.Seatreserves)
|
||||
.HasForeignKey(d => d.TicketId)
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.HasConstraintName("seatreserve_ticket_id_fkey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Ticket>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("ticket_pkey");
|
||||
|
||||
entity.ToTable("ticket");
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.CustomerId).HasColumnName("customer_id");
|
||||
entity.Property(e => e.FlightId).HasColumnName("flight_id");
|
||||
entity.Property(e => e.Payment).HasColumnName("payment");
|
||||
entity.Property(e => e.Status)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("status");
|
||||
entity.Property(e => e.TariffPlan)
|
||||
.HasColumnType("character varying")
|
||||
.HasColumnName("tariff_plan");
|
||||
|
||||
entity.HasOne(d => d.Customer).WithMany(p => p.Tickets)
|
||||
.HasForeignKey(d => d.CustomerId)
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.HasConstraintName("ticket_customer_id_fkey");
|
||||
|
||||
entity.HasOne(d => d.Flight).WithMany(p => p.Tickets)
|
||||
.HasForeignKey(d => d.FlightId)
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.HasConstraintName("ticket_flight_id_fkey");
|
||||
});
|
||||
|
||||
OnModelCreatingPartial(modelBuilder);
|
||||
}
|
||||
|
||||
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||
}
|
8
Airport.Server/CHANGELOG.md
Normal file
8
Airport.Server/CHANGELOG.md
Normal file
@ -0,0 +1,8 @@
|
||||
This file explains how Visual Studio created the project.
|
||||
|
||||
The following steps were used to generate this project:
|
||||
- Create new ASP\.NET Core Web API project.
|
||||
- Update `launchSettings.json` to register the SPA proxy as a startup assembly.
|
||||
- Update project file to add a reference to the frontend project and set SPA properties.
|
||||
- Add project to the startup projects list.
|
||||
- Write this file.
|
62
Airport.Server/Controllers/AirplaneController.cs
Normal file
62
Airport.Server/Controllers/AirplaneController.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using Airport.Server.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Airport.Server.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class AirplaneController : ControllerBase
|
||||
{
|
||||
private IConfiguration _config;
|
||||
private readonly AirportContext _context;
|
||||
|
||||
public AirplaneController(AirportContext context, IConfiguration config)
|
||||
{
|
||||
_context = context;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route(nameof(GetAllAirplanes))]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
public async Task<ActionResult<List<Airplane>>> GetAllAirplanes()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Airplane> todos = await _context.Airplanes
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
|
||||
foreach (Airplane airplane in todos) airplane.Flights = _context.Flights.Where(x=>x.PlaneId==airplane.Id).ToList();
|
||||
|
||||
return Ok(todos);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route(nameof(CreateAirplane))]
|
||||
public async Task<ActionResult<Airplane>> CreateAirplane([FromBody] Airplane airplane)
|
||||
{
|
||||
try
|
||||
{
|
||||
_context.Airplanes.Add(airplane);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
176
Airport.Server/Controllers/CustomerController.cs
Normal file
176
Airport.Server/Controllers/CustomerController.cs
Normal file
@ -0,0 +1,176 @@
|
||||
using Airport.Server.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Text;
|
||||
|
||||
namespace Airport.Server.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class CustomerController : ControllerBase
|
||||
{
|
||||
private IConfiguration _config;
|
||||
private readonly AirportContext _context;
|
||||
|
||||
public CustomerController(AirportContext context, IConfiguration config)
|
||||
{
|
||||
_context = context;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route(nameof(RegisterCustomer))]
|
||||
public async Task<ActionResult<Customer>> RegisterCustomer([FromBody] Customer customer)
|
||||
{
|
||||
try
|
||||
{
|
||||
_context.Customers.Add(customer);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("SignIn/{login}/{password}")]
|
||||
public async Task<IActionResult> SignIn (string login, string password)
|
||||
{
|
||||
try
|
||||
{
|
||||
Customer model = new Customer()
|
||||
{
|
||||
Login = login,
|
||||
Password = password
|
||||
};
|
||||
|
||||
var customerFromDatabase = await AuthenticationCustomer(model);
|
||||
if (customerFromDatabase == null)
|
||||
return NotFound();
|
||||
|
||||
customerFromDatabase.Token = GenerateToken(model);
|
||||
return Ok(customerFromDatabase);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private string GenerateToken(Customer model)
|
||||
{
|
||||
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
|
||||
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
|
||||
_config["Jwt:Issuer"],
|
||||
null,
|
||||
expires: DateTime.Now.AddMinutes(120),
|
||||
signingCredentials: credentials);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
private async Task<Customer?> AuthenticationCustomer(Customer customer)
|
||||
{
|
||||
return await _context.Customers.FirstOrDefaultAsync(x => x.Login == customer.Login && x.Password == customer.Password);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route(nameof(GetAllCustomers))]
|
||||
public async Task<ActionResult<List<Customer>>> GetAllCustomers()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Customer> customers = await
|
||||
_context.Customers.AsNoTracking().ToListAsync();
|
||||
return Ok(customers);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("GetCustomerById/{customerId}")]
|
||||
public async Task<ActionResult<Customer>> GetCustomerById(int customerId)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(customerId, nameof(customerId));
|
||||
|
||||
try
|
||||
{
|
||||
Customer? customerFromDatabase =
|
||||
await _context.Customers.FirstOrDefaultAsync(x => x.Id == customerId);
|
||||
|
||||
if (customerFromDatabase == null)
|
||||
return NotFound();
|
||||
|
||||
return Ok(customerFromDatabase);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpPut("UpdateCustomer/{customerId}")]
|
||||
public async Task<ActionResult> UpdateCustomer(
|
||||
int customerId, [FromBody] Customer customerUpdated)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(customerId, nameof(customerId));
|
||||
try
|
||||
{
|
||||
Customer? customerFromDatabase =
|
||||
await _context.Customers.FirstOrDefaultAsync(x => x.Id == customerId);
|
||||
if (customerFromDatabase == null)
|
||||
return NotFound();
|
||||
|
||||
customerFromDatabase.Name = customerUpdated.Name;
|
||||
customerFromDatabase.Surname = customerUpdated.Surname;
|
||||
customerFromDatabase.Patronymic = customerUpdated.Patronymic;
|
||||
customerFromDatabase.Address = customerUpdated.Address;
|
||||
customerFromDatabase.PhoneNumber = customerUpdated.PhoneNumber;
|
||||
customerFromDatabase.Benefit = customerUpdated.Benefit;
|
||||
customerFromDatabase.Login = customerUpdated.Login;
|
||||
customerFromDatabase.Password = customerUpdated.Password;
|
||||
|
||||
_context.Customers.Update(customerFromDatabase);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
[HttpDelete("DeleteCustomer/{customerId}")]
|
||||
public async Task<ActionResult> DeleteCustomer(int customerId)
|
||||
{
|
||||
try
|
||||
{
|
||||
Customer? customer = await _context.Customers.FirstOrDefaultAsync(y => y.Id == customerId);
|
||||
if (customer is null)
|
||||
return NotFound();
|
||||
|
||||
_context.Customers.Remove(customer);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
Airport.Server/Controllers/FlightController.cs
Normal file
59
Airport.Server/Controllers/FlightController.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using Airport.Server.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Airport.Server.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class FlightController : ControllerBase
|
||||
{
|
||||
private IConfiguration _config;
|
||||
private readonly AirportContext _context;
|
||||
|
||||
public FlightController(AirportContext context, IConfiguration config)
|
||||
{
|
||||
_context = context;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route(nameof(CreateFlight))]
|
||||
public async Task<ActionResult<Airplane>> CreateFlight([FromBody] Flight flight)
|
||||
{
|
||||
try
|
||||
{
|
||||
_context.Flights.Add(flight);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route(nameof(GetAllFlights))]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
public async Task<ActionResult<List<Flight>>> GetAllFlights()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Flight> todos = await _context.Flights
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
foreach (var flight in todos) flight.Plane = _context.Airplanes.FirstOrDefault(x=>x.Id == flight.PlaneId);
|
||||
return Ok(todos);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
61
Airport.Server/Controllers/SeatController.cs
Normal file
61
Airport.Server/Controllers/SeatController.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using Airport.Server.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Airport.Server.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class SeatController : ControllerBase
|
||||
{
|
||||
private IConfiguration _config;
|
||||
private readonly AirportContext _context;
|
||||
|
||||
public SeatController(AirportContext context, IConfiguration config)
|
||||
{
|
||||
_context = context;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpGet("GetSeatsByFlight/{flightId}")]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
public async Task<ActionResult<List<Seatreserve>>> GetSeatsByFlight(int flightId)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Seatreserve> seats = await _context.Seatreserves.Where(x => x.FlightId == flightId)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(seats);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route(nameof(CreateSeat))]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
public async Task<ActionResult<Seatreserve>> CreateSeat([FromBody] Seatreserve seat)
|
||||
{
|
||||
try
|
||||
{
|
||||
_context.Seatreserves.Add(seat);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
Airport.Server/Controllers/TicketController.cs
Normal file
60
Airport.Server/Controllers/TicketController.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using Airport.Server.Models;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Airport.Server.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
public class TicketController : ControllerBase
|
||||
{
|
||||
private IConfiguration _config;
|
||||
private readonly AirportContext _context;
|
||||
|
||||
public TicketController(AirportContext context, IConfiguration config)
|
||||
{
|
||||
_context = context;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route(nameof(CreateTicket))]
|
||||
public async Task<ActionResult<Ticket>> CreateTicket([FromBody] Ticket ticket)
|
||||
{
|
||||
try
|
||||
{
|
||||
_context.Tickets.Add(ticket);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
[HttpGet]
|
||||
[Route(nameof(GetLastTicket))]
|
||||
public async Task<ActionResult<Ticket>> GetLastTicket()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<Ticket> ticket = await _context.Tickets
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(ticket);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
Airport.Server/Controllers/WeatherForecastController.cs
Normal file
33
Airport.Server/Controllers/WeatherForecastController.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Airport.Server.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "GetWeatherForecast")]
|
||||
public IEnumerable<WeatherForecast> Get()
|
||||
{
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = DateTime.Now.AddDays(index),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
13
Airport.Server/Models/Airplane.cs
Normal file
13
Airport.Server/Models/Airplane.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Airport.Server.Models;
|
||||
|
||||
public partial class Airplane
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string NameModel { get; set; } = null!;
|
||||
|
||||
public virtual ICollection<Flight> Flights { get; set; } = new List<Flight>();
|
||||
}
|
31
Airport.Server/Models/Customer.cs
Normal file
31
Airport.Server/Models/Customer.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Airport.Server.Models;
|
||||
|
||||
public partial class Customer
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
public string Surname { get; set; } = null!;
|
||||
|
||||
public string Patronymic { get; set; } = null!;
|
||||
|
||||
public string Address { get; set; } = null!;
|
||||
|
||||
public string PhoneNumber { get; set; } = null!;
|
||||
|
||||
public string? Benefit { get; set; }
|
||||
|
||||
public string Login { get; set; } = null!;
|
||||
|
||||
public string Password { get; set; } = null!;
|
||||
|
||||
public virtual ICollection<Ticket> Tickets { get; set; } = new List<Ticket>();
|
||||
|
||||
[NotMapped]
|
||||
public string? Token { get; set; }
|
||||
}
|
31
Airport.Server/Models/Flight.cs
Normal file
31
Airport.Server/Models/Flight.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Airport.Server.Models;
|
||||
|
||||
public partial class Flight
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public DateTime? DepartureDate { get; set; }
|
||||
|
||||
public DateTime? ArrivalDate { get; set; }
|
||||
|
||||
public string DeparturePlace { get; set; } = null!;
|
||||
|
||||
public string ArrivalPlace { get; set; } = null!;
|
||||
|
||||
public int? PlaneId { get; set; }
|
||||
|
||||
public string StateFlight { get; set; } = null!;
|
||||
|
||||
public string CompanyName { get; set; } = null!;
|
||||
|
||||
public string TariffPlan { get; set; } = null!;
|
||||
|
||||
public virtual Airplane? Plane { get; set; }
|
||||
|
||||
public virtual ICollection<Seatreserve> Seatreserves { get; set; } = new List<Seatreserve>();
|
||||
|
||||
public virtual ICollection<Ticket> Tickets { get; set; } = new List<Ticket>();
|
||||
}
|
19
Airport.Server/Models/Seatreserve.cs
Normal file
19
Airport.Server/Models/Seatreserve.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Airport.Server.Models;
|
||||
|
||||
public partial class Seatreserve
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Pozition { get; set; } = null!;
|
||||
|
||||
public int? FlightId { get; set; }
|
||||
|
||||
public int? TicketId { get; set; }
|
||||
|
||||
public virtual Flight? Flight { get; set; }
|
||||
|
||||
public virtual Ticket? Ticket { get; set; }
|
||||
}
|
25
Airport.Server/Models/Ticket.cs
Normal file
25
Airport.Server/Models/Ticket.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Airport.Server.Models;
|
||||
|
||||
public partial class Ticket
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int Payment { get; set; }
|
||||
|
||||
public string Status { get; set; } = null!;
|
||||
|
||||
public int? CustomerId { get; set; }
|
||||
|
||||
public int? FlightId { get; set; }
|
||||
|
||||
public string TariffPlan { get; set; } = null!;
|
||||
|
||||
public virtual Customer? Customer { get; set; }
|
||||
|
||||
public virtual Flight? Flight { get; set; }
|
||||
|
||||
public virtual ICollection<Seatreserve> Seatreserves { get; set; } = new List<Seatreserve>();
|
||||
}
|
27
Airport.Server/Program.cs
Normal file
27
Airport.Server/Program.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Airport.Server;
|
||||
using api;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace api
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
}
|
34
Airport.Server/Properties/launchSettings.json
Normal file
34
Airport.Server/Properties/launchSettings.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:17854",
|
||||
"sslPort": 44316
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"Airport.Server": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7217;http://localhost:5041",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
129
Airport.Server/Startup.cs
Normal file
129
Airport.Server/Startup.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
using Airport.Server;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace api
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
//Enable Cors
|
||||
services.AddCors(c =>
|
||||
{
|
||||
c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||
});
|
||||
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(x =>
|
||||
{
|
||||
x.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])),
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidIssuer = Configuration["Jwt:Issuer"],
|
||||
ValidAudience = Configuration["Jwt:Issuer"]
|
||||
};
|
||||
});
|
||||
|
||||
services.AddControllers();
|
||||
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
services.AddEndpointsApiExplorer();
|
||||
services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Airport", Version = "v1" });
|
||||
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||
{
|
||||
Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n
|
||||
Enter 'Bearer' [space] and then your token in the text input below.
|
||||
\r\n\r\nExample: 'Bearer 12345abcdef'",
|
||||
Name = "Authorization",
|
||||
In = ParameterLocation.Header,
|
||||
Type = SecuritySchemeType.ApiKey,
|
||||
Scheme = "Bearer"
|
||||
});
|
||||
|
||||
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
},
|
||||
Scheme = "oauth2",
|
||||
Name = "Bearer",
|
||||
In = ParameterLocation.Header,
|
||||
|
||||
},
|
||||
new List<string>()
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
services.AddDbContext<AirportContext>(options => {
|
||||
options.UseNpgsql(Configuration.GetConnectionString("AirportConnection"));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
//Eanble Cors
|
||||
app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapFallbackToFile("/index.html");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
13
Airport.Server/WeatherForecast.cs
Normal file
13
Airport.Server/WeatherForecast.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Airport.Server
|
||||
{
|
||||
public class WeatherForecast
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
|
||||
public string? Summary { get; set; }
|
||||
}
|
||||
}
|
8
Airport.Server/appsettings.Development.json
Normal file
8
Airport.Server/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
19
Airport.Server/appsettings.json
Normal file
19
Airport.Server/appsettings.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"AirportConnection": "Host=localhost;Port=5432;Database=Airport;Username=postgres;Password=postgres"
|
||||
},
|
||||
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
|
||||
"AllowedHosts": "*",
|
||||
|
||||
"Jwt": {
|
||||
"Key": "This is my custom Secret key for authnetication",
|
||||
"Issuer": "MyDomain.com"
|
||||
}
|
||||
}
|
33
Airport.sln
Normal file
33
Airport.sln
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.10.34928.147
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Airport.Server", "Airport.Server\Airport.Server.csproj", "{877F3947-2D9E-4C2D-AA3A-BD7D7796F4D9}"
|
||||
EndProject
|
||||
Project("{54A90642-561A-4BB1-A94E-469ADEE60C69}") = "airport.client", "airport.client\airport.client.esproj", "{D67FB47A-D51D-4570-9017-245C2C070735}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{877F3947-2D9E-4C2D-AA3A-BD7D7796F4D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{877F3947-2D9E-4C2D-AA3A-BD7D7796F4D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{877F3947-2D9E-4C2D-AA3A-BD7D7796F4D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{877F3947-2D9E-4C2D-AA3A-BD7D7796F4D9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D67FB47A-D51D-4570-9017-245C2C070735}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D67FB47A-D51D-4570-9017-245C2C070735}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D67FB47A-D51D-4570-9017-245C2C070735}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{D67FB47A-D51D-4570-9017-245C2C070735}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D67FB47A-D51D-4570-9017-245C2C070735}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D67FB47A-D51D-4570-9017-245C2C070735}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0FAF1C17-14F8-4EAC-9583-7468CC50B973}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
19
airport.client/.eslintrc.cjs
Normal file
19
airport.client/.eslintrc.cjs
Normal file
@ -0,0 +1,19 @@
|
||||
/* eslint-env node */
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended'
|
||||
],
|
||||
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
},
|
||||
|
||||
rules: {
|
||||
'vue/multi-word-component-names': 'off',
|
||||
},
|
||||
}
|
30
airport.client/.gitignore
vendored
Normal file
30
airport.client/.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
6
airport.client/.vscode/extensions.json
vendored
Normal file
6
airport.client/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"dbaeumer.vscode-eslint"
|
||||
]
|
||||
}
|
15
airport.client/CHANGELOG.md
Normal file
15
airport.client/CHANGELOG.md
Normal file
@ -0,0 +1,15 @@
|
||||
This file explains how Visual Studio created the project.
|
||||
|
||||
The following tools were used to generate this project:
|
||||
- create-vite
|
||||
|
||||
The following steps were used to generate this project:
|
||||
- Create vue project with create-vite: `npm init --yes vue@latest airport.client -- --eslint `.
|
||||
- Update `vite.config.js` to set up proxying and certs.
|
||||
- Update `HelloWorld` component to fetch and display weather information.
|
||||
- Create project file (`airport.client.esproj`).
|
||||
- Create `launch.json` to enable debugging.
|
||||
- Create `nuget.config` to specify location of the JavaScript Project System SDK (which is used in the first line in `airport.client.esproj`).
|
||||
- Add project to solution.
|
||||
- Add project to the startup projects list.
|
||||
- Write this file.
|
35
airport.client/README.md
Normal file
35
airport.client/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# airport.client
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
16
airport.client/airport.client.esproj
Normal file
16
airport.client/airport.client.esproj
Normal file
@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.784122">
|
||||
<PropertyGroup>
|
||||
<StartupCommand>npm run dev</StartupCommand>
|
||||
<JavaScriptTestRoot>.\</JavaScriptTestRoot>
|
||||
<JavaScriptTestFramework>Jest</JavaScriptTestFramework>
|
||||
<!-- Allows the build (or compile) script located on package.json to run on Build -->
|
||||
<ShouldRunBuildScript>false</ShouldRunBuildScript>
|
||||
<!-- Folder where production build objects will be placed -->
|
||||
<BuildOutputFolder>$(MSBuildProjectDirectory)\dist</BuildOutputFolder>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="src\plugins\" />
|
||||
<Folder Include="src\services\" />
|
||||
<Folder Include="src\store\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
13
airport.client/index.html
Normal file
13
airport.client/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
8
airport.client/jsconfig.json
Normal file
8
airport.client/jsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
10
airport.client/nuget.config
Normal file
10
airport.client/nuget.config
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
<disabledPackageSources>
|
||||
<clear />
|
||||
</disabledPackageSources>
|
||||
</configuration>
|
4419
airport.client/package-lock.json
generated
Normal file
4419
airport.client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
airport.client/package.json
Normal file
35
airport.client/package.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "airport.client",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.0-5",
|
||||
"axios": "^1.7.2",
|
||||
"bootstrap": "^4.6.2",
|
||||
"jquery": "^3.2.1",
|
||||
"popper.js": "^1.16.1",
|
||||
"vee-validate": "^4.13.1",
|
||||
"vue": "^3.4.21",
|
||||
"vue-axios": "^3.5.2",
|
||||
"vue-dadata-3": "^2.1.1",
|
||||
"vue-router": "^4.3.3",
|
||||
"vuex": "^4.1.0",
|
||||
"yup": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"sass": "^1.77.6",
|
||||
"vite": "^5.2.8"
|
||||
}
|
||||
}
|
BIN
airport.client/public/favicon.ico
Normal file
BIN
airport.client/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
55
airport.client/src/App.vue
Normal file
55
airport.client/src/App.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<nav class="navbar navbar-expand navbar-dark bg-dark">
|
||||
<a href="/home" class="navbar-brand">UL Airways</a>
|
||||
|
||||
|
||||
<div v-if="!currentUser" class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<router-link to="/register" class="nav-link">
|
||||
<font-awesome-icon icon="user-plus" /> Регистрация
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<router-link to="/login" class="nav-link">
|
||||
<font-awesome-icon icon="sign-in-alt" /> Вход
|
||||
</router-link>
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<div v-if="currentUser" class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<router-link to="/profile" class="nav-link">
|
||||
<font-awesome-icon icon="user" />
|
||||
{{ currentUser.name }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" v-on:click="logOut">
|
||||
<font-awesome-icon icon="sign-out-alt" /> Выход
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
currentUser() {
|
||||
return this.$store.state.auth.user;
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
logOut() {
|
||||
this.$store.dispatch('auth/logout');
|
||||
this.$router.push('/login');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
61
airport.client/src/App.vue.bak
Normal file
61
airport.client/src/App.vue.bak
Normal file
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<nav class="navbar navbar-expand navbar-dark bg-dark">
|
||||
<a href="/" class="navbar-brand">bezKoder</a>
|
||||
<div class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<router-link to="/home" class="nav-link">
|
||||
<font-awesome-icon icon="home" /> Äîì
|
||||
</router-link>
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<div v-if="!currentUser" class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<router-link to="/register" class="nav-link">
|
||||
<font-awesome-icon icon="user-plus" /> Sign Up
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<router-link to="/login" class="nav-link">
|
||||
<font-awesome-icon icon="sign-in-alt" /> Login
|
||||
</router-link>
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<div v-if="currentUser" class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<router-link to="/profile" class="nav-link">
|
||||
<font-awesome-icon icon="user" />
|
||||
{{ currentUser.name }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" v-on:click="logOut">
|
||||
<font-awesome-icon icon="sign-out-alt" /> LogOut
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
currentUser() {
|
||||
return this.$store.state.auth.user;
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
logOut() {
|
||||
this.$store.dispatch('auth/logout');
|
||||
this.$router.push('/login');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
86
airport.client/src/assets/base.css
Normal file
86
airport.client/src/assets/base.css
Normal file
@ -0,0 +1,86 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
1
airport.client/src/assets/logo.svg
Normal file
1
airport.client/src/assets/logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
After Width: | Height: | Size: 276 B |
35
airport.client/src/assets/main.css
Normal file
35
airport.client/src/assets/main.css
Normal file
@ -0,0 +1,35 @@
|
||||
@import './base.css';
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
729
airport.client/src/components/Flights.vue
Normal file
729
airport.client/src/components/Flights.vue
Normal file
@ -0,0 +1,729 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-show="step === 1">
|
||||
<h2>Шаг 1. Выберите подходящий рейс.</h2>
|
||||
|
||||
<div class="d-flex flex-row m-4">
|
||||
<input class="form-control" placeholder="Авиакомпания"
|
||||
v-on:input="FilterFn('companyName')" />
|
||||
<input class="form-control" placeholder="Место вылета"
|
||||
v-on:input="FilterFn('departurePlace')" />
|
||||
<input class="form-control" placeholder="Место назначения"
|
||||
v-on:input="FilterFn('arrivalPlace')" />
|
||||
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<div class="card text-dark bg-light mb-3" v-for="flight in flights">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-3">
|
||||
<img src="https://i.fbcd.co/products/resized/resized-750-500/transport-09-d05df5f8ee567a1c9e2f651f4c019dfc5f261a04bc5ae69f04deb72ace991f0b.jpg" class="img-fluid rounded-start" alt="...">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{flight.departurePlace}} - {{flight.arrivalPlace}}</h5>
|
||||
<p class="card-text">Россия {{flight.plane.nameModel}}</p>
|
||||
<p class="card-text"><small class="text-muted">Авиакомпания {{flight.companyName}}</small></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Дата вылета</h5>
|
||||
<p class="card-text">{{flight.departureDate}}</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Дата прибытия</h5>
|
||||
<p class="card-text"> {{flight.arrivalDate}}</p>
|
||||
<button type="button" class="btn btn-success" @click="selectFlight(flight)">
|
||||
<span>Забронировать место</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="step === 2">
|
||||
<h2>Шаг 2. Выберите посадочное место</h2>
|
||||
<div class="plane">
|
||||
<div class="cockpit">
|
||||
<h1>{{currentPlane}}</h1>
|
||||
</div>
|
||||
<div class="exit exit--front fuselage">
|
||||
|
||||
</div>
|
||||
<ol class="cabin fuselage">
|
||||
|
||||
<li class="row row--2">
|
||||
<ol class="seats" type="A">
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="2A" id="2A" />
|
||||
<label for="2A">2A</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="2B" id="2B" />
|
||||
<label for="2B">2B</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="2C" id="2C" />
|
||||
<label for="2C">2C</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="2D" id="2D" />
|
||||
<label for="2D">2D</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="2E" id="2E" />
|
||||
<label for="2E">2E</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="2F" id="2F" />
|
||||
<label for="2F">2F</label>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li class="row row--3">
|
||||
<ol class="seats" type="A">
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="3A" id="3A" />
|
||||
<label for="3A">3A</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="3B" id="3B" />
|
||||
<label for="3B">3B</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="3C" id="3C" />
|
||||
<label for="3C">3C</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="3D" id="3D" />
|
||||
<label for="3D">3D</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="3E" id="3E" />
|
||||
<label for="3E">3E</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="3F" id="3F" />
|
||||
<label for="3F">3F</label>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li class="row row--4">
|
||||
<ol class="seats" type="A">
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="4A" id="4A" />
|
||||
<label for="4A">4A</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="4B" id="4B" />
|
||||
<label for="4B">4B</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="4C" id="4C" />
|
||||
<label for="4C">4C</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="4D" id="4D" />
|
||||
<label for="4D">4D</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="4E" id="4E" />
|
||||
<label for="4E">4E</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="4F" id="4F" />
|
||||
<label for="4F">4F</label>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li class="row row--5">
|
||||
<ol class="seats" type="A">
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="5A" id="5A" />
|
||||
<label for="5A">5A</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="5B" id="5B" />
|
||||
<label for="5B">5B</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="5C" id="5C" />
|
||||
<label for="5C">5C</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="5D" id="5D" />
|
||||
<label for="5D">5D</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="5E" id="5E" />
|
||||
<label for="5E">5E</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="5F" id="5F" />
|
||||
<label for="5F">5F</label>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li class="row row--6">
|
||||
<ol class="seats" type="A">
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="6A" id="6A" />
|
||||
<label for="6A">6A</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="6B" id="6B" />
|
||||
<label for="6B">6B</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="6C" id="6C" />
|
||||
<label for="6C">6C</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="6D" id="6D" />
|
||||
<label for="6D">6D</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="6E" id="6E" />
|
||||
<label for="6E">6E</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="6F" id="6F" />
|
||||
<label for="6F">6F</label>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li class="row row--7">
|
||||
<ol class="seats" type="A">
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="7A" id="7A" />
|
||||
<label for="7A">7A</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="7B" id="7B" />
|
||||
<label for="7B">7B</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="7C" id="7C" />
|
||||
<label for="7C">7C</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="7D" id="7D" />
|
||||
<label for="7D">7D</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="7E" id="7E" />
|
||||
<label for="7E">7E</label>
|
||||
</li>
|
||||
<li class="seat">
|
||||
<input v-model="checkedSeats" name="place" type="checkbox" value="7F" id="7F" />
|
||||
<label for="7F">7F</label>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ol>
|
||||
<div class="exit exit--back fuselage">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="button" class="btn btn-info" @click="reserve">
|
||||
<span>Забронировать места</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-show="step === 3">
|
||||
<h2>Шаг 3. Оплата. Введите банковскую карту.</h2>
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- credit card info-->
|
||||
<div id="nav-tab-card" class="tab-pane fade show active mt-4">
|
||||
<Form @submit="handlePay" :validation-schema="schema">
|
||||
<div class="form-group">
|
||||
<label for="username">Полное имя (указано на карте)</label>
|
||||
<Field name="username" placeholder="Ivanov Ivan" type="text" class="form-control" />
|
||||
<ErrorMessage name="username" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="cardNumber">Номер карты</label>
|
||||
<div class="input-group">
|
||||
<Field name="cardNumber" type="text" placeholder="0000 0000 0000 0000" class="form-control" />
|
||||
<ErrorMessage name="cardNumber" class="error-feedback" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<div class="form-group">
|
||||
<label><span class="hidden-xs">Срок действия</span></label>
|
||||
<div class="input-group">
|
||||
<Field name="month" placeholder="MM" type="number" class="form-control" />
|
||||
|
||||
<Field name="year" placeholder="YY" type="number" class="form-control" />
|
||||
|
||||
</div>
|
||||
<ErrorMessage name="month" class="error-feedback" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group mb-4">
|
||||
<label title="Трёх-значный код с обратной стороны карты">
|
||||
CVV
|
||||
</label>
|
||||
<Field name="cvv" placeholder="***" type="number" class="form-control" />
|
||||
<ErrorMessage name="cvv" class="error-feedback" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block">
|
||||
<span>Подтвердить</span>
|
||||
</button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<h2></h2>
|
||||
<h4>Итого: {{pay}} руб. * {{checkedSeats.length}} билет(ов)</h4>
|
||||
<h4>К оплате: {{currentTicket.payment}} руб.</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { Form, Field, ErrorMessage } from "vee-validate";
|
||||
import * as yup from "yup";
|
||||
import UserService from "../services/user.service";
|
||||
import { computed } from 'vue';
|
||||
|
||||
export default {
|
||||
name: "Flights",
|
||||
components: {
|
||||
Form,
|
||||
Field,
|
||||
ErrorMessage,
|
||||
},
|
||||
data() {
|
||||
|
||||
const schema = yup.object().shape({
|
||||
username: yup
|
||||
.string()
|
||||
.required("Обязательное поле"),
|
||||
cardNumber: yup
|
||||
.string()
|
||||
.required("Обязательное поле"),
|
||||
month: yup
|
||||
.number()
|
||||
.required("Обязательное поле"),
|
||||
year: yup
|
||||
.number()
|
||||
.required("Обязательное поле"),
|
||||
cvv: yup
|
||||
.number()
|
||||
.required("Обязательное поле")
|
||||
});
|
||||
|
||||
return {
|
||||
step: 1,
|
||||
alert:"",
|
||||
currentTicket: {
|
||||
payment: 0,
|
||||
status: "Оплачено",
|
||||
customerId: 0,
|
||||
flightId: 0,
|
||||
tariffPlan: "Эконом"
|
||||
},
|
||||
|
||||
pay: 4999,
|
||||
|
||||
currentPlane:"",
|
||||
flights: [],
|
||||
flightsWithOutFilter: [],
|
||||
|
||||
checkedSeats: [],
|
||||
schema,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentUser() {
|
||||
return this.$store.state.auth.user;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getFlights();
|
||||
},
|
||||
methods: {
|
||||
selectFlight(flight){
|
||||
this.currentTicket.flightId = flight.id;
|
||||
this.currentTicket.customerId = this.currentUser.id;
|
||||
this.currentPlane = flight.plane.nameModel;
|
||||
|
||||
this.checkDisabledSeats();
|
||||
|
||||
this.next();
|
||||
},
|
||||
checkDisabledSeats(){
|
||||
UserService.getSeats(this.currentTicket.flightId).then(
|
||||
(response) => {
|
||||
const disabledSeats = response.data;
|
||||
|
||||
const disSeat = document.querySelectorAll('input[name="place"]');
|
||||
|
||||
for (const checkbox of disSeat) {
|
||||
for(const dis of disabledSeats){
|
||||
if(dis.pozition.includes(checkbox.value)){
|
||||
checkbox.disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.alert =
|
||||
(error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
getFlights() {
|
||||
UserService.getFlights().then(
|
||||
(response) => {
|
||||
this.flights = response.data;
|
||||
this.flightsWithOutFilter = response.data;
|
||||
},
|
||||
(error) => {
|
||||
this.alert =
|
||||
(error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
FilterFn(param){
|
||||
var Search = event.target.value
|
||||
this.flights=this.flightsWithOutFilter.filter(
|
||||
function(el){
|
||||
return el[param].toString().toLowerCase().includes(
|
||||
Search.toString().trim().toLowerCase()
|
||||
)
|
||||
});
|
||||
},
|
||||
reserve(){
|
||||
if (this.checkedSeats.length > 0) this.next();
|
||||
this.currentTicket.payment = this.pay * this.checkedSeats.length;
|
||||
},
|
||||
|
||||
handlePay(){
|
||||
UserService.createTicket(this.currentTicket).then(
|
||||
(error) => {
|
||||
this.alert =
|
||||
(error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
}
|
||||
);
|
||||
|
||||
UserService.getLastTicket().then(
|
||||
(response) => {
|
||||
const newTicket = response.data.at(-1);
|
||||
for (const seat of this.checkedSeats) {
|
||||
UserService.createSeats({
|
||||
Pozition:seat,
|
||||
FlightId:newTicket.flightId,
|
||||
TicketId:newTicket.id
|
||||
})
|
||||
};
|
||||
this.$router.push("/home");
|
||||
},
|
||||
(error) => {
|
||||
this.alert =
|
||||
(error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
prev() {
|
||||
this.step--;
|
||||
},
|
||||
next() {
|
||||
this.step++;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
h2 {
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
background-color: lightblue;
|
||||
}
|
||||
.error-feedback {
|
||||
color: red;
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 1rem;
|
||||
}
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.plane {
|
||||
margin: 20px auto;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.cockpit {
|
||||
height: 250px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
border-bottom: 5px solid #d8d8d8;
|
||||
&:before{
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 500px;
|
||||
width: 100%;
|
||||
border-radius: 50%;
|
||||
border-right: 5px solid #d8d8d8;
|
||||
border-left: 5px solid #d8d8d8;
|
||||
}
|
||||
h1 {
|
||||
width: 60%;
|
||||
margin: 100px auto 35px auto;
|
||||
}
|
||||
}
|
||||
|
||||
.exit {
|
||||
position: relative;
|
||||
height: 50px;
|
||||
&:before, &:after{
|
||||
content: "EXIT";
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
padding: 0px 2px;
|
||||
font-family: "Arial Narrow", Arial, sans-serif;
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: green;
|
||||
color: white;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
} &:before {
|
||||
left: 0;
|
||||
} &:after {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fuselage {
|
||||
border-right: 5px solid #d8d8d8;
|
||||
border-left: 5px solid #d8d8d8;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
}
|
||||
|
||||
.seats {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.seat {
|
||||
display: flex;
|
||||
flex: 0 0 30%;
|
||||
padding: 5px;
|
||||
position: relative;
|
||||
&:nth-child(3){
|
||||
margin-right: 15%;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked {
|
||||
+ label {
|
||||
background: #bada55;
|
||||
-webkit-animation-name: rubberBand;
|
||||
animation-name: rubberBand;
|
||||
animation-duration: 300ms;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
input[type="checkbox"]:disabled {
|
||||
+ label {
|
||||
background: #dddddd;
|
||||
text-indent: -9999px;
|
||||
overflow: hidden;
|
||||
&:after{
|
||||
content: "X";
|
||||
text-indent: 0;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
line-height: 1.5rem;
|
||||
padding: 4px 0;
|
||||
background: #f42536;
|
||||
border-radius: 5px;
|
||||
animation-duration: 300ms;
|
||||
animation-fill-mode: both;
|
||||
&:before
|
||||
|
||||
{
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 75%;
|
||||
height: 75%;
|
||||
top: 1px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 0px 2px #5c6aff;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes rubberBand {
|
||||
0% {
|
||||
-webkit-transform: scale3d(1, 1, 1);
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
|
||||
30% {
|
||||
-webkit-transform: scale3d(1.25, 0.75, 1);
|
||||
transform: scale3d(1.25, 0.75, 1);
|
||||
}
|
||||
|
||||
40% {
|
||||
-webkit-transform: scale3d(0.75, 1.25, 1);
|
||||
transform: scale3d(0.75, 1.25, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: scale3d(1.15, 0.85, 1);
|
||||
transform: scale3d(1.15, 0.85, 1);
|
||||
}
|
||||
|
||||
65% {
|
||||
-webkit-transform: scale3d(0.95, 1.05, 1);
|
||||
transform: scale3d(0.95, 1.05, 1);
|
||||
}
|
||||
|
||||
75% {
|
||||
-webkit-transform: scale3d(1.05, 0.95, 1);
|
||||
transform: scale3d(1.05, 0.95, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: scale3d(1, 1, 1);
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rubberBand {
|
||||
0% {
|
||||
-webkit-transform: scale3d(1, 1, 1);
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
|
||||
30% {
|
||||
-webkit-transform: scale3d(1.25, 0.75, 1);
|
||||
transform: scale3d(1.25, 0.75, 1);
|
||||
}
|
||||
|
||||
40% {
|
||||
-webkit-transform: scale3d(0.75, 1.25, 1);
|
||||
transform: scale3d(0.75, 1.25, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform: scale3d(1.15, 0.85, 1);
|
||||
transform: scale3d(1.15, 0.85, 1);
|
||||
}
|
||||
|
||||
65% {
|
||||
-webkit-transform: scale3d(0.95, 1.05, 1);
|
||||
transform: scale3d(0.95, 1.05, 1);
|
||||
}
|
||||
|
||||
75% {
|
||||
-webkit-transform: scale3d(1.05, 0.95, 1);
|
||||
transform: scale3d(1.05, 0.95, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: scale3d(1, 1, 1);
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.rubberBand {
|
||||
-webkit-animation-name: rubberBand;
|
||||
animation-name: rubberBand;
|
||||
}
|
||||
|
||||
</style>
|
72
airport.client/src/components/Flights.vue.bak
Normal file
72
airport.client/src/components/Flights.vue.bak
Normal file
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="mt-4" v-for="plane in airways">
|
||||
<h4>{{plane.nameModel}}</h4>
|
||||
<div v-for="flight in plane.flights">
|
||||
<div class="card">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-8">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{flight.departureplace}} - {{flight.arrivalplace}}</h5>
|
||||
<p class="card-text">Đîńńč˙ {{plane.namemodel}}</p>
|
||||
<p class="card-text"><small class="text-muted">Ŕâčŕęîěďŕíč˙ {{flight.companyname}}</small></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Äŕňŕ âűëĺňŕ</h5>
|
||||
<p class="card-text">{{flight.departuredate}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Äŕňŕ ďđčë¸ňŕ</h5>
|
||||
<p class="card-text"> {{flight.arrivaldate}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import UserService from "../services/user.service";
|
||||
|
||||
export default {
|
||||
name: "Flights",
|
||||
data() {
|
||||
return {
|
||||
airways: [],
|
||||
alert:"",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentUser() {
|
||||
return this.$store.state.auth.user;
|
||||
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getAirways();
|
||||
},
|
||||
methods: {
|
||||
getAirways() {
|
||||
UserService.getAirways().then(
|
||||
(response) => {
|
||||
this.airways = response.data;
|
||||
console.log(this.airways);
|
||||
},
|
||||
(error) => {
|
||||
this.alert =
|
||||
(error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
87
airport.client/src/components/HelloWorld.vue
Normal file
87
airport.client/src/components/HelloWorld.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="weather-component">
|
||||
<h1>Weather forecast</h1>
|
||||
<p>This component demonstrates fetching data from the server.</p>
|
||||
|
||||
<div v-if="loading" class="loading">
|
||||
Loading... Please refresh once the ASP.NET backend has started. See <a href="https://aka.ms/jspsintegrationvue">https://aka.ms/jspsintegrationvue</a> for more details.
|
||||
</div>
|
||||
|
||||
<div v-if="post" class="content">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Temp. (C)</th>
|
||||
<th>Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="forecast in post" :key="forecast.date">
|
||||
<td>{{ forecast.date }}</td>
|
||||
<td>{{ forecast.temperatureC }}</td>
|
||||
<td>{{ forecast.temperatureF }}</td>
|
||||
<td>{{ forecast.summary }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
post: null
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// fetch the data when the view is created and the data is
|
||||
// already being observed
|
||||
this.fetchData();
|
||||
},
|
||||
watch: {
|
||||
// call again the method if the route changes
|
||||
'$route': 'fetchData'
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
this.post = null;
|
||||
this.loading = true;
|
||||
|
||||
fetch('/api/weatherforecast')
|
||||
.then(r => r.json())
|
||||
.then(json => {
|
||||
this.post = json;
|
||||
this.loading = false;
|
||||
return;
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding-left: .5rem;
|
||||
padding-right: .5rem;
|
||||
}
|
||||
|
||||
.weather-component {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
</style>
|
32
airport.client/src/components/HomePage.vue
Normal file
32
airport.client/src/components/HomePage.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="mt-4">
|
||||
<div class="card bg-dark text-white">
|
||||
<img src="https://avatars.mds.yandex.net/get-mpic/1591646/img_id6716452366759500433.jpeg/orig" class="card-img" alt="...">
|
||||
<div class="card-footer">
|
||||
<button type="button" class="btn btn-light" @click="navigate">
|
||||
<span>Подобрать Авиарейс</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "HomePage",
|
||||
|
||||
computed: {
|
||||
loggedIn() {
|
||||
return this.$store.state.auth.status.loggedIn;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
navigate() {
|
||||
if (this.loggedIn) {
|
||||
this.$router.push("/flights");
|
||||
} else {
|
||||
this.$router.push("/login");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
141
airport.client/src/components/Login.vue
Normal file
141
airport.client/src/components/Login.vue
Normal file
@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div class="col-md-12">
|
||||
<div class="card card-container">
|
||||
<img id="profile-img"
|
||||
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
|
||||
class="profile-img-card" />
|
||||
<Form @submit="handleLogin" :validation-schema="schema">
|
||||
<div class="form-group">
|
||||
<label for="login">Электронная почта</label>
|
||||
<Field v-model="user.login" name="login" type="email" class="form-control" />
|
||||
<ErrorMessage name="login" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Пароль</label>
|
||||
<Field v-model="user.password" name="password" type="password" class="form-control" />
|
||||
<ErrorMessage name="password" class="error-feedback" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block" :disabled="loading">
|
||||
<span v-show="loading"
|
||||
class="spinner-border spinner-border-sm"></span>
|
||||
<span>Login</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div v-if="message" class="alert alert-danger" role="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Form, Field, ErrorMessage } from "vee-validate";
|
||||
import * as yup from "yup";
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
components: {
|
||||
Form,
|
||||
Field,
|
||||
ErrorMessage,
|
||||
},
|
||||
data() {
|
||||
const schema = yup.object().shape({
|
||||
login: yup
|
||||
.string()
|
||||
.required("'Электронная почта' обязательна для заполнения")
|
||||
.email("Email указан некорректно")
|
||||
.max(50, "Максимум 50 символов"),
|
||||
password: yup
|
||||
.string()
|
||||
.required("Пароль обязателен к заполнению"),
|
||||
});
|
||||
|
||||
return {
|
||||
loading: false,
|
||||
message: "",
|
||||
schema,
|
||||
user: {
|
||||
login: "",
|
||||
password: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
loggedIn() {
|
||||
return this.$store.state.auth.status.loggedIn;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.loggedIn) {
|
||||
this.$router.push("/flights");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleLogin(user) {
|
||||
this.loading = true;
|
||||
|
||||
this.$store.dispatch("auth/login", this.user).then(
|
||||
() => {
|
||||
this.$router.push("/profile");
|
||||
},
|
||||
(error) => {
|
||||
this.loading = false;
|
||||
this.message =
|
||||
(error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.card-container.card {
|
||||
max-width: 350px !important;
|
||||
padding: 40px 40px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #f7f7f7;
|
||||
padding: 20px 25px 30px;
|
||||
margin: 0 auto 25px;
|
||||
margin-top: 50px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
-moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.profile-img-card {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
margin: 0 auto 10px;
|
||||
display: block;
|
||||
-moz-border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.error-feedback {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
137
airport.client/src/components/Login.vue.bak
Normal file
137
airport.client/src/components/Login.vue.bak
Normal file
@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div class="col-md-12">
|
||||
<div class="card card-container">
|
||||
<img id="profile-img"
|
||||
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
|
||||
class="profile-img-card" />
|
||||
<Form @submit="handleLogin" :validation-schema="schema">
|
||||
<div class="form-group">
|
||||
<label for="username">Email</label>
|
||||
<Field v-model="user.email" name="email" type="email" class="form-control" />
|
||||
<ErrorMessage name="email" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<Field v-model="user.password" name="password" type="password" class="form-control" />
|
||||
<ErrorMessage name="password" class="error-feedback" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block">
|
||||
<span v-show="process"
|
||||
class="spinner-border spinner-border-sm"></span>
|
||||
<span> Âîéòè </span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div v-if="message" class="alert alert-danger" role="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Form, Field, ErrorMessage } from "vee-validate";
|
||||
import * as yup from "yup";
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
components: {
|
||||
Form,
|
||||
Field,
|
||||
ErrorMessage,
|
||||
},
|
||||
data() {
|
||||
const schema = yup.object().shape({
|
||||
email: yup
|
||||
.string()
|
||||
.required("Email is required!")
|
||||
.email("Email is invalid!")
|
||||
.max(50, "Must be maximum 50 characters!"),
|
||||
password: yup
|
||||
.string()
|
||||
.required("Password is required!"),
|
||||
});
|
||||
|
||||
return {
|
||||
user: {
|
||||
email: "",
|
||||
password: ""
|
||||
},
|
||||
process: false,
|
||||
message: "",
|
||||
schema,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleLogin() {
|
||||
this.process = true;
|
||||
|
||||
axios.get(this.hostname + "/api/customer/signin/" + this.user.email + "/" + this.user.password)
|
||||
.then((response) => {
|
||||
if (response.data.id > 0) {
|
||||
localStorage.setItem('token', JSON.stringify(response.data.token));
|
||||
response.data.token = "";
|
||||
localStorage.setItem('user', JSON.stringify(response.data));
|
||||
this.$router.push("/register");
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.process = false;
|
||||
if (error.response) {
|
||||
this.message = (error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) || error.message ||
|
||||
error.toString();
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.card-container.card {
|
||||
max-width: 350px !important;
|
||||
padding: 40px 40px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #f7f7f7;
|
||||
padding: 20px 25px 30px;
|
||||
margin: 0 auto 25px;
|
||||
margin-top: 50px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
-moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.profile-img-card {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
margin: 0 auto 10px;
|
||||
display: block;
|
||||
-moz-border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.error-feedback {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
87
airport.client/src/components/Profile.vue
Normal file
87
airport.client/src/components/Profile.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="weather-component">
|
||||
<h1>Weather forecast</h1>
|
||||
<p>This component demonstrates fetching data from the server.</p>
|
||||
|
||||
<div v-if="loading" class="loading">
|
||||
Loading... Please refresh once the ASP.NET backend has started. See <a href="https://aka.ms/jspsintegrationvue">https://aka.ms/jspsintegrationvue</a> for more details.
|
||||
</div>
|
||||
|
||||
<div v-if="post" class="content">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Temp. (C)</th>
|
||||
<th>Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="forecast in post" :key="forecast.date">
|
||||
<td>{{ forecast.date }}</td>
|
||||
<td>{{ forecast.temperatureC }}</td>
|
||||
<td>{{ forecast.temperatureF }}</td>
|
||||
<td>{{ forecast.summary }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
post: null
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// fetch the data when the view is created and the data is
|
||||
// already being observed
|
||||
this.fetchData();
|
||||
},
|
||||
watch: {
|
||||
// call again the method if the route changes
|
||||
'$route': 'fetchData'
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
this.post = null;
|
||||
this.loading = true;
|
||||
|
||||
fetch('/api/weatherforecast')
|
||||
.then(r => r.json())
|
||||
.then(json => {
|
||||
this.post = json;
|
||||
this.loading = false;
|
||||
return;
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding-left: .5rem;
|
||||
padding-right: .5rem;
|
||||
}
|
||||
|
||||
.weather-component {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
</style>
|
210
airport.client/src/components/Register.vue
Normal file
210
airport.client/src/components/Register.vue
Normal file
@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<div class="col-md-12">
|
||||
<div class="card card-container">
|
||||
<img id="profile-img"
|
||||
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
|
||||
class="profile-img-card" />
|
||||
<Form @submit="handleRegister" :validation-schema="schema">
|
||||
<div v-if="!successful">
|
||||
<div class="form-group">
|
||||
<label for="name">Имя</label>
|
||||
<Field v-model="user.name" name="name" type="text" class="form-control" />
|
||||
<ErrorMessage name="name" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="surname">Фамилия</label>
|
||||
<Field v-model="user.surname" name="surname" type="text" class="form-control" />
|
||||
<ErrorMessage name="surname" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="patronymic">Отчество</label>
|
||||
<Field v-model="user.patronymic" name="patronymic" type="text" class="form-control" />
|
||||
<ErrorMessage name="patronymic" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="phoneNumber">Номер телефона</label>
|
||||
<Field v-model="user.phoneNumber" name="phoneNumber" type="text" class="form-control" />
|
||||
<ErrorMessage name="phoneNumber" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="address">Адрес проживания</label>
|
||||
<da-data-next v-model="user.address" ></da-data-next>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="login">Электронная почта</label>
|
||||
<Field v-model="user.login" name="login" type="email" class="form-control" />
|
||||
<ErrorMessage name="login" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Пароль</label>
|
||||
<Field v-model="user.password" name="password" type="password" class="form-control" />
|
||||
<ErrorMessage name="password" class="error-feedback" />
|
||||
</div>
|
||||
|
||||
<div class="form-group d-flex flex-column align-items-center">
|
||||
<button class="btn btn-primary btn-block" :disabled="loading">
|
||||
<span v-show="loading"
|
||||
class="spinner-border spinner-border-sm"></span>
|
||||
Зарегистрироваться
|
||||
</button>
|
||||
<button type="button" @click="backClick()" class="btn btn-link">
|
||||
Авторизоваться
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
<div v-if="message" :class="successful ? 'alert alert-success' : 'alert alert-danger'">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Form, Field, ErrorMessage} from "vee-validate";
|
||||
import axios from "axios";
|
||||
import * as yup from "yup";
|
||||
|
||||
export default {
|
||||
name: "Register",
|
||||
components: {
|
||||
Form,
|
||||
Field,
|
||||
ErrorMessage,
|
||||
},
|
||||
data() {
|
||||
const phoneRegExp = /^(\s*)?(\+)?([- _():=+]?\d[- _():=+]?){10,14}(\s*)?$/
|
||||
|
||||
const schema = yup.object().shape({
|
||||
name: yup
|
||||
.string()
|
||||
.required("Поле 'Имя' обязательно для заполнения")
|
||||
.min(2, "Минимум 2 символа")
|
||||
.max(20, "Максимум 20 символов"),
|
||||
surname: yup
|
||||
.string()
|
||||
.required("Поле 'Фамилия' обязательно для заполнения")
|
||||
.min(2, "Минимум 2 символа")
|
||||
.max(20, "Максимум 20 символов"),
|
||||
patronymic: yup
|
||||
.string()
|
||||
.required("Поле 'Отчество' обязательно для заполнения")
|
||||
.min(2, "Минимум 2 символа")
|
||||
.max(20, "Максимум 20 символов"),
|
||||
phoneNumber: yup
|
||||
.string()
|
||||
.required("Поле 'Номер телефона' обязательно для заполнения")
|
||||
.matches(phoneRegExp, 'Номер телефона указан неверно'),
|
||||
login: yup
|
||||
.string()
|
||||
.required("'Электронная почта' обязательна для заполнения")
|
||||
.email("Email указан некорректно")
|
||||
.max(50, "Максимум 50 символов"),
|
||||
password: yup
|
||||
.string()
|
||||
.required("Пароль обязателен к заполнению")
|
||||
.min(6, "Минимум 6 символов")
|
||||
.max(40, "Максимум 40 символов"),
|
||||
});
|
||||
|
||||
return {
|
||||
successful: false,
|
||||
loading: false,
|
||||
message: "",
|
||||
schema,
|
||||
|
||||
user: {
|
||||
name: "",
|
||||
surname: "",
|
||||
patronymic: "",
|
||||
phone: "",
|
||||
address: "",
|
||||
benefir: "Отсутствует",
|
||||
login: "",
|
||||
password: "",
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
loggedIn() {
|
||||
return this.$store.state.auth.status.loggedIn;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.loggedIn) {
|
||||
this.$router.push("/profile");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
backClick(){
|
||||
this.$router.push('/login');
|
||||
},
|
||||
handleRegister(user) {
|
||||
this.message = "";
|
||||
this.successful = false;
|
||||
this.loading = true;
|
||||
|
||||
this.$store.dispatch("auth/register", this.user).then(
|
||||
(data) => {
|
||||
this.message = data.message;
|
||||
this.successful = true;
|
||||
this.loading = false;
|
||||
this.$router.push('/login');
|
||||
},
|
||||
(error) => {
|
||||
this.message =
|
||||
(error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
this.successful = false;
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.card-container.card {
|
||||
max-width: 350px !important;
|
||||
padding: 40px 40px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #f7f7f7;
|
||||
padding: 20px 25px 30px;
|
||||
margin: 0 auto 25px;
|
||||
margin-top: 50px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
-moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.profile-img-card {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
margin: 0 auto 10px;
|
||||
display: block;
|
||||
-moz-border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.error-feedback {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
151
airport.client/src/components/Register.vue.bak
Normal file
151
airport.client/src/components/Register.vue.bak
Normal file
@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div class="col-md-12">
|
||||
<div class="card card-container">
|
||||
<img id="profile-img"
|
||||
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
|
||||
class="profile-img-card" />
|
||||
<Form @submit="handleRegister" :validation-schema="schema">
|
||||
<div v-if="!successful">
|
||||
<div class="form-group">
|
||||
<label for="username">Èìÿ</label>
|
||||
<Field name="username" type="text" class="form-control" />
|
||||
<ErrorMessage name="username" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="usersurname">Ôàìèëèÿ</label>
|
||||
<Field name="usersurname" type="text" class="form-control" />
|
||||
<ErrorMessage name="usersurname" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="userpatronymic">Îò÷åñòâî</label>
|
||||
<Field name="userpatronymic" type="text" class="form-control" />
|
||||
<ErrorMessage name="userpatronymic" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="userphone">Íîìåð òåëåôîíà</label>
|
||||
<Field name="userphone" type="text" class="form-control" />
|
||||
<ErrorMessage name="userphone" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="useraddress">Àäðåñ ïðîæèâàíèÿ</label>
|
||||
<DaDataNext v-model="query" :token="token" type="text" class="form-control"/>
|
||||
<ErrorMessage name="useraddress" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<Field name="email" type="email" class="form-control" />
|
||||
<ErrorMessage name="email" class="error-feedback" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<Field name="password" type="password" class="form-control" />
|
||||
<ErrorMessage name="password" class="error-feedback" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block">
|
||||
<span v-show="loading"
|
||||
class="spinner-border spinner-border-sm"></span>
|
||||
Sign Up
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
<div v-if="message"
|
||||
class="alert"
|
||||
:class="successful ? 'alert-success' : 'alert-danger'">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Form, Field, ErrorMessage } from "vee-validate";
|
||||
import { DaDataNext } from 'vue-dadata-3';
|
||||
import * as yup from "yup";
|
||||
|
||||
export default {
|
||||
name: "Register",
|
||||
components: {
|
||||
Form,
|
||||
Field,
|
||||
ErrorMessage,
|
||||
DaDataNext,
|
||||
},
|
||||
data() {
|
||||
const phoneRegExp = /^((\\+[1-9]{1,4}[ \\-]*)|(\\([0-9]{2,3}\\)[ \\-]*)|([0-9]{2,4})[ \\-]*)*?[0-9]{3,4}?[ \\-]*[0-9]{3,4}?$/
|
||||
|
||||
const schema = yup.object().shape({
|
||||
username: yup
|
||||
.string()
|
||||
.required("Ïîëå 'Èìÿ' îáÿçàòåëüíî äëÿ çàïîëíåíèÿ")
|
||||
.min(2, "Ìèíèìóì 2 ñèìâîëà")
|
||||
.max(20, "Ìàêñèìóì 20 ñèìâîëîâ"),
|
||||
usersurname: yup
|
||||
.string()
|
||||
.required("Ïîëå 'Ôàìèëèÿ' îáÿçàòåëüíî äëÿ çàïîëíåíèÿ")
|
||||
.min(2, "Ìèíèìóì 2 ñèìâîëà")
|
||||
.max(20, "Ìàêñèìóì 20 ñèìâîëîâ"),
|
||||
userpatronymic: yup
|
||||
.string()
|
||||
.required("Ïîëå 'Îò÷åñòâî' îáÿçàòåëüíî äëÿ çàïîëíåíèÿ")
|
||||
.min(2, "Ìèíèìóì 2 ñèìâîëà")
|
||||
.max(20, "Ìàêñèìóì 20 ñèìâîëîâ"),
|
||||
userphone: yup.string()
|
||||
.required("required")
|
||||
.matches(phoneRegExp, 'Íîìåð òåëåôîíà óêàçàí íåâåðíî')
|
||||
.min(10, "too short")
|
||||
.max(10, "too long"),
|
||||
userphone: yup
|
||||
.string()
|
||||
.required("Ïîëå 'Àäðåñ' îáÿçàòåëüíî äëÿ çàïîëíåíèÿ"),
|
||||
email: yup
|
||||
.string()
|
||||
.required("Email is required!")
|
||||
.email("Email is invalid!")
|
||||
.max(50, "Must be maximum 50 characters!"),
|
||||
password: yup
|
||||
.string()
|
||||
.required("Password is required!")
|
||||
.min(6, "Must be at least 6 characters!")
|
||||
.max(40, "Must be maximum 40 characters!"),
|
||||
});
|
||||
|
||||
return {
|
||||
successful: false,
|
||||
loading: false,
|
||||
message: "",
|
||||
schema,
|
||||
query: "",
|
||||
token: "d14f2e04a7bc56c96fe6f6e797d6bfcacd0e7e72",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleRegister(user) {
|
||||
this.message = "";
|
||||
this.successful = false;
|
||||
this.loading = true;
|
||||
|
||||
this.$store.dispatch("auth/register", user).then(
|
||||
(data) => {
|
||||
this.message = data.message;
|
||||
this.successful = true;
|
||||
this.loading = false;
|
||||
},
|
||||
(error) => {
|
||||
this.message =
|
||||
(error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
this.successful = false;
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
88
airport.client/src/components/TheWelcome.vue
Normal file
88
airport.client/src/components/TheWelcome.vue
Normal file
@ -0,0 +1,88 @@
|
||||
<script setup>
|
||||
import WelcomeItem from './WelcomeItem.vue'
|
||||
import DocumentationIcon from './icons/IconDocumentation.vue'
|
||||
import ToolingIcon from './icons/IconTooling.vue'
|
||||
import EcosystemIcon from './icons/IconEcosystem.vue'
|
||||
import CommunityIcon from './icons/IconCommunity.vue'
|
||||
import SupportIcon from './icons/IconSupport.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<DocumentationIcon />
|
||||
</template>
|
||||
<template #heading>Documentation</template>
|
||||
|
||||
Vue’s
|
||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
|
||||
provides you with all information you need to get started.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<ToolingIcon />
|
||||
</template>
|
||||
<template #heading>Tooling</template>
|
||||
|
||||
This project is served and bundled with
|
||||
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
|
||||
recommended IDE setup is
|
||||
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
|
||||
you need to test your components and web pages, check out
|
||||
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
|
||||
<a href="https://on.cypress.io/component" target="_blank" rel="noopener"
|
||||
>Cypress Component Testing</a
|
||||
>.
|
||||
|
||||
<br />
|
||||
|
||||
More instructions are available in <code>README.md</code>.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<EcosystemIcon />
|
||||
</template>
|
||||
<template #heading>Ecosystem</template>
|
||||
|
||||
Get official tools and libraries for your project:
|
||||
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
|
||||
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
|
||||
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
|
||||
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
|
||||
you need more resources, we suggest paying
|
||||
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
|
||||
a visit.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<CommunityIcon />
|
||||
</template>
|
||||
<template #heading>Community</template>
|
||||
|
||||
Got stuck? Ask your question on
|
||||
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
|
||||
Discord server, or
|
||||
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
|
||||
>StackOverflow</a
|
||||
>. You should also subscribe to
|
||||
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
|
||||
the official
|
||||
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
|
||||
twitter account for latest news in the Vue world.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<SupportIcon />
|
||||
</template>
|
||||
<template #heading>Support Vue</template>
|
||||
|
||||
As an independent project, Vue relies on community backing for its sustainability. You can help
|
||||
us by
|
||||
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
|
||||
</WelcomeItem>
|
||||
</template>
|
87
airport.client/src/components/WelcomeItem.vue
Normal file
87
airport.client/src/components/WelcomeItem.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="item">
|
||||
<i>
|
||||
<slot name="icon"></slot>
|
||||
</i>
|
||||
<div class="details">
|
||||
<h3>
|
||||
<slot name="heading"></slot>
|
||||
</h3>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.item {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.details {
|
||||
flex: 1;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
i {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.4rem;
|
||||
color: var(--color-heading);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.item {
|
||||
margin-top: 0;
|
||||
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
i {
|
||||
top: calc(50% - 25px);
|
||||
left: -26px;
|
||||
position: absolute;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-background);
|
||||
border-radius: 8px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.item:before {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:after {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:first-of-type:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.item:last-of-type:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
7
airport.client/src/components/icons/IconCommunity.vue
Normal file
7
airport.client/src/components/icons/IconCommunity.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||
<path
|
||||
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
7
airport.client/src/components/icons/IconEcosystem.vue
Normal file
7
airport.client/src/components/icons/IconEcosystem.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
7
airport.client/src/components/icons/IconSupport.vue
Normal file
7
airport.client/src/components/icons/IconSupport.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
19
airport.client/src/components/icons/IconTooling.vue
Normal file
19
airport.client/src/components/icons/IconTooling.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
class="iconify iconify--mdi"
|
||||
width="24"
|
||||
height="24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
16
airport.client/src/main.js
Normal file
16
airport.client/src/main.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import store from "./store";
|
||||
import DaDataNext from 'vue-dadata-3';
|
||||
import "vue-dadata-3/index.css";
|
||||
import "bootstrap";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import { FontAwesomeIcon } from './plugins/font-awesome';
|
||||
|
||||
createApp(App)
|
||||
.use(router)
|
||||
.use(store)
|
||||
.use(DaDataNext, { tag: 'da-data-next', token: '265601fe9dee1c0fcb9efbde75a4c0de289546ab',})
|
||||
.component("font-awesome-icon", FontAwesomeIcon)
|
||||
.mount("#app");
|
14
airport.client/src/plugins/font-awesome.js
Normal file
14
airport.client/src/plugins/font-awesome.js
Normal file
@ -0,0 +1,14 @@
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import {
|
||||
faHome,
|
||||
faUser,
|
||||
faUserPlus,
|
||||
faSignInAlt,
|
||||
faSignOutAlt,
|
||||
faCreditCard
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
library.add(faHome, faUser, faUserPlus, faSignInAlt, faSignOutAlt, faCreditCard);
|
||||
|
||||
export { FontAwesomeIcon };
|
60
airport.client/src/router.js
Normal file
60
airport.client/src/router.js
Normal file
@ -0,0 +1,60 @@
|
||||
import { createWebHistory, createRouter } from "vue-router";
|
||||
import HomePage from "./components/HomePage.vue";
|
||||
import Login from "./components/Login.vue";
|
||||
import Register from "./components/Register.vue";
|
||||
// lazy-loaded
|
||||
const Profile = () => import("./components/Profile.vue")
|
||||
const Flights = () => import("./components/Flights.vue")
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
component: HomePage,
|
||||
},
|
||||
{
|
||||
path: "/home",
|
||||
component: HomePage,
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
component: Login,
|
||||
},
|
||||
{
|
||||
path: "/register",
|
||||
component: Register,
|
||||
},
|
||||
{
|
||||
path: "/profile",
|
||||
name: "Profile",
|
||||
// lazy-loaded
|
||||
component: Profile,
|
||||
},
|
||||
{
|
||||
path: "/flights",
|
||||
name: "Flights",
|
||||
// lazy-loaded
|
||||
component: Flights,
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const publicPages = ['/login', '/register', '/profile', '/flights', '/home'];
|
||||
const authRequired = !publicPages.includes(to.path);
|
||||
const loggedIn = localStorage.getItem('token');
|
||||
|
||||
// trying to access a restricted page + not logged in
|
||||
// redirect to login page
|
||||
if (authRequired && !loggedIn) {
|
||||
next('/login');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
12
airport.client/src/services/auth-header.js
Normal file
12
airport.client/src/services/auth-header.js
Normal file
@ -0,0 +1,12 @@
|
||||
export default function authHeader() {
|
||||
let user = JSON.parse(localStorage.getItem('user'));
|
||||
|
||||
if (user && user.token) {
|
||||
const config = {
|
||||
headers: { Authorization: 'Bearer ' + user.token }
|
||||
};
|
||||
return config;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
27
airport.client/src/services/auth.service.js
Normal file
27
airport.client/src/services/auth.service.js
Normal file
@ -0,0 +1,27 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const API_URL = 'https://localhost:7217/api/customer/';
|
||||
|
||||
class AuthService {
|
||||
login(user) {
|
||||
return axios
|
||||
.get(API_URL + "signin/" + user.login + "/" + user.password)
|
||||
.then(response => {
|
||||
if (response.data.token) {
|
||||
localStorage.setItem('user', JSON.stringify(response.data));
|
||||
}
|
||||
|
||||
return response.data;
|
||||
});
|
||||
}
|
||||
|
||||
logout() {
|
||||
localStorage.removeItem('user');
|
||||
}
|
||||
|
||||
register(user) {
|
||||
return axios.post(API_URL + "registercustomer", user);
|
||||
}
|
||||
}
|
||||
|
||||
export default new AuthService();
|
40
airport.client/src/services/user.service.js
Normal file
40
airport.client/src/services/user.service.js
Normal file
@ -0,0 +1,40 @@
|
||||
import axios from 'axios';
|
||||
import authHeader from './auth-header';
|
||||
|
||||
const API_URL = 'https://localhost:7217/api/';
|
||||
|
||||
class UserService {
|
||||
getPublicContent() {
|
||||
return axios.get(API_URL + 'customer/getallcutomers');
|
||||
}
|
||||
|
||||
getAirways() {
|
||||
return axios.get(API_URL + 'airplane/getallairplanes', authHeader());
|
||||
}
|
||||
|
||||
getFlights() {
|
||||
return axios.get(API_URL + 'flight/getallflights', authHeader());
|
||||
}
|
||||
|
||||
getSeats(id) {
|
||||
return axios.get(API_URL + 'seat/GetSeatsByFlight/' + id, authHeader());
|
||||
}
|
||||
|
||||
createSeats(seat) {
|
||||
return axios.post(API_URL + 'seat/CreateSeat' , seat, authHeader());
|
||||
}
|
||||
|
||||
createTicket(ticket) {
|
||||
return axios.post(API_URL + 'ticket/createticket', ticket, authHeader());
|
||||
}
|
||||
|
||||
getLastTicket() {
|
||||
return axios.get(API_URL + 'ticket/GetLastTicket', authHeader());
|
||||
}
|
||||
|
||||
//getAdminBoard() {
|
||||
// return axios.get(API_URL + 'admin', { headers: authHeader() });
|
||||
//}
|
||||
}
|
||||
|
||||
export default new UserService();
|
61
airport.client/src/store/auth.module.js
Normal file
61
airport.client/src/store/auth.module.js
Normal file
@ -0,0 +1,61 @@
|
||||
import AuthService from '../services/auth.service';
|
||||
|
||||
const user = JSON.parse(localStorage.getItem('user'));
|
||||
const initialState = user
|
||||
? { status: { loggedIn: true }, user }
|
||||
: { status: { loggedIn: false }, user: null };
|
||||
|
||||
export const auth = {
|
||||
namespaced: true,
|
||||
state: initialState,
|
||||
actions: {
|
||||
login({ commit }, user) {
|
||||
return AuthService.login(user).then(
|
||||
user => {
|
||||
commit('loginSuccess', user);
|
||||
return Promise.resolve(user);
|
||||
},
|
||||
error => {
|
||||
commit('loginFailure');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
logout({ commit }) {
|
||||
AuthService.logout();
|
||||
commit('logout');
|
||||
},
|
||||
register({ commit }, user) {
|
||||
return AuthService.register(user).then(
|
||||
response => {
|
||||
commit('registerSuccess');
|
||||
return Promise.resolve(response.data);
|
||||
},
|
||||
error => {
|
||||
commit('registerFailure');
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
loginSuccess(state, user) {
|
||||
state.status.loggedIn = true;
|
||||
state.user = user;
|
||||
},
|
||||
loginFailure(state) {
|
||||
state.status.loggedIn = false;
|
||||
state.user = null;
|
||||
},
|
||||
logout(state) {
|
||||
state.status.loggedIn = false;
|
||||
state.user = null;
|
||||
},
|
||||
registerSuccess(state) {
|
||||
state.status.loggedIn = false;
|
||||
},
|
||||
registerFailure(state) {
|
||||
state.status.loggedIn = false;
|
||||
}
|
||||
}
|
||||
};
|
10
airport.client/src/store/index.js
Normal file
10
airport.client/src/store/index.js
Normal file
@ -0,0 +1,10 @@
|
||||
import { createStore } from "vuex";
|
||||
import { auth } from "./auth.module";
|
||||
|
||||
const store = createStore({
|
||||
modules: {
|
||||
auth,
|
||||
},
|
||||
});
|
||||
|
||||
export default store;
|
57
airport.client/vite.config.js
Normal file
57
airport.client/vite.config.js
Normal file
@ -0,0 +1,57 @@
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
|
||||
import { defineConfig } from 'vite';
|
||||
import plugin from '@vitejs/plugin-vue';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import child_process from 'child_process';
|
||||
import { env } from 'process';
|
||||
|
||||
const baseFolder =
|
||||
env.APPDATA !== undefined && env.APPDATA !== ''
|
||||
? `${env.APPDATA}/ASP.NET/https`
|
||||
: `${env.HOME}/.aspnet/https`;
|
||||
|
||||
const certificateName = "airport.client";
|
||||
const certFilePath = path.join(baseFolder, `${certificateName}.pem`);
|
||||
const keyFilePath = path.join(baseFolder, `${certificateName}.key`);
|
||||
|
||||
if (!fs.existsSync(certFilePath) || !fs.existsSync(keyFilePath)) {
|
||||
if (0 !== child_process.spawnSync('dotnet', [
|
||||
'dev-certs',
|
||||
'https',
|
||||
'--export-path',
|
||||
certFilePath,
|
||||
'--format',
|
||||
'Pem',
|
||||
'--no-password',
|
||||
], { stdio: 'inherit', }).status) {
|
||||
throw new Error("Could not create certificate.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [plugin()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://localhost:7217',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: path => path.replace(/^\/api/, '')
|
||||
}
|
||||
},
|
||||
port: 5173,
|
||||
https: {
|
||||
key: fs.readFileSync(keyFilePath),
|
||||
cert: fs.readFileSync(certFilePath),
|
||||
}
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue
Block a user