first step

This commit is contained in:
Viltskaa 2023-05-19 17:52:18 +04:00
parent f2142b75a9
commit 76e6ec8bc5
56 changed files with 2129 additions and 334 deletions

View File

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/modules.xml
/contentModel.xml
/projectSettingsUpdater.xml
/.idea.Hotel.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders>
<Path>../../PIbd-22_Bazunov_AI_Hotel</Path>
</attachedFolders>
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -0,0 +1,19 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="CssUnknownProperty" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myCustomPropertiesEnabled" value="true" />
<option name="myIgnoreVendorSpecificProperties" value="false" />
<option name="myCustomPropertiesList">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="flex-line-pack" />
<item index="1" class="java.lang.String" itemvalue="initial-value" />
<item index="2" class="java.lang.String" itemvalue="inherits" />
<item index="3" class="java.lang.String" itemvalue="syntax" />
</list>
</value>
</option>
</inspection_tool>
</profile>
</component>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@ -13,7 +13,7 @@ public class CleaningInstrumentsLogic : ICleaningInstrumentsLogic
private readonly ICleaningInstrumentsStorage _cleaningInstruments;
private readonly ILogger _logger;
public CleaningInstrumentsLogic(ICleaningInstrumentsStorage cleaningInstruments, ILogger logger)
public CleaningInstrumentsLogic(ICleaningInstrumentsStorage cleaningInstruments, ILogger<ICleaningInstrumentsLogic> logger)
{
_cleaningInstruments = cleaningInstruments;
_logger = logger;

View File

@ -71,7 +71,7 @@ public class CleaningLogic : ICleaningLogic
throw new ArgumentNullException(nameof(model));
if (!withParams)
return;
if (model.RoomId < 0)
if (model.RoomId <= 0)
throw new ArgumentException("RoomId must be more then 0");
}
}

View File

@ -65,6 +65,7 @@ public class ReportLogic : IReportLogic
list.Add(new ReportListCleaningModel
{
Id = reservation.Id,
RoomNumber = room.Id,
StartDate = reservation.StartDate,
CleaningInstruments = dict
@ -104,7 +105,7 @@ public class ReportLogic : IReportLogic
dict.Add(item.Key, view);
}
list.Add(new ReportGuestViewModel()
list.Add(new ReportGuestViewModel
{
Name = guest.Name,
SecondName = guest.SecondName,
@ -125,7 +126,7 @@ public class ReportLogic : IReportLogic
_saveToWord.CreateDoc(new WordInfo
{
FileName = model.FileName,
Title = "Cleaning list",
Title = "Комплекты для уборки",
ListCleaningModels = GetListCleaning()
});
}
@ -135,7 +136,7 @@ public class ReportLogic : IReportLogic
_saveToExcel.CreateReport(new ExcelInfo
{
FileName = model.FileName,
Title = "Cleaning list",
Title = "Комплекты для уборки",
ListCleaningModels = GetListCleaning()
});
}
@ -145,9 +146,10 @@ public class ReportLogic : IReportLogic
_saveToPdf.CreateDoc(new PdfInfo
{
FileName = model.FileName,
Title = "Guests with sets of cleanings",
Title = "Постояльцы и комплекты уборки номеров",
DateFrom = (DateTime)model.From,
DateTo = (DateTime)model.To
DateTo = (DateTime)model.To,
Guest = GetGuests(model)
});
}
}

View File

@ -12,8 +12,9 @@
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="2.20.0" />
<PackageReference Include="MailKit" Version="4.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0-preview.2.23128.3" />
<PackageReference Include="PDFsharp-MigraDoc" Version="6.0.0-preview-1" />
<PackageReference Include="PDFsharp-MigraDoc-GDI" Version="1.50.5147" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,57 @@
using HotelContracts.BindingModels;
using Microsoft.Extensions.Logging;
namespace HotelBusinessLogic.MailWorker;
public abstract class AbstractMailWorker
{
protected string _mailLogin = string.Empty;
protected string _mailPassword = string.Empty;
protected string _smtpClientHost = string.Empty;
protected int _smtpClientPort;
protected string _popHost = string.Empty;
protected int _popPort;
private readonly ILogger _logger;
protected AbstractMailWorker(ILogger<AbstractMailWorker> logger)
{
_logger = logger;
}
public void MailConfig(MailConfigBindingModel config)
{
_mailLogin = config.MailLogin;
_mailPassword = config.MailPassword;
_smtpClientHost = config.SmtpClientHost;
_smtpClientPort = config.SmtpClientPort;
_popHost = config.PopHost;
_popPort = config.PopPort;
_logger.LogDebug("Config: {login}, {password}, {clientHost}, {clientPOrt}, {popHost}, {popPort}",
_mailLogin, _mailPassword, _smtpClientHost, _smtpClientPort, _popHost, _popPort);
}
public async void MailSendAsync(MailSendInfoBindingModel info)
{
if (string.IsNullOrEmpty(_mailLogin) ||
string.IsNullOrEmpty(_mailPassword))
{
return;
}
if (string.IsNullOrEmpty(_smtpClientHost) ||
_smtpClientPort == 0)
{
return;
}
if (string.IsNullOrEmpty(info.MailAddress) ||
string.IsNullOrEmpty(info.Subject))
{
return;
}
_logger.LogDebug("Send Mail: {To}, {Subject}", info.MailAddress,
info.Subject);
await SendMailAsync(info);
}
protected abstract Task SendMailAsync(MailSendInfoBindingModel info);
}

View File

@ -0,0 +1,36 @@
using System.Net;
using System.Net.Mail;
using System.Text;
using HotelContracts.BindingModels;
using Microsoft.Extensions.Logging;
namespace HotelBusinessLogic.MailWorker;
public class MailKitWorker : AbstractMailWorker
{
public MailKitWorker(ILogger<MailKitWorker> logger) : base(logger) {}
protected override async Task SendMailAsync(MailSendInfoBindingModel info)
{
using var objMailMessage = new MailMessage();
using var objSmtpClient = new SmtpClient(_smtpClientHost, _smtpClientPort);
objMailMessage.From = new MailAddress(_mailLogin);
objMailMessage.To.Add(new MailAddress(info.MailAddress));
objMailMessage.Subject = info.Subject;
objMailMessage.Attachments.Add(info.AttachmentMail);
objMailMessage.SubjectEncoding = Encoding.UTF8;
objMailMessage.BodyEncoding = Encoding.UTF8;
objSmtpClient.UseDefaultCredentials = false;
objSmtpClient.EnableSsl = true;
objSmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
objSmtpClient.Credentials = new NetworkCredential(_mailLogin, _mailPassword);
try
{
await Task.Run(() => objSmtpClient.Send(objMailMessage));
}
catch (Exception e)
{
throw e;
}
}
}

View File

@ -27,6 +27,49 @@ public abstract class AbstractSaveToExcel
foreach (var mc in info.ListCleaningModels)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = rowIndex,
Text = "Бронь #" + mc.Id,
StyleInfo = ExcelStyleInfoType.Title
});
MergeCells(new ExcelMergeParameters
{
CellFromName = "A" + rowIndex,
CellToName = "C" + rowIndex
});
rowIndex++;
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = rowIndex,
Text = "Дата начала бронирования",
StyleInfo = ExcelStyleInfoType.Text
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "B",
RowIndex = rowIndex,
Text = "Номер гостиницы",
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "C",
RowIndex = rowIndex,
Text = "Набор",
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
rowIndex++;
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
@ -43,8 +86,6 @@ public abstract class AbstractSaveToExcel
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
rowIndex++;
foreach (var valuePair in mc.CleaningInstruments.Values)
{
InsertCellInWorksheet(new ExcelCellParameters

View File

@ -16,14 +16,14 @@ public abstract class AbstractSaveToPdf
});
CreateParagraph(new PdfParagraph
{
Text = $"From {info.DateFrom.ToShortDateString()} to {info.DateTo.ToShortDateString()}",
Text = $"С {info.DateFrom.ToShortDateString()} По {info.DateTo.ToShortDateString()}",
Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Center
});
CreateTable(new List<string> { "3cm", "3cm", "3cm", "3cm", "3cm", "10cm" });
CreateRow(new PdfRowParameters
{
Texts = new List<string> { "Second Name", "Name", "Last Name", "Settlement", "Eviction", "Cleaning set" },
Texts = new List<string> { "Фамилия", "Имя", "Отчество", "Заселение", "Выселение", "Набор" },
Style = "NormalTitle",
ParagraphAlignment = PdfParagraphAlignmentType.Center
});
@ -38,7 +38,7 @@ public abstract class AbstractSaveToPdf
model.LastName,
model.StartDate.ToShortDateString(),
model.EndDate.ToShortDateString(),
model.CleaningInstruments.Values.Select(x => x.Type).ToString()
string.Join(",", model.CleaningInstruments.Values.Select(x => x.Type))
},
Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Left

View File

