feat: places' availability depends on bought tickets

if cities in route you're searching for intersects with cities in the smb's ticket for the route, taken place will be unavailable and capacity in the search results will be decreased
This commit is contained in:
cuqmbr 2022-05-28 20:10:50 +03:00
parent 1b3ac474b8
commit c47d67b15d
18 changed files with 388 additions and 152 deletions

View File

@ -18,8 +18,9 @@ namespace TicketOffice.Data
public DbSet<TicketOffice.Models.User> User { get; set; }
public DbSet<TicketOffice.Models.Route> Route { get; set; }
public DbSet<TicketOffice.Models.City> City { get; set; }
public DbSet<TicketOffice.Models.RouteCity> RouteCity { get; set; }
public DbSet<TicketOffice.Models.TicketCity> TicketCity { get; set; }
public DbSet<TicketOffice.Models.Ticket> Ticket { get; set; }
}

View File

@ -11,15 +11,32 @@ using TicketOffice.Data;
namespace TicketOffice.Migrations
{
[DbContext(typeof(TicketOfficeContext))]
[Migration("20220404151700_InitialCreate")]
partial class InitialCreate
[Migration("20220526065734_Initial_Create")]
partial class Initial_Create
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.3");
modelBuilder.Entity("TicketOffice.Models.City", b =>
modelBuilder.Entity("TicketOffice.Models.Route", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Capacity")
.HasColumnType("INTEGER");
b.Property<int>("Number")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Route");
});
modelBuilder.Entity("TicketOffice.Models.RouteCity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -43,24 +60,7 @@ namespace TicketOffice.Migrations
b.HasIndex("RouteId");
b.ToTable("City");
});
modelBuilder.Entity("TicketOffice.Models.Route", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Capacity")
.HasColumnType("INTEGER");
b.Property<int>("Number")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Route");
b.ToTable("RouteCity");
});
modelBuilder.Entity("TicketOffice.Models.Ticket", b =>
@ -95,6 +95,33 @@ namespace TicketOffice.Migrations
b.ToTable("Ticket");
});
modelBuilder.Entity("TicketOffice.Models.TicketCity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("ArrivalTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("DepartureTime")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(24)
.HasColumnType("TEXT");
b.Property<int>("TicketId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("TicketId");
b.ToTable("TicketCity");
});
modelBuilder.Entity("TicketOffice.Models.User", b =>
{
b.Property<int>("Id")
@ -131,7 +158,7 @@ namespace TicketOffice.Migrations
b.ToTable("User");
});
modelBuilder.Entity("TicketOffice.Models.City", b =>
modelBuilder.Entity("TicketOffice.Models.RouteCity", b =>
{
b.HasOne("TicketOffice.Models.Route", "Route")
.WithMany("Cities")
@ -161,6 +188,17 @@ namespace TicketOffice.Migrations
b.Navigation("User");
});
modelBuilder.Entity("TicketOffice.Models.TicketCity", b =>
{
b.HasOne("TicketOffice.Models.Ticket", "Ticket")
.WithMany("Cities")
.HasForeignKey("TicketId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Ticket");
});
modelBuilder.Entity("TicketOffice.Models.Route", b =>
{
b.Navigation("Cities");
@ -168,6 +206,11 @@ namespace TicketOffice.Migrations
b.Navigation("Tickets");
});
modelBuilder.Entity("TicketOffice.Models.Ticket", b =>
{
b.Navigation("Cities");
});
modelBuilder.Entity("TicketOffice.Models.User", b =>
{
b.Navigation("Tickets");

View File

@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace TicketOffice.Migrations
{
public partial class InitialCreate : Migration
public partial class Initial_Create : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
@ -42,7 +42,7 @@ namespace TicketOffice.Migrations
});
migrationBuilder.CreateTable(
name: "City",
name: "RouteCity",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
@ -54,9 +54,9 @@ namespace TicketOffice.Migrations
},
constraints: table =>
{
table.PrimaryKey("PK_City", x => x.Id);
table.PrimaryKey("PK_RouteCity", x => x.Id);
table.ForeignKey(
name: "FK_City_Route_RouteId",
name: "FK_RouteCity_Route_RouteId",
column: x => x.RouteId,
principalTable: "Route",
principalColumn: "Id",
@ -92,9 +92,31 @@ namespace TicketOffice.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "TicketCity",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(type: "TEXT", maxLength: 24, nullable: false),
ArrivalTime = table.Column<DateTime>(type: "TEXT", nullable: true),
DepartureTime = table.Column<DateTime>(type: "TEXT", nullable: true),
TicketId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TicketCity", x => x.Id);
table.ForeignKey(
name: "FK_TicketCity_Ticket_TicketId",
column: x => x.TicketId,
principalTable: "Ticket",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_City_RouteId",
table: "City",
name: "IX_RouteCity_RouteId",
table: "RouteCity",
column: "RouteId");
migrationBuilder.CreateIndex(
@ -106,12 +128,20 @@ namespace TicketOffice.Migrations
name: "IX_Ticket_UserId",
table: "Ticket",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_TicketCity_TicketId",
table: "TicketCity",
column: "TicketId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "City");
name: "RouteCity");
migrationBuilder.DropTable(
name: "TicketCity");
migrationBuilder.DropTable(
name: "Ticket");

View File

@ -17,7 +17,24 @@ namespace TicketOffice.Migrations
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.3");
modelBuilder.Entity("TicketOffice.Models.City", b =>
modelBuilder.Entity("TicketOffice.Models.Route", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Capacity")
.HasColumnType("INTEGER");
b.Property<int>("Number")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Route");
});
modelBuilder.Entity("TicketOffice.Models.RouteCity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -41,24 +58,7 @@ namespace TicketOffice.Migrations
b.HasIndex("RouteId");
b.ToTable("City");
});
modelBuilder.Entity("TicketOffice.Models.Route", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Capacity")
.HasColumnType("INTEGER");
b.Property<int>("Number")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Route");
b.ToTable("RouteCity");
});
modelBuilder.Entity("TicketOffice.Models.Ticket", b =>
@ -93,6 +93,33 @@ namespace TicketOffice.Migrations
b.ToTable("Ticket");
});
modelBuilder.Entity("TicketOffice.Models.TicketCity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("ArrivalTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("DepartureTime")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(24)
.HasColumnType("TEXT");
b.Property<int>("TicketId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("TicketId");
b.ToTable("TicketCity");
});
modelBuilder.Entity("TicketOffice.Models.User", b =>
{
b.Property<int>("Id")
@ -129,7 +156,7 @@ namespace TicketOffice.Migrations
b.ToTable("User");
});
modelBuilder.Entity("TicketOffice.Models.City", b =>
modelBuilder.Entity("TicketOffice.Models.RouteCity", b =>
{
b.HasOne("TicketOffice.Models.Route", "Route")
.WithMany("Cities")
@ -159,6 +186,17 @@ namespace TicketOffice.Migrations
b.Navigation("User");
});
modelBuilder.Entity("TicketOffice.Models.TicketCity", b =>
{
b.HasOne("TicketOffice.Models.Ticket", "Ticket")
.WithMany("Cities")
.HasForeignKey("TicketId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Ticket");
});
modelBuilder.Entity("TicketOffice.Models.Route", b =>
{
b.Navigation("Cities");
@ -166,6 +204,11 @@ namespace TicketOffice.Migrations
b.Navigation("Tickets");
});
modelBuilder.Entity("TicketOffice.Models.Ticket", b =>
{
b.Navigation("Cities");
});
modelBuilder.Entity("TicketOffice.Models.User", b =>
{
b.Navigation("Tickets");

View File

@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Components;
using Microsoft.EntityFrameworkCore;
namespace TicketOffice.Models;
@ -8,8 +9,7 @@ public class Route
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Поле має бути заповненим")]
[Display(Name = "Номер")]
[Range(1, 256)]
@ -19,9 +19,9 @@ public class Route
[Display(Name = "Ємність")]
[Range(5, 40)]
public int Capacity { get; set; }
[Required]
public ICollection<City> Cities { get; set; }
public ICollection<RouteCity> Cities { get; set; }
public ICollection<Ticket>? Tickets { get; set; }
}

View File

@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace TicketOffice.Models;
public class City
public class RouteCity
{
[Key]
public int Id { get; set; }
@ -21,7 +21,6 @@ public class City
[Display(Name = "Дата прибуття")]
[DataType(DataType.Date)]
public DateTime? DepartureTime { get; set; }
[ForeignKey("Route")]
public int RouteId { get; set; }

View File

@ -16,7 +16,7 @@ public class SeedData
throw new ArgumentNullException("Null TicketOfficeContext");
}
if (context.User.Any() | context.Route.Any() | context.City.Any() | context.Ticket.Any())
if (context.User.Any() | context.Route.Any() | context.RouteCity.Any() | context.Ticket.Any())
{
return; // Data has been seeded
}
@ -44,21 +44,21 @@ public class SeedData
new Route {
Number = 2,
Capacity = 30,
Cities = new City[]
Cities = new RouteCity[]
{
new City
new RouteCity
{
Name = "Кремінна",
ArrivalTime = new DateTime(2022, 03, 28, 8, 15, 0),
DepartureTime = new DateTime(2022, 03, 28, 8, 35, 0),
},
new City
new RouteCity
{
Name = "Рубіжне",
ArrivalTime = new DateTime(2022, 03, 28, 9, 5, 0),
DepartureTime = new DateTime(2022, 03, 28, 9, 25, 0),
},
new City
new RouteCity
{
Name = "Сєвєродонецьк",
ArrivalTime = new DateTime(2022, 03, 28, 9, 55, 0)
@ -69,21 +69,21 @@ public class SeedData
{
Number = 1,
Capacity = 25,
Cities = new City[]
Cities = new RouteCity[]
{
new City
new RouteCity
{
Name = "Сєвєродонецьк",
ArrivalTime = new DateTime(2022, 03, 28, 15, 55, 0),
DepartureTime = new DateTime(2022, 03, 28, 16, 15, 0),
},
new City
new RouteCity
{
Name = "Рубіжне",
ArrivalTime = new DateTime(2022, 03, 28, 16, 45, 0),
DepartureTime = new DateTime(2022, 03, 28, 17, 5, 0),
},
new City
new RouteCity
{
Name = "Кремінна",
ArrivalTime = new DateTime(2022, 03, 28, 17, 40, 0)
@ -94,27 +94,27 @@ public class SeedData
{
Number = 3,
Capacity = 30,
Cities = new City[]
Cities = new RouteCity[]
{
new City
new RouteCity
{
Name = "Кремінна",
ArrivalTime = new DateTime(2022, 03, 28, 9, 20, 0),
DepartureTime = new DateTime(2022, 03, 28, 8, 40, 0),
},
new City
new RouteCity
{
Name = "Житлівка",
ArrivalTime = new DateTime(2022, 03, 28, 10, 0, 0),
DepartureTime = new DateTime(2022, 03, 28, 10, 15, 0),
},
new City
new RouteCity
{
Name = "Рубіжне",
ArrivalTime = new DateTime(2022, 03, 28, 11, 5, 0),
DepartureTime = new DateTime(2022, 03, 28, 11, 20, 0),
},
new City
new RouteCity
{
Name = "Сєвєродонецьк",
ArrivalTime = new DateTime(2022, 03, 28, 11, 55, 0)

View File

@ -19,8 +19,10 @@ public class Ticket
[Required(ErrorMessage = "Поле має бути заповненим")]
[Display(Name = "Номер місця пасажира")]
public int PassengerPlace { get; set; }
[Required]
public ICollection<TicketCity> Cities { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public User User { get; set; }

View File

@ -0,0 +1,28 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TicketOffice.Models;
public class TicketCity
{
[Key]
public int Id { get; set; }
[MaxLength(24, ErrorMessage = "Назва міста не може бути більше 24 символів"),
MinLength(2, ErrorMessage = "Назва міста не може бути менше 2 символів")]
[Display(Name = "Назва міста")]
[Required(ErrorMessage = "Поле має бути заповненим")]
public string Name { get; set; }
[Display(Name = "Дата відправлення")]
[DataType(DataType.Date)]
public DateTime? ArrivalTime { get; set; }
[Display(Name = "Дата прибуття")]
[DataType(DataType.Date)]
public DateTime? DepartureTime { get; set; }
[ForeignKey("Ticket")]
public int TicketId { get; set; }
public Ticket Ticket { get; set; }
}

View File

@ -37,17 +37,17 @@
<div class="ticket-info-line date">
<span>Дата відправлення:</span>
<span>
@ticket.Route.Cities.First().DepartureTime?.ToString("f").Split(",")[0].ToLower(),
@ticket.Route.Cities.First().DepartureTime?.ToString("dd.MM.yyyy"),
@ticket.Route.Cities.First().DepartureTime?.ToString("HH:mm")
@ticket.Cities.First().DepartureTime?.ToString("f").Split(",")[0].ToLower(),
@ticket.Cities.First().DepartureTime?.ToString("dd.MM.yyyy"),
@ticket.Cities.First().DepartureTime?.ToString("HH:mm")
</span>
</div>
<div class="ticket-info-line date">
<span>Дата прибуття:</span>
<span>
@ticket.Route.Cities.Last().ArrivalTime?.ToString("f").Split(",")[0].ToLower(),
@ticket.Route.Cities.Last().ArrivalTime?.ToString("dd.MM.yyyy"),
@ticket.Route.Cities.Last().ArrivalTime?.ToString("HH:mm")
@ticket.Cities.Last().ArrivalTime?.ToString("f").Split(",")[0].ToLower(),
@ticket.Cities.Last().ArrivalTime?.ToString("dd.MM.yyyy"),
@ticket.Cities.Last().ArrivalTime?.ToString("HH:mm")
</span>
</div>
</div>
@ -62,11 +62,11 @@
}
</div>
<div class="divider"></div>
<div class="section">
<div class="section-header">Керування аккаунтом</div>
</div>
@* <div class="divider"></div> *@
@* *@
@* <div class="section"> *@
@* <div class="section-header">Керування аккаунтом</div> *@
@* </div> *@
@* Popup windows *@
@ -103,30 +103,30 @@
Відправлення
</td>
<td class="td-route city-list">
@ticket.Route.Cities.First().Name
@ticket.Cities.First().Name
</td>
<td class="td-route city-list">
-
</td>
<td class="td-route city-list">
@ticket.Route.Cities.First().DepartureTime?.ToString("HH:mm")
@ticket.Cities.First().DepartureTime?.ToString("HH:mm")
</td>
</tr>
@for (int i = 1; i < ticket.Route.Cities.Count - 1; i++)
@for (int i = 1; i < ticket.Cities.Count - 1; i++)
{
<tr class="tr-intermediate city-list">
<td class="td-route city-list">
Проміжна станція
</td>
<td class="td-route city-list">
@ticket.Route.Cities.ToList()[i].Name
@ticket.Cities.ToList()[i].Name
</td>
<td class="td-route city-list">
@ticket.Route.Cities.ToList()[i].ArrivalTime?.ToString("HH:mm")
@ticket.Cities.ToList()[i].ArrivalTime?.ToString("HH:mm")
</td>
<td class="td-route city-list">
@ticket.Route.Cities.ToList()[i].DepartureTime?.ToString("HH:mm")
@ticket.Cities.ToList()[i].DepartureTime?.ToString("HH:mm")
</td>
</tr>
}
@ -136,10 +136,10 @@
Прибуття
</td>
<td class="td-route city-list">
@ticket.Route.Cities.Last().Name
@ticket.Cities.Last().Name
</td>
<td class="td-route city-list">
@ticket.Route.Cities.Last().ArrivalTime?.ToString("HH:mm")
@ticket.Cities.Last().ArrivalTime?.ToString("HH:mm")
</td>
<td class="td-route city-list">
-

View File

@ -12,15 +12,13 @@ public class IndexModel : PageModel
[BindProperty(SupportsGet = true)] public int ReturnTicketId { get; set; }
private readonly TicketOfficeContext _context;
private readonly ILogger<IndexModel> _logger;
public IndexModel(TicketOfficeContext context, ILogger<IndexModel> logger)
{
_context = context;
_logger = logger;
}
public IActionResult OnGet()
public ActionResult OnGet()
{
if (!ValidateSession())
return RedirectToPage("/Auth/Login");
@ -28,18 +26,16 @@ public class IndexModel : PageModel
Tickets = _context.Ticket
.Where(t => t.UserId == HttpContext.Session.GetInt32("UserId"))
.Include(t => t.Route)
.Include(t => t.Route.Cities)
.Include(t => t.Cities)
.ToList();
return Page();
}
public IActionResult OnGetReturnTicket()
public ActionResult OnGetReturnTicket()
{
OnGet();
_logger.Log(LogLevel.Information, $"\n\n\n\n {ReturnTicketId} \n\n\n\n");
Ticket returnTicket = _context.Ticket.Find(ReturnTicketId);
if (returnTicket is not null)

View File

@ -164,7 +164,7 @@
@($"{duration?.TotalHours.ToString().Split(",")[0]}:{duration?.Minutes}")
</td>
<td class="capacity">
@(route.Capacity - route.Tickets.Count)
@Model.GetRemainingCapacity(route)
</td>
<td class="action">
<a class="link-btn-choose" onclick="document.getElementById('popup-ticket-@route.Id').style.display = 'inherit'">Обрати</a>
@ -311,7 +311,7 @@
<option selected disabled value="">Місце</option>
@for (int i = 1; i <= route.Capacity; i++)
{
if (route.Tickets.Any(t => t.PassengerPlace == i))
if (route.Tickets.Any(t => @Model.GetCitiesNames(t.Cities.ToList()).Intersect(@Model.GetCitiesNames(route.Cities.ToList())).ToList().Any() && t.PassengerPlace == i))
{
<option value="@i" disabled>@i - Місце зайняте</option>
}

View File

@ -15,10 +15,12 @@ public class IndexModel : PageModel
public string PassengerLastNameValidationError;
public string PassengerFirstNameValidationError;
public string PassengerPlaceValidationError;
[BindProperty(SupportsGet = true)] public string From { get; set; }
[BindProperty(SupportsGet = true)] public string To { get; set; }
[BindProperty(SupportsGet = true)] public DateTime Date { get; set; } = new DateTime(2022, 03, 28, 0, 0, 0).Date;
[BindProperty(SupportsGet = true)] public string SortString { get; set; }
private readonly TicketOfficeContext _context;
@ -30,40 +32,29 @@ public class IndexModel : PageModel
public ActionResult OnGet()
{
if (!string.IsNullOrWhiteSpace(From) || !string.IsNullOrWhiteSpace(To))
{
RetrieveAllRoutes();
}
if (!string.IsNullOrWhiteSpace(From))
{
FilterRoutesByFrom();
}
if (!string.IsNullOrWhiteSpace(To))
{
FilterRoutesByTo();
}
if (!string.IsNullOrWhiteSpace(From) || !string.IsNullOrWhiteSpace(To))
{
FilterRoutesByDate();
}
GetRoutes();
return Page();
}
public ActionResult OnPost()
{
if (!PassengerNameValidation(Ticket.PassengerLastName, out PassengerLastNameValidationError) | !PassengerNameValidation(Ticket.PassengerFirstName, out PassengerFirstNameValidationError) | !PassengerPlaceValidation(Ticket.PassengerPlace, out PassengerPlaceValidationError))
if (!PassengerNameValidation(Ticket.PassengerLastName,
out PassengerLastNameValidationError) |
!PassengerNameValidation(Ticket.PassengerFirstName,
out PassengerFirstNameValidationError) |
!PassengerPlaceValidation(Ticket.PassengerPlace,
out PassengerPlaceValidationError))
return OnGet();
GetRoutes();
CopyCitiesToTicket();
_context.Ticket.Add(Ticket);
RevertChangesToRouteCities();
_context.SaveChanges();
return RedirectToPage("/Auth/Account");
}
public void OnGetSortByNumber()
{
OnGet();
@ -85,17 +76,20 @@ public class IndexModel : PageModel
Routes.Sort((x, y) =>
{
TimeSpan? totalDuration;
if (SortString == "increasingDeparture")
{
totalDuration = x.Cities.First().DepartureTime - y.Cities.First().DepartureTime;
totalDuration = x.Cities.First().DepartureTime -
y.Cities.First().DepartureTime;
}
else
{
totalDuration = y.Cities.First().DepartureTime - x.Cities.First().DepartureTime;
totalDuration = y.Cities.First().DepartureTime -
x.Cities.First().DepartureTime;
}
return Math.Clamp((int)totalDuration.Value.TotalMilliseconds, -1, 1);
return Math.Clamp((int) totalDuration.Value.TotalMilliseconds, -1,
1);
});
}
@ -109,14 +103,17 @@ public class IndexModel : PageModel
if (SortString == "increasingArrival")
{
totalDuration = x.Cities.Last().ArrivalTime - y.Cities.Last().ArrivalTime;
totalDuration = x.Cities.Last().ArrivalTime -
y.Cities.Last().ArrivalTime;
}
else
{
totalDuration = y.Cities.Last().ArrivalTime - x.Cities.Last().ArrivalTime;
totalDuration = y.Cities.Last().ArrivalTime -
x.Cities.Last().ArrivalTime;
}
return Math.Clamp((int)totalDuration.Value.TotalMilliseconds, -1, 1);
return Math.Clamp((int) totalDuration.Value.TotalMilliseconds, -1,
1);
});
}
@ -126,8 +123,10 @@ public class IndexModel : PageModel
Routes.Sort((x, y) =>
{
TimeSpan? xDuration = x.Cities.Last().ArrivalTime - x.Cities.First().DepartureTime;
TimeSpan? yDuration = y.Cities.Last().ArrivalTime - y.Cities.First().DepartureTime;
TimeSpan? xDuration = x.Cities.Last().ArrivalTime -
x.Cities.First().DepartureTime;
TimeSpan? yDuration = y.Cities.Last().ArrivalTime -
y.Cities.First().DepartureTime;
TimeSpan? totalDuration;
if (SortString == "increasingDuration")
@ -139,31 +138,76 @@ public class IndexModel : PageModel
totalDuration = yDuration - xDuration;
}
return Math.Clamp((int)totalDuration.Value.TotalMilliseconds, -1, 1);
return Math.Clamp((int) totalDuration.Value.TotalMilliseconds, -1,
1);
});
}
public List<string> GetCitiesNames(List<RouteCity> Cities)
{
List<string> citiesNames = new List<string>();
foreach (var city in Cities)
{
citiesNames.Add(city.Name);
}
return citiesNames;
}
public List<string> GetCitiesNames(List<TicketCity> Cities)
{
List<string> citiesNames = new List<string>();
foreach (var city in Cities)
{
citiesNames.Add(city.Name);
}
return citiesNames;
}
public int GetRemainingCapacity(Route route)
{
return route.Capacity - route.Tickets.Count(t =>
GetCitiesNames(t.Cities.ToList())
.Intersect(GetCitiesNames(route.Cities.ToList()))
.ToList().Any());
}
private void RetrieveAllRoutes()
{
Routes = _context.Route
.Include(r => r.Cities)
.Include(r => r.Tickets)
.ToList();
// Add cities to tickets
for (int i = 0; i < Routes.Count; i++)
{
for (int j = 0; j < Routes[i].Tickets.Count; j++)
{
Routes[i].Tickets.ToList()[j].Cities = _context.TicketCity
.Where(tc => tc.Ticket == Routes[i].Tickets.ToList()[j])
.ToList();
}
}
}
private void FilterRoutesByFrom()
{
Routes.ForEach(r => r.Cities = r.Cities
.SkipWhile(c => c.Name.ToLower() != From.Trim().ToLower())
.ToList());
Routes.RemoveAll(r => r.Cities.Count < 2);
}
private void FilterRoutesByTo()
{
Routes.ForEach(r => r.Cities = r.Cities
.Reverse().SkipWhile(c => c.Name.ToLower() != To.Trim().ToLower())
.Reverse().ToList());
@ -173,10 +217,35 @@ public class IndexModel : PageModel
private void FilterRoutesByDate()
{
Routes.RemoveAll(r => r.Cities.First().DepartureTime.Value.DayOfYear != Date.DayOfYear);
Routes.RemoveAll(r =>
r.Cities.First().DepartureTime.Value.DayOfYear != Date.DayOfYear);
}
private bool PassengerNameValidation(string? name, out string validationError)
private void GetRoutes()
{
if (!string.IsNullOrWhiteSpace(From) || !string.IsNullOrWhiteSpace(To))
{
RetrieveAllRoutes();
}
if (!string.IsNullOrWhiteSpace(From))
{
FilterRoutesByFrom();
}
if (!string.IsNullOrWhiteSpace(To))
{
FilterRoutesByTo();
}
if (!string.IsNullOrWhiteSpace(From) || !string.IsNullOrWhiteSpace(To))
{
FilterRoutesByDate();
}
}
private bool PassengerNameValidation(string? name,
out string validationError)
{
if (String.IsNullOrEmpty(name))
{
@ -196,15 +265,39 @@ public class IndexModel : PageModel
return false;
}
Ticket? ticket = _context.Ticket.FirstOrDefault(t => t.RouteId == Ticket.RouteId && t.PassengerPlace == Ticket.PassengerPlace);
Ticket? ticket = _context.Ticket.FirstOrDefault(t =>
t.RouteId == Ticket.RouteId &&
t.PassengerPlace == Ticket.PassengerPlace);
if (ticket is not null)
{
validationError = "Місце вже зайняте";
return false;
}
validationError = String.Empty;
return true;
}
private void CopyCitiesToTicket()
{
List<RouteCity> RouteCities = Routes.Find(r => r.Id == Ticket.RouteId).Cities.ToList();
Ticket.Cities = new List<TicketCity>();
foreach (var city in RouteCities)
{
Ticket.Cities.Add(new TicketCity
{
Name = city.Name,
DepartureTime = city.DepartureTime,
ArrivalTime = city.ArrivalTime
});
}
}
private void RevertChangesToRouteCities()
{
_context.ChangeTracker.Entries()
.Where(e => e.Metadata.Name == "TicketOffice.Models.RouteCity")
.ToList().ForEach(e => e.State = EntityState.Unchanged);
}
}

View File

@ -24,7 +24,7 @@
<div class="topnav-right">
@if (Context.Session.GetString("UserId") != null)
{
<a class="@(path.Contains("account") ? "active" : "")" href="/Auth/Account">Аккаунт</a>
<a class="@(path.Contains("account") ? "active" : "")" href="/Auth/Account">Мої квитки</a>
}
else
{

View File

@ -11,7 +11,8 @@
.popup-city-list {
width: 40rem;
height: 30rem;
height: fit-content;
max-height: 30rem;
background: #eaeef1;
position: fixed;
top: calc(50% - 15rem);

View File

@ -11,7 +11,7 @@ body {
}
div.background {
background-image: url("https://tinyurl.com/24kx479s");
background-image: url("../img/yutong-u12-electric-bus.jpg");
height: calc(100vh - 3.15rem);
background-position: center;
background-repeat: no-repeat;

View File

@ -11,7 +11,7 @@
.popup-info {
width: 30rem;
height: 13rem;
height: fit-content;
background: #eaeef1;
position: fixed;
top: calc(50% - 6.5rem);
@ -33,8 +33,8 @@
.popup-info-body {
width: calc(100% - 2rem);
height: calc(100% - 8rem);
padding: 0.5rem 1rem;
height: fit-content;
padding: 1rem 1rem;
display: flex;
justify-content: center;
align-items: center;

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB