Blazor 5x - قسمت 29 - برنامهی Blazor WASM - یک تمرین: رزرو کردن یک اتاق انتخابی
نویسنده: وحید نصیری
تاریخ: ۱۴۰۰/۰۱/۰۶ ۱۹:۲۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BlazorServer.Entities
{
public class RoomOrderDetail
{
public int Id { get; set; }
[Required]
public string UserId { get; set; }
[Required]
public string StripeSessionId { get; set; }
public DateTime CheckInDate { get; set; }
public DateTime CheckOutDate { get; set; }
public DateTime ActualCheckInDate { get; set; }
public DateTime ActualCheckOutDate { get; set; }
public long TotalCost { get; set; }
public int RoomId { get; set; }
public bool IsPaymentSuccessful { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
public string Phone { get; set; }
[ForeignKey("RoomId")]
public HotelRoom HotelRoom { get; set; }
public string Status { get; set; }
}
} namespace BlazorServer.Common
{
public static class BookingStatus
{
public const string Pending = "Pending";
public const string Booked = "Booked";
public const string CheckedIn = "CheckedIn";
public const string CheckedOutCompleted = "CheckedOut";
public const string NoShow = "NoShow";
public const string Cancelled = "Cancelled";
}
} namespace BlazorServer.DataAccess
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<RoomOrderDetail> RoomOrderDetails { get; set; }
// ...
}
} dotnet tool update --global dotnet-ef --version 5.0.4 dotnet build dotnet ef migrations --startup-project ../../BlazorWasm/BlazorWasm.WebApi/ add AddRoomOrderDetails --context ApplicationDbContext dotnet ef --startup-project ../../BlazorWasm/BlazorWasm.WebApi/ database update --context ApplicationDbContext
using System;
using System.ComponentModel.DataAnnotations;
namespace BlazorServer.Models
{
public class RoomOrderDetailsDTO
{
public int Id { get; set; }
[Required]
public string UserId { get; set; }
[Required]
public string StripeSessionId { get; set; }
[Required]
public DateTime CheckInDate { get; set; }
[Required]
public DateTime CheckOutDate { get; set; }
public DateTime ActualCheckInDate { get; set; }
public DateTime ActualCheckOutDate { get; set; }
[Required]
public long TotalCost { get; set; }
[Required]
public int RoomId { get; set; }
public bool IsPaymentSuccessful { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
public string Phone { get; set; }
public HotelRoomDTO HotelRoomDTO { get; set; }
public string Status { get; set; }
}
} namespace BlazorServer.Models.Mappings
{
public class MappingProfile : Profile
{
public MappingProfile()
{
// ...
CreateMap<RoomOrderDetail, RoomOrderDetailsDTO>().ReverseMap(); // two-way mapping
}
}
} namespace BlazorServer.Services
{
public interface IRoomOrderDetailsService
{
Task<RoomOrderDetailsDTO> CreateAsync(RoomOrderDetailsDTO details);
Task<List<RoomOrderDetailsDTO>> GetAllRoomOrderDetailsAsync();
Task<RoomOrderDetailsDTO> GetRoomOrderDetailAsync(int roomOrderId);
Task<bool> IsRoomBookedAsync(int RoomId, DateTime checkInDate, DateTime checkOutDate);
Task<RoomOrderDetailsDTO> MarkPaymentSuccessfulAsync(int id);
Task<bool> UpdateOrderStatusAsync(int RoomOrderId, string status);
}
} namespace BlazorServer.Services
{
public class RoomOrderDetailsService : IRoomOrderDetailsService
{
private readonly ApplicationDbContext _dbContext;
private readonly IMapper _mapper;
private readonly IConfigurationProvider _mapperConfiguration;
public RoomOrderDetailsService(ApplicationDbContext dbContext, IMapper mapper)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
_mapperConfiguration = mapper.ConfigurationProvider;
}
public async Task<RoomOrderDetailsDTO> CreateAsync(RoomOrderDetailsDTO details)
{
var roomOrder = _mapper.Map<RoomOrderDetail>(details);
roomOrder.Status = BookingStatus.Pending;
var result = await _dbContext.RoomOrderDetails.AddAsync(roomOrder);
await _dbContext.SaveChangesAsync();
return _mapper.Map<RoomOrderDetailsDTO>(result.Entity);
}
public Task<List<RoomOrderDetailsDTO>> GetAllRoomOrderDetailsAsync()
{
return _dbContext.RoomOrderDetails
.Include(roomOrderDetail => roomOrderDetail.HotelRoom)
.ProjectTo<RoomOrderDetailsDTO>(_mapperConfiguration)
.ToListAsync();
}
public async Task<RoomOrderDetailsDTO> GetRoomOrderDetailAsync(int roomOrderId)
{
var roomOrderDetailsDTO = await _dbContext.RoomOrderDetails
.Include(u => u.HotelRoom)
.ThenInclude(x => x.HotelRoomImages)
.ProjectTo<RoomOrderDetailsDTO>(_mapperConfiguration)
.FirstOrDefaultAsync(u => u.Id == roomOrderId);
roomOrderDetailsDTO.HotelRoomDTO.TotalDays =
roomOrderDetailsDTO.CheckOutDate.Subtract(roomOrderDetailsDTO.CheckInDate).Days;
return roomOrderDetailsDTO;
}
public Task<bool> IsRoomBookedAsync(int RoomId, DateTime checkInDate, DateTime checkOutDate)
{
return _dbContext.RoomOrderDetails
.AnyAsync(
roomOrderDetail =>
roomOrderDetail.RoomId == RoomId &&
roomOrderDetail.IsPaymentSuccessful &&
(
(checkInDate < roomOrderDetail.CheckOutDate && checkInDate > roomOrderDetail.CheckInDate) ||
(checkOutDate > roomOrderDetail.CheckInDate && checkInDate < roomOrderDetail.CheckInDate)
)
);
}
public Task<RoomOrderDetailsDTO> MarkPaymentSuccessfulAsync(int id)
{
throw new NotImplementedException();
}
public Task<bool> UpdateOrderStatusAsync(int RoomOrderId, string status)
{
throw new NotImplementedException();
}
}
} namespace BlazorWasm.WebApi
{
public class Startup
{
// ...
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IRoomOrderDetailsService, RoomOrderDetailsService>();
// ... namespace BlazorWasm.Client.Services
{
public interface IClientRoomOrderDetailsService
{
Task<RoomOrderDetailsDTO> MarkPaymentSuccessfulAsync(RoomOrderDetailsDTO details);
Task<RoomOrderDetailsDTO> SaveRoomOrderDetailsAsync(RoomOrderDetailsDTO details);
}
} namespace BlazorWasm.Client.Services
{
public class ClientRoomOrderDetailsService : IClientRoomOrderDetailsService
{
private readonly HttpClient _httpClient;
public ClientRoomOrderDetailsService(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public Task<RoomOrderDetailsDTO> MarkPaymentSuccessfulAsync(RoomOrderDetailsDTO details)
{
throw new NotImplementedException();
}
public Task<RoomOrderDetailsDTO> SaveRoomOrderDetailsAsync(RoomOrderDetailsDTO details)
{
throw new NotImplementedException();
}
}
} namespace BlazorWasm.Client
{
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
// ...
builder.Services.AddScoped<IClientRoomOrderDetailsService, ClientRoomOrderDetailsService>();
// ...
}
}
} using BlazorServer.Models;
namespace BlazorWasm.Client.Models.ViewModels
{
public class HotelRoomBookingVM
{
public RoomOrderDetailsDTO OrderDetails { get; set; }
}
} @page "/hotel/room-details/{Id:int}"
@inject IJSRuntime JsRuntime
@inject ILocalStorageService LocalStorage
@inject IClientHotelRoomService HotelRoomService
@if (HotelBooking?.OrderDetails?.HotelRoomDTO?.HotelRoomImages == null)
{
<div class="spinner"></div>
}
else
{
}
@code {
[Parameter]
public int? Id { get; set; }
HotelRoomBookingVM HotelBooking = new HotelRoomBookingVM();
int NoOfNights = 1;
protected override async Task OnInitializedAsync()
{
try
{
HotelBooking.OrderDetails = new RoomOrderDetailsDTO();
if (Id != null)
{
if (await LocalStorage.GetItemAsync<HomeVM>(ConstantKeys.LocalInitialBooking) != null)
{
var roomInitialInfo = await LocalStorage.GetItemAsync<HomeVM>(ConstantKeys.LocalInitialBooking);
HotelBooking.OrderDetails.HotelRoomDTO = await HotelRoomService.GetHotelRoomDetailsAsync(
Id.Value, roomInitialInfo.StartDate, roomInitialInfo.EndDate);
NoOfNights = roomInitialInfo.NoOfNights;
HotelBooking.OrderDetails.CheckInDate = roomInitialInfo.StartDate;
HotelBooking.OrderDetails.CheckOutDate = roomInitialInfo.EndDate;
HotelBooking.OrderDetails.HotelRoomDTO.TotalDays = roomInitialInfo.NoOfNights;
HotelBooking.OrderDetails.HotelRoomDTO.TotalAmount =
roomInitialInfo.NoOfNights * HotelBooking.OrderDetails.HotelRoomDTO.RegularRate;
}
else
{
HotelBooking.OrderDetails.HotelRoomDTO = await HotelRoomService.GetHotelRoomDetailsAsync(
Id.Value, DateTime.Now, DateTime.Now.AddDays(1));
NoOfNights = 1;
HotelBooking.OrderDetails.CheckInDate = DateTime.Now;
HotelBooking.OrderDetails.CheckOutDate = DateTime.Now.AddDays(1);
HotelBooking.OrderDetails.HotelRoomDTO.TotalDays = 1;
HotelBooking.OrderDetails.HotelRoomDTO.TotalAmount =
HotelBooking.OrderDetails.HotelRoomDTO.RegularRate;
}
}
}
catch (Exception e)
{
await JsRuntime.ToastrError(e.Message);
}
}
} namespace BlazorWasm.Client.Services
{
public class ClientHotelRoomService : IClientHotelRoomService
{
private readonly HttpClient _httpClient;
public ClientHotelRoomService(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public Task<HotelRoomDTO> GetHotelRoomDetailsAsync(int roomId, DateTime checkInDate, DateTime checkOutDate)
{
// How to url-encode query-string parameters properly
var uri = new UriBuilderExt(new Uri(_httpClient.BaseAddress, $"/api/hotelroom/{roomId}"))
.AddParameter("checkInDate", $"{checkInDate:yyyy'-'MM'-'dd}")
.AddParameter("checkOutDate", $"{checkOutDate:yyyy'-'MM'-'dd}")
.Uri;
return _httpClient.GetFromJsonAsync<HotelRoomDTO>(uri);
}
public Task<IEnumerable<HotelRoomDTO>> GetHotelRoomsAsync(DateTime checkInDate, DateTime checkOutDate)
{
// ...
}
}
} @if (HotelBooking?.OrderDetails?.HotelRoomDTO?.HotelRoomImages == null)
{
<div class="spinner"></div>
}
else
{
<div class="mt-4 mx-4 px-0 px-md-5 mx-md-5">
<div class="row p-2 my-3 " style="border-radius:20px; ">
<div class="col-12 col-lg-7 p-4" style="border: 1px solid gray">
<div class="row px-2 text-success border-bottom">
<div class="col-8 py-1"><p style="font-size:x-large;margin:0px;">Selected Room</p></div>
<div class="col-4 p-0"><a href="hotel/rooms" class="btn btn-secondary btn-block">Back to Room's</a></div>
</div>
<div class="row">
<div class="col-6">
<div id="" class="carousel slide mb-4 m-md-3 m-0 pt-3 pt-md-0" data-ride="carousel">
<div id="carouselExampleIndicators" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
<li data-target="#carouselExampleIndicators" data-slide-to="0" class="active"></li>
<li data-target="#carouselExampleIndicators" data-slide-to="1"></li>
</ol>
<div class="carousel-inner">
<div class="carousel-item active">
<img class="d-block w-100" src="images/slide1.jpg" alt="First slide">
</div>
</div>
<a class="carousel-control-prev" href="#carouselExampleIndicators" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#carouselExampleIndicators" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
</div>
</div>
<div class="col-6">
<span class="float-right pt-4">
<span class="float-right">Occupancy : @HotelBooking.OrderDetails.HotelRoomDTO.Occupancy adults </span><br />
<span class="float-right pt-1">Size : @HotelBooking.OrderDetails.HotelRoomDTO.SqFt sqft</span><br />
<h4 class="text-warning font-weight-bold pt-5">
<span style="border-bottom:1px solid #ff6a00">
@HotelBooking.OrderDetails.HotelRoomDTO.TotalAmount.ToString("#,#.00#;(#,#.00#)")
</span>
</h4>
<span class="float-right">Cost for @HotelBooking.OrderDetails.HotelRoomDTO.TotalDays nights</span>
</span>
</div>
</div>
<div class="row p-2">
<div class="col-12">
<p class="card-title text-warning" style="font-size:xx-large">@HotelBooking.OrderDetails.HotelRoomDTO.Name</p>
<p class="card-text" style="font-size:large">
@((MarkupString)@HotelBooking.OrderDetails.HotelRoomDTO.Details)
</p>
</div>
</div>
</div>
} @if (HotelBooking?.OrderDetails?.HotelRoomDTO?.HotelRoomImages == null)
{
<div class="spinner"></div>
}
else
{
// ...
<div class="col-12 col-lg-5 p-4 2 mt-4 mt-md-0" style="border: 1px solid gray;">
<EditForm Model="HotelBooking" class="container" OnValidSubmit="HandleCheckout">
<div class="row px-2 text-success border-bottom"><div class="col-7 py-1"><p style="font-size:x-large;margin:0px;">Enter Details</p></div></div>
<div class="form-group pt-2">
<label class="text-warning">Name</label>
<InputText @bind-Value="HotelBooking.OrderDetails.Name" type="text" class="form-control" />
</div>
<div class="form-group pt-2">
<label class="text-warning">Phone</label>
<InputText @bind-Value="HotelBooking.OrderDetails.Phone" type="text" class="form-control" />
</div>
<div class="form-group">
<label class="text-warning">Email</label>
<InputText @bind-Value="HotelBooking.OrderDetails.Email" type="text" class="form-control" />
</div>
<div class="form-group">
<label class="text-warning">Check in Date</label>
<InputDate @bind-Value="HotelBooking.OrderDetails.CheckInDate" type="date" disabled class="form-control" />
</div>
<div class="form-group">
<label class="text-warning">Check Out Date</label>
<InputDate @bind-Value="HotelBooking.OrderDetails.CheckOutDate" type="date" disabled class="form-control" />
</div>
<div class="form-group">
<label class="text-warning">No. of nights</label>
<select class="form-control" value="@NoOfNights" @onchange="HandleNoOfNightsChange">
@for (var i = 1; i <= 10; i++)
{
if (i == NoOfNights)
{
<option value="@i" selected="selected">@i</option>
}
else
{
<option value="@i">@i</option>
}
}
</select>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success form-control">Checkout Now</button>
</div>
</EditForm>
</div>
</div>
</div>
} @code {
// ...
private async Task HandleNoOfNightsChange(ChangeEventArgs e)
{
NoOfNights = Convert.ToInt32(e.Value.ToString());
HotelBooking.OrderDetails.HotelRoomDTO = await HotelRoomService.GetHotelRoomDetailsAsync(
Id.Value,
HotelBooking.OrderDetails.CheckInDate,
HotelBooking.OrderDetails.CheckInDate.AddDays(NoOfNights));
HotelBooking.OrderDetails.CheckOutDate = HotelBooking.OrderDetails.CheckInDate.AddDays(NoOfNights);
HotelBooking.OrderDetails.HotelRoomDTO.TotalDays = NoOfNights;
HotelBooking.OrderDetails.HotelRoomDTO.TotalAmount =
NoOfNights * HotelBooking.OrderDetails.HotelRoomDTO.RegularRate;
}
private async Task HandleCheckout()
{
if (!await HandleValidation())
{
return;
}
}
private async Task<bool> HandleValidation()
{
if (string.IsNullOrEmpty(HotelBooking.OrderDetails.Name))
{
await JsRuntime.ToastrError("Name cannot be empty");
return false;
}
if (string.IsNullOrEmpty(HotelBooking.OrderDetails.Phone))
{
await JsRuntime.ToastrError("Phone cannot be empty");
return false;
}
if (string.IsNullOrEmpty(HotelBooking.OrderDetails.Email))
{
await JsRuntime.ToastrError("Email cannot be empty");
return false;
}
return true;
}
}