@ -20,17 +20,24 @@ public abstract class AbstractSaveToWord
foreach (var mc in info.ListCleaningModels)
{
string? set = mc.CleaningInstruments
var set = string.Join(", ", mc.CleaningInstruments
.Values
.Select(x => x.Type)
.ToList()
.ToString();
.Select(x => x.Type));
CreateParagraph(new WordParagraph
{
Texts = new List<(string, WordTextProperties)> { ("Бронь " + mc.Id, new WordTextProperties { Bold = true, Size = "24", }) },
TextProperties = new WordTextProperties
{
Size = "24",
JustificationType = WordJustificationType.Both
}
});
CreateParagraph(new WordParagraph
{
Texts = new List<(string, WordTextProperties)>
{(
mc.StartDate.ToShortDateString() + " - " + mc.RoomNumber + " - " + set, new WordTextProperties { Size = "24", Bold=true}
mc.StartDate.ToShortDateString() + " - " + mc.RoomNumber + " - " + set, new WordTextProperties { Size = "20", Bold=false}
)},
TextProperties = new WordTextProperties
{

View File

@ -30,6 +30,7 @@ public class SaveToPdf : AbstractSaveToPdf
style.Font.Size = 14;
}
document.DefaultPageSetup.Orientation = Orientation.Landscape;
style = document.Styles.AddStyle("NormalTitle", "Normal");
style.Font.Bold = true;
}

View File

@ -0,0 +1,11 @@
namespace HotelContracts.BindingModels;
public class MailConfigBindingModel
{
public string MailLogin { get; set; } = string.Empty;
public string MailPassword { get; set; } = string.Empty;
public string SmtpClientHost { get; set; } = string.Empty;
public int SmtpClientPort { get; set; }
public string PopHost { get; set; } = string.Empty;
public int PopPort { get; set; }
}

View File

@ -0,0 +1,10 @@
using System.Net.Mail;
namespace HotelContracts.BindingModels;
public class MailSendInfoBindingModel
{
public string MailAddress { get; set; } = string.Empty;
public string Subject { get; set; } = string.Empty;
public Attachment AttachmentMail { get; set; }
}

View File

@ -7,4 +7,5 @@ public class RoomBindingModel : IRoomModel
public int Id { get; set; }
public string Type { get; set; }
public double Cost { get; set; }
public bool IsReserved { get; set; }
}

View File

@ -5,4 +5,5 @@ public class RoomSearchModel
public int? Id { get; set; }
public double? Cost { get; set; }
public string? Type { get; set; }
public bool? IsReserved { get; set; }
}

View File

@ -8,4 +8,5 @@ public class CleaningViewModel : ICleaningModel
public DateTime Date { get; set; }
public int RoomId { get; set; }
public Dictionary<int, ICleaningInstrumentsModel> CleaningInstruments { get; set; }
public RoomViewModel Room { get; set; }
}

View File

@ -8,4 +8,6 @@ public class GuestViewModel : IGuestModel
public string Name { get; set; } = string.Empty;
public string SecondName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Fio => SecondName + " " + Name + " " + LastName;
}

View File

@ -10,4 +10,6 @@ public class MaitreViewModel : IMaitreModel
public string LastName { get; set; } = string.Empty;
public string Login { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string Fio => SecondName + " " + Name + " " + LastName;
}

View File

@ -13,4 +13,11 @@ public class ReservationViewModel : IReservationModel
public int GuestId { get; set; }
public int MaitreId { get; set; }
public Dictionary<int, IRoomModel> ReservationsRooms { get; set; }
public GuestViewModel Guest { get; set; }
public double GetCost()
{
var days = EndDate - StartDate;
return ReservationsRooms.Sum(room => room.Value.Cost * days.Days);
}
}

View File

@ -6,6 +6,22 @@ namespace HotelContracts.ViewModels;
public class RoomViewModel : IRoomModel
{
public int Id { get; set; }
public string Type { get; set; } = string.Empty;
public string Type { get; set; }
public double Cost { get; set; }
public bool IsReserved { get; set; }
public Dictionary<int, IReservationModel> Reservation { get; set; }
public string GetTypeRoom()
{
return Type switch
{
"standard" => "Стандартный",
"superior" => "Улучшенный",
"bedroom" => "Со спальной комнатой",
"apartment" => "Апартаменты/квартира",
"studio" => "Студия",
"suite" => "Люкс",
_ => ""
};
}
}

View File

@ -20,4 +20,8 @@
<ProjectReference Include="..\HotelDataModels\HotelDataModels.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Migrations" />
</ItemGroup>
</Project>

View File

@ -30,9 +30,14 @@ public class CleaningStorage : ICleaningStorage
public CleaningViewModel? GetElement(CleaningSearchModel model)
{
if (!model.Id.HasValue) return null;
using var context = new HotelDataBase();
return context.Cleanings.FirstOrDefault(x => x.Id == model.Id)?.GetView;
if (model.RoomId.HasValue)
{
return context.Cleanings.FirstOrDefault(x => x.RoomId == model.RoomId)?.GetView;
}
return !model.Id.HasValue ?
null :
context.Cleanings.FirstOrDefault(x => x.Id == model.Id)?.GetView;
}
public CleaningViewModel? Insert(CleaningBindingModel model)

View File

@ -32,9 +32,10 @@ public class MaitreStorage : IMaitreStorage
public MaitreViewModel? GetElement(MaitreSearchModel model)
{
if (!model.Id.HasValue) return null;
using var context = new HotelDataBase();
return context.Maitres.FirstOrDefault(x => x.Id == model.Id)?.GetViewModel;
return string.IsNullOrEmpty(model.Login) ?
context.Maitres.FirstOrDefault(x => x.Id == model.Id)?.GetViewModel :
context.Maitres.FirstOrDefault(x => x.Login == model.Login)?.GetViewModel;
}
public MaitreViewModel? Insert(MaitreBindingModel model)

View File

@ -19,9 +19,16 @@ public class ReservationStorage : IReservationStorage
public List<ReservationViewModel> GetFilteredList(ReservationSearchModel model)
{
if (!model.From.HasValue || !model.To.HasValue)
return new List<ReservationViewModel>();
using var context = new HotelDataBase();
if (model.GuestId.HasValue)
{
return context.Reservations
.Where(x => x.GuestId == model.GuestId)
.Select(x => x.GetView)
.ToList();
}
if (!model.From.HasValue || !model.To.HasValue || !model.GuestId.HasValue)
return new List<ReservationViewModel>();
return context.Reservations
.Where(x => x.StartDate >= model.From && x.StartDate <= model.To)
.Select(x => x.GetView)
@ -35,6 +42,28 @@ public class ReservationStorage : IReservationStorage
return context.Reservations.FirstOrDefault(x => x.Id == model.Id)?.GetView;
}
public void SwitchRoomReserve(Reservation reservation, HotelDataBase context, bool state = true)
{
if (reservation.Rooms.Count > 0)
{
foreach (var roomItem in reservation.Rooms
.Select(x => context.Rooms.FirstOrDefault(y => y.Id == x.RoomId)))
{
if (roomItem is null) return;
roomItem.IsReserved = state;
context.Rooms.Update(roomItem);
}
return;
}
foreach (var roomItem in reservation.ReservationsRooms
.Select(x => context.Rooms.FirstOrDefault(y => y.Id == x.Key)))
{
if (roomItem is null) return;
roomItem.IsReserved = state;
context.Rooms.Update(roomItem);
}
}
public ReservationViewModel? Insert(ReservationBindingModel model)
{
using var context = new HotelDataBase();
@ -42,6 +71,7 @@ public class ReservationStorage : IReservationStorage
if (item == null) return null;
context.Reservations.Add(item);
SwitchRoomReserve(item, context);
context.SaveChanges();
return item.GetView;
@ -64,6 +94,7 @@ public class ReservationStorage : IReservationStorage
var item = context.Reservations.FirstOrDefault(x => x.Id == model.Id);
if (item == null) return null;
context.Reservations.Remove(item);
SwitchRoomReserve(item, context, false);
context.SaveChanges();
return item.GetView;
}

View File

