Добавьте файлы проекта.

This commit is contained in:
Артем Харламов 2024-07-09 21:03:38 +04:00
parent 1eda2f54b5
commit ab5883356b
63 changed files with 8054 additions and 0 deletions

View 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>

View 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);
}

View 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.

View 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;
}
}
}
}

View 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;
}
}
}
}

View 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;
}
}
}
}

View 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;
}
}
}
}

View 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;
}
}
}
}

View 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();
}
}
}

View 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>();
}

View 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; }
}

View 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>();
}

View 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; }
}

View 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
View 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>();
});
}
}

View 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
View 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");
});
}
}
}

View 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; }
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View 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
View 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

View 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
View 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

View File

@ -0,0 +1,6 @@
{
"recommendations": [
"Vue.volar",
"dbaeumer.vscode-eslint"
]
}

View 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
View 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
```

View 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
View 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>

View File

@ -0,0 +1,8 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}

View 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

File diff suppressed because it is too large Load Diff

View 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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View 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>

View 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>

View 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;
}

View 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

View 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;
}
}

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>
Vues
<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>

View 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>

View 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>

View File

@ -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>

View 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>

View 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>

View 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>

View 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");

View 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 };

View 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;

View 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 {};
}
}

View 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();

View 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();

View 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;
}
}
};

View File

@ -0,0 +1,10 @@
import { createStore } from "vuex";
import { auth } from "./auth.module";
const store = createStore({
modules: {
auth,
},
});
export default store;

View 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),
}
}
})