@ -3,6 +3,7 @@ using HotelContracts.SearchModels;
using HotelContracts.StoragesContracts;
using HotelContracts.ViewModels;
using HotelDatabaseImplement.Models;
using Microsoft.EntityFrameworkCore;
namespace HotelDatabaseImplement.Implements;
@ -18,12 +19,15 @@ public class RoomStorage : IRoomStorage
public List<RoomViewModel> GetFilteredList(RoomSearchModel model)
{
if (string.IsNullOrEmpty(model.Type))
using var context = new HotelDataBase();
if (model.IsReserved.HasValue)
{
return new List<RoomViewModel>();
return context.Rooms
.Where(x => x.IsReserved == model.IsReserved)
.Select(x => x.GetView)
.ToList();
}
using var context = new HotelDataBase();
if (model.Cost.HasValue)
{
return context.Rooms

View File

@ -12,8 +12,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace HotelDatabaseImplement.Migrations
{
[DbContext(typeof(HotelDataBase))]
[Migration("20230407095141_init")]
partial class init
[Migration("20230518201459_1")]
partial class _1
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -181,14 +181,11 @@ namespace HotelDatabaseImplement.Migrations
b.Property<int>("RoomId")
.HasColumnType("int");
b.Property<int?>("RoomId1")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("RoomId");
b.HasIndex("ReservationId");
b.HasIndex("RoomId1");
b.HasIndex("RoomId");
b.ToTable("ReservationRooms");
});
@ -204,8 +201,8 @@ namespace HotelDatabaseImplement.Migrations
b.Property<double>("Cost")
.HasColumnType("float");
b.Property<int>("ReservationId")
.HasColumnType("int");
b.Property<bool>("IsReserved")
.HasColumnType("bit");
b.Property<string>("Type")
.IsRequired()
@ -213,8 +210,6 @@ namespace HotelDatabaseImplement.Migrations
b.HasKey("Id");
b.HasIndex("ReservationId");
b.ToTable("Rooms");
});
@ -241,28 +236,19 @@ namespace HotelDatabaseImplement.Migrations
{
b.HasOne("HotelDatabaseImplement.Models.Reservation", "Reservation")
.WithMany("Rooms")
.HasForeignKey("RoomId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("HotelDatabaseImplement.Models.Room", "Room")
.WithMany()
.HasForeignKey("RoomId1");
b.Navigation("Reservation");
b.Navigation("Room");
});
modelBuilder.Entity("HotelDatabaseImplement.Models.Room", b =>
{
b.HasOne("HotelDatabaseImplement.Models.Reservation", "Reservation")
.WithMany()
.HasForeignKey("ReservationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("HotelDatabaseImplement.Models.Room", "Room")
.WithMany("Reservation")
.HasForeignKey("RoomId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reservation");
b.Navigation("Room");
});
modelBuilder.Entity("HotelDatabaseImplement.Models.Cleaning", b =>
@ -274,6 +260,11 @@ namespace HotelDatabaseImplement.Migrations
{
b.Navigation("Rooms");
});
modelBuilder.Entity("HotelDatabaseImplement.Models.Room", b =>
{
b.Navigation("Reservation");
});
#pragma warning restore 612, 618
}
}

View File

@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace HotelDatabaseImplement.Migrations
{
/// <inheritdoc />
public partial class init : Migration
public partial class _1 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
@ -86,6 +86,21 @@ namespace HotelDatabaseImplement.Migrations
table.PrimaryKey("PK_Reservations", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Rooms",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Type = table.Column<string>(type: "nvarchar(max)", nullable: false),
Cost = table.Column<double>(type: "float", nullable: false),
IsReserved = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Rooms", x => x.Id);
});
migrationBuilder.CreateTable(
name: "CleaningInstrument",
columns: table => new
@ -112,27 +127,6 @@ namespace HotelDatabaseImplement.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Rooms",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Type = table.Column<string>(type: "nvarchar(max)", nullable: false),
Cost = table.Column<double>(type: "float", nullable: false),
ReservationId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Rooms", x => x.Id);
table.ForeignKey(
name: "FK_Rooms_Reservations_ReservationId",
column: x => x.ReservationId,
principalTable: "Reservations",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ReservationRooms",
columns: table => new
@ -140,23 +134,23 @@ namespace HotelDatabaseImplement.Migrations
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ReservationId = table.Column<int>(type: "int", nullable: false),
RoomId = table.Column<int>(type: "int", nullable: false),
RoomId1 = table.Column<int>(type: "int", nullable: true)
RoomId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ReservationRooms", x => x.Id);
table.ForeignKey(
name: "FK_ReservationRooms_Reservations_RoomId",
column: x => x.RoomId,
name: "FK_ReservationRooms_Reservations_ReservationId",
column: x => x.ReservationId,
principalTable: "Reservations",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ReservationRooms_Rooms_RoomId1",
column: x => x.RoomId1,
name: "FK_ReservationRooms_Rooms_RoomId",
column: x => x.RoomId,
principalTable: "Rooms",
principalColumn: "Id");
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
@ -169,20 +163,15 @@ namespace HotelDatabaseImplement.Migrations
table: "CleaningInstrument",
column: "CleaningInstrumentsId");
migrationBuilder.CreateIndex(
name: "IX_ReservationRooms_ReservationId",
table: "ReservationRooms",
column: "ReservationId");
migrationBuilder.CreateIndex(
name: "IX_ReservationRooms_RoomId",
table: "ReservationRooms",
column: "RoomId");
migrationBuilder.CreateIndex(
name: "IX_ReservationRooms_RoomId1",
table: "ReservationRooms",
column: "RoomId1");
migrationBuilder.CreateIndex(
name: "IX_Rooms_ReservationId",
table: "Rooms",
column: "ReservationId");
}
/// <inheritdoc />
@ -207,10 +196,10 @@ namespace HotelDatabaseImplement.Migrations
name: "Cleanings");
migrationBuilder.DropTable(
name: "Rooms");
name: "Reservations");
migrationBuilder.DropTable(
name: "Reservations");
name: "Rooms");
}
}
}

View File

@ -178,14 +178,11 @@ namespace HotelDatabaseImplement.Migrations
b.Property<int>("RoomId")
.HasColumnType("int");
b.Property<int?>("RoomId1")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("RoomId");
b.HasIndex("ReservationId");
b.HasIndex("RoomId1");
b.HasIndex("RoomId");
b.ToTable("ReservationRooms");
});
@ -201,8 +198,8 @@ namespace HotelDatabaseImplement.Migrations
b.Property<double>("Cost")
.HasColumnType("float");
b.Property<int>("ReservationId")
.HasColumnType("int");
b.Property<bool>("IsReserved")
.HasColumnType("bit");
b.Property<string>("Type")
.IsRequired()
@ -210,8 +207,6 @@ namespace HotelDatabaseImplement.Migrations
b.HasKey("Id");
b.HasIndex("ReservationId");
b.ToTable("Rooms");
});
@ -238,28 +233,19 @@ namespace HotelDatabaseImplement.Migrations
{
b.HasOne("HotelDatabaseImplement.Models.Reservation", "Reservation")
.WithMany("Rooms")
.HasForeignKey("RoomId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("HotelDatabaseImplement.Models.Room", "Room")
.WithMany()
.HasForeignKey("RoomId1");
b.Navigation("Reservation");
b.Navigation("Room");
});
modelBuilder.Entity("HotelDatabaseImplement.Models.Room", b =>
{
b.HasOne("HotelDatabaseImplement.Models.Reservation", "Reservation")
.WithMany()
.HasForeignKey("ReservationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("HotelDatabaseImplement.Models.Room", "Room")
.WithMany("Reservation")
.HasForeignKey("RoomId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Reservation");
b.Navigation("Room");
});
modelBuilder.Entity("HotelDatabaseImplement.Models.Cleaning", b =>
@ -271,6 +257,11 @@ namespace HotelDatabaseImplement.Migrations
{
b.Navigation("Rooms");
});
modelBuilder.Entity("HotelDatabaseImplement.Models.Room", b =>
{
b.Navigation("Reservation");
});
#pragma warning restore 612, 618
}
}

View File

@ -18,11 +18,12 @@ public class Cleaning : ICleaningModel
public Dictionary<int, ICleaningInstrumentsModel> CleaningInstruments {
get
{
_cleaningInstruments ??= CleaningInstrument
.ToDictionary(
x => x.CleaningInstrumentsId,
x => x.CleaningInstruments as ICleaningInstrumentsModel
);
using var database = new HotelDataBase();
_cleaningInstruments ??= database.CleaningInstrument
.Where(x => x.CleaningId == Id)
.ToDictionary(x => x.CleaningInstrumentsId,
y => database.CleaningInstruments.FirstOrDefault(k => k.Id == y.CleaningInstrumentsId) as
ICleaningInstrumentsModel);
return _cleaningInstruments;
}
}
@ -33,6 +34,7 @@ public class Cleaning : ICleaningModel
{
Id = model.Id,
Date = model.Date,
RoomId = model.RoomId,
CleaningInstrument = model.CleaningInstruments.Select(
x => new CleaningInstrument
{
@ -78,10 +80,18 @@ public class Cleaning : ICleaningModel
_cleaningInstruments = null;
}
public CleaningViewModel GetView => new CleaningViewModel
public CleaningViewModel GetView
{
get
{
using var context = new HotelDataBase();
return new CleaningViewModel
{
Id = Id,
Date = Date,
CleaningInstruments = CleaningInstruments
CleaningInstruments = CleaningInstruments,
Room = context.Rooms.FirstOrDefault(x => x.Id == RoomId).GetView
};
}
}
}

View File

@ -13,7 +13,7 @@ public class Reservation : IReservationModel
public int GuestId { get; set; }
public int MaitreId { get; set; }
[ForeignKey("RoomId")]
[ForeignKey("ReservationId")]
public virtual List<ReservationRoom> Rooms { get; set; } = new();
private Dictionary<int, IRoomModel>? _reservationsRooms = null;
@ -21,8 +21,11 @@ public class Reservation : IReservationModel
{
get
{
_reservationsRooms ??= Rooms
.ToDictionary(record => record.RoomId, room => room.Room as IRoomModel);
using var database = new HotelDataBase();
_reservationsRooms ??= database.ReservationRooms
.Where(x => x.ReservationId == Id)
.ToDictionary(t => t.RoomId,
y => database.Rooms.FirstOrDefault(k => k.Id == y.RoomId)! as IRoomModel);
return _reservationsRooms;
}
}
@ -38,6 +41,7 @@ public class Reservation : IReservationModel
MaitreId = model.MaitreId,
Rooms = model.ReservationsRooms.Select(x => new ReservationRoom
{
RoomId = x.Key,
Room = dataBase.Rooms.First(y => y.Id == x.Key)
}).ToList()
};
@ -68,9 +72,11 @@ public class Reservation : IReservationModel
var reservation = dataBase.Reservations.First(x => x.Id == Id);
foreach (var reservationRoom in model.ReservationsRooms)
{
var room = dataBase.Rooms.First(x => x.Id == reservationRoom.Key);
room.IsReserved = true;
dataBase.ReservationRooms.Add(new ReservationRoom
{
Room = dataBase.Rooms.First(x => x.Id == reservationRoom.Key),
Room = room,
Reservation = reservation
});
dataBase.SaveChanges();
@ -79,13 +85,21 @@ public class Reservation : IReservationModel
_reservationsRooms = null;
}
public ReservationViewModel GetView => new ReservationViewModel
public ReservationViewModel GetView
{
get
{
using var context = new HotelDataBase();
return new()
{
Id = Id,
StartDate = StartDate,
EndDate = EndDate,
GuestId = GuestId,
MaitreId = MaitreId,
ReservationsRooms = ReservationsRooms
ReservationsRooms = ReservationsRooms,
Guest = context.Guests.FirstOrDefault(x => x.Id == GuestId)?.GetView
};
}
}
}

View File

@ -1,4 +1,5 @@
using HotelContracts.BindingModels;
using System.ComponentModel.DataAnnotations.Schema;
using HotelContracts.BindingModels;
using HotelContracts.ViewModels;
using HotelDataModels.Models;
@ -7,10 +8,24 @@ namespace HotelDatabaseImplement.Models;
public class Room : IRoomModel
{
public int Id { get; set; }
public string Type { get; set; }
public string Type { get; set; } = string.Empty;
public double Cost { get; set; }
public bool IsReserved { get; set; }
public virtual Reservation Reservation { get; set; } = new();
[ForeignKey("RoomId")]
public virtual List<ReservationRoom> Reservation { get; set; } = new();
private Dictionary<int, IReservationModel>? _reservationsRooms = null;
public Dictionary<int, IReservationModel> ReservationsRooms
{
get
{
_reservationsRooms ??= Reservation
.ToDictionary(record => record.ReservationId,
room => room.Reservation as IReservationModel);
return _reservationsRooms;
}
}
public static Room? Create(RoomBindingModel? model)
{
@ -38,12 +53,20 @@ public class Room : IRoomModel
if (model == null) return;
Type = model.Type;
Cost = model.Cost;
IsReserved = model.IsReserved;
}
public void CheckReservations()
{
IsReserved = ReservationsRooms.Any(x => x.Value.EndDate > DateTime.Now);
}
public RoomViewModel GetView => new RoomViewModel
{
Id = Id,
Type = Type,
Cost = Cost
Cost = Cost,
Reservation = ReservationsRooms,
IsReserved = IsReserved
};
}

123
Hotel/HotelView/Api.cs Normal file
View File

@ -0,0 +1,123 @@
using System.Net.Mail;
using HotelBusinessLogic.MailWorker;
using HotelContracts.BindingModels;
using HotelContracts.BusinessLogicsContracts;
using HotelContracts.SearchModels;
using HotelContracts.StoragesContracts;
using HotelContracts.ViewModels;
namespace HotelView;
public class Api
{
private readonly IMaitreLogic _maitreLogic;
private readonly AbstractMailWorker _mailWorker;
private readonly IReportLogic _reportLogic;
public MaitreViewModel? Maitre { get; private set; }
public Api(IMaitreLogic maitreLogic,
IRoomLogic roomLogic,
IGuestLogic guestLogic,
IReservationLogic reservationLogic,
ICleaningInstrumentsLogic getCleaningInstrumentsLogic,
ICleaningLogic getCleaningLogic,
AbstractMailWorker mailWorker,
IReportLogic reportLogic)
{
_maitreLogic = maitreLogic;
GetRoomLogic = roomLogic;
GetGuestLogic = guestLogic;
GetReservationLogic = reservationLogic;
GetCleaningInstrumentsLogic = getCleaningInstrumentsLogic;
GetCleaningLogic = getCleaningLogic;
_mailWorker = mailWorker;
_reportLogic = reportLogic;
}
public IRoomLogic GetRoomLogic { get; }
public IGuestLogic GetGuestLogic { get; }
public IReservationLogic GetReservationLogic { get; }
public ICleaningInstrumentsLogic GetCleaningInstrumentsLogic { get; }
public ICleaningLogic GetCleaningLogic { get; }
public void Auth(string email, string password)
{
var maitreViewModel = _maitreLogic.ReadElement(new MaitreSearchModel
{
Login = email
});
if (maitreViewModel is null) return;
if (maitreViewModel.Password.Equals(password))
{
Maitre = maitreViewModel;
}
}
public bool Register(string login,
string password,
string name,
string secondName,
string lastName)
{
return _maitreLogic.Create(new MaitreBindingModel
{
Login = login,
Password = password,
Name = name,
SecondName = secondName,
LastName = lastName
});
}
private string GenerateFileName(string ext = "docx") =>
"C:/Users/andre/source/repos/PIbd-22_Bazunov_AI_Hotel/Hotel/HotelView/TempDirectoryForReports/" +
Maitre.Fio.Replace(" ", "_") + "_" +
DateTime.Now.ToShortDateString() + "." + ext;
public void SendReportWord()
{
var fileName = GenerateFileName();
_reportLogic.SaveListCleaningToWordFile(new ReportBindingModel
{
FileName = fileName
});
_mailWorker.MailSendAsync(new MailSendInfoBindingModel
{
MailAddress = Maitre.Login,
Subject = "Список составляющих комплектов для подготовки номеров",
AttachmentMail = new Attachment(fileName)
});
}
public void SendReportExcel()
{
var filename = GenerateFileName("xlsx");
_reportLogic.SaveListCleaningToExcelFile(new ReportBindingModel
{
FileName = filename
});
_mailWorker.MailSendAsync(new MailSendInfoBindingModel
{
MailAddress = Maitre.Login,
Subject = "Список составляющих комплектов для подготовки номеров",
AttachmentMail = new Attachment(filename)
});
}
public void SendReportPdf(DateTime from, DateTime to)
{
var filename = GenerateFileName("pdf");
_reportLogic.SaveGuestsToPdfFile(new ReportBindingModel
{
FileName = filename,
To = to,
From = from
});
_mailWorker.MailSendAsync(new MailSendInfoBindingModel
{
MailAddress = Maitre.Login,
Subject = "Список гостей с " + from.ToShortDateString() + " по " + to.ToShortDateString(),
AttachmentMail = new Attachment(filename)
});
}
}

View File

@ -1,32 +1,347 @@
using HotelView.Models;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using HotelContracts.BindingModels;
using HotelContracts.SearchModels;
using HotelContracts.ViewModels;
using HotelDataModels.Models;
using HotelView.Utils;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace HotelView.Controllers
namespace HotelView.Controllers;
public class HomeController : Controller
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly Api _api;
public HomeController(ILogger<HomeController> logger)
public HomeController(Api api)
{
_logger = logger;
_api = api;
}
public IActionResult Index()
{
if (_api.Maitre is null)
{
return Redirect("~/Home/Login");
}
var rooms = _api.GetRoomLogic.ReadList(null);
ViewBag.Maitre = _api.Maitre;
ViewBag.RoomIsReserved = Math.Round((double)rooms.Count(x => x.IsReserved) / rooms.Count * 100);
ViewBag.RoomIsReservedCount = rooms.Count(x => x.IsReserved);
ViewBag.RoomAll = rooms.Count;
var reservations = _api.GetReservationLogic.ReadList(null);
var guests = _api.GetGuestLogic.ReadList(null);
var cleaning = _api.GetCleaningLogic.ReadList(null).Last();
ViewBag.GuestCount = guests.Count;
ViewBag.GuestLivingCount = reservations.GroupBy(x => x.GuestId).Count();
ViewBag.GuestLiving = Math.Round((double)ViewBag.GuestLivingCount / guests.Count * 100);
ViewBag.MaxDay = reservations.Max(x => x.EndDate - x.StartDate).Days;
ViewBag.AverageDay = reservations.Select(x => x.EndDate - x.StartDate)
.Select(x => x.Days).Average();
ViewBag.MaxCost = reservations.Max(x => x.GetCost());
ViewBag.AverageCost = reservations.Select(x => x.GetCost()).Average();
ViewBag.LastReservation = reservations.Last();
ViewBag.LastReservationRooms = string.Join(",", reservations.Last().ReservationsRooms
.Select(x => x.Key));
ViewBag.LastCleaning = cleaning;
return View();
}
public IActionResult Privacy()
{
if (_api.Maitre is null)
{
return Redirect("~/Home/Login");
}
ViewBag.Maitre = _api.Maitre;
return View();
}
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost]
public void Login(string login, string password)
{
if (string.IsNullOrEmpty(login) ||
string.IsNullOrEmpty(password))
{
throw new Exception("Enter login and password");
}
_api.Auth(login, password);
if (_api.Maitre == null)
{
throw new Exception("Invalid login or password");
}
Response.Redirect("Index");
}
[HttpGet]
public IActionResult Register()
{
return View();
}
[HttpPost]
public void Register(string login, string password, string passwordConfirm, string fio)
{
if (string.IsNullOrEmpty(login) ||
string.IsNullOrEmpty(password) ||
string.IsNullOrEmpty(fio) ||
string.IsNullOrEmpty(passwordConfirm))
{
throw new Exception("Enter login and password");
}
if (!password.Equals(passwordConfirm))
{
throw new Exception("Password must be equals");
}
var fioSplit = fio.Split(" ");
_api.Register(login, password, fioSplit[0], fioSplit[1], fioSplit[2]);
Response.Redirect("Login");
}
public IActionResult Rooms()
{
ViewBag.Maitre = _api.Maitre;
return View(_api.GetRoomLogic.ReadList(null));
}
[HttpPost]
public void CreateRoom(string type, double cost)
{
if (string.IsNullOrEmpty(type) || cost == 0)
{
throw new Exception("Invalid arguments");
}
_api.GetRoomLogic.Create(new RoomBindingModel
{
Cost = cost,
Type = type
});
Response.Redirect("Rooms");
}
public IActionResult Guests()
{
ViewBag.Maitre = _api.Maitre;
return View(_api.GetGuestLogic.ReadList(null));
}
[HttpPost]
public void CreateGuest(string name, string secondName, string lastName)
{
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(secondName) || string.IsNullOrEmpty(lastName))
{
throw new Exception("Invalid arguments");
}
_api.GetGuestLogic.Create(new GuestBindingModel
{
Name = name.FirstCharToUpper(),
SecondName = secondName.FirstCharToUpper(),
LastName = lastName.FirstCharToUpper()
});
Response.Redirect("Guests");
}
[SuppressMessage("ReSharper.DPA", "DPA0000: DPA issues")]
public IActionResult Reservations()
{
ViewBag.Maitre = _api.Maitre;
ViewBag.Guests = _api.GetGuestLogic.ReadList(null);
ViewBag.Rooms = _api.GetRoomLogic.ReadList(new RoomSearchModel
{
IsReserved = false
});
var list = _api.GetReservationLogic.ReadList(null);
return View(list);
}
[HttpPost]
public void CreateReservation(int clientId, DateTime dateStart, DateTime dateEnd, List<int> rooms, string cleaning)
{
if (dateEnd < dateStart || dateStart < DateTime.Now)
{
throw new Exception("Invalid arguments");
}
_api.GetReservationLogic.Create(new ReservationBindingModel
{
MaitreId = _api.Maitre.Id,
GuestId = clientId,
StartDate = dateStart,
EndDate = dateEnd,
ReservationsRooms = _api.GetRoomLogic.ReadList(null)
.Where(x => rooms.Contains(x.Id))
.ToDictionary(x => x.Id, y => y as IRoomModel)
});
if (string.Equals(cleaning, "on", StringComparison.Ordinal))
{
var clInst = _api.GetCleaningInstrumentsLogic.ReadList(null);
foreach(var id in rooms)
{
_api.GetCleaningLogic.Create(new CleaningBindingModel
{
Date = dateEnd,
RoomId = id,
CleaningInstruments = clInst
.Where(x => x.Id <= 4)
.ToDictionary(x => x.Id,
x => x as ICleaningInstrumentsModel)
});
}
}
Response.Redirect("Reservations");
}
public IActionResult CleaningInstruments()
{
ViewBag.Maitre = _api.Maitre;
return View(_api.GetCleaningInstrumentsLogic.ReadList(null));
}
[HttpPost]
public void CreateCleaningInstrument(string type)
{
if (string.IsNullOrEmpty(type))
throw new ArgumentException("Type must be not null");
_api.GetCleaningInstrumentsLogic.Create(new CleaningInstrumentsBindingModel
{
Type = type
});
Response.Redirect("CleaningInstruments");
}
public IActionResult Cleaning()
{
ViewBag.Maitre = _api.Maitre;
ViewBag.CleaningInstruments = _api.GetCleaningInstrumentsLogic.ReadList(null);
ViewBag.Rooms = _api.GetRoomLogic.ReadList(null);
return View(_api.GetCleaningLogic.ReadList(null));
}
[HttpPost]
public void CreateCleaning(int roomId, DateTime dateTime, List<int> cleanings)
{
if (roomId == 0)
{
throw new ArgumentException("Room id must be more then zero");
}
_api.GetCleaningLogic.Create(new CleaningBindingModel
{
RoomId = roomId,
Date = dateTime,
CleaningInstruments = _api.GetCleaningInstrumentsLogic.ReadList(null)
.Where(x => cleanings.Contains(x.Id))
.ToDictionary(x => x.Id,
x => x as ICleaningInstrumentsModel)
});
Response.Redirect("Cleaning");
}
[HttpGet]
public void DeleteReservation(int id)
{
if (id == 0) return;
_api.GetReservationLogic.Delete(new ReservationBindingModel
{
Id = id
});
Response.Redirect("Reservations");
}
[HttpGet]
public void DeleteRoom(int id)
{
if (id == 0) return;
_api.GetRoomLogic.Delete(new()
{
Id = id
});
Response.Redirect("Rooms");
}
[HttpGet]
public void DeleteGuest(int id)
{
if (id == 0) return;
_api.GetGuestLogic.Delete(new()
{
Id = id
});
Response.Redirect("Guests");
}
[HttpGet]
public void DeleteClean(int id)
{
if (id == 0) return;
_api.GetCleaningLogic.Delete(new()
{
Id = id
});
Response.Redirect("Cleanings");
}
[HttpGet]
public void DeleteInstrument(int id)
{
if (id == 0) return;
_api.GetCleaningInstrumentsLogic.Delete(new()
{
Id = id
});
Response.Redirect("CleaningInstruments");
}
public IActionResult Report()
{
ViewBag.Maitre = _api.Maitre;
return View();
}
[HttpGet]
public void CreateWordReport()
{
_api.SendReportWord();
Response.Redirect("Report");
}
[HttpGet]
public void CreateExcelReport()
{
_api.SendReportExcel();
Response.Redirect("Report");
}
[HttpPost]
public void CreatePdfReport(DateTime from, DateTime to)
{
_api.SendReportPdf(from, to);
Response.Redirect("Report");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}

View File

@ -12,6 +12,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0-preview.2.23128.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
@ -20,4 +21,12 @@
<ProjectReference Include="..\HotelDatabaseImplement\HotelDatabaseImplement.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="TempDirectoryForReports" />
</ItemGroup>
<ItemGroup>
<None Include="wwwroot\css\index.css" />
</ItemGroup>
</Project>

View File

@ -1,15 +1,56 @@
using HotelBusinessLogic.BusinessLogics;
using HotelBusinessLogic.MailWorker;
using HotelBusinessLogic.OfficePackage;
using HotelBusinessLogic.OfficePackage.Implements;
using HotelContracts.BindingModels;
using HotelContracts.BusinessLogicsContracts;
using HotelContracts.StoragesContracts;
using HotelDatabaseImplement.Implements;
using HotelView;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddTransient<IMaitreStorage, MaitreStorage>();
builder.Services.AddTransient<IRoomStorage, RoomStorage>();
builder.Services.AddTransient<IGuestStorage, GuestStorage>();
builder.Services.AddTransient<IReservationStorage, ReservationStorage>();
builder.Services.AddTransient<ICleaningInstrumentsStorage, CleaningInstrumentsStorage>();
builder.Services.AddTransient<ICleaningStorage, CleaningStorage>();
builder.Services.AddTransient<AbstractSaveToExcel, SaveToExcel>();
builder.Services.AddTransient<AbstractSaveToPdf, SaveToPdf>();
builder.Services.AddTransient<AbstractSaveToWord, SaveToWord>();
builder.Services.AddTransient<IReportLogic, ReportLogic>();
builder.Services.AddTransient<ICleaningLogic, CleaningLogic>();
builder.Services.AddTransient<ICleaningInstrumentsLogic, CleaningInstrumentsLogic>();
builder.Services.AddTransient<IReservationLogic, ReservationLogic>();
builder.Services.AddTransient<IGuestLogic, GuestLogic>();
builder.Services.AddTransient<IRoomLogic, RoomLogic>();
builder.Services.AddTransient<IMaitreLogic, MaitreLogic>();
builder.Services.AddSingleton<AbstractMailWorker, MailKitWorker>();
builder.Services.AddSingleton<Api>();
var app = builder.Build();
var mailSender = app.Services.GetService<AbstractMailWorker>();
mailSender?.MailConfig(new MailConfigBindingModel
{
MailLogin = builder.Configuration?.GetSection("MailLogin")?.Value ?? string.Empty,
MailPassword = builder.Configuration?.GetSection("MailPassword")?.Value ?? string.Empty,
SmtpClientHost = builder.Configuration?.GetSection("SmtpClientHost")?.Value ?? string.Empty,
SmtpClientPort = Convert.ToInt32(builder.Configuration?.GetSection("SmtpClientPort")?.Value),
PopHost = builder.Configuration?.GetSection("PopHost")?.Value ?? string.Empty,
PopPort = Convert.ToInt32(builder.Configuration?.GetSection("PopPort")?.Value)
});
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}

View File

@ -0,0 +1,12 @@
namespace HotelView.Utils;
public static class StringExtensions
{
public static string FirstCharToUpper(this string input) =>
input switch
{
null => throw new ArgumentNullException(nameof(input)),
"" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)),
_ => string.Concat(input[0].ToString().ToUpper(), input.AsSpan(1))
};
}

View File

@ -0,0 +1,98 @@
@model List<HotelContracts.ViewModels.CleaningViewModel>
@{
ViewData["Title"] = "Уборки";
Layout = "_Layout";
}
<div class="">
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" id="create">
Новая уборка
</button>
@{
<ul class="demo-list-two mdl-list">
@foreach (var item in Model)
{
<li class="mdl-list__item" style="padding: 0 0 16px;">
<div class="demo-card-wide mdl-card mdl-shadow--2dp" style="width: 100%;">
<div class="mdl-card__title">
<h2 class="mdl-card__title-text">Уборка @item.Date.ToShortDateString()</h2>
</div>
<div class="mdl-card__supporting-text">
<span>Номер: #@item.Room.Id</span>
<ul class="demo-list-item mdl-list">
@{
@foreach (var room in item.CleaningInstruments)
{
<li class="mdl-list__item">
<span class="mdl-list__item-primary-content">
Комплект для уборки №@room.Value.Id @room.Value.Type
</span>
</li>
}
}
</ul>
<span>Всего инструментов @item.CleaningInstruments.Count</span>
</div>
<div class="mdl-card__actions mdl-card--border">
<a
href="/Home/DeleteClean?id=@item.Id"
class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
Удалить
</a>
</div>
</div>
</li>
}
</ul>
}
<dialog class="mdl-dialog">
<h4 class="mdl-dialog__title">Добавление уборки</h4>
<form method="post" asp-controller="Home" asp-action="CreateCleaning">
<div class="mdl-dialog__content">
<span>Дата уборки</span>
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="date" id="date" name="dateTime">
</div>
<span>Номер</span>
<div class="mdl-textfield mdl-js-textfield">
<select name="roomId" id="rooms" class="mdl-textfield__input">
@{
@foreach (var room in ViewBag.Rooms)
{
<option value="@room.Id">#@room.Id @room.GetTypeRoom()</option>
}
}
</select>
</div>
<span>Инструменты для уборки</span>
<div class="mdl-textfield mdl-js-textfield">
<select name="cleanings" id="" class="mdl-textfield__input" multiple="multiple">
@{
@foreach (var item in ViewBag.CleaningInstruments)
{
<option value="@item.Id">#@item.Id @item.Type</option>
}
}
</select>
</div>
</div>
<div class="mdl-dialog__actions">
<button type="submit" class="mdl-button">Добавить</button>
<button type="button" class="mdl-button close">Отмена</button>
</div>
</form>
</dialog>
</div>
<script>
const dialog = document.querySelector('dialog');
const showDialogButton = document.querySelector('#create');
showDialogButton.addEventListener('click', function() {
dialog.showModal();
});
dialog.querySelector('.close').addEventListener('click', function() {
dialog.close();
});
</script>

View File

@ -0,0 +1,55 @@
@model List<HotelContracts.ViewModels.CleaningInstrumentsViewModel>
@{
ViewData["Title"] = "Инструменты для уборки";
Layout = "_Layout";
}
<div class="">
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" id="create">
Добавить инструмент уборки
</button>
@{
<ul class="demo-list-two mdl-list">
@foreach (var item in Model)
{
<li class="mdl-list__item">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">cleaning_services</i>
#@item.Id - @item.Type
</span>
<a
href="/Home/DeleteInstrument?id=@item.Id"
class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
Удалить
</a>
</li>
}
</ul>
}
<dialog class="mdl-dialog">
<h4 class="mdl-dialog__title">Добавление иструмента</h4>
<form method="post" asp-controller="Home" asp-action="CreateCleaningInstrument">
<div class="mdl-dialog__content">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="secondName" name="type">
<label class="mdl-textfield__label" for="secondName">Название</label>
</div>
</div>
<div class="mdl-dialog__actions">
<button type="submit" class="mdl-button">Добавить</button>
<button type="button" class="mdl-button close">Отмена</button>
</div>
</form>
</dialog>
</div>
<script>
const dialog = document.querySelector('dialog');
const showDialogButton = document.querySelector('#create');
showDialogButton.addEventListener('click', function() {
dialog.showModal();
});
dialog.querySelector('.close').addEventListener('click', function() {
dialog.close();
});
</script>

View File

@ -0,0 +1,63 @@
@model List<HotelContracts.ViewModels.GuestViewModel>
@{
ViewData["Title"] = "Гости";
Layout = "_Layout";
}
<div class="">
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" id="create">
Добавить постояльца
</button>
@{
<ul class="demo-list-two mdl-list">
@foreach (var item in Model)
{
<li class="mdl-list__item">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">person</i>
#@item.Id - @item.Fio
</span>
<a
href="/Home/DeleteGuest?id=@item.Id"
class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
Удалить
</a>
</li>
}
</ul>
}
<dialog class="mdl-dialog">
<h4 class="mdl-dialog__title">Добавление гостя</h4>
<form method="post" asp-controller="Home" asp-action="CreateGuest">
<div class="mdl-dialog__content">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="secondName" name="secondName">
<label class="mdl-textfield__label" for="secondName">Фамилия</label>
</div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="name" name="name">
<label class="mdl-textfield__label" for="name">Имя</label>
</div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="LastName" name="LastName">
<label class="mdl-textfield__label" for="LastName">Отчество</label>
</div>
</div>
<div class="mdl-dialog__actions">
<button type="submit" class="mdl-button">Добавить</button>
<button type="button" class="mdl-button close">Отмена</button>
</div>
</form>
</dialog>
</div>
<script>
const dialog = document.querySelector('dialog');
const showDialogButton = document.querySelector('#create');
showDialogButton.addEventListener('click', function() {
dialog.showModal();
});
dialog.querySelector('.close').addEventListener('click', function() {
dialog.close();
});
</script>

View File

@ -1,8 +1,52 @@
@{
ViewData["Title"] = "Home Page";
Layout = "_Layout";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
<link rel="stylesheet" href="~/css/index.css">
<div class="">
<div class="demo-charts mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-grid">
<div class="pie animate no-round" style="--p:@ViewBag.RoomIsReserved;">@ViewBag.RoomIsReserved%</div>
<h6>Номеров забронировано (@ViewBag.RoomIsReservedCount из @ViewBag.RoomAll)</h6>
</div>
<div class="demo-charts mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-grid">
<div class="pie animate no-round" style="--p:@ViewBag.GuestLiving;">@ViewBag.GuestLiving%</div>
<h6>Постояльцев проживает (@ViewBag.GuestLivingCount из @ViewBag.GuestCount)</h6>
</div>
<div class="demo-charts mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-grid">
<div class="line" style="--value:@Math.Round(ViewBag.AverageDay);--max: @ViewBag.MaxDay">
<div class="line_min">0</div>
<div class="line_value">@ViewBag.AverageDay</div>
<div class="line_max">@ViewBag.MaxDay</div>
</div>
<h6 class="line_label">Среднее время проживания (в днях)</h6>
</div>
<div class="demo-charts mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-grid">
<div class="line" style="--value:@ViewBag.AverageCost;--max:@ViewBag.MaxCost;">
<div class="line_min">0</div>
<div class="line_value">@ViewBag.AverageCost</div>
<div class="line_max">@ViewBag.MaxCost</div>
</div>
<h6 class="line_label">Средний чек (в рублях)</h6>
</div>
<div class="demo-charts mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-grid">
<h6 class="info">Последнее бронирование</h6>
<span class="info">
<i class="material-icons mdl-list__item-icon">event_seat</i>
<span>
<b>С</b> @ViewBag.LastReservation.StartDate.ToShortDateString()
<b>по</b> @ViewBag.LastReservation.EndDate.ToShortDateString()
</span>
<span><b>Номер:</b> @ViewBag.LastReservationRooms</span>
<span><b>Клиент:</b> @ViewBag.LastReservation.Guest.Fio</span>
<span><b>На сумму</b> @ViewBag.LastReservation.GetCost()</span>
</span>
</div>
<div class="demo-charts mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-grid">
<h6 class="info">Последняя уборка</h6>
<span class="info-start">
<i class="material-icons mdl-list__item-icon">wash</i>
<span><b>Дата </b> @ViewBag.LastCleaning.Date</span>
<span><b>Номер:</b> @ViewBag.LastCleaning.Room.Id</span>
</span>
</div>
</div>

View File

@ -0,0 +1,44 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
<link rel="stylesheet" href="~/css/site.css">
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
<title>Регистрация</title>
</head>
<body>
<form method="post" class="auth">
<div class="auth-cont">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="email" id="login" name="login">
<label class="mdl-textfield__label" for="login">Логин</label>
</div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="name" name="fio">
<label class="mdl-textfield__label" for="name">Фио</label>
</div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="password" id="password" name="password">
<label class="mdl-textfield__label" for="password">Пароль</label>
</div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="password" id="password1" name="passwordConfirm">
<label class="mdl-textfield__label" for="password1">Подтвердите пароль</label>
</div>
<div class="auth-btn">
<button type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
Регистрация
</button>
<a class="mdl-button mdl-js-button mdl-button--raised mdl-button--accent"
asp-controller="Home"
asp-action="Login">
Назад
</a>
</div>
</div>
</form>
</body>
</html>

View File

@ -0,0 +1,49 @@
@model List<HotelContracts.ViewModels.RoomViewModel>
@{
ViewData["Title"] = "Отчеты";
Layout = "_Layout";
}
<link rel="stylesheet" href="~/css/index.css">
<div class="">
<div class="demo-charts mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-grid" style="padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;">
<h6>Создать отчет в формате (.docx) и отправить его на почту @ViewBag.Maitre.Login</h6>
<a asp-controller="Home"
asp-action="CreateWordReport"
class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
Отправить
</a>
</div>
<div class="demo-charts mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-grid" style="padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;">
<h6>Создать отчет в формате (.xlsx) и отправить его на почту @ViewBag.Maitre.Login</h6>
<a asp-controller="Home"
asp-action="CreateExcelReport"
class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
Отправить
</a>
</div>
<div class="demo-charts mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-grid" style="padding: 1rem;
display: flex;
justify-content: center;
align-items: center;">
<h6 class="line_label">Создать отчет в формате (.pdf) и отправить его на почту @ViewBag.Maitre.Login</h6>
<form asp-controller="Home" asp-action="CreatePdfReport" method="post">
<span>Начальная дата</span>
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="date" name="from">
</div>
<span>Конечная дата</span>
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="date" name="to">
</div>
<button type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
Отправить
</button>
</form>
</div>
</div>

View File

@ -0,0 +1,107 @@
@using HotelContracts.ViewModels
@model List<HotelContracts.ViewModels.ReservationViewModel>
@{
ViewData["Title"] = "Бронирования";
Layout = "_Layout";
}
<div class="">
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" id="create">
Новое бронирование
</button>
@{
<ul class="demo-list-two mdl-list">
@foreach (var item in Model)
{
<li class="mdl-list__item" style="
padding: 0 0 16px;
">
<div class="demo-card-wide mdl-card mdl-shadow--2dp" style="width: 100%;">
<div class="mdl-card__title">
<h2 class="mdl-card__title-text">Бронирование с @item.StartDate.ToShortDateString() по @item.EndDate.ToShortDateString()</h2>
</div>
<div class="mdl-card__supporting-text">
<span>Постоялец: @item.Guest.Fio</span>
<ul class="demo-list-item mdl-list">
@{
@foreach (var room in item.ReservationsRooms)
{
<li class="mdl-list__item">
<span class="mdl-list__item-primary-content">
Номер №@room.Value.Id стоимость за день: @room.Value.Cost
</span>
</li>
}
}
</ul>
<span>Общая стоимость @item.GetCost() рублей</span>
</div>
<div class="mdl-card__actions mdl-card--border">
<a
href="/Home/DeleteReservation?id=@item.Id"
class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
Удалить
</a>
</div>
</div>
</li>
}
</ul>
}
<dialog class="mdl-dialog">
<h4 class="mdl-dialog__title">Новое бронирование</h4>
<form method="post" asp-controller="Home" asp-action="CreateReservation">
<div class="mdl-dialog__content">
<span>Дата заселения</span>
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="date" id="dateStart" name="dateStart">
</div>
<span>Дата выселения</span>
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="date" id="dateEnd" name="dateEnd">
</div>
<div class="mdl-textfield mdl-js-textfield">
<select name="clientId" id="guest" class="mdl-textfield__input">
@{
@foreach (var client in ViewBag.Guests)
{
<option value="@client.Id">@client.Fio</option>
}
}
</select>
<span class="mdl-textfield__label" for="guest">Гость</span>
</div>
<span>Номер(а)</span>
<div class="mdl-textfield mdl-js-textfield">
<select name="rooms" id="rooms" class="mdl-textfield__input" multiple="multiple">
@{
@foreach (var room in ViewBag.Rooms)
{
<option value="@room.Id">#@room.Id @room.GetTypeRoom()</option>
}
}
</select>
</div>
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-1" name="cleaning">
<input type="checkbox" id="checkbox-1" class="mdl-checkbox__input" name="cleaning" checked="checked">
<span class="mdl-checkbox__label">Запланировать уборку?</span>
</label>
</div>
<div class="mdl-dialog__actions">
<button type="submit" class="mdl-button">Добавить</button>
<button type="button" class="mdl-button close">Отмена</button>
</div>
</form>
</dialog>
</div>
<script>
const dialog = document.querySelector('dialog');
const showDialogButton = document.querySelector('#create');
showDialogButton.addEventListener('click', function() {
dialog.showModal();
});
dialog.querySelector('.close').addEventListener('click', function() {
dialog.close();
});
</script>

View File

@ -0,0 +1,98 @@
@model List<HotelContracts.ViewModels.RoomViewModel>
@{
ViewData["Title"] = "Home Page";
Layout = "_Layout";
}
<div class="">
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" id="create">
Добавить номер
</button>
@{
<ul class="demo-list-two mdl-list">
@foreach (var item in Model)
{
<li class="mdl-list__item mdl-list__item--two-line">
<span class="mdl-list__item-primary-content">
@{
switch (item.Type)
{
case "standard":
<i class="material-icons mdl-list__item-avatar">chair</i>
break;
case "superior":
<i class="material-icons mdl-list__item-avatar">weekend</i>
break;
case "bedroom":
<i class="material-icons mdl-list__item-avatar">bed</i>
break;
case "apartment":
<i class="material-icons mdl-list__item-avatar">apartment</i>
break;
case "studio":
<i class="material-icons mdl-list__item-avatar">yard</i>
break;
case "suite":
<i class="material-icons mdl-list__item-avatar">spa</i>
break;
}
}
<span>Номер: №@item.Id</span>
<span class="mdl-list__item-sub-title">Стоимость за сутки: @item.Cost рублей</span>
</span>
@{
if (item.IsReserved)
{
<span>Забронирована</span>
}
}
<span class="mdl-list__item-secondary-content">
<span class="mdl-list__item-secondary-info">@item.GetTypeRoom()</span>
<a
href="/Home/DeleteRoom?id=@item.Id"
class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
Удалить
</a>
</span>
</li>
}
</ul>
}
<dialog class="mdl-dialog">
<h4 class="mdl-dialog__title">Добавление номера</h4>
<form method="post" asp-controller="Home" asp-action="CreateRoom">
<div class="mdl-dialog__content">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="number" id="cost" name="cost">
<label class="mdl-textfield__label" for="cost">Стоимость</label>
</div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<select class="mdl-textfield__input" name="type" id="type">
<option value="standard">Стандартный</option>
<option value="superior">Улучшенный</option>
<option value="bedroom">Со спальной комнатой</option>
<option value="apartment">Апартаменты/квартира</option>
<option value="studio">Студия</option>
<option value="suite">Люкс</option>
</select>
<label class="mdl-textfield__label" for="type">Вид номера</label>
</div>
</div>
<div class="mdl-dialog__actions">
<button type="submit" class="mdl-button">Добавить</button>
<button type="button" class="mdl-button close">Отмена</button>
</div>
</form>
</dialog>
</div>
<script>
const dialog = document.querySelector('dialog');
const showDialogButton = document.querySelector('#create');
showDialogButton.addEventListener('click', function() {
dialog.showModal();
});
dialog.querySelector('.close').addEventListener('click', function() {
dialog.close();
});
</script>

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
<link rel="stylesheet" href="~/css/site.css" runat="server">
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
<title>@ViewData["Title"] - Hotel</title>
</head>
<body>
<div class="demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-drawer mdl-layout--fixed-header">
<header class="demo-header mdl-layout__header mdl-color--grey-100 mdl-color-text--grey-600">
<div class="mdl-layout__header-row">
<span class="mdl-layout-title">Гостиница Принцесса на горошине</span>
</div>
</header>
<div class="demo-drawer mdl-layout__drawer mdl-color--blue-grey-900 mdl-color-text--blue-grey-50">
<header class="demo-drawer-header">
<div class="demo-avatar-dropdown">
<img src="https://cdn.icon-icons.com/icons2/1465/PNG/512/156womanofficeworker2_100687.png" alt="-" class="demo-avatar">
<span>@ViewBag.Maitre.Fio</span>
</div>
</header>
<nav class="demo-navigation mdl-navigation mdl-color--blue-grey-800">
<a class="mdl-navigation__link" asp-controller="Home" asp-action="Index"><i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">home</i>Домой</a>
<a class="mdl-navigation__link" asp-controller="Home" asp-action="Rooms"><i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">bed</i>Номера</a>
<a class="mdl-navigation__link" asp-controller="Home" asp-action="Reservations"><i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">event_seat</i>Бронирования</a>
<a class="mdl-navigation__link" asp-controller="Home" asp-action="Guests"><i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">person</i>Постояльцы</a>
<a class="mdl-navigation__link" asp-controller="Home" asp-action="Cleaning"><i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">wash</i>Уборки</a>
<a class="mdl-navigation__link" asp-controller="Home" asp-action="CleaningInstruments"><i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">cleaning_services</i>Средства для уборки</a>
<a class="mdl-navigation__link" asp-controller="Home" asp-action="Report"><i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">report</i>Отчеты</a>
<div class="mdl-layout-spacer"></div>
<a class="mdl-navigation__link" href=""><i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">help_outline</i><span class="visuallyhidden">Help</span></a>
</nav>
</div>
<main class="mdl-layout__content mdl-color--grey-100 main">
@RenderBody()
</main>
</div>
</body>
</html>

View File

@ -0,0 +1,36 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
<link rel="stylesheet" href="~/css/site.css">
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
<title>Авторизация</title>
</head>
<body>
<form method="post" class="auth">
<div class="auth-cont">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="email" id="login" name="login">
<label class="mdl-textfield__label" for="login">Логин</label>
</div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="password" id="password" name="password">
<label class="mdl-textfield__label" for="password">Пароль</label>
</div>
<div class="auth-btn">
<button type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
Авторизация
</button>
<a class="mdl-button mdl-js-button mdl-button--raised mdl-button--accent"
asp-controller="Home"
asp-action="Register">
Регистрация
</a>
</div>
</div>
</form>
</body>
</html>

View File

@ -1,49 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - HotelView</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/HotelView.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">HotelView</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
&copy; 2023 - HotelView - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View File

@ -1,3 +1 @@
@{
Layout = "_Layout";
}


View File

@ -5,5 +5,11 @@
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"SmtpClientHost": "smtp.gmail.com",
"SmtpClientPort": "587",
"PopHost": "pop.gmail.com",
"PopPort": "995",
"MailLogin": "sushibarulyanosk7@gmail.com",
"MailPassword": "qwzj uqlx uukp bpmc"
}

View File

@ -0,0 +1,146 @@
@property --p {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
.pie {
--p:20;
--b:8px;
--c:lightgreen;
--w:90px;
width: var(--w);
aspect-ratio:1;
display:inline-grid;
position: relative;
place-content:center;
font-size:25px;
margin: 5px 20px 5px 5px;
}
.pie:before,
.pie:after {
content:"";
position:absolute;
border-radius:50%;
}
.pie:before {
inset:0;
background:
radial-gradient(farthest-side,var(--c) 98%,#0000) top/var(--b) var(--b) no-repeat,
conic-gradient(var(--c) calc(var(--p)*1%),#0000 0);
-webkit-mask:radial-gradient(farthest-side,#0000 calc(99% - var(--b)),#000 calc(100% - var(--b)));
mask:radial-gradient(farthest-side,#0000 calc(99% - var(--b)),#000 calc(100% - var(--b)));
}
.pie:after {
inset:calc(50% - var(--b)/2);
background:var(--c);
transform:rotate(calc(var(--p)*3.6deg)) translateY(calc(50% - var(--w)/2));
}
@keyframes p {
from{--p:0}
}
.animate {
animation:p 1s .5s both;
}
.no-round:before {
background-size: 0 0, auto;
}
.no-round:after {
content: none;
}
@property --value {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
.line {
--value: 8;
--max: 10;
position: relative;
margin: 1em;
width: 100%;
height: 2px;
background-color: #343a405A;
animation:l 1s .5s both;
}
.line:after {
content: "";
display: block;
height: 4px;
top: -1px;
position: absolute;
background-color: lightgreen;
width: calc(var(--value) / var(--max) * 100%);
z-index: 2;
}
.line:before {
content: "";
display: block;
height: 8px;
width: 8px;
top: -3px;
position: absolute;
background-color: lightgreen;
left: calc(var(--value) / var(--max) * 100%);
z-index: 2;
}
.line * {
position: absolute;
top: 5px;
color: #343a40;
}
.line .line_max {
right: 0;
}
.line .line_value {
left: calc(var(--value) / var(--max) * 100%);
}
h6.line_label {
width: 100%;
text-align: center;
}
@keyframes l {
from {--value: 0}
}
h6.info {
width: 100%;
text-align: center;
}
span.info {
padding: 0 10px;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
span.info-start {
padding: 0 10px;
display: flex;
align-items: center;
}
span.info-start * {
margin: 0 5px;
}
.cont {
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}

View File

@ -1,18 +1,263 @@
html {
font-size: 14px;
* {
padding: 0;
margin: 0;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
.demo-layout-transparent {
background: url("https://wallpaperaccess.com/full/812556.jpg") center / cover;
}
html {
.demo-layout-transparent .mdl-layout__header,
.demo-layout-transparent .mdl-layout__drawer-button {
color: white;
}
.demo-graph {
width: 100px;
height: 100px;
}
html, body {
font-family: 'Roboto', 'Helvetica', sans-serif;
}
.demo-avatar {
width: 48px;
height: 48px;
border-radius: 24px;
}
.demo-layout .mdl-layout__header .mdl-layout__drawer-button {
color: rgba(0, 0, 0, 0.54);
}
.mdl-layout__drawer .avatar {
margin-bottom: 16px;
}
.demo-drawer {
border: none;
}
/* iOS Safari specific workaround */
.demo-drawer .mdl-menu__container {
z-index: -1;
}
.demo-drawer .demo-navigation {
z-index: -2;
}
/* END iOS Safari specific workaround */
.demo-drawer .mdl-menu .mdl-menu__item {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
}
.demo-drawer-header {
box-sizing: border-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-justify-content: flex-end;
-ms-flex-pack: end;
justify-content: flex-end;
padding: 16px;
height: 151px;
}
.demo-avatar-dropdown {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
position: relative;
min-height: 100%;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
width: 100%;
}
.demo-navigation {
-webkit-flex-grow: 1;
-ms-flex-positive: 1;
flex-grow: 1;
}
.demo-layout .demo-navigation .mdl-navigation__link {
display: -webkit-flex !important;
display: -ms-flexbox !important;
display: flex !important;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
color: rgba(255, 255, 255, 0.56);
font-weight: 500;
}
.demo-layout .demo-navigation .mdl-navigation__link:hover {
background-color: #00BCD4;
color: #37474F;
}
.demo-navigation .mdl-navigation__link .material-icons {
font-size: 24px;
color: rgba(255, 255, 255, 0.56);
margin-right: 32px;
}
.demo-content {
max-width: 1080px;
}
.demo-charts {
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
}
.demo-chart:nth-child(1) {
color: #ACEC00;
}
.demo-chart:nth-child(2) {
color: #00BBD6;
}
.demo-chart:nth-child(3) {
color: #BA65C9;
}
.demo-chart:nth-child(4) {
color: #EF3C79;
}
.demo-graphs {
padding: 16px 32px;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-align-items: stretch;
-ms-flex-align: stretch;
align-items: stretch;
}
_:-ms-input-placeholder, :root .demo-graphs {
min-height: 664px;
}
_:-ms-input-placeholder, :root .demo-graph {
max-height: 300px;
}
/* TODO end */
.demo-graph:nth-child(1) {
color: #00b9d8;
}
.demo-graph:nth-child(2) {
color: #d9006e;
}
.demo-cards {
-webkit-align-items: flex-start;
-ms-flex-align: start;
align-items: flex-start;
-webkit-align-content: flex-start;
-ms-flex-line-pack: start;
align-content: flex-start;
}
.demo-cards .demo-separator {
height: 32px;
}
.demo-cards .mdl-card__title.mdl-card__title {
color: white;
font-size: 24px;
font-weight: 400;
}
.demo-cards ul {
padding: 0;
}
.demo-cards h3 {
font-size: 1em;
}
.demo-updates .mdl-card__title {
min-height: 200px;
background-image: url('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTSIsmpJQm0OTBcGyY-Y3ECq4UMpN2lAcagoQ&usqp=CAU');
background-position: 90% 100%;
background-repeat: no-repeat;
}
.demo-cards .mdl-card__actions a {
color: #00BCD4;
text-decoration: none;
}
.demo-options h3 {
margin: 0;
}
.demo-options .mdl-checkbox__box-outline {
border-color: rgba(255, 255, 255, 0.89);
}
.demo-options ul {
margin: 0;
list-style-type: none;
}
.demo-options li {
margin: 4px 0;
}
.demo-options .material-icons {
color: rgba(255, 255, 255, 0.89);
}
.demo-options .mdl-card__actions {
height: 64px;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
box-sizing: border-box;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
}
body {
margin-bottom: 60px;
background-color: #000;
}
.auth {
display: flex;
height: 100vh;
justify-content: center;
align-items: center;
}
.auth-cont {
padding: 1em;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 10px;
}
.auth-btn {
display: flex;
width: 100%;
justify-content: space-between;
}
.auth-btn * {
width: 100%;
margin: 5px;
}
.main {
padding: 40px;
}
dialog {
position: absolute;
top: 30%;
left: 20px;
}
.mdl-list__item-avatar {
background-color: #00000000 !important;
color: black !important;
}
header.demo-drawer-header {
height: min-content !important;
}